aboutsummaryrefslogtreecommitdiff
path: root/src/backend/catalog/dependency.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/catalog/dependency.c')
-rw-r--r--src/backend/catalog/dependency.c202
1 files changed, 167 insertions, 35 deletions
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 2c548958313..2048d71535b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -99,9 +99,11 @@ typedef struct
#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 */
+#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 */
@@ -478,6 +480,8 @@ findDependentObjects(const ObjectAddress *object,
SysScanDesc scan;
HeapTuple tup;
ObjectAddress otherObject;
+ ObjectAddress owningObject;
+ ObjectAddress partitionObject;
ObjectAddressAndFlags *dependentObjects;
int numDependentObjects;
int maxDependentObjects;
@@ -547,6 +551,10 @@ findDependentObjects(const ObjectAddress *object,
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);
@@ -591,24 +599,26 @@ findDependentObjects(const ObjectAddress *object,
/* 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.
+ * 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)
{
- char *otherObjDesc;
-
if (pendingObjects &&
object_address_present(&otherObject, pendingObjects))
{
@@ -617,14 +627,21 @@ findDependentObjects(const ObjectAddress *object,
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)));
+
+ /*
+ * 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 if there's not more than one INTERNAL and one
+ * EXTENSION dependency. (If there's more, we'll complain
+ * about a random one of them.) Prefer to complain about
+ * EXTENSION, since that's generally a more important
+ * dependency.
+ */
+ if (!OidIsValid(owningObject.classId) ||
+ foundDep->deptype == DEPENDENCY_EXTENSION)
+ owningObject = otherObject;
+ break;
}
/*
@@ -643,14 +660,6 @@ findDependentObjects(const ObjectAddress *object,
* 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
@@ -673,6 +682,13 @@ findDependentObjects(const ObjectAddress *object,
}
/*
+ * 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
@@ -690,10 +706,66 @@ findDependentObjects(const ObjectAddress *object,
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. */
- systable_endscan(scan);
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:
/*
@@ -713,6 +785,28 @@ findDependentObjects(const ObjectAddress *object,
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
@@ -789,10 +883,13 @@ findDependentObjects(const ObjectAddress *object,
case DEPENDENCY_AUTO_EXTENSION:
subflags = DEPFLAG_AUTO;
break;
- case DEPENDENCY_INTERNAL_AUTO:
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;
@@ -868,10 +965,15 @@ findDependentObjects(const ObjectAddress *object,
/*
* 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.
+ * 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 (stack)
+ if (extra.flags & DEPFLAG_IS_PART)
+ extra.dependee = partitionObject;
+ else if (stack)
extra.dependee = *stack->object;
else
memset(&extra.dependee, 0, sizeof(extra.dependee));
@@ -906,8 +1008,37 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
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 work.
+ * 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
@@ -951,11 +1082,12 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
/*
* 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.
+ * 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))
{
/*