From 66094ba081a10337d188789dfdc98d5f19125efb Mon Sep 17 00:00:00 2001 From: James Cor Date: Wed, 13 Aug 2025 12:11:51 -0700 Subject: [PATCH 01/12] cache schemas --- sql/plan/project.go | 18 ++++++++++++------ sql/plan/tablealias.go | 34 ++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/sql/plan/project.go b/sql/plan/project.go index 9e377794c2..c5f28e751d 100644 --- a/sql/plan/project.go +++ b/sql/plan/project.go @@ -35,6 +35,9 @@ type Project struct { // a RowIter. IncludesNestedIters bool deps sql.ColSet + + sch sql.Schema + cachedSch bool } var _ sql.Expressioner = (*Project)(nil) @@ -47,6 +50,7 @@ func NewProject(expressions []sql.Expression, child sql.Node) *Project { return &Project{ UnaryNode: UnaryNode{child}, Projections: expressions, + sch: make(sql.Schema, len(expressions)), } } @@ -122,14 +126,16 @@ func ExprDeps(exprs ...sql.Expression) sql.ColSet { // Schema implements the Node interface. func (p *Project) Schema() sql.Schema { - var s = make(sql.Schema, len(p.Projections)) - for i, expr := range p.Projections { - s[i] = transform.ExpressionToColumn(expr, AliasSubqueryString(expr)) - if gf := unwrapGetField(expr); gf != nil { - s[i].Default = findDefault(p.Child, gf) + if !p.cachedSch { + p.cachedSch = true + for i, expr := range p.Projections { + p.sch[i] = transform.ExpressionToColumn(expr, AliasSubqueryString(expr)) + if gf := unwrapGetField(expr); gf != nil { + p.sch[i].Default = findDefault(p.Child, gf) + } } } - return s + return p.sch } // Resolved implements the Resolvable interface. diff --git a/sql/plan/tablealias.go b/sql/plan/tablealias.go index de92e1fd3f..a7645ac646 100644 --- a/sql/plan/tablealias.go +++ b/sql/plan/tablealias.go @@ -25,6 +25,7 @@ type TableAlias struct { comment string id sql.TableId cols sql.ColSet + sch sql.Schema } var _ sql.RenameableNode = (*TableAlias)(nil) @@ -33,7 +34,18 @@ var _ sql.CollationCoercible = (*TableAlias)(nil) // NewTableAlias returns a new Table alias node. func NewTableAlias(name string, node sql.Node) *TableAlias { - ret := &TableAlias{UnaryNode: &UnaryNode{Child: node}, name: name} + childSchema := node.Schema() + schema := make(sql.Schema, len(childSchema)) + for i, col := range childSchema { + newCol := *col + newCol.Source = name + schema[i] = &newCol + } + ret := &TableAlias{ + UnaryNode: &UnaryNode{Child: node}, + name: name, + sch: schema, + } if tin, ok := node.(TableIdNode); ok { ret.id = tin.Id() ret.cols = tin.Columns() @@ -87,14 +99,7 @@ func (t *TableAlias) Comment() string { // Schema implements the Node interface. TableAlias alters the schema of its child element to rename the source of // columns to the alias. func (t *TableAlias) Schema() sql.Schema { - childSchema := t.Child.Schema() - copy := make(sql.Schema, len(childSchema)) - for i, col := range childSchema { - colCopy := *col - colCopy.Source = t.name - copy[i] = &colCopy - } - return copy + return t.sch } // WithChildren implements the Node interface. @@ -118,21 +123,22 @@ func (t *TableAlias) CollationCoercibility(ctx *sql.Context) (collation sql.Coll return sql.Collation_binary, 7 } -func (t TableAlias) String() string { +func (t *TableAlias) String() string { pr := sql.NewTreePrinter() _ = pr.WriteNode("TableAlias(%s)", t.name) _ = pr.WriteChildren(t.Child.String()) return pr.String() } -func (t TableAlias) DebugString() string { +func (t *TableAlias) DebugString() string { pr := sql.NewTreePrinter() _ = pr.WriteNode("TableAlias(%s)", t.name) _ = pr.WriteChildren(sql.DebugString(t.Child)) return pr.String() } -func (t TableAlias) WithName(name string) sql.Node { - t.name = name - return &t +func (t *TableAlias) WithName(name string) sql.Node { + nt := *t + nt.name = name + return &nt } From 44d7bbad606dec7072397f80426be9b2cce203e4 Mon Sep 17 00:00:00 2001 From: James Cor Date: Wed, 13 Aug 2025 12:26:13 -0700 Subject: [PATCH 02/12] fewer schema allocations --- sql/plan/tablealias.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/sql/plan/tablealias.go b/sql/plan/tablealias.go index a7645ac646..a6ae8c9054 100644 --- a/sql/plan/tablealias.go +++ b/sql/plan/tablealias.go @@ -21,11 +21,12 @@ import ( // TableAlias is a node that acts as a table with a given name. type TableAlias struct { *UnaryNode - name string - comment string - id sql.TableId - cols sql.ColSet - sch sql.Schema + name string + comment string + id sql.TableId + cols sql.ColSet + sch sql.Schema + cachedSch bool } var _ sql.RenameableNode = (*TableAlias)(nil) @@ -34,13 +35,7 @@ var _ sql.CollationCoercible = (*TableAlias)(nil) // NewTableAlias returns a new Table alias node. func NewTableAlias(name string, node sql.Node) *TableAlias { - childSchema := node.Schema() - schema := make(sql.Schema, len(childSchema)) - for i, col := range childSchema { - newCol := *col - newCol.Source = name - schema[i] = &newCol - } + schema := make(sql.Schema, len(node.Schema())) ret := &TableAlias{ UnaryNode: &UnaryNode{Child: node}, name: name, @@ -99,6 +94,14 @@ func (t *TableAlias) Comment() string { // Schema implements the Node interface. TableAlias alters the schema of its child element to rename the source of // columns to the alias. func (t *TableAlias) Schema() sql.Schema { + if !t.cachedSch { + for i, col := range t.Child.Schema() { + newCol := *col + newCol.Source = t.name + t.sch[i] = &newCol + } + t.cachedSch = true + } return t.sch } From 6ade92db73ee4a3118119184f2f50059a7cfa918 Mon Sep 17 00:00:00 2001 From: James Cor Date: Wed, 13 Aug 2025 15:08:06 -0700 Subject: [PATCH 03/12] even fewer allocs --- memory/index.go | 5 +++-- sql/plan/project.go | 8 +++----- sql/plan/tablealias.go | 9 ++++----- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/memory/index.go b/memory/index.go index 44eae97c4c..52baf60ec7 100644 --- a/memory/index.go +++ b/memory/index.go @@ -298,8 +298,9 @@ func (idx *Index) Reversible() bool { return true } -func (idx Index) copy() *Index { - return &idx +func (idx *Index) copy() *Index { + newIdx := *idx + return &newIdx } // columnIndexes returns the indexes in the given schema for the fields in this index diff --git a/sql/plan/project.go b/sql/plan/project.go index c5f28e751d..f1ea767a0a 100644 --- a/sql/plan/project.go +++ b/sql/plan/project.go @@ -36,8 +36,7 @@ type Project struct { IncludesNestedIters bool deps sql.ColSet - sch sql.Schema - cachedSch bool + sch sql.Schema } var _ sql.Expressioner = (*Project)(nil) @@ -50,7 +49,6 @@ func NewProject(expressions []sql.Expression, child sql.Node) *Project { return &Project{ UnaryNode: UnaryNode{child}, Projections: expressions, - sch: make(sql.Schema, len(expressions)), } } @@ -126,8 +124,8 @@ func ExprDeps(exprs ...sql.Expression) sql.ColSet { // Schema implements the Node interface. func (p *Project) Schema() sql.Schema { - if !p.cachedSch { - p.cachedSch = true + if p.sch == nil { + p.sch = make(sql.Schema, len(p.Projections)) for i, expr := range p.Projections { p.sch[i] = transform.ExpressionToColumn(expr, AliasSubqueryString(expr)) if gf := unwrapGetField(expr); gf != nil { diff --git a/sql/plan/tablealias.go b/sql/plan/tablealias.go index a6ae8c9054..856a21e188 100644 --- a/sql/plan/tablealias.go +++ b/sql/plan/tablealias.go @@ -35,11 +35,9 @@ var _ sql.CollationCoercible = (*TableAlias)(nil) // NewTableAlias returns a new Table alias node. func NewTableAlias(name string, node sql.Node) *TableAlias { - schema := make(sql.Schema, len(node.Schema())) ret := &TableAlias{ UnaryNode: &UnaryNode{Child: node}, name: name, - sch: schema, } if tin, ok := node.(TableIdNode); ok { ret.id = tin.Id() @@ -94,13 +92,14 @@ func (t *TableAlias) Comment() string { // Schema implements the Node interface. TableAlias alters the schema of its child element to rename the source of // columns to the alias. func (t *TableAlias) Schema() sql.Schema { - if !t.cachedSch { - for i, col := range t.Child.Schema() { + if t.sch == nil { + childSchema := t.Child.Schema() + t.sch = make(sql.Schema, len(childSchema)) + for i, col := range childSchema { newCol := *col newCol.Source = t.name t.sch[i] = &newCol } - t.cachedSch = true } return t.sch } From e819f8846a9cad9ee1de912be45ac64eba771a68 Mon Sep 17 00:00:00 2001 From: James Cor Date: Wed, 13 Aug 2025 15:43:40 -0700 Subject: [PATCH 04/12] cache index exprs --- memory/index.go | 9 +++++++++ sql/analyzer/index_analyzer_test.go | 10 ++++++++++ sql/index.go | 2 ++ sql/index_builder_test.go | 8 ++++++++ sql/index_registry_test.go | 11 +++++++++++ sql/memo/rel_props.go | 10 ++++------ sql/memo/rel_props_test.go | 4 ++++ 7 files changed, 48 insertions(+), 6 deletions(-) diff --git a/memory/index.go b/memory/index.go index 52baf60ec7..0d07d5ab77 100644 --- a/memory/index.go +++ b/memory/index.go @@ -73,6 +73,15 @@ func (idx *Index) Expressions() []string { return exprs } +func (idx *Index) UnqualifiedExpressions() []string { + exprs := make([]string, len(idx.Exprs)) + for i, e := range idx.Exprs { + str := e.String() + exprs[i] = str[strings.IndexByte(str, '.')+1:] + } + return exprs +} + func (idx *Index) ExtendedExpressions() []string { var exprs []string foundCols := make(map[string]struct{}) diff --git a/sql/analyzer/index_analyzer_test.go b/sql/analyzer/index_analyzer_test.go index 944c5029c8..aba72c77bf 100644 --- a/sql/analyzer/index_analyzer_test.go +++ b/sql/analyzer/index_analyzer_test.go @@ -15,6 +15,7 @@ package analyzer import ( + "strings" "testing" "github.com/stretchr/testify/require" @@ -142,6 +143,15 @@ func (i dummyIdx) Expressions() []string { } return exprs } + +func (i dummyIdx) UnqualifiedExpressions() []string { + exprs := make([]string, len(i.expr)) + for i, e := range i.expr { + str := e.String() + exprs[i] = str[strings.IndexByte(str, '.')+1:] + } + return exprs +} func (i *dummyIdx) ID() string { return i.id } func (i *dummyIdx) Database() string { return i.database } func (i *dummyIdx) Table() string { return i.table } diff --git a/sql/index.go b/sql/index.go index 62aac79cc6..43b28314d7 100644 --- a/sql/index.go +++ b/sql/index.go @@ -102,6 +102,8 @@ type Index interface { // one expression, it means the index has multiple columns indexed. If it's // just one, it means it may be an expression or a column. Expressions() []string + // UnqualifiedExpressions returns the indexed expressions without the source. + UnqualifiedExpressions() []string // IsUnique returns whether this index is unique IsUnique() bool // IsSpatial returns whether this index is a spatial index diff --git a/sql/index_builder_test.go b/sql/index_builder_test.go index a7bde2a244..9f116883aa 100644 --- a/sql/index_builder_test.go +++ b/sql/index_builder_test.go @@ -188,6 +188,14 @@ func (i testIndex) Expressions() []string { return res } +func (i testIndex) UnqualifiedExpressions() []string { + res := make([]string, i.numcols) + for i := range res { + res[i] = fmt.Sprintf("column_%d", i) + } + return res +} + func (testIndex) IsUnique() bool { return false } diff --git a/sql/index_registry_test.go b/sql/index_registry_test.go index dbdbc2e415..2dd7530b5a 100644 --- a/sql/index_registry_test.go +++ b/sql/index_registry_test.go @@ -16,6 +16,7 @@ package sql import ( "fmt" + "strings" "testing" "github.com/stretchr/testify/require" @@ -443,6 +444,16 @@ func (i dummyIdx) Expressions() []string { } return exprs } + +func (i dummyIdx) UnqualifiedExpressions() []string { + exprs := make([]string, len(i.expr)) + for i, e := range i.expr { + str := e.String() + exprs[i] = str[strings.IndexByte(str, '.')+1:] + } + return exprs +} + func (i dummyIdx) ID() string { return i.id } func (i dummyIdx) Database() string { return i.database } func (i dummyIdx) Table() string { return i.table } diff --git a/sql/memo/rel_props.go b/sql/memo/rel_props.go index 91cc182ddc..8888f5cc87 100644 --- a/sql/memo/rel_props.go +++ b/sql/memo/rel_props.go @@ -131,13 +131,11 @@ func newRelProps(rel RelExpr) *relProps { } // idxExprsColumns returns the column names used in an index's expressions. -// TODO: this is unstable as long as periods in Index.Expressions() -// identifiers are ambiguous. +// Identifiers are ambiguous. func idxExprsColumns(idx sql.Index) []string { - columns := make([]string, len(idx.Expressions())) - for i, e := range idx.Expressions() { - parts := strings.Split(e, ".") - columns[i] = strings.ToLower(parts[1]) + columns := idx.UnqualifiedExpressions() + for i := 0; i < len(columns); i++ { + columns[i] = strings.ToLower(columns[i]) } return columns } diff --git a/sql/memo/rel_props_test.go b/sql/memo/rel_props_test.go index a5034768c8..282f462100 100644 --- a/sql/memo/rel_props_test.go +++ b/sql/memo/rel_props_test.go @@ -217,6 +217,10 @@ func (i dummyIndex) Expressions() []string { return i.cols } +func (i dummyIndex) UnqualifiedExpressions() []string { + return i.cols +} + func (dummyIndex) IsUnique() bool { return true } From 25efc39c2adee78b90d84dbb4db076e27b0e84a6 Mon Sep 17 00:00:00 2001 From: James Cor Date: Thu, 14 Aug 2025 12:47:58 -0700 Subject: [PATCH 05/12] fix --- sql/memo/rel_props.go | 11 ++--------- sql/memo/rel_props_test.go | 7 ++++++- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/sql/memo/rel_props.go b/sql/memo/rel_props.go index 8888f5cc87..7ee114c20c 100644 --- a/sql/memo/rel_props.go +++ b/sql/memo/rel_props.go @@ -789,17 +789,10 @@ func sortedColsForRel(rel RelExpr) sql.Schema { } case *MergeJoin: var ret sql.Schema - for _, e := range r.InnerScan.Table.Index().Expressions() { + for _, e := range r.InnerScan.Table.Index().UnqualifiedExpressions() { // TODO columns can have "." characters, this will miss cases - parts := strings.Split(e, ".") - var name string - if len(parts) == 2 { - name = parts[1] - } else { - return nil - } ret = append(ret, &sql.Column{ - Name: strings.ToLower(name), + Name: strings.ToLower(e), Source: strings.ToLower(r.InnerScan.Table.Name()), Nullable: true}, ) diff --git a/sql/memo/rel_props_test.go b/sql/memo/rel_props_test.go index 282f462100..9f8bc0dbc6 100644 --- a/sql/memo/rel_props_test.go +++ b/sql/memo/rel_props_test.go @@ -2,6 +2,7 @@ package memo import ( "fmt" + "strings" "testing" "github.com/stretchr/testify/require" @@ -218,7 +219,11 @@ func (i dummyIndex) Expressions() []string { } func (i dummyIndex) UnqualifiedExpressions() []string { - return i.cols + res := make([]string, len(i.cols)) + for idx, col := range i.cols { + res[idx] = col[strings.IndexByte(col, '.')+1:] + } + return res } func (dummyIndex) IsUnique() bool { From 3c760f62affc773058c014957b2170008239c4ef Mon Sep 17 00:00:00 2001 From: James Cor Date: Thu, 14 Aug 2025 13:05:58 -0700 Subject: [PATCH 06/12] more fix --- sql/analyzer/common_test.go | 2 ++ sql/planbuilder/ddl.go | 9 ++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/sql/analyzer/common_test.go b/sql/analyzer/common_test.go index e4abd9e797..caa2ca925c 100644 --- a/sql/analyzer/common_test.go +++ b/sql/analyzer/common_test.go @@ -157,6 +157,8 @@ func runTestCases(t *testing.T, ctx *sql.Context, testCases []analyzerFnTestCase if expected == nil { expected = tt.node } + // Schema of certain nodes aren't filled until needed + expected.Schema() assertNodesEqualWithDiff(t, expected, result) }) diff --git a/sql/planbuilder/ddl.go b/sql/planbuilder/ddl.go index c5f79201e9..5447518d18 100644 --- a/sql/planbuilder/ddl.go +++ b/sql/planbuilder/ddl.go @@ -395,11 +395,11 @@ func (b *Builder) getIndexDefs(table sql.Table) sql.IndexDefs { if !isIdxTbl { return nil } - var idxDefs sql.IndexDefs idxs, err := idxTbl.GetIndexes(b.ctx) if err != nil { b.handleErr(err) } + idxDefs := make(sql.IndexDefs, 0, len(idxs)) for _, idx := range idxs { if idx.IsGenerated() { continue @@ -412,10 +412,9 @@ func (b *Builder) getIndexDefs(table sql.Table) sql.IndexDefs { constraint = sql.IndexConstraint_Unique } } - columns := make([]sql.IndexColumn, len(idx.Expressions())) - for i, col := range idx.Expressions() { - // TODO: find a better way to get only the column name if the table is present - col = strings.TrimPrefix(col, idxTbl.Name()+".") + exprs := idx.UnqualifiedExpressions() + columns := make([]sql.IndexColumn, len(exprs)) + for i, col := range exprs { columns[i] = sql.IndexColumn{Name: col} } idxDefs = append(idxDefs, &sql.IndexDef{ From d728d6a7fbd248b44ce02af1777b1282a8b88a01 Mon Sep 17 00:00:00 2001 From: James Cor Date: Mon, 18 Aug 2025 10:47:03 -0700 Subject: [PATCH 07/12] revert index change --- memory/index.go | 9 --------- sql/analyzer/index_analyzer_test.go | 13 ++----------- sql/index.go | 2 -- sql/index_builder_test.go | 8 -------- sql/index_registry_test.go | 10 ---------- sql/memo/rel_props.go | 6 +++--- sql/memo/rel_props_test.go | 9 --------- sql/planbuilder/ddl.go | 2 +- 8 files changed, 6 insertions(+), 53 deletions(-) diff --git a/memory/index.go b/memory/index.go index 0d07d5ab77..52baf60ec7 100644 --- a/memory/index.go +++ b/memory/index.go @@ -73,15 +73,6 @@ func (idx *Index) Expressions() []string { return exprs } -func (idx *Index) UnqualifiedExpressions() []string { - exprs := make([]string, len(idx.Exprs)) - for i, e := range idx.Exprs { - str := e.String() - exprs[i] = str[strings.IndexByte(str, '.')+1:] - } - return exprs -} - func (idx *Index) ExtendedExpressions() []string { var exprs []string foundCols := make(map[string]struct{}) diff --git a/sql/analyzer/index_analyzer_test.go b/sql/analyzer/index_analyzer_test.go index aba72c77bf..45937262fa 100644 --- a/sql/analyzer/index_analyzer_test.go +++ b/sql/analyzer/index_analyzer_test.go @@ -15,7 +15,6 @@ package analyzer import ( - "strings" "testing" "github.com/stretchr/testify/require" @@ -132,11 +131,11 @@ type dummyIdx struct { var _ sql.Index = (*dummyIdx)(nil) -func (i dummyIdx) CanSupport(context *sql.Context, r ...sql.Range) bool { +func (i *dummyIdx) CanSupport(context *sql.Context, r ...sql.Range) bool { return true } -func (i dummyIdx) Expressions() []string { +func (i *dummyIdx) Expressions() []string { var exprs []string for _, e := range i.expr { exprs = append(exprs, e.String()) @@ -144,14 +143,6 @@ func (i dummyIdx) Expressions() []string { return exprs } -func (i dummyIdx) UnqualifiedExpressions() []string { - exprs := make([]string, len(i.expr)) - for i, e := range i.expr { - str := e.String() - exprs[i] = str[strings.IndexByte(str, '.')+1:] - } - return exprs -} func (i *dummyIdx) ID() string { return i.id } func (i *dummyIdx) Database() string { return i.database } func (i *dummyIdx) Table() string { return i.table } diff --git a/sql/index.go b/sql/index.go index 43b28314d7..62aac79cc6 100644 --- a/sql/index.go +++ b/sql/index.go @@ -102,8 +102,6 @@ type Index interface { // one expression, it means the index has multiple columns indexed. If it's // just one, it means it may be an expression or a column. Expressions() []string - // UnqualifiedExpressions returns the indexed expressions without the source. - UnqualifiedExpressions() []string // IsUnique returns whether this index is unique IsUnique() bool // IsSpatial returns whether this index is a spatial index diff --git a/sql/index_builder_test.go b/sql/index_builder_test.go index 9f116883aa..a7bde2a244 100644 --- a/sql/index_builder_test.go +++ b/sql/index_builder_test.go @@ -188,14 +188,6 @@ func (i testIndex) Expressions() []string { return res } -func (i testIndex) UnqualifiedExpressions() []string { - res := make([]string, i.numcols) - for i := range res { - res[i] = fmt.Sprintf("column_%d", i) - } - return res -} - func (testIndex) IsUnique() bool { return false } diff --git a/sql/index_registry_test.go b/sql/index_registry_test.go index 2dd7530b5a..49cc1ea363 100644 --- a/sql/index_registry_test.go +++ b/sql/index_registry_test.go @@ -16,7 +16,6 @@ package sql import ( "fmt" - "strings" "testing" "github.com/stretchr/testify/require" @@ -445,15 +444,6 @@ func (i dummyIdx) Expressions() []string { return exprs } -func (i dummyIdx) UnqualifiedExpressions() []string { - exprs := make([]string, len(i.expr)) - for i, e := range i.expr { - str := e.String() - exprs[i] = str[strings.IndexByte(str, '.')+1:] - } - return exprs -} - func (i dummyIdx) ID() string { return i.id } func (i dummyIdx) Database() string { return i.database } func (i dummyIdx) Table() string { return i.table } diff --git a/sql/memo/rel_props.go b/sql/memo/rel_props.go index 7ee114c20c..2c82f2cd8c 100644 --- a/sql/memo/rel_props.go +++ b/sql/memo/rel_props.go @@ -133,9 +133,9 @@ func newRelProps(rel RelExpr) *relProps { // idxExprsColumns returns the column names used in an index's expressions. // Identifiers are ambiguous. func idxExprsColumns(idx sql.Index) []string { - columns := idx.UnqualifiedExpressions() + columns := idx.Expressions() for i := 0; i < len(columns); i++ { - columns[i] = strings.ToLower(columns[i]) + columns[i] = strings.ToLower(columns[i][strings.IndexByte(columns[i], '.')+1:]) } return columns } @@ -789,7 +789,7 @@ func sortedColsForRel(rel RelExpr) sql.Schema { } case *MergeJoin: var ret sql.Schema - for _, e := range r.InnerScan.Table.Index().UnqualifiedExpressions() { + for _, e := range r.InnerScan.Table.Index().Expressions() { // TODO columns can have "." characters, this will miss cases ret = append(ret, &sql.Column{ Name: strings.ToLower(e), diff --git a/sql/memo/rel_props_test.go b/sql/memo/rel_props_test.go index 9f8bc0dbc6..a5034768c8 100644 --- a/sql/memo/rel_props_test.go +++ b/sql/memo/rel_props_test.go @@ -2,7 +2,6 @@ package memo import ( "fmt" - "strings" "testing" "github.com/stretchr/testify/require" @@ -218,14 +217,6 @@ func (i dummyIndex) Expressions() []string { return i.cols } -func (i dummyIndex) UnqualifiedExpressions() []string { - res := make([]string, len(i.cols)) - for idx, col := range i.cols { - res[idx] = col[strings.IndexByte(col, '.')+1:] - } - return res -} - func (dummyIndex) IsUnique() bool { return true } diff --git a/sql/planbuilder/ddl.go b/sql/planbuilder/ddl.go index 5447518d18..d827e52598 100644 --- a/sql/planbuilder/ddl.go +++ b/sql/planbuilder/ddl.go @@ -412,7 +412,7 @@ func (b *Builder) getIndexDefs(table sql.Table) sql.IndexDefs { constraint = sql.IndexConstraint_Unique } } - exprs := idx.UnqualifiedExpressions() + exprs := idx.Expressions() columns := make([]sql.IndexColumn, len(exprs)) for i, col := range exprs { columns[i] = sql.IndexColumn{Name: col} From bdd826a1006897261a109ca790cbcff343cc530b Mon Sep 17 00:00:00 2001 From: James Cor Date: Mon, 18 Aug 2025 11:38:56 -0700 Subject: [PATCH 08/12] restore behavior --- sql/memo/rel_props.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sql/memo/rel_props.go b/sql/memo/rel_props.go index 2c82f2cd8c..14adbc4e0b 100644 --- a/sql/memo/rel_props.go +++ b/sql/memo/rel_props.go @@ -133,11 +133,12 @@ func newRelProps(rel RelExpr) *relProps { // idxExprsColumns returns the column names used in an index's expressions. // Identifiers are ambiguous. func idxExprsColumns(idx sql.Index) []string { - columns := idx.Expressions() - for i := 0; i < len(columns); i++ { - columns[i] = strings.ToLower(columns[i][strings.IndexByte(columns[i], '.')+1:]) + exprs := idx.Expressions() + for i, e := range exprs { + colName := e[strings.IndexByte(e, '.')+1:] + exprs[i] = strings.ToLower(colName) } - return columns + return exprs } func (p *relProps) SetStats(s sql.Statistic) { @@ -791,8 +792,12 @@ func sortedColsForRel(rel RelExpr) sql.Schema { var ret sql.Schema for _, e := range r.InnerScan.Table.Index().Expressions() { // TODO columns can have "." characters, this will miss cases + idx := strings.IndexRune(e, '.') + if idx == -1 { + return nil + } ret = append(ret, &sql.Column{ - Name: strings.ToLower(e), + Name: strings.ToLower(e[idx+1:]), Source: strings.ToLower(r.InnerScan.Table.Name()), Nullable: true}, ) From 6d821e0ecf5498be94c8158836a4a5f5d25291c6 Mon Sep 17 00:00:00 2001 From: James Cor Date: Mon, 18 Aug 2025 11:44:12 -0700 Subject: [PATCH 09/12] missed one --- sql/planbuilder/ddl.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/planbuilder/ddl.go b/sql/planbuilder/ddl.go index d827e52598..5380548945 100644 --- a/sql/planbuilder/ddl.go +++ b/sql/planbuilder/ddl.go @@ -415,6 +415,7 @@ func (b *Builder) getIndexDefs(table sql.Table) sql.IndexDefs { exprs := idx.Expressions() columns := make([]sql.IndexColumn, len(exprs)) for i, col := range exprs { + col = col[strings.IndexByte(col, '.')+1:] columns[i] = sql.IndexColumn{Name: col} } idxDefs = append(idxDefs, &sql.IndexDef{ From cc7253806df21817d91c2be33374d9a77a5faf26 Mon Sep 17 00:00:00 2001 From: James Cor Date: Mon, 18 Aug 2025 13:31:30 -0700 Subject: [PATCH 10/12] fix --- sql/memo/rel_props.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sql/memo/rel_props.go b/sql/memo/rel_props.go index 14adbc4e0b..fa4ee0a3e6 100644 --- a/sql/memo/rel_props.go +++ b/sql/memo/rel_props.go @@ -134,11 +134,12 @@ func newRelProps(rel RelExpr) *relProps { // Identifiers are ambiguous. func idxExprsColumns(idx sql.Index) []string { exprs := idx.Expressions() + columns := make([]string, len(exprs)) for i, e := range exprs { - colName := e[strings.IndexByte(e, '.')+1:] - exprs[i] = strings.ToLower(colName) + colName := e[strings.IndexRune(e, '.')+1:] + columns[i] = strings.ToLower(colName) } - return exprs + return columns } func (p *relProps) SetStats(s sql.Statistic) { From a6bee08e7b1af238eea8cb02875ae1d9708cd297 Mon Sep 17 00:00:00 2001 From: James Cor Date: Mon, 18 Aug 2025 15:05:36 -0700 Subject: [PATCH 11/12] reset schema --- sql/plan/project.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/plan/project.go b/sql/plan/project.go index f1ea767a0a..0328b453fd 100644 --- a/sql/plan/project.go +++ b/sql/plan/project.go @@ -194,6 +194,7 @@ func (p *Project) WithChildren(children ...sql.Node) (sql.Node, error) { } np := *p np.Child = children[0] + np.sch = nil return &np, nil } @@ -209,6 +210,7 @@ func (p *Project) WithExpressions(exprs ...sql.Expression) (sql.Node, error) { } np := *p np.Projections = exprs + np.sch = nil return &np, nil } From 86750f301a3aa9fb01d0d6bf1ab60a64c1273fc9 Mon Sep 17 00:00:00 2001 From: James Cor Date: Tue, 19 Aug 2025 10:38:31 -0700 Subject: [PATCH 12/12] unused --- sql/plan/tablealias.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sql/plan/tablealias.go b/sql/plan/tablealias.go index 856a21e188..301cbc09b4 100644 --- a/sql/plan/tablealias.go +++ b/sql/plan/tablealias.go @@ -21,12 +21,11 @@ import ( // TableAlias is a node that acts as a table with a given name. type TableAlias struct { *UnaryNode - name string - comment string - id sql.TableId - cols sql.ColSet - sch sql.Schema - cachedSch bool + name string + comment string + id sql.TableId + cols sql.ColSet + sch sql.Schema } var _ sql.RenameableNode = (*TableAlias)(nil)