github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/xform/index_scan_builder.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  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    15  	"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
    16  	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt/norm"
    18  	"github.com/cockroachdb/errors"
    19  )
    20  
    21  // indexScanBuilder composes a constrained, limited scan over a table index.
    22  // Any filters are created as close to the scan as possible, and index joins can
    23  // be used to scan a non-covering index. For example, in order to construct:
    24  //
    25  //   (IndexJoin
    26  //     (Select (Scan $scanPrivate) $filters)
    27  //     $indexJoinPrivate
    28  //   )
    29  //
    30  // make the following calls:
    31  //
    32  //   var sb indexScanBuilder
    33  //   sb.init(c, tabID)
    34  //   sb.setScan(scanPrivate)
    35  //   sb.addSelect(filters)
    36  //   sb.addIndexJoin(cols)
    37  //   expr := sb.build()
    38  //
    39  type indexScanBuilder struct {
    40  	c                *CustomFuncs
    41  	f                *norm.Factory
    42  	mem              *memo.Memo
    43  	tabID            opt.TableID
    44  	pkCols           opt.ColSet
    45  	scanPrivate      memo.ScanPrivate
    46  	innerFilters     memo.FiltersExpr
    47  	outerFilters     memo.FiltersExpr
    48  	indexJoinPrivate memo.IndexJoinPrivate
    49  }
    50  
    51  func (b *indexScanBuilder) init(c *CustomFuncs, tabID opt.TableID) {
    52  	b.c = c
    53  	b.f = c.e.f
    54  	b.mem = c.e.mem
    55  	b.tabID = tabID
    56  }
    57  
    58  // primaryKeyCols returns the columns from the scanned table's primary index.
    59  func (b *indexScanBuilder) primaryKeyCols() opt.ColSet {
    60  	// Ensure that pkCols set is initialized with the primary index columns.
    61  	if b.pkCols.Empty() {
    62  		primaryIndex := b.c.e.mem.Metadata().Table(b.tabID).Index(cat.PrimaryIndex)
    63  		for i, cnt := 0, primaryIndex.KeyColumnCount(); i < cnt; i++ {
    64  			b.pkCols.Add(b.tabID.ColumnID(primaryIndex.Column(i).Ordinal))
    65  		}
    66  	}
    67  	return b.pkCols
    68  }
    69  
    70  // setScan constructs a standalone Scan expression. As a side effect, it clears
    71  // any expressions added during previous invocations of the builder. setScan
    72  // makes a copy of scanPrivate so that it doesn't escape.
    73  func (b *indexScanBuilder) setScan(scanPrivate *memo.ScanPrivate) {
    74  	b.scanPrivate = *scanPrivate
    75  	b.innerFilters = nil
    76  	b.outerFilters = nil
    77  	b.indexJoinPrivate = memo.IndexJoinPrivate{}
    78  }
    79  
    80  // addSelect wraps the input expression with a Select expression having the
    81  // given filter.
    82  func (b *indexScanBuilder) addSelect(filters memo.FiltersExpr) {
    83  	if len(filters) != 0 {
    84  		if b.indexJoinPrivate.Table == 0 {
    85  			if b.innerFilters != nil {
    86  				panic(errors.AssertionFailedf("cannot call addSelect methods twice before index join is added"))
    87  			}
    88  			b.innerFilters = filters
    89  		} else {
    90  			if b.outerFilters != nil {
    91  				panic(errors.AssertionFailedf("cannot call addSelect methods twice after index join is added"))
    92  			}
    93  			b.outerFilters = filters
    94  		}
    95  	}
    96  }
    97  
    98  // addSelectAfterSplit first splits the given filter into two parts: a filter
    99  // that only involves columns in the given set, and a remaining filter that
   100  // includes everything else. The filter that is bound by the columns becomes a
   101  // Select expression that wraps the input expression, and the remaining filter
   102  // is returned (or 0 if there is no remaining filter).
   103  func (b *indexScanBuilder) addSelectAfterSplit(
   104  	filters memo.FiltersExpr, cols opt.ColSet,
   105  ) (remainingFilters memo.FiltersExpr) {
   106  	if len(filters) == 0 {
   107  		return nil
   108  	}
   109  
   110  	if b.c.FiltersBoundBy(filters, cols) {
   111  		// Filter is fully bound by the cols, so add entire filter.
   112  		b.addSelect(filters)
   113  		return nil
   114  	}
   115  
   116  	// Try to split filter.
   117  	boundConditions := b.c.ExtractBoundConditions(filters, cols)
   118  	if len(boundConditions) == 0 {
   119  		// None of the filter conjuncts can be bound by the cols, so no expression
   120  		// can be added.
   121  		return filters
   122  	}
   123  
   124  	// Add conditions which are fully bound by the cols and return the rest.
   125  	b.addSelect(boundConditions)
   126  	return b.c.ExtractUnboundConditions(filters, cols)
   127  }
   128  
   129  // addIndexJoin wraps the input expression with an IndexJoin expression that
   130  // produces the given set of columns by lookup in the primary index.
   131  func (b *indexScanBuilder) addIndexJoin(cols opt.ColSet) {
   132  	if b.indexJoinPrivate.Table != 0 {
   133  		panic(errors.AssertionFailedf("cannot call addIndexJoin twice"))
   134  	}
   135  	if b.outerFilters != nil {
   136  		panic(errors.AssertionFailedf("cannot add index join after an outer filter has been added"))
   137  	}
   138  	b.indexJoinPrivate = memo.IndexJoinPrivate{
   139  		Table: b.tabID,
   140  		Cols:  cols,
   141  	}
   142  }
   143  
   144  // build constructs the final memo expression by composing together the various
   145  // expressions that were specified by previous calls to various add methods.
   146  func (b *indexScanBuilder) build(grp memo.RelExpr) {
   147  	// 1. Only scan.
   148  	if len(b.innerFilters) == 0 && b.indexJoinPrivate.Table == 0 {
   149  		b.mem.AddScanToGroup(&memo.ScanExpr{ScanPrivate: b.scanPrivate}, grp)
   150  		return
   151  	}
   152  
   153  	// 2. Wrap scan in inner filter if it was added.
   154  	input := b.f.ConstructScan(&b.scanPrivate)
   155  	if len(b.innerFilters) != 0 {
   156  		if b.indexJoinPrivate.Table == 0 {
   157  			b.mem.AddSelectToGroup(&memo.SelectExpr{Input: input, Filters: b.innerFilters}, grp)
   158  			return
   159  		}
   160  
   161  		input = b.f.ConstructSelect(input, b.innerFilters)
   162  	}
   163  
   164  	// 3. Wrap input in index join if it was added.
   165  	if b.indexJoinPrivate.Table != 0 {
   166  		if len(b.outerFilters) == 0 {
   167  			indexJoin := &memo.IndexJoinExpr{Input: input, IndexJoinPrivate: b.indexJoinPrivate}
   168  			b.mem.AddIndexJoinToGroup(indexJoin, grp)
   169  			return
   170  		}
   171  
   172  		input = b.f.ConstructIndexJoin(input, &b.indexJoinPrivate)
   173  	}
   174  
   175  	// 4. Wrap input in outer filter (which must exist at this point).
   176  	if len(b.outerFilters) == 0 {
   177  		// indexJoinDef == 0: outerFilters == 0 handled by #1 and #2 above.
   178  		// indexJoinDef != 0: outerFilters == 0 handled by #3 above.
   179  		panic(errors.AssertionFailedf("outer filter cannot be 0 at this point"))
   180  	}
   181  	b.mem.AddSelectToGroup(&memo.SelectExpr{Input: input, Filters: b.outerFilters}, grp)
   182  }