fix(contrib/drivers/pgsql): Merge duplicated fields, especially for key constraints. (#4465)

pgsql 执行TableFields 或者字段信息时需要合并key信息
This commit is contained in:
wanna
2025-10-13 18:16:09 +08:00
committed by GitHub
parent 98f0c36a1d
commit 416f314390
3 changed files with 101 additions and 9 deletions

View File

@ -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{

View File

@ -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)

View File

@ -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)