From d6649bb33c749bc60099acae303a80b1edd6f62e Mon Sep 17 00:00:00 2001
From: Vignesh C <vignesh21@gmail.com>
Date: Tue, 27 Jun 2023 14:45:27 +0530
Subject: [PATCH 5/5] POC for autogenerating "alter table" subcommands for ddl
 replication.

POC for autogenerating "alter table" subcommands for ddl replication.
The patch does the following:
a) Reads AlterTableType enum and the members of AlterTableType enum.
b) Each of the AlterTableType enum field has the following common code,
case Enum member:
        pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
        new_jsonb_VA(state, variable_count, "fmt", jbvString, variable_field);
        pushJsonbValue(&state, WJB_END_OBJECT, NULL);
        break;

In the above variable_count and variable_field values can be different.
These values can be annotated like below:
AT_ForceRowSecurity pg_node_attr(deparse_as(1:FORCE ROW LEVEL SECURITY))

Handles each of the AlterTableType enum with one of the below:
1) Generate the variable parameter case specified like:
When
"AT_ForceRowSecurity pg_node_attr(deparse_as(1:FORCE ROW LEVEL SECURITY)),"
is specified. Generate the following code:
case AT_ForceRowSecurity:
	pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
        new_jsonb_VA(state, 1, "fmt", jbvString, "FORCE ROW LEVEL SECURITY");
        pushJsonbValue(&state, WJB_END_OBJECT, NULL);
        break;

When
"AT_SetLogged pg_node_attr(deparse_as(2:SET LOGGED:type:set logged)),"
is specified. Generate the following code:
case AT_SetLogged:
       pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
			new_jsonb_VA(state, 2,
                       "fmt", jbvString, "SET LOGGED",
                       "type", jbvString, "set logged");
                       pushJsonbValue(&state, WJB_END_OBJECT, NULL);
	break;

soon for more number of variable parameters.
2) If there is no annotation specified, just add "case" and "break" statement like:
case AT_ReAddStatistics:
       /* Subtypes used for internal operations; nothing to do here */
	break;
3) If deparse_ignore annotation is specified, do not generate any code for this.
---
 src/backend/nodes/ddldeparsefuncs.c   | 374 +-------------------------
 src/backend/nodes/gen_node_support.pl | 241 ++++++++++++++++-
 src/include/nodes/parsenodes.h        | 199 +++++++++-----
 3 files changed, 382 insertions(+), 432 deletions(-)

diff --git a/src/backend/nodes/ddldeparsefuncs.c b/src/backend/nodes/ddldeparsefuncs.c
index bdded259c7..12d3050bdc 100644
--- a/src/backend/nodes/ddldeparsefuncs.c
+++ b/src/backend/nodes/ddldeparsefuncs.c
@@ -250,6 +250,8 @@ deparse_drop_table(const char *objidentity, const char *objecttype,
 	return JsonbToCString(&str, &jsonb->root, JSONB_ESTIMATED_LEN);
 }
 
+#define GET_ROLE_SPEC_DDL_ALTER_TBL get_rolespec_name(subcmd->newowner)
+
 /*
  * Deparse all the collected subcommands and return jsonb string representing
  * the alter command.
@@ -331,6 +333,7 @@ deparse_AlterTableStmt(CollectedCommand *cmd)
 	{
 		CollectedATSubcmd *sub = (CollectedATSubcmd *) lfirst(cell);
 		AlterTableCmd *subcmd = (AlterTableCmd *) sub->parsetree;
+		char *name = subcmd->name;
 
 		Assert(IsA(subcmd, AlterTableCmd));
 
@@ -442,15 +445,6 @@ deparse_AlterTableStmt(CollectedCommand *cmd)
 					relation_close(idx, AccessShareLock);
 					break;
 				}
-			case AT_ReAddIndex:
-			case AT_ReAddConstraint:
-			case AT_ReAddDomainConstraint:
-			case AT_ReAddComment:
-			case AT_ReplaceRelOptions:
-			case AT_CheckNotNull:
-			case AT_ReAddStatistics:
-				/* Subtypes used for internal operations; nothing to do here */
-				break;
 
 			case AT_ColumnDefault:
 				if (subcmd->def == NULL)
@@ -501,56 +495,6 @@ deparse_AlterTableStmt(CollectedCommand *cmd)
 
 				break;
 
-			case AT_DropNotNull:
-
-				/*
-				 * Syntax: ALTER COLUMN %{column}I DROP NOT NULL
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString,
-							 "ALTER COLUMN %{column}I DROP NOT NULL",
-							 "type", jbvString, "drop not null",
-							 "column", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_ForceRowSecurity:
-
-				/*
-				 * Syntax: FORCE ROW LEVEL SECURITY
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 1, "fmt", jbvString,
-							 "FORCE ROW LEVEL SECURITY");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_NoForceRowSecurity:
-
-				/*
-				 * Syntax: NO FORCE ROW LEVEL SECURITY
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 1, "fmt", jbvString,
-							 "NO FORCE ROW LEVEL SECURITY");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_SetNotNull:
-
-				/*
-				 * Syntax: ALTER COLUMN %{column}I SET NOT NULL
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString,
-							 "ALTER COLUMN %{column}I SET NOT NULL",
-							 "type", jbvString, "set not null",
-							 "column", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
 			case AT_DropExpression:
 				{
 					StringInfoData fmtSub;
@@ -783,20 +727,6 @@ deparse_AlterTableStmt(CollectedCommand *cmd)
 				}
 				break;
 
-			case AT_ValidateConstraint:
-
-				/*
-				 * Syntax: VALIDATE CONSTRAINT %{constraint}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString,
-							 "VALIDATE CONSTRAINT %{constraint}I",
-							 "type", jbvString, "validate constraint",
-							 "constraint", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
 			case AT_DropConstraint:
 				{
 					StringInfoData fmtSub;
@@ -908,109 +838,6 @@ deparse_AlterTableStmt(CollectedCommand *cmd)
 				}
 				break;
 
-			case AT_ChangeOwner:
-
-				/*
-				 * Syntax: OWNER TO %{owner}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString, "OWNER TO %{owner}I",
-							 "type", jbvString, "change owner",
-							 "owner", jbvString,
-							 get_rolespec_name(subcmd->newowner));
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_ClusterOn:
-
-				/*
-				 * Syntax: CLUSTER ON %{index}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString, "CLUSTER ON %{index}I",
-							 "type", jbvString, "cluster on",
-							 "index", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-
-			case AT_DropCluster:
-
-				/*
-				 * Syntax: SET WITHOUT CLUSTER
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString, "SET WITHOUT CLUSTER",
-							 "type", jbvString, "set without cluster");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_SetLogged:
-
-				/*
-				 * Syntax: SET LOGGED
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString, "SET LOGGED",
-							 "type", jbvString, "set logged");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_SetUnLogged:
-
-				/*
-				 * Syntax: SET UNLOGGED
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString, "SET UNLOGGED",
-							 "type", jbvString, "set unlogged");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_DropOids:
-
-				/*
-				 * Syntax: SET WITHOUT OIDS
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString, "SET WITHOUT OIDS",
-							 "type", jbvString, "set without oids");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_SetAccessMethod:
-
-				/*
-				 * Syntax: SET ACCESS METHOD %{access_method}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString,
-							 "SET ACCESS METHOD %{access_method}I",
-							 "type", jbvString, "set access method",
-							 "access_method", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_SetTableSpace:
-
-				/*
-				 * Syntax: SET TABLESPACE %{tablespace}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString, "SET TABLESPACE %{tablespace}I",
-							 "type", jbvString, "set tablespace",
-							 "tablespace", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
 			case AT_SetRelOptions:
 			case AT_ResetRelOptions:
 
@@ -1020,160 +847,6 @@ deparse_AlterTableStmt(CollectedCommand *cmd)
 				deparse_RelSetOptions_toJsonb(state, subcmd);
 				break;
 
-			case AT_EnableTrig:
-
-				/*
-				 * Syntax: ENABLE TRIGGER %{trigger}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString, "ENABLE TRIGGER %{trigger}I",
-							 "type", jbvString, "enable trigger",
-							 "trigger", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_EnableAlwaysTrig:
-
-				/*
-				 * Syntax: ENABLE ALWAYS TRIGGER %{trigger}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString,
-							 "ENABLE ALWAYS TRIGGER %{trigger}I",
-							 "type", jbvString, "enable always trigger",
-							 "trigger", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_EnableReplicaTrig:
-
-				/*
-				 * Syntax: ENABLE REPLICA TRIGGER %{trigger}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString,
-							 "ENABLE REPLICA TRIGGER %{trigger}I",
-							 "type", jbvString, "enable replica trigger",
-							 "trigger", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_DisableTrig:
-
-				/*
-				 * Syntax: DISABLE TRIGGER %{trigger}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString, "DISABLE TRIGGER %{trigger}I",
-							 "type", jbvString, "disable trigger",
-							 "trigger", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_EnableTrigAll:
-
-				/*
-				 * Syntax: ENABLE TRIGGER ALL
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString, "ENABLE TRIGGER ALL",
-							 "type", jbvString, "enable trigger all");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_DisableTrigAll:
-
-				/*
-				 * Syntax: DISABLE TRIGGER ALL
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString, "DISABLE TRIGGER ALL",
-							 "type", jbvString, "disable trigger all");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_EnableTrigUser:
-
-				/*
-				 * Syntax: ENABLE TRIGGER USER
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString, "ENABLE TRIGGER USER",
-							 "type", jbvString, "enable trigger user");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_DisableTrigUser:
-
-				/*
-				 * Syntax: DISABLE TRIGGER USER
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString, "DISABLE TRIGGER USER",
-							 "type", jbvString, "disable trigger user");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_EnableRule:
-
-				/*
-				 * Syntax: ENABLE RULE %{rule}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString, "ENABLE RULE %{rule}I",
-							 "type", jbvString, "enable rule",
-							 "rule", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_EnableAlwaysRule:
-
-				/*
-				 * Syntax: ENABLE ALWAYS RULE %{rule}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString, "ENABLE ALWAYS RULE %{rule}I",
-							 "type", jbvString, "enable always rule",
-							 "rule", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_EnableReplicaRule:
-
-				/*
-				 * Syntax: ENABLE REPLICA RULE %{rule}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString, "ENABLE REPLICA RULE %{rule}I",
-							 "type", jbvString, "enable replica rule",
-							 "rule", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_DisableRule:
-
-				/*
-				 * Syntax: DISABLE RULE %{rule}I
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 3,
-							 "fmt", jbvString, "DISABLE RULE %{rule}I",
-							 "type", jbvString, "disable rule",
-							 "rule", jbvString, subcmd->name);
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
 			case AT_AddInherit:
 
 				/*
@@ -1215,18 +888,6 @@ deparse_AlterTableStmt(CollectedCommand *cmd)
 				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				break;
 
-			case AT_DropOf:
-
-				/*
-				 * Syntax: NOT OF
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString, "NOT OF",
-							 "type", jbvString, "not of");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
 			case AT_ReplicaIdentity:
 
 				/*
@@ -1259,32 +920,6 @@ deparse_AlterTableStmt(CollectedCommand *cmd)
 				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 				break;
 
-			case AT_EnableRowSecurity:
-
-				/*
-				 * Syntax: ENABLE ROW LEVEL SECURITY
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString,
-							 "ENABLE ROW LEVEL SECURITY",
-							 "type", jbvString, "enable row security");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
-			case AT_DisableRowSecurity:
-
-				/*
-				 * Syntax: DISABLE ROW LEVEL SECURITY
-				 */
-				pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
-				new_jsonb_VA(state, 2,
-							 "fmt", jbvString,
-							 "DISABLE ROW LEVEL SECURITY",
-							 "type", jbvString, "disable row security");
-				pushJsonbValue(&state, WJB_END_OBJECT, NULL);
-				break;
-
 			case AT_AttachPartition:
 				{
 					StringInfoData fmtSub;
@@ -1509,6 +1144,9 @@ deparse_AlterTableStmt(CollectedCommand *cmd)
 					pushJsonbValue(&state, WJB_END_OBJECT, NULL);
 					break;
 				}
+
+			#include "ddldeparsefuncs_alter.switch.c"
+
 			default:
 				elog(WARNING, "unsupported alter table subtype %d",
 					 subcmd->subtype);
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 04757b79fb..3c63905361 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -117,6 +117,8 @@ my @node_types = qw(Node);
 # collect info for each node type
 my %node_type_info;
 
+my %enum_type_info;
+
 # node types we don't want copy support for
 my @no_copy;
 # node types we don't want equal support for
@@ -125,6 +127,8 @@ my @no_equal;
 my @no_query_jumble;
 # node types we don't want ddl deparse support for
 my @no_ddl_deparse;
+# node types we don't want ddl alter deparse support for
+my @no_ddl_alt_deparse;
 # node types we don't want read support for
 my @no_read;
 # node types we don't want read/write support for
@@ -167,6 +171,7 @@ push @no_copy, qw(List);
 push @no_equal, qw(List);
 push @no_query_jumble, qw(List);
 push @no_ddl_deparse, qw(List);
+push @no_ddl_alt_deparse, qw(List);
 push @special_read_write, qw(List);
 
 # Nodes with custom copy/equal implementations are skipped from
@@ -200,6 +205,7 @@ my $next_input_file = 0;
 foreach my $infile (@ARGV)
 {
 	my $in_struct;
+	my $in_enum;
 	my $subline;
 	my $is_node_struct;
 	my $supertype;
@@ -248,7 +254,7 @@ foreach my $infile (@ARGV)
 		next if $line =~ /^#(define|ifdef|endif)/;
 
 		# within a struct, don't process until we have whole logical line
-		if ($in_struct && $subline > 0)
+		if (($in_struct) && $subline > 0)
 		{
 			if ($line =~ /;$/)
 			{
@@ -269,6 +275,134 @@ foreach my $infile (@ARGV)
 			}
 		}
 
+		# we are analyzing a struct definition
+		if ($in_enum)
+		{
+			$subline++;
+
+			# first line should have opening brace
+			if ($subline == 1)
+			{
+				next if $line eq '{';
+				die "$infile:$lineno: expected opening brace\n";
+			}
+			# second line could be node attributes
+			elsif ($subline == 2
+				&& $line =~ /^\s*pg_node_attr\(([\w(), ]*)\)$/)
+			{
+				$node_attrs = $1;
+				$node_attrs_lineno = $lineno;
+				# hack: don't count the line
+				$subline--;
+				next;
+			}
+			# next line should have node tag or supertype
+			elsif ($subline == 2)
+			{
+				if ($line =~ /^\s*NodeTag\s+type;/)
+				{
+					$is_node_struct = 1;
+					next;
+				}
+				elsif ($line =~ /\s*(\w+),/ and elem $1, @node_types)
+				{
+					$is_node_struct = 1;
+					$supertype = $1;
+					$supertype_field = $2;
+					next;
+				}
+			}
+
+			# end of enum
+			if ($line =~ /^\}\s*(?:\Q$in_enum\E\s*)?;$/)
+			{
+				# field names, types, attributes
+				my @f = @my_fields;
+				my %fa = %my_field_attrs;
+
+				# save in global info structure
+				$enum_type_info{$in_enum}->{fields} = \@f;
+				$enum_type_info{$in_enum}->{field_attrs} = \%fa;
+
+				# start new cycle
+				$in_enum = undef;
+				$node_attrs = '';
+				@my_fields = ();
+				%my_field_types = ();
+				%my_field_attrs = ();
+			}
+			# normal enum field
+			elsif ($line =~
+				/^\s*\b(\w+)\s*(?:pg_node_attr\(([\w(\w%{\w}\w:), ]*)\))?,/
+			  )
+			{
+				my $name = $1;
+				my $attrs = $2;
+				my $var = $3;
+
+				my @attrs;
+				if ($attrs)
+				{
+					@attrs = split /,\s*/, $attrs;
+				}
+
+				push @my_fields, $name;
+				$my_field_attrs{$name} = \@attrs;
+			}
+			# normal enum field
+			elsif ($line =~
+				/^\s*\b(\w+)\s*(?:pg_node_attr\(([\w(\w:), ]*)\))?/
+			  )
+			{
+				my $name = $1;
+				my $attrs = $2;
+				my $var = $3;
+
+				my @attrs;
+				if ($attrs)
+				{
+					@attrs = split /,\s*/, $attrs;
+					foreach my $attr (@attrs)
+					{
+						if (   $attr !~ /^array_size\(\w+\)$/
+							&& $attr !~ /^copy_as\(\w+\)$/
+							&& $attr !~ /^read_as\(\w+\)$/
+							&& $attr !~ /^deparse_as\([\w\s]+\)$/
+							&& $attr !~ /^deparse_as\(([\w:]*)\)$/
+							&& !elem $attr,
+							qw(copy_as_scalar
+							equal_as_scalar
+							equal_ignore
+							equal_ignore_if_zero
+							query_jumble_ignore
+							query_jumble_location
+							read_write_ignore
+							deparse_ignore
+							write_only_relids
+							write_only_nondefault_pathtarget
+							write_only_req_outer))
+						{
+							die
+								"$infile:$lineno: unrecognized attribute \"$attr\"\n";
+						}
+					}
+				}
+
+				push @my_fields, $name;
+				$my_field_attrs{$name} = \@attrs;
+			}
+			elsif ($line =~ /^\s*/)
+			{
+				next;
+			}
+			else
+			{
+				# We're not too picky about what's outside structs,
+				# but we'd better understand everything inside.
+				die "$infile:$lineno: could not parse enum field\"$line\"\n";
+			}
+		}
+
 		# we are analyzing a struct definition
 		if ($in_struct)
 		{
@@ -576,6 +710,18 @@ foreach my $infile (@ARGV)
 			elsif ($line =~ /^typedef enum (\w+)(\s*\/\*.*)?$/)
 			{
 				push @enum_types, $1;
+
+				if ($1 eq 'AlterTableType')
+				{
+					$in_enum = $1;
+					$subline = 0;
+				}
+			}
+
+			# end of enum
+			if ($in_enum and $line =~ /^\}\s*(?:\Q$in_enum\E\s*)?;$/)
+			{
+				$in_enum = undef;
 			}
 		}
 	}
@@ -1425,8 +1571,6 @@ deparse_${n}(JsonbParseState *state, Oid objectId, Relation relation, Node *pars
 			{
 				$read_as_field1 = $2;
 				$read_as_field2 = $3;
-				print "field1 = $2\n";
-				print "field2 = $3\n";
 			}
 			elsif ($a eq 'deparse_ignore')
 			{
@@ -1561,6 +1705,97 @@ deparse_${n}(JsonbParseState *state, Oid objectId, Relation relation, Node *pars
 close $ddlff;
 close $ddlfs;
 
+
+# ddldeparsefuncs_alter.switch.c
+
+push @output_files, 'ddldeparsefuncs_alter.switch.c';
+open my $ddlalterfs, '>', "$output_path/ddldeparsefuncs_alter.switch.c$tmpext" or die $!;
+
+printf $ddlalterfs $header_comment, 'ddldeparsefuncs_alter.switch.c';
+
+my @enum_ddl_deparse_alter;
+push @enum_ddl_deparse_alter, "AlterTableType";
+
+foreach my $n (@enum_ddl_deparse_alter)
+{
+	# print instructions for each field
+	foreach my $f (@{ $enum_type_info{$n}->{fields} })
+	{
+		my @a = @{ $enum_type_info{$n}->{field_attrs}{$f} };
+		my $ddl_deparse_alter_ignore;
+
+		# extract per-field attributes
+		foreach my $a (@a)
+		{
+			if ($a =~ /^deparse_as\((([\w\s]+):([\w\s]+))\)$/)
+			{
+				print $ddlalterfs "\t\t\tcase ${f}:\n"
+					. "\t\t\t\tpushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);\n"
+					. "\t\t\t\tnew_jsonb_VA(state, $2, \"fmt\", jbvString, \"$3\");\n"
+					. "\t\t\t\tpushJsonbValue(&state, WJB_END_OBJECT, NULL);\n"
+					. "\t\t\t\tbreak;\n"
+					unless $ddl_deparse_alter_ignore;
+			}
+			elsif ($a =~ /^deparse_as\((([\w\s]+):([\w\s]+):([\w\s]+):([\w\s]+))\)$/)
+			{
+				print $ddlalterfs "\t\t\tcase ${f}:\n"
+					. "\t\t\t\tpushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);\n"
+					. "\t\t\t\tnew_jsonb_VA(state, $2,\n"
+					. "\t\t\t\t             \"fmt\", jbvString, \"$3\",\n"
+					. "\t\t\t\t             \"$4\", jbvString, \"$5\");\n"
+					. "\t\t\t\tpushJsonbValue(&state, WJB_END_OBJECT, NULL);\n"
+					. "\t\t\t\tbreak;\n"
+					unless $ddl_deparse_alter_ignore;
+			}
+			elsif ($a =~ /^deparse_as\((([\w\s]+):([\w\s%{\w}\w\s]+):([\w\s]+):([\w\s]+):([\w\s]+):([\w\s]+))\)$/)
+			{
+				print $ddlalterfs "\t\t\tcase ${f}:\n"
+					. "\t\t\t\tpushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);\n"
+					. "\t\t\t\tnew_jsonb_VA(state, $2,\n"
+					. "\t\t\t\t             \"fmt\", jbvString,\n"
+					. "\t\t\t\t             \"$3\",\n"
+					. "\t\t\t\t             \"$4\", jbvString, \"$5\",\n"
+					. "\t\t\t\t             \"$6\", jbvString, $7);\n"
+					. "\t\t\t\tpushJsonbValue(&state, WJB_END_OBJECT, NULL);\n"
+					. "\t\t\t\tbreak;\n"
+					unless $ddl_deparse_alter_ignore;
+			}
+			elsif ($a =~ /^deparse_as\((([\w\s]+):([\w\s%{\w}\w\s]+):([\w\s]+):([\w\s]+):([\w\s]+):([\w\s]+):([\w\s]+):([\w\s]+))\)$/)
+			{
+				print $ddlalterfs "\t\t\tcase ${f}:\n"
+					. "\t\t\t\tpushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);\n"
+					. "\t\t\t\tnew_jsonb_VA(state, $2,\n"
+					. "\t\t\t\t             \"fmt\", jbvString,\n"
+					. "\t\t\t\t             \"$3\",\n"
+					. "\t\t\t\t             \"$4\", jbvString, \"$5\",\n"
+					. "\t\t\t\t             \"$6\", jbvString, \"$7\",\n"
+					. "\t\t\t\t             \"$8\", jbvString, $9);\n"
+					. "\t\t\t\tpushJsonbValue(&state, WJB_END_OBJECT, NULL);\n"
+					. "\t\t\t\tbreak;\n"
+					unless $ddl_deparse_alter_ignore;
+			}
+			elsif ($a eq 'deparse_ignore')
+			{
+				$ddl_deparse_alter_ignore = 1;
+			}
+			else
+			{
+				die "$a did not match";
+			}
+		}
+
+		if (!@a)
+		{
+			print $ddlalterfs "\t\t\t case ${f}:\n"
+				. "\t\t\t\t /* Subtypes used for internal operations; nothing to do here */\n"
+				. "\t\t\t\t break;\n"
+				unless $ddl_deparse_alter_ignore;
+		}
+	}
+}
+
+close $ddlalterfs;
+
 # now rename the temporary files to their final names
 foreach my $file (@output_files)
 {
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 86532a3f02..c5ff3189c5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2170,73 +2170,150 @@ typedef struct AlterTableStmt
 	bool		table_like;		/* internally generated for TableLikeClause */
 } AlterTableStmt;
 
+/*
+ * Various annotations are used for auto generating the required jsonb code for
+ * the sub commands:
+ * a) deparse_as: It uses the following format
+ * 		deparse_as(count:format:key-2:val-2:key-3:val-3:...:key-n:val-n)
+ * In the above 1) count means the number of key value pairs 2) Specify the
+ * format for this command (No need to specify key for format as we always use
+ * "fmt" as key for format). 3) key-n:val-n are the key/values pairs that are
+ * required to generate the command.
+ * b) deparse_ignore: This field will have custom implementation. This will be
+ * used for complex implementation where it will require accessing catalog
+ * tables, it will skip generation of code in this case.
+ * c) no annotation: This can be used in cases which don't require any
+ * implementation, it will just add a case and break statement without any
+ * implementation.
+ */
 typedef enum AlterTableType
 {
-	AT_AddColumn,				/* add column */
-	AT_AddColumnToView,			/* implicitly via CREATE OR REPLACE VIEW */
-	AT_ColumnDefault,			/* alter column default */
-	AT_CookedColumnDefault,		/* add a pre-cooked column default */
-	AT_DropNotNull,				/* alter column drop not null */
-	AT_SetNotNull,				/* alter column set not null */
-	AT_DropExpression,			/* alter column drop expression */
-	AT_CheckNotNull,			/* check column is already marked not null */
-	AT_SetStatistics,			/* alter column set statistics */
-	AT_SetOptions,				/* alter column set ( options ) */
-	AT_ResetOptions,			/* alter column reset ( options ) */
-	AT_SetStorage,				/* alter column set storage */
-	AT_SetCompression,			/* alter column set compression */
-	AT_DropColumn,				/* drop column */
-	AT_AddIndex,				/* add index */
-	AT_ReAddIndex,				/* internal to commands/tablecmds.c */
-	AT_AddConstraint,			/* add constraint */
+	/* add column */
+	AT_AddColumn pg_node_attr(deparse_ignore),
+	/* implicitly via CREATE OR REPLACE VIEW */
+	AT_AddColumnToView pg_node_attr(deparse_ignore),
+	/* alter column default */
+	AT_ColumnDefault pg_node_attr(deparse_ignore),
+	/* add a pre-cooked column default */
+	AT_CookedColumnDefault pg_node_attr(deparse_ignore),
+	/* alter column drop not null */
+	AT_DropNotNull pg_node_attr(deparse_as(3:ALTER COLUMN %{column}I DROP NOT NULL:type:drop not null:column:name)),
+	/* alter column set not null */
+	AT_SetNotNull pg_node_attr(deparse_as(3:ALTER COLUMN %{column}I SET NOT NULL:type:set not null:column:name)),
+	/* alter column drop expression */
+	AT_DropExpression pg_node_attr(deparse_ignore),
+	/* check column is already marked not null */
+	AT_CheckNotNull,
+	/* alter column set statistics */
+	AT_SetStatistics pg_node_attr(deparse_ignore),
+	/* alter column set ( options ) */
+	AT_SetOptions pg_node_attr(deparse_ignore),
+	/* alter column reset ( options ) */
+	AT_ResetOptions  pg_node_attr(deparse_ignore),
+	/* alter column set storage */
+	AT_SetStorage pg_node_attr(deparse_ignore),
+	/* alter column set compression */
+	AT_SetCompression pg_node_attr(deparse_ignore),
+	/* drop column */
+	AT_DropColumn pg_node_attr(deparse_ignore),
+	/* add index */
+	AT_AddIndex pg_node_attr(deparse_ignore),
+	/* internal to commands/tablecmds.c */
+	AT_ReAddIndex,
+	/* add constraint */
+	AT_AddConstraint pg_node_attr(deparse_ignore),
 	AT_ReAddConstraint,			/* internal to commands/tablecmds.c */
 	AT_ReAddDomainConstraint,	/* internal to commands/tablecmds.c */
-	AT_AlterConstraint,			/* alter constraint */
-	AT_ValidateConstraint,		/* validate constraint */
-	AT_AddIndexConstraint,		/* add constraint using existing index */
-	AT_DropConstraint,			/* drop constraint */
+	/* alter constraint */
+	AT_AlterConstraint pg_node_attr(deparse_ignore),
+	/* validate constraint */
+	AT_ValidateConstraint pg_node_attr(deparse_as(3:VALIDATE CONSTRAINT %{constraint}I:type:validate constraint:constraint:name)),
+	/* add constraint using existing index */
+	AT_AddIndexConstraint pg_node_attr(deparse_ignore),
+	/* drop constraint */
+	AT_DropConstraint pg_node_attr(deparse_ignore),
 	AT_ReAddComment,			/* internal to commands/tablecmds.c */
-	AT_AlterColumnType,			/* alter column type */
-	AT_AlterColumnGenericOptions,	/* alter column OPTIONS (...) */
-	AT_ChangeOwner,				/* change owner */
-	AT_ClusterOn,				/* CLUSTER ON */
-	AT_DropCluster,				/* SET WITHOUT CLUSTER */
-	AT_SetLogged,				/* SET LOGGED */
-	AT_SetUnLogged,				/* SET UNLOGGED */
-	AT_DropOids,				/* SET WITHOUT OIDS */
-	AT_SetAccessMethod,			/* SET ACCESS METHOD */
-	AT_SetTableSpace,			/* SET TABLESPACE */
-	AT_SetRelOptions,			/* SET (...) -- AM specific parameters */
-	AT_ResetRelOptions,			/* RESET (...) -- AM specific parameters */
+	/* alter column type */
+	AT_AlterColumnType pg_node_attr(deparse_ignore),
+	/* alter column OPTIONS (...) */
+	AT_AlterColumnGenericOptions pg_node_attr(deparse_ignore),
+	/* change owner */
+	AT_ChangeOwner pg_node_attr(deparse_as(3:OWNER TO %{owner}I:type:change owner:owner:GET_ROLE_SPEC_DDL_ALTER_TBL)),
+	/* CLUSTER ON */
+	AT_ClusterOn pg_node_attr(deparse_as(3:CLUSTER ON %{index}I:type:cluster on:index:name)),
+	/* SET WITHOUT CLUSTER */
+	AT_DropCluster			pg_node_attr(deparse_as(2:SET WITHOUT CLUSTER:type:set without cluster)),
+	/* SET LOGGED */
+	AT_SetLogged pg_node_attr(deparse_as(2:SET LOGGED:type:set logged)),
+	/* SET UNLOGGED */
+	AT_SetUnLogged pg_node_attr(deparse_as(2:SET UNLOGGED:type:set unlogged)),
+	/* SET WITHOUT OIDS */
+	AT_DropOids pg_node_attr(deparse_as(2:SET WITHOUT OIDS:type:set without oids)),
+	/* SET ACCESS METHOD */
+	AT_SetAccessMethod pg_node_attr(deparse_as(3:SET ACCESS METHOD %{access_method}I:type:set access method:access_method:name)),
+	/* SET TABLESPACE */
+	AT_SetTableSpace pg_node_attr(deparse_as(3:SET TABLESPACE %{tablespace}I:type:set tablespace:tablespace:name)),
+	/* SET (...) -- AM specific parameters */
+	AT_SetRelOptions  pg_node_attr(deparse_ignore),
+	/* RESET (...) -- AM specific parameters */
+	AT_ResetRelOptions pg_node_attr(deparse_ignore),
 	AT_ReplaceRelOptions,		/* replace reloption list in its entirety */
-	AT_EnableTrig,				/* ENABLE TRIGGER name */
-	AT_EnableAlwaysTrig,		/* ENABLE ALWAYS TRIGGER name */
-	AT_EnableReplicaTrig,		/* ENABLE REPLICA TRIGGER name */
-	AT_DisableTrig,				/* DISABLE TRIGGER name */
-	AT_EnableTrigAll,			/* ENABLE TRIGGER ALL */
-	AT_DisableTrigAll,			/* DISABLE TRIGGER ALL */
-	AT_EnableTrigUser,			/* ENABLE TRIGGER USER */
-	AT_DisableTrigUser,			/* DISABLE TRIGGER USER */
-	AT_EnableRule,				/* ENABLE RULE name */
-	AT_EnableAlwaysRule,		/* ENABLE ALWAYS RULE name */
-	AT_EnableReplicaRule,		/* ENABLE REPLICA RULE name */
-	AT_DisableRule,				/* DISABLE RULE name */
-	AT_AddInherit,				/* INHERIT parent */
-	AT_DropInherit,				/* NO INHERIT parent */
-	AT_AddOf,					/* OF <type_name> */
-	AT_DropOf,					/* NOT OF */
-	AT_ReplicaIdentity,			/* REPLICA IDENTITY */
-	AT_EnableRowSecurity,		/* ENABLE ROW SECURITY */
-	AT_DisableRowSecurity,		/* DISABLE ROW SECURITY */
-	AT_ForceRowSecurity,		/* FORCE ROW SECURITY */
-	AT_NoForceRowSecurity,		/* NO FORCE ROW SECURITY */
-	AT_GenericOptions,			/* OPTIONS (...) */
-	AT_AttachPartition,			/* ATTACH PARTITION */
-	AT_DetachPartition,			/* DETACH PARTITION */
-	AT_DetachPartitionFinalize, /* DETACH PARTITION FINALIZE */
-	AT_AddIdentity,				/* ADD IDENTITY */
-	AT_SetIdentity,				/* SET identity column options */
-	AT_DropIdentity,			/* DROP IDENTITY */
+	/* ENABLE TRIGGER name */
+	AT_EnableTrig pg_node_attr(deparse_as(3:ENABLE TRIGGER %{trigger}I:type:enable trigger:trigger:name)),
+	/* ENABLE ALWAYS TRIGGER name */
+	AT_EnableAlwaysTrig pg_node_attr(deparse_as(3:ENABLE ALWAYS TRIGGER %{trigger}I:type:enable always trigger:trigger:name)),
+	/* ENABLE REPLICA TRIGGER name */
+	AT_EnableReplicaTrig pg_node_attr(deparse_as(3:ENABLE REPLICA TRIGGER %{trigger}I:type:enable replica trigger:trigger:name)),
+	/* DISABLE TRIGGER name */
+	AT_DisableTrig 	pg_node_attr(deparse_as(3:DISABLE TRIGGER %{trigger}I:type:disable trigger:trigger:name)),
+	/* ENABLE TRIGGER ALL */
+	AT_EnableTrigAll pg_node_attr(deparse_as(2:ENABLE TRIGGER ALL:type:enable trigger all)),
+	/* DISABLE TRIGGER ALL */
+	AT_DisableTrigAll pg_node_attr(deparse_as(2:DISABLE TRIGGER ALL:type:disable trigger all)),
+	/* ENABLE TRIGGER USER */
+	AT_EnableTrigUser pg_node_attr(deparse_as(2:ENABLE TRIGGER USER:type:enable trigger user)),
+	/* DISABLE TRIGGER USER */
+	AT_DisableTrigUser pg_node_attr(deparse_as(2:DISABLE TRIGGER USER:type:disable trigger user)),
+	/* ENABLE RULE name */
+	AT_EnableRule pg_node_attr(deparse_as(3:ENABLE RULE %{rule}I:type:enable rule:rule:name)),
+	/* ENABLE ALWAYS RULE name */
+	AT_EnableAlwaysRule pg_node_attr(deparse_as(3:ENABLE ALWAYS RULE %{rule}I:type:enable always rule:rule:name)),
+	/* ENABLE REPLICA RULE name */
+	AT_EnableReplicaRule pg_node_attr(deparse_as(3:ENABLE REPLICA RULE %{rule}I:type:enable replica rule:rule:name)),
+	/* DISABLE RULE name */
+	AT_DisableRule pg_node_attr(deparse_as(3:DISABLE RULE %{rule}I:type:disable rule:rule:name)),
+	/* INHERIT parent */
+	AT_AddInherit pg_node_attr(deparse_ignore),
+	/* NO INHERIT parent */
+	AT_DropInherit pg_node_attr(deparse_ignore),
+	/* OF <type_name> */
+	AT_AddOf pg_node_attr(deparse_ignore),
+	/* NOT OF */
+	AT_DropOf pg_node_attr(deparse_as(2:NOT OF:type:not of)),
+	/* REPLICA IDENTITY */
+	AT_ReplicaIdentity pg_node_attr(deparse_ignore),
+	/* ENABLE ROW SECURITY */
+	AT_EnableRowSecurity pg_node_attr(deparse_as(2:ENABLE ROW LEVEL SECURITY:type:enable row security)),
+	/* DISABLE ROW SECURITY */
+	AT_DisableRowSecurity pg_node_attr(deparse_as(2:DISABLE ROW LEVEL SECURITY:type:disable row security)),
+	/* FORCE ROW SECURITY */
+	AT_ForceRowSecurity pg_node_attr(deparse_as(1:FORCE ROW LEVEL SECURITY)),
+	/* NO FORCE ROW SECURITY */
+	AT_NoForceRowSecurity pg_node_attr(deparse_as(1:NO FORCE ROW LEVEL SECURITY)),
+	/* OPTIONS (...) */
+	AT_GenericOptions pg_node_attr(deparse_ignore),
+	/* ATTACH PARTITION */
+	AT_AttachPartition pg_node_attr(deparse_ignore),
+	/* DETACH PARTITION */
+	AT_DetachPartition pg_node_attr(deparse_ignore),
+	/* DETACH PARTITION FINALIZE */
+	AT_DetachPartitionFinalize pg_node_attr(deparse_ignore),
+	/* ADD IDENTITY */
+	AT_AddIdentity  pg_node_attr(deparse_ignore),
+	/* SET identity column options */
+	AT_SetIdentity  pg_node_attr(deparse_ignore),
+	/* DROP IDENTITY */
+	AT_DropIdentity  pg_node_attr(deparse_ignore),
 	AT_ReAddStatistics			/* internal to commands/tablecmds.c */
 } AlterTableType;
 
-- 
2.34.1

