Re: Fixing findDependentObjects()'s dependency on scan order (regressions in DROP diagnostic messages)
| От | Tom Lane |
|---|---|
| Тема | Re: Fixing findDependentObjects()'s dependency on scan order (regressions in DROP diagnostic messages) |
| Дата | |
| Msg-id | 24613.1549817419@sss.pgh.pa.us обсуждение исходный текст |
| Ответ на | Re: Fixing findDependentObjects()'s dependency on scan order (regressions in DROP diagnostic messages) (Tom Lane <tgl@sss.pgh.pa.us>) |
| Ответы |
Re: Fixing findDependentObjects()'s dependency on scan order(regressions in DROP diagnostic messages)
|
| Список | pgsql-hackers |
I wrote:
> Peter Geoghegan <pg@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><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><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><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><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);
В списке pgsql-hackers по дате отправления: