]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Use generateClonedIndexStmt to propagate CREATE INDEX to partitions.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 5 Oct 2024 18:46:44 +0000 (14:46 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 5 Oct 2024 18:46:44 +0000 (14:46 -0400)
When instantiating an existing partitioned index for a new child
partition, we use generateClonedIndexStmt to build a suitable
IndexStmt to pass to DefineIndex.  However, when DefineIndex needs
to recurse to instantiate a newly created partitioned index on an
existing child partition, it was doing copyObject on the given
IndexStmt and then applying a bunch of ad-hoc fixups.  This has
a number of problems, primarily that it implies fresh lookups of
referenced objects such as opclasses and collations.  Since commit
2af07e2f7 caused DefineIndex to restrict search_path internally, those
lookups could fail or deliver different results than the original one.
We can avoid those problems and save a few dozen lines of code by
using generateClonedIndexStmt in this code path too.

Another thing this fixes is incorrect propagation of parent-index
comments to child indexes (because the copyObject approach copies
the idxcomment field while generateClonedIndexStmt doesn't).  I had
noticed this in connection with commit c01eb619a, but not run the
problem to ground.

I'm tempted to back-patch this further than v17, but the only thing
it's known to fix in older branches is the comment issue, which is
pretty minor and doesn't seem worth the risk of introducing new
issues in stable branches.  (If anyone does care about that,
clearing idxcomment in the copied IndexStmt would be a safer fix.)

Per bug #18637 from usamoi.  Back-patch to v17 where the search_path
change came in.

Discussion: https://postgr.es/m/18637-f51e314546e3ba2a@postgresql.org

contrib/seg/Makefile
contrib/seg/expected/partition.out [new file with mode: 0644]
contrib/seg/meson.build
contrib/seg/sql/partition.sql [new file with mode: 0644]
src/backend/commands/indexcmds.c
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index 132ec8dbfeaab88f4b63aba0cd5b9b09c2c37b16..b408f4049cbd7a99d5955b3227f262c57900125c 100644 (file)
@@ -14,7 +14,7 @@ PGFILEDESC = "seg - line segment data type"
 
 HEADERS = segdata.h
 
-REGRESS = security seg
+REGRESS = security seg partition
 
 EXTRA_CLEAN = segparse.h segparse.c segscan.c
 
diff --git a/contrib/seg/expected/partition.out b/contrib/seg/expected/partition.out
new file mode 100644 (file)
index 0000000..90d8397
--- /dev/null
@@ -0,0 +1,54 @@
+--
+-- Test that partitioned-index operations cope with objects that are
+-- not in the secure search path.  (This has little to do with seg,
+-- but we need an opclass that isn't in pg_catalog, and the base system
+-- has no such opclass.)  Note that we need to test propagation of the
+-- partitioned index's properties both to partitions that pre-date it
+-- and to partitions created later.
+--
+create function mydouble(int) returns int strict immutable parallel safe
+begin atomic select $1 * 2; end;
+create collation mycollation from "POSIX";
+create table pt (category int, sdata seg, tdata text)
+  partition by list (category);
+-- pre-existing partition
+create table pt12 partition of pt for values in (1,2);
+insert into pt values(1, '0 .. 1'::seg, 'zed');
+-- expression references object in public schema
+create index pti1 on pt ((mydouble(category) + 1));
+-- opclass in public schema
+create index pti2 on pt (sdata seg_ops);
+-- collation in public schema
+create index pti3 on pt (tdata collate mycollation);
+-- new partition
+create table pt34 partition of pt for values in (3,4);
+insert into pt values(4, '-1 .. 1'::seg, 'foo');
+\d+ pt
+                                Partitioned table "public.pt"
+  Column  |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description 
+----------+---------+-----------+----------+---------+----------+--------------+-------------
+ category | integer |           |          |         | plain    |              | 
+ sdata    | seg     |           |          |         | plain    |              | 
+ tdata    | text    |           |          |         | extended |              | 
+Partition key: LIST (category)
+Indexes:
+    "pti1" btree ((mydouble(category) + 1))
+    "pti2" btree (sdata)
+    "pti3" btree (tdata COLLATE mycollation)
+Partitions: pt12 FOR VALUES IN (1, 2),
+            pt34 FOR VALUES IN (3, 4)
+
+\d+ pt12
+                                     Table "public.pt12"
+  Column  |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description 
+----------+---------+-----------+----------+---------+----------+--------------+-------------
+ category | integer |           |          |         | plain    |              | 
+ sdata    | seg     |           |          |         | plain    |              | 
+ tdata    | text    |           |          |         | extended |              | 
+Partition of: pt FOR VALUES IN (1, 2)
+Partition constraint: ((category IS NOT NULL) AND (category = ANY (ARRAY[1, 2])))
+Indexes:
+    "pt12_expr_idx" btree ((mydouble(category) + 1))
+    "pt12_sdata_idx" btree (sdata)
+    "pt12_tdata_idx" btree (tdata COLLATE mycollation)
+
index abeaf08eff161b8dcd69b308ab3c60918052f921..018ba5591ae1e69afab7bb2c8a3e58ab4050fcf4 100644 (file)
@@ -55,6 +55,7 @@ tests += {
     'sql': [
       'security',
       'seg',
+      'partition',
     ],
   },
 }
diff --git a/contrib/seg/sql/partition.sql b/contrib/seg/sql/partition.sql
new file mode 100644 (file)
index 0000000..e1febde
--- /dev/null
@@ -0,0 +1,36 @@
+--
+-- Test that partitioned-index operations cope with objects that are
+-- not in the secure search path.  (This has little to do with seg,
+-- but we need an opclass that isn't in pg_catalog, and the base system
+-- has no such opclass.)  Note that we need to test propagation of the
+-- partitioned index's properties both to partitions that pre-date it
+-- and to partitions created later.
+--
+
+create function mydouble(int) returns int strict immutable parallel safe
+begin atomic select $1 * 2; end;
+
+create collation mycollation from "POSIX";
+
+create table pt (category int, sdata seg, tdata text)
+  partition by list (category);
+
+-- pre-existing partition
+create table pt12 partition of pt for values in (1,2);
+
+insert into pt values(1, '0 .. 1'::seg, 'zed');
+
+-- expression references object in public schema
+create index pti1 on pt ((mydouble(category) + 1));
+-- opclass in public schema
+create index pti2 on pt (sdata seg_ops);
+-- collation in public schema
+create index pti3 on pt (tdata collate mycollation);
+
+-- new partition
+create table pt34 partition of pt for values in (3,4);
+
+insert into pt values(4, '-1 .. 1'::seg, 'foo');
+
+\d+ pt
+\d+ pt12
index 77fab0c72a101f2c9270b9d663cfcdd8bf43c627..3d8df046dad8583a0375b5b4148c61ef52ddbc57 100644 (file)
@@ -50,6 +50,7 @@
 #include "optimizer/optimizer.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_oper.h"
+#include "parser/parse_utilcmd.h"
 #include "partitioning/partdesc.h"
 #include "pgstat.h"
 #include "rewrite/rewriteManip.h"
@@ -1445,55 +1446,20 @@ DefineIndex(Oid tableId,
                                 */
                                if (!found)
                                {
-                                       IndexStmt  *childStmt = copyObject(stmt);
-                                       bool            found_whole_row;
-                                       ListCell   *lc;
+                                       IndexStmt  *childStmt;
                                        ObjectAddress childAddr;
 
                                        /*
-                                        * We can't use the same index name for the child index,
-                                        * so clear idxname to let the recursive invocation choose
-                                        * a new name.  Likewise, the existing target relation
-                                        * field is wrong, and if indexOid or oldNumber are set,
-                                        * they mustn't be applied to the child either.
+                                        * Build an IndexStmt describing the desired child index
+                                        * in the same way that we do during ATTACH PARTITION.
+                                        * Notably, we rely on generateClonedIndexStmt to produce
+                                        * a search-path-independent representation, which the
+                                        * original IndexStmt might not be.
                                         */
-                                       childStmt->idxname = NULL;
-                                       childStmt->relation = NULL;
-                                       childStmt->indexOid = InvalidOid;
-                                       childStmt->oldNumber = InvalidRelFileNumber;
-                                       childStmt->oldCreateSubid = InvalidSubTransactionId;
-                                       childStmt->oldFirstRelfilelocatorSubid = InvalidSubTransactionId;
-
-                                       /*
-                                        * Adjust any Vars (both in expressions and in the index's
-                                        * WHERE clause) to match the partition's column numbering
-                                        * in case it's different from the parent's.
-                                        */
-                                       foreach(lc, childStmt->indexParams)
-                                       {
-                                               IndexElem  *ielem = lfirst(lc);
-
-                                               /*
-                                                * If the index parameter is an expression, we must
-                                                * translate it to contain child Vars.
-                                                */
-                                               if (ielem->expr)
-                                               {
-                                                       ielem->expr =
-                                                               map_variable_attnos((Node *) ielem->expr,
-                                                                                                       1, 0, attmap,
-                                                                                                       InvalidOid,
-                                                                                                       &found_whole_row);
-                                                       if (found_whole_row)
-                                                               elog(ERROR, "cannot convert whole-row table reference");
-                                               }
-                                       }
-                                       childStmt->whereClause =
-                                               map_variable_attnos(stmt->whereClause, 1, 0,
-                                                                                       attmap,
-                                                                                       InvalidOid, &found_whole_row);
-                                       if (found_whole_row)
-                                               elog(ERROR, "cannot convert whole-row table reference");
+                                       childStmt = generateClonedIndexStmt(NULL,
+                                                                                                               parentIndex,
+                                                                                                               attmap,
+                                                                                                               NULL);
 
                                        /*
                                         * Recurse as the starting user ID.  Callee will use that
index 79cf82b5aed1287f6511ab30df07522082186e9e..6de74a26a95f2100ca170fbcc5e54a79c5df72db 100644 (file)
@@ -2215,7 +2215,6 @@ select conname, obj_description(oid, 'pg_constraint') as desc
 (3 rows)
 
 alter table at_partitioned alter column name type varchar(127);
--- Note: these tests currently show the wrong behavior for comments :-(
 select relname,
   c.oid = oldoid as orig_oid,
   case relfilenode
@@ -2232,9 +2231,9 @@ select relname,
 ------------------------------+----------+---------+--------------
  at_partitioned               | t        | none    | 
  at_partitioned_0             | t        | own     | 
- at_partitioned_0_id_name_key | f        | own     | parent index
+ at_partitioned_0_id_name_key | f        | own     | 
  at_partitioned_1             | t        | own     | 
- at_partitioned_1_id_name_key | f        | own     | parent index
+ at_partitioned_1_id_name_key | f        | own     | 
  at_partitioned_id_name_key   | f        | none    | parent index
 (6 rows)
 
index 28cabc49e9fe02400dc65a6466b4cf8abb9a9dd3..da1272447304fb38c58d41fa043b03aa26902da8 100644 (file)
@@ -1496,8 +1496,6 @@ select conname, obj_description(oid, 'pg_constraint') as desc
 
 alter table at_partitioned alter column name type varchar(127);
 
--- Note: these tests currently show the wrong behavior for comments :-(
-
 select relname,
   c.oid = oldoid as orig_oid,
   case relfilenode