Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/ci-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
# Service containers to run with `code-test`
services:
# Etcd service.
# docker run -d --name etcd -p 2379:2379 -e ALLOW_NONE_AUTHENTICATION=yes bitnamilegacy/etcd:3.4.24
# docker run -p 2379:2379 -e ALLOW_NONE_AUTHENTICATION=yes bitnamilegacy/etcd:3.4.24
etcd:
image: bitnamilegacy/etcd:3.4.24
env:
Expand All @@ -75,7 +75,7 @@ jobs:
- 6379:6379

# MySQL backend server.
# docker run -d --name mysql \
# docker run \
# -p 3306:3306 \
# -e MYSQL_DATABASE=test \
# -e MYSQL_ROOT_PASSWORD=12345678 \
Expand All @@ -89,7 +89,7 @@ jobs:
- 3306:3306

# MariaDb backend server.
# docker run -d --name mariadb \
# docker run \
# -p 3307:3306 \
# -e MYSQL_DATABASE=test \
# -e MYSQL_ROOT_PASSWORD=12345678 \
Expand All @@ -103,7 +103,7 @@ jobs:
- 3307:3306

# PostgreSQL backend server.
# docker run -d --name postgres \
# docker run \
# -p 5432:5432 \
# -e POSTGRES_PASSWORD=12345678 \
# -e POSTGRES_USER=postgres \
Expand Down Expand Up @@ -150,7 +150,7 @@ jobs:
--health-retries 10

# ClickHouse backend server.
# docker run -d --name clickhouse \
# docker run \
# -p 9000:9000 -p 8123:8123 -p 9001:9001 \
# clickhouse/clickhouse-server:24.11.1.2557-alpine
clickhouse-server:
Expand All @@ -161,7 +161,7 @@ jobs:
- 9001:9001

# Polaris backend server.
# docker run -d --name polaris \
# docker run \
# -p 8090:8090 -p 8091:8091 -p 8093:8093 -p 9090:9090 -p 9091:9091 \
# polarismesh/polaris-standalone:v1.17.2
polaris:
Expand Down
4 changes: 4 additions & 0 deletions cmd/gf/internal/cmd/gendao/gendao.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ var (
"smallmoney": {
Type: "float64",
},
"uuid": {
Type: "uuid.UUID",
Import: "github.com/google/uuid",
},
}

// tablewriter Options
Expand Down
19 changes: 10 additions & 9 deletions contrib/drivers/README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import _ "github.com/gogf/gf/contrib/drivers/sqlite/v2"

#### cgo version

When the target is a 32-bit Windows system, the cgo version needs to be used.
When the target is a `32-bit` Windows system, the `cgo` version needs to be used.

```go
import _ "github.com/gogf/gf/contrib/drivers/sqlitecgo/v2"
Expand All @@ -69,10 +69,6 @@ import _ "github.com/gogf/gf/contrib/drivers/sqlitecgo/v2"
import _ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
```

Note:

- It does not support `Replace` features.

### SQL Server

```go
Expand All @@ -81,9 +77,10 @@ import _ "github.com/gogf/gf/contrib/drivers/mssql/v2"

Note:

- It does not support `Replace` features.
- `InsertIgnore` returns error if there is no primary key or unique index submitted with record.
- It supports server version >= `SQL Server2005`
- It ONLY supports datetime2 and datetimeoffset types for auto handling created_at/updated_at/deleted_at columns, because datetime type does not support microseconds precision when column value is passed as string.
- It ONLY supports `datetime2` and `datetimeoffset` types for auto handling created_at/updated_at/deleted_at columns,
because datetime type does not support microseconds precision when column value is passed as string.

### Oracle

Expand All @@ -93,8 +90,8 @@ import _ "github.com/gogf/gf/contrib/drivers/oracle/v2"

Note:

- It does not support `Replace` features.
- It does not support `LastInsertId`.
- `InsertIgnore` returns error if there is no primary key or unique index submitted with record.

### ClickHouse

Expand All @@ -104,7 +101,7 @@ import _ "github.com/gogf/gf/contrib/drivers/clickhouse/v2"

Note:

- It does not support `InsertIgnore/InsertGetId` features.
- It does not support `InsertIgnore/InsertAndGetId` features.
- It does not support `Save/Replace` features.
- It does not support `Transaction` feature.
- It does not support `RowsAffected` feature.
Expand All @@ -115,6 +112,10 @@ Note:
import _ "github.com/gogf/gf/contrib/drivers/dm/v2"
```

Note:

- `InsertIgnore` returns error if there is no primary key or unique index submitted with record.

## Custom Drivers

It's quick and easy, please refer to current driver source.
Expand Down
1 change: 1 addition & 0 deletions contrib/drivers/clickhouse/clickhouse_do_insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
)

// DoInsert inserts or updates data for given table.
// The list parameter must contain at least one record, which was previously validated.
func (d *Driver) DoInsert(
ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption,
) (result sql.Result, err error) {
Expand Down
65 changes: 24 additions & 41 deletions contrib/drivers/dm/dm_do_insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
)

// DoInsert inserts or updates data for given table.
// The list parameter must contain at least one record, which was previously validated.
func (d *Driver) DoInsert(
ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption,
) (result sql.Result, err error) {
Expand Down Expand Up @@ -60,46 +61,46 @@ func (d *Driver) doInsertIgnore(ctx context.Context,
// When withUpdate is false, it performs insert ignore (insert only when no conflict).
func (d *Driver) doMergeInsert(
ctx context.Context,
link gdb.Link,
table string,
list gdb.List,
option gdb.DoInsertOption,
withUpdate bool,
link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption, withUpdate bool,
) (result sql.Result, err error) {
// If OnConflict is not specified, automatically get the primary key of the table
conflictKeys := option.OnConflict
if len(conflictKeys) == 0 {
conflictKeys, err = d.getPrimaryKeys(ctx, table)
primaryKeys, err := d.Core.GetPrimaryKeys(ctx, table)
if err != nil {
return nil, gerror.WrapCode(
gcode.CodeInternalError,
err,
`failed to get primary keys for table`,
)
}
if len(conflictKeys) == 0 {
return nil, gerror.NewCode(
foundPrimaryKey := false
for _, primaryKey := range primaryKeys {
for dataKey := range list[0] {
if strings.EqualFold(dataKey, primaryKey) {
foundPrimaryKey = true
break
}
}
if foundPrimaryKey {
break
}
}
if !foundPrimaryKey {
return nil, gerror.NewCodef(
gcode.CodeMissingParameter,
`Please specify conflict columns or ensure the table has a primary key`,
`Replace/Save/InsertIgnore operation requires conflict detection: `+
`either specify OnConflict() columns or ensure table '%s' has a primary key in the data`,
table,
)
}
}

if len(list) == 0 {
opName := "Save"
if !withUpdate {
opName = "InsertIgnore"
}
return nil, gerror.NewCodef(
gcode.CodeInvalidRequest, `%s operation list is empty by dm driver`, opName,
)
conflictKeys = primaryKeys
Comment on lines +77 to +97
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current logic only checks if at least ONE primary key exists in the data, then uses ALL primary keys for conflict detection. For tables with composite primary keys, this could cause issues if only some parts of the composite key are present in the data. Consider checking that ALL primary keys are present in the data before setting conflictKeys = primaryKeys, or only include the primary keys that are actually present in the data.

Copilot uses AI. Check for mistakes.
}

var (
one = list[0]
oneLen = len(one)
charL, charR = d.GetChars()

one = list[0]
oneLen = len(one)
charL, charR = d.GetChars()
conflictKeySet = gset.New(false)

// queryHolders: Handle data with Holder that need to be merged
Expand Down Expand Up @@ -155,24 +156,6 @@ func (d *Driver) doMergeInsert(
return batchResult, nil
}

// getPrimaryKeys retrieves the primary key field names of the table as a slice of strings.
// This method extracts primary key information from TableFields.
func (d *Driver) getPrimaryKeys(ctx context.Context, table string) ([]string, error) {
tableFields, err := d.TableFields(ctx, table)
if err != nil {
return nil, err
}

var primaryKeys []string
for _, field := range tableFields {
if field.Key == "PRI" {
primaryKeys = append(primaryKeys, field.Name)
}
}

return primaryKeys, nil
}

// parseSqlForMerge generates MERGE statement for DM database.
// When updateValues is empty, it only inserts (INSERT IGNORE behavior).
// When updateValues is provided, it performs upsert (INSERT or UPDATE).
Expand Down
43 changes: 25 additions & 18 deletions contrib/drivers/dm/dm_z_unit_basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,31 +602,38 @@ func Test_Model_InsertIgnore(t *testing.T) {
// db.SetDebug(true)

gtest.C(t, func(t *gtest.T) {
data := User{
ID: int64(666),
AccountName: fmt.Sprintf(`name_%d`, 666),
PwdReset: 0,
AttrIndex: 99,
CreatedTime: time.Now(),
UpdatedTime: time.Now(),
data := g.Map{
"id": 1,
"account_name": fmt.Sprintf(`name_%d`, 777),
"pwd_reset": 0,
"attr_index": 777,
"created_time": gtime.Now(),
}
_, err := db.Model(table).Data(data).Insert()
_, err := db.Model(table).Data(data).InsertIgnore()
t.AssertNil(err)

one, err := db.Model(table).WherePri(1).One()
t.AssertNil(err)
t.Assert(one["ACCOUNT_NAME"].String(), "name_1")

count, err := db.Model(table).Count()
t.AssertNil(err)
t.Assert(count, TableSize)
})

gtest.C(t, func(t *gtest.T) {
data := User{
ID: int64(666),
AccountName: fmt.Sprintf(`name_%d`, 777),
PwdReset: 0,
AttrIndex: 99,
CreatedTime: time.Now(),
UpdatedTime: time.Now(),
data := g.Map{
// "id": 1,
"account_name": fmt.Sprintf(`name_%d`, 777),
"pwd_reset": 0,
"attr_index": 777,
"created_time": gtime.Now(),
}
_, err := db.Model(table).Data(data).InsertIgnore()
t.AssertNil(err)
t.AssertNE(err, nil)

one, err := db.Model(table).Where("id", 666).One()
count, err := db.Model(table).Count()
t.AssertNil(err)
t.Assert(one["ACCOUNT_NAME"].String(), "name_666")
t.Assert(count, TableSize)
})
}
4 changes: 2 additions & 2 deletions contrib/drivers/mssql/mssql_do_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ func (r *InsertResult) RowsAffected() (int64, error) {
}

// GetInsertOutputSql gen get last_insert_id code
func (m *Driver) GetInsertOutputSql(ctx context.Context, table string) string {
fds, errFd := m.GetDB().TableFields(ctx, table)
func (d *Driver) GetInsertOutputSql(ctx context.Context, table string) string {
fds, errFd := d.GetDB().TableFields(ctx, table)
if errFd != nil {
return ""
}
Expand Down
Loading
Loading