diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 54d8ad9..1261d30 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -729,7 +729,7 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt, recordDependencyOnOwner(SubscriptionRelationId, subid, owner); - /* Update the Conflict Resolvers in pg_subscription_conflict */ + /* Update the conflict resolvers in pg_subscription_conflict. */ SetSubConflictResolvers(subid, conflict_resolvers); ReplicationOriginNameForLogicalRep(subid, InvalidOid, originname, sizeof(originname)); @@ -1598,10 +1598,8 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt, * Get the list of conflict types and resolvers and validate * them. */ - conflict_resolvers = ParseAndGetSubConflictResolvers( - pstate, - stmt->resolvers, - false); + conflict_resolvers = + ParseAndGetSubConflictResolvers(pstate, stmt->resolvers, false); /* * Update the conflict resolvers for the corresponding @@ -1882,7 +1880,7 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel) /* Remove any associated relation synchronization states. */ RemoveSubscriptionRel(subid, InvalidOid); - /* Remove any associated conflict resolvers */ + /* Remove any associated conflict resolvers. */ RemoveSubConflictResolvers(subid); /* Remove the origin tracking if exists. */ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 28591c9..68e0fa9 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10898,6 +10898,7 @@ AlterSubscriptionStmt: $$ = (Node *) n; } ; + conflict_type: Sconst { $$ = $1; } ; diff --git a/src/backend/replication/logical/conflict.c b/src/backend/replication/logical/conflict.c index eb46e39..a20a86e 100644 --- a/src/backend/replication/logical/conflict.c +++ b/src/backend/replication/logical/conflict.c @@ -40,6 +40,9 @@ static const char *const ConflictTypeNames[] = { [CT_DELETE_MISSING] = "delete_missing" }; +StaticAssertDecl(lengthof(ConflictTypeNames) == CONFLICT_NUM_TYPES, + "array length mismatch"); + static const char *const ConflictResolverNames[] = { [CR_APPLY_REMOTE] = "apply_remote", [CR_KEEP_LOCAL] = "keep_local", @@ -49,7 +52,8 @@ static const char *const ConflictResolverNames[] = { [CR_ERROR] = "error" }; -#define CONFLICT_TYPE_MAX_RESOLVERS 4 +StaticAssertDecl(lengthof(ConflictResolverNames) == CONFLICT_NUM_TYPES, + "array length mismatch"); /* * Valid conflict resolvers for each conflict type. @@ -562,7 +566,7 @@ conf_detection_check_prerequisites(void) * Validate the conflict type and return the corresponding ConflictType enum. */ ConflictType -ValidateConflictType(const char *conflict_type) +ValidateConflictType(const char *conflict_type_name) { ConflictType type; bool valid = false; @@ -570,7 +574,7 @@ ValidateConflictType(const char *conflict_type) /* Check conflict type validity */ for (type = 0; type < CONFLICT_NUM_TYPES; type++) { - if (pg_strcasecmp(ConflictTypeNames[type], conflict_type) == 0) + if (pg_strcasecmp(ConflictTypeNames[type], conflict_type_name) == 0) { valid = true; break; @@ -580,30 +584,30 @@ ValidateConflictType(const char *conflict_type) if (!valid) ereport(ERROR, errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("%s is not a valid conflict type", conflict_type)); + errmsg("%s is not a valid conflict type", conflict_type_name)); return type; } /* * Validate the conflict type and resolver. It returns an enum ConflictType - * corresponding to the conflict type string passed by the caller. + * corresponding to the conflict type name passed by the caller. */ ConflictType -ValidateConflictTypeAndResolver(const char *conflict_type, - const char *conflict_resolver) +ValidateConflictTypeAndResolver(const char *conflict_type_name, + const char *conflict_resolver_name) { ConflictType type; ConflictResolver resolver; bool valid = false; /* Validate conflict type */ - type = ValidateConflictType(conflict_type); + type = ValidateConflictType(conflict_type_name); /* Validate the conflict resolver name */ for (resolver = 0; resolver < CONFLICT_NUM_RESOLVERS; resolver++) { - if (pg_strcasecmp(ConflictResolverNames[resolver], conflict_resolver) == 0) + if (pg_strcasecmp(ConflictResolverNames[resolver], conflict_resolver_name) == 0) { valid = true; break; @@ -614,7 +618,7 @@ ValidateConflictTypeAndResolver(const char *conflict_type, { ereport(ERROR, errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("%s is not a valid conflict resolver", conflict_resolver)); + errmsg("%s is not a valid conflict resolver", conflict_resolver_name)); } /* Reset */ @@ -640,12 +644,13 @@ ValidateConflictTypeAndResolver(const char *conflict_type, ereport(ERROR, errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("%s is not a valid conflict resolver for conflict type %s", - conflict_resolver, - conflict_type)); + conflict_resolver_name, + conflict_type_name)); return type; } + /* * Common 'CONFLICT RESOLVER' parsing function for CREATE and ALTER * SUBSCRIPTION commands. @@ -660,36 +665,33 @@ List * ParseAndGetSubConflictResolvers(ParseState *pstate, List *stmtresolvers, bool add_defaults) { - List *SeenTypes = NIL; + bool seen_type[CONFLICT_NUM_TYPES] = {0}; List *res = NIL; - /* Loop through the user provided resolvers */ + /* Loop through the user provided resolvers. */ foreach_ptr(DefElem, defel, stmtresolvers) { - char *resolver; + char *resolver_name; ConflictTypeResolver *conftyperesolver = NULL; + ConflictType conflict_type; - /* Check if the conflict type has already been seen */ - if (list_member(SeenTypes, makeString(defel->defname))) - errorConflictingDefElem(defel, pstate); + /* Validate the conflict type and resolver. */ + resolver_name = defGetString(defel); + resolver_name = downcase_truncate_identifier(resolver_name, strlen(resolver_name), false); + conflict_type = ValidateConflictTypeAndResolver(defel->defname, resolver_name); - /* Validate the conflict type and resolver */ - resolver = defGetString(defel); - resolver = downcase_truncate_identifier( - resolver, strlen(resolver), false); - ValidateConflictTypeAndResolver(defel->defname, - resolver); - - /* Add the conflict type to the list of seen types */ - SeenTypes = lappend(SeenTypes, makeString(defel->defname)); + /* Error if the conflict type has already been seen, else flag it is as seen. */ + if (seen_type[conflict_type]) + errorConflictingDefElem(defel, pstate); + seen_type[conflict_type] = true; conftyperesolver = palloc(sizeof(ConflictTypeResolver)); conftyperesolver->conflict_type_name = defel->defname; - conftyperesolver->conflict_resolver_name = resolver; + conftyperesolver->conflict_resolver_name = resolver_name; res = lappend(res, conftyperesolver); } - /* Once validation is complete, warn users if prerequisites are not met */ + /* Once validation is complete, warn users if prerequisites are not met. */ if (stmtresolvers) conf_detection_check_prerequisites(); @@ -701,11 +703,10 @@ ParseAndGetSubConflictResolvers(ParseState *pstate, { for (int i = 0; i < CONFLICT_NUM_TYPES; i++) { - ConflictTypeResolver *conftyperesolver = NULL; - - if (!list_member(SeenTypes, makeString((char *) ConflictTypeNames[i]))) + if (!seen_type[i]) { ConflictResolver def_resolver = ConflictTypeResolverMap[i][0]; + ConflictTypeResolver *conftyperesolver; conftyperesolver = palloc(sizeof(ConflictTypeResolver)); conftyperesolver->conflict_type_name = ConflictTypeNames[i]; @@ -715,8 +716,6 @@ ParseAndGetSubConflictResolvers(ParseState *pstate, } } - list_free_deep(SeenTypes); - return res; } @@ -726,7 +725,11 @@ ParseAndGetSubConflictResolvers(ParseState *pstate, List * GetDefaultConflictResolvers() { - List *res = NIL; + static List *res = NIL; + + /* The defaults are always same, so return the same list. */ + if (res) + return res; for (ConflictType type = 0; type < CONFLICT_NUM_TYPES; type++) { @@ -756,13 +759,9 @@ UpdateSubConflictResolvers(List *conflict_resolvers, Oid subid) Datum values[Natts_pg_subscription_conflict]; bool nulls[Natts_pg_subscription_conflict]; bool replaces[Natts_pg_subscription_conflict]; - HeapTuple oldtup; - HeapTuple newtup = NULL; Relation pg_subscription_conflict; - char *cur_conflict_res; - Datum datum; - /* Prepare to update a tuple */ + /* Prepare to update a tuple. */ memset(nulls, false, sizeof(nulls)); memset(replaces, false, sizeof(replaces)); memset(values, 0, sizeof(values)); @@ -771,7 +770,12 @@ UpdateSubConflictResolvers(List *conflict_resolvers, Oid subid) foreach_ptr(ConflictTypeResolver, conftyperesolver, conflict_resolvers) { - /* Set up subid and conflict_type to search in cache */ + HeapTuple oldtup; + HeapTuple newtup = NULL; + char *cur_conflict_res; + Datum datum; + + /* Set up subid and conflict_type to search in cache. */ values[Anum_pg_subscription_conflict_confsubid - 1] = ObjectIdGetDatum(subid); values[Anum_pg_subscription_conflict_conftype - 1] = CStringGetTextDatum(conftyperesolver->conflict_type_name); @@ -795,7 +799,7 @@ UpdateSubConflictResolvers(List *conflict_resolvers, Oid subid) if (pg_strcasecmp(cur_conflict_res, conftyperesolver->conflict_resolver_name) != 0) { - /* Update the new resolver */ + /* Update the new resolver. */ values[Anum_pg_subscription_conflict_confres - 1] = CStringGetTextDatum(conftyperesolver->conflict_resolver_name); replaces[Anum_pg_subscription_conflict_confres - 1] = true; @@ -818,29 +822,28 @@ UpdateSubConflictResolvers(List *conflict_resolvers, Oid subid) * Reset the conflict resolver for this conflict type to its default setting. */ void -ResetSubConflictResolver(Oid subid, char *conflict_type) +ResetSubConflictResolver(Oid subid, char *conflict_type_name) { - ConflictType idx; - ConflictTypeResolver *conflictResolver = NULL; - List *conflictresolver_list = NIL; - - /* Validate the conflict type and get the index */ - idx = ValidateConflictType(conflict_type); - conflictResolver = palloc(sizeof(ConflictTypeResolver)); - conflictResolver->conflict_type_name = conflict_type; - - /* Get the default resolver for this conflict_type */ - conflictResolver->conflict_resolver_name = - ConflictResolverNames[ConflictTypeResolverMap[idx][0]]; - - /* Create a list of conflict resolvers and update in catalog */ - conflictresolver_list = lappend(conflictresolver_list, conflictResolver); - UpdateSubConflictResolvers(conflictresolver_list, subid); - + ConflictType conflict_type; + ConflictTypeResolver *ctr = NULL; + List *ctr_list = NIL; + + /* Validate the conflict type name and get the conflict type. */ + conflict_type = ValidateConflictType(conflict_type_name); + ctr = palloc(sizeof(ConflictTypeResolver)); + ctr->conflict_type_name = conflict_type_name; + + /* Get the default resolver for this conflict type. */ + ctr->conflict_resolver_name = + ConflictResolverNames[ConflictTypeResolverMap[conflict_type][0]]; + + /* Create a (one-element) list of ConflictTypeResolver's and update the catalog. */ + ctr_list = lappend(ctr_list, ctr); + UpdateSubConflictResolvers(ctr_list, subid); } /* - * Set Conflict Resolvers on the subscription + * Set Conflict Resolvers on the subscription. */ void SetSubConflictResolvers(Oid subId, List *conflict_resolvers) @@ -848,17 +851,18 @@ SetSubConflictResolvers(Oid subId, List *conflict_resolvers) Relation pg_subscription_conflict; Datum values[Natts_pg_subscription_conflict]; bool nulls[Natts_pg_subscription_conflict]; - HeapTuple newtup = NULL; - Oid conflict_oid; pg_subscription_conflict = table_open(SubscriptionConflictId, RowExclusiveLock); - /* Prepare to update a tuple */ + /* Prepare to update a tuple. */ memset(nulls, false, sizeof(nulls)); - /* Iterate over the list of resolvers */ + /* Iterate over the list of resolvers. */ foreach_ptr(ConflictTypeResolver, conftyperesolver, conflict_resolvers) { + HeapTuple newtup = NULL; + Oid conflict_oid; + values[Anum_pg_subscription_conflict_confsubid - 1] = ObjectIdGetDatum(subId); values[Anum_pg_subscription_conflict_conftype - 1] = @@ -866,7 +870,7 @@ SetSubConflictResolvers(Oid subId, List *conflict_resolvers) values[Anum_pg_subscription_conflict_confres - 1] = CStringGetTextDatum(conftyperesolver->conflict_resolver_name); - /* Get a new oid and update the tuple into catalog */ + /* Get a new oid and update the tuple into catalog. */ conflict_oid = GetNewOidWithIndex(pg_subscription_conflict, SubscriptionConflictOidIndexId, Anum_pg_subscription_conflict_oid); @@ -882,7 +886,7 @@ SetSubConflictResolvers(Oid subId, List *conflict_resolvers) } /* - * Remove the subscription conflict resolvers for the subscription id + * Remove the subscription conflict resolvers for the subscription id. */ void RemoveSubConflictResolvers(Oid subid) @@ -906,7 +910,7 @@ RemoveSubConflictResolvers(Oid subid) scan = table_beginscan_catalog(rel, 1, skey); - /* Iterate through the tuples and delete them */ + /* Iterate through the tuples and delete them. */ while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection))) CatalogTupleDelete(rel, &tup->t_self); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 572eb52..860422e 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -395,7 +395,7 @@ static void setupDumpWorker(Archive *AH); static TableInfo *getRootTableInfo(const TableInfo *tbinfo); static bool forcePartitionRootLoad(const TableInfo *tbinfo); static void read_dump_filters(const char *filename, DumpOptions *dopt); -static bool is_default_resolver(const char *confType, const char *confRes); +static bool is_default_resolver(const char *conf_type_name, const char *conf_resolver_name); static void destroyConcflictResolverList(SimplePtrList *list); @@ -4832,9 +4832,9 @@ getSubscriptions(Archive *fout) { DumpOptions *dopt = fout->dopt; PQExpBuffer query; - PQExpBuffer confQuery; + PQExpBuffer conf_query; PGresult *res; - PGresult *confRes; + PGresult *conf_res; SubscriptionInfo *subinfo; int i_tableoid; int i_oid; @@ -4855,9 +4855,8 @@ getSubscriptions(Archive *fout) int i_subenabled; int i_subfailover; int i, - j, ntups, - ntuples; + conf_ntuples; if (dopt->no_subscriptions || fout->remoteVersion < 100000) return; @@ -5018,36 +5017,36 @@ getSubscriptions(Archive *fout) subinfo[i].subfailover = pg_strdup(PQgetvalue(res, i, i_subfailover)); - /* Populate conflict type fields using the new query */ - confQuery = createPQExpBuffer(); - appendPQExpBuffer(confQuery, + /* Populate conflict type fields using the new query. */ + conf_query = createPQExpBuffer(); + appendPQExpBuffer(conf_query, "SELECT conftype, confres FROM pg_catalog.pg_subscription_conflict " - "WHERE confsubid = %u order by conftype;", subinfo[i].dobj.catId.oid); - confRes = ExecuteSqlQuery(fout, confQuery->data, PGRES_TUPLES_OK); + "WHERE confsubid = %u ORDER BY conftype;", subinfo[i].dobj.catId.oid); + conf_res = ExecuteSqlQuery(fout, conf_query->data, PGRES_TUPLES_OK); - ntuples = PQntuples(confRes); + conf_ntuples = PQntuples(conf_res); - /* Initialize pointers in the list to NULL */ - subinfo[i].conflict_resolver = (SimplePtrList) + /* Initialize pointers in the list to NULL. */ + subinfo[i].conflict_resolvers = (SimplePtrList) { - 0 + .head = NULL, .tail = NULL }; - /* Store conflict types and resolvers from the query result in subinfo */ - for (j = 0; j < ntuples; j++) + /* Store conflict types and resolvers from the query result in subinfo. */ + for (int j = 0; j < conf_ntuples; j++) { - /* Create the ConflictTypeResolver node */ - ConflictTypeResolver *conftyperesolver = palloc(sizeof(ConflictTypeResolver)); + /* Create the _ConflictTypeResolver node. */ + _ConflictTypeResolver *ctr = palloc(sizeof(_ConflictTypeResolver)); - conftyperesolver->conflict_type = pg_strdup(PQgetvalue(confRes, j, 0)); - conftyperesolver->resolver = pg_strdup(PQgetvalue(confRes, j, 1)); + ctr->conflict_type_name = pg_strdup(PQgetvalue(conf_res, j, 0)); + ctr->conflict_resolver_name = pg_strdup(PQgetvalue(conf_res, j, 1)); - /* Append the node to subinfo's list */ - simple_ptr_list_append(&subinfo[i].conflict_resolver, conftyperesolver); + /* Append the node to subinfo's list. */ + simple_ptr_list_append(&subinfo[i].conflict_resolvers, ctr); } - PQclear(confRes); - destroyPQExpBuffer(confQuery); + PQclear(conf_res); + destroyPQExpBuffer(conf_query); /* Decide whether we want to dump it */ selectDumpableObject(&(subinfo[i].dobj), fout); @@ -5299,26 +5298,24 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo) { bool first_resolver = true; SimplePtrListCell *cell = NULL; - ConflictTypeResolver *conftyperesolver = NULL; - for (cell = subinfo->conflict_resolver.head; cell; cell = cell->next) + for (cell = subinfo->conflict_resolvers.head; cell; cell = cell->next) { - conftyperesolver = (ConflictTypeResolver *) cell->ptr; + _ConflictTypeResolver *ctr = (_ConflictTypeResolver *) cell->ptr; - if (!is_default_resolver(conftyperesolver->conflict_type, - conftyperesolver->resolver)) + if (!is_default_resolver(ctr->conflict_type_name, ctr->conflict_resolver_name)) { if (first_resolver) { appendPQExpBuffer(query, ") CONFLICT RESOLVER (%s = '%s'", - conftyperesolver->conflict_type, - conftyperesolver->resolver); + ctr->conflict_type_name, + ctr->conflict_resolver_name); first_resolver = false; } else appendPQExpBuffer(query, ", %s = '%s'", - conftyperesolver->conflict_type, - conftyperesolver->resolver); + ctr->conflict_type_name, + ctr->conflict_resolver_name); } } } @@ -5382,7 +5379,7 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo) subinfo->dobj.catId, 0, subinfo->dobj.dumpId); /* Clean-up the conflict_resolver list */ - destroyConcflictResolverList((SimplePtrList *) &subinfo->conflict_resolver); + destroyConcflictResolverList((SimplePtrList *) &subinfo->conflict_resolvers); destroyPQExpBuffer(publications); free(pubnames); @@ -19126,7 +19123,7 @@ read_dump_filters(const char *filename, DumpOptions *dopt) * specified conflict type. */ static bool -is_default_resolver(const char *confType, const char *confRes) +is_default_resolver(const char *conf_type_name, const char *conf_resolver_name) { /* * The default resolvers for each conflict type are taken from the @@ -19136,15 +19133,23 @@ is_default_resolver(const char *confType, const char *confRes) * are changed. */ - if (strcmp(confType, "insert_exists") == 0 || - strcmp(confType, "update_exists") == 0) - return strcmp(confRes, "error") == 0; - else if (strcmp(confType, "update_missing") == 0 || - strcmp(confType, "delete_missing") == 0) - return strcmp(confRes, "skip") == 0; - else if (strcmp(confType, "update_origin_differs") == 0 || - strcmp(confType, "delete_origin_differs") == 0) - return strcmp(confRes, "apply_remote") == 0; + if (strcmp(conf_type_name, "insert_exists") == 0) + return strcmp(conf_resolver_name, "error") == 0; + + else if (strcmp(conf_type_name, "update_exists") == 0) + return strcmp(conf_resolver_name, "error") == 0; + + else if (strcmp(conf_type_name, "update_missing") == 0) + return strcmp(conf_resolver_name, "skip") == 0; + + else if (strcmp(conf_type_name, "update_origin_differs") == 0) + return strcmp(conf_resolver_name, "apply_remote") == 0; + + else if (strcmp(conf_type_name, "delete_missing") == 0) + return strcmp(conf_resolver_name, "skip") == 0; + + else if (strcmp(conf_type_name, "delete_origin_differs") == 0) + return strcmp(conf_resolver_name, "apply_remote") == 0; return false; } @@ -19160,7 +19165,7 @@ destroyConcflictResolverList(SimplePtrList *conflictlist) /* Free the list items */ for (cell = conflictlist->head; cell; cell = cell->next) - pfree((ConflictTypeResolver *) cell->ptr); + pfree((_ConflictTypeResolver *) cell->ptr); /* Destroy the pointer list */ simple_ptr_list_destroy(conflictlist); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index bb16e8b..5c69ad9 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -655,12 +655,11 @@ typedef struct _PublicationSchemaInfo /* * The SubscriptionInfo struct is used to represent subscription. */ - -typedef struct ConflictTypeResolver +typedef struct _ConflictTypeResolver { - const char *conflict_type; - const char *resolver; -} ConflictTypeResolver; + const char *conflict_type_name; + const char *conflict_resolver_name; +} _ConflictTypeResolver; typedef struct _SubscriptionInfo { @@ -680,7 +679,7 @@ typedef struct _SubscriptionInfo char *suborigin; char *suboriginremotelsn; char *subfailover; - SimplePtrList conflict_resolver; + SimplePtrList conflict_resolvers; } SubscriptionInfo; /* diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 8599f22..ee37043 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -6628,7 +6628,7 @@ describeSubscriptions(const char *pattern, bool verbose) ", subskiplsn AS \"%s\"\n", gettext_noop("Skip LSN")); - /* Add conflict resolvers information from pg_subscription_conflict */ + /* Add conflict resolvers information from pg_subscription_conflict. */ if (pset.sversion >= 180000) appendPQExpBuffer(&buf, ", (SELECT string_agg(conftype || ' = ' || confres, ', ' \n" diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out index 9791141..9c3a950 100644 --- a/src/test/regress/expected/subscription.out +++ b/src/test/regress/expected/subscription.out @@ -411,12 +411,12 @@ ERROR: foo is not a valid conflict resolver CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub CONFLICT RESOLVER (update_missing = 'apply_remote') WITH (connect = false); ERROR: apply_remote is not a valid conflict resolver for conflict type update_missing --- fail - duplicate conflict type +-- fail - duplicate conflict types CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub CONFLICT RESOLVER (insert_exists = 'keep_local', insert_exists = 'keep_local'); ERROR: conflicting or redundant options LINE 1: ... CONFLICT RESOLVER (insert_exists = 'keep_local', insert_exi... ^ --- creating subscription with no explicit conflict resolvers should +-- ok - creating subscription with no explicit conflict resolvers should -- configure default conflict resolvers CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false); WARNING: subscription was created, but is not connected @@ -444,18 +444,18 @@ HINT: To initiate replication, you must manually create the replication slot, e regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | t | f | f | off | dbname=regress_doesnotexist | 0/0 | delete_missing = skip, delete_origin_differs = keep_local, insert_exists = keep_local, update_exists = error, update_missing = skip, update_origin_differs = apply_remote (1 row) --- fail - altering with invalid conflict type +-- fail - alter with invalid conflict type ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (foo = 'keep_local'); ERROR: foo is not a valid conflict type --- fail - altering with invalid conflict resolver +-- fail - alter with invalid conflict resolver ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (insert_exists = 'foo'); ERROR: foo is not a valid conflict resolver --- fail - altering with duplicate conflict type +-- fail - alter with duplicate conflict types ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (insert_exists = 'apply_remote', insert_exists = 'apply_remote'); ERROR: conflicting or redundant options LINE 1: ...ONFLICT RESOLVER (insert_exists = 'apply_remote', insert_exi... ^ --- ok - valid conflict types and resolvers +-- ok - alter with valid conflict types and resolvers ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (insert_exists = 'apply_remote', update_missing = 'skip', delete_origin_differs = 'keep_local' ); WARNING: conflict detection and resolution could be incomplete due to disabled track_commit_timestamp DETAIL: Conflicts update_origin_differs and delete_origin_differs cannot be detected, and the origin and commit timestamp for the local row will not be logged. @@ -466,7 +466,7 @@ DETAIL: Conflicts update_origin_differs and delete_origin_differs cannot be det regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | t | f | f | off | dbname=regress_doesnotexist | 0/0 | delete_missing = skip, delete_origin_differs = keep_local, insert_exists = apply_remote, update_exists = error, update_missing = skip, update_origin_differs = apply_remote (1 row) --- ok - valid conflict types and resolvers +-- ok - alter with valid conflict types and resolvers ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (update_exists = 'keep_local', delete_missing = 'error', update_origin_differs = 'error'); WARNING: conflict detection and resolution could be incomplete due to disabled track_commit_timestamp DETAIL: Conflicts update_origin_differs and delete_origin_differs cannot be detected, and the origin and commit timestamp for the local row will not be logged. @@ -477,11 +477,11 @@ DETAIL: Conflicts update_origin_differs and delete_origin_differs cannot be det regress_testsub | regress_subscription_user | f | {testpub} | f | off | d | f | any | t | f | f | off | dbname=regress_doesnotexist | 0/0 | delete_missing = error, delete_origin_differs = keep_local, insert_exists = apply_remote, update_exists = keep_local, update_missing = skip, update_origin_differs = error (1 row) --- fail - reset with an invalid conflit type -ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER for 'foo'; +-- fail - reset for invalid conflict type +ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER FOR 'foo'; ERROR: foo is not a valid conflict type --- ok - valid conflict type -ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER for 'insert_exists'; +-- ok - reset for valid conflict type +ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER FOR 'insert_exists'; \dRs+ List of subscriptions Name | Owner | Enabled | Publication | Binary | Streaming | Two-phase commit | Disable on error | Origin | Password required | Run as owner? | Failover | Synchronous commit | Conninfo | Skip LSN | Conflict Resolvers diff --git a/src/test/regress/sql/subscription.sql b/src/test/regress/sql/subscription.sql index 3f08869..9d42900 100644 --- a/src/test/regress/sql/subscription.sql +++ b/src/test/regress/sql/subscription.sql @@ -282,10 +282,10 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub CONFLICT RESOLVER (update_missing = 'apply_remote') WITH (connect = false); --- fail - duplicate conflict type +-- fail - duplicate conflict types CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub CONFLICT RESOLVER (insert_exists = 'keep_local', insert_exists = 'keep_local'); --- creating subscription with no explicit conflict resolvers should +-- ok - creating subscription with no explicit conflict resolvers should -- configure default conflict resolvers CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false); @@ -300,30 +300,30 @@ CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUB --check if above are configured; for non specified conflict types, default resolvers should be seen \dRs+ --- fail - altering with invalid conflict type +-- fail - alter with invalid conflict type ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (foo = 'keep_local'); --- fail - altering with invalid conflict resolver +-- fail - alter with invalid conflict resolver ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (insert_exists = 'foo'); --- fail - altering with duplicate conflict type +-- fail - alter with duplicate conflict types ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (insert_exists = 'apply_remote', insert_exists = 'apply_remote'); --- ok - valid conflict types and resolvers +-- ok - alter with valid conflict types and resolvers ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (insert_exists = 'apply_remote', update_missing = 'skip', delete_origin_differs = 'keep_local' ); \dRs+ --- ok - valid conflict types and resolvers +-- ok - alter with valid conflict types and resolvers ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (update_exists = 'keep_local', delete_missing = 'error', update_origin_differs = 'error'); \dRs+ --- fail - reset with an invalid conflit type -ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER for 'foo'; +-- fail - reset for invalid conflict type +ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER FOR 'foo'; --- ok - valid conflict type -ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER for 'insert_exists'; +-- ok - reset for valid conflict type +ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER FOR 'insert_exists'; \dRs+