mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-10 16:53:35 +08:00
🐛 fix(dameng): 修复索引元数据查询与错误提示
修正 ALL_IND_COLUMNS 与 ALL_INDEXES 的索引归属关联;保留复合索引顺序、唯一性与索引类型元数据;索引加载失败时显示错误,并补齐回归测试和多语言文案。 Fixes #735
This commit is contained in:
@@ -61,6 +61,19 @@ func (damengColumnsMetadataConn) QueryContext(_ context.Context, query string, _
|
||||
}, nil
|
||||
}
|
||||
|
||||
if strings.Contains(query, "all_ind_columns") {
|
||||
if !strings.Contains(query, "i.owner = c.index_owner AND i.index_name = c.index_name") {
|
||||
return nil, errors.New("ALL_IND_COLUMNS must join ALL_INDEXES through INDEX_OWNER")
|
||||
}
|
||||
return &damengColumnsMetadataRows{
|
||||
columns: []string{"INDEX_NAME", "COLUMN_NAME", "UNIQUENESS", "COLUMN_POSITION", "INDEX_TYPE"},
|
||||
values: [][]driver.Value{
|
||||
{"IDX_ORDERS_TENANT_CREATED", "TENANT_ID", "NONUNIQUE", int64(1), "NORMAL"},
|
||||
{"IDX_ORDERS_TENANT_CREATED", "CREATED_AT", "NONUNIQUE", int64(2), "NORMAL"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &damengColumnsMetadataRows{
|
||||
columns: []string{
|
||||
"COLUMN_NAME", "DATA_TYPE", "DATA_LENGTH", "CHAR_LENGTH", "DATA_PRECISION",
|
||||
@@ -160,3 +173,27 @@ func TestDamengGetColumnsKeepsBaseMetadataWhenAutoIncrementQueryFails(t *testing
|
||||
t.Fatalf("unexpected fallback columns: %+v", columns)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDamengGetIndexesUsesIndexOwnerJoinAndMapsColumnOrder(t *testing.T) {
|
||||
resetDamengColumnsMetadataQueryState(t, false)
|
||||
|
||||
damengDB := &DamengDB{conn: openDamengColumnsMetadataDB(t)}
|
||||
indexes, err := damengDB.GetIndexes("biz", "orders")
|
||||
if err != nil {
|
||||
t.Fatalf("GetIndexes returned error: %v", err)
|
||||
}
|
||||
if len(indexes) != 2 {
|
||||
t.Fatalf("unexpected index column count: %d", len(indexes))
|
||||
}
|
||||
if indexes[0].Name != "IDX_ORDERS_TENANT_CREATED" || indexes[0].ColumnName != "TENANT_ID" || indexes[0].SeqInIndex != 1 || indexes[0].IndexType != "NORMAL" {
|
||||
t.Fatalf("unexpected first index column: %+v", indexes[0])
|
||||
}
|
||||
if indexes[1].ColumnName != "CREATED_AT" || indexes[1].SeqInIndex != 2 {
|
||||
t.Fatalf("unexpected second index column: %+v", indexes[1])
|
||||
}
|
||||
|
||||
queries := damengColumnsMetadataQueries()
|
||||
if len(queries) != 1 || !strings.Contains(queries[0], "c.table_owner = 'BIZ'") || !strings.Contains(queries[0], "c.table_name = 'ORDERS'") {
|
||||
t.Fatalf("expected one normalized schema index query, got=%v", queries)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,40 +332,11 @@ func (d *DamengDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefi
|
||||
}
|
||||
|
||||
func (d *DamengDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error) {
|
||||
query := fmt.Sprintf(`SELECT index_name, column_name, uniqueness
|
||||
FROM all_ind_columns
|
||||
JOIN all_indexes USING (index_name, owner)
|
||||
WHERE table_owner = '%s' AND table_name = '%s'`,
|
||||
strings.ToUpper(dbName), strings.ToUpper(tableName))
|
||||
|
||||
if dbName == "" {
|
||||
query = fmt.Sprintf(`SELECT index_name, column_name, uniqueness
|
||||
FROM user_ind_columns
|
||||
JOIN user_indexes USING (index_name)
|
||||
WHERE table_name = '%s'`, strings.ToUpper(tableName))
|
||||
}
|
||||
|
||||
data, _, err := d.Query(query)
|
||||
data, _, err := d.Query(buildDamengIndexesQuery(dbName, tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var indexes []connection.IndexDefinition
|
||||
for _, row := range data {
|
||||
unique := 1
|
||||
if val, ok := row["UNIQUENESS"]; ok && val == "UNIQUE" {
|
||||
unique = 0
|
||||
}
|
||||
|
||||
idx := connection.IndexDefinition{
|
||||
Name: fmt.Sprintf("%v", row["INDEX_NAME"]),
|
||||
ColumnName: fmt.Sprintf("%v", row["COLUMN_NAME"]),
|
||||
NonUnique: unique,
|
||||
IndexType: "BTREE",
|
||||
}
|
||||
indexes = append(indexes, idx)
|
||||
}
|
||||
return indexes, nil
|
||||
return buildDamengIndexDefinitions(data), nil
|
||||
}
|
||||
|
||||
func (d *DamengDB) GetForeignKeys(dbName, tableName string) ([]connection.ForeignKeyDefinition, error) {
|
||||
|
||||
@@ -188,6 +188,59 @@ func applyDamengAutoIncrementColumns(columns []connection.ColumnDefinition, data
|
||||
return columns
|
||||
}
|
||||
|
||||
func buildDamengIndexesQuery(dbName, tableName string) string {
|
||||
upperDBName := strings.ReplaceAll(strings.ToUpper(strings.TrimSpace(dbName)), "'", "''")
|
||||
upperTableName := strings.ReplaceAll(strings.ToUpper(strings.TrimSpace(tableName)), "'", "''")
|
||||
|
||||
if upperDBName == "" {
|
||||
return fmt.Sprintf(`SELECT c.index_name, c.column_name, i.uniqueness, c.column_position, i.index_type
|
||||
FROM user_ind_columns c
|
||||
JOIN user_indexes i ON i.index_name = c.index_name
|
||||
WHERE c.table_name = '%s'
|
||||
AND c.column_name IS NOT NULL
|
||||
ORDER BY c.index_name, c.column_position`, upperTableName)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`SELECT c.index_name, c.column_name, i.uniqueness, c.column_position, i.index_type
|
||||
FROM all_ind_columns c
|
||||
JOIN all_indexes i ON i.owner = c.index_owner AND i.index_name = c.index_name
|
||||
WHERE c.table_owner = '%s'
|
||||
AND c.table_name = '%s'
|
||||
AND c.column_name IS NOT NULL
|
||||
ORDER BY c.index_name, c.column_position`, upperDBName, upperTableName)
|
||||
}
|
||||
|
||||
func buildDamengIndexDefinitions(data []map[string]interface{}) []connection.IndexDefinition {
|
||||
indexes := make([]connection.IndexDefinition, 0, len(data))
|
||||
for _, row := range data {
|
||||
name := getDamengRowString(row, "INDEX_NAME")
|
||||
columnName := getDamengRowString(row, "COLUMN_NAME")
|
||||
if name == "" || columnName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
nonUnique := 1
|
||||
if strings.EqualFold(getDamengRowString(row, "UNIQUENESS"), "UNIQUE") {
|
||||
nonUnique = 0
|
||||
}
|
||||
|
||||
seqInIndex, _ := getDamengRowInt(row, "COLUMN_POSITION")
|
||||
indexType := getDamengRowString(row, "INDEX_TYPE")
|
||||
if indexType == "" {
|
||||
indexType = "BTREE"
|
||||
}
|
||||
|
||||
indexes = append(indexes, connection.IndexDefinition{
|
||||
Name: name,
|
||||
ColumnName: columnName,
|
||||
NonUnique: nonUnique,
|
||||
SeqInIndex: seqInIndex,
|
||||
IndexType: indexType,
|
||||
})
|
||||
}
|
||||
return indexes
|
||||
}
|
||||
|
||||
func buildDamengForeignKeysQuery(dbName, tableName string) string {
|
||||
upperDBName := strings.ToUpper(strings.TrimSpace(dbName))
|
||||
upperTableName := strings.ToUpper(strings.TrimSpace(tableName))
|
||||
|
||||
@@ -166,3 +166,80 @@ func TestBuildDamengColumnDefinitions_MapsComment(t *testing.T) {
|
||||
t.Fatalf("expected comment to be mapped, got=%q", columns[0].Comment)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDamengIndexesQuery_JoinsAllViewsByIndexOwner(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
query := buildDamengIndexesQuery("app", "orders")
|
||||
|
||||
if !strings.Contains(query, "JOIN all_indexes i ON i.owner = c.index_owner AND i.index_name = c.index_name") {
|
||||
t.Fatalf("expected schema query to join ALL_INDEXES through INDEX_OWNER, got: %s", query)
|
||||
}
|
||||
if !strings.Contains(query, "i.index_type") {
|
||||
t.Fatalf("expected index type metadata to be selected, got: %s", query)
|
||||
}
|
||||
if strings.Contains(strings.ToLower(query), "using (index_name, owner)") {
|
||||
t.Fatalf("ALL_IND_COLUMNS exposes INDEX_OWNER rather than OWNER, got: %s", query)
|
||||
}
|
||||
if !strings.Contains(query, "c.table_owner = 'APP'") || !strings.Contains(query, "c.table_name = 'ORDERS'") {
|
||||
t.Fatalf("expected normalized schema and table predicates, got: %s", query)
|
||||
}
|
||||
if !strings.Contains(query, "ORDER BY c.index_name, c.column_position") {
|
||||
t.Fatalf("expected stable multi-column index ordering, got: %s", query)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDamengIndexesQuery_UsesUserViewsWithoutSchema(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
query := buildDamengIndexesQuery("", "orders")
|
||||
|
||||
if !strings.Contains(query, "FROM user_ind_columns c") {
|
||||
t.Fatalf("expected current-schema index columns view, got: %s", query)
|
||||
}
|
||||
if !strings.Contains(query, "JOIN user_indexes i ON i.index_name = c.index_name") {
|
||||
t.Fatalf("expected current-schema index metadata join, got: %s", query)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDamengIndexDefinitions_MapsCaseInsensitiveMultiColumnMetadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
indexes := buildDamengIndexDefinitions([]map[string]interface{}{
|
||||
{
|
||||
"index_name": "IDX_ORDERS_TENANT_CREATED",
|
||||
"column_name": "TENANT_ID",
|
||||
"uniqueness": "NONUNIQUE",
|
||||
"column_position": 1,
|
||||
"index_type": "NORMAL",
|
||||
},
|
||||
{
|
||||
"INDEX_NAME": "IDX_ORDERS_TENANT_CREATED",
|
||||
"COLUMN_NAME": "CREATED_AT",
|
||||
"UNIQUENESS": "NONUNIQUE",
|
||||
"COLUMN_POSITION": "2",
|
||||
},
|
||||
{
|
||||
"INDEX_NAME": "UK_ORDERS_NUMBER",
|
||||
"COLUMN_NAME": "ORDER_NUMBER",
|
||||
"UNIQUENESS": "UNIQUE",
|
||||
"COLUMN_POSITION": 1,
|
||||
},
|
||||
})
|
||||
|
||||
if len(indexes) != 3 {
|
||||
t.Fatalf("expected three index columns, got=%d", len(indexes))
|
||||
}
|
||||
if indexes[0].Name != "IDX_ORDERS_TENANT_CREATED" || indexes[0].ColumnName != "TENANT_ID" || indexes[0].NonUnique != 1 || indexes[0].SeqInIndex != 1 {
|
||||
t.Fatalf("unexpected first index column: %#v", indexes[0])
|
||||
}
|
||||
if indexes[0].IndexType != "NORMAL" {
|
||||
t.Fatalf("expected index type metadata to be preserved, got: %#v", indexes[0])
|
||||
}
|
||||
if indexes[1].SeqInIndex != 2 {
|
||||
t.Fatalf("expected second column position, got: %#v", indexes[1])
|
||||
}
|
||||
if indexes[2].NonUnique != 0 {
|
||||
t.Fatalf("expected UNIQUE metadata to map to NonUnique=0, got: %#v", indexes[2])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user