diff options
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r-- | src/backend/commands/tablecmds.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 50341f6ef9a..ea1c309add8 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -276,6 +276,8 @@ static void AlterIndexNamespaces(Relation classRel, Relation rel, static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode); +static void ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, + bool recurse, bool recursing, LOCKMODE lockmode); static void ATExecValidateConstraint(Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode); static int transformColumnNameList(Oid relId, List *colList, @@ -2887,6 +2889,7 @@ AlterTableGetLockLevel(List *cmds) case AT_SetOptions: case AT_ResetOptions: case AT_SetStorage: + case AT_AlterConstraint: case AT_ValidateConstraint: cmd_lockmode = ShareUpdateExclusiveLock; break; @@ -3125,6 +3128,10 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, ATPrepAddInherit(rel); pass = AT_PASS_MISC; break; + case AT_AlterConstraint: /* ALTER CONSTRAINT */ + ATSimplePermissions(rel, ATT_TABLE); + pass = AT_PASS_MISC; + break; case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */ ATSimplePermissions(rel, ATT_TABLE); /* Recursion occurs during execution phase */ @@ -3304,6 +3311,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */ ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def, lockmode); break; + case AT_AlterConstraint: /* ALTER CONSTRAINT */ + ATExecAlterConstraint(rel, cmd, false, false, lockmode); + break; case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */ ATExecValidateConstraint(rel, cmd->name, false, false, lockmode); break; @@ -6176,6 +6186,135 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, } /* + * ALTER TABLE ALTER CONSTRAINT + * + * Update the attributes of a constraint. + * + * Currently only works for Foreign Key constraints. + * Foreign keys do not inherit, so we purposely ignore the + * recursion bit here, but we keep the API the same for when + * other constraint types are supported. + */ +static void +ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, + bool recurse, bool recursing, LOCKMODE lockmode) +{ + Relation conrel; + SysScanDesc scan; + ScanKeyData key; + HeapTuple contuple; + Form_pg_constraint currcon = NULL; + Constraint *cmdcon = NULL; + bool found = false; + + Assert(IsA(cmd->def, Constraint)); + cmdcon = (Constraint *) cmd->def; + + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + + /* + * Find and check the target constraint + */ + ScanKeyInit(&key, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(rel))); + scan = systable_beginscan(conrel, ConstraintRelidIndexId, + true, SnapshotNow, 1, &key); + + while (HeapTupleIsValid(contuple = systable_getnext(scan))) + { + currcon = (Form_pg_constraint) GETSTRUCT(contuple); + if (strcmp(NameStr(currcon->conname), cmdcon->conname) == 0) + { + found = true; + break; + } + } + + if (!found) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("constraint \"%s\" of relation \"%s\" does not exist", + cmdcon->conname, RelationGetRelationName(rel)))); + + if (currcon->contype != CONSTRAINT_FOREIGN) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint", + cmdcon->conname, RelationGetRelationName(rel)))); + + if (currcon->condeferrable != cmdcon->deferrable || + currcon->condeferred != cmdcon->initdeferred) + { + HeapTuple copyTuple; + HeapTuple tgtuple; + Form_pg_constraint copy_con; + Form_pg_trigger copy_tg; + ScanKeyData tgkey; + SysScanDesc tgscan; + Relation tgrel; + + /* + * Now update the catalog, while we have the door open. + */ + copyTuple = heap_copytuple(contuple); + copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple); + copy_con->condeferrable = cmdcon->deferrable; + copy_con->condeferred = cmdcon->initdeferred; + simple_heap_update(conrel, ©Tuple->t_self, copyTuple); + CatalogUpdateIndexes(conrel, copyTuple); + + InvokeObjectPostAlterHook(ConstraintRelationId, + HeapTupleGetOid(contuple), 0); + + heap_freetuple(copyTuple); + + /* + * Now we need to update the multiple entries in pg_trigger + * that implement the constraint. + */ + tgrel = heap_open(TriggerRelationId, RowExclusiveLock); + + ScanKeyInit(&tgkey, + Anum_pg_trigger_tgconstraint, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(HeapTupleGetOid(contuple))); + + tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true, + SnapshotNow, 1, &tgkey); + + while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan))) + { + copyTuple = heap_copytuple(tgtuple); + copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple); + copy_tg->tgdeferrable = cmdcon->deferrable; + copy_tg->tginitdeferred = cmdcon->initdeferred; + simple_heap_update(tgrel, ©Tuple->t_self, copyTuple); + CatalogUpdateIndexes(tgrel, copyTuple); + + InvokeObjectPostAlterHook(TriggerRelationId, + HeapTupleGetOid(tgtuple), 0); + + heap_freetuple(copyTuple); + } + + systable_endscan(tgscan); + + heap_close(tgrel, RowExclusiveLock); + + /* + * Invalidate relcache so that others see the new attributes. + */ + CacheInvalidateRelcache(rel); + } + + systable_endscan(scan); + + heap_close(conrel, RowExclusiveLock); +} + +/* * ALTER TABLE VALIDATE CONSTRAINT * * XXX The reason we handle recursion here rather than at Phase 1 is because |