From c516a0c36e246a0c746be73cc75cda441a5f748d Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Wed, 6 Oct 2021 22:11:42 +0000
Subject: [PATCH v1 1/1] Add object type validation in RenameRelation().

RenameRelation() is used for renaming several object types, but
there is presently no verification that the object type matches the
statement type.  For example, ALTER TABLE ... RENAME can be used to
rename indexes.  Since the renaming logic can differ between object
types, it is important to ensure that the object type and statement
type match.
---
 src/backend/commands/cluster.c            |  4 ++--
 src/backend/commands/tablecmds.c          | 16 ++++++++++++----
 src/backend/commands/typecmds.c           |  2 +-
 src/include/commands/tablecmds.h          |  2 +-
 src/test/regress/expected/alter_table.out |  6 +++++-
 src/test/regress/expected/sequence.out    |  4 ++--
 src/test/regress/sql/alter_table.sql      |  5 ++++-
 src/test/regress/sql/sequence.sql         |  4 ++--
 8 files changed, 29 insertions(+), 14 deletions(-)

diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 9d22f648a8..7ee00691ab 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1518,14 +1518,14 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
 			snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
 					 OIDOldHeap);
 			RenameRelationInternal(newrel->rd_rel->reltoastrelid,
-								   NewToastName, true, false);
+								   NewToastName, true, OBJECT_TABLE);
 
 			/* ... and its valid index too. */
 			snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
 					 OIDOldHeap);
 
 			RenameRelationInternal(toastidx,
-								   NewToastName, true, true);
+								   NewToastName, true, OBJECT_INDEX);
 
 			/*
 			 * Reset the relrewrite for the toast. The command-counter
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ff97b618e6..f8f5b6f85d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3660,7 +3660,7 @@ rename_constraint_internal(Oid myrelid,
 			|| con->contype == CONSTRAINT_UNIQUE
 			|| con->contype == CONSTRAINT_EXCLUSION))
 		/* rename the index; this renames the constraint as well */
-		RenameRelationInternal(con->conindid, newconname, false, true);
+		RenameRelationInternal(con->conindid, newconname, false, OBJECT_INDEX);
 	else
 		RenameConstraintById(constraintOid, newconname);
 
@@ -3762,7 +3762,7 @@ RenameRelation(RenameStmt *stmt)
 	}
 
 	/* Do the work */
-	RenameRelationInternal(relid, stmt->newname, false, is_index);
+	RenameRelationInternal(relid, stmt->newname, false, stmt->renameType);
 
 	ObjectAddressSet(address, RelationRelationId, relid);
 
@@ -3773,13 +3773,14 @@ RenameRelation(RenameStmt *stmt)
  *		RenameRelationInternal - change the name of a relation
  */
 void
-RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
+RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, ObjectType renameType)
 {
 	Relation	targetrelation;
 	Relation	relrelation;	/* for RELATION relation */
 	HeapTuple	reltup;
 	Form_pg_class relform;
 	Oid			namespaceId;
+	bool		is_index = renameType == OBJECT_INDEX;
 
 	/*
 	 * Grab a lock on the target relation, which we will NOT release until end
@@ -3804,6 +3805,13 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bo
 		elog(ERROR, "cache lookup failed for relation %u", myrelid);
 	relform = (Form_pg_class) GETSTRUCT(reltup);
 
+	if (get_relkind_objtype(relform->relkind) != renameType)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot rename \"%s\"",
+						NameStr(relform->relname)),
+				 errdetail_relkind_not_supported(relform->relkind)));
+
 	if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
 		ereport(ERROR,
 				(errcode(ERRCODE_DUPLICATE_TABLE),
@@ -8620,7 +8628,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
 		ereport(NOTICE,
 				(errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
 						indexName, constraintName)));
-		RenameRelationInternal(index_oid, constraintName, false, true);
+		RenameRelationInternal(index_oid, constraintName, false, OBJECT_INDEX);
 	}
 
 	/* Extra checks needed if making primary key */
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index b290629a45..4a971a103d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3659,7 +3659,7 @@ RenameType(RenameStmt *stmt)
 	 * RenameRelationInternal will call RenameTypeInternal automatically.
 	 */
 	if (typTup->typtype == TYPTYPE_COMPOSITE)
-		RenameRelationInternal(typTup->typrelid, newTypeName, false, false);
+		RenameRelationInternal(typTup->typrelid, newTypeName, false, stmt->renameType);
 	else
 		RenameTypeInternal(typeOid, newTypeName,
 						   typTup->typnamespace);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 336549cc5f..48d3a782af 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -76,7 +76,7 @@ extern ObjectAddress RenameRelation(RenameStmt *stmt);
 
 extern void RenameRelationInternal(Oid myrelid,
 								   const char *newrelname, bool is_internal,
-								   bool is_index);
+								   ObjectType renameType);
 
 extern void ResetRelRewrite(Oid myrelid);
 
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 4bee0c1173..fb1909de3c 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -232,9 +232,13 @@ SET ROLE regress_alter_table_user1;
 ALTER INDEX onek_unique1 RENAME TO fail;  -- permission denied
 ERROR:  must be owner of index onek_unique1
 RESET ROLE;
+-- ALTER TABLE ... RENAME on index fails
+ALTER TABLE onek_unique1 RENAME TO fail;
+ERROR:  cannot rename "onek_unique1"
+DETAIL:  This operation is not supported for indexes.
 -- renaming views
 CREATE VIEW attmp_view (unique1) AS SELECT unique1 FROM tenk1;
-ALTER TABLE attmp_view RENAME TO attmp_view_new;
+ALTER VIEW attmp_view RENAME TO attmp_view_new;
 SET ROLE regress_alter_table_user1;
 ALTER VIEW attmp_view_new RENAME TO fail;  -- permission denied
 ERROR:  must be owner of view attmp_view_new
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 71c2b0f1df..d0a5dd8d76 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -241,7 +241,7 @@ ERROR:  currval of sequence "sequence_test" is not yet defined in this session
 DROP SEQUENCE sequence_test;
 -- renaming sequences
 CREATE SEQUENCE foo_seq;
-ALTER TABLE foo_seq RENAME TO foo_seq_new;
+ALTER SEQUENCE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
  last_value | log_cnt | is_called 
 ------------+---------+-----------
@@ -270,7 +270,7 @@ SELECT last_value, log_cnt IN (31, 32) AS log_cnt_ok, is_called FROM foo_seq_new
 
 DROP SEQUENCE foo_seq_new;
 -- renaming serial sequences
-ALTER TABLE serialtest1_f2_seq RENAME TO serialtest1_f2_foo;
+ALTER SEQUENCE serialtest1_f2_seq RENAME TO serialtest1_f2_foo;
 INSERT INTO serialTest1 VALUES ('more');
 SELECT * FROM serialTest1;
   f1   | f2  
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index dc0200adcb..8106c8617b 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -227,9 +227,12 @@ SET ROLE regress_alter_table_user1;
 ALTER INDEX onek_unique1 RENAME TO fail;  -- permission denied
 RESET ROLE;
 
+-- ALTER TABLE ... RENAME on index fails
+ALTER TABLE onek_unique1 RENAME TO fail;
+
 -- renaming views
 CREATE VIEW attmp_view (unique1) AS SELECT unique1 FROM tenk1;
-ALTER TABLE attmp_view RENAME TO attmp_view_new;
+ALTER VIEW attmp_view RENAME TO attmp_view_new;
 
 SET ROLE regress_alter_table_user1;
 ALTER VIEW attmp_view_new RENAME TO fail;  -- permission denied
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 7928ee23ee..3d3a90b544 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -132,7 +132,7 @@ DROP SEQUENCE sequence_test;
 
 -- renaming sequences
 CREATE SEQUENCE foo_seq;
-ALTER TABLE foo_seq RENAME TO foo_seq_new;
+ALTER SEQUENCE foo_seq RENAME TO foo_seq_new;
 SELECT * FROM foo_seq_new;
 SELECT nextval('foo_seq_new');
 SELECT nextval('foo_seq_new');
@@ -142,7 +142,7 @@ SELECT last_value, log_cnt IN (31, 32) AS log_cnt_ok, is_called FROM foo_seq_new
 DROP SEQUENCE foo_seq_new;
 
 -- renaming serial sequences
-ALTER TABLE serialtest1_f2_seq RENAME TO serialtest1_f2_foo;
+ALTER SEQUENCE serialtest1_f2_seq RENAME TO serialtest1_f2_foo;
 INSERT INTO serialTest1 VALUES ('more');
 SELECT * FROM serialTest1;
 
-- 
2.16.6

