28#include "catalog/pg_authid_d.h"
29#include "catalog/pg_database_d.h"
60#define SUBOPT_CONNECT 0x00000001
61#define SUBOPT_ENABLED 0x00000002
62#define SUBOPT_CREATE_SLOT 0x00000004
63#define SUBOPT_SLOT_NAME 0x00000008
64#define SUBOPT_COPY_DATA 0x00000010
65#define SUBOPT_SYNCHRONOUS_COMMIT 0x00000020
66#define SUBOPT_REFRESH 0x00000040
67#define SUBOPT_BINARY 0x00000080
68#define SUBOPT_STREAMING 0x00000100
69#define SUBOPT_TWOPHASE_COMMIT 0x00000200
70#define SUBOPT_DISABLE_ON_ERR 0x00000400
71#define SUBOPT_PASSWORD_REQUIRED 0x00000800
72#define SUBOPT_RUN_AS_OWNER 0x00001000
73#define SUBOPT_FAILOVER 0x00002000
74#define SUBOPT_RETAIN_DEAD_TUPLES 0x00004000
75#define SUBOPT_MAX_RETENTION_DURATION 0x00008000
76#define SUBOPT_LSN 0x00010000
77#define SUBOPT_ORIGIN 0x00020000
80#define IsSet(val, bits) (((val) & (bits)) == (bits))
121 List *publications,
bool copydata,
122 bool retain_dead_tuples,
124 Oid *subrel_local_oids,
125 int subrel_count,
char *
subname);
128 bool copydata,
char *origin,
129 Oid *subrel_local_oids,
137 bool slot_needs_update,
bool isTopLevel);
156 Assert(supported_opts != 0);
165 opts->connect =
true;
167 opts->enabled =
true;
169 opts->create_slot =
true;
171 opts->copy_data =
true;
173 opts->refresh =
true;
175 opts->binary =
false;
177 opts->streaming = LOGICALREP_STREAM_PARALLEL;
179 opts->twophase =
false;
181 opts->disableonerr =
false;
183 opts->passwordrequired =
true;
185 opts->runasowner =
false;
187 opts->failover =
false;
189 opts->retaindeadtuples =
false;
191 opts->maxretention = 0;
196 foreach(lc, stmt_options)
201 strcmp(defel->
defname,
"connect") == 0)
210 strcmp(defel->
defname,
"enabled") == 0)
219 strcmp(defel->
defname,
"create_slot") == 0)
228 strcmp(defel->
defname,
"slot_name") == 0)
237 if (strcmp(
opts->slot_name,
"none") == 0)
238 opts->slot_name = NULL;
243 strcmp(defel->
defname,
"copy_data") == 0)
252 strcmp(defel->
defname,
"synchronous_commit") == 0)
266 strcmp(defel->
defname,
"refresh") == 0)
275 strcmp(defel->
defname,
"binary") == 0)
284 strcmp(defel->
defname,
"streaming") == 0)
293 strcmp(defel->
defname,
"two_phase") == 0)
302 strcmp(defel->
defname,
"disable_on_error") == 0)
311 strcmp(defel->
defname,
"password_required") == 0)
320 strcmp(defel->
defname,
"run_as_owner") == 0)
329 strcmp(defel->
defname,
"failover") == 0)
338 strcmp(defel->
defname,
"retain_dead_tuples") == 0)
347 strcmp(defel->
defname,
"max_retention_duration") == 0)
356 strcmp(defel->
defname,
"origin") == 0)
375 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
376 errmsg(
"unrecognized origin value: \"%s\"",
opts->origin));
379 strcmp(defel->
defname,
"lsn") == 0)
388 if (strcmp(lsn_str,
"none") == 0)
398 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
399 errmsg(
"invalid WAL location (LSN): %s", lsn_str)));
407 (
errcode(ERRCODE_SYNTAX_ERROR),
408 errmsg(
"unrecognized subscription parameter: \"%s\"", defel->
defname)));
421 (
errcode(ERRCODE_SYNTAX_ERROR),
423 errmsg(
"%s and %s are mutually exclusive options",
424 "connect = false",
"enabled = true")));
426 if (
opts->create_slot &&
429 (
errcode(ERRCODE_SYNTAX_ERROR),
430 errmsg(
"%s and %s are mutually exclusive options",
431 "connect = false",
"create_slot = true")));
433 if (
opts->copy_data &&
436 (
errcode(ERRCODE_SYNTAX_ERROR),
437 errmsg(
"%s and %s are mutually exclusive options",
438 "connect = false",
"copy_data = true")));
441 opts->enabled =
false;
442 opts->create_slot =
false;
443 opts->copy_data =
false;
450 if (!
opts->slot_name &&
457 (
errcode(ERRCODE_SYNTAX_ERROR),
459 errmsg(
"%s and %s are mutually exclusive options",
460 "slot_name = NONE",
"enabled = true")));
463 (
errcode(ERRCODE_SYNTAX_ERROR),
465 errmsg(
"subscription with %s must also set %s",
466 "slot_name = NONE",
"enabled = false")));
469 if (
opts->create_slot)
473 (
errcode(ERRCODE_SYNTAX_ERROR),
475 errmsg(
"%s and %s are mutually exclusive options",
476 "slot_name = NONE",
"create_slot = true")));
479 (
errcode(ERRCODE_SYNTAX_ERROR),
481 errmsg(
"subscription with %s must also set %s",
482 "slot_name = NONE",
"create_slot = false")));
497 Oid tableRow[1] = {TEXTOID};
501 " pg_catalog.pg_publication t WHERE\n"
511 errmsg(
"could not receive list of publications from the publisher: %s",
514 publicationsCopy =
list_copy(publications);
542 errcode(ERRCODE_UNDEFINED_OBJECT),
543 errmsg_plural(
"publication %s does not exist on the publisher",
544 "publications %s do not exist on the publisher",
563 "publicationListToArray to array",
590 bool nulls[Natts_pg_subscription];
622 if (
opts.create_slot)
632 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
633 errmsg(
"permission denied to create subscription"),
634 errdetail(
"Only roles with privileges of the \"%s\" role may create subscriptions.",
635 "pg_create_subscription")));
654 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
655 errmsg(
"password_required=false is superuser-only"),
656 errhint(
"Subscriptions with the password_required option set to false may only be created or modified by the superuser.")));
662#ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
663 if (strncmp(
stmt->subname,
"regress_", 8) != 0)
664 elog(
WARNING,
"subscriptions created by regression test cases should have names starting with \"regress_\"");
676 errmsg(
"subscription \"%s\" already exists",
685 opts.retaindeadtuples,
opts.retaindeadtuples,
686 (
opts.maxretention > 0));
689 opts.slot_name == NULL)
693 if (
opts.synchronous_commit == NULL)
694 opts.synchronous_commit =
"off";
696 conninfo =
stmt->conninfo;
697 publications =
stmt->publication;
707 memset(nulls,
false,
sizeof(nulls));
710 Anum_pg_subscription_oid);
714 values[Anum_pg_subscription_subname - 1] =
720 values[Anum_pg_subscription_subtwophasestate - 1] =
722 LOGICALREP_TWOPHASE_STATE_PENDING :
723 LOGICALREP_TWOPHASE_STATE_DISABLED);
728 values[Anum_pg_subscription_subretaindeadtuples - 1] =
730 values[Anum_pg_subscription_submaxretention - 1] =
732 values[Anum_pg_subscription_subretentionactive - 1] =
734 values[Anum_pg_subscription_subconninfo - 1] =
737 values[Anum_pg_subscription_subslotname - 1] =
740 nulls[Anum_pg_subscription_subslotname - 1] =
true;
741 values[Anum_pg_subscription_subsynccommit - 1] =
743 values[Anum_pg_subscription_subpublications - 1] =
745 values[Anum_pg_subscription_suborigin - 1] =
777 bool must_use_password;
785 (
errcode(ERRCODE_CONNECTION_FAILURE),
786 errmsg(
"subscription \"%s\" could not connect to the publisher: %s",
791 bool has_tables =
false;
799 NULL, 0,
stmt->subname);
802 NULL, 0,
stmt->subname);
804 if (
opts.retaindeadtuples)
811 relation_state =
opts.copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY;
831 has_tables |= (relkind != RELKIND_SEQUENCE);
845 if (
opts.create_slot)
847 bool twophase_enabled =
false;
867 if (
opts.twophase && !
opts.copy_data && has_tables)
868 twophase_enabled =
true;
873 if (twophase_enabled)
877 (
errmsg(
"created replication slot \"%s\" on publisher",
889 (
errmsg(
"subscription was created, but is not connected"),
890 errhint(
"To initiate replication, you must manually create the replication slot, enable the subscription, and alter the subscription to refresh publications.")));
906 if (
opts.enabled ||
opts.retaindeadtuples)
918 List *validate_publications)
922 Oid *pubrel_local_oids;
925 Oid *subrel_local_oids;
926 Oid *subseq_local_oids;
933 typedef struct SubRemoveRels
940 bool must_use_password;
951 (
errcode(ERRCODE_CONNECTION_FAILURE),
952 errmsg(
"subscription \"%s\" could not connect to the publisher: %s",
957 if (validate_publications)
976 subrel_local_oids =
palloc(subrel_count *
sizeof(
Oid));
977 subseq_local_oids =
palloc(subrel_count *
sizeof(
Oid));
978 foreach(lc, subrel_states)
983 subseq_local_oids[seq_count++] = relstate->
relid;
985 subrel_local_oids[tbl_count++] = relstate->
relid;
991 subrel_local_oids, tbl_count,
997 subseq_local_oids, seq_count,
1024 pubrel_local_oids[off++] = relid;
1026 if (!bsearch(&relid, subrel_local_oids,
1028 !bsearch(&relid, subseq_local_oids,
1032 copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY,
1036 relkind == RELKIND_SEQUENCE ?
"sequence" :
"table",
1047 for (off = 0; off < tbl_count; off++)
1049 Oid relid = subrel_local_oids[off];
1051 if (!bsearch(&relid, pubrel_local_oids,
1056 SubRemoveRels *remove_rel =
palloc(
sizeof(SubRemoveRels));
1080 remove_rel->relid = relid;
1081 remove_rel->state =
state;
1083 sub_remove_rels =
lappend(sub_remove_rels, remove_rel);
1091 if (
state != SUBREL_STATE_READY)
1106 sizeof(originname));
1125 if (rel->state != SUBREL_STATE_READY &&
1126 rel->state != SUBREL_STATE_SYNCDONE)
1141 syncslotname,
sizeof(syncslotname));
1150 for (off = 0; off < seq_count; off++)
1152 Oid relid = subseq_local_oids[off];
1154 if (!bsearch(&relid, pubrel_local_oids,
1167 errmsg_internal(
"sequence \"%s.%s\" removed from subscription \"%s\"",
1192 bool must_use_password;
1203 errcode(ERRCODE_CONNECTION_FAILURE),
1204 errmsg(
"subscription \"%s\" could not connect to the publisher: %s",
1209 List *subrel_states;
1218 Oid relid = subrel->relid;
1223 errmsg_internal(
"sequence \"%s.%s\" of subscription \"%s\" set to INIT state",
1242 bool slot_needs_update,
bool isTopLevel)
1245 strcmp(
option,
"two_phase") == 0 ||
1246 strcmp(
option,
"retain_dead_tuples") == 0);
1252 Assert(!slot_needs_update || strcmp(
option,
"retain_dead_tuples") != 0);
1300 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1301 errmsg(
"cannot set option \"%s\" for enabled subscription",
1304 if (slot_needs_update)
1314 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1315 errmsg(
"cannot set option \"%s\" for a subscription that does not have a slot name",
1336 bool nulls[Natts_pg_subscription];
1337 bool replaces[Natts_pg_subscription];
1341 bool update_tuple =
false;
1342 bool update_failover =
false;
1343 bool update_two_phase =
false;
1344 bool check_pub_rdt =
false;
1345 bool retain_dead_tuples;
1347 bool retention_active;
1362 (
errcode(ERRCODE_UNDEFINED_OBJECT),
1363 errmsg(
"subscription \"%s\" does not exist",
1387 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1388 errmsg(
"password_required=false is superuser-only"),
1389 errhint(
"Subscriptions with the password_required option set to false may only be created or modified by the superuser.")));
1396 memset(nulls,
false,
sizeof(nulls));
1397 memset(replaces,
false,
sizeof(replaces));
1414 supported_opts, &
opts);
1427 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1428 errmsg(
"cannot set %s for enabled subscription",
1429 "slot_name = NONE")));
1432 values[Anum_pg_subscription_subslotname - 1] =
1435 nulls[Anum_pg_subscription_subslotname - 1] =
true;
1436 replaces[Anum_pg_subscription_subslotname - 1] =
true;
1439 if (
opts.synchronous_commit)
1441 values[Anum_pg_subscription_subsynccommit - 1] =
1443 replaces[Anum_pg_subscription_subsynccommit - 1] =
true;
1448 values[Anum_pg_subscription_subbinary - 1] =
1450 replaces[Anum_pg_subscription_subbinary - 1] =
true;
1455 values[Anum_pg_subscription_substream - 1] =
1457 replaces[Anum_pg_subscription_substream - 1] =
true;
1462 values[Anum_pg_subscription_subdisableonerr - 1]
1464 replaces[Anum_pg_subscription_subdisableonerr - 1]
1473 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1474 errmsg(
"password_required=false is superuser-only"),
1475 errhint(
"Subscriptions with the password_required option set to false may only be created or modified by the superuser.")));
1477 values[Anum_pg_subscription_subpasswordrequired - 1]
1479 replaces[Anum_pg_subscription_subpasswordrequired - 1]
1485 values[Anum_pg_subscription_subrunasowner - 1] =
1487 replaces[Anum_pg_subscription_subrunasowner - 1] =
true;
1499 update_two_phase = !
opts.twophase;
1509 if (update_two_phase &&
1512 (
errcode(ERRCODE_SYNTAX_ERROR),
1513 errmsg(
"\"slot_name\" and \"two_phase\" cannot be altered at the same time")));
1528 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1529 errmsg(
"cannot alter \"two_phase\" when logical replication worker is still running"),
1530 errhint(
"Try again after some time.")));
1538 if (update_two_phase &&
1542 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1543 errmsg(
"cannot disable \"two_phase\" when prepared transactions exist"),
1544 errhint(
"Resolve these transactions and try again.")));
1547 values[Anum_pg_subscription_subtwophasestate - 1] =
1549 LOGICALREP_TWOPHASE_STATE_PENDING :
1550 LOGICALREP_TWOPHASE_STATE_DISABLED);
1551 replaces[Anum_pg_subscription_subtwophasestate - 1] =
true;
1561 update_failover =
true;
1566 values[Anum_pg_subscription_subfailover - 1] =
1568 replaces[Anum_pg_subscription_subfailover - 1] =
true;
1573 values[Anum_pg_subscription_subretaindeadtuples - 1] =
1575 replaces[Anum_pg_subscription_subretaindeadtuples - 1] =
true;
1593 values[Anum_pg_subscription_subretentionactive - 1] =
1595 replaces[Anum_pg_subscription_subretentionactive - 1] =
true;
1597 retention_active =
opts.retaindeadtuples;
1612 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1613 errmsg(
"cannot alter retain_dead_tuples when logical replication worker is still running"),
1614 errhint(
"Try again after some time.")));
1624 check_pub_rdt =
opts.retaindeadtuples;
1625 retain_dead_tuples =
opts.retaindeadtuples;
1630 values[Anum_pg_subscription_submaxretention - 1] =
1632 replaces[Anum_pg_subscription_submaxretention - 1] =
true;
1634 max_retention =
opts.maxretention;
1647 (max_retention > 0));
1651 values[Anum_pg_subscription_suborigin - 1] =
1653 replaces[Anum_pg_subscription_suborigin - 1] =
true;
1660 check_pub_rdt = retain_dead_tuples &&
1663 origin =
opts.origin;
1666 update_tuple =
true;
1678 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1679 errmsg(
"cannot enable subscription that does not have a slot name")));
1690 values[Anum_pg_subscription_subenabled - 1] =
1692 replaces[Anum_pg_subscription_subenabled - 1] =
true;
1697 update_tuple =
true;
1716 values[Anum_pg_subscription_subconninfo - 1] =
1718 replaces[Anum_pg_subscription_subconninfo - 1] =
true;
1719 update_tuple =
true;
1733 supported_opts, &
opts);
1735 values[Anum_pg_subscription_subpublications - 1] =
1737 replaces[Anum_pg_subscription_subpublications - 1] =
true;
1739 update_tuple =
true;
1746 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1747 errmsg(
"ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"),
1748 errhint(
"Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false).")));
1756 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1757 errmsg(
"ALTER SUBSCRIPTION with refresh and copy_data is not allowed when two_phase is enabled"),
1758 errhint(
"Use ALTER SUBSCRIPTION ... SET PUBLICATION with refresh = false, or with copy_data = false, or use DROP/CREATE SUBSCRIPTION.")));
1780 supported_opts, &
opts);
1783 values[Anum_pg_subscription_subpublications - 1] =
1785 replaces[Anum_pg_subscription_subpublications - 1] =
true;
1787 update_tuple =
true;
1793 List *validate_publications = (isadd) ?
stmt->publication : NULL;
1797 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1798 errmsg(
"ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"),
1802 "ALTER SUBSCRIPTION ... ADD PUBLICATION ... WITH (refresh = false)" :
1803 "ALTER SUBSCRIPTION ... DROP PUBLICATION ... WITH (refresh = false)")));
1811 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1812 errmsg(
"ALTER SUBSCRIPTION with refresh and copy_data is not allowed when two_phase is enabled"),
1814 errhint(
"Use %s with refresh = false, or with copy_data = false, or use DROP/CREATE SUBSCRIPTION.",
1816 "ALTER SUBSCRIPTION ... ADD PUBLICATION" :
1817 "ALTER SUBSCRIPTION ... DROP PUBLICATION")));
1825 validate_publications);
1835 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1836 errmsg(
"%s is not allowed for disabled subscriptions",
1837 "ALTER SUBSCRIPTION ... REFRESH PUBLICATION")));
1861 (
errcode(ERRCODE_SYNTAX_ERROR),
1862 errmsg(
"ALTER SUBSCRIPTION ... REFRESH PUBLICATION with copy_data is not allowed when two_phase is enabled"),
1863 errhint(
"Use ALTER SUBSCRIPTION ... REFRESH PUBLICATION with copy_data = false, or use DROP/CREATE SUBSCRIPTION.")));
1876 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1877 errmsg(
"%s is not allowed for disabled subscriptions",
1878 "ALTER SUBSCRIPTION ... REFRESH SEQUENCES"));
1903 originname,
sizeof(originname));
1910 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1911 errmsg(
"skip WAL location (LSN %X/%08X) must be greater than origin LSN %X/%08X",
1917 replaces[Anum_pg_subscription_subskiplsn - 1] =
true;
1919 update_tuple =
true;
1924 elog(
ERROR,
"unrecognized ALTER SUBSCRIPTION kind %d",
1948 if (update_failover || update_two_phase || check_pub_rdt)
1950 bool must_use_password;
1963 true,
true, must_use_password, sub->
name,
1967 (
errcode(ERRCODE_CONNECTION_FAILURE),
1968 errmsg(
"subscription \"%s\" could not connect to the publisher: %s",
1973 if (retain_dead_tuples)
1977 retain_dead_tuples, origin, NULL, 0,
1980 if (update_failover || update_two_phase)
1982 update_failover ? &
opts.failover : NULL,
1983 update_two_phase ? &
opts.twophase : NULL);
2027 bool must_use_password;
2044 if (!
stmt->missing_ok)
2046 (
errcode(ERRCODE_UNDEFINED_OBJECT),
2047 errmsg(
"subscription \"%s\" does not exist",
2051 (
errmsg(
"subscription \"%s\" does not exist, skipping",
2059 subowner = form->subowner;
2060 must_use_password = !
superuser_arg(subowner) && form->subpasswordrequired;
2078 Anum_pg_subscription_subname);
2083 Anum_pg_subscription_subconninfo);
2088 Anum_pg_subscription_subslotname, &isnull);
2133 foreach(lc, subworkers)
2161 foreach(lc, rstates)
2178 sizeof(originname));
2202 if (!slotname && rstates ==
NIL)
2240 foreach(lc, rstates)
2261 if (rstate->
state != SUBREL_STATE_SYNCDONE)
2266 sizeof(syncslotname));
2318 (
errmsg(
"dropped replication slot \"%s\" on publisher",
2323 res->
sqlstate == ERRCODE_UNDEFINED_OBJECT)
2327 (
errmsg(
"could not drop replication slot \"%s\" on publisher: %s",
2328 slotname, res->
err)));
2334 (
errcode(ERRCODE_CONNECTION_FAILURE),
2335 errmsg(
"could not drop replication slot \"%s\" on publisher: %s",
2336 slotname, res->
err)));
2359 if (form->subowner == newOwnerId)
2370 if (!form->subpasswordrequired && !
superuser())
2372 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2373 errmsg(
"password_required=false is superuser-only"),
2374 errhint(
"Subscriptions with the password_required option set to false may only be created or modified by the superuser.")));
2392 form->subowner = newOwnerId;
2427 (
errcode(ERRCODE_UNDEFINED_OBJECT),
2428 errmsg(
"subscription \"%s\" does not exist",
name)));
2459 (
errcode(ERRCODE_UNDEFINED_OBJECT),
2460 errmsg(
"subscription with OID %u does not exist", subid)));
2494 bool copydata,
bool retain_dead_tuples,
2495 char *origin,
Oid *subrel_local_oids,
2496 int subrel_count,
char *
subname)
2501 Oid tableRow[1] = {TEXTOID};
2505 bool check_table_sync;
2506 bool origin_none = origin &&
2514 check_rdt = retain_dead_tuples && !origin_none;
2520 check_table_sync = copydata && origin_none;
2523 Assert(!(check_rdt && check_table_sync));
2526 if (!check_rdt && !check_table_sync)
2531 "SELECT DISTINCT P.pubname AS pubname\n"
2532 "FROM pg_publication P,\n"
2533 " LATERAL pg_get_publication_tables(P.pubname) GPT\n"
2534 " JOIN pg_subscription_rel PS ON (GPT.relid = PS.srrelid OR"
2535 " GPT.relid IN (SELECT relid FROM pg_partition_ancestors(PS.srrelid) UNION"
2536 " SELECT relid FROM pg_partition_tree(PS.srrelid))),\n"
2537 " pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)\n"
2538 "WHERE C.oid = GPT.relid AND P.pubname IN (");
2551 if (check_table_sync)
2553 for (
i = 0;
i < subrel_count;
i++)
2555 Oid relid = subrel_local_oids[
i];
2559 appendStringInfo(&cmd,
"AND NOT (N.nspname = '%s' AND C.relname = '%s')\n",
2560 schemaname, tablename);
2569 (
errcode(ERRCODE_CONNECTION_FAILURE),
2570 errmsg(
"could not receive list of replicated tables from the publisher: %s",
2607 if (check_table_sync)
2609 appendStringInfo(err_msg,
_(
"subscription \"%s\" requested copy_data with origin = NONE but might copy data that had a different origin"),
2611 appendStringInfoString(err_hint,
_(
"Verify that initial data copied from the publisher tables did not come from other origins."));
2615 appendStringInfo(err_msg,
_(
"subscription \"%s\" enabled retain_dead_tuples but might not reliably detect conflicts for changes from different origins"),
2621 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2623 errdetail_plural(
"The subscription subscribes to a publication (%s) that contains tables that are written to by other subscriptions.",
2624 "The subscription subscribes to publications (%s) that contain tables that are written to by other subscriptions.",
2640 bool copydata,
char *origin,
2641 Oid *subrel_local_oids,
int subrel_count,
2647 Oid tableRow[1] = {TEXTOID};
2655 if (!copydata ||
pg_strcasecmp(origin, LOGICALREP_ORIGIN_NONE) != 0)
2660 "SELECT DISTINCT P.pubname AS pubname\n"
2661 "FROM pg_publication P,\n"
2662 " LATERAL pg_get_publication_sequences(P.pubname) GPS\n"
2663 " JOIN pg_subscription_rel PS ON (GPS.relid = PS.srrelid),\n"
2664 " pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)\n"
2665 "WHERE C.oid = GPS.relid AND P.pubname IN (");
2676 for (
int i = 0;
i < subrel_count;
i++)
2678 Oid relid = subrel_local_oids[
i];
2683 "AND NOT (N.nspname = '%s' AND C.relname = '%s')\n",
2684 schemaname, seqname);
2692 (
errcode(ERRCODE_CONNECTION_FAILURE),
2693 errmsg(
"could not receive list of replicated sequences from the publisher: %s",
2724 appendStringInfo(err_msg,
_(
"subscription \"%s\" requested copy_data with origin = NONE but might copy data that had a different origin"),
2726 appendStringInfoString(err_hint,
_(
"Verify that initial data copied from the publisher sequences did not come from other origins."));
2729 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2731 errdetail_plural(
"The subscription subscribes to a publication (%s) that contains sequences that are written to by other subscriptions.",
2732 "The subscription subscribes to publications (%s) that contain sequences that are written to by other subscriptions.",
2756 Oid RecoveryRow[1] = {BOOLOID};
2759 bool remote_in_recovery;
2763 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2764 errmsg(
"cannot enable retain_dead_tuples if the publisher is running a version earlier than PostgreSQL 19"));
2770 (
errcode(ERRCODE_CONNECTION_FAILURE),
2771 errmsg(
"could not obtain recovery progress from the publisher: %s",
2776 elog(
ERROR,
"failed to fetch tuple for the recovery progress");
2780 if (remote_in_recovery)
2782 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2783 errmsg(
"cannot enable retain_dead_tuples if the publisher is in recovery."));
2815 int elevel_for_sub_disabled,
2816 bool retain_dead_tuples,
bool retention_active,
2817 bool max_retention_set)
2820 elevel_for_sub_disabled ==
WARNING);
2822 if (retain_dead_tuples)
2826 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2827 errmsg(
"\"wal_level\" is insufficient to create the replication slot required by retain_dead_tuples"),
2828 errhint(
"\"wal_level\" must be set to \"replica\" or \"logical\" at server start."));
2832 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2833 errmsg(
"commit timestamp and origin data required for detecting conflicts won't be retained"),
2834 errhint(
"Consider setting \"%s\" to true.",
2835 "track_commit_timestamp"));
2837 if (sub_disabled && retention_active)
2838 ereport(elevel_for_sub_disabled,
2839 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2840 errmsg(
"deleted rows to detect conflicts would not be removed until the subscription is enabled"),
2841 (elevel_for_sub_disabled >
NOTICE)
2842 ?
errhint(
"Consider setting %s to false.",
2843 "retain_dead_tuples") : 0);
2845 else if (max_retention_set)
2848 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2849 errmsg(
"max_retention_duration is ineffective when retain_dead_tuples is disabled"));
2861 if (
equal(relinfo->rv, rv))
2887 int column_count = check_columnlist ? 4 : 3;
2898 tableRow[3] = INT2VECTOROID;
2913 appendStringInfo(&cmd,
"SELECT DISTINCT n.nspname, c.relname, c.relkind, gpt.attrs\n"
2914 " FROM pg_class c\n"
2915 " JOIN pg_namespace n ON n.oid = c.relnamespace\n"
2916 " JOIN ( SELECT (pg_get_publication_tables(VARIADIC array_agg(pubname::text))).*\n"
2917 " FROM pg_publication\n"
2918 " WHERE pubname IN ( %s )) AS gpt\n"
2919 " ON gpt.relid = c.oid\n",
2926 " SELECT DISTINCT s.schemaname, s.sequencename, " CppAsString2(RELKIND_SEQUENCE)
"::\"char\" AS relkind, NULL::int2vector AS attrs\n"
2927 " FROM pg_catalog.pg_publication_sequences s\n"
2928 " WHERE s.pubname IN ( %s )",
2933 tableRow[3] = NAMEARRAYOID;
2937 if (check_columnlist)
2941 " WHERE t.pubname IN ( %s )",
2952 (
errcode(ERRCODE_CONNECTION_FAILURE),
2953 errmsg(
"could not receive list of replicated tables from the publisher: %s",
2976 if (relkind != RELKIND_SEQUENCE &&
2980 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2981 errmsg(
"cannot use different column lists for table \"%s.%s\" in different publications",
2984 relationlist =
lappend(relationlist, relinfo);
2992 return relationlist;
3005 foreach(lc, rstates)
3018 if (rstate->
state != SUBREL_STATE_SYNCDONE)
3023 sizeof(syncslotname));
3024 elog(
WARNING,
"could not drop tablesync replication slot \"%s\"",
3030 (
errcode(ERRCODE_CONNECTION_FAILURE),
3031 errmsg(
"could not connect to publisher when attempting to drop replication slot \"%s\": %s",
3034 errhint(
"Use %s to disable the subscription, and then use %s to disassociate it from the slot.",
3035 "ALTER SUBSCRIPTION ... DISABLE",
3036 "ALTER SUBSCRIPTION ... SET (slot_name = NONE)")));
3050 foreach(cell, publist)
3055 foreach(pcell, publist)
3062 if (strcmp(
name, pname) == 0)
3065 errmsg(
"publication name \"%s\" used more than once",
3093 foreach(lc, newpublist)
3099 foreach(lc2, oldpublist)
3103 if (strcmp(
name, pubname) == 0)
3109 errmsg(
"publication \"%s\" is already in subscription \"%s\"",
3118 if (addpub && !found)
3120 else if (!addpub && !found)
3122 (
errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3123 errmsg(
"publication \"%s\" is not in subscription \"%s\"",
3133 (
errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3134 errmsg(
"cannot drop all the publications from a subscription")));
3150 return LOGICALREP_STREAM_ON;
3161 return LOGICALREP_STREAM_OFF;
3163 return LOGICALREP_STREAM_ON;
3179 return LOGICALREP_STREAM_OFF;
3182 return LOGICALREP_STREAM_ON;
3184 return LOGICALREP_STREAM_PARALLEL;
3190 (
errcode(ERRCODE_SYNTAX_ERROR),
3191 errmsg(
"%s requires a Boolean value or \"parallel\"",
3193 return LOGICALREP_STREAM_OFF;
bool has_privs_of_role(Oid member, Oid role)
void check_can_set_role(Oid member, Oid role)
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
void LogicalRepWorkersWakeupAtCommit(Oid subid)
void ReplicationOriginNameForLogicalRep(Oid suboid, Oid relid, char *originname, Size szoriginname)
static Datum values[MAXATTR]
#define CStringGetTextDatum(s)
#define TextDatumGetCString(d)
#define OidIsValid(objectId)
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
bool track_commit_timestamp
int32 defGetInt32(DefElem *def)
char * defGetString(DefElem *def)
bool defGetBoolean(DefElem *def)
void errorConflictingDefElem(DefElem *defel, ParseState *pstate)
void load_file(const char *filename, bool restricted)
int errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
int errmsg_internal(const char *fmt,...)
int errdetail(const char *fmt,...)
int errhint_internal(const char *fmt,...)
int errdetail_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
bool equal(const void *a, const void *b)
void err(int eval, const char *fmt,...)
void EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
void CheckSubscriptionRelkind(char localrelkind, char remoterelkind, const char *nspname, const char *relname)
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
const TupleTableSlotOps TTSOpsMinimalTuple
#define palloc_object(type)
#define DirectFunctionCall1(func, arg1)
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Assert(PointerIsAligned(start, uint64))
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
void heap_freetuple(HeapTuple htup)
#define HeapTupleIsValid(tuple)
static void * GETSTRUCT(const HeapTupleData *tuple)
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
if(TABLE==NULL||TABLE_index==NULL)
List * logicalrep_workers_find(Oid subid, bool only_running, bool acquire_lock)
void ApplyLauncherWakeupAtCommit(void)
void logicalrep_worker_stop(LogicalRepWorkerType wtype, Oid subid, Oid relid)
void ApplyLauncherForgetWorkerStartTime(Oid subid)
List * lappend(List *list, void *datum)
List * list_delete(List *list, void *datum)
List * list_append_unique(List *list, void *datum)
List * list_copy(const List *oldlist)
void list_free(List *list)
void LockSharedObject(Oid classid, Oid objid, uint16 objsubid, LOCKMODE lockmode)
#define AccessExclusiveLock
char * get_rel_name(Oid relid)
char * get_database_name(Oid dbid)
char get_rel_relkind(Oid relid)
Oid get_rel_namespace(Oid relid)
char * get_namespace_name(Oid nspid)
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
char * pstrdup(const char *in)
void pfree(void *pointer)
MemoryContext CurrentMemoryContext
void MemoryContextDelete(MemoryContext context)
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
Datum namein(PG_FUNCTION_ARGS)
#define RangeVarGetRelid(relation, lockmode, missing_ok)
#define InvokeObjectPostCreateHook(classId, objectId, subId)
#define InvokeObjectPostAlterHook(classId, objectId, subId)
#define InvokeObjectDropHook(classId, objectId, subId)
#define ObjectAddressSet(addr, class_id, object_id)
int oid_cmp(const void *p1, const void *p2)
RepOriginId replorigin_by_name(const char *roname, bool missing_ok)
RepOriginId replorigin_create(const char *roname)
XLogRecPtr replorigin_get_progress(RepOriginId node, bool flush)
void replorigin_drop_by_name(const char *name, bool missing_ok, bool nowait)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
@ ALTER_SUBSCRIPTION_REFRESH_PUBLICATION
@ ALTER_SUBSCRIPTION_ENABLED
@ ALTER_SUBSCRIPTION_DROP_PUBLICATION
@ ALTER_SUBSCRIPTION_SET_PUBLICATION
@ ALTER_SUBSCRIPTION_REFRESH_SEQUENCES
@ ALTER_SUBSCRIPTION_SKIP
@ ALTER_SUBSCRIPTION_OPTIONS
@ ALTER_SUBSCRIPTION_CONNECTION
@ ALTER_SUBSCRIPTION_ADD_PUBLICATION
static AmcheckOptions opts
static int server_version
static int list_length(const List *l)
#define foreach_delete_current(lst, var_or_cell)
#define foreach_ptr(type, var, lst)
Datum pg_lsn_in(PG_FUNCTION_ARGS)
static Datum LSNGetDatum(XLogRecPtr X)
static XLogRecPtr DatumGetLSN(Datum X)
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, int32 objectSubId)
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
void UpdateSubscriptionRelState(Oid subid, Oid relid, char state, XLogRecPtr sublsn, bool already_locked)
void RemoveSubscriptionRel(Oid subid, Oid relid)
char GetSubscriptionRelState(Oid subid, Oid relid, XLogRecPtr *sublsn)
void GetPublicationsStr(List *publications, StringInfo dest, bool quote_literal)
void AddSubscriptionRelState(Oid subid, Oid relid, char state, XLogRecPtr sublsn, bool retain_lock)
Subscription * GetSubscription(Oid subid, bool missing_ok)
List * GetSubscriptionRelations(Oid subid, bool tables, bool sequences, bool not_ready)
FormData_pg_subscription * Form_pg_subscription
void pgstat_drop_subscription(Oid subid)
void pgstat_create_subscription(Oid subid)
int pg_strcasecmp(const char *s1, const char *s2)
#define qsort(a, b, c, d)
static bool DatumGetBool(Datum X)
static Datum PointerGetDatum(const void *X)
static Name DatumGetName(Datum X)
static Datum BoolGetDatum(bool X)
static Datum ObjectIdGetDatum(Oid X)
static char DatumGetChar(Datum X)
static Datum CStringGetDatum(const char *X)
static Datum Int32GetDatum(int32 X)
static Datum CharGetDatum(char X)
#define RelationGetDescr(relation)
const char * quote_identifier(const char *ident)
bool ReplicationSlotValidateName(const char *name, bool allow_reserved_name, int elevel)
#define ERRCODE_DUPLICATE_OBJECT
void destroyStringInfo(StringInfo str)
StringInfo makeStringInfo(void)
void appendStringInfo(StringInfo str, const char *fmt,...)
void appendStringInfoString(StringInfo str, const char *s)
void appendStringInfoChar(StringInfo str, char ch)
void initStringInfo(StringInfo str)
LogicalRepWorkerType type
char * synchronous_commit
Tuplestorestate * tuplestore
void DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
char defGetStreamingMode(DefElem *def)
#define SUBOPT_CREATE_SLOT
#define SUBOPT_PASSWORD_REQUIRED
ObjectAddress CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt, bool isTopLevel)
#define SUBOPT_SYNCHRONOUS_COMMIT
static void check_duplicates_in_publist(List *publist, Datum *datums)
static void CheckAlterSubOption(Subscription *sub, const char *option, bool slot_needs_update, bool isTopLevel)
#define SUBOPT_RETAIN_DEAD_TUPLES
static Datum publicationListToArray(List *publist)
static void check_publications_origin_sequences(WalReceiverConn *wrconn, List *publications, bool copydata, char *origin, Oid *subrel_local_oids, int subrel_count, char *subname)
static void parse_subscription_options(ParseState *pstate, List *stmt_options, bits32 supported_opts, SubOpts *opts)
static void check_publications(WalReceiverConn *wrconn, List *publications)
#define SUBOPT_RUN_AS_OWNER
static List * fetch_relation_list(WalReceiverConn *wrconn, List *publications)
#define SUBOPT_TWOPHASE_COMMIT
static void AlterSubscription_refresh(Subscription *sub, bool copy_data, List *validate_publications)
static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err)
#define SUBOPT_DISABLE_ON_ERR
void CheckSubDeadTupleRetention(bool check_guc, bool sub_disabled, int elevel_for_sub_disabled, bool retain_dead_tuples, bool retention_active, bool max_retention_set)
static void AlterSubscription_refresh_seq(Subscription *sub)
static void AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
ObjectAddress AlterSubscriptionOwner(const char *name, Oid newOwnerId)
struct PublicationRelKind PublicationRelKind
void ReplicationSlotDropAtPubNode(WalReceiverConn *wrconn, char *slotname, bool missing_ok)
void AlterSubscriptionOwner_oid(Oid subid, Oid newOwnerId)
#define SUBOPT_MAX_RETENTION_DURATION
static List * merge_publications(List *oldpublist, List *newpublist, bool addpub, const char *subname)
static void check_publications_origin_tables(WalReceiverConn *wrconn, List *publications, bool copydata, bool retain_dead_tuples, char *origin, Oid *subrel_local_oids, int subrel_count, char *subname)
static bool list_member_rangevar(const List *list, RangeVar *rv)
static void check_pub_dead_tuple_retention(WalReceiverConn *wrconn)
ObjectAddress AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt, bool isTopLevel)
bool superuser_arg(Oid roleid)
void ReleaseSysCache(HeapTuple tuple)
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
#define SearchSysCacheCopy1(cacheId, key1)
#define SearchSysCacheCopy2(cacheId, key1, key2)
#define GetSysCacheOid2(cacheId, oidcol, key1, key2)
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)
void ReplicationSlotNameForTablesync(Oid suboid, Oid relid, char *syncslotname, Size szslot)
void UpdateTwoPhaseState(Oid suboid, char new_state)
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
bool LookupGXactBySubid(Oid subid)
String * makeString(char *str)
static WalReceiverConn * wrconn
#define walrcv_connect(conninfo, replication, logical, must_use_password, appname, err)
#define walrcv_create_slot(conn, slotname, temporary, two_phase, failover, snapshot_action, lsn)
static void walrcv_clear_result(WalRcvExecResult *walres)
#define walrcv_server_version(conn)
#define walrcv_check_conninfo(conninfo, must_use_password)
#define walrcv_alter_slot(conn, slotname, failover, two_phase)
#define walrcv_exec(conn, exec, nRetTypes, retTypes)
#define walrcv_disconnect(conn)
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
#define LSN_FORMAT_ARGS(lsn)
#define XLogRecPtrIsInvalid(r)
#define InvalidXLogRecPtr