>From 4d3b0efb5a9aa7c41abe0c4c879f415474ebc10f Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 25 Apr 2014 16:43:53 -0300
Subject: [PATCH 10/30] deparse: Support for ALTER <OBJECT> RENAME

It supports everything but functions, aggregates, operator classes and
operator families.
---
 src/backend/commands/alter.c       |   7 +-
 src/backend/commands/tablecmds.c   |  26 ++--
 src/backend/tcop/deparse_utility.c | 281 ++++++++++++++++++++++++++++++++++++-
 src/backend/tcop/utility.c         |  11 +-
 src/include/commands/alter.h       |   2 +-
 src/include/commands/tablecmds.h   |   2 +-
 6 files changed, 313 insertions(+), 16 deletions(-)

diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index c9a9baf..298edad 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -299,9 +299,12 @@ AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
 /*
  * Executes an ALTER OBJECT / RENAME TO statement.  Based on the object
  * type, the function appropriate to that type is executed.
+ *
+ * Return value is the OID of the renamed object.  The objectSubId, if any,
+ * is returned in objsubid.
  */
 Oid
-ExecRenameStmt(RenameStmt *stmt)
+ExecRenameStmt(RenameStmt *stmt, int *objsubid)
 {
 	switch (stmt->renameType)
 	{
@@ -330,7 +333,7 @@ ExecRenameStmt(RenameStmt *stmt)
 
 		case OBJECT_COLUMN:
 		case OBJECT_ATTRIBUTE:
-			return renameatt(stmt);
+			return renameatt(stmt, objsubid);
 
 		case OBJECT_RULE:
 			return RenameRewriteRule(stmt->relation, stmt->subname,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9587217..b49bd30 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2158,8 +2158,10 @@ renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
 
 /*
  *		renameatt_internal		- workhorse for renameatt
+ *
+ * Return value is the column number of the attribute in the 'myrelid' relation.
  */
-static void
+static int
 renameatt_internal(Oid myrelid,
 				   const char *oldattname,
 				   const char *newattname,
@@ -2300,6 +2302,8 @@ renameatt_internal(Oid myrelid,
 	heap_close(attrelation, RowExclusiveLock);
 
 	relation_close(targetrelation, NoLock);		/* close rel but keep lock */
+
+	return attnum;
 }
 
 /*
@@ -2324,9 +2328,10 @@ RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
  *		renameatt		- changes the name of a attribute in a relation
  */
 Oid
-renameatt(RenameStmt *stmt)
+renameatt(RenameStmt *stmt, int32 *objsubid)
 {
 	Oid			relid;
+	int			attnum;
 
 	/* lock level taken here should match renameatt_internal */
 	relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
@@ -2342,13 +2347,16 @@ renameatt(RenameStmt *stmt)
 		return InvalidOid;
 	}
 
-	renameatt_internal(relid,
-					   stmt->subname,	/* old att name */
-					   stmt->newname,	/* new att name */
-					   interpretInhOption(stmt->relation->inhOpt),		/* recursive? */
-					   false,	/* recursing? */
-					   0,		/* expected inhcount */
-					   stmt->behavior);
+	attnum =
+		renameatt_internal(relid,
+						   stmt->subname,	/* old att name */
+						   stmt->newname,	/* new att name */
+						   interpretInhOption(stmt->relation->inhOpt), /* recursive? */
+						   false,	/* recursing? */
+						   0,		/* expected inhcount */
+						   stmt->behavior);
+	if (objsubid)
+		*objsubid = attnum;
 
 	/* This is an ALTER TABLE command so it's about the relid */
 	return relid;
diff --git a/src/backend/tcop/deparse_utility.c b/src/backend/tcop/deparse_utility.c
index 6df0ec1..f568df3 100644
--- a/src/backend/tcop/deparse_utility.c
+++ b/src/backend/tcop/deparse_utility.c
@@ -1612,6 +1612,285 @@ deparse_CreateRangeStmt(Oid objectId, Node *parsetree)
 	return command;
 }
 
+/*
+ * Return the given object type as a string.
+ */
+static const char *
+stringify_objtype(ObjectType objtype)
+{
+	switch (objtype)
+	{
+		case OBJECT_AGGREGATE:
+			return "AGGREGATE";
+		case OBJECT_COLUMN:
+			return "COLUMN";
+		case OBJECT_DOMAIN:
+			return "DOMAIN";
+		case OBJECT_COLLATION:
+			return "COLLATION";
+		case OBJECT_CONVERSION:
+			return "CONVERSION";
+		case OBJECT_EXTENSION:
+			return "EXTENSION";
+		case OBJECT_FDW:
+			return "FOREIGN DATA WRAPPER";
+		case OBJECT_FOREIGN_SERVER:
+			return "SERVER";
+		case OBJECT_FOREIGN_TABLE:
+			return "FOREIGN TABLE";
+		case OBJECT_FUNCTION:
+			return "FUNCTION";
+		case OBJECT_INDEX:
+			return "INDEX";
+		case OBJECT_LANGUAGE:
+			return "LANGUAGE";
+		case OBJECT_LARGEOBJECT:
+			return "LARGE OBJECT";
+		case OBJECT_MATVIEW:
+			return "MATERIALIZED VIEW";
+		case OBJECT_OPERATOR:
+			return "OPERATOR";
+		case OBJECT_OPCLASS:
+			return "OPERATOR CLASS";
+		case OBJECT_OPFAMILY:
+			return "OPERATOR FAMILY";
+		case OBJECT_SCHEMA:
+			return "SCHEMA";
+		case OBJECT_SEQUENCE:
+			return "SEQUENCE";
+		case OBJECT_TABLE:
+			return "TABLE";
+		case OBJECT_TSCONFIGURATION:
+			return "TEXT SEARCH CONFIGURATION";
+		case OBJECT_TSDICTIONARY:
+			return "TEXT SEARCH DICTIONARY";
+		case OBJECT_TSPARSER:
+			return "TEXT SEARCH PARSER";
+		case OBJECT_TSTEMPLATE:
+			return "TEXT SEARCH TEMPLATE";
+		case OBJECT_TYPE:
+			return "TYPE";
+		case OBJECT_VIEW:
+			return "VIEW";
+
+		default:
+			elog(ERROR, "unsupported objtype %d", objtype);
+	}
+}
+
+static char *
+deparse_RenameStmt(Oid objectId, Node *parsetree)
+{
+	RenameStmt *node = (RenameStmt *) parsetree;
+	ObjTree	   *renameStmt;
+	char	   *command;
+	char	   *fmtstr;
+	Relation	relation;
+	Oid			schemaId;
+	const char *subthing;
+
+	/*
+	 * FIXME --- this code is missing support for inheritance behavioral flags,
+	 * i.e. the "*" and ONLY elements.
+	 */
+
+	/*
+	 * In a ALTER .. RENAME command, we don't have the original name of the
+	 * object in system catalogs: since we inspect them after the command has
+	 * executed, the old name is already gone.  Therefore, we extract it from
+	 * the parse node.  Note we still extract the schema name from the catalog
+	 * (it might not be present in the parse node); it cannot possibly have
+	 * changed anyway.
+	 *
+	 * XXX what if there's another event trigger running concurrently that
+	 * renames the schema or moves the object to another schema?  Seems
+	 * pretty far-fetched, but possible nonetheless.
+	 */
+	switch (node->renameType)
+	{
+		case OBJECT_TABLE:
+		case OBJECT_SEQUENCE:
+		case OBJECT_VIEW:
+		case OBJECT_MATVIEW:
+		case OBJECT_INDEX:
+		case OBJECT_FOREIGN_TABLE:
+			fmtstr = psprintf("ALTER %s %%{if_exists}s %%{identity}D RENAME TO %%{newname}I",
+							  stringify_objtype(node->renameType));
+			relation = relation_open(objectId, AccessShareLock);
+			schemaId = RelationGetNamespace(relation);
+			renameStmt = new_objtree_VA(fmtstr, 0);
+			append_object_object(renameStmt, "identity",
+								 new_objtree_for_qualname(schemaId,
+														  node->relation->relname));
+			append_string_object(renameStmt, "if_exists",
+								 node->missing_ok ? "IF EXISTS" : "");
+			relation_close(relation, AccessShareLock);
+			break;
+
+		case OBJECT_COLUMN:
+		case OBJECT_ATTRIBUTE:
+			relation = relation_open(objectId, AccessShareLock);
+			schemaId = RelationGetNamespace(relation);
+
+			if (node->renameType == OBJECT_COLUMN)
+				subthing = "COLUMN";
+			else
+				subthing = "ATTRIBUTE";
+
+			fmtstr = psprintf("ALTER %s %%{if_exists}s %%{identity}D RENAME %s %%{colname}I TO %%{newname}I",
+							  stringify_objtype(node->relationType),
+							  subthing);
+			renameStmt = new_objtree_VA(fmtstr, 0);
+			append_object_object(renameStmt, "identity",
+								 new_objtree_for_qualname(schemaId,
+														  node->relation->relname));
+			append_string_object(renameStmt, "colname", node->subname);
+			append_string_object(renameStmt, "if_exists",
+								 node->missing_ok ? "IF EXISTS" : "");
+			relation_close(relation, AccessShareLock);
+			break;
+
+		case OBJECT_SCHEMA:
+		case OBJECT_FDW:
+		case OBJECT_LANGUAGE:
+		case OBJECT_FOREIGN_SERVER:
+			fmtstr = psprintf("ALTER %s %%{identity}I RENAME TO %%{newname}I",
+							  stringify_objtype(node->renameType));
+			renameStmt = new_objtree_VA(fmtstr, 0);
+			append_string_object(renameStmt, "identity",
+								 node->subname);
+			break;
+
+		case OBJECT_COLLATION:
+		case OBJECT_CONVERSION:
+		case OBJECT_DOMAIN:
+		case OBJECT_TSDICTIONARY:
+		case OBJECT_TSPARSER:
+		case OBJECT_TSTEMPLATE:
+		case OBJECT_TSCONFIGURATION:
+		case OBJECT_TYPE:
+			{
+				ObjTree    *ident;
+				HeapTuple	objTup;
+				Oid			catalogId;
+				Relation	catalog;
+				bool		isnull;
+				AttrNumber	nspnum;
+
+				catalogId = get_objtype_catalog_oid(node->renameType);
+				catalog = heap_open(catalogId, AccessShareLock);
+				objTup = get_catalog_object_by_oid(catalog, objectId);
+				nspnum = get_object_attnum_namespace(catalogId);
+
+				schemaId = DatumGetObjectId(heap_getattr(objTup,
+														 nspnum,
+														 RelationGetDescr(catalog),
+														 &isnull));
+
+				fmtstr = psprintf("ALTER %s %%{identity}D RENAME TO %%{newname}I",
+								  stringify_objtype(node->renameType));
+				renameStmt = new_objtree_VA(fmtstr, 0);
+				ident = new_objtree_for_qualname(schemaId,
+												 strVal(llast(node->object)));
+				append_object_object(renameStmt, "identity", ident);
+				relation_close(catalog, AccessShareLock);
+
+			}
+			break;
+
+		case OBJECT_AGGREGATE:
+		case OBJECT_FUNCTION:
+			elog(ERROR, "renaming of functions and aggregates is not supported yet");
+
+		case OBJECT_CONSTRAINT:
+			{
+				HeapTuple		conTup;
+				Form_pg_constraint	constrForm;
+				ObjTree		   *ident;
+
+				conTup = SearchSysCache1(CONSTROID, objectId);
+				constrForm = (Form_pg_constraint) GETSTRUCT(conTup);
+				fmtstr = psprintf("ALTER %s %%{identity}D RENAME CONSTRAINT %%{conname}I TO %%{newname}I",
+								  stringify_objtype(node->relationType));
+				renameStmt = new_objtree_VA(fmtstr, 0);
+
+				switch (node->relationType)
+				{
+					case OBJECT_DOMAIN:
+						ident = new_objtree_for_qualname_id(TypeRelationId,
+															constrForm->contypid);
+						break;
+					case OBJECT_TABLE:
+						ident = new_objtree_for_qualname_id(RelationRelationId,
+															constrForm->conrelid);
+						break;
+					default:
+						elog(ERROR, "invalid relation type %d", node->relationType);
+				}
+
+				append_string_object(renameStmt, "conname", node->subname);
+				append_object_object(renameStmt, "identity", ident);
+				ReleaseSysCache(conTup);
+			}
+			break;
+
+		case OBJECT_OPCLASS:
+		case OBJECT_OPFAMILY:
+			ereport(ERROR,
+					(errmsg("renaming of operator classes and families is not supported")));
+			break;
+
+		case OBJECT_RULE:
+			{
+				HeapTuple	rewrTup;
+				Form_pg_rewrite rewrForm;
+				Relation	pg_rewrite;
+
+				pg_rewrite = relation_open(RewriteRelationId, AccessShareLock);
+				rewrTup = get_catalog_object_by_oid(pg_rewrite, objectId);
+				rewrForm = (Form_pg_rewrite) GETSTRUCT(rewrTup);
+
+				renameStmt = new_objtree_VA("ALTER RULE %{rulename}I ON %{identity}D RENAME TO %{newname}I",
+											0);
+				append_string_object(renameStmt, "rulename", node->subname);
+				append_object_object(renameStmt, "identity",
+									 new_objtree_for_qualname_id(RelationRelationId,
+																 rewrForm->ev_class));
+				relation_close(pg_rewrite, AccessShareLock);
+			}
+			break;
+
+		case OBJECT_TRIGGER:
+			{
+				HeapTuple	trigTup;
+				Form_pg_trigger trigForm;
+				Relation	pg_trigger;
+
+				pg_trigger = relation_open(TriggerRelationId, AccessShareLock);
+				trigTup = get_catalog_object_by_oid(pg_trigger, objectId);
+				trigForm = (Form_pg_trigger) GETSTRUCT(trigTup);
+
+				renameStmt = new_objtree_VA("ALTER TRIGGER %{triggername}I ON %{identity}D RENAME TO %{newname}I",
+											0);
+				append_string_object(renameStmt, "triggername", node->subname);
+				append_object_object(renameStmt, "identity",
+									 new_objtree_for_qualname_id(RelationRelationId,
+																 trigForm->tgrelid));
+				relation_close(pg_trigger, AccessShareLock);
+			}
+			break;
+		default:
+			elog(ERROR, "unsupported object type %d", node->renameType);
+	}
+
+	append_string_object(renameStmt, "newname", node->newname);
+
+	command = jsonize_objtree(renameStmt);
+	free_objtree(renameStmt);
+
+	return command;
+}
+
 static inline ObjElem *
 deparse_Seq_Cache(ObjTree *parent, Form_pg_sequence seqdata)
 {
@@ -2283,7 +2562,7 @@ deparse_parsenode_cmd(StashedCommand *cmd)
 			break;
 
 		case T_RenameStmt:
-			command = NULL;
+			command = deparse_RenameStmt(objectId, parsetree);
 			break;
 
 		case T_AlterObjectSchemaStmt:
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index e09728e..063c9f4 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -798,7 +798,7 @@ standard_ProcessUtility(Node *parsetree,
 									   context, params,
 									   dest, completionTag);
 				else
-					ExecRenameStmt(stmt);
+					ExecRenameStmt(stmt, NULL);
 			}
 			break;
 
@@ -1347,7 +1347,14 @@ ProcessUtilitySlow(Node *parsetree,
 				break;
 
 			case T_RenameStmt:
-				ExecRenameStmt((RenameStmt *) parsetree);
+				{
+					RenameStmt *stmt = (RenameStmt *) parsetree;
+					int			objsubid;
+
+					objectId = ExecRenameStmt(stmt, &objsubid);
+					EventTriggerStashCommand(objectId, objsubid,
+											 stmt->renameType, parsetree);
+				}
 				break;
 
 			case T_AlterObjectSchemaStmt:
diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h
index 5907184..b8615ab 100644
--- a/src/include/commands/alter.h
+++ b/src/include/commands/alter.h
@@ -18,7 +18,7 @@
 #include "nodes/parsenodes.h"
 #include "utils/relcache.h"
 
-extern Oid	ExecRenameStmt(RenameStmt *stmt);
+extern Oid	ExecRenameStmt(RenameStmt *stmt, int *objsubid);
 
 extern Oid	ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
 extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 932322f..213bb29 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -53,7 +53,7 @@ extern void ExecuteTruncate(TruncateStmt *stmt);
 
 extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass);
 
-extern Oid	renameatt(RenameStmt *stmt);
+extern Oid	renameatt(RenameStmt *stmt, int *attnum);
 
 extern Oid	RenameConstraint(RenameStmt *stmt);
 
-- 
1.9.1

