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
+ constraint on a table is made auto-dependent on the table, so
that it will go away if the table is dropped.
</para>
</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). 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.
+ 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_INTERNAL_AUTO</symbol> (<literal>I</literal>)</term>
+ <term><symbol>DEPENDENCY_PARTITION_PRI</symbol> (<literal>P</literal>)</term>
+ <term><symbol>DEPENDENCY_PARTITION_SEC</symbol> (<literal>S</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
+ 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.)
+ Primary and secondary partition dependencies behave identically
+ except that the primary dependency is preferred for use in error
+ messages; hence, a partition-dependent object should have one
+ primary partition dependency and one or more secondary partition
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.
+ 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. The dependency on the parent index is primary,
+ so that if the user tries to drop the child partitioned index,
+ the error message will suggest dropping the parent index instead
+ (not the table).
</para>
</listitem>
</varlistentry>
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>.
+ <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>
<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.
+ 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>
Other dependency flavors might be needed in future.
</para>
+ <para>
+ Note that it's quite possible for two objects to be linked by more than
+ one <structname>pg_depend</structname> entry. For example, a child
+ partitioned index would have both a partition-type dependency on its
+ associated partition table, and an auto dependency on each column of
+ that table that it indexes. This sort of situation expresses the union
+ of multiple dependency semantics. A dependent object can be dropped
+ without <literal>CASCADE</literal> if any of its dependencies satisfies
+ its condition for automatic dropping. Conversely, all the
+ dependencies' restrictions about which objects must be dropped together
+ must be satisfied.
+ </para>
+
</sect1>
#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 */
SysScanDesc scan;
HeapTuple tup;
ObjectAddress otherObject;
+ ObjectAddress owningObject;
+ ObjectAddress partitionObject;
ObjectAddressAndFlags *dependentObjects;
int numDependentObjects;
int maxDependentObjects;
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);
/* 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))
{
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;
}
/*
* 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
return;
}
+ /*
+ * 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.
*
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:
/*
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
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;
/*
* 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));
int numNotReportedClient = 0;
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
/*
* 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))
{
/*
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++)
referenced.objectId = heapRelationId;
referenced.objectSubId = indexInfo->ii_IndexAttrNumbers[i];
- recordDependencyOn(&myself, &referenced, deptype);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
have_simple_col = true;
}
referenced.objectId = heapRelationId;
referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, deptype);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
}
- /* Store dependency on parent index, if any */
+ /*
+ * 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_INTERNAL_AUTO);
+ 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 */
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
/*
- * Also, if this is a constraint on a partition, mark it as depending on
- * the constraint in the parent.
+ * 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))
{
- ObjectAddress parentConstr;
-
- ObjectAddressSet(parentConstr, ConstraintRelationId, parentConstraintId);
- recordDependencyOn(&referenced, &parentConstr, DEPENDENCY_INTERNAL_AUTO);
+ 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);
}
/*
/*
* ConstraintSetParentConstraint
- * Set a partition's constraint as child of its parent table's
+ * 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
- * add a dependency to the parent so that it cannot be removed on its own.
+ * adds PARTITION dependencies to prevent the constraint from being deleted
+ * on its own. Alternatively, reverse that.
*/
void
-ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId)
+ConstraintSetParentConstraint(Oid childConstrId,
+ Oid parentConstrId,
+ Oid childTableId)
{
Relation constrRel;
Form_pg_constraint constrForm;
CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
- ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
ObjectAddressSet(depender, ConstraintRelationId, childConstrId);
- recordDependencyOn(&depender, &referenced, DEPENDENCY_INTERNAL_AUTO);
+ ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
+ recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
+
+ ObjectAddressSet(referenced, RelationRelationId, childTableId);
+ recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
}
else
{
/* Make sure there's no further inheritance. */
Assert(constrForm->coninhcount == 0);
+ CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
+
deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
ConstraintRelationId,
- DEPENDENCY_INTERNAL_AUTO);
- CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
+ DEPENDENCY_PARTITION_PRI);
+ deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
+ RelationRelationId,
+ DEPENDENCY_PARTITION_SEC);
}
ReleaseSysCache(tuple);
IndexSetParentIndex(cldidx, indexRelationId);
if (createdConstraintId != InvalidOid)
ConstraintSetParentConstraint(cldConstrOid,
- createdConstraintId);
+ createdConstraintId,
+ childRelid);
if (!cldidx->rd_index->indisvalid)
invalidate_parent = true;
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.
+ * 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.
*/
- ObjectAddressSet(partIdx, RelationRelationId, partRelid);
-
if (OidIsValid(parentOid))
{
+ ObjectAddress partIdx;
ObjectAddress parentIdx;
+ ObjectAddress partitionTbl;
+ ObjectAddressSet(partIdx, RelationRelationId, partRelid);
ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
- recordDependencyOn(&partIdx, &parentIdx, DEPENDENCY_INTERNAL_AUTO);
+ ObjectAddressSet(partitionTbl, RelationRelationId,
+ partitionIdx->rd_index->indrelid);
+ recordDependencyOn(&partIdx, &parentIdx,
+ DEPENDENCY_PARTITION_PRI);
+ recordDependencyOn(&partIdx, &partitionTbl,
+ DEPENDENCY_PARTITION_SEC);
}
else
{
- ObjectAddress partitionTbl;
-
- ObjectAddressSet(partitionTbl, RelationRelationId,
- partitionIdx->rd_index->indrelid);
-
deleteDependencyRecordsForClass(RelationRelationId, partRelid,
RelationRelationId,
- DEPENDENCY_INTERNAL_AUTO);
-
- recordDependencyOn(&partIdx, &partitionTbl, DEPENDENCY_AUTO);
+ DEPENDENCY_PARTITION_PRI);
+ deleteDependencyRecordsForClass(RelationRelationId, partRelid,
+ RelationRelationId,
+ DEPENDENCY_PARTITION_SEC);
}
/* make our updates visible */
bool attach_it;
Oid constrOid;
ObjectAddress parentAddr,
- childAddr;
+ childAddr,
+ childTableAddr;
ListCell *cell;
int i;
systable_endscan(scan);
table_close(trigrel, RowExclusiveLock);
- ConstraintSetParentConstraint(fk->conoid, parentConstrOid);
+ ConstraintSetParentConstraint(fk->conoid, parentConstrOid,
+ RelationGetRelid(partRel));
CommandCounterIncrement();
attach_it = true;
break;
1, false, true);
subclone = lappend_oid(subclone, constrOid);
+ /* Set up partition dependencies for the new constraint */
ObjectAddressSet(childAddr, ConstraintRelationId, constrOid);
- recordDependencyOn(&childAddr, &parentAddr, DEPENDENCY_INTERNAL_AUTO);
+ 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 */
/* bingo. */
IndexSetParentIndex(attachrelIdxRels[i], idx);
if (OidIsValid(constraintOid))
- ConstraintSetParentConstraint(cldConstrOid, constraintOid);
+ ConstraintSetParentConstraint(cldConstrOid, constraintOid,
+ RelationGetRelid(attachrel));
update_relispartition(NULL, cldIdxId, true);
found = true;
break;
constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
idxid);
if (OidIsValid(constrOid))
- ConstraintSetParentConstraint(constrOid, InvalidOid);
+ ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
index_close(idx, NoLock);
}
}
/* unset conparentid and adjust conislocal, coninhcount, etc. */
- ConstraintSetParentConstraint(fk->conoid, InvalidOid);
+ ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
/*
* Make the action triggers on the referenced relation. When this was
/* All good -- do it */
IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
if (OidIsValid(constraintOid))
- ConstraintSetParentConstraint(cldConstrId, constraintOid);
+ ConstraintSetParentConstraint(cldConstrId, constraintOid,
+ RelationGetRelid(partTbl));
update_relispartition(NULL, partIdxId, true);
pfree(attmap);
* 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);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
if (OidIsValid(constrrelid))
{
recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
}
- /* Depends on the parent trigger, if there is one. */
+ /*
+ * If it's a partition trigger, create the partition dependencies.
+ */
if (OidIsValid(parentTriggerOid))
{
ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
- recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL_AUTO);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
+ ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
}
}
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201902092
+#define CATALOG_VERSION_NO 201902111
#endif
*
* 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.
+ * However, there are several subflavors; see the description of pg_depend
+ * in catalogs.sgml for details.
*/
typedef enum DependencyType
DEPENDENCY_NORMAL = 'n',
DEPENDENCY_AUTO = 'a',
DEPENDENCY_INTERNAL = 'i',
- DEPENDENCY_INTERNAL_AUTO = 'I',
+ DEPENDENCY_PARTITION_PRI = 'P',
+ DEPENDENCY_PARTITION_SEC = 'S',
DEPENDENCY_EXTENSION = 'e',
DEPENDENCY_AUTO_EXTENSION = 'x',
DEPENDENCY_PIN = 'p'
extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
Oid newNspId, bool isType, ObjectAddresses *objsMoved);
extern void ConstraintSetParentConstraint(Oid childConstrId,
- Oid parentConstrId);
+ 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);
---------+---------
(0 rows)
+create table idxpart (a int, b int, c int) partition by range(a);
+create index on idxpart(c);
+create table idxpart1 partition of idxpart for values from (0) to (250);
+create table idxpart2 partition of idxpart for values from (250) to (500);
+alter table idxpart detach partition idxpart2;
+\d idxpart2
+ Table "public.idxpart2"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a | integer | | |
+ b | integer | | |
+ c | integer | | |
+Indexes:
+ "idxpart2_c_idx" btree (c)
+
+alter table idxpart2 drop column c;
+\d idxpart2
+ Table "public.idxpart2"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a | integer | | |
+ b | integer | | |
+
+drop table idxpart, idxpart2;
-- Verify that expression indexes inherit correctly
create table idxpart (a int, b int) partition by range (a);
create table idxpart1 (like idxpart);
drop table idxpart, idxpart1, idxpart2, idxpart3;
select relname, relkind from pg_class where relname like 'idxpart%' order by relname;
+create table idxpart (a int, b int, c int) partition by range(a);
+create index on idxpart(c);
+create table idxpart1 partition of idxpart for values from (0) to (250);
+create table idxpart2 partition of idxpart for values from (250) to (500);
+alter table idxpart detach partition idxpart2;
+\d idxpart2
+alter table idxpart2 drop column c;
+\d idxpart2
+drop table idxpart, idxpart2;
+
-- Verify that expression indexes inherit correctly
create table idxpart (a int, b int) partition by range (a);
create table idxpart1 (like idxpart);