github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/ordering/scan.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 ordering
    12  
    13  import (
    14  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    15  	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
    16  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    18  	"github.com/cockroachdb/errors"
    19  )
    20  
    21  func scanCanProvideOrdering(expr memo.RelExpr, required *physical.OrderingChoice) bool {
    22  	ok, _ := ScanPrivateCanProvide(
    23  		expr.Memo().Metadata(),
    24  		&expr.(*memo.ScanExpr).ScanPrivate,
    25  		required,
    26  	)
    27  	return ok
    28  }
    29  
    30  // ScanIsReverse returns true if the scan must be performed in reverse order
    31  // in order to satisfy the required ordering. If either direction is ok (e.g. no
    32  // required ordering), reutrns false. The scan must be able to satisfy the
    33  // required ordering, according to ScanCanProvideOrdering.
    34  func ScanIsReverse(scan *memo.ScanExpr, required *physical.OrderingChoice) bool {
    35  	ok, reverse := ScanPrivateCanProvide(
    36  		scan.Memo().Metadata(),
    37  		&scan.ScanPrivate,
    38  		required,
    39  	)
    40  	if !ok {
    41  		panic(errors.AssertionFailedf("scan can't provide required ordering"))
    42  	}
    43  	return reverse
    44  }
    45  
    46  // ScanPrivateCanProvide returns true if the scan operator returns rows
    47  // that satisfy the given required ordering; it also returns whether the scan
    48  // needs to be in reverse order to match the required ordering.
    49  func ScanPrivateCanProvide(
    50  	md *opt.Metadata, s *memo.ScanPrivate, required *physical.OrderingChoice,
    51  ) (ok bool, reverse bool) {
    52  	// Scan naturally orders according to scanned index's key columns. A scan can
    53  	// be executed either as a forward or as a reverse scan (unless it has a row
    54  	// limit, in which case the direction is fixed).
    55  	//
    56  	// The code below follows the structure of OrderingChoice.Implies. We go
    57  	// through the columns and determine if the ordering matches with either scan
    58  	// direction.
    59  
    60  	// We start off as accepting either a forward or a reverse scan. Until then,
    61  	// the reverse variable is unset. Once the direction is known, reverseSet is
    62  	// true and reverse indicates whether we need to do a reverse scan.
    63  	const (
    64  		either = 0
    65  		fwd    = 1
    66  		rev    = 2
    67  	)
    68  	direction := either
    69  	if s.HardLimit.IsSet() {
    70  		// When we have a limit, the limit forces a certain scan direction (because
    71  		// it affects the results, not just their ordering).
    72  		direction = fwd
    73  		if s.HardLimit.Reverse() {
    74  			direction = rev
    75  		}
    76  	} else if s.Flags.Direction != 0 {
    77  		direction = fwd
    78  		if s.Flags.Direction == tree.Descending {
    79  			direction = rev
    80  		}
    81  	}
    82  	index := md.Table(s.Table).Index(s.Index)
    83  	for left, right := 0, 0; right < len(required.Columns); {
    84  		if left >= index.KeyColumnCount() {
    85  			return false, false
    86  		}
    87  		indexCol := index.Column(left)
    88  		indexColID := s.Table.ColumnID(indexCol.Ordinal)
    89  		if required.Optional.Contains(indexColID) {
    90  			left++
    91  			continue
    92  		}
    93  		reqCol := &required.Columns[right]
    94  		if !reqCol.Group.Contains(indexColID) {
    95  			return false, false
    96  		}
    97  		// The directions of the index column and the required column impose either
    98  		// a forward or a reverse scan.
    99  		required := fwd
   100  		if indexCol.Descending != reqCol.Descending {
   101  			required = rev
   102  		}
   103  		if direction == either {
   104  			direction = required
   105  		} else if direction != required {
   106  			// We already determined the direction, and according to it, this column
   107  			// has the wrong direction.
   108  			return false, false
   109  		}
   110  		left, right = left+1, right+1
   111  	}
   112  	// If direction is either, we prefer forward scan.
   113  	return true, direction == rev
   114  }
   115  
   116  func scanBuildProvided(expr memo.RelExpr, required *physical.OrderingChoice) opt.Ordering {
   117  	scan := expr.(*memo.ScanExpr)
   118  	md := scan.Memo().Metadata()
   119  	index := md.Table(scan.Table).Index(scan.Index)
   120  	fds := &scan.Relational().FuncDeps
   121  
   122  	// We need to know the direction of the scan.
   123  	reverse := ScanIsReverse(scan, required)
   124  
   125  	// We generate the longest ordering that this scan can provide, then we trim
   126  	// it. This is the longest prefix of index columns that are output by the scan
   127  	// (ignoring constant columns, in the case of constrained scans).
   128  	constCols := fds.ComputeClosure(opt.ColSet{})
   129  	numCols := index.KeyColumnCount()
   130  	provided := make(opt.Ordering, 0, numCols)
   131  	for i := 0; i < numCols; i++ {
   132  		indexCol := index.Column(i)
   133  		colID := scan.Table.ColumnID(indexCol.Ordinal)
   134  		if !scan.Cols.Contains(colID) {
   135  			// Column not in output; we are done.
   136  			break
   137  		}
   138  		if constCols.Contains(colID) {
   139  			// Column constrained to a constant, ignore.
   140  			continue
   141  		}
   142  		direction := (indexCol.Descending != reverse) // != is bool XOR
   143  		provided = append(provided, opt.MakeOrderingColumn(colID, direction))
   144  	}
   145  
   146  	return trimProvided(provided, required, fds)
   147  }
   148  
   149  func init() {
   150  	memo.ScanIsReverseFn = func(
   151  		md *opt.Metadata, s *memo.ScanPrivate, required *physical.OrderingChoice,
   152  	) bool {
   153  		ok, reverse := ScanPrivateCanProvide(md, s, required)
   154  		if !ok {
   155  			panic(errors.AssertionFailedf("scan can't provide required ordering"))
   156  		}
   157  		return reverse
   158  	}
   159  }