Subject: RE: Support logical replication of DDLs

From: "Wei Wang (Fujitsu)" <[email-masked]>
Date: Wed, 31 May 2023 09:02:22 +0000

On Tues, May 30, 2023 at 19:19 PM vignesh C <[email-masked]> wrote:
> The attached patch has the changes for the above.

Thanks for updating the new patch set.
Here are some comments:

===
For patch 0001
1. In the function deparse_Seq_As.
```
+	if (OidIsValid(seqdata->seqtypid))
+		append_object_object(ret, "seqtype",
+							 new_objtree_for_type(seqdata->seqtypid, -1));
+	else
+		append_not_present(ret);
```

I think seqdata->seqtypid is always valid because we get this value from
pg_sequence. I think it's fine to not check this value here.

~~~

2. Deparsed results of the partition table.
When I run the following SQLs:
```
create table parent (a int primary key) partition by range (a);
create table child partition of parent default;
```

I got the following two deparsed results:
```
CREATE  TABLE  public.parent (a pg_catalog.int4 STORAGE PLAIN      , CONSTRAINT parent_pkey PRIMARY KEY (a))  PARTITION BY RANGE (a)
CREATE  TABLE  public.child PARTITION OF public.parent (CONSTRAINT child_pkey PRIMARY KEY (a)) DEFAULT
```

When I run these two deparsed results on another instance, I got the following error:
```
postgres=# CREATE  TABLE  public.parent (a pg_catalog.int4 STORAGE PLAIN      , CONSTRAINT parent_pkey PRIMARY KEY (a))  PARTITION BY RANGE (a);
CREATE TABLE
postgres=# CREATE  TABLE  public.child PARTITION OF public.parent (CONSTRAINT child_pkey PRIMARY KEY (a)) DEFAULT;
ERROR:  multiple primary keys for table "child" are not allowed
```

I think that we could skip deparsing the primary key related constraint for
partition (child) table in the function obtainConstraints for this case.

===
For patch 0008
3. Typos in the comments atop the function append_object_to_format_string
```
- * Return the object name which is extracted from the input "*%{name[:.]}*"
- * style string. And append the input format string to the ObjTree.
+ * Append new jsonb key:value pair to the output parse state -- varargs version.
+ *
+ * The "fmt" argument is used to append as a "fmt" element in current object.
+ * The "skipObject" argument indicates if we want to skip object creation
+ * considering it will be taken care by the caller.
+ * The "numobjs" argument indicates the number of extra elements to append;
+ * for each one, a name (string), type (from the jbvType enum) and value must
+ * be supplied.  The value must match the type given; for instance, jbvBool
+ * requires an * bool, jbvString requires a char * and so no,
+ * Each element type  must match the conversion specifier given in the format
+ * string, as described in ddl_deparse_expand_command.
+ *
+ * Note we don't have the luxury of sprintf-like compiler warnings for
+ * malformed argument lists.
  */
-static char *
-append_object_to_format_string(ObjTree *tree, char *sub_fmt)
+static JsonbValue *
+new_jsonb_VA(JsonbParseState *state, char *fmt, bool skipObject, int numobjs,...)
```

s/and so no/and so on
s/requires an * bool/requires an bool
s/type  must/type must

~~~

4. Typos of the function new_jsonb_for_type
```
 /*
- * Allocate a new object tree to store parameter values.
+ * A helper routine to insert jsonb for coltyp to the output parse state.
  */
-static ObjTree *
-new_objtree(char *fmt)
+static void
+new_jsonb_for_type(JsonbParseState *state, Oid typeId, int32 typmod)
...
+	format_type_detailed(typeId, typmod,
+						 &typnspid, &type_name, &typmodstr, &type_array);
```

s/coltyp/typId
s/typeId/typId

~~~

5. In the function deparse_ColumnDef_toJsonb
+	/*
+	 * create coltype object having 4 elements: schemaname, typename, typemod,
+	 * typearray
+	 */
+	{
+		/* Push the key first */
+		insert_jsonb_key(state, "coltype");
+
+		/* Push the value */
+		new_jsonb_for_type(state, typid, typmod);
+	}

The '{ }' here seems a little strange. Do we need them?
Many places have written the same as here in this patch.

Regards,
Wang wei

---

From: vignesh C <[email-masked]>
Date: Thu, 1 Jun 2023 21:12:16 +0530

> pg_sequence. I think it's fine to not check this value here.

Modified in 0008 patch

> partition (child) table in the function obtainConstraints for this case.

Not applicable for 0008 patch

> s/requires an * bool/requires an bool

Modified in 0008 patch

> +                                                &typnspid, &type_name, &typmodstr, &type_array);

Modified in 0008 patch

> Many places have written the same as here in this patch.

Modified in 0008 patch

The attached patch has the changes for the same. The 0008 patch was
modified to fix the above comments.

Regards,
Vignesh
From 27a787cfb155e91ad7dc0d9a2e972433b686c091 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email-masked]>
Date: Mon, 29 May 2023 15:54:06 +0530
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 | 628 ++++++++++++++++++++----------
 src/include/tcop/ddldeparse.h     |   2 +-
 2 files changed, 424 insertions(+), 206 deletions(-)

diff --git a/src/backend/commands/ddldeparse.c b/src/backend/commands/ddldeparse.c
index 6a001acf41..6799c7fdaf 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,35 @@ 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),
-								 "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));
 
 			/* 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 +3113,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 +3149,8 @@ deparse_utility_command(CollectedCommand *cmd)
 	overridePath->addTemp = true;
 	PushOverrideSearchPath(overridePath);
 
+	verbose = verbose_mode;
+
 	switch (cmd->type)
 	{
 		case SCT_Simple:
@@ -2980,7 +3198,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

From bf772b25d72f1be588c12f836e42569d0e92a871 Mon Sep 17 00:00:00 2001
From: Wang Wei <[email-masked]>
Date: Mon, 24 Apr 2023 17:02:30 +0800
Subject: [PATCH 2/8] Enhance the event trigger to support DDL deparsing

1) ALTER TABLE can have multiple subcommands which might include DROP COLUMN
command and ALTER TYPE referring the drop column in USING expression. As the
dropped column cannot be accessed after the execution of DROP COLUMN, a special
trigger is added to handle this case before the drop column is executed.

2) For CREATE TABLE AS, to avoid the need to ensure all the objects referenced
in the subquery also exists in subscriber, deprase the CREATE TABLE AS into a
simple CREATE TABLE(without subquery) command and WAL log it after creating the
table and before writing data into the table and replicate the incoming writes
later as normal INSERTs. This approach works for all kind of commands(e.g.
CRAETE TABLE AS [SELECT][EXECUTE][VALUES]).

To achieve this, introduce a new type of event trigger "table_init_write".
which would be fired for CREATE TABLE/CREATE TABLE AS/SELECT INTO after
creating the table and before any other modification. we deparse the command in
the table_init_write event trigger and WAL log the deparsed json string. The
walsender will send the string to subscriber. And incoming INSERTs will also be
replicated.
---
 src/backend/commands/createas.c               |  10 +
 src/backend/commands/ddldeparse.c             |  37 +-
 src/backend/commands/event_trigger.c          | 350 +++++++++++++++---
 src/backend/commands/tablecmds.c              |  10 +-
 src/backend/tcop/utility.c                    |   3 +
 src/backend/utils/cache/evtcache.c            |   2 +
 src/include/commands/event_trigger.h          |  50 ++-
 src/include/tcop/deparse_utility.h            |  12 +-
 src/include/utils/evtcache.h                  |   3 +-
 .../t/000_stream_subxact_abort.pl             | 263 +++++++++++++
 10 files changed, 678 insertions(+), 62 deletions(-)
 create mode 100644 src/test/subscription/t/000_stream_subxact_abort.pl

diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index e91920ca14..a7b22cb5db 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -34,6 +34,7 @@
 #include "catalog/namespace.h"
 #include "catalog/toasting.h"
 #include "commands/createas.h"
+#include "commands/event_trigger.h"
 #include "commands/matview.h"
 #include "commands/prepare.h"
 #include "commands/tablecmds.h"
@@ -143,6 +144,15 @@ create_ctas_internal(List *attrList, IntoClause *into)
 		StoreViewQuery(intoRelationAddr.objectId, query, false);
 		CommandCounterIncrement();
 	}
+	else
+	{
+		/*
+		 * Fire the trigger for table_init_write after creating the table so
+		 * that we can access the catalog info about the newly created table
+		 * in the trigger function.
+		 */
+		EventTriggerTableInitWrite((Node *) create, intoRelationAddr);
+	}
 
 	return intoRelationAddr;
 }
diff --git a/src/backend/commands/ddldeparse.c b/src/backend/commands/ddldeparse.c
index 3eeaeee716..6a001acf41 100644
--- a/src/backend/commands/ddldeparse.c
+++ b/src/backend/commands/ddldeparse.c
@@ -1761,6 +1761,26 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 	return ret;
 }
 
+/*
+ * Deparse CREATE TABLE AS command.
+ *
+ * deparse_CreateStmt do the actual work as we deparse the final CreateStmt for
+ * CREATE TABLE AS command.
+ */
+static ObjTree *
+deparse_CreateTableAsStmt(CollectedCommand *cmd)
+{
+	Oid			objectId;
+	Node	   *parsetree;
+
+	Assert(cmd->type == SCT_CreateTableAs);
+
+	parsetree = cmd->d.ctas.real_create;
+	objectId = cmd->d.ctas.address.objectId;
+
+	return deparse_CreateStmt(objectId, parsetree);
+}
+
 /*
  * Deparse all the collected subcommands and return an ObjTree representing the
  * alter command.
@@ -2188,20 +2208,8 @@ deparse_AlterRelation(CollectedCommand *cmd)
 					 */
 					tmp_obj2 = new_objtree("USING %{expression}s");
 					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));
-
 						append_string_object(tmp_obj2, "expression",
-											 TextDatumGetCString(deparsed));
-					}
+											 sub->usingexpr);
 					else
 						append_not_present(tmp_obj2);
 
@@ -2933,6 +2941,9 @@ deparse_utility_command(CollectedCommand *cmd)
 		case SCT_AlterTable:
 			tree = deparse_AlterRelation(cmd);
 			break;
+		case SCT_CreateTableAs:
+			tree = deparse_CreateTableAsStmt(cmd);
+			break;
 		default:
 			elog(ERROR, "unexpected deparse node type %d", cmd->type);
 	}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 4d48e490ed..8561c6fad0 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -36,6 +36,7 @@
 #include "lib/ilist.h"
 #include "miscadmin.h"
 #include "parser/parse_func.h"
+#include "parser/parser.h"
 #include "pgstat.h"
 #include "tcop/ddldeparse.h"
 #include "tcop/deparse_utility.h"
@@ -49,45 +50,7 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
-typedef struct EventTriggerQueryState
-{
-	/* memory context for this state's objects */
-	MemoryContext cxt;
-
-	/* sql_drop */
-	slist_head	SQLDropList;
-	bool		in_sql_drop;
-
-	/* table_rewrite */
-	Oid			table_rewrite_oid;	/* InvalidOid, or set for table_rewrite
-									 * event */
-	int			table_rewrite_reason;	/* AT_REWRITE reason */
-
-	/* Support for command collection */
-	bool		commandCollectionInhibited;
-	CollectedCommand *currentCommand;
-	List	   *commandList;	/* list of CollectedCommand; see
-								 * deparse_utility.h */
-	struct EventTriggerQueryState *previous;
-} EventTriggerQueryState;
-
-static EventTriggerQueryState *currentEventTriggerState = NULL;
-
-/* Support for dropped objects */
-typedef struct SQLDropObject
-{
-	ObjectAddress address;
-	const char *schemaname;
-	const char *objname;
-	const char *objidentity;
-	const char *objecttype;
-	List	   *addrnames;
-	List	   *addrargs;
-	bool		original;
-	bool		normal;
-	bool		istemp;
-	slist_node	next;
-} SQLDropObject;
+EventTriggerQueryState *currentEventTriggerState = NULL;
 
 static void AlterEventTriggerOwner_internal(Relation rel,
 											HeapTuple tup,
@@ -131,7 +94,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 	if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
 		strcmp(stmt->eventname, "ddl_command_end") != 0 &&
 		strcmp(stmt->eventname, "sql_drop") != 0 &&
-		strcmp(stmt->eventname, "table_rewrite") != 0)
+		strcmp(stmt->eventname, "table_rewrite") != 0 &&
+		strcmp(stmt->eventname, "table_init_write") != 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("unrecognized event name \"%s\"",
@@ -157,7 +121,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 	/* Validate tag list, if any. */
 	if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
 		 strcmp(stmt->eventname, "ddl_command_end") == 0 ||
-		 strcmp(stmt->eventname, "sql_drop") == 0)
+		 strcmp(stmt->eventname, "sql_drop") == 0 ||
+		 strcmp(stmt->eventname, "table_init_write") == 0)
 		&& tags != NULL)
 		validate_ddl_tags("tag", tags);
 	else if (strcmp(stmt->eventname, "table_rewrite") == 0
@@ -583,7 +548,8 @@ EventTriggerCommonSetup(Node *parsetree,
 		dbgtag = CreateCommandTag(parsetree);
 		if (event == EVT_DDLCommandStart ||
 			event == EVT_DDLCommandEnd ||
-			event == EVT_SQLDrop)
+			event == EVT_SQLDrop ||
+			event == EVT_TableInitWrite)
 		{
 			if (!command_tag_event_trigger_ok(dbgtag))
 				elog(ERROR, "unexpected command tag \"%s\"", GetCommandTagName(dbgtag));
@@ -866,6 +832,153 @@ EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
 	CommandCounterIncrement();
 }
 
+/*
+ * EventTriggerTableInitWriteStart
+ *     Prepare to receive data on an CREATE TABLE AS/SELECT INTO command about
+ *     to be executed.
+ */
+void
+EventTriggerTableInitWriteStart(Node *parsetree)
+{
+	MemoryContext oldcxt;
+	CollectedCommand *command;
+	CreateTableAsStmt *stmt = (CreateTableAsStmt *)parsetree;
+
+	/* ignore if event trigger context not set, or collection disabled */
+	if (!currentEventTriggerState ||
+		currentEventTriggerState->commandCollectionInhibited)
+		return;
+
+	oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+	command = palloc(sizeof(CollectedCommand));
+
+	command->type = (stmt->objtype == OBJECT_TABLE) ? SCT_CreateTableAs : SCT_Simple;
+	command->in_extension = creating_extension;
+	command->d.ctas.address = InvalidObjectAddress;
+	command->d.ctas.real_create = NULL;
+	command->parsetree = copyObject(parsetree);
+
+	command->parent = currentEventTriggerState->currentCommand;
+	currentEventTriggerState->currentCommand = command;
+
+	MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * EventTriggerTableInitWriteEnd
+ *		Finish up saving an CREATE TABLE AS/SELECT INTO command.
+ *
+ * FIXME this API isn't considering the possibility that an xact/subxact is
+ * aborted partway through.  Probably it's best to add an
+ * AtEOSubXact_EventTriggers() to fix this.
+ */
+void
+EventTriggerTableInitWriteEnd(ObjectAddress address)
+{
+	CollectedCommand *parent;
+	CreateTableAsStmt *stmt;
+
+	/* ignore if event trigger context not set, or collection disabled */
+	if (!currentEventTriggerState ||
+		currentEventTriggerState->commandCollectionInhibited)
+		return;
+
+	Assert(currentEventTriggerState->currentCommand != NULL);
+
+	stmt =  (CreateTableAsStmt *)currentEventTriggerState->currentCommand->parsetree;
+
+	if (stmt->objtype == OBJECT_TABLE)
+	{
+		MemoryContext oldcxt;
+
+		parent = currentEventTriggerState->currentCommand->parent;
+
+		oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+		currentEventTriggerState->commandList =
+			lappend(currentEventTriggerState->commandList,
+					currentEventTriggerState->currentCommand);
+
+		MemoryContextSwitchTo(oldcxt);
+
+		currentEventTriggerState->currentCommand = parent;
+	}
+	else
+	{
+		MemoryContext oldcxt;
+
+		oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+		currentEventTriggerState->currentCommand->d.simple.address = address;
+		currentEventTriggerState->commandList =
+			lappend(currentEventTriggerState->commandList,
+					currentEventTriggerState->currentCommand);
+
+		MemoryContextSwitchTo(oldcxt);
+	}
+}
+
+/*
+ * Fire table_init_rewrite triggers.
+ */
+void
+EventTriggerTableInitWrite(Node *real_create, ObjectAddress address)
+{
+	List	   *runlist;
+	EventTriggerData trigdata;
+	CollectedCommand *command;
+	MemoryContext oldcxt;
+
+	/*
+	 * See EventTriggerDDLCommandStart for a discussion about why event
+	 * triggers are disabled in single user mode.
+	 */
+	if (!IsUnderPostmaster)
+		return;
+
+	/*
+	 * Also do nothing if our state isn't set up, which it won't be if there
+	 * weren't any relevant event triggers at the start of the current DDL
+	 * command.  This test might therefore seem optional, but it's
+	 * *necessary*, because EventTriggerCommonSetup might find triggers that
+	 * didn't exist at the time the command started.
+	 */
+	if (!currentEventTriggerState)
+		return;
+
+	/* Do nothing if no command was collected. */
+	if (!currentEventTriggerState->currentCommand)
+		return;
+
+	command = currentEventTriggerState->currentCommand;
+
+	/* Set the real CreateTable statment and object address */
+	oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+	command->d.ctas.address = address;
+	command->d.ctas.real_create = copyObject(real_create);
+	MemoryContextSwitchTo(oldcxt);
+
+	runlist = EventTriggerCommonSetup(command->parsetree,
+									  EVT_TableInitWrite,
+									  "table_init_write",
+									  &trigdata);
+	if (runlist == NIL)
+		return;
+
+	/* Run the triggers. */
+	EventTriggerInvoke(runlist, &trigdata);
+
+	/* Cleanup. */
+	list_free(runlist);
+
+	/*
+	 * Make sure anything the event triggers did will be visible to the main
+	 * command.
+	 */
+	CommandCounterIncrement();
+}
+
 /*
  * Invoke each event trigger in a list of event triggers.
  */
@@ -1147,7 +1260,8 @@ trackDroppedObjectsNeeded(void)
 	 */
 	return (EventCacheLookup(EVT_SQLDrop) != NIL) ||
 		(EventCacheLookup(EVT_TableRewrite) != NIL) ||
-		(EventCacheLookup(EVT_DDLCommandEnd) != NIL);
+		(EventCacheLookup(EVT_DDLCommandEnd) != NIL) ||
+		(EventCacheLookup(EVT_TableInitWrite) != NIL);
 }
 
 /*
@@ -1538,6 +1652,7 @@ EventTriggerAlterTableStart(Node *parsetree)
 
 	command->d.alterTable.classId = RelationRelationId;
 	command->d.alterTable.objectId = InvalidOid;
+	command->d.alterTable.rewrite = false;
 	command->d.alterTable.subcmds = NIL;
 	command->parsetree = copyObject(parsetree);
 
@@ -1571,7 +1686,7 @@ EventTriggerAlterTableRelid(Oid objectId)
  * internally, so that's all that this code needs to handle at the moment.
  */
 void
-EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
+EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address, bool rewrite)
 {
 	MemoryContext oldcxt;
 	CollectedATSubcmd *newsub;
@@ -1591,12 +1706,156 @@ EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
 	newsub->address = address;
 	newsub->parsetree = copyObject(subcmd);
 
+	currentEventTriggerState->currentCommand->d.alterTable.rewrite |= rewrite;
+	currentEventTriggerState->currentCommand->d.alterTable.subcmds =
+		lappend(currentEventTriggerState->currentCommand->d.alterTable.subcmds, newsub);
+
+	MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * EventTriggerAlterTypeStart
+ *		Save data about a single part of an ALTER TYPE.
+ *
+ * ALTER TABLE can have multiple subcommands which might include DROP COLUMN
+ * command and ALTER TYPE referring the drop column in USING expression.
+ * As the dropped column cannot be accessed after the execution of DROP COLUMN,
+ * a special trigger is required to handle this case before the drop column is
+ * executed.
+ */
+void
+EventTriggerAlterTypeStart(AlterTableCmd *subcmd, Relation rel)
+{
+	MemoryContext oldcxt;
+	CollectedATSubcmd *newsub;
+	ColumnDef  *def;
+	Relation	attrelation;
+	HeapTuple	heapTup;
+	Form_pg_attribute attTup;
+	AttrNumber	attnum;
+	ObjectAddress address;
+
+	/* ignore if event trigger context not set, or collection disabled */
+	if (!currentEventTriggerState ||
+		currentEventTriggerState->commandCollectionInhibited)
+		return;
+
+	Assert(IsA(subcmd, AlterTableCmd));
+	Assert(subcmd->subtype == AT_AlterColumnType);
+	Assert(currentEventTriggerState->currentCommand != NULL);
+	Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
+
+	def = (ColumnDef *) subcmd->def;
+	Assert(IsA(def, ColumnDef));
+
+	oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+	newsub = palloc(sizeof(CollectedATSubcmd));
+	newsub->parsetree = (Node *)copyObject(subcmd);
+
+	attrelation = table_open(AttributeRelationId, RowExclusiveLock);
+
+	/* Look up the target column */
+	heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), subcmd->name);
+	if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
+		ereport(ERROR,
+				errcode(ERRCODE_UNDEFINED_COLUMN),
+				errmsg("column \"%s\" of relation \"%s\" does not exist",
+					   subcmd->name, RelationGetRelationName(rel)));
+	attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
+	attnum = attTup->attnum;
+
+	ObjectAddressSubSet(address, RelationRelationId,
+						RelationGetRelid(rel), attnum);
+	heap_freetuple(heapTup);
+	table_close(attrelation, RowExclusiveLock);
+	newsub->address = address;
+
+	if (def->raw_default)
+	{
+		OverrideSearchPath *overridePath;
+		char	   *defexpr;
+
+		/*
+		 * We want all object names to be qualified when deparsing the
+		 * expression, so that results are "portable" to environments with
+		 * different search_path settings. Rather than inject what would be
+		 * repetitive calls to override search path all over the place, we do
+		 * it centrally here.
+		 */
+		overridePath = GetOverrideSearchPath(CurrentMemoryContext);
+		overridePath->schemas = NIL;
+		overridePath->addCatalog = false;
+		overridePath->addTemp = true;
+		PushOverrideSearchPath(overridePath);
+
+		defexpr = nodeToString(def->cooked_default);
+		newsub->usingexpr = TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
+																	CStringGetTextDatum(defexpr),
+																	RelationGetRelid(rel)));
+
+		PopOverrideSearchPath();
+	}
+	else
+		newsub->usingexpr = NULL;
+
 	currentEventTriggerState->currentCommand->d.alterTable.subcmds =
 		lappend(currentEventTriggerState->currentCommand->d.alterTable.subcmds, newsub);
 
 	MemoryContextSwitchTo(oldcxt);
 }
 
+/*
+ * EventTriggerAlterTypeEnd
+ *		Finish up saving an ALTER TYPE command, and add it to command list.
+ */
+void
+EventTriggerAlterTypeEnd(Node *subcmd, ObjectAddress address, bool rewrite)
+{
+	MemoryContext oldcxt;
+	CollectedATSubcmd *newsub;
+	ListCell   *cell;
+	CollectedCommand *cmd;
+	AlterTableCmd *altsubcmd = (AlterTableCmd *)subcmd;
+
+	/* ignore if event trigger context not set, or collection disabled */
+	if (!currentEventTriggerState ||
+		currentEventTriggerState->commandCollectionInhibited)
+		return;
+
+	cmd = currentEventTriggerState->currentCommand;
+
+	Assert(IsA(subcmd, AlterTableCmd));
+	Assert(cmd != NULL);
+	Assert(OidIsValid(cmd->d.alterTable.objectId));
+
+	foreach(cell, cmd->d.alterTable.subcmds)
+	{
+		CollectedATSubcmd *sub = (CollectedATSubcmd *) lfirst(cell);
+		AlterTableCmd *collcmd = (AlterTableCmd *) sub->parsetree;
+
+		if (collcmd->subtype == altsubcmd->subtype &&
+			address.classId == sub->address.classId &&
+			address.objectId == sub->address.objectId &&
+			address.objectSubId == sub->address.objectSubId)
+		{
+			cmd->d.alterTable.rewrite |= rewrite;
+			return;
+		}
+	}
+
+	oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+	newsub = palloc(sizeof(CollectedATSubcmd));
+	newsub->address = address;
+	newsub->parsetree = copyObject(subcmd);
+
+	cmd->d.alterTable.rewrite |= rewrite;
+	cmd->d.alterTable.subcmds = lappend(cmd->d.alterTable.subcmds, newsub);
+
+	MemoryContextSwitchTo(oldcxt);
+}
+
 /*
  * EventTriggerAlterTableEnd
  *		Finish up saving an ALTER TABLE command, and add it to command list.
@@ -1864,6 +2123,7 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
 			case SCT_AlterOpFamily:
 			case SCT_CreateOpClass:
 			case SCT_AlterTSConfig:
+			case SCT_CreateTableAs:
 				{
 					char	   *identity;
 					char	   *type;
@@ -1881,6 +2141,8 @@ pg_event_trigger_ddl_commands(PG_FUNCTION_ARGS)
 						addr = cmd->d.createopc.address;
 					else if (cmd->type == SCT_AlterTSConfig)
 						addr = cmd->d.atscfg.address;
+					else if (cmd->type == SCT_CreateTableAs)
+						addr = cmd->d.ctas.address;
 
 					/*
 					 * If an object was dropped in the same command we may end
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c15e943f1d..d0e649baaf 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4742,6 +4742,9 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
 									  AT_PASS_UNSET, context);
 			Assert(cmd != NULL);
+
+			EventTriggerAlterTypeStart(cmd, rel);
+
 			/* Performs own recursion */
 			ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
 								  lockmode, context);
@@ -5013,6 +5016,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 {
 	ObjectAddress address = InvalidObjectAddress;
 	Relation	rel = tab->rel;
+	bool		commandCollected = false;
 
 	switch (cmd->subtype)
 	{
@@ -5136,6 +5140,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 		case AT_AlterColumnType:	/* ALTER COLUMN TYPE */
 			/* parse transformation was done earlier */
 			address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
+			EventTriggerAlterTypeEnd((Node *) cmd, address, tab->rewrite);
+			commandCollected = true;
 			break;
 		case AT_AlterColumnGenericOptions:	/* ALTER COLUMN OPTIONS */
 			address =
@@ -5308,8 +5314,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 	/*
 	 * Report the subcommand to interested event triggers.
 	 */
-	if (cmd)
-		EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
+	if (cmd && !commandCollected)
+		EventTriggerCollectAlterTableSubcmd((Node *) cmd, address, tab->rewrite);
 
 	/*
 	 * Bump the command counter to ensure the next subcommand in the sequence
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index c0f7f29747..3aa6270a88 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1675,8 +1675,11 @@ ProcessUtilitySlow(ParseState *pstate,
 				break;
 
 			case T_CreateTableAsStmt:
+				EventTriggerTableInitWriteStart(parsetree);
 				address = ExecCreateTableAs(pstate, (CreateTableAsStmt *) parsetree,
 											params, queryEnv, qc);
+				EventTriggerTableInitWriteEnd(address);
+				commandCollected = true;
 				break;
 
 			case T_RefreshMatViewStmt:
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 1f5e7eb4c6..f2a9f5dcc2 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -167,6 +167,8 @@ BuildEventTriggerCache(void)
 			event = EVT_SQLDrop;
 		else if (strcmp(evtevent, "table_rewrite") == 0)
 			event = EVT_TableRewrite;
+		else if (strcmp(evtevent, "table_init_write") == 0)
+			event = EVT_TableInitWrite;
 		else
 			continue;
 
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 5ed6ece555..cba4e72455 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -16,6 +16,7 @@
 #include "catalog/dependency.h"
 #include "catalog/objectaddress.h"
 #include "catalog/pg_event_trigger.h"
+#include "lib/ilist.h"
 #include "nodes/parsenodes.h"
 #include "tcop/cmdtag.h"
 #include "tcop/deparse_utility.h"
@@ -29,6 +30,44 @@ typedef struct EventTriggerData
 	CommandTag	tag;
 } EventTriggerData;
 
+typedef struct EventTriggerQueryState
+{
+	/* memory context for this state's objects */
+	MemoryContext cxt;
+
+	/* sql_drop */
+	slist_head	SQLDropList;
+	bool		in_sql_drop;
+
+	/* table_rewrite */
+	Oid			table_rewrite_oid;	/* InvalidOid, or set for table_rewrite
+									 * event */
+	int			table_rewrite_reason;	/* AT_REWRITE reason */
+
+	/* Support for command collection */
+	bool		commandCollectionInhibited;
+	CollectedCommand *currentCommand;
+	List	   *commandList;	/* list of CollectedCommand; see
+								 * deparse_utility.h */
+	struct EventTriggerQueryState *previous;
+} EventTriggerQueryState;
+
+/* Support for dropped objects */
+typedef struct SQLDropObject
+{
+	ObjectAddress address;
+	const char *schemaname;
+	const char *objname;
+	const char *objidentity;
+	const char *objecttype;
+	List	   *addrnames;
+	List	   *addrargs;
+	bool		original;
+	bool		normal;
+	bool		istemp;
+	slist_node	next;
+} SQLDropObject;
+
 #define AT_REWRITE_ALTER_PERSISTENCE	0x01
 #define AT_REWRITE_DEFAULT_VAL			0x02
 #define AT_REWRITE_COLUMN_REWRITE		0x04
@@ -55,6 +94,10 @@ extern void EventTriggerDDLCommandEnd(Node *parsetree);
 extern void EventTriggerSQLDrop(Node *parsetree);
 extern void EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason);
 
+extern void EventTriggerTableInitWriteStart(Node *parsetree);
+extern void EventTriggerTableInitWrite(Node *parsetree, ObjectAddress address);
+extern void EventTriggerTableInitWriteEnd(ObjectAddress address);
+
 extern bool EventTriggerBeginCompleteQuery(void);
 extern void EventTriggerEndCompleteQuery(void);
 extern bool trackDroppedObjectsNeeded(void);
@@ -71,7 +114,12 @@ extern void EventTriggerCollectSimpleCommand(ObjectAddress address,
 extern void EventTriggerAlterTableStart(Node *parsetree);
 extern void EventTriggerAlterTableRelid(Oid objectId);
 extern void EventTriggerCollectAlterTableSubcmd(Node *subcmd,
-												ObjectAddress address);
+												ObjectAddress address,
+												bool rewrite);
+
+extern void EventTriggerAlterTypeStart(AlterTableCmd *subcmd, Relation rel);
+extern void EventTriggerAlterTypeEnd(Node *subcmd, ObjectAddress address,
+									 bool rewrite);
 extern void EventTriggerAlterTableEnd(void);
 
 extern void EventTriggerCollectGrant(InternalGrant *istmt);
diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h
index b585810b9a..a4a12377b8 100644
--- a/src/include/tcop/deparse_utility.h
+++ b/src/include/tcop/deparse_utility.h
@@ -29,7 +29,8 @@ typedef enum CollectedCommandType
 	SCT_AlterOpFamily,
 	SCT_AlterDefaultPrivileges,
 	SCT_CreateOpClass,
-	SCT_AlterTSConfig
+	SCT_AlterTSConfig,
+	SCT_CreateTableAs
 } CollectedCommandType;
 
 /*
@@ -39,6 +40,7 @@ typedef struct CollectedATSubcmd
 {
 	ObjectAddress address;		/* affected column, constraint, index, ... */
 	Node	   *parsetree;
+	char	   *usingexpr;
 } CollectedATSubcmd;
 
 typedef struct CollectedCommand
@@ -62,6 +64,7 @@ typedef struct CollectedCommand
 		{
 			Oid			objectId;
 			Oid			classId;
+			bool		rewrite;
 			List	   *subcmds;
 		}			alterTable;
 
@@ -100,6 +103,13 @@ typedef struct CollectedCommand
 		{
 			ObjectType	objtype;
 		}			defprivs;
+
+		/* CREATE TABLE AS */
+		struct
+		{
+			ObjectAddress address;
+			Node		 *real_create;
+		}			ctas;
 	}			d;
 
 	struct CollectedCommand *parent;	/* when nested */
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index d340026518..91d4bdd6b3 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -22,7 +22,8 @@ typedef enum
 	EVT_DDLCommandStart,
 	EVT_DDLCommandEnd,
 	EVT_SQLDrop,
-	EVT_TableRewrite
+	EVT_TableRewrite,
+	EVT_TableInitWrite
 } EventTriggerEvent;
 
 typedef struct
diff --git a/src/test/subscription/t/000_stream_subxact_abort.pl b/src/test/subscription/t/000_stream_subxact_abort.pl
new file mode 100644
index 0000000000..2b67ae1e0a
--- /dev/null
+++ b/src/test/subscription/t/000_stream_subxact_abort.pl
@@ -0,0 +1,263 @@
+
+# Copyright (c) 2021-2023, PostgreSQL Global Development Group
+
+# Test streaming of transaction containing multiple subtransactions and rollbacks
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Check that the parallel apply worker has finished applying the streaming
+# transaction.
+sub check_parallel_log
+{
+	my ($node_subscriber, $offset, $is_parallel, $type) = @_;
+
+	if ($is_parallel)
+	{
+		$node_subscriber->wait_for_log(
+			qr/DEBUG: ( [A-Z0-9]+:)? finished processing the STREAM $type command/,
+			$offset);
+	}
+}
+
+# Common test steps for both the streaming=on and streaming=parallel cases.
+sub test_streaming
+{
+	my ($node_publisher, $node_subscriber, $appname, $is_parallel) = @_;
+
+	my $offset = 0;
+
+	# Check the subscriber log from now on.
+	$offset = -s $node_subscriber->logfile;
+
+	# streamed transaction with DDL, DML and ROLLBACKs
+	$node_publisher->safe_psql(
+		'postgres', q{
+	BEGIN;
+	INSERT INTO test_tab VALUES (3, md5(3::text));
+	SAVEPOINT s1;
+	INSERT INTO test_tab VALUES (4, md5(4::text));
+	SAVEPOINT s2;
+	INSERT INTO test_tab VALUES (5, md5(5::text));
+	SAVEPOINT s3;
+	INSERT INTO test_tab VALUES (6, md5(6::text));
+	ROLLBACK TO s2;
+	INSERT INTO test_tab VALUES (7, md5(7::text));
+	ROLLBACK TO s1;
+	INSERT INTO test_tab VALUES (8, md5(8::text));
+	SAVEPOINT s4;
+	INSERT INTO test_tab VALUES (9, md5(9::text));
+	SAVEPOINT s5;
+	INSERT INTO test_tab VALUES (10, md5(10::text));
+	COMMIT;
+	});
+
+	$node_publisher->wait_for_catchup($appname);
+
+	check_parallel_log($node_subscriber, $offset, $is_parallel, 'COMMIT');
+
+	my $result =
+	  $node_subscriber->safe_psql('postgres',
+		"SELECT count(*), count(c) FROM test_tab");
+	is($result, qq(6|0),
+		'check rollback to savepoint was reflected on subscriber and extra columns contain local defaults'
+	);
+
+	# Check the subscriber log from now on.
+	$offset = -s $node_subscriber->logfile;
+
+	# streamed transaction with subscriber receiving out of order
+	# subtransaction ROLLBACKs
+	$node_publisher->safe_psql(
+		'postgres', q{
+	BEGIN;
+	INSERT INTO test_tab VALUES (11, md5(11::text));
+	SAVEPOINT s1;
+	INSERT INTO test_tab VALUES (12, md5(12::text));
+	SAVEPOINT s2;
+	INSERT INTO test_tab VALUES (13, md5(13::text));
+	SAVEPOINT s3;
+	INSERT INTO test_tab VALUES (14, md5(14::text));
+	RELEASE s2;
+	INSERT INTO test_tab VALUES (15, md5(15::text));
+	ROLLBACK TO s1;
+	COMMIT;
+	});
+
+	$node_publisher->wait_for_catchup($appname);
+
+	check_parallel_log($node_subscriber, $offset, $is_parallel, 'COMMIT');
+
+	$result =
+	  $node_subscriber->safe_psql('postgres',
+		"SELECT count(*), count(c) FROM test_tab");
+	is($result, qq(7|0),
+		'check rollback to savepoint was reflected on subscriber');
+
+	# Check the subscriber log from now on.
+	$offset = -s $node_subscriber->logfile;
+
+	# streamed transaction with subscriber receiving rollback
+	$node_publisher->safe_psql(
+		'postgres', q{
+	BEGIN;
+	INSERT INTO test_tab VALUES (16, md5(16::text));
+	SAVEPOINT s1;
+	INSERT INTO test_tab VALUES (17, md5(17::text));
+	SAVEPOINT s2;
+	INSERT INTO test_tab VALUES (18, md5(18::text));
+	ROLLBACK;
+	});
+
+	$node_publisher->wait_for_catchup($appname);
+
+	check_parallel_log($node_subscriber, $offset, $is_parallel, 'ABORT');
+
+	$result =
+	  $node_subscriber->safe_psql('postgres',
+		"SELECT count(*), count(c) FROM test_tab");
+	is($result, qq(7|0), 'check rollback was reflected on subscriber');
+
+	# Cleanup the test data
+	$node_publisher->safe_psql('postgres',
+		"DELETE FROM test_tab WHERE (a > 2)");
+	$node_publisher->wait_for_catchup($appname);
+}
+
+# Create publisher node
+my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
+$node_publisher->init(allows_streaming => 'logical');
+$node_publisher->append_conf('postgresql.conf',
+	'logical_replication_mode = immediate');
+$node_publisher->start;
+
+# Create subscriber node
+my $node_subscriber = PostgreSQL::Test::Cluster->new('subscriber');
+$node_subscriber->init(allows_streaming => 'logical');
+$node_subscriber->start;
+
+# Create some preexisting content on publisher
+$node_publisher->safe_psql('postgres',
+	"CREATE TABLE test_tab (a int primary key, b varchar)");
+$node_publisher->safe_psql('postgres',
+	"INSERT INTO test_tab VALUES (1, 'foo'), (2, 'bar')");
+$node_publisher->safe_psql('postgres', "CREATE TABLE test_tab_2 (a int)");
+
+# Setup structure on subscriber
+$node_subscriber->safe_psql('postgres',
+	"CREATE TABLE test_tab (a int primary key, b text, c INT, d INT, e INT)");
+$node_subscriber->safe_psql('postgres', "CREATE TABLE test_tab_2 (a int)");
+
+# Setup logical replication
+my $publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
+$node_publisher->safe_psql('postgres',
+	"CREATE PUBLICATION tap_pub FOR TABLE test_tab, test_tab_2");
+
+my $appname = 'tap_sub';
+
+################################
+# Test using streaming mode 'on'
+################################
+$node_subscriber->safe_psql('postgres',
+	"CREATE SUBSCRIPTION tap_sub CONNECTION '$publisher_connstr application_name=$appname' PUBLICATION tap_pub WITH (streaming = on)"
+);
+
+# Wait for initial table sync to finish
+$node_subscriber->wait_for_subscription_sync($node_publisher, $appname);
+
+my $result =
+  $node_subscriber->safe_psql('postgres',
+	"SELECT count(*), count(c) FROM test_tab");
+is($result, qq(2|0), 'check initial data was copied to subscriber');
+
+test_streaming($node_publisher, $node_subscriber, $appname, 0);
+
+######################################
+# Test using streaming mode 'parallel'
+######################################
+my $oldpid = $node_publisher->safe_psql('postgres',
+	"SELECT pid FROM pg_stat_replication WHERE application_name = '$appname' AND state = 'streaming';"
+);
+
+$node_subscriber->safe_psql('postgres',
+	"ALTER SUBSCRIPTION tap_sub SET(streaming = parallel)");
+
+$node_publisher->poll_query_until('postgres',
+	"SELECT pid != $oldpid FROM pg_stat_replication WHERE application_name = '$appname' AND state = 'streaming';"
+  )
+  or die
+  "Timed out while waiting for apply to restart after changing SUBSCRIPTION";
+
+# We need to check DEBUG logs to ensure that the parallel apply worker has
+# applied the transaction. So, bump up the log verbosity.
+$node_subscriber->append_conf('postgresql.conf', "log_min_messages = debug1");
+$node_subscriber->reload;
+
+# Run a query to make sure that the reload has taken effect.
+$node_subscriber->safe_psql('postgres', q{SELECT 1});
+
+test_streaming($node_publisher, $node_subscriber, $appname, 1);
+
+# Test serializing changes to files and notify the parallel apply worker to
+# apply them at the end of the transaction.
+$node_subscriber->append_conf('postgresql.conf',
+	'logical_replication_mode = immediate');
+# Reset the log_min_messages to default.
+$node_subscriber->append_conf('postgresql.conf', "log_min_messages = warning");
+$node_subscriber->reload;
+
+# Run a query to make sure that the reload has taken effect.
+$node_subscriber->safe_psql('postgres', q{SELECT 1});
+
+my $offset = -s $node_subscriber->logfile;
+
+$node_publisher->safe_psql(
+	'postgres', q{
+	BEGIN;
+	INSERT INTO test_tab_2 values(1);
+	ROLLBACK;
+	});
+
+# Ensure that the changes are serialized.
+$node_subscriber->wait_for_log(
+	qr/LOG: ( [A-Z0-9]+:)? logical replication apply worker will serialize the remaining changes of remote transaction \d+ to a file/,
+	$offset);
+
+$node_publisher->wait_for_catchup($appname);
+
+# Check that transaction is aborted on subscriber
+$result =
+  $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM test_tab_2");
+is($result, qq(0), 'check rollback was reflected on subscriber');
+
+# Serialize the ABORT sub-transaction.
+$offset = -s $node_subscriber->logfile;
+
+$node_publisher->safe_psql(
+	'postgres', q{
+	BEGIN;
+	INSERT INTO test_tab_2 values(1);
+	SAVEPOINT sp;
+	INSERT INTO test_tab_2 values(1);
+	ROLLBACK TO sp;
+	COMMIT;
+	});
+
+# Ensure that the changes are serialized.
+$node_subscriber->wait_for_log(
+	qr/LOG: ( [A-Z0-9]+:)? logical replication apply worker will serialize the remaining changes of remote transaction \d+ to a file/,
+	$offset);
+
+$node_publisher->wait_for_catchup($appname);
+
+# Check that only sub-transaction is aborted on subscriber.
+$result =
+  $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM test_tab_2");
+is($result, qq(1), 'check rollback to savepoint was reflected on subscriber');
+
+$node_subscriber->stop;
+$node_publisher->stop;
+
+done_testing();
-- 
2.34.1

From ce4dedf75caacc2ba3180ea042d4f44e316ad6d1 Mon Sep 17 00:00:00 2001
From: Shveta Malik <[email-masked]>
Date: Mon, 22 May 2023 08:36:49 +0530
Subject: [PATCH 1/8] Deparser for Table DDL commands and extending event
 triggers

This patch constructs JSON blobs representing DDL commands, which can
later be re-processed into plain strings by well-defined sprintf-like
expansion. These JSON objects are intended to allow for machine-editing of
the commands, by replacing certain nodes within the objects.

Much of the information in the output blob actually comes from system
catalogs, not from the command parse node, as it is impossible to reliably
construct a fully-specified command (i.e. one not dependent on search_path
etc) looking only at the parse node.

This provides a base for logical replication of DDL statements. Currently,
the patch has support for:

CREATE/ALTER/DROP TABLE
---
 doc/src/sgml/event-trigger.sgml      |  133 +-
 src/backend/commands/Makefile        |    2 +
 src/backend/commands/ddldeparse.c    | 2978 ++++++++++++++++++++++++++
 src/backend/commands/ddljson.c       |  708 ++++++
 src/backend/commands/event_trigger.c |    1 +
 src/backend/commands/meson.build     |    2 +
 src/backend/commands/sequence.c      |   43 +
 src/backend/commands/tablecmds.c     |    3 +-
 src/backend/parser/parse_utilcmd.c   |    1 +
 src/backend/tcop/utility.c           |   17 +
 src/backend/utils/adt/format_type.c  |  108 +-
 src/backend/utils/adt/ruleutils.c    |   32 +-
 src/include/catalog/pg_proc.dat      |    7 +
 src/include/commands/sequence.h      |    9 +
 src/include/commands/tablecmds.h     |    2 +
 src/include/nodes/parsenodes.h       |    1 +
 src/include/tcop/ddldeparse.h        |   22 +
 src/include/tcop/utility.h           |    2 +
 src/include/utils/builtins.h         |    5 +
 src/include/utils/ruleutils.h        |   10 +
 src/tools/pgindent/typedefs.list     |    5 +
 21 files changed, 4069 insertions(+), 22 deletions(-)
 create mode 100644 src/backend/commands/ddldeparse.c
 create mode 100644 src/backend/commands/ddljson.c
 create mode 100644 src/include/tcop/ddldeparse.h

diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index 3b6a5361b3..a349433819 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -30,7 +30,8 @@
      supported events are
      <literal>ddl_command_start</literal>,
      <literal>ddl_command_end</literal>,
-     <literal>table_rewrite</literal>
+     <literal>table_rewrite</literal>,
+     <literal>table_init_write</literal>
      and <literal>sql_drop</literal>.
      Support for additional events may be added in future releases.
    </para>
@@ -82,16 +83,22 @@
     the <literal>table_rewrite</literal> event is not triggered by them.
    </para>
 
+   <para>
+    The <literal>table_init_write</literal> event occurs just after the creation of
+    table while execution of <literal>CREATE TABLE AS</literal> and
+    <literal>SELECT INTO</literal> commands.
+   </para>
+
    <para>
      Event triggers (like other functions) cannot be executed in an aborted
      transaction.  Thus, if a DDL command fails with an error, any associated
      <literal>ddl_command_end</literal> triggers will not be executed.  Conversely,
      if a <literal>ddl_command_start</literal> trigger fails with an error, no
      further event triggers will fire, and no attempt will be made to execute
-     the command itself.  Similarly, if a <literal>ddl_command_end</literal> trigger
-     fails with an error, the effects of the DDL statement will be rolled
-     back, just as they would be in any other case where the containing
-     transaction aborts.
+     the command itself.  Similarly, if a <literal>ddl_command_end</literal> or
+     <literal>table_init_write</literal> trigger fails with an error, the effects
+     of the DDL statement will be rolled back, just as they would be in any other
+     case where the containing transaction aborts.
    </para>
 
    <para>
@@ -138,6 +145,7 @@
       <colspec colname="col4" colwidth="1*"/>
       <colspec colname="col5" colwidth="1*"/>
       <colspec colname="col6" colwidth="1*"/>
+      <colspec colname="col7" colwidth="1*"/>
       <thead>
        <row>
         <entry>Command Tag</entry>
@@ -145,6 +153,7 @@
         <entry><literal>ddl_&zwsp;command_&zwsp;end</literal></entry>
         <entry><literal>sql_&zwsp;drop</literal></entry>
         <entry><literal>table_&zwsp;rewrite</literal></entry>
+        <entry><literal>table_&zwsp;init_&zwsp;write</literal></entry>
         <entry>Notes</entry>
        </row>
       </thead>
@@ -155,6 +164,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -163,6 +173,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -171,6 +182,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -179,6 +191,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -187,6 +200,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -195,6 +209,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -203,6 +218,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -211,6 +227,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -219,6 +236,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -227,6 +245,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -235,6 +254,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -243,6 +263,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -251,6 +272,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -259,6 +281,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -267,6 +290,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -275,6 +299,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -283,6 +308,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -291,6 +317,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -299,6 +326,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -307,6 +335,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -315,6 +344,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -323,6 +353,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -331,6 +362,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -339,6 +371,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -347,6 +380,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -355,6 +389,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -363,6 +398,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -371,6 +407,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -379,6 +416,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -387,6 +425,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -395,6 +434,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -403,6 +443,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -411,6 +452,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -419,6 +461,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left">Only for local objects</entry>
        </row>
        <row>
@@ -427,6 +470,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -435,6 +479,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -443,6 +488,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -451,6 +497,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -459,6 +506,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -467,6 +515,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -475,6 +524,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -483,6 +533,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -491,6 +542,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -499,6 +551,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -507,6 +560,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -515,6 +569,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -523,6 +578,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -531,6 +587,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -539,6 +596,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -547,6 +605,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -555,6 +614,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -563,6 +623,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -571,6 +632,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -579,6 +641,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -587,6 +650,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -595,6 +659,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -603,6 +668,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -611,6 +677,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -619,6 +686,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -627,6 +695,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -635,6 +704,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -643,6 +713,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -651,6 +722,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -659,6 +731,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -667,6 +740,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -675,6 +749,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -683,6 +758,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -691,6 +767,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -699,6 +776,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -707,6 +785,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -715,6 +794,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -723,6 +803,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -731,6 +812,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -739,6 +821,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -747,6 +830,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -755,6 +839,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -763,6 +848,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -771,6 +857,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -779,6 +866,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -787,6 +875,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -795,6 +884,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -803,6 +893,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -811,6 +902,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -819,6 +911,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -827,6 +920,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -835,6 +929,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -843,6 +938,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -851,6 +947,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -859,6 +956,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -867,6 +965,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -875,6 +974,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -883,6 +983,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -891,6 +992,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -899,6 +1001,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -907,6 +1010,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -915,6 +1019,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -923,6 +1028,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -931,6 +1037,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -939,6 +1046,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -947,6 +1055,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -955,6 +1064,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -963,6 +1073,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -971,6 +1082,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -979,6 +1091,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -987,6 +1100,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -995,6 +1109,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left">Only for local objects</entry>
        </row>
        <row>
@@ -1003,6 +1118,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -1011,6 +1127,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
        <row>
@@ -1019,6 +1136,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left">Only for local objects</entry>
        </row>
        <row>
@@ -1027,6 +1145,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
         <entry align="left">Only for local objects</entry>
        </row>
        <row>
@@ -1035,6 +1154,7 @@
         <entry align="center"><literal>X</literal></entry>
         <entry align="center"><literal>-</literal></entry>
         <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
         <entry align="left"></entry>
        </row>
       </tbody>
@@ -1118,7 +1238,8 @@ typedef struct EventTriggerData
        <para>
         Describes the event for which the function is called, one of
         <literal>"ddl_command_start"</literal>, <literal>"ddl_command_end"</literal>,
-        <literal>"sql_drop"</literal>, <literal>"table_rewrite"</literal>.
+        <literal>"sql_drop"</literal>, <literal>"table_rewrite"</literal>,
+        <literal>"table_init_write"</literal>.
         See <xref linkend="event-trigger-definition"/> for the meaning of these
         events.
        </para>
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 48f7348f91..076ac4eb31 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -29,6 +29,8 @@ OBJS = \
 	copyto.o \
 	createas.o \
 	dbcommands.o \
+	ddldeparse.o \
+	ddljson.o \
 	define.o \
 	discard.o \
 	dropcmds.o \
diff --git a/src/backend/commands/ddldeparse.c b/src/backend/commands/ddldeparse.c
new file mode 100644
index 0000000000..3eeaeee716
--- /dev/null
+++ b/src/backend/commands/ddldeparse.c
@@ -0,0 +1,2978 @@
+/*-------------------------------------------------------------------------
+ *
+ * ddldeparse.c
+ *	  Functions to convert utility commands to machine-parseable
+ *	  representation
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ *
+ * This is intended to provide JSON blobs representing DDL commands, which can
+ * later be re-processed into plain strings by well-defined sprintf-like
+ * expansion.  These JSON objects are intended to allow for machine-editing of
+ * the commands, by replacing certain nodes within the objects.
+ *
+ * Much of the information in the output blob actually comes from system
+ * catalogs, not from the command parse node, as it is impossible to reliably
+ * construct a fully-specified command (i.e. one not dependent on search_path
+ * etc) looking only at the parse node.
+ *
+ * Deparse object tree is created by using:
+ * 	a) new_objtree("know contents") where the complete tree content is known or
+ *     the initial tree content is known.
+ * 	b) new_objtree("") for the syntax where the object tree will be derived
+ *     based on some conditional checks.
+ * 	c) new_objtree_VA where the complete tree can be derived using some fixed
+ *     content or by using the initial tree content along with some variable
+ *     arguments.
+ *
+ * IDENTIFICATION
+ *	  src/backend/commands/ddldeparse.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/amapi.h"
+#include "access/relation.h"
+#include "access/table.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_constraint.h"
+#include "catalog/pg_depend.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "commands/sequence.h"
+#include "commands/tablespace.h"
+#include "commands/tablecmds.h"
+#include "funcapi.h"
+#include "optimizer/optimizer.h"
+#include "rewrite/rewriteHandler.h"
+#include "tcop/ddldeparse.h"
+#include "tcop/utility.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/jsonb.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/ruleutils.h"
+#include "utils/syscache.h"
+
+/* Estimated length of the generated jsonb string */
+#define JSONB_ESTIMATED_LEN 128
+
+/*
+ * Before they are turned into JSONB representation, each command is
+ * represented as an object tree, using the structs below.
+ */
+typedef enum
+{
+	ObjTypeNull,
+	ObjTypeBool,
+	ObjTypeString,
+	ObjTypeArray,
+	ObjTypeInteger,
+	ObjTypeObject
+} ObjType;
+
+/*
+ * Represent the command as an object tree.
+ */
+typedef struct ObjTree
+{
+	slist_head	params;			/* Object tree parameters */
+	int			numParams;		/* Number of parameters in the object tree */
+	StringInfo	fmtinfo;		/* Format string of the ObjTree */
+	bool		present;		/* Indicates if boolean value should be stored */
+} ObjTree;
+
+/*
+ * An element of an object tree (ObjTree).
+ */
+typedef struct ObjElem
+{
+	char	   *name;			/* Name of object element */
+	ObjType		objtype;		/* Object type */
+
+	union
+	{
+		bool		boolean;
+		char	   *string;
+		int64		integer;
+		float8		flt;
+		ObjTree    *object;
+		List	   *array;
+	}			value;			/* Store the object value based on the object
+								 * type */
+	slist_node	node;			/* Used in converting back to 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);
+static void append_premade_object(ObjTree *tree, ObjElem *elem);
+static void append_string_object(ObjTree *tree, char *object_name,
+								 char *value);
+static ObjElem *new_object(ObjType type, char *name);
+static ObjTree *new_objtree_for_qualname_id(Oid classId, Oid objectId);
+static ObjElem *new_object_object(ObjTree *value);
+static ObjTree *new_objtree_VA(char *fmt, int numobjs, ...);
+static JsonbValue *objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state);
+static char *RelationGetColumnDefault(Relation rel, AttrNumber attno,
+									  List *dpcontext);
+
+static ObjTree *deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
+								  ColumnDef *coldef, bool is_alter);
+static ObjTree *deparse_ColumnIdentity(Oid seqrelid, char identity, bool alter_table);
+static ObjTree *deparse_ColumnSetOptions(AlterTableCmd *subcmd);
+
+static ObjTree *deparse_DefElem(DefElem *elem, bool is_reset);
+static ObjTree *deparse_OnCommitClause(OnCommitAction option);
+static ObjTree *deparse_RelSetOptions(AlterTableCmd *subcmd);
+
+static inline ObjElem *deparse_Seq_Cache(Form_pg_sequence seqdata, bool alter_table);
+static inline ObjElem *deparse_Seq_Cycle(Form_pg_sequence seqdata, bool alter_table);
+static inline ObjElem *deparse_Seq_IncrementBy(Form_pg_sequence seqdata, bool alter_table);
+static inline ObjElem *deparse_Seq_Minvalue(Form_pg_sequence seqdata, bool alter_table);
+static inline ObjElem *deparse_Seq_Maxvalue(Form_pg_sequence seqdata, bool alter_table);
+static inline ObjElem *deparse_Seq_Restart(int64 last_value);
+static inline ObjElem *deparse_Seq_Startwith(Form_pg_sequence seqdata, bool alter_table);
+static inline ObjElem *deparse_Seq_As(Form_pg_sequence seqdata);
+
+static List *deparse_InhRelations(Oid objectId);
+static List *deparse_TableElements(Relation relation, List *tableElements, List *dpcontext,
+								   bool typed, bool composite);
+
+/*
+ * Append present as false to a tree.
+ */
+static void
+append_not_present(ObjTree *tree)
+{
+	append_bool_object(tree, "present", false);
+}
+
+/*
+ * Append an array parameter to a tree.
+ */
+static void
+append_array_object(ObjTree *tree, char *object_name, List *array)
+{
+	ObjElem    *param;
+
+	param = new_object(ObjTypeArray, object_name);
+	param->value.array = array;
+	append_premade_object(tree, param);
+}
+
+/*
+ * Append a boolean parameter to a tree.
+ */
+static void
+append_bool_object(ObjTree *tree, char *object_name, bool value)
+{
+	ObjElem    *param;
+
+	Assert(object_name);
+
+	/*
+	 * Check if the format string is 'present' and if yes, store the boolean
+	 * value
+	 */
+	if (strcmp(object_name, "present") == 0)
+		tree->present = value;
+
+	param = new_object(ObjTypeBool, object_name);
+	param->value.boolean = value;
+	append_premade_object(tree, param);
+}
+
+/*
+ * Append an object parameter to a tree.
+ */
+static void
+append_object_object(ObjTree *tree, char *object_name, ObjTree *value)
+{
+	ObjElem    *param;
+
+	param = new_object(ObjTypeObject, object_name);
+	param->value.object = value;
+	append_premade_object(tree, param);
+}
+
+/*
+ * Append a preallocated parameter to a tree.
+ */
+static inline void
+append_premade_object(ObjTree *tree, ObjElem *elem)
+{
+	slist_push_head(&tree->params, &elem->node);
+	tree->numParams++;
+}
+
+/*
+ * Append a string parameter to a tree.
+ */
+static void
+append_string_object(ObjTree *tree, char *object_name, char *value)
+{
+	ObjElem    *param;
+
+	param = new_object(ObjTypeString, object_name);
+	param->value.string = value;
+	append_premade_object(tree, param);
+}
+
+/*
+ * Return the string representation of the given RELPERSISTENCE value.
+ */
+static char *
+get_persistence_str(char persistence)
+{
+	switch (persistence)
+	{
+		case RELPERSISTENCE_TEMP:
+			return "TEMPORARY";
+		case RELPERSISTENCE_UNLOGGED:
+			return "UNLOGGED";
+		case RELPERSISTENCE_PERMANENT:
+			return "";
+		default:
+			elog(ERROR, "unexpected persistence marking %c", persistence);
+			return "";			/* make compiler happy */
+	}
+}
+
+/*
+ * Allocate a new parameter.
+ */
+static ObjElem *
+new_object(ObjType type, char *name)
+{
+	ObjElem    *param;
+
+	param = palloc0(sizeof(ObjElem));
+	param->name = name;
+	param->objtype = type;
+
+	return param;
+}
+
+/*
+ * Allocate a new object parameter.
+ */
+static ObjElem *
+new_object_object(ObjTree *value)
+{
+	ObjElem    *param;
+
+	param = new_object(ObjTypeObject, NULL);
+	param->value.object = value;
+
+	return param;
+}
+
+/*
+ * Allocate a new object tree to store parameter values.
+ */
+static ObjTree *
+new_objtree(char *fmt)
+{
+	ObjTree    *objtree;
+
+	objtree = palloc0(sizeof(ObjTree));
+	objtree->present = true;
+	slist_init(&objtree->params);
+
+	if (fmt)
+	{
+		objtree->fmtinfo = makeStringInfo();
+		appendStringInfoString(objtree->fmtinfo, fmt);
+	}
+
+	return objtree;
+}
+
+/*
+ * A helper routine to set up %{}D and %{}O elements.
+ *
+ * Elements "schema_name" and "obj_name" are set.  If the namespace OID
+ * corresponds to a temp schema, that's set to "pg_temp".
+ *
+ * The difference between those two element types is whether the obj_name will
+ * be quoted as an identifier or not, which is not something that this routine
+ * concerns itself with; that will be up to the expand function.
+ */
+static ObjTree *
+new_objtree_for_qualname(Oid nspid, char *name)
+{
+	ObjTree    *qualified;
+	char	   *namespace;
+
+	if (isAnyTempNamespace(nspid))
+		namespace = pstrdup("pg_temp");
+	else
+		namespace = get_namespace_name(nspid);
+
+	qualified = new_objtree_VA(NULL, 2,
+							   "schemaname", ObjTypeString, namespace,
+							   "objname", ObjTypeString, pstrdup(name));
+
+	return qualified;
+}
+
+/*
+ * A helper routine to set up %{}D and %{}O elements, with the object specified
+ * by classId/objId.
+ */
+static ObjTree *
+new_objtree_for_qualname_id(Oid classId, Oid objectId)
+{
+	ObjTree    *qualified;
+	Relation	catalog;
+	HeapTuple	catobj;
+	Datum		obj_nsp;
+	Datum		obj_name;
+	AttrNumber	Anum_name;
+	AttrNumber	Anum_namespace;
+	AttrNumber	Anum_oid = get_object_attnum_oid(classId);
+	bool		isnull;
+
+	catalog = table_open(classId, AccessShareLock);
+
+	catobj = get_catalog_object_by_oid(catalog, Anum_oid, objectId);
+	if (!catobj)
+		elog(ERROR, "cache lookup failed for object with OID %u of catalog \"%s\"",
+			 objectId, RelationGetRelationName(catalog));
+	Anum_name = get_object_attnum_name(classId);
+	Anum_namespace = get_object_attnum_namespace(classId);
+
+	obj_nsp = heap_getattr(catobj, Anum_namespace, RelationGetDescr(catalog),
+						  &isnull);
+	if (isnull)
+		elog(ERROR, "null namespace for object %u", objectId);
+
+	obj_name = heap_getattr(catobj, Anum_name, RelationGetDescr(catalog),
+						   &isnull);
+	if (isnull)
+		elog(ERROR, "null attribute name for object %u", objectId);
+
+	qualified = new_objtree_for_qualname(DatumGetObjectId(obj_nsp),
+										 NameStr(*DatumGetName(obj_name)));
+	table_close(catalog, AccessShareLock);
+
+	return qualified;
+}
+
+/*
+ * A helper routine to setup %{}T elements.
+ */
+static ObjTree *
+new_objtree_for_type(Oid typeId, int32 typmod)
+{
+	Oid			typnspid;
+	char	   *type_nsp;
+	char	   *type_name = NULL;
+	char	   *typmodstr;
+	bool		type_array;
+
+	format_type_detailed(typeId, typmod,
+						 &typnspid, &type_name, &typmodstr, &type_array);
+
+	if (OidIsValid(typnspid))
+		type_nsp = get_namespace_name_or_temp(typnspid);
+	else
+		type_nsp = pstrdup("");
+
+	return new_objtree_VA(NULL, 4,
+						  "schemaname", ObjTypeString, type_nsp,
+						  "typename", ObjTypeString, type_name,
+						  "typmod", ObjTypeString, typmodstr,
+						  "typarray", ObjTypeBool, type_array);
+}
+
+/*
+ * Allocate a new object tree to store parameter values -- varargs version.
+ *
+ * The "fmt" argument is used to append as a "fmt" element in the output blob.
+ * numobjs indicates the number of extra elements to append; for each one, a
+ * name (string), type (from the ObjType enum) and value must be supplied.  The
+ * value must match the type given; for instance, ObjTypeInteger requires an
+ * int64, ObjTypeString requires a char *, ObjTypeArray requires a list (of
+ * ObjElem), ObjTypeObject requires an ObjTree, and so on.  Each element type *
+ * must match the conversion specifier given in the format string, as described
+ * in ddl_deparse_expand_command, q.v.
+ *
+ * Note we don't have the luxury of sprintf-like compiler warnings for
+ * malformed argument lists.
+ */
+static ObjTree *
+new_objtree_VA(char *fmt, int numobjs, ...)
+{
+	ObjTree    *tree;
+	va_list		args;
+	int			i;
+
+	/* Set up the toplevel object and its "fmt" */
+	tree = new_objtree(fmt);
+
+	/* And process the given varargs */
+	va_start(args, numobjs);
+	for (i = 0; i < numobjs; i++)
+	{
+		char	   *name;
+		ObjType		type;
+		ObjElem    *elem;
+
+		name = va_arg(args, char *);
+		type = va_arg(args, ObjType);
+		elem = new_object(type, NULL);
+
+		/*
+		 * For all param types other than ObjTypeNull, there must be a value in
+		 * the varargs. Fetch it and add the fully formed subobject into the
+		 * main object.
+		 */
+		switch (type)
+		{
+			case ObjTypeNull:
+				/* Null params don't have a value (obviously) */
+				break;
+			case ObjTypeBool:
+				elem->value.boolean = va_arg(args, int);
+				break;
+			case ObjTypeString:
+				elem->value.string = va_arg(args, char *);
+				break;
+			case ObjTypeArray:
+				elem->value.array = va_arg(args, List *);
+				break;
+			case ObjTypeInteger:
+				elem->value.integer = va_arg(args, int);
+				break;
+			case ObjTypeObject:
+				elem->value.object = va_arg(args, ObjTree *);
+				break;
+			default:
+				elog(ERROR, "invalid ObjTree element type %d", type);
+		}
+
+		elem->name = name;
+		append_premade_object(tree, elem);
+	}
+
+	va_end(args);
+	return tree;
+}
+
+/*
+ * Process the pre-built format string from the ObjTree into the output parse
+ * state.
+ */
+static void
+objtree_fmt_to_jsonb_element(JsonbParseState *state, ObjTree *tree)
+{
+	JsonbValue	key;
+	JsonbValue	val;
+
+	if (tree->fmtinfo == NULL)
+		return;
+
+	/* Push the key first */
+	key.type = jbvString;
+	key.val.string.val = "fmt";
+	key.val.string.len = strlen(key.val.string.val);
+	pushJsonbValue(&state, WJB_KEY, &key);
+
+	/* Then process the pre-built format string */
+	val.type = jbvString;
+	val.val.string.len = tree->fmtinfo->len;
+	val.val.string.val = tree->fmtinfo->data;
+	pushJsonbValue(&state, WJB_VALUE, &val);
+}
+
+/*
+ * Create a JSONB representation from an ObjTree.
+ */
+static Jsonb *
+objtree_to_jsonb(ObjTree *tree)
+{
+	JsonbValue *value;
+
+	value = objtree_to_jsonb_rec(tree, NULL);
+	return JsonbValueToJsonb(value);
+}
+
+/*
+ * Helper for objtree_to_jsonb_rec: process an individual element from an object or
+ * an array into the output parse state.
+ */
+static void
+objtree_to_jsonb_element(JsonbParseState *state, ObjElem *object,
+						 JsonbIteratorToken elem_token)
+{
+	JsonbValue	val;
+
+	switch (object->objtype)
+	{
+		case ObjTypeNull:
+			val.type = jbvNull;
+			pushJsonbValue(&state, elem_token, &val);
+			break;
+
+		case ObjTypeString:
+			val.type = jbvString;
+			val.val.string.len = strlen(object->value.string);
+			val.val.string.val = object->value.string;
+			pushJsonbValue(&state, elem_token, &val);
+			break;
+
+		case ObjTypeInteger:
+			val.type = jbvNumeric;
+			val.val.numeric = (Numeric)
+				DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+													object->value.integer));
+			pushJsonbValue(&state, elem_token, &val);
+			break;
+
+		case ObjTypeBool:
+			val.type = jbvBool;
+			val.val.boolean = object->value.boolean;
+			pushJsonbValue(&state, elem_token, &val);
+			break;
+
+		case ObjTypeObject:
+			/* Recursively add the object into the existing parse state */
+			objtree_to_jsonb_rec(object->value.object, state);
+			break;
+
+		case ObjTypeArray:
+			{
+				ListCell   *cell;
+
+				pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+				foreach(cell, object->value.array)
+				{
+					ObjElem    *elem = lfirst(cell);
+
+					objtree_to_jsonb_element(state, elem, WJB_ELEM);
+				}
+				pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+			}
+			break;
+
+		default:
+			elog(ERROR, "unrecognized object type %d", object->objtype);
+			break;
+	}
+}
+
+/*
+ * Recursive helper for objtree_to_jsonb.
+ */
+static JsonbValue *
+objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state)
+{
+	slist_iter	iter;
+
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	objtree_fmt_to_jsonb_element(state, tree);
+
+	slist_foreach(iter, &tree->params)
+	{
+		ObjElem    *object = slist_container(ObjElem, node, iter.cur);
+		JsonbValue	key;
+
+		/* Push the key first */
+		key.type = jbvString;
+		key.val.string.len = strlen(object->name);
+		key.val.string.val = object->name;
+		pushJsonbValue(&state, WJB_KEY, &key);
+
+		/* Then process the value according to its type */
+		objtree_to_jsonb_element(state, object, WJB_VALUE);
+	}
+
+	return pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+}
+
+/*
+ * Subroutine for CREATE TABLE deparsing.
+ *
+ * Given a table OID, obtain its constraints and append them to the given
+ * elements list.  The updated list is returned.
+ *
+ * This works for typed tables, regular tables.
+ *
+ * Note that CONSTRAINT_FOREIGN constraints are always ignored.
+ */
+static List *
+obtainConstraints(List *elements, Oid relationId)
+{
+	Relation	conRel;
+	ScanKeyData key;
+	SysScanDesc scan;
+	HeapTuple	tuple;
+	ObjTree    *constr;
+
+	Assert(OidIsValid(relationId));
+
+	/*
+	 * Scan pg_constraint to fetch all constraints linked to the given
+	 * relation.
+	 */
+	conRel = table_open(ConstraintRelationId, AccessShareLock);
+	ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber,
+				F_OIDEQ, ObjectIdGetDatum(relationId));
+	scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &key);
+
+	/*
+	 * For each constraint, add a node to the list of table elements.  In
+	 * these nodes we include not only the printable information ("fmt"), but
+	 * also separate attributes to indicate the type of constraint, for
+	 * automatic processing.
+	 */
+	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+	{
+		Form_pg_constraint constrForm;
+		char	   *contype;
+		ObjTree    *tmp_obj;
+		bool		has_tblspc;
+
+		constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
+
+		switch (constrForm->contype)
+		{
+			case CONSTRAINT_CHECK:
+				contype = "check";
+				break;
+			case CONSTRAINT_FOREIGN:
+				continue;		/* not here */
+			case CONSTRAINT_PRIMARY:
+				contype = "primary key";
+				break;
+			case CONSTRAINT_UNIQUE:
+				contype = "unique";
+				break;
+			case CONSTRAINT_EXCLUSION:
+				contype = "exclusion";
+				break;
+			default:
+				elog(ERROR, "unrecognized constraint type");
+		}
+
+		/*
+		 * "type" and "contype" are not part of the printable output, but are
+		 * useful to programmatically distinguish these from columns and among
+		 * different constraint types.
+		 *
+		 * 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,
+								"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 ||
+			 constrForm->contype == CONSTRAINT_EXCLUSION))
+		{
+			Oid			tblspc = get_rel_tablespace(constrForm->conindid);
+
+			if (OidIsValid(tblspc))
+			{
+				char	   *tblspcname = get_tablespace_name(tblspc);
+
+				if (!tblspcname)
+				{
+					elog(ERROR, "cache lookup failed for tablespace %u",
+									tblspc);
+				}
+
+				append_string_object(tmp_obj, "tablespace", tblspcname);
+				has_tblspc = true;
+			}
+		}
+
+		if (!has_tblspc)
+			append_not_present(tmp_obj);
+
+		append_object_object(constr, "tablespace", tmp_obj);
+
+		elements = lappend(elements, new_object_object(constr));
+	}
+
+	systable_endscan(scan);
+	table_close(conRel, AccessShareLock);
+
+	return elements;
+}
+
+/*
+ * Obtain the deparsed default value for the given column of the given table.
+ *
+ * Caller must have set a correct deparse context and must ensure that the
+ * passed attribute has a default value.
+ */
+static char *
+RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext)
+{
+	Node	   *defval;
+	char	   *defstr;
+
+	defval = build_column_default(rel, attno);
+	Assert(defval != NULL);
+
+	defstr = deparse_expression(defval, dpcontext, false, false);
+
+	return defstr;
+}
+
+/*
+ * Obtain the deparsed partition bound expression for the given table.
+ */
+static char *
+RelationGetPartitionBound(Oid relid)
+{
+	Datum		deparsed;
+	Datum		boundDatum;
+	bool		isnull;
+	HeapTuple	tuple;
+
+	tuple = SearchSysCache1(RELOID, relid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation with OID %u", relid);
+
+	boundDatum = SysCacheGetAttr(RELOID, tuple,
+								 Anum_pg_class_relpartbound,
+								 &isnull);
+
+	deparsed = DirectFunctionCall2(pg_get_expr,
+								   CStringGetTextDatum(TextDatumGetCString(boundDatum)),
+								   relid);
+
+	ReleaseSysCache(tuple);
+
+	return TextDatumGetCString(deparsed);
+}
+
+/*
+ * Deparse a ColumnDef node within a regular (non-typed) table creation.
+ *
+ * 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).
+ */
+static ObjTree *
+deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
+				  ColumnDef *coldef, bool is_alter)
+{
+	ObjTree    *ret;
+	ObjTree    *tmp_obj;
+	Oid			relid = RelationGetRelid(relation);
+	HeapTuple	attrTup;
+	Form_pg_attribute attrForm;
+	Oid			typid;
+	int32		typmod;
+	Oid			typcollation;
+	bool		saw_notnull;
+	ListCell   *cell;
+
+	/*
+	 * Inherited columns without local definitions must not be emitted.
+	 *
+	 * XXX maybe it is useful to have them with "present = false" or some
+	 * such?
+	 */
+	if (!coldef->is_local)
+		return NULL;
+
+	attrTup = SearchSysCacheAttName(relid, coldef->colname);
+	if (!HeapTupleIsValid(attrTup))
+		elog(ERROR, "could not find cache entry for column \"%s\" of relation %u",
+			 coldef->colname, relid);
+	attrForm = (Form_pg_attribute) GETSTRUCT(attrTup);
+
+	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,
+						 "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",
+							 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");
+	if (coldef->compression)
+		append_string_object(tmp_obj, "compression_method",
+							 coldef->compression);
+	else
+		append_not_present(tmp_obj);
+
+	append_object_object(ret, "compression", tmp_obj);
+
+	tmp_obj = new_objtree("COLLATE %{collation_name}D");
+	if (OidIsValid(typcollation))
+		append_object_object(tmp_obj, "collation_name",
+							 new_objtree_for_qualname_id(CollationRelationId,
+														 typcollation));
+	else
+		append_not_present(tmp_obj);
+
+	append_object_object(ret, "collation", tmp_obj);
+
+	if (!composite)
+	{
+		Oid			seqrelid = InvalidOid;
+
+		/*
+		 * Emit a NOT NULL declaration if necessary.  Note that we cannot
+		 * trust pg_attribute.attnotnull here, because that bit is also set
+		 * when primary keys are specified; we must not emit a NOT NULL
+		 * constraint in that case, unless explicitly specified.  Therefore,
+		 * we scan the list of constraints attached to this column to
+		 * determine whether we need to emit anything. (Fortunately, NOT NULL
+		 * constraints cannot be table constraints.)
+		 *
+		 * In the ALTER TABLE cases, we also add a NOT NULL if the colDef is
+		 * marked is_not_null.
+		 */
+		saw_notnull = false;
+		foreach(cell, coldef->constraints)
+		{
+			Constraint *constr = (Constraint *) lfirst(cell);
+
+			if (constr->contype == CONSTR_NOTNULL)
+			{
+				saw_notnull = true;
+				break;
+			}
+		}
+
+		if (is_alter && coldef->is_not_null)
+			saw_notnull = true;
+
+		append_string_object(ret, "not_null", saw_notnull ? "NOT NULL" : "");
+
+		tmp_obj = new_objtree("DEFAULT %{default}s");
+		if (attrForm->atthasdef &&
+			coldef->generated != ATTRIBUTE_GENERATED_STORED)
+		{
+			char	   *defstr;
+
+			defstr = RelationGetColumnDefault(relation, attrForm->attnum,
+											  dpcontext);
+
+			append_string_object(tmp_obj, "default", defstr);
+		}
+		else
+			append_not_present(tmp_obj);
+
+		append_object_object(ret, "default", tmp_obj);
+
+		/* IDENTITY COLUMN */
+		if (coldef->identity)
+		{
+			/*
+			 * For identity column, find the sequence owned by column in order
+			 * to deparse the column definition.
+			 */
+			seqrelid = getIdentitySequence(relid, attrForm->attnum, true);
+			if (OidIsValid(seqrelid) && coldef->identitySequence)
+				seqrelid = RangeVarGetRelid(coldef->identitySequence, NoLock, false);
+		}
+
+		tmp_obj = new_objtree("%{identity_column}s");
+		if (OidIsValid(seqrelid))
+		{
+			ObjTree    *tmp_obj2;
+
+			tmp_obj2 = deparse_ColumnIdentity(seqrelid, coldef->identity, is_alter);
+			append_object_object(tmp_obj, "identity_column", tmp_obj2);
+		}
+		else
+			append_not_present(tmp_obj);
+
+		append_object_object(ret, "identity_column", tmp_obj);
+
+		/* GENERATED COLUMN EXPRESSION */
+		tmp_obj = new_objtree("GENERATED ALWAYS AS (%{generation_expr}s) STORED");
+		if (coldef->generated == ATTRIBUTE_GENERATED_STORED)
+		{
+			char	   *defstr;
+
+			defstr = RelationGetColumnDefault(relation, attrForm->attnum,
+											  dpcontext);
+			append_string_object(tmp_obj, "generation_expr", defstr);
+		}
+		else
+			append_not_present(tmp_obj);
+
+		append_object_object(ret, "generated_column", tmp_obj);
+	}
+
+	ReleaseSysCache(attrTup);
+
+	return ret;
+}
+
+/*
+ * Deparse a ColumnDef node within a typed table creation. This is simpler
+ * than the regular case, because we don't have to emit the type declaration,
+ * collation, or default. Here we only return something if the column is being
+ * declared NOT NULL.
+ *
+ * As in deparse_ColumnDef, any other constraint is processed elsewhere.
+ */
+static ObjTree *
+deparse_ColumnDef_typed(Relation relation, List *dpcontext, ColumnDef *coldef)
+{
+	ObjTree    *ret = NULL;
+	ObjTree    *tmp_obj;
+	Oid			relid = RelationGetRelid(relation);
+	HeapTuple	attrTup;
+	Form_pg_attribute attrForm;
+	Oid			typid;
+	int32		typmod;
+	Oid			typcollation;
+	bool		saw_notnull;
+	ListCell   *cell;
+
+	attrTup = SearchSysCacheAttName(relid, coldef->colname);
+	if (!HeapTupleIsValid(attrTup))
+		elog(ERROR, "could not find cache entry for column \"%s\" of relation %u",
+			 coldef->colname, relid);
+	attrForm = (Form_pg_attribute) GETSTRUCT(attrTup);
+
+	get_atttypetypmodcoll(relid, attrForm->attnum,
+						  &typid, &typmod, &typcollation);
+
+	/*
+	 * Search for a NOT NULL declaration. As in deparse_ColumnDef, we rely on
+	 * finding a constraint on the column rather than coldef->is_not_null.
+	 * (This routine is never used for ALTER cases.)
+	 */
+	saw_notnull = false;
+	foreach(cell, coldef->constraints)
+	{
+		Constraint *constr = (Constraint *) lfirst(cell);
+
+		if (constr->contype == CONSTR_NOTNULL)
+		{
+			saw_notnull = true;
+			break;
+		}
+	}
+
+	if (!saw_notnull && !attrForm->atthasdef)
+	{
+		ReleaseSysCache(attrTup);
+		return NULL;
+	}
+
+	tmp_obj = new_objtree("DEFAULT %{default}s");
+	if (attrForm->atthasdef)
+	{
+		char	   *defstr;
+
+		defstr = RelationGetColumnDefault(relation, attrForm->attnum,
+										  dpcontext);
+
+		append_string_object(tmp_obj, "default", defstr);
+	}
+	else
+		append_not_present(tmp_obj);
+
+	ret = new_objtree_VA("%{name}I WITH OPTIONS %{not_null}s %{default}s", 4,
+						 "type", ObjTypeString, "column",
+						 "name", ObjTypeString, coldef->colname,
+						 "not_null", ObjTypeString,
+						 saw_notnull ? "NOT NULL" : "",
+						 "default", ObjTypeObject, tmp_obj);
+
+	/* Generated columns are not supported on typed tables, so we are done */
+
+	ReleaseSysCache(attrTup);
+
+	return ret;
+}
+
+/*
+ * Deparse the definition of column identity.
+ */
+static ObjTree *
+deparse_ColumnIdentity(Oid seqrelid, char identity, bool alter_table)
+{
+	ObjTree    *ret;
+	ObjTree    *ident_obj;
+	List	   *elems = NIL;
+	Form_pg_sequence seqform;
+	Sequence_values *seqvalues;
+	char	   *identfmt;
+
+	if (alter_table)
+	{
+		identfmt = "SET GENERATED %{option}s";
+		ret = new_objtree("%{identity_type}s %{seq_definition: }s");
+	}
+	else
+	{
+		identfmt = "GENERATED %{option}s AS IDENTITY";
+		ret = new_objtree("%{identity_type}s ( %{seq_definition: }s )");
+	}
+
+	ident_obj = new_objtree(identfmt);
+	if (identity == ATTRIBUTE_IDENTITY_ALWAYS)
+		append_string_object(ident_obj, "option", "ALWAYS");
+	else if (identity == ATTRIBUTE_IDENTITY_BY_DEFAULT)
+		append_string_object(ident_obj, "option", "BY DEFAULT");
+	else
+		append_not_present(ident_obj);
+
+	append_object_object(ret, "identity_type", ident_obj);
+
+	seqvalues = get_sequence_values(seqrelid);
+	seqform = seqvalues->seqform;
+
+	/* Definition elements */
+	elems = lappend(elems, deparse_Seq_Cache(seqform, alter_table));
+	elems = lappend(elems, deparse_Seq_Cycle(seqform, alter_table));
+	elems = lappend(elems, deparse_Seq_IncrementBy(seqform, alter_table));
+	elems = lappend(elems, deparse_Seq_Minvalue(seqform, alter_table));
+	elems = lappend(elems, deparse_Seq_Maxvalue(seqform, alter_table));
+	elems = lappend(elems, deparse_Seq_Startwith(seqform, 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);
+
+	return ret;
+}
+
+/*
+ * ... ALTER COLUMN ... SET/RESET (...)
+ */
+static ObjTree *
+deparse_ColumnSetOptions(AlterTableCmd *subcmd)
+{
+	List	   *sets = NIL;
+	ListCell   *cell;
+	ObjTree    *ret;
+	bool		is_reset = subcmd->subtype == AT_ResetOptions;
+
+	ret = new_objtree_VA("ALTER COLUMN %{column}I %{is_reset}s (%{options:, }s)", 2,
+						 "column", ObjTypeString, subcmd->name,
+						 "is_reset", ObjTypeString, is_reset ? "RESET" : "SET");
+
+	foreach(cell, (List *) subcmd->def)
+	{
+		DefElem    *elem;
+		ObjTree    *set;
+
+		elem = (DefElem *) lfirst(cell);
+		set = deparse_DefElem(elem, is_reset);
+		sets = lappend(sets, new_object_object(set));
+	}
+
+	Assert(sets);
+	append_array_object(ret, "options", sets);
+
+	return ret;
+}
+
+/*
+ * Deparse SET/RESET clause as used by ALTER TABLE ... SET/RESET (...)
+ */
+static ObjTree *
+deparse_RelSetOptions(AlterTableCmd *subcmd)
+{
+	List	   *sets = NIL;
+	ListCell   *cell;
+	bool		is_reset = subcmd->subtype == AT_ResetRelOptions;
+
+	foreach(cell, (List *) subcmd->def)
+	{
+		DefElem    *elem;
+		ObjTree    *set;
+
+		elem = (DefElem *) lfirst(cell);
+		set = deparse_DefElem(elem, is_reset);
+		sets = lappend(sets, new_object_object(set));
+	}
+
+	Assert(sets);
+
+	return new_objtree_VA("%{set_reset}s (%{options:, }s)", 2,
+						  "set_reset", ObjTypeString, is_reset ? "RESET" : "SET",
+						  "options", ObjTypeArray, sets);
+}
+
+/*
+ * Deparse DefElems, as used e.g. by ALTER COLUMN ... SET, into a list of SET
+ * (...)  or RESET (...) contents.
+ */
+static ObjTree *
+deparse_DefElem(DefElem *elem, bool is_reset)
+{
+	ObjTree    *ret;
+	ObjTree    *optname;
+
+	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);
+
+	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,
+							 elem->arg ? defGetString(elem) :
+							 defGetBoolean(elem) ? "TRUE" : "FALSE");
+
+	return ret;
+}
+
+/*
+ * Deparse the INHERITS relations.
+ *
+ * Given a table OID, return a schema-qualified table list representing
+ * the parent tables.
+ */
+static List *
+deparse_InhRelations(Oid objectId)
+{
+	List	   *parents = NIL;
+	Relation	inhRel;
+	SysScanDesc scan;
+	ScanKeyData key;
+	HeapTuple	tuple;
+
+	inhRel = table_open(InheritsRelationId, RowExclusiveLock);
+
+	ScanKeyInit(&key,
+				Anum_pg_inherits_inhrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(objectId));
+
+	scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId,
+							  true, NULL, 1, &key);
+
+	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+	{
+		ObjTree    *parent;
+		Form_pg_inherits formInh = (Form_pg_inherits) GETSTRUCT(tuple);
+
+		parent = new_objtree_for_qualname_id(RelationRelationId,
+											 formInh->inhparent);
+		parents = lappend(parents, new_object_object(parent));
+	}
+
+	systable_endscan(scan);
+	table_close(inhRel, RowExclusiveLock);
+
+	return parents;
+}
+
+/*
+ * Deparse the ON COMMIT ... clause for CREATE ... TEMPORARY ...
+ */
+static ObjTree *
+deparse_OnCommitClause(OnCommitAction option)
+{
+	ObjTree    *ret  = new_objtree("ON COMMIT %{on_commit_value}s");
+	switch (option)
+	{
+		case ONCOMMIT_DROP:
+			append_string_object(ret, "on_commit_value", "DROP");
+			break;
+
+		case ONCOMMIT_DELETE_ROWS:
+			append_string_object(ret, "on_commit_value", "DELETE ROWS");
+			break;
+
+		case ONCOMMIT_PRESERVE_ROWS:
+			append_string_object(ret, "on_commit_value", "PRESERVE ROWS");
+			break;
+
+		case ONCOMMIT_NOOP:
+			append_not_present(ret);
+			break;
+	}
+
+	return ret;
+}
+
+/*
+ * Deparse the sequence CACHE option.
+ */
+static inline ObjElem *
+deparse_Seq_Cache(Form_pg_sequence seqdata, bool alter_table)
+{
+	ObjTree    *ret;
+	char	   *fmt;
+
+	fmt = alter_table ? "SET CACHE %{value}s" : "CACHE %{value}s";
+
+	ret = new_objtree_VA(fmt, 2,
+						 "clause", ObjTypeString, "cache",
+						 "value", ObjTypeString,
+						 psprintf(INT64_FORMAT, seqdata->seqcache));
+
+	return new_object_object(ret);
+}
+
+/*
+ * Deparse the sequence CYCLE option.
+ */
+static inline ObjElem *
+deparse_Seq_Cycle(Form_pg_sequence seqdata, bool alter_table)
+{
+	ObjTree    *ret;
+	char	   *fmt;
+
+	fmt = alter_table ? "SET %{no}s CYCLE" : "%{no}s CYCLE";
+
+	ret = new_objtree_VA(fmt, 2,
+						 "clause", ObjTypeString, "cycle",
+						 "no", ObjTypeString,
+						 seqdata->seqcycle ? "" : "NO");
+
+	return new_object_object(ret);
+}
+
+/*
+ * Deparse the sequence INCREMENT BY option.
+ */
+static inline ObjElem *
+deparse_Seq_IncrementBy(Form_pg_sequence seqdata, bool alter_table)
+{
+	ObjTree    *ret;
+	char	   *fmt;
+
+	fmt = alter_table ? "SET INCREMENT BY %{value}s" : "INCREMENT BY %{value}s";
+
+	ret = new_objtree_VA(fmt, 2,
+						 "clause", ObjTypeString, "seqincrement",
+						 "value", ObjTypeString,
+						 psprintf(INT64_FORMAT, seqdata->seqincrement));
+
+	return new_object_object(ret);
+}
+
+/*
+ * Deparse the sequence MAXVALUE option.
+ */
+static inline ObjElem *
+deparse_Seq_Maxvalue(Form_pg_sequence seqdata, bool alter_table)
+{
+	ObjTree    *ret;
+	char	   *fmt;
+
+	fmt = alter_table ? "SET MAXVALUE %{value}s" : "MAXVALUE %{value}s";
+
+	ret = new_objtree_VA(fmt, 2,
+						 "clause", ObjTypeString, "maxvalue",
+						 "value", ObjTypeString,
+						 psprintf(INT64_FORMAT, seqdata->seqmax));
+
+	return new_object_object(ret);
+}
+
+/*
+ * Deparse the sequence MINVALUE option.
+ */
+static inline ObjElem *
+deparse_Seq_Minvalue(Form_pg_sequence seqdata, bool alter_table)
+{
+	ObjTree    *ret;
+	char	   *fmt;
+
+	fmt = alter_table ? "SET MINVALUE %{value}s" : "MINVALUE %{value}s";
+
+	ret = new_objtree_VA(fmt, 2,
+						 "clause", ObjTypeString, "minvalue",
+						 "value", ObjTypeString,
+						 psprintf(INT64_FORMAT, seqdata->seqmin));
+
+	return new_object_object(ret);
+}
+
+/*
+ * Deparse the sequence OWNED BY command.
+ */
+static ObjElem *
+deparse_Seq_OwnedBy(Oid sequenceId, bool alter_table)
+{
+	ObjTree    *ret = NULL;
+	Relation	depRel;
+	SysScanDesc scan;
+	ScanKeyData keys[3];
+	HeapTuple	tuple;
+
+	depRel = table_open(DependRelationId, AccessShareLock);
+	ScanKeyInit(&keys[0],
+				Anum_pg_depend_classid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationRelationId));
+	ScanKeyInit(&keys[1],
+				Anum_pg_depend_objid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(sequenceId));
+	ScanKeyInit(&keys[2],
+				Anum_pg_depend_objsubid,
+				BTEqualStrategyNumber, F_INT4EQ,
+				Int32GetDatum(0));
+
+	scan = systable_beginscan(depRel, DependDependerIndexId, true,
+							  NULL, 3, keys);
+	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+	{
+		Oid			ownerId;
+		Form_pg_depend depform;
+		ObjTree    *tmp_obj;
+		char	   *colname;
+
+		depform = (Form_pg_depend) GETSTRUCT(tuple);
+
+		/* Only consider AUTO dependencies on pg_class */
+		if (depform->deptype != DEPENDENCY_AUTO)
+			continue;
+		if (depform->refclassid != RelationRelationId)
+			continue;
+		if (depform->refobjsubid <= 0)
+			continue;
+
+		ownerId = depform->refobjid;
+		colname = get_attname(ownerId, depform->refobjsubid, false);
+		if (colname == NULL)
+			continue;
+
+		tmp_obj = new_objtree_for_qualname_id(RelationRelationId, ownerId);
+		append_string_object(tmp_obj, "attrname", colname);
+		ret = new_objtree_VA("OWNED BY %{owner}D", 2,
+							 "clause", ObjTypeString, "owned",
+							 "owner", ObjTypeObject, tmp_obj);
+	}
+
+	systable_endscan(scan);
+	relation_close(depRel, AccessShareLock);
+
+	/*
+	 * If there's no owner column, emit an empty OWNED BY element, set up so
+	 * that it won't print anything.
+	 */
+	if (!ret)
+		/* XXX this shouldn't happen ... */
+		ret = new_objtree_VA("OWNED BY %{owner}D", 3,
+							 "clause", ObjTypeString, "owned",
+							 "owner", ObjTypeNull,
+							 "present", ObjTypeBool, false);
+
+	return new_object_object(ret);
+}
+
+/*
+ * Deparse the sequence RESTART option.
+ */
+static inline ObjElem *
+deparse_Seq_Restart(int64 last_value)
+{
+	ObjTree    *ret;
+
+	ret = new_objtree_VA("RESTART %{value}s", 2,
+						 "clause", ObjTypeString, "restart",
+						 "value", ObjTypeString,
+						 psprintf(INT64_FORMAT, last_value));
+
+	return new_object_object(ret);
+}
+
+/*
+ * Deparse the sequence AS option.
+ */
+static inline ObjElem *
+deparse_Seq_As(Form_pg_sequence seqdata)
+{
+	ObjTree    *ret;
+
+	ret = new_objtree("AS %{seqtype}T");
+	if (OidIsValid(seqdata->seqtypid))
+		append_object_object(ret, "seqtype",
+							 new_objtree_for_type(seqdata->seqtypid, -1));
+	else
+		append_not_present(ret);
+
+	return new_object_object(ret);
+}
+
+/*
+ * Deparse the sequence START WITH option.
+ */
+static inline ObjElem *
+deparse_Seq_Startwith(Form_pg_sequence seqdata, bool alter_table)
+{
+	ObjTree    *ret;
+	char	   *fmt;
+
+	fmt = alter_table ? "SET START WITH %{value}s" : "START WITH %{value}s";
+
+	ret = new_objtree_VA(fmt, 2,
+						 "clause", ObjTypeString, "start",
+						 "value", ObjTypeString,
+						 psprintf(INT64_FORMAT, seqdata->seqstart));
+
+	return new_object_object(ret);
+}
+
+/*
+ * Subroutine for CREATE TABLE deparsing.
+ *
+ * Deal with all the table elements (columns and constraints).
+ *
+ * Note we ignore constraints in the parse node here; they are extracted from
+ * system catalogs instead.
+ */
+static List *
+deparse_TableElements(Relation relation, List *tableElements, List *dpcontext,
+					  bool typed, bool composite)
+{
+	List	   *elements = NIL;
+	ListCell   *lc;
+
+	foreach(lc, tableElements)
+	{
+		Node	   *elt = (Node *) lfirst(lc);
+
+		switch (nodeTag(elt))
+		{
+			case T_ColumnDef:
+				{
+					ObjTree    *tree;
+
+					tree = typed ?
+						deparse_ColumnDef_typed(relation, dpcontext,
+												(ColumnDef *) elt) :
+						deparse_ColumnDef(relation, dpcontext,
+										  composite, (ColumnDef *) elt,
+										  false);
+					if (tree != NULL)
+						elements = lappend(elements, new_object_object(tree));
+				}
+				break;
+			case T_Constraint:
+				break;
+			default:
+				elog(ERROR, "invalid node type %d", nodeTag(elt));
+		}
+	}
+
+	return elements;
+}
+
+/*
+ * Deparse a CreateSeqStmt.
+ *
+ * Given a sequence OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Note: We need to deparse the CREATE SEQUENCE command for the TABLE
+ * 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.
+ */
+static ObjTree *
+deparse_CreateSeqStmt(Oid objectId, Node *parsetree)
+{
+	ObjTree    *ret;
+	Relation	relation;
+	List	   *elems = NIL;
+	Form_pg_sequence seqform;
+	Sequence_values *seqvalues;
+	CreateSeqStmt *createSeqStmt = (CreateSeqStmt *) parsetree;
+
+	/*
+	 * Only support sequence for IDENTITY COLUMN output separately (via CREATE
+	 * TABLE or ALTER TABLE). Otherwise, return empty here.
+	 */
+	if (createSeqStmt->for_identity)
+		return NULL;
+
+	seqvalues = get_sequence_values(objectId);
+	seqform = seqvalues->seqform;
+
+	/* Definition elements */
+	elems = lappend(elems, deparse_Seq_Cache(seqform, false));
+	elems = lappend(elems, deparse_Seq_Cycle(seqform, false));
+	elems = lappend(elems, deparse_Seq_IncrementBy(seqform, false));
+	elems = lappend(elems, deparse_Seq_Minvalue(seqform, false));
+	elems = lappend(elems, deparse_Seq_Maxvalue(seqform, false));
+	elems = lappend(elems, deparse_Seq_Startwith(seqform, false));
+	elems = lappend(elems, deparse_Seq_Restart(seqvalues->last_value));
+	elems = lappend(elems, deparse_Seq_As(seqform));
+
+	/* We purposefully do not emit OWNED BY here */
+
+	relation = relation_open(objectId, AccessShareLock);
+
+	ret = new_objtree_VA("CREATE %{persistence}s SEQUENCE %{if_not_exists}s %{identity}D %{definition: }s", 4,
+						 "persistence", ObjTypeString,
+						 get_persistence_str(relation->rd_rel->relpersistence),
+						 "if_not_exists", ObjTypeString,
+						 createSeqStmt->if_not_exists ? "IF NOT EXISTS" : "",
+						 "identity", ObjTypeObject,
+						 new_objtree_for_qualname(relation->rd_rel->relnamespace,
+												  RelationGetRelationName(relation)),
+						 "definition", ObjTypeArray, elems);
+
+	relation_close(relation, AccessShareLock);
+
+	return ret;
+}
+
+/*
+ * Deparse a CreateStmt (CREATE TABLE).
+ *
+ * Given a table OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ */
+static ObjTree *
+deparse_CreateStmt(Oid objectId, Node *parsetree)
+{
+	CreateStmt *node = (CreateStmt *) parsetree;
+	Relation	relation = relation_open(objectId, AccessShareLock);
+	List	   *dpcontext;
+	ObjTree    *ret;
+	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,
+						 "persistence", ObjTypeString,
+						 get_persistence_str(relation->rd_rel->relpersistence),
+						 "if_not_exists", ObjTypeString,
+						 node->if_not_exists ? "IF NOT EXISTS" : "",
+						 "identity", ObjTypeObject,
+						 new_objtree_for_qualname(relation->rd_rel->relnamespace,
+												  RelationGetRelationName(relation)));
+
+	dpcontext = deparse_context_for(RelationGetRelationName(relation),
+									objectId);
+
+	/*
+	 * Typed tables and partitions use a slightly different format string: we
+	 * must not put table_elements with parents directly in the fmt string,
+	 * because if there are no options the parentheses must not be emitted;
+	 * and also, typed tables do not allow for inheritance.
+	 */
+	if (node->ofTypename || node->partbound)
+	{
+		List	   *tableelts = NIL;
+
+		/*
+		 * We can't put table elements directly in the fmt string as an array
+		 * surrounded by parentheses here, because an empty clause would cause
+		 * a syntax error.  Therefore, we use an indirection element and set
+		 * present=false when there are no elements.
+		 */
+		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);
+		}
+		else
+		{
+			List	   *parents;
+			ObjElem    *elem;
+
+			parents = deparse_InhRelations(objectId);
+			elem = (ObjElem *) linitial(parents);
+
+			Assert(list_length(parents) == 1);
+
+			tmp_obj = new_objtree_VA("PARTITION OF %{parent_identity}D", 1,
+									 "parent_identity", ObjTypeObject,
+									 elem->value.object);
+
+			append_object_object(ret, "parent_identity", tmp_obj);
+		}
+
+		tableelts = deparse_TableElements(relation, node->tableElts, dpcontext,
+										  true, /* typed table */
+										  false);	/* not composite */
+		tableelts = obtainConstraints(tableelts, objectId);
+
+		tmp_obj = new_objtree("(%{elements:, }s)");
+		if (tableelts)
+			append_array_object(tmp_obj, "elements", tableelts);
+		else
+			append_not_present(tmp_obj);
+
+		append_object_object(ret, "table_elements", tmp_obj);
+	}
+	else
+	{
+		List	   *tableelts = NIL;
+
+		/*
+		 * There is no need to process LIKE clauses separately; they have
+		 * already been transformed into columns and constraints.
+		 */
+
+		/*
+		 * Process table elements: column definitions and constraints.  Only
+		 * the column definitions are obtained from the parse node itself.  To
+		 * get constraints we rely on pg_constraint, because the parse node
+		 * might be missing some things such as the name of the constraints.
+		 */
+		tableelts = deparse_TableElements(relation, node->tableElts, dpcontext,
+										  false,	/* not typed table */
+										  false);	/* not composite */
+		tableelts = obtainConstraints(tableelts, objectId);
+
+		append_array_object(ret, "table_elements", tableelts);
+
+		/*
+		 * Add inheritance specification.  We cannot simply scan the list of
+		 * parents from the parser node, because that may lack the actual
+		 * qualified names of the parent relations.  Rather than trying to
+		 * 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)");
+		if (node->inhRelations != NIL)
+			append_array_object(tmp_obj, "parents", deparse_InhRelations(objectId));
+		else
+			append_not_present(tmp_obj);
+
+		append_object_object(ret, "inherits", tmp_obj);
+	}
+
+	/* FOR VALUES clause */
+	if (node->partbound)
+	{
+		/*
+		 * Get pg_class.relpartbound. We cannot use partbound in the parsetree
+		 * directly as it's the original partbound expression which haven't
+		 * been transformed.
+		 */
+		append_string_object(ret, "partition_bound",
+							 RelationGetPartitionBound(objectId));
+	}
+
+	/* PARTITION BY clause */
+	tmp_obj = new_objtree("PARTITION BY %{definition}s");
+	if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+		append_string_object(tmp_obj, "definition",
+							 pg_get_partkeydef_string(objectId));
+	else
+		append_not_present(tmp_obj);
+
+	append_object_object(ret, "partition_by", tmp_obj);
+
+	/* USING clause */
+	tmp_obj = new_objtree("USING %{access_method}I");
+	if (node->accessMethod)
+		append_string_object(tmp_obj, "access_method", node->accessMethod);
+	else
+		append_not_present(tmp_obj);
+
+	append_object_object(ret, "access_method", tmp_obj);
+
+	/* WITH clause */
+	tmp_obj = new_objtree("WITH (%{with:, }s)");
+
+	foreach(cell, node->options)
+	{
+		ObjTree    *tmp_obj2;
+		DefElem    *opt = (DefElem *) lfirst(cell);
+
+		tmp_obj2 = deparse_DefElem(opt, false);
+		list = lappend(list, new_object_object(tmp_obj2));
+	}
+
+	if (list)
+		append_array_object(tmp_obj, "with", list);
+	else
+		append_not_present(tmp_obj);
+
+	append_object_object(ret, "with_clause", tmp_obj);
+
+	append_object_object(ret, "on_commit",
+						 deparse_OnCommitClause(node->oncommit));
+
+	tmp_obj = new_objtree("TABLESPACE %{tablespace}I");
+	if (node->tablespacename)
+		append_string_object(tmp_obj, "tablespace",
+							 node->tablespacename);
+	else
+		append_not_present(tmp_obj);
+
+	append_object_object(ret, "tablespace", tmp_obj);
+
+	relation_close(relation, AccessShareLock);
+
+	return ret;
+}
+
+/*
+ * Deparse all the collected subcommands and return an ObjTree representing the
+ * alter command.
+ */
+static ObjTree *
+deparse_AlterRelation(CollectedCommand *cmd)
+{
+	ObjTree    *ret;
+	ObjTree    *tmp_obj;
+	ObjTree    *tmp_obj2;
+	List	   *dpcontext;
+	Relation	rel;
+	List	   *subcmds = NIL;
+	ListCell   *cell;
+	const char *reltype;
+	Oid			relId = cmd->d.alterTable.objectId;
+	AlterTableStmt *stmt = NULL;
+
+	Assert(cmd->type == SCT_AlterTable);
+	stmt = (AlterTableStmt *) cmd->parsetree;
+	Assert(IsA(stmt, AlterTableStmt));
+
+	/*
+	 * ALTER TABLE subcommands generated for TableLikeClause is processed in
+	 * the top level CREATE TABLE command; return empty here.
+	 */
+	if (stmt->table_like)
+		return NULL;
+
+	rel = relation_open(relId, AccessShareLock);
+
+	switch (rel->rd_rel->relkind)
+	{
+		case RELKIND_RELATION:
+		case RELKIND_PARTITIONED_TABLE:
+			reltype = "TABLE";
+			break;
+		case RELKIND_INDEX:
+		case RELKIND_PARTITIONED_INDEX:
+		case RELKIND_VIEW:
+		case RELKIND_COMPOSITE_TYPE:
+		case RELKIND_FOREIGN_TABLE:
+		case RELKIND_MATVIEW:
+			/* unsupported relkind */
+			table_close(rel, AccessShareLock);
+			return NULL;
+
+		default:
+			elog(ERROR, "unexpected relkind %d", rel->rd_rel->relkind);
+	}
+
+	dpcontext = deparse_context_for(RelationGetRelationName(rel),
+									relId);
+
+	ret = new_objtree_VA("ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", 3,
+						 "objtype", ObjTypeString, reltype,
+						 "only", ObjTypeString,
+						 stmt->relation->inh ? "" : "ONLY",
+						 "identity", ObjTypeObject,
+						 new_objtree_for_qualname(rel->rd_rel->relnamespace,
+												  RelationGetRelationName(rel)));
+
+	foreach(cell, cmd->d.alterTable.subcmds)
+	{
+		CollectedATSubcmd *sub = (CollectedATSubcmd *) lfirst(cell);
+		AlterTableCmd *subcmd = (AlterTableCmd *) sub->parsetree;
+		ObjTree    *tree;
+
+		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;
+
+		switch (subcmd->subtype)
+		{
+			case AT_AddColumn:
+				/* XXX need to set the "recurse" bit somewhere? */
+				Assert(IsA(subcmd->def, ColumnDef));
+				tree = deparse_ColumnDef(rel, dpcontext, false,
+										 (ColumnDef *) subcmd->def, true);
+				tmp_obj = new_objtree_VA("ADD %{objtype}s %{if_not_exists}s %{definition}s", 4,
+										"objtype", ObjTypeString, "COLUMN",
+										"type", ObjTypeString, "add column",
+										"if_not_exists", ObjTypeString,
+										subcmd->missing_ok ? "IF NOT EXISTS" : "",
+										"definition", ObjTypeObject, tree);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_AddIndexConstraint:
+				{
+					IndexStmt  *istmt;
+					Relation	idx;
+					Oid			constrOid = sub->address.objectId;
+
+					Assert(IsA(subcmd->def, IndexStmt));
+					istmt = (IndexStmt *) subcmd->def;
+
+					Assert(istmt->isconstraint && istmt->unique);
+
+					idx = relation_open(istmt->indexOid, AccessShareLock);
+
+					/*
+					 * Verbose syntax
+					 *
+					 * ADD CONSTRAINT %{name}I %{constraint_type}s USING INDEX
+					 * %index_name}I %{deferrable}s %{init_deferred}s
+					 */
+					tmp_obj = new_objtree_VA("ADD CONSTRAINT %{name}I %{constraint_type}s USING INDEX %{index_name}I %{deferrable}s %{init_deferred}s", 6,
+											"type", ObjTypeString, "add constraint using index",
+											"name", ObjTypeString, get_constraint_name(constrOid),
+											"constraint_type", ObjTypeString,
+											istmt->primary ? "PRIMARY KEY" : "UNIQUE",
+											"index_name", ObjTypeString,
+											RelationGetRelationName(idx),
+											"deferrable", ObjTypeString,
+											istmt->deferrable ? "DEFERRABLE" : "NOT DEFERRABLE",
+											"init_deferred", ObjTypeString,
+											istmt->initdeferred ? "INITIALLY DEFERRED" : "INITIALLY IMMEDIATE");
+
+					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+
+					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_CookedColumnDefault:
+				{
+					Relation	attrrel;
+					HeapTuple	atttup;
+					Form_pg_attribute attStruct;
+
+					attrrel = table_open(AttributeRelationId, RowExclusiveLock);
+					atttup = SearchSysCacheCopy2(ATTNUM,
+												 ObjectIdGetDatum(RelationGetRelid(rel)),
+												 Int16GetDatum(subcmd->num));
+					if (!HeapTupleIsValid(atttup))
+						elog(ERROR, "cache lookup failed for attribute %d of relation with OID %u",
+							 subcmd->num, RelationGetRelid(rel));
+					attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
+
+					/*
+					 * Both default and generation expression not supported
+					 * together.
+					 */
+					if (!attStruct->attgenerated)
+						elog(WARNING, "unsupported alter table subtype %d",
+							 subcmd->subtype);
+
+					heap_freetuple(atttup);
+					table_close(attrrel, RowExclusiveLock);
+					break;
+				}
+
+			case AT_AddColumnToView:
+				/* CREATE OR REPLACE VIEW -- nothing to do here */
+				break;
+
+			case AT_ColumnDefault:
+				if (subcmd->def == NULL)
+					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I DROP DEFAULT", 2,
+											"type", ObjTypeString, "drop default",
+											"column", ObjTypeString, subcmd->name);
+				else
+				{
+					List	   *dpcontext_rel;
+					HeapTuple	attrtup;
+					AttrNumber	attno;
+
+					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET DEFAULT %{definition}s", 2,
+											"type", ObjTypeString, "set default",
+											"column", ObjTypeString, subcmd->name);
+
+					dpcontext_rel = deparse_context_for(RelationGetRelationName(rel),
+														RelationGetRelid(rel));
+					attrtup = SearchSysCacheAttName(RelationGetRelid(rel), subcmd->name);
+					attno = ((Form_pg_attribute) GETSTRUCT(attrtup))->attnum;
+					append_string_object(tmp_obj, "definition",
+										 RelationGetColumnDefault(rel, attno,
+																  dpcontext_rel));
+					ReleaseSysCache(attrtup);
+				}
+
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DropNotNull:
+				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I DROP NOT NULL", 2,
+										"type", ObjTypeString, "drop not null",
+										"column", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_ForceRowSecurity:
+				tmp_obj = new_objtree("FORCE ROW LEVEL SECURITY");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_NoForceRowSecurity:
+				tmp_obj = new_objtree("NO FORCE ROW LEVEL SECURITY");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_SetNotNull:
+				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET NOT NULL", 2,
+										"type", ObjTypeString, "set not null",
+										"column", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DropExpression:
+				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I DROP EXPRESSION %{if_exists}s", 3,
+										"type", ObjTypeString, "drop expression",
+										"column", ObjTypeString, subcmd->name,
+										"if_exists", ObjTypeString,
+										subcmd->missing_ok ? "IF EXISTS" : "");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_SetStatistics:
+				{
+					Assert(IsA(subcmd->def, Integer));
+					if (subcmd->name)
+						tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET STATISTICS %{statistics}n", 3,
+												"type", ObjTypeString, "set statistics",
+												"column", ObjTypeString, subcmd->name,
+												"statistics", ObjTypeInteger,
+												intVal((Integer *) subcmd->def));
+					else
+						tmp_obj = new_objtree_VA("ALTER COLUMN %{column}n SET STATISTICS %{statistics}n", 3,
+												"type", ObjTypeString, "set statistics",
+												"column", ObjTypeInteger, subcmd->num,
+												"statistics", ObjTypeInteger,
+												intVal((Integer *) subcmd->def));
+					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				}
+				break;
+
+			case AT_SetOptions:
+			case AT_ResetOptions:
+				subcmds = lappend(subcmds, new_object_object(
+															 deparse_ColumnSetOptions(subcmd)));
+				break;
+
+			case AT_SetStorage:
+				Assert(IsA(subcmd->def, String));
+				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET STORAGE %{storage}s", 3,
+										"type", ObjTypeString, "set storage",
+										"column", ObjTypeString, subcmd->name,
+										"storage", ObjTypeString,
+										strVal((String *) subcmd->def));
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_SetCompression:
+				Assert(IsA(subcmd->def, String));
+				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET COMPRESSION %{compression_method}s", 3,
+										"type", ObjTypeString, "set compression",
+										"column", ObjTypeString, subcmd->name,
+										"compression_method", ObjTypeString,
+										strVal((String *) subcmd->def));
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DropColumn:
+				tmp_obj = new_objtree_VA("DROP %{objtype}s %{if_exists}s %{column}I %{cascade}s", 4,
+										"objtype", ObjTypeString, "COLUMN",
+										"type", ObjTypeString, "drop column",
+										"if_exists", ObjTypeString,
+										subcmd->missing_ok ? "IF EXISTS" : "",
+										"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);
+
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_AddIndex:
+				{
+					Oid			idxOid = sub->address.objectId;
+					IndexStmt  *istmt;
+					Relation	idx;
+					const char *idxname;
+					Oid			constrOid;
+
+					Assert(IsA(subcmd->def, IndexStmt));
+					istmt = (IndexStmt *) subcmd->def;
+
+					if (!istmt->isconstraint)
+						break;
+
+					idx = relation_open(idxOid, AccessShareLock);
+					idxname = RelationGetRelationName(idx);
+
+					constrOid = get_relation_constraint_oid(
+															cmd->d.alterTable.objectId, idxname, false);
+
+					tmp_obj = new_objtree_VA("ADD CONSTRAINT %{name}I %{definition}s", 3,
+											"type", ObjTypeString, "add constraint",
+											"name", ObjTypeString, idxname,
+											"definition", ObjTypeString,
+											pg_get_constraintdef_string(constrOid));
+					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+
+					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 (!constrOid)
+						continue;
+
+					Assert(IsA(subcmd->def, Constraint));
+
+					tmp_obj = new_objtree_VA("ADD CONSTRAINT %{name}I %{definition}s", 3,
+											"type", ObjTypeString, "add constraint",
+											"name", ObjTypeString, get_constraint_name(constrOid),
+											"definition", ObjTypeString,
+											pg_get_constraintdef_string(constrOid));
+					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				}
+				break;
+
+			case AT_AlterConstraint:
+				{
+					Oid			constrOid = sub->address.objectId;
+					Constraint *c = (Constraint *) subcmd->def;
+
+					/* If no constraint was altered, silently skip it */
+					if (!OidIsValid(constrOid))
+						break;
+
+					Assert(IsA(c, Constraint));
+					tmp_obj = new_objtree_VA("ALTER CONSTRAINT %{name}I %{deferrable}s %{init_deferred}s", 4,
+											"type", ObjTypeString, "alter constraint",
+											"name", ObjTypeString, get_constraint_name(constrOid),
+											"deferrable", ObjTypeString,
+											c->deferrable ? "DEFERRABLE" : "NOT DEFERRABLE",
+											"init_deferred", ObjTypeString,
+											c->initdeferred ? "INITIALLY DEFERRED" : "INITIALLY IMMEDIATE");
+
+					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				}
+				break;
+
+			case AT_ValidateConstraint:
+				tmp_obj = new_objtree_VA("VALIDATE CONSTRAINT %{constraint}I", 2,
+										"type", ObjTypeString, "validate constraint",
+										"constraint", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DropConstraint:
+				tmp_obj = new_objtree_VA("DROP CONSTRAINT %{if_exists}s %{constraint}I %{cascade}s", 4,
+										"type", ObjTypeString, "drop constraint",
+										"if_exists", ObjTypeString,
+										subcmd->missing_ok ? "IF EXISTS" : "",
+										"constraint", ObjTypeString, subcmd->name,
+										"cascade", ObjTypeString,
+										subcmd->behavior == DROP_CASCADE ? "CASCADE" : "");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_AlterColumnType:
+				{
+					TupleDesc	tupdesc = RelationGetDescr(rel);
+					Form_pg_attribute att;
+					ColumnDef  *def;
+
+					att = &(tupdesc->attrs[sub->address.objectSubId - 1]);
+					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,
+											"type", ObjTypeString, "alter column type",
+											"column", ObjTypeString, subcmd->name,
+											"datatype", ObjTypeObject,
+											new_objtree_for_type(att->atttypid,
+																 att->atttypmod));
+
+					/* Add a COLLATE clause, if needed */
+					tmp_obj2 = new_objtree("COLLATE %{name}D");
+					if (OidIsValid(att->attcollation))
+					{
+						ObjTree    *collname;
+
+						collname = new_objtree_for_qualname_id(CollationRelationId,
+															   att->attcollation);
+						append_object_object(tmp_obj2, "name", collname);
+					}
+					else
+						append_not_present(tmp_obj2);
+
+					append_object_object(tmp_obj, "collation", 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");
+					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));
+
+						append_string_object(tmp_obj2, "expression",
+											 TextDatumGetCString(deparsed));
+					}
+					else
+						append_not_present(tmp_obj2);
+
+					append_object_object(tmp_obj, "using", tmp_obj2);
+
+					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				}
+				break;
+
+#ifdef TODOLIST
+			case AT_AlterColumnGenericOptions:
+				tmp_obj = deparse_FdwOptions((List *) subcmd->def,
+											subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+#endif
+			case AT_ChangeOwner:
+				tmp_obj = new_objtree_VA("OWNER TO %{owner}I", 2,
+										"type", ObjTypeString, "change owner",
+										"owner", ObjTypeString,
+										get_rolespec_name(subcmd->newowner));
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_ClusterOn:
+				tmp_obj = new_objtree_VA("CLUSTER ON %{index}I", 2,
+										"type", ObjTypeString, "cluster on",
+										"index", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DropCluster:
+				tmp_obj = new_objtree_VA("SET WITHOUT CLUSTER", 1,
+										"type", ObjTypeString, "set without cluster");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_SetLogged:
+				tmp_obj = new_objtree_VA("SET LOGGED", 1,
+										"type", ObjTypeString, "set logged");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_SetUnLogged:
+				tmp_obj = new_objtree_VA("SET UNLOGGED", 1,
+										"type", ObjTypeString, "set unlogged");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DropOids:
+				tmp_obj = new_objtree_VA("SET WITHOUT OIDS", 1,
+										"type", ObjTypeString, "set without oids");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+			case AT_SetAccessMethod:
+				tmp_obj = new_objtree_VA("SET ACCESS METHOD %{access_method}I", 2,
+										"type", ObjTypeString, "set access method",
+										"access_method", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+			case AT_SetTableSpace:
+				tmp_obj = new_objtree_VA("SET TABLESPACE %{tablespace}I", 2,
+										"type", ObjTypeString, "set tablespace",
+										"tablespace", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_SetRelOptions:
+			case AT_ResetRelOptions:
+				subcmds = lappend(subcmds, new_object_object(
+															 deparse_RelSetOptions(subcmd)));
+				break;
+
+			case AT_EnableTrig:
+				tmp_obj = new_objtree_VA("ENABLE TRIGGER %{trigger}I", 2,
+										"type", ObjTypeString, "enable trigger",
+										"trigger", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_EnableAlwaysTrig:
+				tmp_obj = new_objtree_VA("ENABLE ALWAYS TRIGGER %{trigger}I", 2,
+										"type", ObjTypeString, "enable always trigger",
+										"trigger", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_EnableReplicaTrig:
+				tmp_obj = new_objtree_VA("ENABLE REPLICA TRIGGER %{trigger}I", 2,
+										"type", ObjTypeString, "enable replica trigger",
+										"trigger", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DisableTrig:
+				tmp_obj = new_objtree_VA("DISABLE TRIGGER %{trigger}I", 2,
+										"type", ObjTypeString, "disable trigger",
+										"trigger", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_EnableTrigAll:
+				tmp_obj = new_objtree_VA("ENABLE TRIGGER ALL", 1,
+										"type", ObjTypeString, "enable trigger all");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DisableTrigAll:
+				tmp_obj = new_objtree_VA("DISABLE TRIGGER ALL", 1,
+										"type", ObjTypeString, "disable trigger all");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_EnableTrigUser:
+				tmp_obj = new_objtree_VA("ENABLE TRIGGER USER", 1,
+										"type", ObjTypeString, "enable trigger user");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DisableTrigUser:
+				tmp_obj = new_objtree_VA("DISABLE TRIGGER USER", 1,
+										"type", ObjTypeString, "disable trigger user");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_EnableRule:
+				tmp_obj = new_objtree_VA("ENABLE RULE %{rule}I", 2,
+										"type", ObjTypeString, "enable rule",
+										"rule", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_EnableAlwaysRule:
+				tmp_obj = new_objtree_VA("ENABLE ALWAYS RULE %{rule}I", 2,
+										"type", ObjTypeString, "enable always rule",
+										"rule", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_EnableReplicaRule:
+				tmp_obj = new_objtree_VA("ENABLE REPLICA RULE %{rule}I", 2,
+										"type", ObjTypeString, "enable replica rule",
+										"rule", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DisableRule:
+				tmp_obj = new_objtree_VA("DISABLE RULE %{rule}I", 2,
+										"type", ObjTypeString, "disable rule",
+										"rule", ObjTypeString, subcmd->name);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_AddInherit:
+				tmp_obj = new_objtree_VA("INHERIT %{parent}D", 2,
+										"type", ObjTypeString, "inherit",
+										"parent", ObjTypeObject,
+										new_objtree_for_qualname_id(RelationRelationId,
+																	sub->address.objectId));
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DropInherit:
+				tmp_obj = new_objtree_VA("NO INHERIT %{parent}D", 2,
+										"type", ObjTypeString, "drop inherit",
+										"parent", ObjTypeObject,
+										new_objtree_for_qualname_id(RelationRelationId,
+																	sub->address.objectId));
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_AddOf:
+				tmp_obj = new_objtree_VA("OF %{type_of}T", 2,
+										"type", ObjTypeString, "add of",
+										"type_of", ObjTypeObject,
+										new_objtree_for_type(sub->address.objectId, -1));
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DropOf:
+				tmp_obj = new_objtree_VA("NOT OF", 1,
+										"type", ObjTypeString, "not of");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_ReplicaIdentity:
+				tmp_obj = new_objtree_VA("REPLICA IDENTITY %{ident}s", 1,
+										"type", ObjTypeString, "replica identity");
+				switch (((ReplicaIdentityStmt *) subcmd->def)->identity_type)
+				{
+					case REPLICA_IDENTITY_DEFAULT:
+						append_string_object(tmp_obj, "ident", "DEFAULT");
+						break;
+					case REPLICA_IDENTITY_FULL:
+						append_string_object(tmp_obj, "ident", "FULL");
+						break;
+					case REPLICA_IDENTITY_NOTHING:
+						append_string_object(tmp_obj, "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);
+						break;
+				}
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_EnableRowSecurity:
+				tmp_obj = new_objtree_VA("ENABLE ROW LEVEL SECURITY", 1,
+										"type", ObjTypeString, "enable row security");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+
+			case AT_DisableRowSecurity:
+				tmp_obj = new_objtree_VA("DISABLE ROW LEVEL SECURITY", 1,
+										"type", ObjTypeString, "disable row security");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+#ifdef TODOLIST
+			case AT_GenericOptions:
+				tmp_obj = deparse_FdwOptions((List *) subcmd->def, NULL);
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+#endif
+			case AT_AttachPartition:
+				tmp_obj = new_objtree_VA("ATTACH PARTITION %{partition_identity}D %{partition_bound}s",
+										2, "type", ObjTypeString, "attach partition",
+										"partition_identity", ObjTypeObject,
+										new_objtree_for_qualname_id(RelationRelationId,
+																	sub->address.objectId));
+
+				tmp_obj2 = new_objtree("%{partition_bound}s");
+				if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+					append_string_object(tmp_obj2, "partition_bound",
+										 RelationGetPartitionBound(sub->address.objectId));
+				else
+					append_not_present(tmp_obj2);
+
+				append_object_object(tmp_obj, "partition_bound", tmp_obj2);
+
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+			case AT_DetachPartition:
+			{
+				PartitionCmd *cmd;
+
+				Assert(IsA(subcmd->def, PartitionCmd));
+				cmd = (PartitionCmd *) subcmd->def;
+
+				tmp_obj = new_objtree_VA("DETACH PARTITION %{partition_identity}D %{concurrent}s", 3,
+										"type", ObjTypeString,
+										"detach partition",
+										"partition_identity", ObjTypeObject,
+										new_objtree_for_qualname_id(RelationRelationId,
+																	sub->address.objectId),
+										"concurrent", ObjTypeString,
+										cmd->concurrent ? "CONCURRENTLY" : "");
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+			}
+			case AT_DetachPartitionFinalize:
+				tmp_obj = new_objtree_VA("DETACH PARTITION %{partition_identity}D FINALIZE", 2,
+										"type", ObjTypeString, "detach partition finalize",
+										"partition_identity", ObjTypeObject,
+										new_objtree_for_qualname_id(RelationRelationId,
+																	sub->address.objectId));
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+			case AT_AddIdentity:
+				{
+					AttrNumber	attnum;
+					Oid			seq_relid;
+					ObjTree    *seqdef;
+					ColumnDef  *coldef = (ColumnDef *) subcmd->def;
+
+					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I %{definition}s", 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");
+					if (OidIsValid(seq_relid))
+					{
+						seqdef = deparse_ColumnIdentity(seq_relid, coldef->identity, false);
+						append_object_object(tmp_obj2, "identity_column", seqdef);
+					}
+					else
+						append_not_present(tmp_obj2);
+
+					append_object_object(tmp_obj, "definition", tmp_obj2);
+
+					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				}
+				break;
+			case AT_SetIdentity:
+				{
+					DefElem    *defel;
+					char		identity = 0;
+					ObjTree    *seqdef;
+					AttrNumber	attnum;
+					Oid			seq_relid;
+
+
+					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I %{definition}s", 2,
+											"type", ObjTypeString, "set identity",
+											"column", ObjTypeString, 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);
+
+					tmp_obj2 = new_objtree("%{definition}s");
+					if (OidIsValid(seq_relid))
+					{
+						seqdef = deparse_ColumnIdentity(seq_relid, identity, true);
+						append_object_object(tmp_obj2, "definition", seqdef);
+					}
+					else
+						append_not_present(tmp_obj2);
+
+					append_object_object(tmp_obj, "definition", 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,
+										"type", ObjTypeString, "drop identity",
+										"column", ObjTypeString, subcmd->name,
+										"if_exists", ObjTypeString,
+										subcmd->missing_ok ? "IF EXISTS" : "");
+
+				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				break;
+			default:
+				elog(WARNING, "unsupported alter table subtype %d",
+					 subcmd->subtype);
+				break;
+		}
+	}
+
+	table_close(rel, AccessShareLock);
+
+	if (list_length(subcmds) == 0)
+		return NULL;
+
+	append_array_object(ret, "subcmds", subcmds);
+
+	return ret;
+}
+
+/*
+ * Handle deparsing of DROP commands.
+ */
+char *
+deparse_drop_command(const char *objidentity, const char *objecttype,
+					 Node *parsetree)
+{
+	DropStmt   *node = (DropStmt *) parsetree;
+	StringInfoData str;
+	char	   *command;
+	char	   *identity = (char *) objidentity;
+	ObjTree    *stmt;
+	ObjTree    *tmp_obj;
+	Jsonb	   *jsonb;
+
+	initStringInfo(&str);
+
+	stmt = new_objtree_VA("DROP %{objtype}s %{concurrently}s %{if_exists}s %{objidentity}s %{cascade}s", 4,
+						  "objtype", ObjTypeString, objecttype,
+						  "concurrently", ObjTypeString,
+						  node->concurrent ? "CONCURRENTLY" : "",
+						  "if_exists", ObjTypeString,
+						  node->missing_ok ? "IF EXISTS" : "",
+						  "objidentity", ObjTypeString, identity);
+
+	tmp_obj = new_objtree("CASCADE");
+	if (node->behavior != DROP_CASCADE)
+		append_not_present(tmp_obj);
+	append_object_object(stmt, "cascade", tmp_obj);
+
+	jsonb = objtree_to_jsonb(stmt);
+	command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
+
+	return command;
+}
+
+/*
+ * Deparse an AlterObjectSchemaStmt (ALTER ... SET SCHEMA command)
+ *
+ * Given the object address and the parse tree that created it, return an
+ * ObjTree representing the alter command.
+ */
+static ObjTree *
+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;
+
+	/*
+	 * 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)));
+
+	return new_objtree_VA("ALTER %{objtype}s %{identity}s SET SCHEMA %{newschema}I", 3,
+						  "objtype", ObjTypeString,
+						  stringify_objtype(node->objectType),
+						  "identity", ObjTypeString, ident,
+						  "newschema", ObjTypeString, new_schema);
+}
+
+/*
+ * Deparse an AlterOwnerStmt (ALTER ... OWNER TO ...).
+ *
+ * Given the object address and the parse tree that created it, return an
+ * ObjTree representing the alter command.
+ */
+static ObjTree *
+deparse_AlterOwnerStmt(ObjectAddress address, Node *parsetree)
+{
+	AlterOwnerStmt *node = (AlterOwnerStmt *) parsetree;
+
+	return new_objtree_VA("ALTER %{objtype}s %{identity}s OWNER TO %{newowner}I", 3,
+						  "objtype", ObjTypeString,
+						  stringify_objtype(node->objectType),
+						  "identity", ObjTypeString,
+						  getObjectIdentity(&address, false),
+						  "newowner", ObjTypeString,
+						  get_rolespec_name(node->newowner));
+}
+
+/*
+ * Deparse an AlterSeqStmt.
+ *
+ * Given a sequence OID and a parse tree that modified it, return an ObjTree
+ * representing the alter command.
+ *
+ * Note: We need to deparse the ALTER SEQUENCE command for the TABLE 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.
+ */
+static ObjTree *
+deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
+{
+	ObjTree    *ret;
+	Relation	relation;
+	List	   *elems = NIL;
+	ListCell   *cell;
+	Form_pg_sequence seqform;
+	Sequence_values *seqvalues;
+	AlterSeqStmt *alterSeqStmt = (AlterSeqStmt *) parsetree;
+
+	/*
+	 * Sequence for IDENTITY COLUMN output separately (via CREATE TABLE or
+	 * ALTER TABLE); return empty here.
+	 */
+	if (alterSeqStmt->for_identity)
+		return NULL;
+
+	seqvalues = get_sequence_values(objectId);
+	seqform = seqvalues->seqform;
+
+	foreach(cell, ((AlterSeqStmt *) parsetree)->options)
+	{
+		DefElem    *elem = (DefElem *) lfirst(cell);
+		ObjElem    *newelm;
+
+		if (strcmp(elem->defname, "cache") == 0)
+			newelm = deparse_Seq_Cache(seqform, false);
+		else if (strcmp(elem->defname, "cycle") == 0)
+			newelm = deparse_Seq_Cycle(seqform, false);
+		else if (strcmp(elem->defname, "increment") == 0)
+			newelm = deparse_Seq_IncrementBy(seqform, false);
+		else if (strcmp(elem->defname, "minvalue") == 0)
+			newelm = deparse_Seq_Minvalue(seqform, false);
+		else if (strcmp(elem->defname, "maxvalue") == 0)
+			newelm = deparse_Seq_Maxvalue(seqform, false);
+		else if (strcmp(elem->defname, "start") == 0)
+			newelm = deparse_Seq_Startwith(seqform, false);
+		else if (strcmp(elem->defname, "restart") == 0)
+			newelm = deparse_Seq_Restart(seqvalues->last_value);
+		else if (strcmp(elem->defname, "owned_by") == 0)
+			newelm = deparse_Seq_OwnedBy(objectId, false);
+		else if (strcmp(elem->defname, "as") == 0)
+			newelm = deparse_Seq_As(seqform);
+		else
+			elog(ERROR, "invalid sequence option %s", elem->defname);
+
+		elems = lappend(elems, newelm);
+	}
+
+	relation = relation_open(objectId, AccessShareLock);
+
+	ret = new_objtree_VA("ALTER SEQUENCE %{identity}D %{definition: }s", 2,
+						 "identity", ObjTypeObject,
+						 new_objtree_for_qualname(relation->rd_rel->relnamespace,
+												  RelationGetRelationName(relation)),
+						 "definition", ObjTypeArray, elems);
+
+	relation_close(relation, AccessShareLock);
+
+	return ret;
+}
+
+/*
+ * Deparse a RenameStmt.
+ */
+static ObjTree *
+deparse_RenameStmt(ObjectAddress address, Node *parsetree)
+{
+	RenameStmt *node = (RenameStmt *) parsetree;
+	ObjTree    *ret;
+	Relation	relation;
+	Oid			schemaId;
+	ObjTree    *tmp_obj;
+
+	/*
+	 * 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);
+			ret = new_objtree_VA("ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I", 4,
+								 "objtype", ObjTypeString,
+								 stringify_objtype(node->renameType),
+								 "if_exists", ObjTypeString,
+								 node->missing_ok ? "IF EXISTS" : "",
+								 "identity", ObjTypeObject,
+								 new_objtree_for_qualname(schemaId,
+														  node->relation->relname),
+								 "newname", ObjTypeString,
+								 node->newname);
+			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);
+
+				ret = new_objtree_VA("ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I", 4,
+									"only",  ObjTypeString,
+									node->relation->inh ? "" : "ONLY",
+									"identity", ObjTypeObject,
+									new_objtree_for_qualname_id(RelationRelationId,
+																constform->conrelid),
+									"oldname", ObjTypeString, node->subname,
+									"newname", ObjTypeString, node->newname);
+				ReleaseSysCache(constrtup);
+			}
+			break;
+
+		case OBJECT_COLUMN:
+			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,
+								 "objtype", ObjTypeString,
+								 stringify_objtype(node->relationType),
+								 "only", ObjTypeString, node->relation->inh ? "" : "ONLY",
+								 "identity", ObjTypeObject,
+								 new_objtree_for_qualname(schemaId, node->relation->relname),
+								 "colname", ObjTypeString, node->subname,
+								 "newname", ObjTypeString, node->newname
+								 );
+
+			/* 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);
+
+			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);
+
+			relation_close(relation, AccessShareLock);
+			break;
+
+		default:
+			elog(ERROR, "unsupported object type %d", node->renameType);
+	}
+
+	return ret;
+}
+
+/*
+ * Handle deparsing of simple commands.
+ *
+ * This function should cover all cases handled in ProcessUtilitySlow.
+ */
+static ObjTree *
+deparse_simple_command(CollectedCommand *cmd)
+{
+	Oid			objectId;
+	Node	   *parsetree;
+
+	Assert(cmd->type == SCT_Simple);
+
+	parsetree = cmd->parsetree;
+	objectId = cmd->d.simple.address.objectId;
+
+	if (cmd->in_extension && (nodeTag(parsetree) != T_CreateExtensionStmt))
+		return NULL;
+
+	/* 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_AlterOwnerStmt:
+			return deparse_AlterOwnerStmt(cmd->d.simple.address, parsetree);
+
+		case T_AlterSeqStmt:
+			return deparse_AlterSeqStmt(objectId, parsetree);
+
+		case T_CreateSeqStmt:
+			return deparse_CreateSeqStmt(objectId, parsetree);
+
+		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",
+				 (int) nodeTag(parsetree));
+	}
+
+	return NULL;
+}
+
+/*
+ * Workhorse to deparse a CollectedCommand.
+ */
+char *
+deparse_utility_command(CollectedCommand *cmd)
+{
+	OverrideSearchPath *overridePath;
+	MemoryContext oldcxt;
+	MemoryContext tmpcxt;
+	ObjTree    *tree;
+	char	   *command = NULL;
+	StringInfoData str;
+
+	/*
+	 * Allocate everything done by the deparsing routines into a temp context,
+	 * to avoid having to sprinkle them with memory handling code, but
+	 * allocate the output StringInfo before switching.
+	 */
+	initStringInfo(&str);
+	tmpcxt = AllocSetContextCreate(CurrentMemoryContext,
+								   "deparse ctx",
+								   ALLOCSET_DEFAULT_MINSIZE,
+								   ALLOCSET_DEFAULT_INITSIZE,
+								   ALLOCSET_DEFAULT_MAXSIZE);
+	oldcxt = MemoryContextSwitchTo(tmpcxt);
+
+	/*
+	 * Many routines underlying this one will invoke ruleutils.c functionality
+	 * to obtain deparsed versions of expressions.  In such results, we want
+	 * all object names to be qualified, so that results are "portable" to
+	 * environments with different search_path settings.  Rather than
+	 * injecting what would be repetitive calls to override search path all
+	 * over the place, we do it centrally here.
+	 */
+	overridePath = GetOverrideSearchPath(CurrentMemoryContext);
+	overridePath->schemas = NIL;
+	overridePath->addCatalog = false;
+	overridePath->addTemp = true;
+	PushOverrideSearchPath(overridePath);
+
+	switch (cmd->type)
+	{
+		case SCT_Simple:
+			tree = deparse_simple_command(cmd);
+			break;
+		case SCT_AlterTable:
+			tree = deparse_AlterRelation(cmd);
+			break;
+		default:
+			elog(ERROR, "unexpected deparse node type %d", cmd->type);
+	}
+
+	PopOverrideSearchPath();
+
+	if (tree)
+	{
+		Jsonb	   *jsonb;
+
+		jsonb = objtree_to_jsonb(tree);
+		command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
+	}
+
+	/*
+	 * Clean up.  Note that since we created the StringInfo in the caller's
+	 * context, the output string is not deleted here.
+	 */
+	MemoryContextSwitchTo(oldcxt);
+	MemoryContextDelete(tmpcxt);
+
+	return command;
+}
+
+/*
+ * Given a CollectedCommand, return a JSON representation of it.
+ *
+ * The command is expanded fully so that there are no ambiguities even in the
+ * face of search_path changes.
+ */
+Datum
+ddl_deparse_to_json(PG_FUNCTION_ARGS)
+{
+	CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
+	char	   *command;
+
+	command = deparse_utility_command(cmd);
+
+	if (command)
+		PG_RETURN_TEXT_P(cstring_to_text(command));
+
+	PG_RETURN_NULL();
+}
diff --git a/src/backend/commands/ddljson.c b/src/backend/commands/ddljson.c
new file mode 100644
index 0000000000..6d24e6a9aa
--- /dev/null
+++ b/src/backend/commands/ddljson.c
@@ -0,0 +1,708 @@
+/*-------------------------------------------------------------------------
+ *
+ * ddljson.c
+ *	  JSON code related to DDL command deparsing
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/commands/ddljson.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "tcop/ddldeparse.h"
+#include "utils/builtins.h"
+#include "utils/jsonb.h"
+
+
+#define ADVANCE_PARSE_POINTER(ptr,end_ptr) \
+	do { \
+		if (++(ptr) >= (end_ptr)) \
+			ereport(ERROR, \
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE), \
+					errmsg("unterminated format specifier")); \
+	} while (0)
+
+/*
+ * Conversion specifier which determines how to expand the JSON element
+ * into a string.
+ */
+typedef enum
+{
+	SpecDottedName,
+	SpecIdentifier,
+	SpecNumber,
+	SpecString,
+	SpecStringLiteral,
+	SpecTypeName
+} convSpecifier;
+
+/*
+ * A ternary value that represents a boolean type JsonbValue.
+ */
+typedef enum
+{
+	tv_absent,
+	tv_true,
+	tv_false
+}			json_trivalue;
+
+static bool expand_one_jsonb_element(StringInfo buf, char *param,
+									 JsonbValue *jsonval, convSpecifier specifier,
+									 const char *fmt);
+static void expand_jsonb_array(StringInfo buf, char *param,
+							   JsonbValue *jsonarr, char *arraysep,
+							   convSpecifier specifier, const char *fmt);
+static void fmtstr_error_callback(void *arg);
+
+/*
+ * Given a JsonbContainer, find the JsonbValue with the given key name in it.
+ * If it's of a type other than jbvBool, an error is raised. If it doesn't
+ * exist, tv_absent is returned; otherwise return the actual json_trivalue.
+ */
+static json_trivalue
+find_bool_in_jsonbcontainer(JsonbContainer *container, char *keyname)
+{
+	JsonbValue	key;
+	JsonbValue *value;
+	json_trivalue result;
+
+	key.type = jbvString;
+	key.val.string.val = keyname;
+	key.val.string.len = strlen(keyname);
+	value = findJsonbValueFromContainer(container,
+										JB_FOBJECT, &key);
+	if (value == NULL)
+		return tv_absent;
+	if (value->type != jbvBool)
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("element \"%s\" is not of type boolean", keyname));
+	result = value->val.boolean ? tv_true : tv_false;
+	pfree(value);
+
+	return result;
+}
+
+/*
+ * Given a JsonbContainer, find the JsonbValue with the given key name in it.
+ * If it's of a type other than jbvString, an error is raised.  If it doesn't
+ * exist, an error is raised unless missing_ok; otherwise return NULL.
+ *
+ * If it exists and is a string, a freshly palloc'ed copy is returned.
+ *
+ * If *length is not NULL, it is set to the length of the string.
+ */
+static char *
+find_string_in_jsonbcontainer(JsonbContainer *container, char *keyname,
+							  bool missing_ok, int *length)
+{
+	JsonbValue	key;
+	JsonbValue *value;
+	char	   *str;
+
+	/* XXX verify that this is an object, not an array */
+
+	key.type = jbvString;
+	key.val.string.val = keyname;
+	key.val.string.len = strlen(keyname);
+	value = findJsonbValueFromContainer(container,
+										JB_FOBJECT, &key);
+	if (value == NULL)
+	{
+		if (missing_ok)
+			return NULL;
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("missing element \"%s\" in JSON object", keyname));
+	}
+
+	if (value->type != jbvString)
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("element \"%s\" is not of type string", keyname));
+
+	str = pnstrdup(value->val.string.val, value->val.string.len);
+	if (length)
+		*length = value->val.string.len;
+	pfree(value);
+	return str;
+}
+
+/*
+ * Recursive helper for deparse_ddl_json_to_string.
+ *
+ * Find the "fmt" element in the given container, and expand it into the
+ * provided StringInfo.
+ */
+static void
+expand_fmt_recursive(StringInfo buf, JsonbContainer *container)
+{
+	JsonbValue	key;
+	JsonbValue *value;
+	const char *cp;
+	const char *start_ptr;
+	const char *end_ptr;
+	int			len;
+
+	start_ptr = find_string_in_jsonbcontainer(container, "fmt", false, &len);
+	end_ptr = start_ptr + len;
+
+	for (cp = start_ptr; cp < end_ptr; cp++)
+	{
+		convSpecifier specifier;
+		bool		is_array = false;
+		char	   *param = NULL;
+		char	   *arraysep = NULL;
+
+		if (*cp != '%')
+		{
+			appendStringInfoCharMacro(buf, *cp);
+			continue;
+		}
+
+		ADVANCE_PARSE_POINTER(cp, end_ptr);
+
+		/* Easy case: %% outputs a single % */
+		if (*cp == '%')
+		{
+			appendStringInfoCharMacro(buf, *cp);
+			continue;
+		}
+
+		/*
+		 * Scan the mandatory element name.  Allow for an array separator
+		 * (which may be the empty string) to be specified after a colon.
+		 */
+		if (*cp == '{')
+		{
+			StringInfoData parbuf;
+			StringInfoData arraysepbuf;
+			StringInfo	appendTo;
+
+			initStringInfo(&parbuf);
+			appendTo = &parbuf;
+
+			ADVANCE_PARSE_POINTER(cp, end_ptr);
+			while (cp < end_ptr)
+			{
+				if (*cp == ':')
+				{
+					/*
+					 * Found array separator delimiter; element name is now
+					 * complete, start filling the separator.
+					 */
+					initStringInfo(&arraysepbuf);
+					appendTo = &arraysepbuf;
+					is_array = true;
+					ADVANCE_PARSE_POINTER(cp, end_ptr);
+					continue;
+				}
+
+				if (*cp == '}')
+				{
+					ADVANCE_PARSE_POINTER(cp, end_ptr);
+					break;
+				}
+				appendStringInfoCharMacro(appendTo, *cp);
+				ADVANCE_PARSE_POINTER(cp, end_ptr);
+			}
+			param = parbuf.data;
+			if (is_array)
+				arraysep = arraysepbuf.data;
+		}
+		if (param == NULL)
+			ereport(ERROR,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("missing conversion name in conversion specifier"));
+
+		switch (*cp)
+		{
+			case 'I':
+				specifier = SpecIdentifier;
+				break;
+			case 'D':
+				specifier = SpecDottedName;
+				break;
+			case 's':
+				specifier = SpecString;
+				break;
+			case 'L':
+				specifier = SpecStringLiteral;
+				break;
+			case 'T':
+				specifier = SpecTypeName;
+				break;
+			case 'n':
+				specifier = SpecNumber;
+				break;
+			default:
+				ereport(ERROR,
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("invalid conversion specifier \"%c\"", *cp));
+		}
+
+		/*
+		 * Obtain the element to be expanded.
+		 */
+		key.type = jbvString;
+		key.val.string.val = param;
+		key.val.string.len = strlen(param);
+
+		value = findJsonbValueFromContainer(container, JB_FOBJECT, &key);
+		Assert(value != NULL);
+
+		/*
+		 * Expand the data (possibly an array) into the output StringInfo.
+		 */
+		if (is_array)
+			expand_jsonb_array(buf, param, value, arraysep, specifier, start_ptr);
+		else
+			expand_one_jsonb_element(buf, param, value, specifier, start_ptr);
+
+		pfree(value);
+	}
+}
+
+/*
+ * Expand a json value as a quoted identifier.  The value must be of type string.
+ */
+static void
+expand_jsonval_identifier(StringInfo buf, JsonbValue *jsonval)
+{
+	char	   *str;
+
+	Assert(jsonval->type == jbvString);
+
+	str = pnstrdup(jsonval->val.string.val, jsonval->val.string.len);
+	appendStringInfoString(buf, quote_identifier(str));
+	pfree(str);
+}
+
+/*
+ * Expand a json value as a dot-separated-name.  The value must be of type
+ * binary and may contain elements "schemaname" (optional), "objname"
+ * (mandatory), "attrname" (optional).  Double quotes are added to each element
+ * as necessary, and dot separators where needed.
+ *
+ * One day we might need a "catalog" element as well, but no current use case
+ * needs that.
+ */
+static void
+expand_jsonval_dottedname(StringInfo buf, JsonbValue *jsonval)
+{
+	char	   *str;
+	JsonbContainer *data = jsonval->val.binary.data;
+
+	Assert(jsonval->type == jbvBinary);
+
+	str = find_string_in_jsonbcontainer(data, "schemaname", true, NULL);
+	if (str)
+	{
+		appendStringInfo(buf, "%s.", quote_identifier(str));
+		pfree(str);
+	}
+
+	str = find_string_in_jsonbcontainer(data, "objname", false, NULL);
+	appendStringInfo(buf, "%s", quote_identifier(str));
+	pfree(str);
+
+	str = find_string_in_jsonbcontainer(data, "attrname", true, NULL);
+	if (str)
+	{
+		appendStringInfo(buf, ".%s", quote_identifier(str));
+		pfree(str);
+	}
+}
+
+/*
+ * Expand a JSON value as a type name.
+ */
+static void
+expand_jsonval_typename(StringInfo buf, JsonbValue *jsonval)
+{
+	char	   *schema = NULL;
+	char	   *typename = NULL;
+	char	   *typmodstr = NULL;
+	json_trivalue is_array;
+	char	   *array_decor;
+	JsonbContainer *data = jsonval->val.binary.data;
+
+	/*
+	 * We omit schema-qualifying the output name if the schema element is
+	 * either the empty string or NULL; the difference between those two cases
+	 * is that in the latter we quote the type name, in the former we don't.
+	 * This allows for types with special typmod needs, such as interval and
+	 * timestamp (see format_type_detailed), while at the same time allowing
+	 * for the schema name to be omitted from type names that require quotes
+	 * but are to be obtained from a user schema.
+	 */
+
+	schema = find_string_in_jsonbcontainer(data, "schemaname", true, NULL);
+	typename = find_string_in_jsonbcontainer(data, "typename", false, NULL);
+	typmodstr = find_string_in_jsonbcontainer(data, "typmod", true, NULL);
+	is_array = find_bool_in_jsonbcontainer(data, "typarray");
+	switch (is_array)
+	{
+		case tv_true:
+			array_decor = "[]";
+			break;
+
+		case tv_false:
+			array_decor = "";
+			break;
+
+		case tv_absent:
+		default:
+			ereport(ERROR,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("missing typarray element"));
+	}
+
+	if (schema == NULL)
+		appendStringInfo(buf, "%s", quote_identifier(typename));
+	else if (schema[0] == '\0')
+		appendStringInfo(buf, "%s", typename);	/* Special typmod needs */
+	else
+		appendStringInfo(buf, "%s.%s", quote_identifier(schema),
+						 quote_identifier(typename));
+
+	appendStringInfo(buf, "%s%s", typmodstr ? typmodstr : "", array_decor);
+
+	if (schema)
+		pfree(schema);
+	if (typename)
+		pfree(typename);
+	if (typmodstr)
+		pfree(typmodstr);
+}
+
+/*
+ * Expand a JSON value as a string.  The value must be of type string or of
+ * type Binary.  In the latter case, it must contain a "fmt" element which will
+ * be recursively expanded; also, if the object contains an element "present"
+ * and it is set to false, the expansion is the empty string.
+ *
+ * Returns false if no actual expansion was made due to the "present" flag
+ * being set to "false".
+ *
+ * The caller is responsible to check jsonval is of type jbvString or jbvBinary.
+ */
+static bool
+expand_jsonval_string(StringInfo buf, JsonbValue *jsonval)
+{
+	bool expanded = false;
+
+	Assert((jsonval->type == jbvString) || (jsonval->type == jbvBinary));
+
+	if (jsonval->type == jbvString)
+	{
+		appendBinaryStringInfo(buf, jsonval->val.string.val,
+							   jsonval->val.string.len);
+		expanded = true;
+	}
+	else if (jsonval->type == jbvBinary)
+	{
+		json_trivalue present;
+
+		present = find_bool_in_jsonbcontainer(jsonval->val.binary.data,
+											  "present");
+
+		/*
+		 * If "present" is set to false, this element expands to empty;
+		 * otherwise (either true or absent), expand "fmt".
+		 */
+		if (present != tv_false)
+		{
+			expand_fmt_recursive(buf, jsonval->val.binary.data);
+			expanded = true;
+		}
+	}
+
+	return expanded;
+}
+
+/*
+ * Expand a JSON value as a string literal.
+ */
+static void
+expand_jsonval_strlit(StringInfo buf, JsonbValue *jsonval)
+{
+	char	   *str;
+	StringInfoData dqdelim;
+	static const char dqsuffixes[] = "_XYZZYX_";
+	int			dqnextchar = 0;
+
+	Assert(jsonval->type == jbvString);
+
+	str = pnstrdup(jsonval->val.string.val, jsonval->val.string.len);
+
+	/* Easy case: if there are no ' and no \, just use a single quote */
+	if (strpbrk(str, "\'\\") == NULL)
+	{
+		appendStringInfo(buf, "'%s'", str);
+		pfree(str);
+		return;
+	}
+
+	/* Otherwise need to find a useful dollar-quote delimiter */
+	initStringInfo(&dqdelim);
+	appendStringInfoString(&dqdelim, "$");
+	while (strstr(str, dqdelim.data) != NULL)
+	{
+		appendStringInfoChar(&dqdelim, dqsuffixes[dqnextchar++]);
+		dqnextchar = dqnextchar % (sizeof(dqsuffixes) - 1);
+	}
+	/* Add trailing $ */
+	appendStringInfoChar(&dqdelim, '$');
+
+	/* And finally produce the quoted literal into the output StringInfo */
+	appendStringInfo(buf, "%s%s%s", dqdelim.data, str, dqdelim.data);
+	pfree(dqdelim.data);
+	pfree(str);
+}
+
+/*
+ * Expand a JSON value as an integer quantity.
+ */
+static void
+expand_jsonval_number(StringInfo buf, JsonbValue *jsonval)
+{
+	char	   *strdatum;
+
+	Assert(jsonval->type == jbvNumeric);
+
+	strdatum = DatumGetCString(DirectFunctionCall1(numeric_out,
+												   NumericGetDatum(jsonval->val.numeric)));
+	appendStringInfoString(buf, strdatum);
+	pfree(strdatum);
+}
+
+/*
+ * Expand one JSON element into the output StringInfo according to the
+ * conversion specifier.  The element type is validated, and an error is raised
+ * if it doesn't match what we expect for the conversion specifier.
+ *
+ * Returns true, except for the formatted string case if no actual expansion
+ * was made (due to the "present" flag being set to "false").
+ */
+static bool
+expand_one_jsonb_element(StringInfo buf, char *param, JsonbValue *jsonval,
+						 convSpecifier specifier, const char *fmt)
+{
+	bool		string_expanded = true;
+	ErrorContextCallback sqlerrcontext;
+
+	/* If we were given a format string, setup an ereport() context callback */
+	if (fmt)
+	{
+		sqlerrcontext.callback = fmtstr_error_callback;
+		sqlerrcontext.arg = (void *) fmt;
+		sqlerrcontext.previous = error_context_stack;
+		error_context_stack = &sqlerrcontext;
+	}
+
+	if (!jsonval)
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("element \"%s\" not found", param));
+
+	switch (specifier)
+	{
+		case SpecIdentifier:
+			if (jsonval->type != jbvString)
+				ereport(ERROR,
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("expected JSON string for %%I element \"%s\", got %d",
+							   param, jsonval->type));
+			expand_jsonval_identifier(buf, jsonval);
+			break;
+
+		case SpecDottedName:
+			if (jsonval->type != jbvBinary)
+				ereport(ERROR,
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("expected JSON struct for %%D element \"%s\", got %d",
+							   param, jsonval->type));
+			expand_jsonval_dottedname(buf, jsonval);
+			break;
+
+		case SpecString:
+			if (jsonval->type != jbvString &&
+				jsonval->type != jbvBinary)
+				ereport(ERROR,
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("expected JSON string or struct for %%s element \"%s\", got %d",
+							   param, jsonval->type));
+			string_expanded = expand_jsonval_string(buf, jsonval);
+			break;
+
+		case SpecStringLiteral:
+			if (jsonval->type != jbvString)
+				ereport(ERROR,
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("expected JSON string for %%L element \"%s\", got %d",
+							   param, jsonval->type));
+			expand_jsonval_strlit(buf, jsonval);
+			break;
+
+		case SpecTypeName:
+			if (jsonval->type != jbvBinary)
+				ereport(ERROR,
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("expected JSON struct for %%T element \"%s\", got %d",
+							   param, jsonval->type));
+			expand_jsonval_typename(buf, jsonval);
+			break;
+
+		case SpecNumber:
+			if (jsonval->type != jbvNumeric)
+				ereport(ERROR,
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("expected JSON numeric for %%n element \"%s\", got %d",
+							   param, jsonval->type));
+			expand_jsonval_number(buf, jsonval);
+			break;
+	}
+
+	if (fmt)
+		error_context_stack = sqlerrcontext.previous;
+
+	return string_expanded;
+}
+
+/*
+ * Iterate on the elements of a JSON array, expanding each one into the output
+ * StringInfo per the given conversion specifier, separated by the given
+ * separator.
+ */
+static void
+expand_jsonb_array(StringInfo buf, char *param,
+				   JsonbValue *jsonarr, char *arraysep, convSpecifier specifier,
+				   const char *fmt)
+{
+	ErrorContextCallback sqlerrcontext;
+	JsonbContainer *container;
+	JsonbIterator *it;
+	JsonbValue	v;
+	int			type;
+	bool		first = true;
+	StringInfoData arrayelem;
+
+	/* If we were given a format string, setup an ereport() context callback */
+	if (fmt)
+	{
+		sqlerrcontext.callback = fmtstr_error_callback;
+		sqlerrcontext.arg = (void *) fmt;
+		sqlerrcontext.previous = error_context_stack;
+		error_context_stack = &sqlerrcontext;
+	}
+
+	if (!jsonarr)
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("element \"%s\" not found", param));
+
+	if (jsonarr->type != jbvBinary)
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("element \"%s\" is not a JSON array", param));
+
+	container = jsonarr->val.binary.data;
+	if (!JsonContainerIsArray(container))
+		ereport(ERROR,
+				errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				errmsg("element \"%s\" is not a JSON array", param));
+
+	initStringInfo(&arrayelem);
+
+	it = JsonbIteratorInit(container);
+	while ((type = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
+	{
+		if (type == WJB_ELEM)
+		{
+			resetStringInfo(&arrayelem);
+
+			if (expand_one_jsonb_element(&arrayelem, param, &v, specifier, NULL))
+			{
+				if (!first)
+					appendStringInfoString(buf, arraysep);
+
+				appendBinaryStringInfo(buf, arrayelem.data, arrayelem.len);
+				first = false;
+			}
+		}
+	}
+
+	if (fmt)
+		error_context_stack = sqlerrcontext.previous;
+}
+
+/*
+ * Workhorse for ddl_deparse_expand_command.
+ */
+char *
+deparse_ddl_json_to_string(char *json_str)
+{
+	Datum		d;
+	Jsonb	   *jsonb;
+	StringInfo	buf = (StringInfo) palloc0(sizeof(StringInfoData));
+
+	initStringInfo(buf);
+
+	d = DirectFunctionCall1(jsonb_in, PointerGetDatum(json_str));
+	jsonb = (Jsonb *) DatumGetPointer(d);
+
+	expand_fmt_recursive(buf, &jsonb->root);
+
+	return buf->data;
+}
+
+/*------
+ * Returns a formatted string from a JSON object.
+ *
+ * The starting point is the element named "fmt" (which must be a string).
+ * This format string may contain zero or more %-escapes, which consist of an
+ * element name enclosed in { }, possibly followed by a conversion modifier,
+ * followed by a conversion specifier.  Possible conversion specifiers are:
+ *
+ * %		expand to a literal %
+ * I		expand as a single, non-qualified identifier
+ * D		expand as a possibly-qualified identifier
+ * T		expand as a type name
+ * L		expand as a string literal (quote using single quotes)
+ * s		expand as a simple string (no quoting)
+ * n		expand as a simple number (no quoting)
+ *
+ * The element name may have an optional separator specification preceded
+ * by a colon.  Its presence indicates that the element is expected to be
+ * an array; the specified separator is used to join the array elements.
+ *
+ * The actual conversion of single JSON element into string according to
+ * above conversion specifiers takes place in expand_one_jsonb_element()
+ *------
+ */
+Datum
+ddl_deparse_expand_command(PG_FUNCTION_ARGS)
+{
+	text	   *json = PG_GETARG_TEXT_P(0);
+	char	   *json_str;
+
+	json_str = text_to_cstring(json);
+
+	PG_RETURN_TEXT_P(cstring_to_text(deparse_ddl_json_to_string(json_str)));
+}
+
+/*
+ * Error context callback for JSON format string expansion.
+ *
+ * XXX: indicate which element we're expanding, if applicable.
+ */
+static void
+fmtstr_error_callback(void *arg)
+{
+	errcontext("while expanding format string \"%s\"", (char *) arg);
+}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index d4b00d1a82..4d48e490ed 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -37,6 +37,7 @@
 #include "miscadmin.h"
 #include "parser/parse_func.h"
 #include "pgstat.h"
+#include "tcop/ddldeparse.h"
 #include "tcop/deparse_utility.h"
 #include "tcop/utility.h"
 #include "utils/acl.h"
diff --git a/src/backend/commands/meson.build b/src/backend/commands/meson.build
index 42cced9ebe..9539e53bac 100644
--- a/src/backend/commands/meson.build
+++ b/src/backend/commands/meson.build
@@ -17,6 +17,8 @@ backend_sources += files(
   'copyto.c',
   'createas.c',
   'dbcommands.c',
+  'ddldeparse.c',
+  'ddljson.c',
   'define.c',
   'discard.c',
   'dropcmds.c',
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index ef01449678..4bb731d5ff 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1707,6 +1707,49 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
 		relation_close(tablerel, NoLock);
 }
 
+/*
+ * Return sequence parameters, detailed
+ */
+Sequence_values *
+get_sequence_values(Oid sequenceId)
+{
+	Buffer      buf;
+	SeqTable    elm;
+	Relation    seqrel;
+	HeapTuple	seqtuple;
+	HeapTupleData seqtupledata;
+	Form_pg_sequence seqform;
+	Form_pg_sequence_data seq;
+	Sequence_values *seqvalues;
+
+	seqtuple = SearchSysCache1(SEQRELID, sequenceId);
+	if (!HeapTupleIsValid(seqtuple))
+		elog(ERROR, "cache lookup failed for sequence %u", sequenceId);
+	seqform = (Form_pg_sequence) GETSTRUCT(seqtuple);
+
+	ReleaseSysCache(seqtuple);
+
+	/* Open and lock sequence */
+	init_sequence(sequenceId, &elm, &seqrel);
+
+	if (pg_class_aclcheck(sequenceId, GetUserId(),
+		ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
+		ereport(ERROR,
+				errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				errmsg("permission denied for sequence %s",
+					   RelationGetRelationName(seqrel)));
+
+	seq = read_seq_tuple(seqrel, &buf, &seqtupledata);
+
+	seqvalues = (Sequence_values *) palloc(sizeof(Sequence_values));
+	seqvalues->last_value = seq->last_value;
+	seqvalues->seqform = seqform;
+
+	UnlockReleaseBuffer(buf);
+	relation_close(seqrel, NoLock);
+
+	return seqvalues;
+}
 
 /*
  * Return sequence parameters in a list of the form created by the parser.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 4d49d70c33..c15e943f1d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -600,7 +600,6 @@ static ObjectAddress ATExecSetCompression(Relation rel,
 										  const char *column, Node *newValue, LOCKMODE lockmode);
 
 static void index_copy_data(Relation rel, RelFileLocator newrlocator);
-static const char *storage_name(char c);
 
 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
 											Oid oldRelOid, void *arg);
@@ -2266,7 +2265,7 @@ truncate_check_activity(Relation rel)
  * storage_name
  *	  returns the name corresponding to a typstorage/attstorage enum value
  */
-static const char *
+char *
 storage_name(char c)
 {
 	switch (c)
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index d67580fc77..753456ecbc 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1393,6 +1393,7 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
 		atcmd->cmds = atsubcmds;
 		atcmd->objtype = OBJECT_TABLE;
 		atcmd->missing_ok = false;
+		atcmd->table_like = true;
 		result = lcons(atcmd, result);
 	}
 
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/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c
index 12402a0637..4318129558 100644
--- a/src/backend/utils/adt/format_type.c
+++ b/src/backend/utils/adt/format_type.c
@@ -27,8 +27,6 @@
 #include "utils/numeric.h"
 #include "utils/syscache.h"
 
-static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
-
 
 /*
  * SQL function: format_type(type_oid, typemod)
@@ -329,6 +327,110 @@ format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
 	return buf;
 }
 
+/*
+ * Similar to format_type_extended, except we return each bit of information
+ * separately:
+ *
+ * - nspid is the schema OID.  For certain SQL-standard types which have weird
+ *   typmod rules, we return InvalidOid; the caller is expected to not schema-
+ *   qualify the name nor add quotes to the type name in this case.
+ *
+ * - typname is set to the type name, without quotes
+ *
+ * - typemodstr is set to the typemod, if any, as a string with parentheses
+ *
+ * - typarray indicates whether []s must be added
+ *
+ * We don't try to decode type names to their standard-mandated names, except
+ * in the cases of types with unusual typmod rules.
+ */
+void
+format_type_detailed(Oid type_oid, int32 typemod,
+					 Oid *nspid, char **typname, char **typemodstr,
+					 bool *typearray)
+{
+	HeapTuple	tuple;
+	Form_pg_type typeform;
+	Oid			array_base_type;
+
+	tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for type with OID %u", type_oid);
+
+	typeform = (Form_pg_type) GETSTRUCT(tuple);
+
+	/*
+	 * We switch our attention to the array element type for certain cases.
+	 * Check if it's a "true" array type.  Pseudo-array types such as "name"
+	 * shouldn't get deconstructed.  Also check the toast property, and don't
+	 * deconstruct "plain storage" array types --- this is because we don't
+	 * want to show oidvector as oid[].
+	 */
+	array_base_type = typeform->typelem;
+
+	*typearray = (IsTrueArrayType(typeform) && typeform->typstorage != TYPSTORAGE_PLAIN);
+
+	if (*typearray)
+	{
+		ReleaseSysCache(tuple);
+		tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_base_type));
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for type with OID %u", type_oid);
+
+		typeform = (Form_pg_type) GETSTRUCT(tuple);
+		type_oid = array_base_type;
+	}
+
+	/*
+	 * Special-case crock for types with strange typmod rules where we put
+	 * typemod at the middle of name (e.g. TIME(6) with time zone). We cannot
+	 * schema-qualify nor add quotes to the type name in these cases.
+	 */
+	*nspid = InvalidOid;
+
+	switch (type_oid)
+	{
+		case TIMEOID:
+			*typname = pstrdup("TIME");
+			break;
+		case TIMESTAMPOID:
+			*typname = pstrdup("TIMESTAMP");
+			break;
+		case TIMESTAMPTZOID:
+			if (typemod < 0)
+				*typname = pstrdup("TIMESTAMP WITH TIME ZONE");
+			else
+				/* otherwise, WITH TZ is added by typmod. */
+				*typname = pstrdup("TIMESTAMP");
+			break;
+		case INTERVALOID:
+			*typname = pstrdup("INTERVAL");
+			break;
+		case TIMETZOID:
+			if (typemod < 0)
+				*typname = pstrdup("TIME WITH TIME ZONE");
+			else
+				/* otherwise, WITH TZ is added by typmod. */
+				*typname = pstrdup("TIME");
+			break;
+		default:
+
+			/*
+			 * No additional processing is required for other types, so get
+			 * the type name and schema directly from the catalog.
+			 */
+			*nspid = typeform->typnamespace;
+			*typname = pstrdup(NameStr(typeform->typname));
+	}
+
+	if (typemod >= 0)
+		*typemodstr = printTypmod("", typemod, typeform->typmodout);
+	else
+		*typemodstr = pstrdup("");
+
+	ReleaseSysCache(tuple);
+}
+
 /*
  * This version is for use within the backend in error messages, etc.
  * One difference is that it will fail for an invalid type.
@@ -363,7 +465,7 @@ format_type_with_typemod(Oid type_oid, int32 typemod)
 /*
  * Add typmod decoration to the basic type name
  */
-static char *
+char *
 printTypmod(const char *typname, int32 typmod, Oid typmodout)
 {
 	char	   *res;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index d3a973d86b..b8729d8e3d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -51,7 +51,6 @@
 #include "optimizer/optimizer.h"
 #include "parser/parse_agg.h"
 #include "parser/parse_func.h"
-#include "parser/parse_node.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_relation.h"
 #include "parser/parser.h"
@@ -501,22 +500,15 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
 									   deparse_context *context);
 static void get_tablesample_def(TableSampleClause *tablesample,
 								deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
-							 StringInfo buf);
 static Node *processIndirection(Node *node, deparse_context *context);
 static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
 static char *get_relation_name(Oid relid);
 static char *generate_relation_name(Oid relid, List *namespaces);
 static char *generate_qualified_relation_name(Oid relid);
-static char *generate_function_name(Oid funcid, int nargs,
-									List *argnames, Oid *argtypes,
-									bool has_variadic, bool *use_variadic_p,
-									ParseExprKind special_exprkind);
 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
 static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
-static char *flatten_reloptions(Oid relid);
 static void get_reloptions(StringInfo buf, Datum reloptions);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
@@ -1901,6 +1893,14 @@ pg_get_partkeydef_columns(Oid relid, bool pretty)
 	return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
 }
 
+/* Internal version that reports the full partition key definition */
+char *
+pg_get_partkeydef_string(Oid relid)
+{
+	return pg_get_partkeydef_worker(relid, GET_PRETTY_FLAGS(false), false,
+									false);
+}
+
 /*
  * Internal workhorse to decompile a partition key definition.
  */
@@ -2148,6 +2148,16 @@ pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(string_to_text(res));
 }
 
+/*
+ * Internal version that returns the definition of a CONSTRAINT command
+ */
+char *
+pg_get_constraintdef_string(Oid constraintId)
+{
+	return pg_get_constraintdef_worker(constraintId, false,
+									   GET_PRETTY_FLAGS(false), false);
+}
+
 /*
  * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
  */
@@ -11758,7 +11768,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
  * actual_datatype.  (If you don't want this behavior, just pass
  * InvalidOid for actual_datatype.)
  */
-static void
+void
 get_opclass_name(Oid opclass, Oid actual_datatype,
 				 StringInfo buf)
 {
@@ -12152,7 +12162,7 @@ generate_qualified_relation_name(Oid relid)
  *
  * The result includes all necessary quoting and schema-prefixing.
  */
-static char *
+char *
 generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
 					   bool has_variadic, bool *use_variadic_p,
 					   ParseExprKind special_exprkind)
@@ -12538,7 +12548,7 @@ get_reloptions(StringInfo buf, Datum reloptions)
 /*
  * Generate a C string representing a relation's reloptions, or NULL if none.
  */
-static char *
+char *
 flatten_reloptions(Oid relid)
 {
 	char	   *result = NULL;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6996073989..76ff23b779 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12043,4 +12043,11 @@
   proname => 'any_value_transfn', prorettype => 'anyelement',
   proargtypes => 'anyelement anyelement', prosrc => 'any_value_transfn' },
 
+{ oid => '4642', descr => 'deparse the DDL command into a JSON format string',
+  proname => 'ddl_deparse_to_json', prorettype => 'text',
+  proargtypes => 'pg_ddl_command', prosrc => 'ddl_deparse_to_json' },
+{ oid => '4643', descr => 'expand JSON format DDL to a plain text DDL command',
+  proname => 'ddl_deparse_expand_command', prorettype => 'text',
+  proargtypes => 'text', prosrc => 'ddl_deparse_expand_command' },
+
 ]
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 7db7b3da7b..c0a39596ac 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -15,6 +15,7 @@
 
 #include "access/xlogreader.h"
 #include "catalog/objectaddress.h"
+#include "catalog/pg_sequence.h"
 #include "fmgr.h"
 #include "lib/stringinfo.h"
 #include "nodes/parsenodes.h"
@@ -51,9 +52,17 @@ typedef struct xl_seq_rec
 	/* SEQUENCE TUPLE DATA FOLLOWS AT THE END */
 } xl_seq_rec;
 
+/* Information needed to define a sequence. */
+typedef struct Sequence_values
+{
+	Form_pg_sequence seqform;
+	int64		last_value;
+} Sequence_values;
+
 extern int64 nextval_internal(Oid relid, bool check_permissions);
 extern Datum nextval(PG_FUNCTION_ARGS);
 extern List *sequence_options(Oid relid);
+extern Sequence_values *get_sequence_values(Oid sequenceId);
 
 extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *seq);
 extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 17b9404937..5a082e57ea 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -106,4 +106,6 @@ extern void RangeVarCallbackOwnsRelation(const RangeVar *relation,
 extern bool PartConstraintImpliedByRelConstraint(Relation scanrel,
 												 List *partConstraint);
 
+extern char *storage_name(char c);
+
 #endif							/* TABLECMDS_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0ca298f5a1..71fb0d0874 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2167,6 +2167,7 @@ typedef struct AlterTableStmt
 	List	   *cmds;			/* list of subcommands */
 	ObjectType	objtype;		/* type of object */
 	bool		missing_ok;		/* skip error if table missing */
+	bool		table_like;		/* internally generated for TableLikeClause */
 } AlterTableStmt;
 
 typedef enum AlterTableType
diff --git a/src/include/tcop/ddldeparse.h b/src/include/tcop/ddldeparse.h
new file mode 100644
index 0000000000..15481dc3c4
--- /dev/null
+++ b/src/include/tcop/ddldeparse.h
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * ddldeparse.h
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/tcop/ddldeparse.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef DDL_DEPARSE_H
+#define DDL_DEPARSE_H
+
+#include "tcop/deparse_utility.h"
+
+extern char *deparse_utility_command(CollectedCommand *cmd);
+extern char *deparse_ddl_json_to_string(char *jsonb);
+extern char *deparse_drop_command(const char *objidentity, const char *objecttype,
+								  Node *parsetree);
+
+#endif							/* DDL_DEPARSE_H */
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)
 {
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 2f8b46d6da..cfda299dee 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -124,9 +124,14 @@ extern Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS);
 #define FORMAT_TYPE_INVALID_AS_NULL	0x08	/* NULL if undefined */
 extern char *format_type_extended(Oid type_oid, int32 typemod, bits16 flags);
 
+extern void format_type_detailed(Oid type_oid, int32 typemod,
+								  Oid *nspid, char **typname,
+								  char **typemodstr, bool *typearray);
+
 extern char *format_type_be(Oid type_oid);
 extern char *format_type_be_qualified(Oid type_oid);
 extern char *format_type_with_typemod(Oid type_oid, int32 typemod);
+extern char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
 
 extern int32 type_maximum_size(Oid type_oid, int32 typemod);
 
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index b006d9d475..fd1d85fc8d 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -16,6 +16,7 @@
 #include "nodes/nodes.h"
 #include "nodes/parsenodes.h"
 #include "nodes/pg_list.h"
+#include "parser/parse_node.h"
 
 struct Plan;					/* avoid including plannodes.h here */
 struct PlannedStmt;
@@ -31,9 +32,11 @@ extern char *pg_get_indexdef_columns_extended(Oid indexrelid,
 extern char *pg_get_querydef(Query *query, bool pretty);
 
 extern char *pg_get_partkeydef_columns(Oid relid, bool pretty);
+extern char *pg_get_partkeydef_string(Oid relid);
 extern char *pg_get_partconstrdef_string(Oid partitionId, char *aliasname);
 
 extern char *pg_get_constraintdef_command(Oid constraintId);
+extern char *pg_get_constraintdef_string(Oid constraintId);
 extern char *deparse_expression(Node *expr, List *dpcontext,
 								bool forceprefix, bool showimplicit);
 extern List *deparse_context_for(const char *aliasname, Oid relid);
@@ -45,7 +48,14 @@ extern List *select_rtable_names_for_explain(List *rtable,
 											 Bitmapset *rels_used);
 extern char *generate_collation_name(Oid collid);
 extern char *generate_opclass_name(Oid opclass);
+extern char *generate_function_name(Oid funcid, int nargs, List *argnames,
+									Oid *argtypes, bool has_variadic,
+									bool *use_variadic_p,
+									ParseExprKind special_exprkind);
 extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype,
+							 StringInfo buf);
+extern char *flatten_reloptions(Oid relid);
 
 extern char *pg_get_statisticsobjdef_string(Oid statextid);
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 260854747b..87c147dfdb 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1636,6 +1636,9 @@ OSInfo
 OSSLCipher
 OSSLDigest
 OVERLAPPED
+ObjElem
+ObjTree
+ObjType
 ObjectAccessDrop
 ObjectAccessNamespaceSearch
 ObjectAccessPostAlter
@@ -3222,6 +3225,7 @@ config_var_value
 contain_aggs_of_level_context
 contain_placeholder_references_context
 convert_testexpr_context
+convSpecifier
 copy_data_dest_cb
 copy_data_source_cb
 core_YYSTYPE
@@ -3425,6 +3429,7 @@ json_manifest_perwalrange_callback
 json_ofield_action
 json_scalar_action
 json_struct_action
+json_trivalue
 keyEntryData
 key_t
 lclContext
-- 
2.34.1

From aacb99ef6a800ddb4e1f7574a4e8da65a11c5683 Mon Sep 17 00:00:00 2001
From: Shveta Malik <[email-masked]>
Date: Mon, 22 May 2023 08:46:25 +0530
Subject: [PATCH 5/8] DDL replication for Table DDL commands

OVERVIEW
--------
To support DDL replication, we use the event trigger and DDL deparsing
facilities. During CREATE PUBLICATION we register a 'command end' trigger that
deparses the DDL (if the DDL is annotated as ddlreplok for DDL replication in
cmdtaglist.h) as a JSON blob, and WAL logs it. The event trigger is automatically
removed at the time of DROP PUBLICATION. The WALSender decodes the WAL and sends
it downstream similar to other DML commands. The subscriber then converts JSON
back to the DDL command string and executes it. In the subscriber, we also add
the newly added rel to pg_subscription_rel so that the DML changes on the new
table can be replicated without having to manually run
"ALTER SUBSCRIPTION ... REFRESH PUBLICATION".

For non-rewrite ALTER object command and CREATE object command:
---------------------------------------------------------------
We deparse the command at ddl_command_end event trigger and WAL log the
deparsed json string. The WALSender decodes the WAL and sends it to
subscriber if the created/altered table is published. It supports most of
ALTER TABLE command except some commands(DDL related to PARTITIONED TABLE
...) that are recently introduced but are not yet supported by the current
ddl_deparser, we will support that later.

For DROP object:
----------------
The 'command start' event handler logs a ddl message with the relids of
the tables that are dropped which the output plugin (pgoutput) stores in
its internal data structure after verifying that it is for a table that is
part of the publication. Later the 'command end' event handler sends the
actual drop message. On receiving the 'command end', pgoutput sends the
DROP command only if it is for one of the relids marked for deletion.

The reason we have to do this is because, once the logical decoder
receives the 'command end' message,  the relid of the table is no longer
valid as it has been deleted as part of invalidations received for the
drop table command. It is no longer possible to verify if the table is
part of the publication list or not. To make this possible, two more elements
are added to the ddl xlog and ddl message, (relid and cmdtype).

We could have also handled all this on the subscriber side, but
that would mean sending spurious ddl messages for tables that are not part
of the publication.

For table_rewrite ALTER TABLE command:
--------------------------------------
Executing a non-immutable expression during the table rewrite phase is not
allowed, as it may result in different data between publisher and subscriber.
While some may suggest converting the rewrite inserts to updates and replicate
them afte the ddl command to maintain data consistency. But it doesn't work if
the replica identity column is altered in the command. This is because the
rewrite inserts do not contain the old values and therefore cannot be converted
to update.

LIMIT:
Commands contain volatile functions are not allowed. Because
it's possible the functions contain DDL/DML in which case these operations
will be executed twice and cause duplicate data. In addition, we don't know
whether the tables being accessed by these DDL/DML are published or not. So
blindly allowing such functions can allow unintended clauses like the tables
accessed in those functions may not even exist on the subscriber.
---
 contrib/test_decoding/expected/ddl.out        |   26 +
 contrib/test_decoding/sql/ddl.sql             |    5 +
 contrib/test_decoding/test_decoding.c         |   48 +
 doc/src/sgml/catalogs.sgml                    |    9 +
 doc/src/sgml/logical-replication.sgml         | 1093 +++++++++++++++++
 src/backend/access/rmgrdesc/Makefile          |    1 +
 .../access/rmgrdesc/logicalddlmsgdesc.c       |   52 +
 src/backend/access/rmgrdesc/meson.build       |    1 +
 src/backend/access/transam/rmgr.c             |    1 +
 src/backend/catalog/pg_publication.c          |    1 +
 src/backend/commands/alter.c                  |   17 +
 src/backend/commands/ddldeparse.c             |   96 +-
 src/backend/commands/event_trigger.c          |   37 +-
 src/backend/commands/publicationcmds.c        |  238 +++-
 src/backend/replication/logical/Makefile      |    2 +
 src/backend/replication/logical/ddlmessage.c  |   83 ++
 src/backend/replication/logical/ddltrigger.c  |  341 +++++
 src/backend/replication/logical/decode.c      |   41 +
 src/backend/replication/logical/logical.c     |   97 +-
 .../replication/logical/logicalfuncs.c        |   24 +
 src/backend/replication/logical/meson.build   |    2 +
 src/backend/replication/logical/proto.c       |   43 +
 .../replication/logical/reorderbuffer.c       |  135 ++
 src/backend/replication/logical/worker.c      |  237 ++++
 src/backend/replication/pgoutput/pgoutput.c   |  166 ++-
 src/backend/tcop/cmdtag.c                     |   26 +-
 src/backend/tcop/utility.c                    |    2 +-
 src/backend/utils/cache/relcache.c            |    1 +
 src/bin/pg_dump/pg_dump.c                     |   26 +-
 src/bin/pg_dump/pg_dump.h                     |    1 +
 src/bin/pg_dump/t/002_pg_dump.pl              |    2 +-
 src/bin/pg_waldump/.gitignore                 |    1 +
 src/bin/pg_waldump/rmgrdesc.c                 |    1 +
 src/bin/psql/describe.c                       |   21 +-
 src/include/access/rmgrlist.h                 |    1 +
 src/include/catalog/pg_event_trigger.h        |    2 +-
 src/include/catalog/pg_proc.dat               |   20 +
 src/include/catalog/pg_publication.h          |   30 +-
 src/include/commands/event_trigger.h          |    2 +-
 src/include/commands/publicationcmds.h        |   14 +
 src/include/replication/ddlmessage.h          |   60 +
 src/include/replication/decode.h              |    1 +
 src/include/replication/logicalproto.h        |    4 +
 src/include/replication/output_plugin.h       |   27 +
 src/include/replication/pgoutput.h            |    1 +
 src/include/replication/reorderbuffer.h       |   39 +
 src/include/tcop/cmdtag.h                     |    4 +-
 src/include/tcop/cmdtaglist.h                 |  386 +++---
 src/include/tcop/ddldeparse.h                 |   10 +-
 src/include/utils/rel.h                       |    2 +
 src/test/regress/expected/psql.out            |    6 +-
 src/test/regress/expected/publication.out     |  420 +++----
 src/tools/pgindent/typedefs.list              |    6 +
 53 files changed, 3434 insertions(+), 478 deletions(-)
 create mode 100644 src/backend/access/rmgrdesc/logicalddlmsgdesc.c
 create mode 100644 src/backend/replication/logical/ddlmessage.c
 create mode 100644 src/backend/replication/logical/ddltrigger.c
 create mode 100644 src/include/replication/ddlmessage.h

diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index d55fb3a667..f4173f0497 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -831,6 +831,32 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
 ------
 (0 rows)
 
+SELECT 'ddl msg1' FROM pg_logical_emit_ddl_message('ddl msg1', 16394, 1, 'msg1');
+ ?column? 
+----------
+ ddl msg1
+(1 row)
+
+SELECT 'ddl msg2' FROM pg_logical_emit_ddl_message('ddl msg2', 16394, 1, '{"fmt": "CREATE SCHEMA %{if_not_exists}s %{name}I %{authorization}s", "name": "foo", "authorization": {"fmt": "AUTHORIZATION %{authorization_role}I", "present": false, "authorization_role": null}, "if_not_exists": ""}');
+ ?column? 
+----------
+ ddl msg2
+(1 row)
+
+SELECT 'ddl msg3' FROM pg_logical_emit_ddl_message('ddl msg3', 16394, 1, '{"fmt": "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D (%{table_elements:, }s) %{inherits}s %{tablespace}s %{on_commit}s %{partition_by}s %{access_method}s %{with_clause}s", "identity": {"objname": "foo", "schemaname": "element_test"}, "inherits": {"fmt": "INHERITS (%{parents:, }D)", "parents": null, "present": false}, "on_commit": {"fmt": "ON COMMIT %{on_commit_value}s", "present": false, "on_commit_value": null}, "tablespace": {"fmt": "TABLESPACE %{tablespace}I", "present": false, "tablespace": null}, "persistence": "", "with_clause": {"fmt": "WITH", "present": false}, "partition_by": {"fmt": "PARTITION BY %{definition}s", "present": false, "definition": null}, "access_method": {"fmt": "USING %{access_method}I", "present": false, "access_method": null}, "if_not_exists": "", "table_elements": [{"fmt": "%{name}I %{coltype}T STORAGE %{colstorage}s %{compression}s %{collation}s %{not_null}s %{default}s %{generated_column}s", "name": "id", "type": "column", "coltype": {"typmod": "", "typarray": false, "typename": "int4", "schemaname": "pg_catalog"}, "default": {"fmt": "DEFAULT", "present": false}, "not_null": "", "collation": {"fmt": "COLLATE", "present": false}, "colstorage": "plain", "compression": {"fmt": "COMPRESSION %{compression_method}I", "present": false, "compression_method": null}, "generated_column": {"fmt": "GENERATED ALWAYS AS", "present": false}}]}}');
+ ?column? 
+----------
+ ddl msg3
+(1 row)
+
+SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                data                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ DDL message: prefix: ddl msg1, relid: 16394, cmdtype: Drop, sz: 4 content: msg1
+ DDL message: prefix: ddl msg2, relid: 16394, cmdtype: Drop, sz: 217 content: {"fmt": "CREATE SCHEMA %{if_not_exists}s %{name}I %{authorization}s", "name": "foo", "authorization": {"fmt": "AUTHORIZATION %{authorization_role}I", "present": false, "authorization_role": null}, "if_not_exists": ""}
+ DDL message: prefix: ddl msg3, relid: 16394, cmdtype: Drop, sz: 1396 content: {"fmt": "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D (%{table_elements:, }s) %{inherits}s %{tablespace}s %{on_commit}s %{partition_by}s %{access_method}s %{with_clause}s", "identity": {"objname": "foo", "schemaname": "element_test"}, "inherits": {"fmt": "INHERITS (%{parents:, }D)", "parents": null, "present": false}, "on_commit": {"fmt": "ON COMMIT %{on_commit_value}s", "present": false, "on_commit_value": null}, "tablespace": {"fmt": "TABLESPACE %{tablespace}I", "present": false, "tablespace": null}, "persistence": "", "with_clause": {"fmt": "WITH", "present": false}, "partition_by": {"fmt": "PARTITION BY %{definition}s", "present": false, "definition": null}, "access_method": {"fmt": "USING %{access_method}I", "present": false, "access_method": null}, "if_not_exists": "", "table_elements": [{"fmt": "%{name}I %{coltype}T STORAGE %{colstorage}s %{compression}s %{collation}s %{not_null}s %{default}s %{generated_column}s", "name": "id", "type": "column", "coltype": {"typmod": "", "typarray": false, "typename": "int4", "schemaname": "pg_catalog"}, "default": {"fmt": "DEFAULT", "present": false}, "not_null": "", "collation": {"fmt": "COLLATE", "present": false}, "colstorage": "plain", "compression": {"fmt": "COMPRESSION %{compression_method}I", "present": false, "compression_method": null}, "generated_column": {"fmt": "GENERATED ALWAYS AS", "present": false}}]}}
+(3 rows)
+
 SELECT pg_drop_replication_slot('regression_slot');
  pg_drop_replication_slot 
 --------------------------
diff --git a/contrib/test_decoding/sql/ddl.sql b/contrib/test_decoding/sql/ddl.sql
index 57285a828c..f5846b2681 100644
--- a/contrib/test_decoding/sql/ddl.sql
+++ b/contrib/test_decoding/sql/ddl.sql
@@ -431,6 +431,11 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
 -- done, free logical replication slot
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
 
+SELECT 'ddl msg1' FROM pg_logical_emit_ddl_message('ddl msg1', 16394, 1, 'msg1');
+SELECT 'ddl msg2' FROM pg_logical_emit_ddl_message('ddl msg2', 16394, 1, '{"fmt": "CREATE SCHEMA %{if_not_exists}s %{name}I %{authorization}s", "name": "foo", "authorization": {"fmt": "AUTHORIZATION %{authorization_role}I", "present": false, "authorization_role": null}, "if_not_exists": ""}');
+SELECT 'ddl msg3' FROM pg_logical_emit_ddl_message('ddl msg3', 16394, 1, '{"fmt": "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D (%{table_elements:, }s) %{inherits}s %{tablespace}s %{on_commit}s %{partition_by}s %{access_method}s %{with_clause}s", "identity": {"objname": "foo", "schemaname": "element_test"}, "inherits": {"fmt": "INHERITS (%{parents:, }D)", "parents": null, "present": false}, "on_commit": {"fmt": "ON COMMIT %{on_commit_value}s", "present": false, "on_commit_value": null}, "tablespace": {"fmt": "TABLESPACE %{tablespace}I", "present": false, "tablespace": null}, "persistence": "", "with_clause": {"fmt": "WITH", "present": false}, "partition_by": {"fmt": "PARTITION BY %{definition}s", "present": false, "definition": null}, "access_method": {"fmt": "USING %{access_method}I", "present": false, "access_method": null}, "if_not_exists": "", "table_elements": [{"fmt": "%{name}I %{coltype}T STORAGE %{colstorage}s %{compression}s %{collation}s %{not_null}s %{default}s %{generated_column}s", "name": "id", "type": "column", "coltype": {"typmod": "", "typarray": false, "typename": "int4", "schemaname": "pg_catalog"}, "default": {"fmt": "DEFAULT", "present": false}, "not_null": "", "collation": {"fmt": "COLLATE", "present": false}, "colstorage": "plain", "compression": {"fmt": "COMPRESSION %{compression_method}I", "present": false, "compression_method": null}, "generated_column": {"fmt": "GENERATED ALWAYS AS", "present": false}}]}}');
+SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+
 SELECT pg_drop_replication_slot('regression_slot');
 
 /* check that the slot is gone */
diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c
index 12d1d0505d..9286f97258 100644
--- a/contrib/test_decoding/test_decoding.c
+++ b/contrib/test_decoding/test_decoding.c
@@ -14,9 +14,11 @@
 
 #include "catalog/pg_type.h"
 
+#include "replication/ddlmessage.h"
 #include "replication/logical.h"
 #include "replication/origin.h"
 
+#include "tcop/ddldeparse.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -72,6 +74,12 @@ static void pg_decode_message(LogicalDecodingContext *ctx,
 							  ReorderBufferTXN *txn, XLogRecPtr lsn,
 							  bool transactional, const char *prefix,
 							  Size sz, const char *message);
+static void pg_decode_ddl_message(LogicalDecodingContext *ctx,
+								  ReorderBufferTXN *txn,
+								  XLogRecPtr message_lsn,
+								  const char *prefix, Oid relid,
+								  DeparsedCommandType cmdtype,
+								  Size sz, const char *message);
 static bool pg_decode_filter_prepare(LogicalDecodingContext *ctx,
 									 TransactionId xid,
 									 const char *gid);
@@ -135,6 +143,7 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
 	cb->filter_by_origin_cb = pg_decode_filter;
 	cb->shutdown_cb = pg_decode_shutdown;
 	cb->message_cb = pg_decode_message;
+	cb->ddl_cb = pg_decode_ddl_message;
 	cb->filter_prepare_cb = pg_decode_filter_prepare;
 	cb->begin_prepare_cb = pg_decode_begin_prepare_txn;
 	cb->prepare_cb = pg_decode_prepare_txn;
@@ -750,6 +759,45 @@ pg_decode_message(LogicalDecodingContext *ctx,
 	OutputPluginWrite(ctx, true);
 }
 
+static void
+pg_decode_ddl_message(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
+					  XLogRecPtr message_lsn, const char *prefix, Oid relid,
+					  DeparsedCommandType cmdtype, Size sz, const char *message)
+{
+	OutputPluginPrepareWrite(ctx, true);
+	appendStringInfo(ctx->out, "DDL message: prefix: %s, relid: %u, ",
+					 prefix, relid);
+
+	switch(cmdtype)
+	{
+	case DCT_SimpleCmd:
+		appendStringInfo(ctx->out, "cmdtype: Simple, ");
+		break;
+	case DCT_TableDropStart:
+		appendStringInfo(ctx->out, "cmdtype: Drop start, ");
+		break;
+	case DCT_TableDropEnd:
+		appendStringInfo(ctx->out, "cmdtype: Drop end, ");
+		break;
+	case DCT_TableAlter:
+		appendStringInfo(ctx->out, "cmdtype: Alter table, ");
+		break;
+	case DCT_ObjectCreate:
+		appendStringInfo(ctx->out, "cmdtype: Create, ");
+		break;
+	case DCT_ObjectDrop:
+		appendStringInfo(ctx->out, "cmdtype: Drop, ");
+		break;
+	default:
+		appendStringInfo(ctx->out, "cmdtype: Invalid, ");
+		break;
+	}
+
+	appendStringInfo(ctx->out, "sz: %zu content: ", sz);
+	appendBinaryStringInfo(ctx->out, message, sz);
+	OutputPluginWrite(ctx, true);
+}
+
 static void
 pg_decode_stream_start(LogicalDecodingContext *ctx,
 					   ReorderBufferTXN *txn)
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ed32ca0349..e9c1021724 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3863,6 +3863,15 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>evtisinternal</structfield> <type>char</type>
+      </para>
+      <para>
+       True if the event trigger is internally generated.
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>evttags</structfield> <type>text[]</type>
diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml
index 59cf92e6a9..deff492a65 100644
--- a/doc/src/sgml/logical-replication.sgml
+++ b/doc/src/sgml/logical-replication.sgml
@@ -1457,6 +1457,1099 @@ test_sub=# SELECT * FROM t1 ORDER BY id;
 
  </sect1>
 
+ <sect1 id="logical-replication-ddl">
+  <title>DDL Replication</title>
+  <para>
+    Data Definition Language (DDL) commands can be replicated using logical replication.
+    When enabled this feature automatically replicates supported DDL commands
+    that are successfully executed on a publisher to a subscriber. This is
+    especially useful if you have many schema changes over time that need replication.
+  </para>
+
+  <para>
+    For example, a CREATE TABLE command executed on the publisher gets
+    WAL-logged, and forwarded to the subscriber to replay; a subsequent "ALTER
+    SUBSCRIPTION ... REFRESH PUBLICATION" is performed on the subscriber database so any
+    following DML changes on the new table can be replicated.
+  </para>
+
+  <para>
+    DDL replication is disabled by default, it can be enabled using the ddl PUBLICATION
+    option. This option currently has one level and is only allowed to be set if the
+    PUBLICATION is FOR ALL TABLES or FOR TABLES IN SCHEMA.
+  </para>
+
+  <itemizedlist>
+    <listitem>
+      <para>
+        table: this option enables replication of Table DDL commands,
+        which include:
+        <itemizedlist>
+          <listitem>
+            <para>
+              CREATE/ALTER/DROP TABLE
+            </para>
+          </listitem>
+
+          <listitem>
+            <para>
+              CREATE TABLE AS
+            </para>
+          </listitem>
+
+        </itemizedlist>
+      </para>
+    </listitem>
+  </itemizedlist>
+
+  <sect2 id="ddl-replication-option-examples">
+    <title>Examples - Setup DDL Replication on the Publisher</title>
+
+    <para>
+      Enable TABLE DDL replication for an existing PUBLICATION:
+<programlisting>
+ALTER PUBLICATION mypub SET (ddl = 'table');
+</programlisting></para>
+
+  </sect2>
+
+  <sect2 id="ddl-replication-supported-commands">
+    <title>Supported DDL commands</title>
+
+    <para>
+      The DDL commands supported for logical replication are listed in the following
+      table. Note that global commands can be executed at any database and are currently
+      not supported for replication, global commands include ROLE statements, Database
+      statements, TableSpace statements and some of the GrantStmt/RevokeStmt if the target
+      object is a global object. Temporary and unlogged objects will not be replicated.
+      User should take care when creating these objects as these objects might be required
+      by the objects that are replicated (for example creation of tables that might
+      refer to an user-created tablespace will fail in the subscriber if the user
+      created tablespaces do not exist on the subscriber).
+    </para>
+
+    <table id="ddl-replication-by-command-tag">
+      <title>DDL Replication Support by Command Tag</title>
+      <tgroup cols="3">
+        <colspec colname="col1" colwidth="2*"/>
+        <colspec colname="col2" colwidth="1*"/>
+        <colspec colname="col3" colwidth="1*"/>
+      <thead>
+       <row>
+        <entry>Command Tag</entry>
+        <entry>For Replication</entry>
+        <entry>Notes</entry>
+       </row>
+      </thead>
+      <tbody>
+       <row>
+        <entry align="left"><literal>ALTER AGGREGATE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER CAST</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER COLLATION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER CONVERSION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER DATABASE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER DEFAULT PRIVILEGES</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER DOMAIN</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER EVENT TRIGGER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER EXTENSION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER FOREIGN DATA WRAPPER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER FOREIGN TABLE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER FUNCTION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER INDEX</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER LANGUAGE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER LARGE OBJECT</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER MATERIALIZED VIEW</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER OPERATOR</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER OPERATOR CLASS</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER OPERATOR FAMILY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER POLICY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER PROCEDURE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER PUBLICATION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER ROLE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER ROUTINE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER RULER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER SCHEMA</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER SEQUENCE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER SERVER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER STATISTICS</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER SUBSCRIPTION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER SYSTEM</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER TABLE</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER TABLESPACE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER TEXT SEARCH CONFIGURATION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER TEXT SEARCH DICTIONARY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER TEXT SEARCH PARSER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER TEXT SEARCH TEMPLATE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER TRANSFORM</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER TRIGGER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER TYPE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER USER MAPPING</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ALTER VIEW</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ANALYZE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>BEGIN</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CALL</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CHECKPOINT</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CLOSE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CLOSE CURSOR</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CLOSE CURSOR ALL</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CLUSTER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>COMMENT</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>COMMIT</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>COMMIT PREPARED</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>COPY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>COPY FROM</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE ACCESS METHOD</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE AGGREGATE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE CAST</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE COLLATION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE CONSTRAINT</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE CONVERSION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE DATABASE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE DOMAIN</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE EVENT TRIGGER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE EXTENSION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE FOREIGN DATA WRAPPER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE FOREIGN TABLE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE FUNCTION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE INDEX</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE LANGUAGE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE MATERIALIZED VIEW</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE OPERATOR</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE OPERATOR CLASS</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE OPERATOR FAMILY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE POLICY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE PROCEDURE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE PUBLICATION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE ROLE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE RULE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE SCHEMA</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE SEQUENCE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE SERVER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE STATISTICS</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE SUBSCRIPTION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TABLE</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TABLE AS</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TABLESPACE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TEXT SEARCH CONFIGURATION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TEXT SEARCH DICTIONARY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TEXT SEARCH PARSER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TEXT SEARCH TEMPLATE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TRANSFORM</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TRIGGER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE TYPE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE USER MAPPING</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>CREATE VIEW</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DEALLOCATE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DEALLOCATE ALL</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DECLARE CURSOR</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DELETE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DISCARD</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DISCARD ALL</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DISCARD PLANS</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DISCARD SEQUENCES</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DISCARD TEMP</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DO</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP ACCESS METHOD</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP AGGREGATE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP CAST</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP COLLATION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP CONSTRAINT</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP CONVERSION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP DATABASE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP DOMAIN</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP EVENT TRIGGER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP EXTENSION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP FOREIGN DATA WRAPPER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP FOREIGN TABLE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP FUNCTION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP INDEX</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP LANGUAGE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP MATERIALIZED VIEW</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP OPERATOR</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP OPERATOR CLASS</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP OPERATOR FAMILY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP OWNED</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP POLICY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP PROCEDURE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP PUBLICATION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP ROLE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP ROUTINE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP RULE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP SCHEMA</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP SEQUENCE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP SERVER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP STATISTICS</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP SUBSCRIPTION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP TABLE</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP TABLESPACE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP TEXT SEARCH CONFIGURATION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP TEXT SEARCH DICTIONARY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP TEXT SEARCH PARSER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP TEXT SEARCH TEMPLATE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP TRANSFORM</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP TRIGGER</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP TYPE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP USER MAPPING</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>DROP VIEW</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>EXECUTE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>EXPLAIN</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>FETCH</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>GRANT</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>GRANT ROLE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>IMPORT FOREIGN SCHEMA</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>INSERT</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>LISTEN</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>LOAD</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>LOCK TABLE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>MERGE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>MOVE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>NOTIFY</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>PREPARE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>PREPARE TRANSACTION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>REASSIGN OWNED</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>REFRESH MATERIALIZED VIEW</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>REINDEX</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>RELEASE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>RESET</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>REVOKE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>REVOKE ROLE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ROLLBACK</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>ROLLBACK PREPARED</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>SAVEPOINT</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>SECURITY LABEL</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>SET</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>SET CONSTRAINTS</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>SHOW</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>START TRANSACTION</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>UNLISTEN</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>UPDATE</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+       <row>
+        <entry align="left"><literal>VACUUM</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
+    </tbody>
+    </tgroup>
+    </table>
+  </sect2>
+
+  <sect2 id="ddl-replication-ddl-deparser">
+    <title>DDL Deparser</title>
+    <para>
+      The DDL deparser utility is invoked during the replication of DDLs. The DDL
+      deparser is capable of converting a DDL command into formatted JSON blob, with
+      the necessary information to reconstruct the DDL commands at the destination. The
+      benefits of using the deparser output compared to the original command string
+      include:
+      <itemizedlist>
+        <listitem>
+          <para>
+            Every database object in the deparsed output is schema-qualified, so that
+            there are no ambiguities even in the face of search_path changes.
+          </para>
+        </listitem>
+
+        <listitem>
+          <para>
+            The structured JSON and the formatted output makes it possible for
+            machine editing. This can be useful if the subscriber is on a different
+            PG version and has certain DDL syntax differences which need to be
+            resolved before apply.
+          </para>
+        </listitem>
+
+      </itemizedlist>
+    </para>
+
+    <para>
+      The DDL deparser exposes two SQL functions:
+      <itemizedlist>
+        <listitem>
+          <para>
+            ddl_deparse_to_json: given a CollectedCommand from event trigger,
+            returns a JSON representation of it. The command is expanded fully
+            so that there are no ambiguities even in the face of search_path
+            changes.
+          </para>
+        </listitem>
+      </itemizedlist>
+
+      <itemizedlist>
+        <listitem>
+          <para>
+            ddl_deparse_expand_command: Expand JSON format DDL generated by
+            ddl_deparse_to_json to a plain DDL command.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+  </sect2>
+
+  <sect2 id="ddl-replication-ddl-restrictions">
+    <title>DDL Replication Restrictions</title>
+    <para>
+      DDL replication currently has the following restrictions.
+      <itemizedlist>
+        <listitem>
+          <para>
+            <command>ALTER TABLE</command> command which uses volatile functions
+            is not allowed.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            In <literal>ADD COLUMN ... DEFAULT</literal> clause and
+            <literal>ALTER COLUMN TYPE</literal> clause of <command>ALTER
+            TABLE</command> command, the functions and operators used in
+            expression must be immutable.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+
+    <para>
+      The latter case can be worked around.
+      <itemizedlist>
+        <listitem>
+          <para>
+            To add a column with a non-immutable default value, first add a
+            column without a default value, then set a default value for the new
+            column, and update the value of the new column for existing rows.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            To change the column type, first add a new column of the desired
+            type, then update the new column value with the old column value,
+            and finnally drop the old column and rename the new column to the
+            old column.
+          </para>
+        </listitem>
+      </itemizedlist>
+    </para>
+  </sect2>
+
+ </sect1>
+
  <sect1 id="logical-replication-conflicts">
   <title>Conflicts</title>
 
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index cd95eec37f..fd67f044e4 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -18,6 +18,7 @@ OBJS = \
 	gistdesc.o \
 	hashdesc.o \
 	heapdesc.o \
+	logicalddlmsgdesc.o \
 	logicalmsgdesc.o \
 	mxactdesc.o \
 	nbtdesc.o \
diff --git a/src/backend/access/rmgrdesc/logicalddlmsgdesc.c b/src/backend/access/rmgrdesc/logicalddlmsgdesc.c
new file mode 100644
index 0000000000..aeedf5e432
--- /dev/null
+++ b/src/backend/access/rmgrdesc/logicalddlmsgdesc.c
@@ -0,0 +1,52 @@
+/*-------------------------------------------------------------------------
+ *
+ * logicalddlmsgdesc.c
+ *	  rmgr descriptor routines for replication/logical/ddlmessage.c
+ *
+ * Portions Copyright (c) 2015-2023, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/logicalddlmsgdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "replication/ddlmessage.h"
+
+void
+logicalddlmsg_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_LOGICAL_DDL_MESSAGE)
+	{
+		xl_logical_ddl_message *xlrec = (xl_logical_ddl_message *) rec;
+		char	   *prefix = xlrec->message;
+		char	   *message = xlrec->message + xlrec->prefix_size;
+		char	   *sep = "";
+
+		Assert(prefix[xlrec->prefix_size] != '\0');
+
+		appendStringInfo(buf, "prefix \"%s\"; payload (%zu bytes): ",
+						 prefix, xlrec->message_size);
+		appendStringInfo(buf, "relid %u cmdtype %u", xlrec->relid, xlrec->cmdtype);
+		/* Write message payload as a series of hex bytes */
+		for (int cnt = 0; cnt < xlrec->message_size; cnt++)
+		{
+			appendStringInfo(buf, "%s%02X", sep, (unsigned char) message[cnt]);
+			sep = " ";
+		}
+	}
+}
+
+const char *
+logicalddlmsg_identify(uint8 info)
+{
+	if ((info & ~XLR_INFO_MASK) == XLOG_LOGICAL_DDL_MESSAGE)
+		return "DDL";
+
+	return NULL;
+}
diff --git a/src/backend/access/rmgrdesc/meson.build b/src/backend/access/rmgrdesc/meson.build
index f76e87e2d7..614fd7c1ac 100644
--- a/src/backend/access/rmgrdesc/meson.build
+++ b/src/backend/access/rmgrdesc/meson.build
@@ -11,6 +11,7 @@ rmgr_desc_sources = files(
   'gistdesc.c',
   'hashdesc.c',
   'heapdesc.c',
+  'logicalddlmsgdesc.c',
   'logicalmsgdesc.c',
   'mxactdesc.c',
   'nbtdesc.c',
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 7d67eda5f7..678e81ae01 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -27,6 +27,7 @@
 #include "fmgr.h"
 #include "funcapi.h"
 #include "miscadmin.h"
+#include "replication/ddlmessage.h"
 #include "replication/decode.h"
 #include "replication/message.h"
 #include "replication/origin.h"
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index c488b6370b..542cef7cb0 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -1027,6 +1027,7 @@ GetPublication(Oid pubid)
 	pub->pubactions.pubinsert = pubform->pubinsert;
 	pub->pubactions.pubupdate = pubform->pubupdate;
 	pub->pubactions.pubdelete = pubform->pubdelete;
+	pub->pubactions.pubddl_table = pubform->pubddl_table;
 	pub->pubactions.pubtruncate = pubform->pubtruncate;
 	pub->pubviaroot = pubform->pubviaroot;
 
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index e95dc31bde..23435c9915 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -308,6 +308,23 @@ AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
 		/* Wake up related replication workers to handle this change quickly */
 		LogicalRepWorkersWakeupAtCommit(objectId);
 	}
+	else if (classId == EventTriggerRelationId)
+	{
+		Form_pg_event_trigger evtForm = (Form_pg_event_trigger) GETSTRUCT(oldtup);
+
+		if (SearchSysCacheExists1(EVENTTRIGGERNAME, CStringGetDatum(new_name)))
+			report_name_conflict(classId, new_name);
+
+		/*
+		 * Event triggers created internally are not allowed to be altered by
+		 * user.
+		 */
+		if (evtForm->evtisinternal)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("permission denied: \"%s\" is a system event trigger",
+							NameStr(evtForm->evtname))));
+	}
 	else if (nameCacheId >= 0)
 	{
 		if (OidIsValid(namespaceId))
diff --git a/src/backend/commands/ddldeparse.c b/src/backend/commands/ddldeparse.c
index 6799c7fdaf..e304f25f98 100644
--- a/src/backend/commands/ddldeparse.c
+++ b/src/backend/commands/ddldeparse.c
@@ -135,10 +135,10 @@ static ObjElem *new_object_object(ObjTree *value);
 static ObjTree *new_objtree_VA(char *fmt, int numobjs, ...);
 static JsonbValue *objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state);
 static char *RelationGetColumnDefault(Relation rel, AttrNumber attno,
-									  List *dpcontext);
+									  List *dpcontext, Node **expr);
 
 static ObjTree *deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
-								  ColumnDef *coldef, bool is_alter);
+								  ColumnDef *coldef, bool is_alter, Node **expr);
 static ObjTree *deparse_ColumnIdentity(Oid seqrelid, char identity, bool alter_table);
 static ObjTree *deparse_ColumnSetOptions(AlterTableCmd *subcmd);
 
@@ -158,6 +158,27 @@ static inline ObjElem *deparse_Seq_As(Form_pg_sequence seqdata);
 static List *deparse_InhRelations(Oid objectId);
 static List *deparse_TableElements(Relation relation, List *tableElements, List *dpcontext,
 								   bool typed, bool composite);
+static void mark_function_volatile(ddl_deparse_context *context, Node *expr);
+
+/*
+ * Mark the func_volatile flag for an expression in the command.
+ */
+static void
+mark_function_volatile(ddl_deparse_context *context, Node *expr)
+{
+	if (context->func_volatile == PROVOLATILE_VOLATILE)
+		return;
+
+	if (contain_volatile_functions(expr))
+	{
+		context->func_volatile = PROVOLATILE_VOLATILE;
+		return;
+	}
+
+	if (context->func_volatile == PROVOLATILE_IMMUTABLE &&
+		contain_mutable_functions(expr))
+		context->func_volatile = PROVOLATILE_STABLE;
+}
 
 /*
  * Append present as false to a tree.
@@ -850,7 +871,8 @@ obtainConstraints(List *elements, Oid relationId)
  * passed attribute has a default value.
  */
 static char *
-RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext)
+RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext,
+						 Node **expr)
 {
 	Node	   *defval;
 	char	   *defstr;
@@ -860,6 +882,10 @@ RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext)
 
 	defstr = deparse_expression(defval, dpcontext, false, false);
 
+	/* Collect the expression for later replication safety checks */
+	if (expr)
+		*expr = defval;
+
 	return defstr;
 }
 
@@ -903,7 +929,7 @@ RelationGetPartitionBound(Oid relid)
  */
 static ObjTree *
 deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
-				  ColumnDef *coldef, bool is_alter)
+				  ColumnDef *coldef, bool is_alter, Node **expr)
 {
 	ObjTree    *ret;
 	ObjTree    *tmp_obj;
@@ -1006,7 +1032,7 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 			char	   *defstr;
 
 			defstr = RelationGetColumnDefault(relation, attrForm->attnum,
-											  dpcontext);
+											  dpcontext, expr);
 
 			append_string_object(tmp_obj, "%{default}s", "default", defstr);
 		}
@@ -1047,7 +1073,7 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 			char	   *defstr;
 
 			defstr = RelationGetColumnDefault(relation, attrForm->attnum,
-											  dpcontext);
+											  dpcontext, expr);
 			append_string_object(tmp_obj, "(%{generation_expr}s) STORED",
 								 "generation_expr", defstr);
 		}
@@ -1125,7 +1151,7 @@ deparse_ColumnDef_typed(Relation relation, List *dpcontext, ColumnDef *coldef)
 		char	   *defstr;
 
 		defstr = RelationGetColumnDefault(relation, attrForm->attnum,
-										  dpcontext);
+										  dpcontext, NULL);
 
 		append_string_object(tmp_obj, "%{default}s", "default", defstr);
 	}
@@ -1669,7 +1695,7 @@ deparse_TableElements(Relation relation, List *tableElements, List *dpcontext,
 												(ColumnDef *) elt) :
 						deparse_ColumnDef(relation, dpcontext,
 										  composite, (ColumnDef *) elt,
-										  false);
+										  false, NULL);
 					if (tree != NULL)
 						elements = lappend(elements, new_object_object(tree));
 				}
@@ -1970,7 +1996,7 @@ deparse_CreateTableAsStmt(CollectedCommand *cmd)
  * ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s
  */
 static ObjTree *
-deparse_AlterRelation(CollectedCommand *cmd)
+deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 {
 	ObjTree    *ret;
 	ObjTree    *tmp_obj;
@@ -1980,6 +2006,7 @@ deparse_AlterRelation(CollectedCommand *cmd)
 	List	   *subcmds = NIL;
 	ListCell   *cell;
 	const char *reltype;
+	Node	   *expr = NULL;
 	Oid			relId = cmd->d.alterTable.objectId;
 	AlterTableStmt *stmt = NULL;
 
@@ -2052,7 +2079,10 @@ deparse_AlterRelation(CollectedCommand *cmd)
 				/* XXX need to set the "recurse" bit somewhere? */
 				Assert(IsA(subcmd->def, ColumnDef));
 				tree = deparse_ColumnDef(rel, dpcontext, false,
-										 (ColumnDef *) subcmd->def, true);
+										 (ColumnDef *) subcmd->def, true, &expr);
+
+				mark_function_volatile(context, expr);
+
 				tmp_obj = new_objtree_VA("ADD %{objtype}s %{if_not_exists}s %{definition}s", 4,
 										"objtype", ObjTypeString, "COLUMN",
 										"type", ObjTypeString, "add column",
@@ -2162,7 +2192,8 @@ deparse_AlterRelation(CollectedCommand *cmd)
 					attno = ((Form_pg_attribute) GETSTRUCT(attrtup))->attnum;
 					append_string_object(tmp_obj, "%{definition}s", "definition",
 										 RelationGetColumnDefault(rel, attno,
-																  dpcontext_rel));
+																  dpcontext_rel,
+																  NULL));
 					ReleaseSysCache(attrtup);
 				}
 
@@ -2297,12 +2328,40 @@ deparse_AlterRelation(CollectedCommand *cmd)
 				{
 					/* XXX need to set the "recurse" bit somewhere? */
 					Oid			constrOid = sub->address.objectId;
+					bool		isnull;
+					HeapTuple	tup;
+					Datum		val;
+					Constraint *constr;
 
 					/* Skip adding constraint for inherits table sub command */
 					if (!constrOid)
 						continue;
 
 					Assert(IsA(subcmd->def, Constraint));
+					constr = castNode(Constraint, subcmd->def);
+
+					if (!constr->skip_validation)
+					{
+						tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
+
+						if (HeapTupleIsValid(tup))
+						{
+							char	   *conbin;
+
+							/* Fetch constraint expression in parsetree form */
+							val = SysCacheGetAttr(CONSTROID, tup,
+												  Anum_pg_constraint_conbin, &isnull);
+
+							if (!isnull)
+							{
+								conbin = TextDatumGetCString(val);
+								expr = stringToNode(conbin);
+								mark_function_volatile(context, expr);
+							}
+
+							ReleaseSysCache(tup);
+						}
+					}
 
 					tmp_obj = new_objtree_VA("ADD CONSTRAINT %{name}I %{definition}s", 3,
 											"type", ObjTypeString, "add constraint",
@@ -2392,8 +2451,11 @@ deparse_AlterRelation(CollectedCommand *cmd)
 					 */
 					tmp_obj2 = new_objtree("USING");
 					if (def->raw_default)
+					{
 						append_string_object(tmp_obj2, "%{expression}s",
 											 "expression", sub->usingexpr);
+						mark_function_volatile(context, def->cooked_default);
+					}
 					else
 						append_not_present(tmp_obj2, "%{expression}s");
 
@@ -3113,7 +3175,7 @@ deparse_simple_command(CollectedCommand *cmd)
  * Workhorse to deparse a CollectedCommand.
  */
 char *
-deparse_utility_command(CollectedCommand *cmd, bool verbose_mode)
+deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context)
 {
 	OverrideSearchPath *overridePath;
 	MemoryContext oldcxt;
@@ -3149,7 +3211,7 @@ deparse_utility_command(CollectedCommand *cmd, bool verbose_mode)
 	overridePath->addTemp = true;
 	PushOverrideSearchPath(overridePath);
 
-	verbose = verbose_mode;
+	verbose = context->verbose_mode;
 
 	switch (cmd->type)
 	{
@@ -3157,7 +3219,7 @@ deparse_utility_command(CollectedCommand *cmd, bool verbose_mode)
 			tree = deparse_simple_command(cmd);
 			break;
 		case SCT_AlterTable:
-			tree = deparse_AlterRelation(cmd);
+			tree = deparse_AlterRelation(cmd, context);
 			break;
 		case SCT_CreateTableAs:
 			tree = deparse_CreateTableAsStmt(cmd);
@@ -3197,8 +3259,12 @@ ddl_deparse_to_json(PG_FUNCTION_ARGS)
 {
 	CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
 	char	   *command;
+	ddl_deparse_context context;
+
+	context.verbose_mode = true;
+	context.func_volatile = PROVOLATILE_IMMUTABLE;
 
-	command = deparse_utility_command(cmd, true);
+	command = deparse_utility_command(cmd, &context);
 
 	if (command)
 		PG_RETURN_TEXT_P(cstring_to_text(command));
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 8561c6fad0..781f3c1d07 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -58,7 +58,7 @@ static void AlterEventTriggerOwner_internal(Relation rel,
 static void error_duplicate_filter_variable(const char *defname);
 static Datum filter_list_to_array(List *filterlist);
 static Oid	insert_event_trigger_tuple(const char *trigname, const char *eventname,
-									   Oid evtOwner, Oid funcoid, List *taglist);
+									   Oid evtOwner, Oid funcoid, List *taglist, bool isinternal);
 static void validate_ddl_tags(const char *filtervar, List *taglist);
 static void validate_table_rewrite_tags(const char *filtervar, List *taglist);
 static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata);
@@ -69,7 +69,7 @@ static const char *stringify_adefprivs_objtype(ObjectType objtype);
  * Create an event trigger.
  */
 Oid
-CreateEventTrigger(CreateEventTrigStmt *stmt)
+CreateEventTrigger(CreateEventTrigStmt *stmt, bool isinternal)
 {
 	HeapTuple	tuple;
 	Oid			funcoid;
@@ -151,7 +151,7 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 
 	/* Insert catalog entries. */
 	return insert_event_trigger_tuple(stmt->trigname, stmt->eventname,
-									  evtowner, funcoid, tags);
+									  evtowner, funcoid, tags, isinternal);
 }
 
 /*
@@ -220,7 +220,7 @@ error_duplicate_filter_variable(const char *defname)
  */
 static Oid
 insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtOwner,
-						   Oid funcoid, List *taglist)
+						   Oid funcoid, List *taglist, bool isinternal)
 {
 	Relation	tgrel;
 	Oid			trigoid;
@@ -248,6 +248,7 @@ insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtO
 	values[Anum_pg_event_trigger_evtfoid - 1] = ObjectIdGetDatum(funcoid);
 	values[Anum_pg_event_trigger_evtenabled - 1] =
 		CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
+	values[Anum_pg_event_trigger_evtisinternal - 1] = BoolGetDatum(isinternal);
 	if (taglist == NIL)
 		nulls[Anum_pg_event_trigger_evttags - 1] = true;
 	else
@@ -526,6 +527,7 @@ EventTriggerCommonSetup(Node *parsetree,
 	List	   *cachelist;
 	ListCell   *lc;
 	List	   *runlist = NIL;
+	int			pub_deparse_func_cnt = 0;
 
 	/*
 	 * We want the list of command tags for which this procedure is actually
@@ -576,6 +578,12 @@ EventTriggerCommonSetup(Node *parsetree,
 	 * once we do anything at all that touches the catalogs, an invalidation
 	 * might leave cachelist pointing at garbage, so we must do this before we
 	 * can do much else.
+	 *
+	 * Special handling for event triggers created as part of publications.
+	 * If there are multiple publications which publish ddls, only one set of the
+	 * event trigger functions need to be invoked. The ddl deparse event triggers
+	 * write to WAL, so no need to duplicate it as all walsenders will read the same
+	 * WAL.
 	 */
 	foreach(lc, cachelist)
 	{
@@ -583,8 +591,25 @@ EventTriggerCommonSetup(Node *parsetree,
 
 		if (filter_event_trigger(tag, item))
 		{
-			/* We must plan to fire this trigger. */
-			runlist = lappend_oid(runlist, item->fnoid);
+			static const char *trigger_func_prefix = "publication_deparse_%s";
+			char		trigger_func_name[NAMEDATALEN];
+			Oid			pub_funcoid;
+			List 		*pubfuncname;
+
+			/* Get function oid of the publication's ddl deparse event trigger */
+			snprintf(trigger_func_name, sizeof(trigger_func_name), trigger_func_prefix,
+					 eventstr);
+			pubfuncname = SystemFuncName(trigger_func_name);
+			pub_funcoid = LookupFuncName(pubfuncname, 0, NULL, true);
+
+			if (item->fnoid != pub_funcoid)
+				runlist = lappend_oid(runlist, item->fnoid);
+			else
+			{
+				/* Only the first ddl deparse event trigger needs to be invoked */
+				if (pub_deparse_func_cnt++ == 0)
+					runlist = lappend_oid(runlist, item->fnoid);
+			}
 		}
 	}
 
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index f4ba572697..5f255435fa 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -38,10 +38,12 @@
 #include "commands/publicationcmds.h"
 #include "funcapi.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_collate.h"
 #include "parser/parse_relation.h"
+#include "parser/parser.h"
 #include "storage/lmgr.h"
 #include "utils/acl.h"
 #include "utils/array.h"
@@ -85,18 +87,21 @@ parse_publication_options(ParseState *pstate,
 						  bool *publish_given,
 						  PublicationActions *pubactions,
 						  bool *publish_via_partition_root_given,
-						  bool *publish_via_partition_root)
+						  bool *publish_via_partition_root,
+						  bool *ddl_type_given)
 {
 	ListCell   *lc;
 
 	*publish_given = false;
 	*publish_via_partition_root_given = false;
+	*ddl_type_given = false;
 
 	/* defaults */
 	pubactions->pubinsert = true;
 	pubactions->pubupdate = true;
 	pubactions->pubdelete = true;
 	pubactions->pubtruncate = true;
+	pubactions->pubddl_table = false;
 	*publish_via_partition_root = false;
 
 	/* Parse options */
@@ -158,6 +163,42 @@ parse_publication_options(ParseState *pstate,
 			*publish_via_partition_root_given = true;
 			*publish_via_partition_root = defGetBoolean(defel);
 		}
+		else if (strcmp(defel->defname, "ddl") == 0)
+		{
+			char	   *ddl_types;
+			List	   *ddl_type_list;
+			ListCell   *lc2;
+
+			if (*ddl_type_given)
+				errorConflictingDefElem(defel, pstate);
+
+			/*
+			 * If ddl option was given only the explicitly listed ddl types
+			 * should be published.
+			 */
+			pubactions->pubddl_table = false;
+
+			*ddl_type_given = true;
+			ddl_types = defGetString(defel);
+
+			if (!SplitIdentifierString(ddl_types, ',', &ddl_type_list))
+				ereport(ERROR,
+						errcode(ERRCODE_SYNTAX_ERROR),
+						errmsg("invalid list syntax for \"ddl\" option"));
+
+			/* Process the option list. */
+			foreach(lc2, ddl_type_list)
+			{
+				char	   *ddl_opt = (char *) lfirst(lc2);
+
+				if (strcmp(ddl_opt, "table") == 0)
+					pubactions->pubddl_table = true;
+				else
+					ereport(ERROR,
+							errcode(ERRCODE_SYNTAX_ERROR),
+							errmsg("unrecognized \"ddl\" value: \"%s\"", ddl_opt));
+			}
+		}
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
@@ -728,6 +769,140 @@ CheckPubRelationColumnList(char *pubname, List *tables,
 	}
 }
 
+/*
+ * Helper function to create an event trigger for DDL replication.
+ */
+static void
+CreateDDLReplicaEventTrigger(char *eventname, List *commands, Oid puboid)
+{
+	List	   *tags = NIL;
+	ListCell   *lc;
+	Oid			trigger_id;
+	ObjectAddress referenced;
+	ObjectAddress pubaddress;
+	CreateEventTrigStmt *ddl_trigger;
+	char		trigger_name[NAMEDATALEN];
+	char		trigger_func_name[NAMEDATALEN];
+	static const char *trigger_func_format = "publication_deparse_%s";
+
+	ddl_trigger = makeNode(CreateEventTrigStmt);
+
+	snprintf(trigger_name, sizeof(trigger_name), PUB_EVENT_TRIG_FORMAT,
+			 eventname, puboid);
+	snprintf(trigger_func_name, sizeof(trigger_func_name), trigger_func_format,
+			 eventname);
+
+	ddl_trigger->trigname = pstrdup(trigger_name);
+	ddl_trigger->eventname = eventname;
+	ddl_trigger->funcname = SystemFuncName(trigger_func_name);
+
+	foreach(lc, commands)
+	{
+		CommandTag cmdtag = lfirst_int(lc);
+		String	   *tag = makeString(pstrdup(GetCommandTagName(cmdtag)));
+
+		tags = lappend(tags, tag);
+	}
+
+	ddl_trigger->whenclause = list_make1(makeDefElem("tag", (Node *) tags, -1));
+
+	trigger_id = CreateEventTrigger(ddl_trigger, true);
+
+	ObjectAddressSet(pubaddress, PublicationRelationId, puboid);
+
+	/*
+	 * Register the event trigger as internally dependent on the publication.
+	 */
+	ObjectAddressSet(referenced, EventTriggerRelationId, trigger_id);
+	recordDependencyOn(&referenced, &pubaddress, DEPENDENCY_INTERNAL);
+}
+
+/*
+ * If DDL replication is enabled, create event triggers to capture and log any
+ * relevant events.
+ */
+static void
+CreateDDLReplicaEventTriggers(PublicationActions pubactions, Oid puboid)
+{
+	List	   *init_commands = NIL;
+	List	   *start_commands = NIL;
+	List	   *rewrite_commands = NIL;
+	List	   *end_commands = NIL;
+
+	if (!pubactions.pubddl_table)
+		return;
+
+	init_commands = lappend_int(init_commands, CMDTAG_CREATE_TABLE_AS);
+	init_commands = lappend_int(init_commands, CMDTAG_SELECT_INTO);
+
+	start_commands = lappend_int(start_commands, CMDTAG_DROP_TABLE);
+	rewrite_commands = lappend_int(rewrite_commands, CMDTAG_ALTER_TABLE);
+
+	end_commands = lappend_int(end_commands, CMDTAG_CREATE_TABLE);
+	end_commands = lappend_int(end_commands, CMDTAG_ALTER_TABLE);
+	end_commands = lappend_int(end_commands, CMDTAG_DROP_TABLE);
+
+	/* Create the table_init_write event trigger */
+	if (init_commands != NIL)
+		CreateDDLReplicaEventTrigger(PUB_TRIG_TBL_INIT_WRITE, init_commands, puboid);
+
+	/* Create the ddl_command_start event trigger */
+	if (start_commands != NIL)
+		CreateDDLReplicaEventTrigger(PUB_TRIG_DDL_CMD_START, start_commands, puboid);
+
+	/* Create the table_rewrite event trigger */
+	if (rewrite_commands != NIL)
+		CreateDDLReplicaEventTrigger(PUB_TRIG_TBL_REWRITE, rewrite_commands, puboid);
+
+	/* Create the ddl_command_end event trigger */
+	if (end_commands != NIL)
+		CreateDDLReplicaEventTrigger(PUB_TRIG_DDL_CMD_END, end_commands, puboid);
+}
+
+/*
+ * Helper function to drop an event trigger for DDL replication.
+ */
+static void
+DropDDLReplicaEventTrigger(char *eventname, Oid puboid)
+{
+	char			trigger_name[NAMEDATALEN];
+	Oid				evtoid;
+	ObjectAddress	obj;
+
+	snprintf(trigger_name, sizeof(trigger_name), PUB_EVENT_TRIG_FORMAT,
+			 eventname, puboid);
+
+	evtoid = get_event_trigger_oid(trigger_name, true);
+	if (!OidIsValid(evtoid))
+		return;
+
+	deleteDependencyRecordsForClass(EventTriggerRelationId, evtoid,
+									PublicationRelationId,
+									DEPENDENCY_INTERNAL);
+
+	/*
+	 * Ensure that the dependency removal is visible, so that we can drop the
+	 * event trigger.
+	 */
+	CommandCounterIncrement();
+
+	ObjectAddressSet(obj, EventTriggerRelationId, evtoid);
+	performDeletion(&obj, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+}
+
+/*
+ * Drop all the event triggers which are used for DDL replication.
+ */
+static void
+DropDDLReplicaEventTriggers(Oid puboid)
+{
+	DropDDLReplicaEventTrigger(PUB_TRIG_DDL_CMD_START, puboid);
+	DropDDLReplicaEventTrigger(PUB_TRIG_DDL_CMD_END, puboid);
+	DropDDLReplicaEventTrigger(PUB_TRIG_TBL_REWRITE, puboid);
+	DropDDLReplicaEventTrigger(PUB_TRIG_TBL_INIT_WRITE, puboid);
+}
+
+
 /*
  * Create new publication.
  */
@@ -741,6 +916,7 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 	Datum		values[Natts_pg_publication];
 	HeapTuple	tup;
 	bool		publish_given;
+	bool		ddl_type_given;
 	PublicationActions pubactions;
 	bool		publish_via_partition_root_given;
 	bool		publish_via_partition_root;
@@ -783,7 +959,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 							  stmt->options,
 							  &publish_given, &pubactions,
 							  &publish_via_partition_root_given,
-							  &publish_via_partition_root);
+							  &publish_via_partition_root,
+							  &ddl_type_given);
 
 	puboid = GetNewOidWithIndex(rel, PublicationObjectIndexId,
 								Anum_pg_publication_oid);
@@ -798,6 +975,8 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 		BoolGetDatum(pubactions.pubdelete);
 	values[Anum_pg_publication_pubtruncate - 1] =
 		BoolGetDatum(pubactions.pubtruncate);
+	values[Anum_pg_publication_pubddl_table - 1] =
+	BoolGetDatum(pubactions.pubddl_table);
 	values[Anum_pg_publication_pubviaroot - 1] =
 		BoolGetDatum(publish_via_partition_root);
 
@@ -835,6 +1014,11 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 		{
 			List	   *rels;
 
+			if (pubactions.pubddl_table)
+				ereport(ERROR,
+						errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("DDL replication is only supported in FOR ALL TABLES or FOR TABLES IN SCHEMA publications"));
+
 			rels = OpenTableList(relations);
 			TransformPubWhereClauses(rels, pstate->p_sourcetext,
 									 publish_via_partition_root);
@@ -858,6 +1042,11 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
 		}
 	}
 
+	/*
+	 * Create event triggers to allow logging of DDL statements.
+	 */
+	CreateDDLReplicaEventTriggers(pubactions, puboid);
+
 	table_close(rel, RowExclusiveLock);
 
 	InvokeObjectPostCreateHook(PublicationRelationId, puboid, 0);
@@ -882,6 +1071,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
 	bool		replaces[Natts_pg_publication];
 	Datum		values[Natts_pg_publication];
 	bool		publish_given;
+	bool		ddl_type_given;
 	PublicationActions pubactions;
 	bool		publish_via_partition_root_given;
 	bool		publish_via_partition_root;
@@ -890,13 +1080,14 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
 	List	   *root_relids = NIL;
 	ListCell   *lc;
 
+	pubform = (Form_pg_publication) GETSTRUCT(tup);
+
 	parse_publication_options(pstate,
 							  stmt->options,
 							  &publish_given, &pubactions,
 							  &publish_via_partition_root_given,
-							  &publish_via_partition_root);
-
-	pubform = (Form_pg_publication) GETSTRUCT(tup);
+							  &publish_via_partition_root,
+							  &ddl_type_given);
 
 	/*
 	 * If the publication doesn't publish changes via the root partitioned
@@ -978,6 +1169,18 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
 		}
 	}
 
+	if (ddl_type_given && pubactions.pubddl_table)
+	{
+		if (root_relids == NIL)
+			root_relids = GetPublicationRelations(pubform->oid,
+												  PUBLICATION_PART_ROOT);
+
+		if (root_relids)
+			ereport(ERROR,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("DDL replication is only supported in FOR ALL TABLES or FOR TABLES IN SCHEMA publications"));
+	}
+
 	/* Everything ok, form a new tuple. */
 	memset(values, 0, sizeof(values));
 	memset(nulls, false, sizeof(nulls));
@@ -998,6 +1201,19 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
 		replaces[Anum_pg_publication_pubtruncate - 1] = true;
 	}
 
+	if (ddl_type_given)
+	{
+		/* Recreate the event triggers if the ddl option is changed. */
+		if (pubform->pubddl_table != pubactions.pubddl_table)
+		{
+			DropDDLReplicaEventTriggers(pubform->oid);
+			CreateDDLReplicaEventTriggers(pubactions, pubform->oid);
+		}
+
+		values[Anum_pg_publication_pubddl_table - 1] = BoolGetDatum(pubactions.pubddl_table);
+		replaces[Anum_pg_publication_pubddl_table - 1] = true;
+	}
+
 	if (publish_via_partition_root_given)
 	{
 		values[Anum_pg_publication_pubviaroot - 1] = BoolGetDatum(publish_via_partition_root);
@@ -1103,6 +1319,12 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
 
 	if (stmt->action == AP_AddObjects)
 	{
+		if (pubform->pubddl_table)
+			ereport(ERROR,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("cannot add table to publication \"%s\" if DDL replication is enabled",
+						   stmt->pubname));
+
 		TransformPubWhereClauses(rels, queryString, pubform->pubviaroot);
 
 		publish_schema |= is_schema_publication(pubid);
@@ -1121,6 +1343,12 @@ AlterPublicationTables(AlterPublicationStmt *stmt, HeapTuple tup,
 		List	   *delrels = NIL;
 		ListCell   *oldlc;
 
+		if (pubform->pubddl_table)
+			ereport(ERROR,
+					errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("cannot add table to publication \"%s\" if DDL replication is enabled",
+						   stmt->pubname));
+
 		TransformPubWhereClauses(rels, queryString, pubform->pubviaroot);
 
 		CheckPubRelationColumnList(stmt->pubname, rels, publish_schema,
diff --git a/src/backend/replication/logical/Makefile b/src/backend/replication/logical/Makefile
index 2dc25e37bb..b79ddd8cdc 100644
--- a/src/backend/replication/logical/Makefile
+++ b/src/backend/replication/logical/Makefile
@@ -16,6 +16,8 @@ override CPPFLAGS := -I$(srcdir) $(CPPFLAGS)
 
 OBJS = \
 	applyparallelworker.o \
+	ddlmessage.o \
+	ddltrigger.o \
 	decode.o \
 	launcher.o \
 	logical.o \
diff --git a/src/backend/replication/logical/ddlmessage.c b/src/backend/replication/logical/ddlmessage.c
new file mode 100644
index 0000000000..3f144de395
--- /dev/null
+++ b/src/backend/replication/logical/ddlmessage.c
@@ -0,0 +1,83 @@
+/*-------------------------------------------------------------------------
+ *
+ * ddlmessage.c
+ *	  Logical DDL messages.
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/replication/logical/ddlmessage.c
+ *
+ * NOTES
+ *
+ * Logical DDL messages allow XLOG logging of DDL command strings that
+ * get passed to the logical decoding plugin. In normal XLOG processing they
+ * are same as NOOP.
+ *
+ * Unlike generic logical messages, these DDL messages have only transactional
+ * mode. Note by default DDLs in PostgreSQL are transactional.
+ *
+ * These messages are part of current transaction and will be sent to
+ * decoding plugin similar to DML operations.
+ *
+ * Every message includes a prefix to avoid conflicts between different decoding
+ * plugins. Plugin authors must take special care to use a unique prefix (e.g one
+ * idea is to include the name of the extension).
+ * ---------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/xloginsert.h"
+#include "catalog/namespace.h"
+#include "miscadmin.h"
+#include "nodes/execnodes.h"
+#include "replication/logical.h"
+#include "replication/ddlmessage.h"
+#include "utils/memutils.h"
+
+/*
+ * Write logical decoding DDL message into XLog.
+ */
+XLogRecPtr
+LogLogicalDDLMessage(const char *prefix, Oid relid, DeparsedCommandType cmdtype,
+					 const char *message, size_t size)
+{
+	xl_logical_ddl_message xlrec;
+
+	 /* Ensure we have a valid transaction id. */
+	Assert(IsTransactionState());
+	GetCurrentTransactionId();
+
+	xlrec.dbId = MyDatabaseId;
+	/* Trailing zero is critical; see logicalddlmsg_desc */
+	xlrec.prefix_size = strlen(prefix) + 1;
+	xlrec.message_size = size;
+	xlrec.relid = relid;
+	xlrec.cmdtype = cmdtype;
+
+	XLogBeginInsert();
+	XLogRegisterData((char *) &xlrec, SizeOfLogicalDDLMessage);
+	XLogRegisterData(unconstify(char *, prefix), xlrec.prefix_size);
+	XLogRegisterData(unconstify(char *, message), size);
+
+	/* Allow origin filtering */
+	XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
+
+	return XLogInsert(RM_LOGICALDDLMSG_ID, XLOG_LOGICAL_DDL_MESSAGE);
+}
+
+/*
+ * Redo is basically just noop for logical decoding DDL messages.
+ */
+void
+logicalddlmsg_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info != XLOG_LOGICAL_DDL_MESSAGE)
+		elog(PANIC, "logicalddlmsg_redo: unknown op code %u", info);
+
+	/* This is only interesting for logical decoding, see decode.c. */
+}
diff --git a/src/backend/replication/logical/ddltrigger.c b/src/backend/replication/logical/ddltrigger.c
new file mode 100644
index 0000000000..08e251686f
--- /dev/null
+++ b/src/backend/replication/logical/ddltrigger.c
@@ -0,0 +1,341 @@
+/*-------------------------------------------------------------------------
+ *
+ * ddltrigger.c
+ *	  Logical DDL triggers.
+ *
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/replication/logical/ddltrigger.c
+ *
+ * NOTES
+ *
+ * Deparse the ddl command and log it.
+ *
+ * ---------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/table.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_proc.h"
+#include "commands/event_trigger.h"
+#include "funcapi.h"
+#include "lib/ilist.h"
+#include "replication/ddlmessage.h"
+#include "tcop/ddldeparse.h"
+#include "utils/fmgrprotos.h"
+#include "utils/lsyscache.h"
+
+extern EventTriggerQueryState *currentEventTriggerState;
+
+
+/*
+ * Check if the command can be publishable.
+ *
+ * XXX Executing a non-immutable expression during the table rewrite phase is
+ * not allowed, as it may result in different data between publisher and
+ * subscriber. While some may suggest converting the rewrite inserts to updates
+ * and replicate them after the ddl command to maintain data consistency. But it
+ * doesn't work if the replica identity column is altered in the command. This
+ * is because the rewrite inserts do not contain the old values and therefore
+ * cannot be converted to update.
+ *
+ * Apart from that, commands contain volatile functions are not allowed. Because
+ * it's possible the functions contain DDL/DML in which case these operations
+ * will be executed twice and cause duplicate data. In addition, we don't know
+ * whether the tables being accessed by these DDL/DML are published or not. So
+ * blindly allowing such functions can allow unintended clauses like the tables
+ * accessed in those functions may not even exist on the subscriber.
+ */
+static void
+check_command_publishable(ddl_deparse_context context, bool is_rewrite)
+{
+
+	if (is_rewrite && context.func_volatile != PROVOLATILE_IMMUTABLE)
+		ereport(ERROR,
+				errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				errmsg("cannot rewrite table if this command contains mutable function because it cannot be replicated in DDL replication"));
+
+	if (context.func_volatile == PROVOLATILE_VOLATILE)
+		ereport(ERROR,
+				errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				errmsg("cannot use volatile function in ALTER TABLE command because it cannot be replicated in DDL replication"));
+}
+
+/*
+ * Deparse the ddl command and log it prior to
+ * execution. Currently only used for DROP TABLE command
+ * so that catalog can be accessed before being deleted.
+ * This is to check if the table is part of the publication
+ * or not.
+ */
+Datum
+publication_deparse_ddl_command_start(PG_FUNCTION_ARGS)
+{
+	EventTriggerData *trigdata;
+	char	   *command = psprintf("Drop table command start");
+	DropStmt   *stmt;
+	ListCell   *cell1;
+
+	if (!CALLED_AS_EVENT_TRIGGER(fcinfo))
+		elog(ERROR, "not fired by event trigger manager");
+
+	trigdata = (EventTriggerData *) fcinfo->context;
+	stmt = (DropStmt *) trigdata->parsetree;
+
+	/* Extract the relid from the parse tree */
+	foreach(cell1, stmt->objects)
+	{
+		char		relpersist;
+		Node	   *object = lfirst(cell1);
+		ObjectAddress address;
+		Relation	relation = NULL;
+
+		address = get_object_address(stmt->removeType,
+									 object,
+									 &relation,
+									 AccessExclusiveLock,
+									 true);
+
+		/* Object does not exist, nothing to do */
+		if (!relation)
+			continue;
+
+		relpersist = get_rel_persistence(address.objectId);
+
+		/*
+		 * Do not generate wal log for commands whose target table is a
+		 * temporary or unlogged table.
+		 *
+		 * XXX We may generate wal logs for unlogged tables in the future so
+		 * that unlogged tables can also be created and altered on the
+		 * subscriber side. This makes it possible to directly replay the SET
+		 * LOGGED command and the incoming rewrite message without creating a
+		 * new table.
+		 */
+		if (relpersist == RELPERSISTENCE_PERMANENT)
+			LogLogicalDDLMessage("deparse", address.objectId, DCT_TableDropStart,
+								 command, strlen(command) + 1);
+
+		table_close(relation, NoLock);
+	}
+	return PointerGetDatum(NULL);
+}
+
+/*
+ * publication_deparse_table_rewrite
+ *
+ * Deparse the ddl table rewrite command and log it.
+ */
+Datum
+publication_deparse_table_rewrite(PG_FUNCTION_ARGS)
+{
+	char		relpersist;
+	CollectedCommand *cmd;
+
+	if (!CALLED_AS_EVENT_TRIGGER(fcinfo))
+		elog(ERROR, "not fired by event trigger manager");
+
+	cmd = currentEventTriggerState->currentCommand;
+
+	Assert(cmd && cmd->d.alterTable.rewrite);
+
+	relpersist = get_rel_persistence(cmd->d.alterTable.objectId);
+
+	/*
+	 * Do not generate wal log for commands whose target table is a temporary
+	 * or unlogged table.
+	 *
+	 * XXX We may generate wal logs for unlogged tables in the future so that
+	 * unlogged tables can also be created and altered on the subscriber side.
+	 * This makes it possible to directly replay the SET LOGGED command and the
+	 * incoming rewrite message without creating a new table.
+	 */
+	if (relpersist == RELPERSISTENCE_PERMANENT)
+	{
+		ddl_deparse_context context;
+		char	   *json_string;
+
+		context.verbose_mode = false;
+		context.func_volatile = PROVOLATILE_IMMUTABLE;
+
+		/* Deparse the DDL command and WAL log it to allow decoding of the same. */
+		json_string = deparse_utility_command(cmd, &context);
+
+		if (json_string != NULL)
+		{
+			check_command_publishable(context, true);
+			LogLogicalDDLMessage("deparse", cmd->d.alterTable.objectId, DCT_TableAlter,
+								 json_string, strlen(json_string) + 1);
+		}
+	}
+
+	return PointerGetDatum(NULL);
+}
+
+/*
+ * Deparse the ddl command and log it. This function
+ * is called after the execution of the command but before the
+ * transaction commits.
+ */
+Datum
+publication_deparse_ddl_command_end(PG_FUNCTION_ARGS)
+{
+	ListCell   *lc;
+	slist_iter	iter;
+	Oid			relid;
+	char		relkind;
+
+	if (!CALLED_AS_EVENT_TRIGGER(fcinfo))
+		elog(ERROR, "not fired by event trigger manager");
+
+	foreach(lc, currentEventTriggerState->commandList)
+	{
+		char		relpersist = RELPERSISTENCE_PERMANENT;
+		CollectedCommand *cmd = lfirst(lc);
+		DeparsedCommandType cmdtype;
+
+		/* Rewrite DDL has been handled in table_rewrite trigger */
+		if (cmd->d.alterTable.rewrite)
+		{
+			RenameStmt *renameStmt = (RenameStmt *) cmd->parsetree;
+
+			if (renameStmt && renameStmt->relationType != OBJECT_TYPE &&
+				renameStmt->relationType != OBJECT_TABLE)
+				continue;
+		}
+
+		if (cmd->type == SCT_Simple &&
+			!OidIsValid(cmd->d.simple.address.objectId))
+			continue;
+
+		if (cmd->type == SCT_AlterTable)
+		{
+			relid = cmd->d.alterTable.objectId;
+			cmdtype = DCT_TableAlter;
+		}
+		else
+		{
+			/* Only SCT_Simple for now */
+			relid = cmd->d.simple.address.objectId;
+			cmdtype = DCT_SimpleCmd;
+		}
+
+		relkind = get_rel_relkind(relid);
+		if (relkind)
+			relpersist = get_rel_persistence(relid);
+
+		/*
+		 * Do not generate wal log for commands whose target table is a
+		 * temporary or unlogged table.
+		 *
+		 * XXX We may generate wal logs for unlogged tables in the future so
+		 * that unlogged tables can also be created and altered on the
+		 * subscriber side. This makes it possible to directly replay the SET
+		 * LOGGED command and the incoming rewrite message without creating a
+		 * new table.
+		 */
+		if (relpersist == RELPERSISTENCE_PERMANENT)
+		{
+			/*
+			 * Deparse the DDL command and WAL log it to allow decoding of the
+			 * same.
+			 */
+			ddl_deparse_context context;
+			char	   *json_string;
+
+			context.verbose_mode = false;
+			context.func_volatile = PROVOLATILE_IMMUTABLE;
+
+			json_string = deparse_utility_command(cmd, &context);
+
+			if (json_string != NULL)
+			{
+				check_command_publishable(context, false);
+				LogLogicalDDLMessage("deparse", relid, cmdtype, json_string,
+									 strlen(json_string) + 1);
+			}
+		}
+	}
+
+	/* handle drop commands which appear in the SQLDropList */
+	slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
+	{
+		SQLDropObject *obj;
+		EventTriggerData *trigdata;
+		char	   *command;
+		DeparsedCommandType cmdtype;
+
+		trigdata = (EventTriggerData *) fcinfo->context;
+
+		obj = slist_container(SQLDropObject, next, iter.cur);
+
+		if (!obj->original)
+			continue;
+
+		if (strcmp(obj->objecttype, "table") == 0)
+			cmdtype = DCT_TableDropEnd;
+		else
+			continue;
+
+		command = deparse_drop_command(obj->objidentity, obj->objecttype,
+									   trigdata->parsetree);
+
+		if (command)
+			LogLogicalDDLMessage("deparse", obj->address.objectId, cmdtype,
+								 command, strlen(command) + 1);
+	}
+
+	return PointerGetDatum(NULL);
+}
+
+/*
+ * publication_deparse_table_init_write
+ *
+ * Deparse the ddl table create command and log it.
+ */
+Datum
+publication_deparse_table_init_write(PG_FUNCTION_ARGS)
+{
+	char		relpersist;
+	CollectedCommand *cmd;
+	ddl_deparse_context context;
+
+	if (!CALLED_AS_EVENT_TRIGGER(fcinfo))
+		elog(ERROR, "not fired by event trigger manager");
+
+	cmd = currentEventTriggerState->currentCommand;
+	Assert(cmd);
+
+	relpersist = get_rel_persistence(cmd->d.simple.address.objectId);
+
+	/*
+	 * Do not generate wal log for commands whose target table is a temporary
+	 * table.
+	 *
+	 * We will generate wal logs for unlogged tables so that unlogged tables
+	 * can also be created and altered on the subscriber side. This makes it
+	 * possible to directly replay the SET LOGGED command and the incoming
+	 * rewrite message without creating a new table.
+	 */
+	if (relpersist == RELPERSISTENCE_PERMANENT)
+	{
+		char	   *json_string;
+
+		context.verbose_mode = false;
+		context.func_volatile = PROVOLATILE_IMMUTABLE;
+
+		/* Deparse the DDL command and WAL log it to allow decoding of the same. */
+		json_string = deparse_utility_command(cmd, &context);
+
+		if (json_string != NULL)
+		{
+			check_command_publishable(context, false);
+			LogLogicalDDLMessage("deparse", cmd->d.simple.address.objectId, DCT_SimpleCmd,
+								 json_string, strlen(json_string) + 1);
+		}
+	}
+	return PointerGetDatum(NULL);
+}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index d91055a440..b22bbcd15a 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -36,6 +36,7 @@
 #include "access/xlogutils.h"
 #include "catalog/pg_control.h"
 #include "replication/decode.h"
+#include "replication/ddlmessage.h"
 #include "replication/logical.h"
 #include "replication/message.h"
 #include "replication/origin.h"
@@ -641,6 +642,46 @@ logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 							  message->message + message->prefix_size);
 }
 
+/*
+ * Handle rmgr LOGICALDDLMSG_ID records for DecodeRecordIntoReorderBuffer().
+ */
+void
+logicalddl_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
+{
+	SnapBuild  *builder = ctx->snapshot_builder;
+	XLogReaderState *r = buf->record;
+	TransactionId xid = XLogRecGetXid(r);
+	uint8		info = XLogRecGetInfo(r) & ~XLR_INFO_MASK;
+	RepOriginId origin_id = XLogRecGetOrigin(r);
+	xl_logical_ddl_message *message;
+
+	if (info != XLOG_LOGICAL_DDL_MESSAGE)
+		elog(ERROR, "unexpected RM_LOGICALDDLMSG_ID record type: %u", info);
+
+	ReorderBufferProcessXid(ctx->reorder, XLogRecGetXid(r), buf->origptr);
+
+	/*
+	 * If we don't have snapshot or we are just fast-forwarding, there is no
+	 * point in decoding ddl messages.
+	 */
+	if (SnapBuildCurrentState(builder) < SNAPBUILD_FULL_SNAPSHOT ||
+		ctx->fast_forward)
+		return;
+
+	message = (xl_logical_ddl_message *) XLogRecGetData(r);
+
+	if (message->dbId != ctx->slot->data.database ||
+		FilterByOrigin(ctx, origin_id))
+		return;
+
+	if (SnapBuildProcessChange(builder, xid, buf->origptr))
+		ReorderBufferQueueDDLMessage(ctx->reorder, xid, buf->endptr,
+									 message->message, /* first part of message is prefix */
+									 message->message_size,
+									 message->message + message->prefix_size,
+									 message->relid, message->cmdtype);
+}
+
 /*
  * Consolidated commit record handling between the different form of commit
  * records.
diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c
index 41243d0187..1d99fac116 100644
--- a/src/backend/replication/logical/logical.c
+++ b/src/backend/replication/logical/logical.c
@@ -33,6 +33,7 @@
 #include "fmgr.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "replication/ddlmessage.h"
 #include "replication/decode.h"
 #include "replication/logical.h"
 #include "replication/origin.h"
@@ -73,6 +74,10 @@ static void truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
 static void message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
 							   XLogRecPtr message_lsn, bool transactional,
 							   const char *prefix, Size message_size, const char *message);
+static void ddl_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
+								  XLogRecPtr message_lsn, const char *prefix,
+								  Oid relid, DeparsedCommandType cmdtype,
+								  Size message_size, const char *message);
 
 /* streaming callbacks */
 static void stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
@@ -90,6 +95,11 @@ static void stream_change_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn
 static void stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
 									  XLogRecPtr message_lsn, bool transactional,
 									  const char *prefix, Size message_size, const char *message);
+static void stream_ddl_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
+										 XLogRecPtr message_lsn,
+										 const char *prefix,
+										 Oid relid, DeparsedCommandType cmdtype,
+										 Size message_size, const char *message);
 static void stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
 									   int nrelations, Relation relations[], ReorderBufferChange *change);
 
@@ -221,10 +231,11 @@ StartupDecodingContext(List *output_plugin_options,
 	ctx->reorder->apply_truncate = truncate_cb_wrapper;
 	ctx->reorder->commit = commit_cb_wrapper;
 	ctx->reorder->message = message_cb_wrapper;
+	ctx->reorder->ddl = ddl_cb_wrapper;
 
 	/*
 	 * To support streaming, we require start/stop/abort/commit/change
-	 * callbacks. The message and truncate callbacks are optional, similar to
+	 * callbacks. The message, ddl and truncate callbacks are optional, similar to
 	 * regular output plugins. We however enable streaming when at least one
 	 * of the methods is enabled so that we can easily identify missing
 	 * methods.
@@ -237,12 +248,13 @@ StartupDecodingContext(List *output_plugin_options,
 		(ctx->callbacks.stream_commit_cb != NULL) ||
 		(ctx->callbacks.stream_change_cb != NULL) ||
 		(ctx->callbacks.stream_message_cb != NULL) ||
+		(ctx->callbacks.stream_ddl_cb != NULL) ||
 		(ctx->callbacks.stream_truncate_cb != NULL);
 
 	/*
 	 * streaming callbacks
 	 *
-	 * stream_message and stream_truncate callbacks are optional, so we do not
+	 * stream_message, stream_ddl and stream_truncate callbacks are optional, so we do not
 	 * fail with ERROR when missing, but the wrappers simply do nothing. We
 	 * must set the ReorderBuffer callbacks to something, otherwise the calls
 	 * from there will crash (we don't want to move the checks there).
@@ -254,6 +266,7 @@ StartupDecodingContext(List *output_plugin_options,
 	ctx->reorder->stream_commit = stream_commit_cb_wrapper;
 	ctx->reorder->stream_change = stream_change_cb_wrapper;
 	ctx->reorder->stream_message = stream_message_cb_wrapper;
+	ctx->reorder->stream_ddl = stream_ddl_cb_wrapper;
 	ctx->reorder->stream_truncate = stream_truncate_cb_wrapper;

@@ -1260,6 +1273,44 @@ message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
 	error_context_stack = errcallback.previous;
 }
 
+static void
+ddl_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
+					  XLogRecPtr message_lsn,
+					  const char *prefix, Oid relid, DeparsedCommandType cmdtype,
+					  Size message_size,
+					  const char *message)
+{
+	LogicalDecodingContext *ctx = cache->private_data;
+	LogicalErrorCallbackState state;
+	ErrorContextCallback errcallback;
+
+	Assert(!ctx->fast_forward);
+
+	if (ctx->callbacks.ddl_cb == NULL)
+		return;
+
+	/* Push callback + info on the error context stack */
+	state.ctx = ctx;
+	state.callback_name = "ddl";
+	state.report_location = message_lsn;
+	errcallback.callback = output_plugin_error_callback;
+	errcallback.arg = (void *) &state;
+	errcallback.previous = error_context_stack;
+	error_context_stack = &errcallback;
+
+	/* set output state */
+	ctx->accept_writes = true;
+	ctx->write_xid = txn->xid;
+	ctx->write_location = message_lsn;
+
+	/* do the actual work: call callback */
+	ctx->callbacks.ddl_cb(ctx, txn, message_lsn, prefix, relid, cmdtype,
+								 message_size, message);
+
+	/* Pop the error context stack */
+	error_context_stack = errcallback.previous;
+}
+
 static void
 stream_start_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
 						XLogRecPtr first_lsn)
@@ -1575,6 +1626,48 @@ stream_message_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
 	error_context_stack = errcallback.previous;
 }
 
+static void
+stream_ddl_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
+							 XLogRecPtr message_lsn,
+							 const char *prefix, Oid relid, DeparsedCommandType cmdtype,
+							 Size message_size,
+							 const char *message)
+{
+	LogicalDecodingContext *ctx = cache->private_data;
+	LogicalErrorCallbackState state;
+	ErrorContextCallback errcallback;
+
+	Assert(!ctx->fast_forward);
+
+	/* We're only supposed to call this when streaming is supported. */
+	Assert(ctx->streaming);
+
+	/* this callback is optional */
+	if (ctx->callbacks.stream_ddl_cb == NULL)
+		return;
+
+	/* Push callback + info on the error context stack */
+	state.ctx = ctx;
+	state.callback_name = "stream_ddl";
+	state.report_location = message_lsn;
+	errcallback.callback = output_plugin_error_callback;
+	errcallback.arg = (void *) &state;
+	errcallback.previous = error_context_stack;
+	error_context_stack = &errcallback;
+
+	/* set output state */
+	ctx->accept_writes = true;
+	ctx->write_xid = txn->xid;
+	ctx->write_location = message_lsn;
+
+	/* do the actual work: call callback */
+	ctx->callbacks.stream_ddl_cb(ctx, txn, message_lsn, prefix, relid,
+										cmdtype, message_size, message);
+
+	/* Pop the error context stack */
+	error_context_stack = errcallback.previous;
+}
+
 static void
 stream_truncate_cb_wrapper(ReorderBuffer *cache, ReorderBufferTXN *txn,
 						   int nrelations, Relation relations[],
diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c
index 55a24c02c9..bececc3f93 100644
--- a/src/backend/replication/logical/logicalfuncs.c
+++ b/src/backend/replication/logical/logicalfuncs.c
@@ -29,6 +29,7 @@
 #include "nodes/makefuncs.h"
 #include "replication/decode.h"
 #include "replication/logical.h"
+#include "replication/ddlmessage.h"
 #include "replication/message.h"
 #include "storage/fd.h"
 #include "utils/array.h"
@@ -375,3 +376,26 @@ pg_logical_emit_message_text(PG_FUNCTION_ARGS)
 	/* bytea and text are compatible */
 	return pg_logical_emit_message_bytea(fcinfo);
 }
+
+/*
+ * SQL function for writing logical decoding DDL message into WAL.
+ */
+Datum
+pg_logical_emit_ddl_message_bytea(PG_FUNCTION_ARGS)
+{
+	char	   *prefix = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	Oid			relid = PG_GETARG_OID(1);
+	DeparsedCommandType cmdtype = PG_GETARG_INT16(2);
+	char	   *data = text_to_cstring(PG_GETARG_TEXT_PP(3));
+	XLogRecPtr	lsn;
+
+	lsn = LogLogicalDDLMessage(prefix, relid, cmdtype, data, strlen(data));
+	PG_RETURN_LSN(lsn);
+}
+
+Datum
+pg_logical_emit_ddl_message_text(PG_FUNCTION_ARGS)
+{
+	/* bytea and text are compatible */
+	return pg_logical_emit_ddl_message_bytea(fcinfo);
+}
diff --git a/src/backend/replication/logical/meson.build b/src/backend/replication/logical/meson.build
index d48cd4c590..05e775697e 100644
--- a/src/backend/replication/logical/meson.build
+++ b/src/backend/replication/logical/meson.build
@@ -2,6 +2,8 @@
 
 backend_sources += files(
   'applyparallelworker.c',
+  'ddlmessage.c',
+  'ddltrigger.c',
   'decode.c',
   'launcher.c',
   'logical.c',
diff --git a/src/backend/replication/logical/proto.c b/src/backend/replication/logical/proto.c
index f308713275..7ec3b71662 100644
--- a/src/backend/replication/logical/proto.c
+++ b/src/backend/replication/logical/proto.c
@@ -663,6 +663,47 @@ logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
 	pq_sendbytes(out, message, sz);
 }
 
+/*
+ * Read DDL from stream
+ */
+char *
+logicalrep_read_ddl(StringInfo in, XLogRecPtr *lsn,
+						   const char **prefix,
+						   Size *sz)
+{
+	uint8 flags;
+	char *msg;
+
+	flags = pq_getmsgint(in, 1);
+	if (flags != 0)
+		elog(ERROR, "unrecognized flags %u in ddl message", flags);
+
+	*lsn = pq_getmsgint64(in);
+	*prefix = pq_getmsgstring(in);
+	*sz = pq_getmsgint(in, 4);
+	msg = (char *) pq_getmsgbytes(in, *sz);
+
+	return msg;
+}
+
+/*
+ * Write DDL to stream
+ */
+void
+logicalrep_write_ddl(StringInfo out, XLogRecPtr lsn,
+							const char *prefix, Size sz, const char *message)
+{
+	uint8		flags = 0;
+
+	pq_sendbyte(out, LOGICAL_REP_MSG_DDL);
+
+	pq_sendint8(out, flags);
+	pq_sendint64(out, lsn);
+	pq_sendstring(out, prefix);
+	pq_sendint32(out, sz);
+	pq_sendbytes(out, message, sz);
+}
+
 /*
  * Write relation description to the output stream.
  */
@@ -1238,6 +1279,8 @@ logicalrep_message_type(LogicalRepMsgType action)
 			return "TYPE";
 		case LOGICAL_REP_MSG_MESSAGE:
 			return "MESSAGE";
+		case LOGICAL_REP_MSG_DDL:
+			return "DDL";
 		case LOGICAL_REP_MSG_BEGIN_PREPARE:
 			return "BEGIN PREPARE";
 		case LOGICAL_REP_MSG_PREPARE:
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 26d252bd87..8351256f2f 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -94,6 +94,7 @@
 #include "lib/binaryheap.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "replication/ddlmessage.h"
 #include "replication/logical.h"
 #include "replication/reorderbuffer.h"
 #include "replication/slot.h"
@@ -516,6 +517,14 @@ ReorderBufferReturnChange(ReorderBuffer *rb, ReorderBufferChange *change,
 				pfree(change->data.msg.message);
 			change->data.msg.message = NULL;
 			break;
+		case REORDER_BUFFER_CHANGE_DDL:
+			if (change->data.ddl.prefix != NULL)
+				pfree(change->data.ddl.prefix);
+			change->data.ddl.prefix = NULL;
+			if (change->data.ddl.message != NULL)
+				pfree(change->data.ddl.message);
+			change->data.ddl.message = NULL;
+			break;
 		case REORDER_BUFFER_CHANGE_INVALIDATION:
 			if (change->data.inval.invalidations)
 				pfree(change->data.inval.invalidations);
@@ -895,6 +904,36 @@ ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
 	}
 }
 
+/*
+ * A transactional DDL message is queued to be processed upon commit.
+ */
+void
+ReorderBufferQueueDDLMessage(ReorderBuffer *rb, TransactionId xid,
+							 XLogRecPtr lsn, const char *prefix,
+							 Size message_size, const char *message,
+							 Oid relid, DeparsedCommandType cmdtype)
+{
+	MemoryContext oldcontext;
+	ReorderBufferChange *change;
+
+	Assert(TransactionIdIsValid(xid));
+
+	oldcontext = MemoryContextSwitchTo(rb->context);
+
+	change = ReorderBufferGetChange(rb);
+	change->action = REORDER_BUFFER_CHANGE_DDL;
+	change->data.ddl.prefix = pstrdup(prefix);
+	change->data.ddl.relid = relid;
+	change->data.ddl.cmdtype = cmdtype;
+	change->data.ddl.message_size = message_size;
+	change->data.ddl.message = palloc(message_size);
+	memcpy(change->data.ddl.message, message, message_size);
+
+	ReorderBufferQueueChange(rb, xid, lsn, change, false);
+
+	MemoryContextSwitchTo(oldcontext);
+}
+
 /*
  * AssertTXNLsnOrder
  *		Verify LSN ordering of transaction lists in the reorderbuffer
@@ -1997,6 +2036,29 @@ ReorderBufferApplyMessage(ReorderBuffer *rb, ReorderBufferTXN *txn,
 					change->data.msg.message);
 }
 
+/*
+ * Helper function for ReorderBufferProcessTXN for applying the DDL message.
+ */
+static inline void
+ReorderBufferApplyDDLMessage(ReorderBuffer *rb, ReorderBufferTXN *txn,
+							 ReorderBufferChange *change, bool streaming)
+{
+	if (streaming)
+		rb->stream_ddl(rb, txn, change->lsn,
+							  change->data.ddl.prefix,
+							  change->data.ddl.relid,
+							  change->data.ddl.cmdtype,
+							  change->data.ddl.message_size,
+							  change->data.ddl.message);
+	else
+		rb->ddl(rb, txn, change->lsn,
+					   change->data.ddl.prefix,
+					   change->data.ddl.relid,
+					   change->data.ddl.cmdtype,
+					   change->data.ddl.message_size,
+					   change->data.ddl.message);
+}
+
 /*
  * Function to store the command id and snapshot at the end of the current
  * stream so that we can reuse the same while sending the next stream.
@@ -2379,6 +2441,10 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
 					ReorderBufferApplyMessage(rb, txn, change, streaming);
 					break;
 
+				case REORDER_BUFFER_CHANGE_DDL:
+					ReorderBufferApplyDDLMessage(rb, txn, change, streaming);
+					break;
+
 				case REORDER_BUFFER_CHANGE_INVALIDATION:
 					/* Execute the invalidation messages locally */
 					ReorderBufferExecuteInvalidations(change->data.inval.ninvalidations,
@@ -3835,6 +3901,39 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
 					   change->data.msg.message_size);
 				data += change->data.msg.message_size;
 
+				break;
+			}
+		case REORDER_BUFFER_CHANGE_DDL:
+			{
+				char	   *data;
+				Size		prefix_size = strlen(change->data.ddl.prefix) + 1;
+
+				sz += prefix_size + change->data.ddl.message_size +
+					sizeof(Size) + sizeof(Oid) + sizeof(DeparsedCommandType) + sizeof(Size);
+				ReorderBufferSerializeReserve(rb, sz);
+
+				data = ((char *) rb->outbuf) + sizeof(ReorderBufferDiskChange);
+
+				/* might have been reallocated above */
+				ondisk = (ReorderBufferDiskChange *) rb->outbuf;
+
+				/* write the prefix, relid and cmdtype including the size */
+				memcpy(data, &prefix_size, sizeof(Size));
+				data += sizeof(Size);
+				memcpy(data, &change->data.ddl.relid, sizeof(Oid));
+				data += sizeof(Oid);
+				memcpy(data, &change->data.ddl.cmdtype, sizeof(DeparsedCommandType));
+				data += sizeof(DeparsedCommandType);
+				memcpy(data, change->data.ddl.prefix, prefix_size);
+				data += prefix_size;
+
+				/* write the message including the size */
+				memcpy(data, &change->data.ddl.message_size, sizeof(Size));
+				data += sizeof(Size);
+				memcpy(data, change->data.ddl.message,
+					   change->data.ddl.message_size);
+				data += change->data.ddl.message_size;
+
 				break;
 			}
 		case REORDER_BUFFER_CHANGE_INVALIDATION:
@@ -4149,6 +4248,15 @@ ReorderBufferChangeSize(ReorderBufferChange *change)
 				sz += prefix_size + change->data.msg.message_size +
 					sizeof(Size) + sizeof(Size);
 
+				break;
+			}
+		case REORDER_BUFFER_CHANGE_DDL:
+			{
+				Size		prefix_size = strlen(change->data.ddl.prefix) + 1;
+
+				sz += prefix_size + change->data.ddl.message_size +
+					sizeof(Size) + sizeof(Size) + sizeof(Oid) + sizeof(DeparsedCommandType);
+
 				break;
 			}
 		case REORDER_BUFFER_CHANGE_INVALIDATION:
@@ -4426,6 +4534,33 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
 					   change->data.msg.message_size);
 				data += change->data.msg.message_size;
 
+				break;
+			}
+		case REORDER_BUFFER_CHANGE_DDL:
+			{
+				Size		prefix_size;
+
+				/* read prefix */
+				memcpy(&prefix_size, data, sizeof(Size));
+				data += sizeof(Size);
+				memcpy(&change->data.ddl.relid, data, sizeof(Oid));
+				data += sizeof(Oid);
+				memcpy(&change->data.ddl.cmdtype, data, sizeof(DeparsedCommandType));
+				data += sizeof(int);
+				change->data.ddl.prefix = MemoryContextAlloc(rb->context, prefix_size);
+				memcpy(change->data.ddl.prefix, data, prefix_size);
+				Assert(change->data.ddl.prefix[prefix_size - 1] == '\0');
+				data += prefix_size;
+
+				/* read the message */
+				memcpy(&change->data.msg.message_size, data, sizeof(Size));
+				data += sizeof(Size);
+				change->data.msg.message = MemoryContextAlloc(rb->context,
+															  change->data.msg.message_size);
+				memcpy(change->data.msg.message, data,
+					   change->data.msg.message_size);
+				data += change->data.msg.message_size;
+
 				break;
 			}
 		case REORDER_BUFFER_CHANGE_INVALIDATION:
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 78926f8647..edb8ecabbf 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -166,6 +166,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/optimizer.h"
+#include "parser/analyze.h"
 #include "parser/parse_relation.h"
 #include "pgstat.h"
 #include "postmaster/bgworker.h"
@@ -191,7 +192,10 @@
 #include "storage/lmgr.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/ddldeparse.h"
+#include "tcop/pquery.h"
 #include "tcop/tcopprot.h"
+#include "tcop/utility.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/catcache.h"
@@ -3277,6 +3281,235 @@ apply_handle_truncate(StringInfo s)
 	end_replication_step();
 }
 
+/*
+ * CREATE TABLE AS and SELECT INTO require special handling to force them
+ * to skip populating data from the source table on the subscriber. Data
+ * should be replicated from the publisher instead.
+ */
+static void
+preprocess_ddl_create_table(RawStmt *command)
+{
+	CommandTag	commandTag;
+
+	commandTag = CreateCommandTag((Node *) command);
+
+	switch (commandTag)
+	{
+		case CMDTAG_CREATE_TABLE_AS:
+		case CMDTAG_SELECT_INTO:
+			{
+				CreateTableAsStmt *castmt = (CreateTableAsStmt *) command->stmt;
+
+				if (castmt->objtype == OBJECT_TABLE)
+				{
+					/*
+					 * Force skipping data population to avoid data
+					 * inconsistency. Data should be replicated from the
+					 * publisher instead.
+					 */
+					castmt->into->skipData = true;
+				}
+			}
+			break;
+		case CMDTAG_SELECT:
+			{
+				SelectStmt *sstmt = (SelectStmt *) command->stmt;
+
+				if (sstmt->intoClause != NULL)
+					sstmt->intoClause->skipData = true;
+			}
+			break;
+		default:
+			break;
+	}
+}
+
+/*
+ * Handle CREATE TABLE command
+ *
+ * Call AddSubscriptionRelState for CREATE TABLE command to set the relstate to
+ * SUBREL_STATE_READY so DML changes on this new table can be replicated without
+ * having to manually run "ALTER SUBSCRIPTION ... REFRESH PUBLICATION"
+ */
+static void
+postprocess_ddl_create_table(RawStmt *command)
+{
+	CommandTag	commandTag;
+	RangeVar   *rv = NULL;
+	Oid			relid;
+	Oid			relnamespace_oid = InvalidOid;
+	CreateStmt *cstmt;
+	char	   *schemaname = NULL;
+	char	   *relname = NULL;
+
+	commandTag = CreateCommandTag((Node *) command);
+	cstmt = (CreateStmt *) command->stmt;
+	rv = cstmt->relation;
+
+	if (commandTag != CMDTAG_CREATE_TABLE)
+		return;
+
+	cstmt = (CreateStmt *) command->stmt;
+	rv = cstmt->relation;
+	if (!rv)
+		return;
+
+	schemaname = rv->schemaname;
+	relname = rv->relname;
+
+	if (schemaname != NULL)
+		relnamespace_oid = get_namespace_oid(schemaname, false);
+
+	if (OidIsValid(relnamespace_oid))
+		relid = get_relname_relid(relname, relnamespace_oid);
+	else
+		relid = RelnameGetRelid(relname);
+
+	if (OidIsValid(relid))
+	{
+		AddSubscriptionRelState(MySubscription->oid, relid,
+								SUBREL_STATE_READY,
+								InvalidXLogRecPtr);
+		ereport(DEBUG1,
+				(errmsg_internal("table \"%s\" added to subscription \"%s\"",
+								 relname, MySubscription->name)));
+	}
+}
+
+/*
+ * Handle DDL replication messages. Convert the json string into a query
+ * string and run it through the query portal.
+ */
+static void
+apply_handle_ddl(StringInfo s)
+{
+	XLogRecPtr	lsn;
+	const char *prefix = NULL;
+	char	   *message = NULL;
+	char	   *ddl_command;
+	Size		sz;
+	List	   *parsetree_list;
+	ListCell   *parsetree_item;
+	DestReceiver *receiver;
+	MemoryContext oldcontext;
+	const char *save_debug_query_string = debug_query_string;
+
+	message = logicalrep_read_ddl(s, &lsn, &prefix, &sz);
+
+	SetCurrentStatementStartTimestamp();
+
+	if (!IsTransactionState())
+	{
+		StartTransactionCommand();
+		maybe_reread_subscription();
+	}
+
+	MemoryContextSwitchTo(ApplyMessageContext);
+
+	ddl_command = deparse_ddl_json_to_string(message);
+	debug_query_string = ddl_command;
+
+	/* DestNone for logical replication */
+	receiver = CreateDestReceiver(DestNone);
+	parsetree_list = pg_parse_query(ddl_command);
+
+	foreach(parsetree_item, parsetree_list)
+	{
+		List	   *plantree_list;
+		List	   *querytree_list;
+		RawStmt    *command = (RawStmt *) lfirst(parsetree_item);
+		CommandTag	commandTag;
+		MemoryContext per_parsetree_context = NULL;
+		Portal		portal;
+		bool		snapshot_set = false;
+
+		commandTag = CreateCommandTag((Node *) command);
+
+		/* If we got a cancel signal in parsing or prior command, quit */
+		CHECK_FOR_INTERRUPTS();
+
+		/* Remove data population from the command */
+		preprocess_ddl_create_table(command);
+
+		/*
+		 * Set up a snapshot if parse analysis/planning will need one.
+		 */
+		if (analyze_requires_snapshot(command))
+		{
+			PushActiveSnapshot(GetTransactionSnapshot());
+			snapshot_set = true;
+		}
+
+		/*
+		 * We do the work for each parsetree in a short-lived context, to
+		 * limit the memory used when there are many commands in the string.
+		 */
+		per_parsetree_context =
+			AllocSetContextCreate(CurrentMemoryContext,
+								  "execute_sql_string per-statement context",
+								  ALLOCSET_DEFAULT_SIZES);
+		oldcontext = MemoryContextSwitchTo(per_parsetree_context);
+
+		querytree_list = pg_analyze_and_rewrite_fixedparams(command,
+															ddl_command,
+															NULL, 0, NULL);
+
+		plantree_list = pg_plan_queries(querytree_list, ddl_command, 0, NULL);
+
+		/* Done with the snapshot used for parsing/planning */
+		if (snapshot_set)
+			PopActiveSnapshot();
+
+		portal = CreatePortal("logical replication", true, true);
+
+		/*
+		 * We don't have to copy anything into the portal, because everything
+		 * we are passing here is in ApplyMessageContext or the
+		 * per_parsetree_context, and so will outlive the portal anyway.
+		 */
+		PortalDefineQuery(portal,
+						  NULL,
+						  ddl_command,
+						  commandTag,
+						  plantree_list,
+						  NULL);
+
+		/*
+		 * Start the portal.  No parameters here.
+		 */
+		PortalStart(portal, NULL, 0, InvalidSnapshot);
+
+		/*
+		 * Switch back to transaction context for execution.
+		 */
+		MemoryContextSwitchTo(oldcontext);
+
+		(void) PortalRun(portal,
+						 FETCH_ALL,
+						 true,
+						 true,
+						 receiver,
+						 receiver,
+						 NULL);
+
+		PortalDrop(portal, false);
+
+		CommandCounterIncrement();
+
+		/*
+		 * Table created by DDL replication (database level) is automatically
+		 * added to the subscription here.
+		 */
+		postprocess_ddl_create_table(command);
+
+		/* Now we may drop the per-parsetree context, if one was created. */
+		MemoryContextDelete(per_parsetree_context);
+	}
+
+	debug_query_string = save_debug_query_string;
+
+	CommandCounterIncrement();
+}
 
 /*
  * Logical replication protocol message dispatcher.
@@ -3342,6 +3575,10 @@ apply_dispatch(StringInfo s)
 			 */
 			break;
 
+		case LOGICAL_REP_MSG_DDL:
+			apply_handle_ddl(s);
+			break;
+
 		case LOGICAL_REP_MSG_STREAM_START:
 			apply_handle_stream_start(s);
 			break;
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c
index b08ca55041..a3b2e006bd 100644
--- a/src/backend/replication/pgoutput/pgoutput.c
+++ b/src/backend/replication/pgoutput/pgoutput.c
@@ -18,6 +18,7 @@
 #include "catalog/pg_publication_rel.h"
 #include "catalog/pg_subscription.h"
 #include "commands/defrem.h"
+#include "commands/publicationcmds.h"
 #include "commands/subscriptioncmds.h"
 #include "executor/executor.h"
 #include "fmgr.h"
@@ -55,6 +56,11 @@ static void pgoutput_message(LogicalDecodingContext *ctx,
 							 ReorderBufferTXN *txn, XLogRecPtr message_lsn,
 							 bool transactional, const char *prefix,
 							 Size sz, const char *message);
+static void pgoutput_ddl(LogicalDecodingContext *ctx,
+						 ReorderBufferTXN *txn, XLogRecPtr message_lsn,
+						 const char *prefix, Oid relid,
+						 DeparsedCommandType cmdtype,
+						 Size sz, const char *message);
 static bool pgoutput_origin_filter(LogicalDecodingContext *ctx,
 								   RepOriginId origin_id);
 static void pgoutput_begin_prepare_txn(LogicalDecodingContext *ctx,
@@ -207,6 +213,7 @@ typedef struct RelationSyncEntry
 typedef struct PGOutputTxnData
 {
 	bool		sent_begin_txn; /* flag indicating whether BEGIN has been sent */
+	List	   *deleted_relids; /* maintain list of deleted table oids */
 } PGOutputTxnData;
 
 /* Map used to remember which relation schemas we sent. */
@@ -254,6 +261,7 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
 	cb->change_cb = pgoutput_change;
 	cb->truncate_cb = pgoutput_truncate;
 	cb->message_cb = pgoutput_message;
+	cb->ddl_cb = pgoutput_ddl;
 	cb->commit_cb = pgoutput_commit_txn;
 
 	cb->begin_prepare_cb = pgoutput_begin_prepare_txn;
@@ -270,6 +278,7 @@ _PG_output_plugin_init(OutputPluginCallbacks *cb)
 	cb->stream_commit_cb = pgoutput_stream_commit;
 	cb->stream_change_cb = pgoutput_change;
 	cb->stream_message_cb = pgoutput_message;
+	cb->stream_ddl_cb = pgoutput_ddl;
 	cb->stream_truncate_cb = pgoutput_truncate;
 	/* transaction streaming - two-phase commit */
 	cb->stream_prepare_cb = pgoutput_stream_prepare_txn;
@@ -505,6 +514,7 @@ pgoutput_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
 
 		/* Init publication state. */
 		data->publications = NIL;
+		data->deleted_relids = NIL;
 		publications_valid = false;
 
 		/*
@@ -533,6 +543,34 @@ pgoutput_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
 	}
 }
 
+/* Initialize the per-transaction private data for the given transaction. */
+static void
+init_txn_data(LogicalDecodingContext *ctx, ReorderBufferTXN *txn)
+{
+	PGOutputTxnData *txndata;
+
+	if (txn->output_plugin_private != NULL)
+		return;
+
+	txndata = MemoryContextAllocZero(ctx->context, sizeof(PGOutputTxnData));
+
+	txn->output_plugin_private = txndata;
+}
+
+/* Clean up the per-transaction private data for the given transaction. */
+static void
+clean_txn_data(ReorderBufferTXN *txn)
+{
+	PGOutputTxnData *txndata = (PGOutputTxnData *) txn->output_plugin_private;
+
+	if (txndata == NULL)
+		return;
+
+	list_free(txndata->deleted_relids);
+	pfree(txndata);
+	txn->output_plugin_private = NULL;
+}
+
 /*
  * BEGIN callback.
  *
@@ -546,10 +584,7 @@ pgoutput_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt,
 static void
 pgoutput_begin_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn)
 {
-	PGOutputTxnData *txndata = MemoryContextAllocZero(ctx->context,
-													  sizeof(PGOutputTxnData));
-
-	txn->output_plugin_private = txndata;
+	init_txn_data(ctx, txn);
 }
 
 /*
@@ -594,8 +629,7 @@ pgoutput_commit_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
 	 */
 	sent_begin_txn = txndata->sent_begin_txn;
 	OutputPluginUpdateProgress(ctx, !sent_begin_txn);
-	pfree(txndata);
-	txn->output_plugin_private = NULL;
+	clean_txn_data(txn);
 
 	if (!sent_begin_txn)
 	{
@@ -637,6 +671,8 @@ pgoutput_prepare_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
 	OutputPluginPrepareWrite(ctx, true);
 	logicalrep_write_prepare(ctx->out, txn, prepare_lsn);
 	OutputPluginWrite(ctx, true);
+
+	clean_txn_data(txn);
 }
 
 /*
@@ -1665,6 +1701,111 @@ pgoutput_message(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
 	OutputPluginWrite(ctx, true);
 }
 
+/* Check if the given object is published. */
+static bool
+is_object_published(LogicalDecodingContext *ctx, Oid objid)
+{
+	Relation	relation = NULL;
+	RelationSyncEntry *relentry;
+	PGOutputData *data = (PGOutputData *) ctx->output_plugin_private;
+
+	/* First check the DDL command filter. */
+	switch (get_rel_relkind(objid))
+	{
+		case RELKIND_RELATION:
+		case RELKIND_PARTITIONED_TABLE:
+			relation = RelationIdGetRelation(objid);
+			relentry = get_rel_sync_entry(data, relation);
+			RelationClose(relation);
+
+			/*
+			 * Only send this ddl if we don't publish ddl message or the ddl
+			 * need to be published via its root relation.
+			 */
+			if (relentry->pubactions.pubddl_table &&
+				relentry->publish_as_relid == objid)
+				return true;
+
+			break;
+		default:
+			/* unsupported objects */
+			return false;
+	}
+
+	return false;
+}
+
+/*
+ * Send the decoded DDL message.
+ */
+static void
+pgoutput_ddl(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
+			 XLogRecPtr message_lsn,
+			 const char *prefix, Oid relid, DeparsedCommandType cmdtype,
+			 Size sz, const char *message)
+{
+	PGOutputTxnData *txndata = (PGOutputTxnData *) txn->output_plugin_private;
+
+	/*
+	 * Check if the given object is published. Note that for dropped objects,
+	 * we cannot get the required information from the catalog, so we skip the
+	 * check for them.
+	 */
+	if (cmdtype != DCT_TableDropEnd && !is_object_published(ctx, relid))
+		return;
+
+	switch (cmdtype)
+	{
+		case DCT_TableDropStart:
+			{
+				MemoryContext	oldctx;
+
+				init_txn_data(ctx, txn);
+
+				txndata = (PGOutputTxnData *) txn->output_plugin_private;
+
+				/*
+				 * On DROP start, add the relid to a deleted_relid list if the
+				 * relid is part of a publication that supports ddl
+				 * publication. We need this because on DROP end, the relid
+				 * will no longer be valid. Later on Drop end, verify that the
+				 * drop is for a relid that is on the deleted_rid list, and
+				 * only then send the ddl message.
+				 */
+				oldctx = MemoryContextSwitchTo(ctx->context);
+				txndata->deleted_relids = lappend_oid(txndata->deleted_relids,
+													  relid);
+				MemoryContextSwitchTo(oldctx);
+			}
+			return;
+
+		case DCT_TableDropEnd:
+			if (!list_member_oid(txndata->deleted_relids, relid))
+				return;
+
+			txndata->deleted_relids = list_delete_oid(txndata->deleted_relids,
+													  relid);
+			break;
+
+		case DCT_TableAlter:
+		case DCT_SimpleCmd:
+			/* do nothing */
+			break;
+
+		default:
+			elog(ERROR, "unsupported type %d", cmdtype);
+			break;
+	}
+
+	/* Send BEGIN if we haven't yet */
+	if (txndata && !txndata->sent_begin_txn)
+		pgoutput_send_begin(ctx, txn);
+
+	OutputPluginPrepareWrite(ctx, true);
+	logicalrep_write_ddl(ctx->out, message_lsn, prefix, sz, message);
+	OutputPluginWrite(ctx, true);
+}
+
 /*
  * Return true if the data is associated with an origin and the user has
  * requested the changes that don't have an origin, false otherwise.
@@ -1813,6 +1954,7 @@ pgoutput_stream_abort(struct LogicalDecodingContext *ctx,
 	OutputPluginWrite(ctx, true);
 
 	cleanup_rel_sync_cache(toptxn->xid, false);
+	clean_txn_data(txn);
 }
 
 /*
@@ -1838,6 +1980,7 @@ pgoutput_stream_commit(struct LogicalDecodingContext *ctx,
 	OutputPluginWrite(ctx, true);
 
 	cleanup_rel_sync_cache(txn->xid, true);
+	clean_txn_data(txn);
 }
 
 /*
@@ -1856,6 +1999,8 @@ pgoutput_stream_prepare_txn(LogicalDecodingContext *ctx,
 	OutputPluginPrepareWrite(ctx, true);
 	logicalrep_write_stream_prepare(ctx->out, txn, prepare_lsn);
 	OutputPluginWrite(ctx, true);
+
+	clean_txn_data(txn);
 }
 
 /*
@@ -1972,8 +2117,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
 		entry->replicate_valid = false;
 		entry->schema_sent = false;
 		entry->streamed_txns = NIL;
-		entry->pubactions.pubinsert = entry->pubactions.pubupdate =
-			entry->pubactions.pubdelete = entry->pubactions.pubtruncate = false;
+		memset(&entry->pubactions, 0, sizeof(entry->pubactions));
 		entry->new_slot = NULL;
 		entry->old_slot = NULL;
 		memset(entry->exprstate, 0, sizeof(entry->exprstate));
@@ -2027,10 +2171,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
 		entry->streamed_txns = NIL;
 		bms_free(entry->columns);
 		entry->columns = NULL;
-		entry->pubactions.pubinsert = false;
-		entry->pubactions.pubupdate = false;
-		entry->pubactions.pubdelete = false;
-		entry->pubactions.pubtruncate = false;
+		memset(&entry->pubactions, 0, sizeof(entry->pubactions));
 
 		/*
 		 * Tuple slots cleanups. (Will be rebuilt later if needed).
@@ -2144,6 +2285,7 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
 				entry->pubactions.pubupdate |= pub->pubactions.pubupdate;
 				entry->pubactions.pubdelete |= pub->pubactions.pubdelete;
 				entry->pubactions.pubtruncate |= pub->pubactions.pubtruncate;
+				entry->pubactions.pubddl_table |= pub->pubactions.pubddl_table;
 
 				/*
 				 * We want to publish the changes as the top-most ancestor
diff --git a/src/backend/tcop/cmdtag.c b/src/backend/tcop/cmdtag.c
index 4bd713a0b4..a31b061eb7 100644
--- a/src/backend/tcop/cmdtag.c
+++ b/src/backend/tcop/cmdtag.c
@@ -26,10 +26,11 @@ typedef struct CommandTagBehavior
 	const bool	table_rewrite_ok;
 	const bool	display_rowcount;	/* should the number of rows affected be
 									 * shown in the command completion string */
+	const bool	ddl_replication_ok;
 } CommandTagBehavior;
 
-#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt) \
-	{ name, (uint8) (sizeof(name) - 1), evtrgok, rwrok, rowcnt },
+#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt, ddl_repl_ok) \
+	{ name, (uint8) (sizeof(name) - 1), evtrgok, rwrok, rowcnt, ddl_repl_ok },
 
 static const CommandTagBehavior tag_behavior[COMMAND_TAG_NEXTTAG] = {
 #include "tcop/cmdtaglist.h"
@@ -57,6 +58,21 @@ GetCommandTagNameAndLen(CommandTag commandTag, Size *len)
 	return tag_behavior[commandTag].name;
 }
 
+CommandTag *
+GetCommandTagsForDDLRepl(int *ncommands)
+{
+	CommandTag *ddlrepl_commands = palloc0(COMMAND_TAG_NEXTTAG * sizeof(CommandTag));
+	*ncommands = 0;
+
+	for(int i = 0; i < COMMAND_TAG_NEXTTAG; i++)
+	{
+		if (tag_behavior[i].ddl_replication_ok)
+			ddlrepl_commands[(*ncommands)++] = (CommandTag) i;
+	}
+
+	return ddlrepl_commands;
+}
+
 bool
 command_tag_display_rowcount(CommandTag commandTag)
 {
@@ -75,6 +91,12 @@ command_tag_table_rewrite_ok(CommandTag commandTag)
 	return tag_behavior[commandTag].table_rewrite_ok;
 }
 
+bool
+command_tag_ddl_replication_ok(CommandTag commandTag)
+{
+	return tag_behavior[commandTag].ddl_replication_ok;
+}
+
 /*
  * Search CommandTag by name
  *
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 3aa6270a88..6c834ee39f 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -895,7 +895,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
 		case T_CreateEventTrigStmt:
 			/* no event triggers on event triggers */
-			CreateEventTrigger((CreateEventTrigStmt *) parsetree);
+			CreateEventTrigger((CreateEventTrigStmt *) parsetree, false);
 			break;
 
 		case T_AlterEventTrigStmt:
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 8a08463c2b..f3022f08c1 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5720,6 +5720,7 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
 		pubdesc->pubactions.pubupdate |= pubform->pubupdate;
 		pubdesc->pubactions.pubdelete |= pubform->pubdelete;
 		pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;
+		pubdesc->pubactions.pubddl_table |= pubform->pubddl_table;
 
 		/*
 		 * Check if all columns referenced in the filter expression are part
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 3af97a6039..fae8eadbf6 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -51,6 +51,7 @@
 #include "catalog/pg_largeobject_d.h"
 #include "catalog/pg_largeobject_metadata_d.h"
 #include "catalog/pg_proc_d.h"
+#include "catalog/pg_publication.h"
 #include "catalog/pg_subscription.h"
 #include "catalog/pg_trigger_d.h"
 #include "catalog/pg_type_d.h"
@@ -4047,6 +4048,7 @@ getPublications(Archive *fout, int *numPublications)
 	int			i_pubupdate;
 	int			i_pubdelete;
 	int			i_pubtruncate;
+	int			i_pubddl_table;
 	int			i_pubviaroot;
 	int			i,
 				ntups;
@@ -4062,23 +4064,29 @@ getPublications(Archive *fout, int *numPublications)
 	resetPQExpBuffer(query);
 
 	/* Get the publications. */
-	if (fout->remoteVersion >= 130000)
+	if (fout->remoteVersion >= 160000)
 		appendPQExpBufferStr(query,
 							 "SELECT p.tableoid, p.oid, p.pubname, "
 							 "p.pubowner, "
-							 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot "
+							 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubddl_table, p.pubviaroot "
+							 "FROM pg_publication p");
+	else if (fout->remoteVersion >= 130000)
+		appendPQExpBufferStr(query,
+							 "SELECT p.tableoid, p.oid, p.pubname, "
+							 "p.pubowner, "
+							 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false as p.pubddl_table, p.pubviaroot "
 							 "FROM pg_publication p");
 	else if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
 							 "SELECT p.tableoid, p.oid, p.pubname, "
 							 "p.pubowner, "
-							 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot "
+							 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false as p.pubddl_table, false AS pubviaroot "
 							 "FROM pg_publication p");
 	else
 		appendPQExpBufferStr(query,
 							 "SELECT p.tableoid, p.oid, p.pubname, "
 							 "p.pubowner, "
-							 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot "
+							 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false as p.pubddl_table, false AS pubviaroot "
 							 "FROM pg_publication p");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -4094,6 +4102,7 @@ getPublications(Archive *fout, int *numPublications)
 	i_pubupdate = PQfnumber(res, "pubupdate");
 	i_pubdelete = PQfnumber(res, "pubdelete");
 	i_pubtruncate = PQfnumber(res, "pubtruncate");
+	i_pubddl_table = PQfnumber(res, "pubddl_table");
 	i_pubviaroot = PQfnumber(res, "pubviaroot");
 
 	pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
@@ -4117,6 +4126,8 @@ getPublications(Archive *fout, int *numPublications)
 			(strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
 		pubinfo[i].pubtruncate =
 			(strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
+		pubinfo[i].pubddl_table =
+			(strcmp(PQgetvalue(res, i, i_pubddl_table), "t") == 0);
 		pubinfo[i].pubviaroot =
 			(strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
 
@@ -4196,7 +4207,10 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
 		first = false;
 	}
 
-	appendPQExpBufferChar(query, '\'');
+	appendPQExpBufferStr(query, "'");
+
+	if (pubinfo->pubddl_table)
+		appendPQExpBufferStr(query, ", ddl = 'table'");
 
 	if (pubinfo->pubviaroot)
 		appendPQExpBufferStr(query, ", publish_via_partition_root = true");
@@ -7990,6 +8004,7 @@ getEventTriggers(Archive *fout, int *numEventTriggers)
 
 	query = createPQExpBuffer();
 
+	/* skip internally created event triggers by checking evtisinternal */
 	appendPQExpBufferStr(query,
 						 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
 						 "evtevent, evtowner, "
@@ -7998,6 +8013,7 @@ getEventTriggers(Archive *fout, int *numEventTriggers)
 						 " from unnest(evttags) as t(x)), ', ') as evttags, "
 						 "e.evtfoid::regproc as evtfname "
 						 "FROM pg_event_trigger e "
+						 "WHERE NOT e.evtisinternal "
 						 "ORDER BY e.oid");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index ed6ce41ad7..bd5f8fb669 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -620,6 +620,7 @@ typedef struct _PublicationInfo
 	bool		pubupdate;
 	bool		pubdelete;
 	bool		pubtruncate;
+	bool		pubddl_table;
 	bool		pubviaroot;
 } PublicationInfo;
 
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 387c5d3afb..a60e2dd167 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2872,7 +2872,7 @@ my %tests = (
 		create_order => 50,
 		create_sql => 'CREATE PUBLICATION pub2
 						 FOR ALL TABLES
-						 WITH (publish = \'\');',
+						 WITH (publish = \'\', ddl = \'\');',
 		regexp => qr/^
 			\QCREATE PUBLICATION pub2 FOR ALL TABLES WITH (publish = '');\E
 			/xm,
diff --git a/src/bin/pg_waldump/.gitignore b/src/bin/pg_waldump/.gitignore
index ec51f41c76..4df5660b51 100644
--- a/src/bin/pg_waldump/.gitignore
+++ b/src/bin/pg_waldump/.gitignore
@@ -10,6 +10,7 @@
 /gistdesc.c
 /hashdesc.c
 /heapdesc.c
+/logicalddlmsgdesc.c
 /logicalmsgdesc.c
 /mxactdesc.c
 /nbtdesc.c
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 6b8c17bb4c..daf1730252 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -26,6 +26,7 @@
 #include "commands/dbcommands_xlog.h"
 #include "commands/sequence.h"
 #include "commands/tablespace.h"
+#include "replication/ddlmessage.h"
 #include "replication/message.h"
 #include "replication/origin.h"
 #include "rmgrdesc.h"
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 9325a46b8f..750ea19dc1 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -6180,7 +6180,7 @@ listPublications(const char *pattern)
 	PQExpBufferData buf;
 	PGresult   *res;
 	printQueryOpt myopt = pset.popt;
-	static const bool translate_columns[] = {false, false, false, false, false, false, false, false};
+	static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
 
 	if (pset.sversion < 100000)
 	{
@@ -6207,6 +6207,10 @@ listPublications(const char *pattern)
 					  gettext_noop("Inserts"),
 					  gettext_noop("Updates"),
 					  gettext_noop("Deletes"));
+	if (pset.sversion >= 160000)
+		appendPQExpBuffer(&buf,
+						  ",\n  pubddl_table AS \"%s\"",
+						  gettext_noop("Table DDLs"));
 	if (pset.sversion >= 110000)
 		appendPQExpBuffer(&buf,
 						  ",\n  pubtruncate AS \"%s\"",
@@ -6305,6 +6309,7 @@ describePublications(const char *pattern)
 	PGresult   *res;
 	bool		has_pubtruncate;
 	bool		has_pubviaroot;
+	bool		has_pubddl;
 
 	PQExpBufferData title;
 	printTableContent cont;
@@ -6319,6 +6324,7 @@ describePublications(const char *pattern)
 		return true;
 	}
 
+	has_pubddl =  (pset.sversion >= 160000);
 	has_pubtruncate = (pset.sversion >= 110000);
 	has_pubviaroot = (pset.sversion >= 130000);
 
@@ -6328,6 +6334,9 @@ describePublications(const char *pattern)
 					  "SELECT oid, pubname,\n"
 					  "  pg_catalog.pg_get_userbyid(pubowner) AS owner,\n"
 					  "  puballtables, pubinsert, pubupdate, pubdelete");
+	if (has_pubddl)
+		appendPQExpBufferStr(&buf,
+							 ", pubddl_table");
 	if (has_pubtruncate)
 		appendPQExpBufferStr(&buf,
 							 ", pubtruncate");
@@ -6381,6 +6390,8 @@ describePublications(const char *pattern)
 		bool		puballtables = strcmp(PQgetvalue(res, i, 3), "t") == 0;
 		printTableOpt myopt = pset.popt.topt;
 
+		if (has_pubddl)
+			ncols++;
 		if (has_pubtruncate)
 			ncols++;
 		if (has_pubviaroot)
@@ -6395,6 +6406,8 @@ describePublications(const char *pattern)
 		printTableAddHeader(&cont, gettext_noop("Inserts"), true, align);
 		printTableAddHeader(&cont, gettext_noop("Updates"), true, align);
 		printTableAddHeader(&cont, gettext_noop("Deletes"), true, align);
+		if (has_pubddl)
+			printTableAddHeader(&cont, gettext_noop("Table DDLs"), true, align);
 		if (has_pubtruncate)
 			printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
 		if (has_pubviaroot)
@@ -6405,10 +6418,12 @@ describePublications(const char *pattern)
 		printTableAddCell(&cont, PQgetvalue(res, i, 4), false, false);
 		printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
 		printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
-		if (has_pubtruncate)
+		if (has_pubddl)
 			printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
-		if (has_pubviaroot)
+		if (has_pubtruncate)
 			printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
+		if (has_pubviaroot)
+			printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
 
 		if (!puballtables)
 		{
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 463bcb67c5..abcbe97593 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -47,3 +47,4 @@ PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_i
 PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL)
 PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL)
 PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, logicalmsg_decode)
+PG_RMGR(RM_LOGICALDDLMSG_ID, "LogicalDDLMessage", logicalddlmsg_redo, logicalddlmsg_desc, logicalddlmsg_identify, NULL, NULL, NULL, logicalddl_decode)
diff --git a/src/include/catalog/pg_event_trigger.h b/src/include/catalog/pg_event_trigger.h
index e30550f6e1..1dd25edfa4 100644
--- a/src/include/catalog/pg_event_trigger.h
+++ b/src/include/catalog/pg_event_trigger.h
@@ -36,7 +36,7 @@ CATALOG(pg_event_trigger,3466,EventTriggerRelationId)
 												 * called */
 	char		evtenabled;		/* trigger's firing configuration WRT
 								 * session_replication_role */
-
+	bool		evtisinternal;	/* trigger is system-generated */
 #ifdef CATALOG_VARLEN
 	text		evttags[1];		/* command TAGs this event trigger targets */
 #endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 76ff23b779..014116ff9e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11145,6 +11145,14 @@
   proname => 'pg_logical_emit_message', provolatile => 'v', proparallel => 'u',
   prorettype => 'pg_lsn', proargtypes => 'bool text bytea',
   prosrc => 'pg_logical_emit_message_bytea' },
+{ oid => '3813', descr => 'emit a textual logical decoding message',
+  proname => 'pg_logical_emit_ddl_message', provolatile => 'v', proparallel => 'u',
+  prorettype => 'pg_lsn', proargtypes => 'bool text text',
+  prosrc => 'pg_logical_emit_ddl_message_text' },
+{ oid => '3814', descr => 'emit a binary logical decoding message',
+  proname => 'pg_logical_emit_ddl_message', provolatile => 'v', proparallel => 'u',
+  prorettype => 'pg_lsn', proargtypes => 'text regclass int4 text',
+  prosrc => 'pg_logical_emit_ddl_message_bytea' },
 
 # event triggers
 { oid => '3566', descr => 'list objects dropped by the current command',
@@ -12049,5 +12057,17 @@
 { oid => '4643', descr => 'expand JSON format DDL to a plain text DDL command',
   proname => 'ddl_deparse_expand_command', prorettype => 'text',
   proargtypes => 'text', prosrc => 'ddl_deparse_expand_command' },
+{ oid => '4644', descr => 'trigger for ddl command deparse end',
+  proname => 'publication_deparse_ddl_command_end', prorettype => 'event_trigger',
+  proargtypes => '', prosrc => 'publication_deparse_ddl_command_end' },
+{ oid => '4645', descr => 'trigger for ddl command deparse start',
+  proname => 'publication_deparse_ddl_command_start', prorettype => 'event_trigger',
+  proargtypes => '', prosrc => 'publication_deparse_ddl_command_start' },
+{ oid => '4646', descr => 'trigger for ddl command deparse table rewrite',
+  proname => 'publication_deparse_table_rewrite', prorettype => 'event_trigger',
+  proargtypes => '', prosrc => 'publication_deparse_table_rewrite' },
+{ oid => '4647', descr => 'trigger for ddl command deparse table init',
+  proname => 'publication_deparse_table_init_write', prorettype => 'event_trigger',
+  proargtypes => '', prosrc => 'publication_deparse_table_init_write' },
 
 ]
diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h
index 6ecaa2a01e..0d8fe84bf9 100644
--- a/src/include/catalog/pg_publication.h
+++ b/src/include/catalog/pg_publication.h
@@ -18,8 +18,17 @@
 #define PG_PUBLICATION_H
 
 #include "catalog/genbki.h"
-#include "catalog/objectaddress.h"
 #include "catalog/pg_publication_d.h"
+#include "nodes/pg_list.h"
+
+/* Publication trigger events */
+#define PUB_TRIG_DDL_CMD_START "ddl_command_start"
+#define PUB_TRIG_DDL_CMD_END "ddl_command_end"
+#define PUB_TRIG_TBL_REWRITE "table_rewrite"
+#define PUB_TRIG_TBL_INIT_WRITE "table_init_write"
+
+/* Publication event trigger prefix */
+#define PUB_EVENT_TRIG_FORMAT "pg_deparse_trig_%s_%u"
 
 /* ----------------
  *		pg_publication definition.  cpp turns this into
@@ -54,6 +63,9 @@ CATALOG(pg_publication,6104,PublicationRelationId)
 
 	/* true if partition changes are published using root schema */
 	bool		pubviaroot;
+
+	/* true if table ddls are published */
+	bool		pubddl_table;
 } FormData_pg_publication;
 
 /* ----------------
@@ -72,6 +84,7 @@ typedef struct PublicationActions
 	bool		pubupdate;
 	bool		pubdelete;
 	bool		pubtruncate;
+	bool		pubddl_table;
 } PublicationActions;
 
 typedef struct PublicationDesc
@@ -103,13 +116,6 @@ typedef struct Publication
 	PublicationActions pubactions;
 } Publication;
 
-typedef struct PublicationRelInfo
-{
-	Relation	relation;
-	Node	   *whereClause;
-	List	   *columns;
-} PublicationRelInfo;
-
 extern Publication *GetPublication(Oid pubid);
 extern Publication *GetPublicationByName(const char *pubname, bool missing_ok);
 extern List *GetRelationPublications(Oid relid);
@@ -145,14 +151,6 @@ extern List *GetPubPartitionOptionRelations(List *result,
 extern Oid	GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
 											int *ancestor_level);
 
-extern bool is_publishable_relation(Relation rel);
 extern bool is_schema_publication(Oid pubid);
-extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
-											  bool if_not_exists);
-extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
-											bool if_not_exists);
-
-extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols,
-										   MemoryContext mcxt);
 
 #endif							/* PG_PUBLICATION_H */
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index cba4e72455..7914672cf1 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -80,7 +80,7 @@ typedef struct SQLDropObject
 #define CALLED_AS_EVENT_TRIGGER(fcinfo) \
 	((fcinfo)->context != NULL && IsA((fcinfo)->context, EventTriggerData))
 
-extern Oid	CreateEventTrigger(CreateEventTrigStmt *stmt);
+extern Oid	CreateEventTrigger(CreateEventTrigStmt *stmt, bool isinternal);
 extern Oid	get_event_trigger_oid(const char *trigname, bool missing_ok);
 
 extern Oid	AlterEventTrigger(AlterEventTrigStmt *stmt);
diff --git a/src/include/commands/publicationcmds.h b/src/include/commands/publicationcmds.h
index 70d5e3680a..a3fca9e64c 100644
--- a/src/include/commands/publicationcmds.h
+++ b/src/include/commands/publicationcmds.h
@@ -22,6 +22,13 @@
 /* Same as MAXNUMMESSAGES in sinvaladt.c */
 #define MAX_RELCACHE_INVAL_MSGS 4096
 
+typedef struct PublicationRelInfo
+{
+	Relation	relation;
+	Node	   *whereClause;
+	List	   *columns;
+} PublicationRelInfo;
+
 extern ObjectAddress CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt);
 extern void AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt);
 extern void RemovePublicationById(Oid pubid);
@@ -35,5 +42,12 @@ extern bool pub_rf_contains_invalid_column(Oid pubid, Relation relation,
 										   List *ancestors, bool pubviaroot);
 extern bool pub_collist_contains_invalid_column(Oid pubid, Relation relation,
 												List *ancestors, bool pubviaroot);
+extern Bitmapset *pub_collist_to_bitmapset(Bitmapset *columns, Datum pubcols,
+										   MemoryContext mcxt);
+extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *pri,
+											  bool if_not_exists);
+extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
+											bool if_not_exists);
+extern bool is_publishable_relation(Relation rel);
 
 #endif							/* PUBLICATIONCMDS_H */
diff --git a/src/include/replication/ddlmessage.h b/src/include/replication/ddlmessage.h
new file mode 100644
index 0000000000..1d833e5fcd
--- /dev/null
+++ b/src/include/replication/ddlmessage.h
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ * ddlmessage.h
+ *	   Exports from replication/logical/ddlmessage.c
+ *
+ * Copyright (c) 2022, PostgreSQL Global Development Group
+ *
+ * src/include/replication/ddlmessage.h
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_LOGICAL_DDL_MESSAGE_H
+#define PG_LOGICAL_DDL_MESSAGE_H
+
+#include "access/xlog.h"
+#include "access/xlogdefs.h"
+#include "access/xlogreader.h"
+#include "nodes/nodes.h"
+
+
+/*
+ * Support for keeping track of deparsed commands.
+ */
+typedef enum DeparsedCommandType
+{
+	DCT_ObjectCreate,
+	DCT_ObjectDrop,
+	DCT_SimpleCmd,
+	DCT_TableAlter,
+	DCT_TableDropEnd,
+	DCT_TableDropStart
+} DeparsedCommandType;
+
+/*
+ * Generic logical decoding DDL message wal record.
+ */
+typedef struct xl_logical_ddl_message
+{
+	Oid			dbId;			/* database Oid emitted from */
+	Size		prefix_size;	/* length of prefix including null terminator */
+	Oid			relid;			/* id of the table */
+	DeparsedCommandType cmdtype;	/* type of sql command */
+	Size		message_size;	/* size of the message */
+
+	/*
+	 * payload, including null-terminated prefix of length prefix_size
+	 */
+	char		message[FLEXIBLE_ARRAY_MEMBER];
+} xl_logical_ddl_message;
+
+#define SizeOfLogicalDDLMessage	(offsetof(xl_logical_ddl_message, message))
+
+extern XLogRecPtr LogLogicalDDLMessage(const char *prefix, Oid relid, DeparsedCommandType cmdtype,
+									   const char *ddl_message, size_t size);
+
+/* RMGR API*/
+#define XLOG_LOGICAL_DDL_MESSAGE	0x00
+void		logicalddlmsg_redo(XLogReaderState *record);
+void		logicalddlmsg_desc(StringInfo buf, XLogReaderState *record);
+const char *logicalddlmsg_identify(uint8 info);
+
+#endif
diff --git a/src/include/replication/decode.h b/src/include/replication/decode.h
index 14fa921ab4..c9ac708d32 100644
--- a/src/include/replication/decode.h
+++ b/src/include/replication/decode.h
@@ -27,6 +27,7 @@ extern void heap2_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
 extern void xact_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
 extern void standby_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
 extern void logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
+extern void logicalddl_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf);
 
 extern void LogicalDecodingProcessRecord(LogicalDecodingContext *ctx,
 										 XLogReaderState *record);
diff --git a/src/include/replication/logicalproto.h b/src/include/replication/logicalproto.h
index 0ea2df5088..5fb3baaeb1 100644
--- a/src/include/replication/logicalproto.h
+++ b/src/include/replication/logicalproto.h
@@ -66,6 +66,7 @@ typedef enum LogicalRepMsgType
 	LOGICAL_REP_MSG_RELATION = 'R',
 	LOGICAL_REP_MSG_TYPE = 'Y',
 	LOGICAL_REP_MSG_MESSAGE = 'M',
+	LOGICAL_REP_MSG_DDL = 'L',
 	LOGICAL_REP_MSG_BEGIN_PREPARE = 'b',
 	LOGICAL_REP_MSG_PREPARE = 'P',
 	LOGICAL_REP_MSG_COMMIT_PREPARED = 'K',
@@ -246,6 +247,9 @@ extern List *logicalrep_read_truncate(StringInfo in,
 									  bool *cascade, bool *restart_seqs);
 extern void logicalrep_write_message(StringInfo out, TransactionId xid, XLogRecPtr lsn,
 									 bool transactional, const char *prefix, Size sz, const char *message);
+extern void logicalrep_write_ddl(StringInfo out, XLogRecPtr lsn,
+										const char *prefix, Size sz, const char *message);
+extern char *logicalrep_read_ddl(StringInfo in, XLogRecPtr *lsn, const char **prefix, Size *sz);
 extern void logicalrep_write_rel(StringInfo out, TransactionId xid,
 								 Relation rel, Bitmapset *columns);
 extern LogicalRepRelation *logicalrep_read_rel(StringInfo in);
diff --git a/src/include/replication/output_plugin.h b/src/include/replication/output_plugin.h
index 3ac6729386..2ad96d374a 100644
--- a/src/include/replication/output_plugin.h
+++ b/src/include/replication/output_plugin.h
@@ -90,6 +90,18 @@ typedef void (*LogicalDecodeMessageCB) (struct LogicalDecodingContext *ctx,
 										Size message_size,
 										const char *message);
 
+/*
+ * Callback for the logical decoding DDL messages.
+ */
+typedef void (*LogicalDecodeDDLMessageCB) (struct LogicalDecodingContext *ctx,
+										   ReorderBufferTXN *txn,
+										   XLogRecPtr message_lsn,
+										   const char *prefix,
+										   Oid relid,
+										   DeparsedCommandType cmdtype,
+										   Size message_size,
+										   const char *message);
+
 /*
  * Filter changes by origin.
  */
@@ -201,6 +213,19 @@ typedef void (*LogicalDecodeStreamMessageCB) (struct LogicalDecodingContext *ctx
 											  Size message_size,
 											  const char *message);
 
+/*
+ * Callback for streaming logical decoding DDL messages from in-progress
+ * transactions.
+ */
+typedef void (*LogicalDecodeStreamDDLMessageCB) (struct LogicalDecodingContext *ctx,
+												 ReorderBufferTXN *txn,
+												 XLogRecPtr message_lsn,
+												 const char *prefix,
+												 Oid relid,
+												 DeparsedCommandType cmdtype,
+												 Size message_size,
+												 const char *message);
+
 /*
  * Callback for streaming truncates from in-progress transactions.
  */
@@ -221,6 +246,7 @@ typedef struct OutputPluginCallbacks
 	LogicalDecodeTruncateCB truncate_cb;
 	LogicalDecodeCommitCB commit_cb;
 	LogicalDecodeMessageCB message_cb;
+	LogicalDecodeDDLMessageCB ddl_cb;
 	LogicalDecodeFilterByOriginCB filter_by_origin_cb;
 	LogicalDecodeShutdownCB shutdown_cb;
 
@@ -239,6 +265,7 @@ typedef struct OutputPluginCallbacks
 	LogicalDecodeStreamCommitCB stream_commit_cb;
 	LogicalDecodeStreamChangeCB stream_change_cb;
 	LogicalDecodeStreamMessageCB stream_message_cb;
+	LogicalDecodeStreamDDLMessageCB stream_ddl_cb;
 	LogicalDecodeStreamTruncateCB stream_truncate_cb;
 } OutputPluginCallbacks;
 
diff --git a/src/include/replication/pgoutput.h b/src/include/replication/pgoutput.h
index b4a8015403..a2cf99b4e4 100644
--- a/src/include/replication/pgoutput.h
+++ b/src/include/replication/pgoutput.h
@@ -25,6 +25,7 @@ typedef struct PGOutputData
 	uint32		protocol_version;
 	List	   *publication_names;
 	List	   *publications;
+	List	   *deleted_relids;
 	bool		binary;
 	char		streaming;
 	bool		messages;
diff --git a/src/include/replication/reorderbuffer.h b/src/include/replication/reorderbuffer.h
index 1b9db22acb..2b37c96c1f 100644
--- a/src/include/replication/reorderbuffer.h
+++ b/src/include/replication/reorderbuffer.h
@@ -11,6 +11,8 @@
 
 #include "access/htup_details.h"
 #include "lib/ilist.h"
+#include "nodes/nodes.h"
+#include "replication/ddlmessage.h"
 #include "storage/sinval.h"
 #include "utils/hsearch.h"
 #include "utils/relcache.h"
@@ -66,6 +68,7 @@ typedef enum ReorderBufferChangeType
 	REORDER_BUFFER_CHANGE_UPDATE,
 	REORDER_BUFFER_CHANGE_DELETE,
 	REORDER_BUFFER_CHANGE_MESSAGE,
+	REORDER_BUFFER_CHANGE_DDL,
 	REORDER_BUFFER_CHANGE_INVALIDATION,
 	REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT,
 	REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID,
@@ -139,6 +142,16 @@ typedef struct ReorderBufferChange
 			char	   *message;
 		}			msg;
 
+		/* DDL */
+		struct
+		{
+			char	   *prefix;
+			Size		message_size;
+			char	   *message;
+			Oid			relid;
+			DeparsedCommandType cmdtype;
+		}			ddl;
+
 		/* New snapshot, set when action == *_INTERNAL_SNAPSHOT */
 		Snapshot	snapshot;
 
@@ -470,6 +483,16 @@ typedef void (*ReorderBufferMessageCB) (ReorderBuffer *rb,
 										const char *prefix, Size sz,
 										const char *message);
 
+/* DDL message callback signature */
+typedef void (*ReorderBufferDDLMessageCB) (ReorderBuffer *rb,
+										   ReorderBufferTXN *txn,
+										   XLogRecPtr message_lsn,
+										   const char *prefix,
+										   Oid relid,
+										   DeparsedCommandType cmdtype,
+										   Size sz,
+										   const char *message);
+
 /* begin prepare callback signature */
 typedef void (*ReorderBufferBeginPrepareCB) (ReorderBuffer *rb,
 											 ReorderBufferTXN *txn);
@@ -536,6 +559,17 @@ typedef void (*ReorderBufferStreamMessageCB) (
 											  const char *prefix, Size sz,
 											  const char *message);
 
+/* stream DDL message callback signature */
+typedef void (*ReorderBufferStreamDDLMessageCB) (
+												 ReorderBuffer *rb,
+												 ReorderBufferTXN *txn,
+												 XLogRecPtr message_lsn,
+												 const char *prefix,
+												 Oid relid,
+												 DeparsedCommandType cmdtype,
+												 Size sz,
+												 const char *message);
+
 /* stream truncate callback signature */
 typedef void (*ReorderBufferStreamTruncateCB) (
 											   ReorderBuffer *rb,
@@ -592,6 +626,7 @@ struct ReorderBuffer
 	ReorderBufferApplyTruncateCB apply_truncate;
 	ReorderBufferCommitCB commit;
 	ReorderBufferMessageCB message;
+	ReorderBufferDDLMessageCB ddl;
 
 	/*
 	 * Callbacks to be called when streaming a transaction at prepare time.
@@ -611,6 +646,7 @@ struct ReorderBuffer
 	ReorderBufferStreamCommitCB stream_commit;
 	ReorderBufferStreamChangeCB stream_change;
 	ReorderBufferStreamMessageCB stream_message;
+	ReorderBufferStreamDDLMessageCB stream_ddl;
 	ReorderBufferStreamTruncateCB stream_truncate;
 
 	/*
@@ -696,6 +732,9 @@ extern void ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
 									  Snapshot snap, XLogRecPtr lsn,
 									  bool transactional, const char *prefix,
 									  Size message_size, const char *message);
+extern void ReorderBufferQueueDDLMessage(ReorderBuffer *, TransactionId, XLogRecPtr lsn,
+										 const char *prefix, Size message_size,
+										 const char *message, Oid relid, DeparsedCommandType cmdtype);
 extern void ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
 								XLogRecPtr commit_lsn, XLogRecPtr end_lsn,
 								TimestampTz commit_time, RepOriginId origin_id, XLogRecPtr origin_lsn);
diff --git a/src/include/tcop/cmdtag.h b/src/include/tcop/cmdtag.h
index 1e7514dcff..32ccc156d0 100644
--- a/src/include/tcop/cmdtag.h
+++ b/src/include/tcop/cmdtag.h
@@ -16,7 +16,7 @@
 /* buffer size required for command completion tags */
 #define COMPLETION_TAG_BUFSIZE	64
 
-#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt) \
+#define PG_CMDTAG(tag, name, evtrgok, rwrok, rowcnt, ddl_repl_ok) \
 	tag,
 
 typedef enum CommandTag
@@ -53,9 +53,11 @@ CopyQueryCompletion(QueryCompletion *dst, const QueryCompletion *src)
 extern void InitializeQueryCompletion(QueryCompletion *qc);
 extern const char *GetCommandTagName(CommandTag commandTag);
 extern const char *GetCommandTagNameAndLen(CommandTag commandTag, Size *len);
+extern CommandTag *GetCommandTagsForDDLRepl(int *ncommands);
 extern bool command_tag_display_rowcount(CommandTag commandTag);
 extern bool command_tag_event_trigger_ok(CommandTag commandTag);
 extern bool command_tag_table_rewrite_ok(CommandTag commandTag);
+extern bool command_tag_ddl_replication_ok(CommandTag commandTag);
 extern CommandTag GetCommandTagEnum(const char *commandname);
 extern Size BuildQueryCompletionString(char *buff, const QueryCompletion *qc,
 									   bool nameonly);
diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
index e738ac1c09..7f44a150eb 100644
--- a/src/include/tcop/cmdtaglist.h
+++ b/src/include/tcop/cmdtaglist.h
@@ -23,196 +23,196 @@
  * textual name, so that we can bsearch on it; see GetCommandTagEnum().
  */
 
-/* symbol name, textual name, event_trigger_ok, table_rewrite_ok, rowcount */
-PG_CMDTAG(CMDTAG_UNKNOWN, "???", false, false, false)
-PG_CMDTAG(CMDTAG_ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_AGGREGATE, "ALTER AGGREGATE", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_CAST, "ALTER CAST", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_COLLATION, "ALTER COLLATION", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_CONSTRAINT, "ALTER CONSTRAINT", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_CONVERSION, "ALTER CONVERSION", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_DATABASE, "ALTER DATABASE", false, false, false)
-PG_CMDTAG(CMDTAG_ALTER_DEFAULT_PRIVILEGES, "ALTER DEFAULT PRIVILEGES", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_DOMAIN, "ALTER DOMAIN", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_EVENT_TRIGGER, "ALTER EVENT TRIGGER", false, false, false)
-PG_CMDTAG(CMDTAG_ALTER_EXTENSION, "ALTER EXTENSION", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_FOREIGN_DATA_WRAPPER, "ALTER FOREIGN DATA WRAPPER", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_FOREIGN_TABLE, "ALTER FOREIGN TABLE", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_FUNCTION, "ALTER FUNCTION", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_INDEX, "ALTER INDEX", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_LANGUAGE, "ALTER LANGUAGE", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_LARGE_OBJECT, "ALTER LARGE OBJECT", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_MATERIALIZED_VIEW, "ALTER MATERIALIZED VIEW", true, true, false)
-PG_CMDTAG(CMDTAG_ALTER_OPERATOR, "ALTER OPERATOR", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_OPERATOR_CLASS, "ALTER OPERATOR CLASS", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_OPERATOR_FAMILY, "ALTER OPERATOR FAMILY", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_POLICY, "ALTER POLICY", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_PROCEDURE, "ALTER PROCEDURE", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_PUBLICATION, "ALTER PUBLICATION", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_ROLE, "ALTER ROLE", false, false, false)
-PG_CMDTAG(CMDTAG_ALTER_ROUTINE, "ALTER ROUTINE", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_RULE, "ALTER RULE", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_SCHEMA, "ALTER SCHEMA", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_SEQUENCE, "ALTER SEQUENCE", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_SERVER, "ALTER SERVER", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_STATISTICS, "ALTER STATISTICS", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_SUBSCRIPTION, "ALTER SUBSCRIPTION", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_SYSTEM, "ALTER SYSTEM", false, false, false)
-PG_CMDTAG(CMDTAG_ALTER_TABLE, "ALTER TABLE", true, true, false)
-PG_CMDTAG(CMDTAG_ALTER_TABLESPACE, "ALTER TABLESPACE", false, false, false)
-PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION, "ALTER TEXT SEARCH CONFIGURATION", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY, "ALTER TEXT SEARCH DICTIONARY", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_PARSER, "ALTER TEXT SEARCH PARSER", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_TEMPLATE, "ALTER TEXT SEARCH TEMPLATE", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_TRANSFORM, "ALTER TRANSFORM", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_TRIGGER, "ALTER TRIGGER", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_TYPE, "ALTER TYPE", true, true, false)
-PG_CMDTAG(CMDTAG_ALTER_USER_MAPPING, "ALTER USER MAPPING", true, false, false)
-PG_CMDTAG(CMDTAG_ALTER_VIEW, "ALTER VIEW", true, false, false)
-PG_CMDTAG(CMDTAG_ANALYZE, "ANALYZE", false, false, false)
-PG_CMDTAG(CMDTAG_BEGIN, "BEGIN", false, false, false)
-PG_CMDTAG(CMDTAG_CALL, "CALL", false, false, false)
-PG_CMDTAG(CMDTAG_CHECKPOINT, "CHECKPOINT", false, false, false)
-PG_CMDTAG(CMDTAG_CLOSE, "CLOSE", false, false, false)
-PG_CMDTAG(CMDTAG_CLOSE_CURSOR, "CLOSE CURSOR", false, false, false)
-PG_CMDTAG(CMDTAG_CLOSE_CURSOR_ALL, "CLOSE CURSOR ALL", false, false, false)
-PG_CMDTAG(CMDTAG_CLUSTER, "CLUSTER", false, false, false)
-PG_CMDTAG(CMDTAG_COMMENT, "COMMENT", true, false, false)
-PG_CMDTAG(CMDTAG_COMMIT, "COMMIT", false, false, false)
-PG_CMDTAG(CMDTAG_COMMIT_PREPARED, "COMMIT PREPARED", false, false, false)
-PG_CMDTAG(CMDTAG_COPY, "COPY", false, false, true)
-PG_CMDTAG(CMDTAG_COPY_FROM, "COPY FROM", false, false, false)
-PG_CMDTAG(CMDTAG_CREATE_ACCESS_METHOD, "CREATE ACCESS METHOD", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_AGGREGATE, "CREATE AGGREGATE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_CAST, "CREATE CAST", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_COLLATION, "CREATE COLLATION", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_CONSTRAINT, "CREATE CONSTRAINT", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_CONVERSION, "CREATE CONVERSION", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_DATABASE, "CREATE DATABASE", false, false, false)
-PG_CMDTAG(CMDTAG_CREATE_DOMAIN, "CREATE DOMAIN", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_EVENT_TRIGGER, "CREATE EVENT TRIGGER", false, false, false)
-PG_CMDTAG(CMDTAG_CREATE_EXTENSION, "CREATE EXTENSION", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_FOREIGN_DATA_WRAPPER, "CREATE FOREIGN DATA WRAPPER", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_FOREIGN_TABLE, "CREATE FOREIGN TABLE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_FUNCTION, "CREATE FUNCTION", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_INDEX, "CREATE INDEX", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_LANGUAGE, "CREATE LANGUAGE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_MATERIALIZED_VIEW, "CREATE MATERIALIZED VIEW", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_OPERATOR, "CREATE OPERATOR", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_OPERATOR_CLASS, "CREATE OPERATOR CLASS", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_OPERATOR_FAMILY, "CREATE OPERATOR FAMILY", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_POLICY, "CREATE POLICY", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_PROCEDURE, "CREATE PROCEDURE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_PUBLICATION, "CREATE PUBLICATION", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_ROLE, "CREATE ROLE", false, false, false)
-PG_CMDTAG(CMDTAG_CREATE_ROUTINE, "CREATE ROUTINE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_RULE, "CREATE RULE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_SCHEMA, "CREATE SCHEMA", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_SEQUENCE, "CREATE SEQUENCE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_SERVER, "CREATE SERVER", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_STATISTICS, "CREATE STATISTICS", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_SUBSCRIPTION, "CREATE SUBSCRIPTION", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TABLE, "CREATE TABLE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TABLE_AS, "CREATE TABLE AS", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TABLESPACE, "CREATE TABLESPACE", false, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_CONFIGURATION, "CREATE TEXT SEARCH CONFIGURATION", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_DICTIONARY, "CREATE TEXT SEARCH DICTIONARY", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_PARSER, "CREATE TEXT SEARCH PARSER", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_TEMPLATE, "CREATE TEXT SEARCH TEMPLATE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TRANSFORM, "CREATE TRANSFORM", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TRIGGER, "CREATE TRIGGER", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_TYPE, "CREATE TYPE", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_USER_MAPPING, "CREATE USER MAPPING", true, false, false)
-PG_CMDTAG(CMDTAG_CREATE_VIEW, "CREATE VIEW", true, false, false)
-PG_CMDTAG(CMDTAG_DEALLOCATE, "DEALLOCATE", false, false, false)
-PG_CMDTAG(CMDTAG_DEALLOCATE_ALL, "DEALLOCATE ALL", false, false, false)
-PG_CMDTAG(CMDTAG_DECLARE_CURSOR, "DECLARE CURSOR", false, false, false)
-PG_CMDTAG(CMDTAG_DELETE, "DELETE", false, false, true)
-PG_CMDTAG(CMDTAG_DISCARD, "DISCARD", false, false, false)
-PG_CMDTAG(CMDTAG_DISCARD_ALL, "DISCARD ALL", false, false, false)
-PG_CMDTAG(CMDTAG_DISCARD_PLANS, "DISCARD PLANS", false, false, false)
-PG_CMDTAG(CMDTAG_DISCARD_SEQUENCES, "DISCARD SEQUENCES", false, false, false)
-PG_CMDTAG(CMDTAG_DISCARD_TEMP, "DISCARD TEMP", false, false, false)
-PG_CMDTAG(CMDTAG_DO, "DO", false, false, false)
-PG_CMDTAG(CMDTAG_DROP_ACCESS_METHOD, "DROP ACCESS METHOD", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_AGGREGATE, "DROP AGGREGATE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_CAST, "DROP CAST", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_COLLATION, "DROP COLLATION", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_CONSTRAINT, "DROP CONSTRAINT", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_CONVERSION, "DROP CONVERSION", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_DATABASE, "DROP DATABASE", false, false, false)
-PG_CMDTAG(CMDTAG_DROP_DOMAIN, "DROP DOMAIN", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_EVENT_TRIGGER, "DROP EVENT TRIGGER", false, false, false)
-PG_CMDTAG(CMDTAG_DROP_EXTENSION, "DROP EXTENSION", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_FOREIGN_DATA_WRAPPER, "DROP FOREIGN DATA WRAPPER", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_FOREIGN_TABLE, "DROP FOREIGN TABLE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_FUNCTION, "DROP FUNCTION", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_INDEX, "DROP INDEX", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_LANGUAGE, "DROP LANGUAGE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_MATERIALIZED_VIEW, "DROP MATERIALIZED VIEW", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_OPERATOR, "DROP OPERATOR", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_OPERATOR_CLASS, "DROP OPERATOR CLASS", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_OPERATOR_FAMILY, "DROP OPERATOR FAMILY", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_OWNED, "DROP OWNED", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_POLICY, "DROP POLICY", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_PROCEDURE, "DROP PROCEDURE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_PUBLICATION, "DROP PUBLICATION", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_ROLE, "DROP ROLE", false, false, false)
-PG_CMDTAG(CMDTAG_DROP_ROUTINE, "DROP ROUTINE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_RULE, "DROP RULE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_SCHEMA, "DROP SCHEMA", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_SEQUENCE, "DROP SEQUENCE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_SERVER, "DROP SERVER", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_STATISTICS, "DROP STATISTICS", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_SUBSCRIPTION, "DROP SUBSCRIPTION", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_TABLE, "DROP TABLE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_TABLESPACE, "DROP TABLESPACE", false, false, false)
-PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_CONFIGURATION, "DROP TEXT SEARCH CONFIGURATION", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_DICTIONARY, "DROP TEXT SEARCH DICTIONARY", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_PARSER, "DROP TEXT SEARCH PARSER", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_TEMPLATE, "DROP TEXT SEARCH TEMPLATE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_TRANSFORM, "DROP TRANSFORM", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_TRIGGER, "DROP TRIGGER", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_TYPE, "DROP TYPE", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_USER_MAPPING, "DROP USER MAPPING", true, false, false)
-PG_CMDTAG(CMDTAG_DROP_VIEW, "DROP VIEW", true, false, false)
-PG_CMDTAG(CMDTAG_EXECUTE, "EXECUTE", false, false, false)
-PG_CMDTAG(CMDTAG_EXPLAIN, "EXPLAIN", false, false, false)
-PG_CMDTAG(CMDTAG_FETCH, "FETCH", false, false, true)
-PG_CMDTAG(CMDTAG_GRANT, "GRANT", true, false, false)
-PG_CMDTAG(CMDTAG_GRANT_ROLE, "GRANT ROLE", false, false, false)
-PG_CMDTAG(CMDTAG_IMPORT_FOREIGN_SCHEMA, "IMPORT FOREIGN SCHEMA", true, false, false)
-PG_CMDTAG(CMDTAG_INSERT, "INSERT", false, false, true)
-PG_CMDTAG(CMDTAG_LISTEN, "LISTEN", false, false, false)
-PG_CMDTAG(CMDTAG_LOAD, "LOAD", false, false, false)
-PG_CMDTAG(CMDTAG_LOCK_TABLE, "LOCK TABLE", false, false, false)
-PG_CMDTAG(CMDTAG_MERGE, "MERGE", false, false, true)
-PG_CMDTAG(CMDTAG_MOVE, "MOVE", false, false, true)
-PG_CMDTAG(CMDTAG_NOTIFY, "NOTIFY", false, false, false)
-PG_CMDTAG(CMDTAG_PREPARE, "PREPARE", false, false, false)
-PG_CMDTAG(CMDTAG_PREPARE_TRANSACTION, "PREPARE TRANSACTION", false, false, false)
-PG_CMDTAG(CMDTAG_REASSIGN_OWNED, "REASSIGN OWNED", false, false, false)
-PG_CMDTAG(CMDTAG_REFRESH_MATERIALIZED_VIEW, "REFRESH MATERIALIZED VIEW", true, false, false)
-PG_CMDTAG(CMDTAG_REINDEX, "REINDEX", false, false, false)
-PG_CMDTAG(CMDTAG_RELEASE, "RELEASE", false, false, false)
-PG_CMDTAG(CMDTAG_RESET, "RESET", false, false, false)
-PG_CMDTAG(CMDTAG_REVOKE, "REVOKE", true, false, false)
-PG_CMDTAG(CMDTAG_REVOKE_ROLE, "REVOKE ROLE", false, false, false)
-PG_CMDTAG(CMDTAG_ROLLBACK, "ROLLBACK", false, false, false)
-PG_CMDTAG(CMDTAG_ROLLBACK_PREPARED, "ROLLBACK PREPARED", false, false, false)
-PG_CMDTAG(CMDTAG_SAVEPOINT, "SAVEPOINT", false, false, false)
-PG_CMDTAG(CMDTAG_SECURITY_LABEL, "SECURITY LABEL", true, false, false)
-PG_CMDTAG(CMDTAG_SELECT, "SELECT", false, false, true)
-PG_CMDTAG(CMDTAG_SELECT_FOR_KEY_SHARE, "SELECT FOR KEY SHARE", false, false, false)
-PG_CMDTAG(CMDTAG_SELECT_FOR_NO_KEY_UPDATE, "SELECT FOR NO KEY UPDATE", false, false, false)
-PG_CMDTAG(CMDTAG_SELECT_FOR_SHARE, "SELECT FOR SHARE", false, false, false)
-PG_CMDTAG(CMDTAG_SELECT_FOR_UPDATE, "SELECT FOR UPDATE", false, false, false)
-PG_CMDTAG(CMDTAG_SELECT_INTO, "SELECT INTO", true, false, false)
-PG_CMDTAG(CMDTAG_SET, "SET", false, false, false)
-PG_CMDTAG(CMDTAG_SET_CONSTRAINTS, "SET CONSTRAINTS", false, false, false)
-PG_CMDTAG(CMDTAG_SHOW, "SHOW", false, false, false)
-PG_CMDTAG(CMDTAG_START_TRANSACTION, "START TRANSACTION", false, false, false)
-PG_CMDTAG(CMDTAG_TRUNCATE_TABLE, "TRUNCATE TABLE", false, false, false)
-PG_CMDTAG(CMDTAG_UNLISTEN, "UNLISTEN", false, false, false)
-PG_CMDTAG(CMDTAG_UPDATE, "UPDATE", false, false, true)
-PG_CMDTAG(CMDTAG_VACUUM, "VACUUM", false, false, false)
+/* symbol name, textual name, event_trigger_ok, table_rewrite_ok, rowcount, ddl_repl_ok */
+PG_CMDTAG(CMDTAG_UNKNOWN, "???", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_ACCESS_METHOD, "ALTER ACCESS METHOD", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_AGGREGATE, "ALTER AGGREGATE", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_CAST, "ALTER CAST", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_COLLATION, "ALTER COLLATION", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_CONSTRAINT, "ALTER CONSTRAINT", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_CONVERSION, "ALTER CONVERSION", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_DATABASE, "ALTER DATABASE", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_DEFAULT_PRIVILEGES, "ALTER DEFAULT PRIVILEGES", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_DOMAIN, "ALTER DOMAIN", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_EVENT_TRIGGER, "ALTER EVENT TRIGGER", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_EXTENSION, "ALTER EXTENSION", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_FOREIGN_DATA_WRAPPER, "ALTER FOREIGN DATA WRAPPER", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_FOREIGN_TABLE, "ALTER FOREIGN TABLE", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_FUNCTION, "ALTER FUNCTION", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_INDEX, "ALTER INDEX", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_LANGUAGE, "ALTER LANGUAGE", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_LARGE_OBJECT, "ALTER LARGE OBJECT", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_MATERIALIZED_VIEW, "ALTER MATERIALIZED VIEW", true, true, false, true)
+PG_CMDTAG(CMDTAG_ALTER_OPERATOR, "ALTER OPERATOR", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_OPERATOR_CLASS, "ALTER OPERATOR CLASS", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_OPERATOR_FAMILY, "ALTER OPERATOR FAMILY", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_POLICY, "ALTER POLICY", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_PROCEDURE, "ALTER PROCEDURE", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_PUBLICATION, "ALTER PUBLICATION", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_ROLE, "ALTER ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_ROUTINE, "ALTER ROUTINE", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_RULE, "ALTER RULE", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_SCHEMA, "ALTER SCHEMA", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_SEQUENCE, "ALTER SEQUENCE", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_SERVER, "ALTER SERVER", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_STATISTICS, "ALTER STATISTICS", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_SUBSCRIPTION, "ALTER SUBSCRIPTION", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_SYSTEM, "ALTER SYSTEM", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TABLE, "ALTER TABLE", true, true, false, true)
+PG_CMDTAG(CMDTAG_ALTER_TABLESPACE, "ALTER TABLESPACE", false, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_CONFIGURATION, "ALTER TEXT SEARCH CONFIGURATION", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_DICTIONARY, "ALTER TEXT SEARCH DICTIONARY", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_PARSER, "ALTER TEXT SEARCH PARSER", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_TEXT_SEARCH_TEMPLATE, "ALTER TEXT SEARCH TEMPLATE", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_TRANSFORM, "ALTER TRANSFORM", true, false, false, false)
+PG_CMDTAG(CMDTAG_ALTER_TRIGGER, "ALTER TRIGGER", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_TYPE, "ALTER TYPE", true, true, false, true)
+PG_CMDTAG(CMDTAG_ALTER_USER_MAPPING, "ALTER USER MAPPING", true, false, false, true)
+PG_CMDTAG(CMDTAG_ALTER_VIEW, "ALTER VIEW", true, false, false, true)
+PG_CMDTAG(CMDTAG_ANALYZE, "ANALYZE", false, false, false, false)
+PG_CMDTAG(CMDTAG_BEGIN, "BEGIN", false, false, false, false)
+PG_CMDTAG(CMDTAG_CALL, "CALL", false, false, false, false)
+PG_CMDTAG(CMDTAG_CHECKPOINT, "CHECKPOINT", false, false, false, false)
+PG_CMDTAG(CMDTAG_CLOSE, "CLOSE", false, false, false, false)
+PG_CMDTAG(CMDTAG_CLOSE_CURSOR, "CLOSE CURSOR", false, false, false, false)
+PG_CMDTAG(CMDTAG_CLOSE_CURSOR_ALL, "CLOSE CURSOR ALL", false, false, false, false)
+PG_CMDTAG(CMDTAG_CLUSTER, "CLUSTER", false, false, false, false)
+PG_CMDTAG(CMDTAG_COMMENT, "COMMENT", true, false, false, true)
+PG_CMDTAG(CMDTAG_COMMIT, "COMMIT", false, false, false, false)
+PG_CMDTAG(CMDTAG_COMMIT_PREPARED, "COMMIT PREPARED", false, false, false, false)
+PG_CMDTAG(CMDTAG_COPY, "COPY", false, false, true, false)
+PG_CMDTAG(CMDTAG_COPY_FROM, "COPY FROM", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_ACCESS_METHOD, "CREATE ACCESS METHOD", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_AGGREGATE, "CREATE AGGREGATE", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_CAST, "CREATE CAST", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_COLLATION, "CREATE COLLATION", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_CONSTRAINT, "CREATE CONSTRAINT", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_CONVERSION, "CREATE CONVERSION", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_DATABASE, "CREATE DATABASE", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_DOMAIN, "CREATE DOMAIN", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_EVENT_TRIGGER, "CREATE EVENT TRIGGER", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_EXTENSION, "CREATE EXTENSION", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_FOREIGN_DATA_WRAPPER, "CREATE FOREIGN DATA WRAPPER", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_FOREIGN_TABLE, "CREATE FOREIGN TABLE", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_FUNCTION, "CREATE FUNCTION", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_INDEX, "CREATE INDEX", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_LANGUAGE, "CREATE LANGUAGE", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_MATERIALIZED_VIEW, "CREATE MATERIALIZED VIEW", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_OPERATOR, "CREATE OPERATOR", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_OPERATOR_CLASS, "CREATE OPERATOR CLASS", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_OPERATOR_FAMILY, "CREATE OPERATOR FAMILY", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_POLICY, "CREATE POLICY", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_PROCEDURE, "CREATE PROCEDURE", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_PUBLICATION, "CREATE PUBLICATION", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_ROLE, "CREATE ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_ROUTINE, "CREATE ROUTINE", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_RULE, "CREATE RULE", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_SCHEMA, "CREATE SCHEMA", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_SEQUENCE, "CREATE SEQUENCE", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_SERVER, "CREATE SERVER", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_STATISTICS, "CREATE STATISTICS", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_SUBSCRIPTION, "CREATE SUBSCRIPTION", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TABLE, "CREATE TABLE", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_TABLE_AS, "CREATE TABLE AS", true, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TABLESPACE, "CREATE TABLESPACE", false, false, false, false)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_CONFIGURATION, "CREATE TEXT SEARCH CONFIGURATION", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_DICTIONARY, "CREATE TEXT SEARCH DICTIONARY", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_PARSER, "CREATE TEXT SEARCH PARSER", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_TEXT_SEARCH_TEMPLATE, "CREATE TEXT SEARCH TEMPLATE", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_TRANSFORM, "CREATE TRANSFORM", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_TRIGGER, "CREATE TRIGGER", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_TYPE, "CREATE TYPE", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_USER_MAPPING, "CREATE USER MAPPING", true, false, false, true)
+PG_CMDTAG(CMDTAG_CREATE_VIEW, "CREATE VIEW", true, false, false, true)
+PG_CMDTAG(CMDTAG_DEALLOCATE, "DEALLOCATE", false, false, false, false)
+PG_CMDTAG(CMDTAG_DEALLOCATE_ALL, "DEALLOCATE ALL", false, false, false, false)
+PG_CMDTAG(CMDTAG_DECLARE_CURSOR, "DECLARE CURSOR", false, false, false, false)
+PG_CMDTAG(CMDTAG_DELETE, "DELETE", false, false, true, false)
+PG_CMDTAG(CMDTAG_DISCARD, "DISCARD", false, false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_ALL, "DISCARD ALL", false, false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_PLANS, "DISCARD PLANS", false, false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_SEQUENCES, "DISCARD SEQUENCES", false, false, false, false)
+PG_CMDTAG(CMDTAG_DISCARD_TEMP, "DISCARD TEMP", false, false, false, false)
+PG_CMDTAG(CMDTAG_DO, "DO", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_ACCESS_METHOD, "DROP ACCESS METHOD", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_AGGREGATE, "DROP AGGREGATE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_CAST, "DROP CAST", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_COLLATION, "DROP COLLATION", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_CONSTRAINT, "DROP CONSTRAINT", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_CONVERSION, "DROP CONVERSION", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_DATABASE, "DROP DATABASE", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_DOMAIN, "DROP DOMAIN", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_EVENT_TRIGGER, "DROP EVENT TRIGGER", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_EXTENSION, "DROP EXTENSION", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_FOREIGN_DATA_WRAPPER, "DROP FOREIGN DATA WRAPPER", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_FOREIGN_TABLE, "DROP FOREIGN TABLE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_FUNCTION, "DROP FUNCTION", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_INDEX, "DROP INDEX", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_LANGUAGE, "DROP LANGUAGE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_MATERIALIZED_VIEW, "DROP MATERIALIZED VIEW", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_OPERATOR, "DROP OPERATOR", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_OPERATOR_CLASS, "DROP OPERATOR CLASS", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_OPERATOR_FAMILY, "DROP OPERATOR FAMILY", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_OWNED, "DROP OWNED", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_POLICY, "DROP POLICY", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_PROCEDURE, "DROP PROCEDURE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_PUBLICATION, "DROP PUBLICATION", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_ROLE, "DROP ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_ROUTINE, "DROP ROUTINE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_RULE, "DROP RULE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_SCHEMA, "DROP SCHEMA", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_SEQUENCE, "DROP SEQUENCE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_SERVER, "DROP SERVER", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_STATISTICS, "DROP STATISTICS", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_SUBSCRIPTION, "DROP SUBSCRIPTION", true, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TABLE, "DROP TABLE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_TABLESPACE, "DROP TABLESPACE", false, false, false, false)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_CONFIGURATION, "DROP TEXT SEARCH CONFIGURATION", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_DICTIONARY, "DROP TEXT SEARCH DICTIONARY", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_PARSER, "DROP TEXT SEARCH PARSER", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_TEXT_SEARCH_TEMPLATE, "DROP TEXT SEARCH TEMPLATE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_TRANSFORM, "DROP TRANSFORM", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_TRIGGER, "DROP TRIGGER", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_TYPE, "DROP TYPE", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_USER_MAPPING, "DROP USER MAPPING", true, false, false, true)
+PG_CMDTAG(CMDTAG_DROP_VIEW, "DROP VIEW", true, false, false, true)
+PG_CMDTAG(CMDTAG_EXECUTE, "EXECUTE", false, false, false, false)
+PG_CMDTAG(CMDTAG_EXPLAIN, "EXPLAIN", false, false, false, false)
+PG_CMDTAG(CMDTAG_FETCH, "FETCH", false, false, true, false)
+PG_CMDTAG(CMDTAG_GRANT, "GRANT", true, false, false, true)
+PG_CMDTAG(CMDTAG_GRANT_ROLE, "GRANT ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_IMPORT_FOREIGN_SCHEMA, "IMPORT FOREIGN SCHEMA", true, false, false, true)
+PG_CMDTAG(CMDTAG_INSERT, "INSERT", false, false, true, false)
+PG_CMDTAG(CMDTAG_LISTEN, "LISTEN", false, false, false, false)
+PG_CMDTAG(CMDTAG_LOAD, "LOAD", false, false, false, false)
+PG_CMDTAG(CMDTAG_LOCK_TABLE, "LOCK TABLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_MERGE, "MERGE", false, false, true, false)
+PG_CMDTAG(CMDTAG_MOVE, "MOVE", false, false, true, false)
+PG_CMDTAG(CMDTAG_NOTIFY, "NOTIFY", false, false, false, false)
+PG_CMDTAG(CMDTAG_PREPARE, "PREPARE", false, false, false, false)
+PG_CMDTAG(CMDTAG_PREPARE_TRANSACTION, "PREPARE TRANSACTION", false, false, false, false)
+PG_CMDTAG(CMDTAG_REASSIGN_OWNED, "REASSIGN OWNED", false, false, false, false)
+PG_CMDTAG(CMDTAG_REFRESH_MATERIALIZED_VIEW, "REFRESH MATERIALIZED VIEW", true, false, false, true)
+PG_CMDTAG(CMDTAG_REINDEX, "REINDEX", false, false, false, false)
+PG_CMDTAG(CMDTAG_RELEASE, "RELEASE", false, false, false, false)
+PG_CMDTAG(CMDTAG_RESET, "RESET", false, false, false, false)
+PG_CMDTAG(CMDTAG_REVOKE, "REVOKE", true, false, false, true)
+PG_CMDTAG(CMDTAG_REVOKE_ROLE, "REVOKE ROLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_ROLLBACK, "ROLLBACK", false, false, false, false)
+PG_CMDTAG(CMDTAG_ROLLBACK_PREPARED, "ROLLBACK PREPARED", false, false, false, false)
+PG_CMDTAG(CMDTAG_SAVEPOINT, "SAVEPOINT", false, false, false, false)
+PG_CMDTAG(CMDTAG_SECURITY_LABEL, "SECURITY LABEL", true, false, false, true)
+PG_CMDTAG(CMDTAG_SELECT, "SELECT", false, false, true, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_KEY_SHARE, "SELECT FOR KEY SHARE", false, false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_NO_KEY_UPDATE, "SELECT FOR NO KEY UPDATE", false, false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_SHARE, "SELECT FOR SHARE", false, false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_FOR_UPDATE, "SELECT FOR UPDATE", false, false, false, false)
+PG_CMDTAG(CMDTAG_SELECT_INTO, "SELECT INTO", true, false, false, false)
+PG_CMDTAG(CMDTAG_SET, "SET", false, false, false, false)
+PG_CMDTAG(CMDTAG_SET_CONSTRAINTS, "SET CONSTRAINTS", false, false, false, false)
+PG_CMDTAG(CMDTAG_SHOW, "SHOW", false, false, false, false)
+PG_CMDTAG(CMDTAG_START_TRANSACTION, "START TRANSACTION", false, false, false, false)
+PG_CMDTAG(CMDTAG_TRUNCATE_TABLE, "TRUNCATE TABLE", false, false, false, false)
+PG_CMDTAG(CMDTAG_UNLISTEN, "UNLISTEN", false, false, false, false)
+PG_CMDTAG(CMDTAG_UPDATE, "UPDATE", false, false, true, false)
+PG_CMDTAG(CMDTAG_VACUUM, "VACUUM", false, false, false, false)
diff --git a/src/include/tcop/ddldeparse.h b/src/include/tcop/ddldeparse.h
index a712060246..3185d49c8e 100644
--- a/src/include/tcop/ddldeparse.h
+++ b/src/include/tcop/ddldeparse.h
@@ -14,7 +14,15 @@
 
 #include "tcop/deparse_utility.h"
 
-extern char *deparse_utility_command(CollectedCommand *cmd, bool verbose_mode);
+/* Context info needed for deparsing ddl command */
+typedef struct
+{
+	bool		verbose_mode;
+	/* provolatile flag of the function contained in the command */
+	char		func_volatile;
+} ddl_deparse_context;
+
+extern char *deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context);
 extern char *deparse_ddl_json_to_string(char *jsonb);
 extern char *deparse_drop_command(const char *objidentity, const char *objecttype,
 								  Node *parsetree);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 1426a353cd..a656f5bd7c 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -17,10 +17,12 @@
 #include "access/tupdesc.h"
 #include "access/xlog.h"
 #include "catalog/catalog.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
 #include "catalog/pg_publication.h"
 #include "nodes/bitmapset.h"
+#include "nodes/lockoptions.h"
 #include "partitioning/partdefs.h"
 #include "rewrite/prs2lock.h"
 #include "storage/block.h"
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 8f93028363..008f610d11 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -6223,9 +6223,9 @@ List of schemas
 (0 rows)
 
 \dRp "no.such.publication"
-                              List of publications
- Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root 
-------+-------+------------+---------+---------+---------+-----------+----------
+                                    List of publications
+ Name | Owner | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+------+-------+------------+---------+---------+---------+------------+-----------+----------
 (0 rows)
 
 \dRs "no.such.subscription"
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index 69dc6cfd85..6a5af2fd3f 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -30,20 +30,20 @@ ERROR:  conflicting or redundant options
 LINE 1: ...ub_xxx WITH (publish_via_partition_root = 'true', publish_vi...
                                                              ^
 \dRp
-                                              List of publications
-        Name        |          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------+--------------------------+------------+---------+---------+---------+-----------+----------
- testpib_ins_trunct | regress_publication_user | f          | t       | f       | f       | f         | f
- testpub_default    | regress_publication_user | f          | f       | t       | f       | f         | f
+                                                     List of publications
+        Name        |          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ testpib_ins_trunct | regress_publication_user | f          | t       | f       | f       | f          | f         | f
+ testpub_default    | regress_publication_user | f          | f       | t       | f       | f          | f         | f
 (2 rows)
 
 ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete');
 \dRp
-                                              List of publications
-        Name        |          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------+--------------------------+------------+---------+---------+---------+-----------+----------
- testpib_ins_trunct | regress_publication_user | f          | t       | f       | f       | f         | f
- testpub_default    | regress_publication_user | f          | t       | t       | t       | f         | f
+                                                     List of publications
+        Name        |          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ testpib_ins_trunct | regress_publication_user | f          | t       | f       | f       | f          | f         | f
+ testpub_default    | regress_publication_user | f          | t       | t       | t       | f          | f         | f
 (2 rows)
 
 --- adding tables
@@ -87,10 +87,10 @@ RESET client_min_messages;
 -- should be able to add schema to 'FOR TABLE' publication
 ALTER PUBLICATION testpub_fortable ADD TABLES IN SCHEMA pub_test;
 \dRp+ testpub_fortable
-                                Publication testpub_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                      Publication testpub_fortable
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "public.testpub_tbl1"
 Tables from schemas:
@@ -99,20 +99,20 @@ Tables from schemas:
 -- should be able to drop schema from 'FOR TABLE' publication
 ALTER PUBLICATION testpub_fortable DROP TABLES IN SCHEMA pub_test;
 \dRp+ testpub_fortable
-                                Publication testpub_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                      Publication testpub_fortable
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "public.testpub_tbl1"
 
 -- should be able to set schema to 'FOR TABLE' publication
 ALTER PUBLICATION testpub_fortable SET TABLES IN SCHEMA pub_test;
 \dRp+ testpub_fortable
-                                Publication testpub_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                      Publication testpub_fortable
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test"
 
@@ -123,10 +123,10 @@ CREATE PUBLICATION testpub_forschema FOR TABLES IN SCHEMA pub_test;
 CREATE PUBLICATION testpub_for_tbl_schema FOR TABLES IN SCHEMA pub_test, TABLE pub_test.testpub_nopk;
 RESET client_min_messages;
 \dRp+ testpub_for_tbl_schema
-                             Publication testpub_for_tbl_schema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                   Publication testpub_for_tbl_schema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "pub_test.testpub_nopk"
 Tables from schemas:
@@ -144,10 +144,10 @@ LINE 1: ...CATION testpub_parsertst FOR TABLES IN SCHEMA foo, test.foo;
 -- should be able to add a table of the same schema to the schema publication
 ALTER PUBLICATION testpub_forschema ADD TABLE pub_test.testpub_nopk;
 \dRp+ testpub_forschema
-                               Publication testpub_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                      Publication testpub_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "pub_test.testpub_nopk"
 Tables from schemas:
@@ -156,10 +156,10 @@ Tables from schemas:
 -- should be able to drop the table
 ALTER PUBLICATION testpub_forschema DROP TABLE pub_test.testpub_nopk;
 \dRp+ testpub_forschema
-                               Publication testpub_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                      Publication testpub_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test"
 
@@ -170,10 +170,10 @@ ERROR:  relation "testpub_nopk" is not part of the publication
 -- should be able to set table to schema publication
 ALTER PUBLICATION testpub_forschema SET TABLE pub_test.testpub_nopk;
 \dRp+ testpub_forschema
-                               Publication testpub_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                      Publication testpub_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "pub_test.testpub_nopk"
 
@@ -195,10 +195,10 @@ Publications:
     "testpub_foralltables"
 
 \dRp+ testpub_foralltables
-                              Publication testpub_foralltables
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | t          | t       | t       | f       | f         | f
+                                    Publication testpub_foralltables
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | t          | t       | t       | f       | f          | f         | f
 (1 row)
 
 DROP TABLE testpub_tbl2;
@@ -210,19 +210,19 @@ CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3;
 CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3;
 RESET client_min_messages;
 \dRp+ testpub3
-                                    Publication testpub3
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                          Publication testpub3
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "public.testpub_tbl3"
     "public.testpub_tbl3a"
 
 \dRp+ testpub4
-                                    Publication testpub4
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                          Publication testpub4
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "public.testpub_tbl3"
 
@@ -243,10 +243,10 @@ UPDATE testpub_parted1 SET a = 1;
 -- only parent is listed as being in publication, not the partition
 ALTER PUBLICATION testpub_forparted ADD TABLE testpub_parted;
 \dRp+ testpub_forparted
-                               Publication testpub_forparted
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                      Publication testpub_forparted
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "public.testpub_parted"
 
@@ -261,10 +261,10 @@ ALTER TABLE testpub_parted DETACH PARTITION testpub_parted1;
 UPDATE testpub_parted1 SET a = 1;
 ALTER PUBLICATION testpub_forparted SET (publish_via_partition_root = true);
 \dRp+ testpub_forparted
-                               Publication testpub_forparted
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | t
+                                      Publication testpub_forparted
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | t
 Tables:
     "public.testpub_parted"
 
@@ -293,10 +293,10 @@ SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub5 FOR TABLE testpub_rf_tbl1, testpub_rf_tbl2 WHERE (c <> 'test' AND d < 5) WITH (publish = 'insert');
 RESET client_min_messages;
 \dRp+ testpub5
-                                    Publication testpub5
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                          Publication testpub5
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | f       | f       | f          | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "public.testpub_rf_tbl2" WHERE ((c <> 'test'::text) AND (d < 5))
@@ -309,10 +309,10 @@ Tables:
 
 ALTER PUBLICATION testpub5 ADD TABLE testpub_rf_tbl3 WHERE (e > 1000 AND e < 2000);
 \dRp+ testpub5
-                                    Publication testpub5
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                          Publication testpub5
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | f       | f       | f          | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "public.testpub_rf_tbl2" WHERE ((c <> 'test'::text) AND (d < 5))
@@ -328,10 +328,10 @@ Publications:
 
 ALTER PUBLICATION testpub5 DROP TABLE testpub_rf_tbl2;
 \dRp+ testpub5
-                                    Publication testpub5
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                          Publication testpub5
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | f       | f       | f          | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "public.testpub_rf_tbl3" WHERE ((e > 1000) AND (e < 2000))
@@ -339,10 +339,10 @@ Tables:
 -- remove testpub_rf_tbl1 and add testpub_rf_tbl3 again (another WHERE expression)
 ALTER PUBLICATION testpub5 SET TABLE testpub_rf_tbl3 WHERE (e > 300 AND e < 500);
 \dRp+ testpub5
-                                    Publication testpub5
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                          Publication testpub5
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | f       | f       | f          | f         | f
 Tables:
     "public.testpub_rf_tbl3" WHERE ((e > 300) AND (e < 500))
 
@@ -375,10 +375,10 @@ SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub_syntax1 FOR TABLE testpub_rf_tbl1, ONLY testpub_rf_tbl3 WHERE (e < 999) WITH (publish = 'insert');
 RESET client_min_messages;
 \dRp+ testpub_syntax1
-                                Publication testpub_syntax1
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                       Publication testpub_syntax1
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | f       | f       | f          | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "public.testpub_rf_tbl3" WHERE (e < 999)
@@ -388,10 +388,10 @@ SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub_syntax2 FOR TABLE testpub_rf_tbl1, testpub_rf_schema1.testpub_rf_tbl5 WHERE (h < 999) WITH (publish = 'insert');
 RESET client_min_messages;
 \dRp+ testpub_syntax2
-                                Publication testpub_syntax2
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | f         | f
+                                       Publication testpub_syntax2
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | f       | f       | f          | f         | f
 Tables:
     "public.testpub_rf_tbl1"
     "testpub_rf_schema1.testpub_rf_tbl5" WHERE (h < 999)
@@ -506,10 +506,10 @@ CREATE PUBLICATION testpub6 FOR TABLES IN SCHEMA testpub_rf_schema2;
 ALTER PUBLICATION testpub6 SET TABLES IN SCHEMA testpub_rf_schema2, TABLE testpub_rf_schema2.testpub_rf_tbl6 WHERE (i < 99);
 RESET client_min_messages;
 \dRp+ testpub6
-                                    Publication testpub6
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                          Publication testpub6
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "testpub_rf_schema2.testpub_rf_tbl6" WHERE (i < 99)
 Tables from schemas:
@@ -723,10 +723,10 @@ CREATE PUBLICATION testpub_table_ins WITH (publish = 'insert, truncate');
 RESET client_min_messages;
 ALTER PUBLICATION testpub_table_ins ADD TABLE testpub_tbl5 (a);		-- ok
 \dRp+ testpub_table_ins
-                               Publication testpub_table_ins
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | f       | f       | t         | f
+                                      Publication testpub_table_ins
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | f       | f       | f          | t         | f
 Tables:
     "public.testpub_tbl5" (a)
 
@@ -900,10 +900,10 @@ CREATE TABLE testpub_tbl_both_filters (a int, b int, c int, PRIMARY KEY (a,c));
 ALTER TABLE testpub_tbl_both_filters REPLICA IDENTITY USING INDEX testpub_tbl_both_filters_pkey;
 ALTER PUBLICATION testpub_both_filters ADD TABLE testpub_tbl_both_filters (a,c) WHERE (c != 1);
 \dRp+ testpub_both_filters
-                              Publication testpub_both_filters
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                    Publication testpub_both_filters
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "public.testpub_tbl_both_filters" (a, c) WHERE (c <> 1)
 
@@ -1108,10 +1108,10 @@ ERROR:  relation "testpub_tbl1" is already member of publication "testpub_fortbl
 CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1;
 ERROR:  publication "testpub_fortbl" already exists
 \dRp+ testpub_fortbl
-                                 Publication testpub_fortbl
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                       Publication testpub_fortbl
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "pub_test.testpub_nopk"
     "public.testpub_tbl1"
@@ -1149,10 +1149,10 @@ Publications:
     "testpub_fortbl"
 
 \dRp+ testpub_default
-                                Publication testpub_default
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | f         | f
+                                       Publication testpub_default
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | f         | f
 Tables:
     "pub_test.testpub_nopk"
     "public.testpub_tbl1"
@@ -1230,10 +1230,10 @@ REVOKE CREATE ON DATABASE regression FROM regress_publication_user2;
 DROP TABLE testpub_parted;
 DROP TABLE testpub_tbl1;
 \dRp+ testpub_default
-                                Publication testpub_default
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | f         | f
+                                       Publication testpub_default
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | f         | f
 (1 row)
 
 -- fail - must be owner of publication
@@ -1243,20 +1243,20 @@ ERROR:  must be owner of publication testpub_default
 RESET ROLE;
 ALTER PUBLICATION testpub_default RENAME TO testpub_foo;
 \dRp testpub_foo
-                                           List of publications
-    Name     |          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
--------------+--------------------------+------------+---------+---------+---------+-----------+----------
- testpub_foo | regress_publication_user | f          | t       | t       | t       | f         | f
+                                                 List of publications
+    Name     |          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+-------------+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ testpub_foo | regress_publication_user | f          | t       | t       | t       | f          | f         | f
 (1 row)
 
 -- rename back to keep the rest simple
 ALTER PUBLICATION testpub_foo RENAME TO testpub_default;
 ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2;
 \dRp testpub_default
-                                             List of publications
-      Name       |           Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
------------------+---------------------------+------------+---------+---------+---------+-----------+----------
- testpub_default | regress_publication_user2 | f          | t       | t       | t       | f         | f
+                                                    List of publications
+      Name       |           Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+-----------------+---------------------------+------------+---------+---------+---------+------------+-----------+----------
+ testpub_default | regress_publication_user2 | f          | t       | t       | t       | f          | f         | f
 (1 row)
 
 -- adding schemas and tables
@@ -1272,19 +1272,19 @@ CREATE TABLE "CURRENT_SCHEMA"."CURRENT_SCHEMA"(id int);
 SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub1_forschema FOR TABLES IN SCHEMA pub_test1;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
 
 CREATE PUBLICATION testpub2_forschema FOR TABLES IN SCHEMA pub_test1, pub_test2, pub_test3;
 \dRp+ testpub2_forschema
-                               Publication testpub2_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub2_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1298,44 +1298,44 @@ CREATE PUBLICATION testpub6_forschema FOR TABLES IN SCHEMA "CURRENT_SCHEMA", CUR
 CREATE PUBLICATION testpub_fortable FOR TABLE "CURRENT_SCHEMA"."CURRENT_SCHEMA";
 RESET client_min_messages;
 \dRp+ testpub3_forschema
-                               Publication testpub3_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub3_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "public"
 
 \dRp+ testpub4_forschema
-                               Publication testpub4_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub4_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "CURRENT_SCHEMA"
 
 \dRp+ testpub5_forschema
-                               Publication testpub5_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub5_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "CURRENT_SCHEMA"
     "public"
 
 \dRp+ testpub6_forschema
-                               Publication testpub6_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub6_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "CURRENT_SCHEMA"
     "public"
 
 \dRp+ testpub_fortable
-                                Publication testpub_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                      Publication testpub_fortable
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "CURRENT_SCHEMA.CURRENT_SCHEMA"
 
@@ -1369,10 +1369,10 @@ ERROR:  schema "testpub_view" does not exist
 -- dropping the schema should reflect the change in publication
 DROP SCHEMA pub_test3;
 \dRp+ testpub2_forschema
-                               Publication testpub2_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub2_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1380,20 +1380,20 @@ Tables from schemas:
 -- renaming the schema should reflect the change in publication
 ALTER SCHEMA pub_test1 RENAME to pub_test1_renamed;
 \dRp+ testpub2_forschema
-                               Publication testpub2_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub2_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1_renamed"
     "pub_test2"
 
 ALTER SCHEMA pub_test1_renamed RENAME to pub_test1;
 \dRp+ testpub2_forschema
-                               Publication testpub2_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub2_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1401,10 +1401,10 @@ Tables from schemas:
 -- alter publication add schema
 ALTER PUBLICATION testpub1_forschema ADD TABLES IN SCHEMA pub_test2;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1413,10 +1413,10 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema ADD TABLES IN SCHEMA non_existent_schema;
 ERROR:  schema "non_existent_schema" does not exist
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1425,10 +1425,10 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema ADD TABLES IN SCHEMA pub_test1;
 ERROR:  schema "pub_test1" is already member of publication "testpub1_forschema"
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1436,10 +1436,10 @@ Tables from schemas:
 -- alter publication drop schema
 ALTER PUBLICATION testpub1_forschema DROP TABLES IN SCHEMA pub_test2;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
 
@@ -1447,10 +1447,10 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema DROP TABLES IN SCHEMA pub_test2;
 ERROR:  tables from schema "pub_test2" are not part of the publication
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
 
@@ -1458,29 +1458,29 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema DROP TABLES IN SCHEMA non_existent_schema;
 ERROR:  schema "non_existent_schema" does not exist
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
 
 -- drop all schemas
 ALTER PUBLICATION testpub1_forschema DROP TABLES IN SCHEMA pub_test1;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 (1 row)
 
 -- alter publication set multiple schema
 ALTER PUBLICATION testpub1_forschema SET TABLES IN SCHEMA pub_test1, pub_test2;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1489,10 +1489,10 @@ Tables from schemas:
 ALTER PUBLICATION testpub1_forschema SET TABLES IN SCHEMA non_existent_schema;
 ERROR:  schema "non_existent_schema" does not exist
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
     "pub_test2"
@@ -1501,10 +1501,10 @@ Tables from schemas:
 -- removing the duplicate schemas
 ALTER PUBLICATION testpub1_forschema SET TABLES IN SCHEMA pub_test1, pub_test1;
 \dRp+ testpub1_forschema
-                               Publication testpub1_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub1_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
 
@@ -1583,18 +1583,18 @@ SET client_min_messages = 'ERROR';
 CREATE PUBLICATION testpub3_forschema;
 RESET client_min_messages;
 \dRp+ testpub3_forschema
-                               Publication testpub3_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub3_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 (1 row)
 
 ALTER PUBLICATION testpub3_forschema SET TABLES IN SCHEMA pub_test1;
 \dRp+ testpub3_forschema
-                               Publication testpub3_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                     Publication testpub3_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables from schemas:
     "pub_test1"
 
@@ -1604,20 +1604,20 @@ CREATE PUBLICATION testpub_forschema_fortable FOR TABLES IN SCHEMA pub_test1, TA
 CREATE PUBLICATION testpub_fortable_forschema FOR TABLE pub_test2.tbl1, TABLES IN SCHEMA pub_test1;
 RESET client_min_messages;
 \dRp+ testpub_forschema_fortable
-                           Publication testpub_forschema_fortable
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                 Publication testpub_forschema_fortable
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "pub_test2.tbl1"
 Tables from schemas:
     "pub_test1"
 
 \dRp+ testpub_fortable_forschema
-                           Publication testpub_fortable_forschema
-          Owner           | All tables | Inserts | Updates | Deletes | Truncates | Via root 
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f          | t       | t       | t       | t         | f
+                                 Publication testpub_fortable_forschema
+          Owner           | All tables | Inserts | Updates | Deletes | Table DDLs | Truncates | Via root 
+--------------------------+------------+---------+---------+---------+------------+-----------+----------
+ regress_publication_user | f          | t       | t       | t       | f          | t         | f
 Tables:
     "pub_test2.tbl1"
 Tables from schemas:
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 87c147dfdb..f84559029b 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -566,6 +566,7 @@ DefElemAction
 DefaultACLInfo
 DefineStmt
 DeleteStmt
+DeparsedCommandType
 DependencyGenerator
 DependencyGeneratorData
 DependencyType
@@ -1461,6 +1462,7 @@ LogicalDecodeBeginPrepareCB
 LogicalDecodeChangeCB
 LogicalDecodeCommitCB
 LogicalDecodeCommitPreparedCB
+LogicalDecodeDDLMessageCB
 LogicalDecodeFilterByOriginCB
 LogicalDecodeFilterPrepareCB
 LogicalDecodeMessageCB
@@ -1471,6 +1473,7 @@ LogicalDecodeStartupCB
 LogicalDecodeStreamAbortCB
 LogicalDecodeStreamChangeCB
 LogicalDecodeStreamCommitCB
+LogicalDecodeStreamDDLMessageCB
 LogicalDecodeStreamMessageCB
 LogicalDecodeStreamPrepareCB
 LogicalDecodeStreamStartCB
@@ -2317,6 +2320,7 @@ ReorderBufferChange
 ReorderBufferChangeType
 ReorderBufferCommitCB
 ReorderBufferCommitPreparedCB
+ReorderBufferDDLMessageCB
 ReorderBufferDiskChange
 ReorderBufferIterTXNEntry
 ReorderBufferIterTXNState
@@ -2326,6 +2330,7 @@ ReorderBufferRollbackPreparedCB
 ReorderBufferStreamAbortCB
 ReorderBufferStreamChangeCB
 ReorderBufferStreamCommitCB
+ReorderBufferStreamDDLMessageCB
 ReorderBufferStreamMessageCB
 ReorderBufferStreamPrepareCB
 ReorderBufferStreamStartCB
@@ -3919,6 +3924,7 @@ xl_heap_visible
 xl_invalid_page
 xl_invalid_page_key
 xl_invalidations
+xl_logical_ddl_message
 xl_logical_message
 xl_multi_insert_tuple
 xl_multixact_create
-- 
2.34.1

From cfe86ad745e48842466b13a18e7d9d039db640b3 Mon Sep 17 00:00:00 2001
From: Ajin Cherian <[email-masked]>
Date: Wed, 22 Mar 2023 23:10:43 -0400
Subject: [PATCH 4/8] Introduce the test_ddl_deparse_regress test module.

This testing module aims to achieve the following four testing goals for the DDL deparser:
1. Test that the generated JSON blob is expected using SQL tests.
2. Test that the re-formed DDL command is expected using SQL tests.
3. Test that the re-formed DDL command has the same effect as the original command by comparing
   the results of pg_dump, using the SQL tests in 1 and 2.
4. Test that new DDL syntax is handled by the DDL deparser by capturing and deparing DDL commands
   ran by pg_regress.

1 and 2 is tested with SQL tests, by printing the deparsed JSON blob and the re-formed command.

Goal 3 is tested with TAP framework in t/001_compare_dumped_results.pl, see README for details.

Goal 4 is tested with TAP framework and pg_regress in 002_regress_tests.pl, the execution is currently commented out because it will fail due unimplemented commands in the DDL deparser.

Test coverage is added for:
CREATE TABLE
ALTER TABLE
column constraints/table constraints
---
 src/test/modules/Makefile                     |   1 +
 .../test_ddl_deparse_regress/.gitignore       |   4 +
 .../modules/test_ddl_deparse_regress/Makefile |  48 +
 .../test_ddl_deparse_regress/README.md        |  32 +
 .../expected/alter_table.out                  | 822 ++++++++++++++++++
 .../expected/constraints.out                  | 562 ++++++++++++
 .../expected/create_extension.out             |   4 +
 .../expected/create_index.out                 |  22 +
 .../expected/create_rule.out                  |  48 +
 .../expected/create_schema.out                |   5 +
 .../expected/create_table.out                 | 571 ++++++++++++
 .../expected/create_type.out                  |  15 +
 .../expected/test_ddl_deparse.out             |  26 +
 .../test_ddl_deparse_regress/meson.build      |  45 +
 .../sql/alter_table.sql                       | 473 ++++++++++
 .../sql/constraints.sql                       | 385 ++++++++
 .../sql/create_extension.sql                  |   5 +
 .../sql/create_index.sql                      |  21 +
 .../sql/create_rule.sql                       |  41 +
 .../sql/create_schema.sql                     |   6 +
 .../sql/create_table.sql                      | 414 +++++++++
 .../sql/create_type.sql                       |  17 +
 .../sql/test_ddl_deparse.sql                  |  29 +
 .../t/001_compare_dumped_results.pl           | 235 +++++
 .../t/002_regress_tests.pl                    | 213 +++++
 .../test_ddl_deparse_regress--1.0.sql         |   9 +
 .../test_ddl_deparse_regress.c                |  67 ++
 .../test_ddl_deparse_regress.control          |   4 +
 28 files changed, 4124 insertions(+)
 create mode 100644 src/test/modules/test_ddl_deparse_regress/.gitignore
 create mode 100644 src/test/modules/test_ddl_deparse_regress/Makefile
 create mode 100644 src/test/modules/test_ddl_deparse_regress/README.md
 create mode 100644 src/test/modules/test_ddl_deparse_regress/expected/alter_table.out
 create mode 100644 src/test/modules/test_ddl_deparse_regress/expected/constraints.out
 create mode 100644 src/test/modules/test_ddl_deparse_regress/expected/create_extension.out
 create mode 100644 src/test/modules/test_ddl_deparse_regress/expected/create_index.out
 create mode 100644 src/test/modules/test_ddl_deparse_regress/expected/create_rule.out
 create mode 100644 src/test/modules/test_ddl_deparse_regress/expected/create_schema.out
 create mode 100644 src/test/modules/test_ddl_deparse_regress/expected/create_table.out
 create mode 100644 src/test/modules/test_ddl_deparse_regress/expected/create_type.out
 create mode 100644 src/test/modules/test_ddl_deparse_regress/expected/test_ddl_deparse.out
 create mode 100644 src/test/modules/test_ddl_deparse_regress/meson.build
 create mode 100644 src/test/modules/test_ddl_deparse_regress/sql/alter_table.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/sql/constraints.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/sql/create_extension.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/sql/create_index.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/sql/create_rule.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/sql/create_schema.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/sql/create_table.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/sql/create_type.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/sql/test_ddl_deparse.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/t/001_compare_dumped_results.pl
 create mode 100644 src/test/modules/test_ddl_deparse_regress/t/002_regress_tests.pl
 create mode 100644 src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress--1.0.sql
 create mode 100644 src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.c
 create mode 100644 src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.control

diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 6331c976dc..5fb608da6f 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -18,6 +18,7 @@ SUBDIRS = \
 		  test_copy_callbacks \
 		  test_custom_rmgrs \
 		  test_ddl_deparse \
+		  test_ddl_deparse_regress \
 		  test_extensions \
 		  test_ginpostinglist \
 		  test_integerset \
diff --git a/src/test/modules/test_ddl_deparse_regress/.gitignore b/src/test/modules/test_ddl_deparse_regress/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/src/test/modules/test_ddl_deparse_regress/Makefile b/src/test/modules/test_ddl_deparse_regress/Makefile
new file mode 100644
index 0000000000..4c6177998d
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/Makefile
@@ -0,0 +1,48 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/modules/test_ddl_deparse_regress
+#
+# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/test/modules/test_ddl_deparse_regress/Makefile
+#
+#-------------------------------------------------------------------------
+
+
+MODULES = test_ddl_deparse_regress
+PGFILEDESC = "test_ddl_deparse_regress - regression testing for DDL deparsing"
+
+EXTENSION = test_ddl_deparse_regress
+DATA = test_ddl_deparse_regress--1.0.sql
+
+# test_ddl_deparse must be first
+REGRESS = test_ddl_deparse \
+	create_extension \
+	create_type \
+	create_schema \
+	create_rule \
+	create_index \
+	create_table \
+	constraints \
+	alter_table
+
+export REGRESS
+
+EXTRA_INSTALL = contrib/pg_stat_statements
+
+TAP_TESTS = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_ddl_deparse_regress
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+REGRESS_SHLIB=$(abs_top_builddir)/src/test/regress/regress$(DLSUFFIX)
+export REGRESS_SHLIB
diff --git a/src/test/modules/test_ddl_deparse_regress/README.md b/src/test/modules/test_ddl_deparse_regress/README.md
new file mode 100644
index 0000000000..704e61dc0c
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/README.md
@@ -0,0 +1,32 @@
+# Testing harness for DDL deparser
+
+## Testing goals
+
+DDL Deparser provides the ability to encode the original DDL command to a JSON string, then decode it to a fully schema-qualified DDL command which is supposed to have the same effect as the original command. This testing module aims to achieve the following four testing for the DDL deparser:
+
+1. Test that the generated JSON blob is expected using SQL tests.
+2. Test that the re-formed DDL command is expected using SQL tests.
+3. Test that the re-formed DDL command has the same effect as the original command
+   by comparing the results of pg_dump, using the SQL tests in 1 and 2.
+4. Test that new DDL syntax is handled by the DDL deparser by capturing and deparing
+   DDL commands ran by pg_regress.
+
+1 and 2 is tested with SQL tests, by noticing the deparsed JSON blob and the re-formed command.
+
+Goal 3 is tested with TAP framework in t/001_compare_dumped_results.pl
+
+Goal 4 is tested with TAP framework and pg_regress in 002_regress_tests.pl (Not enabled)
+
+## Usage
+
+Run `make check`, it will run the SQL tests first, then it will run the TAP tests. The execution of 002_regress_tests.pl is currently commented out because it will fail due to unimplemented commands in the DDL deparser.
+
+## How to add more test cases and find out the failure?
+
+You can add test cases to existed files in `sql` folder directly. If you need to create a new test file, you can create a file in `sql` folder, add that test file name to `meson.build` and `Makefile` following the convention used by other test files.
+
+After you have added you test cases, run `make check` and check goal 1 and goal 2 of the added test cases in `results` folder, if the result is right, copy that file to `expected` folder.
+
+Now SQL tests should pass, run `make check` again to check the TAP tests. If everything passed, this test case also meet goal 3. If it fails, check the error message to locate the failure position.
+
+You can find execution logs are in `tmp_check/log` folder, dumped results are in `tmp_check/dumps` folder, reformed sql commands are in `tmp_check/ddl` folder for further investigation.
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/alter_table.out b/src/test/modules/test_ddl_deparse_regress/expected/alter_table.out
new file mode 100644
index 0000000000..9b22999cd9
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/expected/alter_table.out
@@ -0,0 +1,822 @@
+-- parent table defintion
+CREATE TABLE orders(
+    id int,
+    name varchar,
+    description text,
+    price float4,
+    quantity int,
+    purchase_date date
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.orders (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+-- ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
+--     action [, ... ]
+CREATE TABLE parent_table(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.parent_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+CREATE TABLE test_only () INHERITS (parent_table);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_only () INHERITS (public.parent_table)     
+ALTER TABLE test_only ADD col1 int;
+NOTICE:  re-formed command: ALTER TABLE  public.test_only ADD COLUMN  col1 pg_catalog.int4 STORAGE PLAIN      
+ALTER TABLE IF EXISTS fake_table ADD col2 int;
+NOTICE:  relation "fake_table" does not exist, skipping
+-- ALTER TABLE IF EXISTS ONLY parent_table ADD PRIMARY KEY (id);
+ALTER TABLE IF EXISTS parent_table * ADD CHECK (id > 10);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "parent_table_id_check", "type": "add constraint", "definition": "CHECK ((id OPERATOR(pg_catalog.>) 10))"}], "identity": {"objname": "parent_table", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.parent_table ADD CONSTRAINT parent_table_id_check CHECK ((id OPERATOR(pg_catalog.>) 10))
+-- ADD [ COLUMN ] [ IF NOT EXISTS ] column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
+CREATE TABLE test_add_column(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_add_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_add_column ADD col1 int;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_column ADD COLUMN  col1 pg_catalog.int4 STORAGE PLAIN      
+ALTER TABLE test_add_column ADD COLUMN col2 int;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_column ADD COLUMN  col2 pg_catalog.int4 STORAGE PLAIN      
+ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS col2 varchar;
+NOTICE:  column "col2" of relation "test_add_column" already exists, skipping
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_column ADD COLUMN IF NOT EXISTS col2 pg_catalog.int4 STORAGE PLAIN      
+ALTER TABLE test_add_column ADD col3 varchar COLLATE "C";
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_column ADD COLUMN  col3 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."C"    
+ALTER TABLE test_add_column ADD col4 int CHECK (col4 > 100) UNIQUE;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_column ADD COLUMN  col4 pg_catalog.int4 STORAGE PLAIN      , ADD CONSTRAINT test_add_column_col4_key UNIQUE (col4), ADD CONSTRAINT test_add_column_col4_check CHECK ((col4 OPERATOR(pg_catalog.>) 100))
+ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS col5 text COLLATE "C" DEFAULT 'foo' NOT NULL;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_column ADD COLUMN IF NOT EXISTS col5 pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."C" NOT NULL DEFAULT 'foo'::pg_catalog.text  
+-- DROP [ COLUMN ] [ IF EXISTS ] column_name [ RESTRICT | CASCADE ]
+CREATE TABLE test_drop_column(
+    LIKE orders,
+    UNIQUE (id),
+    UNIQUE (name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_drop_column_id_key UNIQUE (id), CONSTRAINT test_drop_column_name_key UNIQUE (name))      
+CREATE TABLE foreign_table(
+    id int REFERENCES test_drop_column (id),
+    name varchar REFERENCES test_drop_column (name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.foreign_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: ALTER TABLE  public.foreign_table ADD CONSTRAINT foreign_table_id_fkey FOREIGN KEY (id) REFERENCES public.test_drop_column(id), ADD CONSTRAINT foreign_table_name_fkey FOREIGN KEY (name) REFERENCES public.test_drop_column(name)
+ALTER TABLE test_drop_column DROP price;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP %{objtype}s %{if_exists}s %{column}I %{cascade}s", "type": "drop column", "column": "price", "cascade": {"fmt": "CASCADE", "present": false}, "objtype": "COLUMN", "if_exists": ""}], "identity": {"objname": "test_drop_column", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_column DROP COLUMN  price 
+ALTER TABLE test_drop_column DROP COLUMN quantity;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP %{objtype}s %{if_exists}s %{column}I %{cascade}s", "type": "drop column", "column": "quantity", "cascade": {"fmt": "CASCADE", "present": false}, "objtype": "COLUMN", "if_exists": ""}], "identity": {"objname": "test_drop_column", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_column DROP COLUMN  quantity 
+ALTER TABLE test_drop_column DROP IF EXISTS description RESTRICT;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP %{objtype}s %{if_exists}s %{column}I %{cascade}s", "type": "drop column", "column": "description", "cascade": {"fmt": "CASCADE", "present": false}, "objtype": "COLUMN", "if_exists": "IF EXISTS"}], "identity": {"objname": "test_drop_column", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_column DROP COLUMN IF EXISTS description 
+-- TOFIX
+-- ALTER TABLE test_drop_column DROP IF EXISTS name CASCADE;
+-- ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ COLLATE collation ] [ USING expression ]
+CREATE TABLE test_alter_type(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_type (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_alter_type ALTER price TYPE int;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET DATA TYPE %{datatype}T %{collation}s %{using}s", "type": "alter column type", "using": {"fmt": "USING %{expression}s", "present": false}, "column": "price", "datatype": {"typmod": "", "typarray": false, "typename": "int4", "schemaname": "pg_catalog"}, "collation": {"fmt": "COLLATE %{name}D", "present": false}}], "identity": {"objname": "test_alter_type", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_type ALTER COLUMN price SET DATA TYPE pg_catalog.int4  
+ALTER TABLE test_alter_type ALTER COLUMN purchase_date TYPE text COLLATE "C";
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET DATA TYPE %{datatype}T %{collation}s %{using}s", "type": "alter column type", "using": {"fmt": "USING %{expression}s", "present": false}, "column": "purchase_date", "datatype": {"typmod": "", "typarray": false, "typename": "text", "schemaname": "pg_catalog"}, "collation": {"fmt": "COLLATE %{name}D", "name": {"objname": "C", "schemaname": "pg_catalog"}}}], "identity": {"objname": "test_alter_type", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_type ALTER COLUMN purchase_date SET DATA TYPE pg_catalog.text COLLATE pg_catalog."C" 
+ALTER TABLE test_alter_type ALTER COLUMN quantity SET DATA TYPE float4;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET DATA TYPE %{datatype}T %{collation}s %{using}s", "type": "alter column type", "using": {"fmt": "USING %{expression}s", "present": false}, "column": "quantity", "datatype": {"typmod": "", "typarray": false, "typename": "float4", "schemaname": "pg_catalog"}, "collation": {"fmt": "COLLATE %{name}D", "present": false}}], "identity": {"objname": "test_alter_type", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_type ALTER COLUMN quantity SET DATA TYPE pg_catalog.float4  
+ALTER TABLE test_alter_type ALTER name TYPE int USING id::integer;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET DATA TYPE %{datatype}T %{collation}s %{using}s", "type": "alter column type", "using": {"fmt": "USING %{expression}s", "expression": "id"}, "column": "name", "datatype": {"typmod": "", "typarray": false, "typename": "int4", "schemaname": "pg_catalog"}, "collation": {"fmt": "COLLATE %{name}D", "present": false}}], "identity": {"objname": "test_alter_type", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_type ALTER COLUMN name SET DATA TYPE pg_catalog.int4  USING id
+ALTER TABLE test_alter_type ALTER name TYPE TEXT USING id || ' and ' || description, DROP COLUMN description;
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_type ALTER COLUMN name SET DATA TYPE pg_catalog.text COLLATE pg_catalog."default" USING ((id OPERATOR(pg_catalog.||) ' and '::pg_catalog.text) OPERATOR(pg_catalog.||) description), DROP COLUMN  description 
+-- ALTER [ COLUMN ] column_name SET DEFAULT expression
+CREATE TABLE test_alter_set_default(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_set_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_alter_set_default ALTER price SET DEFAULT 100;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET DEFAULT %{definition}s", "type": "set default", "column": "price", "definition": "100"}], "identity": {"objname": "test_alter_set_default", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_set_default ALTER COLUMN price SET DEFAULT 100
+ALTER TABLE test_alter_set_default ALTER COLUMN quantity SET DEFAULT 10;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET DEFAULT %{definition}s", "type": "set default", "column": "quantity", "definition": "10"}], "identity": {"objname": "test_alter_set_default", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_set_default ALTER COLUMN quantity SET DEFAULT 10
+-- ALTER [ COLUMN ] column_name DROP DEFAULT
+CREATE TABLE test_drop_default(
+    LIKE orders,
+    default_price float4 DEFAULT 10.0,
+    default_name varchar DEFAULT 'foo'
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , default_price pg_catalog.float4 STORAGE PLAIN    DEFAULT 10.0  , default_name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"  DEFAULT 'foo'::character varying  )      
+ALTER TABLE test_drop_default ALTER default_price DROP DEFAULT;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP DEFAULT", "type": "drop default", "column": "default_price"}], "identity": {"objname": "test_drop_default", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_default ALTER COLUMN default_price DROP DEFAULT
+ALTER TABLE test_drop_default ALTER COLUMN default_name DROP DEFAULT;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP DEFAULT", "type": "drop default", "column": "default_name"}], "identity": {"objname": "test_drop_default", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_default ALTER COLUMN default_name DROP DEFAULT
+-- ALTER [ COLUMN ] column_name { SET | DROP } NOT NULL
+CREATE TABLE test_set_not_null(
+    LIKE orders,
+    size int NOT NULL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_not_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , size pg_catalog.int4 STORAGE PLAIN   NOT NULL   )      
+ALTER TABLE test_set_not_null ALTER COLUMN id SET NOT NULL;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}], "identity": {"objname": "test_set_not_null", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_not_null ALTER COLUMN id SET NOT NULL
+ALTER TABLE test_set_not_null ALTER size DROP NOT NULL;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP NOT NULL", "type": "drop not null", "column": "size"}], "identity": {"objname": "test_set_not_null", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_not_null ALTER COLUMN size DROP NOT NULL
+-- ALTER [ COLUMN ] column_name DROP EXPRESSION [ IF EXISTS ]
+CREATE TABLE test_drop_expression(
+    LIKE orders,
+    new_id int GENERATED ALWAYS AS ( 3 * ID ) STORED
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_expression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , new_id pg_catalog.int4 STORAGE PLAIN      GENERATED ALWAYS AS ((3 OPERATOR(pg_catalog.*) id)) STORED)      
+ALTER TABLE test_drop_expression ALTER new_id DROP EXPRESSION;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP EXPRESSION %{if_exists}s", "type": "drop expression", "column": "new_id", "if_exists": ""}], "identity": {"objname": "test_drop_expression", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_expression ALTER COLUMN new_id DROP EXPRESSION 
+ALTER TABLE test_drop_expression ALTER id DROP EXPRESSION IF EXISTS;
+NOTICE:  column "id" of relation "test_drop_expression" is not a stored generated column, skipping
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP EXPRESSION %{if_exists}s", "type": "drop expression", "column": "id", "if_exists": "IF EXISTS"}], "identity": {"objname": "test_drop_expression", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_expression ALTER COLUMN id DROP EXPRESSION IF EXISTS
+-- ALTER [ COLUMN ] column_name ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ]
+CREATE TABLE test_add_generated(
+    LIKE orders,
+    col1 int NOT NULL,
+    col2 int NOT NULL,
+    col3 int NOT NULL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_add_generated (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , col1 pg_catalog.int4 STORAGE PLAIN   NOT NULL   , col2 pg_catalog.int4 STORAGE PLAIN   NOT NULL   , col3 pg_catalog.int4 STORAGE PLAIN   NOT NULL   )      
+ALTER TABLE test_add_generated ALTER col1 ADD GENERATED ALWAYS AS IDENTITY;
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "add identity", "column": "col1", "definition": {"fmt": "ADD %{identity_column}s", "identity_column": {"fmt": "%{identity_type}s ( %{seq_definition: }s )", "identity_type": {"fmt": "GENERATED %{option}s AS IDENTITY", "option": "ALWAYS"}, "seq_definition": [{"fmt": "CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "%{no}s CYCLE", "clause": "cycle"}, {"fmt": "INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_add_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_generated ALTER COLUMN col1 ADD GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 )
+ALTER TABLE test_add_generated ALTER COLUMN col2 ADD GENERATED BY DEFAULT AS IDENTITY;
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "add identity", "column": "col2", "definition": {"fmt": "ADD %{identity_column}s", "identity_column": {"fmt": "%{identity_type}s ( %{seq_definition: }s )", "identity_type": {"fmt": "GENERATED %{option}s AS IDENTITY", "option": "BY DEFAULT"}, "seq_definition": [{"fmt": "CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "%{no}s CYCLE", "clause": "cycle"}, {"fmt": "INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_add_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_generated ALTER COLUMN col2 ADD GENERATED BY DEFAULT AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 )
+ALTER TABLE test_add_generated ALTER col3 ADD GENERATED BY DEFAULT AS IDENTITY ( INCREMENT BY 10 );
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "add identity", "column": "col3", "definition": {"fmt": "ADD %{identity_column}s", "identity_column": {"fmt": "%{identity_type}s ( %{seq_definition: }s )", "identity_type": {"fmt": "GENERATED %{option}s AS IDENTITY", "option": "BY DEFAULT"}, "seq_definition": [{"fmt": "CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "%{no}s CYCLE", "clause": "cycle"}, {"fmt": "INCREMENT BY %{value}s", "value": "10", "clause": "seqincrement"}, {"fmt": "MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_add_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_generated ALTER COLUMN col3 ADD GENERATED BY DEFAULT AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 10 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 )
+-- ALTER [ COLUMN ] column_name { SET GENERATED { ALWAYS | BY DEFAULT } | SET sequence_option | RESTART [ [ WITH ] restart ] } [...]
+CREATE TABLE test_set_generated(
+    id1 int GENERATED BY DEFAULT AS IDENTITY,
+    id2 int GENERATED ALWAYS AS IDENTITY,
+    id3 int GENERATED ALWAYS AS IDENTITY,
+    id4 int GENERATED ALWAYS AS IDENTITY,
+    id5 int GENERATED ALWAYS AS IDENTITY,
+    id6 int GENERATED ALWAYS AS IDENTITY,
+    id7 int GENERATED ALWAYS AS IDENTITY
+);
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_generated (id1 pg_catalog.int4 STORAGE PLAIN     GENERATED BY DEFAULT AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id2 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id3 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id4 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id5 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id6 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id7 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )      
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+ALTER TABLE test_set_generated ALTER id1 SET GENERATED ALWAYS;
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id1", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "option": "ALWAYS"}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id1 SET GENERATED ALWAYS SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 1
+ALTER TABLE test_set_generated ALTER id2 SET GENERATED BY DEFAULT;
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id2", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "option": "BY DEFAULT"}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id2 SET GENERATED BY DEFAULT SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 1
+ALTER TABLE test_set_generated ALTER id3 SET INCREMENT BY 10;
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id3", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "10", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id3  SET CACHE 1 SET NO CYCLE SET INCREMENT BY 10 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 1
+ALTER TABLE test_set_generated ALTER id4 RESTART;
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id4", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id4  SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 1
+ALTER TABLE test_set_generated ALTER id5 RESTART WITH 101;
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id5", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "101", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id5  SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 101
+ALTER TABLE test_set_generated ALTER id6 RESTART WITH 201;
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id6", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "201", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id6  SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 201
+ALTER TABLE test_set_generated ALTER COLUMN id7 SET GENERATED BY DEFAULT SET INCREMENT BY 100 RESTART WITH 301;
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id7", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "option": "BY DEFAULT"}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "100", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "301", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id7 SET GENERATED BY DEFAULT SET CACHE 1 SET NO CYCLE SET INCREMENT BY 100 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 301
+-- ALTER [ COLUMN ] column_name DROP IDENTITY [ IF EXISTS ]
+CREATE TABLE test_drop_identity(
+    id int,
+    id_generated int GENERATED ALWAYS AS IDENTITY
+);
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_identity (id pg_catalog.int4 STORAGE PLAIN      , id_generated pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )      
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+ALTER TABLE test_drop_identity ALTER id_generated DROP IDENTITY;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP IDENTITY %{if_exists}s", "type": "drop identity", "column": "id_generated", "if_exists": ""}], "identity": {"objname": "test_drop_identity", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_identity ALTER COLUMN id_generated DROP IDENTITY 
+ALTER TABLE test_drop_identity ALTER id DROP IDENTITY IF EXISTS;
+NOTICE:  column "id" of relation "test_drop_identity" is not an identity column, skipping
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP IDENTITY %{if_exists}s", "type": "drop identity", "column": "id", "if_exists": "IF EXISTS"}], "identity": {"objname": "test_drop_identity", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_identity ALTER COLUMN id DROP IDENTITY IF EXISTS
+-- ALTER [ COLUMN ] column_name SET STATISTICS integer
+CREATE TABLE test_set_statistics(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_statistics (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_statistics ALTER id SET STATISTICS 1;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STATISTICS %{statistics}n", "type": "set statistics", "column": "id", "statistics": 1}], "identity": {"objname": "test_set_statistics", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_statistics ALTER COLUMN id SET STATISTICS 1
+-- ALTER [ COLUMN ] column_name SET ( attribute_option = value [, ... ] )
+CREATE TABLE test_set_attribute(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_attribute (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_attribute ALTER name SET (n_distinct = 102);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{option}s (%{options:, }s)", "column": "name", "option": "SET", "options": [{"fmt": "%{label}s = %{value}L", "label": {"fmt": "%{label}I", "label": "n_distinct"}, "value": "102"}]}], "identity": {"objname": "test_set_attribute", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_attribute ALTER COLUMN name SET (n_distinct = '102')
+ALTER TABLE test_set_attribute ALTER id SET (n_distinct_inherited = 99, n_distinct = 9);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{option}s (%{options:, }s)", "column": "id", "option": "SET", "options": [{"fmt": "%{label}s = %{value}L", "label": {"fmt": "%{label}I", "label": "n_distinct_inherited"}, "value": "99"}, {"fmt": "%{label}s = %{value}L", "label": {"fmt": "%{label}I", "label": "n_distinct"}, "value": "9"}]}], "identity": {"objname": "test_set_attribute", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_attribute ALTER COLUMN id SET (n_distinct_inherited = '99', n_distinct = '9')
+-- ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] )
+CREATE TABLE test_reset_attribute(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_reset_attribute (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_reset_attribute ALTER name RESET (n_distinct);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{option}s (%{options:, }s)", "column": "name", "option": "RESET", "options": [{"fmt": "%{label}s", "label": {"fmt": "%{label}I", "label": "n_distinct"}}]}], "identity": {"objname": "test_reset_attribute", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_reset_attribute ALTER COLUMN name RESET (n_distinct)
+ALTER TABLE test_reset_attribute ALTER id RESET (n_distinct, n_distinct_inherited);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{option}s (%{options:, }s)", "column": "id", "option": "RESET", "options": [{"fmt": "%{label}s", "label": {"fmt": "%{label}I", "label": "n_distinct"}}, {"fmt": "%{label}s", "label": {"fmt": "%{label}I", "label": "n_distinct_inherited"}}]}], "identity": {"objname": "test_reset_attribute", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_reset_attribute ALTER COLUMN id RESET (n_distinct, n_distinct_inherited)
+-- ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
+CREATE TABLE test_set_storage(
+    LIKE orders,
+    product_name text
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_storage (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , product_name pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+ALTER TABLE test_set_storage ALTER id SET STORAGE PLAIN;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STORAGE %{storage}s", "type": "set storage", "column": "id", "storage": "plain"}], "identity": {"objname": "test_set_storage", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage ALTER COLUMN id SET STORAGE plain
+ALTER TABLE test_set_storage ALTER name SET STORAGE EXTERNAL;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STORAGE %{storage}s", "type": "set storage", "column": "name", "storage": "external"}], "identity": {"objname": "test_set_storage", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage ALTER COLUMN name SET STORAGE external
+ALTER TABLE test_set_storage ALTER description SET STORAGE EXTENDED;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STORAGE %{storage}s", "type": "set storage", "column": "description", "storage": "extended"}], "identity": {"objname": "test_set_storage", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage ALTER COLUMN description SET STORAGE extended
+ALTER TABLE test_set_storage ALTER product_name SET STORAGE MAIN;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STORAGE %{storage}s", "type": "set storage", "column": "product_name", "storage": "main"}], "identity": {"objname": "test_set_storage", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage ALTER COLUMN product_name SET STORAGE main
+-- ALTER [ COLUMN ] column_name SET COMPRESSION compression_method
+CREATE TABLE test_set_compression(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_compression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_compression ALTER name SET COMPRESSION "pglz";
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET COMPRESSION %{compression_method}s", "type": "set compression", "column": "name", "compression_method": "pglz"}], "identity": {"objname": "test_set_compression", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_compression ALTER COLUMN name SET COMPRESSION pglz
+ALTER TABLE test_set_compression ALTER COLUMN description SET COMPRESSION "pglz";
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET COMPRESSION %{compression_method}s", "type": "set compression", "column": "description", "compression_method": "pglz"}], "identity": {"objname": "test_set_compression", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_compression ALTER COLUMN description SET COMPRESSION pglz
+-- ADD table_constraint [ NOT VALID ]
+CREATE TABLE test_add_table_constraint(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_add_table_constraint (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_add_table_constraint ADD PRIMARY KEY (id);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}, {"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "test_add_table_constraint_pkey", "type": "add constraint", "definition": "PRIMARY KEY (id)"}], "identity": {"objname": "test_add_table_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_table_constraint ALTER COLUMN id SET NOT NULL, ADD CONSTRAINT test_add_table_constraint_pkey PRIMARY KEY (id)
+ALTER TABLE test_add_table_constraint ADD CONSTRAINT max_name_len CHECK (length(name) < 4) NOT VALID;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "max_name_len", "type": "add constraint", "definition": "CHECK ((pg_catalog.length((name)::pg_catalog.text) OPERATOR(pg_catalog.<) 4)) NOT VALID"}], "identity": {"objname": "test_add_table_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_table_constraint ADD CONSTRAINT max_name_len CHECK ((pg_catalog.length((name)::pg_catalog.text) OPERATOR(pg_catalog.<) 4)) NOT VALID
+ALTER TABLE test_add_table_constraint ADD CHECK (id < 10);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "test_add_table_constraint_id_check", "type": "add constraint", "definition": "CHECK ((id OPERATOR(pg_catalog.<) 10))"}], "identity": {"objname": "test_add_table_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_table_constraint ADD CONSTRAINT test_add_table_constraint_id_check CHECK ((id OPERATOR(pg_catalog.<) 10))
+-- ADD table_constraint_using_index
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index1;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_constraint_using_index ADD CONSTRAINT test_add_constraint_used_index1 UNIQUE USING INDEX test_add_constraint_used_index1 NOT DEFERRABLE INITIALLY IMMEDIATE
+--TOFIX
+-- ALTER TABLE test_add_constraint_using_index ADD CONSTRAINT primary_constraint_using_index PRIMARY KEY USING INDEX test_add_constraint_used_index2;
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index3 DEFERRABLE;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_constraint_using_index ADD CONSTRAINT test_add_constraint_used_index3 UNIQUE USING INDEX test_add_constraint_used_index3 DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index4 NOT DEFERRABLE;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_constraint_using_index ADD CONSTRAINT test_add_constraint_used_index4 UNIQUE USING INDEX test_add_constraint_used_index4 NOT DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index5 INITIALLY DEFERRED;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_constraint_using_index ADD CONSTRAINT test_add_constraint_used_index5 UNIQUE USING INDEX test_add_constraint_used_index5 DEFERRABLE INITIALLY DEFERRED
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index6 INITIALLY IMMEDIATE;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_constraint_using_index ADD CONSTRAINT test_add_constraint_used_index6 UNIQUE USING INDEX test_add_constraint_used_index6 NOT DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index7 DEFERRABLE INITIALLY DEFERRED;
+NOTICE:  re-formed command: ALTER TABLE  public.test_add_constraint_using_index ADD CONSTRAINT test_add_constraint_used_index7 UNIQUE USING INDEX test_add_constraint_used_index7 DEFERRABLE INITIALLY DEFERRED
+-- ALTER CONSTRAINT constraint_name [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+CREATE TABLE test_alter_constraint_referenced(
+    id1 int UNIQUE,
+    id2 int UNIQUE,
+    id3 int UNIQUE,
+    id4 int UNIQUE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_constraint_referenced (id1 pg_catalog.int4 STORAGE PLAIN      , id2 pg_catalog.int4 STORAGE PLAIN      , id3 pg_catalog.int4 STORAGE PLAIN      , id4 pg_catalog.int4 STORAGE PLAIN      , CONSTRAINT test_alter_constraint_referenced_id1_key UNIQUE (id1), CONSTRAINT test_alter_constraint_referenced_id2_key UNIQUE (id2), CONSTRAINT test_alter_constraint_referenced_id3_key UNIQUE (id3), CONSTRAINT test_alter_constraint_referenced_id4_key UNIQUE (id4))      
+CREATE TABLE test_alter_constraint(
+    id1 int,
+    id2 int,
+    id3 int,
+    id4 int,
+    id5 int,
+    CONSTRAINT alter_cstr1 FOREIGN KEY (id1) REFERENCES test_alter_constraint_referenced (id1) DEFERRABLE INITIALLY DEFERRED,
+    CONSTRAINT alter_cstr2 FOREIGN KEY (id2) REFERENCES test_alter_constraint_referenced (id2) NOT DEFERRABLE,
+    CONSTRAINT alter_cstr3 FOREIGN KEY (id3) REFERENCES test_alter_constraint_referenced (id3) DEFERRABLE INITIALLY DEFERRED,
+    CONSTRAINT alter_cstr4 FOREIGN KEY (id4) REFERENCES test_alter_constraint_referenced (id4) DEFERRABLE INITIALLY IMMEDIATE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_constraint (id1 pg_catalog.int4 STORAGE PLAIN      , id2 pg_catalog.int4 STORAGE PLAIN      , id3 pg_catalog.int4 STORAGE PLAIN      , id4 pg_catalog.int4 STORAGE PLAIN      , id5 pg_catalog.int4 STORAGE PLAIN      )      
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint ADD CONSTRAINT alter_cstr1 FOREIGN KEY (id1) REFERENCES public.test_alter_constraint_referenced(id1) DEFERRABLE INITIALLY DEFERRED, ADD CONSTRAINT alter_cstr2 FOREIGN KEY (id2) REFERENCES public.test_alter_constraint_referenced(id2), ADD CONSTRAINT alter_cstr3 FOREIGN KEY (id3) REFERENCES public.test_alter_constraint_referenced(id3) DEFERRABLE INITIALLY DEFERRED, ADD CONSTRAINT alter_cstr4 FOREIGN KEY (id4) REFERENCES public.test_alter_constraint_referenced(id4) DEFERRABLE
+ALTER TABLE test_alter_constraint ALTER CONSTRAINT alter_cstr1 NOT DEFERRABLE;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER CONSTRAINT %{name}I %{deferrable}s %{init_deferred}s", "name": "alter_cstr1", "type": "alter constraint", "deferrable": "NOT DEFERRABLE", "init_deferred": "INITIALLY IMMEDIATE"}], "identity": {"objname": "test_alter_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint ALTER CONSTRAINT alter_cstr1 NOT DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE test_alter_constraint ALTER CONSTRAINT alter_cstr2 DEFERRABLE;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER CONSTRAINT %{name}I %{deferrable}s %{init_deferred}s", "name": "alter_cstr2", "type": "alter constraint", "deferrable": "DEFERRABLE", "init_deferred": "INITIALLY IMMEDIATE"}], "identity": {"objname": "test_alter_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint ALTER CONSTRAINT alter_cstr2 DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE test_alter_constraint ALTER CONSTRAINT alter_cstr3 DEFERRABLE INITIALLY IMMEDIATE;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER CONSTRAINT %{name}I %{deferrable}s %{init_deferred}s", "name": "alter_cstr3", "type": "alter constraint", "deferrable": "DEFERRABLE", "init_deferred": "INITIALLY IMMEDIATE"}], "identity": {"objname": "test_alter_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint ALTER CONSTRAINT alter_cstr3 DEFERRABLE INITIALLY IMMEDIATE
+ALTER TABLE test_alter_constraint ALTER CONSTRAINT alter_cstr4 DEFERRABLE INITIALLY DEFERRED;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER CONSTRAINT %{name}I %{deferrable}s %{init_deferred}s", "name": "alter_cstr4", "type": "alter constraint", "deferrable": "DEFERRABLE", "init_deferred": "INITIALLY DEFERRED"}], "identity": {"objname": "test_alter_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint ALTER CONSTRAINT alter_cstr4 DEFERRABLE INITIALLY DEFERRED
+-- VALIDATE CONSTRAINT constraint_name
+CREATE TABLE test_validate_constraint(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_validate_constraint (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_validate_constraint ADD CONSTRAINT test_validate_constraint_cstr CHECK (length(name) < 10) NOT VALID;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "test_validate_constraint_cstr", "type": "add constraint", "definition": "CHECK ((pg_catalog.length((name)::pg_catalog.text) OPERATOR(pg_catalog.<) 10)) NOT VALID"}], "identity": {"objname": "test_validate_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_validate_constraint ADD CONSTRAINT test_validate_constraint_cstr CHECK ((pg_catalog.length((name)::pg_catalog.text) OPERATOR(pg_catalog.<) 10)) NOT VALID
+ALTER TABLE test_validate_constraint VALIDATE CONSTRAINT test_validate_constraint_cstr;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "VALIDATE CONSTRAINT %{constraint}I", "type": "validate constraint", "constraint": "test_validate_constraint_cstr"}], "identity": {"objname": "test_validate_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_validate_constraint VALIDATE CONSTRAINT test_validate_constraint_cstr
+-- DROP CONSTRAINT [ IF EXISTS ]  constraint_name [ RESTRICT | CASCADE ]
+CREATE TABLE test_drop_constraint(
+    LIKE orders,
+    CONSTRAINT test_drop_constraint_check CHECK (id < 100),
+    CONSTRAINT test_drop_constraint_uniq UNIQUE (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_constraint (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_drop_constraint_check CHECK ((id OPERATOR(pg_catalog.<) 100)), CONSTRAINT test_drop_constraint_uniq UNIQUE (id))      
+CREATE TABLE test_drop_constraint_reference(
+    id int REFERENCES test_drop_constraint (id),
+    name varchar,
+    CONSTRAINT test_drop_constraint_reference_cstr1 CHECK (length(name) < 10)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_constraint_reference (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT test_drop_constraint_reference_cstr1 CHECK ((pg_catalog.length((name)::pg_catalog.text) OPERATOR(pg_catalog.<) 10)))      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "test_drop_constraint_reference_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id) REFERENCES public.test_drop_constraint(id)"}], "identity": {"objname": "test_drop_constraint_reference", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_constraint_reference ADD CONSTRAINT test_drop_constraint_reference_id_fkey FOREIGN KEY (id) REFERENCES public.test_drop_constraint(id)
+ALTER TABLE test_drop_constraint_reference DROP CONSTRAINT test_drop_constraint_reference_cstr1;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP CONSTRAINT %{if_exists}s %{constraint}I %{cascade}s", "type": "drop constraint", "cascade": "", "if_exists": "", "constraint": "test_drop_constraint_reference_cstr1"}], "identity": {"objname": "test_drop_constraint_reference", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_constraint_reference DROP CONSTRAINT  test_drop_constraint_reference_cstr1 
+ALTER TABLE test_drop_constraint DROP CONSTRAINT test_drop_constraint_check RESTRICT;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP CONSTRAINT %{if_exists}s %{constraint}I %{cascade}s", "type": "drop constraint", "cascade": "", "if_exists": "", "constraint": "test_drop_constraint_check"}], "identity": {"objname": "test_drop_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_constraint DROP CONSTRAINT  test_drop_constraint_check 
+ALTER TABLE test_drop_constraint DROP CONSTRAINT IF EXISTS test_drop_constraint_check RESTRICT;
+NOTICE:  constraint "test_drop_constraint_check" of relation "test_drop_constraint" does not exist, skipping
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP CONSTRAINT %{if_exists}s %{constraint}I %{cascade}s", "type": "drop constraint", "cascade": "", "if_exists": "IF EXISTS", "constraint": "test_drop_constraint_check"}], "identity": {"objname": "test_drop_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_constraint DROP CONSTRAINT IF EXISTS test_drop_constraint_check 
+-- TOFIX
+-- ALTER TABLE test_drop_constraint DROP CONSTRAINT test_drop_constraint_uniq CASCADE;
+-- TODO: This should be tested with TRIGGER related testing
+-- DISABLE TRIGGER [ trigger_name | ALL | USER ]
+-- ENABLE TRIGGER [ trigger_name | ALL | USER ]
+-- ENABLE REPLICA TRIGGER trigger_name
+-- ENABLE ALWAYS TRIGGER trigger_name
+-- DISABLE RULE rewrite_rule_name
+ALTER TABLE test_disable_rule DISABLE RULE sample_rule1;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DISABLE RULE %{rule}I", "rule": "sample_rule1", "type": "disable rule"}], "identity": {"objname": "test_disable_rule", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_disable_rule DISABLE RULE sample_rule1
+-- ENABLE RULE rewrite_rule_name
+ALTER TABLE test_enable_rule DISABLE RULE sample_rule2;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DISABLE RULE %{rule}I", "rule": "sample_rule2", "type": "disable rule"}], "identity": {"objname": "test_enable_rule", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_enable_rule DISABLE RULE sample_rule2
+ALTER TABLE test_enable_rule ENABLE RULE sample_rule2;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ENABLE RULE %{rule}I", "rule": "sample_rule2", "type": "enable rule"}], "identity": {"objname": "test_enable_rule", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_enable_rule ENABLE RULE sample_rule2
+-- ENABLE REPLICA RULE rewrite_rule_name
+ALTER TABLE test_enable_replica_rule ENABLE REPLICA RULE sample_rule_enable_replica;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ENABLE REPLICA RULE %{rule}I", "rule": "sample_rule_enable_replica", "type": "enable replica rule"}], "identity": {"objname": "test_enable_replica_rule", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_enable_replica_rule ENABLE REPLICA RULE sample_rule_enable_replica
+-- ENABLE ALWAYS RULE rewrite_rule_name
+ALTER TABLE test_enable_always_rule ENABLE REPLICA RULE sample_rule_enable_always;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ENABLE REPLICA RULE %{rule}I", "rule": "sample_rule_enable_always", "type": "enable replica rule"}], "identity": {"objname": "test_enable_always_rule", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_enable_always_rule ENABLE REPLICA RULE sample_rule_enable_always
+-- DISABLE ROW LEVEL SECURITY
+CREATE TABLE test_disable_row_security(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_disable_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_disable_row_security DISABLE ROW LEVEL SECURITY;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DISABLE ROW LEVEL SECURITY", "type": "disable row security"}], "identity": {"objname": "test_disable_row_security", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_disable_row_security DISABLE ROW LEVEL SECURITY
+-- ENABLE ROW LEVEL SECURITY
+CREATE TABLE test_enable_row_security(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_enable_row_security ENABLE ROW LEVEL SECURITY;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ENABLE ROW LEVEL SECURITY", "type": "enable row security"}], "identity": {"objname": "test_enable_row_security", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_enable_row_security ENABLE ROW LEVEL SECURITY
+-- FORCE ROW LEVEL SECURITY
+CREATE TABLE test_force_row_security(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_force_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_force_row_security FORCE ROW LEVEL SECURITY;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "FORCE ROW LEVEL SECURITY"}], "identity": {"objname": "test_force_row_security", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_force_row_security FORCE ROW LEVEL SECURITY
+-- NO FORCE ROW LEVEL SECURITY
+CREATE TABLE test_no_force_row_security(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_no_force_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_no_force_row_security NO FORCE ROW LEVEL SECURITY;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "NO FORCE ROW LEVEL SECURITY"}], "identity": {"objname": "test_no_force_row_security", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_no_force_row_security NO FORCE ROW LEVEL SECURITY
+-- CLUSTER ON index_name
+CREATE TABLE test_cluster(
+    LIKE orders,
+    PRIMARY KEY (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_cluster (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_cluster_pkey PRIMARY KEY (id))      
+ALTER TABLE test_cluster CLUSTER ON test_cluster_pkey;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "CLUSTER ON %{index}I", "type": "cluster on", "index": "test_cluster_pkey"}], "identity": {"objname": "test_cluster", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_cluster CLUSTER ON test_cluster_pkey
+-- SET WITHOUT CLUSTER
+CREATE TABLE test_without_cluster(
+    LIKE orders,
+    PRIMARY KEY (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_without_cluster (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_without_cluster_pkey PRIMARY KEY (id))      
+ALTER TABLE test_without_cluster CLUSTER ON test_without_cluster_pkey;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "CLUSTER ON %{index}I", "type": "cluster on", "index": "test_without_cluster_pkey"}], "identity": {"objname": "test_without_cluster", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_without_cluster CLUSTER ON test_without_cluster_pkey
+ALTER TABLE test_without_cluster SET WITHOUT CLUSTER;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET WITHOUT CLUSTER", "type": "set without cluster"}], "identity": {"objname": "test_without_cluster", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_without_cluster SET WITHOUT CLUSTER
+-- SET WITHOUT OIDS
+CREATE TABLE test_set_without_oids(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_without_oids (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_without_oids SET WITHOUT OIDS;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET WITHOUT OIDS", "type": "set without oids"}], "identity": {"objname": "test_set_without_oids", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_without_oids SET WITHOUT OIDS
+-- SET ACCESS METHOD new_access_method
+CREATE TABLE test_set_access_method(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_access_method (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_access_method SET ACCESS METHOD heap;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET ACCESS METHOD %{access_method}I", "type": "set access method", "access_method": "heap"}], "identity": {"objname": "test_set_access_method", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_access_method SET ACCESS METHOD heap
+-- SET TABLESPACE new_tablespace
+CREATE TABLE test_set_tablespace(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_tablespace (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_tablespace SET TABLESPACE pg_default;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET TABLESPACE %{tablespace}I", "type": "set tablespace", "tablespace": "pg_default"}], "identity": {"objname": "test_set_tablespace", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_tablespace SET TABLESPACE pg_default
+-- SET { LOGGED | UNLOGGED }
+CREATE TABLE test_set_logged(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_logged (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_logged SET LOGGED;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET LOGGED", "type": "set logged"}], "identity": {"objname": "test_set_logged", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_logged SET LOGGED
+CREATE TABLE test_set_unlogged(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_unlogged (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_unlogged SET UNLOGGED;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET UNLOGGED", "type": "set unlogged"}], "identity": {"objname": "test_set_unlogged", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_unlogged SET UNLOGGED
+-- SET ( storage_parameter [= value] [, ... ] )
+CREATE TABLE test_set_storage_params1(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_storage_params1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_storage_params1 SET (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true);
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage_params1 SET (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')
+CREATE TABLE test_set_storage_params2(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_storage_params2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_storage_params2 SET (vacuum_index_cleanup = ON);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "%{set_reset}s (%{options:, }s)", "options": [{"fmt": "%{label}s = %{value}L", "label": {"fmt": "%{label}I", "label": "vacuum_index_cleanup"}, "value": "on"}], "set_reset": "SET"}], "identity": {"objname": "test_set_storage_params2", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage_params2 SET (vacuum_index_cleanup = 'on')
+-- RESET ( storage_parameter [, ... ] )
+CREATE TABLE test_reset_storage_params1(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_reset_storage_params1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_storage_params1 SET (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true);
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage_params1 SET (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')
+ALTER TABLE test_reset_storage_params1 RESET (vacuum_index_cleanup, autovacuum_vacuum_scale_factor, vacuum_truncate);
+NOTICE:  re-formed command: ALTER TABLE  public.test_reset_storage_params1 RESET (vacuum_index_cleanup, autovacuum_vacuum_scale_factor, vacuum_truncate)
+CREATE TABLE test_reset_storage_params2(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_reset_storage_params2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_storage_params2 SET (vacuum_index_cleanup = ON);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "%{set_reset}s (%{options:, }s)", "options": [{"fmt": "%{label}s = %{value}L", "label": {"fmt": "%{label}I", "label": "vacuum_index_cleanup"}, "value": "on"}], "set_reset": "SET"}], "identity": {"objname": "test_set_storage_params2", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage_params2 SET (vacuum_index_cleanup = 'on')
+ALTER TABLE test_reset_storage_params2 RESET (vacuum_index_cleanup);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "%{set_reset}s (%{options:, }s)", "options": [{"fmt": "%{label}s", "label": {"fmt": "%{label}I", "label": "vacuum_index_cleanup"}}], "set_reset": "RESET"}], "identity": {"objname": "test_reset_storage_params2", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_reset_storage_params2 RESET (vacuum_index_cleanup)
+-- INHERIT parent_table
+CREATE TABLE test_inherit_parent(
+    parent_id int
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_inherit_parent (parent_id pg_catalog.int4 STORAGE PLAIN      )      
+CREATE TABLE test_inherit_child(
+    parent_id int,
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_inherit_child (parent_id pg_catalog.int4 STORAGE PLAIN      , id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_inherit_child INHERIT test_inherit_parent;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "INHERIT %{parent}D", "type": "inherit", "parent": {"objname": "test_inherit_parent", "schemaname": "public"}}], "identity": {"objname": "test_inherit_child", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_inherit_child INHERIT public.test_inherit_parent
+-- NO INHERIT parent_table
+CREATE TABLE test_no_inherit_parent(
+    parent_id int
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_no_inherit_parent (parent_id pg_catalog.int4 STORAGE PLAIN      )      
+CREATE TABLE test_no_inherit_child(
+    LIKE orders
+) INHERITS (test_no_inherit_parent);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_no_inherit_child (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      ) INHERITS (public.test_no_inherit_parent)     
+ALTER TABLE test_no_inherit_child NO INHERIT test_no_inherit_parent;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "NO INHERIT %{parent}D", "type": "drop inherit", "parent": {"objname": "test_no_inherit_parent", "schemaname": "public"}}], "identity": {"objname": "test_no_inherit_child", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_no_inherit_child NO INHERIT public.test_no_inherit_parent
+-- OF type_name
+CREATE TABLE test_type(
+    id int,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_type (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+ALTER TABLE test_type OF test_type_product_type;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "OF %{type_of}T", "type": "add of", "type_of": {"typmod": "", "typarray": false, "typename": "test_type_product_type", "schemaname": "public"}}], "identity": {"objname": "test_type", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_type OF public.test_type_product_type
+-- NOT OF
+CREATE TABLE test_type_not_of OF test_type_product_type;
+NOTICE:  re-formed command: CREATE  TABLE  public.test_type_not_of OF public.test_type_product_type      
+ALTER TABLE test_type_not_of NOT OF;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "NOT OF", "type": "not of"}], "identity": {"objname": "test_type_not_of", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_type_not_of NOT OF
+-- TODO: This should be tested with ROLE/USER related testing
+-- OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
+-- REPLICA IDENTITY { DEFAULT | USING INDEX index_name | FULL | NOTHING }
+CREATE TABLE test_replica_identity1(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_replica_identity1 REPLICA IDENTITY DEFAULT;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "REPLICA IDENTITY %{ident}s", "type": "replica identity", "ident": "DEFAULT"}], "identity": {"objname": "test_replica_identity1", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_replica_identity1 REPLICA IDENTITY DEFAULT
+CREATE TABLE test_replica_identity2(
+    LIKE orders,
+    PRIMARY KEY (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_replica_identity2_pkey PRIMARY KEY (id))      
+ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_pkey;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "REPLICA IDENTITY %{ident}s", "type": "replica identity", "ident": {"fmt": "USING INDEX %{index}I", "index": "test_replica_identity2_pkey"}}], "identity": {"objname": "test_replica_identity2", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_pkey
+CREATE TABLE test_replica_identity3(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity3 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_replica_identity3 REPLICA IDENTITY FULL;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "REPLICA IDENTITY %{ident}s", "type": "replica identity", "ident": "FULL"}], "identity": {"objname": "test_replica_identity3", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_replica_identity3 REPLICA IDENTITY FULL
+CREATE TABLE test_replica_identity4(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity4 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_replica_identity4 REPLICA IDENTITY NOTHING;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "REPLICA IDENTITY %{ident}s", "type": "replica identity", "ident": "NOTHING"}], "identity": {"objname": "test_replica_identity4", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_replica_identity4 REPLICA IDENTITY NOTHING
+-- RENAME [ COLUMN ] column_name TO new_column_name
+CREATE TABLE test_alter_col_name(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_col_name (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_alter_col_name RENAME id TO new_id;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{if_exists}s %{identity}D RENAME COLUMN %{colname}I TO %{newname}I", "colname": "id", "newname": "new_id", "objtype": "TABLE", "identity": {"objname": "test_alter_col_name", "schemaname": "public"}, "if_exists": ""}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_col_name RENAME COLUMN id TO new_id
+ALTER TABLE test_alter_col_name RENAME COLUMN name TO new_name;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{if_exists}s %{identity}D RENAME COLUMN %{colname}I TO %{newname}I", "colname": "name", "newname": "new_name", "objtype": "TABLE", "identity": {"objname": "test_alter_col_name", "schemaname": "public"}, "if_exists": ""}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_col_name RENAME COLUMN name TO new_name
+-- RENAME CONSTRAINT constraint_name TO new_constraint_name
+CREATE TABLE test_alter_constraint_name(
+    LIKE orders,
+    CONSTRAINT test_alter_constraint_name_old CHECK (id > 10)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_constraint_name (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_alter_constraint_name_old CHECK ((id OPERATOR(pg_catalog.>) 10)))      
+ALTER TABLE test_alter_constraint_name RENAME CONSTRAINT test_alter_constraint_name_old TO test_alter_constraint_name_new;
+NOTICE:  deparsed json: {"fmt": "ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I", "only": "", "newname": "test_alter_constraint_name_new", "oldname": "test_alter_constraint_name_old", "identity": {"objname": "test_alter_constraint_name", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint_name RENAME CONSTRAINT test_alter_constraint_name_old TO test_alter_constraint_name_new
+-- RENAME TO new_name
+CREATE TABLE test_rename_table(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_rename_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_rename_table RENAME to new_test_rename_table;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I", "newname": "new_test_rename_table", "objtype": "TABLE", "identity": {"objname": "test_rename_table", "schemaname": "public"}, "if_exists": ""}
+NOTICE:  re-formed command: ALTER TABLE  public.test_rename_table RENAME TO new_test_rename_table
+-- SET SCHEMA new_schema
+CREATE TABLE test_set_schema(
+    LIKE orders
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_schema (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+ALTER TABLE test_set_schema SET SCHEMA new_test_schema;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{identity}s SET SCHEMA %{newschema}I", "objtype": "TABLE", "identity": "public.test_set_schema", "newschema": "new_test_schema"}
+NOTICE:  re-formed command: ALTER TABLE public.test_set_schema SET SCHEMA new_test_schema
+-- ALTER TABLE ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ]
+--     SET TABLESPACE new_tablespace [ NOWAIT ]
+-- TOFIX: can not be caught by ddl_command_end event trigger.
+-- Deparse of T_AlterTableMoveAllStmt is not supported,
+-- TABLESPACE commands (global object commands) are also not supported.
+-- ALTER TABLE ALL IN TABLESPACE pg_default SET TABLESPACE pg_default;
+-- ALTER TABLE ALL IN TABLESPACE pg_default OWNED BY ddl_testing_role SET TABLESPACE pg_default;
+-- ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT }
+CREATE TABLE test_partition_attach_range(
+    LIKE orders
+) PARTITION BY RANGE (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_range (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY RANGE (id)    
+CREATE TABLE test_partition_attach_range_p_1(
+    LIKE test_partition_attach_range
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_range_p_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+-- TOFIX
+-- ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_1 DEFAULT;
+CREATE TABLE test_partition_attach_range_p_2(
+    LIKE test_partition_attach_range
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_range_p_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+-- TOFIX
+-- ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_2 FOR VALUES FROM (100) TO (200);
+CREATE TABLE test_partition_attach_hash(
+    LIKE orders
+) PARTITION BY HASH (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_hash (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY HASH (id)    
+CREATE TABLE test_partition_attach_hash_p(
+    LIKE test_partition_attach_hash
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_hash_p (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+-- TOFIX
+-- ALTER TABLE test_partition_attach_hash ATTACH PARTITION test_partition_attach_hash_p FOR VALUES WITH (MODULUS 10, REMAINDER 1);
+CREATE TABLE test_partition_attach_list(
+    LIKE orders
+) PARTITION BY LIST (name);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY LIST (name)    
+CREATE TABLE test_partition_attach_list_p1(
+    LIKE test_partition_attach_list
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_list_p1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+CREATE TABLE test_partition_attach_list_p2(
+    LIKE test_partition_attach_list
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_list_p2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+-- TOFIX
+-- ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p1 FOR VALUES IN ('key1');
+-- ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p2 FOR VALUES IN ('key2', 'key3');
+-- DETACH PARTITION partition_name [ CONCURRENTLY | FINALIZE ]
+CREATE TABLE test_detach_partition(
+    LIKE orders
+) PARTITION BY RANGE (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY RANGE (id)    
+CREATE TABLE test_detach_partition_p1 PARTITION OF test_detach_partition FOR VALUES FROM (1) TO (100);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition_p1 PARTITION OF public.test_detach_partition  FOR VALUES FROM (1) TO (100)     
+CREATE TABLE test_detach_partition_p2 PARTITION OF test_detach_partition FOR VALUES FROM (101) TO (200);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition_p2 PARTITION OF public.test_detach_partition  FOR VALUES FROM (101) TO (200)     
+CREATE TABLE test_detach_partition_p3 PARTITION OF test_detach_partition FOR VALUES FROM (201) TO (300);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition_p3 PARTITION OF public.test_detach_partition  FOR VALUES FROM (201) TO (300)     
+-- TOFIX
+-- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p1;
+-- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p2 CONCURRENTLY;
+-- TOFIX: FINALIZE option is not testable
+-- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p3 FINALIZE;
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/constraints.out b/src/test/modules/test_ddl_deparse_regress/expected/constraints.out
new file mode 100644
index 0000000000..45058db174
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/expected/constraints.out
@@ -0,0 +1,562 @@
+-- column constraint, index_parameters
+-- [ CONSTRAINT constraint_name ]
+-- { NOT NULL |
+CREATE TABLE col_cstr_not_null(
+    id int CONSTRAINT id_constraint NOT NULL,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_not_null (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+--  NULL |
+CREATE TABLE col_cstr_null(
+    id int NULL,
+    name varchar CONSTRAINT name_constraint NOT NULL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default" NOT NULL   )      
+--  CHECK ( expression ) [ NO INHERIT ] |
+CREATE TABLE col_cstr_check(
+    id int CHECK (id > 10),
+    name varchar NOT NULL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_check (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default" NOT NULL   , CONSTRAINT col_cstr_check_id_check CHECK ((id OPERATOR(pg_catalog.>) 10)))      
+CREATE TABLE col_cstr_check_no_inherit(
+    id int CHECK (id > 10) NO INHERIT,
+    name varchar NOT NULL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_check_no_inherit (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default" NOT NULL   , CONSTRAINT col_cstr_check_no_inherit_id_check CHECK ((id OPERATOR(pg_catalog.>) 10)) NO INHERIT)      
+--  DEFAULT default_expr |
+CREATE TABLE col_cstr_default(
+    id int NOT NULL,
+    name varchar DEFAULT 'foo'
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_default (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"  DEFAULT 'foo'::character varying  )      
+--  GENERATED ALWAYS AS ( generation_expr ) STORED |
+CREATE TABLE col_cstr_generated_always_as(
+    id int NOT NULL,
+    id_generated int GENERATED ALWAYS AS ( id * 10 ) STORED
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_generated_always_as (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , id_generated pg_catalog.int4 STORAGE PLAIN      GENERATED ALWAYS AS ((id OPERATOR(pg_catalog.*) 10)) STORED)      
+--  GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ] |
+CREATE TABLE col_cstr_generated_always_as_identity(
+    id int NOT NULL,
+    id_generated int GENERATED ALWAYS AS IDENTITY
+);
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_generated_always_as_identity (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , id_generated pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )      
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+CREATE TABLE col_cstr_generated_by_default_as_identity_with_options(
+    id int NOT NULL,
+    id_generated int GENERATED BY DEFAULT AS IDENTITY ( INCREMENT BY 10 )
+);
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_generated_by_default_as_identity_with_options (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , id_generated pg_catalog.int4 STORAGE PLAIN     GENERATED BY DEFAULT AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 10 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )      
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+--  UNIQUE [ NULLS [ NOT ] DISTINCT ] |
+CREATE TABLE col_cstr_unique(
+    id int NOT NULL,
+    name varchar UNIQUE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_unique (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_unique_name_key UNIQUE (name))      
+CREATE TABLE col_cstr_unique_nulls_distinct(
+    id int NOT NULL,
+    name varchar UNIQUE NULLS DISTINCT
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_unique_nulls_distinct (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_unique_nulls_distinct_name_key UNIQUE (name))      
+CREATE TABLE col_cstr_unique_nulls_not_distinct(
+    id int NOT NULL,
+    name varchar UNIQUE NULLS NOT DISTINCT
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_unique_nulls_not_distinct (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_unique_nulls_not_distinct_name_key UNIQUE NULLS NOT DISTINCT (name))      
+--  PRIMARY KEY |
+CREATE TABLE col_cstr_primary_key(
+    id int PRIMARY KEY,
+    name varchar UNIQUE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_primary_key (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_primary_key_name_key UNIQUE (name), CONSTRAINT col_cstr_primary_key_pkey PRIMARY KEY (id))      
+--  REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+--    [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
+CREATE TABLE col_cstr_reference_table_default(
+    id int REFERENCES col_cstr_primary_key,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_default_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id) REFERENCES public.col_cstr_primary_key(id)"}], "identity": {"objname": "col_cstr_reference_table_default", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_default ADD CONSTRAINT col_cstr_reference_table_default_id_fkey FOREIGN KEY (id) REFERENCES public.col_cstr_primary_key(id)
+CREATE TABLE col_cstr_reference_table_column(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)"}], "identity": {"objname": "col_cstr_reference_table_column", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column ADD CONSTRAINT col_cstr_reference_table_column_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)
+-- [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+-- skip testing MATCH PARTIAL, which is treated as a syntax error with message
+-- ERROR:  MATCH PARTIAL not yet implemented
+CREATE TABLE col_cstr_reference_table_column_match_full(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) MATCH FULL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_match_full (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_match_full_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL"}], "identity": {"objname": "col_cstr_reference_table_column_match_full", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_match_full ADD CONSTRAINT col_cstr_reference_table_column_match_full_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL
+CREATE TABLE col_cstr_reference_table_column_match_simple(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) MATCH SIMPLE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_match_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_match_simple_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)"}], "identity": {"objname": "col_cstr_reference_table_column_match_simple", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_match_simple ADD CONSTRAINT col_cstr_reference_table_column_match_simple_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)
+-- [ ON DELETE referential_action ]
+CREATE TABLE col_cstr_reference_table_column_on_delete_no_action(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE NO ACTION
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_no_action (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_no_action_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_no_action", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_no_action ADD CONSTRAINT col_cstr_reference_table_column_on_delete_no_action_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)
+CREATE TABLE col_cstr_reference_table_column_on_delete_restrict(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE RESTRICT
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_restrict (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_restrict_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE RESTRICT"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_restrict", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_restrict ADD CONSTRAINT col_cstr_reference_table_column_on_delete_restrict_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE RESTRICT
+CREATE TABLE col_cstr_reference_table_column_on_delete_cascade(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE CASCADE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_cascade (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_cascade_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE CASCADE"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_cascade", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_cascade ADD CONSTRAINT col_cstr_reference_table_column_on_delete_cascade_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE CASCADE
+CREATE TABLE col_cstr_reference_table_column_on_delete_set_null(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET NULL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_set_null_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET NULL"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_set_null", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_set_null ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_null_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET NULL
+CREATE TABLE col_cstr_reference_table_column_on_delete_set_null_with_column(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET NULL (name),
+    foo varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET NULL (foo)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_null_with_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , foo pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_set_null_with_column ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_null_wi_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET NULL (name), ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_null_wit_foo_fkey FOREIGN KEY (foo) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET NULL (foo)
+CREATE TABLE col_cstr_reference_table_column_on_delete_set_default(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET DEFAULT
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_set_default_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET DEFAULT"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_set_default", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_set_default ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_default_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET DEFAULT
+CREATE TABLE col_cstr_reference_table_column_on_delete_set_default_with_col(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET DEFAULT (name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_default_with_col (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_set_default_with_col ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_defaul_name_fkey1 FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET DEFAULT (name)
+-- [ ON UPDATE referential_action ]
+CREATE TABLE col_cstr_reference_table_column_on_update_no_action(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE NO ACTION
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_no_action (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_no_action_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_no_action", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_no_action ADD CONSTRAINT col_cstr_reference_table_column_on_update_no_action_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)
+CREATE TABLE col_cstr_reference_table_column_on_update_restrict(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE RESTRICT
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_restrict (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_restrict_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE RESTRICT"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_restrict", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_restrict ADD CONSTRAINT col_cstr_reference_table_column_on_update_restrict_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE RESTRICT
+CREATE TABLE col_cstr_reference_table_column_on_update_cascade(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE CASCADE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_cascade (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_cascade_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE CASCADE"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_cascade", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_cascade ADD CONSTRAINT col_cstr_reference_table_column_on_update_cascade_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE CASCADE
+CREATE TABLE col_cstr_reference_table_column_on_update_set_null(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE SET NULL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_set_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_set_null_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE SET NULL"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_set_null", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_set_null ADD CONSTRAINT col_cstr_reference_table_column_on_update_set_null_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE SET NULL
+CREATE TABLE col_cstr_reference_table_column_on_update_set_default(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE SET DEFAULT
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_set_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_set_default_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE SET DEFAULT"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_set_default", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_set_default ADD CONSTRAINT col_cstr_reference_table_column_on_update_set_default_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE SET DEFAULT
+-- complex combinations
+CREATE TABLE col_cstr_reference_table_column_complex_combination1(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) MATCH FULL ON DELETE NO ACTION ON UPDATE NO ACTION
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_complex_combination1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_complex_combination1_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL"}], "identity": {"objname": "col_cstr_reference_table_column_complex_combination1", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_complex_combination1 ADD CONSTRAINT col_cstr_reference_table_column_complex_combination1_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL
+CREATE TABLE col_cstr_reference_table_column_complex_combination2(
+    id int REFERENCES col_cstr_primary_key MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET NULL,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_complex_combination2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_complex_combination2 ADD CONSTRAINT col_cstr_reference_table_column_complex_combination2_id_fkey FOREIGN KEY (id) REFERENCES public.col_cstr_primary_key(id) MATCH FULL ON UPDATE SET NULL ON DELETE SET DEFAULT
+-- [ DEFERRABLE | NOT DEFERRABLE ]
+CREATE TABLE col_cstr_deferable(
+    id int,
+    name varchar UNIQUE DEFERRABLE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_deferable_name_key UNIQUE (name) DEFERRABLE)      
+CREATE TABLE col_cstr_not_deferable(
+    id int PRIMARY KEY NOT DEFERRABLE,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_not_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_not_deferable_pkey PRIMARY KEY (id))      
+-- [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+CREATE TABLE col_cstr_initially_deferred(
+    id int PRIMARY KEY INITIALLY DEFERRED,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_initially_deferred (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_initially_deferred_pkey PRIMARY KEY (id) DEFERRABLE INITIALLY DEFERRED)      
+CREATE TABLE col_cstr_initially_immediate(
+    id int,
+    name varchar UNIQUE INITIALLY IMMEDIATE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_initially_immediate (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_initially_immediate_name_key UNIQUE (name))      
+-- complex combination
+CREATE TABLE col_cstr_complex_combination(
+    id int,
+    name varchar CONSTRAINT name_constraint REFERENCES col_cstr_primary_key (name) MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_complex_combination (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "name_constraint", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL ON UPDATE SET NULL ON DELETE SET DEFAULT"}], "identity": {"objname": "col_cstr_complex_combination", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_complex_combination ADD CONSTRAINT name_constraint FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL ON UPDATE SET NULL ON DELETE SET DEFAULT
+-- part 4: table constraints
+-- [ CONSTRAINT constraint_name ]
+-- { CHECK ( expression ) [ NO INHERIT ] |
+CREATE TABLE tbl_cstr_check_1(
+    CONSTRAINT id_constraint CHECK (id > 10),
+    id int,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_check_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT id_constraint CHECK ((id OPERATOR(pg_catalog.>) 10)))      
+CREATE TABLE tbl_cstr_check_2(
+    id int,
+    name varchar,
+    CONSTRAINT table_check CHECK (id > 10) NO INHERIT
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_check_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT table_check CHECK ((id OPERATOR(pg_catalog.>) 10)) NO INHERIT)      
+CREATE TABLE tbl_cstr_check_no_inherit(
+    id int,
+    name varchar,
+    CHECK (id > 10) NO INHERIT
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_check_no_inherit (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_check_no_inherit_id_check CHECK ((id OPERATOR(pg_catalog.>) 10)) NO INHERIT)      
+--   UNIQUE [ NULLS [ NOT ] DISTINCT ] ( column_name [, ... ] ) [ INCLUDE ( column_name [, ...]) ] |
+CREATE TABLE tbl_cstr_unique(
+    id int,
+    name varchar,
+    UNIQUE (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_id_key UNIQUE (id))      
+CREATE TABLE tbl_cstr_unique_multicols(
+    id int,
+    name varchar,
+    UNIQUE (id, name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_multicols (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_multicols_id_name_key UNIQUE (id, name))      
+CREATE TABLE tbl_cstr_unique_nulls_distinct(
+    id int,
+    name varchar,
+    UNIQUE NULLS DISTINCT (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_distinct (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_distinct_id_key UNIQUE (id))      
+CREATE TABLE tbl_cstr_unique_nulls_not_distinct(
+    id int,
+    name varchar,
+    UNIQUE NULLS NOT DISTINCT (id, name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_not_distinct (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_not_distinct_id_name_key UNIQUE NULLS NOT DISTINCT (id, name))      
+CREATE TABLE tbl_cstr_unique_nulls_distinct_include(
+    id int,
+    name varchar,
+    UNIQUE NULLS DISTINCT (id) INCLUDE (name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_distinct_include (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_distinct_include_id_name_key UNIQUE (id) INCLUDE (name))      
+CREATE TABLE tbl_cstr_unique_nulls_distinct_include_multi(
+    id int,
+    name varchar,
+    info varchar,
+    UNIQUE NULLS DISTINCT (id) INCLUDE (name, info)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_distinct_include_multi (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , info pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_distinct_include_multi_id_name_info_key UNIQUE (id) INCLUDE (name, info))      
+-- PRIMARY KEY ( column_name [, ... ] ) [ INCLUDE ( column_name [, ...]) ] |
+CREATE TABLE tbl_cstr_primary_key(
+    id int,
+    name varchar,
+    PRIMARY KEY (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_pkey PRIMARY KEY (id))      
+CREATE TABLE tbl_cstr_primary_key_multicols(
+    id int,
+    name varchar,
+    PRIMARY KEY (id, name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key_multicols (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_multicols_pkey PRIMARY KEY (id, name))      
+CREATE TABLE tbl_cstr_primary_key_include(
+    id int,
+    name varchar,
+    PRIMARY KEY (id) INCLUDE (name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key_include (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_include_pkey PRIMARY KEY (id) INCLUDE (name))      
+CREATE TABLE tbl_cstr_primary_key_include_multicols(
+    id int,
+    name varchar,
+    info varchar,
+    PRIMARY KEY (id) INCLUDE (name, info)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key_include_multicols (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , info pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_include_multicols_pkey PRIMARY KEY (id) INCLUDE (name, info))      
+--   EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] |
+CREATE TABLE tbl_cstr_exclude(
+    id int,
+    name varchar,
+    EXCLUDE (name WITH =)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_name_excl EXCLUDE USING btree (name WITH OPERATOR(pg_catalog.=)))      
+CREATE TABLE tbl_cstr_exclude_multi(
+    id int,
+    name varchar,
+    EXCLUDE ((id*10) with =, name WITH =)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_multi (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_multi_expr_name_excl EXCLUDE USING btree (((id OPERATOR(pg_catalog.*) 10)) WITH OPERATOR(pg_catalog.=), name WITH OPERATOR(pg_catalog.=)))      
+CREATE TABLE tbl_cstr_exclude_index_method(
+    id int,
+    name varchar,
+    EXCLUDE USING btree ((id*10) with =, name WITH =)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_index_method (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_index_method_expr_name_excl EXCLUDE USING btree (((id OPERATOR(pg_catalog.*) 10)) WITH OPERATOR(pg_catalog.=), name WITH OPERATOR(pg_catalog.=)))      
+-- [ INCLUDE ( column_name [, ... ] ) ]
+CREATE TABLE tbl_cstr_exclude_with_index_params_include_1(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) INCLUDE (name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_include_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_include_1_id_name_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) INCLUDE (name))      
+CREATE TABLE tbl_cstr_exclude_with_index_params_include_2(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) INCLUDE (id, name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_include_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_include_2_id_id1_name_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) INCLUDE (id, name))      
+-- [ WITH ( storage_parameter [= value] [, ... ] ) ]
+CREATE TABLE tbl_cstr_exclude_with_index_params_storage_1(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) WITH (fillfactor = 20)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_storage_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_storage_1_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) WITH (fillfactor='20'))      
+CREATE TABLE tbl_cstr_exclude_with_index_params_storage_2(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) WITH (fillfactor = 20, deduplicate_items = false)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_storage_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_storage_2_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) WITH (fillfactor='20', deduplicate_items='false'))      
+-- [ USING INDEX TABLESPACE tablespace_name ]
+CREATE TABLE tbl_cstr_exclude_with_index_params_tablespace(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) USING INDEX TABLESPACE pg_default
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_tablespace (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_tablespace_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)))      
+-- index_parameters complex combination
+CREATE TABLE tbl_cstr_exclude_with_index_params_complex(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) INCLUDE (id, name) WITH (fillfactor = 20, deduplicate_items = false) USING INDEX TABLESPACE pg_default
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_complex (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_complex_id_id1_name_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) INCLUDE (id, name) WITH (fillfactor='20', deduplicate_items='false'))      
+-- [ WHERE ( predicate ) ]
+CREATE TABLE tbl_cstr_exclude_with_predicate(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) WHERE (name<>'foo')
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_predicate (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_predicate_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) WHERE (((name)::pg_catalog.text OPERATOR(pg_catalog.<>) 'foo'::pg_catalog.text)))      
+-- complex combination for table constraint clauses
+CREATE TABLE tbl_cstr_exclude_complex_combination(
+    id int,
+    name varchar,
+    EXCLUDE USING btree (id WITH =, name WITH =) INCLUDE (id, name) WITH (fillfactor = 20, deduplicate_items = false) USING INDEX TABLESPACE pg_default WHERE (name<>'foo')
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_complex_combination (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_complex_combination_id_name_id1_name1_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=), name WITH OPERATOR(pg_catalog.=)) INCLUDE (id, name) WITH (fillfactor='20', deduplicate_items='false') WHERE (((name)::pg_catalog.text OPERATOR(pg_catalog.<>) 'foo'::pg_catalog.text)))      
+-- FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
+--     [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
+CREATE TABLE tbl_cstr_foreign_table(
+    id int PRIMARY KEY,
+    name varchar UNIQUE,
+    UNIQUE (id, name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_foreign_table_id_name_key UNIQUE (id, name), CONSTRAINT tbl_cstr_foreign_table_name_key UNIQUE (name), CONSTRAINT tbl_cstr_foreign_table_pkey PRIMARY KEY (id))      
+CREATE TABLE tbl_cstr_foreign_key_simple_1(
+    id int,
+    name varchar,
+    FOREIGN KEY (id) REFERENCES tbl_cstr_foreign_table
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_key_simple_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr_foreign_key_simple_1_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id) REFERENCES public.tbl_cstr_foreign_table(id)"}], "identity": {"objname": "tbl_cstr_foreign_key_simple_1", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_foreign_key_simple_1 ADD CONSTRAINT tbl_cstr_foreign_key_simple_1_id_fkey FOREIGN KEY (id) REFERENCES public.tbl_cstr_foreign_table(id)
+CREATE TABLE tbl_cstr_foreign_key_simple_2(
+    id int,
+    name varchar,
+    FOREIGN KEY (id) REFERENCES tbl_cstr_foreign_table(id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_key_simple_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr_foreign_key_simple_2_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id) REFERENCES public.tbl_cstr_foreign_table(id)"}], "identity": {"objname": "tbl_cstr_foreign_key_simple_2", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_foreign_key_simple_2 ADD CONSTRAINT tbl_cstr_foreign_key_simple_2_id_fkey FOREIGN KEY (id) REFERENCES public.tbl_cstr_foreign_table(id)
+CREATE TABLE tbl_cstr_foreign_key_multiple_keys(
+    id int,
+    name varchar,
+    FOREIGN KEY (id, name) REFERENCES tbl_cstr_foreign_table (id, name)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_key_multiple_keys (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr_foreign_key_multiple_keys_id_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id, name) REFERENCES public.tbl_cstr_foreign_table(id, name)"}], "identity": {"objname": "tbl_cstr_foreign_key_multiple_keys", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_foreign_key_multiple_keys ADD CONSTRAINT tbl_cstr_foreign_key_multiple_keys_id_name_fkey FOREIGN KEY (id, name) REFERENCES public.tbl_cstr_foreign_table(id, name)
+-- some combinations from REFERENCES clause, which is already tested in part 3
+CREATE TABLE tbl_cstr_reference_table_complex_combination1(
+    id int,
+    name varchar,
+    FOREIGN KEY (name) REFERENCES tbl_cstr_foreign_table (name) MATCH SIMPLE ON DELETE CASCADE ON UPDATE SET NULL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_complex_combination1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr_reference_table_complex_combination1_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.tbl_cstr_foreign_table(name) ON UPDATE SET NULL ON DELETE CASCADE"}], "identity": {"objname": "tbl_cstr_reference_table_complex_combination1", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_reference_table_complex_combination1 ADD CONSTRAINT tbl_cstr_reference_table_complex_combination1_name_fkey FOREIGN KEY (name) REFERENCES public.tbl_cstr_foreign_table(name) ON UPDATE SET NULL ON DELETE CASCADE
+CREATE TABLE tbl_cstr_reference_table_complex_combination2(
+    id int,
+    name varchar,
+    CONSTRAINT tbl_cstr FOREIGN KEY (id, name) REFERENCES tbl_cstr_foreign_table (id, name) ON DELETE SET NULL (id, name) ON UPDATE SET DEFAULT
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_complex_combination2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr", "type": "add constraint", "definition": "FOREIGN KEY (id, name) REFERENCES public.tbl_cstr_foreign_table(id, name) ON UPDATE SET DEFAULT ON DELETE SET NULL (id, name)"}], "identity": {"objname": "tbl_cstr_reference_table_complex_combination2", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_reference_table_complex_combination2 ADD CONSTRAINT tbl_cstr FOREIGN KEY (id, name) REFERENCES public.tbl_cstr_foreign_table(id, name) ON UPDATE SET DEFAULT ON DELETE SET NULL (id, name)
+-- [ DEFERRABLE | NOT DEFERRABLE ]
+CREATE TABLE tbl_cstr_reference_table_column_deferable(
+    id int,
+    name varchar,
+    UNIQUE (id, name) DEFERRABLE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_deferable_id_name_key UNIQUE (id, name) DEFERRABLE)      
+CREATE TABLE tbl_cstr_reference_table_column_not_deferable(
+    id int,
+    name varchar,
+    PRIMARY KEY (id) NOT DEFERRABLE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_not_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_not_deferable_pkey PRIMARY KEY (id))      
+-- [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+CREATE TABLE tbl_cstr_reference_table_column_initially_deferred(
+    id int,
+    name varchar,
+    UNIQUE (id, name) INITIALLY DEFERRED
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_initially_deferred (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_initially_deferred_id_name_key UNIQUE (id, name) DEFERRABLE INITIALLY DEFERRED)      
+CREATE TABLE tbl_cstr_reference_table_column_initially_immediate(
+    id int,
+    name varchar,
+    PRIMARY KEY (id) INITIALLY IMMEDIATE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_initially_immediate (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_initially_immediate_pkey PRIMARY KEY (id))      
+-- complex combinations
+CREATE TABLE tbl_cstr_reference_table_column_complex_combination1(
+    id int,
+    name varchar,
+    CONSTRAINT tbl_cstr FOREIGN KEY (name) REFERENCES tbl_cstr_foreign_table (name) MATCH FULL ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_complex_combination1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.tbl_cstr_foreign_table(name) MATCH FULL DEFERRABLE INITIALLY DEFERRED"}], "identity": {"objname": "tbl_cstr_reference_table_column_complex_combination1", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_reference_table_column_complex_combination1 ADD CONSTRAINT tbl_cstr FOREIGN KEY (name) REFERENCES public.tbl_cstr_foreign_table(name) MATCH FULL DEFERRABLE INITIALLY DEFERRED
+CREATE TABLE tbl_cstr_reference_table_column_complex_combination2(
+    id int,
+    name varchar,
+    CONSTRAINT tbl_cstr PRIMARY KEY (id, name) NOT DEFERRABLE INITIALLY IMMEDIATE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_complex_combination2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr PRIMARY KEY (id, name))      
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/create_extension.out b/src/test/modules/test_ddl_deparse_regress/expected/create_extension.out
new file mode 100644
index 0000000000..87832181f5
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/expected/create_extension.out
@@ -0,0 +1,4 @@
+---
+--- CREATE_EXTENSION
+---
+CREATE EXTENSION pg_stat_statements;
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/create_index.out b/src/test/modules/test_ddl_deparse_regress/expected/create_index.out
new file mode 100644
index 0000000000..490cac9662
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/expected/create_index.out
@@ -0,0 +1,22 @@
+-- Currently, ddl deparse only supports the TABLE command. Therefore, this
+-- test file is only prepared for testing the TABLE command.
+-- Command prepared for alter_table.sql.
+-- For "ADD table_constraint_using_index"
+CREATE TABLE test_add_constraint_using_index(
+    id1 int,
+    id2 int,
+    id3 int,
+    id4 int,
+    id5 int,
+    id6 int,
+    id7 int
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_add_constraint_using_index (id1 pg_catalog.int4 STORAGE PLAIN      , id2 pg_catalog.int4 STORAGE PLAIN      , id3 pg_catalog.int4 STORAGE PLAIN      , id4 pg_catalog.int4 STORAGE PLAIN      , id5 pg_catalog.int4 STORAGE PLAIN      , id6 pg_catalog.int4 STORAGE PLAIN      , id7 pg_catalog.int4 STORAGE PLAIN      )      
+CREATE UNIQUE INDEX test_add_constraint_used_index1 ON test_add_constraint_using_index (id1);
+CREATE UNIQUE INDEX test_add_constraint_used_index2 ON test_add_constraint_using_index (id2);
+CREATE UNIQUE INDEX test_add_constraint_used_index3 ON test_add_constraint_using_index (id3);
+CREATE UNIQUE INDEX test_add_constraint_used_index4 ON test_add_constraint_using_index (id4);
+CREATE UNIQUE INDEX test_add_constraint_used_index5 ON test_add_constraint_using_index (id5);
+CREATE UNIQUE INDEX test_add_constraint_used_index6 ON test_add_constraint_using_index (id6);
+CREATE UNIQUE INDEX test_add_constraint_used_index7 ON test_add_constraint_using_index (id7);
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/create_rule.out b/src/test/modules/test_ddl_deparse_regress/expected/create_rule.out
new file mode 100644
index 0000000000..14c5664292
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/expected/create_rule.out
@@ -0,0 +1,48 @@
+-- Currently, ddl deparse only supports the TABLE command. Therefore, this
+-- test file is only prepared for testing the TABLE command.
+-- Command prepared for alter_table.sql.
+-- For "DISABLE RULE rewrite_rule_name"
+CREATE TABLE test_disable_rule(
+    id int,
+    name varchar,
+    description text,
+    price float4,
+    quantity int,
+    purchase_date date
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_disable_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+CREATE RULE sample_rule1 AS
+    ON UPDATE TO test_disable_rule
+    DO INSTEAD
+        SELECT * FROM test_disable_rule;
+-- For "ENABLE RULE rewrite_rule_name"
+CREATE TABLE test_enable_rule(
+    LIKE test_disable_rule
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+CREATE RULE sample_rule2 AS
+    ON UPDATE TO test_enable_rule
+    DO INSTEAD
+        SELECT * FROM test_enable_rule;
+-- For "ENABLE REPLICA RULE rewrite_rule_name"
+CREATE TABLE test_enable_replica_rule(
+    LIKE test_disable_rule
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_replica_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+CREATE RULE sample_rule_enable_replica AS
+    ON UPDATE TO test_enable_replica_rule
+    DO INSTEAD
+        SELECT * FROM test_enable_replica_rule;
+-- For "ENABLE ALWAYS RULE rewrite_rule_name"
+CREATE TABLE test_enable_always_rule(
+    LIKE test_disable_rule
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_always_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+CREATE RULE sample_rule_enable_always AS
+    ON UPDATE TO test_enable_always_rule
+    DO INSTEAD
+        SELECT * FROM test_enable_always_rule;
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/create_schema.out b/src/test/modules/test_ddl_deparse_regress/expected/create_schema.out
new file mode 100644
index 0000000000..0029dfc519
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/expected/create_schema.out
@@ -0,0 +1,5 @@
+-- Currently, ddl deparse only supports the TABLE command. Therefore, this
+-- test file is only prepared for testing the TABLE command.
+-- Command prepared for alter_table.sql.
+-- For "SET SCHEMA new_schema"
+CREATE SCHEMA new_test_schema;
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/create_table.out b/src/test/modules/test_ddl_deparse_regress/expected/create_table.out
new file mode 100644
index 0000000000..567dcf9c38
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/expected/create_table.out
@@ -0,0 +1,571 @@
+-- part 1: shared prefixes
+-- [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ]
+CREATE TABLE part1_simple_table(
+    id int,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part1_simple_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TEMPORARY TABLE part1_temp_table0(
+    id int,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_temp_table0 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TEMP TABLE part1_temp_table(
+    id int,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_temp_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+-- GLOBAL TEMP TATBLE is deprecated, expect warning message and create local temp table
+CREATE GLOBAL TEMP TABLE part1_global_temp_table(
+    id int,
+    name varchar
+);
+WARNING:  GLOBAL is deprecated in temporary table creation
+LINE 1: CREATE GLOBAL TEMP TABLE part1_global_temp_table(
+               ^
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_global_temp_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE LOCAL TEMP TABLE part1_local_temp_table(
+    id int,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_local_temp_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE UNLOGGED TABLE part1_unlogged_table(
+    id int,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE UNLOGGED TABLE  public.part1_unlogged_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+-- [ IF NOT EXISTS ]
+CREATE TABLE IF NOT EXISTS part1_simple_table(
+    id int,
+    name varchar
+);
+NOTICE:  relation "part1_simple_table" already exists, skipping
+CREATE TABLE IF NOT EXISTS part1_local_temp_table_not_exists(
+    id int,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE IF NOT EXISTS public.part1_local_temp_table_not_exists (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+-- part 2: shared suffixes
+-- [ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
+CREATE TABLE part2_partition_by_range_simple(
+    id int,
+    name varchar
+) PARTITION BY RANGE (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_by_range_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (id)    
+CREATE TABLE part2_partition_by_list_simple(
+    id int,
+    name varchar
+) PARTITION BY LIST (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_by_list_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY LIST (id)    
+CREATE TABLE part2_partition_by_hash_simple(
+    id int,
+    name varchar
+) PARTITION BY HASH (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_by_hash_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY HASH (id)    
+CREATE TABLE part2_partition_with_expression(
+    id int,
+    name varchar
+) PARTITION BY RANGE ((id * 190), name);
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_expression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (((id OPERATOR(pg_catalog.*) 190)), name)    
+CREATE TABLE part2_partition_with_collation(
+    id int,
+    name varchar
+) PARTITION BY LIST (name COLLATE "C");
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_collation (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY LIST (name COLLATE pg_catalog."C")    
+CREATE TABLE part2_partition_with_opclass(
+    id int,
+    name varchar
+) PARTITION BY HASH (id int4_ops, name varchar_ops);
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_opclass (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY HASH (id, name pg_catalog.varchar_ops)    
+CREATE TABLE part2_partition_with_collation_opclass(
+    id int,
+    name varchar
+) PARTITION BY RANGE ((id * 10) int4_ops, name COLLATE "C" varchar_ops);
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_collation_opclass (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (((id OPERATOR(pg_catalog.*) 10)), name COLLATE pg_catalog."C" pg_catalog.varchar_ops)    
+-- [ USING method ]
+-- default method
+CREATE TABLE part2_using_default_access_method(
+    id int,
+    name varchar
+) USING heap;
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_using_default_access_method (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )   USING heap   
+-- [ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
+CREATE TABLE part2_without_oids(
+    id int,
+    name varchar
+) WITHOUT OIDS;
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_without_oids (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part2_with_one_storage_param(
+    id int,
+    name varchar
+) WITH (fillfactor = 20);
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_with_one_storage_param (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )    WITH (fillfactor = '20')  
+CREATE TABLE part2_with_multiple_storage_params(
+    id int,
+    name varchar
+) WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true);
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_with_multiple_storage_params (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )    WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')  
+-- [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
+CREATE TEMP TABLE part2_on_commit_preserve_rows(
+    id int,
+    name varchar
+) ON COMMIT PRESERVE ROWS;
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_on_commit_preserve_rows (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     ON COMMIT PRESERVE ROWS 
+CREATE TEMP TABLE part2_on_commit_delete_rows(
+    id int,
+    name varchar
+) ON COMMIT DELETE ROWS;
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_on_commit_delete_rows (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     ON COMMIT DELETE ROWS 
+CREATE TEMPORARY TABLE part2_on_commit_drop(
+    id int,
+    name varchar
+) ON COMMIT DROP;
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_on_commit_drop (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     ON COMMIT DROP 
+-- [ TABLESPACE tablespace_name ]
+CREATE TABLE part2_tablespace_pg_default(
+    id int,
+    name varchar
+) TABLESPACE pg_default;
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_tablespace_pg_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      TABLESPACE pg_default
+-- some complex combinations from the components above
+CREATE TEMPORARY TABLE part2_combination_1(
+    id int,
+    name varchar
+) PARTITION BY RANGE (id) ON COMMIT PRESERVE ROWS;
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_combination_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (id)   ON COMMIT PRESERVE ROWS 
+CREATE LOCAL TEMP TABLE part2_combination_2(
+    id int,
+    name varchar
+) USING heap WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true) ON COMMIT PRESERVE ROWS TABLESPACE pg_default;
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_combination_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )   USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') ON COMMIT PRESERVE ROWS TABLESPACE pg_default
+CREATE TABLE part2_combination_3(
+    id int,
+    name varchar
+) USING heap WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true) TABLESPACE pg_default;
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_combination_3 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )   USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')  TABLESPACE pg_default
+-- part 5: LIKE source_table [ like_option ... ]
+CREATE TABLE part5_source_table(
+    id int,
+    name varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_source_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_source_table2(
+    id2 int,
+    name2 varchar
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_source_table2 (id2 pg_catalog.int4 STORAGE PLAIN      , name2 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_like_simple(
+    LIKE part5_source_table
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_like_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+-- { INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
+CREATE TABLE part5_including_all(
+    LIKE part5_source_table INCLUDING ALL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_all (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_including_comments(
+    LIKE part5_source_table INCLUDING COMMENTS
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_comments (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_including_compression(
+    LIKE part5_source_table INCLUDING COMPRESSION
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_compression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_including_defaults(
+    LIKE part5_source_table INCLUDING DEFAULTS
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_defaults (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_including_generated(
+    LIKE part5_source_table INCLUDING GENERATED
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_generated (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_including_identity(
+    LIKE part5_source_table INCLUDING IDENTITY
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_identity (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_including_indexes(
+    LIKE part5_source_table INCLUDING INDEXES
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_indexes (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_including_statistics(
+    LIKE part5_source_table INCLUDING STATISTICS
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_statistics (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_including_storage(
+    LIKE part5_source_table INCLUDING STORAGE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_storage (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_excluding_all(
+    LIKE part5_source_table EXCLUDING ALL
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_all (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_excluding_comments(
+    LIKE part5_source_table EXCLUDING COMMENTS
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_comments (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_excluding_compression(
+    LIKE part5_source_table EXCLUDING COMPRESSION
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_compression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_excluding_defaults(
+    LIKE part5_source_table EXCLUDING DEFAULTS
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_defaults (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_excluding_generated(
+    LIKE part5_source_table EXCLUDING GENERATED
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_generated (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_excluding_identity(
+    LIKE part5_source_table EXCLUDING IDENTITY
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_identity (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_excluding_indexes(
+    LIKE part5_source_table EXCLUDING INDEXES
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_indexes (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_excluding_statistics(
+    LIKE part5_source_table EXCLUDING STATISTICS
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_statistics (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_excluding_storage(
+    LIKE part5_source_table EXCLUDING STORAGE
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_storage (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+CREATE TABLE part5_like_list(
+    LIKE part5_source_table,
+    info text,
+    LIKE part5_source_table2 INCLUDING ALL,
+    CONSTRAINT primary_key_constraint PRIMARY KEY (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_like_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , info pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , id2 pg_catalog.int4 STORAGE PLAIN      , name2 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT primary_key_constraint PRIMARY KEY (id))      
+-- part 6: partition specification
+-- PARTITION OF parent_table { FOR VALUES partition_bound_spec | DEFAULT }
+CREATE TABLE part6_parent_table_range(
+    id int,
+    name varchar
+) PARTITION BY RANGE (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_parent_table_range (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (id)    
+CREATE TABLE part6_parent_table_list(
+    id int,
+    name varchar
+) PARTITION BY LIST (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_parent_table_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY LIST (id)    
+CREATE TABLE part6_parent_table_hash(
+    id int,
+    name varchar
+) PARTITION BY HASH (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_parent_table_hash (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY HASH (id)    
+CREATE TABLE part6_partition_default PARTITION OF part6_parent_table_range DEFAULT;
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_default PARTITION OF public.part6_parent_table_range  DEFAULT     
+-- FROM ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] )
+--  TO ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] ) |
+CREATE TABLE part6_partition_spec_range1 PARTITION OF part6_parent_table_range
+FOR VALUES FROM (MINVALUE) TO (2);
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_range1 PARTITION OF public.part6_parent_table_range  FOR VALUES FROM (MINVALUE) TO (2)     
+CREATE TABLE part6_partition_spec_range2 PARTITION OF part6_parent_table_range
+FOR VALUES FROM (3) TO (MAXVALUE);
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_range2 PARTITION OF public.part6_parent_table_range  FOR VALUES FROM (3) TO (MAXVALUE)     
+-- IN ( partition_bound_expr [, ...] ) |
+CREATE TABLE part6_partition_spec_list PARTITION OF part6_parent_table_list
+FOR VALUES IN (1, (1+2), (4+5));
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_list PARTITION OF public.part6_parent_table_list  FOR VALUES IN (1, 3, 9)     
+-- WITH ( MODULUS numeric_literal, REMAINDER numeric_literal )
+CREATE TABLE part6_partition_spec_hash PARTITION OF part6_parent_table_hash
+FOR VALUES WITH (MODULUS 10, REMAINDER 2);
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_hash PARTITION OF public.part6_parent_table_hash  FOR VALUES WITH (modulus 10, remainder 2)     
+-- part7: create table
+-- all data types
+CREATE TABLE part7_source_table(
+    src_id int
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_source_table (src_id pg_catalog.int4 STORAGE PLAIN      )      
+CREATE TABLE part7_data_types(
+    var1 int8,
+    var2 serial8,
+    var3 bit,
+    var4 bit[5],
+    var5 varbit,
+    var6 varbit[5],
+    var7 bool,
+    var8 box,
+    var9 bytea,
+    var10 char,
+    var11 char[8],
+    var12 varchar,
+    var13 varchar[5],
+    var14 cidr,
+    var15 circle,
+    var16 date,
+    var17 double precision,
+    var18 inet,
+    var19 int,
+    var20 int4,
+    var21 interval,
+    var22 json,
+    var23 jsonb,
+    var24 line,
+    var25 lseg,
+    var26 macaddr,
+    var27 money,
+    var28 decimal,
+    var29 decimal(3,1),
+    var30 path,
+    var31 pg_lsn,
+    var32 pg_snapshot,
+    var33 point,
+    var34 polygon,
+    var35 float4,
+    var36 int2,
+    var37 serial2,
+    var38 serial4,
+    var39 text,
+    var40 time,
+    var41 time(3),
+    var42 timetz,
+    var43 timetz(3),
+    var44 timestamp,
+    var45 timestamp(3),
+    var46 timestamptz,
+    var47 timestamptz(3),
+    var48 tsquery,
+    var49 tsvector,
+    var50 txid_snapshot,
+    var51 uuid,
+    var52 xml
+);
+NOTICE:  re-formed command: CREATE  SEQUENCE  public.part7_data_types_var2_seq CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 RESTART 1 AS pg_catalog.int8
+NOTICE:  deparsed json: {"fmt": "CREATE %{persistence}s SEQUENCE %{if_not_exists}s %{identity}D %{definition: }s", "identity": {"objname": "part7_data_types_var37_seq", "schemaname": "public"}, "definition": [{"fmt": "CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "%{no}s CYCLE", "clause": "cycle"}, {"fmt": "INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "MAXVALUE %{value}s", "value": "32767", "clause": "maxvalue"}, {"fmt": "START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}, {"fmt": "AS %{seqtype}T", "seqtype": {"typmod": "", "typarray": false, "typename": "int2", "schemaname": "pg_catalog"}}], "persistence": "", "if_not_exists": ""}
+NOTICE:  re-formed command: CREATE  SEQUENCE  public.part7_data_types_var37_seq CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 32767 START WITH 1 RESTART 1 AS pg_catalog.int2
+NOTICE:  deparsed json: {"fmt": "CREATE %{persistence}s SEQUENCE %{if_not_exists}s %{identity}D %{definition: }s", "identity": {"objname": "part7_data_types_var38_seq", "schemaname": "public"}, "definition": [{"fmt": "CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "%{no}s CYCLE", "clause": "cycle"}, {"fmt": "INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}, {"fmt": "AS %{seqtype}T", "seqtype": {"typmod": "", "typarray": false, "typename": "int4", "schemaname": "pg_catalog"}}], "persistence": "", "if_not_exists": ""}
+NOTICE:  re-formed command: CREATE  SEQUENCE  public.part7_data_types_var38_seq CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 AS pg_catalog.int4
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_data_types (var1 pg_catalog.int8 STORAGE PLAIN      , var2 pg_catalog.int8 STORAGE PLAIN   NOT NULL DEFAULT pg_catalog.nextval('public.part7_data_types_var2_seq'::pg_catalog.regclass)  , var3 pg_catalog."bit"(1) STORAGE EXTENDED      , var4 pg_catalog."bit"(1)[] STORAGE EXTENDED      , var5 pg_catalog.varbit STORAGE EXTENDED      , var6 pg_catalog.varbit[] STORAGE EXTENDED      , var7 pg_catalog.bool STORAGE PLAIN      , var8 pg_catalog.box STORAGE PLAIN      , var9 pg_catalog.bytea STORAGE EXTENDED      , var10 pg_catalog.bpchar(1) STORAGE EXTENDED  COLLATE pg_catalog."default"    , var11 pg_catalog.bpchar(1)[] STORAGE EXTENDED  COLLATE pg_catalog."default"    , var12 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , var13 pg_catalog."varchar"[] STORAGE EXTENDED  COLLATE pg_catalog."default"    , var14 pg_catalog.cidr STORAGE MAIN      , var15 pg_catalog.circle STORAGE PLAIN      , var16 pg_catalog.date STORAGE PLAIN      , var17 pg_catalog.float8 STORAGE PLAIN      , var18 pg_catalog.inet STORAGE MAIN      , var19 pg_catalog.int4 STORAGE PLAIN      , var20 pg_catalog.int4 STORAGE PLAIN      , var21 INTERVAL STORAGE PLAIN      , var22 pg_catalog.json STORAGE EXTENDED      , var23 pg_catalog.jsonb STORAGE EXTENDED      , var24 pg_catalog.line STORAGE PLAIN      , var25 pg_catalog.lseg STORAGE PLAIN      , var26 pg_catalog.macaddr STORAGE PLAIN      , var27 pg_catalog.money STORAGE PLAIN      , var28 pg_catalog."numeric" STORAGE MAIN      , var29 pg_catalog."numeric"(3,1) STORAGE MAIN      , var30 pg_catalog.path STORAGE EXTENDED      , var31 pg_catalog.pg_lsn STORAGE PLAIN      , var32 pg_catalog.pg_snapshot STORAGE EXTENDED      , var33 pg_catalog.point STORAGE PLAIN      , var34 pg_catalog.polygon STORAGE EXTENDED      , var35 pg_catalog.float4 STORAGE PLAIN      , var36 pg_catalog.int2 STORAGE PLAIN      , var37 pg_catalog.int2 STORAGE PLAIN   NOT NULL DEFAULT pg_catalog.nextval('public.part7_data_types_var37_seq'::pg_catalog.regclass)  , var38 pg_catalog.int4 STORAGE PLAIN   NOT NULL DEFAULT pg_catalog.nextval('public.part7_data_types_var38_seq'::pg_catalog.regclass)  , var39 pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , var40 TIME STORAGE PLAIN      , var41 TIME(3) without time zone STORAGE PLAIN      , var42 TIME WITH TIME ZONE STORAGE PLAIN      , var43 TIME(3) with time zone STORAGE PLAIN      , var44 TIMESTAMP STORAGE PLAIN      , var45 TIMESTAMP(3) without time zone STORAGE PLAIN      , var46 TIMESTAMP WITH TIME ZONE STORAGE PLAIN      , var47 TIMESTAMP(3) with time zone STORAGE PLAIN      , var48 pg_catalog.tsquery STORAGE PLAIN      , var49 pg_catalog.tsvector STORAGE EXTENDED      , var50 pg_catalog.txid_snapshot STORAGE EXTENDED      , var51 pg_catalog.uuid STORAGE PLAIN      , var52 pg_catalog.xml STORAGE EXTENDED      )      
+NOTICE:  deparsed json: {"fmt": "ALTER SEQUENCE %{identity}D %{definition: }s", "identity": {"objname": "part7_data_types_var2_seq", "schemaname": "public"}, "definition": [{"fmt": "OWNED BY %{owner}D", "owner": {"objname": "part7_data_types", "attrname": "var2", "schemaname": "public"}, "clause": "owned"}]}
+NOTICE:  re-formed command: ALTER SEQUENCE public.part7_data_types_var2_seq OWNED BY public.part7_data_types.var2
+NOTICE:  deparsed json: {"fmt": "ALTER SEQUENCE %{identity}D %{definition: }s", "identity": {"objname": "part7_data_types_var37_seq", "schemaname": "public"}, "definition": [{"fmt": "OWNED BY %{owner}D", "owner": {"objname": "part7_data_types", "attrname": "var37", "schemaname": "public"}, "clause": "owned"}]}
+NOTICE:  re-formed command: ALTER SEQUENCE public.part7_data_types_var37_seq OWNED BY public.part7_data_types.var37
+NOTICE:  deparsed json: {"fmt": "ALTER SEQUENCE %{identity}D %{definition: }s", "identity": {"objname": "part7_data_types_var38_seq", "schemaname": "public"}, "definition": [{"fmt": "OWNED BY %{owner}D", "owner": {"objname": "part7_data_types", "attrname": "var38", "schemaname": "public"}, "clause": "owned"}]}
+NOTICE:  re-formed command: ALTER SEQUENCE public.part7_data_types_var38_seq OWNED BY public.part7_data_types.var38
+CREATE TABLE part7_compression_collate(
+    str1 varchar COMPRESSION "pglz",
+    str2 varchar COLLATE "C"
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_compression_collate (str1 pg_catalog."varchar" STORAGE EXTENDED COMPRESSION pglz COLLATE pg_catalog."default"    , str2 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."C"    )      
+CREATE TABLE part7_inherits_parent(
+    id int,
+    name varchar
+)
+INHERITS (part7_data_types, part7_compression_collate);
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_inherits_parent (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    ) INHERITS (public.part7_data_types, public.part7_compression_collate)     
+CREATE TABLE part7_combine_all_clauses(
+    id varchar(5) COMPRESSION "pglz" COLLATE "C" CONSTRAINT id_constraint DEFAULT 'foo',
+    PRIMARY KEY (id),
+    LIKE part7_source_table,
+    name varchar
+)
+INHERITS (part7_data_types, part7_compression_collate)
+USING heap
+WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
+TABLESPACE pg_default;
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_combine_all_clauses (id pg_catalog."varchar"(5) STORAGE EXTENDED COMPRESSION pglz COLLATE pg_catalog."C"  DEFAULT 'foo'::character varying  , src_id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT part7_combine_all_clauses_pkey PRIMARY KEY (id)) INHERITS (public.part7_data_types, public.part7_compression_collate)  USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')  TABLESPACE pg_default
+CREATE TEMP TABLE part7_combine_all_clauses_temp(
+    id varchar(5) COMPRESSION "pglz" COLLATE "C" CONSTRAINT id_constraint DEFAULT 'foo',
+    PRIMARY KEY (id),
+    LIKE part7_source_table,
+    name varchar
+)
+INHERITS (part7_data_types, part7_compression_collate)
+USING heap
+WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
+ON COMMIT DELETE ROWS
+TABLESPACE pg_default;
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part7_combine_all_clauses_temp (id pg_catalog."varchar"(5) STORAGE EXTENDED COMPRESSION pglz COLLATE pg_catalog."C"  DEFAULT 'foo'::character varying  , src_id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT part7_combine_all_clauses_temp_pkey PRIMARY KEY (id)) INHERITS (public.part7_data_types, public.part7_compression_collate)  USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') ON COMMIT DELETE ROWS TABLESPACE pg_default
+-- CREATE TABLE ... AS
+CREATE TABLE test_ctas AS SELECT 0 AS col1, 1 AS col2;
+NOTICE:  re-formed command: CREATE  TABLE  public.test_ctas (col1 pg_catalog.int4 STORAGE PLAIN      , col2 pg_catalog.int4 STORAGE PLAIN      )      
+-- part8: create typed table
+CREATE TABLE part8_create_typed_table OF part8_people_type;
+NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table OF public.part8_people_type      
+CREATE TABLE part8_create_typed_table_simple OF part8_people_type(
+    weight,
+    PRIMARY KEY (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table_simple OF public.part8_people_type (CONSTRAINT part8_create_typed_table_simple_pkey PRIMARY KEY (id))     
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}], "identity": {"objname": "part8_create_typed_table_simple", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.part8_create_typed_table_simple ALTER COLUMN id SET NOT NULL
+CREATE TABLE part8_create_typed_table_with_options_constaints OF part8_people_type(
+    weight WITH OPTIONS NOT NULL,
+    name UNIQUE,
+    PRIMARY KEY (id)
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table_with_options_constaints OF public.part8_people_type (weight WITH OPTIONS NOT NULL , CONSTRAINT part8_create_typed_table_with_options_constaints_name_key UNIQUE (name), CONSTRAINT part8_create_typed_table_with_options_constaints_pkey PRIMARY KEY (id))     
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}], "identity": {"objname": "part8_create_typed_table_with_options_constaints", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.part8_create_typed_table_with_options_constaints ALTER COLUMN id SET NOT NULL
+CREATE TABLE part8_create_typed_table_complex_combinations OF part8_people_type(
+    weight WITH OPTIONS NOT NULL,
+    name UNIQUE,
+    PRIMARY KEY (id)
+)
+USING heap
+WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
+TABLESPACE pg_default;
+NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table_complex_combinations OF public.part8_people_type (weight WITH OPTIONS NOT NULL , CONSTRAINT part8_create_typed_table_complex_combinations_name_key UNIQUE (name), CONSTRAINT part8_create_typed_table_complex_combinations_pkey PRIMARY KEY (id))  USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')  TABLESPACE pg_default
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}], "identity": {"objname": "part8_create_typed_table_complex_combinations", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.part8_create_typed_table_complex_combinations ALTER COLUMN id SET NOT NULL
+-- part9: create table as a partition of parent table, FOR VALUES clause is tested in part 6
+CREATE TABLE part9_parent_table_range(
+    id int,
+    name varchar,
+    height float4,
+    weight float4
+) PARTITION BY RANGE (height);
+NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_range (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , height pg_catalog.float4 STORAGE PLAIN      , weight pg_catalog.float4 STORAGE PLAIN      )  PARTITION BY RANGE (height)    
+CREATE TABLE part9_parent_table_list(
+    id int,
+    name varchar,
+    height float4,
+    weight float4
+) PARTITION BY LIST (name);
+NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , height pg_catalog.float4 STORAGE PLAIN      , weight pg_catalog.float4 STORAGE PLAIN      )  PARTITION BY LIST (name)    
+CREATE TABLE part9_parent_table_hash(
+    id int,
+    name varchar,
+    height float4,
+    weight float4
+) PARTITION BY HASH (id);
+NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_hash (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , height pg_catalog.float4 STORAGE PLAIN      , weight pg_catalog.float4 STORAGE PLAIN      )  PARTITION BY HASH (id)    
+-- TOFIX
+-- CREATE TABLE part9_partition_with_options_constraintscd
+-- PARTITION OF part9_parent_table_range (
+--     id PRIMARY KEY,
+--     name WITH OPTIONS NOT NULL,
+--     weight,
+--     CHECK (height > 0)
+-- )
+-- FOR VALUES FROM (MINVALUE) TO (2);
+-- TOFIX
+-- CREATE TABLE part9_partition_with_options_constraints_default
+-- PARTITION OF part9_parent_table_range (
+--     id PRIMARY KEY,
+--     name WITH OPTIONS NOT NULL,
+--     CHECK (height > 0)
+-- ) DEFAULT;
+-- TOFIX
+-- CREATE TABLE part9_partition_complex_combinations
+-- PARTITION OF part9_parent_table_range (
+--     id PRIMARY KEY,
+--     name WITH OPTIONS NOT NULL,
+--     CHECK (height > 0)
+-- )
+-- FOR VALUES FROM (3) TO (10)
+-- USING heap
+-- WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
+-- TABLESPACE pg_default;
+-- copied from old create_table.sql
+-- Test TableLikeClause is handled properly
+CREATE TABLE ctlt1 (a text CHECK (length(a) > 2) PRIMARY KEY, b text);
+NOTICE:  re-formed command: CREATE  TABLE  public.ctlt1 (a pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , b pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT ctlt1_a_check CHECK ((pg_catalog.length(a) OPERATOR(pg_catalog.>) 2)), CONSTRAINT ctlt1_pkey PRIMARY KEY (a))      
+ALTER TABLE ctlt1 ALTER COLUMN a SET STORAGE MAIN;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STORAGE %{storage}s", "type": "set storage", "column": "a", "storage": "main"}], "identity": {"objname": "ctlt1", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.ctlt1 ALTER COLUMN a SET STORAGE main
+ALTER TABLE ctlt1 ALTER COLUMN b SET STORAGE EXTERNAL;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STORAGE %{storage}s", "type": "set storage", "column": "b", "storage": "external"}], "identity": {"objname": "ctlt1", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.ctlt1 ALTER COLUMN b SET STORAGE external
+CREATE TABLE ctlt1_like (LIKE ctlt1 INCLUDING ALL);
+NOTICE:  re-formed command: CREATE  TABLE  public.ctlt1_like (a pg_catalog.text STORAGE MAIN  COLLATE pg_catalog."default"    , b pg_catalog.text STORAGE EXTERNAL  COLLATE pg_catalog."default"    , CONSTRAINT ctlt1_a_check CHECK ((pg_catalog.length(a) OPERATOR(pg_catalog.>) 2)), CONSTRAINT ctlt1_like_pkey PRIMARY KEY (a))      
+NOTICE:  deparsed json: <NULL>
+NOTICE:  re-formed command: <NULL>
+-- Test foreign key constraint is handled in a following ALTER TABLE ADD CONSTRAINT FOREIGN KEY REFERENCES subcommand
+CREATE TABLE product (id int PRIMARY KEY, name text);
+NOTICE:  re-formed command: CREATE  TABLE  public.product (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT product_pkey PRIMARY KEY (id))      
+CREATE TABLE orders2 (order_id int PRIMARY KEY, product_id int
+REFERENCES product (id));
+NOTICE:  re-formed command: CREATE  TABLE  public.orders2 (order_id pg_catalog.int4 STORAGE PLAIN      , product_id pg_catalog.int4 STORAGE PLAIN      , CONSTRAINT orders2_pkey PRIMARY KEY (order_id))      
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "orders2_product_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (product_id) REFERENCES public.product(id)"}], "identity": {"objname": "orders2", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.orders2 ADD CONSTRAINT orders2_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id)
+-- Test CREATE and ALTER inherited table
+CREATE TABLE gtest30 (
+a int,
+b int GENERATED ALWAYS AS (a * 2) STORED
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.gtest30 (a pg_catalog.int4 STORAGE PLAIN      , b pg_catalog.int4 STORAGE PLAIN      GENERATED ALWAYS AS ((a OPERATOR(pg_catalog.*) 2)) STORED)      
+CREATE TABLE gtest30_1 () INHERITS (gtest30);
+NOTICE:  re-formed command: CREATE  TABLE  public.gtest30_1 () INHERITS (public.gtest30)     
+ALTER TABLE gtest30 ALTER COLUMN b DROP EXPRESSION;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP EXPRESSION %{if_exists}s", "type": "drop expression", "column": "b", "if_exists": ""}], "identity": {"objname": "gtest30", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.gtest30 ALTER COLUMN b DROP EXPRESSION 
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/create_type.out b/src/test/modules/test_ddl_deparse_regress/expected/create_type.out
new file mode 100644
index 0000000000..a0cea8cc4e
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/expected/create_type.out
@@ -0,0 +1,15 @@
+-- Currently, ddl deparse only supports the TABLE command. Therefore, this
+-- test file is only prepared for testing the TABLE command.
+-- Command prepared for create_table.sql.
+CREATE TYPE part8_people_type AS (
+    id int,
+    name varchar,
+    height float4,
+    weight float4
+);
+-- Command prepared for alter_table.sql.
+-- For "OF type_name"
+CREATE TYPE test_type_product_type AS (
+    id int,
+    name varchar
+);
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/test_ddl_deparse.out b/src/test/modules/test_ddl_deparse_regress/expected/test_ddl_deparse.out
new file mode 100644
index 0000000000..0d8256d653
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/expected/test_ddl_deparse.out
@@ -0,0 +1,26 @@
+CREATE ROLE ddl_testing_role SUPERUSER;
+SET ROLE ddl_testing_role;
+CREATE EXTENSION test_ddl_deparse_regress;
+CREATE OR REPLACE FUNCTION test_ddl_deparse()
+  RETURNS event_trigger LANGUAGE plpgsql AS
+$$
+DECLARE
+	r record;
+	deparsed_json text;
+BEGIN
+	FOR r IN SELECT * FROM pg_event_trigger_ddl_commands()
+		-- Some TABLE commands generate sequence-related commands, also deparse them.
+		WHERE command_tag in ('ALTER FOREIGN TABLE', 'ALTER TABLE',
+							  'CREATE FOREIGN TABLE', 'CREATE TABLE',
+							  'CREATE TABLE AS', 'DROP FOREIGN TABLE',
+							  'DROP TABLE', 'ALTER SEQUENCE',
+							  'CREATE SEQUENCE', 'DROP SEQUENCE')
+	LOOP
+		deparsed_json = pg_catalog.ddl_deparse_to_json(r.command);
+		RAISE NOTICE 'deparsed json: %', deparsed_json;
+		RAISE NOTICE 're-formed command: %', pg_catalog.ddl_deparse_expand_command(deparsed_json);
+	END LOOP;
+END;
+$$;
+CREATE EVENT TRIGGER test_ddl_deparse
+ON ddl_command_end EXECUTE PROCEDURE test_ddl_deparse();
diff --git a/src/test/modules/test_ddl_deparse_regress/meson.build b/src/test/modules/test_ddl_deparse_regress/meson.build
new file mode 100644
index 0000000000..b2f4e1c183
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/meson.build
@@ -0,0 +1,45 @@
+# FIXME: prevent install during main install, but not during test :/
+
+test_ddl_deparse_regress_sources = files(
+  'test_ddl_deparse_regress.c',
+)
+
+if host_system == 'windows'
+  test_ddl_deparse_regress_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_ddl_deparse_regress',
+    '--FILEDESC', 'test_ddl_deparse_regress - regression testing for DDL deparsing',])
+endif
+
+test_ddl_deparse_regress = shared_module('test_ddl_deparse_regress',
+  test_ddl_deparse_regress_sources,
+  kwargs: pg_mod_args,
+)
+testprep_targets += test_ddl_deparse_regress
+
+install_data(
+  'test_ddl_deparse_regress.control',
+  'test_ddl_deparse_regress--1.0.sql',
+  kwargs: contrib_data_args,
+)
+
+tests += {
+  'name': 'test_ddl_deparse_regress',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'regress': {
+    'sql': [
+      'test_ddl_deparse',
+      'create_extension',
+      'create_schema',
+      'aggregate',
+      'create_table',
+      'constraints',
+      'alter_table'
+    ],
+  },
+  'tap': {
+    'tests': [
+      't/001_compare_dumped_results.pl',
+    ],
+  },
+}
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/alter_table.sql b/src/test/modules/test_ddl_deparse_regress/sql/alter_table.sql
new file mode 100644
index 0000000000..0141a7e293
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/sql/alter_table.sql
@@ -0,0 +1,473 @@
+-- parent table defintion
+CREATE TABLE orders(
+    id int,
+    name varchar,
+    description text,
+    price float4,
+    quantity int,
+    purchase_date date
+);
+
+-- ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
+--     action [, ... ]
+CREATE TABLE parent_table(
+    LIKE orders
+);
+CREATE TABLE test_only () INHERITS (parent_table);
+ALTER TABLE test_only ADD col1 int;
+ALTER TABLE IF EXISTS fake_table ADD col2 int;
+
+-- ALTER TABLE IF EXISTS ONLY parent_table ADD PRIMARY KEY (id);
+ALTER TABLE IF EXISTS parent_table * ADD CHECK (id > 10);
+
+-- ADD [ COLUMN ] [ IF NOT EXISTS ] column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
+CREATE TABLE test_add_column(
+    LIKE orders
+);
+ALTER TABLE test_add_column ADD col1 int;
+ALTER TABLE test_add_column ADD COLUMN col2 int;
+ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS col2 varchar;
+ALTER TABLE test_add_column ADD col3 varchar COLLATE "C";
+ALTER TABLE test_add_column ADD col4 int CHECK (col4 > 100) UNIQUE;
+ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS col5 text COLLATE "C" DEFAULT 'foo' NOT NULL;
+
+-- DROP [ COLUMN ] [ IF EXISTS ] column_name [ RESTRICT | CASCADE ]
+CREATE TABLE test_drop_column(
+    LIKE orders,
+    UNIQUE (id),
+    UNIQUE (name)
+);
+CREATE TABLE foreign_table(
+    id int REFERENCES test_drop_column (id),
+    name varchar REFERENCES test_drop_column (name)
+);
+ALTER TABLE test_drop_column DROP price;
+ALTER TABLE test_drop_column DROP COLUMN quantity;
+
+ALTER TABLE test_drop_column DROP IF EXISTS description RESTRICT;
+-- TOFIX
+-- ALTER TABLE test_drop_column DROP IF EXISTS name CASCADE;
+
+-- ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ COLLATE collation ] [ USING expression ]
+CREATE TABLE test_alter_type(
+    LIKE orders
+);
+ALTER TABLE test_alter_type ALTER price TYPE int;
+ALTER TABLE test_alter_type ALTER COLUMN purchase_date TYPE text COLLATE "C";
+ALTER TABLE test_alter_type ALTER COLUMN quantity SET DATA TYPE float4;
+ALTER TABLE test_alter_type ALTER name TYPE int USING id::integer;
+ALTER TABLE test_alter_type ALTER name TYPE TEXT USING id || ' and ' || description, DROP COLUMN description;
+
+-- ALTER [ COLUMN ] column_name SET DEFAULT expression
+CREATE TABLE test_alter_set_default(
+    LIKE orders
+);
+ALTER TABLE test_alter_set_default ALTER price SET DEFAULT 100;
+ALTER TABLE test_alter_set_default ALTER COLUMN quantity SET DEFAULT 10;
+
+-- ALTER [ COLUMN ] column_name DROP DEFAULT
+CREATE TABLE test_drop_default(
+    LIKE orders,
+    default_price float4 DEFAULT 10.0,
+    default_name varchar DEFAULT 'foo'
+);
+ALTER TABLE test_drop_default ALTER default_price DROP DEFAULT;
+ALTER TABLE test_drop_default ALTER COLUMN default_name DROP DEFAULT;
+
+-- ALTER [ COLUMN ] column_name { SET | DROP } NOT NULL
+CREATE TABLE test_set_not_null(
+    LIKE orders,
+    size int NOT NULL
+);
+ALTER TABLE test_set_not_null ALTER COLUMN id SET NOT NULL;
+ALTER TABLE test_set_not_null ALTER size DROP NOT NULL;
+
+-- ALTER [ COLUMN ] column_name DROP EXPRESSION [ IF EXISTS ]
+CREATE TABLE test_drop_expression(
+    LIKE orders,
+    new_id int GENERATED ALWAYS AS ( 3 * ID ) STORED
+);
+ALTER TABLE test_drop_expression ALTER new_id DROP EXPRESSION;
+ALTER TABLE test_drop_expression ALTER id DROP EXPRESSION IF EXISTS;
+
+-- ALTER [ COLUMN ] column_name ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ]
+CREATE TABLE test_add_generated(
+    LIKE orders,
+    col1 int NOT NULL,
+    col2 int NOT NULL,
+    col3 int NOT NULL
+);
+ALTER TABLE test_add_generated ALTER col1 ADD GENERATED ALWAYS AS IDENTITY;
+ALTER TABLE test_add_generated ALTER COLUMN col2 ADD GENERATED BY DEFAULT AS IDENTITY;
+ALTER TABLE test_add_generated ALTER col3 ADD GENERATED BY DEFAULT AS IDENTITY ( INCREMENT BY 10 );
+
+-- ALTER [ COLUMN ] column_name { SET GENERATED { ALWAYS | BY DEFAULT } | SET sequence_option | RESTART [ [ WITH ] restart ] } [...]
+CREATE TABLE test_set_generated(
+    id1 int GENERATED BY DEFAULT AS IDENTITY,
+    id2 int GENERATED ALWAYS AS IDENTITY,
+    id3 int GENERATED ALWAYS AS IDENTITY,
+    id4 int GENERATED ALWAYS AS IDENTITY,
+    id5 int GENERATED ALWAYS AS IDENTITY,
+    id6 int GENERATED ALWAYS AS IDENTITY,
+    id7 int GENERATED ALWAYS AS IDENTITY
+);
+ALTER TABLE test_set_generated ALTER id1 SET GENERATED ALWAYS;
+ALTER TABLE test_set_generated ALTER id2 SET GENERATED BY DEFAULT;
+ALTER TABLE test_set_generated ALTER id3 SET INCREMENT BY 10;
+ALTER TABLE test_set_generated ALTER id4 RESTART;
+ALTER TABLE test_set_generated ALTER id5 RESTART WITH 101;
+ALTER TABLE test_set_generated ALTER id6 RESTART WITH 201;
+ALTER TABLE test_set_generated ALTER COLUMN id7 SET GENERATED BY DEFAULT SET INCREMENT BY 100 RESTART WITH 301;
+
+-- ALTER [ COLUMN ] column_name DROP IDENTITY [ IF EXISTS ]
+CREATE TABLE test_drop_identity(
+    id int,
+    id_generated int GENERATED ALWAYS AS IDENTITY
+);
+ALTER TABLE test_drop_identity ALTER id_generated DROP IDENTITY;
+ALTER TABLE test_drop_identity ALTER id DROP IDENTITY IF EXISTS;
+
+-- ALTER [ COLUMN ] column_name SET STATISTICS integer
+CREATE TABLE test_set_statistics(
+    LIKE orders
+);
+ALTER TABLE test_set_statistics ALTER id SET STATISTICS 1;
+
+-- ALTER [ COLUMN ] column_name SET ( attribute_option = value [, ... ] )
+CREATE TABLE test_set_attribute(
+    LIKE orders
+);
+ALTER TABLE test_set_attribute ALTER name SET (n_distinct = 102);
+ALTER TABLE test_set_attribute ALTER id SET (n_distinct_inherited = 99, n_distinct = 9);
+
+-- ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] )
+CREATE TABLE test_reset_attribute(
+    LIKE orders
+);
+ALTER TABLE test_reset_attribute ALTER name RESET (n_distinct);
+ALTER TABLE test_reset_attribute ALTER id RESET (n_distinct, n_distinct_inherited);
+
+-- ALTER [ COLUMN ] column_name SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
+CREATE TABLE test_set_storage(
+    LIKE orders,
+    product_name text
+);
+ALTER TABLE test_set_storage ALTER id SET STORAGE PLAIN;
+ALTER TABLE test_set_storage ALTER name SET STORAGE EXTERNAL;
+ALTER TABLE test_set_storage ALTER description SET STORAGE EXTENDED;
+ALTER TABLE test_set_storage ALTER product_name SET STORAGE MAIN;
+
+-- ALTER [ COLUMN ] column_name SET COMPRESSION compression_method
+CREATE TABLE test_set_compression(
+    LIKE orders
+);
+ALTER TABLE test_set_compression ALTER name SET COMPRESSION "pglz";
+ALTER TABLE test_set_compression ALTER COLUMN description SET COMPRESSION "pglz";
+
+-- ADD table_constraint [ NOT VALID ]
+CREATE TABLE test_add_table_constraint(
+    LIKE orders
+);
+ALTER TABLE test_add_table_constraint ADD PRIMARY KEY (id);
+ALTER TABLE test_add_table_constraint ADD CONSTRAINT max_name_len CHECK (length(name) < 4) NOT VALID;
+ALTER TABLE test_add_table_constraint ADD CHECK (id < 10);
+
+-- ADD table_constraint_using_index
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index1;
+--TOFIX
+-- ALTER TABLE test_add_constraint_using_index ADD CONSTRAINT primary_constraint_using_index PRIMARY KEY USING INDEX test_add_constraint_used_index2;
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index3 DEFERRABLE;
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index4 NOT DEFERRABLE;
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index5 INITIALLY DEFERRED;
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index6 INITIALLY IMMEDIATE;
+ALTER TABLE test_add_constraint_using_index ADD UNIQUE USING INDEX test_add_constraint_used_index7 DEFERRABLE INITIALLY DEFERRED;
+
+-- ALTER CONSTRAINT constraint_name [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+CREATE TABLE test_alter_constraint_referenced(
+    id1 int UNIQUE,
+    id2 int UNIQUE,
+    id3 int UNIQUE,
+    id4 int UNIQUE
+);
+CREATE TABLE test_alter_constraint(
+    id1 int,
+    id2 int,
+    id3 int,
+    id4 int,
+    id5 int,
+    CONSTRAINT alter_cstr1 FOREIGN KEY (id1) REFERENCES test_alter_constraint_referenced (id1) DEFERRABLE INITIALLY DEFERRED,
+    CONSTRAINT alter_cstr2 FOREIGN KEY (id2) REFERENCES test_alter_constraint_referenced (id2) NOT DEFERRABLE,
+    CONSTRAINT alter_cstr3 FOREIGN KEY (id3) REFERENCES test_alter_constraint_referenced (id3) DEFERRABLE INITIALLY DEFERRED,
+    CONSTRAINT alter_cstr4 FOREIGN KEY (id4) REFERENCES test_alter_constraint_referenced (id4) DEFERRABLE INITIALLY IMMEDIATE
+);
+ALTER TABLE test_alter_constraint ALTER CONSTRAINT alter_cstr1 NOT DEFERRABLE;
+ALTER TABLE test_alter_constraint ALTER CONSTRAINT alter_cstr2 DEFERRABLE;
+ALTER TABLE test_alter_constraint ALTER CONSTRAINT alter_cstr3 DEFERRABLE INITIALLY IMMEDIATE;
+ALTER TABLE test_alter_constraint ALTER CONSTRAINT alter_cstr4 DEFERRABLE INITIALLY DEFERRED;
+
+-- VALIDATE CONSTRAINT constraint_name
+CREATE TABLE test_validate_constraint(
+    LIKE orders
+);
+ALTER TABLE test_validate_constraint ADD CONSTRAINT test_validate_constraint_cstr CHECK (length(name) < 10) NOT VALID;
+ALTER TABLE test_validate_constraint VALIDATE CONSTRAINT test_validate_constraint_cstr;
+
+-- DROP CONSTRAINT [ IF EXISTS ]  constraint_name [ RESTRICT | CASCADE ]
+CREATE TABLE test_drop_constraint(
+    LIKE orders,
+    CONSTRAINT test_drop_constraint_check CHECK (id < 100),
+    CONSTRAINT test_drop_constraint_uniq UNIQUE (id)
+);
+CREATE TABLE test_drop_constraint_reference(
+    id int REFERENCES test_drop_constraint (id),
+    name varchar,
+    CONSTRAINT test_drop_constraint_reference_cstr1 CHECK (length(name) < 10)
+);
+ALTER TABLE test_drop_constraint_reference DROP CONSTRAINT test_drop_constraint_reference_cstr1;
+ALTER TABLE test_drop_constraint DROP CONSTRAINT test_drop_constraint_check RESTRICT;
+ALTER TABLE test_drop_constraint DROP CONSTRAINT IF EXISTS test_drop_constraint_check RESTRICT;
+-- TOFIX
+-- ALTER TABLE test_drop_constraint DROP CONSTRAINT test_drop_constraint_uniq CASCADE;
+
+-- TODO: This should be tested with TRIGGER related testing
+-- DISABLE TRIGGER [ trigger_name | ALL | USER ]
+-- ENABLE TRIGGER [ trigger_name | ALL | USER ]
+-- ENABLE REPLICA TRIGGER trigger_name
+-- ENABLE ALWAYS TRIGGER trigger_name
+
+-- DISABLE RULE rewrite_rule_name
+ALTER TABLE test_disable_rule DISABLE RULE sample_rule1;
+
+-- ENABLE RULE rewrite_rule_name
+ALTER TABLE test_enable_rule DISABLE RULE sample_rule2;
+ALTER TABLE test_enable_rule ENABLE RULE sample_rule2;
+
+-- ENABLE REPLICA RULE rewrite_rule_name
+ALTER TABLE test_enable_replica_rule ENABLE REPLICA RULE sample_rule_enable_replica;
+
+-- ENABLE ALWAYS RULE rewrite_rule_name
+ALTER TABLE test_enable_always_rule ENABLE REPLICA RULE sample_rule_enable_always;
+
+-- DISABLE ROW LEVEL SECURITY
+CREATE TABLE test_disable_row_security(
+    LIKE orders
+);
+ALTER TABLE test_disable_row_security DISABLE ROW LEVEL SECURITY;
+
+-- ENABLE ROW LEVEL SECURITY
+CREATE TABLE test_enable_row_security(
+    LIKE orders
+);
+ALTER TABLE test_enable_row_security ENABLE ROW LEVEL SECURITY;
+
+-- FORCE ROW LEVEL SECURITY
+CREATE TABLE test_force_row_security(
+    LIKE orders
+);
+ALTER TABLE test_force_row_security FORCE ROW LEVEL SECURITY;
+
+-- NO FORCE ROW LEVEL SECURITY
+CREATE TABLE test_no_force_row_security(
+    LIKE orders
+);
+ALTER TABLE test_no_force_row_security NO FORCE ROW LEVEL SECURITY;
+
+-- CLUSTER ON index_name
+CREATE TABLE test_cluster(
+    LIKE orders,
+    PRIMARY KEY (id)
+);
+ALTER TABLE test_cluster CLUSTER ON test_cluster_pkey;
+
+-- SET WITHOUT CLUSTER
+CREATE TABLE test_without_cluster(
+    LIKE orders,
+    PRIMARY KEY (id)
+);
+ALTER TABLE test_without_cluster CLUSTER ON test_without_cluster_pkey;
+ALTER TABLE test_without_cluster SET WITHOUT CLUSTER;
+
+-- SET WITHOUT OIDS
+CREATE TABLE test_set_without_oids(
+    LIKE orders
+);
+ALTER TABLE test_set_without_oids SET WITHOUT OIDS;
+
+-- SET ACCESS METHOD new_access_method
+CREATE TABLE test_set_access_method(
+    LIKE orders
+);
+ALTER TABLE test_set_access_method SET ACCESS METHOD heap;
+
+-- SET TABLESPACE new_tablespace
+CREATE TABLE test_set_tablespace(
+    LIKE orders
+);
+ALTER TABLE test_set_tablespace SET TABLESPACE pg_default;
+
+-- SET { LOGGED | UNLOGGED }
+CREATE TABLE test_set_logged(
+    LIKE orders
+);
+ALTER TABLE test_set_logged SET LOGGED;
+CREATE TABLE test_set_unlogged(
+    LIKE orders
+);
+ALTER TABLE test_set_unlogged SET UNLOGGED;
+
+-- SET ( storage_parameter [= value] [, ... ] )
+CREATE TABLE test_set_storage_params1(
+    LIKE orders
+);
+ALTER TABLE test_set_storage_params1 SET (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true);
+CREATE TABLE test_set_storage_params2(
+    LIKE orders
+);
+ALTER TABLE test_set_storage_params2 SET (vacuum_index_cleanup = ON);
+
+-- RESET ( storage_parameter [, ... ] )
+CREATE TABLE test_reset_storage_params1(
+    LIKE orders
+);
+ALTER TABLE test_set_storage_params1 SET (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true);
+ALTER TABLE test_reset_storage_params1 RESET (vacuum_index_cleanup, autovacuum_vacuum_scale_factor, vacuum_truncate);
+CREATE TABLE test_reset_storage_params2(
+    LIKE orders
+);
+ALTER TABLE test_set_storage_params2 SET (vacuum_index_cleanup = ON);
+ALTER TABLE test_reset_storage_params2 RESET (vacuum_index_cleanup);
+
+-- INHERIT parent_table
+CREATE TABLE test_inherit_parent(
+    parent_id int
+);
+CREATE TABLE test_inherit_child(
+    parent_id int,
+    LIKE orders
+);
+ALTER TABLE test_inherit_child INHERIT test_inherit_parent;
+
+-- NO INHERIT parent_table
+CREATE TABLE test_no_inherit_parent(
+    parent_id int
+);
+CREATE TABLE test_no_inherit_child(
+    LIKE orders
+) INHERITS (test_no_inherit_parent);
+ALTER TABLE test_no_inherit_child NO INHERIT test_no_inherit_parent;
+
+-- OF type_name
+CREATE TABLE test_type(
+    id int,
+    name varchar
+);
+ALTER TABLE test_type OF test_type_product_type;
+
+-- NOT OF
+CREATE TABLE test_type_not_of OF test_type_product_type;
+ALTER TABLE test_type_not_of NOT OF;
+
+-- TODO: This should be tested with ROLE/USER related testing
+-- OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
+
+-- REPLICA IDENTITY { DEFAULT | USING INDEX index_name | FULL | NOTHING }
+CREATE TABLE test_replica_identity1(
+    LIKE orders
+);
+ALTER TABLE test_replica_identity1 REPLICA IDENTITY DEFAULT;
+CREATE TABLE test_replica_identity2(
+    LIKE orders,
+    PRIMARY KEY (id)
+);
+ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_pkey;
+CREATE TABLE test_replica_identity3(
+    LIKE orders
+);
+ALTER TABLE test_replica_identity3 REPLICA IDENTITY FULL;
+CREATE TABLE test_replica_identity4(
+    LIKE orders
+);
+ALTER TABLE test_replica_identity4 REPLICA IDENTITY NOTHING;
+
+-- RENAME [ COLUMN ] column_name TO new_column_name
+CREATE TABLE test_alter_col_name(
+    LIKE orders
+);
+ALTER TABLE test_alter_col_name RENAME id TO new_id;
+ALTER TABLE test_alter_col_name RENAME COLUMN name TO new_name;
+
+-- RENAME CONSTRAINT constraint_name TO new_constraint_name
+CREATE TABLE test_alter_constraint_name(
+    LIKE orders,
+    CONSTRAINT test_alter_constraint_name_old CHECK (id > 10)
+);
+ALTER TABLE test_alter_constraint_name RENAME CONSTRAINT test_alter_constraint_name_old TO test_alter_constraint_name_new;
+
+-- RENAME TO new_name
+CREATE TABLE test_rename_table(
+    LIKE orders
+);
+ALTER TABLE test_rename_table RENAME to new_test_rename_table;
+
+-- SET SCHEMA new_schema
+CREATE TABLE test_set_schema(
+    LIKE orders
+);
+ALTER TABLE test_set_schema SET SCHEMA new_test_schema;
+
+-- ALTER TABLE ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ]
+--     SET TABLESPACE new_tablespace [ NOWAIT ]
+-- TOFIX: can not be caught by ddl_command_end event trigger.
+-- Deparse of T_AlterTableMoveAllStmt is not supported,
+-- TABLESPACE commands (global object commands) are also not supported.
+-- ALTER TABLE ALL IN TABLESPACE pg_default SET TABLESPACE pg_default;
+-- ALTER TABLE ALL IN TABLESPACE pg_default OWNED BY ddl_testing_role SET TABLESPACE pg_default;
+
+-- ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT }
+CREATE TABLE test_partition_attach_range(
+    LIKE orders
+) PARTITION BY RANGE (id);
+CREATE TABLE test_partition_attach_range_p_1(
+    LIKE test_partition_attach_range
+);
+-- TOFIX
+-- ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_1 DEFAULT;
+
+CREATE TABLE test_partition_attach_range_p_2(
+    LIKE test_partition_attach_range
+);
+-- TOFIX
+-- ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_2 FOR VALUES FROM (100) TO (200);
+CREATE TABLE test_partition_attach_hash(
+    LIKE orders
+) PARTITION BY HASH (id);
+CREATE TABLE test_partition_attach_hash_p(
+    LIKE test_partition_attach_hash
+);
+-- TOFIX
+-- ALTER TABLE test_partition_attach_hash ATTACH PARTITION test_partition_attach_hash_p FOR VALUES WITH (MODULUS 10, REMAINDER 1);
+CREATE TABLE test_partition_attach_list(
+    LIKE orders
+) PARTITION BY LIST (name);
+CREATE TABLE test_partition_attach_list_p1(
+    LIKE test_partition_attach_list
+);
+CREATE TABLE test_partition_attach_list_p2(
+    LIKE test_partition_attach_list
+);
+-- TOFIX
+-- ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p1 FOR VALUES IN ('key1');
+-- ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p2 FOR VALUES IN ('key2', 'key3');
+
+-- DETACH PARTITION partition_name [ CONCURRENTLY | FINALIZE ]
+CREATE TABLE test_detach_partition(
+    LIKE orders
+) PARTITION BY RANGE (id);
+CREATE TABLE test_detach_partition_p1 PARTITION OF test_detach_partition FOR VALUES FROM (1) TO (100);
+CREATE TABLE test_detach_partition_p2 PARTITION OF test_detach_partition FOR VALUES FROM (101) TO (200);
+CREATE TABLE test_detach_partition_p3 PARTITION OF test_detach_partition FOR VALUES FROM (201) TO (300);
+-- TOFIX
+-- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p1;
+-- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p2 CONCURRENTLY;
+-- TOFIX: FINALIZE option is not testable
+-- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p3 FINALIZE;
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/constraints.sql b/src/test/modules/test_ddl_deparse_regress/sql/constraints.sql
new file mode 100644
index 0000000000..e2d43fa92a
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/sql/constraints.sql
@@ -0,0 +1,385 @@
+-- column constraint, index_parameters
+
+-- [ CONSTRAINT constraint_name ]
+-- { NOT NULL |
+CREATE TABLE col_cstr_not_null(
+    id int CONSTRAINT id_constraint NOT NULL,
+    name varchar
+);
+
+--  NULL |
+CREATE TABLE col_cstr_null(
+    id int NULL,
+    name varchar CONSTRAINT name_constraint NOT NULL
+);
+
+--  CHECK ( expression ) [ NO INHERIT ] |
+CREATE TABLE col_cstr_check(
+    id int CHECK (id > 10),
+    name varchar NOT NULL
+);
+CREATE TABLE col_cstr_check_no_inherit(
+    id int CHECK (id > 10) NO INHERIT,
+    name varchar NOT NULL
+);
+
+--  DEFAULT default_expr |
+CREATE TABLE col_cstr_default(
+    id int NOT NULL,
+    name varchar DEFAULT 'foo'
+);
+
+--  GENERATED ALWAYS AS ( generation_expr ) STORED |
+CREATE TABLE col_cstr_generated_always_as(
+    id int NOT NULL,
+    id_generated int GENERATED ALWAYS AS ( id * 10 ) STORED
+);
+
+--  GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ] |
+CREATE TABLE col_cstr_generated_always_as_identity(
+    id int NOT NULL,
+    id_generated int GENERATED ALWAYS AS IDENTITY
+);
+CREATE TABLE col_cstr_generated_by_default_as_identity_with_options(
+    id int NOT NULL,
+    id_generated int GENERATED BY DEFAULT AS IDENTITY ( INCREMENT BY 10 )
+);
+
+--  UNIQUE [ NULLS [ NOT ] DISTINCT ] |
+CREATE TABLE col_cstr_unique(
+    id int NOT NULL,
+    name varchar UNIQUE
+);
+CREATE TABLE col_cstr_unique_nulls_distinct(
+    id int NOT NULL,
+    name varchar UNIQUE NULLS DISTINCT
+);
+CREATE TABLE col_cstr_unique_nulls_not_distinct(
+    id int NOT NULL,
+    name varchar UNIQUE NULLS NOT DISTINCT
+);
+
+--  PRIMARY KEY |
+CREATE TABLE col_cstr_primary_key(
+    id int PRIMARY KEY,
+    name varchar UNIQUE
+);
+
+--  REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+--    [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
+CREATE TABLE col_cstr_reference_table_default(
+    id int REFERENCES col_cstr_primary_key,
+    name varchar
+);
+CREATE TABLE col_cstr_reference_table_column(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name)
+);
+-- [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+-- skip testing MATCH PARTIAL, which is treated as a syntax error with message
+-- ERROR:  MATCH PARTIAL not yet implemented
+CREATE TABLE col_cstr_reference_table_column_match_full(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) MATCH FULL
+);
+CREATE TABLE col_cstr_reference_table_column_match_simple(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) MATCH SIMPLE
+);
+
+-- [ ON DELETE referential_action ]
+CREATE TABLE col_cstr_reference_table_column_on_delete_no_action(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE NO ACTION
+);
+CREATE TABLE col_cstr_reference_table_column_on_delete_restrict(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE RESTRICT
+);
+CREATE TABLE col_cstr_reference_table_column_on_delete_cascade(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE CASCADE
+);
+CREATE TABLE col_cstr_reference_table_column_on_delete_set_null(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET NULL
+);
+CREATE TABLE col_cstr_reference_table_column_on_delete_set_null_with_column(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET NULL (name),
+    foo varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET NULL (foo)
+);
+CREATE TABLE col_cstr_reference_table_column_on_delete_set_default(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET DEFAULT
+);
+CREATE TABLE col_cstr_reference_table_column_on_delete_set_default_with_col(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET DEFAULT (name)
+);
+
+-- [ ON UPDATE referential_action ]
+CREATE TABLE col_cstr_reference_table_column_on_update_no_action(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE NO ACTION
+);
+CREATE TABLE col_cstr_reference_table_column_on_update_restrict(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE RESTRICT
+);
+CREATE TABLE col_cstr_reference_table_column_on_update_cascade(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE CASCADE
+);
+CREATE TABLE col_cstr_reference_table_column_on_update_set_null(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE SET NULL
+);
+CREATE TABLE col_cstr_reference_table_column_on_update_set_default(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE SET DEFAULT
+);
+-- complex combinations
+CREATE TABLE col_cstr_reference_table_column_complex_combination1(
+    id int,
+    name varchar REFERENCES col_cstr_primary_key (name) MATCH FULL ON DELETE NO ACTION ON UPDATE NO ACTION
+);
+CREATE TABLE col_cstr_reference_table_column_complex_combination2(
+    id int REFERENCES col_cstr_primary_key MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET NULL,
+    name varchar
+);
+
+-- [ DEFERRABLE | NOT DEFERRABLE ]
+CREATE TABLE col_cstr_deferable(
+    id int,
+    name varchar UNIQUE DEFERRABLE
+);
+CREATE TABLE col_cstr_not_deferable(
+    id int PRIMARY KEY NOT DEFERRABLE,
+    name varchar
+);
+
+-- [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+CREATE TABLE col_cstr_initially_deferred(
+    id int PRIMARY KEY INITIALLY DEFERRED,
+    name varchar
+);
+CREATE TABLE col_cstr_initially_immediate(
+    id int,
+    name varchar UNIQUE INITIALLY IMMEDIATE
+);
+
+-- complex combination
+CREATE TABLE col_cstr_complex_combination(
+    id int,
+    name varchar CONSTRAINT name_constraint REFERENCES col_cstr_primary_key (name) MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE
+);
+
+-- part 4: table constraints
+-- [ CONSTRAINT constraint_name ]
+-- { CHECK ( expression ) [ NO INHERIT ] |
+CREATE TABLE tbl_cstr_check_1(
+    CONSTRAINT id_constraint CHECK (id > 10),
+    id int,
+    name varchar
+);
+CREATE TABLE tbl_cstr_check_2(
+    id int,
+    name varchar,
+    CONSTRAINT table_check CHECK (id > 10) NO INHERIT
+);
+CREATE TABLE tbl_cstr_check_no_inherit(
+    id int,
+    name varchar,
+    CHECK (id > 10) NO INHERIT
+);
+
+--   UNIQUE [ NULLS [ NOT ] DISTINCT ] ( column_name [, ... ] ) [ INCLUDE ( column_name [, ...]) ] |
+CREATE TABLE tbl_cstr_unique(
+    id int,
+    name varchar,
+    UNIQUE (id)
+);
+CREATE TABLE tbl_cstr_unique_multicols(
+    id int,
+    name varchar,
+    UNIQUE (id, name)
+);
+CREATE TABLE tbl_cstr_unique_nulls_distinct(
+    id int,
+    name varchar,
+    UNIQUE NULLS DISTINCT (id)
+);
+CREATE TABLE tbl_cstr_unique_nulls_not_distinct(
+    id int,
+    name varchar,
+    UNIQUE NULLS NOT DISTINCT (id, name)
+);
+CREATE TABLE tbl_cstr_unique_nulls_distinct_include(
+    id int,
+    name varchar,
+    UNIQUE NULLS DISTINCT (id) INCLUDE (name)
+);
+CREATE TABLE tbl_cstr_unique_nulls_distinct_include_multi(
+    id int,
+    name varchar,
+    info varchar,
+    UNIQUE NULLS DISTINCT (id) INCLUDE (name, info)
+);
+
+-- PRIMARY KEY ( column_name [, ... ] ) [ INCLUDE ( column_name [, ...]) ] |
+CREATE TABLE tbl_cstr_primary_key(
+    id int,
+    name varchar,
+    PRIMARY KEY (id)
+);
+CREATE TABLE tbl_cstr_primary_key_multicols(
+    id int,
+    name varchar,
+    PRIMARY KEY (id, name)
+);
+CREATE TABLE tbl_cstr_primary_key_include(
+    id int,
+    name varchar,
+    PRIMARY KEY (id) INCLUDE (name)
+);
+CREATE TABLE tbl_cstr_primary_key_include_multicols(
+    id int,
+    name varchar,
+    info varchar,
+    PRIMARY KEY (id) INCLUDE (name, info)
+);
+
+--   EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] |
+CREATE TABLE tbl_cstr_exclude(
+    id int,
+    name varchar,
+    EXCLUDE (name WITH =)
+);
+CREATE TABLE tbl_cstr_exclude_multi(
+    id int,
+    name varchar,
+    EXCLUDE ((id*10) with =, name WITH =)
+);
+CREATE TABLE tbl_cstr_exclude_index_method(
+    id int,
+    name varchar,
+    EXCLUDE USING btree ((id*10) with =, name WITH =)
+);
+-- [ INCLUDE ( column_name [, ... ] ) ]
+CREATE TABLE tbl_cstr_exclude_with_index_params_include_1(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) INCLUDE (name)
+);
+CREATE TABLE tbl_cstr_exclude_with_index_params_include_2(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) INCLUDE (id, name)
+);
+-- [ WITH ( storage_parameter [= value] [, ... ] ) ]
+CREATE TABLE tbl_cstr_exclude_with_index_params_storage_1(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) WITH (fillfactor = 20)
+);
+CREATE TABLE tbl_cstr_exclude_with_index_params_storage_2(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) WITH (fillfactor = 20, deduplicate_items = false)
+);
+-- [ USING INDEX TABLESPACE tablespace_name ]
+CREATE TABLE tbl_cstr_exclude_with_index_params_tablespace(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) USING INDEX TABLESPACE pg_default
+);
+-- index_parameters complex combination
+CREATE TABLE tbl_cstr_exclude_with_index_params_complex(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) INCLUDE (id, name) WITH (fillfactor = 20, deduplicate_items = false) USING INDEX TABLESPACE pg_default
+);
+-- [ WHERE ( predicate ) ]
+CREATE TABLE tbl_cstr_exclude_with_predicate(
+    id int,
+    name varchar,
+    EXCLUDE (id WITH =) WHERE (name<>'foo')
+);
+-- complex combination for table constraint clauses
+CREATE TABLE tbl_cstr_exclude_complex_combination(
+    id int,
+    name varchar,
+    EXCLUDE USING btree (id WITH =, name WITH =) INCLUDE (id, name) WITH (fillfactor = 20, deduplicate_items = false) USING INDEX TABLESPACE pg_default WHERE (name<>'foo')
+);
+
+-- FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
+--     [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
+CREATE TABLE tbl_cstr_foreign_table(
+    id int PRIMARY KEY,
+    name varchar UNIQUE,
+    UNIQUE (id, name)
+);
+CREATE TABLE tbl_cstr_foreign_key_simple_1(
+    id int,
+    name varchar,
+    FOREIGN KEY (id) REFERENCES tbl_cstr_foreign_table
+);
+CREATE TABLE tbl_cstr_foreign_key_simple_2(
+    id int,
+    name varchar,
+    FOREIGN KEY (id) REFERENCES tbl_cstr_foreign_table(id)
+);
+CREATE TABLE tbl_cstr_foreign_key_multiple_keys(
+    id int,
+    name varchar,
+    FOREIGN KEY (id, name) REFERENCES tbl_cstr_foreign_table (id, name)
+);
+
+-- some combinations from REFERENCES clause, which is already tested in part 3
+CREATE TABLE tbl_cstr_reference_table_complex_combination1(
+    id int,
+    name varchar,
+    FOREIGN KEY (name) REFERENCES tbl_cstr_foreign_table (name) MATCH SIMPLE ON DELETE CASCADE ON UPDATE SET NULL
+);
+CREATE TABLE tbl_cstr_reference_table_complex_combination2(
+    id int,
+    name varchar,
+    CONSTRAINT tbl_cstr FOREIGN KEY (id, name) REFERENCES tbl_cstr_foreign_table (id, name) ON DELETE SET NULL (id, name) ON UPDATE SET DEFAULT
+);
+
+-- [ DEFERRABLE | NOT DEFERRABLE ]
+CREATE TABLE tbl_cstr_reference_table_column_deferable(
+    id int,
+    name varchar,
+    UNIQUE (id, name) DEFERRABLE
+);
+CREATE TABLE tbl_cstr_reference_table_column_not_deferable(
+    id int,
+    name varchar,
+    PRIMARY KEY (id) NOT DEFERRABLE
+);
+
+-- [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+CREATE TABLE tbl_cstr_reference_table_column_initially_deferred(
+    id int,
+    name varchar,
+    UNIQUE (id, name) INITIALLY DEFERRED
+);
+CREATE TABLE tbl_cstr_reference_table_column_initially_immediate(
+    id int,
+    name varchar,
+    PRIMARY KEY (id) INITIALLY IMMEDIATE
+);
+
+-- complex combinations
+CREATE TABLE tbl_cstr_reference_table_column_complex_combination1(
+    id int,
+    name varchar,
+    CONSTRAINT tbl_cstr FOREIGN KEY (name) REFERENCES tbl_cstr_foreign_table (name) MATCH FULL ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED
+);
+CREATE TABLE tbl_cstr_reference_table_column_complex_combination2(
+    id int,
+    name varchar,
+    CONSTRAINT tbl_cstr PRIMARY KEY (id, name) NOT DEFERRABLE INITIALLY IMMEDIATE
+);
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/create_extension.sql b/src/test/modules/test_ddl_deparse_regress/sql/create_extension.sql
new file mode 100644
index 0000000000..d23e7fd9ce
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/sql/create_extension.sql
@@ -0,0 +1,5 @@
+---
+--- CREATE_EXTENSION
+---
+
+CREATE EXTENSION pg_stat_statements;
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/create_index.sql b/src/test/modules/test_ddl_deparse_regress/sql/create_index.sql
new file mode 100644
index 0000000000..6794eb4c0d
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/sql/create_index.sql
@@ -0,0 +1,21 @@
+-- Currently, ddl deparse only supports the TABLE command. Therefore, this
+-- test file is only prepared for testing the TABLE command.
+
+-- Command prepared for alter_table.sql.
+-- For "ADD table_constraint_using_index"
+CREATE TABLE test_add_constraint_using_index(
+    id1 int,
+    id2 int,
+    id3 int,
+    id4 int,
+    id5 int,
+    id6 int,
+    id7 int
+);
+CREATE UNIQUE INDEX test_add_constraint_used_index1 ON test_add_constraint_using_index (id1);
+CREATE UNIQUE INDEX test_add_constraint_used_index2 ON test_add_constraint_using_index (id2);
+CREATE UNIQUE INDEX test_add_constraint_used_index3 ON test_add_constraint_using_index (id3);
+CREATE UNIQUE INDEX test_add_constraint_used_index4 ON test_add_constraint_using_index (id4);
+CREATE UNIQUE INDEX test_add_constraint_used_index5 ON test_add_constraint_using_index (id5);
+CREATE UNIQUE INDEX test_add_constraint_used_index6 ON test_add_constraint_using_index (id6);
+CREATE UNIQUE INDEX test_add_constraint_used_index7 ON test_add_constraint_using_index (id7);
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/create_rule.sql b/src/test/modules/test_ddl_deparse_regress/sql/create_rule.sql
new file mode 100644
index 0000000000..9060abc783
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/sql/create_rule.sql
@@ -0,0 +1,41 @@
+-- Currently, ddl deparse only supports the TABLE command. Therefore, this
+-- test file is only prepared for testing the TABLE command.
+
+-- Command prepared for alter_table.sql.
+-- For "DISABLE RULE rewrite_rule_name"
+CREATE TABLE test_disable_rule(
+    id int,
+    name varchar,
+    description text,
+    price float4,
+    quantity int,
+    purchase_date date
+);
+CREATE RULE sample_rule1 AS
+    ON UPDATE TO test_disable_rule
+    DO INSTEAD
+        SELECT * FROM test_disable_rule;
+-- For "ENABLE RULE rewrite_rule_name"
+CREATE TABLE test_enable_rule(
+    LIKE test_disable_rule
+);
+CREATE RULE sample_rule2 AS
+    ON UPDATE TO test_enable_rule
+    DO INSTEAD
+        SELECT * FROM test_enable_rule;
+-- For "ENABLE REPLICA RULE rewrite_rule_name"
+CREATE TABLE test_enable_replica_rule(
+    LIKE test_disable_rule
+);
+CREATE RULE sample_rule_enable_replica AS
+    ON UPDATE TO test_enable_replica_rule
+    DO INSTEAD
+        SELECT * FROM test_enable_replica_rule;
+-- For "ENABLE ALWAYS RULE rewrite_rule_name"
+CREATE TABLE test_enable_always_rule(
+    LIKE test_disable_rule
+);
+CREATE RULE sample_rule_enable_always AS
+    ON UPDATE TO test_enable_always_rule
+    DO INSTEAD
+        SELECT * FROM test_enable_always_rule;
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/create_schema.sql b/src/test/modules/test_ddl_deparse_regress/sql/create_schema.sql
new file mode 100644
index 0000000000..91be48f3da
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/sql/create_schema.sql
@@ -0,0 +1,6 @@
+-- Currently, ddl deparse only supports the TABLE command. Therefore, this
+-- test file is only prepared for testing the TABLE command.
+
+-- Command prepared for alter_table.sql.
+-- For "SET SCHEMA new_schema"
+CREATE SCHEMA new_test_schema;
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/create_table.sql b/src/test/modules/test_ddl_deparse_regress/sql/create_table.sql
new file mode 100644
index 0000000000..6c7b5d72ef
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/sql/create_table.sql
@@ -0,0 +1,414 @@
+-- part 1: shared prefixes
+-- [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ]
+CREATE TABLE part1_simple_table(
+    id int,
+    name varchar
+);
+CREATE TEMPORARY TABLE part1_temp_table0(
+    id int,
+    name varchar
+);
+CREATE TEMP TABLE part1_temp_table(
+    id int,
+    name varchar
+);
+-- GLOBAL TEMP TATBLE is deprecated, expect warning message and create local temp table
+CREATE GLOBAL TEMP TABLE part1_global_temp_table(
+    id int,
+    name varchar
+);
+CREATE LOCAL TEMP TABLE part1_local_temp_table(
+    id int,
+    name varchar
+);
+CREATE UNLOGGED TABLE part1_unlogged_table(
+    id int,
+    name varchar
+);
+-- [ IF NOT EXISTS ]
+CREATE TABLE IF NOT EXISTS part1_simple_table(
+    id int,
+    name varchar
+);
+CREATE TABLE IF NOT EXISTS part1_local_temp_table_not_exists(
+    id int,
+    name varchar
+);
+
+-- part 2: shared suffixes
+-- [ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
+CREATE TABLE part2_partition_by_range_simple(
+    id int,
+    name varchar
+) PARTITION BY RANGE (id);
+CREATE TABLE part2_partition_by_list_simple(
+    id int,
+    name varchar
+) PARTITION BY LIST (id);
+CREATE TABLE part2_partition_by_hash_simple(
+    id int,
+    name varchar
+) PARTITION BY HASH (id);
+CREATE TABLE part2_partition_with_expression(
+    id int,
+    name varchar
+) PARTITION BY RANGE ((id * 190), name);
+CREATE TABLE part2_partition_with_collation(
+    id int,
+    name varchar
+) PARTITION BY LIST (name COLLATE "C");
+CREATE TABLE part2_partition_with_opclass(
+    id int,
+    name varchar
+) PARTITION BY HASH (id int4_ops, name varchar_ops);
+CREATE TABLE part2_partition_with_collation_opclass(
+    id int,
+    name varchar
+) PARTITION BY RANGE ((id * 10) int4_ops, name COLLATE "C" varchar_ops);
+
+-- [ USING method ]
+-- default method
+CREATE TABLE part2_using_default_access_method(
+    id int,
+    name varchar
+) USING heap;
+
+-- [ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
+CREATE TABLE part2_without_oids(
+    id int,
+    name varchar
+) WITHOUT OIDS;
+CREATE TABLE part2_with_one_storage_param(
+    id int,
+    name varchar
+) WITH (fillfactor = 20);
+CREATE TABLE part2_with_multiple_storage_params(
+    id int,
+    name varchar
+) WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true);
+
+-- [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
+CREATE TEMP TABLE part2_on_commit_preserve_rows(
+    id int,
+    name varchar
+) ON COMMIT PRESERVE ROWS;
+CREATE TEMP TABLE part2_on_commit_delete_rows(
+    id int,
+    name varchar
+) ON COMMIT DELETE ROWS;
+CREATE TEMPORARY TABLE part2_on_commit_drop(
+    id int,
+    name varchar
+) ON COMMIT DROP;
+
+-- [ TABLESPACE tablespace_name ]
+CREATE TABLE part2_tablespace_pg_default(
+    id int,
+    name varchar
+) TABLESPACE pg_default;
+
+-- some complex combinations from the components above
+CREATE TEMPORARY TABLE part2_combination_1(
+    id int,
+    name varchar
+) PARTITION BY RANGE (id) ON COMMIT PRESERVE ROWS;
+
+CREATE LOCAL TEMP TABLE part2_combination_2(
+    id int,
+    name varchar
+) USING heap WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true) ON COMMIT PRESERVE ROWS TABLESPACE pg_default;
+
+CREATE TABLE part2_combination_3(
+    id int,
+    name varchar
+) USING heap WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true) TABLESPACE pg_default;
+
+
+-- part 5: LIKE source_table [ like_option ... ]
+CREATE TABLE part5_source_table(
+    id int,
+    name varchar
+);
+CREATE TABLE part5_source_table2(
+    id2 int,
+    name2 varchar
+);
+CREATE TABLE part5_like_simple(
+    LIKE part5_source_table
+);
+-- { INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
+CREATE TABLE part5_including_all(
+    LIKE part5_source_table INCLUDING ALL
+);
+CREATE TABLE part5_including_comments(
+    LIKE part5_source_table INCLUDING COMMENTS
+);
+CREATE TABLE part5_including_compression(
+    LIKE part5_source_table INCLUDING COMPRESSION
+);
+CREATE TABLE part5_including_defaults(
+    LIKE part5_source_table INCLUDING DEFAULTS
+);
+CREATE TABLE part5_including_generated(
+    LIKE part5_source_table INCLUDING GENERATED
+);
+CREATE TABLE part5_including_identity(
+    LIKE part5_source_table INCLUDING IDENTITY
+);
+CREATE TABLE part5_including_indexes(
+    LIKE part5_source_table INCLUDING INDEXES
+);
+CREATE TABLE part5_including_statistics(
+    LIKE part5_source_table INCLUDING STATISTICS
+);
+CREATE TABLE part5_including_storage(
+    LIKE part5_source_table INCLUDING STORAGE
+);
+CREATE TABLE part5_excluding_all(
+    LIKE part5_source_table EXCLUDING ALL
+);
+CREATE TABLE part5_excluding_comments(
+    LIKE part5_source_table EXCLUDING COMMENTS
+);
+CREATE TABLE part5_excluding_compression(
+    LIKE part5_source_table EXCLUDING COMPRESSION
+);
+CREATE TABLE part5_excluding_defaults(
+    LIKE part5_source_table EXCLUDING DEFAULTS
+);
+CREATE TABLE part5_excluding_generated(
+    LIKE part5_source_table EXCLUDING GENERATED
+);
+CREATE TABLE part5_excluding_identity(
+    LIKE part5_source_table EXCLUDING IDENTITY
+);
+CREATE TABLE part5_excluding_indexes(
+    LIKE part5_source_table EXCLUDING INDEXES
+);
+CREATE TABLE part5_excluding_statistics(
+    LIKE part5_source_table EXCLUDING STATISTICS
+);
+CREATE TABLE part5_excluding_storage(
+    LIKE part5_source_table EXCLUDING STORAGE
+);
+CREATE TABLE part5_like_list(
+    LIKE part5_source_table,
+    info text,
+    LIKE part5_source_table2 INCLUDING ALL,
+    CONSTRAINT primary_key_constraint PRIMARY KEY (id)
+);
+
+-- part 6: partition specification
+-- PARTITION OF parent_table { FOR VALUES partition_bound_spec | DEFAULT }
+CREATE TABLE part6_parent_table_range(
+    id int,
+    name varchar
+) PARTITION BY RANGE (id);
+CREATE TABLE part6_parent_table_list(
+    id int,
+    name varchar
+) PARTITION BY LIST (id);
+CREATE TABLE part6_parent_table_hash(
+    id int,
+    name varchar
+) PARTITION BY HASH (id);
+CREATE TABLE part6_partition_default PARTITION OF part6_parent_table_range DEFAULT;
+-- FROM ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] )
+--  TO ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] ) |
+CREATE TABLE part6_partition_spec_range1 PARTITION OF part6_parent_table_range
+FOR VALUES FROM (MINVALUE) TO (2);
+CREATE TABLE part6_partition_spec_range2 PARTITION OF part6_parent_table_range
+FOR VALUES FROM (3) TO (MAXVALUE);
+-- IN ( partition_bound_expr [, ...] ) |
+CREATE TABLE part6_partition_spec_list PARTITION OF part6_parent_table_list
+FOR VALUES IN (1, (1+2), (4+5));
+-- WITH ( MODULUS numeric_literal, REMAINDER numeric_literal )
+CREATE TABLE part6_partition_spec_hash PARTITION OF part6_parent_table_hash
+FOR VALUES WITH (MODULUS 10, REMAINDER 2);
+
+
+-- part7: create table
+-- all data types
+CREATE TABLE part7_source_table(
+    src_id int
+);
+CREATE TABLE part7_data_types(
+    var1 int8,
+    var2 serial8,
+    var3 bit,
+    var4 bit[5],
+    var5 varbit,
+    var6 varbit[5],
+    var7 bool,
+    var8 box,
+    var9 bytea,
+    var10 char,
+    var11 char[8],
+    var12 varchar,
+    var13 varchar[5],
+    var14 cidr,
+    var15 circle,
+    var16 date,
+    var17 double precision,
+    var18 inet,
+    var19 int,
+    var20 int4,
+    var21 interval,
+    var22 json,
+    var23 jsonb,
+    var24 line,
+    var25 lseg,
+    var26 macaddr,
+    var27 money,
+    var28 decimal,
+    var29 decimal(3,1),
+    var30 path,
+    var31 pg_lsn,
+    var32 pg_snapshot,
+    var33 point,
+    var34 polygon,
+    var35 float4,
+    var36 int2,
+    var37 serial2,
+    var38 serial4,
+    var39 text,
+    var40 time,
+    var41 time(3),
+    var42 timetz,
+    var43 timetz(3),
+    var44 timestamp,
+    var45 timestamp(3),
+    var46 timestamptz,
+    var47 timestamptz(3),
+    var48 tsquery,
+    var49 tsvector,
+    var50 txid_snapshot,
+    var51 uuid,
+    var52 xml
+);
+CREATE TABLE part7_compression_collate(
+    str1 varchar COMPRESSION "pglz",
+    str2 varchar COLLATE "C"
+);
+CREATE TABLE part7_inherits_parent(
+    id int,
+    name varchar
+)
+INHERITS (part7_data_types, part7_compression_collate);
+
+CREATE TABLE part7_combine_all_clauses(
+    id varchar(5) COMPRESSION "pglz" COLLATE "C" CONSTRAINT id_constraint DEFAULT 'foo',
+    PRIMARY KEY (id),
+    LIKE part7_source_table,
+    name varchar
+)
+INHERITS (part7_data_types, part7_compression_collate)
+USING heap
+WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
+TABLESPACE pg_default;
+
+CREATE TEMP TABLE part7_combine_all_clauses_temp(
+    id varchar(5) COMPRESSION "pglz" COLLATE "C" CONSTRAINT id_constraint DEFAULT 'foo',
+    PRIMARY KEY (id),
+    LIKE part7_source_table,
+    name varchar
+)
+INHERITS (part7_data_types, part7_compression_collate)
+USING heap
+WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
+ON COMMIT DELETE ROWS
+TABLESPACE pg_default;
+
+-- CREATE TABLE ... AS
+CREATE TABLE test_ctas AS SELECT 0 AS col1, 1 AS col2;
+
+-- part8: create typed table
+CREATE TABLE part8_create_typed_table OF part8_people_type;
+CREATE TABLE part8_create_typed_table_simple OF part8_people_type(
+    weight,
+    PRIMARY KEY (id)
+);
+CREATE TABLE part8_create_typed_table_with_options_constaints OF part8_people_type(
+    weight WITH OPTIONS NOT NULL,
+    name UNIQUE,
+    PRIMARY KEY (id)
+);
+
+CREATE TABLE part8_create_typed_table_complex_combinations OF part8_people_type(
+    weight WITH OPTIONS NOT NULL,
+    name UNIQUE,
+    PRIMARY KEY (id)
+)
+USING heap
+WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
+TABLESPACE pg_default;
+
+-- part9: create table as a partition of parent table, FOR VALUES clause is tested in part 6
+CREATE TABLE part9_parent_table_range(
+    id int,
+    name varchar,
+    height float4,
+    weight float4
+) PARTITION BY RANGE (height);
+CREATE TABLE part9_parent_table_list(
+    id int,
+    name varchar,
+    height float4,
+    weight float4
+) PARTITION BY LIST (name);
+CREATE TABLE part9_parent_table_hash(
+    id int,
+    name varchar,
+    height float4,
+    weight float4
+) PARTITION BY HASH (id);
+
+-- TOFIX
+-- CREATE TABLE part9_partition_with_options_constraintscd
+-- PARTITION OF part9_parent_table_range (
+--     id PRIMARY KEY,
+--     name WITH OPTIONS NOT NULL,
+--     weight,
+--     CHECK (height > 0)
+-- )
+-- FOR VALUES FROM (MINVALUE) TO (2);
+
+-- TOFIX
+-- CREATE TABLE part9_partition_with_options_constraints_default
+-- PARTITION OF part9_parent_table_range (
+--     id PRIMARY KEY,
+--     name WITH OPTIONS NOT NULL,
+--     CHECK (height > 0)
+-- ) DEFAULT;
+
+-- TOFIX
+-- CREATE TABLE part9_partition_complex_combinations
+-- PARTITION OF part9_parent_table_range (
+--     id PRIMARY KEY,
+--     name WITH OPTIONS NOT NULL,
+--     CHECK (height > 0)
+-- )
+-- FOR VALUES FROM (3) TO (10)
+-- USING heap
+-- WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
+-- TABLESPACE pg_default;
+
+-- copied from old create_table.sql
+-- Test TableLikeClause is handled properly
+CREATE TABLE ctlt1 (a text CHECK (length(a) > 2) PRIMARY KEY, b text);
+ALTER TABLE ctlt1 ALTER COLUMN a SET STORAGE MAIN;
+ALTER TABLE ctlt1 ALTER COLUMN b SET STORAGE EXTERNAL;
+CREATE TABLE ctlt1_like (LIKE ctlt1 INCLUDING ALL);
+
+-- Test foreign key constraint is handled in a following ALTER TABLE ADD CONSTRAINT FOREIGN KEY REFERENCES subcommand
+CREATE TABLE product (id int PRIMARY KEY, name text);
+CREATE TABLE orders2 (order_id int PRIMARY KEY, product_id int
+REFERENCES product (id));
+
+-- Test CREATE and ALTER inherited table
+CREATE TABLE gtest30 (
+a int,
+b int GENERATED ALWAYS AS (a * 2) STORED
+);
+CREATE TABLE gtest30_1 () INHERITS (gtest30);
+ALTER TABLE gtest30 ALTER COLUMN b DROP EXPRESSION;
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/create_type.sql b/src/test/modules/test_ddl_deparse_regress/sql/create_type.sql
new file mode 100644
index 0000000000..a5fec467cb
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/sql/create_type.sql
@@ -0,0 +1,17 @@
+-- Currently, ddl deparse only supports the TABLE command. Therefore, this
+-- test file is only prepared for testing the TABLE command.
+
+-- Command prepared for create_table.sql.
+CREATE TYPE part8_people_type AS (
+    id int,
+    name varchar,
+    height float4,
+    weight float4
+);
+
+-- Command prepared for alter_table.sql.
+-- For "OF type_name"
+CREATE TYPE test_type_product_type AS (
+    id int,
+    name varchar
+);
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/test_ddl_deparse.sql b/src/test/modules/test_ddl_deparse_regress/sql/test_ddl_deparse.sql
new file mode 100644
index 0000000000..736b33908c
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/sql/test_ddl_deparse.sql
@@ -0,0 +1,29 @@
+CREATE ROLE ddl_testing_role SUPERUSER;
+SET ROLE ddl_testing_role;
+
+CREATE EXTENSION test_ddl_deparse_regress;
+
+CREATE OR REPLACE FUNCTION test_ddl_deparse()
+  RETURNS event_trigger LANGUAGE plpgsql AS
+$$
+DECLARE
+	r record;
+	deparsed_json text;
+BEGIN
+	FOR r IN SELECT * FROM pg_event_trigger_ddl_commands()
+		-- Some TABLE commands generate sequence-related commands, also deparse them.
+		WHERE command_tag in ('ALTER FOREIGN TABLE', 'ALTER TABLE',
+							  'CREATE FOREIGN TABLE', 'CREATE TABLE',
+							  'CREATE TABLE AS', 'DROP FOREIGN TABLE',
+							  'DROP TABLE', 'ALTER SEQUENCE',
+							  'CREATE SEQUENCE', 'DROP SEQUENCE')
+	LOOP
+		deparsed_json = pg_catalog.ddl_deparse_to_json(r.command);
+		RAISE NOTICE 'deparsed json: %', deparsed_json;
+		RAISE NOTICE 're-formed command: %', pg_catalog.ddl_deparse_expand_command(deparsed_json);
+	END LOOP;
+END;
+$$;
+
+CREATE EVENT TRIGGER test_ddl_deparse
+ON ddl_command_end EXECUTE PROCEDURE test_ddl_deparse();
diff --git a/src/test/modules/test_ddl_deparse_regress/t/001_compare_dumped_results.pl b/src/test/modules/test_ddl_deparse_regress/t/001_compare_dumped_results.pl
new file mode 100644
index 0000000000..d6c27958c1
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/t/001_compare_dumped_results.pl
@@ -0,0 +1,235 @@
+use strict;
+use warnings;
+use Env;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+use File::Basename;
+
+sub execute_test_file {
+    my $test_name = $_[0];
+    my $node = $_[1];
+    my $dbname = $_[2];
+    my $user = $_[3];
+    my $test_file = "./sql/${test_name}.sql";
+    my $content = do{local(@ARGV,$/)=$test_file;<>};
+    my $node_error = '';
+
+    $node -> psql($dbname, $content,
+        stderr => \$node_error,
+        extra_params => ["-U", "${user}"]);
+    # check execution of test SQL commands
+    ok($node_error eq '', "execute test SQL commands from ".$test_name) or diag("Failure from "
+        .$test_file.": ".$node_error);
+}
+
+sub execute_test_case {
+    my $test_name = $_[0];
+    my $pub_node = $_[1];
+    my $sub_node = $_[2];
+    my $dbname = $_[3];
+    my $user = $_[4];
+    my $outputdir = $PostgreSQL::Test::Utils::tmp_check;
+
+    # set up deparse testing resources
+    create_deparse_testing_resources_on_pub_node($pub_node, $dbname);
+
+    # execute the SQL commands
+    execute_test_file($test_name, $pub_node, $dbname, $user);
+
+    # retrieve reformed SQL commands on pub node, write to file
+    my $ddl_sql = '';
+    $pub_node -> psql($dbname,q(
+        select ddl_deparse_expand_command(ddl) || ';' from deparsed_ddls ORDER BY id ASC),
+        stdout => \$ddl_sql,
+        extra_params => ["-U", "${user}"]);
+    mkdir ${outputdir}."/ddl", 0755;
+    my $ddl_output_file = ${outputdir}."/ddl/${test_name}.sql";
+    open(FH, '>', $ddl_output_file) or die $!;
+    print FH $ddl_sql;
+    close(FH);
+
+    # execute reformed SQL commands on sub node
+    my $sub_node_error = '';
+    $sub_node -> psql($dbname, $ddl_sql,
+        stderr => \$sub_node_error,
+        extra_params => ["-U", "${user}"]);
+    # check execution of reformed DDL commands
+    ok($sub_node_error eq '', "replay reformed DDL commands from ".$test_name) or diag("Failure from "
+        .$ddl_output_file.": ".$sub_node_error);
+
+    # clean up deparse testing resources
+    clean_deparse_testing_resources_on_pub_node($pub_node, $dbname);
+    # dump from pub node and sub node
+    mkdir ${outputdir}."/dumps", 0755;
+    my $pub_dump = ${outputdir}."/dumps/${test_name}_pub.dump";
+    my $sub_dump = ${outputdir}."/dumps/${test_name}_sub.dump";
+    my $dump_diff = ${outputdir}."/dumps/${test_name}_dump.diff";
+    system("pg_dumpall "
+        . "-s "
+        . "-f "
+        . $pub_dump . " "
+        . "--no-sync "
+        .  '-p '
+        . $pub_node->port)  == 0 or die "Dump pub node failed in ${test_name}";
+    system("pg_dumpall "
+        . "-s "
+        . "-f "
+        . $sub_dump . " "
+        . "--no-sync "
+        .  '-p '
+        . $sub_node->port)  == 0 or die "Dump sub node failed in ${test_name}";
+
+    # compare dumped results
+    system("diff ".$pub_dump." ".$sub_dump." > ".$dump_diff);
+    ok(system("diff ".$pub_dump." ".$sub_dump) == 0, "compare dumped results in ${test_name}")
+        or diag("Dumped results are different in ".$test_name
+        .", check ".$dump_diff);
+}
+
+sub init_node {
+    my $node_name = $_[0];
+    my $node = PostgreSQL::Test::Cluster->new($node_name);
+    # increase some settings that Cluster->new makes too low by default.
+    $node -> init();
+    $node -> start();
+    $node -> append_conf('postgresql.conf', 'max_connections = 25');
+    $node -> append_conf('postgresql.conf', 'client_min_messages = error');
+    $node -> append_conf('postgresql.conf', 'max_prepared_transactions = 10');
+    $node -> restart();
+    return $node;
+}
+
+sub init_pub_node {
+    my $node_name = $_[0]."_pub";
+    return init_node($node_name)
+}
+
+sub init_sub_node {
+    my $node_name = $_[0]."_sub";
+    return init_node($node_name)
+}
+
+sub create_deparse_testing_resources_on_pub_node {
+    my $node = $_[0];
+    my $dbname = $_[1];
+    $node -> psql($dbname, "
+        begin;
+        CREATE EXTENSION test_ddl_deparse_regress;
+        create table deparsed_ddls(id SERIAL PRIMARY KEY, tag text, object_identity text, ddl text);
+
+        create or replace function deparse_to_json()
+            returns event_trigger language plpgsql as
+        \$\$
+        declare
+            r record;
+        begin
+            for r in select * from pg_event_trigger_ddl_commands()
+		        -- Some TABLE commands generate sequence-related commands, also deparse them.
+		        WHERE command_tag in ('ALTER FOREIGN TABLE', 'ALTER TABLE',
+						              'CREATE FOREIGN TABLE', 'CREATE TABLE',
+						              'CREATE TABLE AS', 'DROP FOREIGN TABLE',
+						              'DROP TABLE', 'ALTER SEQUENCE',
+						              'CREATE SEQUENCE', 'DROP SEQUENCE')
+            loop
+                insert into deparsed_ddls(tag, object_identity, ddl) values (r.command_tag, r.object_identity, pg_catalog.ddl_deparse_to_json(r.command));
+            end loop;
+        END;
+        \$\$;
+
+        create or replace function deparse_drops_to_json()
+            returns event_trigger language plpgsql as
+        \$\$
+        declare
+            r record;
+        begin
+            for r in select * from pg_event_trigger_dropped_objects()
+            loop
+                insert into deparsed_ddls(tag, object_identity, ddl) values (r.object_type, r.object_identity, public.deparse_drop_ddl(r.object_identity, r.object_type));
+            end loop;
+        END;
+        \$\$;
+
+        create event trigger ddl_deparse_trig
+        on ddl_command_end execute procedure deparse_to_json();
+
+        create event trigger ddl_drops_deparse_trig
+        on sql_drop execute procedure deparse_drops_to_json();
+
+        commit;
+    "
+    );
+}
+
+sub clean_deparse_testing_resources_on_pub_node {
+    my $node = $_[0];
+    my $dbname = $_[1];
+    # Drop the event trigger and the function before taking a logical dump.
+    $node -> safe_psql($dbname,q(
+        drop event trigger ddl_deparse_trig;
+        drop event trigger ddl_drops_deparse_trig;
+        drop function deparse_to_json();
+        drop function deparse_drops_to_json();
+        drop table deparsed_ddls;
+        DROP EXTENSION test_ddl_deparse_regress;
+    ));
+}
+
+sub create_prerequisite_resources {
+    my $node = $_[0];
+    my $dbname = $_[1];
+    my $user = $_[2];
+    $node -> safe_psql($dbname, "CREATE ROLE ${user} SUPERUSER LOGIN CREATEDB;");
+}
+
+sub trim {
+    my @out = @_;
+    for (@out) {
+        s/^\s+//;
+        s/\s+$//;
+    }
+    return wantarray ? @out : $out[0];
+}
+
+# Create and start pub sub nodes
+my $initial_dbname = "postgres";
+my $test_dbname = "ddl_testing";
+my $user = "ddl_testing_role";
+my $pub_node = init_pub_node("test");
+my $sub_node = init_sub_node("test");
+create_prerequisite_resources($pub_node, $initial_dbname, $user);
+create_prerequisite_resources($sub_node, $initial_dbname, $user);
+$pub_node -> safe_psql($initial_dbname, "CREATE DATABASE ${test_dbname};", extra_params => ["-U", "${user}"]);
+$sub_node -> safe_psql($initial_dbname, "CREATE DATABASE ${test_dbname};", extra_params => ["-U", "${user}"]);
+
+# load test cases from the regression tests
+my @regress_tests = split /\s+/, $ENV{REGRESS};
+
+foreach(@regress_tests) {
+    my $test_name = trim($_);
+
+    # Skip if it's regression test preparation only on pub node or empty
+    # string
+    if ($test_name eq "" or $test_name eq "test_ddl_deparse" or
+        $test_name eq "create_extension")
+    {
+        next;
+    }
+
+    # Currently, ddl deparse only supports the TABLE commands. Therefore,
+    # these test files are only prepared for testing the TABLE commands.
+    if ($test_name eq "create_type" or $test_name eq "create_schema" or
+        $test_name eq "create_rule" or $test_name eq "create_index")
+    {
+        execute_test_file($test_name, $pub_node, $test_dbname, $user);
+        execute_test_file($test_name, $sub_node, $test_dbname, $user);
+        next;
+    }
+
+    execute_test_case($test_name, $pub_node, $sub_node, $test_dbname, $user);
+}
+done_testing();
+
+# Close nodes
+$pub_node->stop;
+$sub_node->stop;
diff --git a/src/test/modules/test_ddl_deparse_regress/t/002_regress_tests.pl b/src/test/modules/test_ddl_deparse_regress/t/002_regress_tests.pl
new file mode 100644
index 0000000000..32fdd596ff
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/t/002_regress_tests.pl
@@ -0,0 +1,213 @@
+use strict;
+use warnings;
+use Env;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+use File::Basename;
+
+sub execute_regress_test {
+    my $pub_node = $_[0];
+    my $sub_node = $_[1];
+    my $dbname = "postgres";
+    my $dlpath    = dirname($ENV{REGRESS_SHLIB});
+    my $inputdir = "../../regress";
+    my $outputdir = $PostgreSQL::Test::Utils::tmp_check;
+
+    # set up deparse testing resources
+    create_deparse_testing_resources_on_pub_node($pub_node, $dbname);
+
+    # execute core regression tests on pub node
+    my $regress_cmd = $ENV{PG_REGRESS}
+        . " "
+        . "--dlpath=\"$dlpath\" "
+        . "--max-concurrent-tests=20 "
+        . "--dbname="
+        . $dbname . " "
+        . "--use-existing "
+        . "--host="
+        . $pub_node->host . " "
+        . "--port="
+        . $pub_node->port . " "
+        . "--inputdir=$inputdir "
+        . "--outputdir=\"$outputdir\" "
+        . "--schedule=$inputdir/parallel_schedule";
+    print "Regression test command is $regress_cmd";
+    my $rc = system($regress_cmd);
+    if ($rc != 0)
+    {
+        # If regression test fails, dump out the regression diffs file
+        my $diffs = "${outputdir}/regression/regression.diffs";
+        if (-e $diffs)
+        {
+            print "=== dumping $diffs ===\n";
+            print slurp_file($diffs);
+            print "=== EOF ===\n";
+        }
+        die "Regression test failed";
+    }
+
+    # Retrieve SQL commands generated from deparsed DDLs on pub node
+    my $ddl_sql = '';
+    $pub_node -> psql($dbname,q(
+        select ddl_deparse_expand_command(ddl) || ';' from deparsed_ddls ORDER BY id ASC),
+        stdout => \$ddl_sql);
+
+    # Execute SQL commands on sub node
+    $sub_node -> psql($dbname, $ddl_sql);
+
+    # Clean up deparse testing resources
+    clean_deparse_testing_resources_on_pub_node($pub_node, $dbname);
+
+    # Dump from pub node and sub node
+    mkdir ${outputdir}."/dumps", 0755;
+    my $pub_dump = ${outputdir}."/dumps/regress_pub.dump";
+    my $sub_dump = ${outputdir}."/dumps/regress_sub.dump";
+    system("pg_dumpall "
+        . "-s "
+        . "-f "
+        . $pub_dump . " "
+        . "--no-sync "
+        .  '-p '
+        . $pub_node->port)  == 0 or die "Dump pub node failed";
+    system("pg_dumpall "
+        . "-s "
+        . "-f "
+        . $sub_dump . " "
+        . "--no-sync "
+        .  '-p '
+        . $sub_node->port)  == 0 or die "Dump sub node failed";
+
+    # Compare dumped results
+    is(system("diff "
+    . $pub_dump . " "
+    . $sub_dump), 0, "Comparing dumped output");
+
+    # Close nodes
+    $pub_node->stop;
+    $sub_node->stop;
+}
+
+sub init_node {
+    my $node_name = $_[0];
+    my $node = PostgreSQL::Test::Cluster->new($node_name);
+    $node->init;
+    # increase some settings that Cluster->new makes too low by default.
+    $node->adjust_conf('postgresql.conf', 'max_connections', '25');
+    $node->append_conf('postgresql.conf',
+		   'max_prepared_transactions = 10');
+    return $node;
+}
+
+sub init_pub_node {
+    my $node_name = $_[0]."_pub";
+    return init_node($node_name)
+}
+
+sub init_sub_node {
+    my $node_name = $_[0]."_sub";
+    return init_node($node_name)
+}
+
+sub create_deparse_testing_resources_on_pub_node {
+    my $node = $_[0];
+    my $dbname = $_[1];
+    $node -> psql($dbname, q(
+        begin;
+        CREATE EXTENSION test_ddl_deparse_regress;
+        create table deparsed_ddls(id SERIAL PRIMARY KEY, tag text, object_identity text, ddl text);
+        create or replace function deparse_to_json()
+            returns event_trigger language plpgsql as
+        $$
+        declare
+            r record;
+        begin
+            for r in select * from pg_event_trigger_ddl_commands()
+		        -- Some TABLE commands generate sequence-related commands, also deparse them.
+		        WHERE command_tag in ('ALTER FOREIGN TABLE', 'ALTER TABLE',
+						              'CREATE FOREIGN TABLE', 'CREATE TABLE',
+						              'CREATE TABLE AS', 'DROP FOREIGN TABLE',
+						              'DROP TABLE', 'ALTER SEQUENCE',
+						              'CREATE SEQUENCE', 'DROP SEQUENCE')
+            loop
+                insert into deparsed_ddls(tag, object_identity, ddl) values (r.command_tag, r.object_identity, pg_catalog.ddl_deparse_to_json(r.command));
+            end loop;
+        END;
+        $$;
+        create or replace function deparse_drops_to_json()
+            returns event_trigger language plpgsql as
+        $$
+        declare
+            r record;
+        begin
+            for r in select * from pg_event_trigger_dropped_objects()
+            loop
+                insert into deparsed_ddls(tag, object_identity, ddl) values (r.object_type, r.object_identity, public.deparse_drop_ddl(r.object_identity, r.object_type));
+            end loop;
+        END;
+        $$;
+        create event trigger ddl_deparse_trig
+        on ddl_command_end execute procedure deparse_to_json();
+        create event trigger ddl_drops_deparse_trig
+        on sql_drop execute procedure deparse_drops_to_json();
+        commit;
+    ));
+}
+
+sub clean_deparse_testing_resources_on_pub_node {
+    my $node = $_[0];
+    my $dbname = $_[1];
+    # Drop the event trigger and the function before taking a logical dump.
+    $node -> safe_psql($dbname,q(
+        drop event trigger ddl_deparse_trig;
+        drop event trigger ddl_drops_deparse_trig;
+        drop function deparse_to_json();
+        drop function deparse_drops_to_json();
+        drop table deparsed_ddls;
+        DROP EXTENSION test_ddl_deparse_regress;
+    ));
+}
+
+sub trim {
+    my @out = @_;
+    for (@out) {
+        s/^\s+//;
+        s/\s+$//;
+    }
+    return wantarray ? @out : $out[0];
+}
+
+# Create and start pub sub nodes
+my $pub_node = init_pub_node("regress");
+my $sub_node = init_sub_node("regress");
+$pub_node -> start;
+$sub_node -> start;
+
+# Comment the execution temporarily, an error in ddldeparse.c will cause the database exits abnormally, error signature:
+#
+# 2022-12-03 23:02:44.778 UTC [129102] pg_regress/tablespace LOG:  statement: ALTER TABLE ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default;
+# TRAP: failed Assert("IsA(stmt, AlterTableStmt)"), File: "ddldeparse.c", Line: 3371, PID: 129102
+# postgres: regress_pub: runqi postgres [local] ALTER TABLE(ExceptionalCondition+0xbb)[0x563bd6ea5b0d]
+# ...
+# /lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7fd6a74a6d90]
+# /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7fd6a74a6e40]
+# postgres: regress_pub: runqi postgres [local] ALTER TABLE(_start+0x25)[0x563bd672f1e5]
+# 2022-12-03 23:02:44.850 UTC [129058] LOG:  server process (PID 129102) was terminated by signal 6: Aborted
+# 2022-12-03 23:02:44.850 UTC [129058] DETAIL:  Failed process was running: ALTER TABLE ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default;
+# 2022-12-03 23:02:44.850 UTC [129058] LOG:  terminating any other active server processes
+# 2022-12-03 23:02:44.851 UTC [129058] LOG:  shutting down because restart_after_crash is off
+# 2022-12-03 23:02:44.852 UTC [129058] LOG:  database system is shut down
+
+# eval {execute_regress_test($pub_node, $sub_node);};
+# if ($@ ne "")
+# {
+#     fail($@);
+# }
+
+# Close nodes
+$pub_node->stop;
+$sub_node->stop;
+
+pass("regresssion test passed");
+
+done_testing();
\ No newline at end of file
diff --git a/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress--1.0.sql b/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress--1.0.sql
new file mode 100644
index 0000000000..14070cd51f
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress--1.0.sql
@@ -0,0 +1,9 @@
+/* src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ddl_deparse_regress" to load this file. \quit
+
+CREATE FUNCTION deparse_drop_ddl(IN objidentity text,
+    IN objecttype text)
+  RETURNS text IMMUTABLE STRICT
+  AS 'MODULE_PATHNAME' LANGUAGE C;
\ No newline at end of file
diff --git a/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.c b/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.c
new file mode 100644
index 0000000000..04288c68e0
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.c
@@ -0,0 +1,67 @@
+/*----------------------------------------------------------------------
+ * test_ddl_deparse_regress.c
+ *		Support functions for the test_ddl_deparse_regress module
+ *
+ * Copyright (c) 2014-2023, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "funcapi.h"
+#include "nodes/execnodes.h"
+#include "tcop/deparse_utility.h"
+#include "tcop/utility.h"
+#include "utils/builtins.h"
+#include "tcop/ddldeparse.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(deparse_drop_ddl);
+
+/*
+ * Given object_identity and object_type of dropped object, return a JSON representation of DROP command.
+ */
+Datum
+deparse_drop_ddl(PG_FUNCTION_ARGS)
+{
+	text	   *objidentity = PG_GETARG_TEXT_P(0);
+	const char	   *objidentity_str = text_to_cstring(objidentity);
+	text	   *objecttype = PG_GETARG_TEXT_P(1);
+	const char	   *objecttype_str = text_to_cstring(objecttype);
+	DropStmt	   fake_node;
+	char		   *command;
+
+	/*
+	 * XXX We can't get these three actions from
+	 * pg_event_trigger_dropped_objects, so we use fake options here.
+	 */
+	fake_node.behavior = DROP_CASCADE;
+	fake_node.missing_ok = true;
+	fake_node.concurrent = false;
+
+	// constraint is part of alter table command, no need to drop in DROP command
+	if (strcmp(objecttype_str, "table constraint") == 0) {
+		PG_RETURN_NULL();
+	} else if (strcmp(objecttype_str, "toast table") == 0) {
+		objecttype_str = "table";
+	}  else if (strcmp(objecttype_str, "default value") == 0) {
+		PG_RETURN_NULL();
+	} else if (strcmp(objecttype_str, "operator of access method") == 0) {
+		PG_RETURN_NULL();
+	} else if (strcmp(objecttype_str, "function of access method") == 0) {
+		PG_RETURN_NULL();
+	} else if (strcmp(objecttype_str, "table column") == 0) {
+		PG_RETURN_NULL();
+	}
+
+	command = deparse_drop_command(objidentity_str, objecttype_str, (Node *)&fake_node);
+
+	if (command)
+		PG_RETURN_TEXT_P(cstring_to_text(command));
+
+	PG_RETURN_NULL();
+}
\ No newline at end of file
diff --git a/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.control b/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.control
new file mode 100644
index 0000000000..a1f934e658
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.control
@@ -0,0 +1,4 @@
+comment = 'Test code for DDL deparse regress feature'
+default_version = '1.0'
+module_pathname = '$libdir/test_ddl_deparse_regress'
+relocatable = true
-- 
2.34.1

From 9f9b98814021c084cda98b8ff0cc5339e248287a Mon Sep 17 00:00:00 2001
From: Wang Wei <[email-masked]>
Date: Tue, 11 Apr 2023 14:56:47 +0800
Subject: [PATCH 6/8] Add subscription tap test for DDL replication for TABLE
 related command

---
 src/test/subscription/meson.build             |   2 +
 .../subscription/t/034_ddl_replication.pl     | 374 ++++++++++++++++++
 2 files changed, 376 insertions(+)
 create mode 100644 src/test/subscription/t/034_ddl_replication.pl

diff --git a/src/test/subscription/meson.build b/src/test/subscription/meson.build
index f85bf92b6f..7dfcc13899 100644
--- a/src/test/subscription/meson.build
+++ b/src/test/subscription/meson.build
@@ -39,6 +39,8 @@ tests += {
       't/030_origin.pl',
       't/031_column_list.pl',
       't/032_subscribe_use_index.pl',
+      't/033_run_as_table_owner.pl',
+      't/034_ddl_replication.pl',
       't/100_bugs.pl',
     ],
   },
diff --git a/src/test/subscription/t/034_ddl_replication.pl b/src/test/subscription/t/034_ddl_replication.pl
new file mode 100644
index 0000000000..f239d69694
--- /dev/null
+++ b/src/test/subscription/t/034_ddl_replication.pl
@@ -0,0 +1,374 @@
+# Copyright (c) 2022, PostgreSQL Global Development Group
+# Regression tests for logical replication of DDLs
+#
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
+$node_publisher->init(allows_streaming => 'logical');
+$node_publisher->start;
+
+my $node_subscriber = PostgreSQL::Test::Cluster->new('subscriber');
+$node_subscriber->init(allows_streaming => 'logical');
+$node_subscriber->start;
+
+my $ddl = "CREATE TABLE test_rep(id int primary key, name varchar, value integer);";
+$node_publisher->safe_psql('postgres', $ddl);
+$node_publisher->safe_psql('postgres', "INSERT INTO test_rep VALUES (1, 'data1', 1);");
+$node_subscriber->safe_psql('postgres', $ddl);
+
+my $publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
+$node_publisher->safe_psql('postgres',
+	"CREATE PUBLICATION mypub FOR ALL TABLES with (publish = 'insert, update, delete', ddl = 'table');");
+$node_subscriber->safe_psql('postgres',
+	"CREATE SUBSCRIPTION mysub CONNECTION '$publisher_connstr' PUBLICATION mypub;");
+$node_publisher->wait_for_catchup('mysub');
+
+# Make sure we have fully synchronized the table.
+# This prevents ALTER TABLE command below from being executed during table synchronization.
+$node_subscriber->poll_query_until('postgres',
+   "SELECT COUNT(1) = 0 FROM pg_subscription_rel sr WHERE sr.srsubstate NOT IN ('s', 'r') AND sr.srrelid = 'test_rep'::regclass"
+);
+
+# Test ALTER TABLE ADD
+$node_publisher->safe_psql('postgres', "ALTER TABLE test_rep ADD c4 int;");
+$node_publisher->safe_psql('postgres', "INSERT INTO test_rep VALUES (2, 'data2', 2, 2);");
+$node_publisher->wait_for_catchup('mysub');
+my $result = $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM test_rep WHERE c4 = 2;");
+is($result, qq(1), 'ALTER test_rep ADD replicated');
+
+# Test ALTER TABLE DROP
+$node_publisher->safe_psql('postgres', "ALTER TABLE test_rep DROP c4;");
+$node_publisher->safe_psql('postgres', "DELETE FROM test_rep where id = 2;");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from test_rep;");
+is($result, qq(1), 'ALTER test_rep DROP replicated');
+
+# Test ALTER TABLE ALTER TYPE
+$node_publisher->safe_psql('postgres', "ALTER TABLE test_rep ALTER value TYPE varchar");
+$node_publisher->safe_psql('postgres', "INSERT INTO test_rep VALUES (3, 'data3', '3');");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM test_rep WHERE value = '3';");
+is($result, qq(1), 'ALTER test_rep ALTER COLUMN TYPE replicated');
+
+# Test ALTER TABLE ALTER SET DEFAULT
+# Check if we have the default value after the direct insert to subscriber node.
+$node_publisher->safe_psql('postgres', "ALTER TABLE test_rep ALTER COLUMN value SET DEFAULT 'foo'");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->safe_psql('postgres', "INSERT INTO test_rep VALUES (4, 'data4');");
+$result = $node_subscriber->safe_psql('postgres', "SELECT value FROM test_rep WHERE id = 4;");
+is($result, 'foo', 'ALTER test_rep ALTER SET DEFAULT replicated');
+
+# Test ALTER TABLE ALTER DROP DEFAULT
+# Check if we don't have the default value previously defined.
+$node_publisher->safe_psql('postgres', "ALTER TABLE test_rep ALTER COLUMN value DROP DEFAULT;");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->safe_psql('postgres', "INSERT INTO test_rep VALUES (5, 'data5');");
+$result = $node_subscriber->safe_psql('postgres', "SELECT value IS NULL FROM test_rep WHERE id = 5;");
+is($result, q(t), 'ALTER test_rep ALTER DROP DEFAULT replicated');
+
+# Test ALTER TABLE ALTER SET NOT NULL
+# Remove the existing record that contains null value first.
+my ($stdout, $stderr);
+$node_subscriber->safe_psql('postgres', "DELETE FROM test_rep WHERE id = 5;");
+$node_publisher->safe_psql('postgres', "ALTER TABLE test_rep ALTER value SET NOT NULL;");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "INSERT INTO test_rep VALUES (6, 'data6');",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  null value in column \"value\" of relation \"test_rep\" violates not-null constraint/
+  or die "failed to replicate ALTER TABLE ALTER SET NOT NULL";
+
+# Test ALTER TABLE ALTER DROP NOT NULL
+$node_publisher->safe_psql('postgres', "ALTER TABLE test_rep ALTER value DROP NOT NULL;");
+$node_publisher->wait_for_catchup('mysub');
+# Insert same data that has NULL value. This failed before but now should succeed.
+$node_subscriber->safe_psql('postgres', "INSERT INTO test_rep VALUES (6, 'data6');");
+$result = $node_subscriber->safe_psql('postgres', "SELECT value IS NULL FROM test_rep WHERE id = 6;");
+is($result, q(t), "ALTER test_rep ALTER DROP NOT NULL replicated");
+
+# Test ALTER TABLE SET UNLOGGED
+$node_publisher->safe_psql('postgres', 'ALTER TABLE test_rep SET UNLOGGED;');
+$node_publisher->wait_for_catchup('mysub');
+$node_publisher->safe_psql('postgres', "INSERT INTO test_rep VALUES (7, 'data7', '7');");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM test_rep WHERE id = 7;");
+is($result, qq(0), 'ALTER test_rep SET UNLOGGED replicated');
+
+# Test ALTER TABLE SET LOGGED
+$node_publisher->safe_psql('postgres', 'ALTER TABLE test_rep SET LOGGED;');
+$node_publisher->wait_for_catchup('mysub');
+$node_publisher->safe_psql('postgres', "INSERT INTO test_rep VALUES (8, 'data8', '8');");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM test_rep WHERE id = 8;");
+is($result, qq(1), 'ALTER test_rep SET LOGGED replicated');
+
+# Test CREATE TABLE and DML changes
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (a int, b varchar);");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from pg_tables where tablename = 'tmp';");
+is($result, qq(1), 'CREATE tmp is replicated');
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp values (1, 'a')");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp values (2, 'b')");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from tmp;");
+is($result, qq(2), 'DML Changes to tmp are replicated');
+
+# Test CREATE TABLE INHERITS
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp2 (c3 int) INHERITS (tmp);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp2 VALUES (1, 'a', 1);");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from pg_tables where tablename = 'tmp2';");
+is($result, qq(1), 'CREATE TABLE INHERITS is replicated');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from tmp2;");
+is($result, qq(1), 'inserting some data to inherited table replicated');
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp2");
+
+# Test CREATE TABLE LIKE
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp3 (c3 int, LIKE tmp);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp3 VALUES (1, 1, 'a');");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from pg_tables where tablename = 'tmp3';");
+is($result, qq(1), 'CREATE TABLE LIKE replicated');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from tmp3;");
+is($result, qq(1), 'insert some data to a table defined with LIKE replicated');
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp3");
+
+# Test DROP TABLE
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from pg_tables where tablename = 'tmp';");
+is($result, qq(0), 'TABLE tmp is dropped');
+
+# Test CREATE TABLE IF NOT EXISTS
+$node_publisher->safe_psql('postgres', "CREATE TABLE IF NOT EXISTS tmp (id int);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (1);");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from pg_tables where tablename = 'tmp';");
+is($result, qq(1), 'CREATE TABLE IF NOT EXISTS replicated');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from tmp;");
+is($result, qq(1), 'inserting data to a table replicated');
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp");
+
+# Test CREATE TABLE IF NOT EXISTS (check if we skip to create a table
+# when we have the table on the subscriber in advance, and if we succeed
+# in replicating changes.)
+$node_subscriber->safe_psql('postgres', "CREATE TABLE tmp (id int);");
+$node_publisher->safe_psql('postgres', "CREATE TABLE IF NOT EXISTS tmp (id int);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (1);");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from tmp;");
+is($result, qq(1), 'CREATE TABLE IF NOT EXISTS replicated');
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp");
+
+# Test CREATE TABLE IF NOT EXISTS (check if we skip to create a table
+# when we have the table on the publisher, but not on the subscriber.)
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int);");
+$node_publisher->safe_psql('postgres', "CREATE TABLE IF NOT EXISTS tmp (id int);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (1);");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from tmp;");
+is($result, qq(1), 'CREATE TABLE IF NOT EXISTS replicated');
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp");
+
+# Test CREATE TABLE with collate
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (name text COLLATE \"C\");");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES ('foo');");
+$node_publisher->wait_for_catchup('mysub');
+$result = $node_subscriber->safe_psql('postgres', "SELECT collation_name FROM information_schema.columns WHERE table_name = 'tmp';");
+is($result, qq(C), 'CREATE TABLE with collate replicated');
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp");
+
+# Test CREATE TABLE with named constraint
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int CONSTRAINT \"must be bigger than 10\" CHECK (id > 10));");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "INSERT INTO tmp VALUES (1);",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  new row for relation "tmp" violates check constraint "must be bigger than 10"/
+  or die "failed to replicate named constraint at creating table";
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+# Test CREATE TABLE with various types of constraints.
+# NOT NULL constraint
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int, name text NOT NULL);");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "INSERT INTO tmp VALUES (1);",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  null value in column "name" of relation "tmp" violates not-null constraint/
+  or die "failed to replicate non null constraint at creating table";
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+# NULL constraint
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int, name text NULL);");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->safe_psql('postgres', "INSERT INTO tmp VALUES (1);");
+$result = $node_subscriber->safe_psql('postgres', "SELECT name IS NULL FROM tmp;");
+is($result, qq(t), "CREATE TABLE with NULL constraint replicated");
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp");
+
+# CHECK constraint
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int, product_ame text, price int CHECK (price > 0));");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "INSERT INTO tmp VALUES (1, 'foo', -100);",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  new row for relation "tmp" violates check constraint "tmp_price_check"/
+  or die "failed to replicate CHECK constraint";
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+# DEFAULT
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int, name text DEFAULT 'foo');");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->safe_psql('postgres', "INSERT INTO tmp VALUES (1);");
+$result = $node_subscriber->safe_psql('postgres', "SELECT name from tmp;");
+is($result, qq(foo), "CREATE TABLE with default value replicated");
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp");
+
+# UNIQUE constraint
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int UNIQUE);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (1);");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "INSERT INTO tmp VALUES (1);",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  duplicate key value violates unique constraint "tmp_id_key"/
+  or die "failed to replicate constraint at creating table";
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+# PRIMARY KEY
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int PRIMARY KEY, name text);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (1, 'foo');");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "INSERT INTO tmp VALUES (1, 'bar');",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  duplicate key value violates unique constraint "tmp_pkey"/
+  or die "failed to replicate primary key at creating table";
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+# EXCLUDE
+$node_publisher->safe_psql('postgres', "CREATE TABLE circles (c circle, EXCLUDE USING gist (c WITH &&));");
+$node_publisher->safe_psql('postgres', "INSERT INTO circles VALUES ('<(1, 1), 1>'::circle);");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "INSERT INTO circles VALUES ('<(1, 1), 1>'::circle);",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  conflicting key value violates exclusion constraint "circles_c_excl"/
+  or die "failed to replicate EXCLUDE at creating table";
+$node_publisher->safe_psql('postgres', "DROP TABLE circles");
+
+# REFERENCES
+$node_publisher->safe_psql('postgres', "CREATE TABLE product (id int PRIMARY KEY, name text);");
+$node_publisher->safe_psql('postgres', "INSERT INTO product VALUES (1, 'foo');");
+$node_publisher->safe_psql('postgres', "INSERT INTO product VALUES (2, 'bar');");
+$node_publisher->safe_psql('postgres', "CREATE TABLE orders (order_id int PRIMARY KEY, product_id int REFERENCES product (id))");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "INSERT INTO orders VALUES (1, 10)",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  insert or update on table "orders" violates foreign key constraint "orders_product_id_fkey"/
+  or die "failed to replicate REFERENCES at creating table";
+$node_publisher->safe_psql('postgres', "DROP TABLE orders");
+$node_publisher->safe_psql('postgres', "DROP TABLE product");
+
+# DEFERRABLE
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int PRIMARY KEY DEFERRABLE, name text);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (1, 'foo');");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (2, 'bar');");
+$node_publisher->wait_for_catchup('mysub');
+# Quick check of deferrable clause
+$node_subscriber->safe_psql('postgres', "UPDATE tmp SET id = id + 1;");
+# Also, execute a test that should fail for INITIALLY IMMEDIATE(the default)
+$node_subscriber->psql('postgres', qq(
+BEGIN;
+UPDATE tmp SET id = id + 1;
+INSERT INTO tmp VALUES (3, 'foobar');
+DELETE FROM tmp WHERE id = 3;
+COMMIT;
+), on_error_stop => 0, stderr => \$stderr, stdout => \$stdout);
+$stderr =~ /ERROR:  duplicate key value violates unique constraint "tmp_pkey"/
+  or die "failed to replicate DEFERRABLE at creating table";
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+# NOT DEFERRABLE
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int PRIMARY KEY NOT DEFERRABLE, name text);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (1, 'foo');");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (2, 'bar');");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "UPDATE tmp SET id = id + 1;",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  duplicate key value violates unique constraint "tmp_pkey"/
+  or die "failed to replicate NOT DEFERRABLE at creating table";
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+# DEFERRABLE and INITIALLY DEFERRED
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int PRIMARY KEY DEFERRABLE INITIALLY DEFERRED, name text);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (1, 'foo');");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (2, 'bar');");
+$node_publisher->wait_for_catchup('mysub');
+# Quick check of deferrable clause
+$node_subscriber->safe_psql('postgres', "UPDATE tmp SET id = id + 1;");
+# Also, execute a test that should succeed for INITIALLY DEFERRED
+$node_subscriber->safe_psql('postgres', qq(
+BEGIN;
+UPDATE tmp SET id = id + 1;
+INSERT INTO tmp VALUES (3, 'foobar');
+DELETE FROM tmp WHERE id = 3;
+COMMIT;
+));
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+# Test CREATE TABLE with table constraint
+# We will set two checks and conduct two inserts that should fail respectively.
+$node_publisher->safe_psql('postgres',
+	"CREATE TABLE tmp (price int, discounted_price int, CHECK (discounted_price > 0 AND price > discounted_price));");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "INSERT INTO tmp VALUES (100, 0);",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  new row for relation "tmp" violates check constraint "tmp_check"/
+  or die "failed to replicate table constraint (first condition) at creating table";
+$node_subscriber->psql('postgres', "INSERT INTO tmp VALUES (50, 100);",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stderr =~ /ERROR:  new row for relation "tmp" violates check constraint "tmp_check"/
+  or die "failed to replicate table constraint (second condition) at creating table";
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+# Test CREATE TABLE WITH strorage_parameter
+$node_publisher->safe_psql('postgres', "CREATE TABLE tmp (id int) WITH (fillfactor = 80);");
+$node_publisher->safe_psql('postgres', "INSERT INTO tmp VALUES (1);");
+$node_publisher->wait_for_catchup('mysub');
+$node_subscriber->psql('postgres', "SELECT reloptions FROM pg_class WHERE relname = 'tmp';",
+					   on_error_stop => 0,
+					   stderr => \$stderr,
+					   stdout => \$stdout);
+$stdout =~ /{fillfactor=80}/
+  or die "failed to replicate storage option";
+$result = $node_subscriber->safe_psql('postgres', "SELECT count(*) from tmp;");
+is($result, qq(1), 'CREATE TABLE with storage_parameter replicated');
+$node_publisher->safe_psql('postgres', "DROP TABLE tmp;");
+
+pass "DDL replication tests passed:";
+
+$node_subscriber->stop;
+$node_publisher->stop;
+
+done_testing();
-- 
2.34.1

From 4c8f8ddad665221fe298771a49be09523a03e608 Mon Sep 17 00:00:00 2001
From: Shveta Malik <[email-masked]>
Date: Mon, 22 May 2023 08:50:13 +0530
Subject: [PATCH 7/8] Apply the DDL change as that same user that executed the
 DDL on publisher

1. Change event trigger functions to collect the current role in
CollectedCommand.

2. Change Deparser function deparse_utility_command to encode owner role in the
top-level element such as {myowner:role_name, fmt:..., identity:...} of the
deparsed jsonb output for commands that create database objects. Also change
function deparse_ddl_json_to_string to retrieve the myowner element from a
jsonb string.

3. Introduce a new subscription option match_ddl_owner: when turned on, the
apply worker will apply DDL messages in the role retrieved from the "myowner"
field of the deparsed jsonb string. The default value of match_ddl_owner is on.
---
 src/backend/catalog/pg_subscription.c        |   1 +
 src/backend/commands/ddldeparse.c            |  52 +++++--
 src/backend/commands/ddljson.c               |  25 ++-
 src/backend/commands/event_trigger.c         |   5 +
 src/backend/commands/subscriptioncmds.c      |  28 +++-
 src/backend/replication/logical/ddltrigger.c |   3 +
 src/backend/replication/logical/worker.c     |  14 +-
 src/bin/pg_dump/pg_dump.c                    |  13 +-
 src/bin/pg_dump/pg_dump.h                    |   1 +
 src/bin/psql/describe.c                      |   8 +-
 src/include/catalog/pg_subscription.h        |   5 +
 src/include/tcop/ddldeparse.h                |  10 +-
 src/include/tcop/deparse_utility.h           |   1 +
 src/test/regress/expected/subscription.out   | 152 +++++++++----------
 14 files changed, 221 insertions(+), 97 deletions(-)

diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index d07f88ce28..2d82fbfad2 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -73,6 +73,7 @@ GetSubscription(Oid subid, bool missing_ok)
 	sub->disableonerr = subform->subdisableonerr;
 	sub->passwordrequired = subform->subpasswordrequired;
 	sub->runasowner = subform->subrunasowner;
+	sub->matchddlowner = subform->submatchddlowner;
 
 	/* Get conninfo */
 	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID,
diff --git a/src/backend/commands/ddldeparse.c b/src/backend/commands/ddldeparse.c
index e304f25f98..52e2e2216d 100644
--- a/src/backend/commands/ddldeparse.c
+++ b/src/backend/commands/ddldeparse.c
@@ -133,7 +133,7 @@ static ObjElem *new_object(ObjType type, char *name);
 static ObjTree *new_objtree_for_qualname_id(Oid classId, Oid objectId);
 static ObjElem *new_object_object(ObjTree *value);
 static ObjTree *new_objtree_VA(char *fmt, int numobjs, ...);
-static JsonbValue *objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state);
+static JsonbValue *objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state, char *owner);
 static char *RelationGetColumnDefault(Relation rel, AttrNumber attno,
 									  List *dpcontext, Node **expr);
 
@@ -648,14 +648,39 @@ objtree_fmt_to_jsonb_element(JsonbParseState *state, ObjTree *tree)
 }
 
 /*
- * Create a JSONB representation from an ObjTree.
+ * Process the role string into the output parse state.
+ */
+static void
+role_to_jsonb_element(JsonbParseState *state, char *owner)
+{
+	JsonbValue	key;
+	JsonbValue	val;
+
+	if (owner == NULL)
+		return;
+
+	/* Push the key first */
+	key.type = jbvString;
+	key.val.string.val = "myowner";
+	key.val.string.len = strlen(key.val.string.val);
+	pushJsonbValue(&state, WJB_KEY, &key);
+
+	/* Then process the role string */
+	val.type = jbvString;
+	val.val.string.len = strlen(owner);
+	val.val.string.val = owner;
+	pushJsonbValue(&state, WJB_VALUE, &val);
+}
+
+/*
+ * Create a JSONB representation from an ObjTree and its owner (if given).
  */
 static Jsonb *
-objtree_to_jsonb(ObjTree *tree)
+objtree_to_jsonb(ObjTree *tree, char *owner)
 {
 	JsonbValue *value;
 
-	value = objtree_to_jsonb_rec(tree, NULL);
+	value = objtree_to_jsonb_rec(tree, NULL, owner);
 	return JsonbValueToJsonb(value);
 }
 
@@ -699,7 +724,7 @@ objtree_to_jsonb_element(JsonbParseState *state, ObjElem *object,
 
 		case ObjTypeObject:
 			/* Recursively add the object into the existing parse state */
-			objtree_to_jsonb_rec(object->value.object, state);
+			objtree_to_jsonb_rec(object->value.object, state, NULL);
 			break;
 
 		case ObjTypeArray:
@@ -727,12 +752,13 @@ objtree_to_jsonb_element(JsonbParseState *state, ObjElem *object,
  * Recursive helper for objtree_to_jsonb.
  */
 static JsonbValue *
-objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state)
+objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state, char *owner)
 {
 	slist_iter	iter;
 
 	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
+	role_to_jsonb_element(state, owner);
 	objtree_fmt_to_jsonb_element(state, tree);
 
 	slist_foreach(iter, &tree->params)
@@ -2859,7 +2885,7 @@ deparse_drop_command(const char *objidentity, const char *objecttype,
 		append_not_present(tmp_obj, NULL);
 	append_object_object(stmt, "%{cascade}s", tmp_obj);
 
-	jsonb = objtree_to_jsonb(stmt);
+	jsonb = objtree_to_jsonb(stmt, NULL /* Owner/role can be skipped for drop command */);
 	command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
 
 	return command;
@@ -3127,7 +3153,7 @@ deparse_RenameStmt(ObjectAddress address, Node *parsetree)
  * This function should cover all cases handled in ProcessUtilitySlow.
  */
 static ObjTree *
-deparse_simple_command(CollectedCommand *cmd)
+deparse_simple_command(CollectedCommand *cmd, bool *include_owner)
 {
 	Oid			objectId;
 	Node	   *parsetree;
@@ -3144,11 +3170,13 @@ deparse_simple_command(CollectedCommand *cmd)
 	switch (nodeTag(parsetree))
 	{
 		case T_AlterObjectSchemaStmt:
+			*include_owner = false;
 			return deparse_AlterObjectSchemaStmt(cmd->d.simple.address,
 												 parsetree,
 												 cmd->d.simple.secondaryObject);
 
 		case T_AlterOwnerStmt:
+			*include_owner = false;
 			return deparse_AlterOwnerStmt(cmd->d.simple.address, parsetree);
 
 		case T_AlterSeqStmt:
@@ -3161,6 +3189,7 @@ deparse_simple_command(CollectedCommand *cmd)
 			return deparse_CreateStmt(objectId, parsetree);
 
 		case T_RenameStmt:
+			*include_owner = false;
 			return deparse_RenameStmt(cmd->d.simple.address, parsetree);
 
 		default:
@@ -3216,10 +3245,11 @@ deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context)
 	switch (cmd->type)
 	{
 		case SCT_Simple:
-			tree = deparse_simple_command(cmd);
+			tree = deparse_simple_command(cmd, &context->include_owner);
 			break;
 		case SCT_AlterTable:
 			tree = deparse_AlterRelation(cmd, context);
+			context->include_owner = false;
 			break;
 		case SCT_CreateTableAs:
 			tree = deparse_CreateTableAsStmt(cmd);
@@ -3234,7 +3264,8 @@ deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context)
 	{
 		Jsonb	   *jsonb;
 
-		jsonb = objtree_to_jsonb(tree);
+		jsonb = context->include_owner ? objtree_to_jsonb(tree, cmd->role) :
+										 objtree_to_jsonb(tree, NULL);
 		command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
 	}
 
@@ -3262,6 +3293,7 @@ ddl_deparse_to_json(PG_FUNCTION_ARGS)
 	ddl_deparse_context context;
 
 	context.verbose_mode = true;
+	context.include_owner = false;
 	context.func_volatile = PROVOLATILE_IMMUTABLE;
 
 	command = deparse_utility_command(cmd, &context);
diff --git a/src/backend/commands/ddljson.c b/src/backend/commands/ddljson.c
index 6d24e6a9aa..60b5c4c8ff 100644
--- a/src/backend/commands/ddljson.c
+++ b/src/backend/commands/ddljson.c
@@ -645,7 +645,7 @@ expand_jsonb_array(StringInfo buf, char *param,
  * Workhorse for ddl_deparse_expand_command.
  */
 char *
-deparse_ddl_json_to_string(char *json_str)
+deparse_ddl_json_to_string(char *json_str, char** owner)
 {
 	Datum		d;
 	Jsonb	   *jsonb;
@@ -656,6 +656,27 @@ deparse_ddl_json_to_string(char *json_str)
 	d = DirectFunctionCall1(jsonb_in, PointerGetDatum(json_str));
 	jsonb = (Jsonb *) DatumGetPointer(d);
 
+	if (owner != NULL)
+	{
+		const char *key = "myowner";
+		JsonbValue *value;
+
+		value = getKeyJsonValueFromContainer(&jsonb->root, key, strlen(key), NULL);
+		if (value)
+		{
+			char *str;
+
+			/* value->val.string.val may not be NULL terminated */
+			str = palloc(value->val.string.len + 1);
+			memcpy(str, value->val.string.val, value->val.string.len);
+			str[value->val.string.len] = '\0';
+			*owner = str;
+		}
+		else
+			/* myowner is not given in this jsonb, e.g. for Drop Commands */
+			*owner = NULL;
+	}
+
 	expand_fmt_recursive(buf, &jsonb->root);
 
 	return buf->data;
@@ -693,7 +714,7 @@ ddl_deparse_expand_command(PG_FUNCTION_ARGS)
 
 	json_str = text_to_cstring(json);
 
-	PG_RETURN_TEXT_P(cstring_to_text(deparse_ddl_json_to_string(json_str)));
+	PG_RETURN_TEXT_P(cstring_to_text(deparse_ddl_json_to_string(json_str, NULL)));
 }
 
 /*
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 781f3c1d07..4a75d5f2f9 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -880,6 +880,7 @@ EventTriggerTableInitWriteStart(Node *parsetree)
 
 	command->type = (stmt->objtype == OBJECT_TABLE) ? SCT_CreateTableAs : SCT_Simple;
 	command->in_extension = creating_extension;
+	command->role = GetUserNameFromId(GetUserId(), false);
 	command->d.ctas.address = InvalidObjectAddress;
 	command->d.ctas.real_create = NULL;
 	command->parsetree = copyObject(parsetree);
@@ -1638,6 +1639,7 @@ EventTriggerCollectSimpleCommand(ObjectAddress address,
 
 	command->type = SCT_Simple;
 	command->in_extension = creating_extension;
+	command->role = GetUserNameFromId(GetUserId(), false);
 
 	command->d.simple.address = address;
 	command->d.simple.secondaryObject = secondaryObject;
@@ -1674,6 +1676,7 @@ EventTriggerAlterTableStart(Node *parsetree)
 
 	command->type = SCT_AlterTable;
 	command->in_extension = creating_extension;
+	command->role = GetUserNameFromId(GetUserId(), false);
 
 	command->d.alterTable.classId = RelationRelationId;
 	command->d.alterTable.objectId = InvalidOid;
@@ -1957,6 +1960,7 @@ EventTriggerCollectGrant(InternalGrant *istmt)
 	command = palloc(sizeof(CollectedCommand));
 	command->type = SCT_Grant;
 	command->in_extension = creating_extension;
+	command->role = GetUserNameFromId(GetUserId(), false);
 	command->d.grant.istmt = icopy;
 	command->parsetree = NULL;
 
@@ -1988,6 +1992,7 @@ EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
 	command = palloc(sizeof(CollectedCommand));
 	command->type = SCT_AlterOpFamily;
 	command->in_extension = creating_extension;
+	command->role = GetUserNameFromId(GetUserId(), false);
 	ObjectAddressSet(command->d.opfam.address,
 					 OperatorFamilyRelationId, opfamoid);
 	command->d.opfam.operators = operators;
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 1c88c2bccb..b1065506ea 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -71,6 +71,7 @@
 #define SUBOPT_RUN_AS_OWNER			0x00001000
 #define SUBOPT_LSN					0x00002000
 #define SUBOPT_ORIGIN				0x00004000
+#define SUBOPT_MATCH_DDL_OWNER		0x00008000
 
 /* check if the 'val' has 'bits' set */
 #define IsSet(val, bits)  (((val) & (bits)) == (bits))
@@ -95,6 +96,7 @@ typedef struct SubOpts
 	bool		disableonerr;
 	bool		passwordrequired;
 	bool		runasowner;
+	bool		matchddlowner;
 	char	   *origin;
 	XLogRecPtr	lsn;
 } SubOpts;
@@ -157,6 +159,8 @@ parse_subscription_options(ParseState *pstate, List *stmt_options,
 		opts->runasowner = false;
 	if (IsSet(supported_opts, SUBOPT_ORIGIN))
 		opts->origin = pstrdup(LOGICALREP_ORIGIN_ANY);
+	if (IsSet(supported_opts, SUBOPT_MATCH_DDL_OWNER))
+		opts->matchddlowner = true;
 
 	/* Parse options */
 	foreach(lc, stmt_options)
@@ -353,6 +357,15 @@ parse_subscription_options(ParseState *pstate, List *stmt_options,
 			opts->specified_opts |= SUBOPT_LSN;
 			opts->lsn = lsn;
 		}
+		else if (IsSet(supported_opts, SUBOPT_MATCH_DDL_OWNER) &&
+				 strcmp(defel->defname, "match_ddl_owner") == 0)
+		{
+			if (IsSet(opts->specified_opts, SUBOPT_MATCH_DDL_OWNER))
+				errorConflictingDefElem(defel, pstate);
+
+			opts->specified_opts |= SUBOPT_MATCH_DDL_OWNER;
+			opts->matchddlowner = defGetBoolean(defel);
+		}
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
@@ -591,7 +604,8 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
 					  SUBOPT_SYNCHRONOUS_COMMIT | SUBOPT_BINARY |
 					  SUBOPT_STREAMING | SUBOPT_TWOPHASE_COMMIT |
 					  SUBOPT_DISABLE_ON_ERR | SUBOPT_PASSWORD_REQUIRED |
-					  SUBOPT_RUN_AS_OWNER | SUBOPT_ORIGIN);
+					  SUBOPT_RUN_AS_OWNER | SUBOPT_MATCH_DDL_OWNER |
+					  SUBOPT_ORIGIN);
 	parse_subscription_options(pstate, stmt->options, supported_opts, &opts);
 
 	/*
@@ -710,6 +724,7 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
 		publicationListToArray(publications);
 	values[Anum_pg_subscription_suborigin - 1] =
 		CStringGetTextDatum(opts.origin);
+	values[Anum_pg_subscription_submatchddlowner - 1] = BoolGetDatum(opts.matchddlowner);
 
 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
 
@@ -1132,7 +1147,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 								  SUBOPT_SYNCHRONOUS_COMMIT | SUBOPT_BINARY |
 								  SUBOPT_STREAMING | SUBOPT_DISABLE_ON_ERR |
 								  SUBOPT_PASSWORD_REQUIRED |
-								  SUBOPT_RUN_AS_OWNER | SUBOPT_ORIGIN);
+								  SUBOPT_RUN_AS_OWNER | SUBOPT_MATCH_DDL_OWNER |
+								  SUBOPT_ORIGIN);
 
 				parse_subscription_options(pstate, stmt->options,
 										   supported_opts, &opts);
@@ -1211,6 +1227,14 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
 					replaces[Anum_pg_subscription_suborigin - 1] = true;
 				}
 
+				if (IsSet(opts.specified_opts, SUBOPT_MATCH_DDL_OWNER))
+				{
+					values[Anum_pg_subscription_submatchddlowner - 1]
+						= BoolGetDatum(opts.matchddlowner);
+					replaces[Anum_pg_subscription_submatchddlowner - 1]
+						= true;
+				}
+
 				update_tuple = true;
 				break;
 			}
diff --git a/src/backend/replication/logical/ddltrigger.c b/src/backend/replication/logical/ddltrigger.c
index 08e251686f..94161a0497 100644
--- a/src/backend/replication/logical/ddltrigger.c
+++ b/src/backend/replication/logical/ddltrigger.c
@@ -159,6 +159,7 @@ publication_deparse_table_rewrite(PG_FUNCTION_ARGS)
 		char	   *json_string;
 
 		context.verbose_mode = false;
+		context.include_owner = true;
 		context.func_volatile = PROVOLATILE_IMMUTABLE;
 
 		/* Deparse the DDL command and WAL log it to allow decoding of the same. */
@@ -247,6 +248,7 @@ publication_deparse_ddl_command_end(PG_FUNCTION_ARGS)
 			char	   *json_string;
 
 			context.verbose_mode = false;
+			context.include_owner = true;
 			context.func_volatile = PROVOLATILE_IMMUTABLE;
 
 			json_string = deparse_utility_command(cmd, &context);
@@ -325,6 +327,7 @@ publication_deparse_table_init_write(PG_FUNCTION_ARGS)
 		char	   *json_string;
 
 		context.verbose_mode = false;
+		context.include_owner = true;
 		context.func_volatile = PROVOLATILE_IMMUTABLE;
 
 		/* Deparse the DDL command and WAL log it to allow decoding of the same. */
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index edb8ecabbf..c937b6fc72 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -3387,11 +3387,13 @@ apply_handle_ddl(StringInfo s)
 	const char *prefix = NULL;
 	char	   *message = NULL;
 	char	   *ddl_command;
+	char	   *owner;
 	Size		sz;
 	List	   *parsetree_list;
 	ListCell   *parsetree_item;
 	DestReceiver *receiver;
 	MemoryContext oldcontext;
+	UserContext		ucxt;
 	const char *save_debug_query_string = debug_query_string;
 
 	message = logicalrep_read_ddl(s, &lsn, &prefix, &sz);
@@ -3406,9 +3408,16 @@ apply_handle_ddl(StringInfo s)
 
 	MemoryContextSwitchTo(ApplyMessageContext);
 
-	ddl_command = deparse_ddl_json_to_string(message);
+	ddl_command = deparse_ddl_json_to_string(message, &owner);
 	debug_query_string = ddl_command;
 
+	/*
+	 * If requested, set the current role to the owner that executed the
+	 * command on the publication server.
+	 */
+	if (MySubscription->matchddlowner && owner)
+		SwitchToUntrustedUser(get_role_oid(owner, false), &ucxt);
+
 	/* DestNone for logical replication */
 	receiver = CreateDestReceiver(DestNone);
 	parsetree_list = pg_parse_query(ddl_command);
@@ -3506,6 +3515,9 @@ apply_handle_ddl(StringInfo s)
 		MemoryContextDelete(per_parsetree_context);
 	}
 
+	if (MySubscription->matchddlowner && owner)
+		RestoreUserContext(&ucxt);
+
 	debug_query_string = save_debug_query_string;
 
 	CommandCounterIncrement();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index fae8eadbf6..86e107f185 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4607,6 +4607,7 @@ getSubscriptions(Archive *fout)
 	int			i_subpublications;
 	int			i_subbinary;
 	int			i_subpasswordrequired;
+	int			i_submatchddlowner;
 	int			i,
 				ntups;
 
@@ -4661,11 +4662,13 @@ getSubscriptions(Archive *fout)
 	if (fout->remoteVersion >= 160000)
 		appendPQExpBufferStr(query,
 							 " s.suborigin,\n"
-							 " s.subpasswordrequired\n");
+							 " s.subpasswordrequired,\n"
+							 " s.submatchddlowner\n");
 	else
 		appendPQExpBuffer(query,
 						  " '%s' AS suborigin,\n"
-						  " 't' AS subpasswordrequired\n",
+						  " 't' AS subpasswordrequired,\n"
+						  " false AS submatchddlowner\n",
 						  LOGICALREP_ORIGIN_ANY);
 
 	appendPQExpBufferStr(query,
@@ -4695,6 +4698,7 @@ getSubscriptions(Archive *fout)
 	i_subdisableonerr = PQfnumber(res, "subdisableonerr");
 	i_suborigin = PQfnumber(res, "suborigin");
 	i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
+	i_submatchddlowner = PQfnumber(res, "submatchddlowner");
 
 	subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
 
@@ -4727,6 +4731,8 @@ getSubscriptions(Archive *fout)
 		subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
 		subinfo[i].subpasswordrequired =
 			pg_strdup(PQgetvalue(res, i, i_subpasswordrequired));
+		subinfo[i].submatchddlowner =
+			pg_strdup(PQgetvalue(res, i, i_submatchddlowner));
 
 		/* Decide whether we want to dump it */
 		selectDumpableObject(&(subinfo[i].dobj), fout);
@@ -4805,6 +4811,9 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
 	if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
 		appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
 
+	if (strcmp(subinfo->submatchddlowner, "f") == 0)
+		appendPQExpBufferStr(query, ", match_ddl_owner = false");
+
 	if (strcmp(subinfo->subsynccommit, "off") != 0)
 		appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
 
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index bd5f8fb669..f7a8db3423 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -665,6 +665,7 @@ typedef struct _SubscriptionInfo
 	char	   *subsynccommit;
 	char	   *subpublications;
 	char	   *subpasswordrequired;
+	char	   *submatchddlowner;
 } SubscriptionInfo;
 
 /*
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 750ea19dc1..daaccb2663 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -6505,7 +6505,7 @@ describeSubscriptions(const char *pattern, bool verbose)
 	PGresult   *res;
 	printQueryOpt myopt = pset.popt;
 	static const bool translate_columns[] = {false, false, false, false,
-	false, false, false, false, false, false, false, false, false, false};
+	false, false, false, false, false, false, false, false, false, false, false};
 
 	if (pset.sversion < 100000)
 	{
@@ -6564,10 +6564,12 @@ describeSubscriptions(const char *pattern, bool verbose)
 			appendPQExpBuffer(&buf,
 							  ", suborigin AS \"%s\"\n"
 							  ", subpasswordrequired AS \"%s\"\n"
-							  ", subrunasowner AS \"%s\"\n",
+							  ", subrunasowner AS \"%s\"\n"
+							  ", submatchddlowner AS \"%s\"\n",
 							  gettext_noop("Origin"),
 							  gettext_noop("Password required"),
-							  gettext_noop("Run as owner?"));
+							  gettext_noop("Run as owner?"),
+							  gettext_noop("Match DDL owner"));
 
 		appendPQExpBuffer(&buf,
 						  ",  subsynccommit AS \"%s\"\n"
diff --git a/src/include/catalog/pg_subscription.h b/src/include/catalog/pg_subscription.h
index 1d40eebc78..c99ca2b509 100644
--- a/src/include/catalog/pg_subscription.h
+++ b/src/include/catalog/pg_subscription.h
@@ -93,6 +93,9 @@ CATALOG(pg_subscription,6100,SubscriptionRelationId) BKI_SHARED_RELATION BKI_ROW
 	bool		subrunasowner;	/* True if replication should execute as the
 								 * subscription owner */
 
+	bool		submatchddlowner;	/* True if replicated objects by DDL replication
+									 * should match the original owner on the publisher */
+
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	/* Connection string to the publisher */
 	text		subconninfo BKI_FORCE_NOT_NULL;
@@ -144,6 +147,8 @@ typedef struct Subscription
 	List	   *publications;	/* List of publication names to subscribe to */
 	char	   *origin;			/* Only publish data originating from the
 								 * specified origin */
+	bool		matchddlowner;  /* Indicates if replicated objects by DDL replication
+								 * should match the original owner on the publisher */
 } Subscription;
 
 /* Disallow streaming in-progress transactions. */
diff --git a/src/include/tcop/ddldeparse.h b/src/include/tcop/ddldeparse.h
index 3185d49c8e..1cceb53662 100644
--- a/src/include/tcop/ddldeparse.h
+++ b/src/include/tcop/ddldeparse.h
@@ -18,12 +18,20 @@
 typedef struct
 {
 	bool		verbose_mode;
+	/*
+	 * include_owner indicates if the owner/role of the command should be
+	 * included in the deparsed Json output. It is set to false for any commands
+	 * that don't CREATE database objects (ALTER commands for example), this is
+	 * to avoid encoding and sending the owner to downstream for replay as it is
+	 * unnecessary for such commands.
+	 */
+	bool		include_owner;
 	/* provolatile flag of the function contained in the command */
 	char		func_volatile;
 } ddl_deparse_context;
 
 extern char *deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context);
-extern char *deparse_ddl_json_to_string(char *jsonb);
+extern char *deparse_ddl_json_to_string(char *jsonb, char** owner);
 extern char *deparse_drop_command(const char *objidentity, const char *objecttype,
 								  Node *parsetree);
 
diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h
index a4a12377b8..87a761bb3e 100644
--- a/src/include/tcop/deparse_utility.h
+++ b/src/include/tcop/deparse_utility.h
@@ -48,6 +48,7 @@ typedef struct CollectedCommand
 	CollectedCommandType type;
 
 	bool		in_extension;
+	char	   *role;
 	Node	   *parsetree;
 
 	union
diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out
index 3c1a0869ec..630f94da71 100644
--- a/src/test/regress/expected/subscription.out
+++ b/src/test/regress/expected/subscription.out
@@ -116,18 +116,18 @@ CREATE SUBSCRIPTION regress_testsub4 CONNECTION 'dbname=regress_doesnotexist' PU
 WARNING:  subscription was created, but is not connected
 HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
 \dRs+ regress_testsub4
-                                                                                                           List of subscriptions
-       Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
-------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub4 | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | none   | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+       Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub4 | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | none   | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub4 SET (origin = any);
 \dRs+ regress_testsub4
-                                                                                                           List of subscriptions
-       Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
-------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub4 | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+       Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub4 | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 DROP SUBSCRIPTION regress_testsub3;
@@ -145,10 +145,10 @@ ALTER SUBSCRIPTION regress_testsub CONNECTION 'foobar';
 ERROR:  invalid connection string syntax: missing "=" after "foobar" in connection info string
 
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub SET PUBLICATION testpub2, testpub3 WITH (refresh = false);
@@ -156,10 +156,10 @@ ALTER SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist2';
 ALTER SUBSCRIPTION regress_testsub SET (slot_name = 'newname');
 ALTER SUBSCRIPTION regress_testsub SET (password_required = false);
 \dRs+
-                                                                                                               List of subscriptions
-      Name       |           Owner           | Enabled |     Publication     | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |           Conninfo           | Skip LSN 
------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+------------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub2,testpub3} | f      | off       | d                | f                | any    | f                 | f             | off                | dbname=regress_doesnotexist2 | 0/0
+                                                                                                                        List of subscriptions
+      Name       |           Owner           | Enabled |     Publication     | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |           Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+------------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub2,testpub3} | f      | off       | d                | f                | any    | f                 | f             | t               | off                | dbname=regress_doesnotexist2 | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub SET (password_required = true);
@@ -174,10 +174,10 @@ ERROR:  unrecognized subscription parameter: "create_slot"
 -- ok
 ALTER SUBSCRIPTION regress_testsub SKIP (lsn = '0/12345');
 \dRs+
-                                                                                                               List of subscriptions
-      Name       |           Owner           | Enabled |     Publication     | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |           Conninfo           | Skip LSN 
------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+------------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub2,testpub3} | f      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist2 | 0/12345
+                                                                                                                        List of subscriptions
+      Name       |           Owner           | Enabled |     Publication     | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |           Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+------------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub2,testpub3} | f      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist2 | 0/12345
 (1 row)
 
 -- ok - with lsn = NONE
@@ -186,10 +186,10 @@ ALTER SUBSCRIPTION regress_testsub SKIP (lsn = NONE);
 ALTER SUBSCRIPTION regress_testsub SKIP (lsn = '0/0');
 ERROR:  invalid WAL location (LSN): 0/0
 \dRs+
-                                                                                                               List of subscriptions
-      Name       |           Owner           | Enabled |     Publication     | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |           Conninfo           | Skip LSN 
------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+------------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub2,testpub3} | f      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist2 | 0/0
+                                                                                                                        List of subscriptions
+      Name       |           Owner           | Enabled |     Publication     | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |           Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+------------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub2,testpub3} | f      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist2 | 0/0
 (1 row)
 
 BEGIN;
@@ -221,10 +221,10 @@ ALTER SUBSCRIPTION regress_testsub_foo SET (synchronous_commit = foobar);
 ERROR:  invalid value for parameter "synchronous_commit": "foobar"
 HINT:  Available values: local, remote_write, remote_apply, on, off.
 \dRs+
-                                                                                                                 List of subscriptions
-        Name         |           Owner           | Enabled |     Publication     | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |           Conninfo           | Skip LSN 
----------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+------------------------------+----------
- regress_testsub_foo | regress_subscription_user | f       | {testpub2,testpub3} | f      | off       | d                | f                | any    | t                 | f             | local              | dbname=regress_doesnotexist2 | 0/0
+                                                                                                                          List of subscriptions
+        Name         |           Owner           | Enabled |     Publication     | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |           Conninfo           | Skip LSN 
+---------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+------------------------------+----------
+ regress_testsub_foo | regress_subscription_user | f       | {testpub2,testpub3} | f      | off       | d                | f                | any    | t                 | f             | t               | local              | dbname=regress_doesnotexist2 | 0/0
 (1 row)
 
 -- rename back to keep the rest simple
@@ -253,19 +253,19 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
 WARNING:  subscription was created, but is not connected
 HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | t      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | t      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub SET (binary = false);
 ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 DROP SUBSCRIPTION regress_testsub;
@@ -277,27 +277,27 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
 WARNING:  subscription was created, but is not connected
 HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | on        | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | on        | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub SET (streaming = parallel);
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | parallel  | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | parallel  | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub SET (streaming = false);
 ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 -- fail - publication already exists
@@ -312,10 +312,10 @@ ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub1, testpub2 WITH (refr
 ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub1, testpub2 WITH (refresh = false);
 ERROR:  publication "testpub1" is already in subscription "regress_testsub"
 \dRs+
-                                                                                                                   List of subscriptions
-      Name       |           Owner           | Enabled |         Publication         | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-----------------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub,testpub1,testpub2} | f      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                            List of subscriptions
+      Name       |           Owner           | Enabled |         Publication         | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-----------------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub,testpub1,testpub2} | f      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 -- fail - publication used more than once
@@ -330,10 +330,10 @@ ERROR:  publication "testpub3" is not in subscription "regress_testsub"
 -- ok - delete publications
 ALTER SUBSCRIPTION regress_testsub DROP PUBLICATION testpub1, testpub2 WITH (refresh = false);
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 DROP SUBSCRIPTION regress_testsub;
@@ -369,10 +369,10 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
 WARNING:  subscription was created, but is not connected
 HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | p                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | p                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 --fail - alter of two_phase option not supported.
@@ -381,10 +381,10 @@ ERROR:  unrecognized subscription parameter: "two_phase"
 -- but can alter streaming when two_phase enabled
 ALTER SUBSCRIPTION regress_testsub SET (streaming = true);
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | on        | p                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | on        | p                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
@@ -394,10 +394,10 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
 WARNING:  subscription was created, but is not connected
 HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | on        | p                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | on        | p                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
@@ -410,18 +410,18 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB
 WARNING:  subscription was created, but is not connected
 HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | f                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub SET (disable_on_error = true);
 \dRs+
-                                                                                                           List of subscriptions
-      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Synchronous commit |          Conninfo           | Skip LSN 
------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+--------------------+-----------------------------+----------
- regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | t                | any    | t                 | f             | off                | dbname=regress_doesnotexist | 0/0
+                                                                                                                    List of subscriptions
+      Name       |           Owner           | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Match DDL owner | Synchronous commit |          Conninfo           | Skip LSN 
+-----------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+-----------------+--------------------+-----------------------------+----------
+ regress_testsub | regress_subscription_user | f       | {testpub}   | f      | off       | d                | t                | any    | t                 | f             | t               | off                | dbname=regress_doesnotexist | 0/0
 (1 row)
 
 ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE);
-- 
2.34.1

From b3bc2c82d44fdd9899a9f986810af4e6156ff868 Mon Sep 17 00:00:00 2001
From: Vignesh C <[email-masked]>
Date: Mon, 29 May 2023 16:32:30 +0530
Subject: [PATCH 8/8] ObjTree Removal for multiple commands.

1. create table
2. alter table
3. drop table
4. create sequence
5. alter sequence
6. alter object owner to
7. alter object set schema
8. alter object rename
---
 src/backend/commands/ddldeparse.c             | 3610 ++++++++---------
 src/backend/replication/logical/ddltrigger.c  |    5 +-
 src/include/tcop/ddldeparse.h                 |    4 +-
 .../expected/alter_table.out                  |  342 +-
 .../expected/constraints.out                  |  284 +-
 .../expected/create_index.out                 |    4 +-
 .../expected/create_rule.out                  |   16 +-
 .../expected/create_table.out                 |  300 +-
 .../expected/test_ddl_deparse.out             |    2 +
 .../sql/alter_table.sql                       |   44 +-
 .../sql/test_ddl_deparse.sql                  |    3 +
 .../t/001_compare_dumped_results.pl           |    6 +
 .../test_ddl_deparse_regress.c                |    6 +-
 13 files changed, 2284 insertions(+), 2342 deletions(-)

diff --git a/src/backend/commands/ddldeparse.c b/src/backend/commands/ddldeparse.c
index 52e2e2216d..f3d518cfd3 100644
--- a/src/backend/commands/ddldeparse.c
+++ b/src/backend/commands/ddldeparse.c
@@ -20,13 +20,12 @@
  * etc) looking only at the parse node.
  *
  * Deparse object tree is created by using:
- * 	a) new_objtree("know contents") where the complete tree content is known or
- *     the initial tree content is known.
- * 	b) new_objtree("") for the syntax where the object tree will be derived
- *     based on some conditional checks.
- * 	c) new_objtree_VA where the complete tree can be derived using some fixed
- *     content or by using the initial tree content along with some variable
- *     arguments.
+ * 	new_jsonb_VA where the key-value pairs composing an jsonb object can be
+ * 	derived using the passed variable arguments. In order to successfully
+ *  construct one key:value pair, a set of three arguments consisting of a name
+ * 	(string), type (from the jbvType enum) and value must be supplied. It can
+ *  take multiple such sets and construct multiple key-value pairs and append
+ *  those to output parse-state.
  *
  * IDENTIFICATION
  *	  src/backend/commands/ddldeparse.c
@@ -67,53 +66,6 @@
 /* Estimated length of the generated jsonb string */
 #define JSONB_ESTIMATED_LEN 128
 
-/*
- * Before they are turned into JSONB representation, each command is
- * represented as an object tree, using the structs below.
- */
-typedef enum
-{
-	ObjTypeNull,
-	ObjTypeBool,
-	ObjTypeString,
-	ObjTypeArray,
-	ObjTypeInteger,
-	ObjTypeObject
-} ObjType;
-
-/*
- * Represent the command as an object tree.
- */
-typedef struct ObjTree
-{
-	slist_head	params;			/* Object tree parameters */
-	int			numParams;		/* Number of parameters in the object tree */
-	StringInfo	fmtinfo;		/* Format string of the ObjTree */
-	bool		present;		/* Indicates if boolean value should be stored */
-} ObjTree;
-
-/*
- * An element of an object tree (ObjTree).
- */
-typedef struct ObjElem
-{
-	char	   *name;			/* Name of object element */
-	ObjType		objtype;		/* Object type */
-
-	union
-	{
-		bool		boolean;
-		char	   *string;
-		int64		integer;
-		float8		flt;
-		ObjTree    *object;
-		List	   *array;
-	}			value;			/* Store the object value based on the object
-								 * type */
-	slist_node	node;			/* Used in converting back to ObjElem
-								 * structure */
-} ObjElem;
-
 /*
  * Reduce some unnecessary strings from the output json when verbose
  * and "present" member is false. This means these strings won't be merged into
@@ -121,50 +73,11 @@ typedef struct ObjElem
  */
 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 *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);
-static ObjElem *new_object_object(ObjTree *value);
-static ObjTree *new_objtree_VA(char *fmt, int numobjs, ...);
-static JsonbValue *objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state, char *owner);
-static char *RelationGetColumnDefault(Relation rel, AttrNumber attno,
-									  List *dpcontext, Node **expr);
-
-static ObjTree *deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
-								  ColumnDef *coldef, bool is_alter, Node **expr);
-static ObjTree *deparse_ColumnIdentity(Oid seqrelid, char identity, bool alter_table);
-static ObjTree *deparse_ColumnSetOptions(AlterTableCmd *subcmd);
-
-static ObjTree *deparse_DefElem(DefElem *elem, bool is_reset);
-static ObjTree *deparse_OnCommitClause(OnCommitAction option);
-static ObjTree *deparse_RelSetOptions(AlterTableCmd *subcmd);
-
-static inline ObjElem *deparse_Seq_Cache(Form_pg_sequence seqdata, bool alter_table);
-static inline ObjElem *deparse_Seq_Cycle(Form_pg_sequence seqdata, bool alter_table);
-static inline ObjElem *deparse_Seq_IncrementBy(Form_pg_sequence seqdata, bool alter_table);
-static inline ObjElem *deparse_Seq_Minvalue(Form_pg_sequence seqdata, bool alter_table);
-static inline ObjElem *deparse_Seq_Maxvalue(Form_pg_sequence seqdata, bool alter_table);
-static inline ObjElem *deparse_Seq_Restart(int64 last_value);
-static inline ObjElem *deparse_Seq_Startwith(Form_pg_sequence seqdata, bool alter_table);
-static inline ObjElem *deparse_Seq_As(Form_pg_sequence seqdata);
-
-static List *deparse_InhRelations(Oid objectId);
-static List *deparse_TableElements(Relation relation, List *tableElements, List *dpcontext,
-								   bool typed, bool composite);
-static void mark_function_volatile(ddl_deparse_context *context, Node *expr);
-
 /*
  * Mark the func_volatile flag for an expression in the command.
  */
 static void
-mark_function_volatile(ddl_deparse_context *context, Node *expr)
+mark_function_volatile(ddl_deparse_context * context, Node *expr)
 {
 	if (context->func_volatile == PROVOLATILE_VOLATILE)
 		return;
@@ -181,276 +94,261 @@ mark_function_volatile(ddl_deparse_context *context, Node *expr)
 }
 
 /*
- * 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}
+ * Return the string representation of the given RELPERSISTENCE value.
  */
-static void
-append_not_present(ObjTree *tree, char *sub_fmt)
+static char *
+get_persistence_str(char persistence)
 {
-	if (verbose && sub_fmt)
+	switch (persistence)
 	{
-		append_format_string(tree, sub_fmt);
+		case RELPERSISTENCE_TEMP:
+			return "TEMPORARY";
+		case RELPERSISTENCE_UNLOGGED:
+			return "UNLOGGED";
+		case RELPERSISTENCE_PERMANENT:
+			return "";
+		default:
+			elog(ERROR, "unexpected persistence marking %c", persistence);
+			return "";			/* make compiler happy */
 	}
-
-	append_bool_object(tree, "present", false);
 }
 
 /*
- * Append an array parameter to a tree.
+ * Obtain the deparsed default value for the given column of the given table.
+ *
+ * Caller must have set a correct deparse context and must ensure that the
+ * passed attribute has a default value.
  */
-static void
-append_array_object(ObjTree *tree, char *sub_fmt, List *array)
+static char *
+RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext,
+						 Node **expr)
 {
-	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);
-		}
+	Node	   *defval;
+	char	   *defstr;
 
-	}
+	defval = build_column_default(rel, attno);
+	Assert(defval != NULL);
 
-	/* Check for empty list after removing elements */
-	if (list_length(array) == 0)
-		return;
+	defstr = deparse_expression(defval, dpcontext, false, false);
 
-	object_name = append_object_to_format_string(tree, sub_fmt);
+	/* Collect the expression for later replication safety checks */
+	if (expr)
+		*expr = defval;
 
-	param = new_object(ObjTypeArray, object_name);
-	param->value.array = array;
-	append_premade_object(tree, param);
+	return defstr;
 }
 
 /*
- * Append a boolean parameter to a tree.
+ * Obtain the deparsed partition bound expression for the given table.
  */
-static void
-append_bool_object(ObjTree *tree, char *sub_fmt, bool value)
+static char *
+RelationGetPartitionBound(Oid relid)
 {
-	ObjElem    *param;
-	char	   *object_name = sub_fmt;
+	Datum		deparsed;
+	Datum		boundDatum;
+	bool		isnull;
+	HeapTuple	tuple;
 
-	Assert(sub_fmt);
+	tuple = SearchSysCache1(RELOID, relid);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation with OID %u", relid);
 
-	/*
-	 * Check if the format string is 'present' and if yes, store the boolean
-	 * value
-	 */
-	if (strcmp(sub_fmt, "present") == 0)
-		tree->present = value;
-	else
-		object_name = append_object_to_format_string(tree, sub_fmt);
+	boundDatum = SysCacheGetAttr(RELOID, tuple,
+								 Anum_pg_class_relpartbound,
+								 &isnull);
+
+	deparsed = DirectFunctionCall2(pg_get_expr,
+								   CStringGetTextDatum(TextDatumGetCString(boundDatum)),
+								   relid);
+
+	ReleaseSysCache(tuple);
 
-	param = new_object(ObjTypeBool, object_name);
-	param->value.boolean = value;
-	append_premade_object(tree, param);
+	return TextDatumGetCString(deparsed);
 }
 
 /*
- * Append the input format string to the ObjTree.
+ * Insert JsonbValue key to the output parse state.
  */
 static void
-append_format_string(ObjTree *tree, char *sub_fmt)
+insert_jsonb_key(JsonbParseState *state, char *name)
 {
-	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);
+	JsonbValue	key;
 
-	appendStringInfoString(tree->fmtinfo, sub_fmt);
+	/* Push the key */
+	key.type = jbvString;
+	key.val.string.val = name;
+	key.val.string.len = strlen(name);
+	pushJsonbValue(&state, WJB_KEY, &key);
 }
 
 /*
- * Append an object parameter to a tree.
+ * Insert the format string to the output parse state.
  */
 static void
-append_object_object(ObjTree *tree, char *sub_fmt, ObjTree *value)
+fmt_to_jsonb_element(JsonbParseState *state, char *fmtStr)
 {
-	ObjElem    *param;
-	char	   *object_name;
-
-	Assert(sub_fmt);
+	JsonbValue	val;
 
-	if (!verbose && !value->present)
-		return;
+	Assert(fmtStr);
 
-	object_name = append_object_to_format_string(tree, sub_fmt);
+	/* Push the key first */
+	insert_jsonb_key(state, "fmt");
 
-	param = new_object(ObjTypeObject, object_name);
-	param->value.object = value;
-	append_premade_object(tree, param);
+	/* Push the value now */
+	val.type = jbvString;
+	val.val.string.val = pstrdup(fmtStr);
+	val.val.string.len = strlen(fmtStr);
+	pushJsonbValue(&state, WJB_VALUE, &val);
 }
 
 /*
- * Return the object name which is extracted from the input "*%{name[:.]}*"
- * style string. And append the input format string to the ObjTree.
+ * Append new jsonb key:value pair to the output parse state -- varargs version.
+ *
+ * The "fmt" argument is used to append as a "fmt" element in current object.
+ * The "skipObject" argument indicates if we want to skip object creation
+ * considering it will be taken care by the caller.
+ * The "numobjs" argument indicates the number of extra elements to append;
+ * for each one, a name (string), type (from the jbvType enum) and value must
+ * be supplied.  The value must match the type given; for instance, jbvBool
+ * requires an bool, jbvString requires a char * and so on,
+ * Each element type must match the conversion specifier given in the format
+ * string, as described in ddl_deparse_expand_command.
+ *
+ * Note we don't have the luxury of sprintf-like compiler warnings for
+ * malformed argument lists.
  */
-static char *
-append_object_to_format_string(ObjTree *tree, char *sub_fmt)
+static JsonbValue *
+new_jsonb_VA(JsonbParseState *state, char *fmt, bool skipObject,
+			 int numobjs,...)
 {
-	StringInfoData object_name;
-	const char *end_ptr, *start_ptr;
+	va_list		args;
+	int			i;
+	JsonbValue	val;
+	JsonbValue *value = NULL;
 
-	Assert(tree->fmtinfo);
+	/* Set up the toplevel object if not instructed otherwise */
+	if (!skipObject)
+		pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
-	initStringInfo(&object_name);
+	/* Set up the "fmt" */
+	if (fmt)
+		fmt_to_jsonb_element(state, fmt);
 
-	start_ptr = strchr(sub_fmt, '{');
-	end_ptr = strchr(sub_fmt, ':');
-	if (end_ptr == NULL)
-		end_ptr = strchr(sub_fmt, '}');
+	/* And process the given varargs */
+	va_start(args, numobjs);
 
-	if (start_ptr != NULL && end_ptr != NULL)
+	for (i = 0; i < numobjs; i++)
 	{
-		appendBinaryStringInfo(&object_name, start_ptr + 1,
-								end_ptr - start_ptr - 1);
-	}
-
-	if (object_name.len == 0)
-		elog(ERROR, "object name not found");
+		char	   *name;
+		enum jbvType type;
 
-	append_format_string(tree, sub_fmt);
+		name = va_arg(args, char *);
+		type = va_arg(args, enum jbvType);
 
-	return object_name.data;
-}
+		/* Push the key first */
+		insert_jsonb_key(state, name);
 
-/*
- * Append a preallocated parameter to a tree.
- */
-static inline void
-append_premade_object(ObjTree *tree, ObjElem *elem)
-{
-	slist_push_head(&tree->params, &elem->node);
-	tree->numParams++;
-}
+		/*
+		 * For all param types other than jbvNull, there must be a value in
+		 * the varargs. Fetch it and add the fully formed subobject into the
+		 * main object.
+		 */
+		switch (type)
+		{
+			case jbvNull:
+				/* Null params don't have a value (obviously) */
+				val.type = jbvNull;
+				pushJsonbValue(&state, WJB_VALUE, &val);
+				break;
 
-/*
- * Append a string parameter to a tree.
- */
-static void
-append_string_object(ObjTree *tree, char *sub_fmt, char *object_name,
-					 char *value)
-{
-	ObjElem    *param;
+			case jbvBool:
+				/* Push the bool value */
+				val.type = jbvBool;
+				val.val.boolean = va_arg(args, int);
+				pushJsonbValue(&state, WJB_VALUE, &val);
+				break;
 
-	Assert(sub_fmt);
+			case jbvString:
+				/* Push the string value */
+				val.type = jbvString;
+				val.val.string.val = pstrdup(va_arg(args, char *));
+				val.val.string.len = strlen(val.val.string.val);
+				pushJsonbValue(&state, WJB_VALUE, &val);
+				break;
 
-	if (!verbose && (value == NULL || value[0] == '\0'))
-		return;
+			case jbvNumeric:
+				/* Push the numeric value */
+				val.type = jbvNumeric;
+				val.val.numeric = (Numeric) DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+																				va_arg(args, int)));
 
-	append_format_string(tree, sub_fmt);
-	param = new_object(ObjTypeString, object_name);
-	param->value.string = value;
-	append_premade_object(tree, param);
-}
+				pushJsonbValue(&state, WJB_VALUE, &val);
+				break;
 
-/*
- * Return the string representation of the given RELPERSISTENCE value.
- */
-static char *
-get_persistence_str(char persistence)
-{
-	switch (persistence)
-	{
-		case RELPERSISTENCE_TEMP:
-			return "TEMPORARY";
-		case RELPERSISTENCE_UNLOGGED:
-			return "UNLOGGED";
-		case RELPERSISTENCE_PERMANENT:
-			return "";
-		default:
-			elog(ERROR, "unexpected persistence marking %c", persistence);
-			return "";			/* make compiler happy */
+			default:
+				elog(ERROR, "unrecognized jbvType %d", type);
+		}
 	}
+
+	va_end(args);
+
+	if (!skipObject)
+		value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	return value;
 }
 
 /*
- * Allocate a new parameter.
+ * Insert new object with present:false as key-value pair
+ * to the output parse state.
  */
-static ObjElem *
-new_object(ObjType type, char *name)
+static void
+insert_not_present_obj(JsonbParseState *state, char *fmt)
 {
-	ObjElem    *param;
-
-	param = palloc0(sizeof(ObjElem));
-	param->name = name;
-	param->objtype = type;
-
-	return param;
+	new_jsonb_VA(state, fmt, false, 1, "present", jbvBool, false);
 }
 
 /*
- * Allocate a new object parameter.
+ * Process the role string into the output parse state.
  */
-static ObjElem *
-new_object_object(ObjTree *value)
+static void
+role_to_jsonb_element(JsonbParseState *state, char *owner)
 {
-	ObjElem    *param;
-
-	param = new_object(ObjTypeObject, NULL);
-	param->value.object = value;
+	if (owner == NULL)
+		return;
 
-	return param;
+	new_jsonb_VA(state, NULL, true, 1, "myowner", jbvString, owner);
 }
 
 /*
- * Allocate a new object tree to store parameter values.
+ * A helper routine to insert jsonb for typId to the output parse state.
  */
-static ObjTree *
-new_objtree(char *fmt)
+static void
+new_jsonb_for_type(JsonbParseState *state, Oid typId, int32 typmod)
 {
-	ObjTree    *objtree;
+	Oid			typnspid;
+	char	   *type_nsp;
+	char	   *type_name = NULL;
+	char	   *typmodstr;
+	bool		type_array;
 
-	objtree = palloc0(sizeof(ObjTree));
-	objtree->present = true;
-	slist_init(&objtree->params);
+	format_type_detailed(typId, typmod, &typnspid, &type_name, &typmodstr,
+						 &type_array);
 
-	if (fmt)
-	{
-		objtree->fmtinfo = makeStringInfo();
-		appendStringInfoString(objtree->fmtinfo, fmt);
-	}
+	if (OidIsValid(typnspid))
+		type_nsp = get_namespace_name_or_temp(typnspid);
+	else
+		type_nsp = pstrdup("");
 
-	return objtree;
+	new_jsonb_VA(state, NULL, false, 4,
+				 "schemaname", jbvString, type_nsp,
+				 "typename", jbvString, type_name,
+				 "typmod", jbvString, typmodstr,
+				 "typarray", jbvBool, type_array);
 }
 
 /*
- * A helper routine to set up %{}D and %{}O elements.
+ * A helper routine to set up name: schemaname, objname
  *
  * Elements "schema_name" and "obj_name" are set.  If the namespace OID
  * corresponds to a temp schema, that's set to "pg_temp".
@@ -459,10 +357,9 @@ new_objtree(char *fmt)
  * be quoted as an identifier or not, which is not something that this routine
  * concerns itself with; that will be up to the expand function.
  */
-static ObjTree *
-new_objtree_for_qualname(Oid nspid, char *name)
+static void
+new_jsonb_for_qualname(JsonbParseState *state, Oid nspid, char *objName, char *keyName, bool skipObject)
 {
-	ObjTree    *qualified;
 	char	   *namespace;
 
 	if (isAnyTempNamespace(nspid))
@@ -470,21 +367,22 @@ new_objtree_for_qualname(Oid nspid, char *name)
 	else
 		namespace = get_namespace_name(nspid);
 
-	qualified = new_objtree_VA(NULL, 2,
-							   "schemaname", ObjTypeString, namespace,
-							   "objname", ObjTypeString, pstrdup(name));
+	/* Push the key first */
+	if (keyName)
+		insert_jsonb_key(state, keyName);
 
-	return qualified;
+	new_jsonb_VA(state, NULL, skipObject, 2,
+				 "schemaname", jbvString, namespace,
+				 "objname", jbvString, objName);
 }
 
 /*
- * A helper routine to set up %{}D and %{}O elements, with the object specified
- * by classId/objId.
+ * A helper routine to set up name: 'schemaname, objname' where the object is
+ * specified by classId and objId.
  */
-static ObjTree *
-new_objtree_for_qualname_id(Oid classId, Oid objectId)
+static void
+new_jsonb_for_qualname_id(JsonbParseState *state, Oid classId, Oid objectId, char *keyName, bool skipObject)
 {
-	ObjTree    *qualified;
 	Relation	catalog;
 	HeapTuple	catobj;
 	Datum		obj_nsp;
@@ -504,443 +402,385 @@ new_objtree_for_qualname_id(Oid classId, Oid objectId)
 	Anum_namespace = get_object_attnum_namespace(classId);
 
 	obj_nsp = heap_getattr(catobj, Anum_namespace, RelationGetDescr(catalog),
-						  &isnull);
+						   &isnull);
 	if (isnull)
 		elog(ERROR, "null namespace for object %u", objectId);
 
 	obj_name = heap_getattr(catobj, Anum_name, RelationGetDescr(catalog),
-						   &isnull);
+							&isnull);
 	if (isnull)
 		elog(ERROR, "null attribute name for object %u", objectId);
 
-	qualified = new_objtree_for_qualname(DatumGetObjectId(obj_nsp),
-										 NameStr(*DatumGetName(obj_name)));
+	new_jsonb_for_qualname(state, DatumGetObjectId(obj_nsp),
+						   NameStr(*DatumGetName(obj_name)), keyName, skipObject);
 	table_close(catalog, AccessShareLock);
-
-	return qualified;
 }
 
 /*
- * A helper routine to setup %{}T elements.
+ * A helper routine to insert key:value where value is array of qualname to
+ * the output parse state.
  */
-static ObjTree *
-new_objtree_for_type(Oid typeId, int32 typmod)
+static void
+new_jsonbArray_for_qualname_id(JsonbParseState *state, char *keyname, List *array)
 {
-	Oid			typnspid;
-	char	   *type_nsp;
-	char	   *type_name = NULL;
-	char	   *typmodstr;
-	bool		type_array;
+	ListCell   *lc;
 
-	format_type_detailed(typeId, typmod,
-						 &typnspid, &type_name, &typmodstr, &type_array);
+	/* Push the key first */
+	insert_jsonb_key(state, keyname);
 
-	if (OidIsValid(typnspid))
-		type_nsp = get_namespace_name_or_temp(typnspid);
-	else
-		type_nsp = pstrdup("");
+	pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+	/* Push the array elements now */
+	foreach(lc, array)
+		new_jsonb_for_qualname_id(state, RelationRelationId, lfirst_oid(lc),
+								  NULL, false);
 
-	return new_objtree_VA(NULL, 4,
-						  "schemaname", ObjTypeString, type_nsp,
-						  "typename", ObjTypeString, type_name,
-						  "typmod", ObjTypeString, typmodstr,
-						  "typarray", ObjTypeBool, type_array);
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
 }
 
 /*
- * Allocate a new object tree to store parameter values -- varargs version.
- *
- * The "fmt" argument is used to append as a "fmt" element in the output blob.
- * numobjs indicates the number of extra elements to append; for each one, a
- * name (string), type (from the ObjType enum) and value must be supplied.  The
- * value must match the type given; for instance, ObjTypeInteger requires an
- * int64, ObjTypeString requires a char *, ObjTypeArray requires a list (of
- * ObjElem), ObjTypeObject requires an ObjTree, and so on.  Each element type *
- * must match the conversion specifier given in the format string, as described
- * in ddl_deparse_expand_command, q.v.
- *
- * Note we don't have the luxury of sprintf-like compiler warnings for
- * malformed argument lists.
+ * A helper routine to insert collate object for column definition to the output parse state.
  */
-static ObjTree *
-new_objtree_VA(char *fmt, int numobjs, ...)
+static void
+insert_collate_object(JsonbParseState *state, char *fmt, Oid classId, Oid objectId, char *key)
 {
-	ObjTree    *tree;
-	va_list		args;
-	int			i;
-
-	/* Set up the toplevel object and its "fmt" */
-	tree = new_objtree(fmt);
-
-	/* And process the given varargs */
-	va_start(args, numobjs);
-	for (i = 0; i < numobjs; i++)
-	{
-		char	   *name;
-		ObjType		type;
-		ObjElem    *elem;
 
-		name = va_arg(args, char *);
-		type = va_arg(args, ObjType);
-		elem = new_object(type, NULL);
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
-		/*
-		 * For all param types other than ObjTypeNull, there must be a value in
-		 * the varargs. Fetch it and add the fully formed subobject into the
-		 * main object.
-		 */
-		switch (type)
-		{
-			case ObjTypeNull:
-				/* Null params don't have a value (obviously) */
-				break;
-			case ObjTypeBool:
-				elem->value.boolean = va_arg(args, int);
-				break;
-			case ObjTypeString:
-				elem->value.string = va_arg(args, char *);
-				break;
-			case ObjTypeArray:
-				elem->value.array = va_arg(args, List *);
-				break;
-			case ObjTypeInteger:
-				elem->value.integer = va_arg(args, int);
-				break;
-			case ObjTypeObject:
-				elem->value.object = va_arg(args, ObjTree *);
-				break;
-			default:
-				elog(ERROR, "invalid ObjTree element type %d", type);
-		}
+	fmt_to_jsonb_element(state, fmt);
 
-		elem->name = name;
-		append_premade_object(tree, elem);
-	}
+	/* push object now */
+	new_jsonb_for_qualname_id(state, classId, objectId, key, false);
 
-	va_end(args);
-	return tree;
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 }
 
 /*
- * Process the pre-built format string from the ObjTree into the output parse
- * state.
+ * A helper routine to insert identity object for the table definition
+ * to the output parse state.
  */
 static void
-objtree_fmt_to_jsonb_element(JsonbParseState *state, ObjTree *tree)
+insert_identity_object(JsonbParseState *state, Oid nspid, char *relname)
 {
-	JsonbValue	key;
-	JsonbValue	val;
-
-	if (tree->fmtinfo == NULL)
-		return;
-
-	/* Push the key first */
-	key.type = jbvString;
-	key.val.string.val = "fmt";
-	key.val.string.len = strlen(key.val.string.val);
-	pushJsonbValue(&state, WJB_KEY, &key);
-
-	/* Then process the pre-built format string */
-	val.type = jbvString;
-	val.val.string.len = tree->fmtinfo->len;
-	val.val.string.val = tree->fmtinfo->data;
-	pushJsonbValue(&state, WJB_VALUE, &val);
+	new_jsonb_for_qualname(state, nspid, relname, "identity", false);
 }
 
 /*
- * Process the role string into the output parse state.
+ * Deparse the sequence CACHE option to Jsonb
+ *
+ * Verbose syntax
+ * SET CACHE %{value}s
+ * OR
+ * CACHE %{value}
  */
-static void
-role_to_jsonb_element(JsonbParseState *state, char *owner)
+static inline void
+deparse_Seq_Cache_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
 {
-	JsonbValue	key;
-	JsonbValue	val;
-
-	if (owner == NULL)
-		return;
+	char	   *fmt;
 
-	/* Push the key first */
-	key.type = jbvString;
-	key.val.string.val = "myowner";
-	key.val.string.len = strlen(key.val.string.val);
-	pushJsonbValue(&state, WJB_KEY, &key);
+	fmt = alter_table ? "SET CACHE %{value}s" : "CACHE %{value}s";
 
-	/* Then process the role string */
-	val.type = jbvString;
-	val.val.string.len = strlen(owner);
-	val.val.string.val = owner;
-	pushJsonbValue(&state, WJB_VALUE, &val);
+	new_jsonb_VA(state, fmt, false, 2,
+				 "clause", jbvString, "cache",
+				 "value", jbvString, psprintf(INT64_FORMAT, seqdata->seqcache));
 }
 
 /*
- * Create a JSONB representation from an ObjTree and its owner (if given).
+ * Deparse the sequence CYCLE option to Jsonb.
+ *
+ * Verbose syntax
+ * SET %{no}s CYCLE
+ * OR
+ * %{no}s CYCLE
  */
-static Jsonb *
-objtree_to_jsonb(ObjTree *tree, char *owner)
+static inline void
+deparse_Seq_Cycle_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
 {
-	JsonbValue *value;
+	char	   *fmt;
 
-	value = objtree_to_jsonb_rec(tree, NULL, owner);
-	return JsonbValueToJsonb(value);
+	fmt = alter_table ? "SET %{no}s CYCLE" : "%{no}s CYCLE";
+
+	new_jsonb_VA(state, fmt, false, 2,
+				 "clause", jbvString, "cycle",
+				 "no", jbvString, seqdata->seqcycle ? "" : "NO");
 }
 
 /*
- * Helper for objtree_to_jsonb_rec: process an individual element from an object or
- * an array into the output parse state.
+ * Deparse the sequence INCREMENT BY option to Jsonb
+ *
+ * Verbose syntax
+ * SET INCREMENT BY %{value}s
+ * OR
+ * INCREMENT BY %{value}s
  */
-static void
-objtree_to_jsonb_element(JsonbParseState *state, ObjElem *object,
-						 JsonbIteratorToken elem_token)
+static inline void
+deparse_Seq_IncrementBy_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
 {
-	JsonbValue	val;
-
-	switch (object->objtype)
-	{
-		case ObjTypeNull:
-			val.type = jbvNull;
-			pushJsonbValue(&state, elem_token, &val);
-			break;
-
-		case ObjTypeString:
-			val.type = jbvString;
-			val.val.string.len = strlen(object->value.string);
-			val.val.string.val = object->value.string;
-			pushJsonbValue(&state, elem_token, &val);
-			break;
-
-		case ObjTypeInteger:
-			val.type = jbvNumeric;
-			val.val.numeric = (Numeric)
-				DatumGetNumeric(DirectFunctionCall1(int8_numeric,
-													object->value.integer));
-			pushJsonbValue(&state, elem_token, &val);
-			break;
-
-		case ObjTypeBool:
-			val.type = jbvBool;
-			val.val.boolean = object->value.boolean;
-			pushJsonbValue(&state, elem_token, &val);
-			break;
-
-		case ObjTypeObject:
-			/* Recursively add the object into the existing parse state */
-			objtree_to_jsonb_rec(object->value.object, state, NULL);
-			break;
-
-		case ObjTypeArray:
-			{
-				ListCell   *cell;
-
-				pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
-				foreach(cell, object->value.array)
-				{
-					ObjElem    *elem = lfirst(cell);
+	char	   *fmt;
 
-					objtree_to_jsonb_element(state, elem, WJB_ELEM);
-				}
-				pushJsonbValue(&state, WJB_END_ARRAY, NULL);
-			}
-			break;
+	fmt = alter_table ? "SET INCREMENT BY %{value}s" : "INCREMENT BY %{value}s";
 
-		default:
-			elog(ERROR, "unrecognized object type %d", object->objtype);
-			break;
-	}
+	new_jsonb_VA(state, fmt, false, 2,
+				 "clause", jbvString, "seqincrement",
+				 "value", jbvString, psprintf(INT64_FORMAT, seqdata->seqincrement));
 }
 
 /*
- * Recursive helper for objtree_to_jsonb.
+ * Deparse the sequence MAXVALUE option to Jsonb.
+ *
+ * Verbose syntax
+ * SET MAXVALUE %{value}s
+ * OR
+ * MAXVALUE %{value}s
  */
-static JsonbValue *
-objtree_to_jsonb_rec(ObjTree *tree, JsonbParseState *state, char *owner)
+static inline void
+deparse_Seq_Maxvalue_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
 {
-	slist_iter	iter;
-
-	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+	char	   *fmt;
 
-	role_to_jsonb_element(state, owner);
-	objtree_fmt_to_jsonb_element(state, tree);
+	fmt = alter_table ? "SET MAXVALUE %{value}s" : "MAXVALUE %{value}s";
 
-	slist_foreach(iter, &tree->params)
-	{
-		ObjElem    *object = slist_container(ObjElem, node, iter.cur);
-		JsonbValue	key;
+	new_jsonb_VA(state, fmt, false, 2,
+				 "clause", jbvString, "maxvalue",
+				 "value", jbvString, psprintf(INT64_FORMAT, seqdata->seqmax));
+}
 
-		/* Push the key first */
-		key.type = jbvString;
-		key.val.string.len = strlen(object->name);
-		key.val.string.val = object->name;
-		pushJsonbValue(&state, WJB_KEY, &key);
+/*
+ * Deparse the sequence MINVALUE option to Jsonb
+ *
+ * Verbose syntax
+ * SET MINVALUE %{value}s
+ * OR
+ * MINVALUE %{value}s
+ */
+static inline void
+deparse_Seq_Minvalue_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
+{
+	char	   *fmt;
 
-		/* Then process the value according to its type */
-		objtree_to_jsonb_element(state, object, WJB_VALUE);
-	}
+	fmt = alter_table ? "SET MINVALUE %{value}s" : "MINVALUE %{value}s";
 
-	return pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+	new_jsonb_VA(state, fmt, false, 2,
+				 "clause", jbvString, "minvalue",
+				 "value", jbvString, psprintf(INT64_FORMAT, seqdata->seqmin));
 }
 
 /*
- * Subroutine for CREATE TABLE deparsing.
- *
- * Given a table OID, obtain its constraints and append them to the given
- * elements list.  The updated list is returned.
- *
- * This works for typed tables, regular tables.
+ * Deparse the sequence OWNED BY command.
  *
- * Note that CONSTRAINT_FOREIGN constraints are always ignored.
+ * Verbose syntax
+ * OWNED BY %{owner}D
  */
-static List *
-obtainConstraints(List *elements, Oid relationId)
+static void
+deparse_Seq_OwnedBy_toJsonb(JsonbParseState *state, Oid sequenceId, bool alter_table)
 {
-	Relation	conRel;
-	ScanKeyData key;
+	Relation	depRel;
 	SysScanDesc scan;
+	ScanKeyData keys[3];
 	HeapTuple	tuple;
-	ObjTree    *constr;
-
-	Assert(OidIsValid(relationId));
+	bool		elem_found PG_USED_FOR_ASSERTS_ONLY = false;
 
-	/*
-	 * Scan pg_constraint to fetch all constraints linked to the given
-	 * relation.
-	 */
-	conRel = table_open(ConstraintRelationId, AccessShareLock);
-	ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber,
-				F_OIDEQ, ObjectIdGetDatum(relationId));
-	scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true,
-							  NULL, 1, &key);
+	depRel = table_open(DependRelationId, AccessShareLock);
+	ScanKeyInit(&keys[0],
+				Anum_pg_depend_classid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationRelationId));
+	ScanKeyInit(&keys[1],
+				Anum_pg_depend_objid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(sequenceId));
+	ScanKeyInit(&keys[2],
+				Anum_pg_depend_objsubid,
+				BTEqualStrategyNumber, F_INT4EQ,
+				Int32GetDatum(0));
 
-	/*
-	 * For each constraint, add a node to the list of table elements.  In
-	 * these nodes we include not only the printable information ("fmt"), but
-	 * also separate attributes to indicate the type of constraint, for
-	 * automatic processing.
-	 */
+	scan = systable_beginscan(depRel, DependDependerIndexId, true,
+							  NULL, 3, keys);
 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
 	{
-		Form_pg_constraint constrForm;
-		char	   *contype;
+		Oid			ownerId;
+		Form_pg_depend depform;
+		char	   *colname;
 
-		constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
+		depform = (Form_pg_depend) GETSTRUCT(tuple);
 
-		switch (constrForm->contype)
-		{
-			case CONSTRAINT_CHECK:
-				contype = "check";
-				break;
-			case CONSTRAINT_FOREIGN:
-				continue;		/* not here */
-			case CONSTRAINT_PRIMARY:
-				contype = "primary key";
-				break;
-			case CONSTRAINT_UNIQUE:
-				contype = "unique";
-				break;
-			case CONSTRAINT_EXCLUSION:
-				contype = "exclusion";
-				break;
-			default:
-				elog(ERROR, "unrecognized constraint type");
-		}
+		/* Only consider AUTO dependencies on pg_class */
+		if (depform->deptype != DEPENDENCY_AUTO)
+			continue;
+		if (depform->refclassid != RelationRelationId)
+			continue;
+		if (depform->refobjsubid <= 0)
+			continue;
 
-		/*
-		 * "type" and "contype" are not part of the printable output, but are
-		 * useful to programmatically distinguish these from columns and among
-		 * different constraint types.
-		 *
-		 * XXX it might be useful to also list the column names in a PK, etc.
-		 */
-		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));
+		ownerId = depform->refobjid;
+		colname = get_attname(ownerId, depform->refobjsubid, false);
 
-		if (constrForm->conindid &&
-			(constrForm->contype == CONSTRAINT_PRIMARY ||
-			 constrForm->contype == CONSTRAINT_UNIQUE ||
-			 constrForm->contype == CONSTRAINT_EXCLUSION))
-		{
-			Oid			tblspc = get_rel_tablespace(constrForm->conindid);
+		/* mark the begin of owner's definition object */
+		pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
-			if (OidIsValid(tblspc))
-			{
-				char	   *tblspcname = get_tablespace_name(tblspc);
+		new_jsonb_VA(state, "OWNED BY %{owner}D", true, 1,
+					 "clause", jbvString, "owned");
 
-				if (!tblspcname)
-				{
-					elog(ERROR, "cache lookup failed for tablespace %u",
-									tblspc);
-				}
+		/* owner key */
+		insert_jsonb_key(state, "owner");
 
-				append_string_object(constr,
-									 "USING INDEX TABLESPACE %{tblspc}s",
-									 "tblspc",
-									 tblspcname);
-			}
-		}
+		/* owner value begin */
+		pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
-		elements = lappend(elements, new_object_object(constr));
+		new_jsonb_for_qualname_id(state, RelationRelationId, ownerId, NULL,
+								  true);
+		new_jsonb_VA(state, NULL, true, 1, "attrname", jbvString, colname);
+
+		/* owner value end */
+		pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+		/* mark the end of owner's definition object */
+		pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+#ifdef USE_ASSERT_CHECKING
+		elem_found = true;
+#endif
 	}
 
 	systable_endscan(scan);
-	table_close(conRel, AccessShareLock);
+	relation_close(depRel, AccessShareLock);
 
-	return elements;
+	/*
+	 * If there's no owner column, assert. The caller must have checked
+	 * presence of owned_by element before invoking this.
+	 */
+	Assert(elem_found);
 }
 
 /*
- * Obtain the deparsed default value for the given column of the given table.
+ * Deparse the sequence START WITH option to Jsonb.
  *
- * Caller must have set a correct deparse context and must ensure that the
- * passed attribute has a default value.
+ * Verbose syntax
+ * SET START WITH %{value}s
+ * OR
+ * START WITH %{value}s
  */
-static char *
-RelationGetColumnDefault(Relation rel, AttrNumber attno, List *dpcontext,
-						 Node **expr)
+static inline void
+deparse_Seq_Startwith_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata, bool alter_table)
 {
-	Node	   *defval;
-	char	   *defstr;
+	char	   *fmt;
 
-	defval = build_column_default(rel, attno);
-	Assert(defval != NULL);
+	fmt = alter_table ? "SET START WITH %{value}s" : "START WITH %{value}s";
 
-	defstr = deparse_expression(defval, dpcontext, false, false);
+	new_jsonb_VA(state, fmt, false, 2,
+				 "clause", jbvString, "start",
+				 "value", jbvString, psprintf(INT64_FORMAT, seqdata->seqstart));
+}
 
-	/* Collect the expression for later replication safety checks */
-	if (expr)
-		*expr = defval;
+/*
+ * Deparse the sequence RESTART option to Jsonb
+ *
+ * Verbose syntax
+ * RESTART %{value}s
+ */
+static inline void
+deparse_Seq_Restart_toJsonb(JsonbParseState *state, int64 last_value)
+{
+	new_jsonb_VA(state, "RESTART %{value}s", false, 2,
+				 "clause", jbvString, "restart",
+				 "value", jbvString, psprintf(INT64_FORMAT, last_value));
+}
 
-	return defstr;
+/*
+ * Deparse the sequence AS option.
+ *
+ * Verbose syntax
+ * AS %{seqtype}T
+ */
+static inline void
+deparse_Seq_As_toJsonb(JsonbParseState *state, Form_pg_sequence seqdata)
+{
+	StringInfoData fmtStr;
+
+	initStringInfo(&fmtStr);
+	appendStringInfoString(&fmtStr, "AS");
+
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	/* Push the key first */
+	insert_jsonb_key(state, "seqtype");
+
+	appendStringInfoString(&fmtStr, " %{seqtype}T");
+
+	/* Push the value for seqtype */
+	new_jsonb_for_type(state, seqdata->seqtypid, -1);
+
+	/* We have full fmt by now, so add jsonb element for that */
+	fmt_to_jsonb_element(state, fmtStr.data);
+
+	pfree(fmtStr.data);
+
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 }
 
 /*
- * Obtain the deparsed partition bound expression for the given table.
+ * Deparse the definition of column identity to Jsonb.
+ *
+ * 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 char *
-RelationGetPartitionBound(Oid relid)
+static void
+deparse_ColumnIdentity_toJsonb(JsonbParseState *state, Oid seqrelid, char identity, bool alter_table)
 {
-	Datum		deparsed;
-	Datum		boundDatum;
-	bool		isnull;
-	HeapTuple	tuple;
+	Form_pg_sequence seqform;
+	Sequence_values *seqvalues;
+	char	   *fmt;
+	char	   *identfmt;
 
-	tuple = SearchSysCache1(RELOID, relid);
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "cache lookup failed for relation with OID %u", relid);
+	if (alter_table)
+		fmt = "%{identity_type}s %{seq_definition: }s";
+	else
+		fmt = "%{identity_type}s ( %{seq_definition: }s )";
 
-	boundDatum = SysCacheGetAttr(RELOID, tuple,
-								 Anum_pg_class_relpartbound,
-								 &isnull);
+	/* create object now for value of identity_column */
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+	fmt_to_jsonb_element(state, fmt);
 
-	deparsed = DirectFunctionCall2(pg_get_expr,
-								   CStringGetTextDatum(TextDatumGetCString(boundDatum)),
-								   relid);
+	/* identity_type object creation */
+	insert_jsonb_key(state, "identity_type");
 
-	ReleaseSysCache(tuple);
+	if (alter_table)
+		identfmt = "SET GENERATED %{option}s";
+	else
+		identfmt = "GENERATED %{option}s AS IDENTITY";
 
-	return TextDatumGetCString(deparsed);
+	if (identity == ATTRIBUTE_IDENTITY_ALWAYS)
+		new_jsonb_VA(state, identfmt, false, 1, "option", jbvString, "ALWAYS");
+	else if (identity == ATTRIBUTE_IDENTITY_BY_DEFAULT)
+		new_jsonb_VA(state, identfmt, false, 1,
+					 "option", jbvString, "BY DEFAULT");
+	else
+		insert_not_present_obj(state, verbose ? identfmt : (alter_table ? "SET GENERATED " : "GENERATED "));
+
+	/* seq_definition array object creation */
+	insert_jsonb_key(state, "seq_definition");
+
+	pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+	seqvalues = get_sequence_values(seqrelid);
+	seqform = seqvalues->seqform;
+
+	/* Definition elements */
+	deparse_Seq_Cache_toJsonb(state, seqform, alter_table);
+	deparse_Seq_Cycle_toJsonb(state, seqform, alter_table);
+	deparse_Seq_IncrementBy_toJsonb(state, seqform, alter_table);
+	deparse_Seq_Minvalue_toJsonb(state, seqform, alter_table);
+	deparse_Seq_Maxvalue_toJsonb(state, seqform, alter_table);
+	deparse_Seq_Startwith_toJsonb(state, seqform, alter_table);
+	deparse_Seq_Restart_toJsonb(state, seqvalues->last_value);
+	/* We purposefully do not emit OWNED BY here */
+
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+	/* end of idendity_column object */
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 }
 
 /*
@@ -951,20 +791,22 @@ RelationGetPartitionBound(Oid relid)
  * 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
+ * "%{name}I %{coltype}T STORAGE %{colstorage}s %{compression}s %{collation}s
+ *  %{not_null}s %{default}s %{identity_column}s %{generated_column}s"
  */
-static ObjTree *
-deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
-				  ColumnDef *coldef, bool is_alter, Node **expr)
+static void
+deparse_ColumnDef_toJsonb(JsonbParseState *state, Relation relation,
+						  List *dpcontext, bool composite, ColumnDef *coldef,
+						  bool is_alter, Node **expr)
 {
-	ObjTree    *ret;
-	ObjTree    *tmp_obj;
 	Oid			relid = RelationGetRelid(relation);
 	HeapTuple	attrTup;
 	Form_pg_attribute attrForm;
 	Oid			typid;
 	int32		typmod;
 	Oid			typcollation;
+	char	   *fmtStr = "%{name}I %{coltype}T STORAGE %{colstorage}s %{compression}s %{collation}s "
+		"%{not_null}s %{default}s %{identity_column}s %{generated_column}s";
 	bool		saw_notnull;
 	ListCell   *cell;
 
@@ -975,7 +817,7 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 	 * such?
 	 */
 	if (!coldef->is_local)
-		return NULL;
+		return;
 
 	attrTup = SearchSysCacheAttName(relid, coldef->colname);
 	if (!HeapTupleIsValid(attrTup))
@@ -986,36 +828,44 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 	get_atttypetypmodcoll(relid, attrForm->attnum,
 						  &typid, &typmod, &typcollation);
 
-	ret = new_objtree_VA("%{name}I %{coltype}T", 3,
-						 "type", ObjTypeString, "column",
-						 "name", ObjTypeString, coldef->colname,
-						 "coltype", ObjTypeObject,
-						 new_objtree_for_type(typid, typmod));
+	/* start making column object */
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	/* creat name and type elements for column */
+	new_jsonb_VA(state, fmtStr, true, 2,
+				 "name", jbvString, coldef->colname,
+				 "type", jbvString, "column");
+
+	/*
+	 * create coltype object having 4 elements: schemaname, typename, typemod,
+	 * typearray
+	 */
+	insert_jsonb_key(state, "coltype");
+	new_jsonb_for_type(state, typid, typmod);
 
 	/* STORAGE clause */
 	if (!composite)
-		append_string_object(ret, "STORAGE %{colstorage}s", "colstorage",
-							 storage_name(attrForm->attstorage));
+		new_jsonb_VA(state, NULL, true, 1,
+					 "colstorage", jbvString, storage_name(attrForm->attstorage));
 
 	/* COMPRESSION clause */
-	tmp_obj = new_objtree("COMPRESSION");
+	insert_jsonb_key(state, "compression");
+
 	if (coldef->compression)
-		append_string_object(tmp_obj, "%{compression_method}I",
-							 "compression_method", coldef->compression);
+		new_jsonb_VA(state, "COMPRESSION %{compression_method}I", false, 1,
+					 "compression_method", jbvString, coldef->compression);
 	else
-		append_not_present(tmp_obj, "%{compression_method}I");
+		insert_not_present_obj(state, verbose ? "COMPRESSION %{compression_method}I" : "COMPRESSION");
 
-	append_object_object(ret, "%{compression}s", tmp_obj);
+	/* COLLATE clause */
+	insert_jsonb_key(state, "collation");
 
-	tmp_obj = new_objtree("COLLATE");
 	if (OidIsValid(typcollation))
-		append_object_object(tmp_obj, "%{collation_name}D",
-							 new_objtree_for_qualname_id(CollationRelationId,
-														 typcollation));
+		insert_collate_object(state, "COLLATE %{collation_name}D",
+							  CollationRelationId, typcollation,
+							  "collation_name");
 	else
-		append_not_present(tmp_obj, "%{collation_name}D");
-
-	append_object_object(ret, "%{collation}s", tmp_obj);
+		insert_not_present_obj(state, verbose ? "COLLATE %{collation_name}D" : "COLLATE");
 
 	if (!composite)
 	{
@@ -1048,10 +898,14 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 		if (is_alter && coldef->is_not_null)
 			saw_notnull = true;
 
-		append_string_object(ret, "%{not_null}s", "not_null",
-							 saw_notnull ? "NOT NULL" : "");
+		/* NOT NULL */
+		new_jsonb_VA(state, NULL, true, 1,
+					 "not_null", jbvString, saw_notnull ? "NOT NULL" : "");
 
-		tmp_obj = new_objtree("DEFAULT");
+		/* DEFAULT: Push the key first */
+		insert_jsonb_key(state, "default");
+
+		/* DEFAULT: Push the value now */
 		if (attrForm->atthasdef &&
 			coldef->generated != ATTRIBUTE_GENERATED_STORED)
 		{
@@ -1059,13 +913,11 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 
 			defstr = RelationGetColumnDefault(relation, attrForm->attnum,
 											  dpcontext, expr);
-
-			append_string_object(tmp_obj, "%{default}s", "default", defstr);
+			new_jsonb_VA(state, "DEFAULT %{default}s", false, 1,
+						 "default", jbvString, defstr);
 		}
 		else
-			append_not_present(tmp_obj, "%{default}s");
-
-		append_object_object(ret, "%{default}s", tmp_obj);
+			insert_not_present_obj(state, verbose ? "DEFAULT %{default}s" : "DEFAULT");
 
 		/* IDENTITY COLUMN */
 		if (coldef->identity)
@@ -1079,57 +931,48 @@ deparse_ColumnDef(Relation relation, List *dpcontext, bool composite,
 				seqrelid = RangeVarGetRelid(coldef->identitySequence, NoLock, false);
 		}
 
-		tmp_obj = new_objtree("");
-		if (OidIsValid(seqrelid))
-		{
-			ObjTree    *tmp_obj2;
+		/* IDENTITY COLUMN: Push the key first */
+		insert_jsonb_key(state, "identity_column");
 
-			tmp_obj2 = deparse_ColumnIdentity(seqrelid, coldef->identity, is_alter);
-			append_object_object(tmp_obj, "%{identity_column}s", tmp_obj2);
-		}
+		/* IDENTITY COLUMN: Push the value now */
+		if (OidIsValid(seqrelid))
+			deparse_ColumnIdentity_toJsonb(state, seqrelid, coldef->identity, is_alter);
 		else
-			append_not_present(tmp_obj, "%{identity_column}s");
+			insert_not_present_obj(state, verbose ? "%{identity_column}s" : "");
 
-		append_object_object(ret, "%{identity_column}s", tmp_obj);
+		/* GENERATED COLUMN EXPRESSION: Push the key first */
+		insert_jsonb_key(state, "generated_column");
 
-		/* GENERATED COLUMN EXPRESSION */
-		tmp_obj = new_objtree("GENERATED ALWAYS AS");
+		/* GENERATED COLUMN EXPRESSION: Push the value now */
 		if (coldef->generated == ATTRIBUTE_GENERATED_STORED)
 		{
 			char	   *defstr;
 
 			defstr = RelationGetColumnDefault(relation, attrForm->attnum,
 											  dpcontext, expr);
-			append_string_object(tmp_obj, "(%{generation_expr}s) STORED",
-								 "generation_expr", defstr);
+			new_jsonb_VA(state, "GENERATED ALWAYS AS (%{generation_expr}s) STORED", false, 1,
+						 "generation_expr", jbvString, defstr);
 		}
 		else
-			append_not_present(tmp_obj, "(%{generation_expr}s) STORED");
-
-		append_object_object(ret, "%{generated_column}s", tmp_obj);
+			insert_not_present_obj(state, verbose ? "GENERATED ALWAYS AS (%{generation_expr}s) STORED" : "GENERATED ALWAYS AS");
 	}
 
 	ReleaseSysCache(attrTup);
 
-	return ret;
+	/* mark the end of one column object */
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 }
 
 /*
- * Deparse a ColumnDef node within a typed table creation. This is simpler
- * than the regular case, because we don't have to emit the type declaration,
- * collation, or default. Here we only return something if the column is being
- * declared NOT NULL.
- *
- * As in deparse_ColumnDef, any other constraint is processed elsewhere.
+ * Helper for deparse_ColumnDef_typed_toJsonb()
  *
- * Verbose syntax
- * %{name}I WITH OPTIONS %{not_null}s %{default}s.
+ * Returns true if we need to deparse a ColumnDef node within a typed
+ * table creation.
  */
-static ObjTree *
-deparse_ColumnDef_typed(Relation relation, List *dpcontext, ColumnDef *coldef)
+static bool
+deparse_ColDef_typed_needed(Relation relation, ColumnDef *coldef,
+							Form_pg_attribute *atFormOut, bool *notnull)
 {
-	ObjTree    *ret = NULL;
-	ObjTree    *tmp_obj;
 	Oid			relid = RelationGetRelid(relation);
 	HeapTuple	attrTup;
 	Form_pg_attribute attrForm;
@@ -1143,8 +986,12 @@ deparse_ColumnDef_typed(Relation relation, List *dpcontext, ColumnDef *coldef)
 	if (!HeapTupleIsValid(attrTup))
 		elog(ERROR, "could not find cache entry for column \"%s\" of relation %u",
 			 coldef->colname, relid);
+
 	attrForm = (Form_pg_attribute) GETSTRUCT(attrTup);
 
+	if (atFormOut)
+		*atFormOut = attrForm;
+
 	get_atttypetypmodcoll(relid, attrForm->attnum,
 						  &typid, &typmod, &typcollation);
 
@@ -1165,203 +1012,223 @@ deparse_ColumnDef_typed(Relation relation, List *dpcontext, ColumnDef *coldef)
 		}
 	}
 
+	if (notnull)
+		*notnull = saw_notnull;
+
 	if (!saw_notnull && !attrForm->atthasdef)
 	{
 		ReleaseSysCache(attrTup);
-		return NULL;
+		return false;
 	}
 
-	tmp_obj = new_objtree("DEFAULT");
-	if (attrForm->atthasdef)
-	{
-		char	   *defstr;
-
-		defstr = RelationGetColumnDefault(relation, attrForm->attnum,
-										  dpcontext, NULL);
-
-		append_string_object(tmp_obj, "%{default}s", "default", defstr);
-	}
-	else
-		append_not_present(tmp_obj, "%{default}s");
-
-	ret = new_objtree_VA("%{name}I WITH OPTIONS %{not_null}s %{default}s", 4,
-						 "type", ObjTypeString, "column",
-						 "name", ObjTypeString, coldef->colname,
-						 "not_null", ObjTypeString,
-						 saw_notnull ? "NOT NULL" : "",
-						 "default", ObjTypeObject, tmp_obj);
-
-	/* Generated columns are not supported on typed tables, so we are done */
-
 	ReleaseSysCache(attrTup);
-
-	return ret;
+	return true;
 }
 
 /*
- * Deparse the definition of column identity.
+ * Deparse a ColumnDef node within a typed table creation. This is simpler
+ * than the regular case, because we don't have to emit the type declaration,
+ * collation, or default. Here we only return something if the column is being
+ * declared NOT NULL.
+ *
+ * As in deparse_ColumnDef, any other constraint is processed elsewhere.
  *
  * Verbose syntax
- * SET GENERATED %{option}s %{identity_type}s %{seq_definition: }s
- * 	OR
- * GENERATED %{option}s AS IDENTITY %{identity_type}s ( %{seq_definition: }s )
+ * %{name}I WITH OPTIONS %{not_null}s %{default}s.
  */
-static ObjTree *
-deparse_ColumnIdentity(Oid seqrelid, char identity, bool alter_table)
+static void
+deparse_ColumnDef_typed_toJsonb(JsonbParseState *state, Relation relation,
+								List *dpcontext, ColumnDef *coldef)
 {
-	ObjTree    *ret;
-	ObjTree    *ident_obj;
-	List	   *elems = NIL;
-	Form_pg_sequence seqform;
-	Sequence_values *seqvalues;
-	char	   *identfmt;
-	char	   *objfmt;
+	char	   *fmtStr = "%{name}I WITH OPTIONS %{not_null}s %{default}s";
+	bool		needed;
+	Form_pg_attribute attrForm;
+	bool		saw_notnull;
 
-	if (alter_table)
-	{
-		identfmt = "SET GENERATED ";
-		objfmt = "%{option}s";
-	}
-	else
-	{
-		identfmt = "GENERATED ";
-		objfmt = "%{option}s AS IDENTITY";
-	}
+	needed = deparse_ColDef_typed_needed(relation, coldef, &attrForm, &saw_notnull);
+	if (!needed)
+		return;
 
-	ident_obj = new_objtree(identfmt);
-	if (identity == ATTRIBUTE_IDENTITY_ALWAYS)
-		append_string_object(ident_obj, objfmt, "option", "ALWAYS");
-	else if (identity == ATTRIBUTE_IDENTITY_BY_DEFAULT)
-		append_string_object(ident_obj, objfmt, "option", "BY DEFAULT");
-	else
-		append_not_present(ident_obj, objfmt);
+	/* start making column object */
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+	fmt_to_jsonb_element(state, fmtStr);
 
-	ret = new_objtree_VA("%{identity_type}s", 1,
-						 "identity_type", ObjTypeObject, ident_obj);
+	/* Insert TYPE, NAME and NOT_NULL elements */
+	new_jsonb_VA(state, fmtStr, true, 3,
+				 "type", jbvString, "column",
+				 "name", jbvString, coldef->colname,
+				 "not_null", jbvString, saw_notnull ? "NOT NULL" : "");
 
-	seqvalues = get_sequence_values(seqrelid);
-	seqform = seqvalues->seqform;
+	/* DEFAULT: Push the key first */
+	insert_jsonb_key(state, "default");
 
-	/* Definition elements */
-	elems = lappend(elems, deparse_Seq_Cache(seqform, alter_table));
-	elems = lappend(elems, deparse_Seq_Cycle(seqform, alter_table));
-	elems = lappend(elems, deparse_Seq_IncrementBy(seqform, alter_table));
-	elems = lappend(elems, deparse_Seq_Minvalue(seqform, alter_table));
-	elems = lappend(elems, deparse_Seq_Maxvalue(seqform, alter_table));
-	elems = lappend(elems, deparse_Seq_Startwith(seqform, alter_table));
-	elems = lappend(elems, deparse_Seq_Restart(seqvalues->last_value));
-	/* We purposefully do not emit OWNED BY here */
+	/* DEFAULT: Push the value now */
+	if (attrForm->atthasdef)
+	{
+		char	   *defstr;
 
-	if (alter_table)
-		append_array_object(ret, "%{seq_definition: }s", elems);
+		defstr = RelationGetColumnDefault(relation, attrForm->attnum,
+										  dpcontext, NULL);
+		new_jsonb_VA(state, "DEFAULT %{default}s", false, 1,
+					 "default", jbvString, defstr);
+	}
 	else
-		append_array_object(ret, "( %{seq_definition: }s )", elems);
+		insert_not_present_obj(state, verbose ? "DEFAULT %{default}s" : "DEFAULT");
 
-	return ret;
+	/* mark the end of column object */
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	/* Generated columns are not supported on typed tables, so we are done */
 }
 
 /*
- * ... ALTER COLUMN ... SET/RESET (...)
+ * Subroutine for CREATE TABLE deparsing.
  *
- * Verbose syntax
- * ALTER COLUMN %{column}I RESET|SET (%{options:, }s)
+ * Returns true if at-least one local column is present
+ * (i.e. not inherited).
  */
-static ObjTree *
-deparse_ColumnSetOptions(AlterTableCmd *subcmd)
+static bool
+table_elem_present_col(Relation relation, List *tableElements, bool typed)
 {
-	List	   *sets = NIL;
-	ListCell   *cell;
-	ObjTree    *ret;
-	bool		is_reset = subcmd->subtype == AT_ResetOptions;
-
-	ret = new_objtree_VA("ALTER COLUMN %{column}I %{option}s", 2,
-						 "column", ObjTypeString, subcmd->name,
-						 "option", ObjTypeString, is_reset ? "RESET" : "SET");
+	ListCell   *lc;
 
-	foreach(cell, (List *) subcmd->def)
+	foreach(lc, tableElements)
 	{
-		DefElem    *elem;
-		ObjTree    *set;
+		Node	   *elt = (Node *) lfirst(lc);
 
-		elem = (DefElem *) lfirst(cell);
-		set = deparse_DefElem(elem, is_reset);
-		sets = lappend(sets, new_object_object(set));
+		switch (nodeTag(elt))
+		{
+			case T_ColumnDef:
+				{
+					if (typed)
+					{
+						if (deparse_ColDef_typed_needed(relation,
+														((ColumnDef *) elt), NULL, NULL))
+							return true;
+					}
+					else
+					{
+						if (((ColumnDef *) elt)->is_local)
+							return true;
+					}
+				}
+				break;
+			case T_Constraint:
+				break;
+			default:
+				elog(ERROR, "invalid node type %d", nodeTag(elt));
+		}
 	}
 
-	Assert(sets);
-	append_array_object(ret, "(%{options:, }s)", sets);
-
-	return ret;
+	return false;
 }
 
 /*
- * Deparse SET/RESET clause as used by ALTER TABLE ... SET/RESET (...)
+ * Subroutine for CREATE TABLE deparsing.
  *
- * Verbose syntax
- * RESET|SET (%{options:, }s)
+ * Returns true if at-least one constraint is present.
+ *
+ * Note that CONSTRAINT_FOREIGN constraints are always ignored.
  */
-static ObjTree *
-deparse_RelSetOptions(AlterTableCmd *subcmd)
+static bool
+table_elem_present_const(Oid relationId)
 {
-	List	   *sets = NIL;
-	ListCell   *cell;
-	bool		is_reset = subcmd->subtype == AT_ResetRelOptions;
+	Relation	conRel;
+	ScanKeyData key;
+	SysScanDesc scan;
+	HeapTuple	tuple;
+	bool		bconst = false;
 
-	foreach(cell, (List *) subcmd->def)
+	Assert(OidIsValid(relationId));
+
+	/*
+	 * Scan pg_constraint to fetch all constraints linked to the given
+	 * relation.
+	 */
+	conRel = table_open(ConstraintRelationId, AccessShareLock);
+	ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber,
+				F_OIDEQ, ObjectIdGetDatum(relationId));
+	scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &key);
+
+	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
 	{
-		DefElem    *elem;
-		ObjTree    *set;
+		Form_pg_constraint constrForm;
 
-		elem = (DefElem *) lfirst(cell);
-		set = deparse_DefElem(elem, is_reset);
-		sets = lappend(sets, new_object_object(set));
+		constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
+
+		switch (constrForm->contype)
+		{
+			case CONSTRAINT_CHECK:
+			case CONSTRAINT_PRIMARY:
+			case CONSTRAINT_UNIQUE:
+			case CONSTRAINT_EXCLUSION:
+				bconst = true;
+				break;
+			case CONSTRAINT_FOREIGN:
+				continue;		/* not here */
+			default:
+				elog(ERROR, "unrecognized constraint type");
+		}
 	}
 
-	Assert(sets);
+	systable_endscan(scan);
+	table_close(conRel, AccessShareLock);
 
-	return new_objtree_VA("%{set_reset}s (%{options:, }s)", 2,
-						  "set_reset", ObjTypeString, is_reset ? "RESET" : "SET",
-						  "options", ObjTypeArray, sets);
+	return bconst;
 }
 
 /*
- * Deparse DefElems, as used e.g. by ALTER COLUMN ... SET, into a list of SET
- * (...)  or RESET (...) contents.
+ * Subroutine for CREATE TABLE deparsing.
  *
- * Verbose syntax
- * %{label}s = %{value}L
+ * Deal with all the table elements (columns and constraints).
+ *
+ * Note we ignore constraints in the parse node here; they are extracted from
+ * system catalogs instead.
  */
-static ObjTree *
-deparse_DefElem(DefElem *elem, bool is_reset)
-{
-	ObjTree    *ret;
-	ObjTree    *optname = new_objtree("");
-
-	if (elem->defnamespace != NULL)
-		append_string_object(optname, "%{schema}I.", "schema",
-							 elem->defnamespace);
-
-	append_string_object(optname, "%{label}I", "label", elem->defname);
 
-	ret = new_objtree_VA("%{label}s", 1,
-						 "label", ObjTypeObject, optname);
+static void
+deparse_TableElems_ToJsonb(JsonbParseState *state, Relation relation,
+						   List *tableElements, List *dpcontext,
+						   bool typed, bool composite)
+{
+	ListCell   *lc;
 
-	if (!is_reset)
-		append_string_object(ret, "= %{value}L", "value",
-							 elem->arg ? defGetString(elem) :
-							 defGetBoolean(elem) ? "TRUE" : "FALSE");
+	foreach(lc, tableElements)
+	{
+		Node	   *elt = (Node *) lfirst(lc);
 
-	return ret;
+		switch (nodeTag(elt))
+		{
+			case T_ColumnDef:
+				{
+					if (typed)
+						deparse_ColumnDef_typed_toJsonb(state, relation, dpcontext,
+														(ColumnDef *) elt);
+					else
+						deparse_ColumnDef_toJsonb(state, relation, dpcontext,
+												  composite, (ColumnDef *) elt,
+												  false, NULL);
+				}
+				break;
+			case T_Constraint:
+				break;
+			default:
+				elog(ERROR, "invalid node type %d", nodeTag(elt));
+		}
+	}
 }
 
 /*
+ * Subroutine for CREATE TABLE deparsing.
+ *
  * Deparse the INHERITS relations.
  *
  * Given a table OID, return a schema-qualified table list representing
  * the parent tables.
  */
 static List *
-deparse_InhRelations(Oid objectId)
+deparse_InhRels_ToJsonb(Oid objectId)
 {
 	List	   *parents = NIL;
 	Relation	inhRel;
@@ -1381,12 +1248,9 @@ deparse_InhRelations(Oid objectId)
 
 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
 	{
-		ObjTree    *parent;
 		Form_pg_inherits formInh = (Form_pg_inherits) GETSTRUCT(tuple);
 
-		parent = new_objtree_for_qualname_id(RelationRelationId,
-											 formInh->inhparent);
-		parents = lappend(parents, new_object_object(parent));
+		parents = lappend_oid(parents, formInh->inhparent);
 	}
 
 	systable_endscan(scan);
@@ -1396,445 +1260,333 @@ 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");
-	switch (option)
-	{
-		case ONCOMMIT_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}s",
-								 "on_commit_value", "DELETE ROWS");
-			break;
-
-		case ONCOMMIT_PRESERVE_ROWS:
-			append_string_object(ret, "%{on_commit_value}s",
-								 "on_commit_value", "PRESERVE ROWS");
-			break;
-
-		case ONCOMMIT_NOOP:
-			append_not_present(ret, "%{on_commit_value}s");
-			break;
-	}
-
-	return ret;
-}
-
-/*
- * 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)
-{
-	ObjTree    *ret;
-	char	   *fmt;
-
-	fmt = alter_table ? "SET CACHE %{value}s" : "CACHE %{value}s";
-
-	ret = new_objtree_VA(fmt, 2,
-						 "clause", ObjTypeString, "cache",
-						 "value", ObjTypeString,
-						 psprintf(INT64_FORMAT, seqdata->seqcache));
-
-	return new_object_object(ret);
-}
-
-/*
- * Deparse the sequence CYCLE option.
+ * Subroutine for CREATE TABLE deparsing.
  *
- * Verbose syntax
- * SET %{no}s CYCLE
- * OR
- * %{no}s CYCLE
- */
-static inline ObjElem *
-deparse_Seq_Cycle(Form_pg_sequence seqdata, bool alter_table)
-{
-	ObjTree    *ret;
-	char	   *fmt;
-
-	fmt = alter_table ? "SET %{no}s CYCLE" : "%{no}s CYCLE";
-
-	ret = new_objtree_VA(fmt, 2,
-						 "clause", ObjTypeString, "cycle",
-						 "no", ObjTypeString,
-						 seqdata->seqcycle ? "" : "NO");
-
-	return new_object_object(ret);
-}
-
-/*
- * Deparse the sequence INCREMENT BY option.
+ * Given a table OID, obtain its constraints and append them to the given
+ * JsonbParseState.
  *
- * 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)
-{
-	ObjTree    *ret;
-	char	   *fmt;
-
-	fmt = alter_table ? "SET INCREMENT BY %{value}s" : "INCREMENT BY %{value}s";
-
-	ret = new_objtree_VA(fmt, 2,
-						 "clause", ObjTypeString, "seqincrement",
-						 "value", ObjTypeString,
-						 psprintf(INT64_FORMAT, seqdata->seqincrement));
-
-	return new_object_object(ret);
-}
-
-/*
- * Deparse the sequence MAXVALUE option.
+ * This works for typed tables, regular tables.
  *
- * Verbose syntax
- * SET MAXVALUE %{value}s
- * OR
- * MAXVALUE %{value}s
+ * Note that CONSTRAINT_FOREIGN constraints are always ignored.
  */
-static inline ObjElem *
-deparse_Seq_Maxvalue(Form_pg_sequence seqdata, bool alter_table)
+static void
+deparse_Constraints_ToJsonb(JsonbParseState *state, Oid relationId)
 {
-	ObjTree    *ret;
-	char	   *fmt;
-
-	fmt = alter_table ? "SET MAXVALUE %{value}s" : "MAXVALUE %{value}s";
-
-	ret = new_objtree_VA(fmt, 2,
-						 "clause", ObjTypeString, "maxvalue",
-						 "value", ObjTypeString,
-						 psprintf(INT64_FORMAT, seqdata->seqmax));
+	Relation	conRel;
+	ScanKeyData key;
+	SysScanDesc scan;
+	HeapTuple	tuple;
 
-	return new_object_object(ret);
-}
+	Assert(OidIsValid(relationId));
 
-/*
- * 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)
-{
-	ObjTree    *ret;
-	char	   *fmt;
+	/*
+	 * Scan pg_constraint to fetch all constraints linked to the given
+	 * relation.
+	 */
+	conRel = table_open(ConstraintRelationId, AccessShareLock);
+	ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber,
+				F_OIDEQ, ObjectIdGetDatum(relationId));
+	scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true,
+							  NULL, 1, &key);
 
-	fmt = alter_table ? "SET MINVALUE %{value}s" : "MINVALUE %{value}s";
+	/*
+	 * For each constraint, add a node to the list of table elements.  In
+	 * these nodes we include not only the printable information ("fmt"), but
+	 * also separate attributes to indicate the type of constraint, for
+	 * automatic processing.
+	 */
+	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+	{
+		Form_pg_constraint constrForm;
+		char	   *contype;
+		StringInfoData fmtStr;
 
-	ret = new_objtree_VA(fmt, 2,
-						 "clause", ObjTypeString, "minvalue",
-						 "value", ObjTypeString,
-						 psprintf(INT64_FORMAT, seqdata->seqmin));
+		constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
 
-	return new_object_object(ret);
-}
+		switch (constrForm->contype)
+		{
+			case CONSTRAINT_CHECK:
+				contype = "check";
+				break;
+			case CONSTRAINT_FOREIGN:
+				continue;		/* not here */
+			case CONSTRAINT_PRIMARY:
+				contype = "primary key";
+				break;
+			case CONSTRAINT_UNIQUE:
+				contype = "unique";
+				break;
+			case CONSTRAINT_EXCLUSION:
+				contype = "exclusion";
+				break;
+			default:
+				elog(ERROR, "unrecognized constraint type");
+		}
 
-/*
- * Deparse the sequence OWNED BY command.
- *
- * Verbose syntax
- * OWNED BY %{owner}D
- */
-static ObjElem *
-deparse_Seq_OwnedBy(Oid sequenceId, bool alter_table)
-{
-	ObjTree    *ret = NULL;
-	Relation	depRel;
-	SysScanDesc scan;
-	ScanKeyData keys[3];
-	HeapTuple	tuple;
+		/*
+		 * "type" and "contype" are not part of the printable output, but are
+		 * useful to programmatically distinguish these from columns and among
+		 * different constraint types.
+		 *
+		 * XXX it might be useful to also list the column names in a PK, etc.
+		 */
+		initStringInfo(&fmtStr);
+		appendStringInfoString(&fmtStr, "CONSTRAINT %{name}I %{definition}s");
+		pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
-	depRel = table_open(DependRelationId, AccessShareLock);
-	ScanKeyInit(&keys[0],
-				Anum_pg_depend_classid,
-				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(RelationRelationId));
-	ScanKeyInit(&keys[1],
-				Anum_pg_depend_objid,
-				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(sequenceId));
-	ScanKeyInit(&keys[2],
-				Anum_pg_depend_objsubid,
-				BTEqualStrategyNumber, F_INT4EQ,
-				Int32GetDatum(0));
+		new_jsonb_VA(state, NULL, true, 4,
+					 "type", jbvString, "constraint",
+					 "contype", jbvString, contype,
+					 "name", jbvString, NameStr(constrForm->conname),
+					 "definition", jbvString,
+					 pg_get_constraintdef_string(constrForm->oid));
 
-	scan = systable_beginscan(depRel, DependDependerIndexId, true,
-							  NULL, 3, keys);
-	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
-	{
-		Oid			ownerId;
-		Form_pg_depend depform;
-		ObjTree    *tmp_obj;
-		char	   *colname;
+		if (constrForm->conindid &&
+			(constrForm->contype == CONSTRAINT_PRIMARY ||
+			 constrForm->contype == CONSTRAINT_UNIQUE ||
+			 constrForm->contype == CONSTRAINT_EXCLUSION))
+		{
+			Oid			tblspc = get_rel_tablespace(constrForm->conindid);
 
-		depform = (Form_pg_depend) GETSTRUCT(tuple);
+			if (OidIsValid(tblspc))
+			{
+				char	   *tblspcname = get_tablespace_name(tblspc);
 
-		/* Only consider AUTO dependencies on pg_class */
-		if (depform->deptype != DEPENDENCY_AUTO)
-			continue;
-		if (depform->refclassid != RelationRelationId)
-			continue;
-		if (depform->refobjsubid <= 0)
-			continue;
+				if (!tblspcname)
+					elog(ERROR, "cache lookup failed for tablespace %u", tblspc);
 
-		ownerId = depform->refobjid;
-		colname = get_attname(ownerId, depform->refobjsubid, false);
-		if (colname == NULL)
-			continue;
+				appendStringInfoString(&fmtStr, " USING INDEX TABLESPACE %{tblspc}s");
+				new_jsonb_VA(state, NULL, true, 1,
+							 "tblspc", jbvString, tblspcname);
+			}
+		}
+
+		/* We have full fmt by now, so add jsonb element for that */
+		fmt_to_jsonb_element(state, fmtStr.data);
+
+		pfree(fmtStr.data);
 
-		tmp_obj = new_objtree_for_qualname_id(RelationRelationId, ownerId);
-		append_string_object(tmp_obj, "attrname", "attrname", colname);
-		ret = new_objtree_VA("OWNED BY %{owner}D", 2,
-							 "clause", ObjTypeString, "owned",
-							 "owner", ObjTypeObject, tmp_obj);
+		pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 	}
 
 	systable_endscan(scan);
-	relation_close(depRel, AccessShareLock);
-
-	/*
-	 * If there's no owner column, emit an empty OWNED BY element, set up so
-	 * that it won't print anything.
-	 */
-	if (!ret)
-		/* XXX this shouldn't happen ... */
-		ret = new_objtree_VA("OWNED BY %{owner}D", 3,
-							 "clause", ObjTypeString, "owned",
-							 "owner", ObjTypeNull,
-							 "present", ObjTypeBool, false);
-
-	return new_object_object(ret);
+	table_close(conRel, AccessShareLock);
 }
 
 /*
- * Deparse the sequence RESTART option.
+ * Deparse DefElems, as used by Create Table
  *
  * Verbose syntax
- * RESTART %{value}s
+ * %{label}s = %{value}L
  */
-static inline ObjElem *
-deparse_Seq_Restart(int64 last_value)
+static void
+deparse_DefElem_ToJsonb(JsonbParseState *state, DefElem *elem, bool is_reset)
 {
-	ObjTree    *ret;
+	StringInfoData fmtStr;
+	StringInfoData labelfmt;
 
-	ret = new_objtree_VA("RESTART %{value}s", 2,
-						 "clause", ObjTypeString, "restart",
-						 "value", ObjTypeString,
-						 psprintf(INT64_FORMAT, last_value));
+	initStringInfo(&fmtStr);
 
-	return new_object_object(ret);
-}
+	appendStringInfoString(&fmtStr, "%{label}s");
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
-/*
- * Deparse the sequence AS option.
- *
- * Verbose syntax
- * AS %{seqtype}T
- */
-static inline ObjElem *
-deparse_Seq_As(Form_pg_sequence seqdata)
-{
-	ObjTree    *ret;
+	/* LABEL */
+	initStringInfo(&labelfmt);
 
-	ret = new_objtree("AS");
-	if (OidIsValid(seqdata->seqtypid))
-		append_object_object(ret, "%{seqtype}T",
-							 new_objtree_for_type(seqdata->seqtypid, -1));
-	else
-		append_not_present(ret, "%{seqtype}T");
+	insert_jsonb_key(state, "label");
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	if (elem->defnamespace != NULL)
+	{
+		appendStringInfoString(&labelfmt, "%{schema}I.");
+		new_jsonb_VA(state, NULL, true, 1, "schema", jbvString, elem->defnamespace);
+	}
+
+	appendStringInfoString(&labelfmt, "%{label}I");
+	new_jsonb_VA(state, NULL, true, 1, "label", jbvString, elem->defname);
+
+	fmt_to_jsonb_element(state, labelfmt.data);
+	pfree(labelfmt.data);
+
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	/* VALUE */
+	if (!is_reset)
+	{
+		appendStringInfoString(&fmtStr, " = %{value}L");
+		new_jsonb_VA(state, NULL, true, 1, "value", jbvString,
+					 elem->arg ? defGetString(elem) :
+					 defGetBoolean(elem) ? "true" : "false");
+	}
 
-	return new_object_object(ret);
+	fmt_to_jsonb_element(state, fmtStr.data);
+	pfree(fmtStr.data);
+
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 }
 
 /*
- * Deparse the sequence START WITH option.
+ * Deparse SET/RESET as used by
+ * ALTER TABLE ... ALTER COLUMN ... SET/RESET (...)
  *
  * Verbose syntax
- * SET START WITH %{value}s
- * OR
- * START WITH %{value}s
+ * ALTER COLUMN %{column}I RESET|SET (%{options:, }s)
  */
-static inline ObjElem *
-deparse_Seq_Startwith(Form_pg_sequence seqdata, bool alter_table)
+static void
+deparse_ColumnSetOptions_ToJsonb(JsonbParseState *state, AlterTableCmd *subcmd)
 {
-	ObjTree    *ret;
-	char	   *fmt;
+	ListCell   *cell;
+	bool		is_reset = subcmd->subtype == AT_ResetOptions;
+	bool		elem_found PG_USED_FOR_ASSERTS_ONLY = false;
 
-	fmt = alter_table ? "SET START WITH %{value}s" : "START WITH %{value}s";
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+	new_jsonb_VA(state, "ALTER COLUMN %{column}I %{option}s (%{options:, }s)", true, 2,
+				 "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_ToJsonb(state, elem, is_reset);
+
+#ifdef USE_ASSERT_CHECKING
+		elem_found = true;
+#endif
+	}
+
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
 
-	ret = new_objtree_VA(fmt, 2,
-						 "clause", ObjTypeString, "start",
-						 "value", ObjTypeString,
-						 psprintf(INT64_FORMAT, seqdata->seqstart));
+	Assert(elem_found);
 
-	return new_object_object(ret);
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 }
 
 /*
- * Subroutine for CREATE TABLE deparsing.
- *
- * Deal with all the table elements (columns and constraints).
+ * Deparse SET/RESET as used by ALTER TABLE ... SET/RESET (...)
  *
- * Note we ignore constraints in the parse node here; they are extracted from
- * system catalogs instead.
+ * Verbose syntax
+ * RESET|SET (%{options:, }s)
  */
-static List *
-deparse_TableElements(Relation relation, List *tableElements, List *dpcontext,
-					  bool typed, bool composite)
+static void
+deparse_RelSetOptions_toJsonb(JsonbParseState *state, AlterTableCmd *subcmd)
 {
-	List	   *elements = NIL;
-	ListCell   *lc;
+	ListCell   *cell;
+	bool		is_reset = subcmd->subtype == AT_ResetRelOptions;
+	bool		elem_found PG_USED_FOR_ASSERTS_ONLY = false;
 
-	foreach(lc, tableElements)
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+	new_jsonb_VA(state, "%{set_reset}s (%{options:, }s)", true, 1,
+				 "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)
 	{
-		Node	   *elt = (Node *) lfirst(lc);
+		DefElem    *elem;
 
-		switch (nodeTag(elt))
-		{
-			case T_ColumnDef:
-				{
-					ObjTree    *tree;
-
-					tree = typed ?
-						deparse_ColumnDef_typed(relation, dpcontext,
-												(ColumnDef *) elt) :
-						deparse_ColumnDef(relation, dpcontext,
-										  composite, (ColumnDef *) elt,
-										  false, NULL);
-					if (tree != NULL)
-						elements = lappend(elements, new_object_object(tree));
-				}
-				break;
-			case T_Constraint:
-				break;
-			default:
-				elog(ERROR, "invalid node type %d", nodeTag(elt));
-		}
+		elem = (DefElem *) lfirst(cell);
+		deparse_DefElem_ToJsonb(state, elem, is_reset);
+
+#ifdef USE_ASSERT_CHECKING
+		elem_found = true;
+#endif
 	}
 
-	return elements;
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+	Assert(elem_found);
+
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 }
 
 /*
- * Deparse a CreateSeqStmt.
- *
- * Given a sequence OID and the parse tree that created it, return an ObjTree
- * representing the creation command.
- *
- * Note: We need to deparse the CREATE SEQUENCE command for the TABLE
- * 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
+ * Deparse WITH clause, as used by Create Table.
  */
-static ObjTree *
-deparse_CreateSeqStmt(Oid objectId, Node *parsetree)
+static void
+deparse_withObj_ToJsonb(JsonbParseState *state, char *fmt, CreateStmt *node)
 {
-	ObjTree    *ret;
-	Relation	relation;
-	List	   *elems = NIL;
-	Form_pg_sequence seqform;
-	Sequence_values *seqvalues;
-	CreateSeqStmt *createSeqStmt = (CreateSeqStmt *) parsetree;
-
-	/*
-	 * Only support sequence for IDENTITY COLUMN output separately (via CREATE
-	 * TABLE or ALTER TABLE). Otherwise, return empty here.
-	 */
-	if (createSeqStmt->for_identity)
-		return NULL;
-
-	seqvalues = get_sequence_values(objectId);
-	seqform = seqvalues->seqform;
+	ListCell   *cell;
 
-	/* Definition elements */
-	elems = lappend(elems, deparse_Seq_Cache(seqform, false));
-	elems = lappend(elems, deparse_Seq_Cycle(seqform, false));
-	elems = lappend(elems, deparse_Seq_IncrementBy(seqform, false));
-	elems = lappend(elems, deparse_Seq_Minvalue(seqform, false));
-	elems = lappend(elems, deparse_Seq_Maxvalue(seqform, false));
-	elems = lappend(elems, deparse_Seq_Startwith(seqform, false));
-	elems = lappend(elems, deparse_Seq_Restart(seqvalues->last_value));
-	elems = lappend(elems, deparse_Seq_As(seqform));
+	/* with_clause's value is an object */
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+	fmt_to_jsonb_element(state, fmt);
 
-	/* We purposefully do not emit OWNED BY here */
+	/* WITH */
+	insert_jsonb_key(state, "with");
+	pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
 
-	relation = relation_open(objectId, AccessShareLock);
+	/* add elements to array */
+	foreach(cell, node->options)
+	{
+		DefElem    *opt = (DefElem *) lfirst(cell);
 
-	ret = new_objtree_VA("CREATE %{persistence}s SEQUENCE %{if_not_exists}s %{identity}D %{definition: }s", 4,
-						 "persistence", ObjTypeString,
-						 get_persistence_str(relation->rd_rel->relpersistence),
-						 "if_not_exists", ObjTypeString,
-						 createSeqStmt->if_not_exists ? "IF NOT EXISTS" : "",
-						 "identity", ObjTypeObject,
-						 new_objtree_for_qualname(relation->rd_rel->relnamespace,
-												  RelationGetRelationName(relation)),
-						 "definition", ObjTypeArray, elems);
+		deparse_DefElem_ToJsonb(state, opt, false);
+	}
 
-	relation_close(relation, AccessShareLock);
+	/* with's array end */
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
 
-	return ret;
+	/* with_clause's object end */
+	pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 }
 
 /*
  * Deparse a CreateStmt (CREATE TABLE).
  *
- * Given a table OID and the parse tree that created it, return an ObjTree
+ * Given a table OID and the parse tree that created it, return JsonbValue
  * 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
+ * %{tablespace}s
  */
-static ObjTree *
-deparse_CreateStmt(Oid objectId, Node *parsetree)
+static Jsonb *
+deparse_CreateStmt(Oid objectId, Node *parsetree, char *owner)
 {
 	CreateStmt *node = (CreateStmt *) parsetree;
 	Relation	relation = relation_open(objectId, AccessShareLock);
+	Oid			nspid = relation->rd_rel->relnamespace;
+	char	   *relname = RelationGetRelationName(relation);
 	List	   *dpcontext;
-	ObjTree    *ret;
-	ObjTree    *tmp_obj;
-	List	   *list = NIL;
-	ListCell   *cell;
+	StringInfoData fmtStr;
+	JsonbParseState *state = NULL;
+	bool		telems = false;
+	JsonbValue *value;
 
-	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,
-						 node->if_not_exists ? "IF NOT EXISTS" : "",
-						 "identity", ObjTypeObject,
-						 new_objtree_for_qualname(relation->rd_rel->relnamespace,
-												  RelationGetRelationName(relation)));
+	initStringInfo(&fmtStr);
 
 	dpcontext = deparse_context_for(RelationGetRelationName(relation),
 									objectId);
 
+	appendStringInfoString(&fmtStr, "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D");
+
+	/* mark the start of ROOT object */
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	/* create owner jsonb element */
+	role_to_jsonb_element(state, owner);
+
+	/* PERSISTENCE and IF NOT EXISTS */
+	new_jsonb_VA(state, NULL, true, 2,
+				 "persistence", jbvString, get_persistence_str(relation->rd_rel->relpersistence),
+				 "if_not_exists", jbvString, node->if_not_exists ? "IF NOT EXISTS" : "");
+
+	/* IDENTITY creation */
+	insert_identity_object(state, nspid, relname);
+
 	/*
+	 * TABLE-ELEMENTS array creation
+	 *
 	 * Typed tables and partitions use a slightly different format string: we
 	 * must not put table_elements with parents directly in the fmt string,
 	 * because if there are no options the parentheses must not be emitted;
@@ -1842,8 +1594,6 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 	 */
 	if (node->ofTypename || node->partbound)
 	{
-		List	   *tableelts = NIL;
-
 		/*
 		 * We can't put table elements directly in the fmt string as an array
 		 * surrounded by parentheses here, because an empty clause would cause
@@ -1852,41 +1602,67 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 		 */
 		if (node->ofTypename)
 		{
-			tmp_obj = new_objtree_for_type(relation->rd_rel->reloftype, -1);
-			append_object_object(ret, "OF %{of_type}T", tmp_obj);
+			appendStringInfoString(&fmtStr, " OF %{of_type}T");
+
+			/* Push the key first */
+			insert_jsonb_key(state, "of_type");
+
+			/* Push the value */
+			new_jsonb_for_type(state, relation->rd_rel->reloftype, -1);
 		}
 		else
 		{
 			List	   *parents;
-			ObjElem    *elem;
+			Oid			objid;
 
-			parents = deparse_InhRelations(objectId);
-			elem = (ObjElem *) linitial(parents);
+			appendStringInfoString(&fmtStr, " PARTITION OF %{parent_identity}D");
 
-			Assert(list_length(parents) == 1);
+			parents = deparse_InhRels_ToJsonb(objectId);
+			objid = linitial_oid(parents);
 
-			append_format_string(ret, "PARTITION OF");
+			Assert(list_length(parents) == 1);
 
-			append_object_object(ret, "%{parent_identity}D",
-								 elem->value.object);
+			new_jsonb_for_qualname_id(state, RelationRelationId,
+									  objid, "parent_identity", false);
 		}
 
-		tableelts = deparse_TableElements(relation, node->tableElts, dpcontext,
-										  true, /* typed table */
-										  false);	/* not composite */
-		tableelts = obtainConstraints(tableelts, objectId);
+		telems = table_elem_present_col(relation, node->tableElts, true);
+		if (!telems)
+			telems = table_elem_present_const(objectId);
 
-		tmp_obj = new_objtree("");
-		if (tableelts)
-			append_array_object(tmp_obj, "(%{elements:, }s)", tableelts);
-		else
-			append_not_present(tmp_obj, "(%{elements:, }s)");
+		appendStringInfoString(&fmtStr, " %{table_elements}s");
+
+		if (telems)
+		{
+			insert_jsonb_key(state, "table_elements");
+			pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+			fmt_to_jsonb_element(state, "(%{elements:, }s)");
+
+			insert_jsonb_key(state, "elements");
+			pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+			deparse_TableElems_ToJsonb(state, relation, node->tableElts, dpcontext,
+									   true,	/* typed table */
+									   false);	/* not composite */
+
+			deparse_Constraints_ToJsonb(state, objectId);
+
+			/* end of elements array */
+			pushJsonbValue(&state, WJB_END_ARRAY, NULL);
 
-		append_object_object(ret, "%{table_elements}s", tmp_obj);
+			/* end of table_elements object */
+			pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+		}
+		else
+		{
+			insert_jsonb_key(state, "table_elements");
+			insert_not_present_obj(state, "(%{elements:, }s)");
+		}
 	}
 	else
 	{
-		List	   *tableelts = NIL;
+		List	   *inhrelations;
 
 		/*
 		 * There is no need to process LIKE clauses separately; they have
@@ -1899,15 +1675,34 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
 		 * get constraints we rely on pg_constraint, because the parse node
 		 * might be missing some things such as the name of the constraints.
 		 */
-		tableelts = deparse_TableElements(relation, node->tableElts, dpcontext,
-										  false,	/* not typed table */
-										  false);	/* not composite */
-		tableelts = obtainConstraints(tableelts, objectId);
+		telems = table_elem_present_col(relation, node->tableElts, false);
+		if (!telems)
+			telems = table_elem_present_const(objectId);
+
+		if (telems)
+		{
+			appendStringInfoString(&fmtStr, " (%{table_elements:, }s)");
+			insert_jsonb_key(state, "table_elements");
+
+			/*
+			 * It will be of array type for multi-columns table, so lets begin
+			 * an arrayobject. deparse_TableElems_ToJsonb() will add elements
+			 * to it.
+			 */
+			pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+			deparse_TableElems_ToJsonb(state, relation, node->tableElts, dpcontext,
+									   false,	/* not typed table */
+									   false);	/* not composite */
+			deparse_Constraints_ToJsonb(state, objectId);
 
-		if (tableelts)
-			append_array_object(ret, "(%{table_elements:, }s)", tableelts);
+			pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+		}
 		else
-			append_format_string(ret, "()");
+			appendStringInfoString(&fmtStr, " ()");
+
+		appendStringInfoString(&fmtStr, " %{inherits}s");
+		insert_jsonb_key(state, "inherits");
 
 		/*
 		 * Add inheritance specification.  We cannot simply scan the list of
@@ -1916,82 +1711,88 @@ 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");
 		if (node->inhRelations != NIL)
-			append_array_object(tmp_obj, "(%{parents:, }D)",
-								deparse_InhRelations(objectId));
-		else
-			append_not_present(tmp_obj, "(%{parents:, }D)");
+		{
+			pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
-		append_object_object(ret, "%{inherits}s", tmp_obj);
+			fmt_to_jsonb_element(state, "INHERITS (%{parents:, }D)");
+			inhrelations = deparse_InhRels_ToJsonb(objectId);
+
+			new_jsonbArray_for_qualname_id(state, "parents", inhrelations);
+			pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+		}
+		else
+			insert_not_present_obj(state, verbose ? "INHERITS (%{parents:, }D)" : "INHERITS");
 	}
 
+
 	/* FOR VALUES clause */
 	if (node->partbound)
 	{
+		appendStringInfoString(&fmtStr, " %{partition_bound}s");
+
 		/*
 		 * Get pg_class.relpartbound. We cannot use partbound in the parsetree
 		 * directly as it's the original partbound expression which haven't
 		 * been transformed.
 		 */
-		append_string_object(ret, "%{partition_bound}s", "partition_bound",
-							 RelationGetPartitionBound(objectId));
+		new_jsonb_VA(state, NULL, true, 1,
+					 "partition_bound", jbvString, RelationGetPartitionBound(objectId));
 	}
 
 	/* PARTITION BY clause */
-	tmp_obj = new_objtree("PARTITION BY");
+	appendStringInfoString(&fmtStr, " %{partition_by}s");
+	insert_jsonb_key(state, "partition_by");
+
 	if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-		append_string_object(tmp_obj, "%{definition}s", "definition",
-							 pg_get_partkeydef_string(objectId));
+		new_jsonb_VA(state, "PARTITION BY %{definition}s", false, 1,
+					 "definition", jbvString, pg_get_partkeydef_string(objectId));
 	else
-		append_not_present(tmp_obj, "%{definition}s");
+		insert_not_present_obj(state, verbose ? "PARTITION BY %{definition}s" : "PARTITION BY");
 
-	append_object_object(ret, "%{partition_by}s", tmp_obj);
 
 	/* USING clause */
-	tmp_obj = new_objtree("USING");
+	appendStringInfoString(&fmtStr, " %{access_method}s");
+	insert_jsonb_key(state, "access_method");
+
 	if (node->accessMethod)
-		append_string_object(tmp_obj, "%{access_method}I", "access_method",
-							 node->accessMethod);
+		new_jsonb_VA(state, "USING %{access_method}I", false, 1,
+					 "access_method", jbvString, node->accessMethod);
 	else
-		append_not_present(tmp_obj, "%{access_method}I");
-
-	append_object_object(ret, "%{access_method}s", tmp_obj);
+		insert_not_present_obj(state, verbose ? "USING %{access_method}I" : "USING");
 
 	/* WITH clause */
-	tmp_obj = new_objtree("WITH");
-
-	foreach(cell, node->options)
-	{
-		ObjTree    *tmp_obj2;
-		DefElem    *opt = (DefElem *) lfirst(cell);
-
-		tmp_obj2 = deparse_DefElem(opt, false);
-		list = lappend(list, new_object_object(tmp_obj2));
-	}
+	appendStringInfoString(&fmtStr, " %{with_clause}s");
+	insert_jsonb_key(state, "with_clause");
 
-	if (list)
-		append_array_object(tmp_obj, "(%{with:, }s)", list);
+	if (node->options)
+		deparse_withObj_ToJsonb(state, "WITH (%{with:, }s)", node);
 	else
-		append_not_present(tmp_obj, "(%{with:, }s)");
+		insert_not_present_obj(state, verbose ? "WITH (%{with:, }s)" : "WITH");
 
-	append_object_object(ret, "%{with_clause}s", tmp_obj);
+	/* TABLESPACE */
+	appendStringInfoString(&fmtStr, " %{tablespace}s");
+	insert_jsonb_key(state, "tablespace");
 
-	append_object_object(ret, "%{on_commit}s",
-						 deparse_OnCommitClause(node->oncommit));
-
-	tmp_obj = new_objtree("TABLESPACE");
+	/* Push the value now: object in this case */
 	if (node->tablespacename)
-		append_string_object(tmp_obj, "%{tablespace}I", "tablespace",
-							 node->tablespacename);
+		new_jsonb_VA(state, "TABLESPACE %{tablespace}I", false, 1,
+					 "tablespace", jbvString, node->tablespacename);
 	else
-		append_not_present(tmp_obj, "%{tablespace}I");
+		insert_not_present_obj(state, verbose ? "TABLESPACE %{tablespace}I" : "TABLESPACE");
 
-	append_object_object(ret, "%{tablespace}s", tmp_obj);
 
 	relation_close(relation, AccessShareLock);
 
-	return ret;
+	/* We have full fmt by now, so add jsonb element for that */
+	fmt_to_jsonb_element(state, fmtStr.data);
+
+	pfree(fmtStr.data);
+
+	/* Mark the end of ROOT object */
+	value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	return JsonbValueToJsonb(value);
 }
 
 /*
@@ -2000,51 +1801,110 @@ deparse_CreateStmt(Oid objectId, Node *parsetree)
  * deparse_CreateStmt do the actual work as we deparse the final CreateStmt for
  * CREATE TABLE AS command.
  */
-static ObjTree *
-deparse_CreateTableAsStmt(CollectedCommand *cmd)
+static Jsonb *
+deparse_CreateTableAsStmt(CollectedCommand *cmd, ddl_deparse_context * context)
 {
 	Oid			objectId;
 	Node	   *parsetree;
+	char	   *owner = context->include_owner ? cmd->role : NULL;
 
 	Assert(cmd->type == SCT_CreateTableAs);
 
 	parsetree = cmd->d.ctas.real_create;
 	objectId = cmd->d.ctas.address.objectId;
+	return deparse_CreateStmt(objectId, parsetree, owner);
+}
+
+/*
+ * Deparse a DropStmt (DROP TABLE).
+ *
+ * Given an object identity and the parse tree that created it, return
+ * jsonb string representing the drop command.
+ *
+ * Verbose syntax
+ * DROP %{objtype}s %{concurrently}s %{if_exists}s %{objidentity}s %{cascade}s
+ */
+char *
+deparse_drop_table(const char *objidentity, const char *objecttype,
+				   Node *parsetree)
+{
+	DropStmt   *node = (DropStmt *) parsetree;
+	StringInfoData fmtStr;
+	char	   *identity = (char *) objidentity;
+	JsonbValue *jsonbval;
+	Jsonb	   *jsonb;
+	StringInfoData str;
+	JsonbParseState *state = NULL;
+
+	initStringInfo(&str);
+	initStringInfo(&fmtStr);
+
+	/* Start constructing fmt string */
+	appendStringInfoString(&fmtStr, "DROP %{objtype}s %{concurrently}s %{if_exists}s %{objidentity}s");
+
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	new_jsonb_VA(state, NULL, true, 4,
+				 "objtype", jbvString, objecttype,
+				 "concurrently", jbvString, node->concurrent ? "CONCURRENTLY" : "",
+				 "if_exists", jbvString, node->missing_ok ? "IF EXISTS" : "",
+				 "objidentity", jbvString, identity);
+
+	if (node->behavior == DROP_CASCADE)
+	{
+		appendStringInfoString(&fmtStr, " %{cascade}s");
+		insert_jsonb_key(state, "cascade");
 
-	return deparse_CreateStmt(objectId, parsetree);
+		pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+		fmt_to_jsonb_element(state, "CASCADE");
+		pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+	}
+
+	/* We have full fmt by now, so add jsonb element for that */
+	fmt_to_jsonb_element(state, fmtStr.data);
+	pfree(fmtStr.data);
+
+	jsonbval = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	jsonb = JsonbValueToJsonb(jsonbval);
+	return JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
 }
 
 /*
- * Deparse all the collected subcommands and return an ObjTree representing the
- * alter command.
+ * Deparse all the collected subcommands and return jsonb string representing
+ * the alter command.
  *
  * Verbose syntax
  * ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s
  */
-static ObjTree *
-deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
+static Jsonb *
+deparse_AlterTableStmt(CollectedCommand *cmd, ddl_deparse_context * context)
 {
-	ObjTree    *ret;
-	ObjTree    *tmp_obj;
-	ObjTree    *tmp_obj2;
 	List	   *dpcontext;
 	Relation	rel;
-	List	   *subcmds = NIL;
 	ListCell   *cell;
 	const char *reltype;
 	Node	   *expr = NULL;
 	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));
+
+	Assert(IsA(stmt, AlterTableStmt) || IsA(stmt, AlterTableMoveAllStmt));
+
+	initStringInfo(&fmtStr);
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
 	/*
 	 * ALTER TABLE subcommands generated for TableLikeClause is processed in
 	 * the top level CREATE TABLE command; return empty here.
 	 */
-	if (stmt->table_like)
+	if (IsA(stmt, AlterTableStmt) && stmt->table_like)
 		return NULL;
 
 	rel = relation_open(relId, AccessShareLock);
@@ -2069,53 +1929,64 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 			elog(ERROR, "unexpected relkind %d", rel->rd_rel->relkind);
 	}
 
-	dpcontext = deparse_context_for(RelationGetRelationName(rel),
-									relId);
 
-	ret = new_objtree_VA("ALTER %{objtype}s %{only}s %{identity}D", 3,
-						 "objtype", ObjTypeString, reltype,
-						 "only", ObjTypeString,
-						 stmt->relation->inh ? "" : "ONLY",
-						 "identity", ObjTypeObject,
-						 new_objtree_for_qualname(rel->rd_rel->relnamespace,
-												  RelationGetRelationName(rel)));
+	dpcontext = deparse_context_for(RelationGetRelationName(rel), relId);
+
+	/* Start constructing fmt string */
+	appendStringInfoString(&fmtStr, "ALTER %{objtype}s %{only}s %{identity}D ");
+
+	new_jsonb_VA(state, NULL, true, 2,
+				 "objtype", jbvString, reltype,
+				 "only", jbvString, stmt->relation->inh ? "" : "ONLY");
+
+	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;
-		ObjTree    *tree;
 
 		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.
-		*/
+		/*
+		 * 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;
 
+		if (!subCmdArray)
+		{
+			insert_jsonb_key(state, "subcmds");
+			pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+			subCmdArray = true;
+		}
+
 		switch (subcmd->subtype)
 		{
 			case AT_AddColumn:
 				/* XXX need to set the "recurse" bit somewhere? */
 				Assert(IsA(subcmd->def, ColumnDef));
-				tree = deparse_ColumnDef(rel, dpcontext, false,
-										 (ColumnDef *) subcmd->def, true, &expr);
 
-				mark_function_volatile(context, expr);
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state,
+							 "ADD %{objtype}s %{if_not_exists}s %{definition}s", true, 3,
+							 "objtype", jbvString, "COLUMN",
+							 "type", jbvString, "add column",
+							 "if_not_exists", jbvString,
+							 subcmd->missing_ok ? "IF NOT EXISTS" : "");
 
-				tmp_obj = new_objtree_VA("ADD %{objtype}s %{if_not_exists}s %{definition}s", 4,
-										"objtype", ObjTypeString, "COLUMN",
-										"type", ObjTypeString, "add column",
-										"if_not_exists", ObjTypeString,
-										subcmd->missing_ok ? "IF NOT EXISTS" : "",
-										"definition", ObjTypeObject, tree);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				/* Push definition key-value pair */
+				insert_jsonb_key(state, "definition");
+				deparse_ColumnDef_toJsonb(state, rel, dpcontext,
+										  false, (ColumnDef *) subcmd->def,
+										  true, NULL);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				break;
 
 			case AT_AddIndexConstraint:
@@ -2137,24 +2008,21 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 					 * ADD CONSTRAINT %{name}I %{constraint_type}s USING INDEX
 					 * %index_name}I %{deferrable}s %{init_deferred}s
 					 */
-					tmp_obj = new_objtree_VA("ADD CONSTRAINT %{name}I %{constraint_type}s USING INDEX %{index_name}I %{deferrable}s %{init_deferred}s", 6,
-											"type", ObjTypeString, "add constraint using index",
-											"name", ObjTypeString, get_constraint_name(constrOid),
-											"constraint_type", ObjTypeString,
-											istmt->primary ? "PRIMARY KEY" : "UNIQUE",
-											"index_name", ObjTypeString,
-											RelationGetRelationName(idx),
-											"deferrable", ObjTypeString,
-											istmt->deferrable ? "DEFERRABLE" : "NOT DEFERRABLE",
-											"init_deferred", ObjTypeString,
-											istmt->initdeferred ? "INITIALLY DEFERRED" : "INITIALLY IMMEDIATE");
-
-					subcmds = lappend(subcmds, new_object_object(tmp_obj));
-
+					new_jsonb_VA(state,
+								 "ADD CONSTRAINT %{name}I %{constraint_type}s USING INDEX %{index_name}I %{deferrable}s %{init_deferred}s", false, 6,
+								 "type", jbvString, "add constraint using index",
+								 "name", jbvString, get_constraint_name(constrOid),
+								 "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");
 					relation_close(idx, AccessShareLock);
+					break;
 				}
-				break;
-
 			case AT_ReAddIndex:
 			case AT_ReAddConstraint:
 			case AT_ReAddDomainConstraint:
@@ -2162,161 +2030,127 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 			case AT_ReplaceRelOptions:
 			case AT_CheckNotNull:
 			case AT_ReAddStatistics:
-				/* Subtypes used for internal operations; nothing to do here */
-				break;
-
-			case AT_CookedColumnDefault:
-				{
-					Relation	attrrel;
-					HeapTuple	atttup;
-					Form_pg_attribute attStruct;
-
-					attrrel = table_open(AttributeRelationId, RowExclusiveLock);
-					atttup = SearchSysCacheCopy2(ATTNUM,
-												 ObjectIdGetDatum(RelationGetRelid(rel)),
-												 Int16GetDatum(subcmd->num));
-					if (!HeapTupleIsValid(atttup))
-						elog(ERROR, "cache lookup failed for attribute %d of relation with OID %u",
-							 subcmd->num, RelationGetRelid(rel));
-					attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
-
-					/*
-					 * Both default and generation expression not supported
-					 * together.
-					 */
-					if (!attStruct->attgenerated)
-						elog(WARNING, "unsupported alter table subtype %d",
-							 subcmd->subtype);
-
-					heap_freetuple(atttup);
-					table_close(attrrel, RowExclusiveLock);
-					break;
-				}
-
-			case AT_AddColumnToView:
-				/* CREATE OR REPLACE VIEW -- nothing to do here */
+				/* Subtypes used for internal operations; nothing to do here */
 				break;
 
 			case AT_ColumnDefault:
 				if (subcmd->def == NULL)
-					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I DROP DEFAULT", 2,
-											"type", ObjTypeString, "drop default",
-											"column", ObjTypeString, subcmd->name);
+					new_jsonb_VA(state,
+								 "ALTER COLUMN %{column}I DROP DEFAULT", false, 2,
+								 "type", jbvString, "drop default",
+								 "column", jbvString, subcmd->name);
 				else
 				{
 					List	   *dpcontext_rel;
 					HeapTuple	attrtup;
 					AttrNumber	attno;
 
-					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET DEFAULT", 2,
-											"type", ObjTypeString, "set default",
-											"column", ObjTypeString, subcmd->name);
-
 					dpcontext_rel = deparse_context_for(RelationGetRelationName(rel),
 														RelationGetRelid(rel));
 					attrtup = SearchSysCacheAttName(RelationGetRelid(rel), subcmd->name);
 					attno = ((Form_pg_attribute) GETSTRUCT(attrtup))->attnum;
-					append_string_object(tmp_obj, "%{definition}s", "definition",
-										 RelationGetColumnDefault(rel, attno,
-																  dpcontext_rel,
-																  NULL));
+
+					new_jsonb_VA(state,
+								 "ALTER COLUMN %{column}I SET DEFAULT %{definition}s", false, 3,
+								 "type", jbvString, "set default",
+								 "column", jbvString, subcmd->name,
+								 "definition", jbvString,
+								 RelationGetColumnDefault(rel, attno,
+														  dpcontext_rel,
+														  NULL));
 					ReleaseSysCache(attrtup);
 				}
 
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
 				break;
 
 			case AT_DropNotNull:
-				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I DROP NOT NULL", 2,
-										"type", ObjTypeString, "drop not null",
-										"column", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "ALTER COLUMN %{column}I DROP NOT NULL", false, 2,
+							 "type", jbvString, "drop not null",
+							 "column", jbvString, subcmd->name);
 				break;
 
 			case AT_ForceRowSecurity:
-				tmp_obj = new_objtree("FORCE ROW LEVEL SECURITY");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "FORCE ROW LEVEL SECURITY", false, 0);
 				break;
 
 			case AT_NoForceRowSecurity:
-				tmp_obj = new_objtree("NO FORCE ROW LEVEL SECURITY");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "NO FORCE ROW LEVEL SECURITY", false, 0);
 				break;
 
 			case AT_SetNotNull:
-				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET NOT NULL", 2,
-										"type", ObjTypeString, "set not null",
-										"column", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "ALTER COLUMN %{column}I SET NOT NULL", false, 2,
+							 "type", jbvString, "set not null",
+							 "column", jbvString, subcmd->name);
 				break;
 
 			case AT_DropExpression:
-				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I DROP EXPRESSION %{if_exists}s", 3,
-										"type", ObjTypeString, "drop expression",
-										"column", ObjTypeString, subcmd->name,
-										"if_exists", ObjTypeString,
-										subcmd->missing_ok ? "IF EXISTS" : "");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "ALTER COLUMN %{column}I DROP EXPRESSION %{if_exists}s", false, 3,
+							 "type", jbvString, "drop expression",
+							 "column", jbvString, subcmd->name,
+							 "if_exists", jbvString,
+							 subcmd->missing_ok ? "IF EXISTS" : "");
 				break;
 
 			case AT_SetStatistics:
 				{
 					Assert(IsA(subcmd->def, Integer));
-					if (subcmd->name)
-						tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET STATISTICS %{statistics}n", 3,
-												"type", ObjTypeString, "set statistics",
-												"column", ObjTypeString, subcmd->name,
-												"statistics", ObjTypeInteger,
-												intVal((Integer *) subcmd->def));
-					else
-						tmp_obj = new_objtree_VA("ALTER COLUMN %{column}n SET STATISTICS %{statistics}n", 3,
-												"type", ObjTypeString, "set statistics",
-												"column", ObjTypeInteger, subcmd->num,
-												"statistics", ObjTypeInteger,
-												intVal((Integer *) subcmd->def));
-					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+					new_jsonb_VA(state,
+								 "ALTER COLUMN %{column}I SET STATISTICS %{statistics}n", false, 3,
+								 "type", jbvString, "set statistics",
+								 "column", jbvString, subcmd->name,
+								 "statistics", jbvNumeric,
+								 intVal((Integer *) subcmd->def));
 				}
 				break;
 
 			case AT_SetOptions:
 			case AT_ResetOptions:
-				subcmds = lappend(subcmds, new_object_object(
-															 deparse_ColumnSetOptions(subcmd)));
+				deparse_ColumnSetOptions_ToJsonb(state, subcmd);
 				break;
 
 			case AT_SetStorage:
 				Assert(IsA(subcmd->def, String));
-				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET STORAGE %{storage}s", 3,
-										"type", ObjTypeString, "set storage",
-										"column", ObjTypeString, subcmd->name,
-										"storage", ObjTypeString,
-										strVal((String *) subcmd->def));
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "ALTER COLUMN %{column}I SET STORAGE %{storage}s", false, 3,
+							 "type", jbvString, "set storage",
+							 "column", jbvString, subcmd->name,
+							 "storage", jbvString,
+							 strVal((String *) subcmd->def));
 				break;
 
 			case AT_SetCompression:
 				Assert(IsA(subcmd->def, String));
-				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I SET COMPRESSION %{compression_method}s", 3,
-										"type", ObjTypeString, "set compression",
-										"column", ObjTypeString, subcmd->name,
-										"compression_method", ObjTypeString,
-										strVal((String *) subcmd->def));
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "ALTER COLUMN %{column}I SET COMPRESSION %{compression_method}s", false, 3,
+							 "type", jbvString, "set compression",
+							 "column", jbvString, subcmd->name,
+							 "compression_method", jbvString,
+							 strVal((String *) subcmd->def));
 				break;
 
 			case AT_DropColumn:
-				tmp_obj = new_objtree_VA("DROP %{objtype}s %{if_exists}s %{column}I", 4,
-										"objtype", ObjTypeString, "COLUMN",
-										"type", ObjTypeString, "drop column",
-										"if_exists", ObjTypeString,
-										subcmd->missing_ok ? "IF EXISTS" : "",
-										"column", ObjTypeString, subcmd->name);
-				tmp_obj2 = new_objtree("CASCADE");
-				if (subcmd->behavior != DROP_CASCADE)
-					append_not_present(tmp_obj2, NULL);
-				append_object_object(tmp_obj, "%{cascade}s", tmp_obj2);
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state,
+							 "DROP %{objtype}s %{if_exists}s %{column}I %{cascade}s", true, 4,
+							 "objtype", jbvString, "COLUMN",
+							 "type", jbvString, "drop column",
+							 "if_exists", jbvString,
+							 subcmd->missing_ok ? "IF EXISTS" : "",
+							 "column", jbvString, subcmd->name);
+
+				if (subcmd->behavior == DROP_CASCADE)
+					new_jsonb_VA(state, NULL, true, 1, "cascade", jbvString, "CASCADE");
+				else
+				{
+					insert_jsonb_key(state, "cascade");
+					insert_not_present_obj(state, "CASCADE");
+				}
+
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
 				break;
 
 			case AT_AddIndex:
@@ -2339,17 +2173,16 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 					constrOid = get_relation_constraint_oid(
 															cmd->d.alterTable.objectId, idxname, false);
 
-					tmp_obj = new_objtree_VA("ADD CONSTRAINT %{name}I %{definition}s", 3,
-											"type", ObjTypeString, "add constraint",
-											"name", ObjTypeString, idxname,
-											"definition", ObjTypeString,
-											pg_get_constraintdef_string(constrOid));
-					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+					new_jsonb_VA(state,
+								 "ADD CONSTRAINT %{name}I %{definition}s", false, 3,
+								 "type", jbvString, "add constraint",
+								 "name", jbvString, idxname,
+								 "definition", jbvString,
+								 pg_get_constraintdef_string(constrOid));
 
 					relation_close(idx, AccessShareLock);
 				}
 				break;
-
 			case AT_AddConstraint:
 				{
 					/* XXX need to set the "recurse" bit somewhere? */
@@ -2360,7 +2193,7 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 					Constraint *constr;
 
 					/* Skip adding constraint for inherits table sub command */
-					if (!constrOid)
+					if (!OidIsValid(constrOid))
 						continue;
 
 					Assert(IsA(subcmd->def, Constraint));
@@ -2389,12 +2222,12 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 						}
 					}
 
-					tmp_obj = new_objtree_VA("ADD CONSTRAINT %{name}I %{definition}s", 3,
-											"type", ObjTypeString, "add constraint",
-											"name", ObjTypeString, get_constraint_name(constrOid),
-											"definition", ObjTypeString,
-											pg_get_constraintdef_string(constrOid));
-					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+					new_jsonb_VA(state,
+								 "ADD CONSTRAINT %{name}I %{definition}s", false, 3,
+								 "type", jbvString, "add constraint",
+								 "name", jbvString, get_constraint_name(constrOid),
+								 "definition", jbvString,
+								 pg_get_constraintdef_string(constrOid));
 				}
 				break;
 
@@ -2408,34 +2241,34 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 						break;
 
 					Assert(IsA(c, Constraint));
-					tmp_obj = new_objtree_VA("ALTER CONSTRAINT %{name}I %{deferrable}s %{init_deferred}s", 4,
-											"type", ObjTypeString, "alter constraint",
-											"name", ObjTypeString, get_constraint_name(constrOid),
-											"deferrable", ObjTypeString,
-											c->deferrable ? "DEFERRABLE" : "NOT DEFERRABLE",
-											"init_deferred", ObjTypeString,
-											c->initdeferred ? "INITIALLY DEFERRED" : "INITIALLY IMMEDIATE");
-
-					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+
+					new_jsonb_VA(state,
+								 "ALTER CONSTRAINT %{name}I %{deferrable}s %{init_deferred}s", false, 4,
+								 "type", jbvString, "alter constraint",
+								 "name", jbvString, get_constraint_name(constrOid),
+								 "deferrable", jbvString,
+								 c->deferrable ? "DEFERRABLE" : "NOT DEFERRABLE",
+								 "init_deferred", jbvString,
+								 c->initdeferred ? "INITIALLY DEFERRED" : "INITIALLY IMMEDIATE");
 				}
 				break;
 
 			case AT_ValidateConstraint:
-				tmp_obj = new_objtree_VA("VALIDATE CONSTRAINT %{constraint}I", 2,
-										"type", ObjTypeString, "validate constraint",
-										"constraint", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "VALIDATE CONSTRAINT %{constraint}I", false, 2,
+							 "type", jbvString, "validate constraint",
+							 "constraint", jbvString, subcmd->name);
 				break;
 
 			case AT_DropConstraint:
-				tmp_obj = new_objtree_VA("DROP CONSTRAINT %{if_exists}s %{constraint}I %{cascade}s", 4,
-										"type", ObjTypeString, "drop constraint",
-										"if_exists", ObjTypeString,
-										subcmd->missing_ok ? "IF EXISTS" : "",
-										"constraint", ObjTypeString, subcmd->name,
-										"cascade", ObjTypeString,
-										subcmd->behavior == DROP_CASCADE ? "CASCADE" : "");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "DROP CONSTRAINT %{if_exists}s %{constraint}I %{cascade}s", false, 4,
+							 "type", jbvString, "drop constraint",
+							 "if_exists", jbvString,
+							 subcmd->missing_ok ? "IF EXISTS" : "",
+							 "constraint", jbvString, subcmd->name,
+							 "cascade", jbvString,
+							 subcmd->behavior == DROP_CASCADE ? "CASCADE" : "");
 				break;
 
 			case AT_AlterColumnType:
@@ -2448,354 +2281,331 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 					def = (ColumnDef *) subcmd->def;
 					Assert(IsA(def, ColumnDef));
 
-					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,
-											new_objtree_for_type(att->atttypid,
-																 att->atttypmod));
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					new_jsonb_VA(state,
+								 "ALTER COLUMN %{column}I SET DATA TYPE %{datatype}T %{collation}s %{using}s", true, 2,
+								 "type", jbvString, "alter column type",
+								 "column", jbvString, subcmd->name);
+
+					insert_jsonb_key(state, "datatype");
+
+					new_jsonb_for_type(state, att->atttypid, att->atttypmod);
 
 					/* Add a COLLATE clause, if needed */
-					tmp_obj2 = new_objtree("COLLATE");
+					insert_jsonb_key(state, "collation");
 					if (OidIsValid(att->attcollation))
-					{
-						ObjTree    *collname;
-
-						collname = new_objtree_for_qualname_id(CollationRelationId,
-															   att->attcollation);
-						append_object_object(tmp_obj2, "%{name}D", collname);
-					}
+						insert_collate_object(state, "COLLATE %{name}D",
+											  CollationRelationId, att->attcollation, "name");
 					else
-						append_not_present(tmp_obj2, "%{name}D");
-
-					append_object_object(tmp_obj, "%{collation}s", tmp_obj2);
+						insert_not_present_obj(state, verbose ? "COLLATE %{name}D" : "COLLATE");
 
 					/*
 					 * 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");
+					insert_jsonb_key(state, "using");
 					if (def->raw_default)
 					{
-						append_string_object(tmp_obj2, "%{expression}s",
-											 "expression", sub->usingexpr);
+						new_jsonb_VA(state, "USING %{expression}s", false, 1,
+									 "expression", jbvString, sub->usingexpr);
 						mark_function_volatile(context, def->cooked_default);
 					}
 					else
-						append_not_present(tmp_obj2, "%{expression}s");
-
-					append_object_object(tmp_obj, "%{using}s", tmp_obj2);
+						insert_not_present_obj(state,
+											   verbose ? "USING %{expression}s" : "USING");
 
-					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				}
 				break;
 
-#ifdef TODOLIST
-			case AT_AlterColumnGenericOptions:
-				tmp_obj = deparse_FdwOptions((List *) subcmd->def,
-											subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
-				break;
-#endif
 			case AT_ChangeOwner:
-				tmp_obj = new_objtree_VA("OWNER TO %{owner}I", 2,
-										"type", ObjTypeString, "change owner",
-										"owner", ObjTypeString,
-										get_rolespec_name(subcmd->newowner));
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "OWNER TO %{owner}I", false, 2,
+							 "type", jbvString, "change owner",
+							 "owner", jbvString,
+							 get_rolespec_name(subcmd->newowner));
 				break;
 
 			case AT_ClusterOn:
-				tmp_obj = new_objtree_VA("CLUSTER ON %{index}I", 2,
-										"type", ObjTypeString, "cluster on",
-										"index", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "CLUSTER ON %{index}I", false, 2,
+							 "type", jbvString, "cluster on",
+							 "index", jbvString, subcmd->name);
 				break;
 
+
 			case AT_DropCluster:
-				tmp_obj = new_objtree_VA("SET WITHOUT CLUSTER", 1,
-										"type", ObjTypeString, "set without cluster");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "SET WITHOUT CLUSTER", false, 1,
+							 "type", jbvString, "set without cluster");
 				break;
 
 			case AT_SetLogged:
-				tmp_obj = new_objtree_VA("SET LOGGED", 1,
-										"type", ObjTypeString, "set logged");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "SET LOGGED", false, 1,
+							 "type", jbvString, "set logged");
 				break;
 
 			case AT_SetUnLogged:
-				tmp_obj = new_objtree_VA("SET UNLOGGED", 1,
-										"type", ObjTypeString, "set unlogged");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "SET UNLOGGED", false, 1,
+							 "type", jbvString, "set unlogged");
 				break;
 
 			case AT_DropOids:
-				tmp_obj = new_objtree_VA("SET WITHOUT OIDS", 1,
-										"type", ObjTypeString, "set without oids");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "SET WITHOUT OIDS", false, 1,
+							 "type", jbvString, "set without oids");
 				break;
+
 			case AT_SetAccessMethod:
-				tmp_obj = new_objtree_VA("SET ACCESS METHOD %{access_method}I", 2,
-										"type", ObjTypeString, "set access method",
-										"access_method", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "SET ACCESS METHOD %{access_method}I", false, 2,
+							 "type", jbvString, "set access method",
+							 "access_method", jbvString, subcmd->name);
 				break;
+
 			case AT_SetTableSpace:
-				tmp_obj = new_objtree_VA("SET TABLESPACE %{tablespace}I", 2,
-										"type", ObjTypeString, "set tablespace",
-										"tablespace", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "SET TABLESPACE %{tablespace}I", false, 2,
+							 "type", jbvString, "set tablespace",
+							 "tablespace", jbvString, subcmd->name);
 				break;
 
 			case AT_SetRelOptions:
 			case AT_ResetRelOptions:
-				subcmds = lappend(subcmds, new_object_object(
-															 deparse_RelSetOptions(subcmd)));
+				deparse_RelSetOptions_toJsonb(state, subcmd);
 				break;
 
 			case AT_EnableTrig:
-				tmp_obj = new_objtree_VA("ENABLE TRIGGER %{trigger}I", 2,
-										"type", ObjTypeString, "enable trigger",
-										"trigger", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "ENABLE TRIGGER %{trigger}I", false, 2,
+							 "type", jbvString, "enable trigger",
+							 "trigger", jbvString, subcmd->name);
 				break;
 
 			case AT_EnableAlwaysTrig:
-				tmp_obj = new_objtree_VA("ENABLE ALWAYS TRIGGER %{trigger}I", 2,
-										"type", ObjTypeString, "enable always trigger",
-										"trigger", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "ENABLE ALWAYS TRIGGER %{trigger}I", false, 2,
+							 "type", jbvString, "enable always trigger",
+							 "trigger", jbvString, subcmd->name);
 				break;
 
 			case AT_EnableReplicaTrig:
-				tmp_obj = new_objtree_VA("ENABLE REPLICA TRIGGER %{trigger}I", 2,
-										"type", ObjTypeString, "enable replica trigger",
-										"trigger", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state,
+							 "ENABLE REPLICA TRIGGER %{trigger}I", false, 2,
+							 "type", jbvString, "enable replica trigger",
+							 "trigger", jbvString, subcmd->name);
 				break;
 
 			case AT_DisableTrig:
-				tmp_obj = new_objtree_VA("DISABLE TRIGGER %{trigger}I", 2,
-										"type", ObjTypeString, "disable trigger",
-										"trigger", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "DISABLE TRIGGER %{trigger}I", false, 2,
+							 "type", jbvString, "disable trigger",
+							 "trigger", jbvString, subcmd->name);
 				break;
 
 			case AT_EnableTrigAll:
-				tmp_obj = new_objtree_VA("ENABLE TRIGGER ALL", 1,
-										"type", ObjTypeString, "enable trigger all");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "ENABLE TRIGGER ALL", false, 1,
+							 "type", jbvString, "enable trigger all");
 				break;
 
 			case AT_DisableTrigAll:
-				tmp_obj = new_objtree_VA("DISABLE TRIGGER ALL", 1,
-										"type", ObjTypeString, "disable trigger all");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "DISABLE TRIGGER ALL", false, 1,
+							 "type", jbvString, "disable trigger all");
 				break;
 
 			case AT_EnableTrigUser:
-				tmp_obj = new_objtree_VA("ENABLE TRIGGER USER", 1,
-										"type", ObjTypeString, "enable trigger user");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "ENABLE TRIGGER USER", false, 1,
+							 "type", jbvString, "enable trigger user");
 				break;
 
 			case AT_DisableTrigUser:
-				tmp_obj = new_objtree_VA("DISABLE TRIGGER USER", 1,
-										"type", ObjTypeString, "disable trigger user");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "DISABLE TRIGGER USER", false, 1,
+							 "type", jbvString, "disable trigger user");
 				break;
 
 			case AT_EnableRule:
-				tmp_obj = new_objtree_VA("ENABLE RULE %{rule}I", 2,
-										"type", ObjTypeString, "enable rule",
-										"rule", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "ENABLE RULE %{rule}I", false, 2,
+							 "type", jbvString, "enable rule",
+							 "rule", jbvString, subcmd->name);
 				break;
 
 			case AT_EnableAlwaysRule:
-				tmp_obj = new_objtree_VA("ENABLE ALWAYS RULE %{rule}I", 2,
-										"type", ObjTypeString, "enable always rule",
-										"rule", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "ENABLE ALWAYS RULE %{rule}I", false, 2,
+							 "type", jbvString, "enable always rule",
+							 "rule", jbvString, subcmd->name);
 				break;
 
 			case AT_EnableReplicaRule:
-				tmp_obj = new_objtree_VA("ENABLE REPLICA RULE %{rule}I", 2,
-										"type", ObjTypeString, "enable replica rule",
-										"rule", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "ENABLE REPLICA RULE %{rule}I", false, 2,
+							 "type", jbvString, "enable replica rule",
+							 "rule", jbvString, subcmd->name);
 				break;
 
 			case AT_DisableRule:
-				tmp_obj = new_objtree_VA("DISABLE RULE %{rule}I", 2,
-										"type", ObjTypeString, "disable rule",
-										"rule", ObjTypeString, subcmd->name);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "DISABLE RULE %{rule}I", false, 2,
+							 "type", jbvString, "disable rule",
+							 "rule", jbvString, subcmd->name);
 				break;
 
 			case AT_AddInherit:
-				tmp_obj = new_objtree_VA("INHERIT %{parent}D", 2,
-										"type", ObjTypeString, "inherit",
-										"parent", ObjTypeObject,
-										new_objtree_for_qualname_id(RelationRelationId,
-																	sub->address.objectId));
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, "INHERIT %{parent}D", true, 1,
+							 "type", jbvString, "inherit");
+				new_jsonb_for_qualname_id(state, RelationRelationId, sub->address.objectId, "parent", false);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				break;
 
 			case AT_DropInherit:
-				tmp_obj = new_objtree_VA("NO INHERIT %{parent}D", 2,
-										"type", ObjTypeString, "drop inherit",
-										"parent", ObjTypeObject,
-										new_objtree_for_qualname_id(RelationRelationId,
-																	sub->address.objectId));
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, "NO INHERIT %{parent}D", true, 1,
+							 "type", jbvString, "drop inherit");
+				new_jsonb_for_qualname_id(state, RelationRelationId, sub->address.objectId, "parent", false);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				break;
 
 			case AT_AddOf:
-				tmp_obj = new_objtree_VA("OF %{type_of}T", 2,
-										"type", ObjTypeString, "add of",
-										"type_of", ObjTypeObject,
-										new_objtree_for_type(sub->address.objectId, -1));
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, "OF %{type_of}T", true, 1,
+							 "type", jbvString, "add of");
+				insert_jsonb_key(state, "type_of");
+				new_jsonb_for_type(state, sub->address.objectId, -1);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				break;
 
 			case AT_DropOf:
-				tmp_obj = new_objtree_VA("NOT OF", 1,
-										"type", ObjTypeString, "not of");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "NOT OF", false, 1,
+							 "type", jbvString, "not of");
 				break;
 
 			case AT_ReplicaIdentity:
-				tmp_obj = new_objtree_VA("REPLICA IDENTITY", 1,
-										"type", ObjTypeString, "replica identity");
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, "REPLICA IDENTITY %{ident}s", true, 1,
+							 "type", jbvString, "replica identity");
 				switch (((ReplicaIdentityStmt *) subcmd->def)->identity_type)
 				{
 					case REPLICA_IDENTITY_DEFAULT:
-						append_string_object(tmp_obj, "%{ident}s", "ident",
-											 "DEFAULT");
+						new_jsonb_VA(state, NULL, true, 1, "ident", jbvString, "DEFAULT");
 						break;
 					case REPLICA_IDENTITY_FULL:
-						append_string_object(tmp_obj, "%{ident}s", "ident",
-											 "FULL");
+						new_jsonb_VA(state, NULL, true, 1, "ident", jbvString, "FULL");
 						break;
 					case REPLICA_IDENTITY_NOTHING:
-						append_string_object(tmp_obj, "%{ident}s", "ident",
-											 "NOTHING");
+						new_jsonb_VA(state, NULL, true, 1, "ident", jbvString, "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}s", tmp_obj2);
+						insert_jsonb_key(state, "ident");
+						new_jsonb_VA(state, "USING INDEX %{index}I", false, 1,
+									 "index", jbvString,
+									 ((ReplicaIdentityStmt *) subcmd->def)->name);
 						break;
 				}
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				break;
 
 			case AT_EnableRowSecurity:
-				tmp_obj = new_objtree_VA("ENABLE ROW LEVEL SECURITY", 1,
-										"type", ObjTypeString, "enable row security");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "ENABLE ROW LEVEL SECURITY", false, 1,
+							 "type", jbvString, "enable row security");
 				break;
 
 			case AT_DisableRowSecurity:
-				tmp_obj = new_objtree_VA("DISABLE ROW LEVEL SECURITY", 1,
-										"type", ObjTypeString, "disable row security");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
-				break;
-#ifdef TODOLIST
-			case AT_GenericOptions:
-				tmp_obj = deparse_FdwOptions((List *) subcmd->def, NULL);
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				new_jsonb_VA(state, "DISABLE ROW LEVEL SECURITY", false, 1,
+							 "type", jbvString, "disable row security");
 				break;
-#endif
+
 			case AT_AttachPartition:
-				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));
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state,
+							 "ATTACH PARTITION %{partition_identity}D %{partition_bound}s", true, 1,
+							 "type", jbvString, "attach partition");
+				new_jsonb_for_qualname_id(state, RelationRelationId,
+										  sub->address.objectId,
+										  "partition_identity", false);
 
-				tmp_obj2 = new_objtree("");
 				if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-					append_string_object(tmp_obj, "%{partition_bound}s",
-										 "partition_bound",
-										 RelationGetPartitionBound(sub->address.objectId));
+					new_jsonb_VA(state, NULL, true, 1,
+								 "partition_bound", jbvString,
+								 RelationGetPartitionBound(sub->address.objectId));
 				else
-					append_not_present(tmp_obj2, "%{partition_bound}s");
+					new_jsonb_VA(state, NULL, true, 1,
+								 "partition_bound", jbvString, "");
 
-				append_object_object(tmp_obj, "%{partition_bound}s", tmp_obj2);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
 				break;
 			case AT_DetachPartition:
-			{
-				PartitionCmd *cmd;
-
-				Assert(IsA(subcmd->def, PartitionCmd));
-				cmd = (PartitionCmd *) subcmd->def;
-
-				tmp_obj = new_objtree_VA("DETACH PARTITION %{partition_identity}D %{concurrent}s", 3,
-										"type", ObjTypeString,
-										"detach partition",
-										"partition_identity", ObjTypeObject,
-										new_objtree_for_qualname_id(RelationRelationId,
-																	sub->address.objectId),
-										"concurrent", ObjTypeString,
-										cmd->concurrent ? "CONCURRENTLY" : "");
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
-				break;
-			}
+				{
+					PartitionCmd *cmd;
+
+					Assert(IsA(subcmd->def, PartitionCmd));
+					cmd = (PartitionCmd *) subcmd->def;
+
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					new_jsonb_VA(state, "DETACH PARTITION %{partition_identity}D %{concurrent}s", true, 2,
+								 "type", jbvString, "detach partition",
+								 "concurrent", jbvString,
+								 cmd->concurrent ? "CONCURRENTLY" : "");
+					new_jsonb_for_qualname_id(state, RelationRelationId, sub->address.objectId, "partition_identity", false);
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+					break;
+				}
 			case AT_DetachPartitionFinalize:
-				tmp_obj = new_objtree_VA("DETACH PARTITION %{partition_identity}D FINALIZE", 2,
-										"type", ObjTypeString, "detach partition finalize",
-										"partition_identity", ObjTypeObject,
-										new_objtree_for_qualname_id(RelationRelationId,
-																	sub->address.objectId));
-				subcmds = lappend(subcmds, new_object_object(tmp_obj));
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, "DETACH PARTITION %{partition_identity}D FINALIZE", true, 1,
+							 "type", jbvString, "detach partition finalize");
+
+				new_jsonb_for_qualname_id(state, RelationRelationId, sub->address.objectId, "partition_identity", false);
+				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				break;
 			case AT_AddIdentity:
 				{
 					AttrNumber	attnum;
 					Oid			seq_relid;
-					ObjTree    *seqdef;
 					ColumnDef  *coldef = (ColumnDef *) subcmd->def;
 
-					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I", 2,
-											"type", ObjTypeString, "add identity",
-											"column", ObjTypeString, subcmd->name);
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					new_jsonb_VA(state, "ALTER COLUMN %{column}I %{definition}s", true, 2,
+								 "type", jbvString, "add identity",
+								 "column", jbvString, subcmd->name);
 
 					attnum = get_attnum(RelationGetRelid(rel), subcmd->name);
 					seq_relid = getIdentitySequence(RelationGetRelid(rel), attnum, true);
 
-					tmp_obj2 = new_objtree("ADD");
+					insert_jsonb_key(state, "definition");
 					if (OidIsValid(seq_relid))
 					{
-						seqdef = deparse_ColumnIdentity(seq_relid, coldef->identity, false);
-						append_object_object(tmp_obj2, "%{identity_column}s", seqdef);
+						/* insert definition's value now */
+						pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+						fmt_to_jsonb_element(state, "ADD %{identity_column}s");
+
+						/* insert identity_column as key */
+						insert_jsonb_key(state, "identity_column");
+
+						/* insert identity_column's value */
+						deparse_ColumnIdentity_toJsonb(state, seq_relid, coldef->identity, false);
+
+						/* mark definition's value end */
+						pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 					}
 					else
-						append_not_present(tmp_obj2, "%{identity_column}s");
-
-					append_object_object(tmp_obj, "%{definition}s", tmp_obj2);
+						insert_not_present_obj(state, verbose ? "ADD %{identity_column}s" : "ADD");
 
-					subcmds = lappend(subcmds, new_object_object(tmp_obj));
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				}
 				break;
 			case AT_SetIdentity:
 				{
 					DefElem    *defel;
 					char		identity = 0;
-					ObjTree    *seqdef;
 					AttrNumber	attnum;
 					Oid			seq_relid;
 
-
-					tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I", 2,
-											"type", ObjTypeString, "set identity",
-											"column", ObjTypeString, subcmd->name);
+					pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+					new_jsonb_VA(state, "ALTER COLUMN %{column}I %{definition}s", true, 2,
+								 "type", jbvString, "set identity",
+								 "column", jbvString, subcmd->name);
 
 					if (subcmd->def)
 					{
@@ -2810,31 +2620,26 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 					attnum = get_attnum(RelationGetRelid(rel), subcmd->name);
 					seq_relid = getIdentitySequence(RelationGetRelid(rel), attnum, true);
 
-					tmp_obj2 = new_objtree("");
+					insert_jsonb_key(state, "definition");
 					if (OidIsValid(seq_relid))
 					{
-						seqdef = deparse_ColumnIdentity(seq_relid, identity, true);
-						append_object_object(tmp_obj2, "%{definition}s", seqdef);
+						deparse_ColumnIdentity_toJsonb(state, seq_relid, identity, true);
 					}
 					else
-						append_not_present(tmp_obj2, "%{definition}s");
+						insert_not_present_obj(state, verbose ? "%{definition}s" : "");
 
-					append_object_object(tmp_obj, "%{definition}s", tmp_obj2);
+					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 
-					subcmds = lappend(subcmds, new_object_object(tmp_obj));
 					break;
 				}
 			case AT_DropIdentity:
-				tmp_obj = new_objtree_VA("ALTER COLUMN %{column}I DROP IDENTITY", 2,
-										"type", ObjTypeString, "drop identity",
-										"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;
+				{
+					new_jsonb_VA(state, "ALTER COLUMN %{column}I DROP IDENTITY %{if_exists}s", false, 3,
+								 "type", jbvString, "drop identity",
+								 "column", jbvString, subcmd->name,
+								 "if_exists", jbvString, subcmd->missing_ok ? "IF EXISTS" : "");
+					break;
+				}
 			default:
 				elog(WARNING, "unsupported alter table subtype %d",
 					 subcmd->subtype);
@@ -2844,124 +2649,110 @@ deparse_AlterRelation(CollectedCommand *cmd, ddl_deparse_context *context)
 
 	table_close(rel, AccessShareLock);
 
-	if (list_length(subcmds) == 0)
+	/* 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;
+	}
 
-	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,
-					 Node *parsetree)
-{
-	DropStmt   *node = (DropStmt *) parsetree;
-	StringInfoData str;
-	char	   *command;
-	char	   *identity = (char *) objidentity;
-	ObjTree    *stmt;
-	ObjTree    *tmp_obj;
-	Jsonb	   *jsonb;
-
-	initStringInfo(&str);
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+	appendStringInfoString(&fmtStr, "%{subcmds:, }s");
 
-	stmt = new_objtree_VA("DROP %{objtype}s %{concurrently}s %{if_exists}s %{objidentity}s", 4,
-						  "objtype", ObjTypeString, objecttype,
-						  "concurrently", ObjTypeString,
-						  node->concurrent ? "CONCURRENTLY" : "",
-						  "if_exists", ObjTypeString,
-						  node->missing_ok ? "IF EXISTS" : "",
-						  "objidentity", ObjTypeString, identity);
+	/* We have full fmt by now, so add jsonb element for that */
+	fmt_to_jsonb_element(state, fmtStr.data);
 
-	tmp_obj = new_objtree("CASCADE");
-	if (node->behavior != DROP_CASCADE)
-		append_not_present(tmp_obj, NULL);
-	append_object_object(stmt, "%{cascade}s", tmp_obj);
+	pfree(fmtStr.data);
 
-	jsonb = objtree_to_jsonb(stmt, NULL /* Owner/role can be skipped for drop command */);
-	command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
+	/* Mark the end of ROOT object */
+	value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 
-	return command;
+	return JsonbValueToJsonb(value);
 }
 
 /*
- * Deparse an AlterObjectSchemaStmt (ALTER ... SET SCHEMA command)
+ * Deparse a CreateSeqStmt.
  *
- * Given the object address and the parse tree that created it, return an
- * ObjTree representing the alter command.
+ * Given a sequence OID and the parse tree that created it, return Jsonb
+ * representing the creation command.
+ *
+ * Note: We need to deparse the CREATE SEQUENCE command for the TABLE
+ * 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
- * ALTER %{objtype}s %{identity}s SET SCHEMA %{newschema}I
+ * CREATE %{persistence}s SEQUENCE %{identity}D %{definition: }s
  */
-static ObjTree *
-deparse_AlterObjectSchemaStmt(ObjectAddress address, Node *parsetree,
-							  ObjectAddress old_schema)
+static Jsonb *
+deparse_CreateSeqStmt(Oid objectId, Node *parsetree, char *owner)
 {
-	AlterObjectSchemaStmt *node = (AlterObjectSchemaStmt *) parsetree;
-	char	   *identity;
-	char	   *new_schema = node->newschema;
-	char	   *old_schname;
-	char	   *ident;
+	Relation	relation;
+	Form_pg_sequence seqform;
+	Sequence_values *seqvalues;
+	CreateSeqStmt *createSeqStmt = (CreateSeqStmt *) parsetree;
+	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?
+	 * Only support sequence for IDENTITY COLUMN output separately (via CREATE
+	 * TABLE or ALTER TABLE). Otherwise, return empty here.
 	 */
-	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);
+	if (createSeqStmt->for_identity)
+		return NULL;
 
-	ident = psprintf("%s%s", quote_identifier(old_schname),
-					 identity + strlen(quote_identifier(new_schema)));
+	relation = relation_open(objectId, AccessShareLock);
 
-	return new_objtree_VA("ALTER %{objtype}s %{identity}s SET SCHEMA %{newschema}I", 3,
-						  "objtype", ObjTypeString,
-						  stringify_objtype(node->objectType),
-						  "identity", ObjTypeString, ident,
-						  "newschema", ObjTypeString, new_schema);
-}
+	/* mark the start of ROOT object */
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
-/*
- * Deparse an AlterOwnerStmt (ALTER ... OWNER TO ...).
- *
- * 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)
-{
-	AlterOwnerStmt *node = (AlterOwnerStmt *) parsetree;
+	/* create owner jsonb element */
+	role_to_jsonb_element(state, owner);
+
+	new_jsonb_VA(state, "CREATE %{persistence}s SEQUENCE %{if_not_exists}s %{identity}D %{definition: }s", true, 2,
+				 "persistence", jbvString,
+				 get_persistence_str(relation->rd_rel->relpersistence),
+				 "if_not_exists", jbvString,
+				 createSeqStmt->if_not_exists ? "IF NOT EXISTS" : "");
+
+	insert_identity_object(state, relation->rd_rel->relnamespace,
+						   RelationGetRelationName(relation));
+	relation_close(relation, AccessShareLock);
 
-	return new_objtree_VA("ALTER %{objtype}s %{identity}s OWNER TO %{newowner}I", 3,
-						  "objtype", ObjTypeString,
-						  stringify_objtype(node->objectType),
-						  "identity", ObjTypeString,
-						  getObjectIdentity(&address, false),
-						  "newowner", ObjTypeString,
-						  get_rolespec_name(node->newowner));
+	seqvalues = get_sequence_values(objectId);
+	seqform = seqvalues->seqform;
+
+	/* sequence definition array object creation, push the key first */
+	insert_jsonb_key(state, "definition");
+
+	pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
+	/* Definition elements */
+	deparse_Seq_Cache_toJsonb(state, seqform, false);
+	deparse_Seq_Cycle_toJsonb(state, seqform, false);
+	deparse_Seq_IncrementBy_toJsonb(state, seqform, false);
+	deparse_Seq_Minvalue_toJsonb(state, seqform, false);
+	deparse_Seq_Maxvalue_toJsonb(state, seqform, false);
+	deparse_Seq_Startwith_toJsonb(state, seqform, false);
+	deparse_Seq_Restart_toJsonb(state, seqvalues->last_value);
+	deparse_Seq_As_toJsonb(state, seqform);
+	/* We purposefully do not emit OWNED BY here */
+
+	/* mark the end of sequence definition array */
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
+
+	/* mark the end of ROOT object */
+	value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
+	return JsonbValueToJsonb(value);
 }
 
 /*
  * Deparse an AlterSeqStmt.
  *
- * Given a sequence OID and a parse tree that modified it, return an ObjTree
+ * Given a sequence OID and a parse tree that modified it, return Jsonb
  * representing the alter command.
  *
  * Note: We need to deparse the ALTER SEQUENCE command for the TABLE commands.
@@ -2972,16 +2763,16 @@ deparse_AlterOwnerStmt(ObjectAddress address, Node *parsetree)
  * Verbose syntax
  * ALTER SEQUENCE %{identity}D %{definition: }s
  */
-static ObjTree *
-deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
+static Jsonb *
+deparse_AlterSeqStmt(Oid objectId, Node *parsetree, char *owner)
 {
-	ObjTree    *ret;
 	Relation	relation;
-	List	   *elems = NIL;
 	ListCell   *cell;
 	Form_pg_sequence seqform;
 	Sequence_values *seqvalues;
 	AlterSeqStmt *alterSeqStmt = (AlterSeqStmt *) parsetree;
+	JsonbParseState *state = NULL;
+	JsonbValue *value;
 
 	/*
 	 * Sequence for IDENTITY COLUMN output separately (via CREATE TABLE or
@@ -2990,49 +2781,62 @@ deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
 	if (alterSeqStmt->for_identity)
 		return NULL;
 
+	relation = relation_open(objectId, AccessShareLock);
+
+	/* mark the start of ROOT object */
+	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+
+	/* create owner jsonb element */
+	role_to_jsonb_element(state, owner);
+
+	new_jsonb_VA(state, "ALTER SEQUENCE %{identity}D %{definition: }s", true, 0);
+
+	insert_identity_object(state, relation->rd_rel->relnamespace,
+						   RelationGetRelationName(relation));
+	relation_close(relation, AccessShareLock);
+
 	seqvalues = get_sequence_values(objectId);
 	seqform = seqvalues->seqform;
 
+	/* sequence definition array object creation, push the key first */
+	insert_jsonb_key(state, "definition");
+
+	/* mark the start of sequence definition array */
+	pushJsonbValue(&state, WJB_BEGIN_ARRAY, NULL);
+
 	foreach(cell, ((AlterSeqStmt *) parsetree)->options)
 	{
 		DefElem    *elem = (DefElem *) lfirst(cell);
-		ObjElem    *newelm;
 
 		if (strcmp(elem->defname, "cache") == 0)
-			newelm = deparse_Seq_Cache(seqform, false);
+			deparse_Seq_Cache_toJsonb(state, seqform, false);
 		else if (strcmp(elem->defname, "cycle") == 0)
-			newelm = deparse_Seq_Cycle(seqform, false);
+			deparse_Seq_Cycle_toJsonb(state, seqform, false);
 		else if (strcmp(elem->defname, "increment") == 0)
-			newelm = deparse_Seq_IncrementBy(seqform, false);
+			deparse_Seq_IncrementBy_toJsonb(state, seqform, false);
 		else if (strcmp(elem->defname, "minvalue") == 0)
-			newelm = deparse_Seq_Minvalue(seqform, false);
+			deparse_Seq_Minvalue_toJsonb(state, seqform, false);
 		else if (strcmp(elem->defname, "maxvalue") == 0)
-			newelm = deparse_Seq_Maxvalue(seqform, false);
+			deparse_Seq_Maxvalue_toJsonb(state, seqform, false);
 		else if (strcmp(elem->defname, "start") == 0)
-			newelm = deparse_Seq_Startwith(seqform, false);
+			deparse_Seq_Startwith_toJsonb(state, seqform, false);
 		else if (strcmp(elem->defname, "restart") == 0)
-			newelm = deparse_Seq_Restart(seqvalues->last_value);
+			deparse_Seq_Restart_toJsonb(state, seqvalues->last_value);
 		else if (strcmp(elem->defname, "owned_by") == 0)
-			newelm = deparse_Seq_OwnedBy(objectId, false);
+			deparse_Seq_OwnedBy_toJsonb(state, objectId, false);
 		else if (strcmp(elem->defname, "as") == 0)
-			newelm = deparse_Seq_As(seqform);
+			deparse_Seq_As_toJsonb(state, seqform);
 		else
 			elog(ERROR, "invalid sequence option %s", elem->defname);
-
-		elems = lappend(elems, newelm);
 	}
 
-	relation = relation_open(objectId, AccessShareLock);
-
-	ret = new_objtree_VA("ALTER SEQUENCE %{identity}D %{definition: }s", 2,
-						 "identity", ObjTypeObject,
-						 new_objtree_for_qualname(relation->rd_rel->relnamespace,
-												  RelationGetRelationName(relation)),
-						 "definition", ObjTypeArray, elems);
+	/* mark the end of sequence definition array */
+	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
 
-	relation_close(relation, AccessShareLock);
+	/* mark the end of ROOT object */
+	value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 
-	return ret;
+	return JsonbValueToJsonb(value);
 }
 
 /*
@@ -3045,13 +2849,15 @@ deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
  * OR
  * ALTER %{objtype}s %{if_exists}s %{only}s %{identity}D RENAME COLUMN %{colname}I TO %{newname}I %{cascade}s
  */
-static ObjTree *
+
+static Jsonb *
 deparse_RenameStmt(ObjectAddress address, Node *parsetree)
 {
 	RenameStmt *node = (RenameStmt *) parsetree;
-	ObjTree    *ret;
 	Relation	relation;
 	Oid			schemaId;
+	JsonbParseState *state = NULL;
+	JsonbValue *value;
 
 	/*
 	 * In an ALTER .. RENAME command, we don't have the original name of the
@@ -3066,16 +2872,18 @@ deparse_RenameStmt(ObjectAddress address, Node *parsetree)
 		case OBJECT_TABLE:
 			relation = relation_open(address.objectId, AccessShareLock);
 			schemaId = RelationGetNamespace(relation);
-			ret = new_objtree_VA("ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I", 4,
-								 "objtype", ObjTypeString,
-								 stringify_objtype(node->renameType),
-								 "if_exists", ObjTypeString,
-								 node->missing_ok ? "IF EXISTS" : "",
-								 "identity", ObjTypeObject,
-								 new_objtree_for_qualname(schemaId,
-														  node->relation->relname),
-								 "newname", ObjTypeString,
-								 node->newname);
+
+			pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+			new_jsonb_VA(state, "ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I", true, 3,
+						 "objtype", jbvString,
+						 stringify_objtype(node->renameType),
+						 "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;
 
@@ -3091,60 +2899,162 @@ deparse_RenameStmt(ObjectAddress address, Node *parsetree)
 						 address.objectId);
 				constform = (Form_pg_constraint) GETSTRUCT(constrtup);
 
-				ret = new_objtree_VA("ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I", 4,
-									"only",  ObjTypeString,
-									node->relation->inh ? "" : "ONLY",
-									"identity", ObjTypeObject,
-									new_objtree_for_qualname_id(RelationRelationId,
-																constform->conrelid),
-									"oldname", ObjTypeString, node->subname,
-									"newname", ObjTypeString, node->newname);
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+				new_jsonb_VA(state, "ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I", true, 3,
+							 "only", jbvString,
+							 node->relation->inh ? "" : "ONLY",
+							 "oldname", jbvString, node->subname,
+							 "newname", jbvString, node->newname);
+
+				new_jsonb_for_qualname_id(state, RelationRelationId,
+										  constform->conrelid, "identity", false);
+				value = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+
 				ReleaseSysCache(constrtup);
 			}
 			break;
 
 		case OBJECT_COLUMN:
-			relation = relation_open(address.objectId, AccessShareLock);
-			schemaId = RelationGetNamespace(relation);
+			{
+				StringInfoData fmtStr;
 
-			ret = new_objtree_VA("ALTER %{objtype}s", 1,
-								 "objtype", ObjTypeString,
-								 stringify_objtype(node->relationType));
+				initStringInfo(&fmtStr);
 
-			/* Composite types do not support IF EXISTS */
-			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");
+				relation = relation_open(address.objectId, AccessShareLock);
+				schemaId = RelationGetNamespace(relation);
 
-			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);
+				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
 
-			append_string_object(ret, "TO %{newname}I", "newname",
-								 node->newname);
+				appendStringInfoString(&fmtStr, "ALTER %{objtype}s");
 
-			if (node->renameType == OBJECT_ATTRIBUTE)
-			{
-				ObjTree    *tmp_obj = new_objtree("CASCADE");
+				new_jsonb_VA(state, NULL, true, 1,
+							 "objtype", jbvString,
+							 stringify_objtype(node->relationType));
 
-				if (node->behavior != DROP_CASCADE)
-					append_not_present(tmp_obj, NULL);
-				append_object_object(ret, "%{cascade}s", tmp_obj);
-			}
+				/* Composite types do not support IF EXISTS */
+				if (node->renameType == OBJECT_COLUMN)
+				{
+					appendStringInfoString(&fmtStr, " %{if_exists}s");
+					new_jsonb_VA(state, NULL, true, 1,
+								 "if_exists", jbvString,
+								 node->missing_ok ? "IF EXISTS" : "");
+				}
 
-			relation_close(relation, AccessShareLock);
-			break;
+				if (!node->relation->inh)
+				{
+					appendStringInfoString(&fmtStr, " %{only}s");
+					new_jsonb_VA(state, NULL, true, 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, NULL, true, 2,
+							 "colname", jbvString, node->subname,
+							 "newname", jbvString, node->newname);
+
+				if (node->renameType == OBJECT_ATTRIBUTE)
+				{
+					appendStringInfoString(&fmtStr, " %{cascade}s");
+
+					if (node->behavior == DROP_CASCADE)
+						new_jsonb_VA(state, NULL, true, 1,
+									 "cascade", jbvString, "CASCADE");
+					else
+					{
+						insert_jsonb_key(state, "cascade");
+						insert_not_present_obj(state, "CASCADE");
+					}
+				}
+
+				/* We have full fmt by now, so add jsonb element for that */
+				fmt_to_jsonb_element(state, 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 ret;
+	return JsonbValueToJsonb(value);
+}
+
+/*
+ * Deparse an AlterOwnerStmt (ALTER ... OWNER TO ...).
+ *
+ * Given the object address and the parse tree that created it, return
+ * Jsonb representing the alter command.
+ *
+ * Verbose syntax
+ * ALTER %{objtype}s %{identity}s OWNER TO %{newowner}I
+ */
+static Jsonb *
+deparse_AlterOwnerStmt(ObjectAddress address, Node *parsetree)
+{
+	AlterOwnerStmt *node = (AlterOwnerStmt *) parsetree;
+	JsonbParseState *state = NULL;
+	JsonbValue *value;
+
+	value = new_jsonb_VA(state, "ALTER %{objtype}s %{identity}s OWNER TO %{newowner}I", false, 3,
+						 "objtype", jbvString,
+						 stringify_objtype(node->objectType),
+						 "identity", jbvString,
+						 getObjectIdentity(&address, false),
+						 "newowner", jbvString,
+						 get_rolespec_name(node->newowner));
+	return JsonbValueToJsonb(value);
+}
+
+/*
+ * Deparse an AlterObjectSchemaStmt (ALTER ... SET SCHEMA command)
+ *
+ * Given the object address and the parse tree that created it, return an
+ * 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)));
+
+	value = new_jsonb_VA(state, "ALTER %{objtype}s %{identity}s SET SCHEMA %{newschema}I", false, 3,
+						 "objtype", jbvString,
+						 stringify_objtype(node->objectType),
+						 "identity", jbvString, ident,
+						 "newschema", jbvString, new_schema);
+	return JsonbValueToJsonb(value);
 }
 
 /*
@@ -3152,11 +3062,12 @@ deparse_RenameStmt(ObjectAddress address, Node *parsetree)
  *
  * This function should cover all cases handled in ProcessUtilitySlow.
  */
-static ObjTree *
-deparse_simple_command(CollectedCommand *cmd, bool *include_owner)
+static Jsonb *
+deparse_simple_command(CollectedCommand *cmd, ddl_deparse_context * context)
 {
 	Oid			objectId;
 	Node	   *parsetree;
+	char	   *owner = context->include_owner ? cmd->role : NULL;
 
 	Assert(cmd->type == SCT_Simple);
 
@@ -3170,26 +3081,25 @@ deparse_simple_command(CollectedCommand *cmd, bool *include_owner)
 	switch (nodeTag(parsetree))
 	{
 		case T_AlterObjectSchemaStmt:
-			*include_owner = false;
+			context->include_owner = false;
 			return deparse_AlterObjectSchemaStmt(cmd->d.simple.address,
 												 parsetree,
 												 cmd->d.simple.secondaryObject);
 
 		case T_AlterOwnerStmt:
-			*include_owner = false;
+			context->include_owner = false;
 			return deparse_AlterOwnerStmt(cmd->d.simple.address, parsetree);
 
 		case T_AlterSeqStmt:
-			return deparse_AlterSeqStmt(objectId, parsetree);
+			return deparse_AlterSeqStmt(objectId, parsetree, owner);
 
 		case T_CreateSeqStmt:
-			return deparse_CreateSeqStmt(objectId, parsetree);
+			return deparse_CreateSeqStmt(objectId, parsetree, owner);
 
 		case T_CreateStmt:
-			return deparse_CreateStmt(objectId, parsetree);
-
+			return deparse_CreateStmt(objectId, parsetree, owner);
 		case T_RenameStmt:
-			*include_owner = false;
+			context->include_owner = false;
 			return deparse_RenameStmt(cmd->d.simple.address, parsetree);
 
 		default:
@@ -3204,14 +3114,14 @@ deparse_simple_command(CollectedCommand *cmd, bool *include_owner)
  * Workhorse to deparse a CollectedCommand.
  */
 char *
-deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context)
+deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context * context)
 {
 	OverrideSearchPath *overridePath;
 	MemoryContext oldcxt;
 	MemoryContext tmpcxt;
-	ObjTree    *tree;
 	char	   *command = NULL;
 	StringInfoData str;
+	Jsonb	   *jsonb;
 
 	/*
 	 * Allocate everything done by the deparsing routines into a temp context,
@@ -3245,14 +3155,14 @@ deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context)
 	switch (cmd->type)
 	{
 		case SCT_Simple:
-			tree = deparse_simple_command(cmd, &context->include_owner);
+			jsonb = deparse_simple_command(cmd, context);
 			break;
 		case SCT_AlterTable:
-			tree = deparse_AlterRelation(cmd, context);
+			jsonb = deparse_AlterTableStmt(cmd, context);
 			context->include_owner = false;
 			break;
 		case SCT_CreateTableAs:
-			tree = deparse_CreateTableAsStmt(cmd);
+			jsonb = deparse_CreateTableAsStmt(cmd, context);
 			break;
 		default:
 			elog(ERROR, "unexpected deparse node type %d", cmd->type);
@@ -3260,14 +3170,8 @@ deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context)
 
 	PopOverrideSearchPath();
 
-	if (tree)
-	{
-		Jsonb	   *jsonb;
-
-		jsonb = context->include_owner ? objtree_to_jsonb(tree, cmd->role) :
-										 objtree_to_jsonb(tree, NULL);
+	if (jsonb)
 		command = JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
-	}
 
 	/*
 	 * Clean up.  Note that since we created the StringInfo in the caller's
diff --git a/src/backend/replication/logical/ddltrigger.c b/src/backend/replication/logical/ddltrigger.c
index 94161a0497..be1c6b40c9 100644
--- a/src/backend/replication/logical/ddltrigger.c
+++ b/src/backend/replication/logical/ddltrigger.c
@@ -282,12 +282,15 @@ publication_deparse_ddl_command_end(PG_FUNCTION_ARGS)
 		else
 			continue;
 
-		command = deparse_drop_command(obj->objidentity, obj->objecttype,
+		command = deparse_drop_table(obj->objidentity, obj->objecttype,
 									   trigdata->parsetree);
 
 		if (command)
+		{
 			LogLogicalDDLMessage("deparse", obj->address.objectId, cmdtype,
 								 command, strlen(command) + 1);
+			elog(LOG, "COMMAND = %s", command);
+		}
 	}
 
 	return PointerGetDatum(NULL);
diff --git a/src/include/tcop/ddldeparse.h b/src/include/tcop/ddldeparse.h
index 1cceb53662..19b1274859 100644
--- a/src/include/tcop/ddldeparse.h
+++ b/src/include/tcop/ddldeparse.h
@@ -32,7 +32,7 @@ typedef struct
 
 extern char *deparse_utility_command(CollectedCommand *cmd, ddl_deparse_context *context);
 extern char *deparse_ddl_json_to_string(char *jsonb, char** owner);
-extern char *deparse_drop_command(const char *objidentity, const char *objecttype,
-								  Node *parsetree);
+extern char *deparse_drop_table(const char *objidentity, const char *objecttype,
+										   Node *parsetree);
 
 #endif							/* DDL_DEPARSE_H */
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/alter_table.out b/src/test/modules/test_ddl_deparse_regress/expected/alter_table.out
index 9b22999cd9..7cd753a1ba 100644
--- a/src/test/modules/test_ddl_deparse_regress/expected/alter_table.out
+++ b/src/test/modules/test_ddl_deparse_regress/expected/alter_table.out
@@ -7,18 +7,18 @@ CREATE TABLE orders(
     quantity int,
     purchase_date date
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.orders (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.orders (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 -- ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
 --     action [, ... ]
 CREATE TABLE parent_table(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.parent_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.parent_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 CREATE TABLE test_only () INHERITS (parent_table);
-NOTICE:  re-formed command: CREATE  TABLE  public.test_only () INHERITS (public.parent_table)     
+NOTICE:  re-formed command: CREATE  TABLE  public.test_only () INHERITS (public.parent_table)    
 ALTER TABLE test_only ADD col1 int;
 NOTICE:  re-formed command: ALTER TABLE  public.test_only ADD COLUMN  col1 pg_catalog.int4 STORAGE PLAIN      
@@ -32,8 +32,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.parent_table ADD CONSTRAINT pare
 CREATE TABLE test_add_column(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_add_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_add_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_add_column ADD col1 int;
 NOTICE:  re-formed command: ALTER TABLE  public.test_add_column ADD COLUMN  col1 pg_catalog.int4 STORAGE PLAIN      
@@ -59,14 +59,14 @@ CREATE TABLE test_drop_column(
     UNIQUE (id),
     UNIQUE (name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_drop_column_id_key UNIQUE (id), CONSTRAINT test_drop_column_name_key UNIQUE (name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_drop_column_id_key UNIQUE (id), CONSTRAINT test_drop_column_name_key UNIQUE (name))     
 CREATE TABLE foreign_table(
     id int REFERENCES test_drop_column (id),
     name varchar REFERENCES test_drop_column (name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.foreign_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.foreign_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  re-formed command: ALTER TABLE  public.foreign_table ADD CONSTRAINT foreign_table_id_fkey FOREIGN KEY (id) REFERENCES public.test_drop_column(id), ADD CONSTRAINT foreign_table_name_fkey FOREIGN KEY (name) REFERENCES public.test_drop_column(name)
 ALTER TABLE test_drop_column DROP price;
@@ -78,14 +78,16 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_drop_column DROP COLUMN  qu
 ALTER TABLE test_drop_column DROP IF EXISTS description RESTRICT;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP %{objtype}s %{if_exists}s %{column}I %{cascade}s", "type": "drop column", "column": "description", "cascade": {"fmt": "CASCADE", "present": false}, "objtype": "COLUMN", "if_exists": "IF EXISTS"}], "identity": {"objname": "test_drop_column", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_drop_column DROP COLUMN IF EXISTS description 
--- TOFIX
--- ALTER TABLE test_drop_column DROP IF EXISTS name CASCADE;
+ALTER TABLE test_drop_column DROP IF EXISTS name CASCADE;
+NOTICE:  drop cascades to constraint foreign_table_name_fkey on table foreign_table
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP %{objtype}s %{if_exists}s %{column}I %{cascade}s", "type": "drop column", "column": "name", "cascade": "CASCADE", "objtype": "COLUMN", "if_exists": "IF EXISTS"}], "identity": {"objname": "test_drop_column", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_column DROP COLUMN IF EXISTS name CASCADE
 -- ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ COLLATE collation ] [ USING expression ]
 CREATE TABLE test_alter_type(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_type (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_type (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_alter_type ALTER price TYPE int;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET DATA TYPE %{datatype}T %{collation}s %{using}s", "type": "alter column type", "using": {"fmt": "USING %{expression}s", "present": false}, "column": "price", "datatype": {"typmod": "", "typarray": false, "typename": "int4", "schemaname": "pg_catalog"}, "collation": {"fmt": "COLLATE %{name}D", "present": false}}], "identity": {"objname": "test_alter_type", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_alter_type ALTER COLUMN price SET DATA TYPE pg_catalog.int4  
@@ -105,8 +107,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_alter_type ALTER COLUMN nam
 CREATE TABLE test_alter_set_default(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_set_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_set_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_alter_set_default ALTER price SET DEFAULT 100;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET DEFAULT %{definition}s", "type": "set default", "column": "price", "definition": "100"}], "identity": {"objname": "test_alter_set_default", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_alter_set_default ALTER COLUMN price SET DEFAULT 100
@@ -119,8 +121,8 @@ CREATE TABLE test_drop_default(
     default_price float4 DEFAULT 10.0,
     default_name varchar DEFAULT 'foo'
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , default_price pg_catalog.float4 STORAGE PLAIN    DEFAULT 10.0  , default_name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"  DEFAULT 'foo'::character varying  )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , default_price pg_catalog.float4 STORAGE PLAIN    DEFAULT 10.0  , default_name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"  DEFAULT 'foo'::character varying  )     
 ALTER TABLE test_drop_default ALTER default_price DROP DEFAULT;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP DEFAULT", "type": "drop default", "column": "default_price"}], "identity": {"objname": "test_drop_default", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_drop_default ALTER COLUMN default_price DROP DEFAULT
@@ -132,8 +134,8 @@ CREATE TABLE test_set_not_null(
     LIKE orders,
     size int NOT NULL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_not_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , size pg_catalog.int4 STORAGE PLAIN   NOT NULL   )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_not_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , size pg_catalog.int4 STORAGE PLAIN   NOT NULL   )     
 ALTER TABLE test_set_not_null ALTER COLUMN id SET NOT NULL;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}], "identity": {"objname": "test_set_not_null", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_not_null ALTER COLUMN id SET NOT NULL
@@ -145,8 +147,8 @@ CREATE TABLE test_drop_expression(
     LIKE orders,
     new_id int GENERATED ALWAYS AS ( 3 * ID ) STORED
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_expression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , new_id pg_catalog.int4 STORAGE PLAIN      GENERATED ALWAYS AS ((3 OPERATOR(pg_catalog.*) id)) STORED)      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_expression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , new_id pg_catalog.int4 STORAGE PLAIN      GENERATED ALWAYS AS ((3 OPERATOR(pg_catalog.*) id)) STORED)     
 ALTER TABLE test_drop_expression ALTER new_id DROP EXPRESSION;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP EXPRESSION %{if_exists}s", "type": "drop expression", "column": "new_id", "if_exists": ""}], "identity": {"objname": "test_drop_expression", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_drop_expression ALTER COLUMN new_id DROP EXPRESSION 
@@ -161,8 +163,8 @@ CREATE TABLE test_add_generated(
     col2 int NOT NULL,
     col3 int NOT NULL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_add_generated (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , col1 pg_catalog.int4 STORAGE PLAIN   NOT NULL   , col2 pg_catalog.int4 STORAGE PLAIN   NOT NULL   , col3 pg_catalog.int4 STORAGE PLAIN   NOT NULL   )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_add_generated (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , col1 pg_catalog.int4 STORAGE PLAIN   NOT NULL   , col2 pg_catalog.int4 STORAGE PLAIN   NOT NULL   , col3 pg_catalog.int4 STORAGE PLAIN   NOT NULL   )     
 ALTER TABLE test_add_generated ALTER col1 ADD GENERATED ALWAYS AS IDENTITY;
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
@@ -208,8 +210,8 @@ NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_generated (id1 pg_catalog.int4 STORAGE PLAIN     GENERATED BY DEFAULT AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id2 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id3 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id4 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id5 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id6 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id7 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_generated (id1 pg_catalog.int4 STORAGE PLAIN     GENERATED BY DEFAULT AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id2 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id3 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id4 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id5 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id6 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) , id7 pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )     
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
 NOTICE:  deparsed json: <NULL>
@@ -227,37 +229,37 @@ NOTICE:  re-formed command: <NULL>
 ALTER TABLE test_set_generated ALTER id1 SET GENERATED ALWAYS;
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id1", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "option": "ALWAYS"}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id1", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "option": "ALWAYS"}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id1 SET GENERATED ALWAYS SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 1
 ALTER TABLE test_set_generated ALTER id2 SET GENERATED BY DEFAULT;
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id2", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "option": "BY DEFAULT"}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id2", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "option": "BY DEFAULT"}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id2 SET GENERATED BY DEFAULT SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 1
 ALTER TABLE test_set_generated ALTER id3 SET INCREMENT BY 10;
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id3", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "10", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id3", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "10", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id3  SET CACHE 1 SET NO CYCLE SET INCREMENT BY 10 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 1
 ALTER TABLE test_set_generated ALTER id4 RESTART;
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id4", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id4", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}]}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id4  SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 1
 ALTER TABLE test_set_generated ALTER id5 RESTART WITH 101;
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id5", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "101", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id5", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "101", "clause": "restart"}]}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id5  SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 101
 ALTER TABLE test_set_generated ALTER id6 RESTART WITH 201;
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id6", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "201", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id6", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "present": false}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "201", "clause": "restart"}]}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id6  SET CACHE 1 SET NO CYCLE SET INCREMENT BY 1 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 201
 ALTER TABLE test_set_generated ALTER COLUMN id7 SET GENERATED BY DEFAULT SET INCREMENT BY 100 RESTART WITH 301;
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id7", "definition": {"fmt": "%{definition}s", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "option": "BY DEFAULT"}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "100", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "301", "clause": "restart"}]}}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{definition}s", "type": "set identity", "column": "id7", "definition": {"fmt": "%{identity_type}s %{seq_definition: }s", "identity_type": {"fmt": "SET GENERATED %{option}s", "option": "BY DEFAULT"}, "seq_definition": [{"fmt": "SET CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "SET %{no}s CYCLE", "clause": "cycle"}, {"fmt": "SET INCREMENT BY %{value}s", "value": "100", "clause": "seqincrement"}, {"fmt": "SET MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "SET MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "SET START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "301", "clause": "restart"}]}}], "identity": {"objname": "test_set_generated", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_generated ALTER COLUMN id7 SET GENERATED BY DEFAULT SET CACHE 1 SET NO CYCLE SET INCREMENT BY 100 SET MINVALUE 1 SET MAXVALUE 2147483647 SET START WITH 1 RESTART 301
 -- ALTER [ COLUMN ] column_name DROP IDENTITY [ IF EXISTS ]
 CREATE TABLE test_drop_identity(
@@ -266,8 +268,8 @@ CREATE TABLE test_drop_identity(
 );
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_identity (id pg_catalog.int4 STORAGE PLAIN      , id_generated pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_identity (id pg_catalog.int4 STORAGE PLAIN      , id_generated pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )     
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
 ALTER TABLE test_drop_identity ALTER id_generated DROP IDENTITY;
@@ -281,8 +283,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_drop_identity ALTER COLUMN
 CREATE TABLE test_set_statistics(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_statistics (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_statistics (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_statistics ALTER id SET STATISTICS 1;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STATISTICS %{statistics}n", "type": "set statistics", "column": "id", "statistics": 1}], "identity": {"objname": "test_set_statistics", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_statistics ALTER COLUMN id SET STATISTICS 1
@@ -290,8 +292,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_set_statistics ALTER COLUMN
 CREATE TABLE test_set_attribute(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_attribute (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_attribute (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_attribute ALTER name SET (n_distinct = 102);
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{option}s (%{options:, }s)", "column": "name", "option": "SET", "options": [{"fmt": "%{label}s = %{value}L", "label": {"fmt": "%{label}I", "label": "n_distinct"}, "value": "102"}]}], "identity": {"objname": "test_set_attribute", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_attribute ALTER COLUMN name SET (n_distinct = '102')
@@ -302,8 +304,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_set_attribute ALTER COLUMN
 CREATE TABLE test_reset_attribute(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_reset_attribute (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_reset_attribute (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_reset_attribute ALTER name RESET (n_distinct);
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I %{option}s (%{options:, }s)", "column": "name", "option": "RESET", "options": [{"fmt": "%{label}s", "label": {"fmt": "%{label}I", "label": "n_distinct"}}]}], "identity": {"objname": "test_reset_attribute", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_reset_attribute ALTER COLUMN name RESET (n_distinct)
@@ -315,8 +317,8 @@ CREATE TABLE test_set_storage(
     LIKE orders,
     product_name text
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_storage (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , product_name pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_storage (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , product_name pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 ALTER TABLE test_set_storage ALTER id SET STORAGE PLAIN;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STORAGE %{storage}s", "type": "set storage", "column": "id", "storage": "plain"}], "identity": {"objname": "test_set_storage", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage ALTER COLUMN id SET STORAGE plain
@@ -333,8 +335,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage ALTER COLUMN pr
 CREATE TABLE test_set_compression(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_compression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_compression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_compression ALTER name SET COMPRESSION "pglz";
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET COMPRESSION %{compression_method}s", "type": "set compression", "column": "name", "compression_method": "pglz"}], "identity": {"objname": "test_set_compression", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_compression ALTER COLUMN name SET COMPRESSION pglz
@@ -345,8 +347,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_set_compression ALTER COLUM
 CREATE TABLE test_add_table_constraint(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_add_table_constraint (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_add_table_constraint (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_add_table_constraint ADD PRIMARY KEY (id);
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}, {"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "test_add_table_constraint_pkey", "type": "add constraint", "definition": "PRIMARY KEY (id)"}], "identity": {"objname": "test_add_table_constraint", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_add_table_constraint ALTER COLUMN id SET NOT NULL, ADD CONSTRAINT test_add_table_constraint_pkey PRIMARY KEY (id)
@@ -384,8 +386,8 @@ CREATE TABLE test_alter_constraint_referenced(
     id3 int UNIQUE,
     id4 int UNIQUE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_constraint_referenced (id1 pg_catalog.int4 STORAGE PLAIN      , id2 pg_catalog.int4 STORAGE PLAIN      , id3 pg_catalog.int4 STORAGE PLAIN      , id4 pg_catalog.int4 STORAGE PLAIN      , CONSTRAINT test_alter_constraint_referenced_id1_key UNIQUE (id1), CONSTRAINT test_alter_constraint_referenced_id2_key UNIQUE (id2), CONSTRAINT test_alter_constraint_referenced_id3_key UNIQUE (id3), CONSTRAINT test_alter_constraint_referenced_id4_key UNIQUE (id4))      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_constraint_referenced (id1 pg_catalog.int4 STORAGE PLAIN      , id2 pg_catalog.int4 STORAGE PLAIN      , id3 pg_catalog.int4 STORAGE PLAIN      , id4 pg_catalog.int4 STORAGE PLAIN      , CONSTRAINT test_alter_constraint_referenced_id1_key UNIQUE (id1), CONSTRAINT test_alter_constraint_referenced_id2_key UNIQUE (id2), CONSTRAINT test_alter_constraint_referenced_id3_key UNIQUE (id3), CONSTRAINT test_alter_constraint_referenced_id4_key UNIQUE (id4))     
 CREATE TABLE test_alter_constraint(
     id1 int,
     id2 int,
@@ -397,8 +399,8 @@ CREATE TABLE test_alter_constraint(
     CONSTRAINT alter_cstr3 FOREIGN KEY (id3) REFERENCES test_alter_constraint_referenced (id3) DEFERRABLE INITIALLY DEFERRED,
     CONSTRAINT alter_cstr4 FOREIGN KEY (id4) REFERENCES test_alter_constraint_referenced (id4) DEFERRABLE INITIALLY IMMEDIATE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_constraint (id1 pg_catalog.int4 STORAGE PLAIN      , id2 pg_catalog.int4 STORAGE PLAIN      , id3 pg_catalog.int4 STORAGE PLAIN      , id4 pg_catalog.int4 STORAGE PLAIN      , id5 pg_catalog.int4 STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_constraint (id1 pg_catalog.int4 STORAGE PLAIN      , id2 pg_catalog.int4 STORAGE PLAIN      , id3 pg_catalog.int4 STORAGE PLAIN      , id4 pg_catalog.int4 STORAGE PLAIN      , id5 pg_catalog.int4 STORAGE PLAIN      )     
 NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint ADD CONSTRAINT alter_cstr1 FOREIGN KEY (id1) REFERENCES public.test_alter_constraint_referenced(id1) DEFERRABLE INITIALLY DEFERRED, ADD CONSTRAINT alter_cstr2 FOREIGN KEY (id2) REFERENCES public.test_alter_constraint_referenced(id2), ADD CONSTRAINT alter_cstr3 FOREIGN KEY (id3) REFERENCES public.test_alter_constraint_referenced(id3) DEFERRABLE INITIALLY DEFERRED, ADD CONSTRAINT alter_cstr4 FOREIGN KEY (id4) REFERENCES public.test_alter_constraint_referenced(id4) DEFERRABLE
 ALTER TABLE test_alter_constraint ALTER CONSTRAINT alter_cstr1 NOT DEFERRABLE;
@@ -417,8 +419,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint ALTER CONS
 CREATE TABLE test_validate_constraint(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_validate_constraint (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_validate_constraint (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_validate_constraint ADD CONSTRAINT test_validate_constraint_cstr CHECK (length(name) < 10) NOT VALID;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "test_validate_constraint_cstr", "type": "add constraint", "definition": "CHECK ((pg_catalog.length((name)::pg_catalog.text) OPERATOR(pg_catalog.<) 10)) NOT VALID"}], "identity": {"objname": "test_validate_constraint", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_validate_constraint ADD CONSTRAINT test_validate_constraint_cstr CHECK ((pg_catalog.length((name)::pg_catalog.text) OPERATOR(pg_catalog.<) 10)) NOT VALID
@@ -431,15 +433,15 @@ CREATE TABLE test_drop_constraint(
     CONSTRAINT test_drop_constraint_check CHECK (id < 100),
     CONSTRAINT test_drop_constraint_uniq UNIQUE (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_constraint (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_drop_constraint_check CHECK ((id OPERATOR(pg_catalog.<) 100)), CONSTRAINT test_drop_constraint_uniq UNIQUE (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_constraint (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_drop_constraint_check CHECK ((id OPERATOR(pg_catalog.<) 100)), CONSTRAINT test_drop_constraint_uniq UNIQUE (id))     
 CREATE TABLE test_drop_constraint_reference(
     id int REFERENCES test_drop_constraint (id),
     name varchar,
     CONSTRAINT test_drop_constraint_reference_cstr1 CHECK (length(name) < 10)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_constraint_reference (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT test_drop_constraint_reference_cstr1 CHECK ((pg_catalog.length((name)::pg_catalog.text) OPERATOR(pg_catalog.<) 10)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_drop_constraint_reference (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT test_drop_constraint_reference_cstr1 CHECK ((pg_catalog.length((name)::pg_catalog.text) OPERATOR(pg_catalog.<) 10)))     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "test_drop_constraint_reference_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id) REFERENCES public.test_drop_constraint(id)"}], "identity": {"objname": "test_drop_constraint_reference", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_drop_constraint_reference ADD CONSTRAINT test_drop_constraint_reference_id_fkey FOREIGN KEY (id) REFERENCES public.test_drop_constraint(id)
 ALTER TABLE test_drop_constraint_reference DROP CONSTRAINT test_drop_constraint_reference_cstr1;
@@ -452,8 +454,10 @@ ALTER TABLE test_drop_constraint DROP CONSTRAINT IF EXISTS test_drop_constraint_
 NOTICE:  constraint "test_drop_constraint_check" of relation "test_drop_constraint" does not exist, skipping
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP CONSTRAINT %{if_exists}s %{constraint}I %{cascade}s", "type": "drop constraint", "cascade": "", "if_exists": "IF EXISTS", "constraint": "test_drop_constraint_check"}], "identity": {"objname": "test_drop_constraint", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_drop_constraint DROP CONSTRAINT IF EXISTS test_drop_constraint_check 
--- TOFIX
--- ALTER TABLE test_drop_constraint DROP CONSTRAINT test_drop_constraint_uniq CASCADE;
+ALTER TABLE test_drop_constraint DROP CONSTRAINT test_drop_constraint_uniq CASCADE;
+NOTICE:  drop cascades to constraint test_drop_constraint_reference_id_fkey on table test_drop_constraint_reference
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DROP CONSTRAINT %{if_exists}s %{constraint}I %{cascade}s", "type": "drop constraint", "cascade": "CASCADE", "if_exists": "", "constraint": "test_drop_constraint_uniq"}], "identity": {"objname": "test_drop_constraint", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_drop_constraint DROP CONSTRAINT  test_drop_constraint_uniq CASCADE
 -- TODO: This should be tested with TRIGGER related testing
 -- DISABLE TRIGGER [ trigger_name | ALL | USER ]
 -- ENABLE TRIGGER [ trigger_name | ALL | USER ]
@@ -482,8 +486,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_enable_always_rule ENABLE R
 CREATE TABLE test_disable_row_security(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_disable_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_disable_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_disable_row_security DISABLE ROW LEVEL SECURITY;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DISABLE ROW LEVEL SECURITY", "type": "disable row security"}], "identity": {"objname": "test_disable_row_security", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_disable_row_security DISABLE ROW LEVEL SECURITY
@@ -491,8 +495,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_disable_row_security DISABL
 CREATE TABLE test_enable_row_security(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_enable_row_security ENABLE ROW LEVEL SECURITY;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ENABLE ROW LEVEL SECURITY", "type": "enable row security"}], "identity": {"objname": "test_enable_row_security", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_enable_row_security ENABLE ROW LEVEL SECURITY
@@ -500,8 +504,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_enable_row_security ENABLE
 CREATE TABLE test_force_row_security(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_force_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_force_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_force_row_security FORCE ROW LEVEL SECURITY;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "FORCE ROW LEVEL SECURITY"}], "identity": {"objname": "test_force_row_security", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_force_row_security FORCE ROW LEVEL SECURITY
@@ -509,8 +513,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_force_row_security FORCE RO
 CREATE TABLE test_no_force_row_security(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_no_force_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_no_force_row_security (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_no_force_row_security NO FORCE ROW LEVEL SECURITY;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "NO FORCE ROW LEVEL SECURITY"}], "identity": {"objname": "test_no_force_row_security", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_no_force_row_security NO FORCE ROW LEVEL SECURITY
@@ -519,8 +523,8 @@ CREATE TABLE test_cluster(
     LIKE orders,
     PRIMARY KEY (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_cluster (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_cluster_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_cluster (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_cluster_pkey PRIMARY KEY (id))     
 ALTER TABLE test_cluster CLUSTER ON test_cluster_pkey;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "CLUSTER ON %{index}I", "type": "cluster on", "index": "test_cluster_pkey"}], "identity": {"objname": "test_cluster", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_cluster CLUSTER ON test_cluster_pkey
@@ -529,8 +533,8 @@ CREATE TABLE test_without_cluster(
     LIKE orders,
     PRIMARY KEY (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_without_cluster (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_without_cluster_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_without_cluster (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_without_cluster_pkey PRIMARY KEY (id))     
 ALTER TABLE test_without_cluster CLUSTER ON test_without_cluster_pkey;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "CLUSTER ON %{index}I", "type": "cluster on", "index": "test_without_cluster_pkey"}], "identity": {"objname": "test_without_cluster", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_without_cluster CLUSTER ON test_without_cluster_pkey
@@ -541,8 +545,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_without_cluster SET WITHOUT
 CREATE TABLE test_set_without_oids(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_without_oids (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_without_oids (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_without_oids SET WITHOUT OIDS;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET WITHOUT OIDS", "type": "set without oids"}], "identity": {"objname": "test_set_without_oids", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_without_oids SET WITHOUT OIDS
@@ -550,8 +554,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_set_without_oids SET WITHOU
 CREATE TABLE test_set_access_method(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_access_method (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_access_method (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_access_method SET ACCESS METHOD heap;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET ACCESS METHOD %{access_method}I", "type": "set access method", "access_method": "heap"}], "identity": {"objname": "test_set_access_method", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_access_method SET ACCESS METHOD heap
@@ -559,8 +563,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_set_access_method SET ACCES
 CREATE TABLE test_set_tablespace(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_tablespace (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_tablespace (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_tablespace SET TABLESPACE pg_default;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET TABLESPACE %{tablespace}I", "type": "set tablespace", "tablespace": "pg_default"}], "identity": {"objname": "test_set_tablespace", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_tablespace SET TABLESPACE pg_default
@@ -568,16 +572,16 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_set_tablespace SET TABLESPA
 CREATE TABLE test_set_logged(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_logged (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_logged (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_logged SET LOGGED;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET LOGGED", "type": "set logged"}], "identity": {"objname": "test_set_logged", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_logged SET LOGGED
 CREATE TABLE test_set_unlogged(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_unlogged (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_unlogged (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_unlogged SET UNLOGGED;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET UNLOGGED", "type": "set unlogged"}], "identity": {"objname": "test_set_unlogged", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_unlogged SET UNLOGGED
@@ -585,16 +589,16 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_set_unlogged SET UNLOGGED
 CREATE TABLE test_set_storage_params1(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_storage_params1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_storage_params1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_storage_params1 SET (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true);
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage_params1 SET (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')
 CREATE TABLE test_set_storage_params2(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_storage_params2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_storage_params2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_storage_params2 SET (vacuum_index_cleanup = ON);
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "%{set_reset}s (%{options:, }s)", "options": [{"fmt": "%{label}s = %{value}L", "label": {"fmt": "%{label}I", "label": "vacuum_index_cleanup"}, "value": "on"}], "set_reset": "SET"}], "identity": {"objname": "test_set_storage_params2", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage_params2 SET (vacuum_index_cleanup = 'on')
@@ -602,8 +606,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage_params2 SET (va
 CREATE TABLE test_reset_storage_params1(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_reset_storage_params1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_reset_storage_params1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_storage_params1 SET (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true);
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage_params1 SET (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')
@@ -613,8 +617,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_reset_storage_params1 RESET
 CREATE TABLE test_reset_storage_params2(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_reset_storage_params2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_reset_storage_params2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_storage_params2 SET (vacuum_index_cleanup = ON);
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "%{set_reset}s (%{options:, }s)", "options": [{"fmt": "%{label}s = %{value}L", "label": {"fmt": "%{label}I", "label": "vacuum_index_cleanup"}, "value": "on"}], "set_reset": "SET"}], "identity": {"objname": "test_set_storage_params2", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_set_storage_params2 SET (vacuum_index_cleanup = 'on')
@@ -625,14 +629,14 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_reset_storage_params2 RESET
 CREATE TABLE test_inherit_parent(
     parent_id int
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_inherit_parent (parent_id pg_catalog.int4 STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_inherit_parent (parent_id pg_catalog.int4 STORAGE PLAIN      )     
 CREATE TABLE test_inherit_child(
     parent_id int,
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_inherit_child (parent_id pg_catalog.int4 STORAGE PLAIN      , id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_inherit_child (parent_id pg_catalog.int4 STORAGE PLAIN      , id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_inherit_child INHERIT test_inherit_parent;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "INHERIT %{parent}D", "type": "inherit", "parent": {"objname": "test_inherit_parent", "schemaname": "public"}}], "identity": {"objname": "test_inherit_child", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_inherit_child INHERIT public.test_inherit_parent
@@ -640,13 +644,13 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_inherit_child INHERIT publi
 CREATE TABLE test_no_inherit_parent(
     parent_id int
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_no_inherit_parent (parent_id pg_catalog.int4 STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_no_inherit_parent (parent_id pg_catalog.int4 STORAGE PLAIN      )     
 CREATE TABLE test_no_inherit_child(
     LIKE orders
 ) INHERITS (test_no_inherit_parent);
-NOTICE:  re-formed command: CREATE  TABLE  public.test_no_inherit_child (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      ) INHERITS (public.test_no_inherit_parent)     
+NOTICE:  re-formed command: CREATE  TABLE  public.test_no_inherit_child (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      ) INHERITS (public.test_no_inherit_parent)    
 ALTER TABLE test_no_inherit_child NO INHERIT test_no_inherit_parent;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "NO INHERIT %{parent}D", "type": "drop inherit", "parent": {"objname": "test_no_inherit_parent", "schemaname": "public"}}], "identity": {"objname": "test_no_inherit_child", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_no_inherit_child NO INHERIT public.test_no_inherit_parent
@@ -655,15 +659,15 @@ CREATE TABLE test_type(
     id int,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_type (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_type (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 ALTER TABLE test_type OF test_type_product_type;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "OF %{type_of}T", "type": "add of", "type_of": {"typmod": "", "typarray": false, "typename": "test_type_product_type", "schemaname": "public"}}], "identity": {"objname": "test_type", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_type OF public.test_type_product_type
 -- NOT OF
 CREATE TABLE test_type_not_of OF test_type_product_type;
-NOTICE:  re-formed command: CREATE  TABLE  public.test_type_not_of OF public.test_type_product_type      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_type_not_of OF public.test_type_product_type     
 ALTER TABLE test_type_not_of NOT OF;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "NOT OF", "type": "not of"}], "identity": {"objname": "test_type_not_of", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_type_not_of NOT OF
@@ -673,8 +677,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_type_not_of NOT OF
 CREATE TABLE test_replica_identity1(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_replica_identity1 REPLICA IDENTITY DEFAULT;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "REPLICA IDENTITY %{ident}s", "type": "replica identity", "ident": "DEFAULT"}], "identity": {"objname": "test_replica_identity1", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_replica_identity1 REPLICA IDENTITY DEFAULT
@@ -682,24 +686,24 @@ CREATE TABLE test_replica_identity2(
     LIKE orders,
     PRIMARY KEY (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_replica_identity2_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_replica_identity2_pkey PRIMARY KEY (id))     
 ALTER TABLE test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_pkey;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "REPLICA IDENTITY %{ident}s", "type": "replica identity", "ident": {"fmt": "USING INDEX %{index}I", "index": "test_replica_identity2_pkey"}}], "identity": {"objname": "test_replica_identity2", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_replica_identity2 REPLICA IDENTITY USING INDEX test_replica_identity2_pkey
 CREATE TABLE test_replica_identity3(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity3 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity3 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_replica_identity3 REPLICA IDENTITY FULL;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "REPLICA IDENTITY %{ident}s", "type": "replica identity", "ident": "FULL"}], "identity": {"objname": "test_replica_identity3", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_replica_identity3 REPLICA IDENTITY FULL
 CREATE TABLE test_replica_identity4(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity4 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_replica_identity4 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_replica_identity4 REPLICA IDENTITY NOTHING;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "REPLICA IDENTITY %{ident}s", "type": "replica identity", "ident": "NOTHING"}], "identity": {"objname": "test_replica_identity4", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_replica_identity4 REPLICA IDENTITY NOTHING
@@ -707,8 +711,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_replica_identity4 REPLICA I
 CREATE TABLE test_alter_col_name(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_col_name (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_col_name (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_alter_col_name RENAME id TO new_id;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{if_exists}s %{identity}D RENAME COLUMN %{colname}I TO %{newname}I", "colname": "id", "newname": "new_id", "objtype": "TABLE", "identity": {"objname": "test_alter_col_name", "schemaname": "public"}, "if_exists": ""}
 NOTICE:  re-formed command: ALTER TABLE  public.test_alter_col_name RENAME COLUMN id TO new_id
@@ -720,8 +724,8 @@ CREATE TABLE test_alter_constraint_name(
     LIKE orders,
     CONSTRAINT test_alter_constraint_name_old CHECK (id > 10)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_constraint_name (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_alter_constraint_name_old CHECK ((id OPERATOR(pg_catalog.>) 10)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_alter_constraint_name (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      , CONSTRAINT test_alter_constraint_name_old CHECK ((id OPERATOR(pg_catalog.>) 10)))     
 ALTER TABLE test_alter_constraint_name RENAME CONSTRAINT test_alter_constraint_name_old TO test_alter_constraint_name_new;
 NOTICE:  deparsed json: {"fmt": "ALTER TABLE %{only}s %{identity}D RENAME CONSTRAINT %{oldname}I TO %{newname}I", "only": "", "newname": "test_alter_constraint_name_new", "oldname": "test_alter_constraint_name_old", "identity": {"objname": "test_alter_constraint_name", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint_name RENAME CONSTRAINT test_alter_constraint_name_old TO test_alter_constraint_name_new
@@ -729,8 +733,8 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_alter_constraint_name RENAM
 CREATE TABLE test_rename_table(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_rename_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_rename_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_rename_table RENAME to new_test_rename_table;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{if_exists}s %{identity}D RENAME TO %{newname}I", "newname": "new_test_rename_table", "objtype": "TABLE", "identity": {"objname": "test_rename_table", "schemaname": "public"}, "if_exists": ""}
 NOTICE:  re-formed command: ALTER TABLE  public.test_rename_table RENAME TO new_test_rename_table
@@ -738,85 +742,103 @@ NOTICE:  re-formed command: ALTER TABLE  public.test_rename_table RENAME TO new_
 CREATE TABLE test_set_schema(
     LIKE orders
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_set_schema (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_set_schema (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 ALTER TABLE test_set_schema SET SCHEMA new_test_schema;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{identity}s SET SCHEMA %{newschema}I", "objtype": "TABLE", "identity": "public.test_set_schema", "newschema": "new_test_schema"}
 NOTICE:  re-formed command: ALTER TABLE public.test_set_schema SET SCHEMA new_test_schema
 -- ALTER TABLE ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ]
 --     SET TABLESPACE new_tablespace [ NOWAIT ]
--- TOFIX: can not be caught by ddl_command_end event trigger.
--- Deparse of T_AlterTableMoveAllStmt is not supported,
--- TABLESPACE commands (global object commands) are also not supported.
--- ALTER TABLE ALL IN TABLESPACE pg_default SET TABLESPACE pg_default;
--- ALTER TABLE ALL IN TABLESPACE pg_default OWNED BY ddl_testing_role SET TABLESPACE pg_default;
+-- TABLESPACE commands (global object commands) are not supported.
+SET ROLE ddl_testing_role;
+CREATE TABLE test_all_in_tablespace(
+    c1 int
+);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_all_in_tablespace (c1 pg_catalog.int4 STORAGE PLAIN      )     
+ALTER TABLE ALL IN TABLESPACE pg_default OWNED BY ddl_testing_role SET TABLESPACE regress_tblspace;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET TABLESPACE %{tablespace}I", "type": "set tablespace", "tablespace": "regress_tblspace"}], "identity": {"objname": "test_all_in_tablespace", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_all_in_tablespace SET TABLESPACE regress_tblspace
+ALTER TABLE ALL IN TABLESPACE regress_tblspace SET TABLESPACE pg_default;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "SET TABLESPACE %{tablespace}I", "type": "set tablespace", "tablespace": "pg_default"}], "identity": {"objname": "test_all_in_tablespace", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_all_in_tablespace SET TABLESPACE pg_default
+RESET ROLE;
 -- ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT }
 CREATE TABLE test_partition_attach_range(
     LIKE orders
 ) PARTITION BY RANGE (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_range (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY RANGE (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_range (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY RANGE (id)   
 CREATE TABLE test_partition_attach_range_p_1(
     LIKE test_partition_attach_range
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_range_p_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
--- TOFIX
--- ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_1 DEFAULT;
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_range_p_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
+ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_1 DEFAULT;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ATTACH PARTITION %{partition_identity}D %{partition_bound}s", "type": "attach partition", "partition_bound": "DEFAULT", "partition_identity": {"objname": "test_partition_attach_range_p_1", "schemaname": "public"}}], "identity": {"objname": "test_partition_attach_range", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_partition_attach_range ATTACH PARTITION public.test_partition_attach_range_p_1 DEFAULT
 CREATE TABLE test_partition_attach_range_p_2(
     LIKE test_partition_attach_range
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_range_p_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
--- TOFIX
--- ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_2 FOR VALUES FROM (100) TO (200);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_range_p_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
+ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_2 FOR VALUES FROM (100) TO (200);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ATTACH PARTITION %{partition_identity}D %{partition_bound}s", "type": "attach partition", "partition_bound": "FOR VALUES FROM (100) TO (200)", "partition_identity": {"objname": "test_partition_attach_range_p_2", "schemaname": "public"}}], "identity": {"objname": "test_partition_attach_range", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_partition_attach_range ATTACH PARTITION public.test_partition_attach_range_p_2 FOR VALUES FROM (100) TO (200)
 CREATE TABLE test_partition_attach_hash(
     LIKE orders
 ) PARTITION BY HASH (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_hash (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY HASH (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_hash (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY HASH (id)   
 CREATE TABLE test_partition_attach_hash_p(
     LIKE test_partition_attach_hash
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_hash_p (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
--- TOFIX
--- ALTER TABLE test_partition_attach_hash ATTACH PARTITION test_partition_attach_hash_p FOR VALUES WITH (MODULUS 10, REMAINDER 1);
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_hash_p (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
+ALTER TABLE test_partition_attach_hash ATTACH PARTITION test_partition_attach_hash_p FOR VALUES WITH (MODULUS 10, REMAINDER 1);
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ATTACH PARTITION %{partition_identity}D %{partition_bound}s", "type": "attach partition", "partition_bound": "FOR VALUES WITH (modulus 10, remainder 1)", "partition_identity": {"objname": "test_partition_attach_hash_p", "schemaname": "public"}}], "identity": {"objname": "test_partition_attach_hash", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_partition_attach_hash ATTACH PARTITION public.test_partition_attach_hash_p FOR VALUES WITH (modulus 10, remainder 1)
 CREATE TABLE test_partition_attach_list(
     LIKE orders
 ) PARTITION BY LIST (name);
-NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY LIST (name)    
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY LIST (name)   
 CREATE TABLE test_partition_attach_list_p1(
     LIKE test_partition_attach_list
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_list_p1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_list_p1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 CREATE TABLE test_partition_attach_list_p2(
     LIKE test_partition_attach_list
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_list_p2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
--- TOFIX
--- ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p1 FOR VALUES IN ('key1');
--- ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p2 FOR VALUES IN ('key2', 'key3');
+NOTICE:  re-formed command: CREATE  TABLE  public.test_partition_attach_list_p2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
+ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p1 FOR VALUES IN ('key1');
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ATTACH PARTITION %{partition_identity}D %{partition_bound}s", "type": "attach partition", "partition_bound": "FOR VALUES IN ('key1')", "partition_identity": {"objname": "test_partition_attach_list_p1", "schemaname": "public"}}], "identity": {"objname": "test_partition_attach_list", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_partition_attach_list ATTACH PARTITION public.test_partition_attach_list_p1 FOR VALUES IN ('key1')
+ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p2 FOR VALUES IN ('key2', 'key3');
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ATTACH PARTITION %{partition_identity}D %{partition_bound}s", "type": "attach partition", "partition_bound": "FOR VALUES IN ('key2', 'key3')", "partition_identity": {"objname": "test_partition_attach_list_p2", "schemaname": "public"}}], "identity": {"objname": "test_partition_attach_list", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_partition_attach_list ATTACH PARTITION public.test_partition_attach_list_p2 FOR VALUES IN ('key2', 'key3')
 -- DETACH PARTITION partition_name [ CONCURRENTLY | FINALIZE ]
 CREATE TABLE test_detach_partition(
     LIKE orders
 ) PARTITION BY RANGE (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY RANGE (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )  PARTITION BY RANGE (id)   
 CREATE TABLE test_detach_partition_p1 PARTITION OF test_detach_partition FOR VALUES FROM (1) TO (100);
-NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition_p1 PARTITION OF public.test_detach_partition  FOR VALUES FROM (1) TO (100)     
+NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition_p1 PARTITION OF public.test_detach_partition  FOR VALUES FROM (1) TO (100)    
 CREATE TABLE test_detach_partition_p2 PARTITION OF test_detach_partition FOR VALUES FROM (101) TO (200);
-NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition_p2 PARTITION OF public.test_detach_partition  FOR VALUES FROM (101) TO (200)     
+NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition_p2 PARTITION OF public.test_detach_partition  FOR VALUES FROM (101) TO (200)    
 CREATE TABLE test_detach_partition_p3 PARTITION OF test_detach_partition FOR VALUES FROM (201) TO (300);
-NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition_p3 PARTITION OF public.test_detach_partition  FOR VALUES FROM (201) TO (300)     
--- TOFIX
--- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p1;
--- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p2 CONCURRENTLY;
+NOTICE:  re-formed command: CREATE  TABLE  public.test_detach_partition_p3 PARTITION OF public.test_detach_partition  FOR VALUES FROM (201) TO (300)    
+ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p1;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DETACH PARTITION %{partition_identity}D %{concurrent}s", "type": "detach partition", "concurrent": "", "partition_identity": {"objname": "test_detach_partition_p1", "schemaname": "public"}}], "identity": {"objname": "test_detach_partition", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_detach_partition DETACH PARTITION public.test_detach_partition_p1 
+ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p2 CONCURRENTLY;
+NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "DETACH PARTITION %{partition_identity}D %{concurrent}s", "type": "detach partition", "concurrent": "CONCURRENTLY", "partition_identity": {"objname": "test_detach_partition_p2", "schemaname": "public"}}], "identity": {"objname": "test_detach_partition", "schemaname": "public"}}
+NOTICE:  re-formed command: ALTER TABLE  public.test_detach_partition DETACH PARTITION public.test_detach_partition_p2 CONCURRENTLY
 -- TOFIX: FINALIZE option is not testable
 -- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p3 FINALIZE;
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/constraints.out b/src/test/modules/test_ddl_deparse_regress/expected/constraints.out
index 45058db174..9c24b795d0 100644
--- a/src/test/modules/test_ddl_deparse_regress/expected/constraints.out
+++ b/src/test/modules/test_ddl_deparse_regress/expected/constraints.out
@@ -5,42 +5,42 @@ CREATE TABLE col_cstr_not_null(
     id int CONSTRAINT id_constraint NOT NULL,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_not_null (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_not_null (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 --  NULL |
 CREATE TABLE col_cstr_null(
     id int NULL,
     name varchar CONSTRAINT name_constraint NOT NULL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default" NOT NULL   )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default" NOT NULL   )     
 --  CHECK ( expression ) [ NO INHERIT ] |
 CREATE TABLE col_cstr_check(
     id int CHECK (id > 10),
     name varchar NOT NULL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_check (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default" NOT NULL   , CONSTRAINT col_cstr_check_id_check CHECK ((id OPERATOR(pg_catalog.>) 10)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_check (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default" NOT NULL   , CONSTRAINT col_cstr_check_id_check CHECK ((id OPERATOR(pg_catalog.>) 10)))     
 CREATE TABLE col_cstr_check_no_inherit(
     id int CHECK (id > 10) NO INHERIT,
     name varchar NOT NULL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_check_no_inherit (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default" NOT NULL   , CONSTRAINT col_cstr_check_no_inherit_id_check CHECK ((id OPERATOR(pg_catalog.>) 10)) NO INHERIT)      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_check_no_inherit (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default" NOT NULL   , CONSTRAINT col_cstr_check_no_inherit_id_check CHECK ((id OPERATOR(pg_catalog.>) 10)) NO INHERIT)     
 --  DEFAULT default_expr |
 CREATE TABLE col_cstr_default(
     id int NOT NULL,
     name varchar DEFAULT 'foo'
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_default (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"  DEFAULT 'foo'::character varying  )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_default (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"  DEFAULT 'foo'::character varying  )     
 --  GENERATED ALWAYS AS ( generation_expr ) STORED |
 CREATE TABLE col_cstr_generated_always_as(
     id int NOT NULL,
     id_generated int GENERATED ALWAYS AS ( id * 10 ) STORED
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_generated_always_as (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , id_generated pg_catalog.int4 STORAGE PLAIN      GENERATED ALWAYS AS ((id OPERATOR(pg_catalog.*) 10)) STORED)      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_generated_always_as (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , id_generated pg_catalog.int4 STORAGE PLAIN      GENERATED ALWAYS AS ((id OPERATOR(pg_catalog.*) 10)) STORED)     
 --  GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ] |
 CREATE TABLE col_cstr_generated_always_as_identity(
     id int NOT NULL,
@@ -48,8 +48,8 @@ CREATE TABLE col_cstr_generated_always_as_identity(
 );
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_generated_always_as_identity (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , id_generated pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_generated_always_as_identity (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , id_generated pg_catalog.int4 STORAGE PLAIN     GENERATED ALWAYS AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )     
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
 CREATE TABLE col_cstr_generated_by_default_as_identity_with_options(
@@ -58,8 +58,8 @@ CREATE TABLE col_cstr_generated_by_default_as_identity_with_options(
 );
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_generated_by_default_as_identity_with_options (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , id_generated pg_catalog.int4 STORAGE PLAIN     GENERATED BY DEFAULT AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 10 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_generated_by_default_as_identity_with_options (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , id_generated pg_catalog.int4 STORAGE PLAIN     GENERATED BY DEFAULT AS IDENTITY ( CACHE 1 NO CYCLE INCREMENT BY 10 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 ) )     
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
 --  UNIQUE [ NULLS [ NOT ] DISTINCT ] |
@@ -67,43 +67,43 @@ CREATE TABLE col_cstr_unique(
     id int NOT NULL,
     name varchar UNIQUE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_unique (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_unique_name_key UNIQUE (name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_unique (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_unique_name_key UNIQUE (name))     
 CREATE TABLE col_cstr_unique_nulls_distinct(
     id int NOT NULL,
     name varchar UNIQUE NULLS DISTINCT
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_unique_nulls_distinct (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_unique_nulls_distinct_name_key UNIQUE (name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_unique_nulls_distinct (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_unique_nulls_distinct_name_key UNIQUE (name))     
 CREATE TABLE col_cstr_unique_nulls_not_distinct(
     id int NOT NULL,
     name varchar UNIQUE NULLS NOT DISTINCT
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_unique_nulls_not_distinct (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_unique_nulls_not_distinct_name_key UNIQUE NULLS NOT DISTINCT (name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_unique_nulls_not_distinct (id pg_catalog.int4 STORAGE PLAIN   NOT NULL   , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_unique_nulls_not_distinct_name_key UNIQUE NULLS NOT DISTINCT (name))     
 --  PRIMARY KEY |
 CREATE TABLE col_cstr_primary_key(
     id int PRIMARY KEY,
     name varchar UNIQUE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_primary_key (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_primary_key_name_key UNIQUE (name), CONSTRAINT col_cstr_primary_key_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_primary_key (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_primary_key_name_key UNIQUE (name), CONSTRAINT col_cstr_primary_key_pkey PRIMARY KEY (id))     
 --  REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
 --    [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
 CREATE TABLE col_cstr_reference_table_default(
     id int REFERENCES col_cstr_primary_key,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_default_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id) REFERENCES public.col_cstr_primary_key(id)"}], "identity": {"objname": "col_cstr_reference_table_default", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_default ADD CONSTRAINT col_cstr_reference_table_default_id_fkey FOREIGN KEY (id) REFERENCES public.col_cstr_primary_key(id)
 CREATE TABLE col_cstr_reference_table_column(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)"}], "identity": {"objname": "col_cstr_reference_table_column", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column ADD CONSTRAINT col_cstr_reference_table_column_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)
 -- [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
@@ -113,16 +113,16 @@ CREATE TABLE col_cstr_reference_table_column_match_full(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) MATCH FULL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_match_full (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_match_full (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_match_full_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL"}], "identity": {"objname": "col_cstr_reference_table_column_match_full", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_match_full ADD CONSTRAINT col_cstr_reference_table_column_match_full_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL
 CREATE TABLE col_cstr_reference_table_column_match_simple(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) MATCH SIMPLE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_match_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_match_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_match_simple_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)"}], "identity": {"objname": "col_cstr_reference_table_column_match_simple", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_match_simple ADD CONSTRAINT col_cstr_reference_table_column_match_simple_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)
 -- [ ON DELETE referential_action ]
@@ -130,32 +130,32 @@ CREATE TABLE col_cstr_reference_table_column_on_delete_no_action(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON DELETE NO ACTION
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_no_action (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_no_action (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_no_action_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_no_action", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_no_action ADD CONSTRAINT col_cstr_reference_table_column_on_delete_no_action_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)
 CREATE TABLE col_cstr_reference_table_column_on_delete_restrict(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON DELETE RESTRICT
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_restrict (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_restrict (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_restrict_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE RESTRICT"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_restrict", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_restrict ADD CONSTRAINT col_cstr_reference_table_column_on_delete_restrict_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE RESTRICT
 CREATE TABLE col_cstr_reference_table_column_on_delete_cascade(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON DELETE CASCADE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_cascade (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_cascade (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_cascade_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE CASCADE"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_cascade", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_cascade ADD CONSTRAINT col_cstr_reference_table_column_on_delete_cascade_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE CASCADE
 CREATE TABLE col_cstr_reference_table_column_on_delete_set_null(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET NULL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_set_null_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET NULL"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_set_null", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_set_null ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_null_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET NULL
 CREATE TABLE col_cstr_reference_table_column_on_delete_set_null_with_column(
@@ -163,24 +163,24 @@ CREATE TABLE col_cstr_reference_table_column_on_delete_set_null_with_column(
     name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET NULL (name),
     foo varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET NULL (foo)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_null_with_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , foo pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_null_with_column (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , foo pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_set_null_with_column ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_null_wi_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET NULL (name), ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_null_wit_foo_fkey FOREIGN KEY (foo) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET NULL (foo)
 CREATE TABLE col_cstr_reference_table_column_on_delete_set_default(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET DEFAULT
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_delete_set_default_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET DEFAULT"}], "identity": {"objname": "col_cstr_reference_table_column_on_delete_set_default", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_set_default ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_default_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET DEFAULT
 CREATE TABLE col_cstr_reference_table_column_on_delete_set_default_with_col(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON DELETE SET DEFAULT (name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_default_with_col (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_delete_set_default_with_col (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_delete_set_default_with_col ADD CONSTRAINT col_cstr_reference_table_column_on_delete_set_defaul_name_fkey1 FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON DELETE SET DEFAULT (name)
 -- [ ON UPDATE referential_action ]
@@ -188,40 +188,40 @@ CREATE TABLE col_cstr_reference_table_column_on_update_no_action(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE NO ACTION
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_no_action (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_no_action (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_no_action_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_no_action", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_no_action ADD CONSTRAINT col_cstr_reference_table_column_on_update_no_action_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name)
 CREATE TABLE col_cstr_reference_table_column_on_update_restrict(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE RESTRICT
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_restrict (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_restrict (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_restrict_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE RESTRICT"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_restrict", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_restrict ADD CONSTRAINT col_cstr_reference_table_column_on_update_restrict_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE RESTRICT
 CREATE TABLE col_cstr_reference_table_column_on_update_cascade(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE CASCADE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_cascade (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_cascade (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_cascade_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE CASCADE"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_cascade", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_cascade ADD CONSTRAINT col_cstr_reference_table_column_on_update_cascade_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE CASCADE
 CREATE TABLE col_cstr_reference_table_column_on_update_set_null(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE SET NULL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_set_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_set_null (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_set_null_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE SET NULL"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_set_null", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_set_null ADD CONSTRAINT col_cstr_reference_table_column_on_update_set_null_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE SET NULL
 CREATE TABLE col_cstr_reference_table_column_on_update_set_default(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) ON UPDATE SET DEFAULT
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_set_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_on_update_set_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_on_update_set_default_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE SET DEFAULT"}], "identity": {"objname": "col_cstr_reference_table_column_on_update_set_default", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_on_update_set_default ADD CONSTRAINT col_cstr_reference_table_column_on_update_set_default_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) ON UPDATE SET DEFAULT
 -- complex combinations
@@ -229,16 +229,16 @@ CREATE TABLE col_cstr_reference_table_column_complex_combination1(
     id int,
     name varchar REFERENCES col_cstr_primary_key (name) MATCH FULL ON DELETE NO ACTION ON UPDATE NO ACTION
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_complex_combination1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_complex_combination1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "col_cstr_reference_table_column_complex_combination1_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL"}], "identity": {"objname": "col_cstr_reference_table_column_complex_combination1", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_complex_combination1 ADD CONSTRAINT col_cstr_reference_table_column_complex_combination1_name_fkey FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL
 CREATE TABLE col_cstr_reference_table_column_complex_combination2(
     id int REFERENCES col_cstr_primary_key MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET NULL,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_complex_combination2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_reference_table_column_complex_combination2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_reference_table_column_complex_combination2 ADD CONSTRAINT col_cstr_reference_table_column_complex_combination2_id_fkey FOREIGN KEY (id) REFERENCES public.col_cstr_primary_key(id) MATCH FULL ON UPDATE SET NULL ON DELETE SET DEFAULT
 -- [ DEFERRABLE | NOT DEFERRABLE ]
@@ -246,34 +246,34 @@ CREATE TABLE col_cstr_deferable(
     id int,
     name varchar UNIQUE DEFERRABLE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_deferable_name_key UNIQUE (name) DEFERRABLE)      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_deferable_name_key UNIQUE (name) DEFERRABLE)     
 CREATE TABLE col_cstr_not_deferable(
     id int PRIMARY KEY NOT DEFERRABLE,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_not_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_not_deferable_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_not_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_not_deferable_pkey PRIMARY KEY (id))     
 -- [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 CREATE TABLE col_cstr_initially_deferred(
     id int PRIMARY KEY INITIALLY DEFERRED,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_initially_deferred (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_initially_deferred_pkey PRIMARY KEY (id) DEFERRABLE INITIALLY DEFERRED)      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_initially_deferred (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_initially_deferred_pkey PRIMARY KEY (id) DEFERRABLE INITIALLY DEFERRED)     
 CREATE TABLE col_cstr_initially_immediate(
     id int,
     name varchar UNIQUE INITIALLY IMMEDIATE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_initially_immediate (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_initially_immediate_name_key UNIQUE (name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_initially_immediate (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT col_cstr_initially_immediate_name_key UNIQUE (name))     
 -- complex combination
 CREATE TABLE col_cstr_complex_combination(
     id int,
     name varchar CONSTRAINT name_constraint REFERENCES col_cstr_primary_key (name) MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_complex_combination (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.col_cstr_complex_combination (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "name_constraint", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL ON UPDATE SET NULL ON DELETE SET DEFAULT"}], "identity": {"objname": "col_cstr_complex_combination", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.col_cstr_complex_combination ADD CONSTRAINT name_constraint FOREIGN KEY (name) REFERENCES public.col_cstr_primary_key(name) MATCH FULL ON UPDATE SET NULL ON DELETE SET DEFAULT
 -- part 4: table constraints
@@ -284,180 +284,180 @@ CREATE TABLE tbl_cstr_check_1(
     id int,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_check_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT id_constraint CHECK ((id OPERATOR(pg_catalog.>) 10)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_check_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT id_constraint CHECK ((id OPERATOR(pg_catalog.>) 10)))     
 CREATE TABLE tbl_cstr_check_2(
     id int,
     name varchar,
     CONSTRAINT table_check CHECK (id > 10) NO INHERIT
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_check_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT table_check CHECK ((id OPERATOR(pg_catalog.>) 10)) NO INHERIT)      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_check_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT table_check CHECK ((id OPERATOR(pg_catalog.>) 10)) NO INHERIT)     
 CREATE TABLE tbl_cstr_check_no_inherit(
     id int,
     name varchar,
     CHECK (id > 10) NO INHERIT
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_check_no_inherit (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_check_no_inherit_id_check CHECK ((id OPERATOR(pg_catalog.>) 10)) NO INHERIT)      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_check_no_inherit (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_check_no_inherit_id_check CHECK ((id OPERATOR(pg_catalog.>) 10)) NO INHERIT)     
 --   UNIQUE [ NULLS [ NOT ] DISTINCT ] ( column_name [, ... ] ) [ INCLUDE ( column_name [, ...]) ] |
 CREATE TABLE tbl_cstr_unique(
     id int,
     name varchar,
     UNIQUE (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_id_key UNIQUE (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_id_key UNIQUE (id))     
 CREATE TABLE tbl_cstr_unique_multicols(
     id int,
     name varchar,
     UNIQUE (id, name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_multicols (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_multicols_id_name_key UNIQUE (id, name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_multicols (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_multicols_id_name_key UNIQUE (id, name))     
 CREATE TABLE tbl_cstr_unique_nulls_distinct(
     id int,
     name varchar,
     UNIQUE NULLS DISTINCT (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_distinct (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_distinct_id_key UNIQUE (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_distinct (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_distinct_id_key UNIQUE (id))     
 CREATE TABLE tbl_cstr_unique_nulls_not_distinct(
     id int,
     name varchar,
     UNIQUE NULLS NOT DISTINCT (id, name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_not_distinct (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_not_distinct_id_name_key UNIQUE NULLS NOT DISTINCT (id, name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_not_distinct (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_not_distinct_id_name_key UNIQUE NULLS NOT DISTINCT (id, name))     
 CREATE TABLE tbl_cstr_unique_nulls_distinct_include(
     id int,
     name varchar,
     UNIQUE NULLS DISTINCT (id) INCLUDE (name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_distinct_include (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_distinct_include_id_name_key UNIQUE (id) INCLUDE (name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_distinct_include (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_distinct_include_id_name_key UNIQUE (id) INCLUDE (name))     
 CREATE TABLE tbl_cstr_unique_nulls_distinct_include_multi(
     id int,
     name varchar,
     info varchar,
     UNIQUE NULLS DISTINCT (id) INCLUDE (name, info)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_distinct_include_multi (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , info pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_distinct_include_multi_id_name_info_key UNIQUE (id) INCLUDE (name, info))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_unique_nulls_distinct_include_multi (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , info pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_unique_nulls_distinct_include_multi_id_name_info_key UNIQUE (id) INCLUDE (name, info))     
 -- PRIMARY KEY ( column_name [, ... ] ) [ INCLUDE ( column_name [, ...]) ] |
 CREATE TABLE tbl_cstr_primary_key(
     id int,
     name varchar,
     PRIMARY KEY (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_pkey PRIMARY KEY (id))     
 CREATE TABLE tbl_cstr_primary_key_multicols(
     id int,
     name varchar,
     PRIMARY KEY (id, name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key_multicols (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_multicols_pkey PRIMARY KEY (id, name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key_multicols (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_multicols_pkey PRIMARY KEY (id, name))     
 CREATE TABLE tbl_cstr_primary_key_include(
     id int,
     name varchar,
     PRIMARY KEY (id) INCLUDE (name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key_include (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_include_pkey PRIMARY KEY (id) INCLUDE (name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key_include (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_include_pkey PRIMARY KEY (id) INCLUDE (name))     
 CREATE TABLE tbl_cstr_primary_key_include_multicols(
     id int,
     name varchar,
     info varchar,
     PRIMARY KEY (id) INCLUDE (name, info)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key_include_multicols (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , info pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_include_multicols_pkey PRIMARY KEY (id) INCLUDE (name, info))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_primary_key_include_multicols (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , info pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_primary_key_include_multicols_pkey PRIMARY KEY (id) INCLUDE (name, info))     
 --   EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] |
 CREATE TABLE tbl_cstr_exclude(
     id int,
     name varchar,
     EXCLUDE (name WITH =)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_name_excl EXCLUDE USING btree (name WITH OPERATOR(pg_catalog.=)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_name_excl EXCLUDE USING btree (name WITH OPERATOR(pg_catalog.=)))     
 CREATE TABLE tbl_cstr_exclude_multi(
     id int,
     name varchar,
     EXCLUDE ((id*10) with =, name WITH =)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_multi (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_multi_expr_name_excl EXCLUDE USING btree (((id OPERATOR(pg_catalog.*) 10)) WITH OPERATOR(pg_catalog.=), name WITH OPERATOR(pg_catalog.=)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_multi (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_multi_expr_name_excl EXCLUDE USING btree (((id OPERATOR(pg_catalog.*) 10)) WITH OPERATOR(pg_catalog.=), name WITH OPERATOR(pg_catalog.=)))     
 CREATE TABLE tbl_cstr_exclude_index_method(
     id int,
     name varchar,
     EXCLUDE USING btree ((id*10) with =, name WITH =)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_index_method (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_index_method_expr_name_excl EXCLUDE USING btree (((id OPERATOR(pg_catalog.*) 10)) WITH OPERATOR(pg_catalog.=), name WITH OPERATOR(pg_catalog.=)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_index_method (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_index_method_expr_name_excl EXCLUDE USING btree (((id OPERATOR(pg_catalog.*) 10)) WITH OPERATOR(pg_catalog.=), name WITH OPERATOR(pg_catalog.=)))     
 -- [ INCLUDE ( column_name [, ... ] ) ]
 CREATE TABLE tbl_cstr_exclude_with_index_params_include_1(
     id int,
     name varchar,
     EXCLUDE (id WITH =) INCLUDE (name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_include_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_include_1_id_name_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) INCLUDE (name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_include_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_include_1_id_name_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) INCLUDE (name))     
 CREATE TABLE tbl_cstr_exclude_with_index_params_include_2(
     id int,
     name varchar,
     EXCLUDE (id WITH =) INCLUDE (id, name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_include_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_include_2_id_id1_name_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) INCLUDE (id, name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_include_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_include_2_id_id1_name_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) INCLUDE (id, name))     
 -- [ WITH ( storage_parameter [= value] [, ... ] ) ]
 CREATE TABLE tbl_cstr_exclude_with_index_params_storage_1(
     id int,
     name varchar,
     EXCLUDE (id WITH =) WITH (fillfactor = 20)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_storage_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_storage_1_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) WITH (fillfactor='20'))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_storage_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_storage_1_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) WITH (fillfactor='20'))     
 CREATE TABLE tbl_cstr_exclude_with_index_params_storage_2(
     id int,
     name varchar,
     EXCLUDE (id WITH =) WITH (fillfactor = 20, deduplicate_items = false)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_storage_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_storage_2_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) WITH (fillfactor='20', deduplicate_items='false'))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_storage_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_storage_2_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) WITH (fillfactor='20', deduplicate_items='false'))     
 -- [ USING INDEX TABLESPACE tablespace_name ]
 CREATE TABLE tbl_cstr_exclude_with_index_params_tablespace(
     id int,
     name varchar,
     EXCLUDE (id WITH =) USING INDEX TABLESPACE pg_default
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_tablespace (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_tablespace_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_tablespace (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_tablespace_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)))     
 -- index_parameters complex combination
 CREATE TABLE tbl_cstr_exclude_with_index_params_complex(
     id int,
     name varchar,
     EXCLUDE (id WITH =) INCLUDE (id, name) WITH (fillfactor = 20, deduplicate_items = false) USING INDEX TABLESPACE pg_default
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_complex (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_complex_id_id1_name_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) INCLUDE (id, name) WITH (fillfactor='20', deduplicate_items='false'))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_index_params_complex (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_index_params_complex_id_id1_name_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) INCLUDE (id, name) WITH (fillfactor='20', deduplicate_items='false'))     
 -- [ WHERE ( predicate ) ]
 CREATE TABLE tbl_cstr_exclude_with_predicate(
     id int,
     name varchar,
     EXCLUDE (id WITH =) WHERE (name<>'foo')
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_predicate (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_predicate_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) WHERE (((name)::pg_catalog.text OPERATOR(pg_catalog.<>) 'foo'::pg_catalog.text)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_with_predicate (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_with_predicate_id_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=)) WHERE (((name)::pg_catalog.text OPERATOR(pg_catalog.<>) 'foo'::pg_catalog.text)))     
 -- complex combination for table constraint clauses
 CREATE TABLE tbl_cstr_exclude_complex_combination(
     id int,
     name varchar,
     EXCLUDE USING btree (id WITH =, name WITH =) INCLUDE (id, name) WITH (fillfactor = 20, deduplicate_items = false) USING INDEX TABLESPACE pg_default WHERE (name<>'foo')
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_complex_combination (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_complex_combination_id_name_id1_name1_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=), name WITH OPERATOR(pg_catalog.=)) INCLUDE (id, name) WITH (fillfactor='20', deduplicate_items='false') WHERE (((name)::pg_catalog.text OPERATOR(pg_catalog.<>) 'foo'::pg_catalog.text)))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_exclude_complex_combination (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_exclude_complex_combination_id_name_id1_name1_excl EXCLUDE USING btree (id WITH OPERATOR(pg_catalog.=), name WITH OPERATOR(pg_catalog.=)) INCLUDE (id, name) WITH (fillfactor='20', deduplicate_items='false') WHERE (((name)::pg_catalog.text OPERATOR(pg_catalog.<>) 'foo'::pg_catalog.text)))     
 -- FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
 --     [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
 CREATE TABLE tbl_cstr_foreign_table(
@@ -465,15 +465,15 @@ CREATE TABLE tbl_cstr_foreign_table(
     name varchar UNIQUE,
     UNIQUE (id, name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_foreign_table_id_name_key UNIQUE (id, name), CONSTRAINT tbl_cstr_foreign_table_name_key UNIQUE (name), CONSTRAINT tbl_cstr_foreign_table_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_foreign_table_id_name_key UNIQUE (id, name), CONSTRAINT tbl_cstr_foreign_table_name_key UNIQUE (name), CONSTRAINT tbl_cstr_foreign_table_pkey PRIMARY KEY (id))     
 CREATE TABLE tbl_cstr_foreign_key_simple_1(
     id int,
     name varchar,
     FOREIGN KEY (id) REFERENCES tbl_cstr_foreign_table
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_key_simple_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_key_simple_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr_foreign_key_simple_1_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id) REFERENCES public.tbl_cstr_foreign_table(id)"}], "identity": {"objname": "tbl_cstr_foreign_key_simple_1", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_foreign_key_simple_1 ADD CONSTRAINT tbl_cstr_foreign_key_simple_1_id_fkey FOREIGN KEY (id) REFERENCES public.tbl_cstr_foreign_table(id)
 CREATE TABLE tbl_cstr_foreign_key_simple_2(
@@ -481,8 +481,8 @@ CREATE TABLE tbl_cstr_foreign_key_simple_2(
     name varchar,
     FOREIGN KEY (id) REFERENCES tbl_cstr_foreign_table(id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_key_simple_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_key_simple_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr_foreign_key_simple_2_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id) REFERENCES public.tbl_cstr_foreign_table(id)"}], "identity": {"objname": "tbl_cstr_foreign_key_simple_2", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_foreign_key_simple_2 ADD CONSTRAINT tbl_cstr_foreign_key_simple_2_id_fkey FOREIGN KEY (id) REFERENCES public.tbl_cstr_foreign_table(id)
 CREATE TABLE tbl_cstr_foreign_key_multiple_keys(
@@ -490,8 +490,8 @@ CREATE TABLE tbl_cstr_foreign_key_multiple_keys(
     name varchar,
     FOREIGN KEY (id, name) REFERENCES tbl_cstr_foreign_table (id, name)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_key_multiple_keys (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_foreign_key_multiple_keys (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr_foreign_key_multiple_keys_id_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (id, name) REFERENCES public.tbl_cstr_foreign_table(id, name)"}], "identity": {"objname": "tbl_cstr_foreign_key_multiple_keys", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_foreign_key_multiple_keys ADD CONSTRAINT tbl_cstr_foreign_key_multiple_keys_id_name_fkey FOREIGN KEY (id, name) REFERENCES public.tbl_cstr_foreign_table(id, name)
 -- some combinations from REFERENCES clause, which is already tested in part 3
@@ -500,8 +500,8 @@ CREATE TABLE tbl_cstr_reference_table_complex_combination1(
     name varchar,
     FOREIGN KEY (name) REFERENCES tbl_cstr_foreign_table (name) MATCH SIMPLE ON DELETE CASCADE ON UPDATE SET NULL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_complex_combination1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_complex_combination1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr_reference_table_complex_combination1_name_fkey", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.tbl_cstr_foreign_table(name) ON UPDATE SET NULL ON DELETE CASCADE"}], "identity": {"objname": "tbl_cstr_reference_table_complex_combination1", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_reference_table_complex_combination1 ADD CONSTRAINT tbl_cstr_reference_table_complex_combination1_name_fkey FOREIGN KEY (name) REFERENCES public.tbl_cstr_foreign_table(name) ON UPDATE SET NULL ON DELETE CASCADE
 CREATE TABLE tbl_cstr_reference_table_complex_combination2(
@@ -509,8 +509,8 @@ CREATE TABLE tbl_cstr_reference_table_complex_combination2(
     name varchar,
     CONSTRAINT tbl_cstr FOREIGN KEY (id, name) REFERENCES tbl_cstr_foreign_table (id, name) ON DELETE SET NULL (id, name) ON UPDATE SET DEFAULT
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_complex_combination2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_complex_combination2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr", "type": "add constraint", "definition": "FOREIGN KEY (id, name) REFERENCES public.tbl_cstr_foreign_table(id, name) ON UPDATE SET DEFAULT ON DELETE SET NULL (id, name)"}], "identity": {"objname": "tbl_cstr_reference_table_complex_combination2", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_reference_table_complex_combination2 ADD CONSTRAINT tbl_cstr FOREIGN KEY (id, name) REFERENCES public.tbl_cstr_foreign_table(id, name) ON UPDATE SET DEFAULT ON DELETE SET NULL (id, name)
 -- [ DEFERRABLE | NOT DEFERRABLE ]
@@ -519,38 +519,38 @@ CREATE TABLE tbl_cstr_reference_table_column_deferable(
     name varchar,
     UNIQUE (id, name) DEFERRABLE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_deferable_id_name_key UNIQUE (id, name) DEFERRABLE)      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_deferable_id_name_key UNIQUE (id, name) DEFERRABLE)     
 CREATE TABLE tbl_cstr_reference_table_column_not_deferable(
     id int,
     name varchar,
     PRIMARY KEY (id) NOT DEFERRABLE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_not_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_not_deferable_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_not_deferable (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_not_deferable_pkey PRIMARY KEY (id))     
 -- [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 CREATE TABLE tbl_cstr_reference_table_column_initially_deferred(
     id int,
     name varchar,
     UNIQUE (id, name) INITIALLY DEFERRED
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_initially_deferred (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_initially_deferred_id_name_key UNIQUE (id, name) DEFERRABLE INITIALLY DEFERRED)      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_initially_deferred (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_initially_deferred_id_name_key UNIQUE (id, name) DEFERRABLE INITIALLY DEFERRED)     
 CREATE TABLE tbl_cstr_reference_table_column_initially_immediate(
     id int,
     name varchar,
     PRIMARY KEY (id) INITIALLY IMMEDIATE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_initially_immediate (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_initially_immediate_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_initially_immediate (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr_reference_table_column_initially_immediate_pkey PRIMARY KEY (id))     
 -- complex combinations
 CREATE TABLE tbl_cstr_reference_table_column_complex_combination1(
     id int,
     name varchar,
     CONSTRAINT tbl_cstr FOREIGN KEY (name) REFERENCES tbl_cstr_foreign_table (name) MATCH FULL ON DELETE NO ACTION ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_complex_combination1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_complex_combination1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "tbl_cstr", "type": "add constraint", "definition": "FOREIGN KEY (name) REFERENCES public.tbl_cstr_foreign_table(name) MATCH FULL DEFERRABLE INITIALLY DEFERRED"}], "identity": {"objname": "tbl_cstr_reference_table_column_complex_combination1", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.tbl_cstr_reference_table_column_complex_combination1 ADD CONSTRAINT tbl_cstr FOREIGN KEY (name) REFERENCES public.tbl_cstr_foreign_table(name) MATCH FULL DEFERRABLE INITIALLY DEFERRED
 CREATE TABLE tbl_cstr_reference_table_column_complex_combination2(
@@ -558,5 +558,5 @@ CREATE TABLE tbl_cstr_reference_table_column_complex_combination2(
     name varchar,
     CONSTRAINT tbl_cstr PRIMARY KEY (id, name) NOT DEFERRABLE INITIALLY IMMEDIATE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_complex_combination2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr PRIMARY KEY (id, name))      
+NOTICE:  re-formed command: CREATE  TABLE  public.tbl_cstr_reference_table_column_complex_combination2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT tbl_cstr PRIMARY KEY (id, name))     
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/create_index.out b/src/test/modules/test_ddl_deparse_regress/expected/create_index.out
index 490cac9662..f790f92544 100644
--- a/src/test/modules/test_ddl_deparse_regress/expected/create_index.out
+++ b/src/test/modules/test_ddl_deparse_regress/expected/create_index.out
@@ -11,8 +11,8 @@ CREATE TABLE test_add_constraint_using_index(
     id6 int,
     id7 int
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_add_constraint_using_index (id1 pg_catalog.int4 STORAGE PLAIN      , id2 pg_catalog.int4 STORAGE PLAIN      , id3 pg_catalog.int4 STORAGE PLAIN      , id4 pg_catalog.int4 STORAGE PLAIN      , id5 pg_catalog.int4 STORAGE PLAIN      , id6 pg_catalog.int4 STORAGE PLAIN      , id7 pg_catalog.int4 STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_add_constraint_using_index (id1 pg_catalog.int4 STORAGE PLAIN      , id2 pg_catalog.int4 STORAGE PLAIN      , id3 pg_catalog.int4 STORAGE PLAIN      , id4 pg_catalog.int4 STORAGE PLAIN      , id5 pg_catalog.int4 STORAGE PLAIN      , id6 pg_catalog.int4 STORAGE PLAIN      , id7 pg_catalog.int4 STORAGE PLAIN      )     
 CREATE UNIQUE INDEX test_add_constraint_used_index1 ON test_add_constraint_using_index (id1);
 CREATE UNIQUE INDEX test_add_constraint_used_index2 ON test_add_constraint_using_index (id2);
 CREATE UNIQUE INDEX test_add_constraint_used_index3 ON test_add_constraint_using_index (id3);
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/create_rule.out b/src/test/modules/test_ddl_deparse_regress/expected/create_rule.out
index 14c5664292..96d595b7c6 100644
--- a/src/test/modules/test_ddl_deparse_regress/expected/create_rule.out
+++ b/src/test/modules/test_ddl_deparse_regress/expected/create_rule.out
@@ -10,8 +10,8 @@ CREATE TABLE test_disable_rule(
     quantity int,
     purchase_date date
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_disable_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_disable_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 CREATE RULE sample_rule1 AS
     ON UPDATE TO test_disable_rule
     DO INSTEAD
@@ -20,8 +20,8 @@ CREATE RULE sample_rule1 AS
 CREATE TABLE test_enable_rule(
     LIKE test_disable_rule
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 CREATE RULE sample_rule2 AS
     ON UPDATE TO test_enable_rule
     DO INSTEAD
@@ -30,8 +30,8 @@ CREATE RULE sample_rule2 AS
 CREATE TABLE test_enable_replica_rule(
     LIKE test_disable_rule
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_replica_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_replica_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 CREATE RULE sample_rule_enable_replica AS
     ON UPDATE TO test_enable_replica_rule
     DO INSTEAD
@@ -40,8 +40,8 @@ CREATE RULE sample_rule_enable_replica AS
 CREATE TABLE test_enable_always_rule(
     LIKE test_disable_rule
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_always_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_enable_always_rule (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , description pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , price pg_catalog.float4 STORAGE PLAIN      , quantity pg_catalog.int4 STORAGE PLAIN      , purchase_date pg_catalog.date STORAGE PLAIN      )     
 CREATE RULE sample_rule_enable_always AS
     ON UPDATE TO test_enable_always_rule
     DO INSTEAD
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/create_table.out b/src/test/modules/test_ddl_deparse_regress/expected/create_table.out
index 567dcf9c38..688b22dc0b 100644
--- a/src/test/modules/test_ddl_deparse_regress/expected/create_table.out
+++ b/src/test/modules/test_ddl_deparse_regress/expected/create_table.out
@@ -4,20 +4,20 @@ CREATE TABLE part1_simple_table(
     id int,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part1_simple_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part1_simple_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TEMPORARY TABLE part1_temp_table0(
     id int,
     name varchar
 );
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_temp_table0 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_temp_table0 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TEMP TABLE part1_temp_table(
     id int,
     name varchar
 );
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_temp_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_temp_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 -- GLOBAL TEMP TATBLE is deprecated, expect warning message and create local temp table
 CREATE GLOBAL TEMP TABLE part1_global_temp_table(
     id int,
@@ -26,20 +26,20 @@ CREATE GLOBAL TEMP TABLE part1_global_temp_table(
 WARNING:  GLOBAL is deprecated in temporary table creation
 LINE 1: CREATE GLOBAL TEMP TABLE part1_global_temp_table(
                ^
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_global_temp_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_global_temp_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE LOCAL TEMP TABLE part1_local_temp_table(
     id int,
     name varchar
 );
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_local_temp_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part1_local_temp_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE UNLOGGED TABLE part1_unlogged_table(
     id int,
     name varchar
 );
-NOTICE:  re-formed command: CREATE UNLOGGED TABLE  public.part1_unlogged_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE UNLOGGED TABLE  public.part1_unlogged_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 -- [ IF NOT EXISTS ]
 CREATE TABLE IF NOT EXISTS part1_simple_table(
     id int,
@@ -50,291 +50,291 @@ CREATE TABLE IF NOT EXISTS part1_local_temp_table_not_exists(
     id int,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE IF NOT EXISTS public.part1_local_temp_table_not_exists (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE IF NOT EXISTS public.part1_local_temp_table_not_exists (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 -- part 2: shared suffixes
 -- [ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
 CREATE TABLE part2_partition_by_range_simple(
     id int,
     name varchar
 ) PARTITION BY RANGE (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_by_range_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_by_range_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (id)   
 CREATE TABLE part2_partition_by_list_simple(
     id int,
     name varchar
 ) PARTITION BY LIST (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_by_list_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY LIST (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_by_list_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY LIST (id)   
 CREATE TABLE part2_partition_by_hash_simple(
     id int,
     name varchar
 ) PARTITION BY HASH (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_by_hash_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY HASH (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_by_hash_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY HASH (id)   
 CREATE TABLE part2_partition_with_expression(
     id int,
     name varchar
 ) PARTITION BY RANGE ((id * 190), name);
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_expression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (((id OPERATOR(pg_catalog.*) 190)), name)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_expression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (((id OPERATOR(pg_catalog.*) 190)), name)   
 CREATE TABLE part2_partition_with_collation(
     id int,
     name varchar
 ) PARTITION BY LIST (name COLLATE "C");
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_collation (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY LIST (name COLLATE pg_catalog."C")    
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_collation (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY LIST (name COLLATE pg_catalog."C")   
 CREATE TABLE part2_partition_with_opclass(
     id int,
     name varchar
 ) PARTITION BY HASH (id int4_ops, name varchar_ops);
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_opclass (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY HASH (id, name pg_catalog.varchar_ops)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_opclass (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY HASH (id, name pg_catalog.varchar_ops)   
 CREATE TABLE part2_partition_with_collation_opclass(
     id int,
     name varchar
 ) PARTITION BY RANGE ((id * 10) int4_ops, name COLLATE "C" varchar_ops);
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_collation_opclass (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (((id OPERATOR(pg_catalog.*) 10)), name COLLATE pg_catalog."C" pg_catalog.varchar_ops)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_partition_with_collation_opclass (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (((id OPERATOR(pg_catalog.*) 10)), name COLLATE pg_catalog."C" pg_catalog.varchar_ops)   
 -- [ USING method ]
 -- default method
 CREATE TABLE part2_using_default_access_method(
     id int,
     name varchar
 ) USING heap;
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_using_default_access_method (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )   USING heap   
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_using_default_access_method (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )   USING heap  
 -- [ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
 CREATE TABLE part2_without_oids(
     id int,
     name varchar
 ) WITHOUT OIDS;
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_without_oids (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_without_oids (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part2_with_one_storage_param(
     id int,
     name varchar
 ) WITH (fillfactor = 20);
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_with_one_storage_param (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )    WITH (fillfactor = '20')  
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_with_one_storage_param (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )    WITH (fillfactor = '20') 
 CREATE TABLE part2_with_multiple_storage_params(
     id int,
     name varchar
 ) WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true);
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_with_multiple_storage_params (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )    WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')  
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_with_multiple_storage_params (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )    WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') 
 -- [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
 CREATE TEMP TABLE part2_on_commit_preserve_rows(
     id int,
     name varchar
 ) ON COMMIT PRESERVE ROWS;
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_on_commit_preserve_rows (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     ON COMMIT PRESERVE ROWS 
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_on_commit_preserve_rows (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TEMP TABLE part2_on_commit_delete_rows(
     id int,
     name varchar
 ) ON COMMIT DELETE ROWS;
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_on_commit_delete_rows (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     ON COMMIT DELETE ROWS 
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_on_commit_delete_rows (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TEMPORARY TABLE part2_on_commit_drop(
     id int,
     name varchar
 ) ON COMMIT DROP;
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_on_commit_drop (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     ON COMMIT DROP 
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_on_commit_drop (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 -- [ TABLESPACE tablespace_name ]
 CREATE TABLE part2_tablespace_pg_default(
     id int,
     name varchar
 ) TABLESPACE pg_default;
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_tablespace_pg_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      TABLESPACE pg_default
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_tablespace_pg_default (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     TABLESPACE pg_default
 -- some complex combinations from the components above
 CREATE TEMPORARY TABLE part2_combination_1(
     id int,
     name varchar
 ) PARTITION BY RANGE (id) ON COMMIT PRESERVE ROWS;
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_combination_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (id)   ON COMMIT PRESERVE ROWS 
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_combination_1 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (id)   
 CREATE LOCAL TEMP TABLE part2_combination_2(
     id int,
     name varchar
 ) USING heap WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true) ON COMMIT PRESERVE ROWS TABLESPACE pg_default;
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_combination_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )   USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') ON COMMIT PRESERVE ROWS TABLESPACE pg_default
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part2_combination_2 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )   USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') TABLESPACE pg_default
 CREATE TABLE part2_combination_3(
     id int,
     name varchar
 ) USING heap WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true) TABLESPACE pg_default;
-NOTICE:  re-formed command: CREATE  TABLE  public.part2_combination_3 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )   USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')  TABLESPACE pg_default
+NOTICE:  re-formed command: CREATE  TABLE  public.part2_combination_3 (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )   USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') TABLESPACE pg_default
 -- part 5: LIKE source_table [ like_option ... ]
 CREATE TABLE part5_source_table(
     id int,
     name varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_source_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_source_table (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_source_table2(
     id2 int,
     name2 varchar
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_source_table2 (id2 pg_catalog.int4 STORAGE PLAIN      , name2 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_source_table2 (id2 pg_catalog.int4 STORAGE PLAIN      , name2 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_like_simple(
     LIKE part5_source_table
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_like_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_like_simple (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 -- { INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
 CREATE TABLE part5_including_all(
     LIKE part5_source_table INCLUDING ALL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_all (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_all (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_including_comments(
     LIKE part5_source_table INCLUDING COMMENTS
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_comments (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_comments (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_including_compression(
     LIKE part5_source_table INCLUDING COMPRESSION
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_compression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_compression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_including_defaults(
     LIKE part5_source_table INCLUDING DEFAULTS
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_defaults (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_defaults (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_including_generated(
     LIKE part5_source_table INCLUDING GENERATED
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_generated (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_generated (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_including_identity(
     LIKE part5_source_table INCLUDING IDENTITY
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_identity (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_identity (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_including_indexes(
     LIKE part5_source_table INCLUDING INDEXES
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_indexes (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_indexes (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_including_statistics(
     LIKE part5_source_table INCLUDING STATISTICS
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_statistics (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_statistics (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_including_storage(
     LIKE part5_source_table INCLUDING STORAGE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_storage (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_including_storage (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_excluding_all(
     LIKE part5_source_table EXCLUDING ALL
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_all (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_all (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_excluding_comments(
     LIKE part5_source_table EXCLUDING COMMENTS
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_comments (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_comments (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_excluding_compression(
     LIKE part5_source_table EXCLUDING COMPRESSION
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_compression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_compression (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_excluding_defaults(
     LIKE part5_source_table EXCLUDING DEFAULTS
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_defaults (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_defaults (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_excluding_generated(
     LIKE part5_source_table EXCLUDING GENERATED
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_generated (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_generated (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_excluding_identity(
     LIKE part5_source_table EXCLUDING IDENTITY
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_identity (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_identity (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_excluding_indexes(
     LIKE part5_source_table EXCLUDING INDEXES
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_indexes (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_indexes (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_excluding_statistics(
     LIKE part5_source_table EXCLUDING STATISTICS
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_statistics (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_statistics (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_excluding_storage(
     LIKE part5_source_table EXCLUDING STORAGE
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_storage (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_excluding_storage (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )     
 CREATE TABLE part5_like_list(
     LIKE part5_source_table,
     info text,
     LIKE part5_source_table2 INCLUDING ALL,
     CONSTRAINT primary_key_constraint PRIMARY KEY (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part5_like_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , info pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , id2 pg_catalog.int4 STORAGE PLAIN      , name2 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT primary_key_constraint PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.part5_like_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , info pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , id2 pg_catalog.int4 STORAGE PLAIN      , name2 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT primary_key_constraint PRIMARY KEY (id))     
 -- part 6: partition specification
 -- PARTITION OF parent_table { FOR VALUES partition_bound_spec | DEFAULT }
 CREATE TABLE part6_parent_table_range(
     id int,
     name varchar
 ) PARTITION BY RANGE (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.part6_parent_table_range (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_parent_table_range (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY RANGE (id)   
 CREATE TABLE part6_parent_table_list(
     id int,
     name varchar
 ) PARTITION BY LIST (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.part6_parent_table_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY LIST (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_parent_table_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY LIST (id)   
 CREATE TABLE part6_parent_table_hash(
     id int,
     name varchar
 ) PARTITION BY HASH (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.part6_parent_table_hash (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY HASH (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_parent_table_hash (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    )  PARTITION BY HASH (id)   
 CREATE TABLE part6_partition_default PARTITION OF part6_parent_table_range DEFAULT;
-NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_default PARTITION OF public.part6_parent_table_range  DEFAULT     
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_default PARTITION OF public.part6_parent_table_range  DEFAULT    
 -- FROM ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] )
 --  TO ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] ) |
 CREATE TABLE part6_partition_spec_range1 PARTITION OF part6_parent_table_range
 FOR VALUES FROM (MINVALUE) TO (2);
-NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_range1 PARTITION OF public.part6_parent_table_range  FOR VALUES FROM (MINVALUE) TO (2)     
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_range1 PARTITION OF public.part6_parent_table_range  FOR VALUES FROM (MINVALUE) TO (2)    
 CREATE TABLE part6_partition_spec_range2 PARTITION OF part6_parent_table_range
 FOR VALUES FROM (3) TO (MAXVALUE);
-NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_range2 PARTITION OF public.part6_parent_table_range  FOR VALUES FROM (3) TO (MAXVALUE)     
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_range2 PARTITION OF public.part6_parent_table_range  FOR VALUES FROM (3) TO (MAXVALUE)    
 -- IN ( partition_bound_expr [, ...] ) |
 CREATE TABLE part6_partition_spec_list PARTITION OF part6_parent_table_list
 FOR VALUES IN (1, (1+2), (4+5));
-NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_list PARTITION OF public.part6_parent_table_list  FOR VALUES IN (1, 3, 9)     
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_list PARTITION OF public.part6_parent_table_list  FOR VALUES IN (1, 3, 9)    
 -- WITH ( MODULUS numeric_literal, REMAINDER numeric_literal )
 CREATE TABLE part6_partition_spec_hash PARTITION OF part6_parent_table_hash
 FOR VALUES WITH (MODULUS 10, REMAINDER 2);
-NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_hash PARTITION OF public.part6_parent_table_hash  FOR VALUES WITH (modulus 10, remainder 2)     
+NOTICE:  re-formed command: CREATE  TABLE  public.part6_partition_spec_hash PARTITION OF public.part6_parent_table_hash  FOR VALUES WITH (modulus 10, remainder 2)    
 -- part7: create table
 -- all data types
 CREATE TABLE part7_source_table(
     src_id int
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part7_source_table (src_id pg_catalog.int4 STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_source_table (src_id pg_catalog.int4 STORAGE PLAIN      )     
 CREATE TABLE part7_data_types(
     var1 int8,
     var2 serial8,
@@ -395,8 +395,8 @@ NOTICE:  deparsed json: {"fmt": "CREATE %{persistence}s SEQUENCE %{if_not_exists
 NOTICE:  re-formed command: CREATE  SEQUENCE  public.part7_data_types_var37_seq CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 32767 START WITH 1 RESTART 1 AS pg_catalog.int2
 NOTICE:  deparsed json: {"fmt": "CREATE %{persistence}s SEQUENCE %{if_not_exists}s %{identity}D %{definition: }s", "identity": {"objname": "part7_data_types_var38_seq", "schemaname": "public"}, "definition": [{"fmt": "CACHE %{value}s", "value": "1", "clause": "cache"}, {"no": "NO", "fmt": "%{no}s CYCLE", "clause": "cycle"}, {"fmt": "INCREMENT BY %{value}s", "value": "1", "clause": "seqincrement"}, {"fmt": "MINVALUE %{value}s", "value": "1", "clause": "minvalue"}, {"fmt": "MAXVALUE %{value}s", "value": "2147483647", "clause": "maxvalue"}, {"fmt": "START WITH %{value}s", "value": "1", "clause": "start"}, {"fmt": "RESTART %{value}s", "value": "1", "clause": "restart"}, {"fmt": "AS %{seqtype}T", "seqtype": {"typmod": "", "typarray": false, "typename": "int4", "schemaname": "pg_catalog"}}], "persistence": "", "if_not_exists": ""}
 NOTICE:  re-formed command: CREATE  SEQUENCE  public.part7_data_types_var38_seq CACHE 1 NO CYCLE INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 RESTART 1 AS pg_catalog.int4
-NOTICE:  re-formed command: CREATE  TABLE  public.part7_data_types (var1 pg_catalog.int8 STORAGE PLAIN      , var2 pg_catalog.int8 STORAGE PLAIN   NOT NULL DEFAULT pg_catalog.nextval('public.part7_data_types_var2_seq'::pg_catalog.regclass)  , var3 pg_catalog."bit"(1) STORAGE EXTENDED      , var4 pg_catalog."bit"(1)[] STORAGE EXTENDED      , var5 pg_catalog.varbit STORAGE EXTENDED      , var6 pg_catalog.varbit[] STORAGE EXTENDED      , var7 pg_catalog.bool STORAGE PLAIN      , var8 pg_catalog.box STORAGE PLAIN      , var9 pg_catalog.bytea STORAGE EXTENDED      , var10 pg_catalog.bpchar(1) STORAGE EXTENDED  COLLATE pg_catalog."default"    , var11 pg_catalog.bpchar(1)[] STORAGE EXTENDED  COLLATE pg_catalog."default"    , var12 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , var13 pg_catalog."varchar"[] STORAGE EXTENDED  COLLATE pg_catalog."default"    , var14 pg_catalog.cidr STORAGE MAIN      , var15 pg_catalog.circle STORAGE PLAIN      , var16 pg_catalog.date STORAGE PLAIN      , var17 pg_catalog.float8 STORAGE PLAIN      , var18 pg_catalog.inet STORAGE MAIN      , var19 pg_catalog.int4 STORAGE PLAIN      , var20 pg_catalog.int4 STORAGE PLAIN      , var21 INTERVAL STORAGE PLAIN      , var22 pg_catalog.json STORAGE EXTENDED      , var23 pg_catalog.jsonb STORAGE EXTENDED      , var24 pg_catalog.line STORAGE PLAIN      , var25 pg_catalog.lseg STORAGE PLAIN      , var26 pg_catalog.macaddr STORAGE PLAIN      , var27 pg_catalog.money STORAGE PLAIN      , var28 pg_catalog."numeric" STORAGE MAIN      , var29 pg_catalog."numeric"(3,1) STORAGE MAIN      , var30 pg_catalog.path STORAGE EXTENDED      , var31 pg_catalog.pg_lsn STORAGE PLAIN      , var32 pg_catalog.pg_snapshot STORAGE EXTENDED      , var33 pg_catalog.point STORAGE PLAIN      , var34 pg_catalog.polygon STORAGE EXTENDED      , var35 pg_catalog.float4 STORAGE PLAIN      , var36 pg_catalog.int2 STORAGE PLAIN      , var37 pg_catalog.int2 STORAGE PLAIN   NOT NULL DEFAULT pg_catalog.nextval('public.part7_data_types_var37_seq'::pg_catalog.regclass)  , var38 pg_catalog.int4 STORAGE PLAIN   NOT NULL DEFAULT pg_catalog.nextval('public.part7_data_types_var38_seq'::pg_catalog.regclass)  , var39 pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , var40 TIME STORAGE PLAIN      , var41 TIME(3) without time zone STORAGE PLAIN      , var42 TIME WITH TIME ZONE STORAGE PLAIN      , var43 TIME(3) with time zone STORAGE PLAIN      , var44 TIMESTAMP STORAGE PLAIN      , var45 TIMESTAMP(3) without time zone STORAGE PLAIN      , var46 TIMESTAMP WITH TIME ZONE STORAGE PLAIN      , var47 TIMESTAMP(3) with time zone STORAGE PLAIN      , var48 pg_catalog.tsquery STORAGE PLAIN      , var49 pg_catalog.tsvector STORAGE EXTENDED      , var50 pg_catalog.txid_snapshot STORAGE EXTENDED      , var51 pg_catalog.uuid STORAGE PLAIN      , var52 pg_catalog.xml STORAGE EXTENDED      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_data_types (var1 pg_catalog.int8 STORAGE PLAIN      , var2 pg_catalog.int8 STORAGE PLAIN   NOT NULL DEFAULT pg_catalog.nextval('public.part7_data_types_var2_seq'::pg_catalog.regclass)  , var3 pg_catalog."bit"(1) STORAGE EXTENDED      , var4 pg_catalog."bit"(1)[] STORAGE EXTENDED      , var5 pg_catalog.varbit STORAGE EXTENDED      , var6 pg_catalog.varbit[] STORAGE EXTENDED      , var7 pg_catalog.bool STORAGE PLAIN      , var8 pg_catalog.box STORAGE PLAIN      , var9 pg_catalog.bytea STORAGE EXTENDED      , var10 pg_catalog.bpchar(1) STORAGE EXTENDED  COLLATE pg_catalog."default"    , var11 pg_catalog.bpchar(1)[] STORAGE EXTENDED  COLLATE pg_catalog."default"    , var12 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , var13 pg_catalog."varchar"[] STORAGE EXTENDED  COLLATE pg_catalog."default"    , var14 pg_catalog.cidr STORAGE MAIN      , var15 pg_catalog.circle STORAGE PLAIN      , var16 pg_catalog.date STORAGE PLAIN      , var17 pg_catalog.float8 STORAGE PLAIN      , var18 pg_catalog.inet STORAGE MAIN      , var19 pg_catalog.int4 STORAGE PLAIN      , var20 pg_catalog.int4 STORAGE PLAIN      , var21 INTERVAL STORAGE PLAIN      , var22 pg_catalog.json STORAGE EXTENDED      , var23 pg_catalog.jsonb STORAGE EXTENDED      , var24 pg_catalog.line STORAGE PLAIN      , var25 pg_catalog.lseg STORAGE PLAIN      , var26 pg_catalog.macaddr STORAGE PLAIN      , var27 pg_catalog.money STORAGE PLAIN      , var28 pg_catalog."numeric" STORAGE MAIN      , var29 pg_catalog."numeric"(3,1) STORAGE MAIN      , var30 pg_catalog.path STORAGE EXTENDED      , var31 pg_catalog.pg_lsn STORAGE PLAIN      , var32 pg_catalog.pg_snapshot STORAGE EXTENDED      , var33 pg_catalog.point STORAGE PLAIN      , var34 pg_catalog.polygon STORAGE EXTENDED      , var35 pg_catalog.float4 STORAGE PLAIN      , var36 pg_catalog.int2 STORAGE PLAIN      , var37 pg_catalog.int2 STORAGE PLAIN   NOT NULL DEFAULT pg_catalog.nextval('public.part7_data_types_var37_seq'::pg_catalog.regclass)  , var38 pg_catalog.int4 STORAGE PLAIN   NOT NULL DEFAULT pg_catalog.nextval('public.part7_data_types_var38_seq'::pg_catalog.regclass)  , var39 pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , var40 TIME STORAGE PLAIN      , var41 TIME(3) without time zone STORAGE PLAIN      , var42 TIME WITH TIME ZONE STORAGE PLAIN      , var43 TIME(3) with time zone STORAGE PLAIN      , var44 TIMESTAMP STORAGE PLAIN      , var45 TIMESTAMP(3) without time zone STORAGE PLAIN      , var46 TIMESTAMP WITH TIME ZONE STORAGE PLAIN      , var47 TIMESTAMP(3) with time zone STORAGE PLAIN      , var48 pg_catalog.tsquery STORAGE PLAIN      , var49 pg_catalog.tsvector STORAGE EXTENDED      , var50 pg_catalog.txid_snapshot STORAGE EXTENDED      , var51 pg_catalog.uuid STORAGE PLAIN      , var52 pg_catalog.xml STORAGE EXTENDED      )     
 NOTICE:  deparsed json: {"fmt": "ALTER SEQUENCE %{identity}D %{definition: }s", "identity": {"objname": "part7_data_types_var2_seq", "schemaname": "public"}, "definition": [{"fmt": "OWNED BY %{owner}D", "owner": {"objname": "part7_data_types", "attrname": "var2", "schemaname": "public"}, "clause": "owned"}]}
 NOTICE:  re-formed command: ALTER SEQUENCE public.part7_data_types_var2_seq OWNED BY public.part7_data_types.var2
 NOTICE:  deparsed json: {"fmt": "ALTER SEQUENCE %{identity}D %{definition: }s", "identity": {"objname": "part7_data_types_var37_seq", "schemaname": "public"}, "definition": [{"fmt": "OWNED BY %{owner}D", "owner": {"objname": "part7_data_types", "attrname": "var37", "schemaname": "public"}, "clause": "owned"}]}
@@ -407,15 +407,15 @@ CREATE TABLE part7_compression_collate(
     str1 varchar COMPRESSION "pglz",
     str2 varchar COLLATE "C"
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part7_compression_collate (str1 pg_catalog."varchar" STORAGE EXTENDED COMPRESSION pglz COLLATE pg_catalog."default"    , str2 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."C"    )      
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_compression_collate (str1 pg_catalog."varchar" STORAGE EXTENDED COMPRESSION pglz COLLATE pg_catalog."default"    , str2 pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."C"    )     
 CREATE TABLE part7_inherits_parent(
     id int,
     name varchar
 )
 INHERITS (part7_data_types, part7_compression_collate);
-NOTICE:  re-formed command: CREATE  TABLE  public.part7_inherits_parent (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    ) INHERITS (public.part7_data_types, public.part7_compression_collate)     
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_inherits_parent (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    ) INHERITS (public.part7_data_types, public.part7_compression_collate)    
 CREATE TABLE part7_combine_all_clauses(
     id varchar(5) COMPRESSION "pglz" COLLATE "C" CONSTRAINT id_constraint DEFAULT 'foo',
     PRIMARY KEY (id),
@@ -426,8 +426,8 @@ INHERITS (part7_data_types, part7_compression_collate)
 USING heap
 WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
 TABLESPACE pg_default;
-NOTICE:  re-formed command: CREATE  TABLE  public.part7_combine_all_clauses (id pg_catalog."varchar"(5) STORAGE EXTENDED COMPRESSION pglz COLLATE pg_catalog."C"  DEFAULT 'foo'::character varying  , src_id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT part7_combine_all_clauses_pkey PRIMARY KEY (id)) INHERITS (public.part7_data_types, public.part7_compression_collate)  USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')  TABLESPACE pg_default
+NOTICE:  re-formed command: CREATE  TABLE  public.part7_combine_all_clauses (id pg_catalog."varchar"(5) STORAGE EXTENDED COMPRESSION pglz COLLATE pg_catalog."C"  DEFAULT 'foo'::character varying  , src_id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT part7_combine_all_clauses_pkey PRIMARY KEY (id)) INHERITS (public.part7_data_types, public.part7_compression_collate)  USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') TABLESPACE pg_default
 CREATE TEMP TABLE part7_combine_all_clauses_temp(
     id varchar(5) COMPRESSION "pglz" COLLATE "C" CONSTRAINT id_constraint DEFAULT 'foo',
     PRIMARY KEY (id),
@@ -439,22 +439,22 @@ USING heap
 WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
 ON COMMIT DELETE ROWS
 TABLESPACE pg_default;
-NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part7_combine_all_clauses_temp (id pg_catalog."varchar"(5) STORAGE EXTENDED COMPRESSION pglz COLLATE pg_catalog."C"  DEFAULT 'foo'::character varying  , src_id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT part7_combine_all_clauses_temp_pkey PRIMARY KEY (id)) INHERITS (public.part7_data_types, public.part7_compression_collate)  USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') ON COMMIT DELETE ROWS TABLESPACE pg_default
+NOTICE:  re-formed command: CREATE TEMPORARY TABLE  pg_temp.part7_combine_all_clauses_temp (id pg_catalog."varchar"(5) STORAGE EXTENDED COMPRESSION pglz COLLATE pg_catalog."C"  DEFAULT 'foo'::character varying  , src_id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT part7_combine_all_clauses_temp_pkey PRIMARY KEY (id)) INHERITS (public.part7_data_types, public.part7_compression_collate)  USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') TABLESPACE pg_default
 -- CREATE TABLE ... AS
 CREATE TABLE test_ctas AS SELECT 0 AS col1, 1 AS col2;
-NOTICE:  re-formed command: CREATE  TABLE  public.test_ctas (col1 pg_catalog.int4 STORAGE PLAIN      , col2 pg_catalog.int4 STORAGE PLAIN      )      
+NOTICE:  re-formed command: CREATE  TABLE  public.test_ctas (col1 pg_catalog.int4 STORAGE PLAIN      , col2 pg_catalog.int4 STORAGE PLAIN      )     
 -- part8: create typed table
 CREATE TABLE part8_create_typed_table OF part8_people_type;
-NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table OF public.part8_people_type      
+NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table OF public.part8_people_type     
 CREATE TABLE part8_create_typed_table_simple OF part8_people_type(
     weight,
     PRIMARY KEY (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table_simple OF public.part8_people_type (CONSTRAINT part8_create_typed_table_simple_pkey PRIMARY KEY (id))     
+NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table_simple OF public.part8_people_type (CONSTRAINT part8_create_typed_table_simple_pkey PRIMARY KEY (id))    
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}], "identity": {"objname": "part8_create_typed_table_simple", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.part8_create_typed_table_simple ALTER COLUMN id SET NOT NULL
 CREATE TABLE part8_create_typed_table_with_options_constaints OF part8_people_type(
@@ -462,8 +462,8 @@ CREATE TABLE part8_create_typed_table_with_options_constaints OF part8_people_ty
     name UNIQUE,
     PRIMARY KEY (id)
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table_with_options_constaints OF public.part8_people_type (weight WITH OPTIONS NOT NULL , CONSTRAINT part8_create_typed_table_with_options_constaints_name_key UNIQUE (name), CONSTRAINT part8_create_typed_table_with_options_constaints_pkey PRIMARY KEY (id))     
+NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table_with_options_constaints OF public.part8_people_type (weight WITH OPTIONS NOT NULL , CONSTRAINT part8_create_typed_table_with_options_constaints_name_key UNIQUE (name), CONSTRAINT part8_create_typed_table_with_options_constaints_pkey PRIMARY KEY (id))    
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}], "identity": {"objname": "part8_create_typed_table_with_options_constaints", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.part8_create_typed_table_with_options_constaints ALTER COLUMN id SET NOT NULL
 CREATE TABLE part8_create_typed_table_complex_combinations OF part8_people_type(
@@ -474,8 +474,8 @@ CREATE TABLE part8_create_typed_table_complex_combinations OF part8_people_type(
 USING heap
 WITH (vacuum_index_cleanup = ON, autovacuum_vacuum_scale_factor = 0.2, vacuum_truncate = true)
 TABLESPACE pg_default;
-NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table_complex_combinations OF public.part8_people_type (weight WITH OPTIONS NOT NULL , CONSTRAINT part8_create_typed_table_complex_combinations_name_key UNIQUE (name), CONSTRAINT part8_create_typed_table_complex_combinations_pkey PRIMARY KEY (id))  USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true')  TABLESPACE pg_default
+NOTICE:  re-formed command: CREATE  TABLE  public.part8_create_typed_table_complex_combinations OF public.part8_people_type (weight WITH OPTIONS NOT NULL , CONSTRAINT part8_create_typed_table_complex_combinations_name_key UNIQUE (name), CONSTRAINT part8_create_typed_table_complex_combinations_pkey PRIMARY KEY (id))  USING heap WITH (vacuum_index_cleanup = 'on', autovacuum_vacuum_scale_factor = '0.2', vacuum_truncate = 'true') TABLESPACE pg_default
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET NOT NULL", "type": "set not null", "column": "id"}], "identity": {"objname": "part8_create_typed_table_complex_combinations", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.part8_create_typed_table_complex_combinations ALTER COLUMN id SET NOT NULL
 -- part9: create table as a partition of parent table, FOR VALUES clause is tested in part 6
@@ -485,24 +485,24 @@ CREATE TABLE part9_parent_table_range(
     height float4,
     weight float4
 ) PARTITION BY RANGE (height);
-NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_range (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , height pg_catalog.float4 STORAGE PLAIN      , weight pg_catalog.float4 STORAGE PLAIN      )  PARTITION BY RANGE (height)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_range (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , height pg_catalog.float4 STORAGE PLAIN      , weight pg_catalog.float4 STORAGE PLAIN      )  PARTITION BY RANGE (height)   
 CREATE TABLE part9_parent_table_list(
     id int,
     name varchar,
     height float4,
     weight float4
 ) PARTITION BY LIST (name);
-NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , height pg_catalog.float4 STORAGE PLAIN      , weight pg_catalog.float4 STORAGE PLAIN      )  PARTITION BY LIST (name)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_list (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , height pg_catalog.float4 STORAGE PLAIN      , weight pg_catalog.float4 STORAGE PLAIN      )  PARTITION BY LIST (name)   
 CREATE TABLE part9_parent_table_hash(
     id int,
     name varchar,
     height float4,
     weight float4
 ) PARTITION BY HASH (id);
-NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_hash (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , height pg_catalog.float4 STORAGE PLAIN      , weight pg_catalog.float4 STORAGE PLAIN      )  PARTITION BY HASH (id)    
+NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_hash (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog."varchar" STORAGE EXTENDED  COLLATE pg_catalog."default"    , height pg_catalog.float4 STORAGE PLAIN      , weight pg_catalog.float4 STORAGE PLAIN      )  PARTITION BY HASH (id)   
 -- TOFIX
 -- CREATE TABLE part9_partition_with_options_constraintscd
 -- PARTITION OF part9_parent_table_range (
@@ -533,8 +533,8 @@ NOTICE:  re-formed command: CREATE  TABLE  public.part9_parent_table_hash (id pg
 -- copied from old create_table.sql
 -- Test TableLikeClause is handled properly
 CREATE TABLE ctlt1 (a text CHECK (length(a) > 2) PRIMARY KEY, b text);
-NOTICE:  re-formed command: CREATE  TABLE  public.ctlt1 (a pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , b pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT ctlt1_a_check CHECK ((pg_catalog.length(a) OPERATOR(pg_catalog.>) 2)), CONSTRAINT ctlt1_pkey PRIMARY KEY (a))      
+NOTICE:  re-formed command: CREATE  TABLE  public.ctlt1 (a pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , b pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT ctlt1_a_check CHECK ((pg_catalog.length(a) OPERATOR(pg_catalog.>) 2)), CONSTRAINT ctlt1_pkey PRIMARY KEY (a))     
 ALTER TABLE ctlt1 ALTER COLUMN a SET STORAGE MAIN;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STORAGE %{storage}s", "type": "set storage", "column": "a", "storage": "main"}], "identity": {"objname": "ctlt1", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.ctlt1 ALTER COLUMN a SET STORAGE main
@@ -542,18 +542,18 @@ ALTER TABLE ctlt1 ALTER COLUMN b SET STORAGE EXTERNAL;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I SET STORAGE %{storage}s", "type": "set storage", "column": "b", "storage": "external"}], "identity": {"objname": "ctlt1", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.ctlt1 ALTER COLUMN b SET STORAGE external
 CREATE TABLE ctlt1_like (LIKE ctlt1 INCLUDING ALL);
-NOTICE:  re-formed command: CREATE  TABLE  public.ctlt1_like (a pg_catalog.text STORAGE MAIN  COLLATE pg_catalog."default"    , b pg_catalog.text STORAGE EXTERNAL  COLLATE pg_catalog."default"    , CONSTRAINT ctlt1_a_check CHECK ((pg_catalog.length(a) OPERATOR(pg_catalog.>) 2)), CONSTRAINT ctlt1_like_pkey PRIMARY KEY (a))      
+NOTICE:  re-formed command: CREATE  TABLE  public.ctlt1_like (a pg_catalog.text STORAGE MAIN  COLLATE pg_catalog."default"    , b pg_catalog.text STORAGE EXTERNAL  COLLATE pg_catalog."default"    , CONSTRAINT ctlt1_a_check CHECK ((pg_catalog.length(a) OPERATOR(pg_catalog.>) 2)), CONSTRAINT ctlt1_like_pkey PRIMARY KEY (a))     
 NOTICE:  deparsed json: <NULL>
 NOTICE:  re-formed command: <NULL>
 -- Test foreign key constraint is handled in a following ALTER TABLE ADD CONSTRAINT FOREIGN KEY REFERENCES subcommand
 CREATE TABLE product (id int PRIMARY KEY, name text);
-NOTICE:  re-formed command: CREATE  TABLE  public.product (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT product_pkey PRIMARY KEY (id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.product (id pg_catalog.int4 STORAGE PLAIN      , name pg_catalog.text STORAGE EXTENDED  COLLATE pg_catalog."default"    , CONSTRAINT product_pkey PRIMARY KEY (id))     
 CREATE TABLE orders2 (order_id int PRIMARY KEY, product_id int
 REFERENCES product (id));
-NOTICE:  re-formed command: CREATE  TABLE  public.orders2 (order_id pg_catalog.int4 STORAGE PLAIN      , product_id pg_catalog.int4 STORAGE PLAIN      , CONSTRAINT orders2_pkey PRIMARY KEY (order_id))      
+NOTICE:  re-formed command: CREATE  TABLE  public.orders2 (order_id pg_catalog.int4 STORAGE PLAIN      , product_id pg_catalog.int4 STORAGE PLAIN      , CONSTRAINT orders2_pkey PRIMARY KEY (order_id))     
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ADD CONSTRAINT %{name}I %{definition}s", "name": "orders2_product_id_fkey", "type": "add constraint", "definition": "FOREIGN KEY (product_id) REFERENCES public.product(id)"}], "identity": {"objname": "orders2", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.orders2 ADD CONSTRAINT orders2_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.product(id)
 -- Test CREATE and ALTER inherited table
@@ -561,11 +561,11 @@ CREATE TABLE gtest30 (
 a int,
 b int GENERATED ALWAYS AS (a * 2) STORED
 );
-NOTICE:  re-formed command: CREATE  TABLE  public.gtest30 (a pg_catalog.int4 STORAGE PLAIN      , b pg_catalog.int4 STORAGE PLAIN      GENERATED ALWAYS AS ((a OPERATOR(pg_catalog.*) 2)) STORED)      
+NOTICE:  re-formed command: CREATE  TABLE  public.gtest30 (a pg_catalog.int4 STORAGE PLAIN      , b pg_catalog.int4 STORAGE PLAIN      GENERATED ALWAYS AS ((a OPERATOR(pg_catalog.*) 2)) STORED)     
 CREATE TABLE gtest30_1 () INHERITS (gtest30);
-NOTICE:  re-formed command: CREATE  TABLE  public.gtest30_1 () INHERITS (public.gtest30)     
+NOTICE:  re-formed command: CREATE  TABLE  public.gtest30_1 () INHERITS (public.gtest30)    
 ALTER TABLE gtest30 ALTER COLUMN b DROP EXPRESSION;
 NOTICE:  deparsed json: {"fmt": "ALTER %{objtype}s %{only}s %{identity}D %{subcmds:, }s", "only": "", "objtype": "TABLE", "subcmds": [{"fmt": "ALTER COLUMN %{column}I DROP EXPRESSION %{if_exists}s", "type": "drop expression", "column": "b", "if_exists": ""}], "identity": {"objname": "gtest30", "schemaname": "public"}}
 NOTICE:  re-formed command: ALTER TABLE  public.gtest30 ALTER COLUMN b DROP EXPRESSION 
diff --git a/src/test/modules/test_ddl_deparse_regress/expected/test_ddl_deparse.out b/src/test/modules/test_ddl_deparse_regress/expected/test_ddl_deparse.out
index 0d8256d653..f022bfda4a 100644
--- a/src/test/modules/test_ddl_deparse_regress/expected/test_ddl_deparse.out
+++ b/src/test/modules/test_ddl_deparse_regress/expected/test_ddl_deparse.out
@@ -24,3 +24,5 @@ END;
 $$;
 CREATE EVENT TRIGGER test_ddl_deparse
 ON ddl_command_end EXECUTE PROCEDURE test_ddl_deparse();
+SET allow_in_place_tablespaces = true;
+CREATE TABLESPACE regress_tblspace LOCATION '';
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/alter_table.sql b/src/test/modules/test_ddl_deparse_regress/sql/alter_table.sql
index 0141a7e293..e9fe4b935c 100644
--- a/src/test/modules/test_ddl_deparse_regress/sql/alter_table.sql
+++ b/src/test/modules/test_ddl_deparse_regress/sql/alter_table.sql
@@ -45,8 +45,8 @@ ALTER TABLE test_drop_column DROP price;
 ALTER TABLE test_drop_column DROP COLUMN quantity;
 
 ALTER TABLE test_drop_column DROP IF EXISTS description RESTRICT;
--- TOFIX
--- ALTER TABLE test_drop_column DROP IF EXISTS name CASCADE;
+
+ALTER TABLE test_drop_column DROP IF EXISTS name CASCADE;
 
 -- ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ COLLATE collation ] [ USING expression ]
 CREATE TABLE test_alter_type(
@@ -226,8 +226,7 @@ CREATE TABLE test_drop_constraint_reference(
 ALTER TABLE test_drop_constraint_reference DROP CONSTRAINT test_drop_constraint_reference_cstr1;
 ALTER TABLE test_drop_constraint DROP CONSTRAINT test_drop_constraint_check RESTRICT;
 ALTER TABLE test_drop_constraint DROP CONSTRAINT IF EXISTS test_drop_constraint_check RESTRICT;
--- TOFIX
--- ALTER TABLE test_drop_constraint DROP CONSTRAINT test_drop_constraint_uniq CASCADE;
+ALTER TABLE test_drop_constraint DROP CONSTRAINT test_drop_constraint_uniq CASCADE;
 
 -- TODO: This should be tested with TRIGGER related testing
 -- DISABLE TRIGGER [ trigger_name | ALL | USER ]
@@ -417,11 +416,14 @@ ALTER TABLE test_set_schema SET SCHEMA new_test_schema;
 
 -- ALTER TABLE ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ]
 --     SET TABLESPACE new_tablespace [ NOWAIT ]
--- TOFIX: can not be caught by ddl_command_end event trigger.
--- Deparse of T_AlterTableMoveAllStmt is not supported,
--- TABLESPACE commands (global object commands) are also not supported.
--- ALTER TABLE ALL IN TABLESPACE pg_default SET TABLESPACE pg_default;
--- ALTER TABLE ALL IN TABLESPACE pg_default OWNED BY ddl_testing_role SET TABLESPACE pg_default;
+-- TABLESPACE commands (global object commands) are not supported.
+SET ROLE ddl_testing_role;
+CREATE TABLE test_all_in_tablespace(
+    c1 int
+);
+ALTER TABLE ALL IN TABLESPACE pg_default OWNED BY ddl_testing_role SET TABLESPACE regress_tblspace;
+ALTER TABLE ALL IN TABLESPACE regress_tblspace SET TABLESPACE pg_default;
+RESET ROLE;
 
 -- ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT }
 CREATE TABLE test_partition_attach_range(
@@ -430,22 +432,23 @@ CREATE TABLE test_partition_attach_range(
 CREATE TABLE test_partition_attach_range_p_1(
     LIKE test_partition_attach_range
 );
--- TOFIX
--- ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_1 DEFAULT;
+
+ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_1 DEFAULT;
 
 CREATE TABLE test_partition_attach_range_p_2(
     LIKE test_partition_attach_range
 );
--- TOFIX
--- ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_2 FOR VALUES FROM (100) TO (200);
+
+ALTER TABLE test_partition_attach_range ATTACH PARTITION test_partition_attach_range_p_2 FOR VALUES FROM (100) TO (200);
 CREATE TABLE test_partition_attach_hash(
     LIKE orders
 ) PARTITION BY HASH (id);
 CREATE TABLE test_partition_attach_hash_p(
     LIKE test_partition_attach_hash
 );
--- TOFIX
--- ALTER TABLE test_partition_attach_hash ATTACH PARTITION test_partition_attach_hash_p FOR VALUES WITH (MODULUS 10, REMAINDER 1);
+
+ALTER TABLE test_partition_attach_hash ATTACH PARTITION test_partition_attach_hash_p FOR VALUES WITH (MODULUS 10, REMAINDER 1);
+
 CREATE TABLE test_partition_attach_list(
     LIKE orders
 ) PARTITION BY LIST (name);
@@ -455,9 +458,9 @@ CREATE TABLE test_partition_attach_list_p1(
 CREATE TABLE test_partition_attach_list_p2(
     LIKE test_partition_attach_list
 );
--- TOFIX
--- ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p1 FOR VALUES IN ('key1');
--- ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p2 FOR VALUES IN ('key2', 'key3');
+
+ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p1 FOR VALUES IN ('key1');
+ALTER TABLE test_partition_attach_list ATTACH PARTITION test_partition_attach_list_p2 FOR VALUES IN ('key2', 'key3');
 
 -- DETACH PARTITION partition_name [ CONCURRENTLY | FINALIZE ]
 CREATE TABLE test_detach_partition(
@@ -466,8 +469,7 @@ CREATE TABLE test_detach_partition(
 CREATE TABLE test_detach_partition_p1 PARTITION OF test_detach_partition FOR VALUES FROM (1) TO (100);
 CREATE TABLE test_detach_partition_p2 PARTITION OF test_detach_partition FOR VALUES FROM (101) TO (200);
 CREATE TABLE test_detach_partition_p3 PARTITION OF test_detach_partition FOR VALUES FROM (201) TO (300);
--- TOFIX
--- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p1;
--- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p2 CONCURRENTLY;
+ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p1;
+ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p2 CONCURRENTLY;
 -- TOFIX: FINALIZE option is not testable
 -- ALTER TABLE test_detach_partition DETACH PARTITION test_detach_partition_p3 FINALIZE;
diff --git a/src/test/modules/test_ddl_deparse_regress/sql/test_ddl_deparse.sql b/src/test/modules/test_ddl_deparse_regress/sql/test_ddl_deparse.sql
index 736b33908c..1d64a2b991 100644
--- a/src/test/modules/test_ddl_deparse_regress/sql/test_ddl_deparse.sql
+++ b/src/test/modules/test_ddl_deparse_regress/sql/test_ddl_deparse.sql
@@ -27,3 +27,6 @@ $$;
 
 CREATE EVENT TRIGGER test_ddl_deparse
 ON ddl_command_end EXECUTE PROCEDURE test_ddl_deparse();
+
+SET allow_in_place_tablespaces = true;
+CREATE TABLESPACE regress_tblspace LOCATION '';
diff --git a/src/test/modules/test_ddl_deparse_regress/t/001_compare_dumped_results.pl b/src/test/modules/test_ddl_deparse_regress/t/001_compare_dumped_results.pl
index d6c27958c1..58cf7cdf33 100644
--- a/src/test/modules/test_ddl_deparse_regress/t/001_compare_dumped_results.pl
+++ b/src/test/modules/test_ddl_deparse_regress/t/001_compare_dumped_results.pl
@@ -144,6 +144,8 @@ sub create_deparse_testing_resources_on_pub_node {
             r record;
         begin
             for r in select * from pg_event_trigger_dropped_objects()
+		        -- Deparse only the TABLE related commands, skip the internally generated triggers, indexes etc.
+		        WHERE object_type in ('TABLE')
             loop
                 insert into deparsed_ddls(tag, object_identity, ddl) values (r.object_type, r.object_identity, public.deparse_drop_ddl(r.object_identity, r.object_type));
             end loop;
@@ -201,6 +203,10 @@ create_prerequisite_resources($pub_node, $initial_dbname, $user);
 create_prerequisite_resources($sub_node, $initial_dbname, $user);
 $pub_node -> safe_psql($initial_dbname, "CREATE DATABASE ${test_dbname};", extra_params => ["-U", "${user}"]);
 $sub_node -> safe_psql($initial_dbname, "CREATE DATABASE ${test_dbname};", extra_params => ["-U", "${user}"]);
+$pub_node -> safe_psql($initial_dbname, "SET allow_in_place_tablespaces = true;
+CREATE TABLESPACE regress_tblspace LOCATION '';");
+$sub_node -> safe_psql($initial_dbname, "SET allow_in_place_tablespaces = true;
+CREATE TABLESPACE regress_tblspace LOCATION '';");
 
 # load test cases from the regression tests
 my @regress_tests = split /\s+/, $ENV{REGRESS};
diff --git a/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.c b/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.c
index 04288c68e0..79036a197f 100644
--- a/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.c
+++ b/src/test/modules/test_ddl_deparse_regress/test_ddl_deparse_regress.c
@@ -58,10 +58,10 @@ deparse_drop_ddl(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 	}
 
-	command = deparse_drop_command(objidentity_str, objecttype_str, (Node *)&fake_node);
-
+	command = deparse_drop_table(objidentity_str, objecttype_str,
+											(Node *)&fake_node);
 	if (command)
 		PG_RETURN_TEXT_P(cstring_to_text(command));
 
 	PG_RETURN_NULL();
-}
\ No newline at end of file
+}
-- 
2.34.1

---

From: "Wei Wang (Fujitsu)" <[email-masked]>
Date: Tue, 6 Jun 2023 06:01:33 +0000

On Thur, June 1, 2023 at 23:42 vignesh C <[email-masked]> wrote:
> > CREATE  TABLE  public.parent (a pg_catalog.int4 STORAGE PLAIN      ,
> CONSTRAINT parent_pkey PRIMARY KEY (a))  PARTITION BY RANGE (a)
> > postgres=# CREATE  TABLE  public.parent (a pg_catalog.int4 STORAGE PLAIN      ,
> CONSTRAINT parent_pkey PRIMARY KEY (a))  PARTITION BY RANGE (a);
> > CREATE TABLE
> > postgres=# CREATE  TABLE  public.child PARTITION OF public.parent
> (CONSTRAINT child_pkey PRIMARY KEY (a)) DEFAULT;

I think this issue still exists after applying the 0008 patch. Is this error the
result we expected?
If no, I think we could try to address this issue in the function
deparse_Constraints_ToJsonb in 0008 patch like the attachment. What do you
think? BTW, we also need to skip the parentheses in the above case if you think
this approach is OK.

Regards,
Wang wei

---

From: shveta malik <[email-masked]>
Date: Thu, 8 Jun 2023 17:48:14 +0530

> think? BTW, we also need to skip the parentheses in the above case if you think

Thank You Wang-san for the patch, we have included the fix after
slight modification in the latest patch-set (*2023_06_08.patch).

thanks
Shveta