From b37f2171af614850058d70ead8a283b6e72f2047 Mon Sep 17 00:00:00 2001 From: Vignesh C Date: Mon, 11 May 2026 11:30:27 +0530 Subject: [PATCH v33 5/5] Address review comments for conflict log table patch This patch addresses the remaining review comments for the conflict log table: - Add comments explaining the reasoning for the ownership change - change clt display - Test cases for ownership change, truncation, deletion, and select from a non-superuser owner of subscriber. These were the remaining open items identified in the review thread: https://www.postgresql.org/message-id/CAFiTN-sdcjf9xJ2M-%3Dab5e4y662tTmFFiP4gHL44tC9PcQozcw%40mail.gmail.com --- src/bin/initdb/initdb.c | 5 + src/bin/psql/command.c | 5 +- src/bin/psql/describe.c | 405 ++++++++++++++++----- src/bin/psql/describe.h | 5 +- src/test/regress/expected/subscription.out | 211 ++++++----- src/test/regress/sql/subscription.sql | 31 ++ 6 files changed, 482 insertions(+), 180 deletions(-) diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index cda05676a79..803ca4112d4 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -1839,6 +1839,11 @@ setup_privileges(FILE *cmdfd) " AND relacl IS NULL;\n\n", escape_quotes(username)); PG_CMD_PUTS("GRANT USAGE ON SCHEMA pg_catalog, public TO PUBLIC;\n\n"); + + /* + * Allow non-superuser subscription owners to access their associated + * conflict log tables in the pg_conflict schema. + */ PG_CMD_PUTS("GRANT USAGE ON SCHEMA pg_conflict TO PUBLIC;\n\n"); PG_CMD_PUTS("REVOKE ALL ON pg_largeobject FROM PUBLIC;\n\n"); PG_CMD_PUTS("INSERT INTO pg_init_privs " diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 493400f9090..8aba967cb76 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1220,7 +1220,10 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd) success = listPublications(pattern); break; case 's': - success = describeSubscriptions(pattern, show_verbose); + if (show_verbose) + success = describeSubscriptions(pattern); + else + success = listSubscriptions(pattern); break; default: status = PSQL_CMD_UNKNOWN; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 95f78071969..e495956897e 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -7081,25 +7081,23 @@ error_return: /* * \dRs - * Describes subscriptions. + * Lists subscriptions. * * Takes an optional regexp to select particular subscriptions */ bool -describeSubscriptions(const char *pattern, bool verbose) +listSubscriptions(const char *pattern) { PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, false, false, - false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false}; + static const bool translate_columns[] = {false, false, false, false}; if (pset.sversion < 100000) { char sverbuf[32]; - pg_log_error("The server (version %s) does not support subscriptions.", + pg_log_error("The server (version %s) does not support publications.", formatPGVersionNumber(pset.sversion, false, sverbuf, sizeof(sverbuf))); return true; @@ -7118,103 +7116,195 @@ describeSubscriptions(const char *pattern, bool verbose) gettext_noop("Enabled"), gettext_noop("Publication")); - if (verbose) + /* Only display subscriptions in current database. */ + appendPQExpBufferStr(&buf, + "FROM pg_catalog.pg_subscription\n" + "WHERE subdbid = (SELECT oid\n" + " FROM pg_catalog.pg_database\n" + " WHERE datname = pg_catalog.current_database())"); + + if (!validateSQLNamePattern(&buf, pattern, true, false, + NULL, "subname", NULL, + NULL, + NULL, 1)) { - /* Binary mode and streaming are only supported in v14 and higher */ - if (pset.sversion >= 140000) - { - appendPQExpBuffer(&buf, - ", subbinary AS \"%s\"\n", - gettext_noop("Binary")); + termPQExpBuffer(&buf); + return false; + } - if (pset.sversion >= 160000) - appendPQExpBuffer(&buf, - ", (CASE substream\n" - " WHEN " CppAsString2(LOGICALREP_STREAM_OFF) " THEN 'off'\n" - " WHEN " CppAsString2(LOGICALREP_STREAM_ON) " THEN 'on'\n" - " WHEN " CppAsString2(LOGICALREP_STREAM_PARALLEL) " THEN 'parallel'\n" - " END) AS \"%s\"\n", - gettext_noop("Streaming")); - else - appendPQExpBuffer(&buf, - ", substream AS \"%s\"\n", - gettext_noop("Streaming")); - } + appendPQExpBufferStr(&buf, "ORDER BY 1;"); - /* Two_phase and disable_on_error are only supported in v15 and higher */ - if (pset.sversion >= 150000) - appendPQExpBuffer(&buf, - ", subtwophasestate AS \"%s\"\n" - ", subdisableonerr AS \"%s\"\n", - gettext_noop("Two-phase commit"), - gettext_noop("Disable on error")); + res = PSQLexec(buf.data); + termPQExpBuffer(&buf); + if (!res) + return false; - if (pset.sversion >= 160000) - appendPQExpBuffer(&buf, - ", suborigin AS \"%s\"\n" - ", subpasswordrequired AS \"%s\"\n" - ", subrunasowner AS \"%s\"\n", - gettext_noop("Origin"), - gettext_noop("Password required"), - gettext_noop("Run as owner?")); + myopt.title = _("List of subscriptions"); + myopt.translate_header = true; + myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); - if (pset.sversion >= 170000) + printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + + PQclear(res); + + return true; +} + +/* + * \dRs+ + * Describes subscriptions. + * + * Takes an optional regexp to select particular subscriptions + */ +bool +describeSubscriptions(const char *pattern) +{ + PQExpBufferData buf; + int i; + PGresult *res; + int ncols; + int nrows = 1; + + PQExpBufferData title; + printTableContent cont; + + if (pset.sversion < 100000) + { + char sverbuf[32]; + + pg_log_error("The server (version %s) does not support subscriptions.", + formatPGVersionNumber(pset.sversion, false, + sverbuf, sizeof(sverbuf))); + return true; + } + + initPQExpBuffer(&buf); + + printfPQExpBuffer(&buf, "/* %s */\n", _("Get matching subscriptions")); + appendPQExpBuffer(&buf, + "SELECT oid, subname AS \"%s\"\n" + ", pg_catalog.pg_get_userbyid(subowner) AS \"%s\"\n" + ", subenabled AS \"%s\"\n" + ", subpublications AS \"%s\"\n", + gettext_noop("Name"), + gettext_noop("Owner"), + gettext_noop("Enabled"), + gettext_noop("Publication")); + ncols = 3; + + /* Binary mode and streaming are only supported in v14 and higher */ + if (pset.sversion >= 140000) + { + appendPQExpBuffer(&buf, + ", subbinary AS \"%s\"\n", + gettext_noop("Binary")); + ncols++; + + if (pset.sversion >= 160000) appendPQExpBuffer(&buf, - ", subfailover AS \"%s\"\n", - gettext_noop("Failover")); - if (pset.sversion >= 190000) - { + ", (CASE substream\n" + " WHEN " CppAsString2(LOGICALREP_STREAM_OFF) " THEN 'off'\n" + " WHEN " CppAsString2(LOGICALREP_STREAM_ON) " THEN 'on'\n" + " WHEN " CppAsString2(LOGICALREP_STREAM_PARALLEL) " THEN 'parallel'\n" + " END) AS \"%s\"\n", + gettext_noop("Streaming")); + else appendPQExpBuffer(&buf, - ", (select srvname from pg_foreign_server where oid=subserver) AS \"%s\"\n", - gettext_noop("Server")); + ", substream AS \"%s\"\n", + gettext_noop("Streaming")); - appendPQExpBuffer(&buf, - ", subretaindeadtuples AS \"%s\"\n", - gettext_noop("Retain dead tuples")); + ncols++; + } - appendPQExpBuffer(&buf, - ", submaxretention AS \"%s\"\n", - gettext_noop("Max retention duration")); + /* Two_phase and disable_on_error are only supported in v15 and higher */ + if (pset.sversion >= 150000) + { + appendPQExpBuffer(&buf, + ", subtwophasestate AS \"%s\"\n" + ", subdisableonerr AS \"%s\"\n", + gettext_noop("Two-phase commit"), + gettext_noop("Disable on error")); + ncols += 2; + } - appendPQExpBuffer(&buf, - ", subretentionactive AS \"%s\"\n", - gettext_noop("Retention active")); - } + if (pset.sversion >= 160000) + { + appendPQExpBuffer(&buf, + ", suborigin AS \"%s\"\n" + ", subpasswordrequired AS \"%s\"\n" + ", subrunasowner AS \"%s\"\n", + gettext_noop("Origin"), + gettext_noop("Password required"), + gettext_noop("Run as owner?")); + ncols += 3; + } + if (pset.sversion >= 170000) + { appendPQExpBuffer(&buf, - ", subsynccommit AS \"%s\"\n" - ", subconninfo AS \"%s\"\n", - gettext_noop("Synchronous commit"), - gettext_noop("Conninfo")); + ", subfailover AS \"%s\"\n", + gettext_noop("Failover")); + ncols++; + } - if (pset.sversion >= 190000) - appendPQExpBuffer(&buf, - ", subwalrcvtimeout AS \"%s\"\n", - gettext_noop("Receiver timeout")); + if (pset.sversion >= 190000) + { + appendPQExpBuffer(&buf, + ", (select srvname from pg_foreign_server where oid=subserver) AS \"%s\"\n", + gettext_noop("Server")); - /* Skip LSN is only supported in v15 and higher */ - if (pset.sversion >= 150000) - appendPQExpBuffer(&buf, - ", subskiplsn AS \"%s\"\n", - gettext_noop("Skip LSN")); + appendPQExpBuffer(&buf, + ", subretaindeadtuples AS \"%s\"\n", + gettext_noop("Retain dead tuples")); appendPQExpBuffer(&buf, - ", pg_catalog.obj_description(oid, 'pg_subscription') AS \"%s\"\n", - gettext_noop("Description")); + ", submaxretention AS \"%s\"\n", + gettext_noop("Max retention duration")); - /* Conflict log destination is supported in v19 and higher */ - if (pset.sversion >= 190000) - { - appendPQExpBuffer(&buf, - ", subconflictlogdest AS \"%s\"\n", - gettext_noop("Conflict log destination")); + appendPQExpBuffer(&buf, + ", subretentionactive AS \"%s\"\n", + gettext_noop("Retention active")); - appendPQExpBuffer(&buf, - ", (CASE WHEN subconflictlogdest IN ('table', 'all') " - " THEN 'pg_conflict.pg_conflict_log_' || oid " - " ELSE '-' END) AS \"%s\"\n", - gettext_noop("Conflict log table")); - } + ncols += 4; + } + + appendPQExpBuffer(&buf, + ", subsynccommit AS \"%s\"\n" + ", subconninfo AS \"%s\"\n", + gettext_noop("Synchronous commit"), + gettext_noop("Conninfo")); + ncols += 2; + + if (pset.sversion >= 190000) + { + appendPQExpBuffer(&buf, + ", subwalrcvtimeout AS \"%s\"\n", + gettext_noop("Receiver timeout")); + ncols++; + } + + /* Skip LSN is only supported in v15 and higher */ + if (pset.sversion >= 150000) + { + appendPQExpBuffer(&buf, + ", subskiplsn AS \"%s\"\n", + gettext_noop("Skip LSN")); + ncols++; + } + + appendPQExpBuffer(&buf, + ", pg_catalog.obj_description(oid, 'pg_subscription') AS \"%s\"\n", + gettext_noop("Description")); + ncols++; + + /* Conflict log destination is supported in v19 and higher */ + if (pset.sversion >= 190000) + { + appendPQExpBuffer(&buf, + ", subconflictlogdest AS \"%s\"\n", + gettext_noop("Conflict log destination")); + ncols++; } /* Only display subscriptions in current database. */ @@ -7240,13 +7330,148 @@ describeSubscriptions(const char *pattern, bool verbose) if (!res) return false; - myopt.title = _("List of subscriptions"); - myopt.translate_header = true; - myopt.translate_columns = translate_columns; - myopt.n_translate_columns = lengthof(translate_columns); + if (PQntuples(res) == 0) + { + if (!pset.quiet) + { + if (pattern) + pg_log_error("Did not find any subscription named \"%s\".", + pattern); + else + pg_log_error("Did not find any subscriptions."); + } - printQuery(res, &myopt, pset.queryFout, false, pset.logfile); + termPQExpBuffer(&buf); + PQclear(res); + return false; + } + + for (i = 0; i < PQntuples(res); i++) + { + const char align = 'l'; + char *subid = PQgetvalue(res, i, 0); + char *subname = PQgetvalue(res, i, 1); + int current_col = 2; + printTableOpt myopt = pset.popt.topt; + + initPQExpBuffer(&title); + printfPQExpBuffer(&title, _("Subscription %s"), subname); + printTableInit(&cont, &myopt, title.data, ncols, nrows); + + printTableAddHeader(&cont, gettext_noop("Owner"), true, align); + printTableAddHeader(&cont, gettext_noop("Enabled"), true, align); + printTableAddHeader(&cont, gettext_noop("Publication"), true, align); + + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + if (pset.sversion >= 140000) + { + printTableAddHeader(&cont, gettext_noop("Binary"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + printTableAddHeader(&cont, gettext_noop("Streaming"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + } + + if (pset.sversion >= 150000) + { + printTableAddHeader(&cont, gettext_noop("Two-phase commit"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + printTableAddHeader(&cont, gettext_noop("Disable on error"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + } + + if (pset.sversion >= 160000) + { + printTableAddHeader(&cont, gettext_noop("Origin"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + printTableAddHeader(&cont, gettext_noop("Password required"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + printTableAddHeader(&cont, gettext_noop("Run as owner?"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + } + + if (pset.sversion >= 170000) + { + printTableAddHeader(&cont, gettext_noop("Failover"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + } + + if (pset.sversion >= 190000) + { + printTableAddHeader(&cont, gettext_noop("Server"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + printTableAddHeader(&cont, gettext_noop("Retain dead tuples"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + printTableAddHeader(&cont, gettext_noop("Max retention duration"), + true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + printTableAddHeader(&cont, gettext_noop("Retention active"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + } + + printTableAddHeader(&cont, gettext_noop("Synchronous commit"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + printTableAddHeader(&cont, gettext_noop("Conninfo"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + if (pset.sversion >= 190000) + { + printTableAddHeader(&cont, gettext_noop("Receiver timeout"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + } + + if (pset.sversion >= 150000) + { + printTableAddHeader(&cont, gettext_noop("Skip LSN"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + } + + printTableAddHeader(&cont, gettext_noop("Description"), true, align); + printTableAddCell(&cont, PQgetvalue(res, i, current_col++), false, false); + + if (pset.sversion >= 190000) + { + char *logdest; + + printTableAddHeader(&cont, gettext_noop("Conflict log destination"), + true, align); + + logdest = PQgetvalue(res, i, current_col++); + + printTableAddCell(&cont, logdest, false, false); + + if (strcmp(logdest, "table") == 0 || + strcmp(logdest, "all") == 0) + { + char conflictlogtable[NAMEDATALEN + 32]; + + snprintf(conflictlogtable, + sizeof(conflictlogtable), + "pg_conflict.pg_conflict_log_%s", + subid); + + printTableAddFooter(&cont, _("Conflict log table:")); + printTableAddFooter(&cont, psprintf(" %s", conflictlogtable)); + } + } + + printTable(&cont, pset.queryFout, false, pset.logfile); + printTableCleanup(&cont); + + termPQExpBuffer(&title); + } + + termPQExpBuffer(&buf); PQclear(res); return true; } diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index 47fae5ceafb..15c6c685323 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -126,7 +126,10 @@ bool listPublications(const char *pattern); bool describePublications(const char *pattern); /* \dRs */ -bool describeSubscriptions(const char *pattern, bool verbose); +bool listSubscriptions(const char *pattern); + +/* \dRs+ */ +bool describeSubscriptions(const char *pattern); /* \dAc */ extern bool listOperatorClasses(const char *access_method_pattern, diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out index 85f9c60f449..a3dfd8210dd 100644 --- a/src/test/regress/expected/subscription.out +++ b/src/test/regress/expected/subscription.out @@ -124,18 +124,18 @@ CREATE SUBSCRIPTION regress_testsub4 CONNECTION 'dbname=regress_doesnotexist' PU 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. \dRs+ regress_testsub4 - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table -------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub4 | regress_subscription_user | f | {testpub} | f | parallel | d | f | none | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub4 + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | f | none | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub4 SET (origin = any); \dRs+ regress_testsub4 - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table -------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub4 | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub4 + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) DROP SUBSCRIPTION regress_testsub3; @@ -200,10 +200,10 @@ ALTER SUBSCRIPTION regress_testsub CONNECTION 'foobar'; ERROR: invalid connection string syntax: missing "=" after "foobar" in connection info string \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | test subscription | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | test subscription | log (1 row) ALTER SUBSCRIPTION regress_testsub SET PUBLICATION testpub2, testpub3 WITH (refresh = false); @@ -212,10 +212,10 @@ ALTER SUBSCRIPTION regress_testsub SET (slot_name = 'newname'); ALTER SUBSCRIPTION regress_testsub SET (password_required = false); ALTER SUBSCRIPTION regress_testsub SET (run_as_owner = true); \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+------------------------------+------------------+------------+-------------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub2,testpub3} | f | parallel | d | f | any | f | t | f | | f | 0 | f | off | dbname=regress_doesnotexist2 | -1 | 0/00000000 | test subscription | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+------------------------------+------------------+------------+-------------------+-------------------------- + regress_subscription_user | f | {testpub2,testpub3} | f | parallel | d | f | any | f | t | f | | f | 0 | f | off | dbname=regress_doesnotexist2 | -1 | 0/00000000 | test subscription | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (password_required = true); @@ -231,10 +231,10 @@ ERROR: unrecognized subscription parameter: "create_slot" -- ok ALTER SUBSCRIPTION regress_testsub SKIP (lsn = '0/12345'); \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+------------------------------+------------------+------------+-------------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub2,testpub3} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist2 | -1 | 0/00012345 | test subscription | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+------------------------------+------------------+------------+-------------------+-------------------------- + regress_subscription_user | f | {testpub2,testpub3} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist2 | -1 | 0/00012345 | test subscription | log (1 row) -- ok - with lsn = NONE @@ -243,10 +243,10 @@ ALTER SUBSCRIPTION regress_testsub SKIP (lsn = NONE); ALTER SUBSCRIPTION regress_testsub SKIP (lsn = '0/0'); ERROR: invalid WAL location (LSN): 0/0 \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+------------------------------+------------------+------------+-------------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub2,testpub3} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist2 | -1 | 0/00000000 | test subscription | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+------------------------------+------------------+------------+-------------------+-------------------------- + regress_subscription_user | f | {testpub2,testpub3} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist2 | -1 | 0/00000000 | test subscription | log (1 row) BEGIN; @@ -282,10 +282,10 @@ ALTER SUBSCRIPTION regress_testsub_foo SET (wal_receiver_timeout = '80s'); ALTER SUBSCRIPTION regress_testsub_foo SET (wal_receiver_timeout = 'foobar'); ERROR: invalid value for parameter "wal_receiver_timeout": "foobar" \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ----------------------+---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+------------------------------+------------------+------------+-------------------+--------------------------+-------------------- - regress_testsub_foo | regress_subscription_user | f | {testpub2,testpub3} | f | parallel | d | f | any | t | f | f | | f | 0 | f | local | dbname=regress_doesnotexist2 | 80s | 0/00000000 | test subscription | log | - + Subscription regress_testsub_foo + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+---------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+------------------------------+------------------+------------+-------------------+-------------------------- + regress_subscription_user | f | {testpub2,testpub3} | f | parallel | d | f | any | t | f | f | | f | 0 | f | local | dbname=regress_doesnotexist2 | 80s | 0/00000000 | test subscription | log (1 row) -- rename back to keep the rest simple @@ -314,19 +314,19 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB 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. \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | t | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | t | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (binary = false); ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) DROP SUBSCRIPTION regress_testsub; @@ -338,27 +338,27 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB 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. \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | on | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | on | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (streaming = parallel); \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (streaming = false); ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | off | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) -- fail - publication already exists @@ -373,10 +373,10 @@ ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub1, testpub2 WITH (refr ALTER SUBSCRIPTION regress_testsub ADD PUBLICATION testpub1, testpub2 WITH (refresh = false); ERROR: publication "testpub1" is already in subscription "regress_testsub" \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-----------------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub,testpub1,testpub2} | f | off | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-----------------------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub,testpub1,testpub2} | f | off | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) -- fail - publication used more than once @@ -391,10 +391,10 @@ ERROR: publication "testpub3" is not in subscription "regress_testsub" -- ok - delete publications ALTER SUBSCRIPTION regress_testsub DROP PUBLICATION testpub1, testpub2 WITH (refresh = false); \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | off | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) DROP SUBSCRIPTION regress_testsub; @@ -430,19 +430,19 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB 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. \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | p | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | p | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) -- we can alter streaming when two_phase enabled ALTER SUBSCRIPTION regress_testsub SET (streaming = true); \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | on | p | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | on | p | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); @@ -452,10 +452,10 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB 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. \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | on | p | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | on | p | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); @@ -468,18 +468,18 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB 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. \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (disable_on_error = true); \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | t | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | t | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); @@ -492,10 +492,10 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB 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. \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); @@ -509,19 +509,19 @@ NOTICE: max_retention_duration is ineffective when retain_dead_tuples is disabl 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. \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 1000 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 1000 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) -- ok ALTER SUBSCRIPTION regress_testsub SET (max_retention_duration = 0); \dRs+ - List of subscriptions - Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination | Conflict log table ------------------+---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+--------------------------+-------------------- - regress_testsub | regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log | - + Subscription regress_testsub + Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Server | Retain dead tuples | Max retention duration | Retention active | Synchronous commit | Conninfo | Receiver timeout | Skip LSN | Description | Conflict log destination +---------------------------+---------+-------------+--------+-----------+------------------+------------------+--------+-------------------+---------------+----------+--------+--------------------+------------------------+------------------+--------------------+-----------------------------+------------------+------------+-------------+-------------------------- + regress_subscription_user | f | {testpub} | f | parallel | d | f | any | t | f | f | | f | 0 | f | off | dbname=regress_doesnotexist | -1 | 0/00000000 | | log (1 row) ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); @@ -653,6 +653,41 @@ WHERE s.subname = 'regress_conflict_test1' AND a.attnum > 0 11 | local_conflicts (11 rows) +-- Changing the subscription owner should also update the owner +-- of the associated conflict log table. +ALTER SUBSCRIPTION regress_conflict_test1 owner to regress_subscription_user2; +SELECT pg_catalog.pg_get_userbyid(c.relowner) AS owner +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_subscription s + ON c.relname = 'pg_conflict_log_' || s.oid +WHERE s.subname = 'regress_conflict_test1'; + owner +---------------------------- + regress_subscription_user2 +(1 row) + +-- Verify that a non-superuser subscription owner can truncate, +-- delete from, and select from the associated conflict log table. +SET ROLE 'regress_subscription_user2'; +SELECT format('%I.%I', n.nspname, c.relname) AS conflict_log_table +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_namespace n + ON n.oid = c.relnamespace +JOIN pg_catalog.pg_subscription s + ON c.relname = 'pg_conflict_log_' || s.oid +WHERE s.subname = 'regress_conflict_test1' +\gset +TRUNCATE TABLE :conflict_log_table; +DELETE FROM :conflict_log_table; +SELECT COUNT(*) FROM :conflict_log_table; + count +------- + 0 +(1 row) + +RESET ROLE; +-- Restore the original subscription owner. +ALTER SUBSCRIPTION regress_conflict_test1 owner to regress_subscription_user; -- -- ALTER SUBSCRIPTION - conflict_log_destination state transitions -- diff --git a/src/test/regress/sql/subscription.sql b/src/test/regress/sql/subscription.sql index d155f24fdbb..5e0148941f4 100644 --- a/src/test/regress/sql/subscription.sql +++ b/src/test/regress/sql/subscription.sql @@ -475,6 +475,37 @@ JOIN pg_subscription s ON c.relname = 'pg_conflict_log_' || s.oid WHERE s.subname = 'regress_conflict_test1' AND a.attnum > 0 ORDER BY a.attnum; +-- Changing the subscription owner should also update the owner +-- of the associated conflict log table. +ALTER SUBSCRIPTION regress_conflict_test1 owner to regress_subscription_user2; +SELECT pg_catalog.pg_get_userbyid(c.relowner) AS owner +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_subscription s + ON c.relname = 'pg_conflict_log_' || s.oid +WHERE s.subname = 'regress_conflict_test1'; + +-- Verify that a non-superuser subscription owner can truncate, +-- delete from, and select from the associated conflict log table. +SET ROLE 'regress_subscription_user2'; + +SELECT format('%I.%I', n.nspname, c.relname) AS conflict_log_table +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_namespace n + ON n.oid = c.relnamespace +JOIN pg_catalog.pg_subscription s + ON c.relname = 'pg_conflict_log_' || s.oid +WHERE s.subname = 'regress_conflict_test1' +\gset + +TRUNCATE TABLE :conflict_log_table; +DELETE FROM :conflict_log_table; +SELECT COUNT(*) FROM :conflict_log_table; + +RESET ROLE; + +-- Restore the original subscription owner. +ALTER SUBSCRIPTION regress_conflict_test1 owner to regress_subscription_user; + -- -- ALTER SUBSCRIPTION - conflict_log_destination state transitions -- -- 2.43.0