(see <xref linkend="ddl-depend"/>).
</para>
+ <para>
+ Extension dependencies on partition indexes (created via
+ <link linkend="sql-alterindex"><command>ALTER INDEX ... DEPENDS ON
+ EXTENSION</command></link>) are preserved during merge operations.
+ All source partition indexes must have the same extension dependencies;
+ if they differ, an error is raised. This ensures that extension
+ dependencies are not silently lost during merge.
+ </para>
+
<note>
<para>
Merging partitions acquires an <literal>ACCESS EXCLUSIVE</literal> lock on
would fail (see <xref linkend="ddl-depend"/>).
</para>
+ <para>
+ Extension dependencies on partition indexes (created via
+ <link linkend="sql-alterindex"><command>ALTER INDEX ... DEPENDS ON
+ EXTENSION</command></link>) are preserved during split operations.
+ The new partitions' indexes will inherit the extension dependencies
+ from the source partition's indexes.
+ </para>
+
<note>
<para>
Split partition acquires an <literal>ACCESS EXCLUSIVE</literal> lock on
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
+#include "catalog/pg_extension_d.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_largeobject.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
+#include "commands/extension.h"
#include "commands/repack.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
addFkBothSides,
} addFkConstraintSides;
+/*
+ * Hold extension dependencies of one partition index, during
+ * MERGE/SPLIT PARTITION processing.
+ *
+ * collectPartitionIndexExtDeps() builds a list of these entries sorted by
+ * parentIndexOid with exactly one entry per parent partitioned index; the
+ * list is then consumed by applyPartitionIndexExtDeps() to re-record the
+ * same dependencies on the newly created partition's indexes.
+ *
+ * extensionOids is kept sorted ascending so that equality checks between
+ * entries from different partitions can be done in a single pass.
+ * indexOid is carried only so that conflict errors can cite specific
+ * partition index names.
+ */
+typedef struct PartitionIndexExtDepEntry
+{
+ Oid parentIndexOid; /* OID of the parent partitioned index */
+ Oid indexOid; /* OID of a representative partition index */
+ List *extensionOids; /* OIDs of dependent extensions, sorted asc */
+} PartitionIndexExtDepEntry;
+
/*
* Partition tables are expected to be dropped when the parent partitioned
* table gets dropped. Hence for partitioning we use AUTO dependency.
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
Relation rel, PartitionCmd *cmd,
AlterTableUtilityContext *context);
+static List *collectPartitionIndexExtDeps(List *partitionOids);
+static void applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState);
+static void freePartitionIndexExtDeps(List *extDepState);
/* ----------------------------------------------------------------
* DefineRelation
PopActiveSnapshot();
}
+/*
+ * equal_oid_lists: return true if two OID lists, each sorted in ascending
+ * order, contain the same OIDs in the same order.
+ */
+static bool
+equal_oid_lists(const List *a, const List *b)
+{
+ ListCell *la,
+ *lb;
+
+ if (list_length(a) != list_length(b))
+ return false;
+
+ forboth(la, a, lb, b)
+ {
+ if (lfirst_oid(la) != lfirst_oid(lb))
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Comparator for list_sort() on a list of PartitionIndexExtDepEntry *.
+ * Orders by parentIndexOid, then by indexOid as a tiebreaker so conflict
+ * reports for different parent indexes are deterministic.
+ */
+static int
+cmp_partition_index_ext_dep(const ListCell *a, const ListCell *b)
+{
+ const PartitionIndexExtDepEntry *ea = lfirst(a);
+ const PartitionIndexExtDepEntry *eb = lfirst(b);
+
+ if (ea->parentIndexOid != eb->parentIndexOid)
+ return pg_cmp_u32(ea->parentIndexOid, eb->parentIndexOid);
+ return pg_cmp_u32(ea->indexOid, eb->indexOid);
+}
+
+/*
+ * collectPartitionIndexExtDeps: collect extension dependencies from indexes
+ * on the given partitions.
+ *
+ * For each partition index that has a parent partitioned index, we collect
+ * extension dependencies. All source partition indexes sharing the same
+ * parent partitioned index must depend on exactly the same set of
+ * extensions; otherwise an error is raised so that we neither silently drop
+ * nor silently add dependencies on the merged partition's index.
+ *
+ * Indexes that don't have a parent partitioned index (i.e., indexes created
+ * directly on a partition without a corresponding parent index) are skipped.
+ *
+ * The returned list is sorted by parentIndexOid with exactly one entry per
+ * parent partitioned index, so applyPartitionIndexExtDeps() can scan it
+ * linearly.
+ */
+static List *
+collectPartitionIndexExtDeps(List *partitionOids)
+{
+ List *collected = NIL;
+ List *result = NIL;
+ PartitionIndexExtDepEntry *prev = NULL;
+
+ /*
+ * Phase 1: collect one entry per (partition index -> parent index) pair,
+ * with its extension dependency OIDs sorted ascending.
+ */
+ foreach_oid(partOid, partitionOids)
+ {
+ Relation partRel;
+ List *indexList;
+
+ /*
+ * Use NoLock since the caller already holds AccessExclusiveLock on
+ * these partitions.
+ */
+ partRel = table_open(partOid, NoLock);
+ indexList = RelationGetIndexList(partRel);
+
+ foreach_oid(indexOid, indexList)
+ {
+ Oid parentIndexOid;
+ PartitionIndexExtDepEntry *entry;
+
+ if (!get_rel_relispartition(indexOid))
+ continue;
+
+ parentIndexOid = get_partition_parent(indexOid, true);
+ if (!OidIsValid(parentIndexOid))
+ continue;
+
+ entry = palloc(sizeof(PartitionIndexExtDepEntry));
+ entry->parentIndexOid = parentIndexOid;
+ entry->indexOid = indexOid;
+ entry->extensionOids = getAutoExtensionsOfObject(RelationRelationId,
+ indexOid);
+ list_sort(entry->extensionOids, list_oid_cmp);
+
+ collected = lappend(collected, entry);
+ }
+
+ list_free(indexList);
+ table_close(partRel, NoLock);
+ }
+
+ /*
+ * Phase 2: sort by parentIndexOid so entries sharing a parent index sit
+ * adjacent.
+ */
+ list_sort(collected, cmp_partition_index_ext_dep);
+
+ /*
+ * Phase 3: single linear pass verifying that adjacent entries sharing a
+ * parent index have identical extension dependencies, and keeping one
+ * representative entry per parent index.
+ */
+ foreach_ptr(PartitionIndexExtDepEntry, entry, collected)
+ {
+ if (prev != NULL && prev->parentIndexOid == entry->parentIndexOid)
+ {
+ if (!equal_oid_lists(prev->extensionOids, entry->extensionOids))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot merge partitions with conflicting extension dependencies"),
+ errdetail("Partition indexes \"%s\" and \"%s\" depend on different extensions.",
+ get_rel_name(prev->indexOid),
+ get_rel_name(entry->indexOid))));
+
+ /* Duplicate entry for the same parent index; discard. */
+ list_free(entry->extensionOids);
+ pfree(entry);
+ continue;
+ }
+
+ result = lappend(result, entry);
+ prev = entry;
+ }
+
+ list_free(collected);
+
+ return result;
+}
+
+/*
+ * applyPartitionIndexExtDeps: apply collected extension dependencies to
+ * indexes on a new partition.
+ *
+ * For each index on the new partition, look up its parent index in the
+ * extDepState list. If found, record extension dependencies on the new index.
+ * extDepState is sorted by parentIndexOid, so the inner scan can bail out
+ * as soon as it passes the target OID.
+ */
+static void
+applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState)
+{
+ Relation partRel;
+ List *indexList;
+
+ if (extDepState == NIL)
+ return;
+
+ /*
+ * Use NoLock since the caller already holds AccessExclusiveLock on the
+ * new partition.
+ */
+ partRel = table_open(newPartOid, NoLock);
+ indexList = RelationGetIndexList(partRel);
+
+ foreach_oid(indexOid, indexList)
+ {
+ Oid parentIdxOid;
+
+ if (!get_rel_relispartition(indexOid))
+ continue;
+
+ parentIdxOid = get_partition_parent(indexOid, true);
+ if (!OidIsValid(parentIdxOid))
+ continue;
+
+ foreach_ptr(PartitionIndexExtDepEntry, entry, extDepState)
+ {
+ ObjectAddress indexAddr;
+
+ if (entry->parentIndexOid > parentIdxOid)
+ break;
+ if (entry->parentIndexOid < parentIdxOid)
+ continue;
+
+ ObjectAddressSet(indexAddr, RelationRelationId, indexOid);
+
+ foreach_oid(extOid, entry->extensionOids)
+ {
+ ObjectAddress extAddr;
+
+ ObjectAddressSet(extAddr, ExtensionRelationId, extOid);
+ recordDependencyOn(&indexAddr, &extAddr,
+ DEPENDENCY_AUTO_EXTENSION);
+ }
+ break;
+ }
+ }
+
+ list_free(indexList);
+ table_close(partRel, NoLock);
+}
+
+/*
+ * freePartitionIndexExtDeps: free memory allocated by collectPartitionIndexExtDeps.
+ */
+static void
+freePartitionIndexExtDeps(List *extDepState)
+{
+ foreach_ptr(PartitionIndexExtDepEntry, entry, extDepState)
+ {
+ list_free(entry->extensionOids);
+ pfree(entry);
+ }
+ list_free(extDepState);
+}
+
/*
* ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
*/
{
Relation newPartRel;
List *mergingPartitions = NIL;
+ List *extDepState = NIL;
Oid defaultPartOid;
Oid existingRelid;
Oid ownerId = InvalidOid;
defaultPartOid =
get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
+ /*
+ * Collect extension dependencies from indexes on the merging partitions.
+ * We must do this before detaching them, so we can restore the
+ * dependencies on the new partition's indexes later.
+ */
+ extDepState = collectPartitionIndexExtDeps(mergingPartitions);
+
/* Detach all merging partitions. */
foreach_oid(mergingPartitionOid, mergingPartitions)
{
*/
attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
+ /*
+ * Apply extension dependencies to the new partition's indexes. This
+ * preserves any "DEPENDS ON EXTENSION" settings from the merged
+ * partitions.
+ */
+ applyPartitionIndexExtDeps(RelationGetRelid(newPartRel), extDepState);
+
+ freePartitionIndexExtDeps(extDepState);
+
/* Keep the lock until commit. */
table_close(newPartRel, NoLock);
bool isSameName = false;
char tmpRelName[NAMEDATALEN];
List *newPartRels = NIL;
+ List *extDepState = NIL;
ObjectAddress object;
Oid defaultPartOid;
Oid save_userid;
int save_sec_context;
int save_nestlevel;
+ List *splitPartList;
defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
errmsg("relation \"%s\" already exists", sps->name->relname));
}
+ /*
+ * Collect extension dependencies from indexes on the split partition. We
+ * must do this before detaching it, so we can restore the dependencies on
+ * the new partitions' indexes later.
+ */
+ splitPartList = list_make1_oid(splitRelOid);
+
+ extDepState = collectPartitionIndexExtDeps(splitPartList);
+ list_free(splitPartList);
+
/* Detach the split partition. */
detachPartitionTable(rel, splitRel, defaultPartOid);
* needed.
*/
attachPartitionTable(NULL, rel, newPartRel, sps->bound);
+
+ /*
+ * Apply extension dependencies to the new partition's indexes. This
+ * preserves any "DEPENDS ON EXTENSION" settings from the split
+ * partition.
+ */
+ applyPartitionIndexExtDeps(RelationGetRelid(newPartRel), extDepState);
+
/* Keep the lock until commit. */
table_close(newPartRel, NoLock);
}
+ freePartitionIndexExtDeps(extDepState);
+
/* Drop the split partition. */
object.classId = RelationRelationId;
object.objectId = splitRelOid;
DROP INDEX e;
DROP SCHEMA test_ext CASCADE;
NOTICE: drop cascades to table a
+-- Fifth test: extension dependencies on partition indexes survive MERGE and
+-- SPLIT PARTITION operations, and mismatches between source partitions are
+-- reported.
+RESET search_path;
+CREATE EXTENSION test_ext3;
+CREATE EXTENSION test_ext5;
+CREATE TABLE part_extdep (i int, x int) PARTITION BY RANGE (i);
+CREATE TABLE part_extdep_1 PARTITION OF part_extdep FOR VALUES FROM (1) TO (2);
+CREATE TABLE part_extdep_2 PARTITION OF part_extdep FOR VALUES FROM (2) TO (3);
+CREATE TABLE part_extdep_3 PARTITION OF part_extdep FOR VALUES FROM (3) TO (4);
+CREATE TABLE part_extdep_4 PARTITION OF part_extdep FOR VALUES FROM (4) TO (5);
+CREATE TABLE part_extdep_5 PARTITION OF part_extdep FOR VALUES FROM (5) TO (6);
+CREATE INDEX part_extdep_i_idx ON part_extdep(i);
+CREATE INDEX part_extdep_x_idx ON part_extdep(x);
+-- Partitions 1, 2, 3 depend on the same two extensions.
+ALTER INDEX part_extdep_1_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_1_x_idx DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX part_extdep_2_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_2_x_idx DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX part_extdep_3_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_3_x_idx DEPENDS ON EXTENSION test_ext5;
+-- Partition 4 depends on a different extension on one index.
+ALTER INDEX part_extdep_4_i_idx DEPENDS ON EXTENSION test_ext5;
+-- Partition 5 has no dependency at all.
+-- Merge matching partitions: should succeed and preserve dependencies on the
+-- new partition's indexes (DROP EXTENSION must fail, naming the new index).
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_1, part_extdep_2)
+ INTO part_extdep_merged;
+DROP EXTENSION test_ext3;
+ERROR: cannot drop index part_extdep_merged_i_idx because index part_extdep_i_idx requires it
+HINT: You can drop index part_extdep_i_idx instead.
+SELECT c.relname, e.extname
+FROM pg_depend d
+JOIN pg_class c ON d.objid = c.oid
+JOIN pg_extension e ON d.refobjid = e.oid
+WHERE c.relname IN ('part_extdep_merged_i_idx', 'part_extdep_merged_x_idx')
+ AND e.extname IN ('test_ext3', 'test_ext5')
+ AND d.deptype = 'x'
+ORDER BY c.relname, e.extname;
+ relname | extname
+--------------------------+-----------
+ part_extdep_merged_i_idx | test_ext3
+ part_extdep_merged_x_idx | test_ext5
+(2 rows)
+
+-- An index created directly on a partition has no parent in the partitioned
+-- index tree. Such an index is dropped with its old partition during merge,
+-- and any extension dependency it carries goes away with it: the dep is not
+-- promoted to the merged partition. Verify by attaching test_ext9 to such
+-- an orphan index, merging, and observing that test_ext9 becomes droppable.
+CREATE EXTENSION test_ext9;
+CREATE INDEX part_extdep_3_extra_idx ON part_extdep_3(x);
+ALTER INDEX part_extdep_3_extra_idx DEPENDS ON EXTENSION test_ext9;
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_merged, part_extdep_3)
+ INTO part_extdep_merged2;
+DROP EXTENSION test_ext9;
+-- Mismatched dependencies: partition 4's index depends on a different
+-- extension than partition_merged2's. Both orderings must fail, and the
+-- error must cite both partition indexes.
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_merged2, part_extdep_4)
+ INTO part_extdep_bad;
+ERROR: cannot merge partitions with conflicting extension dependencies
+DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_merged2_i_idx" depend on different extensions.
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_merged2)
+ INTO part_extdep_bad;
+ERROR: cannot merge partitions with conflicting extension dependencies
+DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_merged2_i_idx" depend on different extensions.
+-- Empty vs non-empty dependency set (the subset case the earlier linear
+-- check missed in one direction).
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_5)
+ INTO part_extdep_bad;
+ERROR: cannot merge partitions with conflicting extension dependencies
+DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_5_i_idx" depend on different extensions.
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_5, part_extdep_4)
+ INTO part_extdep_bad;
+ERROR: cannot merge partitions with conflicting extension dependencies
+DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_5_i_idx" depend on different extensions.
+-- Subset: partition 5's i_idx depends on a strict superset of partition 4's
+-- i_idx dependencies. Partition 4 = {test_ext5}, partition 5 will be
+-- {test_ext3, test_ext5}. Both orderings must fail; in particular the case
+-- where the first partition we walk has fewer extensions than the second
+-- must still be rejected.
+ALTER INDEX part_extdep_5_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_5_i_idx DEPENDS ON EXTENSION test_ext5;
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_5)
+ INTO part_extdep_bad;
+ERROR: cannot merge partitions with conflicting extension dependencies
+DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_5_i_idx" depend on different extensions.
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_5, part_extdep_4)
+ INTO part_extdep_bad;
+ERROR: cannot merge partitions with conflicting extension dependencies
+DETAIL: Partition indexes "part_extdep_4_i_idx" and "part_extdep_5_i_idx" depend on different extensions.
+-- Reset partition 5 so it doesn't interfere with the SPLIT test below.
+ALTER INDEX part_extdep_5_i_idx NO DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_5_i_idx NO DEPENDS ON EXTENSION test_ext5;
+-- Split: the single source partition's dependencies must appear on every
+-- new partition's matching index, identified by extension name.
+ALTER TABLE part_extdep SPLIT PARTITION part_extdep_merged2 INTO
+ (PARTITION part_extdep_s1 FOR VALUES FROM (1) TO (3),
+ PARTITION part_extdep_s2 FOR VALUES FROM (3) TO (4));
+SELECT c.relname, e.extname
+FROM pg_depend d
+JOIN pg_class c ON d.objid = c.oid
+JOIN pg_extension e ON d.refobjid = e.oid
+WHERE c.relname IN ('part_extdep_s1_i_idx', 'part_extdep_s1_x_idx',
+ 'part_extdep_s2_i_idx', 'part_extdep_s2_x_idx')
+ AND e.extname IN ('test_ext3', 'test_ext5')
+ AND d.deptype = 'x'
+ORDER BY c.relname, e.extname;
+ relname | extname
+----------------------+-----------
+ part_extdep_s1_i_idx | test_ext3
+ part_extdep_s1_x_idx | test_ext5
+ part_extdep_s2_i_idx | test_ext3
+ part_extdep_s2_x_idx | test_ext5
+(4 rows)
+
+DROP TABLE part_extdep;
+DROP EXTENSION test_ext3;
+DROP EXTENSION test_ext5;
DROP MATERIALIZED VIEW d;
DROP INDEX e;
DROP SCHEMA test_ext CASCADE;
+
+-- Fifth test: extension dependencies on partition indexes survive MERGE and
+-- SPLIT PARTITION operations, and mismatches between source partitions are
+-- reported.
+RESET search_path;
+CREATE EXTENSION test_ext3;
+CREATE EXTENSION test_ext5;
+
+CREATE TABLE part_extdep (i int, x int) PARTITION BY RANGE (i);
+CREATE TABLE part_extdep_1 PARTITION OF part_extdep FOR VALUES FROM (1) TO (2);
+CREATE TABLE part_extdep_2 PARTITION OF part_extdep FOR VALUES FROM (2) TO (3);
+CREATE TABLE part_extdep_3 PARTITION OF part_extdep FOR VALUES FROM (3) TO (4);
+CREATE TABLE part_extdep_4 PARTITION OF part_extdep FOR VALUES FROM (4) TO (5);
+CREATE TABLE part_extdep_5 PARTITION OF part_extdep FOR VALUES FROM (5) TO (6);
+CREATE INDEX part_extdep_i_idx ON part_extdep(i);
+CREATE INDEX part_extdep_x_idx ON part_extdep(x);
+
+-- Partitions 1, 2, 3 depend on the same two extensions.
+ALTER INDEX part_extdep_1_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_1_x_idx DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX part_extdep_2_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_2_x_idx DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX part_extdep_3_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_3_x_idx DEPENDS ON EXTENSION test_ext5;
+
+-- Partition 4 depends on a different extension on one index.
+ALTER INDEX part_extdep_4_i_idx DEPENDS ON EXTENSION test_ext5;
+
+-- Partition 5 has no dependency at all.
+
+-- Merge matching partitions: should succeed and preserve dependencies on the
+-- new partition's indexes (DROP EXTENSION must fail, naming the new index).
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_1, part_extdep_2)
+ INTO part_extdep_merged;
+DROP EXTENSION test_ext3;
+SELECT c.relname, e.extname
+FROM pg_depend d
+JOIN pg_class c ON d.objid = c.oid
+JOIN pg_extension e ON d.refobjid = e.oid
+WHERE c.relname IN ('part_extdep_merged_i_idx', 'part_extdep_merged_x_idx')
+ AND e.extname IN ('test_ext3', 'test_ext5')
+ AND d.deptype = 'x'
+ORDER BY c.relname, e.extname;
+
+-- An index created directly on a partition has no parent in the partitioned
+-- index tree. Such an index is dropped with its old partition during merge,
+-- and any extension dependency it carries goes away with it: the dep is not
+-- promoted to the merged partition. Verify by attaching test_ext9 to such
+-- an orphan index, merging, and observing that test_ext9 becomes droppable.
+CREATE EXTENSION test_ext9;
+CREATE INDEX part_extdep_3_extra_idx ON part_extdep_3(x);
+ALTER INDEX part_extdep_3_extra_idx DEPENDS ON EXTENSION test_ext9;
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_merged, part_extdep_3)
+ INTO part_extdep_merged2;
+DROP EXTENSION test_ext9;
+
+-- Mismatched dependencies: partition 4's index depends on a different
+-- extension than partition_merged2's. Both orderings must fail, and the
+-- error must cite both partition indexes.
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_merged2, part_extdep_4)
+ INTO part_extdep_bad;
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_merged2)
+ INTO part_extdep_bad;
+
+-- Empty vs non-empty dependency set (the subset case the earlier linear
+-- check missed in one direction).
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_5)
+ INTO part_extdep_bad;
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_5, part_extdep_4)
+ INTO part_extdep_bad;
+
+-- Subset: partition 5's i_idx depends on a strict superset of partition 4's
+-- i_idx dependencies. Partition 4 = {test_ext5}, partition 5 will be
+-- {test_ext3, test_ext5}. Both orderings must fail; in particular the case
+-- where the first partition we walk has fewer extensions than the second
+-- must still be rejected.
+ALTER INDEX part_extdep_5_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_5_i_idx DEPENDS ON EXTENSION test_ext5;
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_4, part_extdep_5)
+ INTO part_extdep_bad;
+ALTER TABLE part_extdep MERGE PARTITIONS (part_extdep_5, part_extdep_4)
+ INTO part_extdep_bad;
+-- Reset partition 5 so it doesn't interfere with the SPLIT test below.
+ALTER INDEX part_extdep_5_i_idx NO DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX part_extdep_5_i_idx NO DEPENDS ON EXTENSION test_ext5;
+
+-- Split: the single source partition's dependencies must appear on every
+-- new partition's matching index, identified by extension name.
+ALTER TABLE part_extdep SPLIT PARTITION part_extdep_merged2 INTO
+ (PARTITION part_extdep_s1 FOR VALUES FROM (1) TO (3),
+ PARTITION part_extdep_s2 FOR VALUES FROM (3) TO (4));
+SELECT c.relname, e.extname
+FROM pg_depend d
+JOIN pg_class c ON d.objid = c.oid
+JOIN pg_extension e ON d.refobjid = e.oid
+WHERE c.relname IN ('part_extdep_s1_i_idx', 'part_extdep_s1_x_idx',
+ 'part_extdep_s2_i_idx', 'part_extdep_s2_x_idx')
+ AND e.extname IN ('test_ext3', 'test_ext5')
+ AND d.deptype = 'x'
+ORDER BY c.relname, e.extname;
+
+DROP TABLE part_extdep;
+DROP EXTENSION test_ext3;
+DROP EXTENSION test_ext5;
PartitionDispatch
PartitionElem
PartitionHashBound
+PartitionIndexExtDepEntry
PartitionKey
PartitionListValue
PartitionMap