From 4e55f39ef337cff3a3848838755866ab246d8505 Mon Sep 17 00:00:00 2001 From: Andrew Kimball Date: Mon, 31 Dec 2018 12:00:06 -0700 Subject: [PATCH] opt: Inline constant values Inline constants in expressions like: SELECT x, x+1 FROM (VALUES (1)) AS t(x) ; with the new inlining rules, this becomes: VALUES (1, 2) The new inlining rules are useful for mutation expressions (e.g. UPDATE), which can nest multiple Project and Values expressions that often use constant values. For example: CREATE TABLE ab (a INT PRIMARY KEY, b INT AS (a + 1) STORED); UPDATE ab SET a=1 This now gets mapped by the optimizer to this internal equivalent: UPDATE ab SET a=1, b=2 Release note: None --- pkg/sql/opt/exec/execbuilder/testdata/explain | 8 +- pkg/sql/opt/exec/execbuilder/testdata/orderby | 29 +- pkg/sql/opt/memo/testdata/logprops/join | 14 +- pkg/sql/opt/norm/custom_funcs.go | 11 + pkg/sql/opt/norm/inline.go | 94 +++++ pkg/sql/opt/norm/rules/inline.opt | 77 +++- pkg/sql/opt/norm/testdata/rules/inline | 367 +++++++++++++++--- pkg/sql/opt/norm/testdata/rules/join | 26 +- pkg/sql/opt/norm/testdata/rules/project | 6 +- 9 files changed, 543 insertions(+), 89 deletions(-) diff --git a/pkg/sql/opt/exec/execbuilder/testdata/explain b/pkg/sql/opt/exec/execbuilder/testdata/explain index d981523db389..8c23b9a7ea2e 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/explain +++ b/pkg/sql/opt/exec/execbuilder/testdata/explain @@ -601,11 +601,9 @@ values · · (a int) · query TTTTT EXPLAIN (TYPES) SELECT x[1] FROM (SELECT ARRAY[1,2,3] AS x) ---- -render · · (x int) · - │ render 0 ((x)[int[]][(1)[int]])[int] · · - └── values · · (x int[]) · -· size 1 column, 1 row · · -· row 0, expr 0 (ARRAY[(1)[int],(2)[int],(3)[int]])[int[]] · · +values · · (x int) · +· size 1 column, 1 row · · +· row 0, expr 0 (1)[int] · · query T EXPLAIN (OPT) SELECT 1 AS r diff --git a/pkg/sql/opt/exec/execbuilder/testdata/orderby b/pkg/sql/opt/exec/execbuilder/testdata/orderby index 8a7da2aac07b..4944851b4d8b 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/orderby +++ b/pkg/sql/opt/exec/execbuilder/testdata/orderby @@ -730,17 +730,24 @@ render · · (k) · # query TTTTT -EXPLAIN (VERBOSE) SELECT k FROM kv JOIN (VALUES (1,2)) AS z(a,b) ON kv.k = z.a ORDER BY INDEX kv@foo ----- -render · · (k) · - │ render 0 k · · - └── lookup-join · · (column1, k) · - │ type inner · · - ├── values · · (column1) · - │ size 1 column, 1 row · · - │ row 0, expr 0 1 · · - └── scan · · (k) · -· table kv@primary · · +EXPLAIN (VERBOSE) +SELECT k FROM kv JOIN (VALUES (1,2), (3,4)) AS z(a,b) ON kv.k = z.a ORDER BY INDEX kv@foo +---- +render · · (k) · + │ render 0 k · · + └── sort · · (k, v) -v,+k + │ order -v,+k · · + └── render · · (k, v) · + │ render 0 k · · + │ render 1 v · · + └── lookup-join · · (column1, k, v) · + │ type inner · · + ├── values · · (column1) · + │ size 1 column, 2 rows · · + │ row 0, expr 0 1 · · + │ row 1, expr 0 3 · · + └── scan · · (k, v) · +· table kv@primary · · query TTTTT EXPLAIN (VERBOSE) SELECT k FROM kv a NATURAL JOIN kv ORDER BY INDEX kv@foo diff --git a/pkg/sql/opt/memo/testdata/logprops/join b/pkg/sql/opt/memo/testdata/logprops/join index 2428bc47f2d8..cfab7d46591a 100644 --- a/pkg/sql/opt/memo/testdata/logprops/join +++ b/pkg/sql/opt/memo/testdata/logprops/join @@ -838,21 +838,19 @@ semi-join-apply # Calculate anti-join cardinality when left side has non-zero cardinality. opt -SELECT * FROM (SELECT * FROM (VALUES (1))) WHERE NOT EXISTS(SELECT * FROM uv WHERE u=column1) +SELECT * FROM (SELECT * FROM (VALUES (1), (2))) WHERE NOT EXISTS(SELECT * FROM uv WHERE u=column1) ---- anti-join ├── columns: column1:1(int) - ├── cardinality: [0 - 1] - ├── key: () - ├── fd: ()-->(1) + ├── cardinality: [0 - 2] ├── values │ ├── columns: column1:1(int) - │ ├── cardinality: [1 - 1] - │ ├── key: () - │ ├── fd: ()-->(1) + │ ├── cardinality: [2 - 2] │ ├── prune: (1) + │ ├── tuple [type=tuple{int}] + │ │ └── const: 1 [type=int] │ └── tuple [type=tuple{int}] - │ └── const: 1 [type=int] + │ └── const: 2 [type=int] ├── scan uv │ ├── columns: u:2(int) v:3(int!null) │ └── prune: (2,3) diff --git a/pkg/sql/opt/norm/custom_funcs.go b/pkg/sql/opt/norm/custom_funcs.go index 1f46db534c9e..2c5f19cfb15f 100644 --- a/pkg/sql/opt/norm/custom_funcs.go +++ b/pkg/sql/opt/norm/custom_funcs.go @@ -277,6 +277,11 @@ func (c *CustomFuncs) CanHaveZeroRows(input memo.RelExpr) bool { return input.Relational().Cardinality.CanBeZero() } +// ColsAreEmpty returns true if the column set is empty. +func (c *CustomFuncs) ColsAreEmpty(cols opt.ColSet) bool { + return cols.Empty() +} + // ColsAreSubset returns true if the left columns are a subset of the right // columns. func (c *CustomFuncs) ColsAreSubset(left, right opt.ColSet) bool { @@ -288,6 +293,12 @@ func (c *CustomFuncs) ColsAreEqual(left, right opt.ColSet) bool { return left.Equals(right) } +// ColsIntersect returns true if at least one column appears in both the left +// and right sets. +func (c *CustomFuncs) ColsIntersect(left, right opt.ColSet) bool { + return left.Intersects(right) +} + // UnionCols returns the union of the left and right column sets. func (c *CustomFuncs) UnionCols(left, right opt.ColSet) opt.ColSet { return left.Union(right) diff --git a/pkg/sql/opt/norm/inline.go b/pkg/sql/opt/norm/inline.go index 56feec123d96..36be9ec6a377 100644 --- a/pkg/sql/opt/norm/inline.go +++ b/pkg/sql/opt/norm/inline.go @@ -19,6 +19,100 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" ) +// FindInlinableConstants returns the set of input columns that are synthesized +// constant value expressions: ConstOp, TrueOp, FalseOp, or NullOp. Constant +// value expressions can often be inlined into referencing expressions. Only +// Project and Values operators synthesize constant value expressions. +func (c *CustomFuncs) FindInlinableConstants(input memo.RelExpr) opt.ColSet { + var cols opt.ColSet + if project, ok := input.(*memo.ProjectExpr); ok { + for i := range project.Projections { + item := &project.Projections[i] + if opt.IsConstValueOp(item.Element) { + cols.Add(int(item.Col)) + } + } + } else if values, ok := input.(*memo.ValuesExpr); ok && len(values.Rows) == 1 { + tup := values.Rows[0].(*memo.TupleExpr) + for i, scalar := range tup.Elems { + if opt.IsConstValueOp(scalar) { + cols.Add(int(values.Cols[i])) + } + } + } + return cols +} + +// InlineProjectionConstants recursively searches each projection expression and +// replaces any references to input columns that are constant. It returns a new +// Projections list containing the replaced expressions. +func (c *CustomFuncs) InlineProjectionConstants( + projections memo.ProjectionsExpr, input memo.RelExpr, constCols opt.ColSet, +) memo.ProjectionsExpr { + newProjections := make(memo.ProjectionsExpr, len(projections)) + for i := range projections { + item := &projections[i] + newProjections[i].Col = item.Col + newProjections[i].Element = c.inlineConstants(item.Element, input, constCols).(opt.ScalarExpr) + } + return newProjections +} + +// InlineFilterConstants recursively searches each filter expression and +// replaces any references to input columns that are constant. It returns a new +// Filters list containing the replaced expressions. +func (c *CustomFuncs) InlineFilterConstants( + filters memo.FiltersExpr, input memo.RelExpr, constCols opt.ColSet, +) memo.FiltersExpr { + newFilters := make(memo.FiltersExpr, len(filters)) + for i := range filters { + item := &filters[i] + newFilters[i].Condition = c.inlineConstants(item.Condition, input, constCols).(opt.ScalarExpr) + } + return newFilters +} + +// inlineConstants recursively searches the given expression and replaces any +// references to input columns that are constant. It returns the replaced +// expression. +func (c *CustomFuncs) inlineConstants( + e opt.Expr, input memo.RelExpr, constCols opt.ColSet, +) opt.Expr { + var replace ReconstructFunc + replace = func(e opt.Expr) opt.Expr { + switch t := e.(type) { + case *memo.VariableExpr: + if constCols.Contains(int(t.Col)) { + return c.extractColumn(input, t.Col) + } + return t + } + return c.f.Reconstruct(e, replace) + } + return replace(e) +} + +// extractColumn searches a Project or Values input expression for the column +// having the given id. It returns the expression for that column. +func (c *CustomFuncs) extractColumn(input memo.RelExpr, col opt.ColumnID) opt.ScalarExpr { + if project, ok := input.(*memo.ProjectExpr); ok { + for i := range project.Projections { + item := &project.Projections[i] + if item.Col == col { + return item.Element + } + } + } else if values, ok := input.(*memo.ValuesExpr); ok && len(values.Rows) == 1 { + tup := values.Rows[0].(*memo.TupleExpr) + for i, scalar := range tup.Elems { + if values.Cols[i] == col { + return scalar + } + } + } + panic("could not find column to extract") +} + // HasDuplicateRefs returns true if the target projection expressions or // passthrough columns reference any outer column more than one time, or if the // projection expressions contain a correlated subquery. For example: diff --git a/pkg/sql/opt/norm/rules/inline.opt b/pkg/sql/opt/norm/rules/inline.opt index b316a134814a..5ef85f869970 100644 --- a/pkg/sql/opt/norm/rules/inline.opt +++ b/pkg/sql/opt/norm/rules/inline.opt @@ -8,13 +8,82 @@ # # SELECT (x+1)+1 FROM a # -# Inlining variables can result in the complete elimination of Project -# operators, or at least in the ability to more freely reorder them within the -# larger relational expression tree. This allows pushing filters further down -# the tree, as well as with pulling them up in the decorrelation case. +# Inlining variables can result in the simplification or even complete +# elimination of operators, or at least in the ability to more freely reorder +# them within the larger relational expression tree. This allows pushing filters +# further down the tree, as well as with pulling them up in the decorrelation +# case. # ============================================================================= +# InlineProjectConstants finds variable references in Projections expressions +# that refer to constant input values, and then inlines those constant values +# in place of the corresponding variable references. This sometimes allows +# further simplifications such as constant folding or Project merging. +[InlineProjectConstants, Normalize] +(Project + $input:* & ^(ColsAreEmpty $constCols:(FindInlinableConstants $input)) + $projections:[ ... $item:* & (ColsIntersect (OuterCols $item) $constCols) ... ] + $passthrough:* +) +=> +(Project + $input + (InlineProjectionConstants $projections $input $constCols) + $passthrough +) + +# InlineSelectConstants finds variable references in Filters expressions that +# refer to constant input values, and then inlines those constant values in +# place of the corresponding variable references. This sometimes allows further +# simplifications such as constant folding or generation of constrained scans. +[InlineSelectConstants, Normalize] +(Select + $input:* & ^(ColsAreEmpty $constCols:(FindInlinableConstants $input)) + $filters:[ ... $item:* & (ColsIntersect (OuterCols $item) $constCols) ... ] +) +=> +(Select + $input + (InlineFilterConstants $filters $input $constCols) +) + +# InlineJoinConstantsLeft finds variable references in a join condition that +# refers to constant values projected by the left input. It then inlines those +# constant values in place of the corresponding variable references. This +# sometimes allows further simplifications such as constant folding or filter +# pushdown. +[InlineJoinConstantsLeft, Normalize] +(Join + $left:* & ^(ColsAreEmpty $constCols:(FindInlinableConstants $left)) + $right:* + $on:[ ... $item:* & (ColsIntersect (OuterCols $item) $constCols) ... ] +) +=> +((OpName) + $left + $right + (InlineFilterConstants $on $left $constCols) +) + +# InlineJoinConstantsRight finds variable references in a join condition that +# refers to constant values projected by the right input. It then inlines those +# constant values in place of the corresponding variable references. This +# sometimes allows further simplifications such as constant folding or filter +# pushdown. +[InlineJoinConstantsRight, Normalize] +(Join + $left:* + $right:* & ^(ColsAreEmpty $constCols:(FindInlinableConstants $right)) + $on:[ ... $item:* & (ColsIntersect (OuterCols $item) $constCols) ... ] +) +=> +((OpName) + $left + $right + (InlineFilterConstants $on $right $constCols) +) + # PushSelectIntoInlinableProject pushes the Select operator into a Project, even # though the filter references it. This is made possible by inlining the # references to projected columns so that the Select becomes independent of the diff --git a/pkg/sql/opt/norm/testdata/rules/inline b/pkg/sql/opt/norm/testdata/rules/inline index e09d3f55cdf0..617fd13b30d4 100644 --- a/pkg/sql/opt/norm/testdata/rules/inline +++ b/pkg/sql/opt/norm/testdata/rules/inline @@ -19,6 +19,284 @@ TABLE xy └── INDEX primary └── x int not null +exec-ddl +CREATE TABLE computed (a INT PRIMARY KEY, b INT, c INT AS (a+b+1) STORED) +---- +TABLE computed + ├── a int not null + ├── b int + ├── c int + └── INDEX primary + └── a int not null + +# -------------------------------------------------- +# InlineProjectConstants +# -------------------------------------------------- + +# Inline constants from Project expression. +opt expect=InlineProjectConstants +UPDATE computed SET a=1, b=2 +---- +update computed + ├── columns: + ├── fetch columns: a:4(int) b:5(int) c:6(int) + ├── update-mapping: + │ ├── column7:7 => a:1 + │ ├── column8:8 => b:2 + │ └── column9:9 => c:3 + ├── cardinality: [0 - 0] + ├── side-effects, mutations + └── project + ├── columns: column9:9(int!null) column7:7(int!null) column8:8(int!null) a:4(int!null) b:5(int) c:6(int) + ├── key: (4) + ├── fd: ()-->(7-9), (4)-->(5,6) + ├── scan computed + │ ├── columns: a:4(int!null) b:5(int) c:6(int) + │ ├── key: (4) + │ └── fd: (4)-->(5,6) + └── projections + ├── const: 4 [type=int] + ├── const: 1 [type=int] + └── const: 2 [type=int] + +# Inline constants from Values expression. +opt expect=InlineProjectConstants +SELECT one+two+three+four FROM (VALUES (1, $1:::int, 2, $2:::int)) AS t(one, two, three, four) +---- +project + ├── columns: "?column?":5(int) + ├── cardinality: [1 - 1] + ├── has-placeholder + ├── key: () + ├── fd: ()-->(5) + ├── values + │ ├── columns: column2:2(int) column4:4(int) + │ ├── cardinality: [1 - 1] + │ ├── has-placeholder + │ ├── key: () + │ ├── fd: ()-->(2,4) + │ └── ($1, $2) [type=tuple{int, int}] + └── projections + └── column4 + ((column2 + 1) + 2) [type=int, outer=(2,4)] + +# Multiple constant columns, multiple refs to each, interspersed with other +# columns. +opt expect=InlineProjectConstants +SELECT one+two, x, one*two, y FROM (SELECT x, 1 AS one, y, 2 AS two FROM xy) +---- +project + ├── columns: "?column?":5(int!null) x:1(int!null) "?column?":6(int!null) y:2(int) + ├── key: (1) + ├── fd: ()-->(5,6), (1)-->(2) + ├── scan xy + │ ├── columns: x:1(int!null) y:2(int) + │ ├── key: (1) + │ └── fd: (1)-->(2) + └── projections + ├── const: 3 [type=int] + └── const: 2 [type=int] + +# Constant column reference within correlated subquery (which becomes +# uncorrelated as a result). +opt expect=InlineProjectConstants +SELECT EXISTS(SELECT * FROM a WHERE k=one AND i=two) FROM (VALUES (1, 2)) AS t(one, two) +---- +values + ├── columns: exists:8(bool) + ├── cardinality: [1 - 1] + ├── key: () + ├── fd: ()-->(8) + └── tuple [type=tuple{bool}] + └── exists [type=bool] + └── select + ├── columns: k:3(int!null) i:4(int!null) f:5(float) s:6(string) j:7(jsonb) + ├── cardinality: [0 - 1] + ├── key: () + ├── fd: ()-->(3-7) + ├── scan a + │ ├── columns: k:3(int!null) i:4(int) f:5(float) s:6(string) j:7(jsonb) + │ ├── constraint: /3: [/1 - /1] + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ └── fd: ()-->(3-7) + └── filters + └── i = 2 [type=bool, outer=(4), constraints=(/4: [/2 - /2]; tight), fd=()-->(4)] + +# Do not inline constants from Values expression with multiple rows. +opt expect-not=InlineProjectConstants +SELECT one+two FROM (VALUES (1, 2), (3, 4)) AS t(one, two) +---- +project + ├── columns: "?column?":3(int) + ├── cardinality: [2 - 2] + ├── values + │ ├── columns: column1:1(int) column2:2(int) + │ ├── cardinality: [2 - 2] + │ ├── (1, 2) [type=tuple{int, int}] + │ └── (3, 4) [type=tuple{int, int}] + └── projections + └── column1 + column2 [type=int, outer=(1,2)] + +# -------------------------------------------------- +# InlineSelectConstants +# -------------------------------------------------- + +# Inline constants from Project expression. +opt expect=InlineSelectConstants +SELECT * FROM (SELECT 1 AS one from xy) WHERE one > 0 +---- +project + ├── columns: one:3(int!null) + ├── fd: ()-->(3) + ├── select + │ ├── scan xy + │ └── filters + │ └── 1 > 0 [type=bool] + └── projections + └── const: 1 [type=int] + +# Inline constants from Values expression. +opt expect=InlineSelectConstants +SELECT * +FROM (VALUES ($1:::int, 1, $2:::float, 2)) AS t(one, two, three, four) +WHERE one = two OR three = four +---- +select + ├── columns: one:1(int) two:2(int) three:3(float) four:4(int) + ├── cardinality: [0 - 1] + ├── has-placeholder + ├── key: () + ├── fd: ()-->(1-4) + ├── values + │ ├── columns: column1:1(int) column2:2(int) column3:3(float) column4:4(int) + │ ├── cardinality: [1 - 1] + │ ├── has-placeholder + │ ├── key: () + │ ├── fd: ()-->(1-4) + │ └── ($1, 1, $2, 2) [type=tuple{int, int, float, int}] + └── filters + └── (column1 = 1) OR (column3 = 2.0) [type=bool, outer=(1,3)] + +# Multiple constant columns, multiple refs to each, interspersed with other +# columns. +opt expect=InlineSelectConstants +SELECT * FROM (SELECT x, 1 AS one, y, 2 AS two FROM xy) WHERE x=one AND y=two +---- +project + ├── columns: x:1(int!null) one:3(int!null) y:2(int!null) two:4(int!null) + ├── cardinality: [0 - 1] + ├── key: () + ├── fd: ()-->(1-4) + ├── select + │ ├── columns: x:1(int!null) y:2(int!null) + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ ├── fd: ()-->(1,2) + │ ├── scan xy + │ │ ├── columns: x:1(int!null) y:2(int) + │ │ ├── constraint: /1: [/1 - /1] + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ └── fd: ()-->(1,2) + │ └── filters + │ └── y = 2 [type=bool, outer=(2), constraints=(/2: [/2 - /2]; tight), fd=()-->(2)] + └── projections + ├── const: 1 [type=int] + └── const: 2 [type=int] + +# Do not inline constants from Values expression with multiple rows. +opt expect-not=InlineSelectConstants +SELECT * FROM (VALUES (1, 2), (3, 4)) AS t(one, two) WHERE one=two +---- +select + ├── columns: one:1(int!null) two:2(int!null) + ├── cardinality: [0 - 2] + ├── fd: (1)==(2), (2)==(1) + ├── values + │ ├── columns: column1:1(int) column2:2(int) + │ ├── cardinality: [2 - 2] + │ ├── (1, 2) [type=tuple{int, int}] + │ └── (3, 4) [type=tuple{int, int}] + └── filters + └── column1 = column2 [type=bool, outer=(1,2), constraints=(/1: (/NULL - ]; /2: (/NULL - ]), fd=(1)==(2), (2)==(1)] + +# -------------------------------------------------- +# InlineJoinConstantsLeft + InlineJoinConstantsRight +# -------------------------------------------------- +opt expect=InlineJoinConstantsLeft +SELECT * FROM (SELECT 1 AS one) LEFT JOIN a ON k=one +---- +left-join + ├── columns: one:1(int) k:2(int) i:3(int) f:4(float) s:5(string) j:6(jsonb) + ├── cardinality: [1 - 1] + ├── key: () + ├── fd: ()-->(1-6) + ├── values + │ ├── columns: one:1(int) + │ ├── cardinality: [1 - 1] + │ ├── key: () + │ ├── fd: ()-->(1) + │ └── (1,) [type=tuple{int}] + ├── scan a + │ ├── columns: k:2(int!null) i:3(int) f:4(float) s:5(string) j:6(jsonb) + │ ├── constraint: /2: [/1 - /1] + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ └── fd: ()-->(2-6) + └── filters (true) + +opt expect=InlineJoinConstantsRight +SELECT * FROM a RIGHT JOIN (SELECT 1 AS one) ON k=one +---- +right-join + ├── columns: k:1(int) i:2(int) f:3(float) s:4(string) j:5(jsonb) one:6(int) + ├── cardinality: [1 - 1] + ├── key: () + ├── fd: ()-->(1-6) + ├── scan a + │ ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + │ ├── constraint: /1: [/1 - /1] + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ └── fd: ()-->(1-5) + ├── values + │ ├── columns: one:6(int) + │ ├── cardinality: [1 - 1] + │ ├── key: () + │ ├── fd: ()-->(6) + │ └── (1,) [type=tuple{int}] + └── filters (true) + +opt expect=(InlineJoinConstantsLeft,InlineJoinConstantsRight) +SELECT * FROM (SELECT 1 AS one) INNER JOIN (SELECT 2 AS two) ON one=two +---- +values + ├── columns: one:1(int) two:2(int) + ├── cardinality: [0 - 0] + ├── key: () + └── fd: ()-->(1,2) + +# Constant column exists in input, but is not referenced. +opt expect-not=(InlineJoinConstantsLeft,InlineJoinConstantsRight) +SELECT * FROM a INNER JOIN (SELECT 1 AS one, y FROM xy) ON k=y +---- +inner-join + ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) one:8(int!null) y:7(int!null) + ├── fd: ()-->(8), (1)-->(2-5), (1)==(7), (7)==(1) + ├── scan a + │ ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb) + │ ├── key: (1) + │ └── fd: (1)-->(2-5) + ├── project + │ ├── columns: one:8(int!null) y:7(int) + │ ├── fd: ()-->(8) + │ ├── scan xy + │ │ └── columns: y:7(int) + │ └── projections + │ └── const: 1 [type=int] + └── filters + └── k = y [type=bool, outer=(1,7), constraints=(/1: (/NULL - ]; /7: (/NULL - ]), fd=(1)==(7), (7)==(1)] + # -------------------------------------------------- # PushSelectIntoInlinableProject # -------------------------------------------------- @@ -140,7 +418,7 @@ TABLE zones norm SELECT subq_0.c0 AS c0 -FROM (SELECT 1 AS c0, 2 as c1) AS subq_0 +FROM (SELECT zone_id+1 AS c0, zone_id+2 as c1 FROM crdb_internal.zones) AS subq_0 WHERE 1 >= CASE @@ -177,51 +455,52 @@ LIMIT 107 ---- project - ├── columns: c0:1(int) - ├── cardinality: [0 - 1] + ├── columns: c0:6(int) + ├── cardinality: [0 - 107] ├── side-effects - ├── key: () - ├── fd: ()-->(1) - └── select - ├── columns: c0:1(int) c1:2(int) - ├── cardinality: [0 - 1] + └── limit + ├── columns: c0:6(int) c1:7(int) + ├── cardinality: [0 - 107] ├── side-effects - ├── key: () - ├── fd: ()-->(1,2) - ├── values - │ ├── columns: c0:1(int) c1:2(int) - │ ├── cardinality: [1 - 1] - │ ├── key: () - │ ├── fd: ()-->(1,2) - │ └── (1, 2) [type=tuple{int, int}] - └── filters - └── le [type=bool, outer=(1,2), side-effects] - ├── case [type=int] - │ ├── true [type=bool] - │ ├── when [type=int] - │ │ ├── c1 IS NOT NULL [type=bool] - │ │ └── function: extract [type=int] - │ │ ├── case [type=string] - │ │ │ ├── true [type=bool] - │ │ │ ├── when [type=string] - │ │ │ │ ├── exists [type=bool] - │ │ │ │ │ └── limit - │ │ │ │ │ ├── columns: config_yaml:5(bytes!null) - │ │ │ │ │ ├── outer: (1) - │ │ │ │ │ ├── cardinality: [0 - 52] - │ │ │ │ │ ├── select - │ │ │ │ │ │ ├── columns: config_yaml:5(bytes!null) - │ │ │ │ │ │ ├── outer: (1) - │ │ │ │ │ │ ├── scan ref_1 - │ │ │ │ │ │ │ └── columns: config_yaml:5(bytes!null) - │ │ │ │ │ │ └── filters - │ │ │ │ │ │ └── c0 IS NOT NULL [type=bool, outer=(1), constraints=(/1: (/NULL - ]; tight)] - │ │ │ │ │ └── const: 52 [type=int] - │ │ │ │ └── version() [type=string] - │ │ │ └── version() [type=string] - │ │ └── current_date() [type=date] - │ └── const: 1 [type=int] - └── const: 1 [type=int] + ├── select + │ ├── columns: c0:6(int) c1:7(int) + │ ├── side-effects + │ ├── project + │ │ ├── columns: c0:6(int) c1:7(int) + │ │ ├── scan zones + │ │ │ └── columns: zones.zone_id:1(int!null) + │ │ └── projections + │ │ ├── zones.zone_id + 1 [type=int, outer=(1)] + │ │ └── zones.zone_id + 2 [type=int, outer=(1)] + │ └── filters + │ └── le [type=bool, outer=(6,7), side-effects] + │ ├── case [type=int] + │ │ ├── true [type=bool] + │ │ ├── when [type=int] + │ │ │ ├── c1 IS NOT NULL [type=bool] + │ │ │ └── function: extract [type=int] + │ │ │ ├── case [type=string] + │ │ │ │ ├── true [type=bool] + │ │ │ │ ├── when [type=string] + │ │ │ │ │ ├── exists [type=bool] + │ │ │ │ │ │ └── limit + │ │ │ │ │ │ ├── columns: ref_1.config_yaml:10(bytes!null) + │ │ │ │ │ │ ├── outer: (6) + │ │ │ │ │ │ ├── cardinality: [0 - 52] + │ │ │ │ │ │ ├── select + │ │ │ │ │ │ │ ├── columns: ref_1.config_yaml:10(bytes!null) + │ │ │ │ │ │ │ ├── outer: (6) + │ │ │ │ │ │ │ ├── scan ref_1 + │ │ │ │ │ │ │ │ └── columns: ref_1.config_yaml:10(bytes!null) + │ │ │ │ │ │ │ └── filters + │ │ │ │ │ │ │ └── c0 IS NOT NULL [type=bool, outer=(6), constraints=(/6: (/NULL - ]; tight)] + │ │ │ │ │ │ └── const: 52 [type=int] + │ │ │ │ │ └── version() [type=string] + │ │ │ │ └── version() [type=string] + │ │ │ └── current_date() [type=date] + │ │ └── const: 1 [type=int] + │ └── const: 1 [type=int] + └── const: 107 [type=int] # -------------------------------------------------- # InlineProjectInProject diff --git a/pkg/sql/opt/norm/testdata/rules/join b/pkg/sql/opt/norm/testdata/rules/join index 18a32acd1dec..ac487bee4a04 100644 --- a/pkg/sql/opt/norm/testdata/rules/join +++ b/pkg/sql/opt/norm/testdata/rules/join @@ -822,30 +822,26 @@ project # Ensure that we do not map filters for types with composite key encoding. opt expect-not=(PushFilterIntoJoinLeftAndRight,MapFilterIntoJoinLeft,MapFilterIntoJoinRight) -SELECT * FROM (VALUES (1.0)) AS t1(x), (VALUES (1.00)) AS t2(y) WHERE x=y AND x::text = '1.0' +SELECT * +FROM (VALUES (1.0), (2.0)) AS t1(x), (VALUES (1.00), (2.00)) AS t2(y)WHERE x=y AND x::text = '1.0' ---- inner-join ├── columns: x:1(decimal!null) y:2(decimal!null) - ├── cardinality: [0 - 1] - ├── key: () - ├── fd: ()-->(1,2) + ├── cardinality: [0 - 4] + ├── fd: (1)==(2), (2)==(1) ├── values │ ├── columns: column1:2(decimal) - │ ├── cardinality: [1 - 1] - │ ├── key: () - │ ├── fd: ()-->(2) - │ └── (1.00,) [type=tuple{decimal}] + │ ├── cardinality: [2 - 2] + │ ├── (1.00,) [type=tuple{decimal}] + │ └── (2.00,) [type=tuple{decimal}] ├── select │ ├── columns: column1:1(decimal) - │ ├── cardinality: [0 - 1] - │ ├── key: () - │ ├── fd: ()-->(1) + │ ├── cardinality: [0 - 2] │ ├── values │ │ ├── columns: column1:1(decimal) - │ │ ├── cardinality: [1 - 1] - │ │ ├── key: () - │ │ ├── fd: ()-->(1) - │ │ └── (1.0,) [type=tuple{decimal}] + │ │ ├── cardinality: [2 - 2] + │ │ ├── (1.0,) [type=tuple{decimal}] + │ │ └── (2.0,) [type=tuple{decimal}] │ └── filters │ └── column1::STRING = '1.0' [type=bool, outer=(1)] └── filters diff --git a/pkg/sql/opt/norm/testdata/rules/project b/pkg/sql/opt/norm/testdata/rules/project index 39969d32f314..da4015878220 100644 --- a/pkg/sql/opt/norm/testdata/rules/project +++ b/pkg/sql/opt/norm/testdata/rules/project @@ -214,19 +214,21 @@ project # Don't trigger rule when Project column depends on Values column. opt expect-not=MergeProjectWithValues -SELECT column1+1, 3 FROM (VALUES (1, 2)) +SELECT column1+1, 3 FROM (VALUES ($1::int, $2::int)) ---- project ├── columns: "?column?":3(int) "?column?":4(int!null) ├── cardinality: [1 - 1] + ├── has-placeholder ├── key: () ├── fd: ()-->(3,4) ├── values │ ├── columns: column1:1(int) │ ├── cardinality: [1 - 1] + │ ├── has-placeholder │ ├── key: () │ ├── fd: ()-->(1) - │ └── (1,) [type=tuple{int}] + │ └── ($1::INT8,) [type=tuple{int}] └── projections ├── column1 + 1 [type=int, outer=(1)] └── const: 3 [type=int]