I wrote:
> Peter Geoghegan <p...@bowt.ie> 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

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$<replaceable>&lt;iteration
*** 2970,2976 ****
         referenced object, and should be automatically dropped
         (regardless of <literal>RESTRICT</literal> or <literal>CASCADE</literal>
         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.
        </para>
       </listitem>
--- 2970,2976 ----
         referenced object, and should be automatically dropped
         (regardless of <literal>RESTRICT</literal> or <literal>CASCADE</literal>
         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.
        </para>
       </listitem>
*************** SCRAM-SHA-256$<replaceable>&lt;iteration
*** 2982,3019 ****
        <para>
         The dependent object was created as part of creation of the
         referenced object, and is really just a part of its internal
!        implementation.  A <command>DROP</command> of the dependent object
!        will be disallowed outright (we'll tell the user to issue a
!        <command>DROP</command> against the referenced object, instead).  A
!        <command>DROP</command> of the referenced object will be propagated
!        through to drop the dependent object whether
!        <command>CASCADE</command> is specified or not.  Example: a trigger
!        that's created to enforce a foreign-key constraint is made
!        internally dependent on the constraint's
!        <structname>pg_constraint</structname> entry.
        </para>
       </listitem>
      </varlistentry>
  
      <varlistentry>
!      <term><symbol>DEPENDENCY_INTERNAL_AUTO</symbol> (<literal>I</literal>)</term>
       <listitem>
        <para>
         The dependent object was created as part of creation of the
         referenced object, and is really just a part of its internal
!        implementation.  A <command>DROP</command> of the dependent object
!        will be disallowed outright (we'll tell the user to issue a
!        <command>DROP</command> against the referenced object, instead).
!        While a regular internal dependency will prevent
!        the dependent object from being dropped while any such dependencies
!        remain, <literal>DEPENDENCY_INTERNAL_AUTO</literal> 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
!        attached to.
        </para>
       </listitem>
      </varlistentry>
--- 2982,3052 ----
        <para>
         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 <command>DROP</command> of the dependent
!        object will be disallowed outright (we'll tell the user to issue
!        a <command>DROP</command> against the referenced object, instead).
!        A <command>DROP</command> of the referenced object will result in
!        automatically dropping the dependent object
!        whether <literal>CASCADE</literal> is specified or not.  If the
!        dependent object is reached due to a dependency on some other object,
!        the drop is converted to a drop of the referenced object, so
!        that <literal>NORMAL</literal> and <literal>AUTO</literal>
!        dependencies of the dependent object behave much like they were
!        dependencies of the referenced object.
!        Example: a view's <literal>ON SELECT</literal> rule is made
!        internally dependent on the view, preventing it from being dropped
!        while the view remains.  Dependencies of the rule (such as tables it
!        refers to) act as if they were dependencies of the view.
        </para>
       </listitem>
      </varlistentry>
  
      <varlistentry>
!      <term><symbol>DEPENDENCY_PARTITION_PRI</symbol> (<literal>P</literal>)</term>
       <listitem>
        <para>
         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 <literal>INTERNAL</literal>,
!        there is more than one such referenced object.  The dependent object
!        must not be dropped unless at least one of these referenced objects
!        is dropped; if any one is, the dependent object should be dropped
!        whether or not <literal>CASCADE</literal> is specified.  Also
!        unlike <literal>INTERNAL</literal>, 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.  This
!        simplifies <command>ATTACH/DETACH PARTITION</command> 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.
!       </para>
!      </listitem>
!     </varlistentry>
! 
!     <varlistentry>
!      <term><symbol>DEPENDENCY_PARTITION_SEC</symbol> (<literal>S</literal>)</term>
!      <listitem>
!       <para>
!        A <quote>secondary</quote> partition dependency acts identically to
!        a primary one, except that the primary dependency is preferentially
!        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 the
!        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 instead
!        (not the table).
        </para>
       </listitem>
      </varlistentry>
*************** SCRAM-SHA-256$<replaceable>&lt;iteration
*** 3026,3034 ****
         the referenced object (see
         <link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>).
         The dependent object can be dropped only via
!        <command>DROP EXTENSION</command> 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 <application>pg_dump</application>.
        </para>
       </listitem>
      </varlistentry>
--- 3059,3068 ----
         the referenced object (see
         <link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>).
         The dependent object can be dropped only via
!        <command>DROP EXTENSION</command> on the referenced object.
!        Functionally this dependency type acts the same as
!        an <literal>INTERNAL</literal> dependency, but it's kept separate for
!        clarity and to simplify <application>pg_dump</application>.
        </para>
       </listitem>
      </varlistentry>
*************** SCRAM-SHA-256$<replaceable>&lt;iteration
*** 3038,3047 ****
       <listitem>
        <para>
         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.
        </para>
       </listitem>
      </varlistentry>
--- 3072,3084 ----
       <listitem>
        <para>
         The dependent object is not a member of the extension that is the
!        referenced object (and so it should not be ignored
!        by <application>pg_dump</application>), 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 <literal>AUTO</literal> dependency, but it's kept separate for
!        clarity and to simplify <application>pg_dump</application>.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.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 object */
  
  
  /* 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 object */
  
  
  /* 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 = 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 = systable_getnext(scan)))
  	{
  		Form_pg_depend foundDep = (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 == 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 == NULL)
  				{
  					if (pendingObjects &&
  						object_address_present(&otherObject, pendingObjects))
  					{
*************** findDependentObjects(const ObjectAddress
*** 617,630 ****
  						ReleaseDeletionLock(object);
  						return;
  					}
! 					otherObjDesc = 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 = 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 == 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 |= 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 = 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 = 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 |= 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 dependency,
+ 	 * 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 = getObjectDescription(&partitionObject);
+ 		else
+ 			otherObjDesc = 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 object.
  	 * 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 = DEPFLAG_AUTO;
  				break;
- 			case DEPENDENCY_INTERNAL_AUTO:
  			case DEPENDENCY_INTERNAL:
  				subflags = DEPFLAG_INTERNAL;
  				break;
  			case DEPENDENCY_EXTENSION:
  				subflags = DEPFLAG_EXTENSION;
  				break;
--- 879,891 ----
  			case DEPENDENCY_AUTO_EXTENSION:
  				subflags = DEPFLAG_AUTO;
  				break;
  			case DEPENDENCY_INTERNAL:
  				subflags = DEPFLAG_INTERNAL;
  				break;
+ 			case DEPENDENCY_PARTITION_PRI:
+ 			case DEPENDENCY_PARTITION_SEC:
+ 				subflags = DEPFLAG_PARTITION;
+ 				break;
  			case DEPENDENCY_EXTENSION:
  				subflags = 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 recursion
! 	 * levels.
  	 */
  	extra.flags = mystack.flags;
! 	if (stack)
  		extra.dependee = *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 recursion
! 	 * levels.  Record the "dependee" as being either the most important
! 	 * partition owner if there is one, else the object we recursed from, if
! 	 * any.  (The logic in reportDependentObjects() is such that it can only
! 	 * need one of those objects.)
  	 */
  	extra.flags = mystack.flags;
! 	if (extra.flags & DEPFLAG_IS_PART)
! 		extra.dependee = partitionObject;
! 	else if (stack)
  		extra.dependee = *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 values
--- 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. That
+ 	 * 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 = 0; i < targetObjects->numrefs; i++)
+ 	{
+ 		const ObjectAddressExtra *extra = &targetObjects->extras[i];
+ 
+ 		if ((extra->flags & DEPFLAG_IS_PART) &&
+ 			!(extra->flags & DEPFLAG_PARTITION))
+ 		{
+ 			const ObjectAddress *object = &targetObjects->refs[i];
+ 			char	   *otherObjDesc = 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 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 values
*************** 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 = false;
- 			DependencyType deptype;
- 
- 			deptype = OidIsValid(parentIndexRelid) ? DEPENDENCY_INTERNAL_AUTO : DEPENDENCY_AUTO;
  
  			/* Create auto dependencies on simply-referenced columns */
  			for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
--- 1041,1046 ----
*************** index_create(Relation heapRelation,
*** 1054,1060 ****
  					referenced.objectId = heapRelationId;
  					referenced.objectSubId = indexInfo->ii_IndexAttrNumbers[i];
  
! 					recordDependencyOn(&myself, &referenced, deptype);
  
  					have_simple_col = true;
  				}
--- 1051,1057 ----
  					referenced.objectId = heapRelationId;
  					referenced.objectSubId = indexInfo->ii_IndexAttrNumbers[i];
  
! 					recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
  
  					have_simple_col = true;
  				}
*************** index_create(Relation heapRelation,
*** 1072,1089 ****
  				referenced.objectId = heapRelationId;
  				referenced.objectSubId = 0;
  
! 				recordDependencyOn(&myself, &referenced, deptype);
  			}
  		}
  
! 		/* Store dependency on parent index, if any */
  		if (OidIsValid(parentIndexRelid))
  		{
  			referenced.classId = RelationRelationId;
  			referenced.objectId = parentIndexRelid;
  			referenced.objectSubId = 0;
  
! 			recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL_AUTO);
  		}
  
  		/* Store dependency on collations */
--- 1069,1097 ----
  				referenced.objectId = heapRelationId;
  				referenced.objectSubId = 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 = RelationRelationId;
  			referenced.objectId = parentIndexRelid;
  			referenced.objectSubId = 0;
  
! 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
! 
! 			referenced.classId = RelationRelationId;
! 			referenced.objectId = heapRelationId;
! 			referenced.objectSubId = 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 on
! 	 * the constraint in the parent.
  	 */
  	if (OidIsValid(parentConstraintId))
  	{
! 		ObjectAddress parentConstr;
! 
! 		ObjectAddressSet(parentConstr, ConstraintRelationId, parentConstraintId);
! 		recordDependencyOn(&referenced, &parentConstr, DEPENDENCY_INTERNAL_AUTO);
  	}
  
  	/*
--- 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 inherited, and
!  * add a dependency to the parent so that it cannot be removed on its own.
   */
  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 inherited, and
!  * adds PARTITION dependencies to prevent the constraint from being deleted
!  * 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 == 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 == 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/indexcmds.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 != InvalidOid)
  							ConstraintSetParentConstraint(cldConstrOid,
! 														  createdConstraintId);
  
  						if (!cldidx->rd_index->indisvalid)
  							invalidate_parent = true;
--- 971,978 ----
  						IndexSetParentIndex(cldidx, indexRelationId);
  						if (createdConstraintId != InvalidOid)
  							ConstraintSetParentConstraint(cldConstrOid,
! 														  createdConstraintId,
! 														  childRelid);
  
  						if (!cldidx->rd_index->indisvalid)
  							invalidate_parent = 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/tablecmds.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 = true;
  			break;
--- 7967,7974 ----
  			systable_endscan(scan);
  			table_close(trigrel, RowExclusiveLock);
  
! 			ConstraintSetParentConstraint(fk->conoid, parentConstrOid,
! 										  RelationGetRelid(partRel));
  			CommandCounterIncrement();
  			attach_it = true;
  			break;
*************** CloneFkReferencing(Relation pg_constrain
*** 8013,8020 ****
  								  1, false, true);
  		subclone = lappend_oid(subclone, constrOid);
  
  		ObjectAddressSet(childAddr, ConstraintRelationId, constrOid);
! 		recordDependencyOn(&childAddr, &parentAddr, DEPENDENCY_INTERNAL_AUTO);
  
  		fkconstraint = makeNode(Constraint);
  		/* for now this is all we need */
--- 8015,8028 ----
  								  1, false, true);
  		subclone = 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 = 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 = true;
  				break;
--- 14901,14908 ----
  				/* bingo. */
  				IndexSetParentIndex(attachrelIdxRels[i], idx);
  				if (OidIsValid(constraintOid))
! 					ConstraintSetParentConstraint(cldConstrOid, constraintOid,
! 												  RelationGetRelid(attachrel));
  				update_relispartition(NULL, cldIdxId, true);
  				found = true;
  				break;
*************** ATExecDetachPartition(Relation rel, Rang
*** 15151,15157 ****
  		constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
  													idxid);
  		if (OidIsValid(constrOid))
! 			ConstraintSetParentConstraint(constrOid, InvalidOid);
  
  		index_close(idx, NoLock);
  	}
--- 15160,15166 ----
  		constrOid = 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 = RelationRelationId;
  		referenced.objectId = RelationGetRelid(rel);
  		referenced.objectSubId = 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 = RelationRelationId;
  		referenced.objectId = RelationGetRelid(rel);
  		referenced.objectSubId = 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/dependency.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-created
!  * 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 the
!  * referenced object, instead).  While a regular internal dependency will
!  * 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 attached
!  * 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.  Functionally
!  * 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 entry
!  * 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_depend
!  * in catalogs.sgml for details.
   */
  
  typedef enum DependencyType
*************** typedef enum DependencyType
*** 89,95 ****
  	DEPENDENCY_NORMAL = 'n',
  	DEPENDENCY_AUTO = 'a',
  	DEPENDENCY_INTERNAL = 'i',
! 	DEPENDENCY_INTERNAL_AUTO = 'I',
  	DEPENDENCY_EXTENSION = 'e',
  	DEPENDENCY_AUTO_EXTENSION = 'x',
  	DEPENDENCY_PIN = 'p'
--- 33,40 ----
  	DEPENDENCY_NORMAL = 'n',
  	DEPENDENCY_AUTO = 'a',
  	DEPENDENCY_INTERNAL = 'i',
! 	DEPENDENCY_PARTITION_PRI = 'P',
! 	DEPENDENCY_PARTITION_SEC = 'S',
  	DEPENDENCY_EXTENSION = 'e',
  	DEPENDENCY_AUTO_EXTENSION = 'x',
  	DEPENDENCY_PIN = '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, bool 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, bool missing_ok);
  extern Bitmapset *get_relation_constraint_attnos(Oid relid, const char *conname,
  							   bool missing_ok, Oid *constraintOid);

Reply via email to