Skip to content

Commit

Permalink
opt: extend ExtractJoinEqualities to handle inequalities
Browse files Browse the repository at this point in the history
Previously, `ExtractJoinEqualities` would match equalities between
non-constant, non-variable expressions and project the expressions so that
the comparison would be between variables instead. This may allow rules
like `GenerateLookupJoins` to use the equalities later.

This commit modifies `ExtractJoinEqualities` (now `ExtractJoinComparisons`)
so that it also matches inequalities. This is useful because lookup joins
can now use inequalities for lookups, and converting inequalties to
reference variables instead of complex expressions increases the likelihood
that an inequality can be used in a join.

Release note: None
  • Loading branch information
DrewKimball committed Aug 4, 2022
1 parent 41e51ec commit 0fbf5f0
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 96 deletions.
41 changes: 28 additions & 13 deletions pkg/sql/opt/norm/join_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ func (c *CustomFuncs) JoinFiltersMatchAllLeftRows(
return multiplicity.JoinFiltersMatchAllLeftRows()
}

// CanExtractJoinEquality returns true if:
// CanExtractJoinComparison returns true if:
// - one of a, b is bound by the left columns;
// - the other is bound by the right columns;
// - a and b are not "bare" variables;
Expand All @@ -468,7 +468,7 @@ func (c *CustomFuncs) JoinFiltersMatchAllLeftRows(
//
// Such an equality can be converted to a column equality by pushing down
// expressions as projections.
func (c *CustomFuncs) CanExtractJoinEquality(
func (c *CustomFuncs) CanExtractJoinComparison(
a, b opt.ScalarExpr, leftCols, rightCols opt.ColSet,
) bool {
// Disallow simple equality between variables.
Expand Down Expand Up @@ -502,11 +502,11 @@ func (c *CustomFuncs) CanExtractJoinEquality(
return false
}

// ExtractJoinEquality takes an equality FiltersItem that was identified via a
// call to CanExtractJoinEquality, and converts it to an equality on "bare"
// variables, by pushing down more complicated expressions as projections. See
// the ExtractJoinEqualities rule.
func (c *CustomFuncs) ExtractJoinEquality(
// ExtractJoinComparison takes an equality or inequality FiltersItem that was
// identified via a call to CanExtractJoinComparison, and converts it to an
// equality or inequality on "bare" variables, by pushing down more complicated
// expressions as projections. See the ExtractJoinComparisons rule.
func (c *CustomFuncs) ExtractJoinComparison(
joinOp opt.Operator,
left, right memo.RelExpr,
filters memo.FiltersExpr,
Expand All @@ -516,13 +516,16 @@ func (c *CustomFuncs) ExtractJoinEquality(
leftCols := c.OutputCols(left)
rightCols := c.OutputCols(right)

eq := item.Condition.(*memo.EqExpr)
a, b := eq.Left, eq.Right
cmp := item.Condition
condLeft := cmp.Child(0).(opt.ScalarExpr)
a, b := cmp.Child(0).(opt.ScalarExpr), cmp.Child(1).(opt.ScalarExpr)
op := cmp.Op()

var eqLeftProps props.Shared
memo.BuildSharedProps(eq.Left, &eqLeftProps, c.f.evalCtx)
if eqLeftProps.OuterCols.SubsetOf(rightCols) {
var cmpLeftProps props.Shared
memo.BuildSharedProps(condLeft, &cmpLeftProps, c.f.evalCtx)
if cmpLeftProps.OuterCols.SubsetOf(rightCols) {
a, b = b, a
op = commuteInequality(op)
}

var leftProj, rightProj projectBuilder
Expand All @@ -537,7 +540,7 @@ func (c *CustomFuncs) ExtractJoinEquality(
}

newFilters[i] = c.f.ConstructFiltersItem(
c.f.ConstructEq(leftProj.add(a), rightProj.add(b)),
c.f.DynamicConstruct(op, leftProj.add(a), rightProj.add(b)).(opt.ScalarExpr),
)
}

Expand Down Expand Up @@ -569,6 +572,18 @@ func (c *CustomFuncs) ExtractJoinEquality(
return c.f.ConstructProject(join, memo.EmptyProjectionsExpr, outputCols)
}

// commuteInequality returns the commuted version of the given inequality
// operator.
func commuteInequality(op opt.Operator) opt.Operator {
switch op {
case opt.EqOp:
return op
case opt.LtOp, opt.LeOp, opt.GtOp, opt.GeOp:
return opt.NegateOpMap[op]
}
panic(errors.AssertionFailedf("unexpected operator for commuteInequality: %s", op.String()))
}

// CommuteJoinFlags returns a join private for the commuted join (where the left
// and right sides are swapped). It adjusts any join flags that are specific to
// one side.
Expand Down
20 changes: 12 additions & 8 deletions pkg/sql/opt/norm/rules/join.opt
Original file line number Diff line number Diff line change
Expand Up @@ -501,10 +501,11 @@ $left
$private
)

# ExtractJoinEqualities finds equality conditions such that one side only
# depends on left columns and the other only on right columns and pushes the
# expressions down into Project operators. The result is a join that has an
# equality constraint, which is much more efficient. For example:
# ExtractJoinComparisons finds equality and inequality conditions such that
# one side only depends on left columns and the other only on right columns
# and pushes the expressions down into Project operators. The result is a
# join that has an equality or inequality constraint, which is much more
# efficient. For example:
#
# SELECT * FROM abc JOIN xyz ON a=x+1
#
Expand All @@ -516,15 +517,18 @@ $left
# This join can use hash join or lookup on the equality columns.
#
# Depending on the expressions involved, one or both sides require a projection.
[ExtractJoinEqualities, Normalize]
[ExtractJoinComparisons, Normalize]
(JoinNonApply
$left:* & ^(HasOuterCols $left)
$right:* & ^(HasOuterCols $right)
$on:[
...
$item:(FiltersItem
(Eq $a:^(ConstValue) $b:^(ConstValue)) &
(CanExtractJoinEquality
(Eq | Lt | Le | Gt | Ge
$a:^(ConstValue)
$b:^(ConstValue)
) &
(CanExtractJoinComparison
$a
$b
(OutputCols $left)
Expand All @@ -536,7 +540,7 @@ $left
$private:*
)
=>
(ExtractJoinEquality (OpName) $left $right $on $item $private)
(ExtractJoinComparison (OpName) $left $right $on $item $private)

# SortFiltersInJoin ensures that any filters in an inner join are canonicalized
# by sorting them.
Expand Down
Loading

0 comments on commit 0fbf5f0

Please sign in to comment.