Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gssMS-0003rw-Dx for pgsql-hackers@arkaria.postgresql.org; Sun, 10 Feb 2019 16:53:44 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1gssMR-0003Zx-8t for pgsql-hackers@arkaria.postgresql.org; Sun, 10 Feb 2019 16:53:43 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gssJI-0007yV-Nm for pgsql-hackers@lists.postgresql.org; Sun, 10 Feb 2019 16:50:29 +0000 Received: from sss.pgh.pa.us ([66.207.139.130]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gssJE-00080U-IC for pgsql-hackers@postgresql.org; Sun, 10 Feb 2019 16:50:27 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.14.4/8.14.4) with ESMTP id x1AGoJ9k024614; Sun, 10 Feb 2019 11:50:19 -0500 From: Tom Lane To: Peter Geoghegan cc: Alvaro Herrera , PostgreSQL Hackers Subject: Re: Fixing findDependentObjects()'s dependency on scan order (regressions in DROP diagnostic messages) In-reply-to: <27642.1549815178@sss.pgh.pa.us> References: <20190205212829.GA20127@alvherre.pgsql> <4572.1549404528@sss.pgh.pa.us> <20444.1549423295@sss.pgh.pa.us> <26049.1549516498@sss.pgh.pa.us> <18885.1549642539@sss.pgh.pa.us> <27642.1549815178@sss.pgh.pa.us> Comments: In-reply-to Tom Lane message dated "Sun, 10 Feb 2019 11:12:58 -0500" MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" Content-ID: <24581.1549817399.0@sss.pgh.pa.us> Date: Sun, 10 Feb 2019 11:50:19 -0500 Message-ID: <24613.1549817419@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset="us-ascii" Content-ID: <24581.1549817399.1@sss.pgh.pa.us> I wrote: > Peter Geoghegan writes: >>> [ invent separate primary and secondary partition dependencies? ] >> I lean towards changing these on HEAD, ... > Me too. Here's a version of the patch that does it that way. regards, tom lane ------- =_aaaaaaaaaa0 Content-Type: text/x-diff; name="fix-partition-dependencies-2.patch"; charset="us-ascii" Content-ID: <24581.1549817399.2@sss.pgh.pa.us> Content-Description: fix-partition-dependencies-2.patch Content-Transfer-Encoding: quoted-printable diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 6dd0700..ae69a9e 100644 *** a/doc/src/sgml/catalogs.sgml --- b/doc/src/sgml/catalogs.sgml *************** SCRAM-SHA-256$<iteration *** 2970,2976 **** referenced object, and should be automatically dropped (regardless of RESTRICT or CASCADE mode) if the referenced object is dropped. Example: a named ! constraint on a table is made autodependent on the table, so that it will go away if the table is dropped. --- 2970,2976 ---- referenced object, and should be automatically dropped (regardless of RESTRICT or CASCADE mode) if the referenced object is dropped. Example: a named ! constraint on a table is made auto-dependent on the table, so that it will go away if the table is dropped. *************** SCRAM-SHA-256$<iteration *** 2982,3019 **** The dependent object was created as part of creation of the referenced object, and is really just a part of its internal ! implementation. A DROP of the dependent objec= t ! will be disallowed outright (we'll tell the user to issue a ! DROP against the referenced object, instead). = A ! DROP of the referenced object will be propagat= ed ! through to drop the dependent object whether ! CASCADE is specified or not. Example: a trigg= er ! that's created to enforce a foreign-key constraint is made ! internally dependent on the constraint's ! pg_constraint entry. = ! DEPENDENCY_INTERNAL_AUTO (I) The dependent object was created as part of creation of the referenced object, and is really just a part of its internal ! implementation. A DROP of the dependent objec= t ! will be disallowed outright (we'll tell the user to issue a ! DROP against the referenced object, instead). ! While a regular internal dependency will prevent ! the dependent object from being dropped while any such dependenci= es ! remain, DEPENDENCY_INTERNAL_AUTO will allow su= ch ! a drop as long as the object can be found by following any of suc= h ! dependencies. ! Example: an index on a partition is made internal-auto-dependent = on ! both the partition itself as well as on the index on the parent ! partitioned table; so the partition index is dropped together wit= h ! either the partition it indexes, or with the parent index it is ! attached to. --- 2982,3052 ---- The dependent object was created as part of creation of the referenced object, and is really just a part of its internal ! implementation. A direct DROP of the dependen= t ! object will be disallowed outright (we'll tell the user to issue ! a DROP against the referenced object, instead)= . ! A DROP of the referenced object will result in ! automatically dropping the dependent object ! whether CASCADE is specified or not. If the ! dependent object is reached due to a dependency on some other obj= ect, ! the drop is converted to a drop of the referenced object, so ! that NORMAL and AUTO ! dependencies of the dependent object behave much like they were ! dependencies of the referenced object. ! Example: a view's ON SELECT rule is made ! internally dependent on the view, preventing it from being droppe= d ! while the view remains. Dependencies of the rule (such as tables= it ! refers to) act as if they were dependencies of the view. = ! DEPENDENCY_PARTITION_PRI (P) The dependent object was created as part of creation of the referenced object, and is really just a part of its internal ! implementation; however, unlike INTERNAL, ! there is more than one such referenced object. The dependent obj= ect ! must not be dropped unless at least one of these referenced objec= ts ! is dropped; if any one is, the dependent object should be dropped ! whether or not CASCADE is specified. Also ! unlike INTERNAL, a drop of some other object ! that the dependent object depends on does not result in automatic ! deletion of any partition-referenced object. Hence, if the drop ! does not cascade to at least one of these objects via some other ! path, it will be refused. (In most cases, the dependent object ! shares all its non-partition dependencies with at least one ! partition-referenced object, so that this restriction does not ! result in blocking any cascaded delete.) ! Note that partition dependencies are made in addition to, not ! instead of, any dependencies the object would normally have. Thi= s ! simplifies ATTACH/DETACH PARTITION operations: ! the partition dependencies need only be added or removed. ! Example: a child partitioned index is made partition-dependent ! on both the partition table it is on and the parent partitioned ! index, so that it goes away if either of those is dropped, but ! not otherwise. ! ! ! ! = ! ! DEPENDENCY_PARTITION_SEC (S) ! ! ! A secondary partition dependency acts identically = to ! a primary one, except that the primary dependency is preferential= ly ! referenced in error messages. An object should have at most one ! primary partition dependency, but there could perhaps be multiple ! secondary dependencies. ! Example: actually, we'll set up a child partitioned index with th= e ! parent partitioned index as primary partition dependency and the ! partition table as secondary partition dependency. In this way, ! if the user tries to drop the child partitioned index, the error ! message will suggest dropping the parent partitioned index instea= d ! (not the table). *************** SCRAM-SHA-256$<iteration *** 3026,3034 **** the referenced object (see pg_extension). The dependent object can be dropped only via ! DROP EXTENSION on the referenced object. Func= tionally ! this dependency type acts the same as an internal dependency, but ! it's kept separate for clarity and to simplify pg_du= mp. --- 3059,3068 ---- the referenced object (see pg_extension). The dependent object can be dropped only via ! DROP EXTENSION on the referenced object. ! Functionally this dependency type acts the same as ! an INTERNAL dependency, but it's kept separate= for ! clarity and to simplify pg_dump. *************** SCRAM-SHA-256$<iteration *** 3038,3047 **** The dependent object is not a member of the extension that is the ! referenced object (and so should not be ignored by pg_dump), but ! cannot function without it and should be dropped when the ! extension itself is. The dependent object may be dropped on its ! own as well. --- 3072,3084 ---- The dependent object is not a member of the extension that is the ! referenced object (and so it should not be ignored ! by pg_dump), but it cannot function ! without the extension and should be auto-dropped if the extension= is. ! The dependent object may be dropped on its own as well. ! Functionally this dependency type acts the same as ! an AUTO dependency, but it's kept separate for ! clarity and to simplify pg_dump. diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/depend= ency.c index 2c54895..e001334 100644 *** a/src/backend/catalog/dependency.c --- b/src/backend/catalog/dependency.c *************** typedef struct *** 99,107 **** #define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */ #define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */ #define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */ ! #define DEPFLAG_EXTENSION 0x0010 /* reached via extension dependency */ ! #define DEPFLAG_REVERSE 0x0020 /* reverse internal/extension link */ ! #define DEPFLAG_SUBOBJECT 0x0040 /* subobject of another deletable objec= t */ = = /* expansible list of ObjectAddresses */ --- 99,109 ---- #define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */ #define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */ #define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */ ! #define DEPFLAG_PARTITION 0x0010 /* reached via partition dependency */ ! #define DEPFLAG_EXTENSION 0x0020 /* reached via extension dependency */ ! #define DEPFLAG_REVERSE 0x0040 /* reverse internal/extension link */ ! #define DEPFLAG_IS_PART 0x0080 /* has a partition dependency */ ! #define DEPFLAG_SUBOBJECT 0x0100 /* subobject of another deletable objec= t */ = = /* expansible list of ObjectAddresses */ *************** findDependentObjects(const ObjectAddress *** 478,483 **** --- 480,487 ---- SysScanDesc scan; HeapTuple tup; ObjectAddress otherObject; + ObjectAddress owningObject; + ObjectAddress partitionObject; ObjectAddressAndFlags *dependentObjects; int numDependentObjects; int maxDependentObjects; *************** findDependentObjects(const ObjectAddress *** 547,552 **** --- 551,560 ---- scan =3D systable_beginscan(*depRel, DependDependerIndexId, true, NULL, nkeys, key); = + /* initialize variables that loop may fill */ + memset(&owningObject, 0, sizeof(owningObject)); + memset(&partitionObject, 0, sizeof(partitionObject)); + = while (HeapTupleIsValid(tup =3D systable_getnext(scan))) { Form_pg_depend foundDep =3D (Form_pg_depend) GETSTRUCT(tup); *************** findDependentObjects(const ObjectAddress *** 591,614 **** /* FALL THRU */ = case DEPENDENCY_INTERNAL: - case DEPENDENCY_INTERNAL_AUTO: = /* * This object is part of the internal implementation of * another object, or is part of the extension that is the * other object. We have three cases: * ! * 1. At the outermost recursion level, disallow the DROP. (We ! * just ereport here, rather than proceeding, since no other ! * dependencies are likely to be interesting.) However, if ! * the owning object is listed in pendingObjects, just release ! * the caller's lock and return; we'll eventually complete the ! * DROP when we reach that entry in the pending list. */ if (stack =3D=3D NULL) { - char *otherObjDesc; - = if (pendingObjects && object_address_present(&otherObject, pendingObjects)) { --- 599,624 ---- /* FALL THRU */ = case DEPENDENCY_INTERNAL: = /* * This object is part of the internal implementation of * another object, or is part of the extension that is the * other object. We have three cases: * ! * 1. At the outermost recursion level, we must disallow the ! * DROP. However, if the owning object is listed in ! * pendingObjects, just release the caller's lock and return; ! * we'll eventually complete the DROP when we reach that entry ! * in the pending list. ! * ! * Note: the above statement is true only if this pg_depend ! * entry still exists by then; in principle, therefore, we ! * could miss deleting an item the user told us to delete. ! * However, no inconsistency can result: since we're at outer ! * level, there is no object depending on this one. */ if (stack =3D=3D NULL) { if (pendingObjects && object_address_present(&otherObject, pendingObjects)) { *************** findDependentObjects(const ObjectAddress *** 617,630 **** ReleaseDeletionLock(object); return; } ! otherObjDesc =3D getObjectDescription(&otherObject); ! ereport(ERROR, ! (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), ! errmsg("cannot drop %s because %s requires it", ! getObjectDescription(object), ! otherObjDesc), ! errhint("You can drop %s instead.", ! otherObjDesc))); } = /* --- 627,643 ---- ReleaseDeletionLock(object); return; } ! = ! /* ! * We postpone actually issuing the error message until ! * after this loop, so that we can make the behavior ! * independent of the ordering of pg_depend entries, at ! * least so long as there's just one INTERNAL + EXTENSION ! * dependency. (If there's more, we'll complain about a ! * random one of them.) ! */ ! owningObject =3D otherObject; ! break; } = /* *************** findDependentObjects(const ObjectAddress *** 643,656 **** * transform this deletion request into a delete of this * owning object. * - * For INTERNAL_AUTO dependencies, we don't enforce this; in - * other words, we don't follow the links back to the owning - * object. - */ - if (foundDep->deptype =3D=3D DEPENDENCY_INTERNAL_AUTO) - break; - = - /* * First, release caller's lock on this object and get * deletion lock on the owning object. (We must release * caller's lock to avoid deadlock against a concurrent --- 656,661 ---- *************** findDependentObjects(const ObjectAddress *** 673,678 **** --- 678,690 ---- } = /* + * One way or the other, we're done with the scan; might as + * well close it down before recursing, to reduce peak + * resource consumption. + */ + systable_endscan(scan); + = + /* * Okay, recurse to the owning object instead of proceeding. * * We do not need to stack the current object; we want the *************** findDependentObjects(const ObjectAddress *** 690,699 **** targetObjects, pendingObjects, depRel); /* And we're done here. */ - systable_endscan(scan); return; = case DEPENDENCY_PIN: = /* --- 702,767 ---- targetObjects, pendingObjects, depRel); + = + /* + * The current target object should have been added to + * targetObjects while processing the owning object; but it + * probably got only the flag bits associated with the + * dependency we're looking at. We need to add the objflags + * that were passed to this recursion level, too, else we may + * get a bogus failure in reportDependentObjects (if, for + * example, we were called due to a partition dependency). + * + * If somehow the current object didn't get scheduled for + * deletion, bleat. (That would imply that somebody deleted + * this dependency record before the recursion got to it.) + * Another idea would be to reacquire lock on the current + * object and resume trying to delete it, but it seems not + * worth dealing with the race conditions inherent in that. + */ + if (!object_address_present_add_flags(object, objflags, + targetObjects)) + elog(ERROR, "deletion of owning object %s failed to delete %s", + getObjectDescription(&otherObject), + getObjectDescription(object)); + = /* And we're done here. */ return; = + case DEPENDENCY_PARTITION_PRI: + = + /* + * Remember that this object has a partition-type dependency. + * After the dependency scan, we'll complain if we didn't find + * a reason to delete one of its partition dependencies. + */ + objflags |=3D DEPFLAG_IS_PART; + = + /* + * Also remember the primary partition owner, for error + * messages. If there are multiple primary owners (which + * there should not be), we'll report a random one of them. + */ + partitionObject =3D otherObject; + break; + = + case DEPENDENCY_PARTITION_SEC: + = + /* + * Only use secondary partition owners in error messages if we + * find no primary owner (which probably shouldn't happen). + */ + if (!(objflags & DEPFLAG_IS_PART)) + partitionObject =3D otherObject; + = + /* + * Remember that this object has a partition-type dependency. + * After the dependency scan, we'll complain if we didn't find + * a reason to delete one of its partition dependencies. + */ + objflags |=3D DEPFLAG_IS_PART; + break; + = case DEPENDENCY_PIN: = /* *************** findDependentObjects(const ObjectAddress *** 713,718 **** --- 781,808 ---- systable_endscan(scan); = /* + * If we found an INTERNAL or EXTENSION dependency when we're at outer + * level, complain about it now. If we also found a PARTITION dependen= cy, + * we prefer to report the PARTITION dependency. This is arbitrary but + * seems to be more useful in practice. + */ + if (OidIsValid(owningObject.classId)) + { + char *otherObjDesc; + = + if (OidIsValid(partitionObject.classId)) + otherObjDesc =3D getObjectDescription(&partitionObject); + else + otherObjDesc =3D getObjectDescription(&owningObject); + = + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("cannot drop %s because %s requires it", + getObjectDescription(object), otherObjDesc), + errhint("You can drop %s instead.", otherObjDesc))); + } + = + /* * Next, identify all objects that directly depend on the current objec= t. * To ensure predictable deletion order, we collect them up in * dependentObjects and sort the list before actually recursing. (The *************** findDependentObjects(const ObjectAddress *** 789,798 **** case DEPENDENCY_AUTO_EXTENSION: subflags =3D DEPFLAG_AUTO; break; - case DEPENDENCY_INTERNAL_AUTO: case DEPENDENCY_INTERNAL: subflags =3D DEPFLAG_INTERNAL; break; case DEPENDENCY_EXTENSION: subflags =3D DEPFLAG_EXTENSION; break; --- 879,891 ---- case DEPENDENCY_AUTO_EXTENSION: subflags =3D DEPFLAG_AUTO; break; case DEPENDENCY_INTERNAL: subflags =3D DEPFLAG_INTERNAL; break; + case DEPENDENCY_PARTITION_PRI: + case DEPENDENCY_PARTITION_SEC: + subflags =3D DEPFLAG_PARTITION; + break; case DEPENDENCY_EXTENSION: subflags =3D DEPFLAG_EXTENSION; break; *************** findDependentObjects(const ObjectAddress *** 868,877 **** /* * Finally, we can add the target object to targetObjects. Be careful = to * include any flags that were passed back down to us from inner recurs= ion ! * levels. */ extra.flags =3D mystack.flags; ! if (stack) extra.dependee =3D *stack->object; else memset(&extra.dependee, 0, sizeof(extra.dependee)); --- 961,975 ---- /* * Finally, we can add the target object to targetObjects. Be careful = to * include any flags that were passed back down to us from inner recurs= ion ! * levels. Record the "dependee" as being either the most important ! * partition owner if there is one, else the object we recursed from, i= f ! * any. (The logic in reportDependentObjects() is such that it can onl= y ! * need one of those objects.) */ extra.flags =3D mystack.flags; ! if (extra.flags & DEPFLAG_IS_PART) ! extra.dependee =3D partitionObject; ! else if (stack) extra.dependee =3D *stack->object; else memset(&extra.dependee, 0, sizeof(extra.dependee)); *************** reportDependentObjects(const ObjectAddre *** 906,913 **** int i; = /* * If no error is to be thrown, and the msglevel is too low to be shown= to ! * either client or server log, there's no need to do any of the work. * * Note: this code doesn't know all there is to be known about elog * levels, but it works for NOTICE and DEBUG2, which are the only value= s --- 1004,1040 ---- int i; = /* + * If we need to delete any partition-dependent objects, make sure that + * we're deleting at least one of their partition dependencies, too. Th= at + * can be detected by checking that we reached them by a PARTITION + * dependency at some point. + * + * We just report the first such object, as in most cases the only way = to + * trigger this complaint is to explicitly try to delete one partition = of + * a partitioned object. + */ + for (i =3D 0; i < targetObjects->numrefs; i++) + { + const ObjectAddressExtra *extra =3D &targetObjects->extras[i]; + = + if ((extra->flags & DEPFLAG_IS_PART) && + !(extra->flags & DEPFLAG_PARTITION)) + { + const ObjectAddress *object =3D &targetObjects->refs[i]; + char *otherObjDesc =3D getObjectDescription(&extra->dependee); + = + ereport(ERROR, + (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST), + errmsg("cannot drop %s because %s requires it", + getObjectDescription(object), otherObjDesc), + errhint("You can drop %s instead.", otherObjDesc))); + } + } + = + /* * If no error is to be thrown, and the msglevel is too low to be shown= to ! * either client or server log, there's no need to do any of the rest o= f ! * the work. * * Note: this code doesn't know all there is to be known about elog * levels, but it works for NOTICE and DEBUG2, which are the only value= s *************** reportDependentObjects(const ObjectAddre *** 951,961 **** = /* * If, at any stage of the recursive search, we reached the object via ! * an AUTO, INTERNAL, or EXTENSION dependency, then it's okay to ! * delete it even in RESTRICT mode. */ if (extra->flags & (DEPFLAG_AUTO | DEPFLAG_INTERNAL | DEPFLAG_EXTENSION)) { /* --- 1078,1089 ---- = /* * If, at any stage of the recursive search, we reached the object via ! * an AUTO, INTERNAL, PARTITION, or EXTENSION dependency, then it's ! * okay to delete it even in RESTRICT mode. */ if (extra->flags & (DEPFLAG_AUTO | DEPFLAG_INTERNAL | + DEPFLAG_PARTITION | DEPFLAG_EXTENSION)) { /* diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index faf6956..d16c3d0 100644 *** a/src/backend/catalog/index.c --- b/src/backend/catalog/index.c *************** index_create(Relation heapRelation, *** 1041,1049 **** else { bool have_simple_col =3D false; - DependencyType deptype; - = - deptype =3D OidIsValid(parentIndexRelid) ? DEPENDENCY_INTERNAL_AUTO := DEPENDENCY_AUTO; = /* Create auto dependencies on simply-referenced columns */ for (i =3D 0; i < indexInfo->ii_NumIndexAttrs; i++) --- 1041,1046 ---- *************** index_create(Relation heapRelation, *** 1054,1060 **** referenced.objectId =3D heapRelationId; referenced.objectSubId =3D indexInfo->ii_IndexAttrNumbers[i]; = ! recordDependencyOn(&myself, &referenced, deptype); = have_simple_col =3D true; } --- 1051,1057 ---- referenced.objectId =3D heapRelationId; referenced.objectSubId =3D indexInfo->ii_IndexAttrNumbers[i]; = ! recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); = have_simple_col =3D true; } *************** index_create(Relation heapRelation, *** 1072,1089 **** referenced.objectId =3D heapRelationId; referenced.objectSubId =3D 0; = ! recordDependencyOn(&myself, &referenced, deptype); } } = ! /* Store dependency on parent index, if any */ if (OidIsValid(parentIndexRelid)) { referenced.classId =3D RelationRelationId; referenced.objectId =3D parentIndexRelid; referenced.objectSubId =3D 0; = ! recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL_AUTO); } = /* Store dependency on collations */ --- 1069,1097 ---- referenced.objectId =3D heapRelationId; referenced.objectSubId =3D 0; = ! recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); } } = ! /* ! * If this is an index partition, create partition dependencies on ! * both the parent index and the table. (Note: these must be *in ! * addition to*, not instead of, all other dependencies. Otherwise ! * we'll be short some dependencies after DETACH PARTITION.) ! */ if (OidIsValid(parentIndexRelid)) { referenced.classId =3D RelationRelationId; referenced.objectId =3D parentIndexRelid; referenced.objectSubId =3D 0; = ! recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); ! = ! referenced.classId =3D RelationRelationId; ! referenced.objectId =3D heapRelationId; ! referenced.objectSubId =3D 0; ! = ! recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC); } = /* Store dependency on collations */ *************** index_constraint_create(Relation heapRel *** 1342,1356 **** recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); = /* ! * Also, if this is a constraint on a partition, mark it as depending o= n ! * the constraint in the parent. */ if (OidIsValid(parentConstraintId)) { ! ObjectAddress parentConstr; ! = ! ObjectAddressSet(parentConstr, ConstraintRelationId, parentConstraintI= d); ! recordDependencyOn(&referenced, &parentConstr, DEPENDENCY_INTERNAL_AUT= O); } = /* --- 1350,1366 ---- recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); = /* ! * Also, if this is a constraint on a partition, give it partition-type ! * dependencies on the parent constraint as well as the table. */ if (OidIsValid(parentConstraintId)) { ! ObjectAddressSet(myself, ConstraintRelationId, conOid); ! ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId)= ; ! recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); ! ObjectAddressSet(referenced, RelationRelationId, ! RelationGetRelid(heapRelation)); ! recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC); } = /* diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_= constraint.c index 698b493..ad836e0 100644 *** a/src/backend/catalog/pg_constraint.c --- b/src/backend/catalog/pg_constraint.c *************** AlterConstraintNamespaces(Oid ownerId, O *** 760,772 **** = /* * ConstraintSetParentConstraint ! * Set a partition's constraint as child of its parent table's * * This updates the constraint's pg_constraint row to show it as inherit= ed, and ! * add a dependency to the parent so that it cannot be removed on its ow= n. */ void ! ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId) { Relation constrRel; Form_pg_constraint constrForm; --- 760,776 ---- = /* * ConstraintSetParentConstraint ! * Set a partition's constraint as child of its parent constraint, ! * or remove the linkage if parentConstrId is InvalidOid. * * This updates the constraint's pg_constraint row to show it as inherit= ed, and ! * adds PARTITION dependencies to prevent the constraint from being dele= ted ! * on its own. Alternatively, reverse that. */ void ! ConstraintSetParentConstraint(Oid childConstrId, ! Oid parentConstrId, ! Oid childTableId) { Relation constrRel; Form_pg_constraint constrForm; *************** ConstraintSetParentConstraint(Oid childC *** 795,804 **** = CatalogTupleUpdate(constrRel, &tuple->t_self, newtup); = - ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId); ObjectAddressSet(depender, ConstraintRelationId, childConstrId); = ! recordDependencyOn(&depender, &referenced, DEPENDENCY_INTERNAL_AUTO); } else { --- 799,811 ---- = CatalogTupleUpdate(constrRel, &tuple->t_self, newtup); = ObjectAddressSet(depender, ConstraintRelationId, childConstrId); = ! ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId); ! recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI); ! = ! ObjectAddressSet(referenced, RelationRelationId, childTableId); ! recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC); } else { *************** ConstraintSetParentConstraint(Oid childC *** 809,818 **** /* Make sure there's no further inheritance. */ Assert(constrForm->coninhcount =3D=3D 0); = deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId, ConstraintRelationId, ! DEPENDENCY_INTERNAL_AUTO); ! CatalogTupleUpdate(constrRel, &tuple->t_self, newtup); } = ReleaseSysCache(tuple); --- 816,829 ---- /* Make sure there's no further inheritance. */ Assert(constrForm->coninhcount =3D=3D 0); = + CatalogTupleUpdate(constrRel, &tuple->t_self, newtup); + = deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId, ConstraintRelationId, ! DEPENDENCY_PARTITION_PRI); ! deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId, ! RelationRelationId, ! DEPENDENCY_PARTITION_SEC); } = ReleaseSysCache(tuple); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/index= cmds.c index bd85099..7352b9e 100644 *** a/src/backend/commands/indexcmds.c --- b/src/backend/commands/indexcmds.c *************** DefineIndex(Oid relationId, *** 971,977 **** IndexSetParentIndex(cldidx, indexRelationId); if (createdConstraintId !=3D InvalidOid) ConstraintSetParentConstraint(cldConstrOid, ! createdConstraintId); = if (!cldidx->rd_index->indisvalid) invalidate_parent =3D true; --- 971,978 ---- IndexSetParentIndex(cldidx, indexRelationId); if (createdConstraintId !=3D InvalidOid) ConstraintSetParentConstraint(cldConstrOid, ! createdConstraintId, ! childRelid); = if (!cldidx->rd_index->indisvalid) invalidate_parent =3D true; *************** IndexSetParentIndex(Relation partitionId *** 2622,2656 **** = if (fix_dependencies) { - ObjectAddress partIdx; - = /* ! * Insert/delete pg_depend rows. If setting a parent, add an ! * INTERNAL_AUTO dependency to the parent index; if making standalone, ! * remove all existing rows and put back the regular dependency on the ! * table. */ - ObjectAddressSet(partIdx, RelationRelationId, partRelid); - = if (OidIsValid(parentOid)) { ObjectAddress parentIdx; = ObjectAddressSet(parentIdx, RelationRelationId, parentOid); ! recordDependencyOn(&partIdx, &parentIdx, DEPENDENCY_INTERNAL_AUTO); } else { - ObjectAddress partitionTbl; - = - ObjectAddressSet(partitionTbl, RelationRelationId, - partitionIdx->rd_index->indrelid); - = deleteDependencyRecordsForClass(RelationRelationId, partRelid, RelationRelationId, ! DEPENDENCY_INTERNAL_AUTO); ! = ! recordDependencyOn(&partIdx, &partitionTbl, DEPENDENCY_AUTO); } = /* make our updates visible */ --- 2623,2656 ---- = if (fix_dependencies) { /* ! * Insert/delete pg_depend rows. If setting a parent, add PARTITION ! * dependencies on the parent index and the table; if removing a ! * parent, delete PARTITION dependencies. */ if (OidIsValid(parentOid)) { + ObjectAddress partIdx; ObjectAddress parentIdx; + ObjectAddress partitionTbl; = + ObjectAddressSet(partIdx, RelationRelationId, partRelid); ObjectAddressSet(parentIdx, RelationRelationId, parentOid); ! ObjectAddressSet(partitionTbl, RelationRelationId, ! partitionIdx->rd_index->indrelid); ! recordDependencyOn(&partIdx, &parentIdx, ! DEPENDENCY_PARTITION_PRI); ! recordDependencyOn(&partIdx, &partitionTbl, ! DEPENDENCY_PARTITION_SEC); } else { deleteDependencyRecordsForClass(RelationRelationId, partRelid, RelationRelationId, ! DEPENDENCY_PARTITION_PRI); ! deleteDependencyRecordsForClass(RelationRelationId, partRelid, ! RelationRelationId, ! DEPENDENCY_PARTITION_SEC); } = /* make our updates visible */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/table= cmds.c index b66d194..715c6a2 100644 *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** CloneFkReferencing(Relation pg_constrain *** 7825,7831 **** bool attach_it; Oid constrOid; ObjectAddress parentAddr, ! childAddr; ListCell *cell; int i; = --- 7825,7832 ---- bool attach_it; Oid constrOid; ObjectAddress parentAddr, ! childAddr, ! childTableAddr; ListCell *cell; int i; = *************** CloneFkReferencing(Relation pg_constrain *** 7966,7972 **** systable_endscan(scan); table_close(trigrel, RowExclusiveLock); = ! ConstraintSetParentConstraint(fk->conoid, parentConstrOid); CommandCounterIncrement(); attach_it =3D true; break; --- 7967,7974 ---- systable_endscan(scan); table_close(trigrel, RowExclusiveLock); = ! ConstraintSetParentConstraint(fk->conoid, parentConstrOid, ! RelationGetRelid(partRel)); CommandCounterIncrement(); attach_it =3D true; break; *************** CloneFkReferencing(Relation pg_constrain *** 8013,8020 **** 1, false, true); subclone =3D lappend_oid(subclone, constrOid); = ObjectAddressSet(childAddr, ConstraintRelationId, constrOid); ! recordDependencyOn(&childAddr, &parentAddr, DEPENDENCY_INTERNAL_AUTO); = fkconstraint =3D makeNode(Constraint); /* for now this is all we need */ --- 8015,8028 ---- 1, false, true); subclone =3D lappend_oid(subclone, constrOid); = + /* Set up partition dependencies for the new constraint */ ObjectAddressSet(childAddr, ConstraintRelationId, constrOid); ! recordDependencyOn(&childAddr, &parentAddr, ! DEPENDENCY_PARTITION_PRI); ! ObjectAddressSet(childTableAddr, RelationRelationId, ! RelationGetRelid(partRel)); ! recordDependencyOn(&childAddr, &childTableAddr, ! DEPENDENCY_PARTITION_SEC); = fkconstraint =3D makeNode(Constraint); /* for now this is all we need */ *************** AttachPartitionEnsureIndexes(Relation re *** 14893,14899 **** /* bingo. */ IndexSetParentIndex(attachrelIdxRels[i], idx); if (OidIsValid(constraintOid)) ! ConstraintSetParentConstraint(cldConstrOid, constraintOid); update_relispartition(NULL, cldIdxId, true); found =3D true; break; --- 14901,14908 ---- /* bingo. */ IndexSetParentIndex(attachrelIdxRels[i], idx); if (OidIsValid(constraintOid)) ! ConstraintSetParentConstraint(cldConstrOid, constraintOid, ! RelationGetRelid(attachrel)); update_relispartition(NULL, cldIdxId, true); found =3D true; break; *************** ATExecDetachPartition(Relation rel, Rang *** 15151,15157 **** constrOid =3D get_relation_idx_constraint_oid(RelationGetRelid(partRel= ), idxid); if (OidIsValid(constrOid)) ! ConstraintSetParentConstraint(constrOid, InvalidOid); = index_close(idx, NoLock); } --- 15160,15166 ---- constrOid =3D get_relation_idx_constraint_oid(RelationGetRelid(partRel= ), idxid); if (OidIsValid(constrOid)) ! ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid); = index_close(idx, NoLock); } *************** ATExecDetachPartition(Relation rel, Rang *** 15183,15189 **** } = /* unset conparentid and adjust conislocal, coninhcount, etc. */ ! ConstraintSetParentConstraint(fk->conoid, InvalidOid); = /* * Make the action triggers on the referenced relation. When this was --- 15192,15198 ---- } = /* unset conparentid and adjust conislocal, coninhcount, etc. */ ! ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid); = /* * Make the action triggers on the referenced relation. When this was *************** ATExecAttachPartitionIdx(List **wqueue, = *** 15419,15425 **** /* All good -- do it */ IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx)); if (OidIsValid(constraintOid)) ! ConstraintSetParentConstraint(cldConstrId, constraintOid); update_relispartition(NULL, partIdxId, true); = pfree(attmap); --- 15428,15435 ---- /* All good -- do it */ IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx)); if (OidIsValid(constraintOid)) ! ConstraintSetParentConstraint(cldConstrId, constraintOid, ! RelationGetRelid(partTbl)); update_relispartition(NULL, partIdxId, true); = pfree(attmap); diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger= .c index 0b245a6..409bee2 100644 *** a/src/backend/commands/trigger.c --- b/src/backend/commands/trigger.c *************** CreateTrigger(CreateTrigStmt *stmt, cons *** 1012,1028 **** * User CREATE TRIGGER, so place dependencies. We make trigger be * auto-dropped if its relation is dropped or if the FK relation is * dropped. (Auto drop is compatible with our pre-7.3 behavior.) - * - * Exception: if this trigger comes from a parent partitioned table, - * then it's not separately drop-able, but goes away if the partition - * does. */ referenced.classId =3D RelationRelationId; referenced.objectId =3D RelationGetRelid(rel); referenced.objectSubId =3D 0; ! recordDependencyOn(&myself, &referenced, OidIsValid(parentTriggerOid) = ? ! DEPENDENCY_INTERNAL_AUTO : ! DEPENDENCY_AUTO); = if (OidIsValid(constrrelid)) { --- 1012,1022 ---- * User CREATE TRIGGER, so place dependencies. We make trigger be * auto-dropped if its relation is dropped or if the FK relation is * dropped. (Auto drop is compatible with our pre-7.3 behavior.) */ referenced.classId =3D RelationRelationId; referenced.objectId =3D RelationGetRelid(rel); referenced.objectSubId =3D 0; ! recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); = if (OidIsValid(constrrelid)) { *************** CreateTrigger(CreateTrigStmt *stmt, cons *** 1046,1056 **** recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } = ! /* Depends on the parent trigger, if there is one. */ if (OidIsValid(parentTriggerOid)) { ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid); ! recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL_AUTO); } } = --- 1040,1054 ---- recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); } = ! /* ! * If it's a partition trigger, create the partition dependencies. ! */ if (OidIsValid(parentTriggerOid)) { ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid); ! recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); ! ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel= )); ! recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC); } } = diff --git a/src/include/catalog/dependency.h b/src/include/catalog/depend= ency.h index 5dea270..b235a23 100644 *** a/src/include/catalog/dependency.h --- b/src/include/catalog/dependency.h *************** *** 24,87 **** * * In all cases, a dependency relationship indicates that the referenced * object may not be dropped without also dropping the dependent object. ! * However, there are several subflavors: ! * ! * DEPENDENCY_NORMAL ('n'): normal relationship between separately-creat= ed ! * objects. The dependent object may be dropped without affecting the ! * referenced object. The referenced object may only be dropped by ! * specifying CASCADE, in which case the dependent object is dropped too= . ! * Example: a table column has a normal dependency on its datatype. ! * ! * DEPENDENCY_AUTO ('a'): the dependent object can be dropped separately ! * from the referenced object, and should be automatically dropped ! * (regardless of RESTRICT or CASCADE mode) if the referenced object ! * is dropped. ! * Example: a named constraint on a table is made auto-dependent on ! * the table, so that it will go away if the table is dropped. ! * ! * DEPENDENCY_INTERNAL ('i'): the dependent object was created as part ! * of creation of the referenced object, and is really just a part of ! * its internal implementation. A DROP of the dependent object will be ! * disallowed outright (we'll tell the user to issue a DROP against the ! * referenced object, instead). A DROP of the referenced object will be ! * propagated through to drop the dependent object whether CASCADE is ! * specified or not. ! * Example: a trigger that's created to enforce a foreign-key constraint ! * is made internally dependent on the constraint's pg_constraint entry. ! * ! * DEPENDENCY_INTERNAL_AUTO ('I'): the dependent object was created as ! * part of creation of the referenced object, and is really just a part ! * of its internal implementation. A DROP of the dependent object will ! * be disallowed outright (we'll tell the user to issue a DROP against t= he ! * referenced object, instead). While a regular internal dependency wil= l ! * prevent the dependent object from being dropped while any such ! * dependencies remain, DEPENDENCY_INTERNAL_AUTO will allow such a drop = as ! * long as the object can be found by following any of such dependencies= . ! * Example: an index on a partition is made internal-auto-dependent on ! * both the partition itself as well as on the index on the parent ! * partitioned table; so the partition index is dropped together with ! * either the partition it indexes, or with the parent index it is attac= hed ! * to. ! = ! * DEPENDENCY_EXTENSION ('e'): the dependent object is a member of the ! * extension that is the referenced object. The dependent object can be ! * dropped only via DROP EXTENSION on the referenced object. Functional= ly ! * this dependency type acts the same as an internal dependency, but it'= s ! * kept separate for clarity and to simplify pg_dump. ! * ! * DEPENDENCY_AUTO_EXTENSION ('x'): the dependent object is not a member ! * of the extension that is the referenced object (and so should not be ! * ignored by pg_dump), but cannot function without the extension and ! * should be dropped when the extension itself is. The dependent object ! * may be dropped on its own as well. ! * ! * DEPENDENCY_PIN ('p'): there is no dependent object; this type of entr= y ! * is a signal that the system itself depends on the referenced object, ! * and so that object must never be deleted. Entries of this type are ! * created only during initdb. The fields for the dependent object ! * contain zeroes. ! * ! * Other dependency flavors may be needed in future. */ = typedef enum DependencyType --- 24,31 ---- * * In all cases, a dependency relationship indicates that the referenced * object may not be dropped without also dropping the dependent object. ! * However, there are several subflavors; see the description of pg_depe= nd ! * in catalogs.sgml for details. */ = typedef enum DependencyType *************** typedef enum DependencyType *** 89,95 **** DEPENDENCY_NORMAL =3D 'n', DEPENDENCY_AUTO =3D 'a', DEPENDENCY_INTERNAL =3D 'i', ! DEPENDENCY_INTERNAL_AUTO =3D 'I', DEPENDENCY_EXTENSION =3D 'e', DEPENDENCY_AUTO_EXTENSION =3D 'x', DEPENDENCY_PIN =3D 'p' --- 33,40 ---- DEPENDENCY_NORMAL =3D 'n', DEPENDENCY_AUTO =3D 'a', DEPENDENCY_INTERNAL =3D 'i', ! DEPENDENCY_PARTITION_PRI =3D 'P', ! DEPENDENCY_PARTITION_SEC =3D 'S', DEPENDENCY_EXTENSION =3D 'e', DEPENDENCY_AUTO_EXTENSION =3D 'x', DEPENDENCY_PIN =3D 'p' diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_= constraint.h index 55a2694..c87bded 100644 *** a/src/include/catalog/pg_constraint.h --- b/src/include/catalog/pg_constraint.h *************** extern char *ChooseConstraintName(const = *** 239,245 **** extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved); extern void ConstraintSetParentConstraint(Oid childConstrId, ! Oid parentConstrId); extern Oid get_relation_constraint_oid(Oid relid, const char *conname, b= ool missing_ok); extern Bitmapset *get_relation_constraint_attnos(Oid relid, const char *= conname, bool missing_ok, Oid *constraintOid); --- 239,246 ---- extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved); extern void ConstraintSetParentConstraint(Oid childConstrId, ! Oid parentConstrId, ! Oid childTableId); extern Oid get_relation_constraint_oid(Oid relid, const char *conname, b= ool missing_ok); extern Bitmapset *get_relation_constraint_attnos(Oid relid, const char *= conname, bool missing_ok, Oid *constraintOid); ------- =_aaaaaaaaaa0--