From 272adf4f9cd67df323ae57ff3dee238b649d3b73 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Fri, 3 Mar 2017 23:25:34 -0500 Subject: [PATCH] Disallow CREATE/DROP SUBSCRIPTION in transaction block Disallow CREATE SUBSCRIPTION and DROP SUBSCRIPTION in a transaction block when the replication slot is to be created or dropped, since that cannot be rolled back. based on patch by Masahiko Sawada --- doc/src/sgml/ref/create_subscription.sgml | 5 +++ doc/src/sgml/ref/drop_subscription.sgml | 4 +-- src/backend/commands/subscriptioncmds.c | 38 +++++++++++++++++----- src/backend/tcop/utility.c | 5 +-- src/include/commands/subscriptioncmds.h | 5 +-- src/test/regress/expected/subscription.out | 12 +++++++ src/test/regress/sql/subscription.sql | 12 +++++++ 7 files changed, 66 insertions(+), 15 deletions(-) diff --git a/doc/src/sgml/ref/create_subscription.sgml b/doc/src/sgml/ref/create_subscription.sgml index 250806f981..9bed26219c 100644 --- a/doc/src/sgml/ref/create_subscription.sgml +++ b/doc/src/sgml/ref/create_subscription.sgml @@ -51,6 +51,11 @@ CREATE SUBSCRIPTION subscription_name + + CREATE SUBSCRIPTION cannot be executed inside a + transaction block when CREATE SLOT is specified. + + Additional info about subscriptions and logical replication as a whole can is available at and diff --git a/doc/src/sgml/ref/drop_subscription.sgml b/doc/src/sgml/ref/drop_subscription.sgml index 9f2fb93275..4228f1a253 100644 --- a/doc/src/sgml/ref/drop_subscription.sgml +++ b/doc/src/sgml/ref/drop_subscription.sgml @@ -38,8 +38,8 @@ DROP SUBSCRIPTION [ IF EXISTS ] name - The replication worker associated with the subscription will not stop until - after the transaction that issued this command has committed. + DROP SUBSCRIPTION cannot be executed inside a + transaction block when DROP SLOT is specified. diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 0e081388c4..0036d99c2e 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -18,6 +18,7 @@ #include "access/heapam.h" #include "access/htup_details.h" +#include "access/xact.h" #include "catalog/indexing.h" #include "catalog/objectaccess.h" @@ -204,7 +205,7 @@ publicationListToArray(List *publist) * Create new subscription. */ ObjectAddress -CreateSubscription(CreateSubscriptionStmt *stmt) +CreateSubscription(CreateSubscriptionStmt *stmt, bool isTopLevel) { Relation rel; ObjectAddress myself; @@ -221,6 +222,23 @@ CreateSubscription(CreateSubscriptionStmt *stmt) bool create_slot; List *publications; + /* + * Parse and check options. + * Connection and publication should not be specified here. + */ + parse_subscription_options(stmt->options, NULL, NULL, + &enabled_given, &enabled, + &create_slot, &slotname); + + /* + * Since creating a replication slot is not transactional, rolling back + * the transaction leaves the created replication slot. So we cannot run + * CREATE SUBSCRIPTION inside a transaction block if creating a + * replication slot. + */ + if (create_slot) + PreventTransactionChain(isTopLevel, "CREATE SUBSCRIPTION ... CREATE SLOT"); + if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -239,13 +257,6 @@ CreateSubscription(CreateSubscriptionStmt *stmt) stmt->subname))); } - /* - * Parse and check options. - * Connection and publication should not be specified here. - */ - parse_subscription_options(stmt->options, NULL, NULL, - &enabled_given, &enabled, - &create_slot, &slotname); if (slotname == NULL) slotname = stmt->subname; @@ -424,7 +435,7 @@ AlterSubscription(AlterSubscriptionStmt *stmt) * Drop a subscription */ void -DropSubscription(DropSubscriptionStmt *stmt) +DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel) { Relation rel; ObjectAddress myself; @@ -441,6 +452,15 @@ DropSubscription(DropSubscriptionStmt *stmt) WalReceiverConn *wrconn = NULL; StringInfoData cmd; + /* + * Since dropping a replication slot is not transactional, the replication + * slot stays dropped even if the transaction rolls back. So we cannot + * run DROP SUBSCRIPTION inside a transaction block if dropping the + * replication slot. + */ + if (stmt->drop_slot) + PreventTransactionChain(isTopLevel, "DROP SUBSCRIPTION ... DROP SLOT"); + rel = heap_open(SubscriptionRelationId, RowExclusiveLock); tup = SearchSysCache2(SUBSCRIPTIONNAME, MyDatabaseId, diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 3bc0ae5e7e..20b5273405 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1609,7 +1609,8 @@ ProcessUtilitySlow(ParseState *pstate, break; case T_CreateSubscriptionStmt: - address = CreateSubscription((CreateSubscriptionStmt *) parsetree); + address = CreateSubscription((CreateSubscriptionStmt *) parsetree, + isTopLevel); break; case T_AlterSubscriptionStmt: @@ -1617,7 +1618,7 @@ ProcessUtilitySlow(ParseState *pstate, break; case T_DropSubscriptionStmt: - DropSubscription((DropSubscriptionStmt *) parsetree); + DropSubscription((DropSubscriptionStmt *) parsetree, isTopLevel); /* no commands stashed for DROP */ commandCollected = true; break; diff --git a/src/include/commands/subscriptioncmds.h b/src/include/commands/subscriptioncmds.h index 127696c60d..1765879333 100644 --- a/src/include/commands/subscriptioncmds.h +++ b/src/include/commands/subscriptioncmds.h @@ -18,9 +18,10 @@ #include "catalog/objectaddress.h" #include "nodes/parsenodes.h" -extern ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt); +extern ObjectAddress CreateSubscription(CreateSubscriptionStmt *stmt, + bool isTopLevel); extern ObjectAddress AlterSubscription(AlterSubscriptionStmt *stmt); -extern void DropSubscription(DropSubscriptionStmt *stmt); +extern void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel); extern ObjectAddress AlterSubscriptionOwner(const char *name, Oid newOwnerId); extern void AlterSubscriptionOwner_oid(Oid subid, Oid newOwnerId); diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out index cb1ab4e791..a8a61ee8af 100644 --- a/src/test/regress/expected/subscription.out +++ b/src/test/regress/expected/subscription.out @@ -14,6 +14,11 @@ ERROR: syntax error at or near "PUBLICATION" LINE 1: CREATE SUBSCRIPTION testsub PUBLICATION foo; ^ set client_min_messages to error; +-- fail - cannot do CREATE SUBSCRIPTION CREATE SLOT inside transaction block +BEGIN; +CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub WITH (CREATE SLOT); +ERROR: CREATE SUBSCRIPTION ... CREATE SLOT cannot run inside a transaction block +COMMIT; CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub; ERROR: invalid connection string syntax: missing "=" after "testconn" in connection info string @@ -69,6 +74,13 @@ ALTER SUBSCRIPTION testsub RENAME TO testsub_foo; testsub_foo | regress_subscription_user | f | {testpub,testpub1} (1 row) +-- fail - cannot do DROP SUBSCRIPTION DROP SLOT inside transaction block +BEGIN; +DROP SUBSCRIPTION testsub DROP SLOT; +ERROR: DROP SUBSCRIPTION ... DROP SLOT cannot run inside a transaction block +COMMIT; +BEGIN; DROP SUBSCRIPTION testsub_foo NODROP SLOT; +COMMIT; RESET SESSION AUTHORIZATION; DROP ROLE regress_subscription_user; diff --git a/src/test/regress/sql/subscription.sql b/src/test/regress/sql/subscription.sql index fce6069a9c..0b6c8a3f5c 100644 --- a/src/test/regress/sql/subscription.sql +++ b/src/test/regress/sql/subscription.sql @@ -12,6 +12,11 @@ CREATE SUBSCRIPTION testsub CONNECTION 'foo'; CREATE SUBSCRIPTION testsub PUBLICATION foo; set client_min_messages to error; +-- fail - cannot do CREATE SUBSCRIPTION CREATE SLOT inside transaction block +BEGIN; +CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub WITH (CREATE SLOT); +COMMIT; + CREATE SUBSCRIPTION testsub CONNECTION 'testconn' PUBLICATION testpub; CREATE SUBSCRIPTION testsub CONNECTION 'dbname=doesnotexist' PUBLICATION testpub WITH (DISABLED, NOCREATE SLOT); reset client_min_messages; @@ -42,7 +47,14 @@ ALTER SUBSCRIPTION testsub RENAME TO testsub_foo; \dRs +-- fail - cannot do DROP SUBSCRIPTION DROP SLOT inside transaction block +BEGIN; +DROP SUBSCRIPTION testsub DROP SLOT; +COMMIT; + +BEGIN; DROP SUBSCRIPTION testsub_foo NODROP SLOT; +COMMIT; RESET SESSION AUTHORIZATION; DROP ROLE regress_subscription_user; -- 2.40.0