Here are my v15-0001 (code/test) review comments which are "nits". Basically, these are more likely to be cosmetic rather than functional, but that doesn't mean I think they are unimportant. ====== src/backend/commands/subscriptioncmds.c CreateSubscription: N1. + /* Update the Conflict Resolvers in pg_subscription_conflict */ + SetSubConflictResolvers(subid, conflict_resolvers); nit - no need for uppercase /Conflict Resolvers/conflict resolvers/ nit - missing period in comments ~ AlterSubscription: N2. + conflict_resolvers = ParseAndGetSubConflictResolvers( + pstate, + stmt->resolvers, + false); N2a. The comment is inconsistent with other call to this same function. Either they should both say "validate" or they both should not. ~ N2b. nit - rearrange this for less harsh indenting. ~~~ N3. DropSubscription: + /* Remove any associated conflict resolvers */ + RemoveSubConflictResolvers(subid); + nit - add period to comment. ====== src/backend/parser/gram.y N4. + ; +conflict_type: + Sconst { $$ = $1; } nit - add whiespace line above label. ====== src/backend/replication/logical/conflict.c N5. +ConflictType +ValidateConflictType(const char *conflict_type) +{ nit - lets call all the names, with suffix '_name'. So here would be /conflict_type/conflict_type_name/ ~~~ ValidateConflictTypeAndResolver: N6. +ConflictType +ValidateConflictTypeAndResolver(const char *conflict_type, + const char *conflict_resolver) nit - lets call all the names, with suffix '_name'. So here would be: /conflict_type/conflict_type_name/ /conflict_resolver/conflict_resolver_name/ ~ N7. + if (!valid) + { + ereport(ERROR, + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("%s is not a valid conflict resolver", conflict_resolver)); + } nit - Unnecessary { }. ~~~ ParseAndGetSubConflictResolvers: N7. + /* Loop through the user provided resolvers */ + foreach_ptr(DefElem, defel, stmtresolvers) + { + char *resolver; + ConflictTypeResolver *conftyperesolver = NULL; nit - let's call names with '_name' suffix. s/resolver/resolver_name/ nit - let's consistently put a period on end of every comment. ~~~ UpdateSubConflictResolvers: N8. +void +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; nit - Many of those variables can be declared at a lower scope. See the nitpick attachment. nit - Put periods on all the comments in this func. ~~~ ResetSubConflictResolver: N9. +void +ResetSubConflictResolver(Oid subid, char *conflict_type) +{ nit - change the parameter /conflict_type/conflict_type_name/ nit - add periods a end of all comments nit - cleanup some var names ~~~ N10. +/* + * Set Conflict Resolvers on the subscription + */ +void +SetSubConflictResolvers(Oid subId, List *conflict_resolvers) nit - add period to function comment nit - add period to the body comments too nit - put some variables at a lower scope ~~~ N11. +/* + * Remove the subscription conflict resolvers for the subscription id + */ +void +RemoveSubConflictResolvers(Oid subid) nit - add period to function comment. nit - add period to other code comments. ====== src/bin/pg_dump/pg_dump.c N12. +static bool is_default_resolver(const char *confType, const char *confRes); +static void destroyConcflictResolverList(SimplePtrList *list); nit - improve the param names. /confType/conf_type_name/ and /confRes/conf_resolver_name/ ~~~ getSubscriptions: N13. nit - add periods to comments. ~ N14. + PQExpBuffer confQuery; PGresult *res; + PGresult *confRes; nit - let's rename these to "conf_query" amd "conf_res" (to match the existing vars) ~ N15. - ntups; + j, nit - This 'j' can be a for-loop variable. ~ N16. + appendPQExpBuffer(confQuery, + "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); "order by" should be uppercase. ~~~ N17. +static bool +is_default_resolver(const char *confType, const char *confRes) nit - call this param '_name' nit - this function is easier to read if you just expand out each conflict type. (see nits attachment for what I mean) ~~~ N18: +static void +destroyConcflictResolverList(SimplePtrList *conflictlist) nit - typo in this function name "Concflict" ====== src/bin/pg_dump/pg_dump.h N19. + +typedef struct ConflictTypeResolver +{ + const char *conflict_type; + const char *resolver; +} ConflictTypeResolver; + nit - remove excess spaces nit - change the field name to be 'conflict_type_name' and 'conflict_resolver_name' ====== src/bin/psql/describe.c describeSubscriptions: N20. + + /* Add conflict resolvers information from pg_subscription_conflict */ + if (pset.sversion >= 180000) nit - add period to the comments. ====== src/include/nodes/parsenodes.h N21. + List *resolvers; /* List of conflict resolvers */ } CreateSubscriptionStmt; nit - this comment should be same as the other one in this file, and give a proper description what these list element really are. ====== src/include/replication/conflict.h N22. +extern ConflictType ValidateConflictType(const char *conflict_type); +extern ConflictType ValidateConflictTypeAndResolver(const char *conflict_type, + const char *conflict_resolver); +extern List *GetDefaultConflictResolvers(void); +extern void ResetSubConflictResolver(Oid subid, char *conflict_type); nit - Those function parameters that are names should be called XXX_name. e.g. /conflict_type/conflict_type_name/ /conflict_resolver/conflict_resolver_name/ ====== src/test/regress/sql/subscription.sql N23. +-- 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); nit - add word "ok" in the comment. ~ N24. +-- ok - valid conflict types and resolvers +ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (insert_exists = 'apply_remote', update_missing = 'skip', delete_origin_differs = 'keep_local' ); nit - add "alter with" in the comment ~ N25. +-- ok - valid conflict types and resolvers +ALTER SUBSCRIPTION regress_testsub CONFLICT RESOLVER (update_exists = 'keep_local', delete_missing = 'error', update_origin_differs = 'error'); nit - add "alter with" in the comment Also, maybe you should say why there are multiple tests doing the same thing (this and the previous one) ~ N26. +-- fail - reset with an invalid conflit type +ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER for 'foo'; nit - comment "reset for..." nit - typo /conflit/conflict/ nit - use uppercase FOR ~ N27. +-- ok - valid conflict type +ALTER SUBSCRIPTION regress_testsub RESET CONFLICT RESOLVER for 'insert_exists'; nit - comment "reset for..." nit - use uppercase FOR ====== FYI - Most of those nit review comments above have already been changed in the accompanying nitpicks attachment. ====== Kind Regards, Peter Smith. Fujitsu Australia.