@@ -389,17 +389,14 @@ static void AlterIndexNamespaces(Relation classRel, Relation rel,
389389static void AlterSeqNamespaces(Relation classRel, Relation rel,
390390 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391391 LOCKMODE lockmode);
392- static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd ,
393- bool recurse, bool recursing, LOCKMODE lockmode);
394- static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel ,
395- Relation rel, HeapTuple contuple, List **otherrelids ,
396- LOCKMODE lockmode);
392+ static ObjectAddress ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon ,
393+ bool recurse, LOCKMODE lockmode);
394+ static bool ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
395+ Relation tgrel, Relation rel, HeapTuple contuple ,
396+ bool recurse, List **otherrelids, LOCKMODE lockmode);
397397static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
398398 bool deferrable, bool initdeferred,
399399 List **otherrelids);
400- static void ATExecAlterChildConstr(Constraint *cmdcon, Relation conrel, Relation tgrel,
401- Relation rel, HeapTuple contuple, List **otherrelids,
402- LOCKMODE lockmode);
403400static ObjectAddress ATExecValidateConstraint(List **wqueue,
404401 Relation rel, char *constrName,
405402 bool recurse, bool recursing, LOCKMODE lockmode);
@@ -5170,6 +5167,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
51705167 ATSimplePermissions(cmd->subtype, rel,
51715168 ATT_TABLE | ATT_PARTITIONED_TABLE);
51725169 /* Recursion occurs during execution phase */
5170+ if (recurse)
5171+ cmd->recurse = true;
51735172 pass = AT_PASS_MISC;
51745173 break;
51755174 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
@@ -5450,7 +5449,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
54505449 lockmode);
54515450 break;
54525451 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5453- address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5452+ address = ATExecAlterConstraint(rel, castNode(ATAlterConstraint,
5453+ cmd->def),
5454+ cmd->recurse, lockmode);
54545455 break;
54555456 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
54565457 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
@@ -11801,10 +11802,9 @@ GetForeignKeyCheckTriggers(Relation trigrel,
1180111802 * InvalidObjectAddress.
1180211803 */
1180311804static ObjectAddress
11804- ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd , bool recurse,
11805- bool recursing, LOCKMODE lockmode)
11805+ ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon , bool recurse,
11806+ LOCKMODE lockmode)
1180611807{
11807- Constraint *cmdcon;
1180811808 Relation conrel;
1180911809 Relation tgrel;
1181011810 SysScanDesc scan;
@@ -11813,9 +11813,17 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
1181311813 Form_pg_constraint currcon;
1181411814 ObjectAddress address;
1181511815 List *otherrelids = NIL;
11816- ListCell *lc;
1181711816
11818- cmdcon = castNode(Constraint, cmd->def);
11817+ /*
11818+ * Disallow altering ONLY a partitioned table, as it would make no sense.
11819+ * This is okay for legacy inheritance.
11820+ */
11821+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
11822+ ereport(ERROR,
11823+ errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11824+ errmsg("constraint must be altered in child tables too"),
11825+ errhint("Do not specify the ONLY keyword."));
11826+
1181911827
1182011828 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
1182111829 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
@@ -11896,28 +11904,22 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
1189611904 errhint("You may alter the constraint it derives from instead.")));
1189711905 }
1189811906
11907+ address = InvalidObjectAddress;
11908+
1189911909 /*
11900- * Do the actual catalog work. We can skip changing if already in the
11901- * desired state, but not if a partitioned table: partitions need to be
11902- * processed regardless, in case they had the constraint locally changed.
11910+ * Do the actual catalog work, and recurse if necessary.
1190311911 */
11904- address = InvalidObjectAddress;
11905- if (currcon->condeferrable != cmdcon->deferrable ||
11906- currcon->condeferred != cmdcon->initdeferred ||
11907- rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11908- {
11909- if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11910- &otherrelids, lockmode))
11911- ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11912- }
11912+ if (ATExecAlterConstraintInternal(cmdcon, conrel, tgrel, rel, contuple,
11913+ recurse, &otherrelids, lockmode))
11914+ ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
1191311915
1191411916 /*
11915- * ATExecAlterConstrRecurse already invalidated relcache for the relations
11916- * having the constraint itself; here we also invalidate for relations
11917- * that have any triggers that are part of the constraint.
11917+ * ATExecAlterConstraintInternal already invalidated relcache for the
11918+ * relations having the constraint itself; here we also invalidate for
11919+ * relations that have any triggers that are part of the constraint.
1191811920 */
11919- foreach(lc , otherrelids)
11920- CacheInvalidateRelcacheByRelid(lfirst_oid(lc) );
11921+ foreach_oid(relid , otherrelids)
11922+ CacheInvalidateRelcacheByRelid(relid );
1192111923
1192211924 systable_endscan(scan);
1192311925
@@ -11939,30 +11941,30 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
1193911941 * but existing releases don't do that.)
1194011942 */
1194111943static bool
11942- ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel ,
11943- Relation rel, HeapTuple contuple, List **otherrelids ,
11944- LOCKMODE lockmode)
11944+ ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
11945+ Relation tgrel, Relation rel, HeapTuple contuple ,
11946+ bool recurse, List **otherrelids, LOCKMODE lockmode)
1194511947{
1194611948 Form_pg_constraint currcon;
11947- Oid conoid;
11948- Oid refrelid;
11949+ Oid refrelid = InvalidOid;
1194911950 bool changed = false;
1195011951
1195111952 /* since this function recurses, it could be driven to stack overflow */
1195211953 check_stack_depth();
1195311954
1195411955 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11955- conoid = currcon->oid;
11956- refrelid = currcon->confrelid;
11956+ if ( currcon->contype == CONSTRAINT_FOREIGN)
11957+ refrelid = currcon->confrelid;
1195711958
1195811959 /*
1195911960 * Update pg_constraint with the flags from cmdcon.
1196011961 *
1196111962 * If called to modify a constraint that's already in the desired state,
1196211963 * silently do nothing.
1196311964 */
11964- if (currcon->condeferrable != cmdcon->deferrable ||
11965- currcon->condeferred != cmdcon->initdeferred)
11965+ if (cmdcon->alterDeferrability &&
11966+ (currcon->condeferrable != cmdcon->deferrable ||
11967+ currcon->condeferred != cmdcon->initdeferred))
1196611968 {
1196711969 HeapTuple copyTuple;
1196811970 Form_pg_constraint copy_con;
@@ -11973,8 +11975,7 @@ ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
1197311975 copy_con->condeferred = cmdcon->initdeferred;
1197411976 CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
1197511977
11976- InvokeObjectPostAlterHook(ConstraintRelationId,
11977- conoid, 0);
11978+ InvokeObjectPostAlterHook(ConstraintRelationId, currcon->oid, 0);
1197811979
1197911980 heap_freetuple(copyTuple);
1198011981 changed = true;
@@ -11986,32 +11987,59 @@ ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
1198611987 * Now we need to update the multiple entries in pg_trigger that
1198711988 * implement the constraint.
1198811989 */
11989- AlterConstrTriggerDeferrability(conoid, tgrel, rel, cmdcon->deferrable,
11990+ AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
11991+ cmdcon->deferrable,
1199011992 cmdcon->initdeferred, otherrelids);
1199111993 }
1199211994
1199311995 /*
1199411996 * If the table at either end of the constraint is partitioned, we need to
11995- * recurse and handle every constraint that is a child of this one.
11997+ * handle every constraint that is a child of this one.
1199611998 *
11997- * (This assumes that the recurse flag is forcibly set for partitioned
11998- * tables, and not set for legacy inheritance, though we don't check for
11999- * that here.)
11999+ * Note that this doesn't handle recursion the normal way, viz. by
12000+ * scanning the list of child relations and recursing; instead it uses the
12001+ * conparentid relationships. This may need to be reconsidered.
1200012002 */
12001- if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12002- get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
12003- ATExecAlterChildConstr(cmdcon, conrel, tgrel, rel, contuple,
12004- otherrelids, lockmode);
12003+ if (recurse && changed &&
12004+ (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12005+ (OidIsValid(refrelid) &&
12006+ get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)))
12007+ {
12008+ ScanKeyData pkey;
12009+ SysScanDesc pscan;
12010+ HeapTuple childtup;
12011+
12012+ ScanKeyInit(&pkey,
12013+ Anum_pg_constraint_conparentid,
12014+ BTEqualStrategyNumber, F_OIDEQ,
12015+ ObjectIdGetDatum(currcon->oid));
12016+
12017+ pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12018+ true, NULL, 1, &pkey);
12019+
12020+ while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12021+ {
12022+ Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12023+ Relation childrel;
12024+
12025+ childrel = table_open(childcon->conrelid, lockmode);
12026+ ATExecAlterConstraintInternal(cmdcon, conrel, tgrel, childrel, childtup,
12027+ recurse, otherrelids, lockmode);
12028+ table_close(childrel, NoLock);
12029+ }
12030+
12031+ systable_endscan(pscan);
12032+ }
1200512033
1200612034 return changed;
1200712035}
1200812036
1200912037/*
12010- * A subroutine of ATExecAlterConstrRecurse that updated constraint trigger's
12011- * deferrability.
12038+ * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12039+ * trigger's deferrability.
1201212040 *
1201312041 * The arguments to this function have the same meaning as the arguments to
12014- * ATExecAlterConstrRecurse .
12042+ * ATExecAlterConstrDeferrability .
1201512043 */
1201612044static void
1201712045AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
@@ -12071,49 +12099,6 @@ AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
1207112099 systable_endscan(tgscan);
1207212100}
1207312101
12074- /*
12075- * Invokes ATExecAlterConstrRecurse for each constraint that is a child of the
12076- * specified constraint.
12077- *
12078- * The arguments to this function have the same meaning as the arguments to
12079- * ATExecAlterConstrRecurse.
12080- */
12081- static void
12082- ATExecAlterChildConstr(Constraint *cmdcon, Relation conrel, Relation tgrel,
12083- Relation rel, HeapTuple contuple, List **otherrelids,
12084- LOCKMODE lockmode)
12085- {
12086- Form_pg_constraint currcon;
12087- Oid conoid;
12088- ScanKeyData pkey;
12089- SysScanDesc pscan;
12090- HeapTuple childtup;
12091-
12092- currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12093- conoid = currcon->oid;
12094-
12095- ScanKeyInit(&pkey,
12096- Anum_pg_constraint_conparentid,
12097- BTEqualStrategyNumber, F_OIDEQ,
12098- ObjectIdGetDatum(conoid));
12099-
12100- pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12101- true, NULL, 1, &pkey);
12102-
12103- while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12104- {
12105- Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12106- Relation childrel;
12107-
12108- childrel = table_open(childcon->conrelid, lockmode);
12109- ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
12110- otherrelids, lockmode);
12111- table_close(childrel, NoLock);
12112- }
12113-
12114- systable_endscan(pscan);
12115- }
12116-
1211712102/*
1211812103 * ALTER TABLE VALIDATE CONSTRAINT
1211912104 *
0 commit comments