Skip to content

Commit 816b8e9

Browse files
committed
memo: add ExtractExactScalarExprsForColumns
Add helper function `memo.ExtractExactScalarExprsForColumns`. This function is similar to the constraint builder in that it constrains columns using FiltersExpr, but it differs slightly in details. Unlike constraint builder, this function: - constrains columns to scalar expressions instead of constants - always constrains tightly - in fact, always constrains _exactly_ (that is, to scalar expressions that produce not only equivalent values, but _exactly_ the same bytes) These differences are necessary for UpdateSwap and DeleteSwap, because for swap mutations we need (a) the ability to use placeholders and casting and other scalar expressions to feed the swap mutations, and (b) exact byte equivalence for CPut to work correctly. Informs: #144503 Informs: #85328 Release note: None
1 parent 33fca7a commit 816b8e9

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

pkg/sql/opt/memo/extract.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ package memo
88
import (
99
"context"
1010

11+
"github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo"
1112
"github.com/cockroachdb/cockroach/pkg/sql/opt"
13+
"github.com/cockroachdb/cockroach/pkg/sql/opt/props"
1214
"github.com/cockroachdb/cockroach/pkg/sql/sem/eval"
1315
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
16+
"github.com/cockroachdb/cockroach/pkg/sql/types"
1417
"github.com/cockroachdb/cockroach/pkg/util/intsets"
1518
"github.com/cockroachdb/errors"
1619
)
@@ -540,3 +543,126 @@ func ExtractTailCalls(expr opt.Expr, tailCalls map[opt.ScalarExpr]struct{}) {
540543
}
541544
}
542545
}
546+
547+
// ExtractExactScalarExprsForColumns extracts scalar expressions that tightly
548+
// constrain the given columns from the given filters. Unlike constraint
549+
// builder, this function:
550+
// - constrains to scalar expressions instead of constants
551+
// - always constrains tightly
552+
// - in fact, always constrains _exactly_ (that is, to scalar expressions that
553+
// produce not only equivalent values, but _exactly_ the same bytes)
554+
//
555+
// If a filter does not exactly constrain one of the given columns, or
556+
// references more than one of the given columns, the filter will be added to
557+
// remainingFilters. ExtractExactScalarExprsForColumns returns the mapping from
558+
// constrained columns to scalar expressions, the remaining filters, and a
559+
// boolean indicating whether all columns were constrained.
560+
func ExtractExactScalarExprsForColumns(
561+
cols opt.ColSet,
562+
filters FiltersExpr,
563+
evalCtx *eval.Context,
564+
md *opt.Metadata,
565+
constructIsNotNull func(opt.ColumnID) FiltersItem,
566+
constructFiltersItem func(opt.ScalarExpr) FiltersItem,
567+
) (constraints map[opt.ColumnID]opt.ScalarExpr, remainingFilters FiltersExpr, ok bool) {
568+
constraints = make(map[opt.ColumnID]opt.ScalarExpr)
569+
570+
// useExprAsConstraint adds the scalar expression to the constraints map.
571+
useExprAsConstraint := func(col opt.ColumnID, e opt.ScalarExpr) bool {
572+
// Filters on other columns become remainingFilters.
573+
if !cols.Contains(col) {
574+
return false
575+
}
576+
// Only use the first constraining filter found. If there is already a
577+
// constraining filter, any additional filters become part of
578+
// remainingFilters.
579+
if _, ok := constraints[col]; ok {
580+
return false
581+
}
582+
// In the general case, the = operator (SQL equivalence) does not imply
583+
// exactly the same bytes. The scalar expression is exactly the same when:
584+
// - the column does not have composite type
585+
// - the scalar type is identical to the column type OR the scalar is NULL
586+
colType := md.ColumnMeta(col).Type
587+
if colinfo.CanHaveCompositeKeyEncoding(colType) {
588+
// TODO(michae2): If ExtractConstDatum(e) returns a non-composite datum,
589+
// we could still constrain using this expression.
590+
return false
591+
}
592+
if !e.DataType().Identical(colType) && e.DataType().Family() != types.UnknownFamily {
593+
// TODO(michae2): Explore whether we can use identical canonical types
594+
// here. Also add support for tuples.
595+
return false
596+
}
597+
// If this expression references other columns we're trying to constrain, it
598+
// will have to be a remainingFilter.
599+
var sharedProps props.Shared
600+
BuildSharedProps(e, &sharedProps, evalCtx)
601+
if sharedProps.OuterCols.Intersects(cols) {
602+
return false
603+
}
604+
constraints[col] = e
605+
return true
606+
}
607+
608+
// constrainColsInExpr walks a filter predicate, adding scalar expressions for
609+
// any columns constrained to a single value by the predicate.
610+
var constrainColsInExpr func(opt.ScalarExpr)
611+
constrainColsInExpr = func(e opt.ScalarExpr) {
612+
switch t := e.(type) {
613+
case *VariableExpr:
614+
// `WHERE col` will be true if col is a boolean-typed column that has the
615+
// value True.
616+
if md.ColumnMeta(t.Col).Type.Family() == types.BoolFamily {
617+
if useExprAsConstraint(t.Col, TrueSingleton) {
618+
return
619+
}
620+
}
621+
case *NotExpr:
622+
// `WHERE NOT col` will be true if col is a boolean-typed column that has
623+
// the value False.
624+
if v, ok := t.Input.(*VariableExpr); ok {
625+
if md.ColumnMeta(v.Col).Type.Family() == types.BoolFamily {
626+
if useExprAsConstraint(v.Col, FalseSingleton) {
627+
return
628+
}
629+
}
630+
}
631+
case *AndExpr:
632+
constrainColsInExpr(t.Left)
633+
constrainColsInExpr(t.Right)
634+
return
635+
case *EqExpr:
636+
if v, ok := t.Left.(*VariableExpr); ok {
637+
// `WHERE col = <expr>` will be true if col has the value <expr> and
638+
// <expr> IS NOT NULL.
639+
if useExprAsConstraint(v.Col, t.Right) {
640+
// Because NULL = NULL evaluates to NULL, rather than true, we must
641+
// also guard against the scalar expression evaluating to NULL.
642+
remainingFilters = append(remainingFilters, constructIsNotNull(v.Col))
643+
return
644+
}
645+
}
646+
// TODO(michae2): Handle tuple of variables on LHS of EqExpr.
647+
case *IsExpr:
648+
if v, ok := t.Left.(*VariableExpr); ok {
649+
// `WHERE col IS NOT DISTINCT FROM <expr>` will be true if col has the
650+
// value <expr>.
651+
if useExprAsConstraint(v.Col, t.Right) {
652+
return
653+
}
654+
}
655+
// TODO(michae2): Handle tuple of variables on LHS of IsExpr.
656+
}
657+
// TODO(michae2): Add case for *InExpr.
658+
659+
// If the filter did not constrain a column, add it to remainingFilters.
660+
remainingFilters = append(remainingFilters, constructFiltersItem(e))
661+
}
662+
663+
for i := range filters {
664+
constrainColsInExpr(filters[i].Condition)
665+
}
666+
667+
return constraints, remainingFilters, len(constraints) == cols.Len()
668+
}

0 commit comments

Comments
 (0)