/* Start out with cleared opts. */
memset(opts, 0, sizeof(SubOpts));
/* caller must expect some option */
Assert(supported_opts != 0);
/* If connect option is supported, these others also need to be. */
Assert(!IsSet(supported_opts, SUBOPT_CONNECT) ||
IsSet(supported_opts, SUBOPT_ENABLED | SUBOPT_CREATE_SLOT |
SUBOPT_COPY_DATA));
/* Set default values for the supported options. */ if (IsSet(supported_opts, SUBOPT_CONNECT))
opts->connect = true; if (IsSet(supported_opts, SUBOPT_ENABLED))
opts->enabled = true; if (IsSet(supported_opts, SUBOPT_CREATE_SLOT))
opts->create_slot = true; if (IsSet(supported_opts, SUBOPT_COPY_DATA))
opts->copy_data = true; if (IsSet(supported_opts, SUBOPT_REFRESH))
opts->refresh = true; if (IsSet(supported_opts, SUBOPT_BINARY))
opts->binary = false; if (IsSet(supported_opts, SUBOPT_STREAMING))
opts->streaming = LOGICALREP_STREAM_PARALLEL; if (IsSet(supported_opts, SUBOPT_TWOPHASE_COMMIT))
opts->twophase = false; if (IsSet(supported_opts, SUBOPT_DISABLE_ON_ERR))
opts->disableonerr = false; if (IsSet(supported_opts, SUBOPT_PASSWORD_REQUIRED))
opts->passwordrequired = true; if (IsSet(supported_opts, SUBOPT_RUN_AS_OWNER))
opts->runasowner = false; if (IsSet(supported_opts, SUBOPT_FAILOVER))
opts->failover = false; if (IsSet(supported_opts, SUBOPT_ORIGIN))
opts->origin = pstrdup(LOGICALREP_ORIGIN_ANY);
/* Test if the given value is valid for synchronous_commit GUC. */
(void) set_config_option("synchronous_commit", opts->synchronous_commit,
PGC_BACKEND, PGC_S_TEST, GUC_ACTION_SET, false, 0, false);
} elseif (IsSet(supported_opts, SUBOPT_REFRESH) &&
strcmp(defel->defname, "refresh") == 0)
{ if (IsSet(opts->specified_opts, SUBOPT_REFRESH))
errorConflictingDefElem(defel, pstate);
/* *We'vebeenexplicitlyaskedtonotconnect,thatrequiressome *additionalprocessing.
*/ if (!opts->connect && IsSet(supported_opts, SUBOPT_CONNECT))
{ /* Check for incompatible options from the user. */ if (opts->enabled &&
IsSet(opts->specified_opts, SUBOPT_ENABLED))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), /*- translator: both %s are strings of the form "option = value" */
errmsg("%s and %s are mutually exclusive options", "connect = false", "enabled = true")));
if (opts->create_slot &&
IsSet(opts->specified_opts, SUBOPT_CREATE_SLOT))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("%s and %s are mutually exclusive options", "connect = false", "create_slot = true")));
if (opts->copy_data &&
IsSet(opts->specified_opts, SUBOPT_COPY_DATA))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("%s and %s are mutually exclusive options", "connect = false", "copy_data = true")));
/* Change the defaults of other options. */
opts->enabled = false;
opts->create_slot = false;
opts->copy_data = false;
}
/* *Doadditionalcheckingfordisallowedcombinationwhenslot_name=NONE *wasused.
*/ if (!opts->slot_name &&
IsSet(opts->specified_opts, SUBOPT_SLOT_NAME))
{ if (opts->enabled)
{ if (IsSet(opts->specified_opts, SUBOPT_ENABLED))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), /*- translator: both %s are strings of the form "option = value" */
errmsg("%s and %s are mutually exclusive options", "slot_name = NONE", "enabled = true"))); else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), /*- translator: both %s are strings of the form "option = value" */
errmsg("subscription with %s must also set %s", "slot_name = NONE", "enabled = false")));
}
if (opts->create_slot)
{ if (IsSet(opts->specified_opts, SUBOPT_CREATE_SLOT))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), /*- translator: both %s are strings of the form "option = value" */
errmsg("%s and %s are mutually exclusive options", "slot_name = NONE", "create_slot = true"))); else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), /*- translator: both %s are strings of the form "option = value" */
errmsg("subscription with %s must also set %s", "slot_name = NONE", "create_slot = false")));
}
}
}
/* *Checkthatthespecifiedpublicationsarepresentonthepublisher.
*/ staticvoid
check_publications(WalReceiverConn *wrconn, List *publications)
{
WalRcvExecResult *res;
StringInfo cmd;
TupleTableSlot *slot;
List *publicationsCopy = NIL;
Oid tableRow[1] = {TEXTOID};
cmd = makeStringInfo();
appendStringInfoString(cmd, "SELECT t.pubname FROM\n" " pg_catalog.pg_publication t WHERE\n" " t.pubname IN (");
GetPublicationsStr(publications, cmd, true);
appendStringInfoChar(cmd, ')');
res = walrcv_exec(wrconn, cmd->data, 1, tableRow);
destroyStringInfo(cmd);
if (res->status != WALRCV_OK_TUPLES)
ereport(ERROR,
errmsg("could not receive list of publications from the publisher: %s",
res->err));
publicationsCopy = list_copy(publications);
/* Process publication(s). */
slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple); while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
{ char *pubname; bool isnull;
/* Delete the publication present in publisher from the list. */
publicationsCopy = list_delete(publicationsCopy, makeString(pubname));
ExecClearTuple(slot);
}
ExecDropSingleTupleTableSlot(slot);
walrcv_clear_result(res);
if (list_length(publicationsCopy))
{ /* Prepare the list of non-existent publication(s) for error message. */
StringInfo pubnames = makeStringInfo();
GetPublicationsStr(publicationsCopy, pubnames, false);
ereport(WARNING,
errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg_plural("publication %s does not exist on the publisher", "publications %s do not exist on the publisher",
list_length(publicationsCopy),
pubnames->data));
}
}
/* *AuxiliaryfunctiontobuildatextarrayoutofalistofStringnodes.
*/ static Datum
publicationListToArray(List *publist)
{
ArrayType *arr;
Datum *datums;
MemoryContext memcxt;
MemoryContext oldcxt;
/* Create memory context for temporary allocations. */
memcxt = AllocSetContextCreate(CurrentMemoryContext, "publicationListToArray to array",
ALLOCSET_DEFAULT_SIZES);
oldcxt = MemoryContextSwitchTo(memcxt);
/* *Sincecreatingareplicationslotisnottransactional,rollingback *thetransactionleavesthecreatedreplicationslot.Sowecannotrun *CREATESUBSCRIPTIONinsideatransactionblockifcreatinga *replicationslot.
*/ if (opts.create_slot)
PreventInTransactionBlock(isTopLevel, "CREATE SUBSCRIPTION ... WITH (create_slot = true)");
/* *Wedon'twanttoallowunprivilegeduserstobeabletotrigger *attemptstoaccessarbitrarynetworkdestinations,sorequiretheuser *tohavebeenspecificallyauthorizedtocreatesubscriptions.
*/ if (!has_privs_of_role(owner, ROLE_PG_CREATE_SUBSCRIPTION))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create subscription"),
errdetail("Only roles with privileges of the \"%s\" role may create subscriptions.", "pg_create_subscription")));
/* *Non-superusersarerequiredtosetapasswordforauthentication,and *thatpasswordmustbeusedbythetargetserver,butthesuperusercan *exemptasubscriptionfromthisrequirement.
*/ if (!opts.passwordrequired && !superuser_arg(owner))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("password_required=false is superuser-only"),
errhint("Subscriptions with the password_required option set to false may only be created or modified by the superuser.")));
/* *Ifbuiltwithappropriateswitch,whinewhenregression-testing *conventionsforsubscriptionnamesareviolated.
*/ #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS if (strncmp(stmt->subname, "regress_", 8) != 0)
elog(WARNING, "subscriptions created by regression test cases should have names starting with \"regress_\""); #endif
/* Check if name is used */
subid = GetSysCacheOid2(SUBSCRIPTIONNAME, Anum_pg_subscription_oid,
MyDatabaseId, CStringGetDatum(stmt->subname)); if (OidIsValid(subid))
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("subscription \"%s\" already exists",
stmt->subname)));
}
if (!IsSet(opts.specified_opts, SUBOPT_SLOT_NAME) &&
opts.slot_name == NULL)
opts.slot_name = stmt->subname;
/* The default for synchronous_commit of subscriptions is off. */ if (opts.synchronous_commit == NULL)
opts.synchronous_commit = "off";
/* *Connecttoremotesidetoexecuterequestedcommandsandfetchtable *info.
*/ if (opts.connect)
{ char *err;
WalReceiverConn *wrconn;
List *tables;
ListCell *lc; char table_state; bool must_use_password;
/* Try to connect to the publisher. */
must_use_password = !superuser_arg(owner) && opts.passwordrequired;
wrconn = walrcv_connect(conninfo, true, true, must_use_password,
stmt->subname, &err); if (!wrconn)
ereport(ERROR,
(errcode(ERRCODE_CONNECTION_FAILURE),
errmsg("subscription \"%s\" could not connect to the publisher: %s",
stmt->subname, err)));
if (twophase_enabled)
UpdateTwoPhaseState(subid, LOGICALREP_TWOPHASE_STATE_ENABLED);
ereport(NOTICE,
(errmsg("created replication slot \"%s\" on publisher",
opts.slot_name)));
}
}
PG_FINALLY();
{
walrcv_disconnect(wrconn);
}
PG_END_TRY();
} else
ereport(WARNING,
(errmsg("subscription was created, but is not connected"),
errhint("To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.")));
staticvoid
AlterSubscription_refresh(Subscription *sub, bool copy_data,
List *validate_publications)
{ char *err;
List *pubrel_names;
List *subrel_states;
Oid *subrel_local_oids;
Oid *pubrel_local_oids;
ListCell *lc; int off; int remove_rel_len; int subrel_count;
Relation rel = NULL; typedefstruct SubRemoveRels
{
Oid relid; char state;
} SubRemoveRels;
SubRemoveRels *sub_remove_rels;
WalReceiverConn *wrconn; bool must_use_password;
/* Load the library providing us libpq calls. */
load_file("libpqwalreceiver", false);
/* Try to connect to the publisher. */
must_use_password = sub->passwordrequired && !sub->ownersuperuser;
wrconn = walrcv_connect(sub->conninfo, true, true, must_use_password,
sub->name, &err); if (!wrconn)
ereport(ERROR,
(errcode(ERRCODE_CONNECTION_FAILURE),
errmsg("subscription \"%s\" could not connect to the publisher: %s",
sub->name, err)));
PG_TRY();
{ if (validate_publications)
check_publications(wrconn, validate_publications);
/* Get the table list from publisher. */
pubrel_names = fetch_table_list(wrconn, sub->publications);
/* Get local table list. */
subrel_states = GetSubscriptionRelations(sub->oid, false);
subrel_count = list_length(subrel_states);
/* *Donotallowchangingtheoptionifthesubscriptionisenabled.This *isbecausebothfailoverandtwo_phaseoptionsoftheslotonthe *publishercannotbemodifiediftheslotiscurrentlyacquiredbythe *existingwalsender. * *Notethattwo_phaseisenabled(akachangedfrom'false'to'true')on *thepublisherbytheexistingwalsender,sowecouldhaveallowedthat *evenwhenthesubscriptionisenabled.Butwekeptthisrestrictionfor *thesakeofconsistencyandsimplicity.
*/ if (sub->enabled)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot set option \"%s\" for enabled subscription",
option)));
if (slot_needs_update)
{
StringInfoData cmd;
/* *Avalidslotmustbeassociatedwiththesubscriptionforusto *modifyanyoftheslot'sproperties.
*/ if (!sub->slotname)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot set option \"%s\" for a subscription that does not have a slot name",
option)));
/* The changed option of the slot can't be rolled back. */
initStringInfo(&cmd);
appendStringInfo(&cmd, "ALTER SUBSCRIPTION ... SET (%s)", option);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("subscription \"%s\" does not exist",
stmt->subname)));
form = (Form_pg_subscription) GETSTRUCT(tup);
subid = form->oid;
/* must be owner */ if (!object_ownercheck(SubscriptionRelationId, subid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SUBSCRIPTION,
stmt->subname);
sub = GetSubscription(subid, false);
/* *Don'tallownon-superusermodificationofasubscriptionwith *password_required=false.
*/ if (!sub->passwordrequired && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("password_required=false is superuser-only"),
errhint("Subscriptions with the password_required option set to false may only be created or modified by the superuser.")));
/* Lock the subscription so nobody else can do anything with it. */
LockSharedObject(SubscriptionRelationId, subid, 0, AccessExclusiveLock);
/* Form a new tuple. */
memset(values, 0, sizeof(values));
memset(nulls, false, sizeof(nulls));
memset(replaces, false, sizeof(replaces));
if (IsSet(opts.specified_opts, SUBOPT_PASSWORD_REQUIRED))
{ /* Non-superuser may not disable password_required. */ if (!opts.passwordrequired && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("password_required=false is superuser-only"),
errhint("Subscriptions with the password_required option set to false may only be created or modified by the superuser.")));
/* *Modifyingthetwo_phaseslotoptionrequiresaslot *lookupbyslotname,sochangingtheslotnameatthe *sametimeisnotallowed.
*/ if (update_two_phase &&
IsSet(opts.specified_opts, SUBOPT_SLOT_NAME))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"slot_name\" and \"two_phase\" cannot be altered at the same time")));
/* *Notethatworkersmaystillsurviveevenifthe *subscriptionhasbeendisabled. * *Ensureworkershavealreadybeenexitedtoavoid *gettingpreparedtransactionswhilewearedisabling *thetwo_phaseoption.Otherwise,thechangesofan *alreadypreparedtransactioncanbereplicatedagain *alongwithitscorrespondingcommit,leadingto *duplicatedataorerrors.
*/ if (logicalrep_workers_find(subid, true, true))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot alter \"two_phase\" when logical replication worker is still running"),
errhint("Try again after some time.")));
/* *two_phasecannotbedisabledifthereareany *uncommittedpreparedtransactionspresentotherwiseit *canleadtoduplicatedataorerrorsasexplainedin *thecommentabove.
*/ if (update_two_phase &&
sub->twophasestate == LOGICALREP_TWOPHASE_STATE_ENABLED &&
LookupGXactBySubid(subid))
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot disable \"two_phase\" when prepared transactions exist"),
errhint("Resolve these transactions and try again.")));
case ALTER_SUBSCRIPTION_ENABLED:
{
parse_subscription_options(pstate, stmt->options,
SUBOPT_ENABLED, &opts);
Assert(IsSet(opts.specified_opts, SUBOPT_ENABLED));
if (!sub->slotname && opts.enabled)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot enable subscription that does not have a slot name")));
case ALTER_SUBSCRIPTION_CONNECTION: /* Load the library providing us libpq calls. */
load_file("libpqwalreceiver", false); /* Check the connection info string. */
walrcv_check_conninfo(stmt->conninfo,
sub->passwordrequired && !sub->ownersuperuser);
/* Refresh if user asked us to. */ if (opts.refresh)
{ if (!sub->enabled)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"),
errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false).")));
/* *SeeALTER_SUBSCRIPTION_REFRESHfordetailswhythisis *notallowed.
*/ if (sub->twophasestate == LOGICALREP_TWOPHASE_STATE_ENABLED && opts.copy_data)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("ALTER SUBSCRIPTION with refresh and copy_data is not allowed when two_phase is enabled"),
errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION with refresh = false, or with copy_data = false, or use DROP/CREATE SUBSCRIPTION.")));
PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh");
/* Make sure refresh sees the new list of publications. */
sub->publications = stmt->publication;
case ALTER_SUBSCRIPTION_ADD_PUBLICATION: case ALTER_SUBSCRIPTION_DROP_PUBLICATION:
{
List *publist; bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION;
/* Refresh if user asked us to. */ if (opts.refresh)
{ /* We only need to validate user specified publications. */
List *validate_publications = (isadd) ? stmt->publication : NULL;
if (!sub->enabled)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), /* translator: %s is an SQL ALTER command */
errhint("Use %s instead.",
isadd ? "ALTER SUBSCRIPTION ... ADD PUBLICATION ... WITH (refresh = false)" : "ALTER SUBSCRIPTION ... DROP PUBLICATION ... WITH (refresh = false)")));
/* *SeeALTER_SUBSCRIPTION_REFRESHfordetailswhythisis *notallowed.
*/ if (sub->twophasestate == LOGICALREP_TWOPHASE_STATE_ENABLED && opts.copy_data)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("ALTER SUBSCRIPTION with refresh and copy_data is not allowed when two_phase is enabled"), /* translator: %s is an SQL ALTER command */
errhint("Use %s with refresh = false, or with copy_data = false, or use DROP/CREATE SUBSCRIPTION.",
isadd ? "ALTER SUBSCRIPTION ... ADD PUBLICATION" : "ALTER SUBSCRIPTION ... DROP PUBLICATION")));
PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh");
/* Refresh the new list of publications. */
sub->publications = publist;
case ALTER_SUBSCRIPTION_REFRESH:
{ if (!sub->enabled)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("ALTER SUBSCRIPTION ... REFRESH is not allowed for disabled subscriptions")));
/* Check the given LSN is at least a future LSN */ if (!XLogRecPtrIsInvalid(remote_lsn) && opts.lsn < remote_lsn)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("skip WAL location (LSN %X/%X) must be greater than origin LSN %X/%X",
LSN_FORMAT_ARGS(opts.lsn),
LSN_FORMAT_ARGS(remote_lsn))));
}
/* Load the library providing us libpq calls. */
load_file("libpqwalreceiver", false);
/* Try to connect to the publisher. */
must_use_password = sub->passwordrequired && !sub->ownersuperuser;
wrconn = walrcv_connect(sub->conninfo, true, true, must_use_password,
sub->name, &err); if (!wrconn)
ereport(ERROR,
(errcode(ERRCODE_CONNECTION_FAILURE),
errmsg("subscription \"%s\" could not connect to the publisher: %s",
sub->name, err)));
if (!HeapTupleIsValid(tup))
{
table_close(rel, NoLock);
if (!stmt->missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("subscription \"%s\" does not exist",
stmt->subname))); else
ereport(NOTICE,
(errmsg("subscription \"%s\" does not exist, skipping",
stmt->subname)));
/* must be owner */ if (!object_ownercheck(SubscriptionRelationId, subid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SUBSCRIPTION,
stmt->subname);
/* DROP hook for the subscription being removed */
InvokeObjectDropHook(SubscriptionRelationId, subid, 0);
if (!object_ownercheck(SubscriptionRelationId, form->oid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SUBSCRIPTION,
NameStr(form->subname));
/* *Don'tallownon-superusermodificationofasubscriptionwith *password_required=false.
*/ if (!form->subpasswordrequired && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("password_required=false is superuser-only"),
errhint("Subscriptions with the password_required option set to false may only be created or modified by the superuser.")));
/* Must be able to become new owner */
check_can_set_role(GetUserId(), newOwnerId);
/* *Checkandlogawarningifthepublisherhassubscribedtothesametable, *itspartitionancestors(ifit'sapartition),oritspartitionchildren(if *it'sapartitionedtable),fromsomeotherpublishers.Thischeckis *requiredonlyif"copy_data=true"and"origin=none"forCREATE *SUBSCRIPTIONandALTERSUBSCRIPTION...REFRESHstatementstonotifythe *userthatdatahavingoriginmighthavebeencopied. * *Thischeckneednotbeperformedonthetablesthatarealreadyadded *becauseincrementalsyncforthosetableswillhappenthroughWALandthe *originofthedatacanbeidentifiedfromtheWALrecords. * *subrel_local_oidscontainsthelistofrelationoidsthatarealready *presentonthesubscriber.
*/ staticvoid
check_publications_origin(WalReceiverConn *wrconn, List *publications, bool copydata, char *origin, Oid *subrel_local_oids, int subrel_count, char *subname)
{
WalRcvExecResult *res;
StringInfoData cmd;
TupleTableSlot *slot;
Oid tableRow[1] = {TEXTOID};
List *publist = NIL; int i;
if (!copydata || !origin ||
(pg_strcasecmp(origin, LOGICALREP_ORIGIN_NONE) != 0)) return;
initStringInfo(&cmd);
appendStringInfoString(&cmd, "SELECT DISTINCT P.pubname AS pubname\n" "FROM pg_publication P,\n" " LATERAL pg_get_publication_tables(P.pubname) GPT\n" " JOIN pg_subscription_rel PS ON (GPT.relid = PS.srrelid OR" " GPT.relid IN (SELECT relid FROM pg_partition_ancestors(PS.srrelid) UNION" " SELECT relid FROM pg_partition_tree(PS.srrelid))),\n" " pg_class C JOIN pg_namespace N ON (N.oid = C.relnamespace)\n" "WHERE C.oid = GPT.relid AND P.pubname IN (");
GetPublicationsStr(publications, &cmd, true);
appendStringInfoString(&cmd, ")\n");
/* *IncaseofALTERSUBSCRIPTION...REFRESH,subrel_local_oidscontains *thelistofrelationoidsthatarealreadypresentonthesubscriber. *Thischeckshouldbeskippedforthesetables.
*/ for (i = 0; i < subrel_count; i++)
{
Oid relid = subrel_local_oids[i]; char *schemaname = get_namespace_name(get_rel_namespace(relid)); char *tablename = get_rel_name(relid); char *schemaname_lit = quote_literal_cstr(schemaname); char *tablename_lit = quote_literal_cstr(tablename);
appendStringInfo(&cmd, "AND NOT (N.nspname = %s AND C.relname = %s)\n",
schemaname_lit, tablename_lit);
pfree(schemaname_lit);
pfree(tablename_lit);
}
res = walrcv_exec(wrconn, cmd.data, 1, tableRow);
pfree(cmd.data);
if (res->status != WALRCV_OK_TUPLES)
ereport(ERROR,
(errcode(ERRCODE_CONNECTION_FAILURE),
errmsg("could not receive list of replicated tables from the publisher: %s",
res->err)));
/* Process tables. */
slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple); while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
{ char *pubname; bool isnull;
/* Prepare the list of publication(s) for warning message. */
GetPublicationsStr(publist, pubnames, false);
ereport(WARNING,
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("subscription \"%s\" requested copy_data with origin = NONE but might copy data that had a different origin",
subname),
errdetail_plural("The subscription being created subscribes to a publication (%s) that contains tables that are written to by other subscriptions.", "The subscription being created subscribes to publications (%s) that contain tables that are written to by other subscriptions.",
list_length(publist), pubnames->data),
errhint("Verify that initial data copied from the publisher tables did not come from other origins."));
}
ExecDropSingleTupleTableSlot(slot);
walrcv_clear_result(res);
}
/* *Getthelistoftableswhichbelongtospecifiedpublicationsonthe *publisherconnection. * *Notethatwedon'tsupportthecasewherethecolumnlistisdifferentfor *thesametableindifferentpublicationstoavoidsendingunwantedcolumn *informationforsomeoftherows.Thiscanhappenwhenboththecolumn *listandrowfilterarespecifiedfordifferentpublications.
*/ static List *
fetch_table_list(WalReceiverConn *wrconn, List *publications)
{
WalRcvExecResult *res;
StringInfoData cmd;
TupleTableSlot *slot;
Oid tableRow[3] = {TEXTOID, TEXTOID, InvalidOid};
List *tablelist = NIL; int server_version = walrcv_server_version(wrconn); bool check_columnlist = (server_version >= 150000);
StringInfo pub_names = makeStringInfo();
initStringInfo(&cmd);
/* Build the pub_names comma-separated string. */
GetPublicationsStr(publications, pub_names, true);
/* Get the list of tables from the publisher. */ if (server_version >= 160000)
{
tableRow[2] = INT2VECTOROID;
/* *Fromversion16,weallowedpassingmultiplepublicationstothe *functionpg_get_publication_tables.Thishelpedtofilteroutthe *partitiontablewhoseancestorisalsopublishedinthis *publicationarray. * *Joinpg_get_publication_tableswithpg_publicationtoexclude *non-existingpublications. * *Notethatattrsarealwaysstoredinsortedordersowedon'tneed *toworryifdifferentpublicationshavespecifiedthemina *differentorder.Seepub_collist_validate.
*/
appendStringInfo(&cmd, "SELECT DISTINCT n.nspname, c.relname, gpt.attrs\n" " FROM pg_class c\n" " JOIN pg_namespace n ON n.oid = c.relnamespace\n" " JOIN ( SELECT (pg_get_publication_tables(VARIADIC array_agg(pubname::text))).*\n" " FROM pg_publication\n" " WHERE pubname IN ( %s )) AS gpt\n" " ON gpt.relid = c.oid\n",
pub_names->data);
} else
{
tableRow[2] = NAMEARRAYOID;
appendStringInfoString(&cmd, "SELECT DISTINCT t.schemaname, t.tablename \n");
/* Get column lists for each relation if the publisher supports it */ if (check_columnlist)
appendStringInfoString(&cmd, ", t.attnames\n");
appendStringInfo(&cmd, "FROM pg_catalog.pg_publication_tables t\n" " WHERE t.pubname IN ( %s )",
pub_names->data);
}
if (res->status != WALRCV_OK_TUPLES)
ereport(ERROR,
(errcode(ERRCODE_CONNECTION_FAILURE),
errmsg("could not receive list of replicated tables from the publisher: %s",
res->err)));
if (check_columnlist && list_member(tablelist, rv))
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use different column lists for table \"%s.%s\" in different publications",
nspname, relname)); else
tablelist = lappend(tablelist, rv);
ReplicationSlotNameForTablesync(subid, relid, syncslotname, sizeof(syncslotname));
elog(WARNING, "could not drop tablesync replication slot \"%s\"",
syncslotname);
}
}
ereport(ERROR,
(errcode(ERRCODE_CONNECTION_FAILURE),
errmsg("could not connect to publisher when attempting to drop replication slot \"%s\": %s",
slotname, err), /* translator: %s is an SQL ALTER command */
errhint("Use %s to disable the subscription, and then use %s to disassociate it from the slot.", "ALTER SUBSCRIPTION ... DISABLE", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)")));
}
/* *Checkforduplicatesinthegivenlistofpublicationsanderroroutif *foundone.Addpublicationstodatumsastextdatums,ifdatumsisnot *NULL.
*/ staticvoid
check_duplicates_in_publist(List *publist, Datum *datums)
{
ListCell *cell; int j = 0;
if (strcmp(name, pubname) == 0)
{
found = true; if (addpub)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("publication \"%s\" is already in subscription \"%s\"",
name, subname))); else
oldpublist = foreach_delete_current(oldpublist, lc2);
break;
}
}
if (addpub && !found)
oldpublist = lappend(oldpublist, makeString(name)); elseif (!addpub && !found)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("publication \"%s\" is not in subscription \"%s\"",
name, subname)));
}
/* *XXXProbablynostrongreasonforthis,butfornowit'stomakeALTER *SUBSCRIPTION...DROPPUBLICATIONconsistentwithSETPUBLICATION.
*/ if (!oldpublist)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot drop all the publications from a subscription")));
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("%s requires a Boolean value or \"parallel\"",
def->defname))); return LOGICALREP_STREAM_OFF; /* keep compiler quiet */
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.48Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.