From b9a0471e49c4c0c57d8f2fa6e72306921d0ac55d Mon Sep 17 00:00:00 2001 From: Xiang Gu Date: Wed, 1 Feb 2023 17:17:46 -0500 Subject: [PATCH] schemachanger: Implement `ADD FOREIGN KEY NOT VALID` and add tests --- .../logictest/testdata/logic_test/drop_table | 9 +++ .../internal/scbuildstmt/alter_table.go | 5 +- .../scbuildstmt/alter_table_add_constraint.go | 47 +++++++++------ .../testdata/alter_table_add_foreign_key | 12 ++++ .../alter_table_add_foreign_key_unvalidated | 12 ++++ pkg/sql/schemachanger/scdecomp/decomp.go | 34 +++++++---- pkg/sql/schemachanger/scdecomp/testdata/table | 18 ++++++ .../scexec/scmutationexec/constraint.go | 26 ++++++--- .../scexec/scmutationexec/references.go | 26 ++++++++- .../schemachanger/scop/immediate_mutation.go | 7 ++- .../immediate_mutation_visitor_generated.go | 6 +- pkg/sql/schemachanger/scpb/elements.proto | 12 ++++ .../schemachanger/scpb/elements_generated.go | 31 ++++++++++ pkg/sql/schemachanger/scpb/uml/table.puml | 12 ++++ .../scplan/internal/opgen/BUILD.bazel | 1 + .../opgen/opgen_foreign_key_constraint.go | 6 +- ...pgen_foreign_key_constraint_unvalidated.go | 58 +++++++++++++++++++ .../scplan/internal/rules/current/helpers.go | 6 +- .../internal/rules/current/testdata/deprules | 18 +++--- pkg/sql/schemachanger/screl/attr.go | 5 ++ pkg/sql/schemachanger/screl/scalars.go | 2 +- .../explain/alter_table_add_foreign_key | 4 +- .../alter_table_add_unique_without_index | 4 +- .../alter_table_add_foreign_key | 6 +- .../alter_table_add_unique_without_index | 6 +- 25 files changed, 305 insertions(+), 68 deletions(-) create mode 100644 pkg/sql/schemachanger/scbuild/testdata/alter_table_add_foreign_key create mode 100644 pkg/sql/schemachanger/scbuild/testdata/alter_table_add_foreign_key_unvalidated create mode 100644 pkg/sql/schemachanger/scplan/internal/opgen/opgen_foreign_key_constraint_unvalidated.go diff --git a/pkg/sql/logictest/testdata/logic_test/drop_table b/pkg/sql/logictest/testdata/logic_test/drop_table index f2b4c3e20366..d1a43b524334 100644 --- a/pkg/sql/logictest/testdata/logic_test/drop_table +++ b/pkg/sql/logictest/testdata/logic_test/drop_table @@ -120,6 +120,9 @@ subtest drop_table_with_not_valid_constraints statement ok CREATE TABLE t_with_not_valid_constraints_1 (i INT PRIMARY KEY, j INT); +statement ok +CREATE TABLE t_with_not_valid_constraints_2 (i INT PRIMARY KEY, j INT); + statement ok ALTER TABLE t_with_not_valid_constraints_1 ADD CHECK (i > 0) NOT VALID; @@ -127,5 +130,11 @@ statement ok SET experimental_enable_unique_without_index_constraints = true; ALTER TABLE t_with_not_valid_constraints_1 ADD UNIQUE WITHOUT INDEX (j) NOT VALID; +statement ok +ALTER TABLE t_with_not_valid_constraints_1 ADD FOREIGN KEY (i) REFERENCES t_with_not_valid_constraints_2 (i) NOT VALID; + statement ok DROP TABLE t_with_not_valid_constraints_1; + +statement ok +DROP TABLE t_with_not_valid_constraints_2; diff --git a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table.go b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table.go index 63aaffd5d97c..8dd1b4726c91 100644 --- a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table.go +++ b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table.go @@ -65,8 +65,8 @@ var supportedAlterTableStatements = map[reflect.Type]supportedAlterTableCommand{ return true } - // Support ALTER TABLE ... ADD CONSTRAINT FOREIGN KEY - if _, ok := t.ConstraintDef.(*tree.ForeignKeyConstraintTableDef); ok && t.ValidationBehavior == tree.ValidationDefault { + // Support ALTER TABLE ... ADD CONSTRAINT FOREIGN KEY [NOT VALID] + if _, ok := t.ConstraintDef.(*tree.ForeignKeyConstraintTableDef); ok { return true } @@ -86,6 +86,7 @@ var alterTableAddConstraintMinSupportedClusterVersion = map[string]clusterversio "ADD_UNIQUE_WITHOUT_INDEX_DEFAULT": clusterversion.V23_1Start, "ADD_CHECK_SKIP": clusterversion.V23_1, "ADD_UNIQUE_WITHOUT_INDEX_SKIP": clusterversion.V23_1, + "ADD_FOREIGN_KEY_SKIP": clusterversion.V23_1, } func init() { diff --git a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_constraint.go b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_constraint.go index faf567c38a29..e139d46b85ed 100644 --- a/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_constraint.go +++ b/pkg/sql/schemachanger/scbuild/internal/scbuildstmt/alter_table_add_constraint.go @@ -47,9 +47,7 @@ func alterTableAddConstraint( case *tree.CheckConstraintTableDef: alterTableAddCheck(b, tn, tbl, t) case *tree.ForeignKeyConstraintTableDef: - if t.ValidationBehavior == tree.ValidationDefault { - alterTableAddForeignKey(b, tn, tbl, t) - } + alterTableAddForeignKey(b, tn, tbl, t) } } @@ -173,7 +171,7 @@ func getIndexIDForValidationForConstraint(b BuildCtx, tableID catid.DescID) (ret } // alterTableAddForeignKey contains logic for building -// `ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY`. +// `ALTER TABLE ... ADD FOREIGN KEY ... [NOT VALID]`. // It assumes `t` is such a command. func alterTableAddForeignKey( b BuildCtx, tn *tree.TableName, tbl *scpb.Table, t *tree.AlterTableAddConstraint, @@ -369,19 +367,34 @@ func alterTableAddForeignKey( // 12. (Finally!) Add a ForeignKey_Constraint, ConstraintName element to // builder state. constraintID := b.NextTableConstraintID(tbl.TableID) - fk := &scpb.ForeignKeyConstraint{ - TableID: tbl.TableID, - ConstraintID: constraintID, - ColumnIDs: originColIDs, - ReferencedTableID: referencedTableID, - ReferencedColumnIDs: referencedColIDs, - OnUpdateAction: tree.ForeignKeyReferenceActionValue[fkDef.Actions.Update], - OnDeleteAction: tree.ForeignKeyReferenceActionValue[fkDef.Actions.Delete], - CompositeKeyMatchMethod: tree.CompositeKeyMatchMethodValue[fkDef.Match], - IndexIDForValidation: getIndexIDForValidationForConstraint(b, tbl.TableID), - } - b.Add(fk) - b.LogEventForExistingTarget(fk) + if t.ValidationBehavior == tree.ValidationDefault { + fk := &scpb.ForeignKeyConstraint{ + TableID: tbl.TableID, + ConstraintID: constraintID, + ColumnIDs: originColIDs, + ReferencedTableID: referencedTableID, + ReferencedColumnIDs: referencedColIDs, + OnUpdateAction: tree.ForeignKeyReferenceActionValue[fkDef.Actions.Update], + OnDeleteAction: tree.ForeignKeyReferenceActionValue[fkDef.Actions.Delete], + CompositeKeyMatchMethod: tree.CompositeKeyMatchMethodValue[fkDef.Match], + IndexIDForValidation: getIndexIDForValidationForConstraint(b, tbl.TableID), + } + b.Add(fk) + b.LogEventForExistingTarget(fk) + } else { + fk := &scpb.ForeignKeyConstraintUnvalidated{ + TableID: tbl.TableID, + ConstraintID: constraintID, + ColumnIDs: originColIDs, + ReferencedTableID: referencedTableID, + ReferencedColumnIDs: referencedColIDs, + OnUpdateAction: tree.ForeignKeyReferenceActionValue[fkDef.Actions.Update], + OnDeleteAction: tree.ForeignKeyReferenceActionValue[fkDef.Actions.Delete], + CompositeKeyMatchMethod: tree.CompositeKeyMatchMethodValue[fkDef.Match], + } + b.Add(fk) + b.LogEventForExistingTarget(fk) + } b.Add(&scpb.ConstraintWithoutIndexName{ TableID: tbl.TableID, ConstraintID: constraintID, diff --git a/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_foreign_key b/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_foreign_key new file mode 100644 index 000000000000..aa77fd74a243 --- /dev/null +++ b/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_foreign_key @@ -0,0 +1,12 @@ +setup +CREATE TABLE t2 (i INT PRIMARY KEY); +CREATE TABLE t1 (i INT PRIMARY KEY); +---- + +build +ALTER TABLE t1 ADD FOREIGN KEY (i) REFERENCES t2(i); +---- +- [[ForeignKeyConstraint:{DescID: 105, IndexID: 0, ConstraintID: 2, ReferencedDescID: 104}, PUBLIC], ABSENT] + {columnIds: [1], constraintId: 2, referencedColumnIds: [1], referencedTableId: 104, tableId: 105} +- [[ConstraintWithoutIndexName:{DescID: 105, Name: t1_i_fkey, ConstraintID: 2}, PUBLIC], ABSENT] + {constraintId: 2, name: t1_i_fkey, tableId: 105} diff --git a/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_foreign_key_unvalidated b/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_foreign_key_unvalidated new file mode 100644 index 000000000000..27171d7ef64a --- /dev/null +++ b/pkg/sql/schemachanger/scbuild/testdata/alter_table_add_foreign_key_unvalidated @@ -0,0 +1,12 @@ +setup +CREATE TABLE t2 (i INT PRIMARY KEY); +CREATE TABLE t1 (i INT PRIMARY KEY); +---- + +build +ALTER TABLE t1 ADD FOREIGN KEY (i) REFERENCES t2(i) NOT VALID; +---- +- [[ForeignKeyConstraintUnvalidated:{DescID: 105, ConstraintID: 2, ReferencedDescID: 104}, PUBLIC], ABSENT] + {columnIds: [1], constraintId: 2, referencedColumnIds: [1], referencedTableId: 104, tableId: 105} +- [[ConstraintWithoutIndexName:{DescID: 105, Name: t1_i_fkey, ConstraintID: 2}, PUBLIC], ABSENT] + {constraintId: 2, name: t1_i_fkey, tableId: 105} diff --git a/pkg/sql/schemachanger/scdecomp/decomp.go b/pkg/sql/schemachanger/scdecomp/decomp.go index a876d607f502..c49519af15f7 100644 --- a/pkg/sql/schemachanger/scdecomp/decomp.go +++ b/pkg/sql/schemachanger/scdecomp/decomp.go @@ -680,17 +680,29 @@ func (w *walkCtx) walkCheckConstraint(tbl catalog.TableDescriptor, c catalog.Che func (w *walkCtx) walkForeignKeyConstraint( tbl catalog.TableDescriptor, c catalog.ForeignKeyConstraint, ) { - // TODO(postamar): proper handling of constraint status - w.ev(scpb.Status_PUBLIC, &scpb.ForeignKeyConstraint{ - TableID: tbl.GetID(), - ConstraintID: c.GetConstraintID(), - ColumnIDs: c.ForeignKeyDesc().OriginColumnIDs, - ReferencedTableID: c.GetReferencedTableID(), - ReferencedColumnIDs: c.ForeignKeyDesc().ReferencedColumnIDs, - OnUpdateAction: c.OnUpdate(), - OnDeleteAction: c.OnDelete(), - CompositeKeyMatchMethod: c.Match(), - }) + if c.IsConstraintUnvalidated() && w.clusterVersion.IsActive(clusterversion.V23_1) { + w.ev(scpb.Status_PUBLIC, &scpb.ForeignKeyConstraintUnvalidated{ + TableID: tbl.GetID(), + ConstraintID: c.GetConstraintID(), + ColumnIDs: c.ForeignKeyDesc().OriginColumnIDs, + ReferencedTableID: c.GetReferencedTableID(), + ReferencedColumnIDs: c.ForeignKeyDesc().ReferencedColumnIDs, + OnUpdateAction: c.OnUpdate(), + OnDeleteAction: c.OnDelete(), + CompositeKeyMatchMethod: c.Match(), + }) + } else { + w.ev(scpb.Status_PUBLIC, &scpb.ForeignKeyConstraint{ + TableID: tbl.GetID(), + ConstraintID: c.GetConstraintID(), + ColumnIDs: c.ForeignKeyDesc().OriginColumnIDs, + ReferencedTableID: c.GetReferencedTableID(), + ReferencedColumnIDs: c.ForeignKeyDesc().ReferencedColumnIDs, + OnUpdateAction: c.OnUpdate(), + OnDeleteAction: c.OnDelete(), + CompositeKeyMatchMethod: c.Match(), + }) + } w.ev(scpb.Status_PUBLIC, &scpb.ConstraintWithoutIndexName{ TableID: tbl.GetID(), ConstraintID: c.GetConstraintID(), diff --git a/pkg/sql/schemachanger/scdecomp/testdata/table b/pkg/sql/schemachanger/scdecomp/testdata/table index 18be84bb527d..d811c9994f6d 100644 --- a/pkg/sql/schemachanger/scdecomp/testdata/table +++ b/pkg/sql/schemachanger/scdecomp/testdata/table @@ -18,6 +18,7 @@ COMMENT ON CONSTRAINT tbl_pkey ON tbl IS 'primary key'; ALTER TABLE tbl ADD CONSTRAINT mycheck2 CHECK (id < 10) NOT VALID; ALTER TABLE tbl ADD CONSTRAINT myuwi1 UNIQUE WITHOUT INDEX (price); ALTER TABLE tbl ADD CONSTRAINT myuwi2 UNIQUE WITHOUT INDEX (price) NOT VALID; +ALTER TABLE tbl ADD CONSTRAINT myfk2 FOREIGN KEY (id) REFERENCES parent (id) NOT VALID; ---- decompose @@ -374,6 +375,18 @@ ElementState: referencedTableId: 104 tableId: 105 Status: PUBLIC +- ForeignKeyConstraintUnvalidated: + columnIds: + - 1 + compositeKeyMatchMethod: SIMPLE + constraintId: 7 + onDeleteAction: NO_ACTION + onUpdateAction: NO_ACTION + referencedColumnIds: + - 1 + referencedTableId: 104 + tableId: 105 + Status: PUBLIC - TableComment: comment: tbl is good table tableId: 105 @@ -678,6 +691,11 @@ ElementState: name: myuwi2 tableId: 105 Status: PUBLIC +- ConstraintWithoutIndexName: + constraintId: 7 + name: myfk2 + tableId: 105 + Status: PUBLIC - ConstraintComment: comment: must have a parent constraintId: 2 diff --git a/pkg/sql/schemachanger/scexec/scmutationexec/constraint.go b/pkg/sql/schemachanger/scexec/scmutationexec/constraint.go index 0eb1c236f414..7e7d01b1cbbf 100644 --- a/pkg/sql/schemachanger/scexec/scmutationexec/constraint.go +++ b/pkg/sql/schemachanger/scexec/scmutationexec/constraint.go @@ -367,8 +367,8 @@ func (i *immediateVisitor) RemoveUniqueWithoutIndexConstraint( return nil } -func (i *immediateVisitor) MakeAbsentForeignKeyConstraintWriteOnly( - ctx context.Context, op scop.MakeAbsentForeignKeyConstraintWriteOnly, +func (i *immediateVisitor) AddForeignKeyConstraint( + ctx context.Context, op scop.AddForeignKeyConstraint, ) error { out, err := i.checkOutTable(ctx, op.TableID) if err != nil || out.Dropped() { @@ -378,21 +378,27 @@ func (i *immediateVisitor) MakeAbsentForeignKeyConstraintWriteOnly( out.NextConstraintID = op.ConstraintID + 1 } - // Enqueue a mutation in `out` to signal this mutation is now enforced. fk := &descpb.ForeignKeyConstraint{ OriginTableID: op.TableID, OriginColumnIDs: op.ColumnIDs, ReferencedColumnIDs: op.ReferencedColumnIDs, ReferencedTableID: op.ReferencedTableID, Name: tabledesc.ConstraintNamePlaceholder(op.ConstraintID), - Validity: descpb.ConstraintValidity_Validating, + Validity: op.Validity, OnDelete: op.OnDeleteAction, OnUpdate: op.OnUpdateAction, Match: op.CompositeKeyMatchMethod, ConstraintID: op.ConstraintID, } - enqueueNonIndexMutation(out, out.AddForeignKeyMutation, fk, descpb.DescriptorMutation_ADD) - out.Mutations[len(out.Mutations)-1].State = descpb.DescriptorMutation_WRITE_ONLY + if op.Validity == descpb.ConstraintValidity_Unvalidated { + // Unvalidated constraint doesn't need to transition through an intermediate + // state, so we don't enqueue a mutation for it. + out.OutboundFKs = append(out.OutboundFKs, *fk) + } else { + enqueueNonIndexMutation(out, out.AddForeignKeyMutation, fk, descpb.DescriptorMutation_ADD) + out.Mutations[len(out.Mutations)-1].State = descpb.DescriptorMutation_WRITE_ONLY + } + // Add an entry in "InboundFKs" in the referenced table as company. in, err := i.checkOutTable(ctx, op.ReferencedTableID) if err != nil { @@ -454,14 +460,16 @@ func (i *immediateVisitor) MakePublicForeignKeyConstraintValidated( ctx context.Context, op scop.MakePublicForeignKeyConstraintValidated, ) error { // A helper function to update the inbound FK in referenced table to DROPPING. - updateInboundFKAsDropping := func(referencedTableID descpb.ID) error { + // Note that the constraintID in the referencedTable might not match that in + // in the referencing table, so we pass in the FK name as identifier. + updateInboundFKAsDropping := func(referencedTableID descpb.ID, fkName string) error { foundInReferencedTable := false in, err := i.checkOutTable(ctx, referencedTableID) if err != nil { return err } for idx, inboundFk := range in.InboundFKs { - if inboundFk.OriginTableID == op.TableID && inboundFk.ConstraintID == op.ConstraintID { + if inboundFk.OriginTableID == op.TableID && inboundFk.Name == fkName { in.InboundFKs[idx].Validity = descpb.ConstraintValidity_Dropping foundInReferencedTable = true break @@ -486,7 +494,7 @@ func (i *immediateVisitor) MakePublicForeignKeyConstraintValidated( } fk.Validity = descpb.ConstraintValidity_Dropping enqueueNonIndexMutation(out, out.AddForeignKeyMutation, &fk, descpb.DescriptorMutation_DROP) - return updateInboundFKAsDropping(fk.ReferencedTableID) + return updateInboundFKAsDropping(fk.ReferencedTableID, fk.Name) } } diff --git a/pkg/sql/schemachanger/scexec/scmutationexec/references.go b/pkg/sql/schemachanger/scexec/scmutationexec/references.go index 9fff00e5d619..86ad14bb3b3d 100644 --- a/pkg/sql/schemachanger/scexec/scmutationexec/references.go +++ b/pkg/sql/schemachanger/scexec/scmutationexec/references.go @@ -20,6 +20,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/sem/catid" "github.com/cockroachdb/cockroach/pkg/sql/types" "github.com/cockroachdb/cockroach/pkg/util/iterutil" + "github.com/cockroachdb/errors" ) func (i *immediateVisitor) RemoveSchemaParent( @@ -73,6 +74,29 @@ func (i *immediateVisitor) RemoveForeignKeyBackReference( // Exit early if the foreign key back-reference holder is getting dropped. return err } + // Retrieve foreign key name in origin table to identify it in the referenced + // table. + var name string + { + out, err := i.getDescriptor(ctx, op.OriginTableID) + if err != nil { + return err + } + tbl, err := catalog.AsTableDescriptor(out) + if err != nil { + return err + } + for _, fk := range tbl.OutboundForeignKeys() { + if fk.GetConstraintID() == op.OriginConstraintID { + name = fk.GetName() + break + } + } + if name == "" { + return errors.AssertionFailedf("foreign key with ID %d not found in origin table %q (%d)", + op.OriginConstraintID, out.GetName(), out.GetID()) + } + } // Attempt to remove back reference. // Note how we // 1. only check to remove from `in.InboundFKs` but not from `in.Mutations`: @@ -83,7 +107,7 @@ func (i *immediateVisitor) RemoveForeignKeyBackReference( // this is because if we roll back before the adding FK is published in `out`, // such a back-reference won't exist in `in` yet. for idx, fk := range in.InboundFKs { - if fk.OriginTableID == op.OriginTableID && fk.ConstraintID == op.OriginConstraintID { + if fk.OriginTableID == op.OriginTableID && fk.Name == name { in.InboundFKs = append(in.InboundFKs[:idx], in.InboundFKs[idx+1:]...) if len(in.InboundFKs) == 0 { in.InboundFKs = nil diff --git a/pkg/sql/schemachanger/scop/immediate_mutation.go b/pkg/sql/schemachanger/scop/immediate_mutation.go index 429d09ed413e..32952992e494 100644 --- a/pkg/sql/schemachanger/scop/immediate_mutation.go +++ b/pkg/sql/schemachanger/scop/immediate_mutation.go @@ -318,9 +318,9 @@ type MakeValidatedColumnNotNullPublic struct { ColumnID descpb.ColumnID } -// MakeAbsentForeignKeyConstraintWriteOnly adds a non-existent foreign key -// constraint to the table in the WRITE_ONLY state. -type MakeAbsentForeignKeyConstraintWriteOnly struct { +// AddForeignKeyConstraint adds a non-existent foreign key +// constraint to the table. +type AddForeignKeyConstraint struct { immediateMutationOp TableID descpb.ID ConstraintID descpb.ConstraintID @@ -330,6 +330,7 @@ type MakeAbsentForeignKeyConstraintWriteOnly struct { OnUpdateAction semenumpb.ForeignKeyAction OnDeleteAction semenumpb.ForeignKeyAction CompositeKeyMatchMethod semenumpb.Match + Validity descpb.ConstraintValidity } // MakeValidatedForeignKeyConstraintPublic moves a new, validated foreign key diff --git a/pkg/sql/schemachanger/scop/immediate_mutation_visitor_generated.go b/pkg/sql/schemachanger/scop/immediate_mutation_visitor_generated.go index 563b09718047..15dfa957054e 100644 --- a/pkg/sql/schemachanger/scop/immediate_mutation_visitor_generated.go +++ b/pkg/sql/schemachanger/scop/immediate_mutation_visitor_generated.go @@ -59,7 +59,7 @@ type ImmediateMutationVisitor interface { MakePublicColumnNotNullValidated(context.Context, MakePublicColumnNotNullValidated) error MakeValidatedCheckConstraintPublic(context.Context, MakeValidatedCheckConstraintPublic) error MakeValidatedColumnNotNullPublic(context.Context, MakeValidatedColumnNotNullPublic) error - MakeAbsentForeignKeyConstraintWriteOnly(context.Context, MakeAbsentForeignKeyConstraintWriteOnly) error + AddForeignKeyConstraint(context.Context, AddForeignKeyConstraint) error MakeValidatedForeignKeyConstraintPublic(context.Context, MakeValidatedForeignKeyConstraintPublic) error MakePublicForeignKeyConstraintValidated(context.Context, MakePublicForeignKeyConstraintValidated) error RemoveForeignKeyConstraint(context.Context, RemoveForeignKeyConstraint) error @@ -302,8 +302,8 @@ func (op MakeValidatedColumnNotNullPublic) Visit(ctx context.Context, v Immediat } // Visit is part of the ImmediateMutationOp interface. -func (op MakeAbsentForeignKeyConstraintWriteOnly) Visit(ctx context.Context, v ImmediateMutationVisitor) error { - return v.MakeAbsentForeignKeyConstraintWriteOnly(ctx, op) +func (op AddForeignKeyConstraint) Visit(ctx context.Context, v ImmediateMutationVisitor) error { + return v.AddForeignKeyConstraint(ctx, op) } // Visit is part of the ImmediateMutationOp interface. diff --git a/pkg/sql/schemachanger/scpb/elements.proto b/pkg/sql/schemachanger/scpb/elements.proto index 534382c0c283..21b180a43af5 100644 --- a/pkg/sql/schemachanger/scpb/elements.proto +++ b/pkg/sql/schemachanger/scpb/elements.proto @@ -75,6 +75,7 @@ message ElementProto { CheckConstraint check_constraint = 26 [(gogoproto.moretags) = "parent:\"Table\""]; CheckConstraintUnvalidated check_constraint_unvalidated = 170 [(gogoproto.moretags) = "parent:\"Table\""]; ForeignKeyConstraint foreign_key_constraint = 27 [(gogoproto.moretags) = "parent:\"Table\""]; + ForeignKeyConstraintUnvalidated foreign_key_constraint_not_valid = 172 [(gogoproto.moretags) = "parent:\"Table\""]; TableComment table_comment = 28 [(gogoproto.moretags) = "parent:\"Table, View, Sequence\""]; RowLevelTTL row_level_ttl = 29 [(gogoproto.customname) = "RowLevelTTL", (gogoproto.moretags) = "parent:\"Table\""]; TableZoneConfig table_zone_config = 121 [(gogoproto.moretags) = "parent:\"Table, View\""]; @@ -398,6 +399,17 @@ message ForeignKeyConstraint { uint32 index_id_for_validation = 9 [(gogoproto.customname) = "IndexIDForValidation", (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/sql/sem/catid.IndexID"]; } +message ForeignKeyConstraintUnvalidated { + uint32 table_id = 1 [(gogoproto.customname) = "TableID", (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/sql/sem/catid.DescID"]; + uint32 constraint_id = 2 [(gogoproto.customname) = "ConstraintID", (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/sql/sem/catid.ConstraintID"]; + repeated uint32 column_ids = 3 [(gogoproto.customname) = "ColumnIDs", (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/sql/sem/catid.ColumnID"]; + uint32 referenced_table_id = 4 [(gogoproto.customname) = "ReferencedTableID", (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/sql/sem/catid.DescID"]; + repeated uint32 referenced_column_ids = 5 [(gogoproto.customname) = "ReferencedColumnIDs", (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/sql/sem/catid.ColumnID"]; + cockroach.sql.sem.semenumpb.ForeignKeyAction on_update_action = 6 [(gogoproto.customname) = "OnUpdateAction"]; + cockroach.sql.sem.semenumpb.ForeignKeyAction on_delete_action = 7 [(gogoproto.customname) = "OnDeleteAction"]; + cockroach.sql.sem.semenumpb.Match composite_key_match_method = 8 [(gogoproto.customname) = "CompositeKeyMatchMethod"]; +} + message EnumType { uint32 type_id = 1 [(gogoproto.customname) = "TypeID", (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/sql/sem/catid.DescID"]; uint32 array_type_id = 2 [(gogoproto.customname) = "ArrayTypeID", (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/sql/sem/catid.DescID"]; diff --git a/pkg/sql/schemachanger/scpb/elements_generated.go b/pkg/sql/schemachanger/scpb/elements_generated.go index 1d14a6d75368..e3f66c53e8f1 100644 --- a/pkg/sql/schemachanger/scpb/elements_generated.go +++ b/pkg/sql/schemachanger/scpb/elements_generated.go @@ -761,6 +761,37 @@ func FindForeignKeyConstraint(b ElementStatusIterator) (current Status, target T return current, target, element } +func (e ForeignKeyConstraintUnvalidated) element() {} + +// ForEachForeignKeyConstraintUnvalidated iterates over elements of type ForeignKeyConstraintUnvalidated. +func ForEachForeignKeyConstraintUnvalidated( + b ElementStatusIterator, fn func(current Status, target TargetStatus, e *ForeignKeyConstraintUnvalidated), +) { + if b == nil { + return + } + b.ForEachElementStatus(func(current Status, target TargetStatus, e Element) { + if elt, ok := e.(*ForeignKeyConstraintUnvalidated); ok { + fn(current, target, elt) + } + }) +} + +// FindForeignKeyConstraintUnvalidated finds the first element of type ForeignKeyConstraintUnvalidated. +func FindForeignKeyConstraintUnvalidated(b ElementStatusIterator) (current Status, target TargetStatus, element *ForeignKeyConstraintUnvalidated) { + if b == nil { + return current, target, element + } + b.ForEachElementStatus(func(c Status, t TargetStatus, e Element) { + if elt, ok := e.(*ForeignKeyConstraintUnvalidated); ok { + element = elt + current = c + target = t + } + }) + return current, target, element +} + func (e Function) element() {} // ForEachFunction iterates over elements of type Function. diff --git a/pkg/sql/schemachanger/scpb/uml/table.puml b/pkg/sql/schemachanger/scpb/uml/table.puml index 001290e00a04..9e98c7c7acc2 100644 --- a/pkg/sql/schemachanger/scpb/uml/table.puml +++ b/pkg/sql/schemachanger/scpb/uml/table.puml @@ -125,6 +125,17 @@ ForeignKeyConstraint : OnDeleteAction ForeignKeyConstraint : CompositeKeyMatchMethod ForeignKeyConstraint : IndexIDForValidation +object ForeignKeyConstraintUnvalidated + +ForeignKeyConstraintUnvalidated : TableID +ForeignKeyConstraintUnvalidated : ConstraintID +ForeignKeyConstraintUnvalidated : []ColumnIDs +ForeignKeyConstraintUnvalidated : ReferencedTableID +ForeignKeyConstraintUnvalidated : []ReferencedColumnIDs +ForeignKeyConstraintUnvalidated : OnUpdateAction +ForeignKeyConstraintUnvalidated : OnDeleteAction +ForeignKeyConstraintUnvalidated : CompositeKeyMatchMethod + object TableComment TableComment : TableID @@ -386,6 +397,7 @@ Table <|-- UniqueWithoutIndexConstraintUnvalidated Table <|-- CheckConstraint Table <|-- CheckConstraintUnvalidated Table <|-- ForeignKeyConstraint +Table <|-- ForeignKeyConstraintUnvalidated Table <|-- TableComment View <|-- TableComment Sequence <|-- TableComment diff --git a/pkg/sql/schemachanger/scplan/internal/opgen/BUILD.bazel b/pkg/sql/schemachanger/scplan/internal/opgen/BUILD.bazel index daae91b2e229..bd7ba8d0a1a8 100644 --- a/pkg/sql/schemachanger/scplan/internal/opgen/BUILD.bazel +++ b/pkg/sql/schemachanger/scplan/internal/opgen/BUILD.bazel @@ -30,6 +30,7 @@ go_library( "opgen_enum_type.go", "opgen_enum_type_value.go", "opgen_foreign_key_constraint.go", + "opgen_foreign_key_constraint_unvalidated.go", "opgen_function.go", "opgen_function_body.go", "opgen_function_leakproof.go", diff --git a/pkg/sql/schemachanger/scplan/internal/opgen/opgen_foreign_key_constraint.go b/pkg/sql/schemachanger/scplan/internal/opgen/opgen_foreign_key_constraint.go index 52d514a6ee72..bd9ea483f661 100644 --- a/pkg/sql/schemachanger/scplan/internal/opgen/opgen_foreign_key_constraint.go +++ b/pkg/sql/schemachanger/scplan/internal/opgen/opgen_foreign_key_constraint.go @@ -11,6 +11,7 @@ package opgen import ( + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" ) @@ -20,8 +21,8 @@ func init() { toPublic( scpb.Status_ABSENT, to(scpb.Status_WRITE_ONLY, - emit(func(this *scpb.ForeignKeyConstraint) *scop.MakeAbsentForeignKeyConstraintWriteOnly { - return &scop.MakeAbsentForeignKeyConstraintWriteOnly{ + emit(func(this *scpb.ForeignKeyConstraint) *scop.AddForeignKeyConstraint { + return &scop.AddForeignKeyConstraint{ TableID: this.TableID, ConstraintID: this.ConstraintID, ColumnIDs: this.ColumnIDs, @@ -30,6 +31,7 @@ func init() { OnUpdateAction: this.OnUpdateAction, OnDeleteAction: this.OnDeleteAction, CompositeKeyMatchMethod: this.CompositeKeyMatchMethod, + Validity: descpb.ConstraintValidity_Validating, } }), ), diff --git a/pkg/sql/schemachanger/scplan/internal/opgen/opgen_foreign_key_constraint_unvalidated.go b/pkg/sql/schemachanger/scplan/internal/opgen/opgen_foreign_key_constraint_unvalidated.go new file mode 100644 index 000000000000..3d268b7d9b67 --- /dev/null +++ b/pkg/sql/schemachanger/scplan/internal/opgen/opgen_foreign_key_constraint_unvalidated.go @@ -0,0 +1,58 @@ +// Copyright 2023 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package opgen + +import ( + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scop" + "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scpb" +) + +func init() { + opRegistry.register((*scpb.ForeignKeyConstraintUnvalidated)(nil), + toPublic( + scpb.Status_ABSENT, + to(scpb.Status_PUBLIC, + emit(func(this *scpb.ForeignKeyConstraintUnvalidated) *scop.AddForeignKeyConstraint { + return &scop.AddForeignKeyConstraint{ + TableID: this.TableID, + ConstraintID: this.ConstraintID, + ColumnIDs: this.ColumnIDs, + ReferencedTableID: this.ReferencedTableID, + ReferencedColumnIDs: this.ReferencedColumnIDs, + OnUpdateAction: this.OnUpdateAction, + OnDeleteAction: this.OnDeleteAction, + CompositeKeyMatchMethod: this.CompositeKeyMatchMethod, + Validity: descpb.ConstraintValidity_Unvalidated, + } + }), + ), + ), + toAbsent( + scpb.Status_PUBLIC, + to(scpb.Status_ABSENT, + emit(func(this *scpb.ForeignKeyConstraintUnvalidated) *scop.RemoveForeignKeyBackReference { + return &scop.RemoveForeignKeyBackReference{ + ReferencedTableID: this.ReferencedTableID, + OriginTableID: this.TableID, + OriginConstraintID: this.ConstraintID, + } + }), + emit(func(this *scpb.ForeignKeyConstraintUnvalidated) *scop.RemoveForeignKeyConstraint { + return &scop.RemoveForeignKeyConstraint{ + TableID: this.TableID, + ConstraintID: this.ConstraintID, + } + }), + ), + ), + ) +} diff --git a/pkg/sql/schemachanger/scplan/internal/rules/current/helpers.go b/pkg/sql/schemachanger/scplan/internal/rules/current/helpers.go index 4791006c5be6..43cdb8d94332 100644 --- a/pkg/sql/schemachanger/scplan/internal/rules/current/helpers.go +++ b/pkg/sql/schemachanger/scplan/internal/rules/current/helpers.go @@ -210,7 +210,8 @@ func isNonIndexBackedConstraint(e scpb.Element) bool { case *scpb.CheckConstraint, *scpb.UniqueWithoutIndexConstraint, *scpb.ForeignKeyConstraint, *scpb.ColumnNotNull: return true - case *scpb.CheckConstraintUnvalidated, *scpb.UniqueWithoutIndexConstraintUnvalidated: + case *scpb.CheckConstraintUnvalidated, *scpb.UniqueWithoutIndexConstraintUnvalidated, + *scpb.ForeignKeyConstraintUnvalidated: return true } return false @@ -232,7 +233,8 @@ func isNonIndexBackedCrossDescriptorConstraint(e scpb.Element) bool { case *scpb.CheckConstraint, *scpb.UniqueWithoutIndexConstraint, *scpb.ForeignKeyConstraint: return true - case *scpb.CheckConstraintUnvalidated, *scpb.UniqueWithoutIndexConstraintUnvalidated: + case *scpb.CheckConstraintUnvalidated, *scpb.UniqueWithoutIndexConstraintUnvalidated, + *scpb.ForeignKeyConstraintUnvalidated: return true } return false diff --git a/pkg/sql/schemachanger/scplan/internal/rules/current/testdata/deprules b/pkg/sql/schemachanger/scplan/internal/rules/current/testdata/deprules index 57789337eef5..7c7307930c72 100644 --- a/pkg/sql/schemachanger/scplan/internal/rules/current/testdata/deprules +++ b/pkg/sql/schemachanger/scplan/internal/rules/current/testdata/deprules @@ -2077,7 +2077,7 @@ deprules kind: Precedence to: relation-Node query: - - $dependent[Type] IN ['*scpb.ColumnFamily', '*scpb.Column', '*scpb.PrimaryIndex', '*scpb.SecondaryIndex', '*scpb.TemporaryIndex', '*scpb.UniqueWithoutIndexConstraint', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraint', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraint', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TableData', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.ColumnNotNull', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.IndexData', '*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.DatabaseData', '*scpb.SchemaParent', '*scpb.SchemaComment', '*scpb.ObjectParent', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] + - $dependent[Type] IN ['*scpb.ColumnFamily', '*scpb.Column', '*scpb.PrimaryIndex', '*scpb.SecondaryIndex', '*scpb.TemporaryIndex', '*scpb.UniqueWithoutIndexConstraint', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraint', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraint', '*scpb.ForeignKeyConstraintUnvalidated', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TableData', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.ColumnNotNull', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.IndexData', '*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.DatabaseData', '*scpb.SchemaParent', '*scpb.SchemaComment', '*scpb.ObjectParent', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] - $relation[Type] IN ['*scpb.Database', '*scpb.Schema', '*scpb.View', '*scpb.Sequence', '*scpb.Table', '*scpb.EnumType', '*scpb.AliasType', '*scpb.CompositeType', '*scpb.Function'] - joinOnDescID($dependent, $relation, $relation-id) - ToPublicOrTransient($dependent-Target, $relation-Target) @@ -2253,7 +2253,7 @@ deprules to: constraint-Node query: - $dependents[Type] IN ['*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment'] - - $constraint[Type] IN ['*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated'] + - $constraint[Type] IN ['*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraintUnvalidated'] - joinOnConstraintID($dependents, $constraint, $table-id, $constraint-id) - toAbsent($dependents-Target, $constraint-Target) - $dependents-Node[CurrentStatus] = ABSENT @@ -2266,7 +2266,7 @@ deprules to: constraint-Node query: - $dependents[Type] IN ['*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment'] - - $constraint[Type] IN ['*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated'] + - $constraint[Type] IN ['*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraintUnvalidated'] - joinOnConstraintID($dependents, $constraint, $table-id, $constraint-id) - transient($dependents-Target, $constraint-Target) - $dependents-Node[CurrentStatus] = TRANSIENT_ABSENT @@ -2279,7 +2279,7 @@ deprules to: constraint-Node query: - $dependents[Type] IN ['*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment'] - - $constraint[Type] IN ['*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated'] + - $constraint[Type] IN ['*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraintUnvalidated'] - joinOnConstraintID($dependents, $constraint, $table-id, $constraint-id) - $dependents-Target[TargetStatus] = TRANSIENT_ABSENT - $dependents-Node[CurrentStatus] = TRANSIENT_ABSENT @@ -2293,7 +2293,7 @@ deprules to: constraint-Node query: - $dependents[Type] IN ['*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment'] - - $constraint[Type] IN ['*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated'] + - $constraint[Type] IN ['*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraintUnvalidated'] - joinOnConstraintID($dependents, $constraint, $table-id, $constraint-id) - $dependents-Target[TargetStatus] = ABSENT - $dependents-Node[CurrentStatus] = ABSENT @@ -2307,7 +2307,7 @@ deprules to: referencing-via-attr-Node query: - $referenced-descriptor[Type] IN ['*scpb.Database', '*scpb.Schema', '*scpb.View', '*scpb.Sequence', '*scpb.Table', '*scpb.EnumType', '*scpb.AliasType', '*scpb.CompositeType', '*scpb.Function'] - - $referencing-via-attr[Type] IN ['*scpb.ColumnFamily', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.SchemaComment', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] + - $referencing-via-attr[Type] IN ['*scpb.ColumnFamily', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraintUnvalidated', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.SchemaComment', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] - joinReferencedDescID($referencing-via-attr, $referenced-descriptor, $desc-id) - toAbsent($referenced-descriptor-Target, $referencing-via-attr-Target) - $referenced-descriptor-Node[CurrentStatus] = DROPPED @@ -2348,7 +2348,7 @@ deprules to: dependent-Node query: - $descriptor[Type] IN ['*scpb.Database', '*scpb.Schema', '*scpb.View', '*scpb.Sequence', '*scpb.Table', '*scpb.EnumType', '*scpb.AliasType', '*scpb.CompositeType', '*scpb.Function'] - - $dependent[Type] IN ['*scpb.ColumnFamily', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.SchemaParent', '*scpb.SchemaComment', '*scpb.ObjectParent', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] + - $dependent[Type] IN ['*scpb.ColumnFamily', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraintUnvalidated', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.SchemaParent', '*scpb.SchemaComment', '*scpb.ObjectParent', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] - joinOnDescID($descriptor, $dependent, $desc-id) - toAbsent($descriptor-Target, $dependent-Target) - $descriptor-Node[CurrentStatus] = DROPPED @@ -2387,7 +2387,7 @@ deprules to: dependent-Node query: - $relation[Type] IN ['*scpb.Database', '*scpb.Schema', '*scpb.View', '*scpb.Sequence', '*scpb.Table', '*scpb.EnumType', '*scpb.AliasType', '*scpb.CompositeType', '*scpb.Function'] - - $dependent[Type] IN ['*scpb.ColumnFamily', '*scpb.Column', '*scpb.PrimaryIndex', '*scpb.SecondaryIndex', '*scpb.TemporaryIndex', '*scpb.UniqueWithoutIndexConstraint', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraint', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraint', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TableData', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.ColumnNotNull', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.IndexData', '*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.DatabaseData', '*scpb.SchemaParent', '*scpb.SchemaComment', '*scpb.ObjectParent', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] + - $dependent[Type] IN ['*scpb.ColumnFamily', '*scpb.Column', '*scpb.PrimaryIndex', '*scpb.SecondaryIndex', '*scpb.TemporaryIndex', '*scpb.UniqueWithoutIndexConstraint', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraint', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraint', '*scpb.ForeignKeyConstraintUnvalidated', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TableData', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.ColumnNotNull', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.IndexData', '*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.DatabaseData', '*scpb.SchemaParent', '*scpb.SchemaComment', '*scpb.ObjectParent', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] - joinOnDescID($relation, $dependent, $relation-id) - ToPublicOrTransient($relation-Target, $dependent-Target) - $relation-Node[CurrentStatus] = DESCRIPTOR_ADDED @@ -2745,7 +2745,7 @@ deprules kind: Precedence to: descriptor-Node query: - - $dependent[Type] IN ['*scpb.ColumnFamily', '*scpb.Column', '*scpb.PrimaryIndex', '*scpb.SecondaryIndex', '*scpb.TemporaryIndex', '*scpb.UniqueWithoutIndexConstraint', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraint', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraint', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.ColumnNotNull', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.SchemaParent', '*scpb.SchemaComment', '*scpb.ObjectParent', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] + - $dependent[Type] IN ['*scpb.ColumnFamily', '*scpb.Column', '*scpb.PrimaryIndex', '*scpb.SecondaryIndex', '*scpb.TemporaryIndex', '*scpb.UniqueWithoutIndexConstraint', '*scpb.UniqueWithoutIndexConstraintUnvalidated', '*scpb.CheckConstraint', '*scpb.CheckConstraintUnvalidated', '*scpb.ForeignKeyConstraint', '*scpb.ForeignKeyConstraintUnvalidated', '*scpb.TableComment', '*scpb.RowLevelTTL', '*scpb.TableZoneConfig', '*scpb.TablePartitioning', '*scpb.TableLocalityGlobal', '*scpb.TableLocalityPrimaryRegion', '*scpb.TableLocalitySecondaryRegion', '*scpb.TableLocalityRegionalByRow', '*scpb.ColumnName', '*scpb.ColumnType', '*scpb.ColumnDefaultExpression', '*scpb.ColumnOnUpdateExpression', '*scpb.SequenceOwner', '*scpb.ColumnComment', '*scpb.ColumnNotNull', '*scpb.IndexName', '*scpb.IndexPartitioning', '*scpb.SecondaryIndexPartial', '*scpb.IndexComment', '*scpb.IndexColumn', '*scpb.ConstraintWithoutIndexName', '*scpb.ConstraintComment', '*scpb.Namespace', '*scpb.Owner', '*scpb.UserPrivileges', '*scpb.DatabaseRegionConfig', '*scpb.DatabaseRoleSetting', '*scpb.DatabaseComment', '*scpb.SchemaParent', '*scpb.SchemaComment', '*scpb.ObjectParent', '*scpb.EnumTypeValue', '*scpb.CompositeTypeAttrType', '*scpb.CompositeTypeAttrName', '*scpb.FunctionName', '*scpb.FunctionVolatility', '*scpb.FunctionLeakProof', '*scpb.FunctionNullInputBehavior', '*scpb.FunctionBody', '*scpb.FunctionParamDefaultExpression'] - $descriptor[Type] IN ['*scpb.Database', '*scpb.Schema', '*scpb.View', '*scpb.Sequence', '*scpb.Table', '*scpb.EnumType', '*scpb.AliasType', '*scpb.CompositeType', '*scpb.Function'] - joinOnDescID($dependent, $descriptor, $desc-id) - toAbsent($dependent-Target, $descriptor-Target) diff --git a/pkg/sql/schemachanger/screl/attr.go b/pkg/sql/schemachanger/screl/attr.go index 8fdfcab2a083..4d9bb152e8c6 100644 --- a/pkg/sql/schemachanger/screl/attr.go +++ b/pkg/sql/schemachanger/screl/attr.go @@ -201,6 +201,11 @@ var elementSchemaOptions = []rel.SchemaOption{ rel.EntityAttr(ConstraintID, "ConstraintID"), rel.EntityAttr(IndexID, "IndexIDForValidation"), ), + rel.EntityMapping(t((*scpb.ForeignKeyConstraintUnvalidated)(nil)), + rel.EntityAttr(DescID, "TableID"), + rel.EntityAttr(ReferencedDescID, "ReferencedTableID"), + rel.EntityAttr(ConstraintID, "ConstraintID"), + ), rel.EntityMapping(t((*scpb.RowLevelTTL)(nil)), rel.EntityAttr(DescID, "TableID"), ), diff --git a/pkg/sql/schemachanger/screl/scalars.go b/pkg/sql/schemachanger/screl/scalars.go index 732d6128f5c3..95330515b9de 100644 --- a/pkg/sql/schemachanger/screl/scalars.go +++ b/pkg/sql/schemachanger/screl/scalars.go @@ -125,7 +125,7 @@ func MinVersion(el scpb.Element) clusterversion.Key { *scpb.FunctionNullInputBehavior, *scpb.FunctionBody, *scpb.FunctionParamDefaultExpression: return clusterversion.V23_1 case *scpb.ColumnNotNull, *scpb.CheckConstraintUnvalidated, - *scpb.UniqueWithoutIndexConstraintUnvalidated: + *scpb.UniqueWithoutIndexConstraintUnvalidated, *scpb.ForeignKeyConstraintUnvalidated: return clusterversion.V23_1 default: panic(errors.AssertionFailedf("unknown element %T", el)) diff --git a/pkg/sql/schemachanger/testdata/explain/alter_table_add_foreign_key b/pkg/sql/schemachanger/testdata/explain/alter_table_add_foreign_key index 10fe4e9f0bc0..f01f6f97a85f 100644 --- a/pkg/sql/schemachanger/testdata/explain/alter_table_add_foreign_key +++ b/pkg/sql/schemachanger/testdata/explain/alter_table_add_foreign_key @@ -11,7 +11,7 @@ Schema change plan for ALTER TABLE ‹defaultdb›.‹public›.‹t1› ADD CON │ ├── 1 element transitioning toward PUBLIC │ │ └── ABSENT → WRITE_ONLY ForeignKeyConstraint:{DescID: 104, IndexID: 0, ConstraintID: 2, ReferencedDescID: 105} │ └── 1 Mutation operation - │ └── MakeAbsentForeignKeyConstraintWriteOnly {"ConstraintID":2,"ReferencedTableID":105,"TableID":104} + │ └── AddForeignKeyConstraint {"ConstraintID":2,"ReferencedTableID":105,"TableID":104,"Validity":2} ├── PreCommitPhase │ ├── Stage 1 of 2 in PreCommitPhase │ │ ├── 1 element transitioning toward PUBLIC @@ -22,7 +22,7 @@ Schema change plan for ALTER TABLE ‹defaultdb›.‹public›.‹t1› ADD CON │ ├── 1 element transitioning toward PUBLIC │ │ └── ABSENT → WRITE_ONLY ForeignKeyConstraint:{DescID: 104, IndexID: 0, ConstraintID: 2, ReferencedDescID: 105} │ └── 4 Mutation operations - │ ├── MakeAbsentForeignKeyConstraintWriteOnly {"ConstraintID":2,"ReferencedTableID":105,"TableID":104} + │ ├── AddForeignKeyConstraint {"ConstraintID":2,"ReferencedTableID":105,"TableID":104,"Validity":2} │ ├── SetJobStateOnDescriptor {"DescriptorID":104,"Initialize":true} │ ├── SetJobStateOnDescriptor {"DescriptorID":105,"Initialize":true} │ └── CreateSchemaChangerJob {"RunningStatus":"PostCommitPhase ..."} diff --git a/pkg/sql/schemachanger/testdata/explain/alter_table_add_unique_without_index b/pkg/sql/schemachanger/testdata/explain/alter_table_add_unique_without_index index e2bb7f8bc22b..886e161a8cbe 100644 --- a/pkg/sql/schemachanger/testdata/explain/alter_table_add_unique_without_index +++ b/pkg/sql/schemachanger/testdata/explain/alter_table_add_unique_without_index @@ -11,7 +11,7 @@ Schema change plan for ALTER TABLE ‹defaultdb›.‹public›.‹t› ADD CONS │ ├── 1 element transitioning toward PUBLIC │ │ └── ABSENT → WRITE_ONLY UniqueWithoutIndexConstraint:{DescID: 104, ConstraintID: 2} │ └── 1 Mutation operation - │ └── MakeAbsentUniqueWithoutIndexConstraintWriteOnly {"ConstraintID":2,"TableID":104} + │ └── AddUniqueWithoutIndexConstraint {"ConstraintID":2,"TableID":104,"Validity":2} ├── PreCommitPhase │ ├── Stage 1 of 2 in PreCommitPhase │ │ ├── 1 element transitioning toward PUBLIC @@ -22,7 +22,7 @@ Schema change plan for ALTER TABLE ‹defaultdb›.‹public›.‹t› ADD CONS │ ├── 1 element transitioning toward PUBLIC │ │ └── ABSENT → WRITE_ONLY UniqueWithoutIndexConstraint:{DescID: 104, ConstraintID: 2} │ └── 3 Mutation operations - │ ├── MakeAbsentUniqueWithoutIndexConstraintWriteOnly {"ConstraintID":2,"TableID":104} + │ ├── AddUniqueWithoutIndexConstraint {"ConstraintID":2,"TableID":104,"Validity":2} │ ├── SetJobStateOnDescriptor {"DescriptorID":104,"Initialize":true} │ └── CreateSchemaChangerJob {"RunningStatus":"PostCommitPhase ..."} └── PostCommitPhase diff --git a/pkg/sql/schemachanger/testdata/explain_verbose/alter_table_add_foreign_key b/pkg/sql/schemachanger/testdata/explain_verbose/alter_table_add_foreign_key index 09940e034d6b..b3941af0a06e 100644 --- a/pkg/sql/schemachanger/testdata/explain_verbose/alter_table_add_foreign_key +++ b/pkg/sql/schemachanger/testdata/explain_verbose/alter_table_add_foreign_key @@ -21,7 +21,7 @@ EXPLAIN (ddl, verbose) ALTER TABLE t1 ADD FOREIGN KEY (i) REFERENCES t2(i); │ │ │ └── • 1 Mutation operation │ │ -│ └── • MakeAbsentForeignKeyConstraintWriteOnly +│ └── • AddForeignKeyConstraint │ ColumnIDs: │ - 1 │ ConstraintID: 2 @@ -29,6 +29,7 @@ EXPLAIN (ddl, verbose) ALTER TABLE t1 ADD FOREIGN KEY (i) REFERENCES t2(i); │ - 1 │ ReferencedTableID: 105 │ TableID: 104 +│ Validity: 2 │ ├── • PreCommitPhase │ │ @@ -56,7 +57,7 @@ EXPLAIN (ddl, verbose) ALTER TABLE t1 ADD FOREIGN KEY (i) REFERENCES t2(i); │ │ │ └── • 4 Mutation operations │ │ -│ ├── • MakeAbsentForeignKeyConstraintWriteOnly +│ ├── • AddForeignKeyConstraint │ │ ColumnIDs: │ │ - 1 │ │ ConstraintID: 2 @@ -64,6 +65,7 @@ EXPLAIN (ddl, verbose) ALTER TABLE t1 ADD FOREIGN KEY (i) REFERENCES t2(i); │ │ - 1 │ │ ReferencedTableID: 105 │ │ TableID: 104 +│ │ Validity: 2 │ │ │ ├── • SetJobStateOnDescriptor │ │ DescriptorID: 104 diff --git a/pkg/sql/schemachanger/testdata/explain_verbose/alter_table_add_unique_without_index b/pkg/sql/schemachanger/testdata/explain_verbose/alter_table_add_unique_without_index index ba7a056dd550..6d113045ede4 100644 --- a/pkg/sql/schemachanger/testdata/explain_verbose/alter_table_add_unique_without_index +++ b/pkg/sql/schemachanger/testdata/explain_verbose/alter_table_add_unique_without_index @@ -21,11 +21,12 @@ EXPLAIN (ddl, verbose) ALTER TABLE t ADD UNIQUE WITHOUT INDEX (j); │ │ │ └── • 1 Mutation operation │ │ -│ └── • MakeAbsentUniqueWithoutIndexConstraintWriteOnly +│ └── • AddUniqueWithoutIndexConstraint │ ColumnIDs: │ - 2 │ ConstraintID: 2 │ TableID: 104 +│ Validity: 2 │ ├── • PreCommitPhase │ │ @@ -53,11 +54,12 @@ EXPLAIN (ddl, verbose) ALTER TABLE t ADD UNIQUE WITHOUT INDEX (j); │ │ │ └── • 3 Mutation operations │ │ -│ ├── • MakeAbsentUniqueWithoutIndexConstraintWriteOnly +│ ├── • AddUniqueWithoutIndexConstraint │ │ ColumnIDs: │ │ - 2 │ │ ConstraintID: 2 │ │ TableID: 104 +│ │ Validity: 2 │ │ │ ├── • SetJobStateOnDescriptor │ │ DescriptorID: 104