fix(vastbase): tolerate NULL columns in index metadata scan
This commit is contained in:
parent
9a00c10da4
commit
6d2659ac97
|
|
@ -657,17 +657,19 @@ func (s *server) listIndexes(schema, table string) ([]indexInfo, error) {
|
|||
defer rows.Close()
|
||||
catalogIndexes := []vastbaseCatalogIndex{}
|
||||
attributes := map[int]string{}
|
||||
// UNION ALL 双分支各列可空性无法保证(Vastbase G100 在某些表/索引形态下会返回 NULL),
|
||||
// 统一用可空类型扫描以容错 NULL,避免 convertAssign 崩溃(#5602)。
|
||||
for rows.Next() {
|
||||
var rowKind, attributeNumber int
|
||||
var name, kind, columnNumbers, column string
|
||||
var rowKind, attributeNumber sql.NullInt64
|
||||
var name, kind, columnNumbers, column sql.NullString
|
||||
var unique, primary bool
|
||||
if err := rows.Scan(&rowKind, &name, &kind, &unique, &primary, &columnNumbers, &attributeNumber, &column); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rowKind == 0 {
|
||||
catalogIndexes = append(catalogIndexes, vastbaseCatalogIndex{name: name, indexType: kind, unique: unique, primary: primary, columnNumbers: columnNumbers})
|
||||
} else if attributeNumber > 0 && column != "" {
|
||||
attributes[attributeNumber] = column
|
||||
if rowKind.Int64 == 0 {
|
||||
catalogIndexes = append(catalogIndexes, vastbaseCatalogIndex{name: name.String, indexType: kind.String, unique: unique, primary: primary, columnNumbers: columnNumbers.String})
|
||||
} else if attributeNumber.Int64 > 0 && column.String != "" {
|
||||
attributes[int(attributeNumber.Int64)] = column.String
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,43 @@ func TestVastbaseListIndexesMapsCatalogVectorsInOneQuery(t *testing.T) {
|
|||
assertVastbaseIndex(t, indexes[1], "orders_pkey", []string{"id", "tenant_id"}, true, true, "btree")
|
||||
}
|
||||
|
||||
// TestVastbaseListIndexesToleratesNullColumnName 复刻 #5602:Vastbase G100 在 UNION ALL
|
||||
// 索引查询中对 column_name(column index 7)返回 NULL,裸 string 扫描会报
|
||||
// "converting NULL to string is unsupported"。修复后用 sql.NullString 容错,NULL 行被跳过不崩。
|
||||
func TestVastbaseListIndexesToleratesNullColumnName(t *testing.T) {
|
||||
state := &vastbaseIndexMetadataTestState{
|
||||
rows: [][]driver.Value{
|
||||
// 分支1(row_kind=0)的 column_name 占位列也可能为 NULL,不应影响索引元数据解析。
|
||||
{int64(0), "orders_pkey", "btree", true, true, "1 3", int64(0), nil},
|
||||
{int64(1), "", "", false, false, "", int64(1), "id"},
|
||||
// 模拟 Vastbase 对某 attribute 行的 column_name 返回 NULL,该行应被跳过。
|
||||
{int64(1), "", "", false, false, "", int64(2), nil},
|
||||
{int64(1), "", "", false, false, "", int64(3), "code"},
|
||||
},
|
||||
}
|
||||
driverName := fmt.Sprintf("vastbase-index-null-%d", vastbaseIndexMetadataDriverSequence.Add(1))
|
||||
sql.Register(driverName, &vastbaseIndexMetadataTestDriver{state: state})
|
||||
db, err := sql.Open(driverName, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = db.Close() })
|
||||
|
||||
server := newServer()
|
||||
server.db = db
|
||||
server.mode.postgresCatalog = true
|
||||
indexes, err := server.listIndexes("app", "orders")
|
||||
if err != nil {
|
||||
t.Fatalf("listIndexes should tolerate NULL column_name, got error: %v", err)
|
||||
}
|
||||
// orders_pkey 的 indkey 为 "1 3",对应 id(1) 和 code(3);attribute 2 的 NULL 行被跳过,
|
||||
// 但 id 与 code 仍可解析,因此索引应正常返回。
|
||||
if len(indexes) != 1 {
|
||||
t.Fatalf("listIndexes returned %d indexes, want 1: %+v", len(indexes), indexes)
|
||||
}
|
||||
assertVastbaseIndex(t, indexes[0], "orders_pkey", []string{"id", "code"}, true, true, "btree")
|
||||
}
|
||||
|
||||
func TestParseVastbaseAttributeNumbersSupportsCatalogRepresentations(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
raw string
|
||||
|
|
@ -80,6 +117,8 @@ var vastbaseIndexMetadataDriverSequence atomic.Uint64
|
|||
type vastbaseIndexMetadataTestState struct {
|
||||
queryCount int
|
||||
query string
|
||||
// rows 为非空时覆盖默认返回数据,用于注入 NULL 等边界场景。
|
||||
rows [][]driver.Value
|
||||
}
|
||||
|
||||
type vastbaseIndexMetadataTestDriver struct {
|
||||
|
|
@ -103,16 +142,18 @@ func (*vastbaseIndexMetadataTestConn) Begin() (driver.Tx, error) { return nil, d
|
|||
func (conn *vastbaseIndexMetadataTestConn) QueryContext(_ context.Context, query string, _ []driver.NamedValue) (driver.Rows, error) {
|
||||
conn.state.queryCount++
|
||||
conn.state.query = query
|
||||
return &vastbaseIndexMetadataTestRows{
|
||||
rows: [][]driver.Value{
|
||||
rows := conn.state.rows
|
||||
if rows == nil {
|
||||
rows = [][]driver.Value{
|
||||
{int64(0), "orders_code_idx", "btree", true, false, "3 2", int64(0), ""},
|
||||
{int64(0), "orders_expression_idx", "btree", false, false, "0 2", int64(0), ""},
|
||||
{int64(0), "orders_pkey", "btree", true, true, "1 2", int64(0), ""},
|
||||
{int64(1), "", "", false, false, "", int64(1), "id"},
|
||||
{int64(1), "", "", false, false, "", int64(2), "tenant_id"},
|
||||
{int64(1), "", "", false, false, "", int64(3), "code"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return &vastbaseIndexMetadataTestRows{rows: rows}, nil
|
||||
}
|
||||
|
||||
type vastbaseIndexMetadataTestRows struct {
|
||||
|
|
|
|||
Loading…
Reference in New Issue