From e3b589472cc8e299646f2bef3fbaeb486df37f9b Mon Sep 17 00:00:00 2001
From: Ajin Cherian <ajinc@fast.au.fujitsu.com>
Date: Wed, 13 Apr 2022 00:08:10 -0400
Subject: [PATCH] ddl_deparse core support

---
 src/backend/catalog/objectaddress.c |   4 +-
 src/backend/commands/seclabel.c     |   5 +
 src/backend/commands/sequence.c     |  35 +++
 src/backend/utils/adt/format_type.c | 125 +++++++++
 src/backend/utils/adt/regproc.c     | 104 +++++--
 src/backend/utils/adt/ruleutils.c   | 529 ++++++++++++++++++++++++++++++++----
 src/include/commands/sequence.h     |   1 +
 src/include/utils/builtins.h        |   5 +
 src/include/utils/regproc.h         |   3 +-
 src/include/utils/ruleutils.h       |  23 +-
 10 files changed, 746 insertions(+), 88 deletions(-)

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index ac60435..36dfb0f 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2980,7 +2980,7 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
 			{
 				bits16		flags = FORMAT_PROC_INVALID_AS_NULL;
 				char	   *proname = format_procedure_extended(object->objectId,
-																flags);
+																flags, false);
 
 				if (proname == NULL)
 					break;
@@ -4826,7 +4826,7 @@ getObjectIdentityParts(const ObjectAddress *object,
 			{
 				bits16		flags = FORMAT_PROC_FORCE_QUALIFY | FORMAT_PROC_INVALID_AS_NULL;
 				char	   *proname = format_procedure_extended(object->objectId,
-																flags);
+																flags, false);
 
 				if (proname == NULL)
 					break;
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7ae19b9..c1530a1 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -134,6 +134,11 @@ ExecSecLabelStmt(SecLabelStmt *stmt)
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("must specify provider when multiple security label providers have been loaded")));
 		provider = (LabelProvider *) linitial(label_provider_list);
+		/*
+		 * Set the provider in the statement so that DDL deparse can use
+		 * provider explicitly in generated statement.
+		 */
+		stmt->provider = (char *) provider->provider_name;
 	}
 	else
 	{
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index ddf219b..bdd081c 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1699,6 +1699,41 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
 		relation_close(tablerel, NoLock);
 }
 
+/*
+ * Return sequence parameters, detailed
+ */
+
+Form_pg_sequence_data
+get_sequence_values(Oid sequenceId)
+{
+	Buffer      buf;
+	SeqTable    elm;
+	Relation    seqrel;
+	HeapTupleData seqtuple;
+	Form_pg_sequence_data seq;
+	Form_pg_sequence_data retSeq;
+
+	retSeq = palloc(sizeof(FormData_pg_sequence));
+
+	/* open and AccessShareLock sequence */
+	init_sequence(sequenceId, &elm, &seqrel);
+
+	if (pg_class_aclcheck(sequenceId, GetUserId(),
+			ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
+		ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("permission denied for sequence %s",
+							RelationGetRelationName(seqrel))));
+
+	seq = read_seq_tuple(seqrel, &buf, &seqtuple);
+
+	memcpy(retSeq, seq, sizeof(FormData_pg_sequence_data));
+
+	UnlockReleaseBuffer(buf);
+	relation_close(seqrel, NoLock);
+
+	return retSeq;
+}
 
 /*
  * Return sequence parameters in a list of the form created by the parser.
diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c
index 060fd7e..42e3af3 100644
--- a/src/backend/utils/adt/format_type.c
+++ b/src/backend/utils/adt/format_type.c
@@ -334,6 +334,131 @@ format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
 }
 
 /*
+ * Similar to format_type_internal, except we return each bit of information
+ * separately:
+ *
+ * - nspid is the schema OID.  For certain SQL-standard types which have weird
+ *   typmod rules, we return InvalidOid; caller is expected to not schema-
+ *   qualify the name nor add quotes to the type name in this case.
+ *
+ * - typename is set to the type name, without quotes
+ *
+ * - typmod is set to the typemod, if any, as a string with parens
+ *
+ * - typarray indicates whether []s must be added
+ *
+ * We don't try to decode type names to their standard-mandated names, except
+ * in the cases of types with unusual typmod rules.
+ */
+void
+format_type_detailed(Oid type_oid, int32 typemod,
+					 Oid *nspid, char **typname, char **typemodstr,
+					 bool *typarray)
+{
+	HeapTuple	tuple;
+	Form_pg_type typeform;
+	Oid			array_base_type;
+
+	tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for type %u", type_oid);
+
+	typeform = (Form_pg_type) GETSTRUCT(tuple);
+
+	/*	 * Special-case crock for types with strange typmod rules.
+	 */
+	if (type_oid == INTERVALOID ||
+		type_oid == TIMESTAMPOID ||
+		type_oid == TIMESTAMPTZOID ||
+		type_oid == TIMEOID ||
+		type_oid == TIMETZOID)
+	{
+		*typarray = false;
+
+peculiar_typmod:
+		switch (type_oid)
+		{
+			case INTERVALOID:
+				*typname = pstrdup("INTERVAL");
+				break;
+			case TIMESTAMPTZOID:
+				if (typemod < 0)
+				{
+					*typname = pstrdup("TIMESTAMP WITH TIME ZONE");
+					break;
+				}
+				/* otherwise, WITH TZ is added by typmod, so fall through */
+			case TIMESTAMPOID:
+				*typname = pstrdup("TIMESTAMP");
+				break;
+			case TIMETZOID:
+				if (typemod < 0)
+				{
+					*typname = pstrdup("TIME WITH TIME ZONE");
+					break;
+				}
+				/* otherwise, WITH TZ is added by typmode, so fall through */
+			case TIMEOID:
+				*typname = pstrdup("TIME");
+				break;
+		}
+		*nspid = InvalidOid;
+
+		if (typemod >= 0)
+			*typemodstr = printTypmod(NULL, typemod, typeform->typmodout);
+		else
+			*typemodstr = pstrdup("");
+
+		ReleaseSysCache(tuple);
+		return;
+	}
+
+	/*
+	 * Check if it's a regular (variable length) array type.  As above,
+	 * fixed-length array types such as "name" shouldn't get deconstructed.
+	 */
+	array_base_type = typeform->typelem;
+
+	if (array_base_type != InvalidOid &&
+		typeform->typstorage != 'p')
+	{
+		/* Switch our attention to the array element type */
+		ReleaseSysCache(tuple);
+		tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for type %u", type_oid);
+
+		typeform = (Form_pg_type) GETSTRUCT(tuple);
+		type_oid = array_base_type;
+		*typarray = true;
+
+		/*
+		 * If it's an array of one of the types with special typmod rules,
+		 * have the element type be processed as above, but now with typarray
+		 * set to true.
+		 */
+		if (type_oid == INTERVALOID ||
+			type_oid == TIMESTAMPTZOID ||
+			type_oid == TIMESTAMPOID ||
+			type_oid == TIMETZOID ||
+			type_oid == TIMEOID)
+			goto peculiar_typmod;
+	}
+	else
+		*typarray = false;
+
+	*nspid = typeform->typnamespace;
+	*typname = pstrdup(NameStr(typeform->typname));
+
+	if (typemod >= 0)
+		*typemodstr = printTypmod(NULL, typemod, typeform->typmodout);
+	else
+		*typemodstr = pstrdup("");
+
+	ReleaseSysCache(tuple);
+}
+
+/*
  * This version is for use within the backend in error messages, etc.
  * One difference is that it will fail for an invalid type.
  *
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 6d4c1c2..4df3256 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -43,7 +43,8 @@
 
 static void parseNameAndArgTypes(const char *string, bool allowNone,
 								 List **names, int *nargs, Oid *argtypes);
-
+static void format_procedure_args_internal(Form_pg_proc procform,
+							StringInfo buf, bool force_qualify);
 
 /*****************************************************************************
  *	 USER I/O ROUTINES														 *
@@ -322,13 +323,37 @@ to_regprocedure(PG_FUNCTION_ARGS)
 char *
 format_procedure(Oid procedure_oid)
 {
-	return format_procedure_extended(procedure_oid, 0);
+	return format_procedure_extended(procedure_oid, 0, false);
 }
 
 char *
 format_procedure_qualified(Oid procedure_oid)
 {
-	return format_procedure_extended(procedure_oid, FORMAT_PROC_FORCE_QUALIFY);
+	return format_procedure_extended(procedure_oid, true, false);
+}
+
+/*
+ * format_procedure_args   - converts proc OID to "(args)"
+ */
+char *
+format_procedure_args(Oid procedure_oid, bool force_qualify)
+{
+	StringInfoData buf;
+	HeapTuple   proctup;
+	Form_pg_proc procform;
+
+	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
+	if (!HeapTupleIsValid(proctup))
+		elog(ERROR, "cache lookup failed for procedure %u", procedure_oid);
+	procform = (Form_pg_proc) GETSTRUCT(proctup);
+
+
+	initStringInfo(&buf);
+	format_procedure_args_internal(procform, &buf, force_qualify);
+
+	ReleaseSysCache(proctup);
+
+	return buf.data;
 }
 
 /*
@@ -347,10 +372,13 @@ format_procedure_qualified(Oid procedure_oid)
  *			always schema-qualify procedure names, regardless of search_path
  */
 char *
-format_procedure_extended(Oid procedure_oid, bits16 flags)
+format_procedure_extended(Oid procedure_oid, bits16 flags, bool args_only)
 {
 	char	   *result;
 	HeapTuple	proctup;
+	bool		force_qualify;
+
+	force_qualify = (flags & FORMAT_PROC_FORCE_QUALIFY) != 0;
 
 	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
 
@@ -358,8 +386,6 @@ format_procedure_extended(Oid procedure_oid, bits16 flags)
 	{
 		Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
 		char	   *proname = NameStr(procform->proname);
-		int			nargs = procform->pronargs;
-		int			i;
 		char	   *nspname;
 		StringInfoData buf;
 
@@ -368,30 +394,25 @@ format_procedure_extended(Oid procedure_oid, bits16 flags)
 
 		initStringInfo(&buf);
 
-		/*
-		 * Would this proc be found (given the right args) by regprocedurein?
-		 * If not, or if caller requests it, we need to qualify it.
-		 */
-		if ((flags & FORMAT_PROC_FORCE_QUALIFY) == 0 &&
-			FunctionIsVisible(procedure_oid))
-			nspname = NULL;
-		else
-			nspname = get_namespace_name(procform->pronamespace);
-
-		appendStringInfo(&buf, "%s(",
-						 quote_qualified_identifier(nspname, proname));
-		for (i = 0; i < nargs; i++)
+		if (!args_only)
 		{
-			Oid			thisargtype = procform->proargtypes.values[i];
-
-			if (i > 0)
-				appendStringInfoChar(&buf, ',');
-			appendStringInfoString(&buf,
-								   (flags & FORMAT_PROC_FORCE_QUALIFY) != 0 ?
-								   format_type_be_qualified(thisargtype) :
-								   format_type_be(thisargtype));
+
+			/*
+		 	 * Would this proc be found (given the right args) by regprocedurein?
+		 	 * If not, or if caller requests it, we need to qualify it.
+		 	 */
+			if ((flags & FORMAT_PROC_FORCE_QUALIFY) == 0 &&
+				FunctionIsVisible(procedure_oid))
+				nspname = NULL;
+			else
+				nspname = get_namespace_name(procform->pronamespace);
+
+			appendStringInfo(&buf, "%s",
+							 quote_qualified_identifier(nspname, proname));
 		}
-		appendStringInfoChar(&buf, ')');
+
+		/* add the attributes */
+		format_procedure_args_internal(procform, &buf, force_qualify);
 
 		result = buf.data;
 
@@ -413,6 +434,33 @@ format_procedure_extended(Oid procedure_oid, bits16 flags)
 }
 
 /*
+ * Append the parenthised arguments of the given pg_proc row into the output
+ * buffer.  force_qualify indicates whether to schema-qualify type names
+ * regardless of visibility.
+ */
+static void
+format_procedure_args_internal(Form_pg_proc procform, StringInfo buf,
+							   bool force_qualify)
+{
+	int			i;
+	int			nargs = procform->pronargs;
+
+	appendStringInfoChar(buf, '(');
+	for (i = 0; i < nargs; i++)
+	{
+		Oid			thisargtype = procform->proargtypes.values[i];
+
+		if (i > 0)
+			appendStringInfoChar(buf, ',');
+		appendStringInfoString(buf,
+							   force_qualify ?
+							   format_type_be_qualified(thisargtype) :
+							   format_type_be(thisargtype));
+	}
+	appendStringInfoChar(buf, ')');
+}
+
+/*
  * Output an objname/objargs representation for the procedure with the
  * given OID.  If it doesn't exist, an error is thrown.
  *
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3296ad0..f1d6e9a 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -551,6 +551,78 @@ pg_get_ruledef_ext(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(string_to_text(res));
 }
 
+/*
+ * Given a pair of Datum corresponding to a rule's pg_rewrite.ev_qual and
+ * ev_action columns, return their text representation; ev_qual as a single
+ * string in whereClause and ev_action as a List of strings (which might be
+ * NIL, signalling NOTHING) in actions.
+ */
+void
+pg_get_ruledef_details(Datum ev_qual, Datum ev_action,
+					   char **whereClause, List **actions)
+{
+	int prettyFlags = 0;
+	char *qualstr = TextDatumGetCString(ev_qual);
+	char *actionstr = TextDatumGetCString(ev_action);
+	List *actionNodeList = (List *) stringToNode(actionstr);
+	StringInfoData buf;
+
+	initStringInfo(&buf);
+	if (strlen(qualstr) > 0 && strcmp(qualstr, "<>") != 0)
+	{
+		Node	   *qual;
+		Query	   *query;
+		deparse_context context;
+		deparse_namespace dpns;
+
+		qual = stringToNode(qualstr);
+
+		query = (Query *) linitial(actionNodeList);
+		query = getInsertSelectQuery(query, NULL);
+
+		AcquireRewriteLocks(query, false, false);
+
+		context.buf = &buf;
+		context.namespaces = list_make1(&dpns);
+		context.windowClause = NIL;
+		context.windowTList = NIL;
+		context.varprefix = (list_length(query->rtable) != 1);
+		context.prettyFlags = prettyFlags;
+		context.wrapColumn = WRAP_COLUMN_DEFAULT;
+		context.indentLevel = PRETTYINDENT_STD;
+
+		set_deparse_for_query(&dpns, query, NIL);
+
+		get_rule_expr(qual, &context, false);
+
+		*whereClause = pstrdup(buf.data);
+	}
+	else
+		*whereClause = NULL;
+
+	if (list_length(actionNodeList) == 0)
+		*actions = NIL;
+	else
+	{
+		ListCell *cell;
+		List	*output = NIL;
+
+		foreach(cell, actionNodeList)
+		{
+			Query	*query = (Query *) lfirst(cell);
+
+			if (query->commandType == CMD_NOTHING)
+				continue;
+
+			resetStringInfo(&buf);
+			get_query_def(query, &buf, NIL, NULL,
+						  prettyFlags, WRAP_COLUMN_DEFAULT, 0);
+			output = lappend(output, pstrdup(buf.data));
+		}
+		*actions = output;
+	}
+}
+
 
 static char *
 pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
@@ -742,6 +814,14 @@ pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(string_to_text(res));
 }
 
+char *
+pg_get_viewdef_internal(Oid viewoid)
+{
+
+	return  pg_get_viewdef_worker(viewoid, 0, WRAP_COLUMN_DEFAULT);
+
+}
+
 /*
  * Common code for by-OID and by-name variants of pg_get_viewdef
  */
@@ -824,6 +904,21 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
 	return buf.data;
 }
 
+/*
+ * get_createtableas_def   - Get the accompanying query for a CREATE TABLE AS
+ */
+char *
+pg_get_createtableas_def(Query *query)
+{
+	StringInfoData buf;
+
+	initStringInfo(&buf);
+
+	get_query_def(query, &buf, NIL, NULL, 0, 0, 0);
+
+	return buf.data;
+}
+
 /* ----------
  * pg_get_triggerdef		- Get the definition of a trigger
  * ----------
@@ -1021,65 +1116,12 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
 	if (!isnull)
 	{
 		Node	   *qual;
-		char		relkind;
-		deparse_context context;
-		deparse_namespace dpns;
-		RangeTblEntry *oldrte;
-		RangeTblEntry *newrte;
-
-		appendStringInfoString(&buf, "WHEN (");
+		char       *qualstr;
 
 		qual = stringToNode(TextDatumGetCString(value));
+		qualstr = pg_get_trigger_whenclause(trigrec, qual, pretty);
 
-		relkind = get_rel_relkind(trigrec->tgrelid);
-
-		/* Build minimal OLD and NEW RTEs for the rel */
-		oldrte = makeNode(RangeTblEntry);
-		oldrte->rtekind = RTE_RELATION;
-		oldrte->relid = trigrec->tgrelid;
-		oldrte->relkind = relkind;
-		oldrte->rellockmode = AccessShareLock;
-		oldrte->alias = makeAlias("old", NIL);
-		oldrte->eref = oldrte->alias;
-		oldrte->lateral = false;
-		oldrte->inh = false;
-		oldrte->inFromCl = true;
-
-		newrte = makeNode(RangeTblEntry);
-		newrte->rtekind = RTE_RELATION;
-		newrte->relid = trigrec->tgrelid;
-		newrte->relkind = relkind;
-		newrte->rellockmode = AccessShareLock;
-		newrte->alias = makeAlias("new", NIL);
-		newrte->eref = newrte->alias;
-		newrte->lateral = false;
-		newrte->inh = false;
-		newrte->inFromCl = true;
-
-		/* Build two-element rtable */
-		memset(&dpns, 0, sizeof(dpns));
-		dpns.rtable = list_make2(oldrte, newrte);
-		dpns.subplans = NIL;
-		dpns.ctes = NIL;
-		dpns.appendrels = NULL;
-		set_rtable_names(&dpns, NIL, NULL);
-		set_simple_column_names(&dpns);
-
-		/* Set up context with one-deep namespace stack */
-		context.buf = &buf;
-		context.namespaces = list_make1(&dpns);
-		context.windowClause = NIL;
-		context.windowTList = NIL;
-		context.varprefix = true;
-		context.prettyFlags = GET_PRETTY_FLAGS(pretty);
-		context.wrapColumn = WRAP_COLUMN_DEFAULT;
-		context.indentLevel = PRETTYINDENT_STD;
-		context.special_exprkind = EXPR_KIND_NONE;
-		context.appendparents = NULL;
-
-		get_rule_expr(qual, &context, false);
-
-		appendStringInfoString(&buf, ") ");
+		appendStringInfo(&buf, "WHEN (%s) ", qualstr);
 	}
 
 	appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
@@ -1120,6 +1162,64 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
 	return buf.data;
 }
 
+char *
+pg_get_trigger_whenclause(Form_pg_trigger trigrec, Node *whenClause, bool pretty)
+{
+	StringInfoData buf;
+	char        relkind;
+	deparse_context context;
+	deparse_namespace dpns;
+	RangeTblEntry *oldrte;
+	RangeTblEntry *newrte;
+
+   initStringInfo(&buf);
+
+   relkind = get_rel_relkind(trigrec->tgrelid);
+
+   /* Build minimal OLD and NEW RTEs for the rel */
+   oldrte = makeNode(RangeTblEntry);
+   oldrte->rtekind = RTE_RELATION;
+   oldrte->relid = trigrec->tgrelid;
+   oldrte->relkind = relkind;
+   oldrte->alias = makeAlias("old", NIL);
+   oldrte->eref = oldrte->alias;
+   oldrte->lateral = false;
+   oldrte->inh = false;
+   oldrte->inFromCl = true;
+
+   newrte = makeNode(RangeTblEntry);
+   newrte->rtekind = RTE_RELATION;
+   newrte->relid = trigrec->tgrelid;
+   newrte->relkind = relkind;
+   newrte->alias = makeAlias("new", NIL);
+   newrte->eref = newrte->alias;
+   newrte->lateral = false;
+   newrte->inh = false;
+   newrte->inFromCl = true;
+
+   /* Build two-element rtable */
+   memset(&dpns, 0, sizeof(dpns));
+   dpns.rtable = list_make2(oldrte, newrte);
+   dpns.ctes = NIL;
+   set_rtable_names(&dpns, NIL, NULL);
+   set_simple_column_names(&dpns);
+
+   /* Set up context with one-deep namespace stack */
+   context.buf = &buf;
+   context.namespaces = list_make1(&dpns);
+   context.windowClause = NIL;
+   context.windowTList = NIL;
+   context.varprefix = true;
+   context.prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
+   context.wrapColumn = WRAP_COLUMN_DEFAULT;
+   context.indentLevel = PRETTYINDENT_STD;
+   context.special_exprkind = EXPR_KIND_NONE;
+
+   get_rule_expr(whenClause, &context, false);
+
+   return buf.data;
+}
+
 /* ----------
  * pg_get_indexdef			- Get the definition of an index
  *
@@ -1207,6 +1307,8 @@ pg_get_indexdef_columns(Oid indexrelid, bool pretty)
  *
  * This is now used for exclusion constraints as well: if excludeOps is not
  * NULL then it points to an array of exclusion operator OIDs.
+ *
+ * XXX if you change this function, see pg_get_indexdef_detailed too.
  */
 static char *
 pg_get_indexdef_worker(Oid indexrelid, int colno,
@@ -1525,6 +1627,251 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	return buf.data;
 }
 
+/*
+ * Return an index definition, split in several pieces.
+ *
+ * There is a huge lot of code that's a dupe of pg_get_indexdef_worker, but
+ * control flow is different enough that it doesn't seem worth keeping them
+ * together.
+ */
+void
+pg_get_indexdef_detailed(Oid indexrelid,
+						char **index_am,
+						char **definition,
+						char **reloptions,
+						char **tablespace,
+						char **whereClause)
+{
+	HeapTuple   ht_idx;
+	HeapTuple   ht_idxrel;
+	HeapTuple   ht_am;
+	Form_pg_index idxrec;
+	Form_pg_class idxrelrec;
+	Form_pg_am  amrec;
+	IndexAmRoutine *amroutine;
+	List       *indexprs;
+	ListCell   *indexpr_item;
+	List       *context;
+	Oid         indrelid;
+	int         keyno;
+	Datum       indcollDatum;
+	Datum       indclassDatum;
+	Datum       indoptionDatum;
+	bool        isnull;
+	oidvector  *indcollation;
+	oidvector  *indclass;
+	int2vector *indoption;
+	StringInfoData definitionBuf;
+	char       *sep;
+
+	/*
+	 * Fetch the pg_index tuple by the Oid of the index
+	 */
+	ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
+	if (!HeapTupleIsValid(ht_idx))
+		elog(ERROR, "cache lookup failed for index %u", indexrelid);
+	idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
+
+	indrelid = idxrec->indrelid;
+	Assert(indexrelid == idxrec->indexrelid);
+
+	/* Must get indcollation, indclass, and indoption the hard way */
+	indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+									Anum_pg_index_indcollation, &isnull);
+	Assert(!isnull);
+	indcollation = (oidvector *) DatumGetPointer(indcollDatum);
+
+	indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+									Anum_pg_index_indclass, &isnull);
+	Assert(!isnull);
+	indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
+	indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+									 Anum_pg_index_indoption, &isnull);
+	Assert(!isnull);
+	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
+
+	/*
+	 * Fetch the pg_class tuple of the index relation
+	 */
+	ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
+	if (!HeapTupleIsValid(ht_idxrel))
+		elog(ERROR, "cache lookup failed for relation %u", indexrelid);
+	idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
+
+	/*
+	 * Fetch the pg_am tuple of the index' access method
+	 */
+	ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
+	if (!HeapTupleIsValid(ht_am))
+		elog(ERROR, "cache lookup failed for access method %u",
+			idxrelrec->relam);
+	amrec = (Form_pg_am) GETSTRUCT(ht_am);
+
+	/* Fetch the index AM's API struct */
+	amroutine = GetIndexAmRoutine(amrec->amhandler);
+
+	/*
+	 * Get the index expressions, if any.  (NOTE: we do not use the relcache
+	 * versions of the expressions and predicate, because we want to display
+	 * non-const-folded expressions.)
+	 */
+	if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
+	{
+		Datum       exprsDatum;
+		bool        isnull;
+		char       *exprsString;
+
+		exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+									 Anum_pg_index_indexprs, &isnull);
+		Assert(!isnull);
+		exprsString = TextDatumGetCString(exprsDatum);
+		indexprs = (List *) stringToNode(exprsString);
+		pfree(exprsString);
+	}
+	else
+		indexprs = NIL;
+
+	indexpr_item = list_head(indexprs);
+
+	context = deparse_context_for(get_relation_name(indrelid), indrelid);
+
+	initStringInfo(&definitionBuf);
+
+	/* output index AM */
+	*index_am = pstrdup(quote_identifier(NameStr(amrec->amname)));
+
+	/*
+	 * Output index definition.  Note the outer parens must be supplied by
+	 * caller.
+	 */
+	sep = "";
+	for (keyno = 0; keyno < idxrec->indnatts; keyno++)
+	{
+		AttrNumber  attnum = idxrec->indkey.values[keyno];
+		int16       opt = indoption->values[keyno];
+		Oid         keycoltype;
+		Oid         keycolcollation;
+
+		Oid         indcoll;
+
+		appendStringInfoString(&definitionBuf, sep);
+		sep = ", ";
+
+		if (attnum != 0)
+		{
+			/* Simple index column */
+			char       *attname;
+			int32       keycoltypmod;
+
+			attname = get_attname(indrelid, attnum, false);
+			appendStringInfoString(&definitionBuf, quote_identifier(attname));
+			get_atttypetypmodcoll(indrelid, attnum,
+								  &keycoltype, &keycoltypmod,
+								  &keycolcollation);
+		}
+		else
+		{
+			/* expressional index */
+			Node       *indexkey;
+			char       *str;
+
+			if (indexpr_item == NULL)
+				elog(ERROR, "too few entries in indexprs list");
+			indexkey = (Node *) lfirst(indexpr_item);
+			indexpr_item = lnext(indexprs, indexpr_item);
+			/* Deparse */
+			str = deparse_expression_pretty(indexkey, context, false, false,
+											0, 0);
+
+			/* Need parens if it's not a bare function call */
+			if (indexkey && IsA(indexkey, FuncExpr) &&
+				((FuncExpr *) indexkey)->funcformat == COERCE_EXPLICIT_CALL)
+				appendStringInfoString(&definitionBuf, str);
+			else
+				appendStringInfo(&definitionBuf, "(%s)", str);
+
+			keycoltype = exprType(indexkey);
+			keycolcollation = exprCollation(indexkey);
+		}
+
+		/* Add collation, even if default */
+		indcoll = indcollation->values[keyno];
+		if (OidIsValid(indcoll))
+			appendStringInfo(&definitionBuf, " COLLATE %s",
+							 generate_collation_name((indcoll)));
+
+		/* Add the operator class name, even if default */
+		get_opclass_name(indclass->values[keyno], InvalidOid, &definitionBuf);
+
+		/* Add options if relevant */
+		if (amroutine->amcanorder)
+		{
+			/* if it supports sort ordering, report DESC and NULLS opts */
+			if (opt & INDOPTION_DESC)
+			{
+				appendStringInfoString(&definitionBuf, " DESC");
+				/* NULLS FIRST is the default in this case */
+				if (!(opt & INDOPTION_NULLS_FIRST))
+					appendStringInfoString(&definitionBuf, " NULLS LAST");
+			}
+			else
+			{
+				if (opt & INDOPTION_NULLS_FIRST)
+					appendStringInfoString(&definitionBuf, " NULLS FIRST");
+			}
+		}
+
+		/* XXX excludeOps thingy was here; do we need anything? */
+	}
+	*definition = definitionBuf.data;
+
+	/* output reloptions */
+	*reloptions = flatten_reloptions(indexrelid);
+
+	/* output tablespace */
+	{
+		Oid         tblspc;
+
+		tblspc = get_rel_tablespace(indexrelid);
+		if (OidIsValid(tblspc))
+			*tablespace = pstrdup(quote_identifier(get_tablespace_name(tblspc)));
+		else
+			*tablespace = NULL;
+	}
+
+	/* report index predicate, if any */
+	if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
+	{
+		Node       *node;
+		Datum       predDatum;
+		bool        isnull;
+		char       *predString;
+
+		/* Convert text string to node tree */
+		predDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+									Anum_pg_index_indpred, &isnull);
+		Assert(!isnull);
+		predString = TextDatumGetCString(predDatum);
+		node = (Node *) stringToNode(predString);
+		pfree(predString);
+
+		/* Deparse */
+		*whereClause =
+			deparse_expression_pretty(node, context, false, false,
+									  0, 0);
+	}
+	else
+		*whereClause = NULL;
+
+	/* Clean up */
+	ReleaseSysCache(ht_idx);
+	ReleaseSysCache(ht_idxrel);
+	ReleaseSysCache(ht_am);
+
+	/* all done */
+}
+
 /* ----------
  * pg_get_querydef
  *
@@ -12833,3 +13180,73 @@ get_range_partbound_string(List *bound_datums)
 
 	return buf->data;
 }
+
+
+/*
+ * Obtain the deparsed default value for the given column of the given table.
+ *
+ * Caller must have set a correct deparse context.
+ */
+char *
+RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext)
+{
+	Node *defval;
+	char *defstr;
+
+	defval = build_column_default(rel, attno);
+	defstr = deparse_expression_pretty(defval, dpcontext, false, false,
+									   0, 0);
+
+	return defstr;
+}
+
+/*
+ * Return the default value of a domain.
+ */
+char *
+DomainGetDefault(HeapTuple domTup)
+{
+	Datum   def;
+	Node   *defval;
+	char   *defstr;
+	bool    isnull;
+
+	def = SysCacheGetAttr(TYPEOID, domTup, Anum_pg_type_typdefaultbin,
+						  &isnull);
+	if (isnull)
+		elog(ERROR, "domain \"%s\" does not have a default value",
+			NameStr(((Form_pg_type) GETSTRUCT(domTup))->typname));
+	defval = stringToNode(TextDatumGetCString(def));
+	defstr = deparse_expression_pretty(defval, NULL /* dpcontext? */,
+									   false, false, 0, 0);
+
+	return defstr;
+}
+
+
+/*
+ * Return the defaults values of arguments to a function, as a list of
+ * deparsed expressions.
+ */
+List *
+FunctionGetDefaults(text *proargdefaults)
+{
+	List   *nodedefs;
+	List   *strdefs = NIL;
+	ListCell *cell;
+
+	nodedefs = (List *) stringToNode(TextDatumGetCString(proargdefaults));
+	if (!IsA(nodedefs, List))
+		elog(ERROR, "proargdefaults is not a list");
+
+	foreach(cell, nodedefs)
+	{
+		Node   *onedef = lfirst(cell);
+
+		strdefs = lappend(strdefs, deparse_expression_pretty(onedef, NIL, false, false, 0, 0));
+	}
+
+	return strdefs;
+}
+
+
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 9da2300..2875106 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -54,6 +54,7 @@ typedef struct xl_seq_rec
 extern int64 nextval_internal(Oid relid, bool check_permissions);
 extern Datum nextval(PG_FUNCTION_ARGS);
 extern List *sequence_options(Oid relid);
+extern Form_pg_sequence_data get_sequence_values(Oid sequenceId);
 
 extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *stmt);
 extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 666e545..cde659f 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -115,11 +115,16 @@ extern Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS);
 #define FORMAT_TYPE_INVALID_AS_NULL	0x08	/* NULL if undefined */
 extern char *format_type_extended(Oid type_oid, int32 typemod, bits16 flags);
 
+extern char *format_procedure_args(Oid procedure_oid, bool force_qualify);
 extern char *format_type_be(Oid type_oid);
 extern char *format_type_be_qualified(Oid type_oid);
 extern char *format_type_with_typemod(Oid type_oid, int32 typemod);
 
 extern int32 type_maximum_size(Oid type_oid, int32 typemod);
+extern void format_type_detailed(Oid type_oid, int32 typemod,
+                    Oid *nspid, char **typname,
+                    char **typemodstr, bool *is_array);
+
 
 /* quote.c */
 extern char *quote_literal_cstr(const char *rawstr);
diff --git a/src/include/utils/regproc.h b/src/include/utils/regproc.h
index a36ceba..f4d543d 100644
--- a/src/include/utils/regproc.h
+++ b/src/include/utils/regproc.h
@@ -18,7 +18,8 @@
 /* Control flags for format_procedure_extended */
 #define FORMAT_PROC_INVALID_AS_NULL	0x01	/* NULL if undefined */
 #define FORMAT_PROC_FORCE_QUALIFY	0x02	/* force qualification */
-extern char *format_procedure_extended(Oid procedure_oid, bits16 flags);
+extern char *format_procedure_extended(Oid procedure_oid, bits16 flags,
+	   			bool args_only);
 
 /* Control flags for format_operator_extended */
 #define FORMAT_OPERATOR_INVALID_AS_NULL	0x01	/* NULL if undefined */
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 7d48971..83c242d 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -13,6 +13,7 @@
 #ifndef RULEUTILS_H
 #define RULEUTILS_H
 
+#include "catalog/pg_trigger.h"
 #include "nodes/nodes.h"
 #include "nodes/parsenodes.h"
 #include "nodes/pg_list.h"
@@ -23,12 +24,26 @@ struct PlannedStmt;
 
 extern char *pg_get_indexdef_string(Oid indexrelid);
 extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty);
+extern void pg_get_indexdef_detailed(Oid indexrelid,
+                        char **index_am,
+                        char **definition,
+                        char **reloptions,
+                        char **tablespace,
+                        char **whereClause);
+extern char *pg_get_trigger_whenclause(Form_pg_trigger trigrec,
+									   Node *whenClause, bool pretty);
+extern char *pg_get_constraintdef_string(Oid constraintId, bool fullCommand);
+extern char *pg_get_constraintdef_command(Oid constraintId);
+extern void pg_get_ruledef_details(Datum ev_qual, Datum ev_action,
+								   char **whereClause, List **actions);
+extern char *pg_get_viewdef_internal(Oid viewoid);
+extern char *pg_get_createtableas_def(Query *query);
+
 extern char *pg_get_querydef(Query *query, bool pretty);
 
 extern char *pg_get_partkeydef_columns(Oid relid, bool pretty);
 extern char *pg_get_partconstrdef_string(Oid partitionId, char *aliasname);
 
-extern char *pg_get_constraintdef_command(Oid constraintId);
 extern char *deparse_expression(Node *expr, List *dpcontext,
 								bool forceprefix, bool showimplicit);
 extern List *deparse_context_for(const char *aliasname, Oid relid);
@@ -39,6 +54,12 @@ extern List *set_deparse_context_plan(List *dpcontext,
 extern List *select_rtable_names_for_explain(List *rtable,
 											 Bitmapset *rels_used);
 extern char *generate_collation_name(Oid collid);
+extern List *FunctionGetDefaults(text *proargdefaults);
+
+extern char *RelationGetColumnDefault(Relation rel, AttrNumber attno,
+                        List *dpcontext);
+
+extern char *DomainGetDefault(HeapTuple domTup);
 extern char *generate_opclass_name(Oid opclass);
 extern char *get_range_partbound_string(List *bound_datums);
 
-- 
1.8.3.1

