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  }