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 }