github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/colbatch_scan.go (about)

     1  // Copyright 2016 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 colexec
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/col/coldata"
    17  	"github.com/cockroachdb/cockroach/pkg/keys"
    18  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/colexecbase"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/colexecbase/colexecerror"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/colmem"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/execinfra"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/row"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    26  	"github.com/cockroachdb/cockroach/pkg/util"
    27  	"github.com/cockroachdb/errors"
    28  )
    29  
    30  // TODO(yuzefovich): reading the data through a pair of colBatchScan and
    31  // materializer turns out to be more efficient than through a table reader (at
    32  // the moment, the exception is the case of reading very small number of rows
    33  // because we still pre-allocate batches of 1024 size). Once we can control the
    34  // initial size of pre-allocated batches (probably via a batch allocator), we
    35  // should get rid off table readers entirely. We will have to be careful about
    36  // propagating the metadata though.
    37  
    38  // colBatchScan is the exec.Operator implementation of TableReader. It reads a table
    39  // from kv, presenting it as coldata.Batches via the exec.Operator interface.
    40  type colBatchScan struct {
    41  	colexecbase.ZeroInputNode
    42  	spans     roachpb.Spans
    43  	flowCtx   *execinfra.FlowCtx
    44  	rf        *cFetcher
    45  	limitHint int64
    46  	ctx       context.Context
    47  	// maxResults is non-zero if there is a limit on the total number of rows
    48  	// that the colBatchScan will read.
    49  	maxResults uint64
    50  	// init is true after Init() has been called.
    51  	init bool
    52  }
    53  
    54  var _ colexecbase.Operator = &colBatchScan{}
    55  
    56  func (s *colBatchScan) Init() {
    57  	s.ctx = context.Background()
    58  	s.init = true
    59  
    60  	limitBatches := execinfra.ScanShouldLimitBatches(s.maxResults, s.limitHint, s.flowCtx)
    61  
    62  	if err := s.rf.StartScan(
    63  		s.ctx, s.flowCtx.Txn, s.spans,
    64  		limitBatches, s.limitHint, s.flowCtx.TraceKV,
    65  	); err != nil {
    66  		colexecerror.InternalError(err)
    67  	}
    68  }
    69  
    70  func (s *colBatchScan) Next(ctx context.Context) coldata.Batch {
    71  	bat, err := s.rf.NextBatch(ctx)
    72  	if err != nil {
    73  		colexecerror.InternalError(err)
    74  	}
    75  	if bat.Selection() != nil {
    76  		colexecerror.InternalError("unexpectedly a selection vector is set on the batch coming from CFetcher")
    77  	}
    78  	return bat
    79  }
    80  
    81  // DrainMeta is part of the MetadataSource interface.
    82  func (s *colBatchScan) DrainMeta(ctx context.Context) []execinfrapb.ProducerMetadata {
    83  	if !s.init {
    84  		// In some pathological queries like `SELECT 1 FROM t HAVING true`, Init()
    85  		// and Next() may never get called. Return early to avoid using an
    86  		// uninitialized fetcher.
    87  		return nil
    88  	}
    89  	var trailingMeta []execinfrapb.ProducerMetadata
    90  	if !s.flowCtx.Local {
    91  		nodeID, ok := s.flowCtx.NodeID.OptionalNodeID()
    92  		if ok {
    93  			ranges := execinfra.MisplannedRanges(ctx, s.rf.GetRangesInfo(), nodeID)
    94  			if ranges != nil {
    95  				trailingMeta = append(trailingMeta, execinfrapb.ProducerMetadata{Ranges: ranges})
    96  			}
    97  		}
    98  	}
    99  	if tfs := execinfra.GetLeafTxnFinalState(ctx, s.flowCtx.Txn); tfs != nil {
   100  		trailingMeta = append(trailingMeta, execinfrapb.ProducerMetadata{LeafTxnFinalState: tfs})
   101  	}
   102  	return trailingMeta
   103  }
   104  
   105  // newColBatchScan creates a new colBatchScan operator.
   106  func newColBatchScan(
   107  	allocator *colmem.Allocator,
   108  	flowCtx *execinfra.FlowCtx,
   109  	spec *execinfrapb.TableReaderSpec,
   110  	post *execinfrapb.PostProcessSpec,
   111  ) (*colBatchScan, error) {
   112  	// NB: we hit this with a zero NodeID (but !ok) with multi-tenancy.
   113  	if nodeID, ok := flowCtx.NodeID.OptionalNodeID(); nodeID == 0 && ok {
   114  		return nil, errors.Errorf("attempting to create a colBatchScan with uninitialized NodeID")
   115  	}
   116  
   117  	limitHint := execinfra.LimitHint(spec.LimitHint, post)
   118  
   119  	returnMutations := spec.Visibility == execinfra.ScanVisibilityPublicAndNotPublic
   120  	typs := spec.Table.ColumnTypesWithMutations(returnMutations)
   121  	evalCtx := flowCtx.NewEvalCtx()
   122  	// Before we can safely use types from the table descriptor, we need to
   123  	// make sure they are hydrated. In row execution engine it is done during
   124  	// the processor initialization, but neither colBatchScan nor cFetcher are
   125  	// processors, so we need to do the hydration ourselves.
   126  	if err := execinfrapb.HydrateTypeSlice(evalCtx, typs); err != nil {
   127  		return nil, err
   128  	}
   129  	helper := execinfra.ProcOutputHelper{}
   130  	if err := helper.Init(
   131  		post,
   132  		typs,
   133  		evalCtx,
   134  		nil, /* output */
   135  	); err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	neededColumns := helper.NeededColumns()
   140  
   141  	columnIdxMap := spec.Table.ColumnIdxMapWithMutations(returnMutations)
   142  	fetcher := cFetcher{}
   143  	if _, _, err := initCRowFetcher(
   144  		flowCtx.Codec(), allocator, &fetcher, &spec.Table, int(spec.IndexIdx), columnIdxMap,
   145  		spec.Reverse, neededColumns, spec.IsCheck, spec.Visibility, spec.LockingStrength,
   146  	); err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	nSpans := len(spec.Spans)
   151  	spans := make(roachpb.Spans, nSpans)
   152  	for i := range spans {
   153  		spans[i] = spec.Spans[i].Span
   154  	}
   155  	return &colBatchScan{
   156  		spans:      spans,
   157  		flowCtx:    flowCtx,
   158  		rf:         &fetcher,
   159  		limitHint:  limitHint,
   160  		maxResults: spec.MaxResults,
   161  	}, nil
   162  }
   163  
   164  // initCRowFetcher initializes a row.cFetcher. See initRowFetcher.
   165  func initCRowFetcher(
   166  	codec keys.SQLCodec,
   167  	allocator *colmem.Allocator,
   168  	fetcher *cFetcher,
   169  	desc *sqlbase.TableDescriptor,
   170  	indexIdx int,
   171  	colIdxMap map[sqlbase.ColumnID]int,
   172  	reverseScan bool,
   173  	valNeededForCol util.FastIntSet,
   174  	isCheck bool,
   175  	scanVisibility execinfrapb.ScanVisibility,
   176  	lockStr sqlbase.ScanLockingStrength,
   177  ) (index *sqlbase.IndexDescriptor, isSecondaryIndex bool, err error) {
   178  	immutDesc := sqlbase.NewImmutableTableDescriptor(*desc)
   179  	index, isSecondaryIndex, err = immutDesc.FindIndexByIndexIdx(indexIdx)
   180  	if err != nil {
   181  		return nil, false, err
   182  	}
   183  
   184  	cols := immutDesc.Columns
   185  	if scanVisibility == execinfra.ScanVisibilityPublicAndNotPublic {
   186  		cols = immutDesc.ReadableColumns
   187  	}
   188  	tableArgs := row.FetcherTableArgs{
   189  		Desc:             immutDesc,
   190  		Index:            index,
   191  		ColIdxMap:        colIdxMap,
   192  		IsSecondaryIndex: isSecondaryIndex,
   193  		Cols:             cols,
   194  		ValNeededForCol:  valNeededForCol,
   195  	}
   196  	if err := fetcher.Init(
   197  		codec, allocator, reverseScan, lockStr, true /* returnRangeInfo */, isCheck, tableArgs,
   198  	); err != nil {
   199  		return nil, false, err
   200  	}
   201  
   202  	return index, isSecondaryIndex, nil
   203  }