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  }