aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
authorSimon Riggs <simon@2ndQuadrant.com>2013-06-29 00:27:30 +0100
committerSimon Riggs <simon@2ndQuadrant.com>2013-06-29 00:27:30 +0100
commitf177cbfe676dc2c7ca2b206c54d6bf819feeea8b (patch)
tree00b7869b96efdafac8623134af2c35a31c3995f5 /src/backend/commands/tablecmds.c
parent2f74e4ec50dc625605e9a7afd63bd8a48c981d9e (diff)
downloadpostgresql-f177cbfe676dc2c7ca2b206c54d6bf819feeea8b.tar.gz
postgresql-f177cbfe676dc2c7ca2b206c54d6bf819feeea8b.zip
ALTER TABLE ... ALTER CONSTRAINT for FKs
Allow constraint attributes to be altered, so the default setting of NOT DEFERRABLE can be altered to DEFERRABLE and back. Review by Abhijit Menon-Sen
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c139
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, &copyTuple->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, &copyTuple->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