--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1739,6 +1739,18 @@
 {
 	return currentEventTriggerState != NULL &&
 		!currentEventTriggerState->commandCollectionInhibited;
+}
+
+/*
+ * Is the enclosing query explicitly suppressing collection of internal DDL?
+ * Unlike absence of a query state, inhibition must also apply to commands
+ * nested through SPI, before they create their own complete-query state.
+ */
+bool
+EventTriggerCommandCollectionInhibited(void)
+{
+	return currentEventTriggerState != NULL &&
+		currentEventTriggerState->commandCollectionInhibited;
 }
 
 /*
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -73,6 +73,7 @@
 extern void EventTriggerInhibitCommandCollection(void);
 extern void EventTriggerUndoInhibitCommandCollection(void);
 extern bool EventTriggerCommandCollectionActive(void);
+extern bool EventTriggerCommandCollectionInhibited(void);
 extern List *EventTriggerGetCollectedCommands(void);
 
 extern void EventTriggerCollectSimpleCommand(ObjectAddress address,
--- a/src/test/modules/test_ddl_deparse/Makefile
+++ b/src/test/modules/test_ddl_deparse/Makefile
@@ -33,7 +33,8 @@
 	textsearch \
 	matviews \
 	deparse_create_table \
-	deparse_alter_table
+	deparse_alter_table \
+	deparse_nested
 
 TAP_TESTS = 1
 
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -81,12 +81,13 @@
  * so it survives the rollback of the internal subtransaction the trial run
  * executes in.  Only valid while capture_parsetree is set.
  *
- * A single pointer, not a list: one intercepted statement is one complete
- * query, which fires the capture event trigger exactly once (sub-commands
- * such as the internal ALTER TABLE ADD CONSTRAINT generated by a CREATE
- * TABLE with a foreign key are collected into that one firing, not fired
+ * A single pointer for the current capture frame: one intercepted statement
+ * is one complete query, which fires the capture event trigger exactly once
+ * (sub-commands such as the internal ALTER TABLE ADD CONSTRAINT generated by
+ * a CREATE TABLE with a foreign key are collected into that one firing, not fired
  * separately), and one firing deparses to exactly one command.  The trigger
- * errors out if that invariant is ever broken.
+ * errors out if that invariant is ever broken. Nested complete queries save
+ * and restore both capture pointers in the ProcessUtility hook.
  */
 static char *captured_json = NULL;
 
@@ -135,7 +136,6 @@
 							 PGC_USERSET,
 							 0,
 							 NULL, NULL, NULL);
-
 	MarkGUCPrefixReserved("test_ddl_deparse");
 
 	next_ProcessUtility_hook = ProcessUtility_hook;
@@ -172,18 +172,16 @@
 	/*
 	 * Do nothing unless a statement of ours is being captured.  This is the
 	 * common case: the trigger is installed for the whole session, but only
-	 * top-level statements the module intercepts have a raw parse tree saved
-	 * for the deparser to work from.
+	 * statements the module intercepts have a raw parse tree saved for the
+	 * deparser to work from.
 	 */
 	if (capture_parsetree == NULL)
 		PG_RETURN_NULL();
 
 	/*
-	 * A second firing while one statement is being captured means a nested
-	 * complete query (say, DDL run by another event trigger function) also
-	 * produced a supported command.  Its raw statement is not the one saved
-	 * above, and replaying both would double-execute the nested one when the
-	 * replayed outer statement triggers it again, so refuse loudly.
+	 * A frame must capture its complete query only once. Nested user commands
+	 * have their own frame, so a second capture here is an instrumentation
+	 * error rather than another command to append to this frame.
 	 */
 	if (captured_json != NULL)
 		elog(ERROR, "multiple DDL deparse captures for a single statement");
@@ -364,12 +362,17 @@
 								 QueryEnvironment *queryEnv,
 								 DestReceiver *dest, QueryCompletion *qc)
 {
+	const Node *previous_capture_parsetree = capture_parsetree;
+	char	   *previous_captured_json = captured_json;
+
 	/*
-	 * Only top-level statements are captured, and only when something is
-	 * going to be done with the result.  Our own replay of a deparsed command
-	 * must not be captured in turn.
+	 * Capture user commands at any nesting depth under the same settings.
+	 * Internal subcommands remain part of their enclosing command. Our own
+	 * replay must not be captured in turn.
 	 */
-	if (context != PROCESS_UTILITY_TOPLEVEL || in_roundtrip ||
+	if (context == PROCESS_UTILITY_SUBCOMMAND ||
+		EventTriggerCommandCollectionInhibited() ||
+		in_roundtrip ||
 		(!execute_deparsed_ddl && print_deparsed_ddl == PRINT_DEPARSED_OFF))
 	{
 		call_next_ProcessUtility(pstmt, queryString, readOnlyTree,
@@ -387,8 +390,20 @@
 	if (!ddl_deparse_command_supported(pstmt->utilityStmt))
 	{
 		report_declined_command(pstmt->utilityStmt, queryString);
-		call_next_ProcessUtility(pstmt, queryString, readOnlyTree,
-								 context, params, queryEnv, dest, qc);
+		/* Its complete-query event must not use the enclosing raw tree. */
+		capture_parsetree = NULL;
+		captured_json = NULL;
+		PG_TRY();
+		{
+			call_next_ProcessUtility(pstmt, queryString, readOnlyTree,
+									 context, params, queryEnv, dest, qc);
+		}
+		PG_FINALLY();
+		{
+			capture_parsetree = previous_capture_parsetree;
+			captured_json = previous_captured_json;
+		}
+		PG_END_TRY();
 		return;
 	}
 
@@ -497,9 +512,8 @@
 	}
 	PG_FINALLY();
 	{
-		capture_parsetree = NULL;
-		/* don't leave a dangling pointer into transaction-lifetime memory */
-		captured_json = NULL;
+		capture_parsetree = previous_capture_parsetree;
+		captured_json = previous_captured_json;
 	}
 	PG_END_TRY();
 }
--- a/src/test/modules/test_ddl_deparse/meson.build
+++ b/src/test/modules/test_ddl_deparse/meson.build
@@ -51,6 +51,7 @@
       'matviews',
       'deparse_create_table',
       'deparse_alter_table',
+      'deparse_nested',
     ],
   },
   'tap': {
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse/sql/deparse_nested.sql
@@ -0,0 +1,148 @@
+-- Nested user DDL must be captured with its own raw statement. Expected
+-- notices check capture counts and SQL text; queries check data and rollback.
+-- Internal commands remain part of their enclosing user statement.
+LOAD 'test_ddl_deparse';
+SET test_ddl_deparse.execute_deparsed_ddl = off;
+
+-- Silence the generic command inspector installed by the first regression
+-- script, so the expected output focuses on the deparser's own notices.
+ALTER EVENT TRIGGER test_ddl_deparse DISABLE;
+
+CREATE SCHEMA nested_tests;
+CREATE FUNCTION nested_tests.sql_ddl() RETURNS integer LANGUAGE SQL AS $$
+ CREATE TABLE nested_tests.from_sql (a int);
+ SELECT 10;
+$$;
+CREATE FUNCTION nested_tests.plpgsql_ddl() RETURNS void LANGUAGE plpgsql AS $$
+BEGIN
+ CREATE TABLE nested_tests.from_plpgsql (a int, b int);
+ INSERT INTO nested_tests.from_plpgsql VALUES (1, 42);
+ ALTER TABLE nested_tests.from_plpgsql
+  ALTER COLUMN a TYPE text USING b::text, DROP COLUMN b;
+END;
+$$;
+CREATE FUNCTION nested_tests.caught_error() RETURNS void LANGUAGE plpgsql AS $$
+BEGIN
+ BEGIN
+  CREATE TABLE nested_tests.rolled_back (a int);
+  ALTER TABLE nested_tests.rolled_back ADD COLUMN b nonexistent_type;
+ EXCEPTION WHEN undefined_object THEN
+  NULL;
+ END;
+ CREATE TABLE nested_tests.after_error (a int);
+END;
+$$;
+CREATE FUNCTION nested_tests.roundtrip_ddl() RETURNS void LANGUAGE plpgsql AS $$
+BEGIN
+ CREATE TABLE nested_tests.from_roundtrip (a int);
+ INSERT INTO nested_tests.from_roundtrip VALUES (1);
+ ALTER TABLE nested_tests.from_roundtrip
+  ADD COLUMN b int DEFAULT 1, ALTER COLUMN b SET DEFAULT 2;
+ INSERT INTO nested_tests.from_roundtrip (a) VALUES (2);
+END;
+$$;
+CREATE FUNCTION nested_tests.nested_trigger() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+DECLARE
+ cmd record;
+BEGIN
+ FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() LOOP
+  IF cmd.object_identity = 'nested_tests.parent' THEN
+   CREATE TABLE nested_tests.child (a int, b int);
+   INSERT INTO nested_tests.child VALUES (1, 42);
+   ALTER TABLE nested_tests.child
+    ALTER COLUMN a TYPE text USING b::text, DROP COLUMN b;
+   -- CTAS is unsupported, but must not borrow the parent's capture state.
+   CREATE TABLE nested_tests.child_ctas AS SELECT 7 AS a;
+   BEGIN
+    CREATE TABLE nested_tests.child_rolled_back (a int);
+    ALTER TABLE nested_tests.child_rolled_back ADD COLUMN b nonexistent_type;
+   EXCEPTION WHEN undefined_object THEN
+    NULL;
+   END;
+   CREATE TABLE nested_tests.child_after_error (a int);
+  END IF;
+ END LOOP;
+END;
+$$;
+
+SET test_ddl_deparse.print_deparsed_ddl = text;
+
+-- SQL and PL/pgSQL functions issue supported CREATE and ALTER commands.
+SELECT nested_tests.sql_ddl();
+SELECT nested_tests.plpgsql_ddl();
+SELECT a FROM nested_tests.from_plpgsql;
+
+-- A caught error must restore capture state before the next nested command
+-- and before the next top-level command. The failed block leaves no table.
+SELECT nested_tests.caught_error();
+CREATE TABLE nested_tests.after_call (a int);
+SELECT to_regclass('nested_tests.rolled_back') IS NULL AS rolled_back;
+
+-- Explicit transaction rollback must likewise leave capture usable.
+BEGIN;
+CREATE TABLE nested_tests.explicit_rollback (a int);
+ROLLBACK;
+CREATE TABLE nested_tests.after_rollback (a int);
+SELECT to_regclass('nested_tests.explicit_rollback') IS NULL AS rolled_back;
+
+-- The child needs a separate capture frame whether its event trigger runs
+-- before or after capture_deparsed_ddl. Unsupported CTAS must not borrow the
+-- parent's frame, and a caught child error must not discard the parent JSON.
+
+CREATE EVENT TRIGGER aa_nested ON ddl_command_end
+  WHEN TAG IN ('CREATE TABLE')
+  EXECUTE FUNCTION nested_tests.nested_trigger();
+CREATE TABLE nested_tests.parent (a int);
+SELECT a FROM nested_tests.child;
+SELECT a FROM nested_tests.child_ctas;
+SELECT to_regclass('nested_tests.child_rolled_back') IS NULL AS rolled_back;
+DROP EVENT TRIGGER aa_nested;
+DROP TABLE nested_tests.parent, nested_tests.child,
+  nested_tests.child_ctas, nested_tests.child_after_error;
+
+CREATE EVENT TRIGGER zz_nested ON ddl_command_end
+  WHEN TAG IN ('CREATE TABLE')
+  EXECUTE FUNCTION nested_tests.nested_trigger();
+CREATE TABLE nested_tests.parent (a int);
+SELECT a FROM nested_tests.child;
+SELECT a FROM nested_tests.child_ctas;
+SELECT to_regclass('nested_tests.child_rolled_back') IS NULL AS rolled_back;
+DROP EVENT TRIGGER zz_nested;
+DROP TABLE nested_tests.parent, nested_tests.child,
+  nested_tests.child_ctas, nested_tests.child_after_error;
+
+-- Also reconstruct the enclosing command and its nested trial commands.
+SET test_ddl_deparse.execute_deparsed_ddl = on;
+
+CREATE EVENT TRIGGER zz_nested_roundtrip ON ddl_command_end
+  WHEN TAG IN ('CREATE TABLE')
+  EXECUTE FUNCTION nested_tests.nested_trigger();
+CREATE TABLE nested_tests.parent (a int);
+SELECT a FROM nested_tests.child;
+SELECT a FROM nested_tests.child_ctas;
+SELECT to_regclass('nested_tests.child_rolled_back') IS NULL AS rolled_back;
+DROP EVENT TRIGGER zz_nested_roundtrip;
+DROP TABLE nested_tests.parent, nested_tests.child,
+  nested_tests.child_ctas, nested_tests.child_after_error;
+
+-- Reconstruct function-issued DDL and verify that ADD COLUMN's default and
+-- the later SET DEFAULT retain their distinct effects on row values.
+SELECT nested_tests.roundtrip_ddl();
+SELECT a, b FROM nested_tests.from_roundtrip ORDER BY a;
+
+-- Only the user's CREATE is deparsed: internally generated sequence/index
+-- commands and the replay itself must not produce additional deparse notices.
+CREATE TABLE nested_tests.serial_table (a serial PRIMARY KEY);
+
+-- Concurrent refresh creates and alters temporary tables through SPI while
+-- command collection is inhibited. Those are internal commands too.
+CREATE MATERIALIZED VIEW nested_tests.mv AS SELECT 1 AS a;
+CREATE UNIQUE INDEX mv_a_idx ON nested_tests.mv (a);
+REFRESH MATERIALIZED VIEW CONCURRENTLY nested_tests.mv;
+SELECT * FROM nested_tests.mv;
+
+RESET test_ddl_deparse.print_deparsed_ddl;
+RESET test_ddl_deparse.execute_deparsed_ddl;
+DROP SCHEMA nested_tests CASCADE;
+ALTER EVENT TRIGGER test_ddl_deparse ENABLE;
--- /dev/null
+++ b/src/test/modules/test_ddl_deparse/expected/deparse_nested.out
@@ -0,0 +1,264 @@
+-- Nested user DDL must be captured with its own raw statement. Expected
+-- notices check capture counts and SQL text; queries check data and rollback.
+-- Internal commands remain part of their enclosing user statement.
+LOAD 'test_ddl_deparse';
+SET test_ddl_deparse.execute_deparsed_ddl = off;
+-- Silence the generic command inspector installed by the first regression
+-- script, so the expected output focuses on the deparser's own notices.
+ALTER EVENT TRIGGER test_ddl_deparse DISABLE;
+CREATE SCHEMA nested_tests;
+CREATE FUNCTION nested_tests.sql_ddl() RETURNS integer LANGUAGE SQL AS $$
+ CREATE TABLE nested_tests.from_sql (a int);
+ SELECT 10;
+$$;
+CREATE FUNCTION nested_tests.plpgsql_ddl() RETURNS void LANGUAGE plpgsql AS $$
+BEGIN
+ CREATE TABLE nested_tests.from_plpgsql (a int, b int);
+ INSERT INTO nested_tests.from_plpgsql VALUES (1, 42);
+ ALTER TABLE nested_tests.from_plpgsql
+  ALTER COLUMN a TYPE text USING b::text, DROP COLUMN b;
+END;
+$$;
+CREATE FUNCTION nested_tests.caught_error() RETURNS void LANGUAGE plpgsql AS $$
+BEGIN
+ BEGIN
+  CREATE TABLE nested_tests.rolled_back (a int);
+  ALTER TABLE nested_tests.rolled_back ADD COLUMN b nonexistent_type;
+ EXCEPTION WHEN undefined_object THEN
+  NULL;
+ END;
+ CREATE TABLE nested_tests.after_error (a int);
+END;
+$$;
+CREATE FUNCTION nested_tests.roundtrip_ddl() RETURNS void LANGUAGE plpgsql AS $$
+BEGIN
+ CREATE TABLE nested_tests.from_roundtrip (a int);
+ INSERT INTO nested_tests.from_roundtrip VALUES (1);
+ ALTER TABLE nested_tests.from_roundtrip
+  ADD COLUMN b int DEFAULT 1, ALTER COLUMN b SET DEFAULT 2;
+ INSERT INTO nested_tests.from_roundtrip (a) VALUES (2);
+END;
+$$;
+CREATE FUNCTION nested_tests.nested_trigger() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+DECLARE
+ cmd record;
+BEGIN
+ FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() LOOP
+  IF cmd.object_identity = 'nested_tests.parent' THEN
+   CREATE TABLE nested_tests.child (a int, b int);
+   INSERT INTO nested_tests.child VALUES (1, 42);
+   ALTER TABLE nested_tests.child
+    ALTER COLUMN a TYPE text USING b::text, DROP COLUMN b;
+   -- CTAS is unsupported, but must not borrow the parent's capture state.
+   CREATE TABLE nested_tests.child_ctas AS SELECT 7 AS a;
+   BEGIN
+    CREATE TABLE nested_tests.child_rolled_back (a int);
+    ALTER TABLE nested_tests.child_rolled_back ADD COLUMN b nonexistent_type;
+   EXCEPTION WHEN undefined_object THEN
+    NULL;
+   END;
+   CREATE TABLE nested_tests.child_after_error (a int);
+  END IF;
+ END LOOP;
+END;
+$$;
+SET test_ddl_deparse.print_deparsed_ddl = text;
+-- SQL and PL/pgSQL functions issue supported CREATE and ALTER commands.
+SELECT nested_tests.sql_ddl();
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.from_sql (a pg_catalog.int4)
+ sql_ddl 
+---------
+      10
+(1 row)
+
+SELECT nested_tests.plpgsql_ddl();
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.from_plpgsql (a pg_catalog.int4, b pg_catalog.int4)
+NOTICE:  deparsed DDL: ALTER TABLE nested_tests.from_plpgsql DROP COLUMN b, ALTER COLUMN a SET DATA TYPE pg_catalog.text USING (b)::text
+ plpgsql_ddl 
+-------------
+ 
+(1 row)
+
+SELECT a FROM nested_tests.from_plpgsql;
+ a  
+----
+ 42
+(1 row)
+
+-- A caught error must restore capture state before the next nested command
+-- and before the next top-level command. The failed block leaves no table.
+SELECT nested_tests.caught_error();
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.rolled_back (a pg_catalog.int4)
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.after_error (a pg_catalog.int4)
+ caught_error 
+--------------
+ 
+(1 row)
+
+CREATE TABLE nested_tests.after_call (a int);
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.after_call (a pg_catalog.int4)
+SELECT to_regclass('nested_tests.rolled_back') IS NULL AS rolled_back;
+ rolled_back 
+-------------
+ t
+(1 row)
+
+-- Explicit transaction rollback must likewise leave capture usable.
+BEGIN;
+CREATE TABLE nested_tests.explicit_rollback (a int);
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.explicit_rollback (a pg_catalog.int4)
+ROLLBACK;
+CREATE TABLE nested_tests.after_rollback (a int);
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.after_rollback (a pg_catalog.int4)
+SELECT to_regclass('nested_tests.explicit_rollback') IS NULL AS rolled_back;
+ rolled_back 
+-------------
+ t
+(1 row)
+
+-- The child needs a separate capture frame whether its event trigger runs
+-- before or after capture_deparsed_ddl. Unsupported CTAS must not borrow the
+-- parent's frame, and a caught child error must not discard the parent JSON.
+CREATE EVENT TRIGGER aa_nested ON ddl_command_end
+  WHEN TAG IN ('CREATE TABLE')
+  EXECUTE FUNCTION nested_tests.nested_trigger();
+CREATE TABLE nested_tests.parent (a int);
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.child (a pg_catalog.int4, b pg_catalog.int4)
+NOTICE:  deparsed DDL: ALTER TABLE nested_tests.child DROP COLUMN b, ALTER COLUMN a SET DATA TYPE pg_catalog.text USING (b)::text
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.child_rolled_back (a pg_catalog.int4)
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.child_after_error (a pg_catalog.int4)
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.parent (a pg_catalog.int4)
+SELECT a FROM nested_tests.child;
+ a  
+----
+ 42
+(1 row)
+
+SELECT a FROM nested_tests.child_ctas;
+ a 
+---
+ 7
+(1 row)
+
+SELECT to_regclass('nested_tests.child_rolled_back') IS NULL AS rolled_back;
+ rolled_back 
+-------------
+ t
+(1 row)
+
+DROP EVENT TRIGGER aa_nested;
+DROP TABLE nested_tests.parent, nested_tests.child,
+  nested_tests.child_ctas, nested_tests.child_after_error;
+CREATE EVENT TRIGGER zz_nested ON ddl_command_end
+  WHEN TAG IN ('CREATE TABLE')
+  EXECUTE FUNCTION nested_tests.nested_trigger();
+CREATE TABLE nested_tests.parent (a int);
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.parent (a pg_catalog.int4)
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.child (a pg_catalog.int4, b pg_catalog.int4)
+NOTICE:  deparsed DDL: ALTER TABLE nested_tests.child DROP COLUMN b, ALTER COLUMN a SET DATA TYPE pg_catalog.text USING (b)::text
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.child_rolled_back (a pg_catalog.int4)
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.child_after_error (a pg_catalog.int4)
+SELECT a FROM nested_tests.child;
+ a  
+----
+ 42
+(1 row)
+
+SELECT a FROM nested_tests.child_ctas;
+ a 
+---
+ 7
+(1 row)
+
+SELECT to_regclass('nested_tests.child_rolled_back') IS NULL AS rolled_back;
+ rolled_back 
+-------------
+ t
+(1 row)
+
+DROP EVENT TRIGGER zz_nested;
+DROP TABLE nested_tests.parent, nested_tests.child,
+  nested_tests.child_ctas, nested_tests.child_after_error;
+-- Also reconstruct the enclosing command and its nested trial commands.
+SET test_ddl_deparse.execute_deparsed_ddl = on;
+CREATE EVENT TRIGGER zz_nested_roundtrip ON ddl_command_end
+  WHEN TAG IN ('CREATE TABLE')
+  EXECUTE FUNCTION nested_tests.nested_trigger();
+CREATE TABLE nested_tests.parent (a int);
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.parent (a pg_catalog.int4)
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.child (a pg_catalog.int4, b pg_catalog.int4)
+NOTICE:  deparsed DDL: ALTER TABLE nested_tests.child DROP COLUMN b, ALTER COLUMN a SET DATA TYPE pg_catalog.text USING (b)::text
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.child_rolled_back (a pg_catalog.int4)
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.child_after_error (a pg_catalog.int4)
+SELECT a FROM nested_tests.child;
+ a  
+----
+ 42
+(1 row)
+
+SELECT a FROM nested_tests.child_ctas;
+ a 
+---
+ 7
+(1 row)
+
+SELECT to_regclass('nested_tests.child_rolled_back') IS NULL AS rolled_back;
+ rolled_back 
+-------------
+ t
+(1 row)
+
+DROP EVENT TRIGGER zz_nested_roundtrip;
+DROP TABLE nested_tests.parent, nested_tests.child,
+  nested_tests.child_ctas, nested_tests.child_after_error;
+-- Reconstruct function-issued DDL and verify that ADD COLUMN's default and
+-- the later SET DEFAULT retain their distinct effects on row values.
+SELECT nested_tests.roundtrip_ddl();
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.from_roundtrip (a pg_catalog.int4)
+NOTICE:  deparsed DDL: ALTER TABLE nested_tests.from_roundtrip ADD COLUMN b pg_catalog.int4 DEFAULT 1, ALTER COLUMN b SET DEFAULT 2
+ roundtrip_ddl 
+---------------
+ 
+(1 row)
+
+SELECT a, b FROM nested_tests.from_roundtrip ORDER BY a;
+ a | b 
+---+---
+ 1 | 1
+ 2 | 2
+(2 rows)
+
+-- Only the user's CREATE is deparsed: internally generated sequence/index
+-- commands and the replay itself must not produce additional deparse notices.
+CREATE TABLE nested_tests.serial_table (a serial PRIMARY KEY);
+NOTICE:  deparsed DDL: CREATE TABLE nested_tests.serial_table (a serial, PRIMARY KEY (a))
+-- Concurrent refresh creates and alters temporary tables through SPI while
+-- command collection is inhibited. Those are internal commands too.
+CREATE MATERIALIZED VIEW nested_tests.mv AS SELECT 1 AS a;
+CREATE UNIQUE INDEX mv_a_idx ON nested_tests.mv (a);
+REFRESH MATERIALIZED VIEW CONCURRENTLY nested_tests.mv;
+SELECT * FROM nested_tests.mv;
+ a 
+---
+ 1
+(1 row)
+
+RESET test_ddl_deparse.print_deparsed_ddl;
+RESET test_ddl_deparse.execute_deparsed_ddl;
+DROP SCHEMA nested_tests CASCADE;
+NOTICE:  drop cascades to 13 other objects
+DETAIL:  drop cascades to function nested_tests.sql_ddl()
+drop cascades to function nested_tests.plpgsql_ddl()
+drop cascades to function nested_tests.caught_error()
+drop cascades to function nested_tests.roundtrip_ddl()
+drop cascades to function nested_tests.nested_trigger()
+drop cascades to table nested_tests.from_sql
+drop cascades to table nested_tests.from_plpgsql
+drop cascades to table nested_tests.after_error
+drop cascades to table nested_tests.after_call
+drop cascades to table nested_tests.after_rollback
+drop cascades to table nested_tests.from_roundtrip
+drop cascades to table nested_tests.serial_table
+drop cascades to materialized view nested_tests.mv
+ALTER EVENT TRIGGER test_ddl_deparse ENABLE;
