From f5113def389cd29c8f067302d8ef19f816ce813d Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Wed, 2 Sep 2026 17:02:03 +0800 Subject: [PATCH] fix(xugu): match primary keys across catalog case --- agents/drivers/xugu/main.go | 22 ++++++++++++++++++++-- agents/drivers/xugu/main_test.go | 17 +++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/agents/drivers/xugu/main.go b/agents/drivers/xugu/main.go index 3eb7192c1b..fd69e91068 100644 --- a/agents/drivers/xugu/main.go +++ b/agents/drivers/xugu/main.go @@ -2765,7 +2765,7 @@ func (s *server) getColumns(schema, table string) ([]columnInfo, error) { item.DataType = normalizeXuguColumnType(item.DataType, varying) item.IsNullable = !truthy(notNull) item.DefaultOnNull = xuguInt(onNull) - item.IsPrimaryKey = primaryKeys[item.Name] + item.IsPrimaryKey = xuguPrimaryKeyMatches(item.Name, primaryKeys) item.NumericPrecision, item.NumericScale, item.CharacterMaximumLength = decodeXuguScale(item.DataType, scale) result = append(result, item) } @@ -2802,7 +2802,7 @@ func (s *server) columnsFromSelect(schema, table string, primaryKeys map[string] item := columnInfo{ Name: columnType.Name(), DataType: columnType.DatabaseTypeName(), - IsPrimaryKey: primaryKeys[columnType.Name()], + IsPrimaryKey: xuguPrimaryKeyMatches(columnType.Name(), primaryKeys), } if nullable, ok := columnType.Nullable(); ok { item.IsNullable = nullable @@ -2860,6 +2860,24 @@ func (s *server) primaryKeyColumns(schema, table string) (map[string]bool, error return result, rows.Err() } +// Xugu may report an unquoted primary-key name with a different case from +// ALL_COLUMNS (for example, DEFINE contains "ID" while COL_NAME is "id"). +// Exact matches always win so quoted identifiers that differ only by case stay +// distinct; a case-insensitive fallback is used only when it identifies one +// primary-key column. +func xuguPrimaryKeyMatches(columnName string, primaryKeys map[string]bool) bool { + if primaryKeys[columnName] { + return true + } + matches := 0 + for primaryKey := range primaryKeys { + if strings.EqualFold(primaryKey, columnName) { + matches++ + } + } + return matches == 1 +} + func (s *server) listIndexes(schema, table string) ([]indexInfo, error) { catalogSchema, catalogTable, err := s.resolveCatalogTableName(schema, table) if err != nil { diff --git a/agents/drivers/xugu/main_test.go b/agents/drivers/xugu/main_test.go index 7692905556..74c19e7aea 100644 --- a/agents/drivers/xugu/main_test.go +++ b/agents/drivers/xugu/main_test.go @@ -949,6 +949,23 @@ func TestGetColumnsFallsBackWhenOnNullMetadataIsUnavailable(t *testing.T) { } } +func TestXuguPrimaryKeyMatchesColumnCaseWhenCatalogsDisagree(t *testing.T) { + primaryKeys := map[string]bool{"ID": true} + if !xuguPrimaryKeyMatches("id", primaryKeys) { + t.Fatal("expected an unquoted primary key to match the column despite case differences") + } + if !xuguPrimaryKeyMatches("ID", primaryKeys) { + t.Fatal("expected an exact primary-key match") + } +} + +func TestXuguPrimaryKeyMatchingDoesNotGuessAmbiguousCase(t *testing.T) { + primaryKeys := map[string]bool{"ID": true, "id": true} + if xuguPrimaryKeyMatches("Id", primaryKeys) { + t.Fatal("must not choose between primary-key names that differ only by case") + } +} + func TestIndexSQLUsesLowPrivilegeDictionary(t *testing.T) { sqlText := strings.ToUpper(xuguListIndexesSQL)