ALTER-TYPE-preserve-old-index-comment-1.patch

text/plain

Filename: ALTER-TYPE-preserve-old-index-comment-1.patch
Type: text/plain
Part: 0
Message: Re: BUG #15672: PostgreSQL 11.1/11.2 crashed after dropping a partition table

Patch

Format: unified
File+
src/backend/commands/indexcmds.c 3 5
src/backend/commands/tablecmds.c 193 114
src/test/regress/expected/alter_table.out 53 0
src/test/regress/sql/alter_table.sql 31 0
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..3237f15125 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 'alttype_cleanup_idx check constraint';
+comment on constraint c_chk on alttype_cleanup_idx1 is 'alttype_cleanup_idx1 check constraint';
+comment on constraint c_chk on alttype_cleanup_idx2 is 'alttype_cleanup_idx2 check constraint';
+select conname, obj_description(oid, 'pg_constraint') from pg_constraint where conname = 'c_chk' order by 1, 2;
+ conname |            obj_description            
+---------+---------------------------------------
+ c_chk   | alttype_cleanup_idx1 check constraint
+ c_chk   | alttype_cleanup_idx2 check constraint
+ c_chk   | alttype_cleanup_idx check constraint
+(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 1, 2;
+ conname |            obj_description            
+---------+---------------------------------------
+ c_chk   | alttype_cleanup_idx1 check constraint
+ c_chk   | alttype_cleanup_idx2 check constraint
+ c_chk   | alttype_cleanup_idx check constraint
+(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 1, 2;
+ conname |            obj_description            
+---------+---------------------------------------
+ c_chk   | alttype_cleanup_idx1 check constraint
+ c_chk   | alttype_cleanup_idx2 check constraint
+ c_chk   | alttype_cleanup_idx check constraint
+(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..9c1aafc94e 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 'alttype_cleanup_idx check constraint';
+comment on constraint c_chk on alttype_cleanup_idx1 is 'alttype_cleanup_idx1 check constraint';
+comment on constraint c_chk on alttype_cleanup_idx2 is 'alttype_cleanup_idx2 check constraint';
+select conname, obj_description(oid, 'pg_constraint') from pg_constraint where conname = 'c_chk' order by 1, 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 1, 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 1, 2;
+
+drop table alttype_cleanup_idx;