@@ -389,17 +389,14 @@ static void AlterIndexNamespaces(Relation classRel, Relation rel,
389
389
static void AlterSeqNamespaces(Relation classRel, Relation rel,
390
390
Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391
391
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);
397
397
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
398
398
bool deferrable, bool initdeferred,
399
399
List **otherrelids);
400
- static void ATExecAlterChildConstr(Constraint *cmdcon, Relation conrel, Relation tgrel,
401
- Relation rel, HeapTuple contuple, List **otherrelids,
402
- LOCKMODE lockmode);
403
400
static ObjectAddress ATExecValidateConstraint(List **wqueue,
404
401
Relation rel, char *constrName,
405
402
bool recurse, bool recursing, LOCKMODE lockmode);
@@ -5170,6 +5167,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
5170
5167
ATSimplePermissions(cmd->subtype, rel,
5171
5168
ATT_TABLE | ATT_PARTITIONED_TABLE);
5172
5169
/* Recursion occurs during execution phase */
5170
+ if (recurse)
5171
+ cmd->recurse = true;
5173
5172
pass = AT_PASS_MISC;
5174
5173
break;
5175
5174
case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
@@ -5450,7 +5449,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5450
5449
lockmode);
5451
5450
break;
5452
5451
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);
5454
5455
break;
5455
5456
case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5456
5457
address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
@@ -11801,10 +11802,9 @@ GetForeignKeyCheckTriggers(Relation trigrel,
11801
11802
* InvalidObjectAddress.
11802
11803
*/
11803
11804
static 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)
11806
11807
{
11807
- Constraint *cmdcon;
11808
11808
Relation conrel;
11809
11809
Relation tgrel;
11810
11810
SysScanDesc scan;
@@ -11813,9 +11813,17 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11813
11813
Form_pg_constraint currcon;
11814
11814
ObjectAddress address;
11815
11815
List *otherrelids = NIL;
11816
- ListCell *lc;
11817
11816
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
+
11819
11827
11820
11828
conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11821
11829
tgrel = table_open(TriggerRelationId, RowExclusiveLock);
@@ -11896,28 +11904,22 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11896
11904
errhint("You may alter the constraint it derives from instead.")));
11897
11905
}
11898
11906
11907
+ address = InvalidObjectAddress;
11908
+
11899
11909
/*
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.
11903
11911
*/
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);
11913
11915
11914
11916
/*
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.
11918
11920
*/
11919
- foreach(lc , otherrelids)
11920
- CacheInvalidateRelcacheByRelid(lfirst_oid(lc) );
11921
+ foreach_oid(relid , otherrelids)
11922
+ CacheInvalidateRelcacheByRelid(relid );
11921
11923
11922
11924
systable_endscan(scan);
11923
11925
@@ -11939,30 +11941,30 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11939
11941
* but existing releases don't do that.)
11940
11942
*/
11941
11943
static 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)
11945
11947
{
11946
11948
Form_pg_constraint currcon;
11947
- Oid conoid;
11948
- Oid refrelid;
11949
+ Oid refrelid = InvalidOid;
11949
11950
bool changed = false;
11950
11951
11951
11952
/* since this function recurses, it could be driven to stack overflow */
11952
11953
check_stack_depth();
11953
11954
11954
11955
currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11955
- conoid = currcon->oid;
11956
- refrelid = currcon->confrelid;
11956
+ if ( currcon->contype == CONSTRAINT_FOREIGN)
11957
+ refrelid = currcon->confrelid;
11957
11958
11958
11959
/*
11959
11960
* Update pg_constraint with the flags from cmdcon.
11960
11961
*
11961
11962
* If called to modify a constraint that's already in the desired state,
11962
11963
* silently do nothing.
11963
11964
*/
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))
11966
11968
{
11967
11969
HeapTuple copyTuple;
11968
11970
Form_pg_constraint copy_con;
@@ -11973,8 +11975,7 @@ ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11973
11975
copy_con->condeferred = cmdcon->initdeferred;
11974
11976
CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
11975
11977
11976
- InvokeObjectPostAlterHook(ConstraintRelationId,
11977
- conoid, 0);
11978
+ InvokeObjectPostAlterHook(ConstraintRelationId, currcon->oid, 0);
11978
11979
11979
11980
heap_freetuple(copyTuple);
11980
11981
changed = true;
@@ -11986,32 +11987,59 @@ ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11986
11987
* Now we need to update the multiple entries in pg_trigger that
11987
11988
* implement the constraint.
11988
11989
*/
11989
- AlterConstrTriggerDeferrability(conoid, tgrel, rel, cmdcon->deferrable,
11990
+ AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
11991
+ cmdcon->deferrable,
11990
11992
cmdcon->initdeferred, otherrelids);
11991
11993
}
11992
11994
11993
11995
/*
11994
11996
* 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.
11996
11998
*
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.
12000
12002
*/
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
+ }
12005
12033
12006
12034
return changed;
12007
12035
}
12008
12036
12009
12037
/*
12010
- * A subroutine of ATExecAlterConstrRecurse that updated constraint trigger's
12011
- * deferrability.
12038
+ * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12039
+ * trigger's deferrability.
12012
12040
*
12013
12041
* The arguments to this function have the same meaning as the arguments to
12014
- * ATExecAlterConstrRecurse .
12042
+ * ATExecAlterConstrDeferrability .
12015
12043
*/
12016
12044
static void
12017
12045
AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
@@ -12071,49 +12099,6 @@ AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
12071
12099
systable_endscan(tgscan);
12072
12100
}
12073
12101
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
-
12117
12102
/*
12118
12103
* ALTER TABLE VALIDATE CONSTRAINT
12119
12104
*
0 commit comments