From dca5f629b2d8bfe66a85d2d5198e77791575288a Mon Sep 17 00:00:00 2001 From: Syngnat Date: Fri, 17 Apr 2026 18:42:47 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(dameng):=20=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E8=A1=A8=E6=A0=BC=E6=9B=B4=E6=96=B0=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E4=B8=BB=E9=94=AE=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 达梦列元数据查询补充主键关联并返回 column_key - GetColumns 正确映射主键标记,避免表格更新退化为整行 WHERE - 补充达梦列元数据回归测试,并验证带驱动 tag 的实现编译通过 Fixes #389 --- internal/db/dameng_columns_test.go | 63 ++++++++++++++++++++++++++++++ internal/db/dameng_impl.go | 30 +------------- internal/db/dameng_metadata.go | 55 ++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 28 deletions(-) create mode 100644 internal/db/dameng_columns_test.go diff --git a/internal/db/dameng_columns_test.go b/internal/db/dameng_columns_test.go new file mode 100644 index 0000000..e1ad3c6 --- /dev/null +++ b/internal/db/dameng_columns_test.go @@ -0,0 +1,63 @@ +package db + +import ( + "strings" + "testing" +) + +func TestBuildDamengColumnsQuery_IncludesPrimaryKeyMetadata(t *testing.T) { + t.Parallel() + + ownerQuery := buildDamengColumnsQuery("biz", "orders") + if !strings.Contains(ownerQuery, "constraint_type = 'P'") { + t.Fatalf("owner query 应包含主键约束过滤, got=%s", ownerQuery) + } + if !strings.Contains(ownerQuery, "AS column_key") { + t.Fatalf("owner query 应返回 column_key, got=%s", ownerQuery) + } + if !strings.Contains(ownerQuery, "WHERE c.owner = 'BIZ' AND c.table_name = 'ORDERS'") { + t.Fatalf("owner query 应按 owner/table 过滤, got=%s", ownerQuery) + } + + userQuery := buildDamengColumnsQuery("", "orders") + if !strings.Contains(userQuery, "FROM user_tab_columns c") { + t.Fatalf("user query 应使用 user_tab_columns, got=%s", userQuery) + } + if !strings.Contains(userQuery, "JOIN user_cons_columns cols") { + t.Fatalf("user query 应关联 user_cons_columns, got=%s", userQuery) + } +} + +func TestBuildDamengColumnDefinitions_MarksPrimaryKeyColumns(t *testing.T) { + t.Parallel() + + columns := buildDamengColumnDefinitions([]map[string]interface{}{ + { + "COLUMN_NAME": "ID", + "DATA_TYPE": "INTEGER", + "NULLABLE": "N", + "DATA_DEFAULT": nil, + "COLUMN_KEY": "PRI", + }, + { + "COLUMN_NAME": "NAME", + "DATA_TYPE": "VARCHAR2", + "NULLABLE": "Y", + "DATA_DEFAULT": "guest", + "COLUMN_KEY": "", + }, + }) + + if len(columns) != 2 { + t.Fatalf("unexpected column count: %d", len(columns)) + } + if columns[0].Name != "ID" || columns[0].Key != "PRI" { + t.Fatalf("主键列未正确标记: %+v", columns[0]) + } + if columns[1].Name != "NAME" || columns[1].Key != "" { + t.Fatalf("非主键列标记异常: %+v", columns[1]) + } + if columns[1].Default == nil || *columns[1].Default != "guest" { + t.Fatalf("默认值未保留: %+v", columns[1]) + } +} diff --git a/internal/db/dameng_impl.go b/internal/db/dameng_impl.go index a88572f..0159b9a 100644 --- a/internal/db/dameng_impl.go +++ b/internal/db/dameng_impl.go @@ -264,38 +264,12 @@ func (d *DamengDB) GetCreateStatement(dbName, tableName string) (string, error) } func (d *DamengDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) { - query := fmt.Sprintf(`SELECT column_name, data_type, nullable, data_default - FROM all_tab_columns - WHERE owner = '%s' AND table_name = '%s'`, - strings.ToUpper(dbName), strings.ToUpper(tableName)) - - if dbName == "" { - query = fmt.Sprintf(`SELECT column_name, data_type, nullable, data_default - FROM user_tab_columns - WHERE table_name = '%s'`, strings.ToUpper(tableName)) - } - - data, _, err := d.Query(query) + data, _, err := d.Query(buildDamengColumnsQuery(dbName, tableName)) if err != nil { return nil, err } - var columns []connection.ColumnDefinition - for _, row := range data { - col := connection.ColumnDefinition{ - Name: fmt.Sprintf("%v", row["COLUMN_NAME"]), - Type: fmt.Sprintf("%v", row["DATA_TYPE"]), - Nullable: fmt.Sprintf("%v", row["NULLABLE"]), - } - - if row["DATA_DEFAULT"] != nil { - def := fmt.Sprintf("%v", row["DATA_DEFAULT"]) - col.Default = &def - } - - columns = append(columns, col) - } - return columns, nil + return buildDamengColumnDefinitions(data), nil } func (d *DamengDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error) { diff --git a/internal/db/dameng_metadata.go b/internal/db/dameng_metadata.go index 15d12ad..2492a85 100644 --- a/internal/db/dameng_metadata.go +++ b/internal/db/dameng_metadata.go @@ -5,6 +5,7 @@ import ( "sort" "strings" + "GoNavi-Wails/internal/connection" "GoNavi-Wails/internal/logger" ) @@ -103,3 +104,57 @@ func getDamengRowString(row map[string]interface{}, keys ...string) string { } return "" } + +func buildDamengColumnsQuery(dbName, tableName string) string { + upperTableName := strings.ToUpper(strings.TrimSpace(tableName)) + upperDBName := strings.ToUpper(strings.TrimSpace(dbName)) + + if upperDBName == "" { + return fmt.Sprintf(`SELECT c.column_name, c.data_type, c.nullable, c.data_default, + CASE WHEN pk.column_name IS NOT NULL THEN 'PRI' ELSE '' END AS column_key + FROM user_tab_columns c + LEFT JOIN ( + SELECT cols.table_name, cols.column_name + FROM user_constraints cons + JOIN user_cons_columns cols USING (constraint_name) + WHERE cons.constraint_type = 'P' + ) pk ON c.table_name = pk.table_name AND c.column_name = pk.column_name + WHERE c.table_name = '%s' + ORDER BY c.column_id`, upperTableName) + } + + return fmt.Sprintf(`SELECT c.column_name, c.data_type, c.nullable, c.data_default, + CASE WHEN pk.column_name IS NOT NULL THEN 'PRI' ELSE '' END AS column_key + FROM all_tab_columns c + LEFT JOIN ( + SELECT cols.owner, cols.table_name, cols.column_name + FROM all_constraints cons + JOIN all_cons_columns cols + ON cons.owner = cols.owner AND cons.constraint_name = cols.constraint_name + WHERE cons.constraint_type = 'P' + ) pk ON c.owner = pk.owner AND c.table_name = pk.table_name AND c.column_name = pk.column_name + WHERE c.owner = '%s' AND c.table_name = '%s' + ORDER BY c.column_id`, upperDBName, upperTableName) +} + +func buildDamengColumnDefinitions(data []map[string]interface{}) []connection.ColumnDefinition { + columns := make([]connection.ColumnDefinition, 0, len(data)) + for _, row := range data { + col := connection.ColumnDefinition{ + Name: getDamengRowString(row, "COLUMN_NAME"), + Type: getDamengRowString(row, "DATA_TYPE"), + Nullable: getDamengRowString(row, "NULLABLE"), + Key: getDamengRowString(row, "COLUMN_KEY"), + } + + defaultValue := getDamengRowString(row, "DATA_DEFAULT") + if defaultValue != "" { + def := defaultValue + col.Default = &def + } + + columns = append(columns, col) + } + + return columns +}