mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-08 22:12:29 +08:00
🐛 fix(db): 修复达梦DDL缺失字段注释 Fixes #615
- 补齐达梦列元数据查询中的字段注释读取 - 保留原始 CREATE TABLE 结果并追加 COMMENT ON COLUMN 语句 - 修正达梦列定义映射时 COMMENT 丢失的问题 - 新增达梦 DDL 注释补全回归测试
This commit is contained in:
@@ -1766,6 +1766,9 @@ func resolveCreateStatementWithFallbackWithText(dbInst db.Database, config conne
|
||||
sqlStr, sourceErr := dbInst.GetCreateStatement(metadataSchemaName, metadataTableName)
|
||||
if sourceErr == nil && !shouldFallbackCreateStatement(dbType, sqlStr) {
|
||||
if strings.TrimSpace(sqlStr) != "" {
|
||||
if columns, err := loadCreateStatementCommentColumns(dbInst, dbType, metadataSchemaName, metadataTableName); err == nil {
|
||||
sqlStr = appendCreateStatementColumnComments(dbType, ddlSchemaName, ddlTableName, sqlStr, columns)
|
||||
}
|
||||
return sqlStr, nil
|
||||
}
|
||||
if isOceanBaseOracleProtocol(config) {
|
||||
@@ -1916,6 +1919,51 @@ func buildFallbackCreateStatement(dbType string, schemaName string, tableName st
|
||||
return buildFallbackCreateStatementWithText(dbType, schemaName, tableName, columns, nil, defaultDBBackendText)
|
||||
}
|
||||
|
||||
func loadCreateStatementCommentColumns(dbInst db.Database, dbType string, schemaName string, tableName string) ([]connection.ColumnDefinition, error) {
|
||||
if !shouldAppendColumnCommentsToCreateStatement(dbType) {
|
||||
return nil, nil
|
||||
}
|
||||
return dbInst.GetColumns(schemaName, tableName)
|
||||
}
|
||||
|
||||
func shouldAppendColumnCommentsToCreateStatement(dbType string) bool {
|
||||
switch dbType {
|
||||
case "dameng":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func appendCreateStatementColumnComments(dbType string, schemaName string, tableName string, ddl string, columns []connection.ColumnDefinition) string {
|
||||
if !shouldAppendColumnCommentsToCreateStatement(dbType) || strings.TrimSpace(ddl) == "" || len(columns) == 0 {
|
||||
return ddl
|
||||
}
|
||||
|
||||
qualifiedTable := quoteTableIdentByType(dbType, schemaName, tableName)
|
||||
existingDDLUpper := strings.ToUpper(ddl)
|
||||
commentStatements := make([]string, 0, len(columns))
|
||||
for _, col := range columns {
|
||||
commentSQL := buildFallbackColumnCommentStatement(dbType, schemaName, tableName, qualifiedTable, col.Name, col.Comment)
|
||||
if commentSQL == "" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(existingDDLUpper, strings.ToUpper(commentSQL)) {
|
||||
continue
|
||||
}
|
||||
commentStatements = append(commentStatements, commentSQL)
|
||||
}
|
||||
if len(commentStatements) == 0 {
|
||||
return ddl
|
||||
}
|
||||
|
||||
trimmedDDL := strings.TrimRight(ddl, " \t\r\n")
|
||||
if !strings.HasSuffix(trimmedDDL, ";") {
|
||||
trimmedDDL += ";"
|
||||
}
|
||||
return trimmedDDL + "\n" + strings.Join(commentStatements, "\n")
|
||||
}
|
||||
|
||||
func buildFallbackCreateStatementWithText(dbType string, schemaName string, tableName string, columns []connection.ColumnDefinition, indexes []connection.IndexDefinition, text func(string, map[string]any) string) (string, error) {
|
||||
if text == nil {
|
||||
text = defaultDBBackendText
|
||||
|
||||
@@ -286,6 +286,39 @@ func TestResolveCreateStatementWithFallback_KingbaseIncludesColumnComments(t *te
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCreateStatementWithFallback_DamengAppendsColumnComments(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dbInst := &fakeCreateStatementDB{
|
||||
createSQL: `CREATE TABLE "APP"."ORDERS" ("ID" NUMBER NOT NULL, "STATUS" VARCHAR2(32));`,
|
||||
columns: []connection.ColumnDefinition{
|
||||
{Name: "ID", Type: "NUMBER", Nullable: "NO", Key: "PRI", Comment: "主键"},
|
||||
{Name: "STATUS", Type: "VARCHAR2(32)", Nullable: "YES", Comment: "状态"},
|
||||
},
|
||||
}
|
||||
|
||||
ddl, err := resolveCreateStatementWithFallback(dbInst, connection.ConnectionConfig{
|
||||
Type: "dameng",
|
||||
}, "APP", "ORDERS")
|
||||
if err != nil {
|
||||
t.Fatalf("resolveCreateStatementWithFallback() unexpected error: %v", err)
|
||||
}
|
||||
if !strings.Contains(ddl, dbInst.createSQL) {
|
||||
t.Fatalf("expected original dameng create SQL to be preserved, got: %s", ddl)
|
||||
}
|
||||
for _, want := range []string{
|
||||
`COMMENT ON COLUMN "APP"."ORDERS"."ID" IS '主键';`,
|
||||
`COMMENT ON COLUMN "APP"."ORDERS"."STATUS" IS '状态';`,
|
||||
} {
|
||||
if !strings.Contains(ddl, want) {
|
||||
t.Fatalf("expected dameng DDL to contain %q, got: %s", want, ddl)
|
||||
}
|
||||
}
|
||||
if dbInst.columnsCalls != 1 {
|
||||
t.Fatalf("expected dameng path to load column metadata once, got %d", dbInst.columnsCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCreateStatementWithFallback_KingbaseIncludesSecondaryIndexes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -110,9 +110,11 @@ func buildDamengColumnsQuery(dbName, tableName string) string {
|
||||
upperDBName := strings.ToUpper(strings.TrimSpace(dbName))
|
||||
|
||||
if upperDBName == "" {
|
||||
return fmt.Sprintf(`SELECT c.column_name, c.data_type, c.data_length, c.char_length, c.data_precision, c.data_scale, c.nullable, c.data_default,
|
||||
return fmt.Sprintf(`SELECT c.column_name, c.data_type, c.data_length, c.char_length, c.data_precision, c.data_scale, c.nullable, c.data_default, cc.comments AS comment,
|
||||
CASE WHEN pk.column_name IS NOT NULL THEN 'PRI' ELSE '' END AS column_key
|
||||
FROM user_tab_columns c
|
||||
LEFT JOIN user_col_comments cc
|
||||
ON cc.table_name = c.table_name AND cc.column_name = c.column_name
|
||||
LEFT JOIN (
|
||||
SELECT cols.table_name, cols.column_name
|
||||
FROM user_constraints cons
|
||||
@@ -125,9 +127,11 @@ func buildDamengColumnsQuery(dbName, tableName string) string {
|
||||
ORDER BY c.column_id`, upperTableName, upperTableName, upperTableName)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`SELECT c.column_name, c.data_type, c.data_length, c.char_length, c.data_precision, c.data_scale, c.nullable, c.data_default,
|
||||
return fmt.Sprintf(`SELECT c.column_name, c.data_type, c.data_length, c.char_length, c.data_precision, c.data_scale, c.nullable, c.data_default, cc.comments AS comment,
|
||||
CASE WHEN pk.column_name IS NOT NULL THEN 'PRI' ELSE '' END AS column_key
|
||||
FROM all_tab_columns c
|
||||
LEFT JOIN all_col_comments cc
|
||||
ON cc.owner = c.owner AND cc.table_name = c.table_name AND cc.column_name = c.column_name
|
||||
LEFT JOIN (
|
||||
SELECT cols.owner, cols.table_name, cols.column_name
|
||||
FROM all_constraints cons
|
||||
@@ -243,6 +247,7 @@ func buildDamengColumnDefinitions(data []map[string]interface{}) []connection.Co
|
||||
Type: formatDamengColumnType(row),
|
||||
Nullable: normalizeDamengNullable(getDamengRowString(row, "NULLABLE")),
|
||||
Key: getDamengRowString(row, "COLUMN_KEY"),
|
||||
Comment: getDamengRowString(row, "COMMENT", "COMMENTS"),
|
||||
}
|
||||
|
||||
defaultValue := getDamengRowString(row, "DATA_DEFAULT")
|
||||
|
||||
@@ -3,6 +3,7 @@ package db
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -118,3 +119,44 @@ func TestCollectDamengDatabaseNames_FallbackToCurrentUser(t *testing.T) {
|
||||
t.Fatalf("兜底查询应该返回当前用户, got=%v want=%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDamengColumnsQuery_IncludesColumnCommentsJoin(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
userQuery := buildDamengColumnsQuery("", "orders")
|
||||
if !strings.Contains(userQuery, "user_col_comments") {
|
||||
t.Fatalf("expected user query to join user_col_comments, got: %s", userQuery)
|
||||
}
|
||||
if !strings.Contains(userQuery, "cc.comments AS comment") {
|
||||
t.Fatalf("expected user query to select column comments, got: %s", userQuery)
|
||||
}
|
||||
|
||||
allQuery := buildDamengColumnsQuery("app", "orders")
|
||||
if !strings.Contains(allQuery, "all_col_comments") {
|
||||
t.Fatalf("expected schema query to join all_col_comments, got: %s", allQuery)
|
||||
}
|
||||
if !strings.Contains(allQuery, "cc.comments AS comment") {
|
||||
t.Fatalf("expected schema query to select column comments, got: %s", allQuery)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDamengColumnDefinitions_MapsComment(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
columns := buildDamengColumnDefinitions([]map[string]interface{}{
|
||||
{
|
||||
"COLUMN_NAME": "ID",
|
||||
"DATA_TYPE": "NUMBER",
|
||||
"NULLABLE": "N",
|
||||
"COLUMN_KEY": "PRI",
|
||||
"COMMENT": "主键",
|
||||
},
|
||||
})
|
||||
|
||||
if len(columns) != 1 {
|
||||
t.Fatalf("expected one column, got=%d", len(columns))
|
||||
}
|
||||
if columns[0].Comment != "主键" {
|
||||
t.Fatalf("expected comment to be mapped, got=%q", columns[0].Comment)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user