From 69674ea6ef2da763ccd27a3f408c0f5fe489fea1 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 9 Feb 2015 19:17:21 -0300
Subject: [PATCH 31/42] deparse: support ALTER ... SET SCHEMA

---
 src/backend/commands/alter.c       | 16 ++++++++------
 src/backend/commands/extension.c   |  6 +++++-
 src/backend/commands/tablecmds.c   |  5 ++++-
 src/backend/commands/typecmds.c    |  9 ++++++--
 src/backend/tcop/deparse_utility.c | 44 +++++++++++++++++++++++++++++++++++++-
 src/backend/tcop/utility.c         |  7 +++---
 src/include/commands/alter.h       |  3 ++-
 src/include/commands/extension.h   |  3 ++-
 src/include/commands/tablecmds.h   |  2 +-
 src/include/commands/typecmds.h    |  3 ++-
 10 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4150104..943ddef 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -398,24 +398,24 @@ ExecRenameStmt(RenameStmt *stmt, int *objsubid)
  * type, the function appropriate to that type is executed.
  */
 Oid
-ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
+ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt, Oid *oldschema)
 {
 	switch (stmt->objectType)
 	{
 		case OBJECT_EXTENSION:
-			return AlterExtensionNamespace(stmt->object, stmt->newschema);
+			return AlterExtensionNamespace(stmt->object, stmt->newschema, oldschema);
 
 		case OBJECT_FOREIGN_TABLE:
 		case OBJECT_SEQUENCE:
 		case OBJECT_TABLE:
 		case OBJECT_VIEW:
 		case OBJECT_MATVIEW:
-			return AlterTableNamespace(stmt);
+			return AlterTableNamespace(stmt, oldschema);
 
 		case OBJECT_DOMAIN:
 		case OBJECT_TYPE:
 			return AlterTypeNamespace(stmt->object, stmt->newschema,
-									  stmt->objectType);
+									  stmt->objectType, oldschema);
 
 			/* generic code path */
 		case OBJECT_AGGREGATE:
@@ -435,6 +435,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
 				Oid			classId;
 				Oid			nspOid;
 				ObjectAddress address;
+				Oid			oldNspOid;
 
 				address = get_object_address(stmt->objectType,
 											 stmt->object,
@@ -447,10 +448,13 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
 				catalog = heap_open(classId, RowExclusiveLock);
 				nspOid = LookupCreationNamespace(stmt->newschema);
 
-				AlterObjectNamespace_internal(catalog, address.objectId,
-											  nspOid);
+				oldNspOid = AlterObjectNamespace_internal(catalog, address.objectId,
+														  nspOid);
 				heap_close(catalog, RowExclusiveLock);
 
+				if (oldschema)
+					*oldschema = oldNspOid;
+
 				return address.objectId;
 			}
 			break;
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 3b95552..14be4cd 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -2400,7 +2400,7 @@ extension_config_remove(Oid extensionoid, Oid tableoid)
  * Execute ALTER EXTENSION SET SCHEMA
  */
 Oid
-AlterExtensionNamespace(List *names, const char *newschema)
+AlterExtensionNamespace(List *names, const char *newschema, Oid *oldschema)
 {
 	char	   *extensionName;
 	Oid			extensionOid;
@@ -2557,6 +2557,10 @@ AlterExtensionNamespace(List *names, const char *newschema)
 							   get_namespace_name(oldNspOid))));
 	}
 
+	/* report old schema, if caller wants it */
+	if (oldschema)
+		*oldschema = oldNspOid;
+
 	systable_endscan(depScan);
 
 	relation_close(depRel, AccessShareLock);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 377e5dd..7634a7d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -11057,7 +11057,7 @@ ATPrepChangePersistence(Relation rel, bool toLogged)
  * Execute ALTER TABLE SET SCHEMA
  */
 Oid
-AlterTableNamespace(AlterObjectSchemaStmt *stmt)
+AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
 {
 	Relation	rel;
 	Oid			relid;
@@ -11109,6 +11109,9 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt)
 	AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
 	free_object_addresses(objsMoved);
 
+	if (oldschema)
+		*oldschema = oldNspOid;
+
 	/* close rel, but keep lock until commit */
 	relation_close(rel, NoLock);
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 0fd225c..8a1f6b5 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3508,11 +3508,13 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
  * Execute ALTER TYPE SET SCHEMA
  */
 Oid
-AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype)
+AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype,
+				   Oid *oldschema)
 {
 	TypeName   *typename;
 	Oid			typeOid;
 	Oid			nspOid;
+	Oid			oldNspOid;
 	ObjectAddresses *objsMoved;
 
 	/* Make a TypeName so we can use standard type lookup machinery */
@@ -3530,9 +3532,12 @@ AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype)
 	nspOid = LookupCreationNamespace(newschema);
 
 	objsMoved = new_object_addresses();
-	AlterTypeNamespace_oid(typeOid, nspOid, objsMoved);
+	oldNspOid = AlterTypeNamespace_oid(typeOid, nspOid, objsMoved);
 	free_object_addresses(objsMoved);
 
+	if (oldschema)
+		*oldschema = oldNspOid;
+
 	return typeOid;
 }
 
diff --git a/src/backend/tcop/deparse_utility.c b/src/backend/tcop/deparse_utility.c
index b5b6727..a813f94 100644
--- a/src/backend/tcop/deparse_utility.c
+++ b/src/backend/tcop/deparse_utility.c
@@ -4084,6 +4084,47 @@ deparse_AlterEnumStmt(Oid objectId, Node *parsetree)
 }
 
 static ObjTree *
+deparse_AlterObjectSchemaStmt(Oid objectId, Node *parsetree, Oid oldschema)
+{
+	AlterObjectSchemaStmt *node = (AlterObjectSchemaStmt *) parsetree;
+	ObjTree	   *alterStmt;
+	ObjectAddress addr;
+	char	   *fmt;
+	char	   *identity;
+	char	   *newschema;
+	char	   *oldschname;
+	char	   *ident;
+
+	newschema = node->newschema;
+
+	fmt = psprintf("ALTER %s %%{identity}s SET SCHEMA %%{newschema}I",
+				   stringify_objtype(node->objectType));
+	alterStmt = new_objtree_VA(fmt, 0);
+	append_string_object(alterStmt, "newschema", newschema);
+
+	/*
+	 * Since the command has already taken place from the point of view of
+	 * catalogs, getObjectIdentity returns the object name with the already
+	 * changed schema.  The output of our deparsing must return the original
+	 * schema name however, so we chop the schema name off the identity string
+	 * and then prepend the quoted schema name.
+	 */
+	addr.classId = get_objtype_catalog_oid(node->objectType);
+	addr.objectId = objectId;
+	addr.objectSubId = 0;
+	identity = getObjectIdentity(&addr);
+	oldschname = get_namespace_name(oldschema);
+	if (!oldschname)
+		elog(ERROR, "cache lookup failed for schema with OID %u", oldschema);
+	ident = psprintf("%s%s",
+					 quote_identifier(oldschname),
+					 identity + strlen(quote_identifier(newschema)));
+	append_string_object(alterStmt, "identity", ident);
+
+	return alterStmt;
+}
+
+static ObjTree *
 deparse_AlterOwnerStmt(Oid objectId, Node *parsetree)
 {
 	AlterOwnerStmt *node = (AlterOwnerStmt *) parsetree;
@@ -5194,7 +5235,8 @@ deparse_simple_command(StashedCommand *cmd)
 			break;
 
 		case T_AlterObjectSchemaStmt:
-			elog(ERROR, "unimplemented deparse of %s", CreateCommandTag(parsetree));
+			command = deparse_AlterObjectSchemaStmt(objectId, parsetree,
+													cmd->d.simple.secondaryOid);
 			break;
 
 		case T_AlterOwnerStmt:
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 20d6565..438ee9b 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -818,7 +818,7 @@ standard_ProcessUtility(Node *parsetree,
 									   context, params,
 									   dest, completionTag);
 				else
-					ExecAlterObjectSchemaStmt(stmt);
+					ExecAlterObjectSchemaStmt(stmt, NULL);
 			}
 			break;
 
@@ -1438,10 +1438,11 @@ ProcessUtilitySlow(Node *parsetree,
 				break;
 
 			case T_AlterObjectSchemaStmt:
-				objectId = ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree);
+				objectId = ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree,
+													 &secondaryOid);
 				EventTriggerStashCommand(objectId, 0,
 										 ((AlterObjectSchemaStmt *) parsetree)->objectType,
-										 parsetree);
+										 secondaryOid, parsetree);
 				break;
 
 			case T_AlterOwnerStmt:
diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h
index 0382037..ecba1d7 100644
--- a/src/include/commands/alter.h
+++ b/src/include/commands/alter.h
@@ -20,7 +20,8 @@
 
 extern Oid	ExecRenameStmt(RenameStmt *stmt, int *objsubid);
 
-extern Oid	ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
+extern Oid	ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
+						  Oid *oldschema);
 extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
 						 ObjectAddresses *objsMoved);
 
diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h
index 4259072..f678950 100644
--- a/src/include/commands/extension.h
+++ b/src/include/commands/extension.h
@@ -43,7 +43,8 @@ extern Oid	ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt);
 extern Oid	get_extension_oid(const char *extname, bool missing_ok);
 extern char *get_extension_name(Oid ext_oid);
 
-extern Oid	AlterExtensionNamespace(List *names, const char *newschema);
+extern Oid	AlterExtensionNamespace(List *names, const char *newschema,
+						Oid *oldschema);
 
 extern void AlterExtensionOwner_oid(Oid extensionOid, Oid newOwnerId);
 
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index d56f393..fa77235 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -37,7 +37,7 @@ extern void AlterTableInternal(Oid relid, List *cmds, bool recurse);
 
 extern Oid	AlterTableMoveAll(AlterTableMoveAllStmt *stmt);
 
-extern Oid	AlterTableNamespace(AlterObjectSchemaStmt *stmt);
+extern Oid	AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema);
 
 extern void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid,
 							Oid nspOid, ObjectAddresses *objsMoved);
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index b38584c..ecc4551 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -45,7 +45,8 @@ extern Oid	RenameType(RenameStmt *stmt);
 extern Oid	AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype);
 extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
 					   bool hasDependEntry);
-extern Oid	AlterTypeNamespace(List *names, const char *newschema, ObjectType objecttype);
+extern Oid	AlterTypeNamespace(List *names, const char *newschema,
+				   ObjectType objecttype, Oid *oldschema);
 extern Oid	AlterTypeNamespace_oid(Oid typeOid, Oid nspOid, ObjectAddresses *objsMoved);
 extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 						   bool isImplicitArray,
-- 
2.1.4

