>From abe2ad87a4feec2f18de816a24ebc9b0be74783b Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Mon, 16 Mar 2015 15:54:52 -0300
Subject: [PATCH 31/37] deparse: support CREATE OPERATOR CLASS

---
 src/backend/commands/event_trigger.c |  35 +++++++-
 src/backend/commands/opclasscmds.c   |   5 +-
 src/backend/tcop/deparse_utility.c   | 158 ++++++++++++++++++++++++++++++++++-
 src/backend/tcop/utility.c           |   2 +
 src/include/commands/defrem.h        |   1 +
 src/include/commands/event_trigger.h |   2 +
 src/include/tcop/deparse_utility.h   |  11 ++-
 7 files changed, 210 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 95491a1..ba97550 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -20,6 +20,7 @@
 #include "catalog/objectaccess.h"
 #include "catalog/pg_event_trigger.h"
 #include "catalog/pg_namespace.h"
+#include "catalog/pg_opclass.h"
 #include "catalog/pg_opfamily.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_trigger.h"
@@ -1887,6 +1888,33 @@ EventTriggerStashAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
 	MemoryContextSwitchTo(oldcxt);
 }
 
+void
+EventTriggerStashCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid,
+							   List *operators, List *procedures)
+{
+	MemoryContext	oldcxt;
+	StashedCommand *stashed;
+
+	if (currentEventTriggerState->commandCollectionInhibited)
+		return;
+
+	oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+	stashed = palloc0(sizeof(StashedCommand));
+	stashed->type = SCT_CreateOpClass;
+	stashed->in_extension = creating_extension;
+	stashed->d.createopc.opcOid = opcoid;
+	stashed->d.createopc.operators = operators;	/* XXX prolly need to copy */
+	stashed->d.createopc.procedures = procedures;
+	stashed->parsetree = copyObject(stmt);
+
+	currentEventTriggerState->stash = lappend(currentEventTriggerState->stash,
+											  stashed);
+
+	MemoryContextSwitchTo(oldcxt);
+}
+
+
 /*
  * EventTriggerStashAlterDefPrivs
  * 		Save data about an ALTER DEFAULT PRIVILEGES command being
@@ -2013,7 +2041,8 @@ pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
 
 		if (cmd->type == SCT_Simple ||
 			cmd->type == SCT_AlterTable ||
-			cmd->type == SCT_AlterOpFamily)
+			cmd->type == SCT_AlterOpFamily ||
+			cmd->type == SCT_CreateOpClass)
 		{
 			const char *tag;
 			char	   *identity;
@@ -2030,6 +2059,10 @@ pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
 				ObjectAddressSet(addr,
 								 OperatorFamilyRelationId,
 								 cmd->d.opfam.opfamOid);
+			else if (cmd->type == SCT_CreateOpClass)
+				ObjectAddressSet(addr,
+								 OperatorClassRelationId,
+								 cmd->d.createopc.opcOid);
 
 			tag = CreateCommandTag(cmd->parsetree);
 
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 68624c3..38b2ff1 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -665,6 +665,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
 					opclassoid, procedures, false);
 
+	/* let event triggers know what happened */
+	EventTriggerStashCreateOpClass(stmt, opclassoid, operators, procedures);
+
 	/*
 	 * Create dependencies for the opclass proper.  Note: we do not create a
 	 * dependency link to the AM, because we don't currently support DROP
@@ -1673,7 +1676,7 @@ RemoveAmProcEntryById(Oid entryOid)
 	heap_close(rel, RowExclusiveLock);
 }
 
-static char *
+char *
 get_am_name(Oid amOid)
 {
 	HeapTuple	tup;
diff --git a/src/backend/tcop/deparse_utility.c b/src/backend/tcop/deparse_utility.c
index 860eb0f..19769fa 100644
--- a/src/backend/tcop/deparse_utility.c
+++ b/src/backend/tcop/deparse_utility.c
@@ -5330,6 +5330,158 @@ deparse_CreateCastStmt(Oid objectId, Node *parsetree)
 }
 
 static ObjTree *
+deparse_CreateOpClassStmt(StashedCommand *cmd)
+{
+	Oid			opcoid = cmd->d.createopc.opcOid;
+	HeapTuple   opcTup;
+	HeapTuple   opfTup;
+	Form_pg_opfamily opfForm;
+	Form_pg_opclass opcForm;
+	ObjTree	   *stmt;
+	ObjTree	   *tmp;
+	List	   *list;
+	ListCell   *cell;
+
+	opcTup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcoid));
+	if (!HeapTupleIsValid(opcTup))
+		elog(ERROR, "cache lookup failed for opclass with OID %u", opcoid);
+	opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
+
+	opfTup = SearchSysCache1(OPFAMILYOID, opcForm->opcfamily);
+	if (!HeapTupleIsValid(opfTup))
+		elog(ERROR, "cache lookup failed for operator family with OID %u", opcForm->opcfamily);
+	opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
+
+	stmt = new_objtree_VA("CREATE OPERATOR CLASS %{identity}D %{default}s "
+						  "FOR TYPE %{type}T USING %{amname}I %{opfamily}s "
+						  "AS %{items:, }s", 0);
+
+	append_object_object(stmt, "identity",
+						 new_objtree_for_qualname(opcForm->opcnamespace,
+												  NameStr(opcForm->opcname)));
+
+	/*
+	 * Add the FAMILY clause; but if it has the same name and namespace as the
+	 * opclass, then have it expand to empty, because it would cause a failure
+	 * if the opfamily was created internally.
+	 */
+	tmp = new_objtree_VA("FAMILY %{opfamily}D", 1,
+						 "opfamily", ObjTypeObject,
+						 new_objtree_for_qualname(opfForm->opfnamespace,
+												  NameStr(opfForm->opfname)));
+	if (strcmp(NameStr(opfForm->opfname), NameStr(opcForm->opcname)) == 0 &&
+		opfForm->opfnamespace == opcForm->opcnamespace)
+		append_bool_object(tmp, "present", false);
+	append_object_object(stmt, "opfamily",  tmp);
+
+	/* Add the DEFAULT clause */
+	append_string_object(stmt, "default",
+						 opcForm->opcdefault ? "DEFAULT" : "");
+
+	/* Add the FOR TYPE clause */
+	append_object_object(stmt, "type",
+						 new_objtree_for_type(opcForm->opcintype, -1));
+
+	/* Add the USING clause */
+	append_string_object(stmt, "amname", get_am_name(opcForm->opcmethod));
+
+	/*
+	 * Add the initial item list.  Note we always add the STORAGE clause, even
+	 * when it is implicit in the original command.
+	 */
+	tmp = new_objtree_VA("STORAGE %{type}T", 0);
+	append_object_object(tmp, "type",
+						 new_objtree_for_type(opcForm->opckeytype != InvalidOid ?
+											  opcForm->opckeytype : opcForm->opcintype,
+											  -1));
+	list = list_make1(new_object_object(tmp));
+
+	/* Add the declared operators */
+	/* XXX this duplicates code in deparse_AlterOpFamily */
+	foreach(cell, cmd->d.createopc.operators)
+	{
+		OpFamilyMember *oper = lfirst(cell);
+		ObjTree	   *tmp;
+
+		tmp = new_objtree_VA("OPERATOR %{num}n %{operator}O(%{ltype}T, %{rtype}T) %{purpose}s",
+							 0);
+		append_integer_object(tmp, "num", oper->number);
+		append_object_object(tmp, "operator",
+							 new_objtree_for_qualname_id(OperatorRelationId,
+														 oper->object));
+		/* add the types */
+		append_object_object(tmp, "ltype",
+							 new_objtree_for_type(oper->lefttype, -1));
+		append_object_object(tmp, "rtype",
+							 new_objtree_for_type(oper->righttype, -1));
+		/* Add the FOR SEARCH / FOR ORDER BY clause */
+		if (oper->sortfamily == InvalidOid)
+			append_string_object(tmp, "purpose", "FOR SEARCH");
+		else
+		{
+			ObjTree	   *tmp2;
+
+			tmp2 = new_objtree_VA("FOR ORDER BY %{opfamily}D", 0);
+			append_object_object(tmp2, "opfamily",
+								 new_objtree_for_qualname_id(OperatorFamilyRelationId,
+															 oper->sortfamily));
+			append_object_object(tmp, "purpose", tmp2);
+		}
+
+		list = lappend(list, new_object_object(tmp));
+	}
+
+	/* Add the declared support functions */
+	foreach(cell, cmd->d.createopc.procedures)
+	{
+		OpFamilyMember *proc = lfirst(cell);
+		ObjTree	   *tmp;
+		HeapTuple	procTup;
+		Form_pg_proc procForm;
+		Oid		   *proargtypes;
+		List	   *arglist;
+		int			i;
+
+		tmp = new_objtree_VA("FUNCTION %{num}n (%{ltype}T, %{rtype}T) %{function}D(%{argtypes:, }T)", 0);
+		append_integer_object(tmp, "num", proc->number);
+		procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc->object));
+		if (!HeapTupleIsValid(procTup))
+			elog(ERROR, "cache lookup failed for procedure %u", proc->object);
+		procForm = (Form_pg_proc) GETSTRUCT(procTup);
+
+		append_object_object(tmp, "function",
+							 new_objtree_for_qualname(procForm->pronamespace,
+													  NameStr(procForm->proname)));
+		proargtypes = procForm->proargtypes.values;
+		arglist = NIL;
+		for (i = 0; i < procForm->pronargs; i++)
+		{
+			ObjTree	   *arg;
+
+			arg = new_objtree_for_type(proargtypes[i], -1);
+			arglist = lappend(arglist, new_object_object(arg));
+		}
+		append_array_object(tmp, "argtypes", arglist);
+
+		ReleaseSysCache(procTup);
+		/* Add the types */
+		append_object_object(tmp, "ltype",
+							 new_objtree_for_type(proc->lefttype, -1));
+		append_object_object(tmp, "rtype",
+							 new_objtree_for_type(proc->righttype, -1));
+
+		list = lappend(list, new_object_object(tmp));
+	}
+
+	append_array_object(stmt, "items", list);
+
+	ReleaseSysCache(opfTup);
+	ReleaseSysCache(opcTup);
+
+	return stmt;
+}
+
+static ObjTree *
 deparse_CreateOpFamily(Oid objectId, Node *parsetree)
 {
 	HeapTuple   opfTup;
@@ -6541,7 +6693,8 @@ deparse_simple_command(StashedCommand *cmd)
 			break;
 
 		case T_CreateOpClassStmt:
-			elog(ERROR, "unimplemented deparse of %s", CreateCommandTag(parsetree));
+			/* handled elsewhere */
+			elog(ERROR, "unexpected command type %s", CreateCommandTag(parsetree));
 			break;
 
 		case T_CreateOpFamilyStmt:
@@ -6677,6 +6830,9 @@ deparse_utility_command(StashedCommand *cmd)
 		case SCT_AlterOpFamily:
 			tree = deparse_AlterOpFamily(cmd);
 			break;
+		case SCT_CreateOpClass:
+			tree = deparse_CreateOpClassStmt(cmd);
+			break;
 		case SCT_AlterDefaultPrivileges:
 			tree = deparse_AlterDefaultPrivilegesStmt(cmd);
 			break;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index eaa057f..4886a3f 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1401,6 +1401,8 @@ ProcessUtilitySlow(Node *parsetree,
 
 			case T_CreateOpClassStmt:
 				address = DefineOpClass((CreateOpClassStmt *) parsetree);
+				/* command is stashed in DefineOpClass */
+				commandStashed = true;
 				break;
 
 			case T_CreateOpFamilyStmt:
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 595f93f..89a7e49 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -87,6 +87,7 @@ extern void IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
 extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
 						   Oid opfnamespace);
 extern Oid	get_am_oid(const char *amname, bool missing_ok);
+extern char *get_am_name(Oid amOid);
 extern Oid	get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
 extern Oid	get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
 
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 35fbfd1..8d53e8b 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -76,6 +76,8 @@ extern void EventTriggerAlterTableEnd(void);
 extern void EventTriggerStashGrant(InternalGrant *istmt);
 extern void EventTriggerStashAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
 							List *operators, List *procedures);
+extern void EventTriggerStashCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid,
+							   List *operators, List *procedures);
 extern void EventTriggerStashAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt);
 
 #endif   /* EVENT_TRIGGER_H */
diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h
index 3a51b24..6ee3ce2 100644
--- a/src/include/tcop/deparse_utility.h
+++ b/src/include/tcop/deparse_utility.h
@@ -31,7 +31,8 @@ typedef enum StashedCommandType
 	SCT_AlterTable,
 	SCT_Grant,
 	SCT_AlterOpFamily,
-	SCT_AlterDefaultPrivileges
+	SCT_AlterDefaultPrivileges,
+	SCT_CreateOpClass
 } StashedCommandType;
 
 /*
@@ -82,6 +83,14 @@ typedef struct StashedCommand
 			List   *procedures;
 		} opfam;
 
+		/* CREATE OPERATOR CLASS */
+		struct
+		{
+			Oid		opcOid;
+			List   *operators;
+			List   *procedures;
+		} createopc;
+
 		/* ALTER DEFAULT PRIVILEGES */
 		struct
 		{
-- 
2.1.4

