mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-21 16:34:21 +08:00
🐛 fix(dameng): 修复表设计字段注释加载失败
增加 SYSCOLUMNCOMMENTS 原生字典降级查询,按字段名合并注释;原生字典无权限时保留基础元数据,并补充回归测试。
This commit is contained in:
@@ -28,6 +28,7 @@ var registerDamengColumnsMetadataDriverOnce sync.Once
|
||||
var damengColumnsMetadataQueryState struct {
|
||||
sync.Mutex
|
||||
failAutoIncrementQuery bool
|
||||
failColumnCommentQuery bool
|
||||
queries []string
|
||||
}
|
||||
|
||||
@@ -49,6 +50,7 @@ func (damengColumnsMetadataConn) QueryContext(_ context.Context, query string, _
|
||||
damengColumnsMetadataQueryState.Lock()
|
||||
damengColumnsMetadataQueryState.queries = append(damengColumnsMetadataQueryState.queries, query)
|
||||
failAutoIncrementQuery := damengColumnsMetadataQueryState.failAutoIncrementQuery
|
||||
failColumnCommentQuery := damengColumnsMetadataQueryState.failColumnCommentQuery
|
||||
damengColumnsMetadataQueryState.Unlock()
|
||||
|
||||
if strings.Contains(query, "DBMS_METADATA.GET_DDL") {
|
||||
@@ -67,6 +69,19 @@ func (damengColumnsMetadataConn) QueryContext(_ context.Context, query string, _
|
||||
}, nil
|
||||
}
|
||||
|
||||
if strings.Contains(query, "SYS.SYSCOLUMNCOMMENTS") {
|
||||
if failColumnCommentQuery {
|
||||
return nil, errors.New("insufficient privilege for SYS.SYSCOLUMNCOMMENTS")
|
||||
}
|
||||
return &damengColumnsMetadataRows{
|
||||
columns: []string{"COLUMN_NAME", "COL_COMMENT"},
|
||||
values: [][]driver.Value{
|
||||
{"ID", "订单主键"},
|
||||
{"NAME", "客户名称"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
if strings.Contains(query, "SYS.SYSCOLUMNS") {
|
||||
if failAutoIncrementQuery {
|
||||
return nil, errors.New("insufficient privilege for SYS.SYSCOLUMNS")
|
||||
@@ -132,16 +147,18 @@ func openDamengColumnsMetadataDB(t *testing.T) *sql.DB {
|
||||
return conn
|
||||
}
|
||||
|
||||
func resetDamengColumnsMetadataQueryState(t *testing.T, failAutoIncrementQuery bool) {
|
||||
func resetDamengColumnsMetadataQueryState(t *testing.T, failAutoIncrementQuery, failColumnCommentQuery bool) {
|
||||
t.Helper()
|
||||
|
||||
damengColumnsMetadataQueryState.Lock()
|
||||
damengColumnsMetadataQueryState.failAutoIncrementQuery = failAutoIncrementQuery
|
||||
damengColumnsMetadataQueryState.failColumnCommentQuery = failColumnCommentQuery
|
||||
damengColumnsMetadataQueryState.queries = nil
|
||||
damengColumnsMetadataQueryState.Unlock()
|
||||
t.Cleanup(func() {
|
||||
damengColumnsMetadataQueryState.Lock()
|
||||
damengColumnsMetadataQueryState.failAutoIncrementQuery = false
|
||||
damengColumnsMetadataQueryState.failColumnCommentQuery = false
|
||||
damengColumnsMetadataQueryState.queries = nil
|
||||
damengColumnsMetadataQueryState.Unlock()
|
||||
})
|
||||
@@ -154,7 +171,7 @@ func damengColumnsMetadataQueries() []string {
|
||||
}
|
||||
|
||||
func TestDamengGetColumnsMarksAutoIncrementColumns(t *testing.T) {
|
||||
resetDamengColumnsMetadataQueryState(t, false)
|
||||
resetDamengColumnsMetadataQueryState(t, false, false)
|
||||
|
||||
damengDB := &DamengDB{conn: openDamengColumnsMetadataDB(t)}
|
||||
columns, err := damengDB.GetColumns("biz", "orders")
|
||||
@@ -170,15 +187,18 @@ func TestDamengGetColumnsMarksAutoIncrementColumns(t *testing.T) {
|
||||
if columns[1].Extra != "" {
|
||||
t.Fatalf("non-identity column should not be marked: %+v", columns[1])
|
||||
}
|
||||
if columns[0].Comment != "订单主键" || columns[1].Comment != "客户名称" {
|
||||
t.Fatalf("native column comments should fill empty compatibility-view values: %+v", columns)
|
||||
}
|
||||
|
||||
queries := damengColumnsMetadataQueries()
|
||||
if len(queries) != 2 || !strings.Contains(queries[1], "SYS.SYSCOLUMNS") {
|
||||
t.Fatalf("expected base and system metadata queries, got=%v", queries)
|
||||
if len(queries) != 3 || !strings.Contains(queries[1], "SYS.SYSCOLUMNCOMMENTS") || !strings.Contains(queries[2], "SYS.SYSCOLUMNS") {
|
||||
t.Fatalf("expected base, native comment, and system column metadata queries, got=%v", queries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDamengGetColumnsKeepsBaseMetadataWhenAutoIncrementQueryFails(t *testing.T) {
|
||||
resetDamengColumnsMetadataQueryState(t, true)
|
||||
resetDamengColumnsMetadataQueryState(t, true, false)
|
||||
|
||||
damengDB := &DamengDB{conn: openDamengColumnsMetadataDB(t)}
|
||||
columns, err := damengDB.GetColumns("biz", "orders")
|
||||
@@ -190,8 +210,21 @@ func TestDamengGetColumnsKeepsBaseMetadataWhenAutoIncrementQueryFails(t *testing
|
||||
}
|
||||
}
|
||||
|
||||
func TestDamengGetColumnsKeepsBaseMetadataWhenNativeCommentQueryFails(t *testing.T) {
|
||||
resetDamengColumnsMetadataQueryState(t, false, true)
|
||||
|
||||
damengDB := &DamengDB{conn: openDamengColumnsMetadataDB(t)}
|
||||
columns, err := damengDB.GetColumns("biz", "orders")
|
||||
if err != nil {
|
||||
t.Fatalf("GetColumns should keep base metadata when native comments are unavailable: %v", err)
|
||||
}
|
||||
if len(columns) != 2 || columns[0].Name != "ID" || columns[0].Extra != "auto_increment" {
|
||||
t.Fatalf("unexpected fallback columns: %+v", columns)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDamengGetCreateStatementAppendsTableComment(t *testing.T) {
|
||||
resetDamengColumnsMetadataQueryState(t, false)
|
||||
resetDamengColumnsMetadataQueryState(t, false, false)
|
||||
|
||||
damengDB := &DamengDB{conn: openDamengColumnsMetadataDB(t)}
|
||||
ddl, err := damengDB.GetCreateStatement("biz", "orders")
|
||||
@@ -218,7 +251,7 @@ func TestDamengGetCreateStatementAppendsTableComment(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDamengGetIndexesUsesIndexOwnerJoinAndMapsColumnOrder(t *testing.T) {
|
||||
resetDamengColumnsMetadataQueryState(t, false)
|
||||
resetDamengColumnsMetadataQueryState(t, false, false)
|
||||
|
||||
damengDB := &DamengDB{conn: openDamengColumnsMetadataDB(t)}
|
||||
indexes, err := damengDB.GetIndexes("biz", "orders")
|
||||
|
||||
@@ -331,6 +331,14 @@ func (d *DamengDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefi
|
||||
if len(columns) == 0 {
|
||||
return columns, nil
|
||||
}
|
||||
if !hasDamengColumnComments(columns) {
|
||||
commentData, _, commentErr := d.Query(buildDamengColumnCommentsQuery(dbName, tableName))
|
||||
if commentErr != nil {
|
||||
logger.Warnf("达梦 GetColumns 原生字段注释查询失败,已返回基础字段定义:%v", commentErr)
|
||||
} else {
|
||||
columns = applyDamengColumnComments(columns, commentData)
|
||||
}
|
||||
}
|
||||
|
||||
autoIncrementData, _, autoIncrementErr := d.Query(buildDamengAutoIncrementColumnsQuery(dbName, tableName))
|
||||
if autoIncrementErr != nil {
|
||||
|
||||
@@ -148,6 +148,25 @@ func buildDamengColumnsQuery(dbName, tableName string) string {
|
||||
ORDER BY c.column_id`, upperDBName, upperTableName, upperDBName, upperTableName, upperDBName, upperTableName)
|
||||
}
|
||||
|
||||
// buildDamengColumnCommentsQuery uses Dameng's native comment dictionary as a
|
||||
// fallback. Some DM8 deployments expose column metadata through the Oracle-
|
||||
// compatible ALL_COL_COMMENTS/USER_COL_COMMENTS views but return empty comment
|
||||
// values when those views are joined with the column dictionary.
|
||||
func buildDamengColumnCommentsQuery(dbName, tableName string) string {
|
||||
upperTableName := strings.ReplaceAll(strings.ToUpper(strings.TrimSpace(tableName)), "'", "''")
|
||||
upperDBName := strings.ReplaceAll(strings.ToUpper(strings.TrimSpace(dbName)), "'", "''")
|
||||
|
||||
schemaPredicate := "SCHNAME = USER"
|
||||
if upperDBName != "" {
|
||||
schemaPredicate = fmt.Sprintf("SCHNAME = '%s'", upperDBName)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`SELECT COLNAME AS column_name, COMMENT$ AS col_comment
|
||||
FROM SYS.SYSCOLUMNCOMMENTS
|
||||
WHERE %s AND TVNAME = '%s' AND COMMENT$ IS NOT NULL
|
||||
ORDER BY COLNAME`, schemaPredicate, upperTableName)
|
||||
}
|
||||
|
||||
func buildDamengTableCommentQuery(dbName, tableName string) string {
|
||||
upperTableName := strings.ReplaceAll(strings.ToUpper(strings.TrimSpace(tableName)), "'", "''")
|
||||
upperDBName := strings.ReplaceAll(strings.ToUpper(strings.TrimSpace(dbName)), "'", "''")
|
||||
@@ -393,3 +412,30 @@ func buildDamengColumnDefinitions(data []map[string]interface{}) []connection.Co
|
||||
|
||||
return columns
|
||||
}
|
||||
|
||||
func hasDamengColumnComments(columns []connection.ColumnDefinition) bool {
|
||||
for _, column := range columns {
|
||||
if strings.TrimSpace(column.Comment) != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func applyDamengColumnComments(columns []connection.ColumnDefinition, data []map[string]interface{}) []connection.ColumnDefinition {
|
||||
commentsByColumn := make(map[string]string, len(data))
|
||||
for _, row := range data {
|
||||
columnName := strings.ToUpper(strings.TrimSpace(getDamengRowString(row, "COLUMN_NAME", "COLNAME")))
|
||||
if columnName == "" {
|
||||
continue
|
||||
}
|
||||
commentsByColumn[columnName] = getDamengRowString(row, "COL_COMMENT", "COMMENT$", "COMMENT", "COMMENTS")
|
||||
}
|
||||
|
||||
for i := range columns {
|
||||
if comment, ok := commentsByColumn[strings.ToUpper(strings.TrimSpace(columns[i].Name))]; ok {
|
||||
columns[i].Comment = comment
|
||||
}
|
||||
}
|
||||
return columns
|
||||
}
|
||||
|
||||
@@ -146,6 +146,28 @@ func TestBuildDamengColumnsQuery_IncludesColumnCommentsJoin(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDamengColumnCommentsQueryUsesNativeDictionary(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
userQuery := buildDamengColumnCommentsQuery("", "orders")
|
||||
for _, want := range []string{
|
||||
"FROM SYS.SYSCOLUMNCOMMENTS",
|
||||
"SCHNAME = USER",
|
||||
"TVNAME = 'ORDERS'",
|
||||
"COLNAME AS column_name",
|
||||
"COMMENT$ AS col_comment",
|
||||
} {
|
||||
if !strings.Contains(userQuery, want) {
|
||||
t.Fatalf("current-schema native comment query should contain %q, got: %s", want, userQuery)
|
||||
}
|
||||
}
|
||||
|
||||
ownerQuery := buildDamengColumnCommentsQuery("biz", "orders")
|
||||
if !strings.Contains(ownerQuery, "SCHNAME = 'BIZ'") || !strings.Contains(ownerQuery, "TVNAME = 'ORDERS'") {
|
||||
t.Fatalf("schema native comment query should target the selected table, got: %s", ownerQuery)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDamengTableCommentQueryUsesSchemaAppropriateDictionaryView(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -182,11 +204,11 @@ func TestBuildDamengColumnDefinitions_MapsComment(t *testing.T) {
|
||||
|
||||
columns := buildDamengColumnDefinitions([]map[string]interface{}{
|
||||
{
|
||||
"COLUMN_NAME": "ID",
|
||||
"DATA_TYPE": "NUMBER",
|
||||
"NULLABLE": "N",
|
||||
"COLUMN_KEY": "PRI",
|
||||
"COL_COMMENT": "主键",
|
||||
"COLUMN_NAME": "ID",
|
||||
"DATA_TYPE": "NUMBER",
|
||||
"NULLABLE": "N",
|
||||
"COLUMN_KEY": "PRI",
|
||||
"COL_COMMENT": "主键",
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user