From 282708ef9354d385a5e508434a54e4840e4c7ad4 Mon Sep 17 00:00:00 2001
From: Hou Zhijie <houzj.fnst@cn.fujitsu.com>
Date: Mon, 24 Apr 2023 17:10:03 +0800
Subject: [PATCH 3/8] Add verbose option for ddl deparse module

Currently, the output from the deparser includes all the unused syntax parts
that are not present in the original DDL command. This generates unnecessarily
large output strings and subsequent WAL sizes. To address this issue, we are
introducing a verbose option in the upcoming version. This option allows users
to control whether to output the not-present syntax parts, helping to reduce the
size of the output string and WAL size.
---
 src/backend/commands/ddldeparse.c | 629 ++++++++++++++++++++----------
 src/include/tcop/ddldeparse.h     |   2 +-
 2 files changed, 425 insertions(+), 206 deletions(-)

diff --git a/src/backend/commands/ddldeparse.c b/src/backend/commands/ddldeparse.c
index 05c2b22d7e..3bdf91be65 100644
--- a/src/backend/commands/ddldeparse.c
+++ b/src/backend/commands/ddldeparse.c
@@ -114,11 +114,20 @@ typedef struct ObjElem
 								 * structure */
 } ObjElem;
 
-static void append_array_object(ObjTree *tree, char *object_name, List *array);
-static void append_bool_object(ObjTree *tree, char *object_name, bool value);
-static void append_object_object(ObjTree *tree, char *object_name, ObjTree *value);
+/*
+ * Reduce some unnecessary strings from the output json when verbose
+ * and "present" member is false. This means these strings won't be merged into
+ * the last DDL command.
+ */
+bool		verbose = true;
+
+static void append_format_string(ObjTree *tree, char *sub_fmt);
+static void append_array_object(ObjTree *tree, char *sub_fmt, List *array);
+static void append_bool_object(ObjTree *tree, char *sub_fmt, bool value);
+static void append_object_object(ObjTree *tree, char *sub_fmt, ObjTree *value);
+static char *append_object_to_format_string(ObjTree *tree, char *sub_fmt);
 static void append_premade_object(ObjTree *tree, ObjElem *elem);
-static void append_string_object(ObjTree *tree, char *object_name,
+static void append_string_object(ObjTree *tree, char *sub_fmt, char *object_name,
 								 char *value);
 static ObjElem *new_object(ObjType type, char *name);
 static ObjTree *new_objtree_for_qualname_id(Oid classId, Oid objectId);
@@ -152,10 +161,23 @@ static List *deparse_TableElements(Relation relation, List *tableElements, List
 
 /*
  * Append present as false to a tree.
+ * If sub_fmt is passed and verbose mode is ON,
+ * append sub_fmt as well to tree.
+ *
+ * Example:
+ * in non-verbose mode, element will be like:
+ * "collation": {"fmt": "COLLATE", "present": false}
+ * in verbose mode:
+ * "collation": {"fmt": "COLLATE %{name}D", "present": false}
  */
 static void
-append_not_present(ObjTree *tree)
+append_not_present(ObjTree *tree, char *sub_fmt)
 {
+	if (verbose && sub_fmt)
+	{
+		append_format_string(tree, sub_fmt);
+	}
+
 	append_bool_object(tree, "present", false);
 }
 
@@ -163,9 +185,40 @@ append_not_present(ObjTree *tree)
  * Append an array parameter to a tree.
  */
 static void
-append_array_object(ObjTree *tree, char *object_name, List *array)
+append_array_object(ObjTree *tree, char *sub_fmt, List *array)
 {
 	ObjElem    *param;
+	char	   *object_name;
+
+	Assert(sub_fmt);
+
+	if (list_length(array) == 0)
+		return;
+
+	if (!verbose)
+	{
+		ListCell   *lc;
+
+		/* Remove elements where present flag is false */
+		foreach(lc, array)
+		{
+			ObjElem    *elem = (ObjElem *) lfirst(lc);
+
+			Assert(elem->objtype == ObjTypeObject ||
+				   elem->objtype == ObjTypeString);
+
+			if (!elem->value.object->present &&
+				elem->objtype == ObjTypeObject)
+				array = foreach_delete_current(array, lc);
+		}
+
+	}
+
+	/* Check for empty list after removing elements */
+	if (list_length(array) == 0)
+		return;
+
+	object_name = append_object_to_format_string(tree, sub_fmt);
 
 	param = new_object(ObjTypeArray, object_name);
 	param->value.array = array;
@@ -176,37 +229,105 @@ append_array_object(ObjTree *tree, char *object_name, List *array)
  * Append a boolean parameter to a tree.
  */
 static void
-append_bool_object(ObjTree *tree, char *object_name, bool value)
+append_bool_object(ObjTree *tree, char *sub_fmt, bool value)
 {
 	ObjElem    *param;
+	char	   *object_name = sub_fmt;
 
-	Assert(object_name);
+	Assert(sub_fmt);
 
 	/*
 	 * Check if the format string is 'present' and if yes, store the boolean
 	 * value
 	 */
-	if (strcmp(object_name, "present") == 0)
+	if (strcmp(sub_fmt, "present") == 0)
 		tree->present = value;
+	else
+		object_name = append_object_to_format_string(tree, sub_fmt);
 
 	param = new_object(ObjTypeBool, object_name);
 	param->value.boolean = value;
 	append_premade_object(tree, param);
 }
 
+/*
+ * Append the input format string to the ObjTree.
+ */
+static void
+append_format_string(ObjTree *tree, char *sub_fmt)
+{
+	int			len;
+	char	   *fmt;
+
+	Assert(sub_fmt);
+
+	if (tree->fmtinfo == NULL)
+		return;
+
+	fmt = tree->fmtinfo->data;
+	len = tree->fmtinfo->len;
+
+	/* Add a separator if necessary */
+	if (len > 0 && fmt[len - 1] != ' ')
+		appendStringInfoSpaces(tree->fmtinfo, 1);
+
+	appendStringInfoString(tree->fmtinfo, sub_fmt);
+}
+
 /*
  * Append an object parameter to a tree.
  */
 static void
-append_object_object(ObjTree *tree, char *object_name, ObjTree *value)
+append_object_object(ObjTree *tree, char *sub_fmt, ObjTree *value)
 {
 	ObjElem    *param;
+	char	   *object_name;
+
+	Assert(sub_fmt);
+
+	if (!verbose && !value->present)
+		return;
+
+	object_name = append_object_to_format_string(tree, sub_fmt);
 
 	param = new_object(ObjTypeObject, object_name);
 	param->value.object = value;
 	append_premade_object(tree, param);
 }
 
+/*
+ * Return the object name which is extracted from the input "*%{name[:.]}*"
+ * style string. And append the input format string to the ObjTree.
+ */
+static char *
+append_object_to_format_string(ObjTree *tree, char *sub_fmt)
+{
+	StringInfoData object_name;
+	const char *end_ptr, *start_ptr;
+
+	Assert(tree->fmtinfo);
+
+	initStringInfo(&object_name);
+
+	start_ptr = strchr(sub_fmt, '{');
+	end_ptr = strchr(sub_fmt, ':');
+	if (end_ptr == NULL)
+		end_ptr = strchr(sub_fmt, '}');
+
+	if (start_ptr != NULL && end_ptr != NULL)
+	{
+		appendBinaryStringInfo(&object_name, start_ptr + 1,
+								end_ptr - start_ptr - 1);
+	}
+
+	if (object_name.len == 0)
+		elog(ERROR, "object name not found");
+
+	append_format_string(tree, sub_fmt);
+
+	return object_name.data;
+}
+
 /*
  * Append a preallocated parameter to a tree.
  */
@@ -221,10 +342,17 @@ append_premade_object(ObjTree *tree, ObjElem *elem)
  * Append a string parameter to a tree.
  */
 static void
-append_string_object(ObjTree *tree, char *object_name, char *value)
+append_string_object(ObjTree *tree, char *sub_fmt, char *object_name,
+					 char *value)
 {
 	ObjElem    *param;
 
+	Assert(sub_fmt);
+
+	if (!verbose && (value == NULL || value[0] == '\0'))
+		return;
+
+	append_format_string(tree, sub_fmt);
 	param = new_object(ObjTypeString, object_name);
 	param->value.string = value;
 	append_premade_object(tree, param);
@@ -645,8 +773,6 @@ obtainConstraints(List *elements, Oid relationId)
 	{
 		Form_pg_constraint constrForm;
 		char	   *contype;
-		ObjTree    *tmp_obj;
-		bool		has_tblspc;
 
 		constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
 
@@ -677,14 +803,13 @@ obtainConstraints(List *elements, Oid relationId)
 		 *
 		 * XXX it might be useful to also list the column names in a PK, etc.
 		 */
-		constr = new_objtree_VA("CONSTRAINT %{name}I %{definition}s %{tablespace}s", 4,
+		constr = new_objtree_VA("CONSTRAINT %{name}I %{definition}s", 4,
 								"type", ObjTypeString, "constraint",
 								"contype", ObjTypeString, contype,
 								"name", ObjTypeString, NameStr(constrForm->conname),
 								"definition", ObjTypeString,
 								pg_get_constraintdef_string(constrForm->oid));
 
-		tmp_obj = new_objtree("USING INDEX TABLESPACE %{tablespace}I");
 		if (constrForm->conindid &&
 			(constrForm->contype == CONSTRAINT_PRIMARY ||
 			 constrForm->contype == CONSTRAINT_UNIQUE ||
@@ -702,16 +827,13 @@ obtainConstraints(List *elements, Oid relationId)
 									tblspc);
 				}
 
-				append_string_object(tmp_obj, "tablespace", tblspcname);
-				has_tblspc = true;
+				append_string_object(constr,
+									 "USING INDEX TABLESPACE %{tblspc}s",
+									 "tblspc",
+									 tblspcname);
 			}
 		}
 
-		if (!has_tblspc)
-			append_not_present(tmp_obj);
-
-		append_object_object(constr, "tablespace", tmp_obj);
-
 		elements = lappend(elements, new_object_object(constr));
 	}
 
@@ -775,6 +897,9 @@ RelationGetPartitionBound(Oid relid)
  * NOT NULL constraints in the column definition are emitted directly in the
  * column definition by this routine; other constraints must be emitted
  * elsewhere (the info in the parse node is incomplete anyway).
+ *
+ * Verbose syntax
+ * %{name}I %{coltype}T %{compression}s %{default}s %{not_null}s %{collation}s
  */
 static ObjTree *
 deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
@@ -809,42 +934,36 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 	get_atttypetypmodcoll(relid, attrForm->attnum,
 						  &typid, &typmod, &typcollation);
 
-	ret = new_objtree_VA("%{name}I %{coltype}T %{colstorage}s %{compression}s "
-						 "%{collation}s %{not_null}s %{default}s "
-						 "%{identity_column}s %{generated_column}s", 3,
+	ret = new_objtree_VA("%{name}I %{coltype}T", 3,
 						 "type", ObjTypeString, "column",
 						 "name", ObjTypeString, coldef->colname,
 						 "coltype", ObjTypeObject,
 						 new_objtree_for_type(typid, typmod));
+
 	/* STORAGE clause */
-	tmp_obj = new_objtree("STORAGE %{storage_form}s");
 	if (!composite)
-		append_string_object(tmp_obj, "storage_form",
+		append_string_object(ret, "STORAGE %{colstorage}s", "colstorage",
 							 storage_name(attrForm->attstorage));
-	else
-		append_not_present(tmp_obj);
-
-	append_object_object(ret, "colstorage", tmp_obj);
 
 	/* COMPRESSION clause */
-	tmp_obj = new_objtree("COMPRESSION %{compression_method}I");
+	tmp_obj = new_objtree("COMPRESSION");
 	if (coldef->compression)
-		append_string_object(tmp_obj, "compression_method",
-							 coldef->compression);
+		append_string_object(tmp_obj, "%{compression_method}I",
+							 "compression_method", coldef->compression);
 	else
-		append_not_present(tmp_obj);
+		append_not_present(tmp_obj, "%{compression_method}I");
 
-	append_object_object(ret, "compression", tmp_obj);
+	append_object_object(ret, "%{compression}s", tmp_obj);
 
-	tmp_obj = new_objtree("COLLATE %{collation_name}D");
+	tmp_obj = new_objtree("COLLATE");
 	if (OidIsValid(typcollation))
-		append_object_object(tmp_obj, "collation_name",
+		append_object_object(tmp_obj, "%{collation_name}D",
 							 new_objtree_for_qualname_id(CollationRelationId,
 														 typcollation));
 	else
-		append_not_present(tmp_obj);
+		append_not_present(tmp_obj, "%{collation_name}D");
 
-	append_object_object(ret, "collation", tmp_obj);
+	append_object_object(ret, "%{collation}s", tmp_obj);
 
 	if (!composite)
 	{
@@ -877,9 +996,10 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 		if (is_alter && coldef->is_not_null)
 			saw_notnull = true;
 
-		append_string_object(ret, "not_null", saw_notnull ? "NOT NULL" : "");
+		append_string_object(ret, "%{not_null}s", "not_null",
+							 saw_notnull ? "NOT NULL" : "");
 
-		tmp_obj = new_objtree("DEFAULT %{default}s");
+		tmp_obj = new_objtree("DEFAULT");
 		if (attrForm->atthasdef &&
 			coldef->generated != ATTRIBUTE_GENERATED_STORED)
 		{
@@ -888,12 +1008,12 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 			defstr = RelationGetColumnDefault(relation, attrForm->attnum,
 											  dpcontext);
 
-			append_string_object(tmp_obj, "default", defstr);
+			append_string_object(tmp_obj, "%{default}s", "default", defstr);
 		}
 		else
-			append_not_present(tmp_obj);
+			append_not_present(tmp_obj, "%{default}s");
 
-		append_object_object(ret, "default", tmp_obj);
+		append_object_object(ret, "%{default}s", tmp_obj);
 
 		/* IDENTITY COLUMN */
 		if (coldef->identity)
@@ -907,33 +1027,34 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 				seqrelid = RangeVarGetRelid(coldef->identitySequence, NoLock, false);
 		}
 
-		tmp_obj = new_objtree("%{identity_column}s");
+		tmp_obj = new_objtree("");
 		if (OidIsValid(seqrelid))
 		{
 			ObjTree    *tmp_obj2;
 
 			tmp_obj2 = deparse_ColumnIdentity(seqrelid, coldef->identity, is_alter);
-			append_object_object(tmp_obj, "identity_column", tmp_obj2);
+			append_object_object(tmp_obj, "%{identity_column}s", tmp_obj2);
 		}
 		else
-			append_not_present(tmp_obj);
+			append_not_present(tmp_obj, "%{identity_column}s");
 
-		append_object_object(ret, "identity_column", tmp_obj);
+		append_object_object(ret, "%{identity_column}s", tmp_obj);
 
 		/* GENERATED COLUMN EXPRESSION */
-		tmp_obj = new_objtree("GENERATED ALWAYS AS (%{generation_expr}s) STORED");
+		tmp_obj = new_objtree("GENERATED ALWAYS AS");
 		if (coldef->generated == ATTRIBUTE_GENERATED_STORED)
 		{
 			char	   *defstr;
 
 			defstr = RelationGetColumnDefault(relation, attrForm->attnum,
 											  dpcontext);
-			append_string_object(tmp_obj, "generation_expr", defstr);
+			append_string_object(tmp_obj, "(%{generation_expr}s) STORED",
+								 "generation_expr", defstr);
 		}
 		else
-			append_not_present(tmp_obj);
+			append_not_present(tmp_obj, "(%{generation_expr}s) STORED");
 
-		append_object_object(ret, "generated_column", tmp_obj);
+		append_object_object(ret, "%{generated_column}s", tmp_obj);
 	}
 
 	ReleaseSysCache(attrTup);
@@ -948,6 +1069,9 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
  * declared NOT NULL.
  *
  * As in deparse_ColumnDef, any other constraint is processed elsewhere.
+ *
+ * Verbose syntax
+ * %{name}I WITH OPTIONS %{not_null}s %{default}s.
  */
 static ObjTree *
 deparse_ColumnDef_typed(Relation relation, List *dpcontext, ColumnDef *coldef)
@@ -995,7 +1119,7 @@ deparse_ColumnDef_typed(Relation relation, List *dpcontext, ColumnDef *coldef)
 		return NULL;
 	}
 
-	tmp_obj = new_objtree("DEFAULT %{default}s");
+	tmp_obj = new_objtree("DEFAULT");
 	if (attrForm->atthasdef)
 	{
 		char	   *defstr;
@@ -1003,10 +1127,10 @@ deparse_ColumnDef_typed(Relation relation, List *dpcontext, ColumnDef *coldef)
 		defstr = RelationGetColumnDefault(relation, attrForm->attnum,
 										  dpcontext);
 
-		append_string_object(tmp_obj, "default", defstr);
+		append_string_object(tmp_obj, "%{default}s", "default", defstr);
 	}
 	else
-		append_not_present(tmp_obj);
+		append_not_present(tmp_obj, "%{default}s");
 
 	ret = new_objtree_VA("%{name}I WITH OPTIONS %{not_null}s %{default}s", 4,
 						 "type", ObjTypeString, "column",
@@ -1024,6 +1148,11 @@ deparse_ColumnDef_typed(Relation relation, List *dpcontext, ColumnDef *coldef)
 
 /*
  * Deparse the definition of column identity.
+ *
+ * Verbose syntax
+ * SET GENERATED %{option}s %{identity_type}s %{seq_definition: }s
+ * 	OR
+ * GENERATED %{option}s AS IDENTITY %{identity_type}s ( %{seq_definition: }s )
  */
 static ObjTree *
 deparse_ColumnIdentity(Oid seqrelid, char identity, bool alter_table)
@@ -1034,27 +1163,29 @@ deparse_ColumnIdentity(Oid seqrelid, char identity, bool alter_table)
 	Form_pg_sequence seqform;
 	Sequence_values *seqvalues;
 	char	   *identfmt;
+	char	   *objfmt;
 
 	if (alter_table)
 	{
-		identfmt = "SET GENERATED %{option}s";
-		ret = new_objtree("%{identity_type}s %{seq_definition: }s");
+		identfmt = "SET GENERATED ";
+		objfmt = "%{option}s";
 	}
 	else
 	{
-		identfmt = "GENERATED %{option}s AS IDENTITY";
-		ret = new_objtree("%{identity_type}s ( %{seq_definition: }s )");
+		identfmt = "GENERATED ";
+		objfmt = "%{option}s AS IDENTITY";
 	}
 
 	ident_obj = new_objtree(identfmt);
 	if (identity == ATTRIBUTE_IDENTITY_ALWAYS)
-		append_string_object(ident_obj, "option", "ALWAYS");
+		append_string_object(ident_obj, objfmt, "option", "ALWAYS");
 	else if (identity == ATTRIBUTE_IDENTITY_BY_DEFAULT)
-		append_string_object(ident_obj, "option", "BY DEFAULT");
+		append_string_object(ident_obj, objfmt, "option", "BY DEFAULT");
 	else
-		append_not_present(ident_obj);
+		append_not_present(ident_obj, objfmt);
 
-	append_object_object(ret, "identity_type", ident_obj);
+	ret = new_objtree_VA("%{identity_type}s", 1,
+						 "identity_type", ObjTypeObject, ident_obj);
 
 	seqvalues = get_sequence_values(seqrelid);
 	seqform = seqvalues->seqform;
@@ -1069,13 +1200,19 @@ deparse_ColumnIdentity(Oid seqrelid, char identity, bool alter_table)
 	elems = lappend(elems, deparse_Seq_Restart(seqvalues->last_value));
 	/* We purposefully do not emit OWNED BY here */
 
-	append_array_object(ret, "seq_definition", elems);
+	if (alter_table)
+		append_array_object(ret, "%{seq_definition: }s", elems);
+	else
+		append_array_object(ret, "( %{seq_definition: }s )", elems);
 
 	return ret;
 }
 
 /*
  * ... ALTER COLUMN ... SET/RESET (...)
+ *
+ * Verbose syntax
+ * ALTER COLUMN %{column}I RESET|SET (%{options:, }s)
  */
 static ObjTree *
 deparse_ColumnSetOptions(AlterTableCmd *subcmd)
@@ -1085,9 +1222,9 @@ deparse_ColumnSetOptions(AlterTableCmd *subcmd)
 	ObjTree    *ret;
 	bool		is_reset = subcmd->subtype == AT_ResetOptions;
 
-	ret = new_objtree_VA("ALTER COLUMN %{column}I %{is_reset}s (%{options:, }s)", 2,
+	ret = new_objtree_VA("ALTER COLUMN %{column}I %{option}s", 2,
 						 "column", ObjTypeString, subcmd->name,
-						 "is_reset", ObjTypeString, is_reset ? "RESET" : "SET");
+						 "option", ObjTypeString, is_reset ? "RESET" : "SET");
 
 	foreach(cell, (List *) subcmd->def)
 	{
@@ -1100,13 +1237,16 @@ deparse_ColumnSetOptions(AlterTableCmd *subcmd)
 	}
 
 	Assert(sets);
-	append_array_object(ret, "options", sets);
+	append_array_object(ret, "(%{options:, }s)", sets);
 
 	return ret;
 }
 
 /*
  * Deparse SET/RESET clause as used by ALTER TABLE ... SET/RESET (...)
+ *
+ * Verbose syntax
+ * RESET|SET (%{options:, }s)
  */
 static ObjTree *
 deparse_RelSetOptions(AlterTableCmd *subcmd)
@@ -1135,27 +1275,27 @@ deparse_RelSetOptions(AlterTableCmd *subcmd)
 /*
  * Deparse DefElems, as used e.g. by ALTER COLUMN ... SET, into a list of SET
  * (...)  or RESET (...) contents.
+ *
+ * Verbose syntax
+ * %{label}s = %{value}L
  */
 static ObjTree *
 deparse_DefElem(DefElem *elem, bool is_reset)
 {
 	ObjTree    *ret;
-	ObjTree    *optname;
+	ObjTree    *optname = new_objtree("");
 
 	if (elem->defnamespace != NULL)
-		optname = new_objtree_VA("%{schema}I.%{label}I", 1,
-								 "schema", ObjTypeString, elem->defnamespace,
-								 "label", ObjTypeString, elem->defname);
-	else
-		optname = new_objtree_VA("%{label}I", 1,
-								 "label", ObjTypeString, elem->defname);
+		append_string_object(optname, "%{schema}I.", "schema",
+							 elem->defnamespace);
 
-	if (is_reset)
-		ret = new_objtree_VA("%{label}s", 1, "label", ObjTypeObject, optname);
-	else
-		ret = new_objtree_VA("%{label}s = %{value}L", 2,
-							 "label", ObjTypeObject, optname,
-							 "value", ObjTypeString,
+	append_string_object(optname, "%{label}I", "label", elem->defname);
+
+	ret = new_objtree_VA("%{label}s", 1,
+						 "label", ObjTypeObject, optname);
+
+	if (!is_reset)
+		append_string_object(ret, "= %{value}L", "value",
 							 elem->arg ? defGetString(elem) :
 							 defGetBoolean(elem) ? "TRUE" : "FALSE");
 
@@ -1205,27 +1345,33 @@ deparse_InhRelations(Oid objectId)
 
 /*
  * Deparse the ON COMMIT ... clause for CREATE ... TEMPORARY ...
+ *
+ * Verbose syntax
+ * ON COMMIT %{on_commit_value}s
  */
 static ObjTree *
 deparse_OnCommitClause(OnCommitAction option)
 {
-	ObjTree    *ret  = new_objtree("ON COMMIT %{on_commit_value}s");
+	ObjTree    *ret  = new_objtree("ON COMMIT");
 	switch (option)
 	{
 		case ONCOMMIT_DROP:
-			append_string_object(ret, "on_commit_value", "DROP");
+			append_string_object(ret, "%{on_commit_value}s",
+								 "on_commit_value", "DROP");
 			break;
 
 		case ONCOMMIT_DELETE_ROWS:
-			append_string_object(ret, "on_commit_value", "DELETE ROWS");
+			append_string_object(ret, "%{on_commit_value}s",
+								 "on_commit_value", "DELETE ROWS");
 			break;
 
 		case ONCOMMIT_PRESERVE_ROWS:
-			append_string_object(ret, "on_commit_value", "PRESERVE ROWS");
+			append_string_object(ret, "%{on_commit_value}s",
+								 "on_commit_value", "PRESERVE ROWS");
 			break;
 
 		case ONCOMMIT_NOOP:
-			append_not_present(ret);
+			append_not_present(ret, "%{on_commit_value}s");
 			break;
 	}
 
@@ -1234,6 +1380,11 @@ deparse_OnCommitClause(OnCommitAction option)
 
 /*
  * Deparse the sequence CACHE option.
+ *
+ * Verbose syntax
+ * SET CACHE %{value}s
+ * OR
+ * CACHE %{value}
  */
 static inline ObjElem *
 deparse_Seq_Cache(Form_pg_sequence seqdata, bool alter_table)
@@ -1253,6 +1404,11 @@ deparse_Seq_Cache(Form_pg_sequence seqdata, bool alter_table)
 
 /*
  * Deparse the sequence CYCLE option.
+ *
+ * Verbose syntax
+ * SET %{no}s CYCLE
+ * OR
+ * %{no}s CYCLE
  */
 static inline ObjElem *
 deparse_Seq_Cycle(Form_pg_sequence seqdata, bool alter_table)
@@ -1272,6 +1428,11 @@ deparse_Seq_Cycle(Form_pg_sequence seqdata, bool alter_table)
 
 /*
  * Deparse the sequence INCREMENT BY option.
+ *
+ * Verbose syntax
+ * SET INCREMENT BY %{value}s
+ * OR
+ * INCREMENT BY %{value}s
  */
 static inline ObjElem *
 deparse_Seq_IncrementBy(Form_pg_sequence seqdata, bool alter_table)
@@ -1291,6 +1452,11 @@ deparse_Seq_IncrementBy(Form_pg_sequence seqdata, bool alter_table)
 
 /*
  * Deparse the sequence MAXVALUE option.
+ *
+ * Verbose syntax
+ * SET MAXVALUE %{value}s
+ * OR
+ * MAXVALUE %{value}s
  */
 static inline ObjElem *
 deparse_Seq_Maxvalue(Form_pg_sequence seqdata, bool alter_table)
@@ -1310,6 +1476,11 @@ deparse_Seq_Maxvalue(Form_pg_sequence seqdata, bool alter_table)
 
 /*
  * Deparse the sequence MINVALUE option.
+ *
+ * Verbose syntax
+ * SET MINVALUE %{value}s
+ * OR
+ * MINVALUE %{value}s
  */
 static inline ObjElem *
 deparse_Seq_Minvalue(Form_pg_sequence seqdata, bool alter_table)
@@ -1329,6 +1500,9 @@ deparse_Seq_Minvalue(Form_pg_sequence seqdata, bool alter_table)
 
 /*
  * Deparse the sequence OWNED BY command.
+ *
+ * Verbose syntax
+ * OWNED BY %{owner}D
  */
 static ObjElem *
 deparse_Seq_OwnedBy(Oid sequenceId, bool alter_table)
@@ -1378,7 +1552,7 @@ deparse_Seq_OwnedBy(Oid sequenceId, bool alter_table)
 			continue;
 
 		tmp_obj = new_objtree_for_qualname_id(RelationRelationId, ownerId);
-		append_string_object(tmp_obj, "attrname", colname);
+		append_string_object(tmp_obj, "attrname", "attrname", colname);
 		ret = new_objtree_VA("OWNED BY %{owner}D", 2,
 							 "clause", ObjTypeString, "owned",
 							 "owner", ObjTypeObject, tmp_obj);
@@ -1403,6 +1577,9 @@ deparse_Seq_OwnedBy(Oid sequenceId, bool alter_table)
 
 /*
  * Deparse the sequence RESTART option.
+ *
+ * Verbose syntax
+ * RESTART %{value}s
  */
 static inline ObjElem *
 deparse_Seq_Restart(int64 last_value)
@@ -1419,24 +1596,32 @@ deparse_Seq_Restart(int64 last_value)
 
 /*
  * Deparse the sequence AS option.
+ *
+ * Verbose syntax
+ * AS %{seqtype}T
  */
 static inline ObjElem *
 deparse_Seq_As(Form_pg_sequence seqdata)
 {
 	ObjTree    *ret;
 
-	ret = new_objtree("AS %{seqtype}T");
+	ret = new_objtree("AS");
 	if (OidIsValid(seqdata->seqtypid))
-		append_object_object(ret, "seqtype",
+		append_object_object(ret, "%{seqtype}T",
 							 new_objtree_for_type(seqdata->seqtypid, -1));
 	else
-		append_not_present(ret);
+		append_not_present(ret, "%{seqtype}T");
 
 	return new_object_object(ret);
 }
 
 /*
  * Deparse the sequence START WITH option.
+ *
+ * Verbose syntax
+ * SET START WITH %{value}s
+ * OR
+ * START WITH %{value}s
  */
 static inline ObjElem *
 deparse_Seq_Startwith(Form_pg_sequence seqdata, bool alter_table)
@@ -1509,6 +1694,9 @@ deparse_TableElements(Relation relation, List *tableElements, List *dpcontext,
  * commands. For example, When creating a table, if we specify a column as a
  * sequence type, then we will create a sequence for that column and set that
  * sequence OWNED BY the table.
+ *
+ * Verbose syntax
+ * CREATE %{persistence}s SEQUENCE %{identity}D %{definition: }s
  */
 static ObjTree *
 deparse_CreateSeqStmt(Oid objectId, Node *parsetree)
@@ -1564,6 +1752,12 @@ deparse_CreateSeqStmt(Oid objectId, Node *parsetree)
  *
  * Given a table OID and the parse tree that created it, return an ObjTree
  * representing the creation command.
+ *
+ * Verbose syntax
+ * CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D [OF
+ * %{of_type}T | PARTITION OF %{parent_identity}D] %{table_elements}s
+ * %{inherits}s %{partition_by}s %{access_method}s %{with_clause}s
+ * %{on_commit}s %{tablespace}s
  */
 static ObjTree *
 deparse_CreateStmt(Oid objectId, Node *parsetree)
@@ -1575,23 +1769,8 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 	ObjTree    *tmp_obj;
 	List	   *list = NIL;
 	ListCell   *cell;
-	char	   *fmtstr;
-
-	if (node->ofTypename)
-		fmtstr = "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D "
-			"%{of_type}s %{table_elements}s %{partition_by}s "
-			"%{access_method}s %{with_clause}s %{on_commit}s %{tablespace}s";
-	else if (node->partbound)
-		fmtstr = "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D "
-			"%{parent_identity}s %{table_elements}s %{partition_bound}s "
-			"%{partition_by}s %{access_method}s %{with_clause}s %{on_commit}s "
-			"%{tablespace}s";
-	else
-		fmtstr = "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D "
-			"(%{table_elements:, }s) %{inherits}s %{partition_by}s "
-			"%{access_method}s %{with_clause}s %{on_commit}s %{tablespace}s";
 
-	ret = new_objtree_VA(fmtstr, 3,
+	ret = new_objtree_VA("CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D", 3,
 						 "persistence", ObjTypeString,
 						 get_persistence_str(relation->rd_rel->relpersistence),
 						 "if_not_exists", ObjTypeString,
@@ -1621,10 +1800,8 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 		 */
 		if (node->ofTypename)
 		{
-			tmp_obj = new_objtree_VA("OF %{of_type}T", 1,
-									 "of_type", ObjTypeObject,
-									 new_objtree_for_type(relation->rd_rel->reloftype, -1));
-			append_object_object(ret, "of_type", tmp_obj);
+			tmp_obj = new_objtree_for_type(relation->rd_rel->reloftype, -1);
+			append_object_object(ret, "OF %{of_type}T", tmp_obj);
 		}
 		else
 		{
@@ -1636,11 +1813,10 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 
 			Assert(list_length(parents) == 1);
 
-			tmp_obj = new_objtree_VA("PARTITION OF %{parent_identity}D", 1,
-									 "parent_identity", ObjTypeObject,
-									 elem->value.object);
+			append_format_string(ret, "PARTITION OF");
 
-			append_object_object(ret, "parent_identity", tmp_obj);
+			append_object_object(ret, "%{parent_identity}D",
+								 elem->value.object);
 		}
 
 		tableelts = deparse_TableElements(relation, node->tableElts, dpcontext,
@@ -1648,13 +1824,13 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 										  false);	/* not composite */
 		tableelts = obtainConstraints(tableelts, objectId);
 
-		tmp_obj = new_objtree("(%{elements:, }s)");
+		tmp_obj = new_objtree("");
 		if (tableelts)
-			append_array_object(tmp_obj, "elements", tableelts);
+			append_array_object(tmp_obj, "(%{elements:, }s)", tableelts);
 		else
-			append_not_present(tmp_obj);
+			append_not_present(tmp_obj, "(%{elements:, }s)");
 
-		append_object_object(ret, "table_elements", tmp_obj);
+		append_object_object(ret, "%{table_elements}s", tmp_obj);
 	}
 	else
 	{
@@ -1676,7 +1852,10 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 										  false);	/* not composite */
 		tableelts = obtainConstraints(tableelts, objectId);
 
-		append_array_object(ret, "table_elements", tableelts);
+		if (tableelts)
+			append_array_object(ret, "(%{table_elements:, }s)", tableelts);
+		else
+			append_format_string(ret, "()");
 
 		/*
 		 * Add inheritance specification.  We cannot simply scan the list of
@@ -1685,13 +1864,14 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 		 * re-resolve them from the information in the parse node, it seems
 		 * more accurate and convenient to grab it from pg_inherits.
 		 */
-		tmp_obj = new_objtree("INHERITS (%{parents:, }D)");
+		tmp_obj = new_objtree("INHERITS");
 		if (node->inhRelations != NIL)
-			append_array_object(tmp_obj, "parents", deparse_InhRelations(objectId));
+			append_array_object(tmp_obj, "(%{parents:, }D)",
+								deparse_InhRelations(objectId));
 		else
-			append_not_present(tmp_obj);
+			append_not_present(tmp_obj, "(%{parents:, }D)");
 
-		append_object_object(ret, "inherits", tmp_obj);
+		append_object_object(ret, "%{inherits}s", tmp_obj);
 	}
 
 	/* FOR VALUES clause */
@@ -1702,31 +1882,32 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 		 * directly as it's the original partbound expression which haven't
 		 * been transformed.
 		 */
-		append_string_object(ret, "partition_bound",
+		append_string_object(ret, "%{partition_bound}s", "partition_bound",
 							 RelationGetPartitionBound(objectId));
 	}
 
 	/* PARTITION BY clause */
-	tmp_obj = new_objtree("PARTITION BY %{definition}s");
+	tmp_obj = new_objtree("PARTITION BY");
 	if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-		append_string_object(tmp_obj, "definition",
+		append_string_object(tmp_obj, "%{definition}s", "definition",
 							 pg_get_partkeydef_string(objectId));
 	else
-		append_not_present(tmp_obj);
+		append_not_present(tmp_obj, "%{definition}s");
 
-	append_object_object(ret, "partition_by", tmp_obj);
+	append_object_object(ret, "%{partition_by}s", tmp_obj);
 
 	/* USING clause */
-	tmp_obj = new_objtree("USING %{access_method}I");
+	tmp_obj = new_objtree("USING");
 	if (node->accessMethod)
-		append_string_object(tmp_obj, "access_method", node->accessMethod);
+		append_string_object(tmp_obj, "%{access_method}I", "access_method",
+							 node->accessMethod);
 	else
-		append_not_present(tmp_obj);
+		append_not_present(tmp_obj, "%{access_method}I");
 
-	append_object_object(ret, "access_method", tmp_obj);
+	append_object_object(ret, "%{access_method}s", tmp_obj);
 
 	/* WITH clause */
-	tmp_obj = new_objtree("WITH (%{with:, }s)");
+	tmp_obj = new_objtree("WITH");
 
 	foreach(cell, node->options)
 	{
@@ -1738,23 +1919,23 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 	}
 
 	if (list)
-		append_array_object(tmp_obj, "with", list);
+		append_array_object(tmp_obj, "(%{with:, }s)", list);
 	else
-		append_not_present(tmp_obj);
+		append_not_present(tmp_obj, "(%{with:, }s)");
 
-	append_object_object(ret, "with_clause", tmp_obj);
+	append_object_object(ret, "%{with_clause}s", tmp_obj);
 
-	append_object_object(ret, "on_commit",
+	append_object_object(ret, "%{on_commit}s",
 						 deparse_OnCommitClause(node->oncommit));
 
-	tmp_obj = new_objtree("TABLESPACE %{tablespace}I");
+	tmp_obj = new_objtree("TABLESPACE");
 	if (node->tablespacename)
-		append_string_object(tmp_obj, "tablespace",
+		append_string_object(tmp_obj, "%{tablespace}I", "tablespace",
 							 node->tablespacename);
 	else
-		append_not_present(tmp_obj);
+		append_not_present(tmp_obj, "%{tablespace}I");
 
-	append_object_object(ret, "tablespace", tmp_obj);
+	append_object_object(ret, "%{tablespace}s", tmp_obj);
 
 	relation_close(relation, AccessShareLock);
 
@@ -1784,6 +1965,9 @@ deparse_CreateTableAsStmt(CollectedCommand *cmd)
 /*
  * Deparse all the collected subcommands and return an ObjTree representing the
  * alter command.
+ *
+ * Verbose syntax
+ * ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s
  */
 static ObjTree *
 deparse_AlterRelation(CollectedCommand *cmd)
@@ -1835,7 +2019,7 @@ deparse_AlterRelation(CollectedCommand *cmd)
 	dpcontext = deparse_context_for(RelationGetRelationName(rel),
 									relId);
 
-	ret = new_objtree_VA("ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", 3,
+	ret = new_objtree_VA("ALTER %{objtype}s %{only}s %{identity}D", 3,
 						 "objtype", ObjTypeString, reltype,
 						 "only", ObjTypeString,
 						 stmt->relation->inh ? "" : "ONLY",
@@ -1968,7 +2152,7 @@ deparse_AlterRelation(CollectedCommand *cmd)
 					HeapTuple	attrtup;
 					AttrNumber	attno;
 
-					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET DEFAULT %{definition}s", 2,
+					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET DEFAULT", 2,
 											"type", ObjTypeString, "set default",
 											"column", ObjTypeString, subcmd->name);
 
@@ -1976,7 +2160,7 @@ deparse_AlterRelation(CollectedCommand *cmd)
 														RelationGetRelid(rel));
 					attrtup = SearchSysCacheAttName(RelationGetRelid(rel), subcmd->name);
 					attno = ((Form_pg_attribute) GETSTRUCT(attrtup))->attnum;
-					append_string_object(tmp_obj, "definition",
+					append_string_object(tmp_obj, "%{definition}s", "definition",
 										 RelationGetColumnDefault(rel, attno,
 																  dpcontext_rel));
 					ReleaseSysCache(attrtup);
@@ -2064,7 +2248,7 @@ deparse_AlterRelation(CollectedCommand *cmd)
 				break;
 
 			case AT_DropColumn:
-				tmp_obj = new_objtree_VA("DROP %{objtype}s %{if_exists}s %{column}I %{cascade}s", 4,
+				tmp_obj = new_objtree_VA("DROP %{objtype}s %{if_exists}s %{column}I", 4,
 										"objtype", ObjTypeString, "COLUMN",
 										"type", ObjTypeString, "drop column",
 										"if_exists", ObjTypeString,
@@ -2072,8 +2256,8 @@ deparse_AlterRelation(CollectedCommand *cmd)
 										"column", ObjTypeString, subcmd->name);
 				tmp_obj2 = new_objtree("CASCADE");
 				if (subcmd->behavior != DROP_CASCADE)
-					append_not_present(tmp_obj2);
-				append_object_object(tmp_obj, "cascade", tmp_obj2);
+					append_not_present(tmp_obj2, NULL);
+				append_object_object(tmp_obj, "%{cascade}s", tmp_obj2);
 
 				subcmds = lappend(subcmds, new_object_object(tmp_obj));
 				break;
@@ -2179,7 +2363,7 @@ deparse_AlterRelation(CollectedCommand *cmd)
 					def = (ColumnDef *) subcmd->def;
 					Assert(IsA(def, ColumnDef));
 
-					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET DATA TYPE %{datatype}T %{collation}s %{using}s", 3,
+					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET DATA TYPE %{datatype}T", 3,
 											"type", ObjTypeString, "alter column type",
 											"column", ObjTypeString, subcmd->name,
 											"datatype", ObjTypeObject,
@@ -2187,33 +2371,33 @@ deparse_AlterRelation(CollectedCommand *cmd)
 																 att->atttypmod));
 
 					/* Add a COLLATE clause, if needed */
-					tmp_obj2 = new_objtree("COLLATE %{name}D");
+					tmp_obj2 = new_objtree("COLLATE");
 					if (OidIsValid(att->attcollation))
 					{
 						ObjTree    *collname;
 
 						collname = new_objtree_for_qualname_id(CollationRelationId,
 															   att->attcollation);
-						append_object_object(tmp_obj2, "name", collname);
+						append_object_object(tmp_obj2, "%{name}D", collname);
 					}
 					else
-						append_not_present(tmp_obj2);
+						append_not_present(tmp_obj2, "%{name}D");
 
-					append_object_object(tmp_obj, "collation", tmp_obj2);
+					append_object_object(tmp_obj, "%{collation}s", tmp_obj2);
 
 					/*
 					 * If there's a USING clause, transformAlterTableStmt ran
 					 * it through transformExpr and stored the resulting node
 					 * in cooked_default, which we can use here.
 					 */
-					tmp_obj2 = new_objtree("USING %{expression}s");
+					tmp_obj2 = new_objtree("USING");
 					if (def->raw_default)
-						append_string_object(tmp_obj2, "expression",
-											 sub->usingexpr);
+						append_string_object(tmp_obj2, "%{expression}s",
+											 "expression", sub->usingexpr);
 					else
-						append_not_present(tmp_obj2);
+						append_not_present(tmp_obj2, "%{expression}s");
 
-					append_object_object(tmp_obj, "using", tmp_obj2);
+					append_object_object(tmp_obj, "%{using}s", tmp_obj2);
 
 					subcmds = lappend(subcmds, new_object_object(tmp_obj));
 				}
@@ -2396,24 +2580,27 @@ deparse_AlterRelation(CollectedCommand *cmd)
 				break;
 
 			case AT_ReplicaIdentity:
-				tmp_obj = new_objtree_VA("REPLICA IDENTITY %{ident}s", 1,
+				tmp_obj = new_objtree_VA("REPLICA IDENTITY", 1,
 										"type", ObjTypeString, "replica identity");
 				switch (((ReplicaIdentityStmt *) subcmd->def)->identity_type)
 				{
 					case REPLICA_IDENTITY_DEFAULT:
-						append_string_object(tmp_obj, "ident", "DEFAULT");
+						append_string_object(tmp_obj, "%{ident}s", "ident",
+											 "DEFAULT");
 						break;
 					case REPLICA_IDENTITY_FULL:
-						append_string_object(tmp_obj, "ident", "FULL");
+						append_string_object(tmp_obj, "%{ident}s", "ident",
+											 "FULL");
 						break;
 					case REPLICA_IDENTITY_NOTHING:
-						append_string_object(tmp_obj, "ident", "NOTHING");
+						append_string_object(tmp_obj, "%{ident}s", "ident",
+											 "NOTHING");
 						break;
 					case REPLICA_IDENTITY_INDEX:
 						tmp_obj2 = new_objtree_VA("USING INDEX %{index}I", 1,
 												 "index", ObjTypeString,
 												 ((ReplicaIdentityStmt *) subcmd->def)->name);
-						append_object_object(tmp_obj, "ident", tmp_obj2);
+						append_object_object(tmp_obj, "%{ident}s", tmp_obj2);
 						break;
 				}
 				subcmds = lappend(subcmds, new_object_object(tmp_obj));
@@ -2437,20 +2624,21 @@ deparse_AlterRelation(CollectedCommand *cmd)
 				break;
 #endif
 			case AT_AttachPartition:
-				tmp_obj = new_objtree_VA("ATTACH PARTITION %{partition_identity}D %{partition_bound}s",
+				tmp_obj = new_objtree_VA("ATTACH PARTITION %{partition_identity}D",
 										2, "type", ObjTypeString, "attach partition",
 										"partition_identity", ObjTypeObject,
 										new_objtree_for_qualname_id(RelationRelationId,
 																	sub->address.objectId));
 
-				tmp_obj2 = new_objtree("%{partition_bound}s");
+				tmp_obj2 = new_objtree("");
 				if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-					append_string_object(tmp_obj2, "partition_bound",
+					append_string_object(tmp_obj, "%{partition_bound}s",
+										 "partition_bound",
 										 RelationGetPartitionBound(sub->address.objectId));
 				else
-					append_not_present(tmp_obj2);
+					append_not_present(tmp_obj2, "%{partition_bound}s");
 
-				append_object_object(tmp_obj, "partition_bound", tmp_obj2);
+				append_object_object(tmp_obj, "%{partition_bound}s", tmp_obj2);
 
 				subcmds = lappend(subcmds, new_object_object(tmp_obj));
 				break;
@@ -2487,23 +2675,23 @@ deparse_AlterRelation(CollectedCommand *cmd)
 					ObjTree    *seqdef;
 					ColumnDef  *coldef = (ColumnDef *) subcmd->def;
 
-					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I %{definition}s", 2,
+					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I", 2,
 											"type", ObjTypeString, "add identity",
 											"column", ObjTypeString, subcmd->name);
 
 					attnum = get_attnum(RelationGetRelid(rel), subcmd->name);
 					seq_relid = getIdentitySequence(RelationGetRelid(rel), attnum, true);
 
-					tmp_obj2 = new_objtree("ADD %{identity_column}s");
+					tmp_obj2 = new_objtree("ADD");
 					if (OidIsValid(seq_relid))
 					{
 						seqdef = deparse_ColumnIdentity(seq_relid, coldef->identity, false);
-						append_object_object(tmp_obj2, "identity_column", seqdef);
+						append_object_object(tmp_obj2, "%{identity_column}s", seqdef);
 					}
 					else
-						append_not_present(tmp_obj2);
+						append_not_present(tmp_obj2, "%{identity_column}s");
 
-					append_object_object(tmp_obj, "definition", tmp_obj2);
+					append_object_object(tmp_obj, "%{definition}s", tmp_obj2);
 
 					subcmds = lappend(subcmds, new_object_object(tmp_obj));
 				}
@@ -2517,7 +2705,7 @@ deparse_AlterRelation(CollectedCommand *cmd)
 					Oid			seq_relid;
 
 
-					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I %{definition}s", 2,
+					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I", 2,
 											"type", ObjTypeString, "set identity",
 											"column", ObjTypeString, subcmd->name);
 
@@ -2534,26 +2722,28 @@ deparse_AlterRelation(CollectedCommand *cmd)
 					attnum = get_attnum(RelationGetRelid(rel), subcmd->name);
 					seq_relid = getIdentitySequence(RelationGetRelid(rel), attnum, true);
 
-					tmp_obj2 = new_objtree("%{definition}s");
+					tmp_obj2 = new_objtree("");
 					if (OidIsValid(seq_relid))
 					{
 						seqdef = deparse_ColumnIdentity(seq_relid, identity, true);
-						append_object_object(tmp_obj2, "definition", seqdef);
+						append_object_object(tmp_obj2, "%{definition}s", seqdef);
 					}
 					else
-						append_not_present(tmp_obj2);
+						append_not_present(tmp_obj2, "%{definition}s");
 
-					append_object_object(tmp_obj, "definition", tmp_obj2);
+					append_object_object(tmp_obj, "%{definition}s", tmp_obj2);
 
 					subcmds = lappend(subcmds, new_object_object(tmp_obj));
 					break;
 				}
 			case AT_DropIdentity:
-				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I DROP IDENTITY %{if_exists}s", 3,
+				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I DROP IDENTITY", 2,
 										"type", ObjTypeString, "drop identity",
-										"column", ObjTypeString, subcmd->name,
-										"if_exists", ObjTypeString,
-										subcmd->missing_ok ? "IF EXISTS" : "");
+										"column", ObjTypeString, subcmd->name);
+
+				append_string_object(tmp_obj, "%{if_exists}s",
+									 "if_exists",
+									 subcmd->missing_ok ? "IF EXISTS" : "");
 
 				subcmds = lappend(subcmds, new_object_object(tmp_obj));
 				break;
@@ -2569,13 +2759,16 @@ deparse_AlterRelation(CollectedCommand *cmd)
 	if (list_length(subcmds) == 0)
 		return NULL;
 
-	append_array_object(ret, "subcmds", subcmds);
+	append_array_object(ret, "%{subcmds:, }s", subcmds);
 
 	return ret;
 }
 
 /*
  * Handle deparsing of DROP commands.
+ *
+ * Verbose syntax
+ * DROP %{objtype}s %{concurrently}s %{if_exists}s %{objidentity}s %{cascade}s
  */
 char *
 deparse_drop_command(const char *objidentity, const char *objecttype,
@@ -2591,7 +2784,7 @@ deparse_drop_command(const char *objidentity, const char *objecttype,
 
 	initStringInfo(&str);
 
-	stmt = new_objtree_VA("DROP %{objtype}s %{concurrently}s %{if_exists}s %{objidentity}s %{cascade}s", 4,
+	stmt = new_objtree_VA("DROP %{objtype}s %{concurrently}s %{if_exists}s %{objidentity}s", 4,
 						  "objtype", ObjTypeString, objecttype,
 						  "concurrently", ObjTypeString,
 						  node->concurrent ? "CONCURRENTLY" : "",
@@ -2601,8 +2794,8 @@ deparse_drop_command(const char *objidentity, const char *objecttype,
 
 	tmp_obj = new_objtree("CASCADE");
 	if (node->behavior != DROP_CASCADE)
-		append_not_present(tmp_obj);
-	append_object_object(stmt, "cascade", tmp_obj);
+		append_not_present(tmp_obj, NULL);
+	append_object_object(stmt, "%{cascade}s", tmp_obj);
 
 	jsonb = objtree_to_jsonb(stmt);
 	command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
@@ -2615,6 +2808,9 @@ deparse_drop_command(const char *objidentity, const char *objecttype,
  *
  * Given the object address and the parse tree that created it, return an
  * ObjTree representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER %{objtype}s %{identity}s SET SCHEMA %{newschema}I
  */
 static ObjTree *
 deparse_AlterObjectSchemaStmt(ObjectAddress address, Node *parsetree,
@@ -2656,6 +2852,9 @@ deparse_AlterObjectSchemaStmt(ObjectAddress address, Node *parsetree,
  *
  * Given the object address and the parse tree that created it, return an
  * ObjTree representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER %{objtype}s %{identity}s OWNER TO %{newowner}I
  */
 static ObjTree *
 deparse_AlterOwnerStmt(ObjectAddress address, Node *parsetree)
@@ -2681,6 +2880,9 @@ deparse_AlterOwnerStmt(ObjectAddress address, Node *parsetree)
  * For example, When creating a table, if we specify a column as a sequence
  * type, then we will create a sequence for that column and set that sequence
  * OWNED BY the table.
+ *
+ * Verbose syntax
+ * ALTER SEQUENCE %{identity}D %{definition: }s
  */
 static ObjTree *
 deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
@@ -2747,6 +2949,13 @@ deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
 
 /*
  * Deparse a RenameStmt.
+ *
+ * Verbose syntax
+ * ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I
+ * OR
+ * ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I
+ * OR
+ * ALTER %{objtype}s %{if_exists}s %{only}s %{identity}D RENAME COLUMN %{colname}I TO %{newname}I %{cascade}s
  */
 static ObjTree *
 deparse_RenameStmt(ObjectAddress address, Node *parsetree)
@@ -2755,7 +2964,6 @@ deparse_RenameStmt(ObjectAddress address, Node *parsetree)
 	ObjTree    *ret;
 	Relation	relation;
 	Oid			schemaId;
-	ObjTree    *tmp_obj;
 
 	/*
 	 * In an ALTER .. RENAME command, we don't have the original name of the
@@ -2811,27 +3019,36 @@ deparse_RenameStmt(ObjectAddress address, Node *parsetree)
 			relation = relation_open(address.objectId, AccessShareLock);
 			schemaId = RelationGetNamespace(relation);
 
-			ret = new_objtree_VA("ALTER %{objtype}s %{if_exists}s %{only}s %{identity}D RENAME COLUMN %{colname}I TO %{newname}I %{cascade}s", 5,
+			ret = new_objtree_VA("ALTER %{objtype}s", 1,
 								 "objtype", ObjTypeString,
-								 stringify_objtype(node->relationType, false),
-								 "only", ObjTypeString, node->relation->inh ? "" : "ONLY",
-								 "identity", ObjTypeObject,
-								 new_objtree_for_qualname(schemaId, node->relation->relname),
-								 "colname", ObjTypeString, node->subname,
-								 "newname", ObjTypeString, node->newname
-								 );
+								 stringify_objtype(node->relationType, false));
 
 			/* Composite types do not support IF EXISTS */
-			tmp_obj = new_objtree("IF EXISTS");
-			if (node->renameType != OBJECT_COLUMN || !node->missing_ok)
-				append_not_present(tmp_obj);
-			append_object_object(ret, "if_exists", tmp_obj);
+			if (node->renameType == OBJECT_COLUMN)
+				append_string_object(ret, "%{if_exists}s",
+									 "if_exists",
+									 node->missing_ok ? "IF EXISTS" : "");
+			if (!node->relation->inh)
+				append_string_object(ret, "%{only}s",
+									 "only",
+									 "ONLY");
+			append_object_object(ret, "%{identity}D",
+								 new_objtree_for_qualname(schemaId,
+														  node->relation->relname));
+			append_string_object(ret, "RENAME COLUMN %{colname}I",
+								 "colname", node->subname);
+
+			append_string_object(ret, "TO %{newname}I", "newname",
+								 node->newname);
 
-			tmp_obj = new_objtree("CASCADE");
-			if (node->renameType != OBJECT_ATTRIBUTE ||
-				node->behavior != DROP_CASCADE)
-				append_not_present(tmp_obj);
-			append_object_object(ret, "cascade", tmp_obj);
+			if (node->renameType == OBJECT_ATTRIBUTE)
+			{
+				ObjTree	   *tmp_obj = new_objtree("CASCADE");
+
+				if (node->behavior != DROP_CASCADE)
+					append_not_present(tmp_obj, NULL);
+				append_object_object(ret, "%{cascade}s", tmp_obj);
+			}
 
 			relation_close(relation, AccessShareLock);
 			break;
@@ -2897,7 +3114,7 @@ deparse_simple_command(CollectedCommand *cmd)
  * Workhorse to deparse a CollectedCommand.
  */
 char *
-deparse_utility_command(CollectedCommand *cmd)
+deparse_utility_command(CollectedCommand *cmd, bool verbose_mode)
 {
 	OverrideSearchPath *overridePath;
 	MemoryContext oldcxt;
@@ -2933,6 +3150,8 @@ deparse_utility_command(CollectedCommand *cmd)
 	overridePath->addTemp = true;
 	PushOverrideSearchPath(overridePath);
 
+	verbose = verbose_mode;
+
 	switch (cmd->type)
 	{
 		case SCT_Simple:
@@ -2980,7 +3199,7 @@ ddl_deparse_to_json(PG_FUNCTION_ARGS)
 	CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
 	char	   *command;
 
-	command = deparse_utility_command(cmd);
+	command = deparse_utility_command(cmd, true);
 
 	if (command)
 		PG_RETURN_TEXT_P(cstring_to_text(command));
diff --git a/src/include/tcop/ddldeparse.h b/src/include/tcop/ddldeparse.h
index 15481dc3c4..a712060246 100644
--- a/src/include/tcop/ddldeparse.h
+++ b/src/include/tcop/ddldeparse.h
@@ -14,7 +14,7 @@
 
 #include "tcop/deparse_utility.h"
 
-extern char *deparse_utility_command(CollectedCommand *cmd);
+extern char *deparse_utility_command(CollectedCommand *cmd, bool verbose_mode);
 extern char *deparse_ddl_json_to_string(char *jsonb);
 extern char *deparse_drop_command(const char *objidentity, const char *objecttype,
 								  Node *parsetree);
-- 
2.34.1

