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

     1  // Copyright 2019 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 rowexec
    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/row"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    22  	"github.com/cockroachdb/errors"
    23  )
    24  
    25  // indexSkipTableReader is a processor that retrieves distinct rows from
    26  // a table using the prefix of an index to skip reading some rows in the
    27  // table. Specifically, given a prefix of an index to distinct over,
    28  // the indexSkipTableReader returns all distinct rows where that prefix
    29  // of the index is distinct. It uses the index to seek to distinct values
    30  // of the prefix instead of doing a full table scan.
    31  type indexSkipTableReader struct {
    32  	execinfra.ProcessorBase
    33  
    34  	spans roachpb.Spans
    35  
    36  	// currentSpan maintains which span we are currently scanning.
    37  	currentSpan int
    38  
    39  	// keyPrefixLen holds the length of the prefix of the index
    40  	// that we are performing a distinct over.
    41  	keyPrefixLen int
    42  	// indexLen holds the number of columns in the index that
    43  	// is being considered.
    44  	indexLen int
    45  
    46  	reverse bool
    47  
    48  	ignoreMisplannedRanges bool
    49  	misplannedRanges       []roachpb.RangeInfo
    50  
    51  	fetcher row.Fetcher
    52  	alloc   sqlbase.DatumAlloc
    53  }
    54  
    55  const indexSkipTableReaderProcName = "index skip table reader"
    56  
    57  var istrPool = sync.Pool{
    58  	New: func() interface{} {
    59  		return &indexSkipTableReader{}
    60  	},
    61  }
    62  
    63  var _ execinfra.Processor = &indexSkipTableReader{}
    64  var _ execinfra.RowSource = &indexSkipTableReader{}
    65  var _ execinfrapb.MetadataSource = &indexSkipTableReader{}
    66  
    67  func newIndexSkipTableReader(
    68  	flowCtx *execinfra.FlowCtx,
    69  	processorID int32,
    70  	spec *execinfrapb.IndexSkipTableReaderSpec,
    71  	post *execinfrapb.PostProcessSpec,
    72  	output execinfra.RowReceiver,
    73  ) (*indexSkipTableReader, error) {
    74  	// NB: we hit this with a zero NodeID (but !ok) with multi-tenancy.
    75  	if nodeID, ok := flowCtx.NodeID.OptionalNodeID(); nodeID == 0 && ok {
    76  		return nil, errors.Errorf("attempting to create a tableReader with uninitialized NodeID")
    77  	}
    78  
    79  	t := istrPool.Get().(*indexSkipTableReader)
    80  
    81  	returnMutations := spec.Visibility == execinfra.ScanVisibilityPublicAndNotPublic
    82  	types := spec.Table.ColumnTypesWithMutations(returnMutations)
    83  	t.ignoreMisplannedRanges = flowCtx.Local
    84  	t.reverse = spec.Reverse
    85  
    86  	if err := t.Init(
    87  		t,
    88  		post,
    89  		types,
    90  		flowCtx,
    91  		processorID,
    92  		output,
    93  		nil, /* memMonitor */
    94  		execinfra.ProcStateOpts{
    95  			InputsToDrain:        nil,
    96  			TrailingMetaCallback: t.generateTrailingMeta,
    97  		},
    98  	); err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	neededColumns := t.Out.NeededColumns()
   103  	t.keyPrefixLen = neededColumns.Len()
   104  
   105  	columnIdxMap := spec.Table.ColumnIdxMapWithMutations(returnMutations)
   106  
   107  	immutDesc := sqlbase.NewImmutableTableDescriptor(spec.Table)
   108  	index, isSecondaryIndex, err := immutDesc.FindIndexByIndexIdx(int(spec.IndexIdx))
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	t.indexLen = len(index.ColumnIDs)
   113  
   114  	cols := immutDesc.Columns
   115  	if returnMutations {
   116  		cols = immutDesc.ReadableColumns
   117  	}
   118  
   119  	tableArgs := row.FetcherTableArgs{
   120  		Desc:             immutDesc,
   121  		Index:            index,
   122  		ColIdxMap:        columnIdxMap,
   123  		IsSecondaryIndex: isSecondaryIndex,
   124  		Cols:             cols,
   125  		ValNeededForCol:  neededColumns,
   126  	}
   127  
   128  	if err := t.fetcher.Init(
   129  		flowCtx.Codec(),
   130  		t.reverse,
   131  		spec.LockingStrength,
   132  		true,  /* returnRangeInfo */
   133  		false, /* isCheck */
   134  		&t.alloc,
   135  		tableArgs,
   136  	); err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	// Make a copy of the spans for this reader, as we will modify them.
   141  	nSpans := len(spec.Spans)
   142  	if cap(t.spans) >= nSpans {
   143  		t.spans = t.spans[:nSpans]
   144  	} else {
   145  		t.spans = make(roachpb.Spans, nSpans)
   146  	}
   147  
   148  	// If we are scanning in reverse, then copy the spans in backwards.
   149  	if t.reverse {
   150  		for i, s := range spec.Spans {
   151  			t.spans[len(spec.Spans)-i-1] = s.Span
   152  		}
   153  	} else {
   154  		for i, s := range spec.Spans {
   155  			t.spans[i] = s.Span
   156  		}
   157  	}
   158  
   159  	return t, nil
   160  }
   161  
   162  func (t *indexSkipTableReader) Start(ctx context.Context) context.Context {
   163  	t.StartInternal(ctx, indexSkipTableReaderProcName)
   164  	return ctx
   165  }
   166  
   167  func (t *indexSkipTableReader) Next() (sqlbase.EncDatumRow, *execinfrapb.ProducerMetadata) {
   168  	for t.State == execinfra.StateRunning {
   169  		if t.currentSpan >= len(t.spans) {
   170  			t.MoveToDraining(nil)
   171  			return nil, t.DrainHelper()
   172  		}
   173  
   174  		// Start a scan to get the smallest value within this span.
   175  		err := t.fetcher.StartScan(
   176  			t.Ctx, t.FlowCtx.Txn, t.spans[t.currentSpan:t.currentSpan+1],
   177  			true, 1 /* batch size limit */, t.FlowCtx.TraceKV,
   178  		)
   179  		if err != nil {
   180  			t.MoveToDraining(err)
   181  			return nil, &execinfrapb.ProducerMetadata{Err: err}
   182  		}
   183  
   184  		// Range info resets once a scan begins, so we need to maintain
   185  		// the range info we get after each scan.
   186  		if !t.ignoreMisplannedRanges {
   187  			nodeID, ok := t.FlowCtx.NodeID.OptionalNodeID()
   188  			if ok {
   189  				ranges := execinfra.MisplannedRanges(t.Ctx, t.fetcher.GetRangesInfo(), nodeID)
   190  				for _, r := range ranges {
   191  					t.misplannedRanges = roachpb.InsertRangeInfo(t.misplannedRanges, r)
   192  				}
   193  			}
   194  		}
   195  
   196  		// This key *must not* be modified, as this will cause the fetcher
   197  		// to begin acting incorrectly. This is because modifications
   198  		// will corrupt the row internal to the fetcher.
   199  		key, err := t.fetcher.PartialKey(t.keyPrefixLen)
   200  		if err != nil {
   201  			t.MoveToDraining(err)
   202  			return nil, &execinfrapb.ProducerMetadata{Err: err}
   203  		}
   204  
   205  		row, _, _, err := t.fetcher.NextRow(t.Ctx)
   206  		if err != nil {
   207  			t.MoveToDraining(err)
   208  			return nil, &execinfrapb.ProducerMetadata{Err: err}
   209  		}
   210  		if row == nil {
   211  			// No more rows in this span, so move to the next one.
   212  			t.currentSpan++
   213  			continue
   214  		}
   215  
   216  		if !t.reverse {
   217  			// We set the new key to be the largest key with the prefix that we have
   218  			// so that we skip all values with the same prefix, and "skip" to the
   219  			// next distinct value.
   220  			t.spans[t.currentSpan].Key = key.PrefixEnd()
   221  		} else {
   222  			// In the case of reverse, this is much easier. The reverse fetcher
   223  			// returns the key retrieved, in this case the first key smaller
   224  			// than EndKey in the current span. Since EndKey is exclusive, we
   225  			// just set the retrieved key as EndKey for the next scan.
   226  			t.spans[t.currentSpan].EndKey = key
   227  		}
   228  
   229  		// If the changes we made turned our current span invalid, mark that
   230  		// we should move on to the next span before returning the row.
   231  		if !t.spans[t.currentSpan].Valid() {
   232  			t.currentSpan++
   233  		}
   234  
   235  		if outRow := t.ProcessRowHelper(row); outRow != nil {
   236  			return outRow, nil
   237  		}
   238  	}
   239  	return nil, t.DrainHelper()
   240  }
   241  
   242  func (t *indexSkipTableReader) Release() {
   243  	t.ProcessorBase.Reset()
   244  	t.fetcher.Reset()
   245  	*t = indexSkipTableReader{
   246  		ProcessorBase:    t.ProcessorBase,
   247  		fetcher:          t.fetcher,
   248  		spans:            t.spans[:0],
   249  		misplannedRanges: t.misplannedRanges[:0],
   250  		currentSpan:      0,
   251  	}
   252  	istrPool.Put(t)
   253  }
   254  
   255  func (t *indexSkipTableReader) ConsumerClosed() {
   256  	t.InternalClose()
   257  }
   258  
   259  func (t *indexSkipTableReader) generateTrailingMeta(
   260  	ctx context.Context,
   261  ) []execinfrapb.ProducerMetadata {
   262  	trailingMeta := t.generateMeta(ctx)
   263  	t.InternalClose()
   264  	return trailingMeta
   265  }
   266  
   267  func (t *indexSkipTableReader) generateMeta(ctx context.Context) []execinfrapb.ProducerMetadata {
   268  	var trailingMeta []execinfrapb.ProducerMetadata
   269  	if !t.ignoreMisplannedRanges {
   270  		if len(t.misplannedRanges) != 0 {
   271  			trailingMeta = append(trailingMeta, execinfrapb.ProducerMetadata{Ranges: t.misplannedRanges})
   272  		}
   273  	}
   274  	if tfs := execinfra.GetLeafTxnFinalState(ctx, t.FlowCtx.Txn); tfs != nil {
   275  		trailingMeta = append(trailingMeta, execinfrapb.ProducerMetadata{LeafTxnFinalState: tfs})
   276  	}
   277  	return trailingMeta
   278  }
   279  
   280  func (t *indexSkipTableReader) DrainMeta(ctx context.Context) []execinfrapb.ProducerMetadata {
   281  	return t.generateMeta(ctx)
   282  }