Skip to content

Commit

Permalink
colbuilder: force planning of optimized projection operators
Browse files Browse the repository at this point in the history
Whenever we're planning a projection expression, we have 3 cases: the
left is constant, the right is constants, and neither are constants. For
the second case we have some optimized operators. Previously, those
operators weren't exercised via the TestEval/vectorized because in the
eval tests the left side is constant. This commit switches the planning
to force planning of those optimized operators. This shouldn't really
have an effect on planning of actual queries.

This was prompted by the bug in LIKE operators that is fixed in the
previous commit. Had we forced the planning for our eval tests, we would
have caught it earlier.

This also revealed an incompatibility for our IN operator implementation when
the right side is an empty tuple which this commit also fixes. However,
I don't think this scenario can be hit in production because the
optimizer folds such an expression into correct `false`. Thus, there is
no release note.

Release note: None
  • Loading branch information
yuzefovich committed Jul 30, 2021
1 parent cc4ee69 commit 9b590d3
Showing 1 changed file with 24 additions and 16 deletions.
40 changes: 24 additions & 16 deletions pkg/sql/colexec/colbuilder/execplan.go
Original file line number Diff line number Diff line change
Expand Up @@ -1889,11 +1889,7 @@ func planSelectionOperators(
case tree.In, tree.NotIn:
negate := cmpOp.Symbol == tree.NotIn
datumTuple, ok := tree.AsDTuple(constArg)
if !ok || tupleContainsTuples(datumTuple) {
// Optimized IN operator is supported only on constant
// expressions that don't contain tuples (because tuples
// require special null-handling logic), so we fallback to
// the default comparison operator.
if !ok || useDefaultCmpOpForIn(datumTuple) {
break
}
op, err = colexec.GetInOperator(lTyp, leftOp, leftIdx, datumTuple, negate)
Expand Down Expand Up @@ -2249,10 +2245,20 @@ func planProjectionExpr(
}
allocator := colmem.NewAllocator(ctx, acc, factory)
resultIdx = -1

cmpProjOp, isCmpProjOp := projOp.(tree.ComparisonOperator)
var hasOptimizedOp bool
if isCmpProjOp {
switch cmpProjOp.Symbol {
case tree.Like, tree.NotLike, tree.In, tree.NotIn, tree.IsDistinctFrom, tree.IsNotDistinctFrom:
hasOptimizedOp = true
}
}
// There are 3 cases. Either the left is constant, the right is constant,
// or neither are constant.
if lConstArg, lConst := left.(tree.Datum); lConst {
// Case one: The left is constant.
if lConstArg, lConst := left.(tree.Datum); lConst && !hasOptimizedOp {
// Case one: The left is constant (and we don't have an optimized
// operator for this expression).
// Normally, the optimizer normalizes binary exprs so that the constant
// argument is on the right side. This doesn't happen for
// non-commutative operators such as - and /, though, so we still need
Expand Down Expand Up @@ -2292,8 +2298,6 @@ func planProjectionExpr(
right = tupleDatum
}

cmpProjOp, isCmpProjOp := projOp.(tree.ComparisonOperator)

// We have a special case behavior for Is{Not}DistinctFrom before
// checking whether the right expression is constant below in order to
// extract NULL from the cast expression.
Expand Down Expand Up @@ -2325,11 +2329,7 @@ func planProjectionExpr(
case tree.In, tree.NotIn:
negate := cmpProjOp.Symbol == tree.NotIn
datumTuple, ok := tree.AsDTuple(rConstArg)
if !ok || tupleContainsTuples(datumTuple) {
// Optimized IN operator is supported only on constant
// expressions that don't contain tuples (because tuples
// require special null-handling logic), so we fallback to
// the default comparison operator.
if !ok || useDefaultCmpOpForIn(datumTuple) {
break
}
op, err = colexec.GetInProjectionOperator(
Expand Down Expand Up @@ -2491,8 +2491,16 @@ func appendOneType(typs []*types.T, t *types.T) []*types.T {
return newTyps
}

func tupleContainsTuples(tuple *tree.DTuple) bool {
for _, typ := range tuple.ResolvedType().TupleContents() {
// useDefaultCmpOpForIn returns whether IN and NOT IN projection/selection
// operators should be handled via the default operators. This is the case when
// we have an empty tuple or the tuple contains other tuples (these cases
// require special null-handling logic).
func useDefaultCmpOpForIn(tuple *tree.DTuple) bool {
tupleContents := tuple.ResolvedType().TupleContents()
if len(tupleContents) == 0 {
return true
}
for _, typ := range tupleContents {
if typ.Family() == types.TupleFamily {
return true
}
Expand Down

0 comments on commit 9b590d3

Please sign in to comment.