Commit 1d92a0c9 authored by Tom Lane's avatar Tom Lane

Redesign the partition dependency mechanism.

The original setup for dependencies of partitioned objects had
serious problems:

1. It did not verify that a drop cascading to a partition-child object
also cascaded to at least one of the object's partition parents.  Now,
normally a child object would share all its dependencies with one or
another parent (e.g. a child index's opclass dependencies would be shared
with the parent index), so that this oversight is usually harmless.
But if some dependency failed to fit this pattern, the child could be
dropped while all its parents remain, creating a logically broken
situation.  (It's easy to construct artificial cases that break it,
such as attaching an unrelated extension dependency to the child object
and then dropping the extension.  I'm not sure if any less-artificial
cases exist.)

2. Management of partition dependencies during ATTACH/DETACH PARTITION
was complicated and buggy; for example, after detaching a partition
table it was possible to create cases where a formerly-child index
should be dropped and was not, because the correct set of dependencies
had not been reconstructed.

Less seriously, because multiple partition relationships were
represented identically in pg_depend, there was an order-of-traversal
dependency on which partition parent was cited in error messages.
We also had some pre-existing order-of-traversal hazards for error
messages related to internal and extension dependencies.  This is
cosmetic to users but causes testing problems.

To fix #1, add a check at the end of the partition tree traversal
to ensure that at least one partition parent got deleted.  To fix #2,
establish a new policy that partition dependencies are in addition to,
not instead of, a child object's usual dependencies; in this way
ATTACH/DETACH PARTITION need not cope with adding or removing the
usual dependencies.

To fix the cosmetic problem, distinguish between primary and secondary
partition dependency entries in pg_depend, by giving them different
deptypes.  (They behave identically except for having different
priorities for being cited in error messages.)  This means that the
former 'I' dependency type is replaced with new 'P' and 'S' types.

This also fixes a longstanding bug that after handling an internal
dependency by recursing to the owning object, findDependentObjects
did not verify that the current target was now scheduled for deletion,
and did not apply the current recursion level's objflags to it.
Perhaps that should be back-patched; but in the back branches it
would only matter if some concurrent transaction had removed the
internal-linkage pg_depend entry before the recursive call found it,
or the recursive call somehow failed to find it, both of which seem
unlikely.

Catversion bump because the contents of pg_depend change for
partitioning relationships.

Patch HEAD only.  It's annoying that we're not fixing #2 in v11,
but there seems no practical way to do so given that the problem
is exactly a poor choice of what entries to put in pg_depend.
We can't really fix that while staying compatible with what's
in pg_depend in existing v11 installations.

Discussion: https://postgr.es/m/CAH2-Wzkypv1R+teZrr71U23J578NnTBt2X8+Y=Odr4pOdW1rXg@mail.gmail.com
parent c603b392
...@@ -2970,7 +2970,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l ...@@ -2970,7 +2970,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
referenced object, and should be automatically dropped referenced object, and should be automatically dropped
(regardless of <literal>RESTRICT</literal> or <literal>CASCADE</literal> (regardless of <literal>RESTRICT</literal> or <literal>CASCADE</literal>
mode) if the referenced object is dropped. Example: a named 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. that it will go away if the table is dropped.
</para> </para>
</listitem> </listitem>
...@@ -2982,38 +2982,61 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l ...@@ -2982,38 +2982,61 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
<para> <para>
The dependent object was created as part of creation of the The dependent object was created as part of creation of the
referenced object, and is really just a part of its internal referenced object, and is really just a part of its internal
implementation. A <command>DROP</command> of the dependent object implementation. A direct <command>DROP</command> of the dependent
will be disallowed outright (we'll tell the user to issue a object will be disallowed outright (we'll tell the user to issue
<command>DROP</command> against the referenced object, instead). A a <command>DROP</command> against the referenced object, instead).
<command>DROP</command> of the referenced object will be propagated A <command>DROP</command> of the referenced object will result in
through to drop the dependent object whether automatically dropping the dependent object
<command>CASCADE</command> is specified or not. Example: a trigger whether <literal>CASCADE</literal> is specified or not. If the
that's created to enforce a foreign-key constraint is made dependent object is reached due to a dependency on some other object,
internally dependent on the constraint's the drop is converted to a drop of the referenced object, so
<structname>pg_constraint</structname> entry. 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<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> <listitem>
<para> <para>
The dependent object was created as part of creation of the The dependent object was created as part of creation of the
referenced object, and is really just a part of its internal referenced object, and is really just a part of its internal
implementation. A <command>DROP</command> of the dependent object implementation; however, unlike <literal>INTERNAL</literal>,
will be disallowed outright (we'll tell the user to issue a there is more than one such referenced object. The dependent object
<command>DROP</command> against the referenced object, instead). must not be dropped unless at least one of these referenced objects
While a regular internal dependency will prevent is dropped; if any one is, the dependent object should be dropped
the dependent object from being dropped while any such dependencies whether or not <literal>CASCADE</literal> is specified. Also
remain, <literal>DEPENDENCY_INTERNAL_AUTO</literal> will allow such unlike <literal>INTERNAL</literal>, a drop of some other object
a drop as long as the object can be found by following any of such 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. dependencies.
Example: an index on a partition is made internal-auto-dependent on Note that partition dependencies are made in addition to, not
both the partition itself as well as on the index on the parent instead of, any dependencies the object would normally have. This
partitioned table; so the partition index is dropped together with simplifies <command>ATTACH/DETACH PARTITION</command> operations:
either the partition it indexes, or with the parent index it is the partition dependencies need only be added or removed.
attached to. 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -3026,9 +3049,10 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l ...@@ -3026,9 +3049,10 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
the referenced object (see the referenced object (see
<link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>). <link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>).
The dependent object can be dropped only via The dependent object can be dropped only via
<command>DROP EXTENSION</command> on the referenced object. Functionally <command>DROP EXTENSION</command> on the referenced object.
this dependency type acts the same as an internal dependency, but Functionally this dependency type acts the same as
it's kept separate for clarity and to simplify <application>pg_dump</application>. an <literal>INTERNAL</literal> dependency, but it's kept separate for
clarity and to simplify <application>pg_dump</application>.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -3038,10 +3062,13 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l ...@@ -3038,10 +3062,13 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
<listitem> <listitem>
<para> <para>
The dependent object is not a member of the extension that is the 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 referenced object (and so it should not be ignored
cannot function without it and should be dropped when the by <application>pg_dump</application>), but it cannot function
extension itself is. The dependent object may be dropped on its without the extension and should be auto-dropped if the extension is.
own as well. 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -3063,6 +3090,19 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l ...@@ -3063,6 +3090,19 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
Other dependency flavors might be needed in future. Other dependency flavors might be needed in future.
</para> </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> </sect1>
......
This diff is collapsed.
...@@ -1041,9 +1041,6 @@ index_create(Relation heapRelation, ...@@ -1041,9 +1041,6 @@ index_create(Relation heapRelation,
else else
{ {
bool have_simple_col = false; bool have_simple_col = false;
DependencyType deptype;
deptype = OidIsValid(parentIndexRelid) ? DEPENDENCY_INTERNAL_AUTO : DEPENDENCY_AUTO;
/* Create auto dependencies on simply-referenced columns */ /* Create auto dependencies on simply-referenced columns */
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
...@@ -1054,7 +1051,7 @@ index_create(Relation heapRelation, ...@@ -1054,7 +1051,7 @@ index_create(Relation heapRelation,
referenced.objectId = heapRelationId; referenced.objectId = heapRelationId;
referenced.objectSubId = indexInfo->ii_IndexAttrNumbers[i]; referenced.objectSubId = indexInfo->ii_IndexAttrNumbers[i];
recordDependencyOn(&myself, &referenced, deptype); recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
have_simple_col = true; have_simple_col = true;
} }
...@@ -1072,18 +1069,29 @@ index_create(Relation heapRelation, ...@@ -1072,18 +1069,29 @@ index_create(Relation heapRelation,
referenced.objectId = heapRelationId; referenced.objectId = heapRelationId;
referenced.objectSubId = 0; 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)) if (OidIsValid(parentIndexRelid))
{ {
referenced.classId = RelationRelationId; referenced.classId = RelationRelationId;
referenced.objectId = parentIndexRelid; referenced.objectId = parentIndexRelid;
referenced.objectSubId = 0; 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 */ /* Store dependency on collations */
...@@ -1342,15 +1350,17 @@ index_constraint_create(Relation heapRelation, ...@@ -1342,15 +1350,17 @@ index_constraint_create(Relation heapRelation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
/* /*
* Also, if this is a constraint on a partition, mark it as depending on * Also, if this is a constraint on a partition, give it partition-type
* the constraint in the parent. * dependencies on the parent constraint as well as the table.
*/ */
if (OidIsValid(parentConstraintId)) if (OidIsValid(parentConstraintId))
{ {
ObjectAddress parentConstr; ObjectAddressSet(myself, ConstraintRelationId, conOid);
ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId);
ObjectAddressSet(parentConstr, ConstraintRelationId, parentConstraintId); recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
recordDependencyOn(&referenced, &parentConstr, DEPENDENCY_INTERNAL_AUTO); ObjectAddressSet(referenced, RelationRelationId,
RelationGetRelid(heapRelation));
recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
} }
/* /*
......
...@@ -760,13 +760,17 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, ...@@ -760,13 +760,17 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
/* /*
* ConstraintSetParentConstraint * 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 * 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 void
ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId) ConstraintSetParentConstraint(Oid childConstrId,
Oid parentConstrId,
Oid childTableId)
{ {
Relation constrRel; Relation constrRel;
Form_pg_constraint constrForm; Form_pg_constraint constrForm;
...@@ -795,10 +799,13 @@ ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId) ...@@ -795,10 +799,13 @@ ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId)
CatalogTupleUpdate(constrRel, &tuple->t_self, newtup); CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
ObjectAddressSet(depender, ConstraintRelationId, childConstrId); 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 else
{ {
...@@ -809,10 +816,14 @@ ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId) ...@@ -809,10 +816,14 @@ ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId)
/* Make sure there's no further inheritance. */ /* Make sure there's no further inheritance. */
Assert(constrForm->coninhcount == 0); Assert(constrForm->coninhcount == 0);
CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId, deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
ConstraintRelationId, ConstraintRelationId,
DEPENDENCY_INTERNAL_AUTO); DEPENDENCY_PARTITION_PRI);
CatalogTupleUpdate(constrRel, &tuple->t_self, newtup); deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
RelationRelationId,
DEPENDENCY_PARTITION_SEC);
} }
ReleaseSysCache(tuple); ReleaseSysCache(tuple);
......
...@@ -971,7 +971,8 @@ DefineIndex(Oid relationId, ...@@ -971,7 +971,8 @@ DefineIndex(Oid relationId,
IndexSetParentIndex(cldidx, indexRelationId); IndexSetParentIndex(cldidx, indexRelationId);
if (createdConstraintId != InvalidOid) if (createdConstraintId != InvalidOid)
ConstraintSetParentConstraint(cldConstrOid, ConstraintSetParentConstraint(cldConstrOid,
createdConstraintId); createdConstraintId,
childRelid);
if (!cldidx->rd_index->indisvalid) if (!cldidx->rd_index->indisvalid)
invalidate_parent = true; invalidate_parent = true;
...@@ -2622,35 +2623,34 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid) ...@@ -2622,35 +2623,34 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
if (fix_dependencies) if (fix_dependencies)
{ {
ObjectAddress partIdx;
/* /*
* Insert/delete pg_depend rows. If setting a parent, add an * Insert/delete pg_depend rows. If setting a parent, add PARTITION
* INTERNAL_AUTO dependency to the parent index; if making standalone, * dependencies on the parent index and the table; if removing a
* remove all existing rows and put back the regular dependency on the * parent, delete PARTITION dependencies.
* table.
*/ */
ObjectAddressSet(partIdx, RelationRelationId, partRelid);
if (OidIsValid(parentOid)) if (OidIsValid(parentOid))
{ {
ObjectAddress partIdx;
ObjectAddress parentIdx; ObjectAddress parentIdx;
ObjectAddress partitionTbl;
ObjectAddressSet(partIdx, RelationRelationId, partRelid);
ObjectAddressSet(parentIdx, RelationRelationId, parentOid); 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 else
{ {
ObjectAddress partitionTbl;
ObjectAddressSet(partitionTbl, RelationRelationId,
partitionIdx->rd_index->indrelid);
deleteDependencyRecordsForClass(RelationRelationId, partRelid, deleteDependencyRecordsForClass(RelationRelationId, partRelid,
RelationRelationId, RelationRelationId,
DEPENDENCY_INTERNAL_AUTO); DEPENDENCY_PARTITION_PRI);
deleteDependencyRecordsForClass(RelationRelationId, partRelid,
recordDependencyOn(&partIdx, &partitionTbl, DEPENDENCY_AUTO); RelationRelationId,
DEPENDENCY_PARTITION_SEC);
} }
/* make our updates visible */ /* make our updates visible */
......
...@@ -7825,7 +7825,8 @@ CloneFkReferencing(Relation pg_constraint, Relation parentRel, ...@@ -7825,7 +7825,8 @@ CloneFkReferencing(Relation pg_constraint, Relation parentRel,
bool attach_it; bool attach_it;
Oid constrOid; Oid constrOid;
ObjectAddress parentAddr, ObjectAddress parentAddr,
childAddr; childAddr,
childTableAddr;
ListCell *cell; ListCell *cell;
int i; int i;
...@@ -7966,7 +7967,8 @@ CloneFkReferencing(Relation pg_constraint, Relation parentRel, ...@@ -7966,7 +7967,8 @@ CloneFkReferencing(Relation pg_constraint, Relation parentRel,
systable_endscan(scan); systable_endscan(scan);
table_close(trigrel, RowExclusiveLock); table_close(trigrel, RowExclusiveLock);
ConstraintSetParentConstraint(fk->conoid, parentConstrOid); ConstraintSetParentConstraint(fk->conoid, parentConstrOid,
RelationGetRelid(partRel));
CommandCounterIncrement(); CommandCounterIncrement();
attach_it = true; attach_it = true;
break; break;
...@@ -8013,8 +8015,14 @@ CloneFkReferencing(Relation pg_constraint, Relation parentRel, ...@@ -8013,8 +8015,14 @@ CloneFkReferencing(Relation pg_constraint, Relation parentRel,
1, false, true); 1, false, true);
subclone = lappend_oid(subclone, constrOid); subclone = lappend_oid(subclone, constrOid);
/* Set up partition dependencies for the new constraint */
ObjectAddressSet(childAddr, ConstraintRelationId, constrOid); 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); fkconstraint = makeNode(Constraint);
/* for now this is all we need */ /* for now this is all we need */
...@@ -14893,7 +14901,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) ...@@ -14893,7 +14901,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel)
/* bingo. */ /* bingo. */
IndexSetParentIndex(attachrelIdxRels[i], idx); IndexSetParentIndex(attachrelIdxRels[i], idx);
if (OidIsValid(constraintOid)) if (OidIsValid(constraintOid))
ConstraintSetParentConstraint(cldConstrOid, constraintOid); ConstraintSetParentConstraint(cldConstrOid, constraintOid,
RelationGetRelid(attachrel));
update_relispartition(NULL, cldIdxId, true); update_relispartition(NULL, cldIdxId, true);
found = true; found = true;
break; break;
...@@ -15151,7 +15160,7 @@ ATExecDetachPartition(Relation rel, RangeVar *name) ...@@ -15151,7 +15160,7 @@ ATExecDetachPartition(Relation rel, RangeVar *name)
constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel), constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
idxid); idxid);
if (OidIsValid(constrOid)) if (OidIsValid(constrOid))
ConstraintSetParentConstraint(constrOid, InvalidOid); ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
index_close(idx, NoLock); index_close(idx, NoLock);
} }
...@@ -15183,7 +15192,7 @@ ATExecDetachPartition(Relation rel, RangeVar *name) ...@@ -15183,7 +15192,7 @@ ATExecDetachPartition(Relation rel, RangeVar *name)
} }
/* unset conparentid and adjust conislocal, coninhcount, etc. */ /* 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 * Make the action triggers on the referenced relation. When this was
...@@ -15419,7 +15428,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) ...@@ -15419,7 +15428,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
/* All good -- do it */ /* All good -- do it */
IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx)); IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
if (OidIsValid(constraintOid)) if (OidIsValid(constraintOid))
ConstraintSetParentConstraint(cldConstrId, constraintOid); ConstraintSetParentConstraint(cldConstrId, constraintOid,
RelationGetRelid(partTbl));
update_relispartition(NULL, partIdxId, true); update_relispartition(NULL, partIdxId, true);
pfree(attmap); pfree(attmap);
......
...@@ -1012,17 +1012,11 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, ...@@ -1012,17 +1012,11 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
* User CREATE TRIGGER, so place dependencies. We make trigger be * User CREATE TRIGGER, so place dependencies. We make trigger be
* auto-dropped if its relation is dropped or if the FK relation is * auto-dropped if its relation is dropped or if the FK relation is
* dropped. (Auto drop is compatible with our pre-7.3 behavior.) * 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.classId = RelationRelationId;
referenced.objectId = RelationGetRelid(rel); referenced.objectId = RelationGetRelid(rel);
referenced.objectSubId = 0; referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, OidIsValid(parentTriggerOid) ? recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
DEPENDENCY_INTERNAL_AUTO :
DEPENDENCY_AUTO);
if (OidIsValid(constrrelid)) if (OidIsValid(constrrelid))
{ {
...@@ -1046,11 +1040,15 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, ...@@ -1046,11 +1040,15 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); 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)) if (OidIsValid(parentTriggerOid))
{ {
ObjectAddressSet(referenced, TriggerRelationId, 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);
} }
} }
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201902092 #define CATALOG_VERSION_NO 201902111
#endif #endif
...@@ -24,64 +24,8 @@ ...@@ -24,64 +24,8 @@
* *
* In all cases, a dependency relationship indicates that the referenced * In all cases, a dependency relationship indicates that the referenced
* object may not be dropped without also dropping the dependent object. * object may not be dropped without also dropping the dependent object.
* However, there are several subflavors: * However, there are several subflavors; see the description of pg_depend
* * in catalogs.sgml for details.
* 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 typedef enum DependencyType
...@@ -89,7 +33,8 @@ typedef enum DependencyType ...@@ -89,7 +33,8 @@ typedef enum DependencyType
DEPENDENCY_NORMAL = 'n', DEPENDENCY_NORMAL = 'n',
DEPENDENCY_AUTO = 'a', DEPENDENCY_AUTO = 'a',
DEPENDENCY_INTERNAL = 'i', DEPENDENCY_INTERNAL = 'i',
DEPENDENCY_INTERNAL_AUTO = 'I', DEPENDENCY_PARTITION_PRI = 'P',
DEPENDENCY_PARTITION_SEC = 'S',
DEPENDENCY_EXTENSION = 'e', DEPENDENCY_EXTENSION = 'e',
DEPENDENCY_AUTO_EXTENSION = 'x', DEPENDENCY_AUTO_EXTENSION = 'x',
DEPENDENCY_PIN = 'p' DEPENDENCY_PIN = 'p'
......
...@@ -239,7 +239,8 @@ extern char *ChooseConstraintName(const char *name1, const char *name2, ...@@ -239,7 +239,8 @@ extern char *ChooseConstraintName(const char *name1, const char *name2,
extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
Oid newNspId, bool isType, ObjectAddresses *objsMoved); Oid newNspId, bool isType, ObjectAddresses *objsMoved);
extern void ConstraintSetParentConstraint(Oid childConstrId, 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 Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok);
extern Bitmapset *get_relation_constraint_attnos(Oid relid, const char *conname, extern Bitmapset *get_relation_constraint_attnos(Oid relid, const char *conname,
bool missing_ok, Oid *constraintOid); bool missing_ok, Oid *constraintOid);
......
...@@ -528,6 +528,30 @@ select relname, relkind from pg_class where relname like 'idxpart%' order by rel ...@@ -528,6 +528,30 @@ select relname, relkind from pg_class where relname like 'idxpart%' order by rel
---------+--------- ---------+---------
(0 rows) (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 -- Verify that expression indexes inherit correctly
create table idxpart (a int, b int) partition by range (a); create table idxpart (a int, b int) partition by range (a);
create table idxpart1 (like idxpart); create table idxpart1 (like idxpart);
......
...@@ -249,6 +249,16 @@ select relname, relkind from pg_class where relname like 'idxpart%' order by rel ...@@ -249,6 +249,16 @@ select relname, relkind from pg_class where relname like 'idxpart%' order by rel
drop table idxpart, idxpart1, idxpart2, idxpart3; drop table idxpart, idxpart1, idxpart2, idxpart3;
select relname, relkind from pg_class where relname like 'idxpart%' order by relname; 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 -- Verify that expression indexes inherit correctly
create table idxpart (a int, b int) partition by range (a); create table idxpart (a int, b int) partition by range (a);
create table idxpart1 (like idxpart); create table idxpart1 (like idxpart);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment