diff --git a/src/backend/commands/ddl_deparse.c b/src/backend/commands/ddl_deparse.c
--- a/src/backend/commands/ddl_deparse.c
+++ b/src/backend/commands/ddl_deparse.c
@@ -796,7 +796,7 @@
  */
 static void
 deparse_ColumnDef(JsonbInState *state, Oid relid, List *dpcontext,
-				  ColumnDef *coldef)
+				  ColumnDef *coldef, const CollectedATSubcmd *sub)
 {
 	HeapTuple	attrTup;
 	Form_pg_attribute attrForm;
@@ -894,7 +894,15 @@
 	 */
 	constraint = find_column_constraint(coldef, CONSTR_DEFAULT);
 	if (constraint != NULL)
-		deparse_column_default(state, relid, attrForm->attnum, dpcontext);
+	{
+		if (sub != NULL && sub->default_expr_captured)
+			new_jsonb_string_clause(state, "default", " DEFAULT %{default_expr}s",
+									"default_expr", sub->default_expr != NULL ?
+									deparse_expression(sub->default_expr, dpcontext, false, false) :
+									"NULL");
+		else
+			deparse_column_default(state, relid, attrForm->attnum, dpcontext);
+	}
 	else
 		new_jsonb_null(state, "default");
 
@@ -1668,7 +1676,7 @@
 			}
 
 			if (raw != NULL)
-				deparse_ColumnDef(state, objectId, dpcontext, raw);
+				deparse_ColumnDef(state, objectId, dpcontext, raw, NULL);
 			else
 				deparse_ColumnDef_like(state, objectId, dpcontext, att);
 
@@ -1713,7 +1721,7 @@
 												(ColumnDef *) elt);
 					else
 						deparse_ColumnDef(state, objectId, dpcontext,
-										  (ColumnDef *) elt);
+										  (ColumnDef *) elt, NULL);
 					nelems++;
 
 					break;
@@ -2098,11 +2106,15 @@
  *
  * Some subcommands (ADD/SET IDENTITY) are rewritten by parse analysis, with
  * their options split out into a separate internal ALTER SEQUENCE.  The raw
- * subcommand is what carries the clause as the user wrote it.
+ * subcommand is what carries the clause as the user wrote it. Matching
+ * occurrences are consumed in collection order, so repeated identity clauses
+ * for the same column do not all reuse the first raw clause. Their collected
+ * order follows their raw order; internally generated clauses and recursed
+ * children are filtered out before reaching this helper.
  */
 static AlterTableCmd *
 find_raw_alter_column(AlterTableStmt *rawstmt, AlterTableType subtype,
-					  const char *colname)
+					  const char *colname, List **matched_cmds)
 {
 	ListCell   *lc;
 
@@ -2112,8 +2124,12 @@
 
 		if (rawcmd->subtype == subtype &&
 			rawcmd->name != NULL &&
-			strcmp(rawcmd->name, colname) == 0)
+			strcmp(rawcmd->name, colname) == 0 &&
+			!list_member_ptr(*matched_cmds, rawcmd))
+		{
+			*matched_cmds = lappend(*matched_cmds, rawcmd);
 			return rawcmd;
+		}
 	}
 
 	elog(ERROR, "could not find raw ALTER COLUMN subcommand for column \"%s\"",
@@ -2328,7 +2344,7 @@
 static bool
 deparse_AlterTableSubcmd(JsonbInState *state, Oid relId, List *dpcontext,
 						 CollectedATSubcmd *sub, AlterTableCmd *subcmd,
-						 AlterTableStmt *rawstmt, List *cmds)
+						 AlterTableStmt *rawstmt, List *cmds, List **matched_cmds)
 {
 	switch (subcmd->subtype)
 	{
@@ -2354,7 +2370,7 @@
 					new_jsonb_null(state, "if_not_exists");
 
 				insert_jsonb_key(state, "definition");
-				deparse_ColumnDef(state, relId, dpcontext, rawcoldef);
+				deparse_ColumnDef(state, relId, dpcontext, rawcoldef, sub);
 				end_jsonb_object(state);
 			}
 			return true;
@@ -2399,9 +2415,13 @@
 				 * Tolerate its absence and render it as written, as the
 				 * CREATE TABLE path does in deparse_column_default().
 				 */
-				defexpr = get_stored_default_expr(relId,
-												  ((Form_pg_attribute) GETSTRUCT(atup))->attnum,
-												  dpcontext, true);
+				if (sub->default_expr_captured)
+					defexpr = sub->default_expr != NULL ?
+						deparse_expression(sub->default_expr, dpcontext, false, false) : NULL;
+				else
+					defexpr = get_stored_default_expr(relId,
+													  ((Form_pg_attribute) GETSTRUCT(atup))->attnum,
+													  dpcontext, true);
 				append_jsonb_pair2(state,
 								   "fmt", jbv_str("ALTER COLUMN %{column}I SET DEFAULT %{definition}s"),
 								   "column", jbv_str(subcmd->name));
@@ -2455,7 +2475,7 @@
 				 */
 				AlterTableCmd *rawcmd = find_raw_alter_column(rawstmt,
 															  AT_AddIdentity,
-															  subcmd->name);
+															  subcmd->name, matched_cmds);
 				Constraint *coldef = castNode(Constraint, rawcmd->def);
 
 				begin_jsonb_object(state);
@@ -2478,7 +2498,7 @@
 				 */
 				AlterTableCmd *rawcmd = find_raw_alter_column(rawstmt,
 															  AT_SetIdentity,
-															  subcmd->name);
+															  subcmd->name, matched_cmds);
 				ListCell   *lc;
 
 				begin_jsonb_object(state);
@@ -3244,6 +3264,7 @@
 	List	   *dpcontext;
 	int			nsubcmds = 0;
 	ListCell   *cell;
+	List	   *matched_cmds = NIL;
 
 	Assert(IsA(rawstmt, AlterTableStmt));
 
@@ -3252,7 +3273,12 @@
 
 	begin_jsonb_object(state);
 	append_jsonb_pair1(state, "fmt",
-					   jbv_str("ALTER TABLE%{only}s %{identity}D %{subcmds:, }s"));
+					   jbv_str("ALTER TABLE%{if_exists}s%{only}s %{identity}D %{subcmds:, }s"));
+
+	if (rawstmt->missing_ok)
+		new_jsonb_clause(state, "if_exists", " IF EXISTS");
+	else
+		new_jsonb_null(state, "if_exists");
 
 	/* ONLY, from the raw statement */
 	if (!rawstmt->relation->inh)
@@ -3329,7 +3355,7 @@
 				continue;
 
 			if (deparse_AlterTableSubcmd(state, relId, dpcontext, sub, subcmd,
-										 rawstmt, cmds))
+										 rawstmt, cmds, &matched_cmds))
 				nsubcmds++;
 		}
 	}
@@ -3386,10 +3412,14 @@
 		case OBJECT_COLUMN:
 			begin_jsonb_object(state);
 			append_jsonb_pair3(state,
-							   "fmt", jbv_str("ALTER TABLE%{only}s %{identity}D "
+							   "fmt", jbv_str("ALTER TABLE%{if_exists}s%{only}s %{identity}D "
 											  "RENAME COLUMN %{oldname}I TO %{newname}I"),
 							   "oldname", jbv_str(node->subname),
 							   "newname", jbv_str(node->newname));
+			if (node->missing_ok)
+				new_jsonb_clause(state, "if_exists", " IF EXISTS");
+			else
+				new_jsonb_null(state, "if_exists");
 			if (!node->relation->inh)
 				new_jsonb_clause(state, "only", " ONLY");
 			else
@@ -3413,10 +3443,14 @@
 
 				begin_jsonb_object(state);
 				append_jsonb_pair3(state,
-								   "fmt", jbv_str("ALTER TABLE%{only}s %{identity}D "
+								   "fmt", jbv_str("ALTER TABLE%{if_exists}s%{only}s %{identity}D "
 												  "RENAME CONSTRAINT %{oldname}I TO %{newname}I"),
 								   "oldname", jbv_str(node->subname),
 								   "newname", jbv_str(node->newname));
+				if (node->missing_ok)
+					new_jsonb_clause(state, "if_exists", " IF EXISTS");
+				else
+					new_jsonb_null(state, "if_exists");
 				if (!node->relation->inh)
 					new_jsonb_clause(state, "only", " ONLY");
 				else
@@ -3451,8 +3485,12 @@
 
 	begin_jsonb_object(state);
 	append_jsonb_pair2(state,
-					   "fmt", jbv_str("ALTER TABLE %{identity}D SET SCHEMA %{newschema}I"),
+					   "fmt", jbv_str("ALTER TABLE%{if_exists}s %{identity}D SET SCHEMA %{newschema}I"),
 					   "newschema", jbv_str(node->newschema));
+	if (node->missing_ok)
+		new_jsonb_clause(state, "if_exists", " IF EXISTS");
+	else
+		new_jsonb_null(state, "if_exists");
 	new_jsonb_for_qualname(state, old_schema, get_rel_name(objectId),
 						   "identity", true);
 	end_jsonb_object(state);
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1915,6 +1915,55 @@
 	newsub->address = address;
 	newsub->parsetree = copyObject(subcmd);
 	newsub->using_text = NULL;
+	newsub->default_expr_captured = false;
+	newsub->default_expr = NULL;
+
+	/*
+	 * Retain this subcommand's default before the next one can replace it.
+	 * Collection precedes ATExecCmd's CommandCounterIncrement, so use
+	 * SnapshotSelf to see catalog changes made by the current command. Do not
+	 * read a domain's fallback default when an explicit NULL left no
+	 * pg_attrdef row: a captured NULL represents DEFAULT NULL.
+	 */
+	if (address.classId == RelationRelationId &&
+		OidIsValid(address.objectId) && address.objectSubId > 0 &&
+		((((const AlterTableCmd *) subcmd)->subtype == AT_ColumnDefault &&
+		  ((const AlterTableCmd *) subcmd)->def != NULL) ||
+		 (((const AlterTableCmd *) subcmd)->subtype == AT_AddColumn &&
+		  castNode(ColumnDef, ((const AlterTableCmd *) subcmd)->def)->raw_default != NULL &&
+		  !castNode(ColumnDef, ((const AlterTableCmd *) subcmd)->def)->generated)))
+	{
+		Relation	attrdefrel;
+		ScanKeyData keys[2];
+		SysScanDesc scan;
+		HeapTuple	tuple;
+
+		attrdefrel = table_open(AttrDefaultRelationId, AccessShareLock);
+		ScanKeyInit(&keys[0], Anum_pg_attrdef_adrelid,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(address.objectId));
+		ScanKeyInit(&keys[1], Anum_pg_attrdef_adnum,
+					BTEqualStrategyNumber, F_INT2EQ,
+					Int16GetDatum(address.objectSubId));
+		scan = systable_beginscan(attrdefrel, AttrDefaultIndexId, true,
+								  SnapshotSelf, 2, keys);
+		tuple = systable_getnext(scan);
+		if (HeapTupleIsValid(tuple))
+		{
+			Datum		adbin;
+			bool		isnull;
+
+			adbin = heap_getattr(tuple, Anum_pg_attrdef_adbin,
+								 RelationGetDescr(attrdefrel), &isnull);
+			if (isnull)
+				elog(ERROR, "null adbin for attribute %d of relation %u",
+					 address.objectSubId, address.objectId);
+			newsub->default_expr = stringToNode(TextDatumGetCString(adbin));
+		}
+		newsub->default_expr_captured = true;
+		systable_endscan(scan);
+		table_close(attrdefrel, AccessShareLock);
+	}
 
 	/*
 	 * For ALTER COLUMN TYPE, attach the USING text rendered at prep time (if
diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h
--- a/src/include/tcop/deparse_utility.h
+++ b/src/include/tcop/deparse_utility.h
@@ -46,6 +46,14 @@
 	 * subcommand), or NULL.  See EventTriggerCollectAlterColumnTypeUsing().
 	 */
 	char	   *using_text;
+
+	/*
+	 * ADD COLUMN and SET DEFAULT must retain the expression in effect for
+	 * this subcommand, before a later subcommand replaces it. A captured NULL
+	 * expression represents an explicit DEFAULT NULL.
+	 */
+	bool		default_expr_captured;
+	Node	   *default_expr;
 } CollectedATSubcmd;
 
 typedef struct CollectedCommand
diff --git a/src/test/modules/test_ddl_deparse/expected/deparse_alter_table.out b/src/test/modules/test_ddl_deparse/expected/deparse_alter_table.out
--- a/src/test/modules/test_ddl_deparse/expected/deparse_alter_table.out
+++ b/src/test/modules/test_ddl_deparse/expected/deparse_alter_table.out
@@ -115,6 +115,89 @@
 NOTICE:  DDL test: type alter table, tag ALTER TABLE
 NOTICE:    subcommand: type DROP COLUMN (and recurse) desc column ........pg.dropped.2........ of table deparse_at.u2
 NOTICE:    subcommand: type ALTER COLUMN SET TYPE desc column a of table deparse_at.u2
+-- Repeated identity options must match successive raw subcommands, even when
+-- they have the same column name and subtype.
+CREATE TABLE deparse_at.id_repeat (a int GENERATED BY DEFAULT AS IDENTITY);
+NOTICE:  deparsed DDL: CREATE TABLE deparse_at.id_repeat (a pg_catalog.int4 GENERATED BY DEFAULT AS IDENTITY)
+NOTICE:  DDL test: type simple, tag CREATE SEQUENCE
+NOTICE:  DDL test: type simple, tag CREATE TABLE
+NOTICE:  DDL test: type simple, tag ALTER SEQUENCE
+ALTER TABLE deparse_at.id_repeat
+  ALTER COLUMN a SET GENERATED ALWAYS,
+  ALTER COLUMN a SET GENERATED BY DEFAULT;
+NOTICE:  deparsed DDL: ALTER TABLE deparse_at.id_repeat ALTER COLUMN a SET GENERATED ALWAYS, ALTER COLUMN a SET GENERATED BY DEFAULT
+NOTICE:  DDL test: type simple, tag ALTER SEQUENCE
+NOTICE:  DDL test: type alter table, tag ALTER TABLE
+NOTICE:    subcommand: type SET IDENTITY (and recurse) desc column a of table deparse_at.id_repeat
+NOTICE:  DDL test: type simple, tag ALTER SEQUENCE
+NOTICE:  DDL test: type alter table, tag ALTER TABLE
+NOTICE:    subcommand: type SET IDENTITY (and recurse) desc column a of table deparse_at.id_repeat
+SELECT attidentity FROM pg_attribute
+  WHERE attrelid = 'deparse_at.id_repeat'::regclass AND attname = 'a';
+ attidentity 
+-------------
+ d
+(1 row)
+
+ALTER TABLE deparse_at.id_repeat
+  ALTER COLUMN a SET INCREMENT BY 3,
+  ALTER COLUMN a SET INCREMENT BY 7;
+NOTICE:  deparsed DDL: ALTER TABLE deparse_at.id_repeat ALTER COLUMN a SET INCREMENT BY 3, ALTER COLUMN a SET INCREMENT BY 7
+NOTICE:  DDL test: type simple, tag ALTER SEQUENCE
+NOTICE:  DDL test: type alter table, tag ALTER TABLE
+NOTICE:    subcommand: type SET IDENTITY (and recurse) desc <NULL>
+NOTICE:  DDL test: type simple, tag ALTER SEQUENCE
+NOTICE:  DDL test: type alter table, tag ALTER TABLE
+NOTICE:    subcommand: type SET IDENTITY (and recurse) desc <NULL>
+SELECT seqincrement FROM pg_sequence
+  WHERE seqrelid = 'deparse_at.id_repeat_a_seq'::regclass;
+ seqincrement 
+--------------
+            7
+(1 row)
+
+-- Each default belongs to its own subcommand. Existing rows receive the ADD
+-- COLUMN default, while subsequent inserts receive the later SET DEFAULT.
+CREATE TABLE deparse_at.defaults (a int);
+NOTICE:  deparsed DDL: CREATE TABLE deparse_at.defaults (a pg_catalog.int4)
+NOTICE:  DDL test: type simple, tag CREATE TABLE
+INSERT INTO deparse_at.defaults VALUES (1);
+ALTER TABLE deparse_at.defaults
+  ADD COLUMN b int DEFAULT 1,
+  ALTER COLUMN b SET DEFAULT 2;
+NOTICE:  deparsed DDL: ALTER TABLE deparse_at.defaults ADD COLUMN b pg_catalog.int4 DEFAULT 1, ALTER COLUMN b SET DEFAULT 2
+NOTICE:  DDL test: type alter table, tag ALTER TABLE
+NOTICE:    subcommand: type ADD COLUMN (and recurse) desc column b of table deparse_at.defaults
+NOTICE:    subcommand: type ALTER COLUMN SET DEFAULT desc column b of table deparse_at.defaults
+INSERT INTO deparse_at.defaults (a) VALUES (2);
+SELECT * FROM deparse_at.defaults ORDER BY a;
+ a | b 
+---+---
+ 1 | 1
+ 2 | 2
+(2 rows)
+
+ALTER TABLE deparse_at.defaults
+  ALTER COLUMN b SET DEFAULT 3,
+  ALTER COLUMN b SET DEFAULT 4;
+NOTICE:  deparsed DDL: ALTER TABLE deparse_at.defaults ALTER COLUMN b SET DEFAULT 3, ALTER COLUMN b SET DEFAULT 4
+NOTICE:  DDL test: type alter table, tag ALTER TABLE
+NOTICE:    subcommand: type ALTER COLUMN SET DEFAULT desc column b of table deparse_at.defaults
+NOTICE:    subcommand: type ALTER COLUMN SET DEFAULT desc column b of table deparse_at.defaults
+ALTER TABLE deparse_at.defaults
+  ALTER COLUMN b SET DEFAULT NULL,
+  ALTER COLUMN b SET DEFAULT 5;
+NOTICE:  deparsed DDL: ALTER TABLE deparse_at.defaults ALTER COLUMN b SET DEFAULT NULL, ALTER COLUMN b SET DEFAULT 5
+NOTICE:  DDL test: type alter table, tag ALTER TABLE
+NOTICE:    subcommand: type ALTER COLUMN SET DEFAULT desc column b of table deparse_at.defaults
+NOTICE:    subcommand: type ALTER COLUMN SET DEFAULT desc column b of table deparse_at.defaults
+ALTER TABLE deparse_at.defaults
+  ALTER COLUMN b SET DEFAULT 6,
+  ALTER COLUMN b DROP DEFAULT;
+NOTICE:  deparsed DDL: ALTER TABLE deparse_at.defaults ALTER COLUMN b DROP DEFAULT, ALTER COLUMN b SET DEFAULT 6
+NOTICE:  DDL test: type alter table, tag ALTER TABLE
+NOTICE:    subcommand: type ALTER COLUMN SET DEFAULT desc column b of table deparse_at.defaults
+NOTICE:    subcommand: type ALTER COLUMN SET DEFAULT desc column b of table deparse_at.defaults
 -- SET STATISTICS by column number, for an unnamed expression column of an
 -- index reached via ALTER TABLE naming that index.  The subcommand carries
 -- the column position instead of a name.  The core tests reach such a column
@@ -193,14 +276,57 @@
 NOTICE:  deparsed DDL: ALTER TABLE deparse_at.tr ENABLE ALWAYS RULE trg_rule
 NOTICE:  DDL test: type alter table, tag ALTER TABLE
 NOTICE:    subcommand: type ENABLE RULE (always) desc <NULL>
+-- Table-level IF EXISTS must survive reconstruction in each supported ALTER
+-- form. ONLY follows IF EXISTS where both clauses are allowed.
+CREATE TABLE deparse_at.guarded (a int CONSTRAINT guarded_check CHECK (a > 0));
+NOTICE:  deparsed DDL: CREATE TABLE deparse_at.guarded (a pg_catalog.int4, CONSTRAINT guarded_check CHECK ((a > 0)))
+NOTICE:  DDL test: type simple, tag CREATE TABLE
+ALTER TABLE IF EXISTS ONLY deparse_at.guarded ADD COLUMN b int;
+NOTICE:  deparsed DDL: ALTER TABLE IF EXISTS ONLY deparse_at.guarded ADD COLUMN b pg_catalog.int4
+NOTICE:  DDL test: type alter table, tag ALTER TABLE
+NOTICE:    subcommand: type ADD COLUMN desc column b of table deparse_at.guarded
+ALTER TABLE IF EXISTS ONLY deparse_at.guarded RENAME COLUMN b TO c;
+NOTICE:  deparsed DDL: ALTER TABLE IF EXISTS ONLY deparse_at.guarded RENAME COLUMN b TO c
+NOTICE:  DDL test: type simple, tag ALTER TABLE
+ALTER TABLE IF EXISTS ONLY deparse_at.guarded
+  RENAME CONSTRAINT guarded_check TO renamed_check;
+NOTICE:  deparsed DDL: ALTER TABLE IF EXISTS ONLY deparse_at.guarded RENAME CONSTRAINT guarded_check TO renamed_check
+NOTICE:  DDL test: type simple, tag ALTER TABLE
+ALTER TABLE IF EXISTS deparse_at.guarded RENAME TO guarded_renamed;
+NOTICE:  deparsed DDL: ALTER TABLE IF EXISTS deparse_at.guarded RENAME TO guarded_renamed
+NOTICE:  DDL test: type simple, tag ALTER TABLE
+CREATE SCHEMA deparse_guard;
+NOTICE:  DDL test: type simple, tag CREATE SCHEMA
+ALTER TABLE IF EXISTS deparse_at.guarded_renamed SET SCHEMA deparse_guard;
+NOTICE:  deparsed DDL: ALTER TABLE IF EXISTS deparse_at.guarded_renamed SET SCHEMA deparse_guard
+NOTICE:  DDL test: type simple, tag ALTER TABLE
+SELECT column_name FROM information_schema.columns
+  WHERE table_schema = 'deparse_guard' AND table_name = 'guarded_renamed'
+  ORDER BY ordinal_position;
+ column_name 
+-------------
+ a
+ c
+(2 rows)
+
+DROP TABLE deparse_guard.guarded_renamed;
+DROP SCHEMA deparse_guard;
 -- IF EXISTS on a missing table is a no-op: nothing is collected, so there is
 -- nothing to deparse and no warning.
 ALTER TABLE IF EXISTS deparse_at.nonesuch ADD COLUMN z int;
 NOTICE:  relation "nonesuch" does not exist, skipping
+ALTER TABLE IF EXISTS deparse_at.nonesuch RENAME COLUMN a TO b;
+NOTICE:  relation "nonesuch" does not exist, skipping
+ALTER TABLE IF EXISTS deparse_at.nonesuch RENAME CONSTRAINT c TO d;
+NOTICE:  relation "nonesuch" does not exist, skipping
+ALTER TABLE IF EXISTS deparse_at.nonesuch RENAME TO other;
+NOTICE:  relation "nonesuch" does not exist, skipping
+ALTER TABLE IF EXISTS deparse_at.nonesuch SET SCHEMA public;
+NOTICE:  relation "nonesuch" does not exist, skipping
 RESET test_ddl_deparse.print_deparsed_ddl;
 RESET test_ddl_deparse.execute_deparsed_ddl;
 DROP SCHEMA deparse_at CASCADE;
-NOTICE:  drop cascades to 13 other objects
+NOTICE:  drop cascades to 15 other objects
 DETAIL:  drop cascades to table deparse_at.t
 drop cascades to table deparse_at.con
 drop cascades to table deparse_at.ref
@@ -209,6 +335,8 @@
 drop cascades to table deparse_at.nnmc
 drop cascades to table deparse_at.u
 drop cascades to table deparse_at.u2
+drop cascades to table deparse_at.id_repeat
+drop cascades to table deparse_at.defaults
 drop cascades to table deparse_at.ei
 drop cascades to table deparse_at.ui3
 drop cascades to table deparse_at.cl
diff --git a/src/test/modules/test_ddl_deparse/sql/deparse_alter_table.sql b/src/test/modules/test_ddl_deparse/sql/deparse_alter_table.sql
--- a/src/test/modules/test_ddl_deparse/sql/deparse_alter_table.sql
+++ b/src/test/modules/test_ddl_deparse/sql/deparse_alter_table.sql
@@ -62,6 +62,39 @@
 INSERT INTO deparse_at.u2 VALUES (1, 42);
 ALTER TABLE deparse_at.u2 DROP COLUMN b, ALTER COLUMN a TYPE text USING b::text;
 
+-- Repeated identity options must match successive raw subcommands, even when
+-- they have the same column name and subtype.
+CREATE TABLE deparse_at.id_repeat (a int GENERATED BY DEFAULT AS IDENTITY);
+ALTER TABLE deparse_at.id_repeat
+  ALTER COLUMN a SET GENERATED ALWAYS,
+  ALTER COLUMN a SET GENERATED BY DEFAULT;
+SELECT attidentity FROM pg_attribute
+  WHERE attrelid = 'deparse_at.id_repeat'::regclass AND attname = 'a';
+ALTER TABLE deparse_at.id_repeat
+  ALTER COLUMN a SET INCREMENT BY 3,
+  ALTER COLUMN a SET INCREMENT BY 7;
+SELECT seqincrement FROM pg_sequence
+  WHERE seqrelid = 'deparse_at.id_repeat_a_seq'::regclass;
+
+-- Each default belongs to its own subcommand. Existing rows receive the ADD
+-- COLUMN default, while subsequent inserts receive the later SET DEFAULT.
+CREATE TABLE deparse_at.defaults (a int);
+INSERT INTO deparse_at.defaults VALUES (1);
+ALTER TABLE deparse_at.defaults
+  ADD COLUMN b int DEFAULT 1,
+  ALTER COLUMN b SET DEFAULT 2;
+INSERT INTO deparse_at.defaults (a) VALUES (2);
+SELECT * FROM deparse_at.defaults ORDER BY a;
+ALTER TABLE deparse_at.defaults
+  ALTER COLUMN b SET DEFAULT 3,
+  ALTER COLUMN b SET DEFAULT 4;
+ALTER TABLE deparse_at.defaults
+  ALTER COLUMN b SET DEFAULT NULL,
+  ALTER COLUMN b SET DEFAULT 5;
+ALTER TABLE deparse_at.defaults
+  ALTER COLUMN b SET DEFAULT 6,
+  ALTER COLUMN b DROP DEFAULT;
+
 -- SET STATISTICS by column number, for an unnamed expression column of an
 -- index reached via ALTER TABLE naming that index.  The subcommand carries
 -- the column position instead of a name.  The core tests reach such a column
@@ -99,9 +132,29 @@
 ALTER TABLE deparse_at.tr ENABLE TRIGGER USER;
 ALTER TABLE deparse_at.tr ENABLE ALWAYS RULE trg_rule;
 
+-- Table-level IF EXISTS must survive reconstruction in each supported ALTER
+-- form. ONLY follows IF EXISTS where both clauses are allowed.
+CREATE TABLE deparse_at.guarded (a int CONSTRAINT guarded_check CHECK (a > 0));
+ALTER TABLE IF EXISTS ONLY deparse_at.guarded ADD COLUMN b int;
+ALTER TABLE IF EXISTS ONLY deparse_at.guarded RENAME COLUMN b TO c;
+ALTER TABLE IF EXISTS ONLY deparse_at.guarded
+  RENAME CONSTRAINT guarded_check TO renamed_check;
+ALTER TABLE IF EXISTS deparse_at.guarded RENAME TO guarded_renamed;
+CREATE SCHEMA deparse_guard;
+ALTER TABLE IF EXISTS deparse_at.guarded_renamed SET SCHEMA deparse_guard;
+SELECT column_name FROM information_schema.columns
+  WHERE table_schema = 'deparse_guard' AND table_name = 'guarded_renamed'
+  ORDER BY ordinal_position;
+DROP TABLE deparse_guard.guarded_renamed;
+DROP SCHEMA deparse_guard;
+
 -- IF EXISTS on a missing table is a no-op: nothing is collected, so there is
 -- nothing to deparse and no warning.
 ALTER TABLE IF EXISTS deparse_at.nonesuch ADD COLUMN z int;
+ALTER TABLE IF EXISTS deparse_at.nonesuch RENAME COLUMN a TO b;
+ALTER TABLE IF EXISTS deparse_at.nonesuch RENAME CONSTRAINT c TO d;
+ALTER TABLE IF EXISTS deparse_at.nonesuch RENAME TO other;
+ALTER TABLE IF EXISTS deparse_at.nonesuch SET SCHEMA public;
 
 RESET test_ddl_deparse.print_deparsed_ddl;
 RESET test_ddl_deparse.execute_deparsed_ddl;
