From 8723999afc8e3748fc330f423bcf5b2f1ec16e2d Mon Sep 17 00:00:00 2001 From: John Guo Date: Thu, 4 Dec 2025 20:33:08 +0800 Subject: [PATCH] up --- contrib/drivers/README.MD | 4 -- contrib/drivers/dm/dm_do_insert.go | 2 +- contrib/drivers/pgsql/pgsql_do_insert.go | 46 +++++++++++++++++-- contrib/drivers/pgsql/pgsql_z_unit_db_test.go | 23 +++++++++- .../drivers/pgsql/pgsql_z_unit_model_test.go | 43 ++++++++++++++++- 5 files changed, 105 insertions(+), 13 deletions(-) diff --git a/contrib/drivers/README.MD b/contrib/drivers/README.MD index c10d58d66..a94c07d82 100644 --- a/contrib/drivers/README.MD +++ b/contrib/drivers/README.MD @@ -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 diff --git a/contrib/drivers/dm/dm_do_insert.go b/contrib/drivers/dm/dm_do_insert.go index 2606167f2..3932fe8e4 100644 --- a/contrib/drivers/dm/dm_do_insert.go +++ b/contrib/drivers/dm/dm_do_insert.go @@ -165,7 +165,7 @@ func (d *Driver) getPrimaryKeys(ctx context.Context, table string) ([]string, er var primaryKeys []string for _, field := range tableFields { - if field.Key == "PRI" { + if gstr.Equal(field.Key, "PRI") { primaryKeys = append(primaryKeys, field.Name) } } diff --git a/contrib/drivers/pgsql/pgsql_do_insert.go b/contrib/drivers/pgsql/pgsql_do_insert.go index ec24edde3..e5ca80f2a 100644 --- a/contrib/drivers/pgsql/pgsql_do_insert.go +++ b/contrib/drivers/pgsql/pgsql_do_insert.go @@ -13,16 +13,34 @@ import ( "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/text/gstr" ) // DoInsert inserts or updates data for given table. func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption) (result sql.Result, err error) { switch option.InsertOption { case gdb.InsertOptionReplace: - return nil, gerror.NewCode( - gcode.CodeNotSupported, - `Replace operation is not supported by pgsql driver`, - ) + // PostgreSQL does not support REPLACE INTO syntax, use Save (ON CONFLICT ... DO UPDATE) instead. + // Automatically detect primary keys if OnConflict is not specified. + if len(option.OnConflict) == 0 { + primaryKeys, err := d.getPrimaryKeys(ctx, table) + if err != nil { + return nil, gerror.WrapCode( + gcode.CodeInternalError, + err, + `failed to get primary keys for Replace operation`, + ) + } + if len(primaryKeys) == 0 { + return nil, gerror.NewCode( + gcode.CodeMissingParameter, + `Replace operation requires primary key, but table has no primary key defined`, + ) + } + option.OnConflict = primaryKeys + } + // Treat Replace as Save operation + option.InsertOption = gdb.InsertOptionSave case gdb.InsertOptionDefault: tableFields, err := d.GetCore().GetDB().TableFields(ctx, table) @@ -35,6 +53,26 @@ func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list } } } + + default: } return d.Core.DoInsert(ctx, link, table, list, option) } + +// getPrimaryKeys retrieves the primary key field list of the table. +// 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 gstr.Equal(field.Key, "PRI") { + primaryKeys = append(primaryKeys, field.Name) + } + } + + return primaryKeys, nil +} diff --git a/contrib/drivers/pgsql/pgsql_z_unit_db_test.go b/contrib/drivers/pgsql/pgsql_z_unit_db_test.go index 67b9d7978..12366f629 100644 --- a/contrib/drivers/pgsql/pgsql_z_unit_db_test.go +++ b/contrib/drivers/pgsql/pgsql_z_unit_db_test.go @@ -99,6 +99,7 @@ func Test_DB_Replace(t *testing.T) { createTable("t_user") defer dropTable("t_user") + // Insert initial record i := 10 data := g.Map{ "id": i, @@ -107,8 +108,26 @@ func Test_DB_Replace(t *testing.T) { "nickname": fmt.Sprintf(`T%d`, i), "create_time": gtime.Now().String(), } - _, err := db.Replace(ctx, "t_user", data, 10) - gtest.AssertNE(err, nil) + _, err := db.Insert(ctx, "t_user", data) + gtest.AssertNil(err) + + // Replace with new data + data2 := g.Map{ + "id": i, + "passport": fmt.Sprintf(`t%d_new`, i), + "password": fmt.Sprintf(`p%d_new`, i), + "nickname": fmt.Sprintf(`T%d_new`, i), + "create_time": gtime.Now().String(), + } + _, err = db.Replace(ctx, "t_user", data2) + gtest.AssertNil(err) + + // Verify the data was replaced + one, err := db.GetOne(ctx, fmt.Sprintf("SELECT * FROM t_user WHERE id=?"), i) + gtest.AssertNil(err) + gtest.Assert(one["passport"].String(), fmt.Sprintf(`t%d_new`, i)) + gtest.Assert(one["password"].String(), fmt.Sprintf(`p%d_new`, i)) + gtest.Assert(one["nickname"].String(), fmt.Sprintf(`T%d_new`, i)) }) } diff --git a/contrib/drivers/pgsql/pgsql_z_unit_model_test.go b/contrib/drivers/pgsql/pgsql_z_unit_model_test.go index 56db22a85..2134a91bf 100644 --- a/contrib/drivers/pgsql/pgsql_z_unit_model_test.go +++ b/contrib/drivers/pgsql/pgsql_z_unit_model_test.go @@ -334,14 +334,53 @@ func Test_Model_Replace(t *testing.T) { defer dropTable(table) gtest.C(t, func(t *gtest.T) { - _, err := db.Model(table).Data(g.Map{ + // Insert initial record + result, err := db.Model(table).Data(g.Map{ + "id": 1, + "passport": "t1", + "password": "pass1", + "nickname": "T1", + "create_time": "2018-10-24 10:00:00", + }).Insert() + t.AssertNil(err) + n, _ := result.RowsAffected() + t.Assert(n, 1) + + // Replace with new data + result, err = db.Model(table).Data(g.Map{ "id": 1, "passport": "t11", "password": "25d55ad283aa400af464c76d713c07ad", "nickname": "T11", "create_time": "2018-10-24 10:00:00", }).Replace() - t.Assert(err, "Replace operation is not supported by pgsql driver") + t.AssertNil(err) + n, _ = result.RowsAffected() + t.Assert(n, 1) + + // Verify the data was replaced + one, err := db.Model(table).Where("id", 1).One() + t.AssertNil(err) + t.Assert(one["passport"].String(), "t11") + t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad") + t.Assert(one["nickname"].String(), "T11") + + // Replace with new ID (insert new record) + result, err = db.Model(table).Data(g.Map{ + "id": 2, + "passport": "t22", + "password": "pass22", + "nickname": "T22", + "create_time": "2018-10-24 11:00:00", + }).Replace() + t.AssertNil(err) + n, _ = result.RowsAffected() + t.Assert(n, 1) + + // Verify new record was inserted + count, err := db.Model(table).Count() + t.AssertNil(err) + t.Assert(count, 2) }) }