github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/scan.go (about) 1 // Copyright 2015 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 sql 12 13 import ( 14 "context" 15 "sync" 16 17 "github.com/cockroachdb/cockroach/pkg/roachpb" 18 "github.com/cockroachdb/cockroach/pkg/sql/execinfra" 19 "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" 20 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 21 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 22 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 23 "github.com/cockroachdb/cockroach/pkg/sql/types" 24 "github.com/cockroachdb/cockroach/pkg/util" 25 "github.com/cockroachdb/errors" 26 ) 27 28 var scanNodePool = sync.Pool{ 29 New: func() interface{} { 30 return &scanNode{} 31 }, 32 } 33 34 // A scanNode handles scanning over the key/value pairs for a table and 35 // reconstructing them into rows. 36 type scanNode struct { 37 // This struct must be allocated on the heap and its location stay 38 // stable after construction because it implements 39 // IndexedVarContainer and the IndexedVar objects in sub-expressions 40 // will link to it by reference after checkRenderStar / analyzeExpr. 41 // Enforce this using NoCopy. 42 _ util.NoCopy 43 44 desc *sqlbase.ImmutableTableDescriptor 45 index *sqlbase.IndexDescriptor 46 47 // Set if an index was explicitly specified. 48 specifiedIndex *sqlbase.IndexDescriptor 49 // Set if the NO_INDEX_JOIN hint was given. 50 noIndexJoin bool 51 52 colCfg scanColumnsConfig 53 // The table columns, possibly including ones currently in schema changes. 54 // TODO(radu/knz): currently we always load the entire row from KV and only 55 // skip unnecessary decodes to Datum. Investigate whether performance is to 56 // be gained (e.g. for tables with wide rows) by reading only certain 57 // columns from KV using point lookups instead of a single range lookup for 58 // the entire row. 59 cols []sqlbase.ColumnDescriptor 60 // There is a 1-1 correspondence between cols and resultColumns. 61 resultColumns sqlbase.ResultColumns 62 63 // Map used to get the index for columns in cols. 64 colIdxMap map[sqlbase.ColumnID]int 65 66 spans []roachpb.Span 67 reverse bool 68 69 reqOrdering ReqOrdering 70 71 // filter that can be evaluated using only this table/index; it contains 72 // tree.IndexedVar leaves generated using filterVars. 73 filter tree.TypedExpr 74 filterVars tree.IndexedVarHelper 75 76 // if non-zero, hardLimit indicates that the scanNode only needs to provide 77 // this many rows (after applying any filter). It is a "hard" guarantee that 78 // Next will only be called this many times. 79 hardLimit int64 80 // if non-zero, softLimit is an estimation that only this many rows (after 81 // applying any filter) might be needed. It is a (potentially optimistic) 82 // "hint". If hardLimit is set (non-zero), softLimit must be unset (zero). 83 softLimit int64 84 85 disableBatchLimits bool 86 87 // Should be set to true if sqlbase.ParallelScans is true. 88 parallelScansEnabled bool 89 90 // Is this a full scan of an index? 91 isFull bool 92 93 // Indicates if this scanNode will do a physical data check. This is 94 // only true when running SCRUB commands. 95 isCheck bool 96 97 // maxResults, if greater than 0, is the maximum number of results that a 98 // scan is guaranteed to return. 99 maxResults uint64 100 101 // estimatedRowCount is the estimated number of rows that this scanNode will 102 // output. When there are no statistics to make the estimation, it will be 103 // set to zero. 104 estimatedRowCount uint64 105 106 // lockingStrength and lockingWaitPolicy represent the row-level locking 107 // mode of the Scan. 108 lockingStrength sqlbase.ScanLockingStrength 109 lockingWaitPolicy sqlbase.ScanLockingWaitPolicy 110 } 111 112 // scanColumnsConfig controls the "schema" of a scan node. The zero value is the 113 // default: all "public" columns. 114 // Note that not all columns in the schema are read and decoded; that is further 115 // controlled by scanNode.valNeededForCol. 116 type scanColumnsConfig struct { 117 // If set, only these columns are part of the scan node schema, in this order 118 // (with the caveat that the addUnwantedAsHidden flag below can add more 119 // columns). Non public columns can only be added if allowed by the visibility 120 // flag below. 121 // If not set, then all visible columns will be part of the scan node schema, 122 // as specified by the visibility flag below. The addUnwantedAsHidden flag 123 // is ignored in this case. 124 wantedColumns []tree.ColumnID 125 126 // When set, the columns that are not in the wantedColumns list are added to 127 // the list of columns as hidden columns. Only useful in conjunction with 128 // wantedColumns. 129 addUnwantedAsHidden bool 130 131 // If visibility is set to execinfra.ScanVisibilityPublicAndNotPublic, then 132 // mutation columns can be added to the list of columns. 133 visibility execinfrapb.ScanVisibility 134 } 135 136 var publicColumnsCfg = scanColumnsConfig{} 137 138 func (p *planner) Scan() *scanNode { 139 n := scanNodePool.Get().(*scanNode) 140 return n 141 } 142 143 // scanNode implements tree.IndexedVarContainer. 144 var _ tree.IndexedVarContainer = &scanNode{} 145 146 func (n *scanNode) IndexedVarEval(idx int, ctx *tree.EvalContext) (tree.Datum, error) { 147 panic("scanNode can't be run in local mode") 148 } 149 150 func (n *scanNode) IndexedVarResolvedType(idx int) *types.T { 151 return n.resultColumns[idx].Typ 152 } 153 154 func (n *scanNode) IndexedVarNodeFormatter(idx int) tree.NodeFormatter { 155 return (*tree.Name)(&n.resultColumns[idx].Name) 156 } 157 158 func (n *scanNode) startExec(params runParams) error { 159 panic("scanNode can't be run in local mode") 160 } 161 162 func (n *scanNode) Close(context.Context) { 163 *n = scanNode{} 164 scanNodePool.Put(n) 165 } 166 167 func (n *scanNode) Next(params runParams) (bool, error) { 168 panic("scanNode can't be run in local mode") 169 } 170 171 func (n *scanNode) Values() tree.Datums { 172 panic("scanNode can't be run in local mode") 173 } 174 175 // disableBatchLimit disables the kvfetcher batch limits. Used for index-join, 176 // where we scan batches of unordered spans. 177 func (n *scanNode) disableBatchLimit() { 178 n.disableBatchLimits = true 179 n.hardLimit = 0 180 n.softLimit = 0 181 } 182 183 // canParallelize returns true if this scanNode can be parallelized at the 184 // distSender level safely. 185 func (n *scanNode) canParallelize() bool { 186 // We choose only to parallelize if we are certain that no more than 187 // ParallelScanResultThreshold results will be returned, to prevent potential 188 // memory blowup. 189 // We can't parallelize if we have a non-zero limit hint, since DistSender 190 // is limited to running limited batches serially. 191 return n.maxResults != 0 && 192 n.maxResults < execinfra.ParallelScanResultThreshold && 193 n.limitHint() == 0 && 194 n.parallelScansEnabled 195 } 196 197 func (n *scanNode) limitHint() int64 { 198 var limitHint int64 199 if n.hardLimit != 0 { 200 limitHint = n.hardLimit 201 if !isFilterTrue(n.filter) { 202 // The limit is hard, but it applies after the filter; read a multiple of 203 // the limit to avoid needing a second batch. The multiple should be an 204 // estimate for the selectivity of the filter, but we have no way of 205 // calculating that right now. 206 limitHint *= 2 207 } 208 } else { 209 // Like above, read a multiple of the limit when the limit is "soft". 210 // TODO(yuzefovich): shouldn't soft limit already account for the 211 // selectivity of any filter and whatnot? 212 limitHint = n.softLimit * 2 213 } 214 return limitHint 215 } 216 217 // Initializes a scanNode with a table descriptor. 218 func (n *scanNode) initTable( 219 ctx context.Context, 220 p *planner, 221 desc *sqlbase.ImmutableTableDescriptor, 222 indexFlags *tree.IndexFlags, 223 colCfg scanColumnsConfig, 224 ) error { 225 n.desc = desc 226 227 if !p.skipSelectPrivilegeChecks { 228 if err := p.CheckPrivilege(ctx, n.desc, privilege.SELECT); err != nil { 229 return err 230 } 231 } 232 233 if indexFlags != nil { 234 if err := n.lookupSpecifiedIndex(indexFlags); err != nil { 235 return err 236 } 237 } 238 239 n.noIndexJoin = (indexFlags != nil && indexFlags.NoIndexJoin) 240 return n.initDescDefaults(colCfg) 241 } 242 243 func (n *scanNode) lookupSpecifiedIndex(indexFlags *tree.IndexFlags) error { 244 if indexFlags.Index != "" { 245 // Search index by name. 246 indexName := string(indexFlags.Index) 247 if indexName == n.desc.PrimaryIndex.Name { 248 n.specifiedIndex = &n.desc.PrimaryIndex 249 } else { 250 for i := range n.desc.Indexes { 251 if indexName == n.desc.Indexes[i].Name { 252 n.specifiedIndex = &n.desc.Indexes[i] 253 break 254 } 255 } 256 } 257 if n.specifiedIndex == nil { 258 return errors.Errorf("index %q not found", tree.ErrString(&indexFlags.Index)) 259 } 260 } else if indexFlags.IndexID != 0 { 261 // Search index by ID. 262 if n.desc.PrimaryIndex.ID == sqlbase.IndexID(indexFlags.IndexID) { 263 n.specifiedIndex = &n.desc.PrimaryIndex 264 } else { 265 for i := range n.desc.Indexes { 266 if n.desc.Indexes[i].ID == sqlbase.IndexID(indexFlags.IndexID) { 267 n.specifiedIndex = &n.desc.Indexes[i] 268 break 269 } 270 } 271 } 272 if n.specifiedIndex == nil { 273 return errors.Errorf("index [%d] not found", indexFlags.IndexID) 274 } 275 } 276 return nil 277 } 278 279 // initColsForScan initializes cols according to desc and colCfg. 280 func initColsForScan( 281 desc *sqlbase.ImmutableTableDescriptor, colCfg scanColumnsConfig, 282 ) (cols []sqlbase.ColumnDescriptor, err error) { 283 if colCfg.wantedColumns == nil { 284 // Add all active and maybe mutation columns. 285 if colCfg.visibility == execinfra.ScanVisibilityPublic { 286 cols = desc.Columns 287 } else { 288 cols = desc.ReadableColumns 289 } 290 return cols, nil 291 } 292 293 cols = make([]sqlbase.ColumnDescriptor, 0, len(desc.ReadableColumns)) 294 for _, wc := range colCfg.wantedColumns { 295 var c *sqlbase.ColumnDescriptor 296 var err error 297 if id := sqlbase.ColumnID(wc); colCfg.visibility == execinfra.ScanVisibilityPublic { 298 c, err = desc.FindActiveColumnByID(id) 299 } else { 300 c, _, err = desc.FindReadableColumnByID(id) 301 } 302 if err != nil { 303 return cols, err 304 } 305 306 cols = append(cols, *c) 307 } 308 309 if colCfg.addUnwantedAsHidden { 310 for i := range desc.Columns { 311 c := &desc.Columns[i] 312 found := false 313 for _, wc := range colCfg.wantedColumns { 314 if sqlbase.ColumnID(wc) == c.ID { 315 found = true 316 break 317 } 318 } 319 if !found { 320 col := *c 321 col.Hidden = true 322 cols = append(cols, col) 323 } 324 } 325 } 326 327 return cols, nil 328 } 329 330 // Initializes the column structures. 331 func (n *scanNode) initDescDefaults(colCfg scanColumnsConfig) error { 332 n.colCfg = colCfg 333 n.index = &n.desc.PrimaryIndex 334 335 var err error 336 n.cols, err = initColsForScan(n.desc, n.colCfg) 337 if err != nil { 338 return err 339 } 340 341 // Set up the rest of the scanNode. 342 n.resultColumns = sqlbase.ResultColumnsFromColDescs(n.desc.GetID(), n.cols) 343 n.colIdxMap = make(map[sqlbase.ColumnID]int, len(n.cols)) 344 for i, c := range n.cols { 345 n.colIdxMap[c.ID] = i 346 } 347 n.filterVars = tree.MakeIndexedVarHelper(n, len(n.cols)) 348 return nil 349 }