On 2019/04/25 19:02, Amit Langote wrote:
> On 2019/04/25 11:21, Amit Langote wrote:
>> On 2019/04/25 8:27, Tom Lane wrote:
>>> BTW, I hadn't ever looked closely at what the index reuse code
>>> does, and now that I have, my heart just sinks.  I think that
>>> logic needs to be nuked from orbit.  RelationPreserveStorage was
>>> never meant to be abused in this way; I invite you to contemplate
>>> whether it's not a problem that it doesn't check the backend and
>>> nestLevel fields of PendingRelDelete entries before killing them.
>>> (In its originally-designed use for mapped rels at transaction end,
>>> that wasn't a problem, but I'm afraid that it may be one now.)
>>>
>>> The right way to do this IMO would be something closer to the
>>> heap-swap logic in cluster.c, where we exchange the relfilenodes
>>> of two live indexes, rather than what is happening now.  Or for
>>> that matter, do we really need to delete the old indexes at all?
>>
>> Yeah, we wouldn't need TryReuseIndex and subsequent
>> RelationPreserveStorage if we hadn't dropped the old indexes to begin
>> with.  Instead, in ATPostAlterTypeParse, check if the index after ALTER
>> TYPE is still incompatible (CheckIndexCompatible) and if it is, don't add
>> a new AT_ReAddIndex command.  If it's not, *then* drop the index, and
>> recreate the index from scratch using an IndexStmt generated from the old
>> index definition.  I guess We can get rid of IndexStmt.oldNode too.
> 
> Thinking on this more and growing confident that we could indeed avoid
> drop index + recreate-it-while-preserving-storage, instead by just not
> doing anything when CheckIndexCompatible says the old index will be fine
> despite ALTER TYPE, but only if the table is not rewritten.  I gave this a
> try and came up with the attached patch.  It fixes the bug related to
> partitioned indexes (the originally reported one) and then some.
> 
> Basically, I aimed to rewrite the code in ATPostAlterTypeCleanup and
> ATPostAlterTypeParse such that we no longer have to rely on an
> implementation based on setting "oldNode" to preserve old indexes.  With
> the attached, for the cases in which the table won't be rewritten and
> hence the indexes not rebuilt, ATPostAlterTypeParse() simply won't queue a
> AT_ReAddIndex command to rebuild index while preserving the storage.  That
> means both ATAddIndex() and DefineIndex can be freed of the duty of
> looking out for the "oldNode" case, because that case no longer exists.
> 
> Another main change is that inherited (!conislocal) constraints are now
> recognized by ATPostAlterTypeParse directly, instead of
> ATPostAlterTypeCleanup checking for them and skipping
> ATPostAlterTypeParse() as a whole for such constraints.  For one, I had to
> make that change to make the above-described approach work.  Also, doing
> that allowed to fix another bug whereby the comments of child constraints
> would go away when they're reconstructed.  Notice what happens on
> un-patched PG 11:
> 
> create table pp (a int, b text, unique (a, b), c varchar(64)) partition by
> list (a);
> create table pp1 partition of pp for values in (1);
> create table pp2 partition of pp for values in (2);
> alter table pp add constraint c_chk check (c <> '');
> comment on constraint c_chk ON pp is 'parent check constraint';
> comment on constraint c_chk ON pp1 is 'child check constraint 1';
> comment on constraint c_chk ON pp2 is 'child check constraint 2';
> select conname, obj_description(oid, 'pg_constraint') from pg_constraint
> where conname = 'c_chk';
>  conname │     obj_description
> ─────────┼──────────────────────────
>  c_chk   │ parent check constraint
>  c_chk   │ child check constraint 1
>  c_chk   │ child check constraint 2
> (3 rows)
> 
> alter table pp alter c type varchar(64);
> 
> select conname, obj_description(oid, 'pg_constraint') from pg_constraint
> where conname = 'c_chk';
>  conname │     obj_description
> ─────────┼─────────────────────────
>  c_chk   │ parent check constraint
>  c_chk   │
>  c_chk   │
> (3 rows)
> 
> The patch fixes that with some surgery of RebuildConstraintComment
> combined with aforementioned changes.  With the patch:
> 
> alter table pp alter c type varchar(64);
> 
> select conname, obj_description(oid, 'pg_constraint') from pg_constraint
> where conname = 'c_chk';
>  conname │     obj_description
> ─────────┼──────────────────────────
>  c_chk   │ parent check constraint
>  c_chk   │ child check constraint 1
>  c_chk   │ child check constraint 2
> (3 rows)
> 
> alter table pp alter c type varchar(128);
> 
> select conname, obj_description(oid, 'pg_constraint') from pg_constraint
> where conname = 'c_chk';
>  conname │     obj_description
> ─────────┼──────────────────────────
>  c_chk   │ parent check constraint
>  c_chk   │ child check constraint 1
>  c_chk   │ child check constraint 2
> (3 rows)
> 
> Also for index comments, but only in the case when indexes are not rebuilt.
> 
> comment on index pp_a_b_key is 'parent index';
> comment on index pp1_a_b_key is 'child index 1';
> comment on index pp2_a_b_key is 'child index 2';
> 
> select relname, relfilenode, obj_description(oid,'pg_class') from pg_class
> where relname like 'pp%key';
>    relname   │ relfilenode │ obj_description
> ─────────────┼─────────────┼─────────────────
>  pp1_a_b_key │       17280 │ child index 1
>  pp2_a_b_key │       17284 │ child index 2
>  pp_a_b_key  │       17271 │ parent index
> (3 rows)
> 
> -- no rewrite, indexes untouched, comments preserved
> alter table pp alter b type varchar(128);
> 
> select relname, relfilenode, obj_description(oid,'pg_class') from pg_class
> where relname like 'pp%key';
>    relname   │ relfilenode │ obj_description
> ─────────────┼─────────────┼─────────────────
>  pp1_a_b_key │       17280 │ child index 1
>  pp2_a_b_key │       17284 │ child index 2
>  pp_a_b_key  │       17271 │ parent index
> (3 rows)
> 
> -- table rewritten, indexes rebuild, child indexes' comments gone
> alter table pp alter b type varchar(64);
> 
> select relname, relfilenode, obj_description(oid,'pg_class') from pg_class
> where relname like 'pp%key';
>    relname   │ relfilenode │ obj_description
> ─────────────┼─────────────┼─────────────────
>  pp1_a_b_key │       17294 │
>  pp2_a_b_key │       17298 │
>  pp_a_b_key  │       17285 │ parent index
> (3 rows)
> 
> 
> I've also added tests for both the originally reported bug and the comment
> ones.
> 
> The patch applies to PG 11.

Per Alvaro's report, regression tests added weren't portable.  Fixed that
in the attached updated patch.

Thanks,
Amit
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index f8ee4b0a84..64bf2fc932 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -809,11 +809,9 @@ DefineIndex(Oid relationId,
                                                indexRelationName, 
RelationGetRelationName(rel))));
        }
 
-       /*
-        * A valid stmt->oldNode implies that we already have a built form of 
the
-        * index.  The caller should also decline any index build.
-        */
-       Assert(!OidIsValid(stmt->oldNode) || (skip_build && !stmt->concurrent));
+
+       /* stmt->oldNode is no longer used. */
+       Assert(!OidIsValid(stmt->oldNode));
 
        /*
         * Make the catalog entries for the index, including constraints. This
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 65ede339f2..5048e464d5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -431,11 +431,10 @@ static void ATPostAlterTypeCleanup(List **wqueue, 
AlteredTableInfo *tab,
                                           LOCKMODE lockmode);
 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
                                         char *cmd, List **wqueue, LOCKMODE 
lockmode,
-                                        bool rewrite);
-static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
-                                                Oid objid, Relation rel, List 
*domname,
-                                                const char *conname);
-static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
+                                        bool rewrite, bool old_inherited, bool 
*drop_old);
+static void RebuildComment(AlteredTableInfo *tab, int pass,
+                                                Oid objid, Oid classId, 
Relation rel, List *domname,
+                                                const char *conname, const 
char *idxname);
 static void TryReuseForeignKey(Oid oldId, Constraint *con);
 static void change_owner_fix_column_acls(Oid relationOid,
                                                         Oid oldOwnerId, Oid 
newOwnerId);
@@ -6993,10 +6992,13 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
        /* The IndexStmt has already been through transformIndexStmt */
        Assert(stmt->transformed);
 
+       /* stmt->oldNode is no longer used. */
+       Assert(!OidIsValid(stmt->oldNode));
+
        /* suppress schema rights check when rebuilding existing index */
        check_rights = !is_rebuild;
-       /* skip index build if phase 3 will do it or we're reusing an old one */
-       skip_build = tab->rewrite > 0 || OidIsValid(stmt->oldNode);
+       /* skip index build if phase 3 will do it */
+       skip_build = tab->rewrite > 0;
        /* suppress notices when rebuilding existing index */
        quiet = is_rebuild;
 
@@ -7011,20 +7013,6 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
                                                  skip_build,
                                                  quiet);
 
-       /*
-        * If TryReuseIndex() stashed a relfilenode for us, we used it for the 
new
-        * index instead of building from scratch.  The DROP of the old edition 
of
-        * this index will have scheduled the storage for deletion at commit, so
-        * cancel that pending deletion.
-        */
-       if (OidIsValid(stmt->oldNode))
-       {
-               Relation        irel = index_open(address.objectId, NoLock);
-
-               RelationPreserveStorage(irel->rd_node, true);
-               index_close(irel, NoLock);
-       }
-
        return address;
 }
 
@@ -10394,6 +10382,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo 
*tab, LOCKMODE lockmode)
                Oid                     confrelid;
                char            contype;
                bool            conislocal;
+               bool            drop_old;
 
                tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
                if (!HeapTupleIsValid(tup)) /* should not happen */
@@ -10413,18 +10402,6 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo 
*tab, LOCKMODE lockmode)
                conislocal = con->conislocal;
                ReleaseSysCache(tup);
 
-               ObjectAddressSet(obj, ConstraintRelationId, oldId);
-               add_exact_object_address(&obj, objects);
-
-               /*
-                * If the constraint is inherited (only), we don't want to 
inject a
-                * new definition here; it'll get recreated when 
ATAddCheckConstraint
-                * recurses from adding the parent table's constraint.  But we 
had to
-                * carry the info this far so that we can drop the constraint 
below.
-                */
-               if (!conislocal)
-                       continue;
-
                /*
                 * When rebuilding an FK constraint that references the table 
we're
                 * modifying, we might not yet have any lock on the FK's table, 
so get
@@ -10434,23 +10411,46 @@ ATPostAlterTypeCleanup(List **wqueue, 
AlteredTableInfo *tab, LOCKMODE lockmode)
                if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
                        LockRelationOid(relid, AccessExclusiveLock);
 
+               /*
+                * If the old constraint is inherited (!conislocal), it's not 
necessary
+                * to inject a new definition, because it will get recreated 
when
+                * ATAddCheckConstraint (or ATAddForeignKeyConstraint only in 
the case
+                * of partitioning) recurses from adding the parent's 
constraint.
+                */
                ATPostAlterTypeParse(oldId, relid, confrelid,
                                                         (char *) 
lfirst(def_item),
-                                                        wqueue, lockmode, 
tab->rewrite);
+                                                        wqueue, lockmode, 
tab->rewrite,
+                                                        !conislocal, 
&drop_old);
+
+               if (drop_old)
+               {
+                       ObjectAddressSet(obj, ConstraintRelationId, oldId);
+                       add_exact_object_address(&obj, objects);
+               }
        }
        forboth(oid_item, tab->changedIndexOids,
                        def_item, tab->changedIndexDefs)
        {
                Oid                     oldId = lfirst_oid(oid_item);
                Oid                     relid;
+               bool            drop_old;
 
                relid = IndexGetRelation(oldId, false);
+               /*
+                * If the old index is a partition, it's not necessary to 
inject a new
+                * definition, because it will get recreated when DefineIndex 
recurses
+                * from adding the parent's index.
+                */
                ATPostAlterTypeParse(oldId, relid, InvalidOid,
                                                         (char *) 
lfirst(def_item),
-                                                        wqueue, lockmode, 
tab->rewrite);
+                                                        wqueue, lockmode, 
tab->rewrite,
+                                                        
get_rel_relispartition(oldId), &drop_old);
 
-               ObjectAddressSet(obj, RelationRelationId, oldId);
-               add_exact_object_address(&obj, objects);
+               if (drop_old)
+               {
+                       ObjectAddressSet(obj, RelationRelationId, oldId);
+                       add_exact_object_address(&obj, objects);
+               }
        }
 
        /*
@@ -10469,7 +10469,8 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo 
*tab, LOCKMODE lockmode)
 
 static void
 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
-                                        List **wqueue, LOCKMODE lockmode, bool 
rewrite)
+                                        List **wqueue, LOCKMODE lockmode, bool 
rewrite,
+                                        bool old_inherited, bool *drop_old)
 {
        List       *raw_parsetree_list;
        List       *querytree_list;
@@ -10526,16 +10527,46 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid 
refRelId, char *cmd,
                        IndexStmt  *stmt = (IndexStmt *) stm;
                        AlterTableCmd *newcmd;
 
-                       if (!rewrite)
-                               TryReuseIndex(oldId, stmt);
-                       /* keep the index's comment */
-                       stmt->idxcomment = GetComment(oldId, 
RelationRelationId, 0);
+                       /*
+                        * If we won't rewrite the table and the old index is 
compatible
+                        * even after the ALTER TYPE, we can skip rebuilding 
the index.
+                        */
+                       if (!rewrite &&
+                               CheckIndexCompatible(oldId,
+                                                                        
stmt->accessMethod,
+                                                                        
stmt->indexParams,
+                                                                        
stmt->excludeOpNames))
+                       {
+                               *drop_old = false;
+                               continue;
+                       }
 
-                       newcmd = makeNode(AlterTableCmd);
-                       newcmd->subtype = AT_ReAddIndex;
-                       newcmd->def = (Node *) stmt;
-                       tab->subcmds[AT_PASS_OLD_INDEX] =
-                               lappend(tab->subcmds[AT_PASS_OLD_INDEX], 
newcmd);
+                       *drop_old = true;
+
+                       /*
+                        * If the old index was inherited to begin with,
+                        * DefineIndex() will recreate it when rebuilding the
+                        * parent index.  Although to keep the old comment on
+                        * the child index, emit a command to re-add it.
+                        */
+                       if (!old_inherited)
+                       {
+                               newcmd = makeNode(AlterTableCmd);
+                               newcmd->subtype = AT_ReAddIndex;
+                               newcmd->def = (Node *) stmt;
+                               tab->subcmds[AT_PASS_OLD_INDEX] =
+                                       
lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
+                       }
+
+                       /* recreate any comment on the index */
+                       RebuildComment(tab,
+                                                  AT_PASS_OLD_INDEX,
+                                                  oldId,
+                                                  RelationRelationId,
+                                                  rel,
+                                                  NIL,
+                                                  NULL,
+                                                  stmt->idxname);
                }
                else if (IsA(stm, AlterTableStmt))
                {
@@ -10554,44 +10585,94 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid 
refRelId, char *cmd,
                                        indstmt = castNode(IndexStmt, cmd->def);
                                        indoid = get_constraint_index(oldId);
 
-                                       if (!rewrite)
-                                               TryReuseIndex(indoid, indstmt);
-                                       /* keep any comment on the index */
-                                       indstmt->idxcomment = GetComment(indoid,
-                                                                               
                         RelationRelationId, 0);
+                                       /*
+                                        * If we won't rewrite the table and 
the old index is
+                                        * compatible even after the ALTER 
TYPE, we can skip
+                                        * rebuilding the index.
+                                        */
+                                       if (!rewrite &&
+                                               CheckIndexCompatible(indoid,
+                                                                               
         indstmt->accessMethod,
+                                                                               
         indstmt->indexParams,
+                                                                               
         indstmt->excludeOpNames))
+                                       {
+                                               *drop_old = false;
+                                               continue;
+                                       }
 
-                                       cmd->subtype = AT_ReAddIndex;
-                                       tab->subcmds[AT_PASS_OLD_INDEX] =
-                                               
lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
+                                       *drop_old = true;
 
-                                       /* recreate any comment on the 
constraint */
-                                       RebuildConstraintComment(tab,
-                                                                               
         AT_PASS_OLD_INDEX,
-                                                                               
         oldId,
-                                                                               
         rel,
-                                                                               
         NIL,
-                                                                               
         indstmt->idxname);
+                                       /*
+                                        * If the old index was inherited to 
begin with,
+                                        * DefineIndex() will recreate it when 
rebuilding the
+                                        * parent index.  Although to keep the 
old comment on
+                                        * the child index, emit a command to 
re-add it.
+                                        */
+                                       if (!old_inherited)
+                                       {
+                                               cmd->subtype = AT_ReAddIndex;
+                                               tab->subcmds[AT_PASS_OLD_INDEX] 
=
+                                                       
lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
+                                       }
+
+                                       /* recreate any comment on the index 
and the constraint */
+                                       RebuildComment(tab,
+                                                                  
AT_PASS_OLD_INDEX,
+                                                                  indoid,
+                                                                  
RelationRelationId,
+                                                                  rel,
+                                                                  NIL,
+                                                                  NULL,
+                                                                  
indstmt->idxname);
+                                       RebuildComment(tab,
+                                                                  
AT_PASS_OLD_INDEX,
+                                                                  oldId,
+                                                                  
ConstraintRelationId,
+                                                                  rel,
+                                                                  NIL,
+                                                                  
indstmt->idxname,
+                                                                  NULL);
                                }
                                else if (cmd->subtype == AT_AddConstraint)
                                {
                                        Constraint *con = castNode(Constraint, 
cmd->def);
 
-                                       con->old_pktable_oid = refRelId;
-                                       /* rewriting neither side of a FK */
-                                       if (con->contype == CONSTR_FOREIGN &&
-                                               !rewrite && tab->rewrite == 0)
-                                               TryReuseForeignKey(oldId, con);
-                                       cmd->subtype = AT_ReAddConstraint;
-                                       tab->subcmds[AT_PASS_OLD_CONSTR] =
-                                               
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
+                                       if (!rewrite)
+                                       {
+                                               *drop_old = false;
+                                               continue;
+                                       }
+
+                                       *drop_old = true;
+
+                                       /*
+                                        * If the old index was inherited to 
begin with,
+                                        * ATExecAddConstraint will recreate it 
when rebuilding
+                                        * the parent constraint.  Although to 
keep any old
+                                        * comment on the child constraint, 
emit a command to
+                                        * re-add it.
+                                        */
+                                       if (!old_inherited)
+                                       {
+                                               con->old_pktable_oid = refRelId;
+                                               /* rewriting neither side of a 
FK */
+                                               if (con->contype == 
CONSTR_FOREIGN &&
+                                                       !rewrite && 
tab->rewrite == 0)
+                                                       
TryReuseForeignKey(oldId, con);
+                                               cmd->subtype = 
AT_ReAddConstraint;
+                                               
tab->subcmds[AT_PASS_OLD_CONSTR] =
+                                                       
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
+                                       }
 
                                        /* recreate any comment on the 
constraint */
-                                       RebuildConstraintComment(tab,
-                                                                               
         AT_PASS_OLD_CONSTR,
-                                                                               
         oldId,
-                                                                               
         rel,
-                                                                               
         NIL,
-                                                                               
         con->conname);
+                                       RebuildComment(tab,
+                                                                  
AT_PASS_OLD_CONSTR,
+                                                                  oldId,
+                                                                  
ConstraintRelationId,
+                                                                  rel,
+                                                                  NIL,
+                                                                  con->conname,
+                                                                  NULL);
                                }
                                else
                                        elog(ERROR, "unexpected statement 
subtype: %d",
@@ -10607,18 +10688,22 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid 
refRelId, char *cmd,
                                Constraint *con = castNode(Constraint, 
stmt->def);
                                AlterTableCmd *cmd = makeNode(AlterTableCmd);
 
+                               *drop_old = true;
+
                                cmd->subtype = AT_ReAddDomainConstraint;
                                cmd->def = (Node *) stmt;
                                tab->subcmds[AT_PASS_OLD_CONSTR] =
                                        
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
 
                                /* recreate any comment on the constraint */
-                               RebuildConstraintComment(tab,
-                                                                               
 AT_PASS_OLD_CONSTR,
-                                                                               
 oldId,
-                                                                               
 NULL,
-                                                                               
 stmt->typeName,
-                                                                               
 con->conname);
+                               RebuildComment(tab,
+                                                          AT_PASS_OLD_CONSTR,
+                                                          oldId,
+                                                          ConstraintRelationId,
+                                                          NULL,
+                                                          stmt->typeName,
+                                                          con->conname,
+                                                          NULL);
                        }
                        else
                                elog(ERROR, "unexpected statement subtype: %d",
@@ -10634,25 +10719,27 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid 
refRelId, char *cmd,
 
 /*
  * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
- * for a table or domain constraint that is being rebuilt.
+ * for an index or a table or domain constraint that is being rebuilt.
  *
  * objid is the OID of the constraint.
- * Pass "rel" for a table constraint, or "domname" (domain's qualified name
- * as a string list) for a domain constraint.
+ * Pass "rel" for adding a comment on an index or a table constraint, or
+ * "domname" (domain's qualified name as a string list) for a domain
+ * constraint.
+ *
  * (We could dig that info, as well as the conname, out of the pg_constraint
  * entry; but callers already have them so might as well pass them.)
  */
 static void
-RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
-                                                Relation rel, List *domname,
-                                                const char *conname)
+RebuildComment(AlteredTableInfo *tab, int pass, Oid objid, Oid classId,
+                          Relation rel, List *domname,
+                          const char *conname, const char *idxname)
 {
        CommentStmt *cmd;
        char       *comment_str;
        AlterTableCmd *newcmd;
 
        /* Look for comment for object wanted, and leave if none */
-       comment_str = GetComment(objid, ConstraintRelationId, 0);
+       comment_str = GetComment(objid, classId, 0);
        if (comment_str == NULL)
                return;
 
@@ -10660,11 +10747,22 @@ RebuildConstraintComment(AlteredTableInfo *tab, int 
pass, Oid objid,
        cmd = makeNode(CommentStmt);
        if (rel)
        {
-               cmd->objtype = OBJECT_TABCONSTRAINT;
-               cmd->object = (Node *)
-                       
list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
-                                          
makeString(pstrdup(RelationGetRelationName(rel))),
-                                          makeString(pstrdup(conname)));
+               if (conname)
+               {
+                       cmd->objtype = OBJECT_TABCONSTRAINT;
+                       cmd->object = (Node *)
+                               
list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
+                                                  
makeString(pstrdup(RelationGetRelationName(rel))),
+                                                  
makeString(pstrdup(conname)));
+               }
+               /* else an index */
+               else
+               {
+                       cmd->objtype = OBJECT_INDEX;
+                       cmd->object = (Node *)
+                               
list_make2(makeString(get_namespace_name(RelationGetNamespace(rel))),
+                                                  
makeString(pstrdup(idxname)));
+               }
        }
        else
        {
@@ -10683,25 +10781,6 @@ RebuildConstraintComment(AlteredTableInfo *tab, int 
pass, Oid objid,
 }
 
 /*
- * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
- * for the real analysis, then mutates the IndexStmt based on that verdict.
- */
-static void
-TryReuseIndex(Oid oldId, IndexStmt *stmt)
-{
-       if (CheckIndexCompatible(oldId,
-                                                        stmt->accessMethod,
-                                                        stmt->indexParams,
-                                                        stmt->excludeOpNames))
-       {
-               Relation        irel = index_open(oldId, NoLock);
-
-               stmt->oldNode = irel->rd_node.relNode;
-               index_close(irel, NoLock);
-       }
-}
-
-/*
  * Subroutine for ATPostAlterTypeParse().
  *
  * Stash the old P-F equality operator into the Constraint node, for possible
@@ -15527,7 +15606,7 @@ ATExecAttachPartitionIdx(List **wqueue, Relation 
parentIdx, RangeVar *name)
                                                          partIdx->rd_opfamily,
                                                          
parentIdx->rd_opfamily,
                                                          attmap,
-                                                         
RelationGetDescr(parentTbl)->natts))
+                                                         
RelationGetDescr(partTbl)->natts))
                        ereport(ERROR,
                                        
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                         errmsg("cannot attach index \"%s\" as 
a partition of index \"%s\"",
diff --git a/src/test/regress/expected/alter_table.out 
b/src/test/regress/expected/alter_table.out
index b9aa4f189b..e97c68f981 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -4021,3 +4021,56 @@ alter table at_test_sql_partop attach partition 
at_test_sql_partop_1 for values
 drop table at_test_sql_partop;
 drop operator class at_test_sql_partop using btree;
 drop function at_test_sql_partop;
+-- test that ALTER TYPE correctly handles partitioned index rebuild correctly
+create table alttype_cleanup_idx (a int, b varchar(64), primary key (a, b)) 
partition by list (a);
+create table alttype_cleanup_idx1 partition of alttype_cleanup_idx for values 
in (1);
+create table alttype_cleanup_idx2 partition of alttype_cleanup_idx for values 
in (2);
+insert into alttype_cleanup_idx values (1, 'xxx');
+-- tables rewriten, indexes rebuilt
+alter table alttype_cleanup_idx alter b type varchar(128);
+-- primary key still works
+insert into alttype_cleanup_idx values (1, 'xxx');
+ERROR:  duplicate key value violates unique constraint 
"alttype_cleanup_idx1_pkey"
+DETAIL:  Key (a, b)=(1, xxx) already exists.
+-- no rewrite, no indexes rebuilt
+alter table alttype_cleanup_idx alter b type varchar(10);
+-- primary key still works
+insert into alttype_cleanup_idx values (1, 'xxx');
+ERROR:  duplicate key value violates unique constraint 
"alttype_cleanup_idx1_pkey"
+DETAIL:  Key (a, b)=(1, xxx) already exists.
+-- test that comments on child constraints are preserved through constraint
+-- reconstruction
+alter table alttype_cleanup_idx add c varchar(64);
+alter table alttype_cleanup_idx add constraint c_chk check (c <> '');
+comment on constraint c_chk on alttype_cleanup_idx is 'parent check';
+comment on constraint c_chk on alttype_cleanup_idx1 is 'child check 1';
+comment on constraint c_chk on alttype_cleanup_idx2 is 'child check 2';
+select conname, obj_description(oid, 'pg_constraint') from pg_constraint where 
conname = 'c_chk' order by 2;
+ conname | obj_description 
+---------+-----------------
+ c_chk   | child check 1
+ c_chk   | child check 2
+ c_chk   | parent check
+(3 rows)
+
+-- tables rewritten, constraints rebuilt
+alter table alttype_cleanup_idx alter c type varchar(128);
+select conname, obj_description(oid, 'pg_constraint') from pg_constraint where 
conname = 'c_chk' order by 2;
+ conname | obj_description 
+---------+-----------------
+ c_chk   | child check 1
+ c_chk   | child check 2
+ c_chk   | parent check
+(3 rows)
+
+-- no tables rewrite, constraints still rebuilt
+alter table alttype_cleanup_idx alter c type varchar(128);
+select conname, obj_description(oid, 'pg_constraint') from pg_constraint where 
conname = 'c_chk' order by 2;
+ conname | obj_description 
+---------+-----------------
+ c_chk   | child check 1
+ c_chk   | child check 2
+ c_chk   | parent check
+(3 rows)
+
+drop table alttype_cleanup_idx;
diff --git a/src/test/regress/sql/alter_table.sql 
b/src/test/regress/sql/alter_table.sql
index d675579977..1e67c789b8 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2668,3 +2668,34 @@ alter table at_test_sql_partop attach partition 
at_test_sql_partop_1 for values
 drop table at_test_sql_partop;
 drop operator class at_test_sql_partop using btree;
 drop function at_test_sql_partop;
+
+-- test that ALTER TYPE correctly handles partitioned index rebuild correctly
+create table alttype_cleanup_idx (a int, b varchar(64), primary key (a, b)) 
partition by list (a);
+create table alttype_cleanup_idx1 partition of alttype_cleanup_idx for values 
in (1);
+create table alttype_cleanup_idx2 partition of alttype_cleanup_idx for values 
in (2);
+insert into alttype_cleanup_idx values (1, 'xxx');
+-- tables rewriten, indexes rebuilt
+alter table alttype_cleanup_idx alter b type varchar(128);
+-- primary key still works
+insert into alttype_cleanup_idx values (1, 'xxx');
+-- no rewrite, no indexes rebuilt
+alter table alttype_cleanup_idx alter b type varchar(10);
+-- primary key still works
+insert into alttype_cleanup_idx values (1, 'xxx');
+
+-- test that comments on child constraints are preserved through constraint
+-- reconstruction
+alter table alttype_cleanup_idx add c varchar(64);
+alter table alttype_cleanup_idx add constraint c_chk check (c <> '');
+comment on constraint c_chk on alttype_cleanup_idx is 'parent check';
+comment on constraint c_chk on alttype_cleanup_idx1 is 'child check 1';
+comment on constraint c_chk on alttype_cleanup_idx2 is 'child check 2';
+select conname, obj_description(oid, 'pg_constraint') from pg_constraint where 
conname = 'c_chk' order by 2;
+-- tables rewritten, constraints rebuilt
+alter table alttype_cleanup_idx alter c type varchar(128);
+select conname, obj_description(oid, 'pg_constraint') from pg_constraint where 
conname = 'c_chk' order by 2;
+-- no tables rewrite, constraints still rebuilt
+alter table alttype_cleanup_idx alter c type varchar(128);
+select conname, obj_description(oid, 'pg_constraint') from pg_constraint where 
conname = 'c_chk' order by 2;
+
+drop table alttype_cleanup_idx;

Reply via email to