github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/xform/custom_funcs.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package xform
    12  
    13  import (
    14  	"fmt"
    15  	"sort"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/geo/geoindex"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/opt/constraint"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/opt/idxconstraint"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/opt/norm"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/opt/ordering"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    28  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    29  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    30  	"github.com/cockroachdb/cockroach/pkg/util"
    31  	"github.com/cockroachdb/errors"
    32  )
    33  
    34  // CustomFuncs contains all the custom match and replace functions used by
    35  // the exploration rules. The unnamed xfunc.CustomFuncs allows
    36  // CustomFuncs to provide a clean interface for calling functions from both the
    37  // xform and xfunc packages using the same struct.
    38  type CustomFuncs struct {
    39  	norm.CustomFuncs
    40  	e *explorer
    41  }
    42  
    43  // Init initializes a new CustomFuncs with the given explorer.
    44  func (c *CustomFuncs) Init(e *explorer) {
    45  	c.CustomFuncs.Init(e.f)
    46  	c.e = e
    47  }
    48  
    49  // ----------------------------------------------------------------------
    50  //
    51  // Scan Rules
    52  //   Custom match and replace functions used with scan.opt rules.
    53  //
    54  // ----------------------------------------------------------------------
    55  
    56  // IsCanonicalScan returns true if the given ScanPrivate is an original
    57  // unaltered primary index Scan operator (i.e. unconstrained and not limited).
    58  func (c *CustomFuncs) IsCanonicalScan(scan *memo.ScanPrivate) bool {
    59  	return scan.IsCanonical()
    60  }
    61  
    62  // IsLocking returns true if the ScanPrivate is configured to use a row-level
    63  // locking mode. This can be the case either because the Scan is in the scope of
    64  // a SELECT .. FOR [KEY] UPDATE/SHARE clause or because the Scan was configured
    65  // as part of the row retrieval of a DELETE or UPDATE statement.
    66  func (c *CustomFuncs) IsLocking(scan *memo.ScanPrivate) bool {
    67  	return scan.IsLocking()
    68  }
    69  
    70  // GenerateIndexScans enumerates all secondary indexes on the given Scan
    71  // operator's table and generates an alternate Scan operator for each index that
    72  // includes the set of needed columns specified in the ScanOpDef.
    73  //
    74  // NOTE: This does not generate index joins for non-covering indexes (except in
    75  //       case of ForceIndex). Index joins are usually only introduced "one level
    76  //       up", when the Scan operator is wrapped by an operator that constrains
    77  //       or limits scan output in some way (e.g. Select, Limit, InnerJoin).
    78  //       Index joins are only lower cost when their input does not include all
    79  //       rows from the table. See ConstrainScans and LimitScans for cases where
    80  //       index joins are introduced into the memo.
    81  func (c *CustomFuncs) GenerateIndexScans(grp memo.RelExpr, scanPrivate *memo.ScanPrivate) {
    82  	// Iterate over all secondary indexes.
    83  	var iter scanIndexIter
    84  	iter.init(c.e.mem, scanPrivate, onlyStandardIndexes)
    85  	for iter.next() {
    86  		// Skip primary index.
    87  		if iter.indexOrdinal == cat.PrimaryIndex {
    88  			continue
    89  		}
    90  
    91  		// If the secondary index includes the set of needed columns, then construct
    92  		// a new Scan operator using that index.
    93  		if iter.isCovering() {
    94  			scan := memo.ScanExpr{ScanPrivate: *scanPrivate}
    95  			scan.Index = iter.indexOrdinal
    96  			c.e.mem.AddScanToGroup(&scan, grp)
    97  			continue
    98  		}
    99  
   100  		// Otherwise, if the index must be forced, then construct an IndexJoin
   101  		// operator that provides the columns missing from the index. Note that
   102  		// if ForceIndex=true, scanIndexIter only returns the one index that is
   103  		// being forced, so no need to check that here.
   104  		if !scanPrivate.Flags.ForceIndex {
   105  			continue
   106  		}
   107  
   108  		var sb indexScanBuilder
   109  		sb.init(c, scanPrivate.Table)
   110  
   111  		// Scan whatever columns we need which are available from the index, plus
   112  		// the PK columns.
   113  		newScanPrivate := *scanPrivate
   114  		newScanPrivate.Index = iter.indexOrdinal
   115  		newScanPrivate.Cols = iter.indexCols().Intersection(scanPrivate.Cols)
   116  		newScanPrivate.Cols.UnionWith(sb.primaryKeyCols())
   117  		sb.setScan(&newScanPrivate)
   118  
   119  		sb.addIndexJoin(scanPrivate.Cols)
   120  		sb.build(grp)
   121  	}
   122  }
   123  
   124  // ----------------------------------------------------------------------
   125  //
   126  // Select Rules
   127  //   Custom match and replace functions used with select.opt rules.
   128  //
   129  // ----------------------------------------------------------------------
   130  
   131  // GenerateConstrainedScans enumerates all secondary indexes on the Scan
   132  // operator's table and tries to push the given Select filter into new
   133  // constrained Scan operators using those indexes. Since this only needs to be
   134  // done once per table, GenerateConstrainedScans should only be called on the
   135  // original unaltered primary index Scan operator (i.e. not constrained or
   136  // limited).
   137  //
   138  // For each secondary index that "covers" the columns needed by the scan, there
   139  // are three cases:
   140  //
   141  //  - a filter that can be completely converted to a constraint over that index
   142  //    generates a single constrained Scan operator (to be added to the same
   143  //    group as the original Select operator):
   144  //
   145  //      (Scan $scanDef)
   146  //
   147  //  - a filter that can be partially converted to a constraint over that index
   148  //    generates a constrained Scan operator in a new memo group, wrapped in a
   149  //    Select operator having the remaining filter (to be added to the same group
   150  //    as the original Select operator):
   151  //
   152  //      (Select (Scan $scanDef) $filter)
   153  //
   154  //  - a filter that cannot be converted to a constraint generates nothing
   155  //
   156  // And for a secondary index that does not cover the needed columns:
   157  //
   158  //  - a filter that can be completely converted to a constraint over that index
   159  //    generates a single constrained Scan operator in a new memo group, wrapped
   160  //    in an IndexJoin operator that looks up the remaining needed columns (and
   161  //    is added to the same group as the original Select operator)
   162  //
   163  //      (IndexJoin (Scan $scanDef) $indexJoinDef)
   164  //
   165  //  - a filter that can be partially converted to a constraint over that index
   166  //    generates a constrained Scan operator in a new memo group, wrapped in an
   167  //    IndexJoin operator that looks up the remaining needed columns; the
   168  //    remaining filter is distributed above and/or below the IndexJoin,
   169  //    depending on which columns it references:
   170  //
   171  //      (IndexJoin
   172  //        (Select (Scan $scanDef) $filter)
   173  //        $indexJoinDef
   174  //      )
   175  //
   176  //      (Select
   177  //        (IndexJoin (Scan $scanDef) $indexJoinDef)
   178  //        $filter
   179  //      )
   180  //
   181  //      (Select
   182  //        (IndexJoin
   183  //          (Select (Scan $scanDef) $innerFilter)
   184  //          $indexJoinDef
   185  //        )
   186  //        $outerFilter
   187  //      )
   188  //
   189  // GenerateConstrainedScans will further constrain the enumerated index scans
   190  // by trying to use the check constraints and computed columns that apply to the
   191  // table being scanned, as well as the partitioning defined for the index. See
   192  // comments above checkColumnFilters, computedColFilters, and
   193  // partitionValuesFilters for more detail.
   194  func (c *CustomFuncs) GenerateConstrainedScans(
   195  	grp memo.RelExpr, scanPrivate *memo.ScanPrivate, explicitFilters memo.FiltersExpr,
   196  ) {
   197  	var sb indexScanBuilder
   198  	sb.init(c, scanPrivate.Table)
   199  
   200  	// Generate implicit filters from constraints and computed columns and add
   201  	// them to the list of explicit filters provided in the query.
   202  	optionalFilters := c.checkConstraintFilters(scanPrivate.Table)
   203  	computedColFilters := c.computedColFilters(scanPrivate.Table, explicitFilters, optionalFilters)
   204  	optionalFilters = append(optionalFilters, computedColFilters...)
   205  
   206  	filterColumns := c.FilterOuterCols(explicitFilters)
   207  	filterColumns.UnionWith(c.FilterOuterCols(optionalFilters))
   208  
   209  	// Iterate over all indexes.
   210  	var iter scanIndexIter
   211  	md := c.e.mem.Metadata()
   212  	tabMeta := md.TableMeta(scanPrivate.Table)
   213  	iter.init(c.e.mem, scanPrivate, onlyStandardIndexes)
   214  	for iter.next() {
   215  		// We only consider the partition values when a particular index can otherwise
   216  		// not be constrained. For indexes that are constrained, the partitioned values
   217  		// add no benefit as they don't really constrain anything.
   218  		// Furthermore, if the filters don't take advantage of the index (use any of the
   219  		// index columns), using the partition values add no benefit.
   220  		//
   221  		// If the index is partitioned (by list), we generate two constraints and
   222  		// union them: the "main" constraint and the "in-between" constraint.The
   223  		// "main" constraint restricts the index to the known partition ranges. The
   224  		// "in-between" constraint restricts the index to the rest of the ranges
   225  		// (i.e. everything that falls in-between the main ranges); the in-between
   226  		// constraint is necessary for correctness (there can be rows outside of the
   227  		// partitioned ranges).
   228  		//
   229  		// For both constraints, the partition-related filters are passed as
   230  		// "optional" which guarantees that they return no remaining filters. This
   231  		// allows us to merge the remaining filters from both constraints.
   232  		//
   233  		// Consider the following index and its partition:
   234  		//
   235  		// CREATE INDEX orders_by_seq_num
   236  		//     ON orders (region, seq_num, id)
   237  		//     STORING (total)
   238  		//     PARTITION BY LIST (region)
   239  		//         (
   240  		//             PARTITION us_east1 VALUES IN ('us-east1'),
   241  		//             PARTITION us_west1 VALUES IN ('us-west1'),
   242  		//             PARTITION europe_west2 VALUES IN ('europe-west2')
   243  		//         )
   244  		//
   245  		// The constraint generated for the query:
   246  		//   SELECT sum(total) FROM orders WHERE seq_num >= 100 AND seq_num < 200
   247  		// is:
   248  		//   [/'europe-west2'/100 - /'europe-west2'/199]
   249  		//   [/'us-east1'/100 - /'us-east1'/199]
   250  		//   [/'us-west1'/100 - /'us-west1'/199]
   251  		//
   252  		// The spans before europe-west2, after us-west1 and in between the defined
   253  		// partitions are missing. We must add these spans now, appropriately
   254  		// constrained using the filters.
   255  		//
   256  		// It is important that we add these spans after the partition spans are generated
   257  		// because otherwise these spans would merge with the partition spans and would
   258  		// disallow the partition spans (and the in between ones) to be constrained further.
   259  		// Using the partitioning example and the query above, if we added the in between
   260  		// spans at the same time as the partitioned ones, we would end up with a span that
   261  		// looked like:
   262  		//   [ - /'europe-west2'/99]
   263  		//
   264  		// Allowing the partition spans to be constrained further and then adding
   265  		// the spans give us a more constrained index scan as shown below:
   266  		//   [ - /'europe-west2')
   267  		//   [/'europe-west2'/100 - /'europe-west2'/199]
   268  		//   [/e'europe-west2\x00'/100 - /'us-east1')
   269  		//   [/'us-east1'/100 - /'us-east1'/199]
   270  		//   [/e'us-east1\x00'/100 - /'us-west1')
   271  		//   [/'us-west1'/100 - /'us-west1'/199]
   272  		//   [/e'us-west1\x00'/100 - ]
   273  		//
   274  		// Notice how we 'skip' all the europe-west2 rows with seq_num < 100.
   275  		//
   276  		var partitionFilters, inBetweenFilters memo.FiltersExpr
   277  
   278  		indexColumns := tabMeta.IndexKeyColumns(iter.indexOrdinal)
   279  		firstIndexCol := scanPrivate.Table.ColumnID(iter.index.Column(0).Ordinal)
   280  		if !filterColumns.Contains(firstIndexCol) && indexColumns.Intersects(filterColumns) {
   281  			// Calculate any partition filters if appropriate (see below).
   282  			partitionFilters, inBetweenFilters = c.partitionValuesFilters(scanPrivate.Table, iter.index)
   283  		}
   284  
   285  		// Check whether the filter (along with any partitioning filters) can constrain the index.
   286  		constraint, remainingFilters, ok := c.tryConstrainIndex(
   287  			explicitFilters,
   288  			append(optionalFilters, partitionFilters...),
   289  			scanPrivate.Table,
   290  			iter.indexOrdinal,
   291  			false, /* isInverted */
   292  		)
   293  		if !ok {
   294  			continue
   295  		}
   296  
   297  		if len(partitionFilters) > 0 {
   298  			inBetweenConstraint, inBetweenRemainingFilters, ok := c.tryConstrainIndex(
   299  				explicitFilters,
   300  				append(optionalFilters, inBetweenFilters...),
   301  				scanPrivate.Table,
   302  				iter.indexOrdinal,
   303  				false, /* isInverted */
   304  			)
   305  			if !ok {
   306  				panic(errors.AssertionFailedf("in-between filters didn't yield a constraint"))
   307  			}
   308  
   309  			constraint.UnionWith(c.e.evalCtx, inBetweenConstraint)
   310  
   311  			// Even though the partitioned constraints and the inBetween constraints
   312  			// were consolidated, we must make sure their Union is as well.
   313  			constraint.ConsolidateSpans(c.e.evalCtx)
   314  
   315  			// Add all remaining filters that need to be present in the
   316  			// inBetween spans. Some of the remaining filters are common
   317  			// between them, so we must deduplicate them.
   318  			remainingFilters = c.ConcatFilters(remainingFilters, inBetweenRemainingFilters)
   319  			remainingFilters.Sort()
   320  			remainingFilters.Deduplicate()
   321  		}
   322  
   323  		// Construct new constrained ScanPrivate.
   324  		newScanPrivate := *scanPrivate
   325  		newScanPrivate.Index = iter.indexOrdinal
   326  		newScanPrivate.Constraint = constraint
   327  		// Record whether we were able to use partitions to constrain the scan.
   328  		newScanPrivate.PartitionConstrainedScan = (len(partitionFilters) > 0)
   329  
   330  		// If the alternate index includes the set of needed columns, then construct
   331  		// a new Scan operator using that index.
   332  		if iter.isCovering() {
   333  			sb.setScan(&newScanPrivate)
   334  
   335  			// If there are remaining filters, then the constrained Scan operator
   336  			// will be created in a new group, and a Select operator will be added
   337  			// to the same group as the original operator.
   338  			sb.addSelect(remainingFilters)
   339  
   340  			sb.build(grp)
   341  			continue
   342  		}
   343  
   344  		// Otherwise, construct an IndexJoin operator that provides the columns
   345  		// missing from the index.
   346  		if scanPrivate.Flags.NoIndexJoin {
   347  			continue
   348  		}
   349  
   350  		// Scan whatever columns we need which are available from the index, plus
   351  		// the PK columns.
   352  		newScanPrivate.Cols = iter.indexCols().Intersection(scanPrivate.Cols)
   353  		newScanPrivate.Cols.UnionWith(sb.primaryKeyCols())
   354  		sb.setScan(&newScanPrivate)
   355  
   356  		// If remaining filter exists, split it into one part that can be pushed
   357  		// below the IndexJoin, and one part that needs to stay above.
   358  		remainingFilters = sb.addSelectAfterSplit(remainingFilters, newScanPrivate.Cols)
   359  		sb.addIndexJoin(scanPrivate.Cols)
   360  		sb.addSelect(remainingFilters)
   361  
   362  		sb.build(grp)
   363  	}
   364  }
   365  
   366  // checkConstraintFilters generates all filters that we can derive from the
   367  // check constraints. These are constraints that have been validated and are
   368  // non-nullable. We only use non-nullable check constraints because they
   369  // behave differently from filters on NULL. Check constraints are satisfied
   370  // when their expression evaluates to NULL, while filters are not.
   371  //
   372  // For example, the check constraint a > 1 is satisfied if a is NULL but the
   373  // equivalent filter a > 1 is not.
   374  //
   375  // These filters do not really filter any rows, they are rather facts or
   376  // guarantees about the data but treating them as filters may allow some
   377  // indexes to be constrained and used. Consider the following example:
   378  //
   379  // CREATE TABLE abc (
   380  // 	a INT PRIMARY KEY,
   381  // 	b INT NOT NULL,
   382  // 	c STRING NOT NULL,
   383  // 	CHECK (a < 10 AND a > 1),
   384  // 	CHECK (b < 10 AND b > 1),
   385  // 	CHECK (c in ('first', 'second')),
   386  // 	INDEX secondary (b, a),
   387  // 	INDEX tertiary (c, b, a))
   388  //
   389  // Now consider the query: SELECT a, b WHERE a > 5
   390  //
   391  // Notice that the filter provided previously wouldn't let the optimizer use
   392  // the secondary or tertiary indexes. However, given that we can use the
   393  // constraints on a, b and c, we can actually use the secondary and tertiary
   394  // indexes. In fact, for the above query we can do the following:
   395  //
   396  // select
   397  //  ├── columns: a:1(int!null) b:2(int!null)
   398  //  ├── scan abc@tertiary
   399  //  │		├── columns: a:1(int!null) b:2(int!null)
   400  //  │		└── constraint: /3/2/1: [/'first'/2/6 - /'first'/9/9] [/'second'/2/6 - /'second'/9/9]
   401  //  └── filters
   402  //        └── gt [type=bool]
   403  //            ├── variable: a [type=int]
   404  //            └── const: 5 [type=int]
   405  //
   406  // Similarly, the secondary index could also be used. All such index scans
   407  // will be added to the memo group.
   408  func (c *CustomFuncs) checkConstraintFilters(tabID opt.TableID) memo.FiltersExpr {
   409  	md := c.e.mem.Metadata()
   410  	tabMeta := md.TableMeta(tabID)
   411  	if tabMeta.Constraints == nil {
   412  		return memo.FiltersExpr{}
   413  	}
   414  	filters := *tabMeta.Constraints.(*memo.FiltersExpr)
   415  	// Limit slice capacity to allow the caller to append if necessary.
   416  	return filters[:len(filters):len(filters)]
   417  }
   418  
   419  // computedColFilters generates all filters that can be derived from the list of
   420  // computed column expressions from the given table. A computed column can be
   421  // used as a filter when it has a constant value. That is true when:
   422  //
   423  //   1. All other columns it references are constant, because other filters in
   424  //      the query constrain them to be so.
   425  //   2. All functions in the computed column expression can be folded into
   426  //      constants (i.e. they do not have problematic side effects).
   427  //
   428  // Note that computed columns can depend on other computed columns; in general
   429  // the dependencies form an acyclic directed graph. computedColFilters will
   430  // return filters for all constant computed columns, regardless of the order of
   431  // their dependencies.
   432  //
   433  // As with checkConstraintFilters, computedColFilters do not really filter any
   434  // rows, they are rather facts or guarantees about the data. Treating them as
   435  // filters may allow some indexes to be constrained and used. Consider the
   436  // following example:
   437  //
   438  //   CREATE TABLE t (
   439  //     k INT NOT NULL,
   440  //     hash INT AS (k % 4) STORED,
   441  //     PRIMARY KEY (hash, k)
   442  //   )
   443  //
   444  //   SELECT * FROM t WHERE k = 5
   445  //
   446  // Notice that the filter provided explicitly wouldn't allow the optimizer to
   447  // seek using the primary index (it would have to fall back to a table scan).
   448  // However, column "hash" can be proven to have the constant value of 1, since
   449  // it's dependent on column "k", which has the constant value of 5. This enables
   450  // usage of the primary index:
   451  //
   452  //     scan t
   453  //      ├── columns: k:1(int!null) hash:2(int!null)
   454  //      ├── constraint: /2/1: [/1/5 - /1/5]
   455  //      ├── key: (2)
   456  //      └── fd: ()-->(1)
   457  //
   458  // The values of both columns in that index are known, enabling a single value
   459  // constraint to be generated.
   460  func (c *CustomFuncs) computedColFilters(
   461  	tabID opt.TableID, requiredFilters, optionalFilters memo.FiltersExpr,
   462  ) memo.FiltersExpr {
   463  	tabMeta := c.e.mem.Metadata().TableMeta(tabID)
   464  	if len(tabMeta.ComputedCols) == 0 {
   465  		return nil
   466  	}
   467  
   468  	// Start with set of constant columns, as derived from the list of filter
   469  	// conditions.
   470  	constCols := make(map[opt.ColumnID]opt.ScalarExpr)
   471  	c.findConstantFilterCols(constCols, tabID, requiredFilters)
   472  	c.findConstantFilterCols(constCols, tabID, optionalFilters)
   473  	if len(constCols) == 0 {
   474  		// No constant values could be derived from filters, so assume that there
   475  		// are also no constant computed columns.
   476  		return nil
   477  	}
   478  
   479  	// Construct a new filter condition for each computed column that is
   480  	// constant (i.e. all of its variables are in the constCols set).
   481  	var computedColFilters memo.FiltersExpr
   482  	for colID := range tabMeta.ComputedCols {
   483  		if c.tryFoldComputedCol(tabMeta, colID, constCols) {
   484  			constVal := constCols[colID]
   485  			// Note: Eq is not correct here because of NULLs.
   486  			eqOp := c.e.f.ConstructIs(c.e.f.ConstructVariable(colID), constVal)
   487  			computedColFilters = append(computedColFilters, c.e.f.ConstructFiltersItem(eqOp))
   488  		}
   489  	}
   490  	return computedColFilters
   491  }
   492  
   493  // findConstantFilterCols adds to constFilterCols mappings from table column ID
   494  // to the constant value of that column. It does this by iterating over the
   495  // given lists of filters and finding expressions that constrain columns to a
   496  // single constant value. For example:
   497  //
   498  //   x = 5 AND y = 'foo'
   499  //
   500  // This would add a mapping from x => 5 and y => 'foo', which constants can
   501  // then be used to prove that dependent computed columns are also constant.
   502  func (c *CustomFuncs) findConstantFilterCols(
   503  	constFilterCols map[opt.ColumnID]opt.ScalarExpr, tabID opt.TableID, filters memo.FiltersExpr,
   504  ) {
   505  	tab := c.e.mem.Metadata().Table(tabID)
   506  	for i := range filters {
   507  		// If filter constraints are not tight, then no way to derive constant
   508  		// values.
   509  		props := filters[i].ScalarProps()
   510  		if !props.TightConstraints {
   511  			continue
   512  		}
   513  
   514  		// Iterate over constraint conjuncts with a single column and single
   515  		// span having a single key.
   516  		for i, n := 0, props.Constraints.Length(); i < n; i++ {
   517  			cons := props.Constraints.Constraint(i)
   518  			if cons.Columns.Count() != 1 || cons.Spans.Count() != 1 {
   519  				continue
   520  			}
   521  
   522  			// Skip columns with a data type that uses a composite key encoding.
   523  			// Each of these data types can have multiple distinct values that
   524  			// compare equal. For example, 0 == -0 for the FLOAT data type. It's
   525  			// not safe to treat these as constant inputs to computed columns,
   526  			// since the computed expression may differentiate between the
   527  			// different forms of the same value.
   528  			colID := cons.Columns.Get(0).ID()
   529  			colTyp := tab.Column(tabID.ColumnOrdinal(colID)).DatumType()
   530  			if sqlbase.HasCompositeKeyEncoding(colTyp) {
   531  				continue
   532  			}
   533  
   534  			span := cons.Spans.Get(0)
   535  			if !span.HasSingleKey(c.e.evalCtx) {
   536  				continue
   537  			}
   538  
   539  			datum := span.StartKey().Value(0)
   540  			if datum != tree.DNull {
   541  				constFilterCols[colID] = c.e.f.ConstructConstVal(datum, colTyp)
   542  			}
   543  		}
   544  	}
   545  }
   546  
   547  // tryFoldComputedCol tries to reduce the computed column with the given column
   548  // ID into a constant value, by evaluating it with respect to a set of other
   549  // columns that are constant. If the computed column is constant, enter it into
   550  // the constCols map and return false. Otherwise, return false.
   551  func (c *CustomFuncs) tryFoldComputedCol(
   552  	tabMeta *opt.TableMeta, computedColID opt.ColumnID, constCols map[opt.ColumnID]opt.ScalarExpr,
   553  ) bool {
   554  	// Check whether computed column has already been folded.
   555  	if _, ok := constCols[computedColID]; ok {
   556  		return true
   557  	}
   558  
   559  	var replace func(e opt.Expr) opt.Expr
   560  	replace = func(e opt.Expr) opt.Expr {
   561  		if variable, ok := e.(*memo.VariableExpr); ok {
   562  			// Can variable be folded?
   563  			if constVal, ok := constCols[variable.Col]; ok {
   564  				// Yes, so replace it with its constant value.
   565  				return constVal
   566  			}
   567  
   568  			// No, but that may be because the variable refers to a dependent
   569  			// computed column. In that case, try to recursively fold that
   570  			// computed column. There are no infinite loops possible because the
   571  			// dependency graph is guaranteed to be acyclic.
   572  			if _, ok := tabMeta.ComputedCols[variable.Col]; ok {
   573  				if c.tryFoldComputedCol(tabMeta, variable.Col, constCols) {
   574  					return constCols[variable.Col]
   575  				}
   576  			}
   577  
   578  			return e
   579  		}
   580  		return c.e.f.Replace(e, replace)
   581  	}
   582  
   583  	computedCol := tabMeta.ComputedCols[computedColID]
   584  	replaced := replace(computedCol).(opt.ScalarExpr)
   585  
   586  	// If the computed column is constant, enter it into the constCols map.
   587  	if opt.IsConstValueOp(replaced) {
   588  		constCols[computedColID] = replaced
   589  		return true
   590  	}
   591  	return false
   592  }
   593  
   594  // inBetweenFilters returns a set of filters that are required to cover all the
   595  // in-between spans given a set of partition values. This is required for
   596  // correctness reasons; although values are unlikely to exist between defined
   597  // partitions, they may exist and so the constraints of the scan must incorporate
   598  // these spans.
   599  func (c *CustomFuncs) inBetweenFilters(
   600  	tabID opt.TableID, index cat.Index, partitionValues []tree.Datums,
   601  ) memo.FiltersExpr {
   602  	var inBetween memo.ScalarListExpr
   603  
   604  	if len(partitionValues) == 0 {
   605  		return memo.EmptyFiltersExpr
   606  	}
   607  
   608  	// Sort the partitionValues lexicographically.
   609  	sort.Slice(partitionValues, func(i, j int) bool {
   610  		return partitionValues[i].Compare(c.e.evalCtx, partitionValues[j]) < 0
   611  	})
   612  
   613  	// Add the beginning span.
   614  	beginExpr := c.columnComparison(tabID, index, partitionValues[0], -1)
   615  	inBetween = append(inBetween, beginExpr)
   616  
   617  	// Add the end span.
   618  	endExpr := c.columnComparison(tabID, index, partitionValues[len(partitionValues)-1], 1)
   619  	inBetween = append(inBetween, endExpr)
   620  
   621  	// Add the in-between spans.
   622  	for i := 1; i < len(partitionValues); i++ {
   623  		lowerPartition := partitionValues[i-1]
   624  		higherPartition := partitionValues[i]
   625  
   626  		// The between spans will be greater than the lower partition but smaller
   627  		// than the higher partition.
   628  		var largerThanLower opt.ScalarExpr
   629  		if c.isPrefixOf(lowerPartition, higherPartition) {
   630  
   631  			// Since the lower partition is a prefix of the higher partition, the span
   632  			// must begin with the values defined in the lower partition. Consider the
   633  			// partitions ('us') and ('us', 'cali'). In this case the in-between span
   634  			// should be [/'us - /'us'/'cali').
   635  			largerThanLower = c.columnComparison(tabID, index, lowerPartition, 0)
   636  		} else {
   637  			largerThanLower = c.columnComparison(tabID, index, lowerPartition, 1)
   638  		}
   639  
   640  		smallerThanHigher := c.columnComparison(tabID, index, higherPartition, -1)
   641  
   642  		// Add the in-between span to the list of inBetween spans.
   643  		betweenExpr := c.e.f.ConstructAnd(largerThanLower, smallerThanHigher)
   644  		inBetween = append(inBetween, betweenExpr)
   645  	}
   646  
   647  	// Return an Or expression between all the expressions.
   648  	return memo.FiltersExpr{c.e.f.ConstructFiltersItem(c.constructOr(inBetween))}
   649  }
   650  
   651  // columnComparison returns a filter that compares the index columns to the
   652  // given values. The comp parameter can be -1, 0 or 1 to indicate whether the
   653  // comparison type of the filter should be a Lt, Eq or Gt.
   654  func (c *CustomFuncs) columnComparison(
   655  	tabID opt.TableID, index cat.Index, values tree.Datums, comp int,
   656  ) opt.ScalarExpr {
   657  	colTypes := make([]*types.T, len(values))
   658  	for i := range values {
   659  		colTypes[i] = values[i].ResolvedType()
   660  	}
   661  
   662  	columnVariables := make(memo.ScalarListExpr, len(values))
   663  	scalarValues := make(memo.ScalarListExpr, len(values))
   664  
   665  	for i, val := range values {
   666  		colID := tabID.ColumnID(index.Column(i).Ordinal)
   667  		columnVariables[i] = c.e.f.ConstructVariable(colID)
   668  		scalarValues[i] = c.e.f.ConstructConstVal(val, val.ResolvedType())
   669  	}
   670  
   671  	colsTuple := c.e.f.ConstructTuple(columnVariables, types.MakeTuple(colTypes))
   672  	valsTuple := c.e.f.ConstructTuple(scalarValues, types.MakeTuple(colTypes))
   673  	if comp == 0 {
   674  		return c.e.f.ConstructEq(colsTuple, valsTuple)
   675  	} else if comp > 0 {
   676  		return c.e.f.ConstructGt(colsTuple, valsTuple)
   677  	}
   678  
   679  	return c.e.f.ConstructLt(colsTuple, valsTuple)
   680  }
   681  
   682  // inPartitionFilters returns a FiltersExpr that is required to cover
   683  // all the partition spans. For each partition defined, inPartitionFilters
   684  // will contain a FilterItem that restricts the index columns by
   685  // the partition values. Use inBetweenFilters to generate filters that
   686  // cover all the spans that the partitions don't cover.
   687  func (c *CustomFuncs) inPartitionFilters(
   688  	tabID opt.TableID, index cat.Index, partitionValues []tree.Datums,
   689  ) memo.FiltersExpr {
   690  	var partitions memo.ScalarListExpr
   691  
   692  	// Sort the partition values so the most selective ones are first.
   693  	sort.Slice(partitionValues, func(i, j int) bool {
   694  		return len(partitionValues[i]) >= len(partitionValues[j])
   695  	})
   696  
   697  	// Construct all the partition filters.
   698  	for i, partition := range partitionValues {
   699  
   700  		// Only add this partition if a more selective partition hasn't
   701  		// been defined on the same partition.
   702  		partitionSeen := false
   703  		for j, moreSelectivePartition := range partitionValues {
   704  			if j >= i {
   705  				break
   706  			}
   707  
   708  			// At this point we know whether the current partition was seen before.
   709  			partitionSeen = c.isPrefixOf(partition, moreSelectivePartition)
   710  			if partitionSeen {
   711  				break
   712  			}
   713  		}
   714  
   715  		// This partition is a prefix of a more selective partition and so,
   716  		// will be taken care of by the in-between partitions.
   717  		if partitionSeen {
   718  			continue
   719  		}
   720  
   721  		// Get an expression that restricts the values of the index to the
   722  		// partition values.
   723  		inPartition := c.columnComparison(tabID, index, partition, 0)
   724  		partitions = append(partitions, inPartition)
   725  	}
   726  
   727  	// Return an Or expression between all the expressions.
   728  	return memo.FiltersExpr{c.e.f.ConstructFiltersItem(c.constructOr(partitions))}
   729  }
   730  
   731  // isPrefixOf returns whether pre is a prefix of other.
   732  func (c *CustomFuncs) isPrefixOf(pre []tree.Datum, other []tree.Datum) bool {
   733  	if len(pre) > len(other) {
   734  		// Pre can't be a prefix of other as it is larger.
   735  		return false
   736  	}
   737  	for i := range pre {
   738  		if pre[i].Compare(c.e.evalCtx, other[i]) != 0 {
   739  			return false
   740  		}
   741  	}
   742  
   743  	return true
   744  }
   745  
   746  // constructOr constructs an expression that is an OR between all the
   747  // provided conditions
   748  func (c *CustomFuncs) constructOr(conditions memo.ScalarListExpr) opt.ScalarExpr {
   749  	if len(conditions) == 0 {
   750  		return c.e.f.ConstructFalse()
   751  	}
   752  
   753  	orExpr := conditions[0]
   754  	for i := 1; i < len(conditions); i++ {
   755  		orExpr = c.e.f.ConstructOr(conditions[i], orExpr)
   756  	}
   757  
   758  	return orExpr
   759  }
   760  
   761  // partitionValuesFilters constructs filters with the purpose of
   762  // constraining an index scan using the partition values similar to
   763  // the filters added from the check constraints (see
   764  // checkConstraintFilters). It returns two sets of filters, one to
   765  // create the partition spans, and one to create the spans for all
   766  // the in between ranges that are not part of any partitions.
   767  //
   768  // For example consider the following table and partitioned index:
   769  //
   770  // CREATE TABLE orders (
   771  //     region STRING NOT NULL, id INT8 NOT NULL, total DECIMAL NOT NULL, seq_num INT NOT NULL,
   772  //     PRIMARY KEY (region, id)
   773  // )
   774  //
   775  // CREATE INDEX orders_by_seq_num
   776  //     ON orders (region, seq_num, id)
   777  //     STORING (total)
   778  //     PARTITION BY LIST (region)
   779  //         (
   780  //             PARTITION us_east1 VALUES IN ('us-east1'),
   781  //             PARTITION us_west1 VALUES IN ('us-west1'),
   782  //             PARTITION europe_west2 VALUES IN ('europe-west2')
   783  //         )
   784  //
   785  // Now consider the following query:
   786  // SELECT sum(total) FROM orders WHERE seq_num >= 100 AND seq_num < 200
   787  //
   788  // Normally, the index would not be utilized but because we know what the
   789  // partition values are for the prefix of the index, we can generate
   790  // filters that allow us to use the index (adding the appropriate in-between
   791  // filters to catch all the values that are not part of the partitions).
   792  // By doing so, we get the following plan:
   793  // scalar-group-by
   794  //  ├── select
   795  //  │    ├── scan orders@orders_by_seq_num
   796  //  │    │    └── constraint: /1/4/2: [ - /'europe-west2')
   797  //  │    │                            [/'europe-west2'/100 - /'europe-west2'/199]
   798  //  │    │                            [/e'europe-west2\x00'/100 - /'us-east1')
   799  //  │    │                            [/'us-east1'/100 - /'us-east1'/199]
   800  //  │    │                            [/e'us-east1\x00'/100 - /'us-west1')
   801  //  │    │                            [/'us-west1'/100 - /'us-west1'/199]
   802  //  │    │                            [/e'us-west1\x00'/100 - ]
   803  //  │    └── filters
   804  //  │         └── (seq_num >= 100) AND (seq_num < 200)
   805  //  └── aggregations
   806  //       └── sum
   807  //            └── variable: total
   808  //
   809  func (c *CustomFuncs) partitionValuesFilters(
   810  	tabID opt.TableID, index cat.Index,
   811  ) (partitionFilter, inBetweenFilter memo.FiltersExpr) {
   812  
   813  	// Find all the partition values
   814  	partitionValues := index.PartitionByListPrefixes()
   815  	if len(partitionValues) == 0 {
   816  		return partitionFilter, inBetweenFilter
   817  	}
   818  
   819  	// Get the in partition expressions.
   820  	inPartition := c.inPartitionFilters(tabID, index, partitionValues)
   821  
   822  	// Get the in between expressions.
   823  	inBetween := c.inBetweenFilters(tabID, index, partitionValues)
   824  
   825  	return inPartition, inBetween
   826  }
   827  
   828  // HasInvertedIndexes returns true if at least one inverted index is defined on
   829  // the Scan operator's table.
   830  func (c *CustomFuncs) HasInvertedIndexes(scanPrivate *memo.ScanPrivate) bool {
   831  	// Don't bother matching unless there's an inverted index.
   832  	var iter scanIndexIter
   833  	iter.init(c.e.mem, scanPrivate, onlyInvertedIndexes)
   834  	return iter.next()
   835  }
   836  
   837  // GenerateInvertedIndexScans enumerates all inverted indexes on the Scan
   838  // operator's table and generates an alternate Scan operator for each inverted
   839  // index that can service the query.
   840  //
   841  // The resulting Scan operator is pre-constrained and requires an IndexJoin to
   842  // project columns other than the primary key columns. The reason it's pre-
   843  // constrained is that we cannot treat an inverted index in the same way as a
   844  // regular index, since it does not actually contain the indexed column.
   845  func (c *CustomFuncs) GenerateInvertedIndexScans(
   846  	grp memo.RelExpr, scanPrivate *memo.ScanPrivate, filters memo.FiltersExpr,
   847  ) {
   848  	var sb indexScanBuilder
   849  	sb.init(c, scanPrivate.Table)
   850  
   851  	// Iterate over all inverted indexes.
   852  	var iter scanIndexIter
   853  	iter.init(c.e.mem, scanPrivate, onlyInvertedIndexes)
   854  	for iter.next() {
   855  		// Check whether the filter can constrain the index.
   856  		constraint, remaining, ok := c.tryConstrainIndex(
   857  			filters, nil /* optionalFilters */, scanPrivate.Table, iter.indexOrdinal, true /* isInverted */)
   858  		if !ok {
   859  			continue
   860  		}
   861  
   862  		// Construct new ScanOpDef with the new index and constraint.
   863  		newScanPrivate := *scanPrivate
   864  		newScanPrivate.Index = iter.indexOrdinal
   865  		newScanPrivate.Constraint = constraint
   866  
   867  		// Though the index is marked as containing the JSONB column being
   868  		// indexed, it doesn't actually, and it's only valid to extract the
   869  		// primary key columns from it.
   870  		newScanPrivate.Cols = sb.primaryKeyCols()
   871  
   872  		// The Scan operator always goes in a new group, since it's always nested
   873  		// underneath the IndexJoin. The IndexJoin may also go into its own group,
   874  		// if there's a remaining filter above it.
   875  		// TODO(justin): We might not need to do an index join in order to get the
   876  		// correct columns, but it's difficult to tell at this point.
   877  		sb.setScan(&newScanPrivate)
   878  
   879  		// If remaining filter exists, split it into one part that can be pushed
   880  		// below the IndexJoin, and one part that needs to stay above.
   881  		remaining = sb.addSelectAfterSplit(remaining, newScanPrivate.Cols)
   882  		sb.addIndexJoin(scanPrivate.Cols)
   883  		sb.addSelect(remaining)
   884  
   885  		sb.build(grp)
   886  	}
   887  }
   888  
   889  func (c *CustomFuncs) initIdxConstraintForIndex(
   890  	requiredFilters, optionalFilters memo.FiltersExpr,
   891  	tabID opt.TableID,
   892  	indexOrd int,
   893  	isInverted bool,
   894  ) (ic *idxconstraint.Instance) {
   895  	ic = &idxconstraint.Instance{}
   896  
   897  	// Fill out data structures needed to initialize the idxconstraint library.
   898  	// Use LaxKeyColumnCount, since all columns <= LaxKeyColumnCount are
   899  	// guaranteed to be part of each row's key (i.e. not stored in row's value,
   900  	// which does not take part in an index scan). Note that the OrderingColumn
   901  	// slice cannot be reused, as Instance.Init can use it in the constraint.
   902  	md := c.e.mem.Metadata()
   903  	index := md.Table(tabID).Index(indexOrd)
   904  	columns := make([]opt.OrderingColumn, index.LaxKeyColumnCount())
   905  	var notNullCols opt.ColSet
   906  	for i := range columns {
   907  		col := index.Column(i)
   908  		colID := tabID.ColumnID(col.Ordinal)
   909  		columns[i] = opt.MakeOrderingColumn(colID, col.Descending)
   910  		if !col.IsNullable() {
   911  			notNullCols.Add(colID)
   912  		}
   913  	}
   914  
   915  	// Generate index constraints.
   916  	ic.Init(requiredFilters, optionalFilters, columns, notNullCols, isInverted, c.e.evalCtx, c.e.f)
   917  	return ic
   918  }
   919  
   920  // tryConstrainIndex tries to derive a constraint for the given index from the
   921  // specified filter. If a constraint is derived, it is returned along with any
   922  // filter remaining after extracting the constraint. If no constraint can be
   923  // derived, then tryConstrainIndex returns ok = false.
   924  func (c *CustomFuncs) tryConstrainIndex(
   925  	requiredFilters, optionalFilters memo.FiltersExpr,
   926  	tabID opt.TableID,
   927  	indexOrd int,
   928  	isInverted bool,
   929  ) (constraint *constraint.Constraint, remainingFilters memo.FiltersExpr, ok bool) {
   930  	// Start with fast check to rule out indexes that cannot be constrained.
   931  	if !isInverted &&
   932  		!c.canMaybeConstrainIndex(requiredFilters, tabID, indexOrd) &&
   933  		!c.canMaybeConstrainIndex(optionalFilters, tabID, indexOrd) {
   934  		return nil, nil, false
   935  	}
   936  
   937  	ic := c.initIdxConstraintForIndex(requiredFilters, optionalFilters, tabID, indexOrd, isInverted)
   938  	constraint = ic.Constraint()
   939  	if constraint.IsUnconstrained() {
   940  		return nil, nil, false
   941  	}
   942  
   943  	// Return 0 if no remaining filter.
   944  	remaining := ic.RemainingFilters()
   945  
   946  	// Make copy of constraint so that idxconstraint instance is not referenced.
   947  	copy := *constraint
   948  	return &copy, remaining, true
   949  }
   950  
   951  // allInvIndexConstraints tries to derive all constraints for the specified inverted
   952  // index that can be derived. If no constraint is derived, then it returns ok = false,
   953  // similar to tryConstrainIndex.
   954  func (c *CustomFuncs) allInvIndexConstraints(
   955  	filters memo.FiltersExpr, tabID opt.TableID, indexOrd int,
   956  ) (constraints []*constraint.Constraint, ok bool) {
   957  	ic := c.initIdxConstraintForIndex(filters, nil /* optionalFilters */, tabID, indexOrd, true /* isInverted */)
   958  	constraints, err := ic.AllInvertedIndexConstraints()
   959  	if err != nil {
   960  		return nil, false
   961  	}
   962  	// As long as there was no error, AllInvertedIndexConstraints is guaranteed
   963  	// to add at least one constraint to the slice. It will be set to
   964  	// unconstrained if no constraints could be derived for this index.
   965  	constraint := constraints[0]
   966  	if constraint.IsUnconstrained() {
   967  		return constraints, false
   968  	}
   969  
   970  	return constraints, true
   971  }
   972  
   973  // canMaybeConstrainIndex returns true if we should try to constrain a given
   974  // index by the given filter. It returns false if it is impossible for the
   975  // filter can constrain the scan.
   976  //
   977  // If any of the three following statements are true, then it is
   978  // possible that the index can be constrained:
   979  //
   980  //   1. The filter references the first index column.
   981  //   2. The constraints are not tight (see props.Scalar.TightConstraints).
   982  //   3. Any of the filter's constraints start with the first index column.
   983  //
   984  func (c *CustomFuncs) canMaybeConstrainIndex(
   985  	filters memo.FiltersExpr, tabID opt.TableID, indexOrd int,
   986  ) bool {
   987  	md := c.e.mem.Metadata()
   988  	index := md.Table(tabID).Index(indexOrd)
   989  
   990  	for i := range filters {
   991  		filterProps := filters[i].ScalarProps()
   992  
   993  		// If the filter involves the first index column, then the index can
   994  		// possibly be constrained.
   995  		firstIndexCol := tabID.ColumnID(index.Column(0).Ordinal)
   996  		if filterProps.OuterCols.Contains(firstIndexCol) {
   997  			return true
   998  		}
   999  
  1000  		// If the constraints are not tight, then the index can possibly be
  1001  		// constrained, because index constraint generation supports more
  1002  		// expressions than filter constraint generation.
  1003  		if !filterProps.TightConstraints {
  1004  			return true
  1005  		}
  1006  
  1007  		// If any constraint involves the first index column, then the index can
  1008  		// possibly be constrained.
  1009  		cset := filterProps.Constraints
  1010  		for i := 0; i < cset.Length(); i++ {
  1011  			firstCol := cset.Constraint(i).Columns.Get(0).ID()
  1012  			if firstCol == firstIndexCol {
  1013  				return true
  1014  			}
  1015  		}
  1016  	}
  1017  
  1018  	return false
  1019  }
  1020  
  1021  // ----------------------------------------------------------------------
  1022  //
  1023  // Limit Rules
  1024  //   Custom match and replace functions used with limit.opt rules.
  1025  //
  1026  // ----------------------------------------------------------------------
  1027  
  1028  // LimitScanPrivate constructs a new ScanPrivate value that is based on the
  1029  // given ScanPrivate. The new private's HardLimit is set to the given limit,
  1030  // which must be a constant int datum value. The other fields are inherited from
  1031  // the existing private.
  1032  func (c *CustomFuncs) LimitScanPrivate(
  1033  	scanPrivate *memo.ScanPrivate, limit tree.Datum, required physical.OrderingChoice,
  1034  ) *memo.ScanPrivate {
  1035  	// Determine the scan direction necessary to provide the required ordering.
  1036  	_, reverse := ordering.ScanPrivateCanProvide(c.e.mem.Metadata(), scanPrivate, &required)
  1037  
  1038  	newScanPrivate := *scanPrivate
  1039  	newScanPrivate.HardLimit = memo.MakeScanLimit(int64(*limit.(*tree.DInt)), reverse)
  1040  	return &newScanPrivate
  1041  }
  1042  
  1043  // CanLimitConstrainedScan returns true if the given scan has already been
  1044  // constrained and can have a row count limit installed as well. This is only
  1045  // possible when the required ordering of the rows to be limited can be
  1046  // satisfied by the Scan operator.
  1047  //
  1048  // NOTE: Limiting unconstrained scans is done by the PushLimitIntoScan rule,
  1049  //       since that can require IndexJoin operators to be generated.
  1050  func (c *CustomFuncs) CanLimitConstrainedScan(
  1051  	scanPrivate *memo.ScanPrivate, required physical.OrderingChoice,
  1052  ) bool {
  1053  	if scanPrivate.HardLimit != 0 {
  1054  		// Don't push limit into scan if scan is already limited. This would
  1055  		// usually only happen when normalizations haven't run, as otherwise
  1056  		// redundant Limit operators would be discarded.
  1057  		return false
  1058  	}
  1059  
  1060  	if scanPrivate.Constraint == nil {
  1061  		// This is not a constrained scan, so skip it. The PushLimitIntoScan rule
  1062  		// is responsible for limited unconstrained scans.
  1063  		return false
  1064  	}
  1065  
  1066  	ok, _ := ordering.ScanPrivateCanProvide(c.e.mem.Metadata(), scanPrivate, &required)
  1067  	return ok
  1068  }
  1069  
  1070  // GenerateLimitedScans enumerates all secondary indexes on the Scan operator's
  1071  // table and tries to create new limited Scan operators from them. Since this
  1072  // only needs to be done once per table, GenerateLimitedScans should only be
  1073  // called on the original unaltered primary index Scan operator (i.e. not
  1074  // constrained or limited).
  1075  //
  1076  // For a secondary index that "covers" the columns needed by the scan, a single
  1077  // limited Scan operator is created. For a non-covering index, an IndexJoin is
  1078  // constructed to add missing columns to the limited Scan.
  1079  func (c *CustomFuncs) GenerateLimitedScans(
  1080  	grp memo.RelExpr,
  1081  	scanPrivate *memo.ScanPrivate,
  1082  	limit tree.Datum,
  1083  	required physical.OrderingChoice,
  1084  ) {
  1085  	limitVal := int64(*limit.(*tree.DInt))
  1086  
  1087  	var sb indexScanBuilder
  1088  	sb.init(c, scanPrivate.Table)
  1089  
  1090  	// Iterate over all indexes, looking for those that can be limited.
  1091  	var iter scanIndexIter
  1092  	iter.init(c.e.mem, scanPrivate, onlyStandardIndexes)
  1093  	for iter.next() {
  1094  		newScanPrivate := *scanPrivate
  1095  		newScanPrivate.Index = iter.indexOrdinal
  1096  
  1097  		// If the alternate index does not conform to the ordering, then skip it.
  1098  		// If reverse=true, then the scan needs to be in reverse order to match
  1099  		// the required ordering.
  1100  		ok, reverse := ordering.ScanPrivateCanProvide(
  1101  			c.e.mem.Metadata(), &newScanPrivate, &required,
  1102  		)
  1103  		if !ok {
  1104  			continue
  1105  		}
  1106  		newScanPrivate.HardLimit = memo.MakeScanLimit(limitVal, reverse)
  1107  
  1108  		// If the alternate index includes the set of needed columns, then construct
  1109  		// a new Scan operator using that index.
  1110  		if iter.isCovering() {
  1111  			sb.setScan(&newScanPrivate)
  1112  			sb.build(grp)
  1113  			continue
  1114  		}
  1115  
  1116  		// Otherwise, try to construct an IndexJoin operator that provides the
  1117  		// columns missing from the index.
  1118  		if scanPrivate.Flags.NoIndexJoin {
  1119  			continue
  1120  		}
  1121  
  1122  		// Scan whatever columns we need which are available from the index, plus
  1123  		// the PK columns.
  1124  		newScanPrivate.Cols = iter.indexCols().Intersection(scanPrivate.Cols)
  1125  		newScanPrivate.Cols.UnionWith(sb.primaryKeyCols())
  1126  		sb.setScan(&newScanPrivate)
  1127  
  1128  		// The Scan operator will go into its own group (because it projects a
  1129  		// different set of columns), and the IndexJoin operator will be added to
  1130  		// the same group as the original Limit operator.
  1131  		sb.addIndexJoin(scanPrivate.Cols)
  1132  
  1133  		sb.build(grp)
  1134  	}
  1135  }
  1136  
  1137  // ----------------------------------------------------------------------
  1138  //
  1139  // Join Rules
  1140  //   Custom match and replace functions used with join.opt rules.
  1141  //
  1142  // ----------------------------------------------------------------------
  1143  
  1144  // NoJoinHints returns true if no hints were specified for this join.
  1145  func (c *CustomFuncs) NoJoinHints(p *memo.JoinPrivate) bool {
  1146  	return p.Flags.Empty()
  1147  }
  1148  
  1149  // CommuteJoinFlags returns a join private for the commuted join (where the left
  1150  // and right sides are swapped). It adjusts any join flags that are specific to
  1151  // one side.
  1152  func (c *CustomFuncs) CommuteJoinFlags(p *memo.JoinPrivate) *memo.JoinPrivate {
  1153  	if p.Flags.Empty() {
  1154  		return p
  1155  	}
  1156  
  1157  	// swap is a helper function which swaps the values of two (single-bit) flags.
  1158  	swap := func(f, a, b memo.JoinFlags) memo.JoinFlags {
  1159  		// If the bits are different, flip them both.
  1160  		if f.Has(a) != f.Has(b) {
  1161  			f ^= (a | b)
  1162  		}
  1163  		return f
  1164  	}
  1165  	f := p.Flags
  1166  	f = swap(f, memo.AllowLookupJoinIntoLeft, memo.AllowLookupJoinIntoRight)
  1167  	f = swap(f, memo.AllowHashJoinStoreLeft, memo.AllowHashJoinStoreRight)
  1168  	if p.Flags == f {
  1169  		return p
  1170  	}
  1171  	res := *p
  1172  	res.Flags = f
  1173  	return &res
  1174  }
  1175  
  1176  // GenerateMergeJoins spawns MergeJoinOps, based on any interesting orderings.
  1177  func (c *CustomFuncs) GenerateMergeJoins(
  1178  	grp memo.RelExpr,
  1179  	originalOp opt.Operator,
  1180  	left, right memo.RelExpr,
  1181  	on memo.FiltersExpr,
  1182  	joinPrivate *memo.JoinPrivate,
  1183  ) {
  1184  	if !joinPrivate.Flags.Has(memo.AllowMergeJoin) {
  1185  		return
  1186  	}
  1187  
  1188  	leftProps := left.Relational()
  1189  	rightProps := right.Relational()
  1190  
  1191  	leftEq, rightEq := memo.ExtractJoinEqualityColumns(
  1192  		leftProps.OutputCols, rightProps.OutputCols, on,
  1193  	)
  1194  	n := len(leftEq)
  1195  	if n == 0 {
  1196  		return
  1197  	}
  1198  
  1199  	// We generate MergeJoin expressions based on interesting orderings from the
  1200  	// left side. The CommuteJoin rule will ensure that we actually try both
  1201  	// sides.
  1202  	orders := DeriveInterestingOrderings(left).Copy()
  1203  	orders.RestrictToCols(leftEq.ToSet())
  1204  
  1205  	if !joinPrivate.Flags.Has(memo.AllowHashJoinStoreLeft) &&
  1206  		!joinPrivate.Flags.Has(memo.AllowHashJoinStoreRight) {
  1207  		// If we don't allow hash join, we must do our best to generate a merge
  1208  		// join, even if it means sorting both sides. We append an arbitrary
  1209  		// ordering, in case the interesting orderings don't result in any merge
  1210  		// joins.
  1211  		o := make(opt.Ordering, len(leftEq))
  1212  		for i := range o {
  1213  			o[i] = opt.MakeOrderingColumn(leftEq[i], false /* descending */)
  1214  		}
  1215  		orders.Add(o)
  1216  	}
  1217  
  1218  	if len(orders) == 0 {
  1219  		return
  1220  	}
  1221  
  1222  	var colToEq util.FastIntMap
  1223  	for i := range leftEq {
  1224  		colToEq.Set(int(leftEq[i]), i)
  1225  		colToEq.Set(int(rightEq[i]), i)
  1226  	}
  1227  
  1228  	var remainingFilters memo.FiltersExpr
  1229  
  1230  	for _, o := range orders {
  1231  		if len(o) < n {
  1232  			// TODO(radu): we have a partial ordering on the equality columns. We
  1233  			// should augment it with the other columns (in arbitrary order) in the
  1234  			// hope that we can get the full ordering cheaply using a "streaming"
  1235  			// sort. This would not useful now since we don't support streaming sorts.
  1236  			continue
  1237  		}
  1238  
  1239  		if remainingFilters == nil {
  1240  			remainingFilters = memo.ExtractRemainingJoinFilters(on, leftEq, rightEq)
  1241  		}
  1242  
  1243  		merge := memo.MergeJoinExpr{Left: left, Right: right, On: remainingFilters}
  1244  		merge.JoinPrivate = *joinPrivate
  1245  		merge.JoinType = originalOp
  1246  		merge.LeftEq = make(opt.Ordering, n)
  1247  		merge.RightEq = make(opt.Ordering, n)
  1248  		merge.LeftOrdering.Columns = make([]physical.OrderingColumnChoice, 0, n)
  1249  		merge.RightOrdering.Columns = make([]physical.OrderingColumnChoice, 0, n)
  1250  		for i := 0; i < n; i++ {
  1251  			eqIdx, _ := colToEq.Get(int(o[i].ID()))
  1252  			l, r, descending := leftEq[eqIdx], rightEq[eqIdx], o[i].Descending()
  1253  			merge.LeftEq[i] = opt.MakeOrderingColumn(l, descending)
  1254  			merge.RightEq[i] = opt.MakeOrderingColumn(r, descending)
  1255  			merge.LeftOrdering.AppendCol(l, descending)
  1256  			merge.RightOrdering.AppendCol(r, descending)
  1257  		}
  1258  
  1259  		// Simplify the orderings with the corresponding FD sets.
  1260  		merge.LeftOrdering.Simplify(&leftProps.FuncDeps)
  1261  		merge.RightOrdering.Simplify(&rightProps.FuncDeps)
  1262  
  1263  		c.e.mem.AddMergeJoinToGroup(&merge, grp)
  1264  	}
  1265  }
  1266  
  1267  // GenerateLookupJoins looks at the possible indexes and creates lookup join
  1268  // expressions in the current group. A lookup join can be created when the ON
  1269  // condition has equality constraints on a prefix of the index columns.
  1270  //
  1271  // There are two cases:
  1272  //
  1273  //  1. The index has all the columns we need; this is the simple case, where we
  1274  //     generate a LookupJoin expression in the current group:
  1275  //
  1276  //         Join                       LookupJoin(t@idx))
  1277  //         /   \                           |
  1278  //        /     \            ->            |
  1279  //      Input  Scan(t)                   Input
  1280  //
  1281  //
  1282  //  2. The index is not covering. We have to generate an index join above the
  1283  //     lookup join. Note that this index join is also implemented as a
  1284  //     LookupJoin, because an IndexJoin can only output columns from one table,
  1285  //     whereas we also need to output columns from Input.
  1286  //
  1287  //         Join                       LookupJoin(t@primary)
  1288  //         /   \                           |
  1289  //        /     \            ->            |
  1290  //      Input  Scan(t)                LookupJoin(t@idx)
  1291  //                                         |
  1292  //                                         |
  1293  //                                       Input
  1294  //
  1295  //     For example:
  1296  //      CREATE TABLE abc (a PRIMARY KEY, b INT, c INT)
  1297  //      CREATE TABLE xyz (x PRIMARY KEY, y INT, z INT, INDEX (y))
  1298  //      SELECT * FROM abc JOIN xyz ON a=y
  1299  //
  1300  //     We want to first join abc with the index on y (which provides columns y, x)
  1301  //     and then use a lookup join to retrieve column z. The "index join" (top
  1302  //     LookupJoin) will produce columns a,b,c,x,y; the lookup columns are just z
  1303  //     (the original index join produced x,y,z).
  1304  //
  1305  //     Note that the top LookupJoin "sees" column IDs from the table on both
  1306  //     "sides" (in this example x,y on the left and z on the right) but there is
  1307  //     no overlap.
  1308  //
  1309  func (c *CustomFuncs) GenerateLookupJoins(
  1310  	grp memo.RelExpr,
  1311  	joinType opt.Operator,
  1312  	input memo.RelExpr,
  1313  	scanPrivate *memo.ScanPrivate,
  1314  	on memo.FiltersExpr,
  1315  	joinPrivate *memo.JoinPrivate,
  1316  ) {
  1317  	if !joinPrivate.Flags.Has(memo.AllowLookupJoinIntoRight) {
  1318  		return
  1319  	}
  1320  	md := c.e.mem.Metadata()
  1321  	inputProps := input.Relational()
  1322  
  1323  	leftEq, rightEq := memo.ExtractJoinEqualityColumns(inputProps.OutputCols, scanPrivate.Cols, on)
  1324  	n := len(leftEq)
  1325  	if n == 0 {
  1326  		return
  1327  	}
  1328  
  1329  	var pkCols opt.ColList
  1330  
  1331  	var iter scanIndexIter
  1332  	iter.init(c.e.mem, scanPrivate, onlyStandardIndexes)
  1333  	for iter.next() {
  1334  		// Find the longest prefix of index key columns that are constrained by
  1335  		// an equality with another column or a constant.
  1336  		numIndexKeyCols := iter.index.LaxKeyColumnCount()
  1337  
  1338  		var projections memo.ProjectionsExpr
  1339  		var constFilters memo.FiltersExpr
  1340  
  1341  		// Check if the first column in the index has an equality constraint, or if
  1342  		// it is constrained to a constant value. This check doesn't guarantee that
  1343  		// we will find lookup join key columns, but it avoids the unnecessary work
  1344  		// in most cases.
  1345  		firstIdxCol := scanPrivate.Table.ColumnID(iter.index.Column(0).Ordinal)
  1346  		if _, ok := rightEq.Find(firstIdxCol); !ok {
  1347  			if _, _, ok := c.findConstantFilter(on, firstIdxCol); !ok {
  1348  				continue
  1349  			}
  1350  		}
  1351  
  1352  		lookupJoin := memo.LookupJoinExpr{Input: input}
  1353  		lookupJoin.JoinPrivate = *joinPrivate
  1354  		lookupJoin.JoinType = joinType
  1355  		lookupJoin.Table = scanPrivate.Table
  1356  		lookupJoin.Index = iter.indexOrdinal
  1357  
  1358  		lookupJoin.KeyCols = make(opt.ColList, 0, numIndexKeyCols)
  1359  		rightSideCols := make(opt.ColList, 0, numIndexKeyCols)
  1360  		needProjection := false
  1361  
  1362  		// All the lookup conditions must apply to the prefix of the index and so
  1363  		// the projected columns created must be created in order.
  1364  		for j := 0; j < numIndexKeyCols; j++ {
  1365  			idxCol := scanPrivate.Table.ColumnID(iter.index.Column(j).Ordinal)
  1366  			if eqIdx, ok := rightEq.Find(idxCol); ok {
  1367  				lookupJoin.KeyCols = append(lookupJoin.KeyCols, leftEq[eqIdx])
  1368  				rightSideCols = append(rightSideCols, idxCol)
  1369  				continue
  1370  			}
  1371  
  1372  			// Try to find a filter that constrains this column to a non-NULL constant
  1373  			// value. We cannot use a NULL value because the lookup join implements
  1374  			// logic equivalent to simple equality between columns (where NULL never
  1375  			// equals anything).
  1376  			foundVal, onIdx, ok := c.findConstantFilter(on, idxCol)
  1377  			if !ok || foundVal == tree.DNull {
  1378  				break
  1379  			}
  1380  
  1381  			// We will project this constant value in the input to make it an equality
  1382  			// column.
  1383  			if projections == nil {
  1384  				projections = make(memo.ProjectionsExpr, 0, numIndexKeyCols-j)
  1385  				constFilters = make(memo.FiltersExpr, 0, numIndexKeyCols-j)
  1386  			}
  1387  
  1388  			idxColType := c.e.f.Metadata().ColumnMeta(idxCol).Type
  1389  			constColID := c.e.f.Metadata().AddColumn(
  1390  				fmt.Sprintf("project_const_col_@%d", idxCol),
  1391  				idxColType,
  1392  			)
  1393  			projections = append(projections, c.e.f.ConstructProjectionsItem(
  1394  				c.e.f.ConstructConst(foundVal, idxColType),
  1395  				constColID,
  1396  			))
  1397  
  1398  			needProjection = true
  1399  			lookupJoin.KeyCols = append(lookupJoin.KeyCols, constColID)
  1400  			rightSideCols = append(rightSideCols, idxCol)
  1401  			constFilters = append(constFilters, on[onIdx])
  1402  		}
  1403  
  1404  		if len(lookupJoin.KeyCols) == 0 {
  1405  			// We couldn't find equality columns which we can lookup.
  1406  			continue
  1407  		}
  1408  
  1409  		tableFDs := memo.MakeTableFuncDep(md, scanPrivate.Table)
  1410  		// A lookup join will drop any input row which contains NULLs, so a lax key
  1411  		// is sufficient.
  1412  		lookupJoin.LookupColsAreTableKey = tableFDs.ColsAreLaxKey(rightSideCols.ToSet())
  1413  
  1414  		// Construct the projections for the constant columns.
  1415  		if needProjection {
  1416  			lookupJoin.Input = c.e.f.ConstructProject(input, projections, input.Relational().OutputCols)
  1417  		}
  1418  
  1419  		// Remove the redundant filters and update the lookup condition.
  1420  		lookupJoin.On = memo.ExtractRemainingJoinFilters(on, lookupJoin.KeyCols, rightSideCols)
  1421  		lookupJoin.On.RemoveCommonFilters(constFilters)
  1422  		lookupJoin.ConstFilters = constFilters
  1423  
  1424  		if iter.isCovering() {
  1425  			// Case 1 (see function comment).
  1426  			lookupJoin.Cols = scanPrivate.Cols.Union(inputProps.OutputCols)
  1427  			c.e.mem.AddLookupJoinToGroup(&lookupJoin, grp)
  1428  			continue
  1429  		}
  1430  
  1431  		// Case 2 (see function comment).
  1432  		if scanPrivate.Flags.NoIndexJoin {
  1433  			continue
  1434  		}
  1435  
  1436  		if pkCols == nil {
  1437  			pkIndex := iter.tab.Index(cat.PrimaryIndex)
  1438  			pkCols = make(opt.ColList, pkIndex.KeyColumnCount())
  1439  			for i := range pkCols {
  1440  				pkCols[i] = scanPrivate.Table.ColumnID(pkIndex.Column(i).Ordinal)
  1441  			}
  1442  		}
  1443  
  1444  		// The lower LookupJoin must return all PK columns (they are needed as key
  1445  		// columns for the index join).
  1446  		indexCols := iter.indexCols()
  1447  		lookupJoin.Cols = scanPrivate.Cols.Intersection(indexCols)
  1448  		for i := range pkCols {
  1449  			lookupJoin.Cols.Add(pkCols[i])
  1450  		}
  1451  		lookupJoin.Cols.UnionWith(inputProps.OutputCols)
  1452  
  1453  		var indexJoin memo.LookupJoinExpr
  1454  
  1455  		// onCols are the columns that the ON condition in the (lower) lookup join
  1456  		// can refer to: input columns, or columns available in the index.
  1457  		onCols := indexCols.Union(inputProps.OutputCols)
  1458  		if c.FiltersBoundBy(lookupJoin.On, onCols) {
  1459  			// The ON condition refers only to the columns available in the index.
  1460  			//
  1461  			// For LeftJoin, both LookupJoins perform a LeftJoin. A null-extended row
  1462  			// from the lower LookupJoin will not have any matches in the top
  1463  			// LookupJoin (it has NULLs on key columns) and will get null-extended
  1464  			// there as well.
  1465  			indexJoin.On = memo.TrueFilter
  1466  		} else {
  1467  			// ON has some conditions that are bound by the columns in the index (at
  1468  			// the very least, the equality conditions we used for KeyCols), and some
  1469  			// conditions that refer to other columns. We can put the former in the
  1470  			// lower LookupJoin and the latter in the index join.
  1471  			//
  1472  			// This works for InnerJoin but not for LeftJoin because of a
  1473  			// technicality: if an input (left) row has matches in the lower
  1474  			// LookupJoin but has no matches in the index join, only the columns
  1475  			// looked up by the top index join get NULL-extended.
  1476  			if joinType == opt.LeftJoinOp {
  1477  				// TODO(radu): support LeftJoin, perhaps by looking up all columns and
  1478  				// discarding columns that are already available from the lower
  1479  				// LookupJoin. This requires a projection to avoid having the same
  1480  				// ColumnIDs on both sides of the index join.
  1481  				continue
  1482  			}
  1483  			conditions := lookupJoin.On
  1484  			lookupJoin.On = c.ExtractBoundConditions(conditions, onCols)
  1485  			indexJoin.On = c.ExtractUnboundConditions(conditions, onCols)
  1486  		}
  1487  
  1488  		indexJoin.Input = c.e.f.ConstructLookupJoin(
  1489  			lookupJoin.Input,
  1490  			lookupJoin.On,
  1491  			&lookupJoin.LookupJoinPrivate,
  1492  		)
  1493  		indexJoin.JoinType = joinType
  1494  		indexJoin.Table = scanPrivate.Table
  1495  		indexJoin.Index = cat.PrimaryIndex
  1496  		indexJoin.KeyCols = pkCols
  1497  		indexJoin.Cols = scanPrivate.Cols.Union(inputProps.OutputCols)
  1498  		indexJoin.LookupColsAreTableKey = true
  1499  
  1500  		// Create the LookupJoin for the index join in the same group.
  1501  		c.e.mem.AddLookupJoinToGroup(&indexJoin, grp)
  1502  	}
  1503  }
  1504  
  1505  // GenerateGeoLookupJoins is similar to GenerateLookupJoins, but instead
  1506  // of generating lookup joins with regular indexes, it generates geospatial
  1507  // lookup joins with inverted geospatial indexes. Since these indexes are not
  1508  // covering, all geospatial lookup joins must be wrapped in an index join with
  1509  // the primary index of the table. See the description of Case 2 in the comment
  1510  // above GenerateLookupJoins for details about how this works.
  1511  func (c *CustomFuncs) GenerateGeoLookupJoins(
  1512  	grp memo.RelExpr,
  1513  	joinType opt.Operator,
  1514  	input memo.RelExpr,
  1515  	scanPrivate *memo.ScanPrivate,
  1516  	on memo.FiltersExpr,
  1517  	joinPrivate *memo.JoinPrivate,
  1518  	fn opt.ScalarExpr,
  1519  ) {
  1520  	if !joinPrivate.Flags.Has(memo.AllowLookupJoinIntoRight) {
  1521  		return
  1522  	}
  1523  
  1524  	// Geospatial lookup joins are not covering, so we must wrap them in an
  1525  	// index join.
  1526  	if scanPrivate.Flags.NoIndexJoin {
  1527  		return
  1528  	}
  1529  
  1530  	function := fn.(*memo.FunctionExpr)
  1531  	inputProps := input.Relational()
  1532  
  1533  	// Extract the geospatial relationship as well as the variable inputs to
  1534  	// the geospatial function.
  1535  	relationship, ok := geoRelationshipMap[function.Name]
  1536  	if !ok {
  1537  		panic(errors.AssertionFailedf(
  1538  			"GenerateGeoLookupJoins called on a function that cannot be index-accelerated",
  1539  		))
  1540  	}
  1541  
  1542  	if function.Args.ChildCount() != 2 {
  1543  		panic(errors.AssertionFailedf(
  1544  			"all index-accelerated geospatial functions should have two arguments",
  1545  		))
  1546  	}
  1547  
  1548  	variable, ok := function.Args.Child(0).(*memo.VariableExpr)
  1549  	if !ok {
  1550  		panic(errors.AssertionFailedf(
  1551  			"GenerateGeoLookupJoins called on function containing non-variable inputs",
  1552  		))
  1553  	}
  1554  	indexGeoCol := variable.Col
  1555  
  1556  	variable, ok = function.Args.Child(1).(*memo.VariableExpr)
  1557  	if !ok {
  1558  		panic(errors.AssertionFailedf(
  1559  			"GenerateGeoLookupJoins called on function containing non-variable inputs",
  1560  		))
  1561  	}
  1562  	if !inputProps.OutputCols.Contains(variable.Col) {
  1563  		// The second argument should be from the input.
  1564  		// TODO(rytaft): Commute the geospatial function in this case.
  1565  		//   Contains    <->  ContainedBy
  1566  		//   Intersects  <->  Intersects
  1567  		return
  1568  	}
  1569  	inputGeoCol := variable.Col
  1570  
  1571  	var pkCols opt.ColList
  1572  
  1573  	var iter scanIndexIter
  1574  	iter.init(c.e.mem, scanPrivate, onlyInvertedIndexes)
  1575  	for iter.next() {
  1576  		if scanPrivate.Table.ColumnID(iter.index.Column(0).Ordinal) != indexGeoCol {
  1577  			continue
  1578  		}
  1579  
  1580  		if pkCols == nil {
  1581  			pkIndex := iter.tab.Index(cat.PrimaryIndex)
  1582  			pkCols = make(opt.ColList, pkIndex.KeyColumnCount())
  1583  			for i := range pkCols {
  1584  				pkCols[i] = scanPrivate.Table.ColumnID(pkIndex.Column(i).Ordinal)
  1585  			}
  1586  		}
  1587  
  1588  		// Though the index is marked as containing the geospatial column being
  1589  		// indexed, it doesn't actually, and it is only valid to extract the
  1590  		// primary key columns from it.
  1591  		indexCols := pkCols.ToSet()
  1592  
  1593  		lookupJoin := memo.GeoLookupJoinExpr{Input: input}
  1594  		lookupJoin.JoinPrivate = *joinPrivate
  1595  		lookupJoin.JoinType = joinType
  1596  		lookupJoin.Table = scanPrivate.Table
  1597  		lookupJoin.Index = iter.indexOrdinal
  1598  		lookupJoin.GeoRelationshipType = relationship
  1599  		lookupJoin.GeoCol = inputGeoCol
  1600  		lookupJoin.Cols = indexCols.Union(inputProps.OutputCols)
  1601  
  1602  		var indexJoin memo.LookupJoinExpr
  1603  
  1604  		// ON may have some conditions that are bound by the columns in the index
  1605  		// and some conditions that refer to other columns. We can put the former
  1606  		// in the GeospatialLookupJoin and the latter in the index join.
  1607  		lookupJoin.On = c.ExtractBoundConditions(on, lookupJoin.Cols)
  1608  		indexJoin.On = c.ExtractUnboundConditions(on, lookupJoin.Cols)
  1609  
  1610  		indexJoin.Input = c.e.f.ConstructGeoLookupJoin(
  1611  			lookupJoin.Input,
  1612  			lookupJoin.On,
  1613  			&lookupJoin.GeoLookupJoinPrivate,
  1614  		)
  1615  		indexJoin.JoinType = joinType
  1616  		indexJoin.Table = scanPrivate.Table
  1617  		indexJoin.Index = cat.PrimaryIndex
  1618  		indexJoin.KeyCols = pkCols
  1619  		indexJoin.Cols = scanPrivate.Cols.Union(inputProps.OutputCols)
  1620  		indexJoin.LookupColsAreTableKey = true
  1621  
  1622  		// Create the LookupJoin for the index join in the same group.
  1623  		c.e.mem.AddLookupJoinToGroup(&indexJoin, grp)
  1624  	}
  1625  }
  1626  
  1627  // geoRelationshipMap contains all the geospatial functions that can be index-
  1628  // accelerated. Each function implies a certain type of geospatial relationship,
  1629  // which affects how the index is queried as part of a constrained scan or
  1630  // geospatial lookup join. geoRelationshipMap maps the function name to its
  1631  // corresponding relationship (Covers, CoveredBy, or Intersects).
  1632  //
  1633  // Note that for all of these functions, a geospatial lookup join or constrained
  1634  // index scan may produce false positives. Therefore, the original function must
  1635  // be called on the output of the index operation to filter the results.
  1636  // TODO(rytaft): add ST_DFullyWithin (geoindex.Covers) and ST_DWithin
  1637  //  (geoindex.Intersects) once we add support for extending a geometry.
  1638  var geoRelationshipMap = map[string]geoindex.RelationshipType{
  1639  	"st_covers":           geoindex.Covers,
  1640  	"st_coveredby":        geoindex.CoveredBy,
  1641  	"st_contains":         geoindex.Covers,
  1642  	"st_containsproperly": geoindex.Covers,
  1643  	"st_crosses":          geoindex.Intersects,
  1644  	"st_equals":           geoindex.Intersects,
  1645  	"st_intersects":       geoindex.Intersects,
  1646  	"st_overlaps":         geoindex.Intersects,
  1647  	"st_touches":          geoindex.Intersects,
  1648  	"st_within":           geoindex.CoveredBy,
  1649  }
  1650  
  1651  // IsGeoIndexFunction returns true if the given function is a geospatial
  1652  // function that can be index-accelerated.
  1653  func (c *CustomFuncs) IsGeoIndexFunction(fn opt.ScalarExpr) bool {
  1654  	return IsGeoIndexFunction(fn)
  1655  }
  1656  
  1657  // IsGeoIndexFunction returns true if the given function is a geospatial
  1658  // function that can be index-accelerated.
  1659  func IsGeoIndexFunction(fn opt.ScalarExpr) bool {
  1660  	function := fn.(*memo.FunctionExpr)
  1661  	_, ok := geoRelationshipMap[function.Name]
  1662  	return ok
  1663  }
  1664  
  1665  // HasAllVariableArgs returns true if all the arguments to the given function
  1666  // are variables.
  1667  func (c *CustomFuncs) HasAllVariableArgs(fn opt.ScalarExpr) bool {
  1668  	function := fn.(*memo.FunctionExpr)
  1669  	for i, n := 0, function.Args.ChildCount(); i < n; i++ {
  1670  		if _, ok := function.Args.Child(i).(*memo.VariableExpr); !ok {
  1671  			return false
  1672  		}
  1673  	}
  1674  	return true
  1675  }
  1676  
  1677  // findConstantFilter tries to find a filter that is exactly equivalent to
  1678  // constraining the given column to a constant value. Note that the constant
  1679  // value can be NULL (for an `x IS NULL` filter).
  1680  func (c *CustomFuncs) findConstantFilter(
  1681  	filters memo.FiltersExpr, col opt.ColumnID,
  1682  ) (value tree.Datum, filterIdx int, ok bool) {
  1683  	for filterIdx := range filters {
  1684  		props := filters[filterIdx].ScalarProps()
  1685  		if props.TightConstraints {
  1686  			constCol, constVal, ok := props.Constraints.IsSingleColumnConstValue(c.e.evalCtx)
  1687  			if ok && constCol == col {
  1688  				return constVal, filterIdx, true
  1689  			}
  1690  		}
  1691  	}
  1692  	return nil, -1, false
  1693  }
  1694  
  1695  // eqColsForZigzag is a helper function to generate eqCol lists for the zigzag
  1696  // joiner. The zigzag joiner requires that the equality columns immediately
  1697  // follow the fixed columns in the index. Fixed here refers to columns that
  1698  // have been constrained to a constant value.
  1699  //
  1700  // There are two kinds of equality columns that this function takes care of:
  1701  // columns that have the same ColumnID on both sides (i.e. the same column),
  1702  // as well as columns that have been equated in some ON filter (i.e. they are
  1703  // contained in leftEqCols and rightEqCols at the same index).
  1704  //
  1705  // This function iterates through all columns of the indexes in order,
  1706  // skips past the fixed columns, and then generates however many eqCols
  1707  // there are that meet the above criteria.
  1708  //
  1709  // Returns a list of column ordinals for each index.
  1710  //
  1711  // See the comment in pkg/sql/rowexec/zigzag_joiner.go for more details
  1712  // on the role eqCols and fixed cols play in zigzag joins.
  1713  func eqColsForZigzag(
  1714  	tab cat.Table,
  1715  	tabID opt.TableID,
  1716  	leftIndex cat.Index,
  1717  	rightIndex cat.Index,
  1718  	fixedCols opt.ColSet,
  1719  	leftEqCols opt.ColList,
  1720  	rightEqCols opt.ColList,
  1721  ) (leftEqPrefix, rightEqPrefix opt.ColList) {
  1722  	leftEqPrefix = make(opt.ColList, 0, len(leftEqCols))
  1723  	rightEqPrefix = make(opt.ColList, 0, len(rightEqCols))
  1724  	// We can only zigzag on columns present in the key component of the index,
  1725  	// so use the LaxKeyColumnCount here because that's the longest prefix of the
  1726  	// columns in the index which is guaranteed to exist in the key component.
  1727  	// Using KeyColumnCount is invalid, because if we have a unique index with
  1728  	// nullable columns, the "key columns" include the primary key of the table,
  1729  	// which is only present in the key component if one of the other columns is
  1730  	// NULL.
  1731  	i, leftCnt := 0, leftIndex.LaxKeyColumnCount()
  1732  	j, rightCnt := 0, rightIndex.LaxKeyColumnCount()
  1733  	for ; i < leftCnt; i++ {
  1734  		colID := tabID.ColumnID(leftIndex.Column(i).Ordinal)
  1735  		if !fixedCols.Contains(colID) {
  1736  			break
  1737  		}
  1738  	}
  1739  	for ; j < rightCnt; j++ {
  1740  		colID := tabID.ColumnID(rightIndex.Column(j).Ordinal)
  1741  		if !fixedCols.Contains(colID) {
  1742  			break
  1743  		}
  1744  	}
  1745  
  1746  	for i < leftCnt && j < rightCnt {
  1747  		leftColID := tabID.ColumnID(leftIndex.Column(i).Ordinal)
  1748  		rightColID := tabID.ColumnID(rightIndex.Column(j).Ordinal)
  1749  		i++
  1750  		j++
  1751  
  1752  		if leftColID == rightColID {
  1753  			leftEqPrefix = append(leftEqPrefix, leftColID)
  1754  			rightEqPrefix = append(rightEqPrefix, rightColID)
  1755  			continue
  1756  		}
  1757  		leftIdx, leftOk := leftEqCols.Find(leftColID)
  1758  		rightIdx, rightOk := rightEqCols.Find(rightColID)
  1759  		// If both columns are at the same index in their respective
  1760  		// EqCols lists, they were equated in the filters.
  1761  		if leftOk && rightOk && leftIdx == rightIdx {
  1762  			leftEqPrefix = append(leftEqPrefix, leftColID)
  1763  			rightEqPrefix = append(rightEqPrefix, rightColID)
  1764  			continue
  1765  		} else {
  1766  			// We've reached the first non-equal column; the zigzag
  1767  			// joiner does not support non-contiguous/non-prefix equal
  1768  			// columns.
  1769  			break
  1770  		}
  1771  
  1772  	}
  1773  
  1774  	return leftEqPrefix, rightEqPrefix
  1775  }
  1776  
  1777  // fixedColsForZigzag is a helper function to generate FixedCols lists for the
  1778  // zigzag join expression. This function iterates through the columns of the
  1779  // specified index in order until it comes across the first column ID that is
  1780  // not constrained to a constant.
  1781  func (c *CustomFuncs) fixedColsForZigzag(
  1782  	index cat.Index, tabID opt.TableID, filters memo.FiltersExpr,
  1783  ) (fixedCols opt.ColList, vals memo.ScalarListExpr, typs []*types.T) {
  1784  	for i, cnt := 0, index.ColumnCount(); i < cnt; i++ {
  1785  		colID := tabID.ColumnID(index.Column(i).Ordinal)
  1786  		val := memo.ExtractValueForConstColumn(filters, c.e.mem, c.e.evalCtx, colID)
  1787  		if val == nil {
  1788  			break
  1789  		}
  1790  		if vals == nil {
  1791  			vals = make(memo.ScalarListExpr, 0, cnt-i)
  1792  			typs = make([]*types.T, 0, cnt-i)
  1793  			fixedCols = make(opt.ColList, 0, cnt-i)
  1794  		}
  1795  
  1796  		dt := val.ResolvedType()
  1797  		vals = append(vals, c.e.f.ConstructConstVal(val, dt))
  1798  		typs = append(typs, dt)
  1799  		fixedCols = append(fixedCols, colID)
  1800  	}
  1801  	return fixedCols, vals, typs
  1802  }
  1803  
  1804  // GenerateZigzagJoins generates zigzag joins for all pairs of indexes of the
  1805  // Scan table which contain one of the constant columns in the FiltersExpr as
  1806  // its prefix.
  1807  //
  1808  // Similar to the lookup join, if the selected index pair does not contain
  1809  // all the columns in the output of the scan, we wrap the zigzag join
  1810  // in another index join (implemented as a lookup join) on the primary index.
  1811  // The index join is implemented with a lookup join since the index join does
  1812  // not support arbitrary input sources that are not plain index scans.
  1813  func (c *CustomFuncs) GenerateZigzagJoins(
  1814  	grp memo.RelExpr, scanPrivate *memo.ScanPrivate, filters memo.FiltersExpr,
  1815  ) {
  1816  
  1817  	// Short circuit unless zigzag joins are explicitly enabled.
  1818  	if !c.e.evalCtx.SessionData.ZigzagJoinEnabled {
  1819  		return
  1820  	}
  1821  
  1822  	fixedCols := memo.ExtractConstColumns(filters, c.e.mem, c.e.evalCtx)
  1823  
  1824  	if fixedCols.Len() == 0 {
  1825  		// Zigzagging isn't helpful in the absence of fixed columns.
  1826  		return
  1827  	}
  1828  
  1829  	// Iterate through indexes, looking for those prefixed with fixedEq cols.
  1830  	// Efficiently finding a set of indexes that make the most efficient zigzag
  1831  	// join, with no limit on the number of indexes selected, is an instance of
  1832  	// this NP-hard problem:
  1833  	// https://en.wikipedia.org/wiki/Maximum_coverage_problem
  1834  	//
  1835  	// A formal definition would be: Suppose we have a set of fixed columns F
  1836  	// (defined as fixedCols in the code above), and a set of indexes I. The
  1837  	// "fixed prefix" of every index, in this context, refers to the longest
  1838  	// prefix of each index's columns that is in F. In other words, we stop
  1839  	// adding to the prefix when we come across the first non-fixed column
  1840  	// in an index.
  1841  	//
  1842  	// We want to find at most k = 2 indexes from I (in the future k could be
  1843  	// >= 2 when the zigzag joiner supports 2+ index zigzag joins) that cover
  1844  	// the maximum number of columns in F. An index is defined to have covered
  1845  	// a column if that column is in the index's fixed prefix.
  1846  	//
  1847  	// Since only 2-way zigzag joins are currently supported, the naive
  1848  	// approach is bounded at n^2. For now, just do that - a quadratic
  1849  	// iteration through all indexes.
  1850  	//
  1851  	// TODO(itsbilal): Implement the greedy or weighted version of the
  1852  	// algorithm laid out here:
  1853  	// https://en.wikipedia.org/wiki/Maximum_coverage_problem
  1854  	var iter, iter2 scanIndexIter
  1855  	iter.init(c.e.mem, scanPrivate, onlyStandardIndexes)
  1856  	for iter.next() {
  1857  		if iter.indexOrdinal == cat.PrimaryIndex {
  1858  			continue
  1859  		}
  1860  
  1861  		leftFixed := c.indexConstrainedCols(iter.index, scanPrivate.Table, fixedCols)
  1862  		// Short-circuit quickly if the first column in the index is not a fixed
  1863  		// column.
  1864  		if leftFixed.Len() == 0 {
  1865  			continue
  1866  		}
  1867  		iter2.init(c.e.mem, scanPrivate, onlyStandardIndexes)
  1868  		// Only look at indexes after this one.
  1869  		iter2.indexOrdinal = iter.indexOrdinal
  1870  
  1871  		for iter2.next() {
  1872  			rightFixed := c.indexConstrainedCols(iter2.index, scanPrivate.Table, fixedCols)
  1873  			// If neither side contributes a fixed column not contributed by the
  1874  			// other, then there's no reason to zigzag on this pair of indexes.
  1875  			if leftFixed.SubsetOf(rightFixed) || rightFixed.SubsetOf(leftFixed) {
  1876  				continue
  1877  			}
  1878  			// Columns that are in both indexes are, by definition, equal.
  1879  			leftCols := iter.indexCols()
  1880  			rightCols := iter2.indexCols()
  1881  			eqCols := leftCols.Intersection(rightCols)
  1882  			eqCols.DifferenceWith(fixedCols)
  1883  			if eqCols.Len() == 0 {
  1884  				// A simple index join is more efficient in such cases.
  1885  				continue
  1886  			}
  1887  
  1888  			// If there are any equalities across the columns of the two indexes,
  1889  			// push them into the zigzag join spec.
  1890  			leftEq, rightEq := memo.ExtractJoinEqualityColumns(
  1891  				leftCols, rightCols, filters,
  1892  			)
  1893  			leftEqCols, rightEqCols := eqColsForZigzag(
  1894  				iter.tab,
  1895  				scanPrivate.Table,
  1896  				iter.index,
  1897  				iter2.index,
  1898  				fixedCols,
  1899  				leftEq,
  1900  				rightEq,
  1901  			)
  1902  
  1903  			if len(leftEqCols) == 0 || len(rightEqCols) == 0 {
  1904  				// One of the indexes is not sorted by any of the equality
  1905  				// columns, because the equality columns do not immediately
  1906  				// succeed the fixed columns. A zigzag join cannot be planned.
  1907  				continue
  1908  			}
  1909  
  1910  			// Confirm the primary key columns are in both leftEqCols and
  1911  			// rightEqCols. The conversion of a select with filters to a
  1912  			// zigzag join requires the primary key columns to be in the output
  1913  			// for output correctness; otherwise, we could be outputting more
  1914  			// results than there should be (due to an equality on a non-unique
  1915  			// non-required value).
  1916  			pkIndex := iter.tab.Index(cat.PrimaryIndex)
  1917  			pkCols := make(opt.ColList, pkIndex.KeyColumnCount())
  1918  			pkColsFound := true
  1919  			for i := range pkCols {
  1920  				pkCols[i] = scanPrivate.Table.ColumnID(pkIndex.Column(i).Ordinal)
  1921  
  1922  				if _, ok := leftEqCols.Find(pkCols[i]); !ok {
  1923  					pkColsFound = false
  1924  					break
  1925  				}
  1926  				if _, ok := rightEqCols.Find(pkCols[i]); !ok {
  1927  					pkColsFound = false
  1928  					break
  1929  				}
  1930  			}
  1931  			if !pkColsFound {
  1932  				continue
  1933  			}
  1934  
  1935  			zigzagJoin := memo.ZigzagJoinExpr{
  1936  				On: filters,
  1937  				ZigzagJoinPrivate: memo.ZigzagJoinPrivate{
  1938  					LeftTable:   scanPrivate.Table,
  1939  					LeftIndex:   iter.indexOrdinal,
  1940  					RightTable:  scanPrivate.Table,
  1941  					RightIndex:  iter2.indexOrdinal,
  1942  					LeftEqCols:  leftEqCols,
  1943  					RightEqCols: rightEqCols,
  1944  				},
  1945  			}
  1946  
  1947  			leftFixedCols, leftVals, leftTypes := c.fixedColsForZigzag(
  1948  				iter.index, scanPrivate.Table, filters,
  1949  			)
  1950  			rightFixedCols, rightVals, rightTypes := c.fixedColsForZigzag(
  1951  				iter2.index, scanPrivate.Table, filters,
  1952  			)
  1953  
  1954  			if len(leftFixedCols) != leftFixed.Len() || len(rightFixedCols) != rightFixed.Len() {
  1955  				panic(errors.AssertionFailedf("could not populate all fixed columns for zig zag join"))
  1956  			}
  1957  
  1958  			zigzagJoin.LeftFixedCols = leftFixedCols
  1959  			zigzagJoin.RightFixedCols = rightFixedCols
  1960  
  1961  			leftTupleTyp := types.MakeTuple(leftTypes)
  1962  			rightTupleTyp := types.MakeTuple(rightTypes)
  1963  			zigzagJoin.FixedVals = memo.ScalarListExpr{
  1964  				c.e.f.ConstructTuple(leftVals, leftTupleTyp),
  1965  				c.e.f.ConstructTuple(rightVals, rightTupleTyp),
  1966  			}
  1967  
  1968  			zigzagJoin.On = memo.ExtractRemainingJoinFilters(
  1969  				filters,
  1970  				zigzagJoin.LeftEqCols,
  1971  				zigzagJoin.RightEqCols,
  1972  			)
  1973  			zigzagCols := leftCols.Copy()
  1974  			zigzagCols.UnionWith(rightCols)
  1975  
  1976  			if scanPrivate.Cols.SubsetOf(zigzagCols) {
  1977  				// Case 1 (zigzagged indexes contain all requested columns).
  1978  				zigzagJoin.Cols = scanPrivate.Cols
  1979  				c.e.mem.AddZigzagJoinToGroup(&zigzagJoin, grp)
  1980  				continue
  1981  			}
  1982  
  1983  			if scanPrivate.Flags.NoIndexJoin {
  1984  				continue
  1985  			}
  1986  
  1987  			// Case 2 (wrap zigzag join in an index join).
  1988  			var indexJoin memo.LookupJoinExpr
  1989  			// Ensure the zigzag join returns pk columns.
  1990  			zigzagJoin.Cols = scanPrivate.Cols.Intersection(zigzagCols)
  1991  			for i := range pkCols {
  1992  				zigzagJoin.Cols.Add(pkCols[i])
  1993  			}
  1994  
  1995  			if c.FiltersBoundBy(zigzagJoin.On, zigzagCols) {
  1996  				// The ON condition refers only to the columns available in the zigzag
  1997  				// indices.
  1998  				indexJoin.On = memo.TrueFilter
  1999  			} else {
  2000  				// ON has some conditions that are bound by the columns in the index (at
  2001  				// the very least, the equality conditions we used for EqCols and FixedCols),
  2002  				// and some conditions that refer to other table columns. We can put
  2003  				// the former in the lower ZigzagJoin and the latter in the index join.
  2004  				conditions := zigzagJoin.On
  2005  				zigzagJoin.On = c.ExtractBoundConditions(conditions, zigzagCols)
  2006  				indexJoin.On = c.ExtractUnboundConditions(conditions, zigzagCols)
  2007  			}
  2008  
  2009  			indexJoin.Input = c.e.f.ConstructZigzagJoin(
  2010  				zigzagJoin.On,
  2011  				&zigzagJoin.ZigzagJoinPrivate,
  2012  			)
  2013  			indexJoin.JoinType = opt.InnerJoinOp
  2014  			indexJoin.Table = scanPrivate.Table
  2015  			indexJoin.Index = cat.PrimaryIndex
  2016  			indexJoin.KeyCols = pkCols
  2017  			indexJoin.Cols = scanPrivate.Cols
  2018  			indexJoin.LookupColsAreTableKey = true
  2019  
  2020  			// Create the LookupJoin for the index join in the same group as the
  2021  			// original select.
  2022  			c.e.mem.AddLookupJoinToGroup(&indexJoin, grp)
  2023  		}
  2024  	}
  2025  }
  2026  
  2027  // indexConstrainedCols computes the set of columns in allFixedCols which form
  2028  // a prefix of the key columns in idx.
  2029  func (c *CustomFuncs) indexConstrainedCols(
  2030  	idx cat.Index, tab opt.TableID, allFixedCols opt.ColSet,
  2031  ) opt.ColSet {
  2032  	var constrained opt.ColSet
  2033  	for i, n := 0, idx.ColumnCount(); i < n; i++ {
  2034  		col := tab.ColumnID(idx.Column(i).Ordinal)
  2035  		if allFixedCols.Contains(col) {
  2036  			constrained.Add(col)
  2037  		} else {
  2038  			break
  2039  		}
  2040  	}
  2041  	return constrained
  2042  }
  2043  
  2044  // GenerateInvertedIndexZigzagJoins generates zigzag joins for constraints on
  2045  // inverted index. It looks for cases where one inverted index can satisfy
  2046  // two constraints, and it produces zigzag joins with the same index on both
  2047  // sides of the zigzag join for those cases, fixed on different constant values.
  2048  func (c *CustomFuncs) GenerateInvertedIndexZigzagJoins(
  2049  	grp memo.RelExpr, scanPrivate *memo.ScanPrivate, filters memo.FiltersExpr,
  2050  ) {
  2051  	// Short circuit unless zigzag joins are explicitly enabled.
  2052  	if !c.e.evalCtx.SessionData.ZigzagJoinEnabled {
  2053  		return
  2054  	}
  2055  
  2056  	var sb indexScanBuilder
  2057  	sb.init(c, scanPrivate.Table)
  2058  
  2059  	// Iterate over all inverted indexes.
  2060  	var iter scanIndexIter
  2061  	iter.init(c.e.mem, scanPrivate, onlyInvertedIndexes)
  2062  	for iter.next() {
  2063  		// See if there are two or more constraints that can be satisfied
  2064  		// by this inverted index. This is possible with inverted indexes as
  2065  		// opposed to secondary indexes, because one row in the primary index
  2066  		// can often correspond to multiple rows in an inverted index. This
  2067  		// function generates all constraints it can derive for this index;
  2068  		// not all of which might get used in this function.
  2069  		constraints, ok := c.allInvIndexConstraints(
  2070  			filters, scanPrivate.Table, iter.indexOrdinal,
  2071  		)
  2072  		if !ok || len(constraints) < 2 {
  2073  			continue
  2074  		}
  2075  		// In theory, we could explore zigzag joins on all constraint pairs.
  2076  		// However, in the absence of stats on inverted indexes, we will not
  2077  		// be able to distinguish more selective constraints from less
  2078  		// selective ones anyway, so just pick the first two constraints.
  2079  		//
  2080  		// TODO(itsbilal): Use the remaining constraints to build a remaining
  2081  		// filters expression, instead of just reusing filters from the scan.
  2082  		constraint := constraints[0]
  2083  		constraint2 := constraints[1]
  2084  
  2085  		minPrefix := constraint.ExactPrefix(c.e.evalCtx)
  2086  		if otherPrefix := constraint2.ExactPrefix(c.e.evalCtx); otherPrefix < minPrefix {
  2087  			minPrefix = otherPrefix
  2088  		}
  2089  
  2090  		if minPrefix == 0 {
  2091  			continue
  2092  		}
  2093  
  2094  		zigzagJoin := memo.ZigzagJoinExpr{
  2095  			On: filters,
  2096  			ZigzagJoinPrivate: memo.ZigzagJoinPrivate{
  2097  				LeftTable:  scanPrivate.Table,
  2098  				LeftIndex:  iter.indexOrdinal,
  2099  				RightTable: scanPrivate.Table,
  2100  				RightIndex: iter.indexOrdinal,
  2101  			},
  2102  		}
  2103  
  2104  		// Get constant values from each constraint. Add them to FixedVals as
  2105  		// tuples, with associated Column IDs in both {Left,Right}FixedCols.
  2106  		leftVals := make(memo.ScalarListExpr, minPrefix)
  2107  		leftTypes := make([]*types.T, minPrefix)
  2108  		rightVals := make(memo.ScalarListExpr, minPrefix)
  2109  		rightTypes := make([]*types.T, minPrefix)
  2110  
  2111  		zigzagJoin.LeftFixedCols = make(opt.ColList, minPrefix)
  2112  		zigzagJoin.RightFixedCols = make(opt.ColList, minPrefix)
  2113  		for i := 0; i < minPrefix; i++ {
  2114  			leftVal := constraint.Spans.Get(0).StartKey().Value(i)
  2115  			rightVal := constraint2.Spans.Get(0).StartKey().Value(i)
  2116  
  2117  			leftVals[i] = c.e.f.ConstructConstVal(leftVal, leftVal.ResolvedType())
  2118  			leftTypes[i] = leftVal.ResolvedType()
  2119  			rightVals[i] = c.e.f.ConstructConstVal(rightVal, rightVal.ResolvedType())
  2120  			rightTypes[i] = rightVal.ResolvedType()
  2121  			zigzagJoin.LeftFixedCols[i] = constraint.Columns.Get(i).ID()
  2122  			zigzagJoin.RightFixedCols[i] = constraint.Columns.Get(i).ID()
  2123  		}
  2124  
  2125  		leftTupleTyp := types.MakeTuple(leftTypes)
  2126  		rightTupleTyp := types.MakeTuple(rightTypes)
  2127  		zigzagJoin.FixedVals = memo.ScalarListExpr{
  2128  			c.e.f.ConstructTuple(leftVals, leftTupleTyp),
  2129  			c.e.f.ConstructTuple(rightVals, rightTupleTyp),
  2130  		}
  2131  
  2132  		// Set equality columns - all remaining columns after the fixed prefix
  2133  		// need to be equal.
  2134  		eqColLen := iter.index.ColumnCount() - minPrefix
  2135  		zigzagJoin.LeftEqCols = make(opt.ColList, eqColLen)
  2136  		zigzagJoin.RightEqCols = make(opt.ColList, eqColLen)
  2137  		for i := minPrefix; i < iter.index.ColumnCount(); i++ {
  2138  			colID := scanPrivate.Table.ColumnID(iter.index.Column(i).Ordinal)
  2139  			zigzagJoin.LeftEqCols[i-minPrefix] = colID
  2140  			zigzagJoin.RightEqCols[i-minPrefix] = colID
  2141  		}
  2142  		zigzagJoin.On = filters
  2143  
  2144  		// Don't output the first column (i.e. the inverted index's JSON key
  2145  		// col) from the zigzag join. It could contain partial values, so
  2146  		// presenting it in the output or checking ON conditions against
  2147  		// it makes little sense.
  2148  		zigzagCols := iter.indexCols()
  2149  		for i, cnt := 0, iter.index.KeyColumnCount(); i < cnt; i++ {
  2150  			colID := scanPrivate.Table.ColumnID(iter.index.Column(i).Ordinal)
  2151  			zigzagCols.Remove(colID)
  2152  		}
  2153  
  2154  		pkIndex := iter.tab.Index(cat.PrimaryIndex)
  2155  		pkCols := make(opt.ColList, pkIndex.KeyColumnCount())
  2156  		for i := range pkCols {
  2157  			pkCols[i] = scanPrivate.Table.ColumnID(pkIndex.Column(i).Ordinal)
  2158  			// Ensure primary key columns are always retrieved from the zigzag
  2159  			// join.
  2160  			zigzagCols.Add(pkCols[i])
  2161  		}
  2162  
  2163  		// Case 1 (zigzagged indexes contain all requested columns).
  2164  		if scanPrivate.Cols.SubsetOf(zigzagCols) {
  2165  			zigzagJoin.Cols = scanPrivate.Cols
  2166  			c.e.mem.AddZigzagJoinToGroup(&zigzagJoin, grp)
  2167  			continue
  2168  		}
  2169  
  2170  		if scanPrivate.Flags.NoIndexJoin {
  2171  			continue
  2172  		}
  2173  
  2174  		// Case 2 (wrap zigzag join in an index join).
  2175  
  2176  		var indexJoin memo.LookupJoinExpr
  2177  		// Ensure the zigzag join returns pk columns.
  2178  		zigzagJoin.Cols = scanPrivate.Cols.Intersection(zigzagCols)
  2179  		for i := range pkCols {
  2180  			zigzagJoin.Cols.Add(pkCols[i])
  2181  		}
  2182  
  2183  		if c.FiltersBoundBy(zigzagJoin.On, zigzagCols) {
  2184  			// The ON condition refers only to the columns available in the zigzag
  2185  			// indices.
  2186  			indexJoin.On = memo.TrueFilter
  2187  		} else {
  2188  			// ON has some conditions that are bound by the columns in the index (at
  2189  			// the very least, the equality conditions we used for EqCols and FixedCols),
  2190  			// and some conditions that refer to other table columns. We can put
  2191  			// the former in the lower ZigzagJoin and the latter in the index join.
  2192  			conditions := zigzagJoin.On
  2193  			zigzagJoin.On = c.ExtractBoundConditions(conditions, zigzagCols)
  2194  			indexJoin.On = c.ExtractUnboundConditions(conditions, zigzagCols)
  2195  		}
  2196  
  2197  		indexJoin.Input = c.e.f.ConstructZigzagJoin(
  2198  			zigzagJoin.On,
  2199  			&zigzagJoin.ZigzagJoinPrivate,
  2200  		)
  2201  		indexJoin.JoinType = opt.InnerJoinOp
  2202  		indexJoin.Table = scanPrivate.Table
  2203  		indexJoin.Index = cat.PrimaryIndex
  2204  		indexJoin.KeyCols = pkCols
  2205  		indexJoin.Cols = scanPrivate.Cols
  2206  		indexJoin.LookupColsAreTableKey = true
  2207  
  2208  		// Create the LookupJoin for the index join in the same group as the
  2209  		// original select.
  2210  		c.e.mem.AddLookupJoinToGroup(&indexJoin, grp)
  2211  	}
  2212  }
  2213  
  2214  // deriveJoinSize returns the number of base relations (i.e., not joins)
  2215  // being joined underneath the given relational expression.
  2216  func (c *CustomFuncs) deriveJoinSize(e memo.RelExpr) int {
  2217  	relProps := e.Relational()
  2218  	if relProps.IsAvailable(props.JoinSize) {
  2219  		return relProps.Rule.JoinSize
  2220  	}
  2221  	relProps.SetAvailable(props.JoinSize)
  2222  
  2223  	switch j := e.(type) {
  2224  	case *memo.InnerJoinExpr:
  2225  		relProps.Rule.JoinSize = c.deriveJoinSize(j.Left) + c.deriveJoinSize(j.Right)
  2226  	default:
  2227  		relProps.Rule.JoinSize = 1
  2228  	}
  2229  
  2230  	return relProps.Rule.JoinSize
  2231  }
  2232  
  2233  // ShouldReorderJoins returns whether the optimizer should attempt to find
  2234  // a better ordering of inner joins.
  2235  func (c *CustomFuncs) ShouldReorderJoins(left, right memo.RelExpr) bool {
  2236  	// TODO(justin): referencing left and right here is a hack: ideally
  2237  	// we'd want to be able to reference the logical properties of the
  2238  	// expression being explored in this CustomFunc.
  2239  	size := c.deriveJoinSize(left) + c.deriveJoinSize(right)
  2240  	return size <= c.e.evalCtx.SessionData.ReorderJoinsLimit
  2241  }
  2242  
  2243  // IsSimpleEquality returns true if all of the filter conditions are equalities
  2244  // between simple data types (constants, variables, tuples and NULL).
  2245  func (c *CustomFuncs) IsSimpleEquality(filters memo.FiltersExpr) bool {
  2246  	for i := range filters {
  2247  		eqFilter, ok := filters[i].Condition.(*memo.EqExpr)
  2248  		if !ok {
  2249  			return false
  2250  		}
  2251  
  2252  		left, right := eqFilter.Left, eqFilter.Right
  2253  		switch left.Op() {
  2254  		case opt.VariableOp, opt.ConstOp, opt.NullOp, opt.TupleOp:
  2255  		default:
  2256  			return false
  2257  		}
  2258  
  2259  		switch right.Op() {
  2260  		case opt.VariableOp, opt.ConstOp, opt.NullOp, opt.TupleOp:
  2261  		default:
  2262  			return false
  2263  		}
  2264  	}
  2265  
  2266  	return true
  2267  }
  2268  
  2269  // ----------------------------------------------------------------------
  2270  //
  2271  // GroupBy Rules
  2272  //   Custom match and replace functions used with groupby.opt rules.
  2273  //
  2274  // ----------------------------------------------------------------------
  2275  
  2276  // IsCanonicalGroupBy returns true if the private is for the canonical version
  2277  // of the grouping operator. This is the operator that is built initially (and
  2278  // has all grouping columns as optional in the ordering), as opposed to variants
  2279  // generated by the GenerateStreamingGroupBy exploration rule.
  2280  func (c *CustomFuncs) IsCanonicalGroupBy(private *memo.GroupingPrivate) bool {
  2281  	return private.Ordering.Any() || private.GroupingCols.SubsetOf(private.Ordering.Optional)
  2282  }
  2283  
  2284  // MakeProjectFromPassthroughAggs constructs a top-level Project operator that
  2285  // contains one output column per function in the given aggregrate list. The
  2286  // input expression is expected to return zero or one rows, and the aggregate
  2287  // functions are expected to always pass through their values in that case.
  2288  func (c *CustomFuncs) MakeProjectFromPassthroughAggs(
  2289  	grp memo.RelExpr, input memo.RelExpr, aggs memo.AggregationsExpr,
  2290  ) {
  2291  	if !input.Relational().Cardinality.IsZeroOrOne() {
  2292  		panic(errors.AssertionFailedf("input expression cannot have more than one row: %v", input))
  2293  	}
  2294  
  2295  	var passthrough opt.ColSet
  2296  	projections := make(memo.ProjectionsExpr, 0, len(aggs))
  2297  	for i := range aggs {
  2298  		// If aggregate remaps the column ID, need to synthesize projection item;
  2299  		// otherwise, can just pass through.
  2300  		variable := aggs[i].Agg.Child(0).(*memo.VariableExpr)
  2301  		if variable.Col == aggs[i].Col {
  2302  			passthrough.Add(variable.Col)
  2303  		} else {
  2304  			projections = append(projections, c.e.f.ConstructProjectionsItem(variable, aggs[i].Col))
  2305  		}
  2306  	}
  2307  	c.e.mem.AddProjectToGroup(&memo.ProjectExpr{
  2308  		Input:       input,
  2309  		Projections: projections,
  2310  		Passthrough: passthrough,
  2311  	}, grp)
  2312  }
  2313  
  2314  // GenerateStreamingGroupBy generates variants of a GroupBy or DistinctOn
  2315  // expression with more specific orderings on the grouping columns, using the
  2316  // interesting orderings property. See the GenerateStreamingGroupBy rule.
  2317  func (c *CustomFuncs) GenerateStreamingGroupBy(
  2318  	grp memo.RelExpr,
  2319  	op opt.Operator,
  2320  	input memo.RelExpr,
  2321  	aggs memo.AggregationsExpr,
  2322  	private *memo.GroupingPrivate,
  2323  ) {
  2324  	orders := DeriveInterestingOrderings(input)
  2325  	intraOrd := private.Ordering
  2326  	for _, o := range orders {
  2327  		// We are looking for a prefix of o that satisfies the intra-group ordering
  2328  		// if we ignore grouping columns.
  2329  		oIdx, intraIdx := 0, 0
  2330  		for ; oIdx < len(o); oIdx++ {
  2331  			oCol := o[oIdx].ID()
  2332  			if private.GroupingCols.Contains(oCol) || intraOrd.Optional.Contains(oCol) {
  2333  				// Grouping or optional column.
  2334  				continue
  2335  			}
  2336  
  2337  			if intraIdx < len(intraOrd.Columns) &&
  2338  				intraOrd.Columns[intraIdx].Group.Contains(oCol) &&
  2339  				intraOrd.Columns[intraIdx].Descending == o[oIdx].Descending() {
  2340  				// Column matches the one in the ordering.
  2341  				intraIdx++
  2342  				continue
  2343  			}
  2344  			break
  2345  		}
  2346  		if oIdx == 0 || intraIdx < len(intraOrd.Columns) {
  2347  			// No match.
  2348  			continue
  2349  		}
  2350  		o = o[:oIdx]
  2351  
  2352  		var newOrd physical.OrderingChoice
  2353  		newOrd.FromOrderingWithOptCols(o, opt.ColSet{})
  2354  
  2355  		// Simplify the ordering according to the input's FDs. Note that this is not
  2356  		// necessary for correctness because buildChildPhysicalProps would do it
  2357  		// anyway, but doing it here once can make things more efficient (and we may
  2358  		// generate fewer expressions if some of these orderings turn out to be
  2359  		// equivalent).
  2360  		newOrd.Simplify(&input.Relational().FuncDeps)
  2361  
  2362  		newPrivate := *private
  2363  		newPrivate.Ordering = newOrd
  2364  
  2365  		switch op {
  2366  		case opt.GroupByOp:
  2367  			newExpr := memo.GroupByExpr{
  2368  				Input:           input,
  2369  				Aggregations:    aggs,
  2370  				GroupingPrivate: newPrivate,
  2371  			}
  2372  			c.e.mem.AddGroupByToGroup(&newExpr, grp)
  2373  
  2374  		case opt.DistinctOnOp:
  2375  			newExpr := memo.DistinctOnExpr{
  2376  				Input:           input,
  2377  				Aggregations:    aggs,
  2378  				GroupingPrivate: newPrivate,
  2379  			}
  2380  			c.e.mem.AddDistinctOnToGroup(&newExpr, grp)
  2381  
  2382  		case opt.EnsureDistinctOnOp:
  2383  			newExpr := memo.EnsureDistinctOnExpr{
  2384  				Input:           input,
  2385  				Aggregations:    aggs,
  2386  				GroupingPrivate: newPrivate,
  2387  			}
  2388  			c.e.mem.AddEnsureDistinctOnToGroup(&newExpr, grp)
  2389  
  2390  		case opt.UpsertDistinctOnOp:
  2391  			newExpr := memo.UpsertDistinctOnExpr{
  2392  				Input:           input,
  2393  				Aggregations:    aggs,
  2394  				GroupingPrivate: newPrivate,
  2395  			}
  2396  			c.e.mem.AddUpsertDistinctOnToGroup(&newExpr, grp)
  2397  
  2398  		case opt.EnsureUpsertDistinctOnOp:
  2399  			newExpr := memo.EnsureUpsertDistinctOnExpr{
  2400  				Input:           input,
  2401  				Aggregations:    aggs,
  2402  				GroupingPrivate: newPrivate,
  2403  			}
  2404  			c.e.mem.AddEnsureUpsertDistinctOnToGroup(&newExpr, grp)
  2405  		}
  2406  	}
  2407  }
  2408  
  2409  // OtherAggsAreConst returns true if all items in the given aggregate list
  2410  // contain ConstAgg functions, except for the "except" item. The ConstAgg
  2411  // functions will always return the same value, as long as there is at least
  2412  // one input row.
  2413  func (c *CustomFuncs) OtherAggsAreConst(
  2414  	aggs memo.AggregationsExpr, except *memo.AggregationsItem,
  2415  ) bool {
  2416  	for i := range aggs {
  2417  		agg := &aggs[i]
  2418  		if agg == except {
  2419  			continue
  2420  		}
  2421  
  2422  		switch agg.Agg.Op() {
  2423  		case opt.ConstAggOp:
  2424  			// Ensure that argument is a VariableOp.
  2425  			if agg.Agg.Child(0).Op() != opt.VariableOp {
  2426  				return false
  2427  			}
  2428  
  2429  		default:
  2430  			return false
  2431  		}
  2432  	}
  2433  	return true
  2434  }
  2435  
  2436  // MakeOrderingChoiceFromColumn constructs a new OrderingChoice with
  2437  // one element in the sequence: the columnID in the order defined by
  2438  // (MIN/MAX) operator. This function was originally created to be used
  2439  // with the Replace(Min|Max)WithLimit exploration rules.
  2440  //
  2441  // WARNING: The MinOp case can return a NULL value if the column allows it. This
  2442  // is because NULL values sort first in CRDB.
  2443  func (c *CustomFuncs) MakeOrderingChoiceFromColumn(
  2444  	op opt.Operator, col opt.ColumnID,
  2445  ) physical.OrderingChoice {
  2446  	oc := physical.OrderingChoice{}
  2447  	switch op {
  2448  	case opt.MinOp:
  2449  		oc.AppendCol(col, false /* descending */)
  2450  	case opt.MaxOp:
  2451  		oc.AppendCol(col, true /* descending */)
  2452  	}
  2453  	return oc
  2454  }
  2455  
  2456  // ExprPair stores a left and right ScalarExpr. ExprPairForSplitDisjunction
  2457  // returns ExprPair, which can be deconstructed later, to avoid extra
  2458  // computation in determining the left and right expression groups.
  2459  type ExprPair struct {
  2460  	left          opt.ScalarExpr
  2461  	right         opt.ScalarExpr
  2462  	itemToReplace *memo.FiltersItem
  2463  }
  2464  
  2465  // ExprPairLeft returns the left ScalarExpr in an ExprPair.
  2466  func (c *CustomFuncs) ExprPairLeft(ep ExprPair) opt.ScalarExpr {
  2467  	return ep.left
  2468  }
  2469  
  2470  // ExprPairRight returns the right ScalarExpr in an ExprPair.
  2471  func (c *CustomFuncs) ExprPairRight(ep ExprPair) opt.ScalarExpr {
  2472  	return ep.right
  2473  }
  2474  
  2475  // ExprPairFiltersItemToReplace returns the original FiltersItem that the
  2476  // ExprPair was generated from. This FiltersItem should be replaced by
  2477  // ExprPairLeft and ExprPairRight in the newly generated filters in
  2478  // SplitDisjunction(AddKey).
  2479  func (c *CustomFuncs) ExprPairFiltersItemToReplace(ep ExprPair) *memo.FiltersItem {
  2480  	return ep.itemToReplace
  2481  }
  2482  
  2483  // ExprPairSucceeded returns true if the ExprPair is not nil.
  2484  func (c *CustomFuncs) ExprPairSucceeded(ep ExprPair) bool {
  2485  	return ep != ExprPair{}
  2486  }
  2487  
  2488  // ExprPairForSplitDisjunction finds the first "interesting" ExprPair in the
  2489  // filters and returns it. If an "interesting" ExprPair is not found, an empty
  2490  // ExprPair is returned.
  2491  //
  2492  // For details on what makes an ExprPair "interesting", see
  2493  // buildExprPairForSplitDisjunction.
  2494  func (c *CustomFuncs) ExprPairForSplitDisjunction(
  2495  	sp *memo.ScanPrivate, filters memo.FiltersExpr,
  2496  ) ExprPair {
  2497  	for i := range filters {
  2498  		if filters[i].Condition.Op() == opt.OrOp {
  2499  			ep := c.buildExprPairForSplitDisjunction(sp, &filters[i])
  2500  			if (ep != ExprPair{}) {
  2501  				return ep
  2502  			}
  2503  		}
  2504  	}
  2505  	return ExprPair{}
  2506  }
  2507  
  2508  // buildExprPairForSplitDisjunction groups disjuction sub-expressions into an
  2509  // "interesting" ExprPair.
  2510  //
  2511  // An "interesting" ExprPair is one where:
  2512  //
  2513  //   1. The column sets of both expressions in the pair are not
  2514  //      equal.
  2515  //   2. Two index scans can potentially be constrained by both expressions in
  2516  //      the pair.
  2517  //
  2518  // Consider the expression:
  2519  //
  2520  //   u = 1 OR v = 2
  2521  //
  2522  // If an index exists on u and another on v, an "interesting" ExprPair exists,
  2523  // ("u = 1", "v = 1"). If both indexes do not exist, there is no "interesting"
  2524  // ExprPair possible.
  2525  //
  2526  // Now consider the expression:
  2527  //
  2528  //   u = 1 OR u = 2
  2529  //
  2530  // There is no possible "interesting" ExprPair here because the left and right
  2531  // sides of the disjunction share the same columns.
  2532  //
  2533  // buildExprPairForSplitDisjunction groups all sub-expressions adjacent to the
  2534  // input's top-level OrExpr into left and right expression groups. These two
  2535  // groups form the new filter expressions on the left and right side of the
  2536  // generated UnionAll in SplitDisjunction(AddKey).
  2537  //
  2538  // All sub-expressions with the same columns as the left-most sub-expression
  2539  // are grouped in the left group. All other sub-expressions are grouped in the
  2540  // right group.
  2541  //
  2542  // buildExprPairForSplitDisjunction returns an empty ExprPair if all
  2543  // sub-expressions have the same columns. It also returns an empty ExprPair if
  2544  // either expression in the pair found is not likely to constrain an index
  2545  // scan. See canMaybeConstrainIndexWithCols for details on how this is
  2546  // determined.
  2547  func (c *CustomFuncs) buildExprPairForSplitDisjunction(
  2548  	sp *memo.ScanPrivate, filter *memo.FiltersItem,
  2549  ) ExprPair {
  2550  	var leftExprs memo.ScalarListExpr
  2551  	var rightExprs memo.ScalarListExpr
  2552  	var leftColSet opt.ColSet
  2553  	var rightColSet opt.ColSet
  2554  
  2555  	// Traverse all adjacent OrExpr.
  2556  	var collect func(opt.ScalarExpr)
  2557  	collect = func(expr opt.ScalarExpr) {
  2558  		switch t := expr.(type) {
  2559  		case *memo.OrExpr:
  2560  			collect(t.Left)
  2561  			collect(t.Right)
  2562  			return
  2563  		}
  2564  
  2565  		cols := c.OuterCols(expr)
  2566  
  2567  		// Set the left-most non-Or expression as the left ColSet to match (or
  2568  		// not match) on.
  2569  		if leftColSet.Empty() {
  2570  			leftColSet = cols
  2571  		}
  2572  
  2573  		// If the current expression ColSet matches leftColSet, add the expr to
  2574  		// the left group. Otherwise, add it to the right group.
  2575  		if leftColSet.Equals(cols) {
  2576  			leftExprs = append(leftExprs, expr)
  2577  		} else {
  2578  			rightColSet.UnionWith(cols)
  2579  			rightExprs = append(rightExprs, expr)
  2580  		}
  2581  	}
  2582  	collect(filter.Condition)
  2583  
  2584  	// Return an empty pair if either of the groups is empty or if either the
  2585  	// left or right groups are unlikely to constrain an index scan.
  2586  	if len(leftExprs) == 0 ||
  2587  		len(rightExprs) == 0 ||
  2588  		!c.canMaybeConstrainIndexWithCols(sp, leftColSet) ||
  2589  		!c.canMaybeConstrainIndexWithCols(sp, rightColSet) {
  2590  		return ExprPair{}
  2591  	}
  2592  
  2593  	return ExprPair{
  2594  		left:          c.constructOr(leftExprs),
  2595  		right:         c.constructOr(rightExprs),
  2596  		itemToReplace: filter,
  2597  	}
  2598  }
  2599  
  2600  // canMaybeConstrainIndexWithCols returns true if any indexes on the
  2601  // ScanPrivate's table could be constrained by cols. It is a fast check for
  2602  // SplitDisjunction to avoid matching a large number of queries that won't
  2603  // obviously be improved by the rule.
  2604  //
  2605  // canMaybeConstrainIndexWithCols checks for an intersection between the input
  2606  // columns and an index's columns. An intersection between column sets implies
  2607  // that cols could constrain a scan on that index. For example, the columns "a"
  2608  // would constrain a scan on an index over columns "a, b", because the "a" is a
  2609  // subset of the index columns. Likewise, the columns "a" and "b" would
  2610  // constrain a scan on an index over column "a", because "a" and "b" are a
  2611  // superset of the index columns.
  2612  //
  2613  // Notice that this function can return both false positives and false
  2614  // negatives. As an example of a false negative, consider the following table
  2615  // and query.
  2616  //
  2617  //   CREATE TABLE t (
  2618  //     k PRIMARY KEY,
  2619  //     a INT,
  2620  //     hash INT AS (a % 4) STORED,
  2621  //     INDEX hash (hash)
  2622  //   )
  2623  //
  2624  //   SELECT * FROM t WHERE a = 5
  2625  //
  2626  // The expression "a = 5" can constrain a scan over the hash index: The columns
  2627  // "hash" must be a constant value of 1 because it is dependent on column "a"
  2628  // with a constant value of 5. However, canMaybeConstrainIndexWithCols will
  2629  // return false in this case because "a" does not intersect with the index
  2630  // column, "hash".
  2631  func (c *CustomFuncs) canMaybeConstrainIndexWithCols(sp *memo.ScanPrivate, cols opt.ColSet) bool {
  2632  	md := c.e.mem.Metadata()
  2633  	tabMeta := md.TableMeta(sp.Table)
  2634  
  2635  	var iter scanIndexIter
  2636  	iter.init(c.e.mem, sp, allIndexes)
  2637  	for iter.next() {
  2638  		// Iterate through all indexes of the table and return true if cols
  2639  		// intersect with the index's columns.
  2640  		indexColumns := tabMeta.IndexKeyColumns(iter.indexOrdinal)
  2641  		if cols.Intersects(indexColumns) {
  2642  			return true
  2643  		}
  2644  	}
  2645  
  2646  	return false
  2647  }
  2648  
  2649  // DuplicateScanPrivate constructs a new ScanPrivate that is identical to the
  2650  // input, but has new table and column IDs.
  2651  //
  2652  // DuplicateScanPrivate can only be called on canonical ScanPrivates because not
  2653  // all scan properties are copied to the new ScanPrivate, e.g. constraints.
  2654  func (c *CustomFuncs) DuplicateScanPrivate(sp *memo.ScanPrivate) *memo.ScanPrivate {
  2655  	if !c.IsCanonicalScan(sp) {
  2656  		panic(errors.AssertionFailedf("input ScanPrivate must be canonical: %v", sp))
  2657  	}
  2658  
  2659  	md := c.e.mem.Metadata()
  2660  	tabMeta := md.TableMeta(sp.Table)
  2661  	dupTabID := md.AddTable(tabMeta.Table, &tabMeta.Alias)
  2662  
  2663  	var dupTabColIDs opt.ColSet
  2664  	cols := sp.Cols
  2665  	for i, ok := cols.Next(0); ok; i, ok = cols.Next(i + 1) {
  2666  		ord := tabMeta.MetaID.ColumnOrdinal(i)
  2667  		dupColID := dupTabID.ColumnID(ord)
  2668  		dupTabColIDs.Add(dupColID)
  2669  	}
  2670  
  2671  	return &memo.ScanPrivate{
  2672  		Table:   dupTabID,
  2673  		Cols:    dupTabColIDs,
  2674  		Flags:   sp.Flags,
  2675  		Locking: sp.Locking,
  2676  	}
  2677  }
  2678  
  2679  // MapScanFilterCols returns a new FiltersExpr with all the src column IDs in
  2680  // the input expression replaced with column IDs in dst.
  2681  //
  2682  // NOTE: Every ColumnID in src must map to the a ColumnID in dst with the same
  2683  // relative position in the ColSets. For example, if src and dst are (1, 5, 6)
  2684  // and (7, 12, 15), then the following mapping would be applied:
  2685  //
  2686  //   1 => 7
  2687  //   5 => 12
  2688  //   6 => 15
  2689  func (c *CustomFuncs) MapScanFilterCols(
  2690  	filters memo.FiltersExpr, src *memo.ScanPrivate, dst *memo.ScanPrivate,
  2691  ) memo.FiltersExpr {
  2692  	if src.Cols.Len() != dst.Cols.Len() {
  2693  		panic(errors.AssertionFailedf(
  2694  			"src and dst must have the same number of columns, src.Cols: %v, dst.Cols: %v",
  2695  			src.Cols,
  2696  			dst.Cols,
  2697  		))
  2698  	}
  2699  
  2700  	// Map each column in src to a column in dst based on the relative position
  2701  	// of both the src and dst ColumnIDs in the ColSet.
  2702  	var colMap util.FastIntMap
  2703  	dstCol, _ := dst.Cols.Next(0)
  2704  	for srcCol, ok := src.Cols.Next(0); ok; srcCol, ok = src.Cols.Next(srcCol + 1) {
  2705  		colMap.Set(int(srcCol), int(dstCol))
  2706  		dstCol, _ = dst.Cols.Next(dstCol + 1)
  2707  	}
  2708  
  2709  	// Map the columns of each filter in the FiltersExpr.
  2710  	newFilters := make([]memo.FiltersItem, len(filters))
  2711  	for i := range filters {
  2712  		expr := c.MapFiltersItemCols(&filters[i], colMap)
  2713  		newFilters[i] = c.e.f.ConstructFiltersItem(expr)
  2714  	}
  2715  
  2716  	return newFilters
  2717  }
  2718  
  2719  // MakeSetPrivateForSplitDisjunction constructs a new SetPrivate with column sets
  2720  // from the left and right ScanPrivate. We use the same ColList for the
  2721  // LeftCols and OutCols of the SetPrivate because we've used the original
  2722  // ScanPrivate column IDs for the left ScanPrivate and those are safe to use as
  2723  // output column IDs of the Union expression.
  2724  func (c *CustomFuncs) MakeSetPrivateForSplitDisjunction(
  2725  	left, right *memo.ScanPrivate,
  2726  ) *memo.SetPrivate {
  2727  	leftAndOutCols := opt.ColSetToList(left.Cols)
  2728  	return &memo.SetPrivate{
  2729  		LeftCols:  leftAndOutCols,
  2730  		RightCols: opt.ColSetToList(right.Cols),
  2731  		OutCols:   leftAndOutCols,
  2732  	}
  2733  }
  2734  
  2735  // AddPrimaryKeyColsToScanPrivate creates a new ScanPrivate that is the same as
  2736  // the input ScanPrivate, but has primary keys added to the ColSet.
  2737  func (c *CustomFuncs) AddPrimaryKeyColsToScanPrivate(sp *memo.ScanPrivate) *memo.ScanPrivate {
  2738  	keyCols := c.PrimaryKeyCols(sp.Table)
  2739  	return &memo.ScanPrivate{
  2740  		Table:   sp.Table,
  2741  		Cols:    sp.Cols.Union(keyCols),
  2742  		Flags:   sp.Flags,
  2743  		Locking: sp.Locking,
  2744  	}
  2745  }
  2746  
  2747  // indexIterType is an option passed to scanIndexIter.init() to specify index types
  2748  // to include during iteration.
  2749  type indexIterType int
  2750  
  2751  const (
  2752  	// allIndexes sepcifies that no indexes will be skipped during iteration.
  2753  	allIndexes indexIterType = iota
  2754  
  2755  	// onlyStandardIndexes specifies iteration over all standard indexes,
  2756  	// skipping inverted indexes.
  2757  	onlyStandardIndexes
  2758  
  2759  	// onlyInvertedIndexes specifies iteration over all inverted indexes,
  2760  	// skipping standard indexes.
  2761  	onlyInvertedIndexes
  2762  )
  2763  
  2764  // scanIndexIter is a helper struct that supports iteration over the indexes
  2765  // of a Scan operator table. For example:
  2766  //
  2767  //   var iter scanIndexIter
  2768  //   iter.init(mem, scanOpDef, onlyStandardIndexes)
  2769  //   for iter.next() {
  2770  //     doSomething(iter.indexOrdinal)
  2771  //   }
  2772  //
  2773  type scanIndexIter struct {
  2774  	mem          *memo.Memo
  2775  	scanPrivate  *memo.ScanPrivate
  2776  	tab          cat.Table
  2777  	indexOrdinal cat.IndexOrdinal
  2778  	index        cat.Index
  2779  	indexType    indexIterType
  2780  	cols         opt.ColSet
  2781  }
  2782  
  2783  func (it *scanIndexIter) init(mem *memo.Memo, scanPrivate *memo.ScanPrivate, t indexIterType) {
  2784  	it.mem = mem
  2785  	it.scanPrivate = scanPrivate
  2786  	it.tab = mem.Metadata().Table(scanPrivate.Table)
  2787  	it.indexOrdinal = -1
  2788  	it.index = nil
  2789  	it.indexType = t
  2790  }
  2791  
  2792  // next advances iteration to the next index of the Scan operator's table. This
  2793  // is the primary index if it's the first time next is called, or a secondary
  2794  // index thereafter. When there are no more indexes to enumerate, next returns
  2795  // false. The current index is accessible via the iterator's "index" field.
  2796  //
  2797  // The indexType determines which indexes to skip when iterating, if any.
  2798  //
  2799  // If the ForceIndex flag is set, then all indexes except the forced index are
  2800  // skipped.
  2801  func (it *scanIndexIter) next() bool {
  2802  	for {
  2803  		it.indexOrdinal++
  2804  
  2805  		if it.indexOrdinal >= it.tab.IndexCount() {
  2806  			it.index = nil
  2807  			return false
  2808  		}
  2809  
  2810  		it.index = it.tab.Index(it.indexOrdinal)
  2811  
  2812  		// Skip over inverted indexes if indexType is onlyStandardIndexes.
  2813  		if it.indexType == onlyStandardIndexes && it.index.IsInverted() {
  2814  			continue
  2815  		}
  2816  
  2817  		// Skip over standard indexes if indexType is onlyInvertedIndexes.
  2818  		if it.indexType == onlyInvertedIndexes && !it.index.IsInverted() {
  2819  			continue
  2820  		}
  2821  
  2822  		if it.scanPrivate.Flags.ForceIndex && it.scanPrivate.Flags.Index != it.indexOrdinal {
  2823  			// If we are forcing a specific index, ignore the others.
  2824  			continue
  2825  		}
  2826  
  2827  		it.cols = opt.ColSet{}
  2828  		return true
  2829  	}
  2830  }
  2831  
  2832  // indexCols returns the set of columns contained in the current index.
  2833  func (it *scanIndexIter) indexCols() opt.ColSet {
  2834  	if it.cols.Empty() {
  2835  		it.cols = it.mem.Metadata().TableMeta(it.scanPrivate.Table).IndexColumns(it.indexOrdinal)
  2836  	}
  2837  	return it.cols
  2838  }
  2839  
  2840  // isCovering returns true if the current index contains all columns projected
  2841  // by the Scan operator.
  2842  func (it *scanIndexIter) isCovering() bool {
  2843  	return it.scanPrivate.Cols.SubsetOf(it.indexCols())
  2844  }