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.

address PR comments, update a few tests and comments
  • Loading branch information
Tyler314 committed Jul 11, 2019
1 parent bb0ef6b commit 6075476
Show file tree
Hide file tree
Showing 9 changed files with 634 additions and 53 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 6075476

Please sign in to comment.