Skip to content

Commit

Permalink
sqlbase: add support for ON DELETE/UPDATE SET DEFAULT actions for FK …
Browse files Browse the repository at this point in the history
…references

This change follows the two that added ON DELETE SET NULL and ON UPDATE SET
NULL and reuses the same code paths in the cascader. In this case, instead of
creating a collection of NULLs for Column IDs, the default values are evaluated
and use when creating the updated row.

Again, most of the work was in adding in new tests to ensure that all strange
edge cases were covered.

The addition of SET NULL added the complication of possibly cascading a NULL
value into NOT NULL column. For the addition of SET DEFAULT, a new complication
arose, it is now possible to inadvertently break a UNIQUE constraint. Luckily,
this was being handled correctly and failing the transaction's run batch, but
the errors were left unconverted so by adding in the ConvertBatchError this
edge case was correctly handled.

Similar to with SET NULL, a restriction had to be added to prevent adding a SET
DEFAULT action to a column that doesn't have a default value. While there I
also updated the error produced by the similar SET NULL on a NOT NULL column's
error message to use the tree.ErrString.

Release note (sql change): ON DELETE SET DEFAULT and ON UPDATE SET DEFAULT
foreign key constraints actions are now fully supported
  • Loading branch information
BramGruneir committed Jan 30, 2018
1 parent 677d610 commit 657b7a3
Show file tree
Hide file tree
Showing 4 changed files with 1,327 additions and 68 deletions.
46 changes: 35 additions & 11 deletions pkg/sql/create_table.go
Expand Up @@ -469,15 +469,6 @@ func resolveFK(
}
}

if d.Actions.Delete == tree.SetDefault {
feature := fmt.Sprintf("unsupported: ON DELETE %s", d.Actions.Delete)
return pgerror.Unimplemented(feature, feature)
}
if d.Actions.Update == tree.SetDefault {
feature := fmt.Sprintf("unsupported: ON UPDATE %s", d.Actions.Update)
return pgerror.Unimplemented(feature, feature)
}

// Don't add a cascading action if there is a CHECK on any column.
// See #21688
if len(tbl.Checks) > 0 &&
Expand Down Expand Up @@ -519,9 +510,42 @@ func resolveFK(
if d.Actions.Delete == tree.SetNull || d.Actions.Update == tree.SetNull {
for _, sourceColumn := range srcCols {
if !sourceColumn.Nullable {
database, err := sqlbase.GetDatabaseDescFromID(ctx, txn, tbl.ParentID)
if err != nil {
return err
}
return pgerror.NewErrorf(pgerror.CodeInvalidForeignKeyError,
"cannot add a SET NULL cascading action on column %q which has a NOT NULL constraint",
tree.ErrString(&tree.ColumnItem{
TableName: tree.TableName{
DatabaseName: tree.Name(database.Name),
TableName: tree.Name(tbl.Name),
},
ColumnName: tree.Name(sourceColumn.Name),
}),
)
}
}
}

// Don't add a SET DEFAULT action on an index that has any column that does
// not have a DEFAULT expression.
if d.Actions.Delete == tree.SetDefault || d.Actions.Update == tree.SetDefault {
for _, sourceColumn := range srcCols {
if sourceColumn.DefaultExpr == nil {
database, err := sqlbase.GetDatabaseDescFromID(ctx, txn, tbl.ParentID)
if err != nil {
return err
}
return pgerror.NewErrorf(pgerror.CodeInvalidForeignKeyError,
"cannot add a SET NULL cascading action on column \"%s\" which has a NOT NULL constraint",
sourceColumn.Name,
"cannot add a SET DEFAULT cascading action on column %q which has no DEFAULT expression",
tree.ErrString(&tree.ColumnItem{
TableName: tree.TableName{
DatabaseName: tree.Name(database.Name),
TableName: tree.Name(tbl.Name),
},
ColumnName: tree.Name(sourceColumn.Name),
}),
)
}
}
Expand Down

0 comments on commit 657b7a3

Please sign in to comment.