github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/upsert.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 sql
    12  
    13  import (
    14  	"context"
    15  	"sync"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    19  	"github.com/cockroachdb/cockroach/pkg/util"
    20  	"github.com/cockroachdb/cockroach/pkg/util/tracing"
    21  )
    22  
    23  var upsertNodePool = sync.Pool{
    24  	New: func() interface{} {
    25  		return &upsertNode{}
    26  	},
    27  }
    28  
    29  type upsertNode struct {
    30  	source planNode
    31  
    32  	// columns is set if this UPDATE is returning any rows, to be
    33  	// consumed by a renderNode upstream. This occurs when there is a
    34  	// RETURNING clause with some scalar expressions.
    35  	columns sqlbase.ResultColumns
    36  
    37  	run upsertRun
    38  }
    39  
    40  // upsertRun contains the run-time state of upsertNode during local execution.
    41  type upsertRun struct {
    42  	tw        optTableUpserter
    43  	checkOrds checkSet
    44  
    45  	// insertCols are the columns being inserted/upserted into.
    46  	insertCols []sqlbase.ColumnDescriptor
    47  
    48  	// done informs a new call to BatchedNext() that the previous call to
    49  	// BatchedNext() has completed the work already.
    50  	done bool
    51  
    52  	// traceKV caches the current KV tracing flag.
    53  	traceKV bool
    54  }
    55  
    56  func (n *upsertNode) startExec(params runParams) error {
    57  	// cache traceKV during execution, to avoid re-evaluating it for every row.
    58  	n.run.traceKV = params.p.ExtendedEvalContext().Tracing.KVTracingEnabled()
    59  
    60  	return n.run.tw.init(params.ctx, params.p.txn, params.EvalContext())
    61  }
    62  
    63  // Next is required because batchedPlanNode inherits from planNode, but
    64  // batchedPlanNode doesn't really provide it. See the explanatory comments
    65  // in plan_batch.go.
    66  func (n *upsertNode) Next(params runParams) (bool, error) { panic("not valid") }
    67  
    68  // Values is required because batchedPlanNode inherits from planNode, but
    69  // batchedPlanNode doesn't really provide it. See the explanatory comments
    70  // in plan_batch.go.
    71  func (n *upsertNode) Values() tree.Datums { panic("not valid") }
    72  
    73  // maxUpsertBatchSize is the max number of entries in the KV batch for
    74  // the upsert operation (including secondary index updates, FK
    75  // cascading updates, etc), before the current KV batch is executed
    76  // and a new batch is started.
    77  const maxUpsertBatchSize = 10000
    78  
    79  // BatchedNext implements the batchedPlanNode interface.
    80  func (n *upsertNode) BatchedNext(params runParams) (bool, error) {
    81  	if n.run.done {
    82  		return false, nil
    83  	}
    84  
    85  	tracing.AnnotateTrace()
    86  
    87  	// Now consume/accumulate the rows for this batch.
    88  	lastBatch := false
    89  	for {
    90  		if err := params.p.cancelChecker.Check(); err != nil {
    91  			return false, err
    92  		}
    93  
    94  		// Advance one individual row.
    95  		if next, err := n.source.Next(params); !next {
    96  			lastBatch = true
    97  			if err != nil {
    98  				return false, err
    99  			}
   100  			break
   101  		}
   102  
   103  		// Process the insertion for the current source row, potentially
   104  		// accumulating the result row for later.
   105  		if err := n.processSourceRow(params, n.source.Values()); err != nil {
   106  			return false, err
   107  		}
   108  
   109  		// Are we done yet with the current batch?
   110  		if n.run.tw.curBatchSize() >= maxUpsertBatchSize {
   111  			break
   112  		}
   113  	}
   114  
   115  	// In Upsert, curBatchSize indicates whether "there is still work to do in this batch".
   116  	batchSize := n.run.tw.curBatchSize()
   117  
   118  	if batchSize > 0 {
   119  		if err := n.run.tw.atBatchEnd(params.ctx, n.run.traceKV); err != nil {
   120  			return false, err
   121  		}
   122  
   123  		if !lastBatch {
   124  			// We only run/commit the batch if there were some rows processed
   125  			// in this batch.
   126  			if err := n.run.tw.flushAndStartNewBatch(params.ctx); err != nil {
   127  				return false, err
   128  			}
   129  		}
   130  	}
   131  
   132  	if lastBatch {
   133  		if _, err := n.run.tw.finalize(params.ctx, n.run.traceKV); err != nil {
   134  			return false, err
   135  		}
   136  		// Remember we're done for the next call to BatchedNext().
   137  		n.run.done = true
   138  	}
   139  
   140  	// Possibly initiate a run of CREATE STATISTICS.
   141  	params.ExecCfg().StatsRefresher.NotifyMutation(
   142  		n.run.tw.tableDesc().ID,
   143  		n.run.tw.batchedCount(),
   144  	)
   145  
   146  	return n.run.tw.batchedCount() > 0, nil
   147  }
   148  
   149  // processSourceRow processes one row from the source for upsertion.
   150  // The table writer is in charge of accumulating the result rows.
   151  func (n *upsertNode) processSourceRow(params runParams, rowVals tree.Datums) error {
   152  	if err := enforceLocalColumnConstraints(rowVals, n.run.insertCols); err != nil {
   153  		return err
   154  	}
   155  
   156  	// Verify the CHECK constraints by inspecting boolean columns from the input that
   157  	// contain the results of evaluation.
   158  	if !n.run.checkOrds.Empty() {
   159  		ord := len(n.run.insertCols) + len(n.run.tw.fetchCols) + len(n.run.tw.updateCols)
   160  		if n.run.tw.canaryOrdinal != -1 {
   161  			ord++
   162  		}
   163  		checkVals := rowVals[ord:]
   164  		if err := checkMutationInput(n.run.tw.tableDesc(), n.run.checkOrds, checkVals); err != nil {
   165  			return err
   166  		}
   167  		rowVals = rowVals[:ord]
   168  	}
   169  
   170  	// Process the row. This is also where the tableWriter will accumulate
   171  	// the row for later.
   172  	// TODO(mgartner): Add partial index IDs to ignoreIndexes that we should
   173  	// not write entries to.
   174  	var ignoreIndexes util.FastIntSet
   175  	return n.run.tw.row(params.ctx, rowVals, ignoreIndexes, n.run.traceKV)
   176  }
   177  
   178  // BatchedCount implements the batchedPlanNode interface.
   179  func (n *upsertNode) BatchedCount() int { return n.run.tw.batchedCount() }
   180  
   181  // BatchedCount implements the batchedPlanNode interface.
   182  func (n *upsertNode) BatchedValues(rowIdx int) tree.Datums { return n.run.tw.batchedValues(rowIdx) }
   183  
   184  func (n *upsertNode) Close(ctx context.Context) {
   185  	n.source.Close(ctx)
   186  	n.run.tw.close(ctx)
   187  	*n = upsertNode{}
   188  	upsertNodePool.Put(n)
   189  }
   190  
   191  func (n *upsertNode) enableAutoCommit() {
   192  	n.run.tw.enableAutoCommit()
   193  }