Re: BUG #18637: CREATE INDEX won't look up operator classes in search_path if PARTITION BY is specified - Mailing list pgsql-bugs

From Tom Lane
Subject Re: BUG #18637: CREATE INDEX won't look up operator classes in search_path if PARTITION BY is specified
Date
Msg-id 427540.1727979435@sss.pgh.pa.us
Whole thread Raw
In response to Re: BUG #18637: CREATE INDEX won't look up operator classes in search_path if PARTITION BY is specified  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: BUG #18637: CREATE INDEX won't look up operator classes in search_path if PARTITION BY is specified
List pgsql-bugs
I wrote:
> So that's pretty awful: creating the partitioned index by itself is
> willing to look up the opclass in the current search path, and then
> adding a new partition will play nice with that, but creating a
> partitioned index when there's already a partition will not.
> It's got to be considered a bug that the two paths for making a
> child index behave differently.

I looked into this and found that

* When adding a partition to a pre-existing partitioned index,
we use generateClonedIndexStmt() to construct the IndexStmt
for DefineIndex to use.  This is proof against the current
problem because it will always schema-qualify the opclass
name, cf. get_opclass().

* However, if CREATE INDEX recurses to create a child partition,
it does this:

                    IndexStmt  *childStmt = copyObject(stmt);

followed by some pretty ad-hoc cleanup.  If the original
IndexStmt wasn't written in a search-path-independent fashion
then we've got trouble.

It seems clear to me that the right fix for this is to use
generateClonedIndexStmt in this code path too.  I am not
claiming that generateClonedIndexStmt is entirely free of
related issues, but to the extent it has any we'd have to
fix them anyway.  Building the IndexStmt in two fundamentally
different ways is just asking for trouble.

I hacked this up and was amused to discover that it also
fixes a TODO left over from c01eb619a: if there's a comment
for the parent index, we've been incorrectly applying it
to children too.  generateClonedIndexStmt knows better than
to fill idxcomment for the child IndexStmt, but copyObject
does not.

This could stand an additional regression test case perhaps,
but the core tests don't have a suitable opclass at hand.
Maybe we could test it in some contrib module, but that
seems a shade hacky.

Thoughts?

            regards, tom lane

diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 130cebd658..e33ad81529 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -51,6 +51,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"
@@ -1465,55 +1466,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
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index fb8db37623..143ae7c09c 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -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)

diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index cba15ebfec..c5dd43a15c 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -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

pgsql-bugs by date:

Previous
From: Tom Lane
Date:
Subject: Re: BUG #18637: CREATE INDEX won't look up operator classes in search_path if PARTITION BY is specified
Next
From: Noah Misch
Date:
Subject: Re: BUG #18637: CREATE INDEX won't look up operator classes in search_path if PARTITION BY is specified