0002-Deparser-for-Alter-Table-DDL-commands-2023_06_30.patch

application/octet-stream

Filename: 0002-Deparser-for-Alter-Table-DDL-commands-2023_06_30.patch
Type: application/octet-stream
Part: 1
Message: Re: Support logical replication of DDLs

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch 0002
Subject: Deparser for Alter Table DDL commands.
File+
src/backend/commands/ddldeparse.c 1664 41
src/backend/tcop/utility.c 17 0
src/include/tcop/utility.h 2 0
From af8d4a38c411d8d92c6c5fd275fde7e2a85209fc Mon Sep 17 00:00:00 2001
From: Shveta Malik <shveta.malik@gmail.com>
Date: Wed, 28 Jun 2023 14:37:02 +0530
Subject: [PATCH 2/5] Deparser for Alter Table DDL commands.

This patch constructs JSON blobs representing the Alter Table
DDL commands which can later be re-processed into plain strings
by well-defined sprintf-like expansion.
---
 src/backend/commands/ddldeparse.c | 1705 ++++++++++++++++++++++++++++-
 src/backend/tcop/utility.c        |   17 +
 src/include/tcop/utility.h        |    2 +
 3 files changed, 1683 insertions(+), 41 deletions(-)

diff --git a/src/backend/commands/ddldeparse.c b/src/backend/commands/ddldeparse.c
index c8e885ca1a..cb024b45b8 100644
--- a/src/backend/commands/ddldeparse.c
+++ b/src/backend/commands/ddldeparse.c
@@ -363,11 +363,16 @@ insert_identity_object(JsonbParseState *state, Oid nspid, char *relname)
  * CACHE %{value}
  */
 static inline void
-deparse_Seq_Cache(JsonbParseState *state, Form_pg_sequence seqdata)
+deparse_Seq_Cache(JsonbParseState *state, Form_pg_sequence seqdata,
+				  bool alter_table)
 {
+	char	   *fmt;
+
+	fmt = alter_table ? "SET CACHE %{value}s" : "CACHE %{value}s";
+
 	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 	new_jsonb_VA(state, 3,
-				 "fmt", jbvString, "CACHE %{value}s",
+				 "fmt", jbvString, fmt,
 				 "clause", jbvString, "cache",
 				 "value", jbvString,
 				 psprintf(INT64_FORMAT, seqdata->seqcache));
@@ -378,10 +383,13 @@ deparse_Seq_Cache(JsonbParseState *state, Form_pg_sequence seqdata)
  * Deparse the sequence CYCLE option to Jsonb.
  *
  * Verbose syntax
+ * SET %{no}s CYCLE
+ * OR
  * %{no}s CYCLE
  */
 static inline void
-deparse_Seq_Cycle(JsonbParseState *state, Form_pg_sequence seqdata)
+deparse_Seq_Cycle(JsonbParseState *state, Form_pg_sequence seqdata,
+				  bool alter_table)
 {
 	StringInfoData fmtStr;
 
@@ -389,6 +397,9 @@ deparse_Seq_Cycle(JsonbParseState *state, Form_pg_sequence seqdata)
 
 	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
+	if (alter_table)
+		appendStringInfoString(&fmtStr, "SET ");
+
 	if (!seqdata->seqcycle)
 	{
 		appendStringInfoString(&fmtStr, "%{no}s ");
@@ -408,14 +419,22 @@ deparse_Seq_Cycle(JsonbParseState *state, Form_pg_sequence seqdata)
  * Deparse the sequence INCREMENT BY option to Jsonb
  *
  * Verbose syntax
+ * SET INCREMENT BY %{value}s
+ * OR
  * INCREMENT BY %{value}s
  */
 static inline void
-deparse_Seq_IncrementBy(JsonbParseState *state, Form_pg_sequence seqdata)
+deparse_Seq_IncrementBy(JsonbParseState *state, Form_pg_sequence seqdata,
+						bool alter_table)
 {
+	char	   *fmt;
+
+	fmt = alter_table ? "SET INCREMENT BY %{value}s"
+		: "INCREMENT BY %{value}s";
+
 	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 	new_jsonb_VA(state, 3,
-				 "fmt", jbvString, "INCREMENT BY %{value}s",
+				 "fmt", jbvString, fmt,
 				 "clause", jbvString, "seqincrement",
 				 "value", jbvString,
 				 psprintf(INT64_FORMAT, seqdata->seqincrement));
@@ -426,14 +445,21 @@ deparse_Seq_IncrementBy(JsonbParseState *state, Form_pg_sequence seqdata)
  * Deparse the sequence MAXVALUE option to Jsonb.
  *
  * Verbose syntax
+ * SET MAXVALUE %{value}s
+ * OR
  * MAXVALUE %{value}s
  */
 static inline void
-deparse_Seq_Maxvalue(JsonbParseState *state, Form_pg_sequence seqdata)
+deparse_Seq_Maxvalue(JsonbParseState *state, Form_pg_sequence seqdata,
+					 bool alter_table)
 {
+	char	   *fmt;
+
+	fmt = alter_table ? "SET MAXVALUE %{value}s" : "MAXVALUE %{value}s";
+
 	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 	new_jsonb_VA(state, 3,
-				 "fmt", jbvString, "MAXVALUE %{value}s",
+				 "fmt", jbvString, fmt,
 				 "clause", jbvString, "maxvalue",
 				 "value", jbvString,
 				 psprintf(INT64_FORMAT, seqdata->seqmax));
@@ -444,14 +470,21 @@ deparse_Seq_Maxvalue(JsonbParseState *state, Form_pg_sequence seqdata)
  * Deparse the sequence MINVALUE option to Jsonb
  *
  * Verbose syntax
+ * SET MINVALUE %{value}s
+ * OR
  * MINVALUE %{value}s
  */
 static inline void
-deparse_Seq_Minvalue(JsonbParseState *state, Form_pg_sequence seqdata)
+deparse_Seq_Minvalue(JsonbParseState *state, Form_pg_sequence seqdata,
+					 bool alter_table)
 {
+	char	   *fmt;
+
+	fmt = alter_table ? "SET MINVALUE %{value}s" : "MINVALUE %{value}s";
+
 	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 	new_jsonb_VA(state, 3,
-				 "fmt", jbvString, "MINVALUE %{value}s",
+				 "fmt", jbvString, fmt,
 				 "clause", jbvString, "minvalue",
 				 "value", jbvString,
 				 psprintf(INT64_FORMAT, seqdata->seqmin));
@@ -465,7 +498,8 @@ deparse_Seq_Minvalue(JsonbParseState *state, Form_pg_sequence seqdata)
  * OWNED BY %{owner}D
  */
 static void
-deparse_Seq_OwnedBy(JsonbParseState *state, Oid sequenceId)
+deparse_Seq_OwnedBy(JsonbParseState *state, Oid sequenceId,
+					bool alter_table)
 {
 	Relation	depRel;
 	SysScanDesc scan;
@@ -550,14 +584,21 @@ deparse_Seq_OwnedBy(JsonbParseState *state, Oid sequenceId)
  * Deparse the sequence START WITH option to Jsonb.
  *
  * Verbose syntax
+ * SET START WITH %{value}s
+ * OR
  * START WITH %{value}s
  */
 static inline void
-deparse_Seq_Startwith(JsonbParseState *state, Form_pg_sequence seqdata)
+deparse_Seq_Startwith(JsonbParseState *state,
+					  Form_pg_sequence seqdata, bool alter_table)
 {
+	char	   *fmt;
+
+	fmt = alter_table ? "SET START WITH %{value}s" : "START WITH %{value}s";
+
 	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 	new_jsonb_VA(state, 3,
-				 "fmt", jbvString, "START WITH %{value}s",
+				 "fmt", jbvString, fmt,
 				 "clause", jbvString, "start",
 				 "value", jbvString,
 				 psprintf(INT64_FORMAT, seqdata->seqstart));
@@ -601,15 +642,17 @@ deparse_Seq_As(JsonbParseState *state, Form_pg_sequence seqdata)
  * Deparse the definition of column identity to Jsonb.
  *
  * Verbose syntax
- * %{identity_type}s ( %{seq_definition: }s )
- * where identity_type is: "GENERATED %{option}s AS IDENTITY"
+ * SET GENERATED %{option}s %{seq_definition: }s
+ * OR
+ * GENERATED %{option}s AS IDENTITY ( %{seq_definition: }s )
  */
 static void
 deparse_ColumnIdentity(JsonbParseState *state, char *parentKey,
-					   Oid seqrelid, char identity)
+					   Oid seqrelid, char identity, bool alter_table)
 {
 	Form_pg_sequence seqform;
 	Sequence_values *seqvalues;
+	char	   *identfmt;
 	StringInfoData fmtStr;
 
 	initStringInfo(&fmtStr);
@@ -631,10 +674,11 @@ deparse_ColumnIdentity(JsonbParseState *state, char *parentKey,
 		insert_jsonb_key(state, "identity_type");
 
 		pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+		identfmt = alter_table ? "SET GENERATED %{option}s" :
+			"GENERATED %{option}s AS IDENTITY";
 
 		new_jsonb_VA(state, 2,
-					 "fmt", jbvString,
-					 "GENERATED %{option}s AS IDENTITY",
+					 "fmt", jbvString, identfmt,
 					 "option", jbvString,
 					 (identity == ATTRIBUTE_IDENTITY_ALWAYS ?
 					  "ALWAYS" : "BY DEFAULT"));
@@ -644,7 +688,8 @@ deparse_ColumnIdentity(JsonbParseState *state, char *parentKey,
 	/* seq_definition array object creation */
 	insert_jsonb_key(state, "seq_definition");
 
-	appendStringInfoString(&fmtStr, " ( %{seq_definition: }s )");
+	appendStringInfoString(&fmtStr, alter_table ? " %{seq_definition: }s" :
+						   " ( %{seq_definition: }s )");
 
 	pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
 
@@ -652,12 +697,12 @@ deparse_ColumnIdentity(JsonbParseState *state, char *parentKey,
 	seqform = seqvalues->seqform;
 
 	/* Definition elements */
-	deparse_Seq_Cache(state, seqform);
-	deparse_Seq_Cycle(state, seqform);
-	deparse_Seq_IncrementBy(state, seqform);
-	deparse_Seq_Minvalue(state, seqform);
-	deparse_Seq_Maxvalue(state, seqform);
-	deparse_Seq_Startwith(state, seqform);
+	deparse_Seq_Cache(state, seqform, alter_table);
+	deparse_Seq_Cycle(state, seqform, alter_table);
+	deparse_Seq_IncrementBy(state, seqform, alter_table);
+	deparse_Seq_Minvalue(state, seqform, alter_table);
+	deparse_Seq_Maxvalue(state, seqform, alter_table);
+	deparse_Seq_Startwith(state, seqform, alter_table);
 	deparse_Seq_Restart(state, seqvalues->last_value);
 	/* We purposefully do not emit OWNED BY here */
 
@@ -685,7 +730,8 @@ deparse_ColumnIdentity(JsonbParseState *state, char *parentKey,
  */
 static void
 deparse_ColumnDef(JsonbParseState *state, Relation relation,
-				  List *dpcontext, bool composite, ColumnDef *coldef)
+				  List *dpcontext, bool composite, ColumnDef *coldef,
+				  bool is_alter)
 {
 	Oid			relid = RelationGetRelid(relation);
 	HeapTuple	attrTup;
@@ -790,6 +836,9 @@ deparse_ColumnDef(JsonbParseState *state, Relation relation,
 			}
 		}
 
+		if (is_alter && coldef->is_not_null)
+			saw_notnull = true;
+
 		/* NOT NULL */
 		if (saw_notnull)
 		{
@@ -836,7 +885,7 @@ deparse_ColumnDef(JsonbParseState *state, Relation relation,
 				appendStringInfoString(&fmtStr, " %{identity_column}s");
 				deparse_ColumnIdentity(state, "identity_column",
 									   seqrelid,
-									   coldef->identity);
+									   coldef->identity, is_alter);
 			}
 		}
 
@@ -1036,7 +1085,7 @@ deparse_TableElems(JsonbParseState *state, Relation relation,
 												(ColumnDef *) elt);
 					else
 						deparse_ColumnDef(state, relation, dpcontext,
-										  composite, (ColumnDef *) elt);
+										  composite, (ColumnDef *) elt, false);
 				}
 				break;
 			case T_Constraint:
@@ -1275,6 +1324,93 @@ deparse_DefElem(JsonbParseState *state, DefElem *elem, bool is_reset)
 	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 }
 
+/*
+ * Deparse SET/RESET as used by
+ * ALTER TABLE ... ALTER COLUMN ... SET/RESET (...)
+ *
+ * Verbose syntax
+ * ALTER COLUMN %{column}I RESET|SET (%{options:, }s)
+ */
+static void
+deparse_ColumnSetOptions(JsonbParseState *state, AlterTableCmd *subcmd)
+{
+	ListCell   *cell;
+	bool		is_reset = subcmd->subtype == AT_ResetOptions;
+	bool		elem_found PG_USED_FOR_ASSERTS_ONLY = false;
+
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	new_jsonb_VA(state, 3,
+				 "fmt", jbvString, "ALTER COLUMN %{column}I"
+				 " %{option}s (%{options:, }s)",
+				 "column", jbvString, subcmd->name,
+				 "option", jbvString, is_reset ? "RESET" : "SET");
+
+	insert_jsonb_key(state, "options");
+	pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+	foreach(cell, (List *) subcmd->def)
+	{
+		DefElem    *elem;
+
+		elem = (DefElem *) lfirst(cell);
+		deparse_DefElem(state, elem, is_reset);
+
+#ifdef USE_ASSERT_CHECKING
+		elem_found = true;
+#endif
+	}
+
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+	Assert(elem_found);
+
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Deparse SET/RESET as used by ALTER TABLE ... SET/RESET (...)
+ *
+ * Verbose syntax
+ * RESET|SET (%{options:, }s)
+ */
+static void
+deparse_RelSetOptions(JsonbParseState *state, AlterTableCmd *subcmd)
+{
+	ListCell   *cell;
+	bool		is_reset = subcmd->subtype == AT_ResetRelOptions;
+	bool		elem_found PG_USED_FOR_ASSERTS_ONLY = false;
+
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	new_jsonb_VA(state, 2,
+				 "fmt", jbvString, "%{set_reset}s (%{options:, }s)",
+				 "set_reset", jbvString, is_reset ? "RESET" : "SET");
+
+	/* insert options array */
+	insert_jsonb_key(state, "options");
+
+	pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+	foreach(cell, (List *) subcmd->def)
+	{
+		DefElem    *elem;
+
+		elem = (DefElem *) lfirst(cell);
+		deparse_DefElem(state, elem, is_reset);
+
+#ifdef USE_ASSERT_CHECKING
+		elem_found = true;
+#endif
+	}
+
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+	Assert(elem_found);
+
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
 /*
  * Deparse WITH clause, as used by Create Table.
  *
@@ -1575,6 +1711,1281 @@ deparse_drop_table(const char *objidentity, Node *parsetree)
 	return JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
 }
 
+/*
+ * Deparse all the collected subcommands and return jsonb string representing
+ * the alter command.
+ *
+ * Verbose syntax
+ * ALTER TABLE %{only}s %{identity}D %{subcmds:, }s
+ */
+static Jsonb *
+deparse_AlterTableStmt(CollectedCommand *cmd)
+{
+	List	   *dpcontext;
+	Relation	rel;
+	ListCell   *cell;
+	Oid			relId = cmd->d.alterTable.objectId;
+	AlterTableStmt *stmt = NULL;
+	StringInfoData fmtStr;
+	JsonbParseState *state = NULL;
+	bool		subCmdArray = false;
+	JsonbValue *value;
+
+	Assert(cmd->type == SCT_AlterTable);
+	stmt = (AlterTableStmt *) cmd->parsetree;
+
+	Assert(IsA(stmt, AlterTableStmt) || IsA(stmt, AlterTableMoveAllStmt));
+
+	initStringInfo(&fmtStr);
+
+	/*
+	 * ALTER TABLE subcommands generated for TableLikeClause is processed in
+	 * the top level CREATE TABLE command; return empty here.
+	 */
+	if (IsA(stmt, AlterTableStmt) && stmt->table_like)
+		return NULL;
+
+	rel = relation_open(relId, AccessShareLock);
+
+	if (rel->rd_rel->relkind != RELKIND_RELATION &&
+		rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+	{
+		/* unsupported relkind */
+		table_close(rel, AccessShareLock);
+		return NULL;
+	}
+
+	dpcontext = deparse_context_for(RelationGetRelationName(rel), relId);
+
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	/* Start constructing fmt string */
+	appendStringInfoString(&fmtStr, "ALTER TABLE");
+
+	if (!stmt->relation->inh)
+	{
+		appendStringInfoString(&fmtStr, " %{only}s");
+		new_jsonb_VA(state, 1, "only", jbvString, "ONLY");
+	}
+
+	appendStringInfoString(&fmtStr, " %{identity}D");
+	insert_identity_object(state, rel->rd_rel->relnamespace,
+						   RelationGetRelationName(rel));
+
+	foreach(cell, cmd->d.alterTable.subcmds)
+	{
+		CollectedATSubcmd *sub = (CollectedATSubcmd *) lfirst(cell);
+		AlterTableCmd *subcmd = (AlterTableCmd *) sub->parsetree;
+
+		Assert(IsA(subcmd, AlterTableCmd));
+
+		/*
+		 * Skip deparse of the subcommand if the objectId doesn't match the
+		 * target relation ID. It can happen for inherited tables when
+		 * subcommands for inherited tables and the parent table are both
+		 * collected in the ALTER TABLE command for the parent table.
+		 */
+		if (subcmd->subtype != AT_AttachPartition &&
+			sub->address.objectId != relId &&
+			has_superclass(sub->address.objectId))
+			continue;
+
+		/* Mark the begin of subcmds array */
+		if (!subCmdArray)
+		{
+			appendStringInfoString(&fmtStr, " %{subcmds:, }s");
+			insert_jsonb_key(state, "subcmds");
+			pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+			subCmdArray = true;
+		}
+
+		switch (subcmd->subtype)
+		{
+			case AT_AddColumn:
+				{
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					/* XXX need to set the "recurse" bit somewhere? */
+					Assert(IsA(subcmd->def, ColumnDef));
+
+					/*
+					 * Syntax: ADD COLUMN %{if_not_exists}s %{definition}s"
+					 * where definition: "%{name}I %{coltype}T STORAGE
+					 * %{colstorage}s %{compression}s %{collation}s
+					 * %{not_null}s %{default}s %{identity_column}s
+					 * %{generated_column}s"
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+					appendStringInfoString(&fmtSub, "ADD COLUMN");
+					new_jsonb_VA(state, 1, "type", jbvString, "add column");
+
+					if (subcmd->missing_ok)
+					{
+						appendStringInfoString(&fmtSub, " %{if_not_exists}s");
+						new_jsonb_VA(state, 1,
+									 "if_not_exists", jbvString, "IF NOT EXISTS");
+					}
+
+					/* Push definition key-value pair */
+					appendStringInfoString(&fmtSub, " %{definition}s");
+					insert_jsonb_key(state, "definition");
+
+					deparse_ColumnDef(state, rel, dpcontext,
+									  false, (ColumnDef *) subcmd->def,
+									  true);
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+					pfree(fmtSub.data);
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+					break;
+				}
+
+			case AT_AddIndexConstraint:
+				{
+					IndexStmt  *istmt;
+					Relation	idx;
+					Oid			conOid = sub->address.objectId;
+
+					Assert(IsA(subcmd->def, IndexStmt));
+					istmt = (IndexStmt *) subcmd->def;
+
+					Assert(istmt->isconstraint && istmt->unique);
+
+					idx = relation_open(istmt->indexOid, AccessShareLock);
+
+					/*
+					 * Syntax: ADD CONSTRAINT %{name}I %{constraint_type}s
+					 * USING INDEX %index_name}I %{deferrable}s
+					 * %{init_deferred}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+					new_jsonb_VA(state, 7,
+								 "fmt", jbvString,
+								 "ADD CONSTRAINT %{name}I %{constraint_type}s"
+								 " USING INDEX %{index_name}I %{deferrable}s"
+								 " %{init_deferred}s",
+								 "type", jbvString, "add constraint using index",
+								 "name", jbvString, get_constraint_name(conOid),
+								 "constraint_type", jbvString,
+								 istmt->primary ? "PRIMARY KEY" : "UNIQUE",
+								 "index_name", jbvString,
+								 RelationGetRelationName(idx),
+								 "deferrable", jbvString,
+								 istmt->deferrable ? "DEFERRABLE" :
+								 "NOT DEFERRABLE",
+								 "init_deferred", jbvString,
+								 istmt->initdeferred ? "INITIALLY DEFERRED" :
+								 "INITIALLY IMMEDIATE");
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+					relation_close(idx, AccessShareLock);
+					break;
+				}
+			case AT_ReAddIndex:
+			case AT_ReAddConstraint:
+			case AT_ReAddDomainConstraint:
+			case AT_ReAddComment:
+			case AT_ReplaceRelOptions:
+			case AT_CheckNotNull:
+			case AT_ReAddStatistics:
+				/* Subtypes used for internal operations; nothing to do here */
+				break;
+
+			case AT_ColumnDefault:
+				if (subcmd->def == NULL)
+				{
+					/*
+					 * Syntax: ALTER COLUMN %{column}I DROP DEFAULT
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					new_jsonb_VA(state, 3,
+								 "fmt", jbvString,
+								 "ALTER COLUMN %{column}I DROP DEFAULT",
+								 "type", jbvString, "drop default",
+								 "column", jbvString, subcmd->name);
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				}
+				else
+				{
+					List	   *dpcontext_rel;
+					HeapTuple	attrtup;
+					AttrNumber	attno;
+
+					dpcontext_rel = deparse_context_for(
+														RelationGetRelationName(rel),
+														RelationGetRelid(rel));
+					attrtup = SearchSysCacheAttName(RelationGetRelid(rel),
+													subcmd->name);
+					attno = ((Form_pg_attribute) GETSTRUCT(attrtup))->attnum;
+
+					/*
+					 * Syntax: ALTER COLUMN %{column}I SET DEFAULT
+					 * %{definition}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+					new_jsonb_VA(state, 4,
+								 "fmt", jbvString,
+								 "ALTER COLUMN %{column}I SET DEFAULT"
+								 " %{definition}s",
+								 "type", jbvString, "set default",
+								 "column", jbvString, subcmd->name,
+								 "definition", jbvString,
+								 relation_get_column_default(rel, attno,
+															 dpcontext_rel));
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+					ReleaseSysCache(attrtup);
+				}
+
+				break;
+
+			case AT_DropNotNull:
+
+				/*
+				 * Syntax: ALTER COLUMN %{column}I DROP NOT NULL
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString,
+							 "ALTER COLUMN %{column}I DROP NOT NULL",
+							 "type", jbvString, "drop not null",
+							 "column", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_ForceRowSecurity:
+
+				/*
+				 * Syntax: FORCE ROW LEVEL SECURITY
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 1, "fmt", jbvString,
+							 "FORCE ROW LEVEL SECURITY");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_NoForceRowSecurity:
+
+				/*
+				 * Syntax: NO FORCE ROW LEVEL SECURITY
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 1, "fmt", jbvString,
+							 "NO FORCE ROW LEVEL SECURITY");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_SetNotNull:
+
+				/*
+				 * Syntax: ALTER COLUMN %{column}I SET NOT NULL
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString,
+							 "ALTER COLUMN %{column}I SET NOT NULL",
+							 "type", jbvString, "set not null",
+							 "column", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DropExpression:
+				{
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					/*
+					 * Syntax: ALTER COLUMN %{column}I DROP EXPRESSION
+					 * %{if_exists}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					appendStringInfoString(&fmtSub, "ALTER COLUMN"
+										   " %{column}I DROP EXPRESSION");
+					new_jsonb_VA(state, 2,
+								 "type", jbvString, "drop expression",
+								 "column", jbvString, subcmd->name);
+
+					if (subcmd->missing_ok)
+					{
+						appendStringInfoString(&fmtSub, " %{if_exists}s");
+						new_jsonb_VA(state, 1,
+									 "if_exists", jbvString, "IF EXISTS");
+					}
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+					pfree(fmtSub.data);
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+					break;
+				}
+
+			case AT_SetStatistics:
+				Assert(IsA(subcmd->def, Integer));
+
+				/*
+				 * Syntax: ALTER COLUMN %{column}I SET STATISTICS
+				 * %{statistics}n
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 4,
+							 "fmt", jbvString,
+							 "ALTER COLUMN %{column}I SET STATISTICS"
+							 " %{statistics}n",
+							 "type", jbvString, "set statistics",
+							 "column", jbvString, subcmd->name,
+							 "statistics", jbvNumeric,
+							 intVal((Integer *) subcmd->def));
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_SetOptions:
+			case AT_ResetOptions:
+
+				/*
+				 * Syntax: ALTER COLUMN %{column}I RESET|SET (%{options:, }s)
+				 */
+				deparse_ColumnSetOptions(state, subcmd);
+				break;
+
+			case AT_SetStorage:
+				Assert(IsA(subcmd->def, String));
+
+				/*
+				 * Syntax: ALTER COLUMN %{column}I SET STORAGE %{storage}s
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 4,
+							 "fmt", jbvString,
+							 "ALTER COLUMN %{column}I SET STORAGE"
+							 " %{storage}s",
+							 "type", jbvString, "set storage",
+							 "column", jbvString, subcmd->name,
+							 "storage", jbvString,
+							 strVal((String *) subcmd->def));
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_SetCompression:
+				Assert(IsA(subcmd->def, String));
+
+				/*
+				 * Syntax: ALTER COLUMN %{column}I SET COMPRESSION
+				 * %{compression_method}s
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 4,
+							 "fmt", jbvString,
+							 "ALTER COLUMN %{column}I SET COMPRESSION"
+							 " %{compression_method}s",
+							 "type", jbvString, "set compression",
+							 "column", jbvString, subcmd->name,
+							 "compression_method", jbvString,
+							 strVal((String *) subcmd->def));
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DropColumn:
+				{
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					/*
+					 * Syntax: DROP COLUMN %{if_exists}s %{column}I
+					 * %{cascade}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+					appendStringInfoString(&fmtSub, "DROP COLUMN");
+					new_jsonb_VA(state, 1, "type", jbvString, "drop column");
+
+					if (subcmd->missing_ok)
+					{
+						appendStringInfoString(&fmtSub, " %{if_exists}s");
+						new_jsonb_VA(state, 1,
+									 "if_exists", jbvString, "IF EXISTS");
+					}
+
+					appendStringInfoString(&fmtSub, " %{column}I");
+					new_jsonb_VA(state, 1, "column", jbvString, subcmd->name);
+
+					if (subcmd->behavior == DROP_CASCADE)
+					{
+						appendStringInfoString(&fmtSub, " %{cascade}s");
+						new_jsonb_VA(state, 1,
+									 "cascade", jbvString, "CASCADE");
+					}
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+					pfree(fmtSub.data);
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+					break;
+				}
+			case AT_AddIndex:
+				{
+					Oid			idxOid = sub->address.objectId;
+					IndexStmt  *istmt PG_USED_FOR_ASSERTS_ONLY =
+						(IndexStmt *) subcmd->def;
+					Relation	idx;
+					const char *idxname;
+					Oid			constrOid;
+
+					Assert(IsA(subcmd->def, IndexStmt));
+					Assert(istmt->isconstraint);
+
+					idx = relation_open(idxOid, AccessShareLock);
+					idxname = RelationGetRelationName(idx);
+
+					constrOid = get_relation_constraint_oid(
+															cmd->d.alterTable.objectId,
+															idxname, false);
+
+					/*
+					 * Syntax: ADD CONSTRAINT %{name}I %{definition}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+					new_jsonb_VA(state, 4,
+								 "fmt", jbvString,
+								 "ADD CONSTRAINT %{name}I %{definition}s",
+								 "type", jbvString, "add constraint",
+								 "name", jbvString, idxname,
+								 "definition", jbvString,
+								 pg_get_constraintdef_string(constrOid));
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+					relation_close(idx, AccessShareLock);
+				}
+				break;
+			case AT_AddConstraint:
+				{
+					/* XXX need to set the "recurse" bit somewhere? */
+					Oid			constrOid = sub->address.objectId;
+
+					/* Skip adding constraint for inherits table sub command */
+					if (!OidIsValid(constrOid))
+						continue;
+
+					Assert(IsA(subcmd->def, Constraint));
+
+					/*
+					 * Syntax: ADD CONSTRAINT %{name}I %{definition}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					new_jsonb_VA(state, 4,
+								 "fmt", jbvString,
+								 "ADD CONSTRAINT %{name}I %{definition}s",
+								 "type", jbvString, "add constraint",
+								 "name", jbvString, get_constraint_name(constrOid),
+								 "definition", jbvString,
+								 pg_get_constraintdef_string(constrOid));
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+					break;
+				}
+
+			case AT_AlterConstraint:
+				{
+					Oid			conOid = sub->address.objectId;
+					Constraint *c = (Constraint *) subcmd->def;
+
+					/* If no constraint was altered, silently skip it */
+					if (!OidIsValid(conOid))
+						break;
+
+					Assert(IsA(c, Constraint));
+
+					/*
+					 * Syntax: ALTER CONSTRAINT %{name}I %{deferrable}s
+					 * %{init_deferred}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					new_jsonb_VA(state, 5,
+								 "fmt", jbvString,
+								 "ALTER CONSTRAINT %{name}I %{deferrable}s"
+								 " %{init_deferred}s",
+								 "type", jbvString, "alter constraint",
+								 "name", jbvString, get_constraint_name(conOid),
+								 "deferrable", jbvString,
+								 c->deferrable ? "DEFERRABLE" : "NOT DEFERRABLE",
+								 "init_deferred", jbvString,
+								 c->initdeferred ? "INITIALLY DEFERRED" :
+								 "INITIALLY IMMEDIATE");
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				}
+				break;
+
+			case AT_ValidateConstraint:
+
+				/*
+				 * Syntax: VALIDATE CONSTRAINT %{constraint}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString,
+							 "VALIDATE CONSTRAINT %{constraint}I",
+							 "type", jbvString, "validate constraint",
+							 "constraint", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DropConstraint:
+				{
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					/*
+					 * Syntax: DROP CONSTRAINT %{if_exists}s %{constraint}I
+					 * %{cascade}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					appendStringInfoString(&fmtSub, "DROP CONSTRAINT");
+					new_jsonb_VA(state, 1, "type", jbvString, "drop constraint");
+
+					if (subcmd->missing_ok)
+					{
+						appendStringInfoString(&fmtSub, " %{if_exists}s");
+						new_jsonb_VA(state, 1,
+									 "if_exists", jbvString, "IF EXISTS");
+					}
+
+					appendStringInfoString(&fmtSub, " %{constraint}I");
+					new_jsonb_VA(state, 1,
+								 "constraint", jbvString, subcmd->name);
+
+					if (subcmd->behavior == DROP_CASCADE)
+					{
+						appendStringInfoString(&fmtSub, " %{cascade}s");
+						new_jsonb_VA(state, 1, "cascade", jbvString, "CASCADE");
+					}
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+					pfree(fmtSub.data);
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				}
+				break;
+
+			case AT_AlterColumnType:
+				{
+					TupleDesc	tupdesc = RelationGetDescr(rel);
+					Form_pg_attribute att;
+					ColumnDef  *def;
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					att = &(tupdesc->attrs[sub->address.objectSubId - 1]);
+					def = (ColumnDef *) subcmd->def;
+					Assert(IsA(def, ColumnDef));
+
+					/*
+					 * Syntax: ALTER COLUMN %{column}I SET DATA TYPE
+					 * %{datatype}T %{collation}s %{using}s where using: USING
+					 * %{expression}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					appendStringInfoString(&fmtSub, "ALTER COLUMN %{column}I"
+										   " SET DATA TYPE %{datatype}T");
+					new_jsonb_VA(state, 2,
+								 "type", jbvString, "alter column type",
+								 "column", jbvString, subcmd->name);
+
+					new_jsonb_for_type(state, "datatype",
+									   att->atttypid, att->atttypmod);
+
+					/* Add a COLLATE clause, if needed */
+					if (OidIsValid(att->attcollation))
+					{
+						appendStringInfoString(&fmtSub, " %{collation}s");
+						insert_collate_object(state, "collation",
+											  "COLLATE %{name}D",
+											  CollationRelationId,
+											  att->attcollation, "name");
+					}
+
+					/*
+					 * If there's a USING clause, transformAlterTableStmt ran
+					 * it through transformExpr and stored the resulting node
+					 * in cooked_default, which we can use here.
+					 */
+					if (def->raw_default)
+					{
+						Datum		deparsed;
+						char	   *defexpr;
+						List	   *exprs = NIL;
+
+						exprs = lappend(exprs, def->cooked_default);
+						defexpr = nodeToString(def->cooked_default);
+						deparsed = DirectFunctionCall2(pg_get_expr,
+													   CStringGetTextDatum(defexpr),
+													   RelationGetRelid(rel));
+						appendStringInfoString(&fmtSub, " %{using}s");
+						insert_jsonb_key(state, "using");
+						pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+						new_jsonb_VA(state, 2,
+									 "fmt", jbvString, "USING %{expression}s",
+									 "expression", jbvString,
+									 TextDatumGetCString(deparsed));
+						pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+					}
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+
+					pfree(fmtSub.data);
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				}
+				break;
+
+			case AT_ChangeOwner:
+
+				/*
+				 * Syntax: OWNER TO %{owner}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString, "OWNER TO %{owner}I",
+							 "type", jbvString, "change owner",
+							 "owner", jbvString,
+							 get_rolespec_name(subcmd->newowner));
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_ClusterOn:
+
+				/*
+				 * Syntax: CLUSTER ON %{index}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString, "CLUSTER ON %{index}I",
+							 "type", jbvString, "cluster on",
+							 "index", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+
+			case AT_DropCluster:
+
+				/*
+				 * Syntax: SET WITHOUT CLUSTER
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "SET WITHOUT CLUSTER",
+							 "type", jbvString, "set without cluster");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_SetLogged:
+
+				/*
+				 * Syntax: SET LOGGED
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "SET LOGGED",
+							 "type", jbvString, "set logged");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_SetUnLogged:
+
+				/*
+				 * Syntax: SET UNLOGGED
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "SET UNLOGGED",
+							 "type", jbvString, "set unlogged");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DropOids:
+
+				/*
+				 * Syntax: SET WITHOUT OIDS
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "SET WITHOUT OIDS",
+							 "type", jbvString, "set without oids");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_SetAccessMethod:
+
+				/*
+				 * Syntax: SET ACCESS METHOD %{access_method}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString,
+							 "SET ACCESS METHOD %{access_method}I",
+							 "type", jbvString, "set access method",
+							 "access_method", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_SetTableSpace:
+
+				/*
+				 * Syntax: SET TABLESPACE %{tablespace}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString, "SET TABLESPACE %{tablespace}I",
+							 "type", jbvString, "set tablespace",
+							 "tablespace", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_SetRelOptions:
+			case AT_ResetRelOptions:
+
+				/*
+				 * Syntax: SET|RESET (%{options:, }s)
+				 */
+				deparse_RelSetOptions(state, subcmd);
+				break;
+
+			case AT_EnableTrig:
+
+				/*
+				 * Syntax: ENABLE TRIGGER %{trigger}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString, "ENABLE TRIGGER %{trigger}I",
+							 "type", jbvString, "enable trigger",
+							 "trigger", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_EnableAlwaysTrig:
+
+				/*
+				 * Syntax: ENABLE ALWAYS TRIGGER %{trigger}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString,
+							 "ENABLE ALWAYS TRIGGER %{trigger}I",
+							 "type", jbvString, "enable always trigger",
+							 "trigger", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_EnableReplicaTrig:
+
+				/*
+				 * Syntax: ENABLE REPLICA TRIGGER %{trigger}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString,
+							 "ENABLE REPLICA TRIGGER %{trigger}I",
+							 "type", jbvString, "enable replica trigger",
+							 "trigger", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DisableTrig:
+
+				/*
+				 * Syntax: DISABLE TRIGGER %{trigger}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString, "DISABLE TRIGGER %{trigger}I",
+							 "type", jbvString, "disable trigger",
+							 "trigger", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_EnableTrigAll:
+
+				/*
+				 * Syntax: ENABLE TRIGGER ALL
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "ENABLE TRIGGER ALL",
+							 "type", jbvString, "enable trigger all");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DisableTrigAll:
+
+				/*
+				 * Syntax: DISABLE TRIGGER ALL
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "DISABLE TRIGGER ALL",
+							 "type", jbvString, "disable trigger all");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_EnableTrigUser:
+
+				/*
+				 * Syntax: ENABLE TRIGGER USER
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "ENABLE TRIGGER USER",
+							 "type", jbvString, "enable trigger user");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DisableTrigUser:
+
+				/*
+				 * Syntax: DISABLE TRIGGER USER
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "DISABLE TRIGGER USER",
+							 "type", jbvString, "disable trigger user");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_EnableRule:
+
+				/*
+				 * Syntax: ENABLE RULE %{rule}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString, "ENABLE RULE %{rule}I",
+							 "type", jbvString, "enable rule",
+							 "rule", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_EnableAlwaysRule:
+
+				/*
+				 * Syntax: ENABLE ALWAYS RULE %{rule}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString, "ENABLE ALWAYS RULE %{rule}I",
+							 "type", jbvString, "enable always rule",
+							 "rule", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_EnableReplicaRule:
+
+				/*
+				 * Syntax: ENABLE REPLICA RULE %{rule}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString, "ENABLE REPLICA RULE %{rule}I",
+							 "type", jbvString, "enable replica rule",
+							 "rule", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DisableRule:
+
+				/*
+				 * Syntax: DISABLE RULE %{rule}I
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 3,
+							 "fmt", jbvString, "DISABLE RULE %{rule}I",
+							 "type", jbvString, "disable rule",
+							 "rule", jbvString, subcmd->name);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_AddInherit:
+
+				/*
+				 * Syntax: INHERIT %{parent}D
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "INHERIT %{parent}D",
+							 "type", jbvString, "inherit");
+				new_jsonb_for_qualname_id(state, RelationRelationId,
+										  sub->address.objectId, "parent", true);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DropInherit:
+
+				/*
+				 * Syntax: NO INHERIT %{parent}D
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "NO INHERIT %{parent}D",
+							 "type", jbvString, "drop inherit");
+				new_jsonb_for_qualname_id(state, RelationRelationId,
+										  sub->address.objectId, "parent", true);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_AddOf:
+
+				/*
+				 * Syntax: OF %{type_of}T
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "OF %{type_of}T",
+							 "type", jbvString, "add of");
+				new_jsonb_for_type(state, "type_of", sub->address.objectId, -1);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DropOf:
+
+				/*
+				 * Syntax: NOT OF
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "NOT OF",
+							 "type", jbvString, "not of");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_ReplicaIdentity:
+
+				/*
+				 * Syntax: REPLICA IDENTITY %{ident}s
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString,
+							 "REPLICA IDENTITY %{ident}s",
+							 "type", jbvString, "replica identity");
+				switch (((ReplicaIdentityStmt *) subcmd->def)->identity_type)
+				{
+					case REPLICA_IDENTITY_DEFAULT:
+						new_jsonb_VA(state, 1, "ident", jbvString, "DEFAULT");
+						break;
+					case REPLICA_IDENTITY_FULL:
+						new_jsonb_VA(state, 1, "ident", jbvString, "FULL");
+						break;
+					case REPLICA_IDENTITY_NOTHING:
+						new_jsonb_VA(state, 1, "ident", jbvString, "NOTHING");
+						break;
+					case REPLICA_IDENTITY_INDEX:
+						insert_jsonb_key(state, "ident");
+						new_jsonb_VA(state, 2,
+									 "fmt", jbvString, "USING INDEX %{index}I",
+									 "index", jbvString,
+									 ((ReplicaIdentityStmt *) subcmd->def)->name);
+						break;
+				}
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_EnableRowSecurity:
+
+				/*
+				 * Syntax: ENABLE ROW LEVEL SECURITY
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString,
+							 "ENABLE ROW LEVEL SECURITY",
+							 "type", jbvString, "enable row security");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_DisableRowSecurity:
+
+				/*
+				 * Syntax: DISABLE ROW LEVEL SECURITY
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString,
+							 "DISABLE ROW LEVEL SECURITY",
+							 "type", jbvString, "disable row security");
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+
+			case AT_AttachPartition:
+				{
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					/*
+					 * Syntax: ATTACH PARTITION %{partition_identity}D
+					 * %{partition_bound}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					appendStringInfoString(&fmtSub, "ATTACH PARTITION"
+										   " %{partition_identity}D");
+
+					new_jsonb_VA(state, 1, "type", jbvString,
+								 "attach partition");
+					new_jsonb_for_qualname_id(state, RelationRelationId,
+											  sub->address.objectId,
+											  "partition_identity", true);
+
+					if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+					{
+						appendStringInfoString(&fmtSub, " %{partition_bound}s");
+						new_jsonb_VA(state, 1,
+									 "partition_bound", jbvString,
+									 relation_get_part_bound(sub->address.objectId));
+					}
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+					pfree(fmtSub.data);
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+					break;
+				}
+			case AT_DetachPartition:
+				{
+					PartitionCmd *cmd;
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					Assert(IsA(subcmd->def, PartitionCmd));
+					cmd = (PartitionCmd *) subcmd->def;
+
+					/*
+					 * Syntax: DETACH PARTITION %{partition_identity}D
+					 * %{concurrent}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					appendStringInfoString(&fmtSub, "DETACH PARTITION"
+										   " %{partition_identity}D");
+
+					new_jsonb_VA(state, 1, "type", jbvString, "detach partition");
+					new_jsonb_for_qualname_id(state, RelationRelationId,
+											  sub->address.objectId,
+											  "partition_identity", true);
+					if (cmd->concurrent)
+					{
+						appendStringInfoString(&fmtSub, " %{concurrent}s");
+						new_jsonb_VA(state, 1,
+									 "concurrent", jbvString, "CONCURRENTLY");
+					}
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+					pfree(fmtSub.data);
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+					break;
+				}
+			case AT_DetachPartitionFinalize:
+
+				/*
+				 * Syntax: DETACH PARTITION %{partition_identity}D FINALIZE
+				 */
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 2,
+							 "fmt", jbvString, "DETACH PARTITION"
+							 " %{partition_identity}D FINALIZE",
+							 "type", jbvString, "detach partition finalize");
+
+				new_jsonb_for_qualname_id(state, RelationRelationId,
+										  sub->address.objectId,
+										  "partition_identity", true);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				break;
+			case AT_AddIdentity:
+				{
+					AttrNumber	attnum;
+					Oid			seq_relid;
+					ColumnDef  *coldef = (ColumnDef *) subcmd->def;
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					/*
+					 * Syntax: ALTER COLUMN %{column}I %{definition}s where
+					 * definition : ADD %{identity_column}s where
+					 * identity_column: %{identity_type}s ( %{seq_definition:
+					 * }s )
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+					appendStringInfoString(&fmtSub, "ALTER COLUMN %{column}I");
+					new_jsonb_VA(state, 2,
+								 "type", jbvString, "add identity",
+								 "column", jbvString, subcmd->name);
+
+					attnum = get_attnum(RelationGetRelid(rel), subcmd->name);
+					seq_relid = getIdentitySequence(RelationGetRelid(rel),
+													attnum, true);
+
+					if (OidIsValid(seq_relid))
+					{
+
+						appendStringInfoString(&fmtSub, " %{definition}s");
+						insert_jsonb_key(state, "definition");
+
+						/* insert definition's value now */
+						pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+						new_jsonb_VA(state, 1,
+									 "fmt", jbvString, "ADD %{identity_column}s");
+
+						/* insert identity_column */
+						deparse_ColumnIdentity(state, "identity_column",
+											   seq_relid,
+											   coldef->identity, false);
+
+						/* mark definition's value end */
+						pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+					}
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+
+					pfree(fmtSub.data);
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+				}
+				break;
+			case AT_SetIdentity:
+				{
+					DefElem    *defel;
+					char		identity = 0;
+					AttrNumber	attnum;
+					Oid			seq_relid;
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					/*
+					 * Syntax: ALTER COLUMN %{column}I %{definition}s where
+					 * definition : %{identity_type}s ( %{seq_definition: }s )
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+					appendStringInfoString(&fmtSub, "ALTER COLUMN %{column}I");
+					new_jsonb_VA(state, 2,
+								 "type", jbvString, "set identity",
+								 "column", jbvString, subcmd->name);
+
+					if (subcmd->def)
+					{
+						List	   *def = (List *) subcmd->def;
+
+						Assert(IsA(subcmd->def, List));
+
+						defel = linitial_node(DefElem, def);
+						identity = defGetInt32(defel);
+					}
+
+					attnum = get_attnum(RelationGetRelid(rel), subcmd->name);
+					seq_relid = getIdentitySequence(RelationGetRelid(rel),
+													attnum, true);
+
+					if (OidIsValid(seq_relid))
+					{
+						appendStringInfoString(&fmtSub, " %{definition}s");
+						deparse_ColumnIdentity(state, "definition",
+											   seq_relid, identity,
+											   true);
+					}
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+
+					pfree(fmtSub.data);
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+					break;
+				}
+			case AT_DropIdentity:
+				{
+					StringInfoData fmtSub;
+
+					initStringInfo(&fmtSub);
+
+					/*
+					 * Syntax: ALTER COLUMN %{column}I DROP IDENTITY
+					 * %{if_exists}s
+					 */
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					appendStringInfoString(&fmtSub, "ALTER COLUMN"
+										   " %{column}I DROP IDENTITY");
+					new_jsonb_VA(state, 2,
+								 "type", jbvString, "drop identity",
+								 "column", jbvString, subcmd->name);
+
+					if (subcmd->missing_ok)
+					{
+						appendStringInfoString(&fmtSub, " %{if_exists}s");
+						new_jsonb_VA(state, 1,
+									 "if_exists", jbvString, "IF EXISTS");
+					}
+
+					/* We have full fmt by now, so add jsonb element for that */
+					new_jsonb_VA(state, 1, "fmt", jbvString, fmtSub.data);
+					pfree(fmtSub.data);
+
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+					break;
+				}
+			default:
+				elog(WARNING, "unsupported alter table subtype %d",
+					 subcmd->subtype);
+				break;
+		}
+	}
+
+	table_close(rel, AccessShareLock);
+
+	/* if subcmds array is not even created or has 0 elements, return NULL */
+	if (!subCmdArray ||
+		((state->contVal.type == jbvArray) &&
+		 (state->contVal.val.array.nElems == 0)))
+	{
+		pfree(fmtStr.data);
+		return NULL;
+	}
+
+	/* Mark the end of subcmds array */
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+	/* We have full fmt by now, so add jsonb element for that */
+	new_jsonb_VA(state, 1, "fmt", jbvString, fmtStr.data);
+
+	pfree(fmtStr.data);
+
+	/* Mark the end of ROOT object */
+	value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	return JsonbValueToJsonb(value);
+}
+
 /*
  * Deparse a CreateSeqStmt.
  *
@@ -1589,7 +3000,8 @@ deparse_drop_table(const char *objidentity, Node *parsetree)
  * the column default value and sequences creation while parsing.
  *
  * Verbose syntax
- * CREATE %{persistence}s SEQUENCE %{if_not_exists}s %{identity}D %{definition: }s
+ * CREATE %{persistence}s SEQUENCE %{if_not_exists}s %{identity}D
+ * %{definition: }s
  */
 static Jsonb *
 deparse_CreateSeqStmt(Oid objectId, Node *parsetree)
@@ -1653,12 +3065,12 @@ deparse_CreateSeqStmt(Oid objectId, Node *parsetree)
 	pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
 
 	/* Definition elements */
-	deparse_Seq_Cache(state, seqform);
-	deparse_Seq_Cycle(state, seqform);
-	deparse_Seq_IncrementBy(state, seqform);
-	deparse_Seq_Minvalue(state, seqform);
-	deparse_Seq_Maxvalue(state, seqform);
-	deparse_Seq_Startwith(state, seqform);
+	deparse_Seq_Cache(state, seqform, false);
+	deparse_Seq_Cycle(state, seqform, false);
+	deparse_Seq_IncrementBy(state, seqform, false);
+	deparse_Seq_Minvalue(state, seqform, false);
+	deparse_Seq_Maxvalue(state, seqform, false);
+	deparse_Seq_Startwith(state, seqform, false);
 	deparse_Seq_Restart(state, seqvalues->last_value);
 	deparse_Seq_As(state, seqform);
 	/* We purposefully do not emit OWNED BY here */
@@ -1737,21 +3149,21 @@ deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
 		DefElem    *elem = (DefElem *) lfirst(cell);
 
 		if (strcmp(elem->defname, "cache") == 0)
-			deparse_Seq_Cache(state, seqform);
+			deparse_Seq_Cache(state, seqform, false);
 		else if (strcmp(elem->defname, "cycle") == 0)
-			deparse_Seq_Cycle(state, seqform);
+			deparse_Seq_Cycle(state, seqform, false);
 		else if (strcmp(elem->defname, "increment") == 0)
-			deparse_Seq_IncrementBy(state, seqform);
+			deparse_Seq_IncrementBy(state, seqform, false);
 		else if (strcmp(elem->defname, "minvalue") == 0)
-			deparse_Seq_Minvalue(state, seqform);
+			deparse_Seq_Minvalue(state, seqform, false);
 		else if (strcmp(elem->defname, "maxvalue") == 0)
-			deparse_Seq_Maxvalue(state, seqform);
+			deparse_Seq_Maxvalue(state, seqform, false);
 		else if (strcmp(elem->defname, "start") == 0)
-			deparse_Seq_Startwith(state, seqform);
+			deparse_Seq_Startwith(state, seqform, false);
 		else if (strcmp(elem->defname, "restart") == 0)
 			deparse_Seq_Restart(state, seqvalues->last_value);
 		else if (strcmp(elem->defname, "owned_by") == 0)
-			deparse_Seq_OwnedBy(state, objectId);
+			deparse_Seq_OwnedBy(state, objectId, false);
 		else if (strcmp(elem->defname, "as") == 0)
 			deparse_Seq_As(state, seqform);
 		else
@@ -1767,6 +3179,207 @@ deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
 	return JsonbValueToJsonb(value);
 }
 
+/*
+ * Deparse a RenameStmt.
+ *
+ * Verbose syntax
+ * ALTER TABLE %{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 Jsonb *
+deparse_RenameStmt(ObjectAddress address, Node *parsetree)
+{
+	RenameStmt *node = (RenameStmt *) parsetree;
+	Relation	relation;
+	Oid			schemaId;
+	JsonbParseState *state = NULL;
+	JsonbValue *value;
+
+	/*
+	 * In an ALTER .. RENAME command, we don't have the original name of the
+	 * object in system catalogs: since we inspect them after the command has
+	 * executed, the old name is already gone.  Therefore, we extract it from
+	 * the parse node.  Note we still extract the schema name from the catalog
+	 * (it might not be present in the parse node); it cannot possibly have
+	 * changed anyway.
+	 */
+	switch (node->renameType)
+	{
+		case OBJECT_TABLE:
+			relation = relation_open(address.objectId, AccessShareLock);
+			schemaId = RelationGetNamespace(relation);
+
+			pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+			new_jsonb_VA(state, 3,
+						 "fmt", jbvString,
+						 "ALTER TABLE %{if_exists}s %{identity}D"
+						 " RENAME TO %{newname}I",
+						 "if_exists", jbvString,
+						 node->missing_ok ? "IF EXISTS" : "",
+						 "newname", jbvString, node->newname);
+
+			insert_identity_object(state, schemaId, node->relation->relname);
+			value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+			relation_close(relation, AccessShareLock);
+			break;
+
+		case OBJECT_TABCONSTRAINT:
+			{
+				HeapTuple	constrtup;
+				Form_pg_constraint constform;
+
+				constrtup = SearchSysCache1(CONSTROID,
+											ObjectIdGetDatum(address.objectId));
+				if (!HeapTupleIsValid(constrtup))
+					elog(ERROR, "cache lookup failed for constraint with OID %u",
+						 address.objectId);
+				constform = (Form_pg_constraint) GETSTRUCT(constrtup);
+
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, 4,
+							 "fmt", jbvString,
+							 "ALTER TABLE %{only}s %{identity}D RENAME"
+							 " CONSTRAINT %{oldname}I TO %{newname}I",
+							 "only", jbvString,
+							 node->relation->inh ? "" : "ONLY",
+							 "oldname", jbvString, node->subname,
+							 "newname", jbvString, node->newname);
+
+				new_jsonb_for_qualname_id(state, RelationRelationId,
+										  constform->conrelid, "identity", true);
+				value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+				ReleaseSysCache(constrtup);
+			}
+			break;
+
+		case OBJECT_COLUMN:
+			{
+				StringInfoData fmtStr;
+
+				initStringInfo(&fmtStr);
+
+				relation = relation_open(address.objectId, AccessShareLock);
+				schemaId = RelationGetNamespace(relation);
+
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+				appendStringInfoString(&fmtStr, "ALTER %{objtype}s");
+
+				new_jsonb_VA(state, 1,
+							 "objtype", jbvString,
+							 stringify_objtype(node->relationType));
+
+				/* Composite types do not support IF EXISTS */
+				if (node->renameType == OBJECT_COLUMN)
+				{
+					appendStringInfoString(&fmtStr, " %{if_exists}s");
+					new_jsonb_VA(state, 1,
+								 "if_exists", jbvString,
+								 node->missing_ok ? "IF EXISTS" : "");
+				}
+
+				if (!node->relation->inh)
+				{
+					appendStringInfoString(&fmtStr, " %{only}s");
+					new_jsonb_VA(state, 1, "only", jbvString, "ONLY");
+				}
+
+				appendStringInfoString(&fmtStr, " %{identity}D RENAME COLUMN"
+									   " %{colname}I TO %{newname}I");
+				insert_identity_object(state, schemaId, node->relation->relname);
+				new_jsonb_VA(state, 2,
+							 "colname", jbvString, node->subname,
+							 "newname", jbvString, node->newname);
+
+				if (node->renameType == OBJECT_ATTRIBUTE)
+				{
+
+					if (node->behavior == DROP_CASCADE)
+					{
+						appendStringInfoString(&fmtStr, " %{cascade}s");
+						new_jsonb_VA(state, 1, "cascade", jbvString, "CASCADE");
+					}
+				}
+
+				/* We have full fmt by now, so add jsonb element for that */
+				new_jsonb_VA(state, 1, "fmt", jbvString, fmtStr.data);
+
+				pfree(fmtStr.data);
+
+				value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+				relation_close(relation, AccessShareLock);
+				break;
+			}
+
+		default:
+			elog(ERROR, "unsupported object type %d", node->renameType);
+	}
+
+	return JsonbValueToJsonb(value);
+}
+
+/*
+ * Deparse an AlterObjectSchemaStmt (ALTER TABLE... SET SCHEMA command)
+ *
+ * Given the object(table) address and the parse tree that created it, return
+ * Jsonb representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER %{objtype}s %{identity}s SET SCHEMA %{newschema}I
+ */
+static Jsonb *
+deparse_AlterObjectSchemaStmt(ObjectAddress address, Node *parsetree,
+							  ObjectAddress old_schema)
+{
+	AlterObjectSchemaStmt *node = (AlterObjectSchemaStmt *) parsetree;
+	char	   *identity;
+	char	   *new_schema = node->newschema;
+	char	   *old_schname;
+	char	   *ident;
+	JsonbParseState *state = NULL;
+	JsonbValue *value;
+
+	/*
+	 * Since the command has already taken place from the point of view of
+	 * catalogs, getObjectIdentity returns the object name with the already
+	 * changed schema.  The output of our deparsing must return the original
+	 * schema name, however, so we chop the schema name off the identity
+	 * string and then prepend the quoted schema name.
+	 *
+	 * XXX This is pretty clunky. Can we do better?
+	 */
+	identity = getObjectIdentity(&address, false);
+	old_schname = get_namespace_name(old_schema.objectId);
+	if (!old_schname)
+		elog(ERROR, "cache lookup failed for schema with OID %u",
+			 old_schema.objectId);
+
+	ident = psprintf("%s%s", quote_identifier(old_schname),
+					 identity + strlen(quote_identifier(new_schema)));
+
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+	new_jsonb_VA(state, 4,
+				 "fmt", jbvString,
+				 "ALTER %{objtype}s %{identity}s SET SCHEMA"
+				 " %{newschema}I",
+				 "objtype", jbvString,
+				 stringify_objtype(node->objectType),
+				 "identity", jbvString, ident,
+				 "newschema", jbvString, new_schema);
+	value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	return JsonbValueToJsonb(value);
+}
+
 /*
  * Handle deparsing of simple commands.
  *
@@ -1789,6 +3402,11 @@ deparse_simple_command(CollectedCommand *cmd)
 	/* This switch needs to handle everything that ProcessUtilitySlow does */
 	switch (nodeTag(parsetree))
 	{
+		case T_AlterObjectSchemaStmt:
+			return deparse_AlterObjectSchemaStmt(cmd->d.simple.address,
+												 parsetree,
+												 cmd->d.simple.secondaryObject);
+
 		case T_AlterSeqStmt:
 			return deparse_AlterSeqStmt(objectId, parsetree);
 
@@ -1797,6 +3415,8 @@ deparse_simple_command(CollectedCommand *cmd)
 
 		case T_CreateStmt:
 			return deparse_CreateStmt(objectId, parsetree);
+		case T_RenameStmt:
+			return deparse_RenameStmt(cmd->d.simple.address, parsetree);
 
 		default:
 			elog(LOG, "unrecognized node type in deparse command: %d",
@@ -1851,6 +3471,9 @@ deparse_utility_command(CollectedCommand *cmd)
 		case SCT_Simple:
 			jsonb = deparse_simple_command(cmd);
 			break;
+		case SCT_AlterTable:
+			jsonb = deparse_AlterTableStmt(cmd);
+			break;
 		default:
 			elog(ERROR, "unexpected deparse node type %d", cmd->type);
 	}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 30b51bf4d3..c0f7f29747 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -2206,6 +2206,23 @@ UtilityContainsQuery(Node *parsetree)
 	}
 }
 
+/*
+ * stringify_objtype
+ * 		Return the given object type as a string.
+ */
+const char *
+stringify_objtype(ObjectType objtype)
+{
+	switch (objtype)
+	{
+		case OBJECT_TABLE:
+			return "TABLE";
+		default:
+			elog(ERROR, "unsupported object type %d", objtype);
+	}
+
+	return "???";				/* keep compiler quiet */
+}
 
 /*
  * AlterObjectTypeCommandTag
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index 59e64aea07..22ce3e1b6f 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -99,6 +99,8 @@ extern Query *UtilityContainsQuery(Node *parsetree);
 
 extern CommandTag CreateCommandTag(Node *parsetree);
 
+extern const char *stringify_objtype(ObjectType objtype);
+
 static inline const char *
 CreateCommandName(Node *parsetree)
 {
-- 
2.34.1