Skip to content

Commit

Permalink
sql: add support for DELETE FROM ... USING to optbuilder
Browse files Browse the repository at this point in the history
Previously, the optbuilder would return an error when
given sql statements of the form `DELETE FROM USING`.
This commit adds support to the Optbuilder to build query
plans for statements of the form `DELETE FROM ... USING`.

Release note: None
  • Loading branch information
faizaanmadhani committed Oct 14, 2022
1 parent a2b871e commit b375900
Show file tree
Hide file tree
Showing 5 changed files with 795 additions and 16 deletions.
1 change: 1 addition & 0 deletions pkg/sql/opt/memo/expr_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) {
}
f.formatOptionalColList(e, tp, "fetch columns:", t.FetchCols)
f.formatMutationCols(e, tp, "return-mapping:", t.ReturnCols, t.Table)
f.formatOptionalColList(e, tp, "passthrough columns", opt.OptionalColList(t.PassthroughCols))
f.formatOptionalColList(e, tp, "partial index del columns:", t.PartialIndexDelCols)
f.formatMutationCommon(tp, &t.MutationPrivate)
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/sql/opt/ops/mutation.opt
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,10 @@ define MutationPrivate {

# PassthroughCols are columns that the mutation needs to passthrough from
# its input. It's similar to the passthrough columns in projections. This
# is useful for `UPDATE .. FROM` mutations where the `RETURNING` clause
# references columns from tables in the `FROM` clause. When this happens
# the update will need to pass through those refenced columns from its input.
# is useful for `UPDATE .. FROM` and `DELETE ... USING` mutations where the
# `RETURNING` clause references columns from tables in the `FROM` or `USING`
# clause, respectively. When this happens the mutation will need to pass through
# those referenced columns from its input.
PassthroughCols ColList

# Mutation operators can act similarly to a With operator: they buffer their
Expand Down
5 changes: 5 additions & 0 deletions pkg/sql/opt/optbuilder/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ func (mb *mutationBuilder) buildDelete(returning tree.ReturningExprs) {
mb.projectPartialIndexDelCols()

private := mb.makeMutationPrivate(returning != nil)
for _, col := range mb.extraAccessibleCols {
if col.id != 0 {
private.PassthroughCols = append(private.PassthroughCols, col.id)
}
}
mb.outScope.expr = mb.b.factory.ConstructDelete(
mb.outScope.expr, mb.uniqueChecks, mb.fkChecks, private,
)
Expand Down
70 changes: 57 additions & 13 deletions pkg/sql/opt/optbuilder/mutation_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,9 @@ type mutationBuilder struct {

// extraAccessibleCols stores all the columns that are available to the
// mutation that are not part of the target table. This is useful for
// UPDATE ... FROM queries, as the columns from the FROM tables must be
// made accessible to the RETURNING clause.
// UPDATE ... FROM queries and DELETE ... USING queries, as the columns
// from the FROM and USING tables must be made accessible to the
// RETURNING clause, respectively.
extraAccessibleCols []scopeColumn

// fkCheckHelper is used to prevent allocating the helper separately.
Expand Down Expand Up @@ -376,7 +377,7 @@ func (mb *mutationBuilder) buildInputForUpdate(
// the Delete operator, similar to this:
//
// SELECT <cols>
// FROM <table>
// FROM <table> [, <using-tables>]
// WHERE <where>
// ORDER BY <order-by>
// LIMIT <limit>
Expand Down Expand Up @@ -418,7 +419,39 @@ func (mb *mutationBuilder) buildInputForDelete(
inScope,
false, /* disableNotVisibleIndex */
)
mb.outScope = mb.fetchScope

// Set list of columns that will be fetched by the input expression.
mb.setFetchColIDs(mb.fetchScope.cols)

// USING
usingClausePresent := len(using) > 0
if usingClausePresent {
usingScope := mb.b.buildFromTables(using, noRowLocking, inScope)

// Check that the same table name is not used multiple times
mb.b.validateJoinTableNames(mb.fetchScope, usingScope)

// The USING table columns can be accessed by the RETURNING clause of the
// query and so we have to make them accessible.
mb.extraAccessibleCols = usingScope.cols

// Add the columns to the USING scope.
// We create a new scope so that fetchScope is not modified
// as fetchScope contains the set of columns from the target
// table specified by USING. This will be used later with partial
// index predicate expressions and will prevent ambiguities with
// column names in the USING clause.
mb.outScope = mb.fetchScope.replace()
mb.outScope.appendColumnsFromScope(mb.fetchScope)
mb.outScope.appendColumnsFromScope(usingScope)

left := mb.fetchScope.expr
right := usingScope.expr

mb.outScope.expr = mb.b.factory.ConstructInnerJoin(left, right, memo.TrueFilter, memo.EmptyJoinPrivate)
} else {
mb.outScope = mb.fetchScope
}

// WHERE
mb.b.buildWhere(where, mb.outScope)
Expand All @@ -430,20 +463,30 @@ func (mb *mutationBuilder) buildInputForDelete(
mb.b.buildOrderBy(mb.outScope, projectionsScope, orderByScope)
mb.b.constructProjectForScope(mb.outScope, projectionsScope)

// USING
if using != nil {
panic("DELETE USING is unimplemented so should not be used")
}

// LIMIT
if limit != nil {
mb.b.buildLimit(limit, inScope, projectionsScope)
}

mb.outScope = projectionsScope

// Set list of columns that will be fetched by the input expression.
mb.setFetchColIDs(mb.outScope.cols)
// Build a distinct on to ensure there is at most one row in the joined output
// for every row in the table
if usingClausePresent {
var pkCols opt.ColSet

// We need to ensure that the join has a maximum of one row for every row
// in the table and we ensure this by constructing a distinct on the primary
// key columns.
primaryIndex := mb.tab.Index(cat.PrimaryIndex)
for i := 0; i < primaryIndex.KeyColumnCount(); i++ {
col := primaryIndex.Column(i)
pkCols.Add(mb.fetchColIDs[col.Ordinal()])
}

mb.outScope = mb.b.buildDistinctOn(
pkCols, mb.outScope, false /* nullsAreDistinct */, "" /* errorOnDup */)
}
}

// addTargetColsByName adds one target column for each of the names in the given
Expand Down Expand Up @@ -1011,8 +1054,9 @@ func (mb *mutationBuilder) buildReturning(returning tree.ReturningExprs) {

// extraAccessibleCols contains all the columns that the RETURNING
// clause can refer to in addition to the table columns. This is useful for
// UPDATE ... FROM statements, where all columns from tables in the FROM clause
// are in scope for the RETURNING clause.
// UPDATE ... FROM and DELETE ... USING statements, where all columns from
// tables in the FROM clause and USING clause are in scope for the RETURNING
// clause, respectively.
inScope.appendColumns(mb.extraAccessibleCols)

// Construct the Project operator that projects the RETURNING expressions.
Expand Down
Loading

0 comments on commit b375900

Please sign in to comment.