On Mon Apr 20, 2026 at 4:08 PM -03, Alexander Korotkov wrote:
> On Thu, Apr 16, 2026 at 9:03 PM Matheus Alcantara
> <[email protected]> wrote:
>>
>> On Tue Apr 14, 2026 at 6:05 AM -03, Dmitry Koval wrote:
>> > Hi Matheus!
>> >
>> > Thank you for patch.
>> > I agree that dependency should be automatically added for SPLIT
>> > PARTITION. But I'm not sure about MERGE PARTITION ...
>> > Might be it would be more correct to automatically add a dependency only
>> > if all merged partitions have it?
>>
>> Hi,
>>
>> Thank you for taking a look on this!
>>
>> I agree with your suggestion. The attached patch implements the
>> intersection behavior for MERGE PARTITIONS: extension dependencies are
>> only preserved on the merged partition's index if all source partition
>> indexes have that dependency.
>>
>> For example:
>> MERGE(idx1(ext_a, ext_b), idx2(ext_a)) -> idx3(ext_a)  -- only ext_a is 
>> common
>> MERGE(idx1(ext_a), idx2())             -> idx3()       -- no common deps
>
> This is not obvious for me.  I would rather trigger an error if there
> are different dependencies on merging partitions.
>

Yeah, I agree that this sounds a bit confusing, although this behavior
is documented on the last patch version I think that raising an error is
more simple and maybe is more obvious. The attached patch implement
this.

--
Matheus Alcantara
EDB: https://www.enterprisedb.com
From 14fdb594d2ea3a2ccaf86a7243c62cc7a030ecba Mon Sep 17 00:00:00 2001
From: Matheus Alcantara <[email protected]>
Date: Wed, 11 Mar 2026 11:39:56 -0300
Subject: [PATCH v5] Preserve extension dependencies on indexes during
 partition merge/split

When using ALTER TABLE ... MERGE PARTITIONS or ALTER TABLE ... SPLIT
PARTITION, extension dependencies on partition indexes  were being lost.
This happened because the new partition indexes are created fresh from
the parent partitioned table's indexes, while the old partition indexes
(with their extension dependencies) are dropped.

Fix this by collecting extension dependencies from source partition
indexes before detaching them, then applying those dependencies to
the corresponding new partition indexes after they're created. The
mapping between old and new indexes is done via their common parent
partitioned index.

For 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.

For SPLIT operations, the new partition indexes simply inherit all
extension dependencies from the source partition's index.

Author: Matheus Alcantara <[email protected]>
Reported-by: Kirill Reshke
Reviewed-by: Dmitry Koval <[email protected]>
Reviewed-by: Alexander Korotkov <[email protected]>
Discussion: 
https://www.postgresql.org/message-id/CALdSSPjXtzGM7Uk4fWRwRMXcCczge5uNirPQcYCHKPAWPkp9iQ%40mail.gmail.com
---
 doc/src/sgml/ref/alter_table.sgml             |  17 ++
 src/backend/commands/tablecmds.c              | 255 ++++++++++++++++++
 src/test/regress/expected/partition_merge.out |  86 ++++++
 src/test/regress/expected/partition_split.out |  41 +++
 src/test/regress/sql/partition_merge.sql      |  76 ++++++
 src/test/regress/sql/partition_split.sql      |  39 +++
 src/tools/pgindent/typedefs.list              |   1 +
 7 files changed, 515 insertions(+)

diff --git a/doc/src/sgml/ref/alter_table.sgml 
b/doc/src/sgml/ref/alter_table.sgml
index 54f38cb964a..1f9a456fd33 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -1255,6 +1255,15 @@ WITH ( MODULUS <replaceable 
class="parameter">numeric_literal</replaceable>, REM
       (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
@@ -1342,6 +1351,14 @@ WITH ( MODULUS <replaceable 
class="parameter">numeric_literal</replaceable>, REM
       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
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index eec09ba1ded..1e2b2fc2cb0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -40,6 +40,7 @@
 #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"
@@ -60,6 +61,7 @@
 #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"
@@ -365,6 +367,19 @@ typedef enum addFkConstraintSides
        addFkBothSides,
 } addFkConstraintSides;
 
+/*
+ * Hold extension dependencies for a partitioned index. Used by
+ * collectPartitionIndexExtDeps and applyPartitionIndexExtDeps.
+ *
+ * Extension dependencies are created on the new partition based
+ * on the indexes that share the same parent index oid.
+ */
+typedef struct PartitionIndexExtDepEntry
+{
+       Oid                     parentIndexOid; /* OID of the parent 
partitioned index */
+       List       *extensionOids;      /* List of extension OIDs this index 
depends */
+} PartitionIndexExtDepEntry;
+
 /*
  * Partition tables are expected to be dropped when the parent partitioned
  * table gets dropped. Hence for partitioning we use AUTO dependency.
@@ -760,6 +775,9 @@ static void ATExecMergePartitions(List **wqueue, 
AlteredTableInfo *tab, Relation
 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
@@ -23000,6 +23018,204 @@ detachPartitionTable(Relation parent_rel, Relation 
child_rel, Oid defaultPartOid
        PopActiveSnapshot();
 }
 
+/*
+ * collectPartitionIndexExtDeps: collect extension dependencies from indexes
+ * on the given partitions.
+ *
+ * For each partition index that has a parent partitioned index, we collect
+ * extension dependencies. Dependencies are stored grouped by parent index OID.
+ *
+ * When multiple partitions have indexes with the same parent, all source
+ * partition indexes must have the same extension dependencies. If there is
+ * a mismatch (e.g., one partition's index depends on an extension while
+ * another doesn't, or they depend on different extensions), an error is
+ * raised. This ensures consistent behavior and avoids silently dropping
+ * dependencies during merge operations.
+ *
+ * Indexes that don't have a parent partitioned index (i.e., indexes created
+ * directly on a partition without a corresponding parent index) are skipped.
+ *
+ * Returns a list of PartitionIndexExtDepEntry structs.
+ */
+static List *
+collectPartitionIndexExtDeps(List *partitionOids)
+{
+       List       *result = NIL;
+
+       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;
+                       List       *extDeps;
+                       PartitionIndexExtDepEntry *entry = NULL;
+
+                       /* Get the parent index if this is a partition index */
+                       if (!get_rel_relispartition(indexOid))
+                               continue;
+
+                       parentIndexOid = get_partition_parent(indexOid, true);
+                       if (!OidIsValid(parentIndexOid))
+                               continue;
+
+                       /* Get extension dependencies for this index. */
+                       extDeps = getAutoExtensionsOfObject(RelationRelationId, 
indexOid);
+
+                       /* Look for existing partition entry for this parent 
index */
+                       foreach_ptr(PartitionIndexExtDepEntry, e, result)
+                       {
+                               if (e->parentIndexOid == parentIndexOid)
+                               {
+                                       entry = e;
+                                       break;
+                               }
+                       }
+
+                       if (entry != NULL)
+                       {
+                               /*
+                                * We already have an entry for this parent 
index from a
+                                * previous partition. Verify that the 
dependencies match
+                                * exactly.
+                                */
+                               if (extDeps == NIL && entry->extensionOids != 
NIL)
+                                       ereport(ERROR,
+                                                       
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                        errmsg("cannot merge 
partitions with conflicting extension dependencies"),
+                                                        errdetail("Partition 
index \"%s\" has no extension dependencies, but another partition's index 
does.",
+                                                                          
get_rel_name(indexOid))));
+
+                               if (extDeps != NIL && entry->extensionOids == 
NIL)
+                                       ereport(ERROR,
+                                                       
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                        errmsg("cannot merge 
partitions with conflicting extension dependencies"),
+                                                        errdetail("Partition 
index \"%s\" has extension dependencies, but another partition's index does 
not.",
+                                                                          
get_rel_name(indexOid))));
+
+                               foreach_oid(extOid, extDeps)
+                               {
+                                       if 
(!list_member_oid(entry->extensionOids, extOid))
+                                               ereport(ERROR,
+                                                               
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                                errmsg("cannot 
merge partitions with conflicting extension dependencies"),
+                                                                
errdetail("Partition index \"%s\" depends on extension \"%s\", but another 
partition's index does not.",
+                                                                               
   get_rel_name(indexOid),
+                                                                               
   get_extension_name(extOid))));
+                               }
+                       }
+                       else
+                       {
+                               /*
+                                * First partition index we've seen for this 
parent. Create a
+                                * new entry with its dependencies (may be NIL) 
- subsequent
+                                * partitions will be checked against this.
+                                */
+                               entry = 
palloc(sizeof(PartitionIndexExtDepEntry));
+                               entry->parentIndexOid = parentIndexOid;
+                               entry->extensionOids = extDeps;
+                               result = lappend(result, entry);
+                       }
+               }
+
+               list_free(indexList);
+               table_close(partRel, NoLock);
+       }
+
+       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.
+ */
+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;
+               ListCell   *lc;
+
+               /* Skip if this is not a partition index */
+               if (!get_rel_relispartition(indexOid))
+                       continue;
+
+               parentIdxOid = get_partition_parent(indexOid, true);
+               if (!OidIsValid(parentIdxOid))
+                       continue;
+
+               /* Look for extension dependencies to apply */
+               foreach(lc, extDepState)
+               {
+                       PartitionIndexExtDepEntry *entry = lfirst(lc);
+
+                       if (entry->parentIndexOid == parentIdxOid)
+                       {
+                               ObjectAddress indexAddr;
+
+                               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)
+{
+       ListCell   *lc;
+
+       foreach(lc, extDepState)
+       {
+               PartitionIndexExtDepEntry *entry = lfirst(lc);
+
+               list_free(entry->extensionOids);
+               pfree(entry);
+       }
+       list_free(extDepState);
+}
+
 /*
  * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
  */
@@ -23009,6 +23225,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo 
*tab, Relation rel,
 {
        Relation        newPartRel;
        List       *mergingPartitions = NIL;
+       List       *extDepState = NIL;
        Oid                     defaultPartOid;
        Oid                     existingRelid;
        Oid                     ownerId = InvalidOid;
@@ -23098,6 +23315,13 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo 
*tab, Relation rel,
        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)
        {
@@ -23175,6 +23399,15 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo 
*tab, Relation rel,
         */
        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);
 
@@ -23469,11 +23702,13 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo 
*tab, Relation rel,
        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));
 
@@ -23506,6 +23741,16 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo 
*tab, Relation rel,
                                        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);
 
@@ -23585,10 +23830,20 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo 
*tab, Relation rel,
                 * 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;
diff --git a/src/test/regress/expected/partition_merge.out 
b/src/test/regress/expected/partition_merge.out
index 883110e25d9..541f88fdbb1 100644
--- a/src/test/regress/expected/partition_merge.out
+++ b/src/test/regress/expected/partition_merge.out
@@ -1091,6 +1091,92 @@ SELECT count(*) FROM t WHERE i = 15 AND g IN (SELECT g + 
10 FROM t WHERE i = 5);
 (1 row)
 
 DROP TABLE t;
+--
+-- Test that extension dependencies on partition indexes are preserved
+-- after MERGE PARTITIONS.
+--
+CREATE EXTENSION IF NOT EXISTS test_ext3;
+CREATE EXTENSION IF NOT EXISTS test_ext5;
+CREATE TABLE t_merge_extdep (i int, x int, y int) PARTITION BY RANGE (i);
+CREATE TABLE t_merge_extdep_1 PARTITION OF t_merge_extdep FOR VALUES FROM (1) 
TO (2);
+CREATE TABLE t_merge_extdep_2 PARTITION OF t_merge_extdep FOR VALUES FROM (2) 
TO (3);
+CREATE TABLE t_merge_extdep_3 PARTITION OF t_merge_extdep FOR VALUES FROM (3) 
TO (4);
+CREATE TABLE t_merge_extdep_4 PARTITION OF t_merge_extdep FOR VALUES FROM (4) 
TO (5);
+CREATE TABLE t_merge_extdep_5 PARTITION OF t_merge_extdep FOR VALUES FROM (5) 
TO (6); -- Don't have a dependency
+CREATE INDEX t_merge_extdep_idx ON t_merge_extdep(i);
+CREATE INDEX t_merge_extdep_x ON t_merge_extdep(x);
+-- Add extension dependency for some partition indexes
+ALTER INDEX t_merge_extdep_1_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX t_merge_extdep_1_x_idx DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX t_merge_extdep_2_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX t_merge_extdep_2_x_idx DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX t_merge_extdep_3_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX t_merge_extdep_3_x_idx DEPENDS ON EXTENSION test_ext5;
+-- Add only a single dependency to test that it fails when merge partition with
+-- different extension dependencies.
+ALTER INDEX t_merge_extdep_4_i_idx DEPENDS ON EXTENSION test_ext5;
+-- Should fail: dependencies exist
+DROP EXTENSION test_ext3;
+ERROR:  cannot drop index t_merge_extdep_3_i_idx because index 
t_merge_extdep_idx requires it
+HINT:  You can drop index t_merge_extdep_idx instead.
+-- Merge partitions
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_1, 
t_merge_extdep_2) INTO t_merge_extdep_merged;
+-- Should still fail: dependencies should be preserved on the new partition's 
index
+DROP EXTENSION test_ext3;
+ERROR:  cannot drop index t_merge_extdep_merged_i_idx because index 
t_merge_extdep_idx requires it
+HINT:  You can drop index t_merge_extdep_idx instead.
+-- Verify that dependencies for all indexes exists in pg_depend
+SELECT relname, 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 ('t_merge_extdep_merged_i_idx', 
't_merge_extdep_merged_x_idx')
+  AND e.extname IN ('test_ext3', 'test_ext5')
+  AND d.deptype = 'x'
+ORDER BY relname, extname;
+           relname           |  extname  
+-----------------------------+-----------
+ t_merge_extdep_merged_i_idx | test_ext3
+ t_merge_extdep_merged_x_idx | test_ext5
+(2 rows)
+
+-- Create an index directly on a partition (without a parent partitioned 
index).
+-- Such indexes are not recreated on merge because they have no parent to map 
to.
+-- This test verifies that partition-only indexes don't cause issues during 
merge.
+CREATE INDEX t_merge_extdep_3_y_idx ON t_merge_extdep_3(y);
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_merged, 
t_merge_extdep_3) INTO t_merge_extdep_merged2;
+-- The partition-only index is dropped with its partition
+SELECT relname
+FROM pg_class
+WHERE relname LIKE 't_merge_extdep_merged2%idx'
+ORDER BY relname;
+           relname            
+------------------------------
+ t_merge_extdep_merged2_i_idx
+ t_merge_extdep_merged2_x_idx
+(2 rows)
+
+-- Should fail: Partitions to be merged have different extension dependencies.
+-- Also test with different ordering to ensure correctness.
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_merged2, 
t_merge_extdep_4) INTO t_merge_extdep_merged3;
+ERROR:  cannot merge partitions with conflicting extension dependencies
+DETAIL:  Partition index "t_merge_extdep_4_i_idx" depends on extension 
"test_ext5", but another partition's index does not.
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_4, 
t_merge_extdep_merged2) INTO t_merge_extdep_merged3;
+ERROR:  cannot merge partitions with conflicting extension dependencies
+DETAIL:  Partition index "t_merge_extdep_merged2_i_idx" depends on extension 
"test_ext3", but another partition's index does not.
+-- Should fail: Partitions to be merged have different extension dependencies
+-- (t_merge_extdep_5 don't have dependencies)
+-- Also test with different ordering to ensure correctness.
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_4, 
t_merge_extdep_5) INTO t_merge_extdep_merged3;
+ERROR:  cannot merge partitions with conflicting extension dependencies
+DETAIL:  Partition index "t_merge_extdep_5_i_idx" has no extension 
dependencies, but another partition's index does.
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_5, 
t_merge_extdep_4) INTO t_merge_extdep_merged3;
+ERROR:  cannot merge partitions with conflicting extension dependencies
+DETAIL:  Partition index "t_merge_extdep_4_i_idx" has extension dependencies, 
but another partition's index does not.
+-- Clean up
+DROP TABLE t_merge_extdep;
+DROP EXTENSION test_ext3;
+DROP EXTENSION test_ext5;
 RESET search_path;
 --
 DROP SCHEMA partitions_merge_schema;
diff --git a/src/test/regress/expected/partition_split.out 
b/src/test/regress/expected/partition_split.out
index 43ca299648e..574119c11a7 100644
--- a/src/test/regress/expected/partition_split.out
+++ b/src/test/regress/expected/partition_split.out
@@ -1589,6 +1589,47 @@ SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT 
tab_id FROM t WHERE i =
 (1 row)
 
 DROP TABLE t;
+--
+-- Test that extension dependencies on partition indexes are preserved
+-- after SPLIT PARTITION.
+--
+CREATE EXTENSION citext;
+CREATE TABLE t_extdep (i int) PARTITION BY RANGE (i);
+CREATE TABLE t_extdep_1_3 PARTITION OF t_extdep FOR VALUES FROM (1) TO (3);
+CREATE INDEX t_extdep_idx ON t_extdep (i);
+-- Add extension dependency on partition index
+ALTER INDEX t_extdep_1_3_i_idx DEPENDS ON EXTENSION citext;
+-- Should fail: dependency exists
+DROP EXTENSION citext;
+ERROR:  cannot drop index t_extdep_1_3_i_idx because index t_extdep_idx 
requires it
+HINT:  You can drop index t_extdep_idx instead.
+-- Split partition
+ALTER TABLE t_extdep SPLIT PARTITION t_extdep_1_3 INTO
+  (PARTITION t_extdep_1 FOR VALUES FROM (1) TO (2),
+   PARTITION t_extdep_2 FOR VALUES FROM (2) TO (3));
+-- Should still fail: dependencies should be preserved on all new partitions' 
indexes
+DROP EXTENSION citext;
+ERROR:  cannot drop index t_extdep_2_i_idx because index t_extdep_idx requires 
it
+HINT:  You can drop index t_extdep_idx instead.
+-- Verify the dependencies exist in pg_depend for both new partitions
+SELECT c.relname, COUNT(*) > 0 AS has_ext_dep
+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 ('t_extdep_1_i_idx', 't_extdep_2_i_idx')
+  AND e.extname = 'citext'
+  AND d.deptype = 'x'
+GROUP BY c.relname
+ORDER BY c.relname;
+     relname      | has_ext_dep 
+------------------+-------------
+ t_extdep_1_i_idx | t
+ t_extdep_2_i_idx | t
+(2 rows)
+
+-- Clean up
+DROP TABLE t_extdep;
+DROP EXTENSION citext;
 RESET search_path;
 --
 DROP SCHEMA partition_split_schema;
diff --git a/src/test/regress/sql/partition_merge.sql 
b/src/test/regress/sql/partition_merge.sql
index a211fee2ad1..0f62129dcc1 100644
--- a/src/test/regress/sql/partition_merge.sql
+++ b/src/test/regress/sql/partition_merge.sql
@@ -784,6 +784,82 @@ SELECT count(*) FROM t WHERE i = 15 AND g IN (SELECT g + 
10 FROM t WHERE i = 5);
 DROP TABLE t;
 
 
+--
+-- Test that extension dependencies on partition indexes are preserved
+-- after MERGE PARTITIONS.
+--
+CREATE EXTENSION IF NOT EXISTS test_ext3;
+CREATE EXTENSION IF NOT EXISTS test_ext5;
+
+CREATE TABLE t_merge_extdep (i int, x int, y int) PARTITION BY RANGE (i);
+CREATE TABLE t_merge_extdep_1 PARTITION OF t_merge_extdep FOR VALUES FROM (1) 
TO (2);
+CREATE TABLE t_merge_extdep_2 PARTITION OF t_merge_extdep FOR VALUES FROM (2) 
TO (3);
+CREATE TABLE t_merge_extdep_3 PARTITION OF t_merge_extdep FOR VALUES FROM (3) 
TO (4);
+CREATE TABLE t_merge_extdep_4 PARTITION OF t_merge_extdep FOR VALUES FROM (4) 
TO (5);
+CREATE TABLE t_merge_extdep_5 PARTITION OF t_merge_extdep FOR VALUES FROM (5) 
TO (6); -- Don't have a dependency
+CREATE INDEX t_merge_extdep_idx ON t_merge_extdep(i);
+CREATE INDEX t_merge_extdep_x ON t_merge_extdep(x);
+
+-- Add extension dependency for some partition indexes
+ALTER INDEX t_merge_extdep_1_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX t_merge_extdep_1_x_idx DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX t_merge_extdep_2_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX t_merge_extdep_2_x_idx DEPENDS ON EXTENSION test_ext5;
+ALTER INDEX t_merge_extdep_3_i_idx DEPENDS ON EXTENSION test_ext3;
+ALTER INDEX t_merge_extdep_3_x_idx DEPENDS ON EXTENSION test_ext5;
+
+-- Add only a single dependency to test that it fails when merge partition with
+-- different extension dependencies.
+ALTER INDEX t_merge_extdep_4_i_idx DEPENDS ON EXTENSION test_ext5;
+
+-- Should fail: dependencies exist
+DROP EXTENSION test_ext3;
+
+-- Merge partitions
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_1, 
t_merge_extdep_2) INTO t_merge_extdep_merged;
+
+-- Should still fail: dependencies should be preserved on the new partition's 
index
+DROP EXTENSION test_ext3;
+
+-- Verify that dependencies for all indexes exists in pg_depend
+SELECT relname, 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 ('t_merge_extdep_merged_i_idx', 
't_merge_extdep_merged_x_idx')
+  AND e.extname IN ('test_ext3', 'test_ext5')
+  AND d.deptype = 'x'
+ORDER BY relname, extname;
+
+-- Create an index directly on a partition (without a parent partitioned 
index).
+-- Such indexes are not recreated on merge because they have no parent to map 
to.
+-- This test verifies that partition-only indexes don't cause issues during 
merge.
+CREATE INDEX t_merge_extdep_3_y_idx ON t_merge_extdep_3(y);
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_merged, 
t_merge_extdep_3) INTO t_merge_extdep_merged2;
+
+-- The partition-only index is dropped with its partition
+SELECT relname
+FROM pg_class
+WHERE relname LIKE 't_merge_extdep_merged2%idx'
+ORDER BY relname;
+
+-- Should fail: Partitions to be merged have different extension dependencies.
+-- Also test with different ordering to ensure correctness.
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_merged2, 
t_merge_extdep_4) INTO t_merge_extdep_merged3;
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_4, 
t_merge_extdep_merged2) INTO t_merge_extdep_merged3;
+
+-- Should fail: Partitions to be merged have different extension dependencies
+-- (t_merge_extdep_5 don't have dependencies)
+-- Also test with different ordering to ensure correctness.
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_4, 
t_merge_extdep_5) INTO t_merge_extdep_merged3;
+ALTER TABLE t_merge_extdep MERGE PARTITIONS (t_merge_extdep_5, 
t_merge_extdep_4) INTO t_merge_extdep_merged3;
+
+-- Clean up
+DROP TABLE t_merge_extdep;
+DROP EXTENSION test_ext3;
+DROP EXTENSION test_ext5;
+
+
 RESET search_path;
 
 --
diff --git a/src/test/regress/sql/partition_split.sql 
b/src/test/regress/sql/partition_split.sql
index 44fcf208ac6..22563d3804c 100644
--- a/src/test/regress/sql/partition_split.sql
+++ b/src/test/regress/sql/partition_split.sql
@@ -1127,6 +1127,45 @@ SELECT count(*) FROM t WHERE i = 0 AND tab_id IN (SELECT 
tab_id FROM t WHERE i =
 DROP TABLE t;
 
 
+--
+-- Test that extension dependencies on partition indexes are preserved
+-- after SPLIT PARTITION.
+--
+CREATE EXTENSION citext;
+
+CREATE TABLE t_extdep (i int) PARTITION BY RANGE (i);
+CREATE TABLE t_extdep_1_3 PARTITION OF t_extdep FOR VALUES FROM (1) TO (3);
+CREATE INDEX t_extdep_idx ON t_extdep (i);
+
+-- Add extension dependency on partition index
+ALTER INDEX t_extdep_1_3_i_idx DEPENDS ON EXTENSION citext;
+
+-- Should fail: dependency exists
+DROP EXTENSION citext;
+
+-- Split partition
+ALTER TABLE t_extdep SPLIT PARTITION t_extdep_1_3 INTO
+  (PARTITION t_extdep_1 FOR VALUES FROM (1) TO (2),
+   PARTITION t_extdep_2 FOR VALUES FROM (2) TO (3));
+
+-- Should still fail: dependencies should be preserved on all new partitions' 
indexes
+DROP EXTENSION citext;
+
+-- Verify the dependencies exist in pg_depend for both new partitions
+SELECT c.relname, COUNT(*) > 0 AS has_ext_dep
+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 ('t_extdep_1_i_idx', 't_extdep_2_i_idx')
+  AND e.extname = 'citext'
+  AND d.deptype = 'x'
+GROUP BY c.relname
+ORDER BY c.relname;
+
+-- Clean up
+DROP TABLE t_extdep;
+DROP EXTENSION citext;
+
 RESET search_path;
 
 --
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 49dfb662abc..9f1dd55213d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2200,6 +2200,7 @@ PartitionDirectoryEntry
 PartitionDispatch
 PartitionElem
 PartitionHashBound
+PartitionIndexExtDepEntry
 PartitionKey
 PartitionListValue
 PartitionMap
-- 
2.53.0

Reply via email to