diff --git a/pkg/sql/opt/exec/execbuilder/testdata/aggregate b/pkg/sql/opt/exec/execbuilder/testdata/aggregate index 5a659b518b72..9cbef6b46c58 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/aggregate +++ b/pkg/sql/opt/exec/execbuilder/testdata/aggregate @@ -1256,17 +1256,11 @@ INSERT INTO ab VALUES exec-explain SELECT 1 FROM kv GROUP BY kv.*; ---- -render 0 render · · (column5) · - │ 0 · render 0 1 · · - └── group 1 group · · (k, v, w, s) · - │ 1 · aggregate 0 k · · - │ 1 · aggregate 1 v · · - │ 1 · aggregate 2 w · · - │ 1 · aggregate 3 s · · - │ 1 · group by @1-@4 · · - └── scan 2 scan · · (k, v, w, s) · -· 2 · table kv@primary · · -· 2 · spans ALL · · +render 0 render · · (column5) · + │ 0 · render 0 1 · · + └── scan 1 scan · · () · +· 1 · table kv@primary · · +· 1 · spans ALL · · exec SELECT 1 FROM kv GROUP BY kv.*; @@ -1456,15 +1450,11 @@ column3:tuple{int, int} exec-explain SELECT (b, a) FROM ab GROUP BY (b, a) ---- -render 0 render · · (column3) · - │ 0 · render 0 (b, a) · · - └── group 1 group · · (a, b) · - │ 1 · aggregate 0 a · · - │ 1 · aggregate 1 b · · - │ 1 · group by @1-@2 · · - └── scan 2 scan · · (a, b) · -· 2 · table ab@primary · · -· 2 · spans ALL · · +render 0 render · · (column3) · + │ 0 · render 0 (b, a) · · + └── scan 1 scan · · (a, b) · +· 1 · table ab@primary · · +· 1 · spans ALL · · exec rowsort SELECT MIN(y), (b, a) FROM ab, xy GROUP BY (x, (a, b)) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/scan b/pkg/sql/opt/exec/execbuilder/testdata/scan index c3c71a34564a..b775f53997a0 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/scan +++ b/pkg/sql/opt/exec/execbuilder/testdata/scan @@ -13,7 +13,8 @@ SELECT * FROM t.a scan a ├── columns: x:1(int!null) y:2(float) s:3(string) ├── stats: [rows=1000] - └── cost: 1000.00 + ├── cost: 1000.00 + └── keys: (1) exec-explain SELECT * FROM t.a @@ -37,7 +38,8 @@ SELECT s, x FROM t.a scan a ├── columns: s:3(string) x:1(int!null) ├── stats: [rows=1000] - └── cost: 1000.00 + ├── cost: 1000.00 + └── keys: (1) exec-explain SELECT s, x FROM t.a diff --git a/pkg/sql/opt/memo/expr_view.go b/pkg/sql/opt/memo/expr_view.go index ef520cb44d7c..6e85791f1e61 100644 --- a/pkg/sql/opt/memo/expr_view.go +++ b/pkg/sql/opt/memo/expr_view.go @@ -208,6 +208,9 @@ const ( // ExprFmtHideConstraints does not show inferred constraints in the output. ExprFmtHideConstraints + // ExprFmtHideKeys does not show keys in the output. + ExprFmtHideKeys + // ExprFmtHideAll shows only the most basic properties of the expression. ExprFmtHideAll ExprFmtFlags = (1 << iota) - 1 ) @@ -256,7 +259,6 @@ func (ev ExprView) formatRelational(tp treeprinter.Node, flags ExprFmtFlags) { logProps := ev.Logical() tp = tp.Child(buf.String()) - buf.Reset() // If a particular column presentation is required of the expression, then // print columns using that information. @@ -320,6 +322,11 @@ func (ev ExprView) formatRelational(tp treeprinter.Node, flags ExprFmtFlags) { tp.Childf("cost: %.2f", ev.lookupBestExpr().cost) } + // Format weak keys. + if !flags.HasFlags(ExprFmtHideKeys) { + ev.formatWeakKeys(tp) + } + if physProps.Ordering.Defined() { tp.Childf("ordering: %s", physProps.Ordering.String()) } @@ -416,6 +423,23 @@ func (ev ExprView) formatPresentation(tp treeprinter.Node, presentation Presenta tp.Child(buf.String()) } +func (ev ExprView) formatWeakKeys(tp treeprinter.Node) { + var buf bytes.Buffer + rel := ev.Logical().Relational + for i, key := range rel.WeakKeys { + if i != 0 { + buf.WriteRune(' ') + } + if !key.SubsetOf(rel.NotNullCols) { + buf.WriteString("weak") + } + buf.WriteString(key.String()) + } + if buf.Len() != 0 { + tp.Childf("keys: %s", buf.String()) + } +} + // MatchesTupleOfConstants returns true if the expression is a TupleOp with // ConstValue children. func MatchesTupleOfConstants(ev ExprView) bool { diff --git a/pkg/sql/opt/memo/logical_props.go b/pkg/sql/opt/memo/logical_props.go index 42eb5d0d1efb..0fcd4fbfd01f 100644 --- a/pkg/sql/opt/memo/logical_props.go +++ b/pkg/sql/opt/memo/logical_props.go @@ -55,6 +55,28 @@ type RelationalProps struct { // derived from filters that are NULL-intolerant. NotNullCols opt.ColSet + // WeakKeys are the column sets which form weak keys and are subsets of the + // expression's output columns. A weak key set cannot contain any other weak + // key set (it would be redundant). + // + // A column set is a key if no two rows are equal after projection onto that + // set. This definition treats NULL as if were equal to NULL, so two rows + // having duplicate NULL values would *not* qualify as key rows. Therefore, + // in the usual case, the key columns are also not nullable. The simplest + // example of a key is the primary key for a table (recall that all of the + // columns of the primary key are defined to be NOT NULL). + // + // A weak key is similar to a key, with the difference that NULL values are + // treated as *not equal* to other NULL values. Therefore, two rows having + // duplicate NULL values could still qualify as weak key rows. A UNIQUE index + // on a table is a weak key and possibly a key if all of the columns are NOT + // NULL. A weak key is a key if "(WeakKeys[i] & NotNullCols) == WeakKeys[i]". + // + // An empty key is valid (an empty key implies there is at most one row). Note + // that an empty key is always the only key in the set, since it's a subset of + // every other key (i.e. every other key would be redundant). + WeakKeys opt.WeakKeys + // OuterCols is the set of columns that are referenced by variables within // this relational sub-expression, but are not bound within the scope of // the expression. For example: diff --git a/pkg/sql/opt/memo/logical_props_factory.go b/pkg/sql/opt/memo/logical_props_factory.go index c428ba226d84..1b1a38f49423 100644 --- a/pkg/sql/opt/memo/logical_props_factory.go +++ b/pkg/sql/opt/memo/logical_props_factory.go @@ -101,6 +101,10 @@ func (f logicalPropsFactory) constructScanProps(ev ExprView) LogicalProps { } } + // Initialize weak keys from the table schema. + props.Relational.WeakKeys = md.TableWeakKeys(def.Table) + filterWeakKeys(props.Relational) + // TODO: Need actual number of rows. if def.Constraint != nil { props.Relational.Stats.RowCount = 100 @@ -121,11 +125,8 @@ func (f logicalPropsFactory) constructSelectProps(ev ExprView) LogicalProps { inputProps := ev.lookupChildGroup(0).logical.Relational - // Inherit output columns from input. - props.Relational.OutputCols = inputProps.OutputCols - - // Inherit not null columns from input. - props.Relational.NotNullCols = inputProps.NotNullCols + // Inherit input properties as starting point. + *props.Relational = *inputProps // TODO: Need better estimate based on actual filter conditions. props.Relational.Stats.RowCount = inputProps.Stats.RowCount / 10 @@ -141,10 +142,18 @@ func (f logicalPropsFactory) constructProjectProps(ev ExprView) LogicalProps { // Use output columns from projection list. props.Relational.OutputCols = opt.ColListToSet(ev.Child(1).Private().(opt.ColList)) - // Inherit not null columns from input. + // Inherit not null columns from input, but only use those that are also + // output columns. props.Relational.NotNullCols = inputProps.NotNullCols filterNullCols(props.Relational) + // Inherit outer columns from input. + props.Relational.OuterCols = inputProps.OuterCols + + // Inherit weak keys that are composed entirely of output columns. + props.Relational.WeakKeys = inputProps.WeakKeys + filterWeakKeys(props.Relational) + props.Relational.Stats.RowCount = inputProps.Stats.RowCount return props @@ -185,6 +194,9 @@ func (f logicalPropsFactory) constructJoinProps(ev ExprView) LogicalProps { props.Relational.NotNullCols.UnionWith(leftProps.NotNullCols) } + // TODO(andyk): Need to derive weak keys for joins, for example when weak + // keys on both sides are equivalent cols. + // TODO: Need better estimate based on actual on conditions. props.Relational.Stats.RowCount = leftProps.Stats.RowCount * rightProps.Stats.RowCount if ev.Child(2).Operator() != opt.TrueOp { @@ -210,10 +222,25 @@ func (f logicalPropsFactory) constructGroupByProps(ev ExprView) LogicalProps { props.Relational.NotNullCols = inputProps.NotNullCols.Copy() props.Relational.NotNullCols.IntersectionWith(groupingColSet) + // Scalar group by has no grouping columns and always a single row. if groupingColSet.Empty() { - // Scalar group by. + // Any combination of columns is a weak key when there is one row. + props.Relational.WeakKeys = opt.WeakKeys{groupingColSet} props.Relational.Stats.RowCount = 1 } else { + // The grouping columns always form a key because the GroupBy operation + // eliminates all duplicates. The result WeakKeys property either contains + // only the grouping column set, or else it contains one or more weak keys + // that are strict subsets of the grouping column set. This is because + // the grouping column set contains every output column (except aggregate + // columns, which aren't relevant since they're newly synthesized). + if inputProps.WeakKeys.ContainsSubsetOf(groupingColSet) { + props.Relational.WeakKeys = inputProps.WeakKeys + filterWeakKeys(props.Relational) + } else { + props.Relational.WeakKeys = opt.WeakKeys{groupingColSet} + } + // TODO: Need better estimate. props.Relational.Stats.RowCount = inputProps.Stats.RowCount / 10 } @@ -324,11 +351,35 @@ func (f logicalPropsFactory) constructScalarProps(ev ExprView) LogicalProps { return props } -// filterNullCols will ensure that the set of null columns is a subset of the -// output columns. It respects immutability by making a copy of the null -// columns if they need to be updated. +// filterNullCols ensures that the set of null columns is a subset of the output +// columns. It respects immutability by making a copy of the null columns if +// they need to be updated. func filterNullCols(props *RelationalProps) { if !props.NotNullCols.SubsetOf(props.OutputCols) { props.NotNullCols = props.NotNullCols.Intersection(props.OutputCols) } } + +// filterWeakKeys ensures that each weak key is a subset of the output columns. +// It respects immutability by making a copy of the weak keys if they need to be +// updated. +func filterWeakKeys(props *RelationalProps) { + var filtered opt.WeakKeys + for i, weakKey := range props.WeakKeys { + // Discard weak keys that have columns that are not part of the output + // column set. + if !weakKey.SubsetOf(props.OutputCols) { + if filtered == nil { + filtered = make(opt.WeakKeys, i, len(props.WeakKeys)-1) + copy(filtered, props.WeakKeys[:i]) + } + } else { + if filtered != nil { + filtered = append(filtered, weakKey) + } + } + } + if filtered != nil { + props.WeakKeys = filtered + } +} diff --git a/pkg/sql/opt/memo/logical_props_factory_test.go b/pkg/sql/opt/memo/logical_props_factory_test.go index 2a4f3ff4b10e..67ef0a39a44f 100644 --- a/pkg/sql/opt/memo/logical_props_factory_test.go +++ b/pkg/sql/opt/memo/logical_props_factory_test.go @@ -24,7 +24,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/opt/norm" "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" - "github.com/cockroachdb/cockroach/pkg/util/treeprinter" ) func TestLogicalPropsFactory(t *testing.T) { @@ -61,14 +60,14 @@ func TestLogicalJoinProps(t *testing.T) { testLogicalProps(t, f.Metadata(), ev, expected) } - joinFunc(opt.InnerJoinApplyOp, "a.x:1(int!null) a.y:2(int) b.x:3(int!null) b.z:4(int!null)\n") - joinFunc(opt.LeftJoinApplyOp, "a.x:1(int!null) a.y:2(int) b.x:3(int) b.z:4(int)\n") - joinFunc(opt.RightJoinApplyOp, "a.x:1(int) a.y:2(int) b.x:3(int!null) b.z:4(int!null)\n") - joinFunc(opt.FullJoinApplyOp, "a.x:1(int) a.y:2(int) b.x:3(int) b.z:4(int)\n") - joinFunc(opt.SemiJoinOp, "a.x:1(int!null) a.y:2(int)\n") - joinFunc(opt.SemiJoinApplyOp, "a.x:1(int!null) a.y:2(int)\n") - joinFunc(opt.AntiJoinOp, "a.x:1(int!null) a.y:2(int)\n") - joinFunc(opt.AntiJoinApplyOp, "a.x:1(int!null) a.y:2(int)\n") + joinFunc(opt.InnerJoinApplyOp, "a.x:1(int!null) a.y:2(int) b.x:3(int!null) b.z:4(int!null)") + joinFunc(opt.LeftJoinApplyOp, "a.x:1(int!null) a.y:2(int) b.x:3(int) b.z:4(int)") + joinFunc(opt.RightJoinApplyOp, "a.x:1(int) a.y:2(int) b.x:3(int!null) b.z:4(int!null)") + joinFunc(opt.FullJoinApplyOp, "a.x:1(int) a.y:2(int) b.x:3(int) b.z:4(int)") + joinFunc(opt.SemiJoinOp, "a.x:1(int!null) a.y:2(int)") + joinFunc(opt.SemiJoinApplyOp, "a.x:1(int!null) a.y:2(int)") + joinFunc(opt.AntiJoinOp, "a.x:1(int!null) a.y:2(int)") + joinFunc(opt.AntiJoinApplyOp, "a.x:1(int!null) a.y:2(int)") } func constructScanOpDef(md *opt.Metadata, tabID opt.TableID) *memo.ScanOpDef { @@ -81,18 +80,10 @@ func constructScanOpDef(md *opt.Metadata, tabID opt.TableID) *memo.ScanOpDef { func testLogicalProps(t *testing.T, md *opt.Metadata, ev memo.ExprView, expected string) { t.Helper() + actual := ev.String() - logical := ev.Logical() - if logical.Relational == nil { - panic("only relational properties are supported") - } - - tp := treeprinter.New() - logical.FormatColSet(tp, md, "", logical.Relational.OutputCols) - actual := strings.Trim(tp.String(), " ") - - if actual != expected { - t.Fatalf("\nexpected: %s\nactual : %s", expected, actual) + if !strings.Contains(actual, expected) { + t.Fatalf("\nexpected to contain: %s\nactual:\n%s", expected, actual) } } diff --git a/pkg/sql/opt/memo/testdata/logprops/constraints b/pkg/sql/opt/memo/testdata/logprops/constraints index 79ffd9323066..86fda3b929d4 100644 --- a/pkg/sql/opt/memo/testdata/logprops/constraints +++ b/pkg/sql/opt/memo/testdata/logprops/constraints @@ -213,9 +213,11 @@ SELECT * FROM kuv WHERE u > 1::INT select ├── columns: k:1(int!null) u:2(float) v:3(string) ├── stats: [rows=100] + ├── keys: (1) ├── scan kuv │ ├── columns: kuv.k:1(int!null) kuv.u:2(float) kuv.v:3(string) - │ └── stats: [rows=1000] + │ ├── stats: [rows=1000] + │ └── keys: (1) └── filters [type=bool, outer=(2)] └── gt [type=bool, outer=(2)] ├── variable: kuv.u [type=float, outer=(2)] @@ -227,9 +229,11 @@ SELECT * FROM kuv WHERE v <= 'foo' AND v >= 'bar' select ├── columns: k:1(int!null) u:2(float) v:3(string) ├── stats: [rows=100] + ├── keys: (1) ├── scan kuv │ ├── columns: kuv.k:1(int!null) kuv.u:2(float) kuv.v:3(string) - │ └── stats: [rows=1000] + │ ├── stats: [rows=1000] + │ └── keys: (1) └── filters [type=bool, outer=(3), constraints=(/3: [/'bar' - /'foo']; tight)] ├── le [type=bool, outer=(3), constraints=(/3: (/NULL - /'foo']; tight)] │ ├── variable: kuv.v [type=string, outer=(3)] diff --git a/pkg/sql/opt/memo/testdata/logprops/groupby b/pkg/sql/opt/memo/testdata/logprops/groupby index 000904508610..89c203a89115 100644 --- a/pkg/sql/opt/memo/testdata/logprops/groupby +++ b/pkg/sql/opt/memo/testdata/logprops/groupby @@ -1,32 +1,48 @@ exec-ddl -CREATE TABLE a (x INT PRIMARY KEY, y INT, z FLOAT NOT NULL) +CREATE TABLE a (x INT PRIMARY KEY, y INT, z FLOAT NOT NULL, s STRING, UNIQUE (s DESC, z)) ---- TABLE a ├── x int not null ├── y int ├── z float not null - └── INDEX primary - └── x int not null + ├── s string + ├── INDEX primary + │ └── x int not null + └── INDEX secondary + ├── s string desc + ├── z float not null + └── x int not null (storing) build SELECT a.y, SUM(a.z), a.x, False FROM a GROUP BY a.x, a.y ---- project - ├── columns: y:2(int) column4:4(float) x:1(int!null) column5:5(bool) + ├── columns: y:2(int) column5:5(float) x:1(int!null) column6:6(bool) ├── stats: [rows=100] + ├── keys: (1) ├── group-by - │ ├── columns: a.x:1(int!null) a.y:2(int) column4:4(float) + │ ├── columns: a.x:1(int!null) a.y:2(int) column5:5(float) │ ├── grouping columns: a.x:1(int!null) a.y:2(int) │ ├── stats: [rows=100] - │ ├── scan a + │ ├── keys: (1) + │ ├── project │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) - │ │ └── stats: [rows=1000] + │ │ ├── stats: [rows=1000] + │ │ ├── keys: (1) + │ │ ├── scan a + │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ │ │ ├── stats: [rows=1000] + │ │ │ └── keys: (1) weak(3,4) + │ │ └── projections [outer=(1-3)] + │ │ ├── variable: a.x [type=int, outer=(1)] + │ │ ├── variable: a.y [type=int, outer=(2)] + │ │ └── variable: a.z [type=float, outer=(3)] │ └── aggregations [outer=(3)] │ └── function: sum [type=float, outer=(3)] │ └── variable: a.z [type=float, outer=(3)] - └── projections [outer=(1,2,4)] + └── projections [outer=(1,2,5)] ├── variable: a.y [type=int, outer=(2)] - ├── variable: column4 [type=float, outer=(4)] + ├── variable: column5 [type=float, outer=(5)] ├── variable: a.x [type=int, outer=(1)] └── false [type=bool] @@ -35,14 +51,17 @@ build SELECT SUM(a.x), MAX(a.y) FROM a ---- group-by - ├── columns: column4:4(decimal) column5:5(int) + ├── columns: column5:5(decimal) column6:6(int) ├── stats: [rows=1] + ├── keys: () ├── project │ ├── columns: a.x:1(int!null) a.y:2(int) │ ├── stats: [rows=1000] + │ ├── keys: (1) │ ├── scan a - │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) - │ │ └── stats: [rows=1000] + │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ │ ├── stats: [rows=1000] + │ │ └── keys: (1) weak(3,4) │ └── projections [outer=(1,2)] │ ├── variable: a.x [type=int, outer=(1)] │ └── variable: a.y [type=int, outer=(2)] @@ -51,3 +70,103 @@ group-by │ └── variable: a.x [type=int, outer=(1)] └── function: max [type=int, outer=(2)] └── variable: a.y [type=int, outer=(2)] + +# Group by unique index columns. +build +SELECT s FROM a GROUP BY z, s +---- +project + ├── columns: s:4(string) + ├── stats: [rows=100] + ├── group-by + │ ├── columns: a.z:3(float!null) a.s:4(string) + │ ├── grouping columns: a.z:3(float!null) a.s:4(string) + │ ├── stats: [rows=100] + │ ├── keys: weak(3,4) + │ ├── project + │ │ ├── columns: a.z:3(float!null) a.s:4(string) + │ │ ├── stats: [rows=1000] + │ │ ├── keys: weak(3,4) + │ │ ├── scan a + │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ │ │ ├── stats: [rows=1000] + │ │ │ └── keys: (1) weak(3,4) + │ │ └── projections [outer=(3,4)] + │ │ ├── variable: a.z [type=float, outer=(3)] + │ │ └── variable: a.s [type=string, outer=(4)] + │ └── aggregations + └── projections [outer=(4)] + └── variable: a.s [type=string, outer=(4)] + +# Group by columns that otherwise wouldn't be weak key. +build +SELECT y, SUM(z) FROM a GROUP BY z, y +---- +project + ├── columns: y:2(int) column5:5(float) + ├── stats: [rows=100] + ├── group-by + │ ├── columns: a.y:2(int) a.z:3(float!null) column5:5(float) + │ ├── grouping columns: a.y:2(int) a.z:3(float!null) + │ ├── stats: [rows=100] + │ ├── keys: weak(2,3) + │ ├── project + │ │ ├── columns: a.z:3(float!null) a.y:2(int) + │ │ ├── stats: [rows=1000] + │ │ ├── scan a + │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ │ │ ├── stats: [rows=1000] + │ │ │ └── keys: (1) weak(3,4) + │ │ └── projections [outer=(2,3)] + │ │ ├── variable: a.z [type=float, outer=(3)] + │ │ └── variable: a.y [type=int, outer=(2)] + │ └── aggregations [outer=(3)] + │ └── function: sum [type=float, outer=(3)] + │ └── variable: a.z [type=float, outer=(3)] + └── projections [outer=(2,5)] + ├── variable: a.y [type=int, outer=(2)] + └── variable: column5 [type=float, outer=(5)] + +# Group by column that is subset of unique index. +build +SELECT z, MAX(s) FROM a GROUP BY z +---- +group-by + ├── columns: z:3(float!null) column5:5(string) + ├── grouping columns: a.z:3(float!null) + ├── stats: [rows=100] + ├── keys: (3) + ├── project + │ ├── columns: a.z:3(float!null) a.s:4(string) + │ ├── stats: [rows=1000] + │ ├── keys: weak(3,4) + │ ├── scan a + │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ │ ├── stats: [rows=1000] + │ │ └── keys: (1) weak(3,4) + │ └── projections [outer=(3,4)] + │ ├── variable: a.z [type=float, outer=(3)] + │ └── variable: a.s [type=string, outer=(4)] + └── aggregations [outer=(4)] + └── function: max [type=string, outer=(4)] + └── variable: a.s [type=string, outer=(4)] + +# Group by all columns. +build +SELECT s FROM a GROUP BY a.* +---- +project + ├── columns: s:4(string) + ├── stats: [rows=100] + ├── group-by + │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ ├── grouping columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ ├── stats: [rows=100] + │ ├── keys: (1) weak(3,4) + │ ├── scan a + │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ │ ├── stats: [rows=1000] + │ │ └── keys: (1) weak(3,4) + │ └── aggregations + └── projections [outer=(4)] + └── variable: a.s [type=string, outer=(4)] diff --git a/pkg/sql/opt/memo/testdata/logprops/join b/pkg/sql/opt/memo/testdata/logprops/join index b6d73281a424..4babb834700f 100644 --- a/pkg/sql/opt/memo/testdata/logprops/join +++ b/pkg/sql/opt/memo/testdata/logprops/join @@ -1,11 +1,17 @@ exec-ddl -CREATE TABLE a (x INT PRIMARY KEY, y INT) +CREATE TABLE a (x INT PRIMARY KEY, y INT, s STRING, d DECIMAL NOT NULL, UNIQUE (s DESC, d)) ---- TABLE a ├── x int not null ├── y int - └── INDEX primary - └── x int not null + ├── s string + ├── d decimal not null + ├── INDEX primary + │ └── x int not null + └── INDEX secondary + ├── s string desc + ├── d decimal not null + └── x int not null (storing) exec-ddl CREATE TABLE b (x INT, z INT NOT NULL) @@ -21,84 +27,112 @@ build SELECT *, rowid FROM a INNER JOIN b ON a.x=b.x ---- inner-join - ├── columns: x:1(int!null) y:2(int) x:3(int) z:4(int!null) rowid:5(int!null) + ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) x:5(int) z:6(int!null) rowid:7(int!null) ├── stats: [rows=100000] ├── scan a - │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:3(string) a.d:4(decimal!null) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) ├── scan b - │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ └── stats: [rows=1000] - └── eq [type=bool, outer=(1,3)] + │ ├── columns: b.x:5(int) b.z:6(int!null) b.rowid:7(int!null) + │ ├── stats: [rows=1000] + │ └── keys: (7) + └── eq [type=bool, outer=(1,5)] ├── variable: a.x [type=int, outer=(1)] - └── variable: b.x [type=int, outer=(3)] + └── variable: b.x [type=int, outer=(5)] build SELECT *, rowid FROM a LEFT JOIN b ON a.x=b.x ---- left-join - ├── columns: x:1(int!null) y:2(int) x:3(int) z:4(int) rowid:5(int) + ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) x:5(int) z:6(int) rowid:7(int) ├── stats: [rows=100000] ├── scan a - │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:3(string) a.d:4(decimal!null) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) ├── scan b - │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ └── stats: [rows=1000] - └── eq [type=bool, outer=(1,3)] + │ ├── columns: b.x:5(int) b.z:6(int!null) b.rowid:7(int!null) + │ ├── stats: [rows=1000] + │ └── keys: (7) + └── eq [type=bool, outer=(1,5)] ├── variable: a.x [type=int, outer=(1)] - └── variable: b.x [type=int, outer=(3)] + └── variable: b.x [type=int, outer=(5)] build SELECT *, rowid FROM a RIGHT JOIN b ON a.x=b.x ---- right-join - ├── columns: x:1(int) y:2(int) x:3(int) z:4(int!null) rowid:5(int!null) + ├── columns: x:1(int) y:2(int) s:3(string) d:4(decimal) x:5(int) z:6(int!null) rowid:7(int!null) ├── stats: [rows=100000] ├── scan a - │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:3(string) a.d:4(decimal!null) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) ├── scan b - │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ └── stats: [rows=1000] - └── eq [type=bool, outer=(1,3)] + │ ├── columns: b.x:5(int) b.z:6(int!null) b.rowid:7(int!null) + │ ├── stats: [rows=1000] + │ └── keys: (7) + └── eq [type=bool, outer=(1,5)] ├── variable: a.x [type=int, outer=(1)] - └── variable: b.x [type=int, outer=(3)] + └── variable: b.x [type=int, outer=(5)] build SELECT *, rowid FROM a FULL JOIN b ON a.x=b.x ---- full-join - ├── columns: x:1(int) y:2(int) x:3(int) z:4(int) rowid:5(int) + ├── columns: x:1(int) y:2(int) s:3(string) d:4(decimal) x:5(int) z:6(int) rowid:7(int) ├── stats: [rows=100000] ├── scan a - │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:3(string) a.d:4(decimal!null) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) ├── scan b - │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ └── stats: [rows=1000] - └── eq [type=bool, outer=(1,3)] + │ ├── columns: b.x:5(int) b.z:6(int!null) b.rowid:7(int!null) + │ ├── stats: [rows=1000] + │ └── keys: (7) + └── eq [type=bool, outer=(1,5)] ├── variable: a.x [type=int, outer=(1)] - └── variable: b.x [type=int, outer=(3)] + └── variable: b.x [type=int, outer=(5)] build SELECT * FROM a, b ---- project - ├── columns: x:1(int!null) y:2(int) x:3(int) z:4(int!null) + ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) x:5(int) z:6(int!null) ├── stats: [rows=1000000] ├── inner-join - │ ├── columns: a.x:1(int!null) a.y:2(int) b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) + │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:3(string) a.d:4(decimal!null) b.x:5(int) b.z:6(int!null) b.rowid:7(int!null) │ ├── stats: [rows=1000000] │ ├── scan a - │ │ ├── columns: a.x:1(int!null) a.y:2(int) - │ │ └── stats: [rows=1000] + │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:3(string) a.d:4(decimal!null) + │ │ ├── stats: [rows=1000] + │ │ └── keys: (1) weak(3,4) │ ├── scan b - │ │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ │ └── stats: [rows=1000] + │ │ ├── columns: b.x:5(int) b.z:6(int!null) b.rowid:7(int!null) + │ │ ├── stats: [rows=1000] + │ │ └── keys: (7) │ └── true [type=bool] - └── projections [outer=(1-4)] + └── projections [outer=(1-6)] ├── variable: a.x [type=int, outer=(1)] ├── variable: a.y [type=int, outer=(2)] - ├── variable: b.x [type=int, outer=(3)] - └── variable: b.z [type=int, outer=(4)] + ├── variable: a.s [type=string, outer=(3)] + ├── variable: a.d [type=decimal, outer=(4)] + ├── variable: b.x [type=int, outer=(5)] + └── variable: b.z [type=int, outer=(6)] + +build +SELECT * FROM a, a +---- +inner-join + ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) x:5(int!null) y:6(int) s:7(string) d:8(decimal!null) + ├── stats: [rows=1000000] + ├── scan a + │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:3(string) a.d:4(decimal!null) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) + ├── scan a + │ ├── columns: a.x:5(int!null) a.y:6(int) a.s:7(string) a.d:8(decimal!null) + │ ├── stats: [rows=1000] + │ └── keys: (5) weak(7,8) + └── true [type=bool] diff --git a/pkg/sql/opt/memo/testdata/logprops/limit b/pkg/sql/opt/memo/testdata/logprops/limit index 68ab49036f98..169c8b07bebf 100644 --- a/pkg/sql/opt/memo/testdata/logprops/limit +++ b/pkg/sql/opt/memo/testdata/logprops/limit @@ -1,53 +1,65 @@ exec-ddl -CREATE TABLE a (x INT PRIMARY KEY, y INT) +CREATE TABLE a (x INT PRIMARY KEY, y INT, z FLOAT NOT NULL, s STRING, UNIQUE (s DESC, z)) ---- TABLE a ├── x int not null ├── y int - └── INDEX primary - └── x int not null + ├── z float not null + ├── s string + ├── INDEX primary + │ └── x int not null + └── INDEX secondary + ├── s string desc + ├── z float not null + └── x int not null (storing) build SELECT * FROM a LIMIT 1 ---- limit - ├── columns: x:1(int!null) y:2(int) + ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) ├── stats: [rows=1] + ├── keys: (1) weak(3,4) ├── scan a - │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) └── const: 1 [type=int] build SELECT * FROM a LIMIT (SELECT 1) ---- limit - ├── columns: x:1(int!null) y:2(int) + ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) ├── stats: [rows=1000] + ├── keys: (1) weak(3,4) ├── scan a - │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] - └── subquery [type=int, outer=(3)] + │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) + └── subquery [type=int, outer=(5)] ├── max1-row - │ ├── columns: column3:3(int) + │ ├── columns: column5:5(int) │ ├── stats: [rows=1] │ └── project - │ ├── columns: column3:3(int) + │ ├── columns: column5:5(int) │ ├── stats: [rows=1] │ ├── values │ │ ├── stats: [rows=1] │ │ └── tuple [type=tuple{}] │ └── projections │ └── const: 1 [type=int] - └── variable: column3 [type=int, outer=(3)] + └── variable: column5 [type=int, outer=(5)] build SELECT * FROM a LIMIT 0 ---- limit - ├── columns: x:1(int!null) y:2(int) + ├── columns: x:1(int!null) y:2(int) z:3(float!null) s:4(string) ├── stats: [rows=1000] + ├── keys: (1) weak(3,4) ├── scan a - │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── columns: a.x:1(int!null) a.y:2(int) a.z:3(float!null) a.s:4(string) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) └── const: 0 [type=int] diff --git a/pkg/sql/opt/memo/testdata/logprops/project b/pkg/sql/opt/memo/testdata/logprops/project index adc8dae43016..87a2f89626a5 100644 --- a/pkg/sql/opt/memo/testdata/logprops/project +++ b/pkg/sql/opt/memo/testdata/logprops/project @@ -1,21 +1,29 @@ exec-ddl -CREATE TABLE a (x INT PRIMARY KEY, y INT) +CREATE TABLE a (x INT PRIMARY KEY, y INT, s STRING, d DECIMAL NOT NULL, UNIQUE (s DESC, d)) ---- TABLE a ├── x int not null ├── y int - └── INDEX primary - └── x int not null + ├── s string + ├── d decimal not null + ├── INDEX primary + │ └── x int not null + └── INDEX secondary + ├── s string desc + ├── d decimal not null + └── x int not null (storing) build SELECT a.y, a.x+1, 1, a.x FROM a ---- project - ├── columns: y:2(int) column3:3(int) column4:4(int) x:1(int!null) + ├── columns: y:2(int) column5:5(int) column6:6(int) x:1(int!null) ├── stats: [rows=1000] + ├── keys: (1) ├── scan a - │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:3(string) a.d:4(decimal!null) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) └── projections [outer=(1,2)] ├── variable: a.y [type=int, outer=(2)] ├── plus [type=int, outer=(1)] @@ -23,3 +31,16 @@ project │ └── const: 1 [type=int] ├── const: 1 [type=int] └── variable: a.x [type=int, outer=(1)] + +build +SELECT s FROM a +---- +project + ├── columns: s:3(string) + ├── stats: [rows=1000] + ├── scan a + │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:3(string) a.d:4(decimal!null) + │ ├── stats: [rows=1000] + │ └── keys: (1) weak(3,4) + └── projections [outer=(3)] + └── variable: a.s [type=string, outer=(3)] diff --git a/pkg/sql/opt/memo/testdata/logprops/scalar b/pkg/sql/opt/memo/testdata/logprops/scalar index 45076fe49776..33e592df98f9 100644 --- a/pkg/sql/opt/memo/testdata/logprops/scalar +++ b/pkg/sql/opt/memo/testdata/logprops/scalar @@ -23,9 +23,11 @@ SELECT * FROM a WHERE x < 5 select ├── columns: x:1(int!null) y:2(int) ├── stats: [rows=100] + ├── keys: (1) ├── scan a │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── stats: [rows=1000] + │ └── keys: (1) └── lt [type=bool, outer=(1), constraints=(/1: (/NULL - /4]; tight)] ├── variable: a.x [type=int, outer=(1)] └── const: 5 [type=int] @@ -41,10 +43,12 @@ project │ ├── stats: [rows=1000000] │ ├── scan a │ │ ├── columns: a.x:1(int!null) a.y:2(int) - │ │ └── stats: [rows=1000] + │ │ ├── stats: [rows=1000] + │ │ └── keys: (1) │ ├── scan b │ │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ │ └── stats: [rows=1000] + │ │ ├── stats: [rows=1000] + │ │ └── keys: (5) │ └── true [type=bool] └── projections [outer=(1,2,5)] ├── eq [type=bool, outer=(1,2)] diff --git a/pkg/sql/opt/memo/testdata/logprops/scan b/pkg/sql/opt/memo/testdata/logprops/scan index bd9152265b0f..4d0f44793583 100644 --- a/pkg/sql/opt/memo/testdata/logprops/scan +++ b/pkg/sql/opt/memo/testdata/logprops/scan @@ -1,13 +1,17 @@ exec-ddl -CREATE TABLE a (x INT PRIMARY KEY, y INT, s STRING, d DECIMAL NOT NULL) +CREATE TABLE a (x INT PRIMARY KEY, y INT, s STRING, d DECIMAL NOT NULL, UNIQUE (s DESC, d)) ---- TABLE a ├── x int not null ├── y int ├── s string ├── d decimal not null - └── INDEX primary - └── x int not null + ├── INDEX primary + │ └── x int not null + └── INDEX secondary + ├── s string desc + ├── d decimal not null + └── x int not null (storing) exec-ddl CREATE TABLE b (x INT, z INT NOT NULL) @@ -24,7 +28,8 @@ SELECT * FROM a ---- scan a ├── columns: x:1(int!null) y:2(int) s:3(string) d:4(decimal!null) - └── stats: [rows=1000] + ├── stats: [rows=1000] + └── keys: (1) weak(3,4) build SELECT * FROM b @@ -34,7 +39,8 @@ project ├── stats: [rows=1000] ├── scan b │ ├── columns: b.x:1(int) b.z:2(int!null) b.rowid:3(int!null) - │ └── stats: [rows=1000] + │ ├── stats: [rows=1000] + │ └── keys: (3) └── projections [outer=(1,2)] ├── variable: b.x [type=int, outer=(1)] └── variable: b.z [type=int, outer=(2)] @@ -45,18 +51,20 @@ SELECT s, x FROM a ---- scan a ├── columns: s:3(string) x:1(int!null) - └── stats: [rows=1000] + ├── stats: [rows=1000] + └── keys: (1) -# Test constrained span. +# Test constrained scan. opt SELECT s, x FROM a WHERE x=1 ---- scan a ├── columns: s:3(string) x:1(int!null) ├── constraint: /1: [/1 - /1] - └── stats: [rows=100] + ├── stats: [rows=100] + └── keys: (1) -# Test limited span. +# Test limited scan. opt SELECT s, x FROM a WHERE x=1 LIMIT 2 ---- @@ -64,4 +72,13 @@ scan a ├── columns: s:3(string) x:1(int!null) ├── constraint: /1: [/1 - /1] ├── limit: 2 - └── stats: [rows=2] + ├── stats: [rows=2] + └── keys: (1) + +# Test case where there are no weak keys available. +opt +SELECT d FROM a +---- +scan a + ├── columns: d:4(decimal!null) + └── stats: [rows=1000] diff --git a/pkg/sql/opt/memo/testdata/logprops/select b/pkg/sql/opt/memo/testdata/logprops/select index d0235d802076..96568cabab2d 100644 --- a/pkg/sql/opt/memo/testdata/logprops/select +++ b/pkg/sql/opt/memo/testdata/logprops/select @@ -23,9 +23,11 @@ SELECT * FROM a WHERE x=1 select ├── columns: x:1(int!null) y:2(int) ├── stats: [rows=100] + ├── keys: (1) ├── scan a │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── stats: [rows=1000] + │ └── keys: (1) └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight)] ├── variable: a.x [type=int, outer=(1)] └── const: 1 [type=int] @@ -44,10 +46,12 @@ project │ │ ├── stats: [rows=1000000] │ │ ├── scan a │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) - │ │ │ └── stats: [rows=1000] + │ │ │ ├── stats: [rows=1000] + │ │ │ └── keys: (1) │ │ ├── scan b │ │ │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ │ │ └── stats: [rows=1000] + │ │ │ ├── stats: [rows=1000] + │ │ │ └── keys: (5) │ │ └── true [type=bool] │ └── eq [type=bool, outer=(1,3)] │ ├── variable: a.x [type=int, outer=(1)] diff --git a/pkg/sql/opt/memo/testdata/logprops/set b/pkg/sql/opt/memo/testdata/logprops/set index 0f816019adb2..cfdcdf9fabef 100644 --- a/pkg/sql/opt/memo/testdata/logprops/set +++ b/pkg/sql/opt/memo/testdata/logprops/set @@ -27,13 +27,15 @@ union ├── stats: [rows=2000] ├── scan a │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── stats: [rows=1000] + │ ├── stats: [rows=1000] + │ └── keys: (1) └── project ├── columns: b.x:3(int) b.z:4(int!null) ├── stats: [rows=1000] ├── scan b │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ └── stats: [rows=1000] + │ ├── stats: [rows=1000] + │ └── keys: (5) └── projections [outer=(3,4)] ├── variable: b.x [type=int, outer=(3)] └── variable: b.z [type=int, outer=(4)] @@ -49,21 +51,26 @@ intersect ├── project │ ├── columns: a.x:1(int!null) a.y:2(int) │ ├── stats: [rows=1000] + │ ├── keys: (1) │ ├── scan a │ │ ├── columns: a.x:1(int!null) a.y:2(int) - │ │ └── stats: [rows=1000] + │ │ ├── stats: [rows=1000] + │ │ └── keys: (1) │ └── projections [outer=(1,2)] │ ├── variable: a.x [type=int, outer=(1)] │ └── variable: a.y [type=int, outer=(2)] └── project ├── columns: b.z:4(int!null) b.x:3(int) b.rowid:5(int!null) ├── stats: [rows=100] + ├── keys: (5) ├── select │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) │ ├── stats: [rows=100] + │ ├── keys: (5) │ ├── scan b │ │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ │ └── stats: [rows=1000] + │ │ ├── stats: [rows=1000] + │ │ └── keys: (5) │ └── eq [type=bool, outer=(3), constraints=(/3: [/1 - /1]; tight)] │ ├── variable: b.x [type=int, outer=(3)] │ └── const: 1 [type=int] @@ -83,9 +90,11 @@ except ├── project │ ├── columns: a.x:1(int!null) a.y:2(int) │ ├── stats: [rows=1000] + │ ├── keys: (1) │ ├── scan a │ │ ├── columns: a.x:1(int!null) a.y:2(int) - │ │ └── stats: [rows=1000] + │ │ ├── stats: [rows=1000] + │ │ └── keys: (1) │ └── projections [outer=(1,2)] │ ├── variable: a.x [type=int, outer=(1)] │ └── variable: a.y [type=int, outer=(2)] @@ -98,9 +107,11 @@ except │ ├── select │ │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) │ │ ├── stats: [rows=100] + │ │ ├── keys: (5) │ │ ├── scan b │ │ │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null) - │ │ │ └── stats: [rows=1000] + │ │ │ ├── stats: [rows=1000] + │ │ │ └── keys: (5) │ │ └── eq [type=bool, outer=(3), constraints=(/3: [/1 - /1]; tight)] │ │ ├── variable: b.x [type=int, outer=(3)] │ │ └── const: 1 [type=int] diff --git a/pkg/sql/opt/memo/testdata/memo b/pkg/sql/opt/memo/testdata/memo index 1c017bce90fc..008a7d94e111 100644 --- a/pkg/sql/opt/memo/testdata/memo +++ b/pkg/sql/opt/memo/testdata/memo @@ -48,11 +48,13 @@ limit │ │ │ ├── scan a │ │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) │ │ │ │ ├── stats: [rows=1000] - │ │ │ │ └── cost: 1000.00 + │ │ │ │ ├── cost: 1000.00 + │ │ │ │ └── keys: (1) │ │ │ ├── scan b │ │ │ │ ├── columns: b.x:3(string!null) b.z:4(decimal!null) │ │ │ │ ├── stats: [rows=1000] - │ │ │ │ └── cost: 1000.00 + │ │ │ │ ├── cost: 1000.00 + │ │ │ │ └── keys: (3) │ │ │ └── true [type=bool] │ │ └── and [type=bool, outer=(1-3), constraints=(/2: [/1 - /1])] │ │ ├── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight)] @@ -104,10 +106,12 @@ project │ │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) │ │ │ │ ├── stats: [rows=100] │ │ │ │ ├── cost: 1100.00 + │ │ │ │ ├── keys: (1) │ │ │ │ ├── scan a │ │ │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) │ │ │ │ │ ├── stats: [rows=1000] - │ │ │ │ │ └── cost: 1000.00 + │ │ │ │ │ ├── cost: 1000.00 + │ │ │ │ │ └── keys: (1) │ │ │ │ └── filters [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight)] │ │ │ │ └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight)] │ │ │ │ ├── variable: a.y [type=int, outer=(2)] @@ -115,7 +119,8 @@ project │ │ │ ├── scan b │ │ │ │ ├── columns: b.x:3(string!null) │ │ │ │ ├── stats: [rows=1000] - │ │ │ │ └── cost: 1000.00 + │ │ │ │ ├── cost: 1000.00 + │ │ │ │ └── keys: (3) │ │ │ └── filters [type=bool, outer=(1,3)] │ │ │ └── eq [type=bool, outer=(1,3)] │ │ │ ├── variable: b.x [type=string, outer=(3)] diff --git a/pkg/sql/opt/metadata.go b/pkg/sql/opt/metadata.go index b4bf9d9d3285..592775a654f5 100644 --- a/pkg/sql/opt/metadata.go +++ b/pkg/sql/opt/metadata.go @@ -55,17 +55,27 @@ type TableID int32 type Metadata struct { // cols stores information about each metadata column, indexed by ColumnID. // Skip id 0 so that it is reserved for "unknown column". - cols []mdCol + cols []mdColumn // tables maps from table id to the catalog metadata for the table. The // table id is the id of the first column in the table. The remaining // columns form a contiguous group following that id. - tables map[TableID]Table + tables map[TableID]mdTable } -// mdCol stores information about one of the columns stored in the metadata, +// mdTable stores information about one of the tables stored in the metadata. +type mdTable struct { + // tab is a reference to the table in the catalog. + tab Table + + // weakKeys is a cache of the weak key column combinations on this table. + // See RelationalProps.WeakKeys for more details. + weakKeys WeakKeys +} + +// mdColumn stores information about one of the columns stored in the metadata, // including its label and type. -type mdCol struct { +type mdColumn struct { // label is the best-effort name of this column. Since the same column can // have multiple labels (using aliasing), one of those is chosen to be used // for pretty-printing and debugging. This might be different than what is @@ -78,14 +88,14 @@ type mdCol struct { // NewMetadata constructs a new instance of metadata for the optimizer. func NewMetadata() *Metadata { - // Skip mdCol index 0 so that it is reserved for "unknown column". - return &Metadata{cols: make([]mdCol, 1)} + // Skip mdColumn index 0 so that it is reserved for "unknown column". + return &Metadata{cols: make([]mdColumn, 1)} } // AddColumn assigns a new unique id to a column within the query and records // its label and type. func (md *Metadata) AddColumn(label string, typ types.T) ColumnID { - md.cols = append(md.cols, mdCol{label: label, typ: typ}) + md.cols = append(md.cols, mdColumn{label: label, typ: typ}) return ColumnID(len(md.cols) - 1) } @@ -130,17 +140,17 @@ func (md *Metadata) AddTable(tab Table) TableID { } if md.tables == nil { - md.tables = make(map[TableID]Table) + md.tables = make(map[TableID]mdTable) } - md.tables[tabID] = tab + md.tables[tabID] = mdTable{tab: tab} return tabID } // Table looks up the catalog table associated with the given metadata id. The // same table can be associated with multiple metadata ids. func (md *Metadata) Table(tabID TableID) Table { - return md.tables[tabID] + return md.tables[tabID].tab } // TableColumn returns the metadata id of the column at the given ordinal @@ -148,3 +158,23 @@ func (md *Metadata) Table(tabID TableID) Table { func (md *Metadata) TableColumn(tabID TableID, ord int) ColumnID { return ColumnID(int(tabID) + ord) } + +// TableWeakKeys returns the weak key column combinations on the given table. +// Weak keys are derived lazily and are cached in the metadata, since they may +// be accessed multiple times during query optimization. For more details, see +// RelationalProps.WeakKeys. +func (md *Metadata) TableWeakKeys(tabID TableID) WeakKeys { + mdTab := md.tables[tabID] + if mdTab.weakKeys == nil { + mdTab.weakKeys = make(WeakKeys, 0, mdTab.tab.IndexCount()) + for idx := 0; idx < mdTab.tab.IndexCount(); idx++ { + var cs ColSet + index := mdTab.tab.Index(idx) + for col := 0; col < index.UniqueColumnCount(); col++ { + cs.Add(int(md.TableColumn(tabID, index.Column(col).Ordinal))) + } + mdTab.weakKeys.Add(cs) + } + } + return mdTab.weakKeys +} diff --git a/pkg/sql/opt/metadata_column.go b/pkg/sql/opt/metadata_column.go index 6467ff13a995..185e400b5e97 100644 --- a/pkg/sql/opt/metadata_column.go +++ b/pkg/sql/opt/metadata_column.go @@ -82,3 +82,45 @@ func ColListToSet(colList ColList) ColSet { } return r } + +// WeakKeys are combinations of columns that form a weak key. No two non-null +// rows are equal if they contain columns from a weak key. For more details, see +// LogicalProps.WeakKeys. +type WeakKeys []ColSet + +// ContainsSubsetOf returns true if the weak key list contains a key that is a +// subset of the given key. In that case, there's no reason to add the key to +// the list, since it's redundant. +func (wk *WeakKeys) ContainsSubsetOf(weakKey ColSet) bool { + for _, existing := range *wk { + if existing.SubsetOf(weakKey) { + return true + } + } + return false +} + +// Add appends a new weak key to the list of weak keys. It also ensures that no +// weak key is a superset of another, since that is a redundant weak key. +func (wk *WeakKeys) Add(new ColSet) { + // If one weak key is a subset of another, then use that, since the + // longer key is redundant. + insert := 0 + for i, existing := range *wk { + // If new key is redundant, don't add it. + if existing.SubsetOf(new) { + return + } + + // If existing key is redundant, then remove it from the list. Since + // there may be multiple redundant keys, wait until after looping to + // remove all at once. + if !new.SubsetOf(existing) { + if insert != i { + (*wk)[insert] = existing + } + insert++ + } + } + *wk = append((*wk)[:insert], new) +} diff --git a/pkg/sql/opt/metadata_test.go b/pkg/sql/opt/metadata_test.go index 36898334ecda..1c452ca85938 100644 --- a/pkg/sql/opt/metadata_test.go +++ b/pkg/sql/opt/metadata_test.go @@ -15,11 +15,13 @@ package opt_test import ( + "fmt" "testing" "github.com/cockroachdb/cockroach/pkg/sql/opt" "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils" "github.com/cockroachdb/cockroach/pkg/sql/sem/types" + "github.com/cockroachdb/cockroach/pkg/util" ) func TestMetadataColumns(t *testing.T) { @@ -109,3 +111,68 @@ func TestMetadataTables(t *testing.T) { t.Fatalf("unexpected column label: %s", label) } } + +func TestMetadataWeakKeys(t *testing.T) { + test := func(weakKeys opt.WeakKeys, expected string) { + t.Helper() + actual := fmt.Sprintf("%v", weakKeys) + if actual != expected { + t.Errorf("expected: %s, actual: %s", expected, actual) + } + } + + testContains := func(weakKeys opt.WeakKeys, cs opt.ColSet, expected bool) { + t.Helper() + actual := weakKeys.ContainsSubsetOf(cs) + if actual != expected { + t.Errorf("expected: %v, actual: %v", expected, actual) + } + } + + md := opt.NewMetadata() + + // Create table with the following interesting indexes: + // 1. Primary key index with multiple columns. + // 2. Single column index. + // 3. Storing values (should have no impact). + // 4. Non-unique index (should always be superset of primary key). + // 5. Unique index that has subset of cols of another unique index, but + // which is defined afterwards (triggers removal of previous weak key). + cat := testutils.NewTestCatalog() + testutils.ExecuteTestDDL(t, + "CREATE TABLE a ("+ + "k INT, "+ + "i INT, "+ + "d DECIMAL, "+ + "f FLOAT, "+ + "s STRING, "+ + "PRIMARY KEY (k, i), "+ + "UNIQUE INDEX (f) STORING (s, i),"+ + "UNIQUE INDEX (d DESC, i, s),"+ + "UNIQUE INDEX (d, i DESC) STORING (f),"+ + "INDEX (s DESC, i))", + cat) + a := md.AddTable(cat.Table("a")) + + wk := md.TableWeakKeys(a) + test(wk, "[(1,2) (4) (2,3)]") + + // Test ContainsSubsetOf method. + testContains(wk, util.MakeFastIntSet(1, 2), true) + testContains(wk, util.MakeFastIntSet(1, 2, 3), true) + testContains(wk, util.MakeFastIntSet(4), true) + testContains(wk, util.MakeFastIntSet(4, 3, 2, 1), true) + testContains(wk, util.MakeFastIntSet(1), false) + testContains(wk, util.MakeFastIntSet(1, 3), false) + testContains(wk, util.MakeFastIntSet(5), false) + + // Add additional weak keys to additionally verify Add method. + wk.Add(util.MakeFastIntSet(1, 2, 3)) + test(wk, "[(1,2) (4) (2,3)]") + + wk.Add(util.MakeFastIntSet(2, 1)) + test(wk, "[(1,2) (4) (2,3)]") + + wk.Add(util.MakeFastIntSet(2)) + test(wk, "[(4) (2)]") +} diff --git a/pkg/sql/opt/norm/factory.go b/pkg/sql/opt/norm/factory.go index 926228ebbcf0..51772eb79735 100644 --- a/pkg/sql/opt/norm/factory.go +++ b/pkg/sql/opt/norm/factory.go @@ -350,6 +350,11 @@ func (f *Factory) onlyConstants(group memo.GroupID) bool { return f.lookupScalar(group).OuterCols.Empty() } +// hasNoCols returns true if the group has zero output columns. +func (f *Factory) hasNoCols(group memo.GroupID) bool { + return f.outputCols(group).Empty() +} + // hasSameCols returns true if the two groups have an identical set of output // columns. func (f *Factory) hasSameCols(left, right memo.GroupID) bool { @@ -683,6 +688,27 @@ func (f *Factory) concatFilters(left, right memo.GroupID) memo.GroupID { return f.ConstructFilters(f.InternList(conditions)) } +// ---------------------------------------------------------------------- +// +// GroupBy Rules +// Custom match and replace functions used with groupby.opt rules. +// +// ---------------------------------------------------------------------- + +// colsAreKey returns true if the given columns form a strong key for the output +// rows of the given group. A strong key means that the set of given column +// values are unique and not null. +func (f *Factory) colsAreKey(cols memo.PrivateID, group memo.GroupID) bool { + colSet := f.mem.LookupPrivate(cols).(opt.ColSet) + props := f.lookupLogical(group).Relational + for _, weakKey := range props.WeakKeys { + if weakKey.SubsetOf(colSet) && weakKey.SubsetOf(props.NotNullCols) { + return true + } + } + return false +} + // ---------------------------------------------------------------------- // // Boolean Rules diff --git a/pkg/sql/opt/norm/factory.og.go b/pkg/sql/opt/norm/factory.og.go index 619887d9dc24..6e712e57fb0a 100644 --- a/pkg/sql/opt/norm/factory.og.go +++ b/pkg/sql/opt/norm/factory.og.go @@ -1523,6 +1523,19 @@ func (_f *Factory) ConstructGroupBy( return _group } + // [EliminateDistinct] + { + if _f.hasNoCols(aggregations) { + if _f.colsAreKey(groupingCols, input) { + if _f.onRuleMatch == nil || _f.onRuleMatch(opt.EliminateDistinct) { + _group = input + _f.mem.AddAltFingerprint(_groupByExpr.Fingerprint(), _group) + return _group + } + } + } + } + // [FilterUnusedGroupByCols] { if _f.hasUnusedColumns(input, _f.neededColsGroupBy(aggregations, groupingCols)) { diff --git a/pkg/sql/opt/norm/rules/groupby.opt b/pkg/sql/opt/norm/rules/groupby.opt new file mode 100644 index 000000000000..bb62f3c50572 --- /dev/null +++ b/pkg/sql/opt/norm/rules/groupby.opt @@ -0,0 +1,16 @@ +# ============================================================================= +# groupby.opt contains normalization rules for the GroupBy operator. +# ============================================================================= + +# EliminateDistinct discards a GroupBy operator that is eliminating duplicate +# rows by using grouping columns that are statically known to form a strong key. +# By definition, a strong key does not allow duplicate values, so the GroupBy is +# redundant and can be eliminated. +[EliminateDistinct, Normalize] +(GroupBy + $input:* + $aggregations:* & (HasNoCols $aggregations) + $groupingCols:* & (ColsAreKey $groupingCols $input) +) +=> +$input diff --git a/pkg/sql/opt/norm/testdata/bool b/pkg/sql/opt/norm/testdata/bool index 952383a094ed..c04570f51157 100644 --- a/pkg/sql/opt/norm/testdata/bool +++ b/pkg/sql/opt/norm/testdata/bool @@ -26,7 +26,8 @@ opt SELECT * FROM a WHERE True AND True ---- scan a - └── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + └── keys: (1) # -------------------------------------------------- # EliminateEmptyOr @@ -90,7 +91,8 @@ SELECT true AND k=1 FROM a project ├── columns: column6:6(bool) ├── scan a - │ └── columns: a.k:1(int!null) + │ ├── columns: a.k:1(int!null) + │ └── keys: (1) └── projections [outer=(1)] └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight)] ├── variable: a.k [type=int, outer=(1)] @@ -102,7 +104,8 @@ SELECT k=1 AND i=2 AND true FROM a project ├── columns: column6:6(bool) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) + │ ├── columns: a.k:1(int!null) a.i:2(int) + │ └── keys: (1) └── projections [outer=(1,2)] └── and [type=bool, outer=(1,2), constraints=(/1: [/1 - /1]; /2: [/2 - /2]; tight)] ├── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight)] @@ -117,7 +120,8 @@ opt SELECT * FROM a WHERE true AND true ---- scan a - └── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + └── keys: (1) # Flatten nested And operands. opt @@ -126,7 +130,8 @@ SELECT (k>1 AND k<5) AND (f=3.5 AND s='foo') FROM a project ├── columns: column6:6(bool) ├── scan a - │ └── columns: a.k:1(int!null) a.f:3(float) a.s:4(string) + │ ├── columns: a.k:1(int!null) a.f:3(float) a.s:4(string) + │ └── keys: (1) └── projections [outer=(1,3,4)] └── and [type=bool, outer=(1,3,4), constraints=(/1: [/2 - /4]; /3: [/3.5 - /3.5]; /4: [/'foo' - /'foo']; tight)] ├── gt [type=bool, outer=(1), constraints=(/1: [/2 - ]; tight)] @@ -172,7 +177,8 @@ SELECT false OR k=1 FROM a project ├── columns: column6:6(bool) ├── scan a - │ └── columns: a.k:1(int!null) + │ ├── columns: a.k:1(int!null) + │ └── keys: (1) └── projections [outer=(1)] └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight)] ├── variable: a.k [type=int, outer=(1)] @@ -183,8 +189,10 @@ SELECT * FROM a WHERE k=1 OR i=2 OR false ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1,2)] └── or [type=bool, outer=(1,2)] ├── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight)] @@ -200,7 +208,8 @@ SELECT * FROM a WHERE false OR false ---- scan a ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) - └── constraint: /1: contradiction + ├── constraint: /1: contradiction + └── keys: (1) # Flatten nested Or operands. opt @@ -208,8 +217,10 @@ SELECT * FROM a WHERE (k=1 OR i=2) OR (f=3.5 OR s='foo') ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1-4)] └── or [type=bool, outer=(1-4)] ├── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight)] @@ -234,7 +245,8 @@ SELECT (k=1 OR false) AND (false OR k=2 OR false) AND true FROM a project ├── columns: column6:6(bool) ├── scan a - │ └── columns: a.k:1(int!null) + │ ├── columns: a.k:1(int!null) + │ └── keys: (1) └── projections [outer=(1)] └── and [type=bool, outer=(1), constraints=(contradiction; tight)] ├── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight)] @@ -251,7 +263,8 @@ SELECT (k=1 OR (i=2 OR f=3.5)) AND (s='foo' AND s<>'bar') FROM a project ├── columns: column6:6(bool) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) + │ └── keys: (1) └── projections [outer=(1-4)] └── and [type=bool, outer=(1-4), constraints=(/4: [/'foo' - /'foo'])] ├── or [type=bool, outer=(1-3)] @@ -279,7 +292,8 @@ SELECT * FROM a WHERE Null ---- scan a ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) - └── constraint: /1: contradiction + ├── constraint: /1: contradiction + └── keys: (1) opt SELECT * FROM a INNER JOIN b ON NULL @@ -287,9 +301,11 @@ SELECT * FROM a INNER JOIN b ON NULL inner-join ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) z:7(int) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) ├── scan b - │ └── columns: b.x:6(int!null) b.z:7(int) + │ ├── columns: b.x:6(int!null) b.z:7(int) + │ └── keys: (6) └── false [type=bool] opt @@ -297,7 +313,8 @@ SELECT * FROM a WHERE i=1 AND Null ---- scan a ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) - └── constraint: /1: contradiction + ├── constraint: /1: contradiction + └── keys: (1) # -------------------------------------------------- # FoldNullAndOr @@ -336,7 +353,8 @@ SELECT null or (null and k=1) FROM a project ├── columns: column6:6(bool) ├── scan a - │ └── columns: a.k:1(int!null) + │ ├── columns: a.k:1(int!null) + │ └── keys: (1) └── projections [outer=(1)] └── or [type=bool, outer=(1)] ├── null [type=unknown] @@ -356,8 +374,10 @@ SELECT * FROM a WHERE NOT(i=1) AND NOT(i<>1) AND NOT(i>1) AND NOT(i>=1) AND NOT( ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2), constraints=(contradiction)] ├── ne [type=bool, outer=(2)] │ ├── variable: a.i [type=int, outer=(2)] @@ -386,8 +406,10 @@ WHERE NOT(i IN (1,2)) AND NOT(i NOT IN (3,4)) AND NOT(i IS NULL) AND NOT(i IS NO ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2), constraints=(/2: [/3 - /3] [/4 - /4])] ├── not-in [type=bool, outer=(2)] │ ├── variable: a.i [type=int, outer=(2)] @@ -414,8 +436,10 @@ WHERE NOT(s LIKE 'foo') AND NOT(s NOT LIKE 'foo') AND NOT(s ILIKE 'foo') AND NOT ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(4)] ├── not-like [type=bool, outer=(4)] │ ├── variable: a.s [type=string, outer=(4)] @@ -436,8 +460,10 @@ SELECT * FROM a WHERE NOT(s SIMILAR TO 'foo') AND NOT(s NOT SIMILAR TO 'foo') ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(4)] ├── not-similar-to [type=bool, outer=(4)] │ ├── variable: a.s [type=string, outer=(4)] @@ -452,8 +478,10 @@ SELECT * FROM a WHERE NOT(s ~ 'foo') AND NOT(s !~ 'foo') AND NOT(s ~* 'foo') AND ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(4)] ├── not-reg-match [type=bool, outer=(4)] │ ├── variable: a.s [type=string, outer=(4)] @@ -478,8 +506,10 @@ SELECT * FROM a WHERE ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(5)] ├── not [type=bool, outer=(5)] │ └── contains [type=bool, outer=(5)] @@ -512,8 +542,10 @@ SELECT * FROM a WHERE NOT(NOT('[1, 2]' @> j)) ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(5)] └── contains [type=bool, outer=(5)] ├── const: '[1, 2]' [type=jsonb] @@ -527,8 +559,10 @@ SELECT * FROM a WHERE NOT (k >= i AND i < f) ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1-3)] └── or [type=bool, outer=(1-3)] ├── lt [type=bool, outer=(1,2)] @@ -543,8 +577,10 @@ SELECT * FROM a WHERE NOT (k >= i AND i < f AND (i > 5 AND i < 10 AND f > 1)) ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1-3)] └── or [type=bool, outer=(1-3)] ├── lt [type=bool, outer=(1,2)] @@ -572,8 +608,10 @@ SELECT * FROM a WHERE NOT (k >= i OR i < f OR k + i < f) ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1-3)] ├── lt [type=bool, outer=(1,2)] │ ├── variable: a.k [type=int, outer=(1)] @@ -592,8 +630,10 @@ SELECT * FROM a WHERE NOT (k >= i OR i < f OR (i > 5 OR i < 10 OR f > 1)) ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1-3), constraints=(contradiction)] ├── lt [type=bool, outer=(1,2)] │ ├── variable: a.k [type=int, outer=(1)] @@ -619,8 +659,10 @@ SELECT * FROM a WHERE NOT ((k >= i OR i < f) AND (i > 5 OR f > 1)) ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1-3)] └── or [type=bool, outer=(1-3)] ├── and [type=bool, outer=(1-3)] @@ -643,8 +685,10 @@ SELECT * FROM a WHERE NOT ((k >= i AND i < f) OR (i > 5 AND f > 1)) ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1-3)] ├── or [type=bool, outer=(1-3)] │ ├── lt [type=bool, outer=(1,2)] diff --git a/pkg/sql/opt/norm/testdata/combo b/pkg/sql/opt/norm/testdata/combo index f8c4d25fdcea..ea0c904b2224 100644 --- a/pkg/sql/opt/norm/testdata/combo +++ b/pkg/sql/opt/norm/testdata/combo @@ -32,9 +32,11 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 ├── inner-join │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int!null) b.z:7(int) │ ├── scan a - │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ └── keys: (6) │ └── and [type=bool, outer=(1,2,6)] │ ├── eq [type=bool, outer=(1,6)] │ │ ├── variable: a.x [type=int, outer=(1)] @@ -53,9 +55,11 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 ├── inner-join │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int!null) b.z:7(int) │ ├── scan a - │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ └── keys: (6) │ └── and [type=bool, outer=(1,2,6)] │ ├── eq [type=bool, outer=(1,6)] │ │ ├── variable: a.x [type=int, outer=(1)] @@ -78,9 +82,11 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 ├── inner-join │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int!null) b.z:7(int) │ ├── scan a - │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ └── keys: (6) - │ └── and [type=bool, outer=(1,2,6)] + │ └── filters [type=bool, outer=(1,2,6)] │ ├── eq [type=bool, outer=(1,6)] @@ -100,11 +106,13 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 ├── inner-join │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int!null) b.z:7(int) - │ ├── scan a - - │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── select - + │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + - │ │ └── keys: (1) + + │ │ ├── keys: (1) + │ │ ├── scan a - + │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ └── keys: (1) + │ │ └── filters [type=bool, outer=(2)] + │ │ └── eq [type=bool, outer=(2)] + │ │ ├── variable: a.i [type=int, outer=(2)] @@ -112,7 +120,8 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 + │ │ ├── const: 10 [type=int] + │ │ └── const: 1 [type=int] │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ └── keys: (6) - │ └── filters [type=bool, outer=(1,2,6)] - │ ├── eq [type=bool, outer=(1,6)] - │ │ ├── variable: a.x [type=int, outer=(1)] @@ -136,21 +145,24 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 - │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int!null) b.z:7(int) - │ ├── select - │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ ├── columns: a.x:1(int!null) a.s:4(string) b.x:6(int!null) b.z:7(int) + + │ ├── project + + │ │ ├── columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) - │ │ ├── scan a - - │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ ├── select + │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + - │ │ │ └── keys: (1) - │ │ └── filters [type=bool, outer=(2)] - │ │ └── eq [type=bool, outer=(2)] - │ │ ├── variable: a.i [type=int, outer=(2)] - │ │ └── minus [type=int] - │ │ ├── const: 10 [type=int] - │ │ └── const: 1 [type=int] - + │ ├── columns: a.x:1(int!null) a.s:4(string) b.x:6(int!null) b.z:7(int) - + │ ├── project - + │ │ ├── columns: a.x:1(int!null) a.s:4(string) - + │ │ ├── select - + │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ ├── keys: (1) + │ │ │ ├── scan a - + │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ │ └── keys: (1) + │ │ │ └── filters [type=bool, outer=(2)] + │ │ │ └── eq [type=bool, outer=(2)] + │ │ │ ├── variable: a.i [type=int, outer=(2)] @@ -161,7 +173,8 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 + │ │ ├── variable: a.x [type=int, outer=(1)] + │ │ └── variable: a.s [type=string, outer=(4)] │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ └── keys: (6) │ └── filters [type=bool, outer=(1,6)] │ └── eq [type=bool, outer=(1,6)] │ ├── variable: a.x [type=int, outer=(1)] @@ -176,12 +189,15 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 │ ├── columns: a.x:1(int!null) a.s:4(string) b.x:6(int!null) b.z:7(int) │ ├── project │ │ ├── columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── select - │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ ├── keys: (1) │ │ │ ├── scan a - - │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) - + │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.s:4(string) + - │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ │ └── keys: (1) │ │ │ └── filters [type=bool, outer=(2)] │ │ │ └── eq [type=bool, outer=(2)] │ │ │ ├── variable: a.i [type=int, outer=(2)] @@ -192,7 +208,8 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 │ │ ├── variable: a.x [type=int, outer=(1)] │ │ └── variable: a.s [type=string, outer=(4)] │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ └── keys: (6) │ └── filters [type=bool, outer=(1,6)] │ └── eq [type=bool, outer=(1,6)] │ ├── variable: a.x [type=int, outer=(1)] @@ -208,10 +225,13 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 + │ ├── columns: a.x:1(int!null) a.s:4(string) b.x:6(int!null) │ ├── project │ │ ├── columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── select │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ ├── keys: (1) │ │ │ ├── scan a - │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ │ └── keys: (1) │ │ │ └── filters [type=bool, outer=(2)] │ │ │ └── eq [type=bool, outer=(2)] │ │ │ ├── variable: a.i [type=int, outer=(2)] @@ -222,8 +242,9 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 │ │ ├── variable: a.x [type=int, outer=(1)] │ │ └── variable: a.s [type=string, outer=(4)] │ ├── scan b - - │ │ └── columns: b.x:6(int!null) b.z:7(int) - + │ │ └── columns: b.x:6(int!null) + - │ │ ├── columns: b.x:6(int!null) b.z:7(int) + + │ │ ├── columns: b.x:6(int!null) + │ │ └── keys: (6) │ └── filters [type=bool, outer=(1,6)] │ └── eq [type=bool, outer=(1,6)] │ ├── variable: a.x [type=int, outer=(1)] @@ -244,10 +265,13 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 │ ├── columns: a.x:1(int!null) a.s:4(string) b.x:6(int!null) │ ├── project │ │ ├── columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── select │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ ├── keys: (1) │ │ │ ├── scan a - │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ │ └── keys: (1) │ │ │ └── filters [type=bool, outer=(2)] │ │ │ └── eq [type=bool, outer=(2)] │ │ │ ├── variable: a.i [type=int, outer=(2)] @@ -258,7 +282,8 @@ SELECT s FROM a INNER JOIN b ON a.x=b.x AND i+1=10 │ │ ├── variable: a.x [type=int, outer=(1)] │ │ └── variable: a.s [type=string, outer=(4)] │ ├── scan b - │ │ └── columns: b.x:6(int!null) + │ │ ├── columns: b.x:6(int!null) + │ │ └── keys: (6) │ └── filters [type=bool, outer=(1,6)] │ └── eq [type=bool, outer=(1,6)] │ ├── variable: a.x [type=int, outer=(1)] @@ -290,9 +315,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET │ │ │ ├── full-join │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.z:7(int) │ │ │ │ ├── scan a - │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ │ │ └── keys: (1) │ │ │ │ ├── scan b - │ │ │ │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ │ │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ │ │ │ └── keys: (6) │ │ │ │ └── eq [type=bool, outer=(1,6)] │ │ │ │ ├── variable: a.x [type=int, outer=(1)] │ │ │ │ └── variable: b.x [type=int, outer=(6)] @@ -324,9 +351,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET │ │ │ ├── full-join │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.z:7(int) │ │ │ │ ├── scan a - │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ │ │ └── keys: (1) │ │ │ │ ├── scan b - │ │ │ │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ │ │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ │ │ │ └── keys: (6) - │ │ │ │ └── eq [type=bool, outer=(1,6)] - │ │ │ │ ├── variable: a.x [type=int, outer=(1)] - │ │ │ │ └── variable: b.x [type=int, outer=(6)] @@ -362,9 +391,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET │ │ │ ├── full-join │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.z:7(int) │ │ │ │ ├── scan a - │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ │ │ └── keys: (1) │ │ │ │ ├── scan b - │ │ │ │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ │ │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ │ │ │ └── keys: (6) │ │ │ │ └── filters [type=bool, outer=(1,6)] │ │ │ │ └── eq [type=bool, outer=(1,6)] │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -401,9 +432,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ ├── full-join - │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.z:7(int) - │ │ │ │ ├── scan a - - │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + - │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + - │ │ │ │ │ └── keys: (1) - │ │ │ │ ├── scan b - - │ │ │ │ │ └── columns: b.x:6(int!null) b.z:7(int) + - │ │ │ │ │ ├── columns: b.x:6(int!null) b.z:7(int) + - │ │ │ │ │ └── keys: (6) - │ │ │ │ └── filters [type=bool, outer=(1,6)] - │ │ │ │ └── eq [type=bool, outer=(1,6)] - │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -414,9 +447,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET + │ │ │ │ ├── full-join + │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.z:7(int) + │ │ │ │ │ ├── scan a - + │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ │ │ │ └── keys: (1) + │ │ │ │ │ ├── scan b - + │ │ │ │ │ │ └── columns: b.x:6(int!null) b.z:7(int) + + │ │ │ │ │ │ ├── columns: b.x:6(int!null) b.z:7(int) + + │ │ │ │ │ │ └── keys: (6) + │ │ │ │ │ └── filters [type=bool, outer=(1,6)] + │ │ │ │ │ └── eq [type=bool, outer=(1,6)] + │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -455,10 +490,12 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.z:7(int) + │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) b.z:7(int) │ │ │ │ │ ├── scan a - - │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) - + │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + │ │ │ │ │ │ └── keys: (1) │ │ │ │ │ ├── scan b - │ │ │ │ │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ │ │ │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ │ │ │ │ └── keys: (6) │ │ │ │ │ └── filters [type=bool, outer=(1,6)] │ │ │ │ │ └── eq [type=bool, outer=(1,6)] │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -497,10 +534,12 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) b.z:7(int) + │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) │ │ │ │ │ ├── scan a - │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + │ │ │ │ │ │ └── keys: (1) │ │ │ │ │ ├── scan b - - │ │ │ │ │ │ └── columns: b.x:6(int!null) b.z:7(int) - + │ │ │ │ │ │ └── columns: b.x:6(int!null) + - │ │ │ │ │ │ ├── columns: b.x:6(int!null) b.z:7(int) + + │ │ │ │ │ │ ├── columns: b.x:6(int!null) + │ │ │ │ │ │ └── keys: (6) │ │ │ │ │ └── filters [type=bool, outer=(1,6)] │ │ │ │ │ └── eq [type=bool, outer=(1,6)] │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -539,9 +578,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET + │ │ │ │ ├── select │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) - │ │ │ │ │ ├── scan a - - │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ │ └── keys: (1) - │ │ │ │ │ ├── scan b - - │ │ │ │ │ │ └── columns: b.x:6(int!null) + - │ │ │ │ │ │ ├── columns: b.x:6(int!null) + - │ │ │ │ │ │ └── keys: (6) - │ │ │ │ │ └── filters [type=bool, outer=(1,6)] - │ │ │ │ │ └── eq [type=bool, outer=(1,6)] - │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -549,9 +590,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET + │ │ │ │ │ ├── full-join + │ │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) + │ │ │ │ │ │ ├── scan a - + │ │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ │ │ └── keys: (1) + │ │ │ │ │ │ ├── scan b - + │ │ │ │ │ │ │ └── columns: b.x:6(int!null) + + │ │ │ │ │ │ │ ├── columns: b.x:6(int!null) + + │ │ │ │ │ │ │ └── keys: (6) + │ │ │ │ │ │ └── filters [type=bool, outer=(1,6)] + │ │ │ │ │ │ └── eq [type=bool, outer=(1,6)] + │ │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -596,9 +639,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET │ │ │ │ │ ├── full-join │ │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) │ │ │ │ │ │ ├── scan a - │ │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + │ │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + │ │ │ │ │ │ │ └── keys: (1) │ │ │ │ │ │ ├── scan b - │ │ │ │ │ │ │ └── columns: b.x:6(int!null) + │ │ │ │ │ │ │ ├── columns: b.x:6(int!null) + │ │ │ │ │ │ │ └── keys: (6) │ │ │ │ │ │ └── filters [type=bool, outer=(1,6)] │ │ │ │ │ │ └── eq [type=bool, outer=(1,6)] │ │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -644,9 +689,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ │ │ ├── full-join - │ │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) - │ │ │ │ │ │ ├── scan a - - │ │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ │ │ └── keys: (1) - │ │ │ │ │ │ ├── scan b - - │ │ │ │ │ │ │ └── columns: b.x:6(int!null) + - │ │ │ │ │ │ │ ├── columns: b.x:6(int!null) + - │ │ │ │ │ │ │ └── keys: (6) - │ │ │ │ │ │ └── filters [type=bool, outer=(1,6)] - │ │ │ │ │ │ └── eq [type=bool, outer=(1,6)] - │ │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -659,9 +706,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ │ └── variable: a.i [type=int, outer=(2)] - │ │ │ └── true [type=bool] + │ │ │ │ │ ├── scan a - + │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ │ └── keys: (1) + │ │ │ │ │ ├── scan b - + │ │ │ │ │ │ └── columns: b.x:6(int!null) + + │ │ │ │ │ │ ├── columns: b.x:6(int!null) + + │ │ │ │ │ │ └── keys: (6) + │ │ │ │ │ └── filters [type=bool, outer=(1,6)] + │ │ │ │ │ └── eq [type=bool, outer=(1,6)] + │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -702,9 +751,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ │ ├── full-join - │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) - │ │ │ │ │ ├── scan a - - │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ │ └── keys: (1) - │ │ │ │ │ ├── scan b - - │ │ │ │ │ │ └── columns: b.x:6(int!null) + - │ │ │ │ │ │ ├── columns: b.x:6(int!null) + - │ │ │ │ │ │ └── keys: (6) - │ │ │ │ │ └── filters [type=bool, outer=(1,6)] - │ │ │ │ │ └── eq [type=bool, outer=(1,6)] - │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -716,9 +767,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ └── projections [outer=(2)] - │ │ │ └── variable: a.i [type=int, outer=(2)] + │ │ │ │ ├── scan a - + │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ └── keys: (1) + │ │ │ │ ├── scan b - + │ │ │ │ │ └── columns: b.x:6(int!null) + + │ │ │ │ │ ├── columns: b.x:6(int!null) + + │ │ │ │ │ └── keys: (6) + │ │ │ │ └── filters [type=bool, outer=(1,6)] + │ │ │ │ └── eq [type=bool, outer=(1,6)] + │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -755,9 +808,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET + │ │ │ ├── select │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) - │ │ │ │ ├── scan a - - │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ └── keys: (1) - │ │ │ │ ├── scan b - - │ │ │ │ │ └── columns: b.x:6(int!null) + - │ │ │ │ │ ├── columns: b.x:6(int!null) + - │ │ │ │ │ └── keys: (6) - │ │ │ │ └── filters [type=bool, outer=(1,6)] - │ │ │ │ └── eq [type=bool, outer=(1,6)] - │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -769,9 +824,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET + │ │ │ │ ├── full-join + │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) + │ │ │ │ │ ├── scan a - + │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ │ └── keys: (1) + │ │ │ │ │ ├── scan b - + │ │ │ │ │ │ └── columns: b.x:6(int!null) + + │ │ │ │ │ │ ├── columns: b.x:6(int!null) + + │ │ │ │ │ │ └── keys: (6) + │ │ │ │ │ └── filters [type=bool, outer=(1,6)] + │ │ │ │ │ └── eq [type=bool, outer=(1,6)] + │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -812,9 +869,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ │ ├── full-join - │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) - │ │ │ │ │ ├── scan a - - │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ │ └── keys: (1) - │ │ │ │ │ ├── scan b - - │ │ │ │ │ │ └── columns: b.x:6(int!null) + - │ │ │ │ │ │ ├── columns: b.x:6(int!null) + - │ │ │ │ │ │ └── keys: (6) - │ │ │ │ │ └── filters [type=bool, outer=(1,6)] - │ │ │ │ │ └── eq [type=bool, outer=(1,6)] - │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -826,9 +885,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ └── projections [outer=(2)] - │ │ │ └── variable: a.i [type=int, outer=(2)] + │ │ │ │ ├── scan a - + │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ └── keys: (1) + │ │ │ │ ├── scan b - + │ │ │ │ │ └── columns: b.x:6(int!null) + + │ │ │ │ │ ├── columns: b.x:6(int!null) + + │ │ │ │ │ └── keys: (6) + │ │ │ │ └── filters [type=bool, outer=(1,6)] + │ │ │ │ └── eq [type=bool, outer=(1,6)] + │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -865,9 +926,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET - │ │ │ ├── full-join - │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) - │ │ │ │ ├── scan a - - │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ └── keys: (1) - │ │ │ │ ├── scan b - - │ │ │ │ │ └── columns: b.x:6(int!null) + - │ │ │ │ │ ├── columns: b.x:6(int!null) + - │ │ │ │ │ └── keys: (6) - │ │ │ │ └── filters [type=bool, outer=(1,6)] - │ │ │ │ └── eq [type=bool, outer=(1,6)] - │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -890,9 +953,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET + │ │ │ ├── full-join + │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) + │ │ │ │ ├── scan a - + │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ └── keys: (1) + │ │ │ │ ├── scan b - + │ │ │ │ │ └── columns: b.x:6(int!null) + + │ │ │ │ │ ├── columns: b.x:6(int!null) + + │ │ │ │ │ └── keys: (6) + │ │ │ │ └── filters [type=bool, outer=(1,6)] + │ │ │ │ └── eq [type=bool, outer=(1,6)] + │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -932,9 +997,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET + │ │ │ ├── select │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) - │ │ │ │ ├── scan a - - │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + - │ │ │ │ │ └── keys: (1) - │ │ │ │ ├── scan b - - │ │ │ │ │ └── columns: b.x:6(int!null) + - │ │ │ │ │ ├── columns: b.x:6(int!null) + - │ │ │ │ │ └── keys: (6) - │ │ │ │ └── filters [type=bool, outer=(1,6)] - │ │ │ │ └── eq [type=bool, outer=(1,6)] - │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -946,9 +1013,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET + │ │ │ │ ├── full-join + │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) + │ │ │ │ │ ├── scan a - + │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + + │ │ │ │ │ │ └── keys: (1) + │ │ │ │ │ ├── scan b - + │ │ │ │ │ │ └── columns: b.x:6(int!null) + + │ │ │ │ │ │ ├── columns: b.x:6(int!null) + + │ │ │ │ │ │ └── keys: (6) + │ │ │ │ │ └── filters [type=bool, outer=(1,6)] + │ │ │ │ │ └── eq [type=bool, outer=(1,6)] + │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -990,9 +1059,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET │ │ │ │ ├── full-join │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) │ │ │ │ │ ├── scan a - │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + │ │ │ │ │ │ └── keys: (1) │ │ │ │ │ ├── scan b - │ │ │ │ │ │ └── columns: b.x:6(int!null) + │ │ │ │ │ │ ├── columns: b.x:6(int!null) + │ │ │ │ │ │ └── keys: (6) │ │ │ │ │ └── filters [type=bool, outer=(1,6)] │ │ │ │ │ └── eq [type=bool, outer=(1,6)] │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -1041,9 +1112,11 @@ SELECT i, i+1 FROM a FULL JOIN b ON a.x=b.x WHERE i=10 ORDER BY i LIMIT 5 OFFSET │ │ │ │ ├── full-join │ │ │ │ │ ├── columns: a.x:1(int) a.i:2(int) b.x:6(int) │ │ │ │ │ ├── scan a - │ │ │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) + │ │ │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) + │ │ │ │ │ │ └── keys: (1) │ │ │ │ │ ├── scan b - │ │ │ │ │ │ └── columns: b.x:6(int!null) + │ │ │ │ │ │ ├── columns: b.x:6(int!null) + │ │ │ │ │ │ └── keys: (6) │ │ │ │ │ └── filters [type=bool, outer=(1,6)] │ │ │ │ │ └── eq [type=bool, outer=(1,6)] │ │ │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -1075,13 +1148,17 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 ├── columns: column6:6(decimal) ├── select │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + │ ├── keys: (1) │ ├── group-by │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── project │ │ │ ├── columns: a.s:4(string) a.x:1(int!null) + │ │ │ ├── keys: (1) │ │ │ ├── scan a - │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ │ └── keys: (1) │ │ │ └── projections [outer=(1,4)] │ │ │ ├── variable: a.s [type=string, outer=(4)] │ │ │ └── variable: a.x [type=int, outer=(1)] @@ -1099,14 +1176,18 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 ├── columns: column6:6(decimal) ├── select │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + │ ├── keys: (1) │ ├── group-by │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── project │ │ │ ├── columns: a.s:4(string) a.x:1(int!null) + │ │ │ ├── keys: (1) │ │ │ ├── scan a - - │ │ │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) - + │ │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + + │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + │ │ │ │ └── keys: (1) │ │ │ └── projections [outer=(1,4)] │ │ │ ├── variable: a.s [type=string, outer=(4)] │ │ │ └── variable: a.x [type=int, outer=(1)] @@ -1124,18 +1205,23 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 ├── columns: column6:6(decimal) ├── select │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + │ ├── keys: (1) │ ├── group-by │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) - │ │ ├── project - │ │ │ ├── columns: a.s:4(string) a.x:1(int!null) + - │ │ │ ├── keys: (1) - │ │ │ ├── scan a - - │ │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ └── keys: (1) - │ │ │ └── projections [outer=(1,4)] - │ │ │ ├── variable: a.s [type=string, outer=(4)] - │ │ │ └── variable: a.x [type=int, outer=(1)] + │ │ ├── scan a - + │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ └── keys: (1) │ │ └── aggregations [outer=(1)] │ │ └── function: sum [type=decimal, outer=(1)] │ │ └── variable: a.x [type=int, outer=(1)] @@ -1150,11 +1236,14 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 ├── columns: column6:6(decimal) ├── select │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + │ ├── keys: (1) │ ├── group-by │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── scan a - │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + │ │ │ └── keys: (1) │ │ └── aggregations [outer=(1)] │ │ └── function: sum [type=decimal, outer=(1)] │ │ └── variable: a.x [type=int, outer=(1)] @@ -1173,11 +1262,14 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 ├── columns: column6:6(decimal) ├── select - │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + - │ ├── keys: (1) - │ ├── group-by - │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) - │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + - │ │ ├── keys: (1) - │ │ ├── scan a - - │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ └── keys: (1) - │ │ └── aggregations [outer=(1)] - │ │ └── function: sum [type=decimal, outer=(1)] - │ │ └── variable: a.x [type=int, outer=(1)] @@ -1187,8 +1279,10 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 + │ │ ├── group-by + │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + │ │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + + │ │ │ ├── keys: (1) + │ │ │ ├── scan a - + │ │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ │ └── keys: (1) + │ │ │ └── aggregations [outer=(1)] + │ │ │ └── function: sum [type=decimal, outer=(1)] + │ │ │ └── variable: a.x [type=int, outer=(1)] @@ -1212,16 +1306,20 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 + │ │ ├── select │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) - │ │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ │ ├── keys: (1) - │ │ │ ├── scan a - - │ │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ └── keys: (1) - │ │ │ └── aggregations [outer=(1)] - │ │ │ └── function: sum [type=decimal, outer=(1)] - │ │ │ └── variable: a.x [type=int, outer=(1)] + │ │ │ ├── group-by + │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + │ │ │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + + │ │ │ │ ├── keys: (1) + │ │ │ │ ├── scan a - + │ │ │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ │ │ └── keys: (1) + │ │ │ │ └── aggregations [outer=(1)] + │ │ │ │ └── function: sum [type=decimal, outer=(1)] + │ │ │ │ └── variable: a.x [type=int, outer=(1)] @@ -1248,11 +1346,14 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 │ │ ├── columns: column6:6(decimal) │ │ ├── select │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + │ │ │ ├── keys: (1) │ │ │ ├── group-by │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) │ │ │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ │ │ ├── keys: (1) │ │ │ │ ├── scan a - │ │ │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + │ │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + │ │ │ │ │ └── keys: (1) │ │ │ │ └── aggregations [outer=(1)] │ │ │ │ └── function: sum [type=decimal, outer=(1)] │ │ │ │ └── variable: a.x [type=int, outer=(1)] @@ -1278,13 +1379,18 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 - │ │ ├── select + │ ├── select + │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + + │ │ ├── keys: (1) + │ │ ├── group-by │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + + │ │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ │ ├── keys: (1) - │ │ │ ├── group-by - │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) - │ │ │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ ├── keys: (1) - │ │ │ │ ├── scan a - - │ │ │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ │ └── keys: (1) - │ │ │ │ └── aggregations [outer=(1)] - │ │ │ │ └── function: sum [type=decimal, outer=(1)] - │ │ │ │ └── variable: a.x [type=int, outer=(1)] @@ -1295,9 +1401,9 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 - │ │ └── projections [outer=(6)] - │ │ └── variable: column6 [type=decimal, outer=(6)] - │ └── true [type=bool] - + │ │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ │ ├── scan a - + │ │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ │ └── keys: (1) + │ │ │ └── aggregations [outer=(1)] + │ │ │ └── function: sum [type=decimal, outer=(1)] + │ │ │ └── variable: a.x [type=int, outer=(1)] @@ -1318,13 +1424,18 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 - │ ├── select + ├── select + │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + + │ ├── keys: (1) + │ ├── group-by │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + + │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) - │ │ ├── group-by - │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) - │ │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + - │ │ │ ├── keys: (1) - │ │ │ ├── scan a - - │ │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + - │ │ │ │ └── keys: (1) - │ │ │ └── aggregations [outer=(1)] - │ │ │ └── function: sum [type=decimal, outer=(1)] - │ │ │ └── variable: a.x [type=int, outer=(1)] @@ -1334,9 +1445,9 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 - │ │ └── const: 1 [type=decimal] - │ └── projections [outer=(6)] - │ └── variable: column6 [type=decimal, outer=(6)] - + │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ ├── scan a - + │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + + │ │ │ └── keys: (1) + │ │ └── aggregations [outer=(1)] + │ │ └── function: sum [type=decimal, outer=(1)] + │ │ └── variable: a.x [type=int, outer=(1)] @@ -1354,11 +1465,14 @@ SELECT SUM(x) FROM a GROUP BY s, x HAVING SUM(x)=1 ├── columns: column6:6(decimal) ├── select │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) + │ ├── keys: (1) │ ├── group-by │ │ ├── columns: a.x:1(int!null) a.s:4(string) column6:6(decimal) │ │ ├── grouping columns: a.x:1(int!null) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── scan a - │ │ │ └── columns: a.x:1(int!null) a.s:4(string) + │ │ │ ├── columns: a.x:1(int!null) a.s:4(string) + │ │ │ └── keys: (1) │ │ └── aggregations [outer=(1)] │ │ └── function: sum [type=decimal, outer=(1)] │ │ └── variable: a.x [type=int, outer=(1)] diff --git a/pkg/sql/opt/norm/testdata/comp b/pkg/sql/opt/norm/testdata/comp index 91715534240c..2242e6bf1908 100644 --- a/pkg/sql/opt/norm/testdata/comp +++ b/pkg/sql/opt/norm/testdata/comp @@ -21,8 +21,10 @@ SELECT * FROM a WHERE 1+ik AND k/2>=i ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1,2)] ├── gt [type=bool, outer=(1,2)] │ ├── variable: a.k [type=int, outer=(1)] @@ -53,8 +55,10 @@ SELECT * FROM a WHERE length('foo')+1i AND 'fo ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1,2,4), constraints=(/2: (/NULL - /4]; /4: (/NULL - /'foo'])] ├── gt [type=bool, outer=(1,2)] │ ├── plus [type=int, outer=(1,2)] @@ -92,8 +96,10 @@ WHERE ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2,3)] ├── eq [type=bool, outer=(2)] │ ├── variable: a.i [type=int, outer=(2)] @@ -133,8 +139,10 @@ SELECT * FROM a WHERE s::date + '02:00:00'::time = '2000-01-01T02:00:00'::timest ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(4)] └── eq [type=bool, outer=(4)] ├── plus [type=timestamp, outer=(4)] @@ -158,8 +166,10 @@ WHERE ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2,3)] ├── eq [type=bool, outer=(2)] │ ├── variable: a.i [type=int, outer=(2)] @@ -201,8 +211,10 @@ SELECT * FROM a WHERE s::json - 1 = '[1]'::json ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(4)] └── eq [type=bool, outer=(4)] ├── minus [type=jsonb, outer=(4)] @@ -226,8 +238,10 @@ WHERE ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2,3)] ├── eq [type=bool, outer=(2)] │ ├── variable: a.i [type=int, outer=(2)] @@ -269,8 +283,10 @@ SELECT * FROM a WHERE '[1, 2]'::json - i = '[1]' ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2)] └── eq [type=bool, outer=(2)] ├── minus [type=jsonb, outer=(2)] @@ -286,8 +302,10 @@ SELECT * FROM a WHERE (i, f, s) = (1, 3.5, 'foo') ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2-4), constraints=(/2: [/1 - /1]; /3: [/3.5 - /3.5]; /4: [/'foo' - /'foo']; tight)] ├── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight)] │ ├── variable: a.i [type=int, outer=(2)] @@ -309,9 +327,11 @@ SELECT * FROM a WHERE (1, (2, 'foo')) = (k, (i, s)) ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) - │ └── constraint: /1: [/1 - /1] + │ ├── constraint: /1: [/1 - /1] + │ └── keys: (1) └── filters [type=bool, outer=(2,4), constraints=(/2: [/2 - /2]; /4: [/'foo' - /'foo']; tight)] ├── eq [type=bool, outer=(2), constraints=(/2: [/2 - /2]; tight)] │ ├── variable: a.i [type=int, outer=(2)] @@ -352,4 +372,5 @@ WHERE ---- scan a ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) - └── constraint: /1: contradiction + ├── constraint: /1: contradiction + └── keys: (1) diff --git a/pkg/sql/opt/norm/testdata/groupby b/pkg/sql/opt/norm/testdata/groupby new file mode 100644 index 000000000000..5699fcf0a5db --- /dev/null +++ b/pkg/sql/opt/norm/testdata/groupby @@ -0,0 +1,104 @@ +exec-ddl +CREATE TABLE a +( + k INT PRIMARY KEY, + i INT NOT NULL, + f FLOAT, + s STRING NOT NULL, + j JSON, + UNIQUE INDEX si_idx (s DESC, i) STORING (j), + UNIQUE INDEX fi_idx (f, i) +) +---- +TABLE a + ├── k int not null + ├── i int not null + ├── f float + ├── s string not null + ├── j jsonb + ├── INDEX primary + │ └── k int not null + ├── INDEX si_idx + │ ├── s string not null desc + │ ├── i int not null + │ ├── k int not null (storing) + │ └── j jsonb (storing) + └── INDEX fi_idx + ├── f float + ├── i int not null + └── k int not null (storing) + +exec-ddl +CREATE TABLE sort (k INT PRIMARY KEY, v INT, w INT) +---- +TABLE sort + ├── k int not null + ├── v int + ├── w int + └── INDEX primary + └── k int not null + +# -------------------------------------------------- +# EliminateDistinct +# -------------------------------------------------- +opt +SELECT DISTINCT k FROM a +---- +scan a + ├── columns: k:1(int!null) + └── keys: (1) + +opt +SELECT DISTINCT s, i FROM a +---- +scan a + ├── columns: s:4(string!null) i:2(int!null) + └── keys: (2,4) + +# Strict superset of key. +opt +SELECT DISTINCT s, i, f FROM a +---- +scan a + ├── columns: s:4(string!null) i:2(int!null) f:3(float) + └── keys: (2,4) weak(2,3) + +# Distinct not eliminated because columns aren't superset of any weak key. +opt +SELECT DISTINCT i FROM a +---- +group-by + ├── columns: i:2(int!null) + ├── grouping columns: a.i:2(int!null) + ├── keys: (2) + ├── scan a + │ └── columns: a.i:2(int!null) + └── aggregations + +# Distinct not eliminated because one column is nullable. +opt +SELECT DISTINCT f, i FROM a +---- +group-by + ├── columns: f:3(float) i:2(int!null) + ├── grouping columns: a.i:2(int!null) a.f:3(float) + ├── keys: weak(2,3) + ├── scan a + │ ├── columns: a.i:2(int!null) a.f:3(float) + │ └── keys: weak(2,3) + └── aggregations + +# Group by not eliminated because it has aggregation. +opt +SELECT s, i, SUM(i) FROM a GROUP BY s, i +---- +group-by + ├── columns: s:4(string!null) i:2(int!null) column6:6(decimal) + ├── grouping columns: a.i:2(int!null) a.s:4(string!null) + ├── keys: (2,4) + ├── scan a + │ ├── columns: a.i:2(int!null) a.s:4(string!null) + │ └── keys: (2,4) + └── aggregations [outer=(2)] + └── function: sum [type=decimal, outer=(2)] + └── variable: a.i [type=int, outer=(2)] diff --git a/pkg/sql/opt/norm/testdata/join b/pkg/sql/opt/norm/testdata/join index 19fb6e2db857..e04ef73664fe 100644 --- a/pkg/sql/opt/norm/testdata/join +++ b/pkg/sql/opt/norm/testdata/join @@ -28,9 +28,11 @@ SELECT * FROM a INNER JOIN b ON a.x=b.x AND b.z10) AND b.z=1 AND a.s='foo' AND b.x left-join ├── columns: x:1(int!null) z:2(int) x:3(int) i:4(int) f:5(float) s:6(string) j:7(jsonb) ├── scan b - │ └── columns: b.x:1(int!null) b.z:2(int) + │ ├── columns: b.x:1(int!null) b.z:2(int) + │ └── keys: (1) ├── select │ ├── columns: a.x:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ ├── keys: (3) │ ├── scan a - │ │ └── columns: a.x:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ ├── columns: a.x:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ └── keys: (3) │ └── filters [type=bool, outer=(4,6), constraints=(/6: [/'foo' - /'foo'])] │ ├── or [type=bool, outer=(4)] │ │ ├── lt [type=bool, outer=(4), constraints=(/4: (/NULL - /-1]; tight)] @@ -205,9 +225,11 @@ SELECT * FROM b RIGHT JOIN a ON b.x=a.x AND a.i=1 right-join ├── columns: x:1(int) z:2(int) x:3(int!null) i:4(int) f:5(float) s:6(string) j:7(jsonb) ├── scan b - │ └── columns: b.x:1(int!null) b.z:2(int) + │ ├── columns: b.x:1(int!null) b.z:2(int) + │ └── keys: (1) ├── scan a - │ └── columns: a.x:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ ├── columns: a.x:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ └── keys: (3) └── filters [type=bool, outer=(1,3,4), constraints=(/4: [/1 - /1])] ├── eq [type=bool, outer=(1,3)] │ ├── variable: b.x [type=int, outer=(1)] @@ -227,16 +249,20 @@ inner-join ├── columns: x:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) z:7(int) ├── select │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── keys: (1) │ ├── scan a - │ │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ └── filters [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight)] │ └── eq [type=bool, outer=(2), constraints=(/2: [/1 - /1]; tight)] │ ├── variable: a.i [type=int, outer=(2)] │ └── const: 1 [type=int] ├── select │ ├── columns: b.x:6(int!null) b.z:7(int) + │ ├── keys: (6) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.z:7(int) + │ │ ├── columns: b.x:6(int!null) b.z:7(int) + │ │ └── keys: (6) │ └── filters [type=bool, outer=(7), constraints=(/7: [/1 - /1]; tight)] │ └── eq [type=bool, outer=(7), constraints=(/7: [/1 - /1]; tight)] │ ├── variable: b.z [type=int, outer=(7)] @@ -253,9 +279,11 @@ SELECT * FROM a FULL JOIN b ON a.x=b.x AND a.i=1 AND b.z=1 full-join ├── columns: x:1(int) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int) z:7(int) ├── scan a - │ └── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.x:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) ├── scan b - │ └── columns: b.x:6(int!null) b.z:7(int) + │ ├── columns: b.x:6(int!null) b.z:7(int) + │ └── keys: (6) └── filters [type=bool, outer=(1,2,6,7), constraints=(/2: [/1 - /1]; /7: [/1 - /1])] ├── eq [type=bool, outer=(1,6)] │ ├── variable: a.x [type=int, outer=(1)] diff --git a/pkg/sql/opt/norm/testdata/limit b/pkg/sql/opt/norm/testdata/limit index 4dafc98fdaf4..113dffd611b0 100644 --- a/pkg/sql/opt/norm/testdata/limit +++ b/pkg/sql/opt/norm/testdata/limit @@ -27,9 +27,11 @@ SELECT k, f*2.0 FROM a LIMIT 5 ---- project ├── columns: k:1(int!null) column6:6(float) + ├── keys: (1) ├── scan a │ ├── columns: a.k:1(int!null) a.f:3(float) - │ └── limit: 5 + │ ├── limit: 5 + │ └── keys: (1) └── projections [outer=(1,3)] ├── variable: a.k [type=int, outer=(1)] └── mult [type=float, outer=(3)] @@ -54,6 +56,7 @@ project │ │ ├── group-by │ │ │ ├── columns: a.i:2(int) a.f:3(float) │ │ │ ├── grouping columns: a.i:2(int) a.f:3(float) + │ │ │ ├── keys: weak(2,3) │ │ │ ├── scan a │ │ │ │ └── columns: a.i:2(int) a.f:3(float) │ │ │ └── aggregations @@ -74,10 +77,13 @@ SELECT k, f*2.0 FROM a OFFSET 5 ---- project ├── columns: k:1(int!null) column6:6(float) + ├── keys: (1) ├── offset │ ├── columns: a.k:1(int!null) a.f:3(float) + │ ├── keys: (1) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.f:3(float) + │ │ ├── columns: a.k:1(int!null) a.f:3(float) + │ │ └── keys: (1) │ └── const: 5 [type=int] └── projections [outer=(1,3)] ├── variable: a.k [type=int, outer=(1)] @@ -103,6 +109,7 @@ project │ │ ├── group-by │ │ │ ├── columns: a.i:2(int) a.f:3(float) │ │ │ ├── grouping columns: a.i:2(int) a.f:3(float) + │ │ │ ├── keys: weak(2,3) │ │ │ ├── scan a │ │ │ │ └── columns: a.i:2(int) a.f:3(float) │ │ │ └── aggregations @@ -123,12 +130,16 @@ SELECT k, f*2.0 FROM a OFFSET 5 LIMIT 10 ---- project ├── columns: k:1(int!null) column6:6(float) + ├── keys: (1) ├── limit │ ├── columns: a.k:1(int!null) a.f:3(float) + │ ├── keys: (1) │ ├── offset │ │ ├── columns: a.k:1(int!null) a.f:3(float) + │ │ ├── keys: (1) │ │ ├── scan a - │ │ │ └── columns: a.k:1(int!null) a.f:3(float) + │ │ │ ├── columns: a.k:1(int!null) a.f:3(float) + │ │ │ └── keys: (1) │ │ └── const: 5 [type=int] │ └── const: 10 [type=int] └── projections [outer=(1,3)] @@ -157,6 +168,7 @@ project │ │ │ ├── group-by │ │ │ │ ├── columns: a.i:2(int) a.f:3(float) │ │ │ │ ├── grouping columns: a.i:2(int) a.f:3(float) + │ │ │ │ ├── keys: weak(2,3) │ │ │ │ ├── scan a │ │ │ │ │ └── columns: a.i:2(int) a.f:3(float) │ │ │ │ └── aggregations diff --git a/pkg/sql/opt/norm/testdata/project b/pkg/sql/opt/norm/testdata/project index dcb5ac047afa..c10aa6bdd90b 100644 --- a/pkg/sql/opt/norm/testdata/project +++ b/pkg/sql/opt/norm/testdata/project @@ -27,21 +27,24 @@ opt SELECT x, y FROM t.a ---- scan a - └── columns: x:1(int!null) y:2(int) + ├── columns: x:1(int!null) y:2(int) + └── keys: (1) # Different order, aliased names. opt SELECT a.y AS aliasy, a.x FROM t.a ---- scan a - └── columns: aliasy:2(int) x:1(int!null) + ├── columns: aliasy:2(int) x:1(int!null) + └── keys: (1) # Reordered, duplicate, aliased columns. opt SELECT a.y AS alias1, a.x, a.y AS alias1, a.x FROM t.a ---- scan a - └── columns: alias1:2(int) x:1(int!null) alias1:2(int) x:1(int!null) + ├── columns: alias1:2(int) x:1(int!null) alias1:2(int) x:1(int!null) + └── keys: (1) # Added column (projection should not be eliminated). opt @@ -49,8 +52,10 @@ SELECT *, 1 FROM t.a ---- project ├── columns: x:1(int!null) y:2(int) f:3(float) s:4(string) column5:5(int) + ├── keys: (1) ├── scan a - │ └── columns: a.x:1(int!null) a.y:2(int) a.f:3(float) a.s:4(string) + │ ├── columns: a.x:1(int!null) a.y:2(int) a.f:3(float) a.s:4(string) + │ └── keys: (1) └── projections [outer=(1-4)] ├── variable: a.x [type=int, outer=(1)] ├── variable: a.y [type=int, outer=(2)] @@ -70,9 +75,11 @@ project ├── inner-join │ ├── columns: a.x:1(int!null) a.y:2(int) b.x:5(int!null) │ ├── scan a - │ │ └── columns: a.x:1(int!null) a.y:2(int) + │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:5(int!null) + │ │ ├── columns: b.x:5(int!null) + │ │ └── keys: (5) │ └── filters [type=bool, outer=(1,5)] │ └── eq [type=bool, outer=(1,5)] │ ├── variable: a.x [type=int, outer=(1)] @@ -91,7 +98,8 @@ opt SELECT x FROM (SELECT x, y+1 FROM t.a) a ---- scan a - └── columns: x:1(int!null) + ├── columns: x:1(int!null) + └── keys: (1) # Discard all columns. opt @@ -110,7 +118,8 @@ SELECT x+y FROM (SELECT y, x, s || 'foo' FROM t.a) a project ├── columns: column6:6(int) ├── scan a - │ └── columns: a.x:1(int!null) a.y:2(int) + │ ├── columns: a.x:1(int!null) a.y:2(int) + │ └── keys: (1) └── projections [outer=(1,2)] └── plus [type=int, outer=(1,2)] ├── variable: a.x [type=int, outer=(1)] @@ -122,8 +131,10 @@ SELECT l, x FROM (SELECT length(s) l, * FROM a) a ---- project ├── columns: l:5(int) x:1(int!null) + ├── keys: (1) ├── scan a - │ └── columns: a.x:1(int!null) a.s:4(string) + │ ├── columns: a.x:1(int!null) a.s:4(string) + │ └── keys: (1) └── projections [outer=(1,4)] ├── function: length [type=int, outer=(4)] │ └── variable: a.s [type=string, outer=(4)] @@ -135,10 +146,13 @@ SELECT l*l, x FROM (SELECT x, length(s) l, y FROM a) a ---- project ├── columns: column6:6(int) x:1(int!null) + ├── keys: (1) ├── project │ ├── columns: a.x:1(int!null) l:5(int) + │ ├── keys: (1) │ ├── scan a - │ │ └── columns: a.x:1(int!null) a.s:4(string) + │ │ ├── columns: a.x:1(int!null) a.s:4(string) + │ │ └── keys: (1) │ └── projections [outer=(1,4)] │ ├── variable: a.x [type=int, outer=(1)] │ └── function: length [type=int, outer=(4)] @@ -158,7 +172,8 @@ opt SELECT x FROM t.a ---- scan a - └── columns: x:1(int!null) + ├── columns: x:1(int!null) + └── keys: (1) # Project subset of columns, some used in computed columns. opt @@ -166,8 +181,10 @@ SELECT x, x+1, y+1 FROM t.a ---- project ├── columns: x:1(int!null) column5:5(int) column6:6(int) + ├── keys: (1) ├── scan a - │ └── columns: a.x:1(int!null) a.y:2(int) + │ ├── columns: a.x:1(int!null) a.y:2(int) + │ └── keys: (1) └── projections [outer=(1,2)] ├── variable: a.x [type=int, outer=(1)] ├── plus [type=int, outer=(1)] @@ -184,7 +201,8 @@ SELECT x+y FROM t.a project ├── columns: column5:5(int) ├── scan a - │ └── columns: a.x:1(int!null) a.y:2(int) + │ ├── columns: a.x:1(int!null) a.y:2(int) + │ └── keys: (1) └── projections [outer=(1,2)] └── plus [type=int, outer=(1,2)] ├── variable: a.x [type=int, outer=(1)] @@ -210,10 +228,13 @@ SELECT x FROM t.a WHERE y<5 ---- project ├── columns: x:1(int!null) + ├── keys: (1) ├── select │ ├── columns: a.x:1(int!null) a.y:2(int) + │ ├── keys: (1) │ ├── scan a - │ │ └── columns: a.x:1(int!null) a.y:2(int) + │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ └── keys: (1) │ └── filters [type=bool, outer=(2), constraints=(/2: (/NULL - /4]; tight)] │ └── lt [type=bool, outer=(2), constraints=(/2: (/NULL - /4]; tight)] │ ├── variable: a.y [type=int, outer=(2)] @@ -227,9 +248,11 @@ SELECT x, y FROM t.a WHERE x=1 AND y<5 ---- select ├── columns: x:1(int!null) y:2(int) + ├── keys: (1) ├── scan a │ ├── columns: a.x:1(int!null) a.y:2(int) - │ └── constraint: /1: [/1 - /1] + │ ├── constraint: /1: [/1 - /1] + │ └── keys: (1) └── filters [type=bool, outer=(2), constraints=(/2: (/NULL - /4]; tight)] └── lt [type=bool, outer=(2), constraints=(/2: (/NULL - /4]; tight)] ├── variable: a.y [type=int, outer=(2)] @@ -258,8 +281,10 @@ project ├── columns: column5:5(int) column6:6(int) ├── select │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ ├── keys: (1) │ ├── scan a - │ │ └── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ └── keys: (1) │ └── filters [type=bool, outer=(1,4)] │ ├── lt [type=bool, outer=(1)] │ │ ├── variable: a.x [type=int, outer=(1)] @@ -291,7 +316,8 @@ project │ │ ├── columns: a.y:2(int) f:5(float) │ │ ├── scan a │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.f:3(float) - │ │ │ └── constraint: /1: [/5 - /5] + │ │ │ ├── constraint: /1: [/5 - /5] + │ │ │ └── keys: (1) │ │ └── projections [outer=(2,3)] │ │ ├── variable: a.y [type=int, outer=(2)] │ │ └── plus [type=float, outer=(3)] @@ -313,11 +339,14 @@ project ├── columns: f:3(float) column6:6(float) ├── select │ ├── columns: a.x:1(int!null) a.f:3(float) column5:5(decimal) + │ ├── keys: (1) │ ├── group-by │ │ ├── columns: a.x:1(int!null) a.f:3(float) column5:5(decimal) │ │ ├── grouping columns: a.x:1(int!null) a.f:3(float) + │ │ ├── keys: (1) │ │ ├── scan a - │ │ │ └── columns: a.x:1(int!null) a.f:3(float) + │ │ │ ├── columns: a.x:1(int!null) a.f:3(float) + │ │ │ └── keys: (1) │ │ └── aggregations [outer=(1)] │ │ └── function: sum [type=decimal, outer=(1)] │ │ └── variable: a.x [type=int, outer=(1)] @@ -341,13 +370,17 @@ SELECT x FROM (SELECT x, y, f FROM t.a ORDER BY y LIMIT 10) ---- project ├── columns: x:1(int!null) + ├── keys: (1) ├── limit │ ├── columns: a.x:1(int!null) a.y:2(int) + │ ├── keys: (1) │ ├── sort │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ ├── keys: (1) │ │ ├── ordering: +2 │ │ └── scan a - │ │ └── columns: a.x:1(int!null) a.y:2(int) + │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ └── keys: (1) │ └── const: 10 [type=int] └── projections [outer=(1)] └── variable: a.x [type=int, outer=(1)] @@ -360,7 +393,8 @@ project ├── columns: s:4(string) ├── scan a │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) - │ └── limit: 10 + │ ├── limit: 10 + │ └── keys: (1) └── projections [outer=(4)] └── variable: a.s [type=string, outer=(4)] @@ -370,16 +404,18 @@ SELECT x, s FROM (SELECT x, y, f, s FROM t.a ORDER BY x, y LIMIT 10) ---- project ├── columns: x:1(int!null) s:4(string) + ├── keys: (1) ├── scan a │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) - │ └── limit: 10 + │ ├── limit: 10 + │ └── keys: (1) └── projections [outer=(1,4)] ├── variable: a.x [type=int, outer=(1)] └── variable: a.s [type=string, outer=(4)] # Project filter limit columns, but can't push all the way down to scan. opt -SELECT f, f*2.0 FROM (SELECT f, x FROM a GROUP BY f, x LIMIT 5) a +SELECT f, f*2.0 FROM (SELECT f, s FROM a GROUP BY f, s LIMIT 5) a ---- project ├── columns: f:3(float) column5:5(float) @@ -388,10 +424,11 @@ project │ ├── project │ │ ├── columns: a.f:3(float) │ │ ├── group-by - │ │ │ ├── columns: a.x:1(int!null) a.f:3(float) - │ │ │ ├── grouping columns: a.x:1(int!null) a.f:3(float) + │ │ │ ├── columns: a.f:3(float) a.s:4(string) + │ │ │ ├── grouping columns: a.f:3(float) a.s:4(string) + │ │ │ ├── keys: weak(3,4) │ │ │ ├── scan a - │ │ │ │ └── columns: a.x:1(int!null) a.f:3(float) + │ │ │ │ └── columns: a.f:3(float) a.s:4(string) │ │ │ └── aggregations │ │ └── projections [outer=(3)] │ │ └── variable: a.f [type=float, outer=(3)] @@ -411,13 +448,17 @@ SELECT x FROM (SELECT x, y, f FROM t.a ORDER BY y OFFSET 10) ---- project ├── columns: x:1(int!null) + ├── keys: (1) ├── offset │ ├── columns: a.x:1(int!null) a.y:2(int) + │ ├── keys: (1) │ ├── sort │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ ├── keys: (1) │ │ ├── ordering: +2 │ │ └── scan a - │ │ └── columns: a.x:1(int!null) a.y:2(int) + │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ └── keys: (1) │ └── const: 10 [type=int] └── projections [outer=(1)] └── variable: a.x [type=int, outer=(1)] @@ -430,8 +471,10 @@ project ├── columns: s:4(string) ├── offset │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ ├── keys: (1) │ ├── scan a │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ ├── keys: (1) │ │ └── ordering: +1,+2 │ └── const: 10 [type=int] └── projections [outer=(4)] @@ -443,10 +486,13 @@ SELECT x, s FROM (SELECT x, y, f, s FROM t.a ORDER BY x, y OFFSET 10) ---- project ├── columns: x:1(int!null) s:4(string) + ├── keys: (1) ├── offset │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ ├── keys: (1) │ ├── scan a │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ ├── keys: (1) │ │ └── ordering: +1,+2 │ └── const: 10 [type=int] └── projections [outer=(1,4)] @@ -455,7 +501,7 @@ project # Project filter offset columns, but can't push all the way down to scan. opt -SELECT f, f*2.0 FROM (SELECT f, x FROM a GROUP BY f, x OFFSET 5) a +SELECT f, f*2.0 FROM (SELECT f, s FROM a GROUP BY f, s OFFSET 5) a ---- project ├── columns: f:3(float) column5:5(float) @@ -464,10 +510,11 @@ project │ ├── project │ │ ├── columns: a.f:3(float) │ │ ├── group-by - │ │ │ ├── columns: a.x:1(int!null) a.f:3(float) - │ │ │ ├── grouping columns: a.x:1(int!null) a.f:3(float) + │ │ │ ├── columns: a.f:3(float) a.s:4(string) + │ │ │ ├── grouping columns: a.f:3(float) a.s:4(string) + │ │ │ ├── keys: weak(3,4) │ │ │ ├── scan a - │ │ │ │ └── columns: a.x:1(int!null) a.f:3(float) + │ │ │ │ └── columns: a.f:3(float) a.s:4(string) │ │ │ └── aggregations │ │ └── projections [outer=(3)] │ │ └── variable: a.f [type=float, outer=(3)] @@ -487,16 +534,21 @@ SELECT x FROM (SELECT x, y, f FROM t.a ORDER BY y LIMIT 10 OFFSET 10) ---- project ├── columns: x:1(int!null) + ├── keys: (1) ├── limit │ ├── columns: a.x:1(int!null) a.y:2(int) + │ ├── keys: (1) │ ├── offset │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ ├── keys: (1) │ │ ├── ordering: +2 │ │ ├── sort │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ │ ├── keys: (1) │ │ │ ├── ordering: +2 │ │ │ └── scan a - │ │ │ └── columns: a.x:1(int!null) a.y:2(int) + │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ │ └── keys: (1) │ │ └── const: 10 [type=int] │ └── const: 10 [type=int] └── projections [outer=(1)] @@ -510,11 +562,14 @@ project ├── columns: s:4(string) ├── limit │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ ├── keys: (1) │ ├── offset │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── ordering: +1,+2 │ │ ├── scan a │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ │ ├── keys: (1) │ │ │ └── ordering: +1,+2 │ │ └── const: 10 [type=int] │ └── const: 10 [type=int] @@ -527,13 +582,17 @@ SELECT x, s FROM (SELECT x, y, f, s FROM t.a ORDER BY x, y LIMIT 10 OFFSET 10) ---- project ├── columns: x:1(int!null) s:4(string) + ├── keys: (1) ├── limit │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ ├── keys: (1) │ ├── offset │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── ordering: +1,+2 │ │ ├── scan a │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ │ ├── keys: (1) │ │ │ └── ordering: +1,+2 │ │ └── const: 10 [type=int] │ └── const: 10 [type=int] @@ -543,7 +602,7 @@ project # Project filter offset/limit columns, but can't push all the way down to scan. opt -SELECT f, f*2.0 FROM (SELECT f, x FROM a GROUP BY f, x OFFSET 5 LIMIT 5) a +SELECT f, f*2.0 FROM (SELECT f, s FROM a GROUP BY f, s OFFSET 5 LIMIT 5) a ---- project ├── columns: f:3(float) column5:5(float) @@ -554,10 +613,11 @@ project │ │ ├── project │ │ │ ├── columns: a.f:3(float) │ │ │ ├── group-by - │ │ │ │ ├── columns: a.x:1(int!null) a.f:3(float) - │ │ │ │ ├── grouping columns: a.x:1(int!null) a.f:3(float) + │ │ │ │ ├── columns: a.f:3(float) a.s:4(string) + │ │ │ │ ├── grouping columns: a.f:3(float) a.s:4(string) + │ │ │ │ ├── keys: weak(3,4) │ │ │ │ ├── scan a - │ │ │ │ │ └── columns: a.x:1(int!null) a.f:3(float) + │ │ │ │ │ └── columns: a.f:3(float) a.s:4(string) │ │ │ │ └── aggregations │ │ │ └── projections [outer=(3)] │ │ │ └── variable: a.f [type=float, outer=(3)] @@ -582,9 +642,11 @@ project ├── inner-join │ ├── columns: a.x:1(int!null) a.y:2(int) b.x:5(int!null) b.z:6(int) │ ├── scan a - │ │ └── columns: a.x:1(int!null) a.y:2(int) + │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:5(int!null) b.z:6(int) + │ │ ├── columns: b.x:5(int!null) b.z:6(int) + │ │ └── keys: (5) │ └── filters [type=bool, outer=(1,5)] │ └── eq [type=bool, outer=(1,5)] │ ├── variable: a.x [type=int, outer=(1)] @@ -601,9 +663,11 @@ SELECT a.x, a.y, b.* FROM t.a LEFT JOIN t.b ON a.x=b.x AND a.y<5 left-join ├── columns: x:1(int!null) y:2(int) x:5(int) z:6(int) ├── scan a - │ └── columns: a.x:1(int!null) a.y:2(int) + │ ├── columns: a.x:1(int!null) a.y:2(int) + │ └── keys: (1) ├── scan b - │ └── columns: b.x:5(int!null) b.z:6(int) + │ ├── columns: b.x:5(int!null) b.z:6(int) + │ └── keys: (5) └── filters [type=bool, outer=(1,2,5), constraints=(/2: (/NULL - /4])] ├── eq [type=bool, outer=(1,5)] │ ├── variable: a.x [type=int, outer=(1)] @@ -621,9 +685,11 @@ project ├── right-join │ ├── columns: a.x:1(int) b.x:5(int!null) b.z:6(int) │ ├── scan a - │ │ └── columns: a.x:1(int!null) + │ │ ├── columns: a.x:1(int!null) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:5(int!null) b.z:6(int) + │ │ ├── columns: b.x:5(int!null) b.z:6(int) + │ │ └── keys: (5) │ └── filters [type=bool, outer=(1,5)] │ └── eq [type=bool, outer=(1,5)] │ ├── variable: a.x [type=int, outer=(1)] @@ -641,9 +707,11 @@ project ├── full-join │ ├── columns: a.x:1(int) b.x:5(int) b.z:6(int) │ ├── scan a - │ │ └── columns: a.x:1(int!null) + │ │ ├── columns: a.x:1(int!null) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:5(int!null) b.z:6(int) + │ │ ├── columns: b.x:5(int!null) b.z:6(int) + │ │ └── keys: (5) │ └── true [type=bool] └── projections [outer=(1,5,6)] ├── plus [type=int, outer=(1)] @@ -660,7 +728,8 @@ inner-join ├── columns: x:5(int!null) z:6(int) ├── scan a ├── scan b - │ └── columns: b.x:5(int!null) b.z:6(int) + │ ├── columns: b.x:5(int!null) b.z:6(int) + │ └── keys: (5) └── true [type=bool] # Computed columns. @@ -673,10 +742,13 @@ project │ ├── columns: a.x:1(int!null) a.y:2(int) b.x:5(int!null) b.z:6(int) │ ├── project │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ ├── keys: (1) │ │ ├── select │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ │ ├── keys: (1) │ │ │ ├── scan a - │ │ │ │ └── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) a.s:4(string) + │ │ │ │ └── keys: (1) │ │ │ └── filters [type=bool, outer=(4)] │ │ │ └── eq [type=bool, outer=(4)] │ │ │ ├── concat [type=string, outer=(4)] @@ -687,7 +759,8 @@ project │ │ ├── variable: a.x [type=int, outer=(1)] │ │ └── variable: a.y [type=int, outer=(2)] │ ├── scan b - │ │ └── columns: b.x:5(int!null) b.z:6(int) + │ │ ├── columns: b.x:5(int!null) b.z:6(int) + │ │ └── keys: (5) │ └── filters [type=bool, outer=(1,5)] │ └── eq [type=bool, outer=(1,5)] │ ├── variable: b.x [type=int, outer=(5)] @@ -723,9 +796,11 @@ project │ │ ├── inner-join │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) b.x:5(int!null) │ │ │ ├── scan a - │ │ │ │ └── columns: a.x:1(int!null) a.y:2(int) + │ │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ │ │ └── keys: (1) │ │ │ ├── scan b - │ │ │ │ └── columns: b.x:5(int!null) + │ │ │ │ ├── columns: b.x:5(int!null) + │ │ │ │ └── keys: (5) │ │ │ └── filters [type=bool, outer=(1,5)] │ │ │ └── eq [type=bool, outer=(1,5)] │ │ │ ├── variable: a.x [type=int, outer=(1)] @@ -734,7 +809,8 @@ project │ │ ├── variable: a.x [type=int, outer=(1)] │ │ └── variable: a.y [type=int, outer=(2)] │ ├── scan b - │ │ └── columns: b.x:7(int!null) b.z:8(int) + │ │ ├── columns: b.x:7(int!null) b.z:8(int) + │ │ └── keys: (7) │ └── filters [type=bool, outer=(2,8)] │ └── lt [type=bool, outer=(2,8)] │ ├── variable: a.y [type=int, outer=(2)] @@ -757,9 +833,11 @@ project ├── inner-join │ ├── columns: b.x:1(int!null) b.z:2(int) a.x:3(int!null) a.y:4(int) │ ├── scan b - │ │ └── columns: b.x:1(int!null) b.z:2(int) + │ │ ├── columns: b.x:1(int!null) b.z:2(int) + │ │ └── keys: (1) │ ├── scan a - │ │ └── columns: a.x:3(int!null) a.y:4(int) + │ │ ├── columns: a.x:3(int!null) a.y:4(int) + │ │ └── keys: (3) │ └── filters [type=bool, outer=(1,3)] │ └── eq [type=bool, outer=(1,3)] │ ├── variable: b.x [type=int, outer=(1)] @@ -776,9 +854,11 @@ SELECT b.*, a.x, a.y FROM t.b LEFT JOIN t.a ON b.x=a.x AND a.y 5) ---- project ├── columns: x:1(int!null) + ├── keys: (1) ├── select │ ├── columns: a.x:1(int!null) column5:5(decimal) + │ ├── keys: (1) │ ├── group-by │ │ ├── columns: a.x:1(int!null) column5:5(decimal) │ │ ├── grouping columns: a.x:1(int!null) + │ │ ├── keys: (1) │ │ ├── scan a - │ │ │ └── columns: a.x:1(int!null) a.y:2(int) + │ │ │ ├── columns: a.x:1(int!null) a.y:2(int) + │ │ │ └── keys: (1) │ │ └── aggregations [outer=(2)] │ │ └── function: sum [type=decimal, outer=(2)] │ │ └── variable: a.y [type=int, outer=(2)] diff --git a/pkg/sql/opt/norm/testdata/scalar b/pkg/sql/opt/norm/testdata/scalar index e06d3155aac3..1c9f1330355b 100644 --- a/pkg/sql/opt/norm/testdata/scalar +++ b/pkg/sql/opt/norm/testdata/scalar @@ -34,7 +34,8 @@ FROM a project ├── columns: column7:7(bool) column8:8(bool) column9:9(bool) column10:10(bool) column11:11(int) column12:12(int) column13:13(int) column14:14(int) column15:15(int) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) + │ ├── columns: a.k:1(int!null) a.i:2(int) + │ └── keys: (1) └── projections [outer=(1,2)] ├── eq [type=bool, outer=(1,2)] │ ├── variable: a.k [type=int, outer=(1)] @@ -102,7 +103,8 @@ FROM a project ├── columns: column7:7(bool) column8:8(bool) column9:9(bool) column10:10(bool) column11:11(float) column12:12(int) column13:13(int) column14:14(int) column15:15(int) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) + │ └── keys: (1) └── projections [outer=(1-3)] ├── eq [type=bool, outer=(1,2)] │ ├── plus [type=int, outer=(1,2)] diff --git a/pkg/sql/opt/norm/testdata/select b/pkg/sql/opt/norm/testdata/select index a4103a94cd3d..536dcad31917 100644 --- a/pkg/sql/opt/norm/testdata/select +++ b/pkg/sql/opt/norm/testdata/select @@ -27,8 +27,10 @@ SELECT * FROM a WHERE i=5 AND s<'foo' ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2,4), constraints=(/2: [/5 - /5]; /4: (/NULL - /'foo'); tight)] ├── eq [type=bool, outer=(2), constraints=(/2: [/5 - /5]; tight)] │ ├── variable: a.i [type=int, outer=(2)] @@ -45,8 +47,10 @@ SELECT * FROM a WHERE i<5 ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2), constraints=(/2: (/NULL - /4]; tight)] └── lt [type=bool, outer=(2), constraints=(/2: (/NULL - /4]; tight)] ├── variable: a.i [type=int, outer=(2)] @@ -57,8 +61,10 @@ SELECT * FROM a WHERE i<5 OR s='foo' ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2,4)] └── or [type=bool, outer=(2,4)] ├── lt [type=bool, outer=(2), constraints=(/2: (/NULL - /4]; tight)] @@ -73,14 +79,16 @@ opt SELECT * FROM a WHERE True ---- scan a - └── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + └── keys: (1) opt SELECT * FROM a WHERE False ---- scan a ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) - └── constraint: /1: contradiction + ├── constraint: /1: contradiction + └── keys: (1) # -------------------------------------------------- # EliminateSelect @@ -89,7 +97,8 @@ opt SELECT * FROM a WHERE True ---- scan a - └── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + └── keys: (1) # -------------------------------------------------- # MergeSelects @@ -99,29 +108,34 @@ SELECT * FROM (SELECT * FROM a WHERE False) WHERE s='foo' ---- scan a ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) - └── constraint: /1: contradiction + ├── constraint: /1: contradiction + └── keys: (1) opt SELECT * FROM (SELECT * FROM a WHERE i=1) WHERE False ---- scan a ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) - └── constraint: /1: contradiction + ├── constraint: /1: contradiction + └── keys: (1) opt SELECT * FROM (SELECT * FROM a WHERE i=1) WHERE False ---- scan a ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) - └── constraint: /1: contradiction + ├── constraint: /1: contradiction + └── keys: (1) opt SELECT * FROM (SELECT * FROM a WHERE i<5) WHERE s='foo' ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(2,4), constraints=(/2: (/NULL - /4]; /4: [/'foo' - /'foo']; tight)] ├── lt [type=bool, outer=(2), constraints=(/2: (/NULL - /4]; tight)] │ ├── variable: a.i [type=int, outer=(2)] @@ -135,8 +149,10 @@ SELECT * FROM (SELECT * FROM a WHERE i>1 AND i<10) WHERE s='foo' OR k=5 ---- select ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + ├── keys: (1) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) └── filters [type=bool, outer=(1,2,4), constraints=(/2: [/2 - /9])] ├── gt [type=bool, outer=(2), constraints=(/2: [/2 - ]; tight)] │ ├── variable: a.i [type=int, outer=(2)] @@ -235,9 +251,11 @@ project ├── columns: f:3(float) column7:7(float) ├── select │ ├── columns: a.i:2(int) a.f:3(float) column6:6(float) + │ ├── keys: weak(2,3) │ ├── group-by │ │ ├── columns: a.i:2(int) a.f:3(float) column6:6(float) │ │ ├── grouping columns: a.i:2(int) a.f:3(float) + │ │ ├── keys: weak(2,3) │ │ ├── scan a │ │ │ └── columns: a.i:2(int) a.f:3(float) │ │ └── aggregations [outer=(3)] @@ -264,14 +282,17 @@ inner-join ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int) ├── select │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── keys: (1) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ └── filters [type=bool, outer=(3), constraints=(/3: [/1.1 - /1.1]; tight)] │ └── eq [type=bool, outer=(3), constraints=(/3: [/1.1 - /1.1]; tight)] │ ├── variable: a.f [type=float, outer=(3)] │ └── const: 1.1 [type=float] ├── scan b - │ └── columns: b.x:6(int!null) b.y:7(int) + │ ├── columns: b.x:6(int!null) b.y:7(int) + │ └── keys: (6) └── filters [type=bool, outer=(1,6)] └── eq [type=bool, outer=(1,6)] ├── variable: a.k [type=int, outer=(1)] @@ -286,8 +307,10 @@ select │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.y:7(int) │ ├── select │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── keys: (1) │ │ ├── scan a - │ │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ │ └── keys: (1) │ │ └── filters [type=bool, outer=(3,4), constraints=(/3: [/1.1 - /1.1])] │ │ ├── eq [type=bool, outer=(3), constraints=(/3: [/1.1 - /1.1]; tight)] │ │ │ ├── variable: a.f [type=float, outer=(3)] @@ -300,7 +323,8 @@ select │ │ ├── variable: a.s [type=string, outer=(4)] │ │ └── const: 'bar' [type=string] │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.y:7(int) + │ │ ├── columns: b.x:6(int!null) b.y:7(int) + │ │ └── keys: (6) │ └── filters [type=bool, outer=(1,6)] │ └── eq [type=bool, outer=(1,6)] │ ├── variable: a.k [type=int, outer=(1)] @@ -318,8 +342,10 @@ inner-join ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int) ├── select │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── keys: (1) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ └── filters [type=bool, outer=(2), constraints=(/2: [/100 - /100])] │ ├── eq [type=bool, outer=(2), constraints=(/2: [/100 - /100]; tight)] │ │ ├── variable: a.i [type=int, outer=(2)] @@ -328,7 +354,8 @@ inner-join │ ├── function: now [type=timestamptz] │ └── const: '2000-01-01 01:00:00+00:00' [type=timestamptz] ├── scan b - │ └── columns: b.x:6(int!null) b.y:7(int) + │ ├── columns: b.x:6(int!null) b.y:7(int) + │ └── keys: (6) └── true [type=bool] # Don't push down conditions in case of RIGHT JOIN. @@ -340,9 +367,11 @@ select ├── right-join │ ├── columns: a.k:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int!null) b.y:7(int) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.y:7(int) + │ │ ├── columns: b.x:6(int!null) b.y:7(int) + │ │ └── keys: (6) │ └── filters [type=bool, outer=(1,6)] │ └── eq [type=bool, outer=(1,6)] │ ├── variable: a.k [type=int, outer=(1)] @@ -361,9 +390,11 @@ select ├── full-join │ ├── columns: a.k:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.y:7(int) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.y:7(int) + │ │ ├── columns: b.x:6(int!null) b.y:7(int) + │ │ └── keys: (6) │ └── filters [type=bool, outer=(1,6)] │ └── eq [type=bool, outer=(1,6)] │ ├── variable: a.k [type=int, outer=(1)] @@ -382,11 +413,14 @@ SELECT * FROM b INNER JOIN a ON b.x=a.k WHERE a.f=1.1 inner-join ├── columns: x:1(int!null) y:2(int) k:3(int!null) i:4(int) f:5(float) s:6(string) j:7(jsonb) ├── scan b - │ └── columns: b.x:1(int!null) b.y:2(int) + │ ├── columns: b.x:1(int!null) b.y:2(int) + │ └── keys: (1) ├── select │ ├── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ ├── keys: (3) │ ├── scan a - │ │ └── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ ├── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ └── keys: (3) │ └── filters [type=bool, outer=(5), constraints=(/5: [/1.1 - /1.1]; tight)] │ └── eq [type=bool, outer=(5), constraints=(/5: [/1.1 - /1.1]; tight)] │ ├── variable: a.f [type=float, outer=(5)] @@ -404,11 +438,14 @@ select ├── right-join │ ├── columns: b.x:1(int) b.y:2(int) a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) │ ├── scan b - │ │ └── columns: b.x:1(int!null) b.y:2(int) + │ │ ├── columns: b.x:1(int!null) b.y:2(int) + │ │ └── keys: (1) │ ├── select │ │ ├── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ ├── keys: (3) │ │ ├── scan a - │ │ │ └── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ │ ├── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ │ └── keys: (3) │ │ └── filters [type=bool, outer=(5,6), constraints=(/5: [/1.1 - /1.1])] │ │ ├── eq [type=bool, outer=(5), constraints=(/5: [/1.1 - /1.1]; tight)] │ │ │ ├── variable: a.f [type=float, outer=(5)] @@ -438,9 +475,11 @@ select ├── left-join │ ├── columns: b.x:1(int!null) b.y:2(int) a.k:3(int) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) │ ├── scan b - │ │ └── columns: b.x:1(int!null) b.y:2(int) + │ │ ├── columns: b.x:1(int!null) b.y:2(int) + │ │ └── keys: (1) │ ├── scan a - │ │ └── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ ├── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ └── keys: (3) │ └── filters [type=bool, outer=(1,3)] │ └── eq [type=bool, outer=(1,3)] │ ├── variable: a.k [type=int, outer=(3)] @@ -459,9 +498,11 @@ select ├── full-join │ ├── columns: b.x:1(int) b.y:2(int) a.k:3(int) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) │ ├── scan b - │ │ └── columns: b.x:1(int!null) b.y:2(int) + │ │ ├── columns: b.x:1(int!null) b.y:2(int) + │ │ └── keys: (1) │ ├── scan a - │ │ └── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ ├── columns: a.k:3(int!null) a.i:4(int) a.f:5(float) a.s:6(string) a.j:7(jsonb) + │ │ └── keys: (3) │ └── filters [type=bool, outer=(1,3)] │ └── eq [type=bool, outer=(1,3)] │ ├── variable: a.k [type=int, outer=(3)] @@ -480,9 +521,11 @@ SELECT * FROM a, b WHERE a.k=b.x AND (a.s='foo' OR b.y<100) inner-join ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) ├── scan b - │ └── columns: b.x:6(int!null) b.y:7(int) + │ ├── columns: b.x:6(int!null) b.y:7(int) + │ └── keys: (6) └── filters [type=bool, outer=(1,4,6,7)] ├── eq [type=bool, outer=(1,6)] │ ├── variable: a.k [type=int, outer=(1)] @@ -501,9 +544,11 @@ SELECT * FROM a INNER JOIN b ON a.k=b.x WHERE (a.s='foo' OR b.y<100) inner-join ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) ├── scan b - │ └── columns: b.x:6(int!null) b.y:7(int) + │ ├── columns: b.x:6(int!null) b.y:7(int) + │ └── keys: (6) └── filters [type=bool, outer=(1,4,6,7)] ├── eq [type=bool, outer=(1,6)] │ ├── variable: a.k [type=int, outer=(1)] @@ -522,9 +567,11 @@ SELECT * FROM a INNER JOIN b ON a.k=b.x WHERE False inner-join ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int) ├── scan a - │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ └── keys: (1) ├── scan b - │ └── columns: b.x:6(int!null) b.y:7(int) + │ ├── columns: b.x:6(int!null) b.y:7(int) + │ └── keys: (6) └── false [type=bool] # Don't merge with LEFT JOIN. @@ -536,9 +583,11 @@ select ├── left-join │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.y:7(int) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.y:7(int) + │ │ ├── columns: b.x:6(int!null) b.y:7(int) + │ │ └── keys: (6) │ └── true [type=bool] └── filters [type=bool, outer=(1,6)] └── eq [type=bool, outer=(1,6)] @@ -554,9 +603,11 @@ select ├── right-join │ ├── columns: a.k:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int!null) b.y:7(int) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.y:7(int) + │ │ ├── columns: b.x:6(int!null) b.y:7(int) + │ │ └── keys: (6) │ └── true [type=bool] └── filters [type=bool, outer=(1,6)] └── eq [type=bool, outer=(1,6)] @@ -572,9 +623,11 @@ select ├── full-join │ ├── columns: a.k:1(int) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) b.x:6(int) b.y:7(int) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.y:7(int) + │ │ ├── columns: b.x:6(int!null) b.y:7(int) + │ │ └── keys: (6) │ └── true [type=bool] └── filters [type=bool, outer=(1,6)] └── eq [type=bool, outer=(1,6)] @@ -591,8 +644,10 @@ inner-join ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int) ├── select │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── keys: (1) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ └── filters [type=bool, outer=(3,4), constraints=(/3: [/1.1 - /1.1]; /4: [/'foo' - /'foo']; tight)] │ ├── eq [type=bool, outer=(3), constraints=(/3: [/1.1 - /1.1]; tight)] │ │ ├── variable: a.f [type=float, outer=(3)] @@ -602,8 +657,10 @@ inner-join │ └── const: 'foo' [type=string] ├── select │ ├── columns: b.x:6(int!null) b.y:7(int) + │ ├── keys: (6) │ ├── scan b - │ │ └── columns: b.x:6(int!null) b.y:7(int) + │ │ ├── columns: b.x:6(int!null) b.y:7(int) + │ │ └── keys: (6) │ └── filters [type=bool, outer=(7), constraints=(/7: [/10 - /10]; tight)] │ └── eq [type=bool, outer=(7), constraints=(/7: [/10 - /10]; tight)] │ ├── variable: b.y [type=int, outer=(7)] @@ -623,8 +680,10 @@ inner-join ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) x:6(int!null) y:7(int) ├── select │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ ├── keys: (1) │ ├── scan a - │ │ └── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.f:3(float) a.s:4(string) a.j:5(jsonb) + │ │ └── keys: (1) │ └── filters [type=bool, outer=(2), constraints=(/2: [/100 - /100])] │ ├── eq [type=bool, outer=(2), constraints=(/2: [/100 - /100]; tight)] │ │ ├── variable: a.i [type=int, outer=(2)] @@ -633,7 +692,8 @@ inner-join │ ├── function: now [type=timestamptz] │ └── const: '2000-01-01 01:00:00+00:00' [type=timestamptz] ├── scan b - │ └── columns: b.x:6(int!null) b.y:7(int) + │ ├── columns: b.x:6(int!null) b.y:7(int) + │ └── keys: (6) └── filters [type=bool, outer=(1,6)] └── eq [type=bool, outer=(1,6)] ├── variable: b.x [type=int, outer=(6)] @@ -650,6 +710,7 @@ SELECT * FROM (SELECT i, COUNT(*) FROM a GROUP BY i) a WHERE i=1 group-by ├── columns: i:2(int) column6:6(int) ├── grouping columns: a.i:2(int) + ├── keys: weak(2) ├── select │ ├── columns: a.i:2(int) │ ├── scan a @@ -668,6 +729,7 @@ SELECT * FROM (SELECT i FROM a GROUP BY i) a WHERE i=1 group-by ├── columns: i:2(int) ├── grouping columns: a.i:2(int) + ├── keys: weak(2) ├── select │ ├── columns: a.i:2(int) │ ├── scan a @@ -684,13 +746,17 @@ SELECT * FROM (SELECT k, i, MAX(s) m FROM a GROUP BY k, i) a WHERE i=k AND m='fo ---- select ├── columns: k:1(int!null) i:2(int) m:6(string) + ├── keys: (1) ├── group-by │ ├── columns: a.k:1(int!null) a.i:2(int) m:6(string) │ ├── grouping columns: a.k:1(int!null) a.i:2(int) + │ ├── keys: (1) │ ├── select │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.s:4(string) + │ │ ├── keys: (1) │ │ ├── scan a - │ │ │ └── columns: a.k:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ ├── columns: a.k:1(int!null) a.i:2(int) a.s:4(string) + │ │ │ └── keys: (1) │ │ └── filters [type=bool, outer=(1,2)] │ │ └── eq [type=bool, outer=(1,2)] │ │ ├── variable: a.i [type=int, outer=(2)] @@ -709,8 +775,10 @@ SELECT * FROM (SELECT COUNT(*) c FROM a) a WHERE now()<'2000-01-01T10:00:00' AND ---- select ├── columns: c:6(int) + ├── keys: () ├── group-by │ ├── columns: c:6(int) + │ ├── keys: () │ ├── scan a │ └── aggregations │ └── function: count_rows [type=int] diff --git a/pkg/sql/opt/rule_name.og.go b/pkg/sql/opt/rule_name.og.go index 3085e9978417..737c5543b526 100644 --- a/pkg/sql/opt/rule_name.og.go +++ b/pkg/sql/opt/rule_name.og.go @@ -27,6 +27,7 @@ const ( NormalizeTupleEquality FoldNullComparisonLeft FoldNullComparisonRight + EliminateDistinct EnsureJoinFiltersAnd EnsureJoinFilters PushFilterIntoJoinLeft diff --git a/pkg/sql/opt/rule_name_string.go b/pkg/sql/opt/rule_name_string.go index d872a75e7ba4..8034339cc98d 100644 --- a/pkg/sql/opt/rule_name_string.go +++ b/pkg/sql/opt/rule_name_string.go @@ -4,9 +4,9 @@ package opt import "strconv" -const _RuleName_name = "InvalidRuleNameNumManualRuleNamesEliminateEmptyAndEliminateEmptyOrEliminateSingletonAndOrSimplifyAndSimplifyOrSimplifyFiltersFoldNullAndOrNegateComparisonEliminateNotNegateAndNegateOrCommuteVarInequalityCommuteConstInequalityNormalizeCmpPlusConstNormalizeCmpMinusConstNormalizeCmpConstMinusNormalizeTupleEqualityFoldNullComparisonLeftFoldNullComparisonRightEnsureJoinFiltersAndEnsureJoinFiltersPushFilterIntoJoinLeftPushFilterIntoJoinRightPushLimitIntoProjectPushOffsetIntoProjectFoldPlusZeroFoldZeroPlusFoldMinusZeroFoldMultOneFoldOneMultFoldDivOneInvertMinusEliminateUnaryMinusEliminateProjectEliminateProjectProjectFilterUnusedProjectColsFilterUnusedScanColsFilterUnusedSelectColsFilterUnusedLimitColsFilterUnusedOffsetColsFilterUnusedJoinLeftColsFilterUnusedJoinRightColsFilterUnusedAggColsFilterUnusedGroupByColsFilterUnusedValueColsCommuteVarCommuteConstEliminateCoalesceSimplifyCoalesceEliminateCastFoldNullCastFoldNullUnaryFoldNullBinaryLeftFoldNullBinaryRightFoldNullInNonEmptyFoldNullInEmptyFoldNullNotInEmptyNormalizeInConstFoldInNullEnsureSelectFiltersAndEnsureSelectFiltersEliminateSelectMergeSelectsPushSelectIntoProjectPushSelectIntoJoinLeftPushSelectIntoJoinRightMergeSelectInnerJoinPushSelectIntoGroupByPushLimitIntoScanGenerateIndexScansConstrainScanNumRuleNames" +const _RuleName_name = "InvalidRuleNameNumManualRuleNamesEliminateEmptyAndEliminateEmptyOrEliminateSingletonAndOrSimplifyAndSimplifyOrSimplifyFiltersFoldNullAndOrNegateComparisonEliminateNotNegateAndNegateOrCommuteVarInequalityCommuteConstInequalityNormalizeCmpPlusConstNormalizeCmpMinusConstNormalizeCmpConstMinusNormalizeTupleEqualityFoldNullComparisonLeftFoldNullComparisonRightEliminateDistinctEnsureJoinFiltersAndEnsureJoinFiltersPushFilterIntoJoinLeftPushFilterIntoJoinRightPushLimitIntoProjectPushOffsetIntoProjectFoldPlusZeroFoldZeroPlusFoldMinusZeroFoldMultOneFoldOneMultFoldDivOneInvertMinusEliminateUnaryMinusEliminateProjectEliminateProjectProjectFilterUnusedProjectColsFilterUnusedScanColsFilterUnusedSelectColsFilterUnusedLimitColsFilterUnusedOffsetColsFilterUnusedJoinLeftColsFilterUnusedJoinRightColsFilterUnusedAggColsFilterUnusedGroupByColsFilterUnusedValueColsCommuteVarCommuteConstEliminateCoalesceSimplifyCoalesceEliminateCastFoldNullCastFoldNullUnaryFoldNullBinaryLeftFoldNullBinaryRightFoldNullInNonEmptyFoldNullInEmptyFoldNullNotInEmptyNormalizeInConstFoldInNullEnsureSelectFiltersAndEnsureSelectFiltersEliminateSelectMergeSelectsPushSelectIntoProjectPushSelectIntoJoinLeftPushSelectIntoJoinRightMergeSelectInnerJoinPushSelectIntoGroupByPushLimitIntoScanGenerateIndexScansConstrainScanNumRuleNames" -var _RuleName_index = [...]uint16{0, 15, 33, 50, 66, 89, 100, 110, 125, 138, 154, 166, 175, 183, 203, 225, 246, 268, 290, 312, 334, 357, 377, 394, 416, 439, 459, 480, 492, 504, 517, 528, 539, 549, 560, 579, 595, 618, 641, 661, 683, 704, 726, 750, 775, 794, 817, 838, 848, 860, 877, 893, 906, 918, 931, 949, 968, 986, 1001, 1019, 1035, 1045, 1067, 1086, 1101, 1113, 1134, 1156, 1179, 1199, 1220, 1237, 1255, 1268, 1280} +var _RuleName_index = [...]uint16{0, 15, 33, 50, 66, 89, 100, 110, 125, 138, 154, 166, 175, 183, 203, 225, 246, 268, 290, 312, 334, 357, 374, 394, 411, 433, 456, 476, 497, 509, 521, 534, 545, 556, 566, 577, 596, 612, 635, 658, 678, 700, 721, 743, 767, 792, 811, 834, 855, 865, 877, 894, 910, 923, 935, 948, 966, 985, 1003, 1018, 1036, 1052, 1062, 1084, 1103, 1118, 1130, 1151, 1173, 1196, 1216, 1237, 1254, 1272, 1285, 1297} func (i RuleName) String() string { if i >= RuleName(len(_RuleName_index)-1) { diff --git a/pkg/sql/opt/xform/testdata/coster/join b/pkg/sql/opt/xform/testdata/coster/join index 08e925028c6d..aa0031a66202 100644 --- a/pkg/sql/opt/xform/testdata/coster/join +++ b/pkg/sql/opt/xform/testdata/coster/join @@ -30,14 +30,17 @@ inner-join │ ├── columns: a.k:1(int!null) │ ├── stats: [rows=100] │ ├── cost: 1100.00 + │ ├── keys: (1) │ ├── select │ │ ├── columns: a.k:1(int!null) a.d:4(decimal!null) │ │ ├── stats: [rows=100] │ │ ├── cost: 1100.00 + │ │ ├── keys: (1) │ │ ├── scan a │ │ │ ├── columns: a.k:1(int!null) a.d:4(decimal!null) │ │ │ ├── stats: [rows=1000] - │ │ │ └── cost: 1000.00 + │ │ │ ├── cost: 1000.00 + │ │ │ └── keys: (1) │ │ └── filters [type=bool, outer=(4), constraints=(/4: [/1.0 - /1.0]; tight)] │ │ └── eq [type=bool, outer=(4), constraints=(/4: [/1.0 - /1.0]; tight)] │ │ ├── variable: a.d [type=decimal, outer=(4)] diff --git a/pkg/sql/opt/xform/testdata/coster/scan b/pkg/sql/opt/xform/testdata/coster/scan index b2970f917c08..64f69f92bd08 100644 --- a/pkg/sql/opt/xform/testdata/coster/scan +++ b/pkg/sql/opt/xform/testdata/coster/scan @@ -15,4 +15,5 @@ SELECT k, s FROM a scan a ├── columns: k:1(int!null) s:3(string) ├── stats: [rows=1000] - └── cost: 1000.00 + ├── cost: 1000.00 + └── keys: (1) diff --git a/pkg/sql/opt/xform/testdata/coster/select b/pkg/sql/opt/xform/testdata/coster/select index aaea5e7f5e07..9693af39d4dc 100644 --- a/pkg/sql/opt/xform/testdata/coster/select +++ b/pkg/sql/opt/xform/testdata/coster/select @@ -16,10 +16,12 @@ select ├── columns: k:1(int!null) s:3(string) ├── stats: [rows=100] ├── cost: 1100.00 + ├── keys: (1) ├── scan a │ ├── columns: a.k:1(int!null) a.s:3(string) │ ├── stats: [rows=1000] - │ └── cost: 1000.00 + │ ├── cost: 1000.00 + │ └── keys: (1) └── filters [type=bool, outer=(3), constraints=(/3: [/'foo' - ]; tight)] └── ge [type=bool, outer=(3), constraints=(/3: [/'foo' - ]; tight)] ├── variable: a.s [type=string, outer=(3)] diff --git a/pkg/sql/opt/xform/testdata/rules/limit b/pkg/sql/opt/xform/testdata/rules/limit index c7d9d044b193..9eccc61f0c40 100644 --- a/pkg/sql/opt/xform/testdata/rules/limit +++ b/pkg/sql/opt/xform/testdata/rules/limit @@ -38,7 +38,8 @@ SELECT * FROM a LIMIT 1 ---- scan a ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) - └── limit: 1 + ├── limit: 1 + └── keys: (1) # Combine limit with needed columns. opt @@ -97,9 +98,11 @@ limit └── subquery [type=int, outer=(6)] ├── max1-row │ ├── columns: a.k:6(int!null) + │ ├── keys: (6) │ └── scan a │ ├── columns: a.k:6(int!null) - │ └── limit: 1 + │ ├── limit: 1 + │ └── keys: (6) └── variable: a.k [type=int, outer=(6)] memo diff --git a/pkg/sql/opt/xform/testdata/rules/scan b/pkg/sql/opt/xform/testdata/rules/scan index c6b5453e4ddd..2c43e4423996 100644 --- a/pkg/sql/opt/xform/testdata/rules/scan +++ b/pkg/sql/opt/xform/testdata/rules/scan @@ -39,6 +39,7 @@ SELECT s, i, f FROM a ORDER BY s, k, i ---- scan a@s_idx ├── columns: s:4(string) i:2(int) f:3(float) + ├── keys: (1) └── ordering: +4,+1,+2 memo @@ -64,6 +65,7 @@ SELECT s, i, f FROM a ORDER BY k, i, s ---- scan a ├── columns: s:4(string) i:2(int) f:3(float) + ├── keys: (1) └── ordering: +1,+2,+4 memo @@ -114,6 +116,7 @@ SELECT i, k FROM a ORDER BY s DESC, i, k ---- scan a@si_idx ├── columns: i:2(int) k:1(int!null) + ├── keys: (1) └── ordering: -4,+2,+1 memo diff --git a/pkg/sql/opt/xform/testdata/rules/select b/pkg/sql/opt/xform/testdata/rules/select index 021c85fd8231..d0b8b01a6e50 100644 --- a/pkg/sql/opt/xform/testdata/rules/select +++ b/pkg/sql/opt/xform/testdata/rules/select @@ -32,7 +32,8 @@ SELECT k FROM a WHERE k = 1 ---- scan a ├── columns: k:1(int!null) - └── constraint: /1: [/1 - /1] + ├── constraint: /1: [/1 - /1] + └── keys: (1) memo SELECT k FROM a WHERE k = 1 @@ -59,9 +60,11 @@ SELECT k FROM a WHERE v > 1 ---- project ├── columns: k:1(int!null) + ├── keys: (1) ├── scan a@v │ ├── columns: a.k:1(int!null) a.v:3(int) - │ └── constraint: /3: [/2 - ] + │ ├── constraint: /3: [/2 - ] + │ └── keys: (1) weak(3) └── projections [outer=(1)] └── variable: a.k [type=int, outer=(1)] @@ -94,9 +97,11 @@ SELECT k FROM a WHERE u = 1 AND k = 5 ---- project ├── columns: k:1(int!null) + ├── keys: (1) ├── scan a@u │ ├── columns: a.k:1(int!null) a.u:2(int) - │ └── constraint: /2/1: [/1/5 - /1/5] + │ ├── constraint: /2/1: [/1/5 - /1/5] + │ └── keys: (1) └── projections [outer=(1)] └── variable: a.k [type=int, outer=(1)] @@ -137,11 +142,14 @@ SELECT k FROM a WHERE u = 1 AND k+u = 1 ---- project ├── columns: k:1(int!null) + ├── keys: (1) ├── select │ ├── columns: a.k:1(int!null) a.u:2(int) + │ ├── keys: (1) │ ├── scan a@u │ │ ├── columns: a.k:1(int!null) a.u:2(int) - │ │ └── constraint: /2/1: [/1 - /1] + │ │ ├── constraint: /2/1: [/1 - /1] + │ │ └── keys: (1) │ └── filters [type=bool, outer=(1,2)] │ └── eq [type=bool, outer=(1,2)] │ ├── plus [type=int, outer=(1,2)] @@ -187,11 +195,14 @@ SELECT k FROM a WHERE u = 1 AND v = 5 ---- project ├── columns: k:1(int!null) + ├── keys: (1) ├── select │ ├── columns: a.k:1(int!null) a.u:2(int) a.v:3(int) + │ ├── keys: (1) weak(3) │ ├── scan a@u │ │ ├── columns: a.k:1(int!null) a.u:2(int) a.v:3(int) - │ │ └── constraint: /2/1: [/1 - /1] + │ │ ├── constraint: /2/1: [/1 - /1] + │ │ └── keys: (1) weak(3) │ └── filters [type=bool, outer=(3), constraints=(/3: [/5 - /5]; tight)] │ └── eq [type=bool, outer=(3), constraints=(/3: [/5 - /5]; tight)] │ ├── variable: a.v [type=int, outer=(3)] @@ -239,11 +250,14 @@ SELECT k FROM a WHERE u=v ---- project ├── columns: k:1(int!null) + ├── keys: (1) ├── select │ ├── columns: a.k:1(int!null) a.u:2(int) a.v:3(int) + │ ├── keys: (1) weak(3) │ ├── scan a@u │ │ ├── columns: a.k:1(int!null) a.u:2(int) a.v:3(int) - │ │ └── constraint: /2/1: (/NULL - ] + │ │ ├── constraint: /2/1: (/NULL - ] + │ │ └── keys: (1) weak(3) │ └── filters [type=bool, outer=(2,3)] │ └── eq [type=bool, outer=(2,3)] │ ├── variable: a.u [type=int, outer=(2)] @@ -257,9 +271,11 @@ SELECT k FROM (SELECT k FROM a ORDER BY u LIMIT 1) a WHERE k = 1 ---- select ├── columns: k:1(int!null) + ├── keys: (1) ├── scan a@u │ ├── columns: a.k:1(int!null) a.u:2(int) - │ └── limit: 1 + │ ├── limit: 1 + │ └── keys: (1) └── filters [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight)] └── eq [type=bool, outer=(1), constraints=(/1: [/1 - /1]; tight)] ├── variable: a.k [type=int, outer=(1)]