Skip to content

Commit dd72e81

Browse files
committed
fix: handle TEMP/TEMPORARY in CREATE TABLE DDL and preserve index column casing
- Use regex to match CREATE [TEMP|TEMPORARY] TABLE variants when injecting primary key constraints, fixing DDL generation for temporary tables. - Resolve index column names against known columns to preserve actual database casing (e.g., Oracle uppercase folding). - Clean up verbose comments in index capability definitions.
1 parent 5844396 commit dd72e81

2 files changed

Lines changed: 29 additions & 28 deletions

File tree

core/dbio/database/schemata.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,21 +1220,19 @@ func (t *Table) ColumnCommentsDDL(conn Connection, columns iop.Columns) (stmts [
12201220
return stmts
12211221
}
12221222

1223+
var createTableRegex = regexp.MustCompile(`(?i)create\s+(?:temp\s+|temporary\s+)?table`)
1224+
12231225
func (t *Table) AddPrimaryKeyToDDL(ddl string, columns iop.Columns) (string, error) {
12241226

12251227
if pkCols := columns.GetKeys(iop.PrimaryKey); len(pkCols) > 0 {
12261228
ddl = strings.TrimSpace(ddl)
12271229

1228-
// Find the closing parenthesis of the column definitions
1229-
// We need to find the first balanced closing paren that matches the opening
1230-
// paren of the CREATE TABLE column list, not just the last paren in the DDL
1231-
// This handles cases like: CREATE TABLE t (col1 int) WITH (data_compression=page)
1232-
1233-
// Find "CREATE TABLE" pattern to locate start of statement
1234-
createTableIdx := strings.Index(strings.ToUpper(ddl), "CREATE TABLE")
1235-
if createTableIdx == -1 {
1230+
// anchor on the CREATE [TEMP|TEMPORARY] TABLE keyword to start the column-list search
1231+
loc := createTableRegex.FindStringIndex(ddl)
1232+
if loc == nil {
12361233
return ddl, g.Error("could not find CREATE TABLE in DDL")
12371234
}
1235+
createTableIdx := loc[0]
12381236

12391237
// Find the opening paren after CREATE TABLE (this is the column list)
12401238
openParen := strings.Index(ddl[createTableIdx:], "(")

core/dbio/database/schemata_keys.go

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,12 @@ func indexColumnEntry(token string, knownColumns iop.Columns) (col iop.IndexColu
115115
return iop.IndexColumn{Name: token}, true, nil
116116
}
117117
if len(knownColumns) > 0 {
118-
if c := knownColumns.GetColumn(token); c == nil {
118+
c := knownColumns.GetColumn(token)
119+
if c == nil {
119120
return col, false, g.Error("index references unknown column %q\navailable: %s", token, g.Marshal(knownColumns.Names()))
120121
}
122+
// use the resolved column's actual casing (e.g. Oracle folds to upper)
123+
token = c.Name
121124
}
122125
return iop.IndexColumn{Name: token}, false, nil
123126
}
@@ -306,10 +309,8 @@ func (ti *TableIndex) indexDef() iop.IndexDef {
306309
return def
307310
}
308311

309-
// CreateDDL renders the CREATE INDEX statement for the dialect, honoring the
310-
// §3.1 capability matrix: unsupported attributes are dropped with a one-time
311-
// warning, and a closed-set `type` violation returns empty (the caller has
312-
// already validated; this method warns and renders best-effort).
312+
// CreateDDL renders the CREATE INDEX statement for the dialect; unsupported
313+
// attributes are dropped with a warning, a closed-set type violation returns "".
313314
func (ti *TableIndex) CreateDDL() string {
314315
dialect := ti.Table.Dialect
315316
def := ti.indexDef()
@@ -464,27 +465,21 @@ func (t *Table) Indexes(columns iop.Columns) (indexes []TableIndex) {
464465
}
465466

466467
// indexCapability describes, per engine, which index attributes are rendered,
467-
// silently dropped (with a warning), or rejected. It is the single Go-side
468-
// source of truth for the §3.1 coverage matrix; the actual statement placement
469-
// of `type` lives in each engine's template (create_index_full).
468+
// dropped (with a warning), or rejected.
470469
type indexCapability struct {
471-
// noIndexes: engine has no secondary index concept; the whole index is a
472-
// no-op (accepted + one-time warning, never an error).
473-
noIndexes bool
470+
noIndexes bool // no standalone CREATE INDEX; the whole index is a no-op
474471

475472
supportsWhere bool // partial index predicate
476473
supportsInclude bool // covering-index columns
477474
supportsUnique bool // UNIQUE keyword
478475

479-
// supportsType: engine accepts a `type`/`using` clause. When typeClosedSet
480-
// is non-nil, an unknown value is a hard error; otherwise the value is
481-
// passed through to the engine verbatim.
476+
// supportsType: engine accepts a type/using clause. When typeClosedSet is
477+
// non-nil, an unknown value is a hard error; else passed through verbatim.
482478
supportsType bool
483-
typeClosedSet []string // closed set of recognized type values (lowercased)
479+
typeClosedSet []string // recognized type values (lowercased)
484480
}
485481

486-
// indexCapabilities encodes the §3.1 matrix. Engines absent from this map use
487-
// indexCapDefault (basic CREATE INDEX, no extras).
482+
// Engines absent from this map use indexCapDefault (basic CREATE INDEX).
488483
var indexCapabilities = map[dbio.Type]indexCapability{
489484
dbio.TypeDbPostgres: {supportsWhere: true, supportsInclude: true, supportsUnique: true, supportsType: true},
490485
dbio.TypeDbRedshift: {noIndexes: true},
@@ -494,10 +489,18 @@ var indexCapabilities = map[dbio.Type]indexCapability{
494489
supportsWhere: true, supportsInclude: true, supportsUnique: true,
495490
supportsType: true, typeClosedSet: []string{"clustered", "nonclustered"},
496491
},
497-
dbio.TypeDbClickhouse: {supportsType: true}, // handled inline in create_table
498-
dbio.TypeDbProton: {supportsType: true},
492+
// ClickHouse data-skipping indexes are declared inline in CREATE TABLE
493+
// (see injectInlineIndexes); standalone CREATE INDEX is unsupported, so the
494+
// standalone path is a no-op here.
495+
dbio.TypeDbClickhouse: {noIndexes: true},
496+
dbio.TypeDbProton: {noIndexes: true},
499497
dbio.TypeDbSnowflake: {noIndexes: true},
500-
dbio.TypeDbBigQuery: {supportsType: true, typeClosedSet: []string{"search"}},
498+
// BigQuery has no plain secondary index (only CREATE SEARCH/VECTOR INDEX,
499+
// not generated here); treat table_keys.index as a no-op.
500+
dbio.TypeDbBigQuery: {noIndexes: true},
501+
// StarRocks indexes (BITMAP/inverted) only apply to specific column/table
502+
// models and don't fit the generic CREATE INDEX form; no-op for now.
503+
dbio.TypeDbStarRocks: {noIndexes: true},
501504
dbio.TypeDbDuckDb: {supportsUnique: true},
502505
dbio.TypeDbMotherDuck: {supportsUnique: true},
503506
dbio.TypeDbOracle: {supportsUnique: true, supportsType: true, typeClosedSet: []string{"bitmap"}},

0 commit comments

Comments
 (0)