mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
This pull request introduces significant improvements to the DM database driver, especially around insert operations, and refines documentation and tests to reflect these changes. The main focus is enabling support for "replace" and "insert ignore" operations using DM's `MERGE` statement, improving type reporting for table fields, and updating documentation for clarity and accuracy. ### DM Driver Insert Operations * Added support for `Replace` and `InsertIgnore` operations in the DM driver by internally mapping them to DM's `MERGE` statement. This enables upsert and insert-ignore behavior for DM databases, improving compatibility with other drivers. * Implemented helper methods (`doMergeInsert`, `doInsertIgnore`, and `getPrimaryKeys`) to generate correct `MERGE` SQL statements and automatically detect primary keys when needed. [[1]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL31-R94) [[2]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL115-R212) * Updated the logic for building update values and SQL generation to ensure correct behavior for both upsert and insert-ignore cases. [[1]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL61-R109) [[2]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL89-R132) [[3]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL100-R144) [[4]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL115-R212) ### Table Field Type Reporting * Improved the DM driver's `TableFields` method to report column types with length/precision (e.g., `VARCHAR(128)` instead of just `VARCHAR`), aligning with expectations and other drivers. [[1]](diffhunk://#diff-40a365112421ae1967bd960f8acefcc91ddb8180865b78bc49cd090fbf4883daL26-R26) [[2]](diffhunk://#diff-40a365112421ae1967bd960f8acefcc91ddb8180865b78bc49cd090fbf4883daR88-R105) * Updated related unit tests to expect the new type format for DM table fields. ### Documentation Updates * Removed outdated or redundant documentation in both English and Chinese driver README files, and clarified supported features and limitations for DM and other drivers. [[1]](diffhunk://#diff-d49f5bc3a34b11a6ccb82cc54675b06a7dea5f0a943ae91c4ca0d28bd5003299L1) [[2]](diffhunk://#diff-d49f5bc3a34b11a6ccb82cc54675b06a7dea5f0a943ae91c4ca0d28bd5003299L47-R46) [[3]](diffhunk://#diff-d49f5bc3a34b11a6ccb82cc54675b06a7dea5f0a943ae91c4ca0d28bd5003299L119-L122) [[4]](diffhunk://#diff-05411a14e9c7ca235f7f436bfde732853aa93b364361fe80d65ac768f4e4d613L1-L126) ### Test Suite Enhancements * Refactored and restored unit tests for DM driver insert operations, including tests for `Save`, `Insert`, and the new `InsertIgnore` functionality to ensure correct behavior and compatibility. [[1]](diffhunk://#diff-2b1a59b8b2adaa1ca3074629374ab122929e4d4fbb4cc794b8e1db60ebf8d4c2L143-L245) [[2]](diffhunk://#diff-2b1a59b8b2adaa1ca3074629374ab122929e4d4fbb4cc794b8e1db60ebf8d4c2R512-R632) * Minor adjustments to DM test initialization for improved clarity. ### Core Insert Logic Minor Refactoring * Minor variable renaming for clarity in the core insert logic (`gdb_core.go`), improving code readability. [[1]](diffhunk://#diff-b1bbe5e3995261813e4e0ac6ffee8a37c236eaa2759f2bd82e211711695a70bcL449-R452) [[2]](diffhunk://#diff-b1bbe5e3995261813e4e0ac6ffee8a37c236eaa2759f2bd82e211711695a70bcL466-R474) --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
223 lines
5.2 KiB
Go
223 lines
5.2 KiB
Go
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
|
//
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
// If a copy of the MIT was not distributed with this file,
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
package dm_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/container/garray"
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/gogf/gf/v2/test/gtest"
|
|
)
|
|
|
|
var (
|
|
db gdb.DB
|
|
dblink gdb.DB
|
|
dbErr gdb.DB
|
|
ctx context.Context
|
|
TableSize = 10
|
|
)
|
|
|
|
const (
|
|
TestDBHost = "127.0.0.1"
|
|
TestDBPort = "5236"
|
|
TestDBUser = "SYSDBA"
|
|
TestDBPass = "SYSDBA001"
|
|
TestDBName = "SYSDBA"
|
|
TestDBType = "dm"
|
|
TestCharset = "utf8"
|
|
)
|
|
|
|
type User struct {
|
|
ID int64 `orm:"id"`
|
|
AccountName string `orm:"account_name"`
|
|
PwdReset int64 `orm:"pwd_reset"`
|
|
AttrIndex int64 `orm:"attr_index"`
|
|
Enabled int64 `orm:"enabled"`
|
|
Deleted int64 `orm:"deleted"`
|
|
CreatedBy string `orm:"created_by"`
|
|
CreatedTime time.Time `orm:"created_time"`
|
|
UpdatedBy string `orm:"updated_by"`
|
|
UpdatedTime time.Time `orm:"updated_time"`
|
|
}
|
|
|
|
func init() {
|
|
node := gdb.ConfigNode{
|
|
Host: TestDBHost,
|
|
Port: TestDBPort,
|
|
User: TestDBUser,
|
|
Pass: TestDBPass,
|
|
Name: TestDBName,
|
|
Type: TestDBType,
|
|
Role: "master",
|
|
Charset: TestCharset,
|
|
Weight: 1,
|
|
MaxIdleConnCount: 10,
|
|
MaxOpenConnCount: 10,
|
|
// CreatedAt: "created_time",
|
|
// UpdatedAt: "updated_time",
|
|
}
|
|
|
|
nodeLink := gdb.ConfigNode{
|
|
Type: TestDBType,
|
|
Name: TestDBName,
|
|
Link: fmt.Sprintf(
|
|
"dm:%s:%s@tcp(%s:%s)/%s?charset=%s",
|
|
TestDBUser, TestDBPass, TestDBHost, TestDBPort, TestDBName, TestCharset,
|
|
),
|
|
}
|
|
|
|
nodeErr := gdb.ConfigNode{
|
|
Host: TestDBHost,
|
|
Port: TestDBPort,
|
|
User: TestDBUser,
|
|
Pass: "1234",
|
|
Name: TestDBName,
|
|
Type: TestDBType,
|
|
Role: "master",
|
|
Charset: TestCharset,
|
|
Weight: 1,
|
|
}
|
|
|
|
gdb.AddConfigNode(gdb.DefaultGroupName, node)
|
|
if r, err := gdb.New(node); err != nil {
|
|
gtest.Fatal(err)
|
|
} else {
|
|
db = r
|
|
}
|
|
|
|
gdb.AddConfigNode("dblink", nodeLink)
|
|
if r, err := gdb.New(nodeLink); err != nil {
|
|
gtest.Fatal(err)
|
|
} else {
|
|
dblink = r
|
|
}
|
|
|
|
gdb.AddConfigNode("dbErr", nodeErr)
|
|
if r, err := gdb.New(nodeErr); err != nil {
|
|
gtest.Fatal(err)
|
|
} else {
|
|
dbErr = r
|
|
}
|
|
|
|
ctx = context.Background()
|
|
|
|
// db.SetDebug(true)
|
|
}
|
|
|
|
func dropTable(table string) {
|
|
count, err := db.GetCount(
|
|
ctx,
|
|
"SELECT COUNT(*) FROM all_tables WHERE owner = ? And table_name= ?", TestDBName, strings.ToUpper(table),
|
|
)
|
|
if err != nil {
|
|
gtest.Fatal(err)
|
|
}
|
|
|
|
if count == 0 {
|
|
return
|
|
}
|
|
if _, err := db.Exec(ctx, fmt.Sprintf("DROP TABLE %s", table)); err != nil {
|
|
gtest.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func createTable(table ...string) (name string) {
|
|
if len(table) > 0 {
|
|
name = table[0]
|
|
} else {
|
|
name = fmt.Sprintf("random_%d", gtime.Timestamp())
|
|
}
|
|
|
|
dropTable(name)
|
|
|
|
if _, err := db.Exec(ctx, fmt.Sprintf(`
|
|
CREATE TABLE "%s"
|
|
(
|
|
"ID" BIGINT NOT NULL,
|
|
"ACCOUNT_NAME" VARCHAR(128) DEFAULT '' NOT NULL COMMENT 'Account Name',
|
|
"PWD_RESET" TINYINT DEFAULT 0 NOT NULL,
|
|
"ENABLED" INT DEFAULT 1 NOT NULL,
|
|
"DELETED" INT DEFAULT 0 NOT NULL,
|
|
"ATTR_INDEX" INT DEFAULT 0 ,
|
|
"CREATED_BY" VARCHAR(32) DEFAULT '' NOT NULL,
|
|
"CREATED_TIME" TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP() NOT NULL,
|
|
"UPDATED_BY" VARCHAR(32) DEFAULT '' NOT NULL,
|
|
"UPDATED_TIME" TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP() NOT NULL,
|
|
NOT CLUSTER PRIMARY KEY("ID")) STORAGE(ON "MAIN", CLUSTERBTR) ;
|
|
`, name)); err != nil {
|
|
gtest.Fatal(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
func createInitTable(table ...string) (name string) {
|
|
name = createTable(table...)
|
|
array := garray.New(true)
|
|
for i := 1; i <= TableSize; i++ {
|
|
array.Append(g.Map{
|
|
"id": i,
|
|
"account_name": fmt.Sprintf(`name_%d`, i),
|
|
"pwd_reset": 0,
|
|
"attr_index": i,
|
|
"created_time": gtime.Now(),
|
|
})
|
|
}
|
|
result, err := db.Schema(TestDBName).Insert(context.Background(), name, array.Slice())
|
|
gtest.AssertNil(err)
|
|
|
|
n, e := result.RowsAffected()
|
|
gtest.Assert(e, nil)
|
|
gtest.Assert(n, TableSize)
|
|
return
|
|
}
|
|
|
|
func createTableFalse(table ...string) (name string, err error) {
|
|
if len(table) > 0 {
|
|
name = table[0]
|
|
} else {
|
|
name = fmt.Sprintf("random_%d", gtime.Timestamp())
|
|
}
|
|
|
|
dropTable(name)
|
|
|
|
if _, err := db.Exec(ctx, fmt.Sprintf(`
|
|
CREATE TABLE "%s"
|
|
(
|
|
"ID" BIGINT NOT NULL,
|
|
"ACCOUNT_NAME" VARCHAR(128) DEFAULT '' NOT NULL,
|
|
"PWD_RESET" TINYINT DEFAULT 0 NOT NULL,
|
|
"ENABLED" INT DEFAULT 1 NOT NULL,
|
|
"DELETED" INT DEFAULT 0 NOT NULL,
|
|
"INDEX" INT DEFAULT 0 ,
|
|
"ATTR_INDEX" INT DEFAULT 0 ,
|
|
"CREATED_BY" VARCHAR(32) DEFAULT '' NOT NULL,
|
|
"CREATED_TIME" TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP() NOT NULL,
|
|
"UPDATED_BY" VARCHAR(32) DEFAULT '' NOT NULL,
|
|
"UPDATED_TIME" TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP() NOT NULL,
|
|
NOT CLUSTER PRIMARY KEY("ID")) STORAGE(ON "MAIN", CLUSTERBTR) ;
|
|
`, name)); err != nil {
|
|
// gtest.Fatal(err)
|
|
return name, fmt.Errorf("createTableFalse")
|
|
}
|
|
|
|
return name, nil
|
|
}
|
|
|
|
func createInitTables(len int) []string {
|
|
tables := make([]string, 0, len)
|
|
for range len {
|
|
tables = append(tables, createInitTable())
|
|
}
|
|
return tables
|
|
}
|