From 8d184c8570bc375258258000d2ea9bd0abb9d35a Mon Sep 17 00:00:00 2001 From: Max Hoffman Date: Thu, 26 Dec 2024 10:36:29 -0600 Subject: [PATCH 1/3] [memo] merge joins must use globally sorted indexes --- sql/analyzer/indexed_joins.go | 6 ++++++ sql/memo/memo.go | 13 +++++++++---- sql/memo/rel_props.go | 7 +++++-- sql/memo/rel_props_test.go | 8 ++++---- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/sql/analyzer/indexed_joins.go b/sql/analyzer/indexed_joins.go index 159af7a8a7..f78e132374 100644 --- a/sql/analyzer/indexed_joins.go +++ b/sql/analyzer/indexed_joins.go @@ -1070,6 +1070,12 @@ func addMergeJoins(ctx *sql.Context, m *memo.Memo) error { // Check to see if any rIndexes match that set of filters // Remove the last matched filter for _, lIndex := range lIndexes { + if lIndex.Order() == sql.IndexOrderNone { + // lookups can be unordered, merge indexes need to + // be globally ordered + continue + } + matchedEqFilters := matchedFiltersForLeftIndex(lIndex, join.Left.RelProps.FuncDeps().Constants(), eqFilters) for len(matchedEqFilters) > 0 { for _, rIndex := range rIndexes { diff --git a/sql/memo/memo.go b/sql/memo/memo.go index 061575edb4..b7c813c22a 100644 --- a/sql/memo/memo.go +++ b/sql/memo/memo.go @@ -743,14 +743,15 @@ type SourceRel interface { type Index struct { // ordered list of index columns - order []sql.ColumnId + cols []sql.ColumnId // unordered column set - set sql.ColSet - idx sql.Index + set sql.ColSet + idx sql.Index + order sql.IndexOrder } func (i *Index) Cols() []sql.ColumnId { - return i.order + return i.cols } func (i *Index) ColSet() sql.ColSet { @@ -761,6 +762,10 @@ func (i *Index) SqlIdx() sql.Index { return i.idx } +func (i *Index) Order() sql.IndexOrder { + return i.order +} + type sourceBase struct { *relBase indexes []*Index diff --git a/sql/memo/rel_props.go b/sql/memo/rel_props.go index d8bfbc322b..d901252da0 100644 --- a/sql/memo/rel_props.go +++ b/sql/memo/rel_props.go @@ -226,7 +226,10 @@ func (p *relProps) populateFds() { // strict if primary key or all nonNull and unique columns := idxExprsColumns(idx) strict := true - normIdx := &Index{idx: idx, order: make([]sql.ColumnId, len(columns))} + normIdx := &Index{idx: idx, cols: make([]sql.ColumnId, len(columns))} + if oidx, ok := idx.(sql.OrderedIndex); ok { + normIdx.order = oidx.Order() + } for i, c := range columns { ord := sch.IndexOfColName(strings.ToLower(c)) idOffset := firstCol + sql.ColumnId(ord) @@ -236,7 +239,7 @@ func (p *relProps) populateFds() { p.grp.m.HandleErr(err) } normIdx.set.Add(colId) - normIdx.order[i] = colId + normIdx.cols[i] = colId if !notNull.Contains(colId) { strict = false } diff --git a/sql/memo/rel_props_test.go b/sql/memo/rel_props_test.go index 721caf1b66..8c86a1d6e2 100644 --- a/sql/memo/rel_props_test.go +++ b/sql/memo/rel_props_test.go @@ -39,8 +39,8 @@ func TestPopulateFDs(t *testing.T) { notNull: sql.NewColSet(1, 2, 3), indexes: []*Index{ { - order: []sql.ColumnId{2, 1}, - set: sql.NewColSet(1, 2), + cols: []sql.ColumnId{2, 1}, + set: sql.NewColSet(1, 2), }, }, key: sql.NewColSet(1, 2), @@ -62,8 +62,8 @@ func TestPopulateFDs(t *testing.T) { notNull: sql.NewColSet(1, 2, 3), indexes: []*Index{ { - order: []sql.ColumnId{1, 2, 3}, - set: sql.NewColSet(1, 2, 3), + cols: []sql.ColumnId{1, 2, 3}, + set: sql.NewColSet(1, 2, 3), }, }, key: sql.NewColSet(1, 2, 3), From 32ef0c6e3e73124a555f1f11f588351f24cbfd25 Mon Sep 17 00:00:00 2001 From: Max Hoffman Date: Thu, 26 Dec 2024 12:04:24 -0600 Subject: [PATCH 2/3] explicitly label default unordered --- sql/memo/rel_props.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/memo/rel_props.go b/sql/memo/rel_props.go index d901252da0..6be7c9086e 100644 --- a/sql/memo/rel_props.go +++ b/sql/memo/rel_props.go @@ -226,7 +226,7 @@ func (p *relProps) populateFds() { // strict if primary key or all nonNull and unique columns := idxExprsColumns(idx) strict := true - normIdx := &Index{idx: idx, cols: make([]sql.ColumnId, len(columns))} + normIdx := &Index{idx: idx, cols: make([]sql.ColumnId, len(columns)), order: sql.IndexOrderNone} if oidx, ok := idx.(sql.OrderedIndex); ok { normIdx.order = oidx.Order() } From e01b2a800f4e56046a112e0cd07573b787b3e661 Mon Sep 17 00:00:00 2001 From: Max Hoffman Date: Thu, 26 Dec 2024 12:44:02 -0600 Subject: [PATCH 3/3] second merge join side idx order check --- sql/analyzer/indexed_joins.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sql/analyzer/indexed_joins.go b/sql/analyzer/indexed_joins.go index f78e132374..734fff5689 100644 --- a/sql/analyzer/indexed_joins.go +++ b/sql/analyzer/indexed_joins.go @@ -1079,6 +1079,9 @@ func addMergeJoins(ctx *sql.Context, m *memo.Memo) error { matchedEqFilters := matchedFiltersForLeftIndex(lIndex, join.Left.RelProps.FuncDeps().Constants(), eqFilters) for len(matchedEqFilters) > 0 { for _, rIndex := range rIndexes { + if rIndex.Order() == sql.IndexOrderNone { + continue + } if rightIndexMatchesFilters(rIndex, join.Left.RelProps.FuncDeps().Constants(), matchedEqFilters) { jb := join.Copy() if d, ok := jb.Left.First.(*memo.Distinct); ok && lIndex.SqlIdx().IsUnique() {