WIP: ALTER TABLE ... ALTER CONSTRAINT ... SET DEFERRABLE on UNIQUE or PK

Started by Craig Ringerover 10 years ago2 messages
#1Craig Ringer
craig@2ndquadrant.com
1 attachment(s)

Hi all

Attached is a patch to implement ALTER TABLE ... ALTER CONSTRAINT ...
SET DEFERRABLE on UNIQUE or PRIMARY KEY constraints.

Currently only FOREIGN KEY constraints are supported. Others are rejected with:

constraint \"%s\" of relation \"%s\" is not a foreign key constraint

The patch also adds some regression tests for DEFERRABLE constraints.

The ALTER doesn't take effect in the session it's run in, which makes
me suspect I need to do additional cache invalidations - maybe the
index backing the constraint? Anyway, posted here as-is because I'm
out of time for now and it might be useful for someone who's looking
for info on this.

--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

0001-Allow-ALTER-TABLE.ALTER-CONSTRAINT-on-PK-and-UNIQUE.patchtext/x-patch; charset=US-ASCII; name=0001-Allow-ALTER-TABLE.ALTER-CONSTRAINT-on-PK-and-UNIQUE.patchDownload
From c0a041f0ca5d884842820538b56d82472a701c3c Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig@2ndquadrant.com>
Date: Fri, 26 Jun 2015 11:59:30 +0800
Subject: [PATCH] Allow ALTER TABLE...ALTER CONSTRAINT on PK and UNIQUE

We presently support DEFERRABLE constraints on PRIMARY KEY and UNIQUE
constraints, but do not permit ALTER TABLE to modify their deferrable
state.

Fix that and add some regression test coverage.
---
 src/backend/commands/tablecmds.c     | 72 +++++++++++++++++++-----------------
 src/test/regress/sql/alter_table.sql | 37 ++++++++++++++++++
 2 files changed, 76 insertions(+), 33 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d394713..5875987 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -6647,22 +6647,20 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
 				 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
 						cmdcon->conname, RelationGetRelationName(rel))));
 
-	if (currcon->contype != CONSTRAINT_FOREIGN)
+	if (currcon->contype != CONSTRAINT_FOREIGN &&
+		currcon->contype != CONSTRAINT_PRIMARY &&
+		currcon->contype != CONSTRAINT_UNIQUE)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
+				 errmsg("constraint \"%s\" of relation \"%s\" must be FOREIGN KEY, PRIMARY KEY or UNIQUE",
 						cmdcon->conname, RelationGetRelationName(rel))));
 
 	if (currcon->condeferrable != cmdcon->deferrable ||
 		currcon->condeferred != cmdcon->initdeferred)
 	{
 		HeapTuple	copyTuple;
-		HeapTuple	tgtuple;
 		Form_pg_constraint copy_con;
 		List	   *otherrelids = NIL;
-		ScanKeyData tgkey;
-		SysScanDesc tgscan;
-		Relation	tgrel;
 		ListCell   *lc;
 
 		/*
@@ -6682,44 +6680,52 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
 
 		/*
 		 * Now we need to update the multiple entries in pg_trigger that
-		 * implement the constraint.
+		 * implement the constraint if it's a foreign key constraint.
 		 */
-		tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
+		if (currcon->contype == CONSTRAINT_FOREIGN)
+		{
+			HeapTuple	tgtuple;
+			ScanKeyData tgkey;
+			SysScanDesc tgscan;
+			Relation	tgrel;
 
-		ScanKeyInit(&tgkey,
-					Anum_pg_trigger_tgconstraint,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum(HeapTupleGetOid(contuple)));
+			tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
 
-		tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
-									NULL, 1, &tgkey);
+			ScanKeyInit(&tgkey,
+						Anum_pg_trigger_tgconstraint,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(HeapTupleGetOid(contuple)));
 
-		while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
-		{
-			Form_pg_trigger copy_tg;
+			tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
+										NULL, 1, &tgkey);
 
-			copyTuple = heap_copytuple(tgtuple);
-			copy_tg = (Form_pg_trigger) GETSTRUCT(copyTuple);
+			while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
+			{
+				Form_pg_trigger copy_tg;
 
-			/* Remember OIDs of other relation(s) involved in FK constraint */
-			if (copy_tg->tgrelid != RelationGetRelid(rel))
-				otherrelids = list_append_unique_oid(otherrelids,
-													 copy_tg->tgrelid);
+				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);
+				/* Remember OIDs of other relation(s) involved in FK constraint */
+				if (copy_tg->tgrelid != RelationGetRelid(rel))
+					otherrelids = list_append_unique_oid(otherrelids,
+														 copy_tg->tgrelid);
 
-			InvokeObjectPostAlterHook(TriggerRelationId,
-									  HeapTupleGetOid(tgtuple), 0);
+				copy_tg->tgdeferrable = cmdcon->deferrable;
+				copy_tg->tginitdeferred = cmdcon->initdeferred;
+				simple_heap_update(tgrel, &copyTuple->t_self, copyTuple);
+				CatalogUpdateIndexes(tgrel, copyTuple);
 
-			heap_freetuple(copyTuple);
-		}
+				InvokeObjectPostAlterHook(TriggerRelationId,
+										  HeapTupleGetOid(tgtuple), 0);
+
+				heap_freetuple(copyTuple);
+			}
 
-		systable_endscan(tgscan);
+			systable_endscan(tgscan);
 
-		heap_close(tgrel, RowExclusiveLock);
+			heap_close(tgrel, RowExclusiveLock);
+		}
 
 		/*
 		 * Invalidate relcache so that others see the new attributes.  We must
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 29c1875..ec31597 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1688,3 +1688,40 @@ ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing
 DROP TABLE logged3;
 DROP TABLE logged2;
 DROP TABLE logged1;
+-- Make sure that ALTERing a table to make a PK or UNIQUE constraint deferrable works
+CREATE TABLE pkuk(
+   pk INTEGER PRIMARY KEY,
+   uk INTEGER,
+   CONSTRAINT pkuk_unique_uk UNIQUE(uk)
+);
+INSERT INTO pkuk(pk, uk) VALUES (1,1), (2,2);
+ALTER TABLE pkuk ALTER CONSTRAINT pkuk_pkey DEFERRABLE;
+ALTER TABLE pkuk ALTER CONSTRAINT pkuk_unique_uk DEFERRABLE;
+\d pkuk
+BEGIN;
+INSERT INTO pkuk(pk,uk) VALUES (2,2);
+COMMIT;
+BEGIN;
+SET CONSTRAINTS ALL DEFERRED;
+INSERT INTO pkuk(pk,uk) VALUES (2,2);
+COMMIT;
+ALTER TABLE pkuk ALTER CONSTRAINT pkuk_pkey DEFERRABLE INITIALLY DEFERRED;
+ALTER TABLE pkuk ALTER CONSTRAINT pkuk_unique_uk DEFERRABLE INITIALLY DEFERRED;
+\d pkuk
+BEGIN;
+INSERT INTO pkuk(pk,uk) VALUES (2,2);
+COMMIT;
+BEGIN;
+SET CONSTRAINTS ALL IMMEDIATE;
+INSERT INTO pkuk(pk,uk) VALUES (2,2);
+COMMIT;
+ALTER TABLE pkuk ALTER CONSTRAINT pkuk_pkey DEFERRABLE INITIALLY IMMEDIATE;
+ALTER TABLE pkuk ALTER CONSTRAINT pkuk_unique_uk DEFERRABLE INITIALLY IMMEDIATE;
+\d pkuk
+BEGIN;
+INSERT INTO pkuk(pk,uk) VALUES (2,2);
+COMMIT;
+BEGIN;
+SET CONSTRAINTS ALL DEFERRED;
+INSERT INTO pkuk(pk,uk) VALUES (2,2);
+COMMIT;
-- 
2.1.0

#2Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Craig Ringer (#1)
Re: WIP: ALTER TABLE ... ALTER CONSTRAINT ... SET DEFERRABLE on UNIQUE or PK

On 26 June 2015 at 07:20, Craig Ringer <craig@2ndquadrant.com> wrote:

Hi all

Attached is a patch to implement ALTER TABLE ... ALTER CONSTRAINT ...
SET DEFERRABLE on UNIQUE or PRIMARY KEY constraints.

Currently only FOREIGN KEY constraints are supported. Others are rejected with:

+1
I was disappointed that this wasn't part of the patch that added
support for it for FKs. What about exclusion constraints? I think
making them work should be more-or-less identical, and then we'll have
support for the full set, since CHECK and NOT NULL constraints can't
currently be deferred.

constraint \"%s\" of relation \"%s\" is not a foreign key constraint

The patch also adds some regression tests for DEFERRABLE constraints.

The ALTER doesn't take effect in the session it's run in, which makes
me suspect I need to do additional cache invalidations - maybe the
index backing the constraint? Anyway, posted here as-is because I'm
out of time for now and it might be useful for someone who's looking
for info on this.

If you add it to the next commitfest, I'll review it.

Regards,
Dean

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers