mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
fix(contrib/drivers/pgsql): Merge duplicated fields, especially for key constraints. (#4465)
pgsql 执行TableFields 或者字段信息时需要合并key信息
This commit is contained in:
@ -57,14 +57,21 @@ func (d *Driver) TableFields(ctx context.Context, table string, schema ...string
|
||||
}
|
||||
fields = make(map[string]*gdb.TableField)
|
||||
var (
|
||||
index = 0
|
||||
name string
|
||||
ok bool
|
||||
index = 0
|
||||
name string
|
||||
ok bool
|
||||
existingField *gdb.TableField
|
||||
)
|
||||
for _, m := range result {
|
||||
name = m["field"].String()
|
||||
// Filter duplicated fields.
|
||||
if _, ok = fields[name]; ok {
|
||||
// Merge duplicated fields, especially for key constraints.
|
||||
// Priority: pri > uni > others
|
||||
if existingField, ok = fields[name]; ok {
|
||||
currentKey := m["key"].String()
|
||||
// Merge key information with priority: pri > uni
|
||||
if currentKey == "pri" || (currentKey == "uni" && existingField.Key != "pri") {
|
||||
existingField.Key = currentKey
|
||||
}
|
||||
continue
|
||||
}
|
||||
fields[name] = &gdb.TableField{
|
||||
|
||||
@ -302,8 +302,8 @@ func Test_DB_TableFields(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
|
||||
var expect = map[string][]any{
|
||||
//[]string: Index Type Null Key Default Comment
|
||||
//id is bigserial so the default is a pgsql function
|
||||
// []string: Index Type Null Key Default Comment
|
||||
// id is bigserial so the default is a pgsql function
|
||||
"id": {0, "int8", false, "pri", fmt.Sprintf("nextval('%s_id_seq'::regclass)", table), ""},
|
||||
"passport": {1, "varchar", false, "", nil, ""},
|
||||
"password": {2, "varchar", false, "", nil, ""},
|
||||
@ -384,6 +384,91 @@ int_col INT);`
|
||||
|
||||
}
|
||||
|
||||
func Test_DB_TableFields_DuplicateConstraints(t *testing.T) {
|
||||
// Test for the fix of duplicate field results with multiple constraints
|
||||
// This test verifies that when a field has multiple constraints (e.g., both primary key and unique),
|
||||
// the TableFields method correctly merges the results with proper priority (pri > uni > others)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
tableName := "test_multi_constraint"
|
||||
createSql := fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id bigserial NOT NULL PRIMARY KEY,
|
||||
email varchar(100) NOT NULL UNIQUE,
|
||||
username varchar(50) NOT NULL,
|
||||
status int NOT NULL DEFAULT 1
|
||||
)`, tableName)
|
||||
|
||||
_, err := db.Exec(ctx, createSql)
|
||||
t.AssertNil(err)
|
||||
defer dropTable(tableName)
|
||||
|
||||
// Get table fields
|
||||
fields, err := db.TableFields(ctx, tableName)
|
||||
t.AssertNil(err)
|
||||
|
||||
// Verify id field has primary key constraint
|
||||
t.AssertNE(fields["id"], nil)
|
||||
t.Assert(fields["id"].Key, "pri")
|
||||
t.Assert(fields["id"].Name, "id")
|
||||
t.Assert(fields["id"].Type, "int8")
|
||||
|
||||
// Verify email field has unique constraint
|
||||
t.AssertNE(fields["email"], nil)
|
||||
t.Assert(fields["email"].Key, "uni")
|
||||
t.Assert(fields["email"].Name, "email")
|
||||
t.Assert(fields["email"].Type, "varchar")
|
||||
|
||||
// Verify username field has no constraint
|
||||
t.AssertNE(fields["username"], nil)
|
||||
t.Assert(fields["username"].Key, "")
|
||||
t.Assert(fields["username"].Name, "username")
|
||||
|
||||
// Verify status field has no constraint and has default value
|
||||
t.AssertNE(fields["status"], nil)
|
||||
t.Assert(fields["status"].Key, "")
|
||||
t.Assert(fields["status"].Name, "status")
|
||||
t.Assert(fields["status"].Default, 1)
|
||||
|
||||
// Verify field count is correct (no duplicates)
|
||||
t.Assert(len(fields), 4)
|
||||
})
|
||||
|
||||
// Test table with composite constraints
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
tableName := "test_composite_constraint"
|
||||
createSql := fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
user_id bigint NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
role varchar(50) NOT NULL,
|
||||
PRIMARY KEY (user_id, project_id)
|
||||
)`, tableName)
|
||||
|
||||
_, err := db.Exec(ctx, createSql)
|
||||
t.AssertNil(err)
|
||||
defer dropTable(tableName)
|
||||
|
||||
// Get table fields
|
||||
fields, err := db.TableFields(ctx, tableName)
|
||||
t.AssertNil(err)
|
||||
|
||||
// In PostgreSQL, composite primary keys may appear in query results
|
||||
// The first field in the composite key should be marked as 'pri'
|
||||
t.AssertNE(fields["user_id"], nil)
|
||||
t.Assert(fields["user_id"].Name, "user_id")
|
||||
|
||||
t.AssertNE(fields["project_id"], nil)
|
||||
t.Assert(fields["project_id"].Name, "project_id")
|
||||
|
||||
t.AssertNE(fields["role"], nil)
|
||||
t.Assert(fields["role"].Name, "role")
|
||||
t.Assert(fields["role"].Key, "")
|
||||
|
||||
// Verify field count is correct (no duplicates)
|
||||
t.Assert(len(fields), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_InsertIgnore(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
@ -37,8 +37,8 @@ func init() {
|
||||
Link: `pgsql:postgres:12345678@tcp(127.0.0.1:5432)`,
|
||||
}
|
||||
|
||||
//pgsql only permit to connect to the designation database.
|
||||
//so you need to create the pgsql database before you use orm
|
||||
// pgsql only permit to connect to the designation database.
|
||||
// so you need to create the pgsql database before you use orm
|
||||
gdb.AddConfigNode(gdb.DefaultGroupName, configNode)
|
||||
if r, err := gdb.New(configNode); err != nil {
|
||||
gtest.Fatal(err)
|
||||
|
||||
Reference in New Issue
Block a user