From e49e9fc2f8e62a0826922889e4504a280ea76f23 Mon Sep 17 00:00:00 2001
From: Vaibhav Dalvi <vaibhav.dalvi@enterprisedb.com>
Date: Fri, 31 Oct 2025 10:15:13 +0000
Subject: [PATCH v1 1/1] Add pg_get_subscription_ddl() function

This new SQL-callable function returns the `CREATE SUBSCRIPTION`
statement for a given subscription name.

Like `pg_dump`, the returned DDL explicitly sets `connect = false`.
This is because the original `CONNECT` option value is not cataloged,
and using `connect = false` ensures the DDL can be successfully executed
even if the remote publisher is unreachable.

This function is restricted to users that have the "pg_read_all_data" and/or
"pg_create_subscription" privilege.  This is a security measure because
subscription connection strings often contain sensitive information, such as
passwords.
---
 doc/src/sgml/func/func-info.sgml              |  51 +++++
 src/backend/catalog/pg_subscription.c         |   4 +-
 src/backend/utils/adt/ruleutils.c             | 177 ++++++++++++++++++
 src/include/catalog/pg_proc.dat               |   3 +
 src/include/catalog/pg_subscription.h         |   2 +
 .../regress/expected/subscription_ddl.out     |  85 +++++++++
 src/test/regress/parallel_schedule            |   2 +-
 src/test/regress/sql/subscription_ddl.sql     |  55 ++++++
 8 files changed, 375 insertions(+), 4 deletions(-)
 create mode 100644 src/test/regress/expected/subscription_ddl.out
 create mode 100644 src/test/regress/sql/subscription_ddl.sql

diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml
index c393832d94c..d266c2ea4bf 100644
--- a/doc/src/sgml/func/func-info.sgml
+++ b/doc/src/sgml/func/func-info.sgml
@@ -3797,4 +3797,55 @@ acl      | {postgres=arwdDxtm/postgres,foo=r/postgres}
 
   </sect2>
 
+  <sect2 id="functions-get-object-ddl">
+   <title>Get Object DDL Functions</title>
+
+   <para>
+    The functions shown in <xref linkend="functions-get-object-ddl-table"/>
+    print the DDL statements for various database objects.
+    (This is a decompiled reconstruction, not the original text
+    of the command.)
+   </para>
+
+   <table id="functions-get-object-ddl-table">
+    <title>Get Object DDL Functions</title>
+    <tgroup cols="1">
+     <thead>
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        Function
+       </para>
+       <para>
+        Description
+       </para></entry>
+      </row>
+     </thead>
+
+     <tbody>
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_get_subscription_ddl</primary>
+        </indexterm>
+        <function>pg_get_subscription_ddl</function> ( <parameter>subscription</parameter> <type>text</type> )
+        <returnvalue>text</returnvalue>
+       </para>
+       <para>
+        Reconstructs the creating command for a subscription.
+        The result is a complete <command>CREATE SUBSCRIPTION</command>
+        statement. The <literal>connect</literal> option set to
+        <literal>false</literal>.
+       </para>
+       <para>
+        This function is restricted to users that have the
+        <literal>pg_read_all_data</literal> and/or
+        <literal>pg_create_subscription</literal> privilege.
+       </para></entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  </sect2>
+
   </sect1>
diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c
index 15b233a37d8..4a3e476cf63 100644
--- a/src/backend/catalog/pg_subscription.c
+++ b/src/backend/catalog/pg_subscription.c
@@ -32,8 +32,6 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
-static List *textarray_to_stringlist(ArrayType *textarray);
-
 /*
  * Add a comma-separated list of publication names to the 'dest' string.
  */
@@ -240,7 +238,7 @@ DisableSubscription(Oid subid)
  *
  * Note: the resulting list of strings is pallocated here.
  */
-static List *
+List *
 textarray_to_stringlist(ArrayType *textarray)
 {
 	Datum	   *elems;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 79ec136231b..87c0b90534a 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -35,6 +35,7 @@
 #include "catalog/pg_partitioned_table.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_statistic_ext.h"
+#include "catalog/pg_subscription.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
@@ -57,6 +58,7 @@
 #include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rewriteSupport.h"
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -13738,3 +13740,178 @@ get_range_partbound_string(List *bound_datums)
 
 	return buf->data;
 }
+
+/*
+ * pg_get_subscription_ddl
+ *		Get CREATE SUBSCRIPTION statement for the given subscription
+ */
+Datum
+pg_get_subscription_ddl(PG_FUNCTION_ARGS)
+{
+	char	   *subname = text_to_cstring(PG_GETARG_TEXT_P(0));
+	StringInfo	pubnames;
+	StringInfoData buf;
+	HeapTuple	tup;
+	char	   *conninfo;
+	List	   *publist;
+	Datum		datum;
+	bool		isnull;
+
+	/*
+	 * To prevent unprivileged users from initiating unauthorized network
+	 * connections, dumping subscription creation is restricted.  A user must
+	 * be specifically authorized (via the appropriate role privilege) to
+	 * create subscriptions and/or to read all data.
+	 */
+	if (!(has_privs_of_role(GetUserId(), ROLE_PG_CREATE_SUBSCRIPTION) ||
+		has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_DATA)))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied to get the create subscription ddl"),
+				 errdetail("Only roles with privileges of the \"%s\" and/or \"%s\" role may get ddl.",
+						   "pg_create_subscription", "pg_read_all_data")));
+
+	/* Look up the subscription in pg_subscription */
+	tup = SearchSysCache2(SUBSCRIPTIONNAME, ObjectIdGetDatum(MyDatabaseId),
+						  CStringGetDatum(subname));
+	if (!HeapTupleIsValid(tup))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("subscription \"%s\" does not exist", subname)));
+
+	initStringInfo(&buf);
+
+	/* Build the CREATE SUBSCRIPTION statement */
+	appendStringInfo(&buf, "CREATE SUBSCRIPTION %s ",
+					 quote_identifier(subname));
+
+	/* Get conninfo */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subconninfo);
+	conninfo = TextDatumGetCString(datum);
+
+	/* Append connection info to the CREATE SUBSCRIPTION statement */
+	appendStringInfo(&buf, "CONNECTION \'%s\'", conninfo);
+
+	/* Build list of quoted publications and append them to query */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subpublications);
+	publist = textarray_to_stringlist(DatumGetArrayTypeP(datum));
+	pubnames = makeStringInfo();
+	GetPublicationsStr(publist, pubnames, false);
+	appendStringInfo(&buf, " PUBLICATION %s", pubnames->data);
+
+	/*
+	 * Add options using WITH clause.  The 'connect' option value given at the
+	 * time of subscription creation is not available in the catalog.  When
+	 * creating a subscription, the remote host is not reachable or in an
+	 * unclear state, in that case, the subscription can be created using
+	 * 'connect = false' option.  This is what pg_dump uses.
+	 *
+	 * The status or value of the options 'create_slot' and 'copy_data' not
+	 * available in the catalog table.  We can use default values i.e. TRUE
+	 * for both.  This is what pg_dump uses.
+	 */
+	appendStringInfoString(&buf, " WITH (connect = false");
+
+	/* Get slotname */
+	datum = SysCacheGetAttr(SUBSCRIPTIONOID, tup,
+							Anum_pg_subscription_subslotname,
+							&isnull);
+	if (!isnull)
+		appendStringInfo(&buf, ", slot_name = \'%s\'",
+						 NameStr(*DatumGetName(datum)));
+	else
+	{
+		appendStringInfoString(&buf, ", slot_name = none");
+		/* Setting slot_name to none must set create_slot to false */
+		appendStringInfoString(&buf, ", create_slot = false");
+	}
+
+	/* Get enabled option */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subenabled);
+	/* Setting 'slot_name' to none must set 'enabled' to false as well */
+	if (!DatumGetBool(datum) || isnull)
+		appendStringInfoString(&buf, ", enabled = false");
+	else
+		appendStringInfoString(&buf, ", enabled = true");
+
+	/* Get binary option */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subbinary);
+	appendStringInfo(&buf, ", binary = %s",
+					 DatumGetBool(datum) ? "true" : "false");
+
+	/* Get streaming option */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_substream);
+	if (DatumGetChar(datum) == LOGICALREP_STREAM_OFF)
+		appendStringInfoString(&buf, ", streaming = off");
+	else if (DatumGetChar(datum) == LOGICALREP_STREAM_ON)
+		appendStringInfoString(&buf, ", streaming = on");
+	else
+		appendStringInfoString(&buf, ", streaming = parallel");
+
+	/* Get sync commit option */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subsynccommit);
+	appendStringInfo(&buf, ", synchronous_commit = %s",
+					 TextDatumGetCString(datum));
+
+	/* Get two-phase commit option */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subtwophasestate);
+	if (DatumGetChar(datum) == LOGICALREP_TWOPHASE_STATE_DISABLED)
+		appendStringInfoString(&buf, ", two_phase = off");
+	else
+		appendStringInfoString(&buf, ", two_phase = on");
+
+	/* Disable on error? */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subdisableonerr);
+	appendStringInfo(&buf, ", disable_on_error = %s",
+					 DatumGetBool(datum) ? "on" : "off");
+
+	/* Password required? */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subpasswordrequired);
+	appendStringInfo(&buf, ", password_required = %s",
+					 DatumGetBool(datum) ? "on" : "off");
+
+	/* Run as owner? */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subrunasowner);
+	appendStringInfo(&buf, ", run_as_owner = %s",
+					 DatumGetBool(datum) ? "on" : "off");
+
+	/* Get origin */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_suborigin);
+	appendStringInfo(&buf, ", origin = %s", TextDatumGetCString(datum));
+
+	/* Failover? */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subfailover);
+	appendStringInfo(&buf, ", failover = %s",
+					 DatumGetBool(datum) ? "on" : "off");
+
+	/* Retain dead tuples? */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_subretaindeadtuples);
+	appendStringInfo(&buf, ", retain_dead_tuples = %s",
+					 DatumGetBool(datum) ? "on" : "off");
+
+	/* Max retention duration */
+	datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID, tup,
+								   Anum_pg_subscription_submaxretention);
+	appendStringInfo(&buf, ", max_retention_duration = %lu",
+					 Int32GetDatum(datum));
+
+	/* Finally close parenthesis and add semicolon to the statement */
+	appendStringInfoString(&buf, ");");
+
+	ReleaseSysCache(tup);
+
+	PG_RETURN_TEXT_P(string_to_text(buf.data));
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 9121a382f76..4526853aad2 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -3993,6 +3993,9 @@
 { oid => '1387', descr => 'constraint description',
   proname => 'pg_get_constraintdef', provolatile => 's', prorettype => 'text',
   proargtypes => 'oid', prosrc => 'pg_get_constraintdef' },
+{ oid => '8001', descr => 'get CREATE statement for subscription',
+  proname => 'pg_get_subscription_ddl', prorettype => 'text',
+  proargtypes => 'text', prosrc => 'pg_get_subscription_ddl' },
 { oid => '1716', descr => 'deparse an encoded expression',
   proname => 'pg_get_expr', provolatile => 's', prorettype => 'text',
   proargtypes => 'pg_node_tree oid', prosrc => 'pg_get_expr' },
diff --git a/src/include/catalog/pg_subscription.h b/src/include/catalog/pg_subscription.h
index 55cb9b1eefa..3082ab8dffc 100644
--- a/src/include/catalog/pg_subscription.h
+++ b/src/include/catalog/pg_subscription.h
@@ -22,6 +22,7 @@
 #include "catalog/pg_subscription_d.h"	/* IWYU pragma: export */
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
+#include "utils/array.h"
 
 /* ----------------
  *		pg_subscription definition. cpp turns this into
@@ -207,5 +208,6 @@ extern int	CountDBSubscriptions(Oid dbid);
 
 extern void GetPublicationsStr(List *publications, StringInfo dest,
 							   bool quote_literal);
+extern List *textarray_to_stringlist(ArrayType *textarray);
 
 #endif							/* PG_SUBSCRIPTION_H */
diff --git a/src/test/regress/expected/subscription_ddl.out b/src/test/regress/expected/subscription_ddl.out
new file mode 100644
index 00000000000..ce4b3138f81
--- /dev/null
+++ b/src/test/regress/expected/subscription_ddl.out
@@ -0,0 +1,85 @@
+--
+-- Get CREATE SUBSCRIPTION statement
+--
+CREATE ROLE createsub_role LOGIN;
+CREATE ROLE readalldata_role LOGIN;
+-- Create subscription with minimal options
+CREATE SUBSCRIPTION testsub1 CONNECTION 'dbname=db_doesnotexist'
+  PUBLICATION testpub1 WITH (connect=false);
+WARNING:  subscription was created, but is not connected
+HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and alter the subscription to refresh publications.
+-- Check that the subscription ddl is correctly created
+SELECT pg_get_subscription_ddl('testsub1');
+                                                                                                                                                                                      pg_get_subscription_ddl                                                                                                                                                                                      
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE SUBSCRIPTION testsub1 CONNECTION 'dbname=db_doesnotexist' PUBLICATION "testpub1" WITH (connect = false, slot_name = 'testsub1', enabled = false, binary = false, streaming = parallel, synchronous_commit = off, two_phase = off, disable_on_error = off, password_required = on, run_as_owner = off, origin = any, failover = off, retain_dead_tuples = off, max_retention_duration = 0);
+(1 row)
+
+-- Create subscription with more options
+CREATE SUBSCRIPTION "TestSubddL2" CONNECTION 'host=unknown user=dvd password=pass123'
+  PUBLICATION "testpub2", "TestPub3" WITH (connect=false, slot_name='slot1',
+  enabled=off);
+WARNING:  subscription was created, but is not connected
+HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and alter the subscription to refresh publications.
+SELECT pg_get_subscription_ddl('TestSubddL2');
+                                                                                                                                                                                                     pg_get_subscription_ddl                                                                                                                                                                                                     
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE SUBSCRIPTION "TestSubddL2" CONNECTION 'host=unknown user=dvd password=pass123' PUBLICATION "testpub2", "TestPub3" WITH (connect = false, slot_name = 'slot1', enabled = false, binary = false, streaming = parallel, synchronous_commit = off, two_phase = off, disable_on_error = off, password_required = on, run_as_owner = off, origin = any, failover = off, retain_dead_tuples = off, max_retention_duration = 0);
+(1 row)
+
+-- Create subscription with all options
+CREATE SUBSCRIPTION testsub3 CONNECTION 'host=unknown user=dvd password=pass12'
+  PUBLICATION testpub4 WITH (connect=false, slot_name=none, enabled=false,
+  create_slot=false, copy_data=false, binary=true, streaming=off,
+  synchronous_commit=local, two_phase=true, disable_on_error=true,
+  password_required=false, run_as_owner=true, origin=none, failover=true,
+  retain_dead_tuples=false, max_retention_duration=100);
+NOTICE:  max_retention_duration is ineffective when retain_dead_tuples is disabled
+WARNING:  subscription was created, but is not connected
+HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and alter the subscription to refresh publications.
+SELECT pg_get_subscription_ddl('testsub3');
+                                                                                                                                                                                                   pg_get_subscription_ddl                                                                                                                                                                                                   
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE SUBSCRIPTION testsub3 CONNECTION 'host=unknown user=dvd password=pass12' PUBLICATION "testpub4" WITH (connect = false, slot_name = none, create_slot = false, enabled = false, binary = true, streaming = off, synchronous_commit = local, two_phase = on, disable_on_error = on, password_required = off, run_as_owner = on, origin = none, failover = on, retain_dead_tuples = off, max_retention_duration = 100);
+(1 row)
+
+-- Non-superusers and which don't have pg_create_subscription and/or
+-- pg_read_all_data permission can't get ddl
+SET SESSION AUTHORIZATION 'createsub_role';
+SELECT pg_get_subscription_ddl('TestSubddL2');
+ERROR:  permission denied to get the create subscription ddl
+DETAIL:  Only roles with privileges of the "pg_create_subscription" and/or "pg_read_all_data" role may get ddl.
+RESET SESSION AUTHORIZATION;
+SET SESSION AUTHORIZATION 'readalldata_role';
+SELECT pg_get_subscription_ddl('TestSubddL2');
+ERROR:  permission denied to get the create subscription ddl
+DETAIL:  Only roles with privileges of the "pg_create_subscription" and/or "pg_read_all_data" role may get ddl.
+RESET SESSION AUTHORIZATION;
+-- Administrators can change who can access this function
+GRANT pg_create_subscription TO createsub_role;
+GRANT pg_read_all_data TO readalldata_role;
+SET SESSION AUTHORIZATION 'createsub_role';
+SELECT pg_get_subscription_ddl('TestSubddL2');
+                                                                                                                                                                                                     pg_get_subscription_ddl                                                                                                                                                                                                     
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE SUBSCRIPTION "TestSubddL2" CONNECTION 'host=unknown user=dvd password=pass123' PUBLICATION "testpub2", "TestPub3" WITH (connect = false, slot_name = 'slot1', enabled = false, binary = false, streaming = parallel, synchronous_commit = off, two_phase = off, disable_on_error = off, password_required = on, run_as_owner = off, origin = any, failover = off, retain_dead_tuples = off, max_retention_duration = 0);
+(1 row)
+
+RESET SESSION AUTHORIZATION;
+SET SESSION AUTHORIZATION 'readalldata_role';
+SELECT pg_get_subscription_ddl('TestSubddL2');
+                                                                                                                                                                                                     pg_get_subscription_ddl                                                                                                                                                                                                     
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE SUBSCRIPTION "TestSubddL2" CONNECTION 'host=unknown user=dvd password=pass123' PUBLICATION "testpub2", "TestPub3" WITH (connect = false, slot_name = 'slot1', enabled = false, binary = false, streaming = parallel, synchronous_commit = off, two_phase = off, disable_on_error = off, password_required = on, run_as_owner = off, origin = any, failover = off, retain_dead_tuples = off, max_retention_duration = 0);
+(1 row)
+
+RESET SESSION AUTHORIZATION;
+REVOKE pg_create_subscription FROM createsub_role;
+REVOKE pg_read_all_data FROM readalldata_role;
+ALTER SUBSCRIPTION testsub1 SET (slot_name=NONE);
+DROP SUBSCRIPTION testsub1;
+ALTER SUBSCRIPTION "TestSubddL2" SET (slot_name=NONE);
+DROP SUBSCRIPTION "TestSubddL2";
+DROP SUBSCRIPTION testsub3;
+DROP ROLE createsub_role;
+DROP ROLE readalldata_role;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index a0f5fab0f5d..cbe563413bb 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -28,7 +28,7 @@ test: strings md5 numerology point lseg line box path polygon circle date time t
 # geometry depends on point, lseg, line, box, path, polygon, circle
 # horology depends on date, time, timetz, timestamp, timestamptz, interval
 # ----------
-test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc database stats_import
+test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc database stats_import subscription_ddl
 
 # ----------
 # Load huge amounts of data
diff --git a/src/test/regress/sql/subscription_ddl.sql b/src/test/regress/sql/subscription_ddl.sql
new file mode 100644
index 00000000000..6e8eecf010e
--- /dev/null
+++ b/src/test/regress/sql/subscription_ddl.sql
@@ -0,0 +1,55 @@
+--
+-- Get CREATE SUBSCRIPTION statement
+--
+
+CREATE ROLE createsub_role LOGIN;
+CREATE ROLE readalldata_role LOGIN;
+
+-- Create subscription with minimal options
+CREATE SUBSCRIPTION testsub1 CONNECTION 'dbname=db_doesnotexist'
+  PUBLICATION testpub1 WITH (connect=false);
+-- Check that the subscription ddl is correctly created
+SELECT pg_get_subscription_ddl('testsub1');
+
+-- Create subscription with more options
+CREATE SUBSCRIPTION "TestSubddL2" CONNECTION 'host=unknown user=dvd password=pass123'
+  PUBLICATION "testpub2", "TestPub3" WITH (connect=false, slot_name='slot1',
+  enabled=off);
+SELECT pg_get_subscription_ddl('TestSubddL2');
+
+-- Create subscription with all options
+CREATE SUBSCRIPTION testsub3 CONNECTION 'host=unknown user=dvd password=pass12'
+  PUBLICATION testpub4 WITH (connect=false, slot_name=none, enabled=false,
+  create_slot=false, copy_data=false, binary=true, streaming=off,
+  synchronous_commit=local, two_phase=true, disable_on_error=true,
+  password_required=false, run_as_owner=true, origin=none, failover=true,
+  retain_dead_tuples=false, max_retention_duration=100);
+SELECT pg_get_subscription_ddl('testsub3');
+
+-- Non-superusers and which don't have pg_create_subscription and/or
+-- pg_read_all_data permission can't get ddl
+SET SESSION AUTHORIZATION 'createsub_role';
+SELECT pg_get_subscription_ddl('TestSubddL2');
+RESET SESSION AUTHORIZATION;
+SET SESSION AUTHORIZATION 'readalldata_role';
+SELECT pg_get_subscription_ddl('TestSubddL2');
+RESET SESSION AUTHORIZATION;
+-- Administrators can change who can access this function
+GRANT pg_create_subscription TO createsub_role;
+GRANT pg_read_all_data TO readalldata_role;
+SET SESSION AUTHORIZATION 'createsub_role';
+SELECT pg_get_subscription_ddl('TestSubddL2');
+RESET SESSION AUTHORIZATION;
+SET SESSION AUTHORIZATION 'readalldata_role';
+SELECT pg_get_subscription_ddl('TestSubddL2');
+
+RESET SESSION AUTHORIZATION;
+REVOKE pg_create_subscription FROM createsub_role;
+REVOKE pg_read_all_data FROM readalldata_role;
+ALTER SUBSCRIPTION testsub1 SET (slot_name=NONE);
+DROP SUBSCRIPTION testsub1;
+ALTER SUBSCRIPTION "TestSubddL2" SET (slot_name=NONE);
+DROP SUBSCRIPTION "TestSubddL2";
+DROP SUBSCRIPTION testsub3;
+DROP ROLE createsub_role;
+DROP ROLE readalldata_role;
-- 
2.43.0

