github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/optbuilder/update.go (about) 1 // Copyright 2018 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 optbuilder 12 13 import ( 14 "fmt" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/opt" 17 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 18 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 19 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 20 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 21 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 22 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 23 "github.com/cockroachdb/cockroach/pkg/sql/types" 24 "github.com/cockroachdb/errors" 25 ) 26 27 // buildUpdate builds a memo group for an UpdateOp expression. First, an input 28 // expression is constructed that outputs the existing values for all rows from 29 // the target table that match the WHERE clause. Additional column(s) that 30 // provide updated values are projected for each of the SET expressions, as well 31 // as for any computed columns. For example: 32 // 33 // CREATE TABLE abc (a INT PRIMARY KEY, b INT, c INT) 34 // UPDATE abc SET b=1 WHERE a=2 35 // 36 // This would create an input expression similar to this SQL: 37 // 38 // SELECT a AS oa, b AS ob, c AS oc, 1 AS nb FROM abc WHERE a=2 39 // 40 // The execution engine evaluates this relational expression and uses the 41 // resulting values to form the KV keys and values. 42 // 43 // Tuple SET expressions are decomposed into individual columns: 44 // 45 // UPDATE abc SET (b, c)=(1, 2) WHERE a=3 46 // => 47 // SELECT a AS oa, b AS ob, c AS oc, 1 AS nb, 2 AS nc FROM abc WHERE a=3 48 // 49 // Subqueries become correlated left outer joins: 50 // 51 // UPDATE abc SET b=(SELECT y FROM xyz WHERE x=a) 52 // => 53 // SELECT a AS oa, b AS ob, c AS oc, y AS nb 54 // FROM abc 55 // LEFT JOIN LATERAL (SELECT y FROM xyz WHERE x=a) 56 // ON True 57 // 58 // Computed columns result in an additional wrapper projection that can depend 59 // on input columns. 60 // 61 // Note that the ORDER BY clause can only be used if the LIMIT clause is also 62 // present. In that case, the ordering determines which rows are included by the 63 // limit. The ORDER BY makes no additional guarantees about the order in which 64 // mutations are applied, or the order of any returned rows (i.e. it won't 65 // become a physical property required of the Update operator). 66 func (b *Builder) buildUpdate(upd *tree.Update, inScope *scope) (outScope *scope) { 67 if upd.OrderBy != nil && upd.Limit == nil { 68 panic(pgerror.Newf(pgcode.Syntax, 69 "UPDATE statement requires LIMIT when ORDER BY is used")) 70 } 71 72 // UX friendliness safeguard. 73 if upd.Where == nil && b.evalCtx.SessionData.SafeUpdates { 74 panic(pgerror.DangerousStatementf("UPDATE without WHERE clause")) 75 } 76 77 // Find which table we're working on, check the permissions. 78 tab, depName, alias, refColumns := b.resolveTableForMutation(upd.Table, privilege.UPDATE) 79 80 if refColumns != nil { 81 panic(pgerror.Newf(pgcode.Syntax, 82 "cannot specify a list of column IDs with UPDATE")) 83 } 84 85 // Check Select permission as well, since existing values must be read. 86 b.checkPrivilege(depName, tab, privilege.SELECT) 87 88 var mb mutationBuilder 89 mb.init(b, "update", tab, alias) 90 91 // Build the input expression that selects the rows that will be updated: 92 // 93 // WITH <with> 94 // SELECT <cols> FROM <table> WHERE <where> 95 // ORDER BY <order-by> LIMIT <limit> 96 // 97 // All columns from the update table will be projected. 98 mb.buildInputForUpdate(inScope, upd.Table, upd.From, upd.Where, upd.Limit, upd.OrderBy) 99 100 // Derive the columns that will be updated from the SET expressions. 101 mb.addTargetColsForUpdate(upd.Exprs) 102 103 // Build each of the SET expressions. 104 mb.addUpdateCols(upd.Exprs) 105 106 // Build the final update statement, including any returned expressions. 107 if resultsNeeded(upd.Returning) { 108 mb.buildUpdate(*upd.Returning.(*tree.ReturningExprs)) 109 } else { 110 mb.buildUpdate(nil /* returning */) 111 } 112 113 return mb.outScope 114 } 115 116 // addTargetColsForUpdate compiles the given SET expressions and adds the user- 117 // specified column names to the list of table columns that will be updated by 118 // the Update operation. Verify that the RHS of the SET expression provides 119 // exactly as many columns as are expected by the named SET columns. 120 func (mb *mutationBuilder) addTargetColsForUpdate(exprs tree.UpdateExprs) { 121 if len(mb.targetColList) != 0 { 122 panic(errors.AssertionFailedf("addTargetColsForUpdate cannot be called more than once")) 123 } 124 125 for _, expr := range exprs { 126 mb.addTargetColsByName(expr.Names) 127 128 if expr.Tuple { 129 n := -1 130 switch t := expr.Expr.(type) { 131 case *tree.Subquery: 132 // Build the subquery in order to determine how many columns it 133 // projects, and store it for later use in the addUpdateCols method. 134 // Use the data types of the target columns to resolve expressions 135 // with ambiguous types (e.g. should 1 be interpreted as an INT or 136 // as a FLOAT). 137 desiredTypes := make([]*types.T, len(expr.Names)) 138 targetIdx := len(mb.targetColList) - len(expr.Names) 139 for i := range desiredTypes { 140 desiredTypes[i] = mb.md.ColumnMeta(mb.targetColList[targetIdx+i]).Type 141 } 142 outScope := mb.b.buildSelectStmt(t.Select, noRowLocking, desiredTypes, mb.outScope) 143 mb.subqueries = append(mb.subqueries, outScope) 144 n = len(outScope.cols) 145 146 case *tree.Tuple: 147 n = len(t.Exprs) 148 } 149 if n < 0 { 150 panic(unimplementedWithIssueDetailf(35713, fmt.Sprintf("%T", expr.Expr), 151 "source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression; not supported: %T", expr.Expr)) 152 } 153 if len(expr.Names) != n { 154 panic(pgerror.Newf(pgcode.Syntax, 155 "number of columns (%d) does not match number of values (%d)", 156 len(expr.Names), n)) 157 } 158 } 159 } 160 } 161 162 // addUpdateCols builds nested Project and LeftOuterJoin expressions that 163 // correspond to the given SET expressions: 164 // 165 // SET a=1 (single-column SET) 166 // Add as synthesized Project column: 167 // SELECT <fetch-cols>, 1 FROM <input> 168 // 169 // SET (a, b)=(1, 2) (tuple SET) 170 // Add as multiple Project columns: 171 // SELECT <fetch-cols>, 1, 2 FROM <input> 172 // 173 // SET (a, b)=(SELECT 1, 2) (subquery) 174 // Wrap input in Max1Row + LeftJoinApply expressions: 175 // SELECT * FROM <fetch-cols> LEFT JOIN LATERAL (SELECT 1, 2) ON True 176 // 177 // Multiple subqueries result in multiple left joins successively wrapping the 178 // input. A final Project operator is built if any single-column or tuple SET 179 // expressions are present. 180 func (mb *mutationBuilder) addUpdateCols(exprs tree.UpdateExprs) { 181 // SET expressions should reject aggregates, generators, etc. 182 scalarProps := &mb.b.semaCtx.Properties 183 defer scalarProps.Restore(*scalarProps) 184 mb.b.semaCtx.Properties.Require("UPDATE SET", tree.RejectSpecial) 185 186 // UPDATE input columns are accessible to SET expressions. 187 inScope := mb.outScope 188 189 // Project additional column(s) for each update expression (can be multiple 190 // columns in case of tuple assignment). 191 projectionsScope := mb.outScope.replace() 192 projectionsScope.appendColumnsFromScope(mb.outScope) 193 194 checkCol := func(sourceCol *scopeColumn, scopeOrd scopeOrdinal, targetColID opt.ColumnID) { 195 // Type check the input expression against the corresponding table column. 196 ord := mb.tabID.ColumnOrdinal(targetColID) 197 checkDatumTypeFitsColumnType(mb.tab.Column(ord), sourceCol.typ) 198 199 // Add ordinal of new scope column to the list of columns to update. 200 mb.updateOrds[ord] = scopeOrd 201 202 // Rename the column to match the target column being updated. 203 sourceCol.name = mb.tab.Column(ord).ColName() 204 } 205 206 addCol := func(expr tree.Expr, targetColID opt.ColumnID) { 207 // Allow right side of SET to be DEFAULT. 208 if _, ok := expr.(tree.DefaultVal); ok { 209 expr = mb.parseDefaultOrComputedExpr(targetColID) 210 } 211 212 // Add new column to the projections scope. 213 targetColMeta := mb.md.ColumnMeta(targetColID) 214 desiredType := targetColMeta.Type 215 texpr := inScope.resolveType(expr, desiredType) 216 scopeCol := mb.b.addColumn(projectionsScope, targetColMeta.Alias+"_new", texpr) 217 scopeColOrd := scopeOrdinal(len(projectionsScope.cols) - 1) 218 mb.b.buildScalar(texpr, inScope, projectionsScope, scopeCol, nil) 219 220 checkCol(scopeCol, scopeColOrd, targetColID) 221 } 222 223 n := 0 224 subquery := 0 225 for _, set := range exprs { 226 if set.Tuple { 227 switch t := set.Expr.(type) { 228 case *tree.Subquery: 229 // Get the subquery scope that was built by addTargetColsForUpdate. 230 subqueryScope := mb.subqueries[subquery] 231 subquery++ 232 233 // Type check and rename columns. 234 for i := range subqueryScope.cols { 235 scopeColOrd := scopeOrdinal(len(projectionsScope.cols) + i) 236 checkCol(&subqueryScope.cols[i], scopeColOrd, mb.targetColList[n]) 237 n++ 238 } 239 240 // Lazily create new scope to hold results of join. 241 if mb.outScope == inScope { 242 mb.outScope = inScope.replace() 243 mb.outScope.appendColumnsFromScope(inScope) 244 mb.outScope.expr = inScope.expr 245 } 246 247 // Wrap input with Max1Row + LOJ. 248 mb.outScope.appendColumnsFromScope(subqueryScope) 249 mb.outScope.expr = mb.b.factory.ConstructLeftJoinApply( 250 mb.outScope.expr, 251 mb.b.factory.ConstructMax1Row(subqueryScope.expr, multiRowSubqueryErrText), 252 memo.TrueFilter, 253 memo.EmptyJoinPrivate, 254 ) 255 256 // Project all subquery output columns. 257 projectionsScope.appendColumnsFromScope(subqueryScope) 258 259 case *tree.Tuple: 260 for _, expr := range t.Exprs { 261 addCol(expr, mb.targetColList[n]) 262 n++ 263 } 264 } 265 } else { 266 addCol(set.Expr, mb.targetColList[n]) 267 n++ 268 } 269 } 270 271 mb.b.constructProjectForScope(mb.outScope, projectionsScope) 272 mb.outScope = projectionsScope 273 274 // Add additional columns for computed expressions that may depend on the 275 // updated columns. 276 mb.addSynthesizedColsForUpdate() 277 } 278 279 // addSynthesizedColsForUpdate wraps an Update input expression with a Project 280 // operator containing any computed columns that need to be updated. This 281 // includes write-only mutation columns that are computed. 282 func (mb *mutationBuilder) addSynthesizedColsForUpdate() { 283 // Allow mutation columns to be referenced by other computed mutation 284 // columns (otherwise the scope will raise an error if a mutation column 285 // is referenced). These do not need to be set back to true again because 286 // mutation columns are not projected by the Update operator. 287 for i := range mb.outScope.cols { 288 mb.outScope.cols[i].mutation = false 289 } 290 291 // Add non-computed columns that are being dropped or added (mutated) to the 292 // table. These are not visible to queries, and will always be updated to 293 // their default values. This is necessary because they may not yet have been 294 // set by the backfiller. 295 mb.addSynthesizedCols( 296 mb.updateOrds, 297 func(colOrd int) bool { 298 return !mb.tab.Column(colOrd).IsComputed() && cat.IsMutationColumn(mb.tab, colOrd) 299 }, 300 ) 301 302 // Possibly round DECIMAL-related columns containing update values. Do 303 // this before evaluating computed expressions, since those may depend on 304 // the inserted columns. 305 mb.roundDecimalValues(mb.updateOrds, false /* roundComputedCols */) 306 307 // Disambiguate names so that references in the computed expression refer to 308 // the correct columns. 309 mb.disambiguateColumns() 310 311 // Add all computed columns in case their values have changed. 312 mb.addSynthesizedCols( 313 mb.updateOrds, 314 func(colOrd int) bool { return mb.tab.Column(colOrd).IsComputed() }, 315 ) 316 317 // Possibly round DECIMAL-related computed columns. 318 mb.roundDecimalValues(mb.updateOrds, true /* roundComputedCols */) 319 } 320 321 // buildUpdate constructs an Update operator, possibly wrapped by a Project 322 // operator that corresponds to the given RETURNING clause. 323 func (mb *mutationBuilder) buildUpdate(returning tree.ReturningExprs) { 324 // Disambiguate names so that references in any expressions, such as a 325 // check constraint, refer to the correct columns. 326 mb.disambiguateColumns() 327 328 mb.addCheckConstraintCols() 329 330 mb.buildFKChecksForUpdate() 331 332 private := mb.makeMutationPrivate(returning != nil) 333 for _, col := range mb.extraAccessibleCols { 334 if col.id != 0 { 335 private.PassthroughCols = append(private.PassthroughCols, col.id) 336 } 337 } 338 mb.outScope.expr = mb.b.factory.ConstructUpdate(mb.outScope.expr, mb.checks, private) 339 mb.buildReturning(returning) 340 }