>From 29ef698f7eda99312a6faafb7de76220d712567d 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 19/36] deparse: add support for ALTER THING RENAME

It supports everything but functions, aggregates, operator classes and
operator families.
---
 src/backend/tcop/deparse_utility.c | 276 ++++++++++++++++++++++++++++++++++++-
 src/backend/tcop/utility.c         |  28 +++-
 2 files changed, 302 insertions(+), 2 deletions(-)

diff --git a/src/backend/tcop/deparse_utility.c b/src/backend/tcop/deparse_utility.c
index 691c78a..682a7f4 100644
--- a/src/backend/tcop/deparse_utility.c
+++ b/src/backend/tcop/deparse_utility.c
@@ -630,7 +630,7 @@ get_persistence_str(char persistence)
 
 /*
  * deparse_CreateExtensionStmt
- * 		deparse a CreateExtensionStmt
+ *		deparse a CreateExtensionStmt
  *
  * Given an extension OID and the parsetree that created it, return the JSON
  * blob representing the creation command.
@@ -1665,6 +1665,276 @@ 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_DOMAIN:
+			return "DOMAIN";
+		case OBJECT_COLLATION:
+			return "COLLATION";
+		case OBJECT_CONVERSION:
+			return "CONVERSION";
+		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_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_SCHEMA:
+			return "SCHEMA";
+		case OBJECT_SEQUENCE:
+			return "SEQUENCE";
+		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->relationType));
+			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);
+
+				if (node->relationType == OBJECT_DOMAIN)
+					ident = new_objtree_for_qualname_id(TypeRelationId,
+														constrForm->contypid);
+				else if (node->relationType == OBJECT_TABLE)
+					ident = new_objtree_for_qualname_id(RelationRelationId,
+														constrForm->conrelid);
+				else
+					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)
 {
@@ -2284,6 +2554,10 @@ deparse_parsenode_cmd(StashedCommand *cmd)
 			command = deparse_CreateRangeStmt(objectId, parsetree);
 			break;
 
+		case T_RenameStmt:		/* ALTER .. RENAME */
+			command = deparse_RenameStmt(objectId, parsetree);
+			break;
+
 		case T_CreateDomainStmt:
 		case T_CreateFunctionStmt:
 		case T_CreateTableAsStmt:
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b606cb8..beedb01 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1343,7 +1343,33 @@ ProcessUtilitySlow(Node *parsetree,
 				break;
 
 			case T_RenameStmt:
-				ExecRenameStmt((RenameStmt *) parsetree);
+				{
+					ObjectType	objtype;
+
+					objectId = ExecRenameStmt((RenameStmt *) parsetree);
+
+					/*
+					 * Kludge alert: the event trigger machinery as a whole
+					 * doesn't support OBJECT_COLUMN nor OBJECT_ATTRIBUTE, so
+					 * fool it by using the relation type instead.  In certain
+					 * cases this is actually incorrect (for example we might
+					 * be renaming a type attribute or a view column), but the
+					 * deparsing functions will cope with this by actually
+					 * looking at the renameType directly.  The object type and
+					 * identity as reported by
+					 * pg_event_trigger_get_creation_commands might be
+					 * misleading, though.
+					 *
+					 * To support this better we ought to have the attribute
+					 * number for the column or attribute here.  Maybe have
+					 * ExecRenameStmt pass it back?
+					 */
+					objtype = ((RenameStmt *) parsetree)->renameType;
+					if (objtype == OBJECT_COLUMN ||
+						objtype == OBJECT_ATTRIBUTE)
+						objtype = ((RenameStmt *) parsetree)->relationType;
+					EventTriggerStashCommand(objectId, objtype, parsetree);
+				}
 				break;
 
 			case T_AlterObjectSchemaStmt:
-- 
1.9.1

