Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

opt: Inline constant values #33417

Merged
merged 1 commit into from
Jan 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions pkg/sql/opt/exec/execbuilder/testdata/explain
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 18 additions & 11 deletions pkg/sql/opt/exec/execbuilder/testdata/orderby
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 6 additions & 8 deletions pkg/sql/opt/memo/testdata/logprops/join
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 11 additions & 0 deletions pkg/sql/opt/norm/custom_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
94 changes: 94 additions & 0 deletions pkg/sql/opt/norm/inline.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
77 changes: 73 additions & 4 deletions pkg/sql/opt/norm/rules/inline.opt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading