Skip to content

Commit 57fb417

Browse files
KhannaShubham1197Commitfest Bot
authored and
Commitfest Bot
committed
Support for dropping all publications in 'pg_createsubscriber'
This patch introduces a new '--remove' option in the 'pg_createsubscriber' utility to specify the object types to be removed from the subscriber. This patch adds supports to specify 'publications' as an object type. This feature ensures a clean and streamlined setup of logical replication by removing publications on the subscriber that were originally replicated from the primary server during physical replication. These publications become redundant once the setup transitions to logical replication and serve no further purpose. This cleanup process removes all publications from the subscriber, regardless of their origin.
1 parent 5941946 commit 57fb417

File tree

3 files changed

+128
-17
lines changed

3 files changed

+128
-17
lines changed

doc/src/sgml/ref/pg_createsubscriber.sgml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,29 @@ PostgreSQL documentation
146146
</listitem>
147147
</varlistentry>
148148

149+
<varlistentry>
150+
<term><option>-R</option></term>
151+
<term><option>--remove</option></term>
152+
<listitem>
153+
<para>
154+
Remove all objects of the specified type from specified databases on the
155+
target server.
156+
</para>
157+
<para>
158+
publications: The "all tables" publications established for this
159+
subscriber are always removed; specifying this object type causes all
160+
other publications replicated from the source server to be dropped as
161+
well.
162+
</para>
163+
<para>
164+
The objects selected to be dropped are individually logged and do show
165+
up in a --dry-run. There is no opportunity to affect or stop the
166+
dropping of the selected objects so consider taking a backup of them
167+
using pg_dump.
168+
</para>
169+
</listitem>
170+
</varlistentry>
171+
149172
<varlistentry>
150173
<term><option>-s <replaceable class="parameter">dir</replaceable></option></term>
151174
<term><option>--socketdir=<replaceable class="parameter">dir</replaceable></option></term>

src/bin/pg_basebackup/pg_createsubscriber.c

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "getopt_long.h"
3030

3131
#define DEFAULT_SUB_PORT "50432"
32+
#define OBJECTTYPE_PUBLICATIONS 0x0001
3233

3334
/* Command-line options */
3435
struct CreateSubscriberOptions
@@ -44,6 +45,7 @@ struct CreateSubscriberOptions
4445
SimpleStringList sub_names; /* list of subscription names */
4546
SimpleStringList replslot_names; /* list of replication slot names */
4647
int recovery_timeout; /* stop recovery after this time */
48+
SimpleStringList objecttypes_to_remove; /* list of object types to remove */
4749
};
4850

4951
/* per-database publication/subscription info */
@@ -68,6 +70,8 @@ struct LogicalRepInfos
6870
{
6971
struct LogicalRepInfo *dbinfo;
7072
bool two_phase; /* enable-two-phase option */
73+
bits32 objecttypes_to_remove; /* flags indicating which object types
74+
* to remove on subscriber */
7175
};
7276

7377
static void cleanup_objects_atexit(void);
@@ -109,7 +113,9 @@ static void stop_standby_server(const char *datadir);
109113
static void wait_for_end_recovery(const char *conninfo,
110114
const struct CreateSubscriberOptions *opt);
111115
static void create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo);
112-
static void drop_publication(PGconn *conn, struct LogicalRepInfo *dbinfo);
116+
static void drop_publication(PGconn *conn, const char *pubname,
117+
const char *dbname, bool *made_publication);
118+
static void check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo);
113119
static void create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo);
114120
static void set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo,
115121
const char *lsn);
@@ -194,7 +200,8 @@ cleanup_objects_atexit(void)
194200
if (conn != NULL)
195201
{
196202
if (dbinfo->made_publication)
197-
drop_publication(conn, dbinfo);
203+
drop_publication(conn, dbinfo->pubname, dbinfo->dbname,
204+
&dbinfo->made_publication);
198205
if (dbinfo->made_replslot)
199206
drop_replication_slot(conn, dbinfo, dbinfo->replslotname);
200207
disconnect_database(conn, false);
@@ -241,6 +248,8 @@ usage(void)
241248
printf(_(" -n, --dry-run dry run, just show what would be done\n"));
242249
printf(_(" -p, --subscriber-port=PORT subscriber port number (default %s)\n"), DEFAULT_SUB_PORT);
243250
printf(_(" -P, --publisher-server=CONNSTR publisher connection string\n"));
251+
printf(_(" -R, --remove=OBJECTTYPE remove all objects of the specified type from specified\n"
252+
" databases on the subscriber; accepts: publications\n"));
244253
printf(_(" -s, --socketdir=DIR socket directory to use (default current dir.)\n"));
245254
printf(_(" -t, --recovery-timeout=SECS seconds to wait for recovery to end\n"));
246255
printf(_(" -T, --enable-two-phase enable two-phase commit for all subscriptions\n"));
@@ -1193,12 +1202,8 @@ setup_subscriber(struct LogicalRepInfo *dbinfo, const char *consistent_lsn)
11931202
*/
11941203
check_and_drop_existing_subscriptions(conn, &dbinfo[i]);
11951204

1196-
/*
1197-
* Since the publication was created before the consistent LSN, it is
1198-
* available on the subscriber when the physical replica is promoted.
1199-
* Remove publications from the subscriber because it has no use.
1200-
*/
1201-
drop_publication(conn, &dbinfo[i]);
1205+
/* Check and drop the required publications in the given database. */
1206+
check_and_drop_publications(conn, &dbinfo[i]);
12021207

12031208
create_subscription(conn, &dbinfo[i]);
12041209

@@ -1663,21 +1668,22 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
16631668
}
16641669

16651670
/*
1666-
* Remove publication if it couldn't finish all steps.
1671+
* Drop the specified publication in the given database.
16671672
*/
16681673
static void
1669-
drop_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
1674+
drop_publication(PGconn *conn, const char *pubname, const char *dbname,
1675+
bool *made_publication)
16701676
{
16711677
PQExpBuffer str = createPQExpBuffer();
16721678
PGresult *res;
16731679
char *pubname_esc;
16741680

16751681
Assert(conn != NULL);
16761682

1677-
pubname_esc = PQescapeIdentifier(conn, dbinfo->pubname, strlen(dbinfo->pubname));
1683+
pubname_esc = PQescapeIdentifier(conn, pubname, strlen(pubname));
16781684

16791685
pg_log_info("dropping publication \"%s\" in database \"%s\"",
1680-
dbinfo->pubname, dbinfo->dbname);
1686+
pubname, dbname);
16811687

16821688
appendPQExpBuffer(str, "DROP PUBLICATION %s", pubname_esc);
16831689

@@ -1691,8 +1697,8 @@ drop_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
16911697
if (PQresultStatus(res) != PGRES_COMMAND_OK)
16921698
{
16931699
pg_log_error("could not drop publication \"%s\" in database \"%s\": %s",
1694-
dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
1695-
dbinfo->made_publication = false; /* don't try again. */
1700+
pubname, dbname, PQresultErrorMessage(res));
1701+
*made_publication = false; /* don't try again. */
16961702

16971703
/*
16981704
* Don't disconnect and exit here. This routine is used by primary
@@ -1708,6 +1714,49 @@ drop_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
17081714
destroyPQExpBuffer(str);
17091715
}
17101716

1717+
/*
1718+
* Retrieve and drop the publications.
1719+
*
1720+
* Since the publications were created before the consistent LSN, they
1721+
* remain on the subscriber even after the physical replica is
1722+
* promoted. Remove these publications from the subscriber because
1723+
* they have no use. Additionally, if requested, drop all pre-existing
1724+
* publications.
1725+
*/
1726+
static void
1727+
check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
1728+
{
1729+
PGresult *res;
1730+
1731+
Assert(conn != NULL);
1732+
1733+
if (dbinfos.objecttypes_to_remove & OBJECTTYPE_PUBLICATIONS)
1734+
{
1735+
pg_log_info("dropping all existing publications in database \"%s\"",
1736+
dbinfo->dbname);
1737+
1738+
/* Fetch all publication names */
1739+
res = PQexec(conn, "SELECT pubname FROM pg_catalog.pg_publication;");
1740+
if (PQresultStatus(res) != PGRES_TUPLES_OK)
1741+
{
1742+
pg_log_error("could not obtain publication information: %s",
1743+
PQresultErrorMessage(res));
1744+
PQclear(res);
1745+
disconnect_database(conn, true);
1746+
}
1747+
1748+
/* Drop each publication */
1749+
for (int i = 0; i < PQntuples(res); i++)
1750+
drop_publication(conn, PQgetvalue(res, i, 0), dbinfo->dbname,
1751+
&dbinfo->made_publication);
1752+
1753+
PQclear(res);
1754+
}
1755+
else
1756+
drop_publication(conn, dbinfo->pubname, dbinfo->dbname,
1757+
&dbinfo->made_publication);
1758+
}
1759+
17111760
/*
17121761
* Create a subscription with some predefined options.
17131762
*
@@ -1914,6 +1963,7 @@ main(int argc, char **argv)
19141963
{"dry-run", no_argument, NULL, 'n'},
19151964
{"subscriber-port", required_argument, NULL, 'p'},
19161965
{"publisher-server", required_argument, NULL, 'P'},
1966+
{"remove", required_argument, NULL, 'R'},
19171967
{"socketdir", required_argument, NULL, 's'},
19181968
{"recovery-timeout", required_argument, NULL, 't'},
19191969
{"enable-two-phase", no_argument, NULL, 'T'},
@@ -1995,7 +2045,7 @@ main(int argc, char **argv)
19952045

19962046
get_restricted_token();
19972047

1998-
while ((c = getopt_long(argc, argv, "d:D:np:P:s:t:TU:v",
2048+
while ((c = getopt_long(argc, argv, "d:D:np:P:R:s:t:TU:v",
19992049
long_options, &option_index)) != -1)
20002050
{
20012051
switch (c)
@@ -2025,6 +2075,12 @@ main(int argc, char **argv)
20252075
case 'P':
20262076
opt.pub_conninfo_str = pg_strdup(optarg);
20272077
break;
2078+
case 'R':
2079+
if (!simple_string_list_member(&opt.objecttypes_to_remove, optarg))
2080+
simple_string_list_append(&opt.objecttypes_to_remove, optarg);
2081+
else
2082+
pg_fatal("object type \"%s\" is specified more than once for --remove", optarg);
2083+
break;
20282084
case 's':
20292085
opt.socket_dir = pg_strdup(optarg);
20302086
canonicalize_path(opt.socket_dir);
@@ -2189,6 +2245,19 @@ main(int argc, char **argv)
21892245
exit(1);
21902246
}
21912247

2248+
/* Verify the object types specified for removal from the subscriber */
2249+
for (SimpleStringListCell *cell = opt.objecttypes_to_remove.head; cell; cell = cell->next)
2250+
{
2251+
if (pg_strcasecmp(cell->val, "publications") == 0)
2252+
dbinfos.objecttypes_to_remove |= OBJECTTYPE_PUBLICATIONS;
2253+
else
2254+
{
2255+
pg_log_error("invalid object type \"%s\" specified for --remove", cell->val);
2256+
pg_log_error_hint("The valid option is: \"publications\"");
2257+
exit(1);
2258+
}
2259+
}
2260+
21922261
/* Get the absolute path of pg_ctl and pg_resetwal on the subscriber */
21932262
pg_ctl_path = get_exec_path(argv[0], "pg_ctl");
21942263
pg_resetwal_path = get_exec_path(argv[0], "pg_resetwal");

src/bin/pg_basebackup/t/040_pg_createsubscriber.pl

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,20 @@ sub generate_db
329329
"CREATE SUBSCRIPTION $dummy_sub CONNECTION 'dbname=dummy' PUBLICATION pub_dummy WITH (connect=false)"
330330
);
331331
$node_p->wait_for_replay_catchup($node_s);
332+
333+
# Create user-defined publications, wait for streaming replication to sync them
334+
# to the standby, then verify that '--remove' removes them.
335+
$node_p->safe_psql(
336+
$db1, qq(
337+
CREATE PUBLICATION test_pub1 FOR ALL TABLES;
338+
CREATE PUBLICATION test_pub2 FOR ALL TABLES;
339+
));
340+
341+
$node_p->wait_for_replay_catchup($node_s);
342+
343+
ok($node_s->safe_psql($db1, "SELECT COUNT(*) = 2 FROM pg_publication"),
344+
'two pre-existing publications on subscriber');
345+
332346
$node_s->stop;
333347

334348
# dry run mode on node S
@@ -373,7 +387,7 @@ sub generate_db
373387

374388
# Run pg_createsubscriber on node S. --verbose is used twice
375389
# to show more information.
376-
# In passing, also test the --enable-two-phase option
390+
# In passing, also test the --enable-two-phase option and --remove option
377391
command_ok(
378392
[
379393
'pg_createsubscriber',
@@ -389,7 +403,8 @@ sub generate_db
389403
'--replication-slot' => 'replslot2',
390404
'--database' => $db1,
391405
'--database' => $db2,
392-
'--enable-two-phase'
406+
'--enable-two-phase',
407+
'--remove' => 'publications',
393408
],
394409
'run pg_createsubscriber on node S');
395410

@@ -408,6 +423,10 @@ sub generate_db
408423
# Start subscriber
409424
$node_s->start;
410425

426+
# Confirm publications are removed from the subscriber node
427+
is($node_s->safe_psql($db1, "SELECT COUNT(*) FROM pg_publication;"),
428+
'0', 'all publications on subscriber have been removed');
429+
411430
# Verify that all subtwophase states are pending or enabled,
412431
# e.g. there are no subscriptions where subtwophase is disabled ('d')
413432
is( $node_s->safe_psql(

0 commit comments

Comments
 (0)