Skip to content

Commit

Permalink
sql: add support for NOT VALID Foreign Key constraints
Browse files Browse the repository at this point in the history
Implemented the support of not validating an ALTER TABLE foreign key
constraint to a table. Within alter_table.go we set the validity of
the foreign key reference to be unvalidated, and within shema_changer.go
we then check the case where the foreign key being added is unvalidated
and do not check its validity.

This allows users to create a FK reference between two tables whose rows
don't comply with the constraint when it's first added.

Release note (sql change): Support NOT VALID for Foreign Key constraints.

Update logic tests to more thoroughly test the added support of NOT VALID.
  • Loading branch information
Tyler314 committed Jul 10, 2019
1 parent d9f6f15 commit 81f1036
Show file tree
Hide file tree
Showing 9 changed files with 637 additions and 42 deletions.
2 changes: 1 addition & 1 deletion pkg/ccl/importccl/read_import_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ type delayedFK struct {
func addDelayedFKs(ctx context.Context, defs []delayedFK, resolver fkResolver) error {
for _, def := range defs {
if err := sql.ResolveFK(
ctx, nil, resolver, def.tbl, def.def, map[sqlbase.ID]*sqlbase.MutableTableDescriptor{}, sql.NewTable,
ctx, nil, resolver, def.tbl, def.def, map[sqlbase.ID]*sqlbase.MutableTableDescriptor{}, sql.NewTable, tree.ValidationDefault,
); err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/ccl/importccl/read_import_pgdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func readPostgresCreateTable(
continue
}
for _, constraint := range constraints {
if err := sql.ResolveFK(evalCtx.Ctx(), nil /* txn */, fks.resolver, desc, constraint, backrefs, sql.NewTable); err != nil {
if err := sql.ResolveFK(evalCtx.Ctx(), nil /* txn */, fks.resolver, desc, constraint, backrefs, sql.NewTable, tree.ValidationDefault); err != nil {
return nil, err
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/alter_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ func (n *alterTableNode) startExec(params runParams) error {
} else {
tableState = NonEmptyTable
}
err = params.p.resolveFK(params.ctx, n.tableDesc, d, affected, tableState)
err = params.p.resolveFK(params.ctx, n.tableDesc, d, affected, tableState, t.ValidationBehavior)
})
if err != nil {
return err
Expand Down
20 changes: 15 additions & 5 deletions pkg/sql/create_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,9 @@ func (p *planner) resolveFK(
d *tree.ForeignKeyConstraintTableDef,
backrefs map[sqlbase.ID]*sqlbase.MutableTableDescriptor,
ts FKTableState,
validationBehavior tree.ValidationBehavior,
) error {
return ResolveFK(ctx, p.txn, p, tbl, d, backrefs, ts)
return ResolveFK(ctx, p.txn, p, tbl, d, backrefs, ts, validationBehavior)
}

func qualifyFKColErrorWithDB(
Expand Down Expand Up @@ -421,8 +422,8 @@ const (
// It may, in doing so, add to or alter descriptors in the passed in `backrefs`
// map of other tables that need to be updated when this table is created.
// Constraints that are not known to hold for existing data are created
// "unvalidated", but when table is empty (e.g. during creation), no existing
// data imples no existing violations, and thus the constraint can be created
// "unvalidated", but when table is empty (e.g. during creating on), no existing
// data implies no existing violations, and thus the constraint can be created
// without the unvalidated flag.
//
// The caller should pass an instance of fkSelfResolver as
Expand All @@ -437,6 +438,10 @@ const (
//
// The passed Txn is used to lookup databases to qualify names in error messages
// but if nil, will result in unqualified names in those errors.
//
// The passed validationBehavior is used to determine whether or not preexisting
// entries in the table need to be validated against the foreign key being added.
// This only applies for existing tables, not new tables.
func ResolveFK(
ctx context.Context,
txn *client.Txn,
Expand All @@ -445,6 +450,7 @@ func ResolveFK(
d *tree.ForeignKeyConstraintTableDef,
backrefs map[sqlbase.ID]*sqlbase.MutableTableDescriptor,
ts FKTableState,
validationBehavior tree.ValidationBehavior,
) error {
for _, col := range d.FromCols {
col, _, err := tbl.FindColumnByName(col)
Expand Down Expand Up @@ -586,7 +592,11 @@ func ResolveFK(
}

if ts != NewTable {
ref.Validity = sqlbase.ConstraintValidity_Validating
if validationBehavior == tree.ValidationSkip {
ref.Validity = sqlbase.ConstraintValidity_Unvalidated
} else {
ref.Validity = sqlbase.ConstraintValidity_Validating
}
}
backref := sqlbase.ForeignKeyReference{Table: tbl.ID}

Expand Down Expand Up @@ -1282,7 +1292,7 @@ func MakeTableDesc(
desc.Checks = append(desc.Checks, ck)

case *tree.ForeignKeyConstraintTableDef:
if err := ResolveFK(ctx, txn, fkResolver, &desc, d, affected, NewTable); err != nil {
if err := ResolveFK(ctx, txn, fkResolver, &desc, d, affected, NewTable, tree.ValidationDefault); err != nil {
return desc, err
}

Expand Down
Loading

0 comments on commit 81f1036

Please sign in to comment.