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 }