public inbox for [email protected]help / color / mirror / Atom feed
[PATCH v3 1/4] Introduce a new syntax to add/drop publications 72+ messages / 3 participants [nested] [flat]
* [PATCH v3 1/4] Introduce a new syntax to add/drop publications @ 2021-01-26 07:43 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-01-26 07:43 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 121 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 143 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..88fa7f1b3f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, TupleDesc tupledesc, + List *publications, bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,51 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, RelationGetDescr(rel), + stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1325,77 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, TupleDesc tupledesc, + List *newpublist, bool isadd) +{ + int i; + int npublications; + Datum *publications; + bool nulls[Natts_pg_subscription]; + Datum values[Natts_pg_subscription]; + List *publist = NIL; + ListCell *lc; + ArrayType *array; + + /* deconstruct the subpublications */ + heap_deform_tuple(tuple, tupledesc, values, nulls); + array = DatumGetArrayTypeP(values[Anum_pg_subscription_subpublications - 1]); + deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT, + &publications, NULL, &npublications); + + for (i = 0; i < npublications; i++) + publist = lappend(publist, + makeString(TextDatumGetCString((publications[i])))); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" do not in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 7574d545e0..d20e513518 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9615,6 +9615,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index dc2bb40926..9148ca9888 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3553,6 +3553,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v3-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v4 2/5] Introduce a new syntax to add/drop publications @ 2021-02-05 12:59 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-05 12:59 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 113 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 +++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 135 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 082f7855b8..7f296f76a8 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -46,6 +46,8 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *publications, + bool addpub); /* * Common option parsing function for CREATE and ALTER SUBSCRIPTION commands. @@ -857,6 +859,50 @@ AlterSubscription(AlterSubscriptionStmt *stmt) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1278,3 +1324,70 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications) return tablelist; } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool isadd) +{ + Datum datum; + bool isnull; + List *publist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (isadd) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" is already in subscription", + name))); + } + else + { + publist = list_delete_cell(publist, cell); + break; + } + } + } + + if (isadd) + publist = lappend(publist, makeString(name)); + else if (cell == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" does not exists in subscription", + name))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..77b28fc0cf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9619,6 +9619,26 @@ AlterSubscriptionStmt: n->options = $7; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name ENABLE_P { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..8021f66595 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3581,6 +3581,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.30.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v4-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v5 2/5] Introduce a new syntax to add/drop publications @ 2021-02-13 05:11 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-02-13 05:11 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 144 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 166 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 5cf874e0b4..ad271deb64 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -962,6 +963,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_subpublications(tup, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1546,3 +1594,99 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge ccurrent subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If isadd == true, we will add the list of publications into current + * subscription's publications. Otherwise, we will delete the list of + * publications from current subscription's publications. + */ +static List * +merge_subpublications(HeapTuple tuple, List *newpublist, bool addpub) +{ + Datum datum; + bool isnull; + List *publist = NIL; + List *errlist = NIL; + ListCell *lc; + + /* Get publications */ + datum = SysCacheGetAttr(SUBSCRIPTIONOID, + tuple, + Anum_pg_subscription_subpublications, + &isnull); + Assert(!isnull); + publist = textarray_to_stringlist(DatumGetArrayTypeP(datum)); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, publist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + errlist = lappend(errlist, makeString(name)); + else + publist = list_delete_cell(publist, cell); + break; + } + } + + if (addpub && cell == NULL) + publist = lappend(publist, makeString(name)); + else if (!addpub && cell == NULL) + errlist = lappend(errlist, makeString(name)); + } + + if (errlist != NIL) + { + StringInfoData errstr; + bool first = true; + int len = list_length(errlist); + + initStringInfo(&errstr); + if (len == 1) + appendStringInfo(&errstr, "publication name"); + else + appendStringInfo(&errstr, "publication names"); + + appendStringInfoString(&errstr, " \""); + foreach(lc, errlist) + { + char *name = strVal(lfirst(lc)); + + if (first) + appendStringInfo(&errstr, "%s", name); + else + appendStringInfo(&errstr, ", %s", name); + + first = false; + } + appendStringInfoChar(&errstr, '"'); + + if (addpub) + appendStringInfo(&errstr, " %s already in subscription", + len == 1 ? "is" : "are"); + else + appendStringInfo(&errstr, " %s no exist in subscription", + len == 1 ? "does" : "do"); + + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", errstr.data))); + } + + if (publist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return publist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dd72a9fc3c..e45f98d353 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v5-0003-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v8 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 153 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 175 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..368ee36961 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,108 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *plc; + + /* Check for duplicates. */ + foreach(plc, newpublist) + { + char *pname = strVal(lfirst(plc)); + + if (plc == lc) + break; + + if (strcmp(name, pname) == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("publication name \"%s\" used more than once", + pname))); + } + } + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index bc43641ffe..ab0468520c 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9640,6 +9640,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 68425eb2c0..83cb7d9fe4 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3593,6 +3593,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v8-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* [PATCH v7 1/4] Introduce a new syntax to add/drop publications @ 2021-03-07 12:56 Japin Li <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Japin Li @ 2021-03-07 12:56 UTC (permalink / raw) At present, if we want to update publications in subscription, we can use SET PUBLICATION, however, it requires supply all publications that exists and the new publications if we want to add new publications, it's inconvenient. The new syntax only supply the new publications. When the refresh is true, it only refresh the new publications. --- src/backend/commands/subscriptioncmds.c | 132 ++++++++++++++++++++++++ src/backend/parser/gram.y | 20 ++++ src/include/nodes/parsenodes.h | 2 + 3 files changed, 154 insertions(+) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index bfd3514546..bf1e579e6f 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -47,6 +47,7 @@ #include "utils/syscache.h" static List *fetch_table_list(WalReceiverConn *wrconn, List *publications); +static List *merge_publications(List *oldpublist, List *newpublist, bool addpub); static void ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err); @@ -964,6 +965,53 @@ AlterSubscription(AlterSubscriptionStmt *stmt, bool isTopLevel) break; } + case ALTER_SUBSCRIPTION_ADD_PUBLICATION: + case ALTER_SUBSCRIPTION_DROP_PUBLICATION: + { + bool copy_data = false; + bool isadd = stmt->kind == ALTER_SUBSCRIPTION_ADD_PUBLICATION; + bool refresh; + List *publist = NIL; + + publist = merge_publications(sub->publications, stmt->publication, isadd); + + parse_subscription_options(stmt->options, + NULL, /* no "connect" */ + NULL, NULL, /* no "enabled" */ + NULL, /* no "create_slot" */ + NULL, NULL, /* no "slot_name" */ + isadd ? ©_data : NULL, /* for drop, no "copy_data" */ + NULL, /* no "synchronous_commit" */ + &refresh, + NULL, NULL, /* no "binary" */ + NULL, NULL); /* no "streaming" */ + + values[Anum_pg_subscription_subpublications - 1] = + publicationListToArray(publist); + replaces[Anum_pg_subscription_subpublications - 1] = true; + + update_tuple = true; + + /* Refresh if user asked us to. */ + if (refresh) + { + if (!sub->enabled) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ALTER SUBSCRIPTION with refresh is not allowed for disabled subscriptions"), + errhint("Use ALTER SUBSCRIPTION ... SET PUBLICATION ... WITH (refresh = false)."))); + + PreventInTransactionBlock(isTopLevel, "ALTER SUBSCRIPTION with refresh"); + + /* Only refresh the added/dropped list of publications. */ + sub->publications = stmt->publication; + + AlterSubscription_refresh(sub, copy_data); + } + + break; + } + case ALTER_SUBSCRIPTION_REFRESH: { bool copy_data; @@ -1551,3 +1599,87 @@ ReportSlotConnectionError(List *rstates, Oid subid, char *slotname, char *err) errhint("Use %s to disassociate the subscription from the slot.", "ALTER SUBSCRIPTION ... SET (slot_name = NONE)"))); } + +/* + * Merge current subscription's publications and user specified publications + * by ADD/DROP PUBLICATIONS. + * + * If addpub is true, we will add the list of publications into oldpublist. + * Otherwise, we will delete the list of publications from oldpublist. + */ +static List * +merge_publications(List *oldpublist, List *newpublist, bool addpub) +{ + StringInfoData errstr; + int errstrcnt = 0; + ListCell *lc; + + initStringInfo(&errstr); + + foreach(lc, newpublist) + { + char *name = strVal(lfirst(lc)); + ListCell *cell = NULL; + + foreach(cell, oldpublist) + { + char *pubname = strVal(lfirst(cell)); + + if (strcmp(name, pubname) == 0) + { + if (addpub) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + else + oldpublist = list_delete_cell(oldpublist, cell); + + break; + } + } + + if (addpub && cell == NULL) + oldpublist = lappend(oldpublist, makeString(name)); + else if (!addpub && cell == NULL) + { + errstrcnt++; + + if (errstrcnt == 1) + appendStringInfo(&errstr, _("\"%s\""), name); + else + appendStringInfo(&errstr, _(", \"%s\""), name); + } + } + + if (errstrcnt >= 1) + { + if (addpub) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s is already present in the subscription", + "publications %s are already present in the subscription", + errstrcnt, errstr.data))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_plural("publication %s doesn't exist in the subscription", + "publications %s do not exist in the subscription", + errstrcnt, errstr.data))); + } + } + + if (oldpublist == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subscription must contain at least one publication"))); + + return oldpublist; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 652be0b96d..6d75e82096 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9609,6 +9609,26 @@ AlterSubscriptionStmt: n->options = $6; $$ = (Node *)n; } + | ALTER SUBSCRIPTION name ADD_P PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ADD_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DROP PUBLICATION name_list opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_DROP_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = $7; + $$ = (Node *)n; + } | ALTER SUBSCRIPTION name SET PUBLICATION name_list opt_definition { AlterSubscriptionStmt *n = diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 236832a2ca..e109607936 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3580,6 +3580,8 @@ typedef enum AlterSubscriptionType ALTER_SUBSCRIPTION_OPTIONS, ALTER_SUBSCRIPTION_CONNECTION, ALTER_SUBSCRIPTION_PUBLICATION, + ALTER_SUBSCRIPTION_ADD_PUBLICATION, + ALTER_SUBSCRIPTION_DROP_PUBLICATION, ALTER_SUBSCRIPTION_REFRESH, ALTER_SUBSCRIPTION_ENABLED } AlterSubscriptionType; -- 2.25.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=v7-0002-Test-ALTER-SUBSCRIPTION-.-ADD-DROP-PUBLICATION.patch ^ permalink raw reply [nested|flat] 72+ messages in thread
* Re: GIN pageinspect support for entry tree and posting tree @ 2026-01-01 14:00 Andrey Borodin <[email protected]> 2026-01-02 19:10 ` Re: GIN pageinspect support for entry tree and posting tree Kirill Reshke <[email protected]> 0 siblings, 1 reply; 72+ messages in thread From: Andrey Borodin @ 2026-01-01 14:00 UTC (permalink / raw) To: Kirill Reshke <[email protected]>; +Cc: pgsql-hackers > On 29 Dec 2025, at 17:51, Kirill Reshke <[email protected]> wrote: > > Attached v2 I've looked into the patch. The functionality is useful and seems to reflect pageinspect style. > Patch still is very raw, many things to improve. Yup, but let's work on it! Please update the documentation here [0]. Other AM's seems to defend from each other: if (!IS_INDEX(rel) || !IS_BTREE(rel)) or if (!IS_GIST(indexRel)). I don't see such check in new functions. B-tree also protects from temp tables of other sessions: if (RELATION_IS_OTHER_TEMP(rel)). gin_datapage_items() seem to ignore reloid, did you have some ideas how to use it? In gin_entrypage_items() buf and tmpTupdesc seem to be recreated for every offset, can we reuse them? gin_entrypage_items() errors out with "input page is not a valid GIN data leaf page", but function is for entry pages. There's no tests for gin_datapage_items(). There's a typo "unsuppoerted" and "rejrect" in gin.sql. gin_entrypage_items() emits elog(NOTICE, "page is deleted"), but gin_datapage_items() does not. Also, note that corresponding GiST code does "OffsetNumber maxoff = InvalidOffsetNumber;", but gin_entrypage_items() has no this initialization. I'd convert Assert(!isnull) into if+elog: inspected data might be corrupted. Thanks! Best regards, Andrey Borodin. [0] https://www.postgresql.org/docs/current/pageinspect.html#PAGEINSPECT-GIN-FUNCS ^ permalink raw reply [nested|flat] 72+ messages in thread
* Re: GIN pageinspect support for entry tree and posting tree 2026-01-01 14:00 Re: GIN pageinspect support for entry tree and posting tree Andrey Borodin <[email protected]> @ 2026-01-02 19:10 ` Kirill Reshke <[email protected]> 0 siblings, 0 replies; 72+ messages in thread From: Kirill Reshke @ 2026-01-02 19:10 UTC (permalink / raw) To: Andrey Borodin <[email protected]>; +Cc: pgsql-hackers; Roman Khapov <[email protected]> On Thu, 1 Jan 2026 at 19:00, Andrey Borodin <[email protected]> wrote: > > > > On 29 Dec 2025, at 17:51, Kirill Reshke <[email protected]> wrote: > > > > Attached v2 > > I've looked into the patch. > The functionality is useful and seems to reflect pageinspect style. Thank you for taking a look. > > Patch still is very raw, many things to improve. > > Yup, but let's work on it! > > Please update the documentation here [0]. > > Other AM's seems to defend from each other: if (!IS_INDEX(rel) || !IS_BTREE(rel)) or if (!IS_GIST(indexRel)). I don't see such check in new functions. B-tree also protects from temp tables of other sessions: if (RELATION_IS_OTHER_TEMP(rel)). I have added protections for functions that accept regclass. RELATION_IS_OTHER_TEMP is unnecessary here, because generic get_raw_page functions already take care of that. > gin_datapage_items() seem to ignore reloid, did you have some ideas how to use it? Yes, I removed that 'reloid'. It was of no use. > In gin_entrypage_items() buf and tmpTupdesc seem to be recreated for every offset, can we reuse them? Well, I did not get it, how we can reuse them, but v3 now releases memory after use. For tmpTupdesc, the issue here is that GIN entry tree pages can have tuples which need different tuple descriptors to access data, on the same page. That's why I re-evaluate on every offset. I think caching tuple descriptors here would be a cure worse than a disease. Example from my first email here: ``` reshke=# select * from gin_entrypage_items(get_raw_page('x_i_j_idx', 1), 'x_i_j_idx'::regclass); itemoffset | downlink | tids | keys ------------+----------+------+------------------------------------ 1 | (3,0) | {} | i=113 2 | (5,0) | {} | j=34173cb38f07f89ddbebc2ac9128303f 3 | (2,0) | {} | j=a0a080f42e6f13b3a2df133f073095dd 4 | (4,0) | {} | j=fc490ca45c00b1249bbe3554a4fdf6fb (4 rows) ``` To display that, we need different tupdescs for offset 1 & 2. for offsets 3&4 we can reuse tupdesc from offset 2, but this would (unnecessary?) increase patch complexity. WDYT? > gin_entrypage_items() errors out with "input page is not a valid GIN data leaf page", but function is for entry pages. I have added more sanity checks and regression tests for them. > There's no tests for gin_datapage_items(). I have added them in v3. My main concern here was that we have to generate many tuples to trigger GIN to create an internal posting tree page, It is also block-size-dependent, and will not work for values other than 8192, actually. Maybe we need to compute the number of inserted tuples in-flight? > There's a typo "unsuppoerted" and "rejrect" in gin.sql. Fxd > gin_entrypage_items() emits elog(NOTICE, "page is deleted"), but gin_datapage_items() does not. This is stupid copy-paste from GiST pageinspect. My fault, removed. (GiST deleted page do not store info to compute max offset correctly, while GIN does store maxOffset in its Opaque info) > Also, note that corresponding GiST code does "OffsetNumber maxoff = InvalidOffsetNumber;", but gin_entrypage_items() has no this initialization. It actually does but in a separate line, is this a problem? > I'd convert Assert(!isnull) into if+elog: inspected data might be corrupted. I do not have a strong opinion here, but we are implementing pageinspect here, not amcheck? I can only see one ereport with corruption error code in pageinspect, and it is for HASH indexes. Anyway, I changed assert to if-elog > Thanks! > > > Best regards, Andrey Borodin. > > > [0] https://www.postgresql.org/docs/current/pageinspect.html#PAGEINSPECT-GIN-FUNCS PFA v3. I added you and Romka (cc-ed) as reviewers. Romka did a review of v1 off-list which triggered me to actually bump this thread. -- Best regards, Kirill Reshke Attachments: [application/octet-stream] v3-0001-GIN-pageinspect-support-for-entry-tree-and-postin.patch (20.8K, ../../CALdSSPh2xBThU2vpaiQrsUSxqdH6psxuSa3kXVJHSNTkj3_DgA@mail.gmail.com/2-v3-0001-GIN-pageinspect-support-for-entry-tree-and-postin.patch) download | inline diff: From bb80ecd61a830bc24b297d486a99d419ad77fc5c Mon Sep 17 00:00:00 2001 From: reshke <[email protected]> Date: Mon, 13 Oct 2025 20:14:26 +0000 Subject: [PATCH v3] GIN pageinspect support for entry tree and posting tree internal pages This patch provides new version for pageinspect contrib module including two new functions: * gin_entrypage_items. * gin_datapage_items. These two functions can be used to examine GIN entry tree and posting tree pages. Namely, gin_entrypage_items can be used of both leaf and non-leaf entry tree pages. gin_datapage_items is provided in pairs with already-existing gin_leafpage_items to examine non-leaf posting tree pages. We keep the different functions here mainly because of different GIN pages layoff. Note that fast-list pages are out of scope of this patch. Reviewed-by: Andrey Borodin [email protected] Reviewed-by: Roman Khapov [email protected] --- contrib/pageinspect/Makefile | 2 +- contrib/pageinspect/expected/gin.out | 84 ++++- contrib/pageinspect/ginfuncs.c | 337 ++++++++++++++++++ .../pageinspect/pageinspect--1.13--1.14.sql | 28 ++ contrib/pageinspect/pageinspect.control | 2 +- contrib/pageinspect/sql/gin.sql | 34 +- 6 files changed, 481 insertions(+), 6 deletions(-) create mode 100644 contrib/pageinspect/pageinspect--1.13--1.14.sql diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index eae989569d0..09774fd340c 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -13,7 +13,7 @@ OBJS = \ rawpage.o EXTENSION = pageinspect -DATA = pageinspect--1.12--1.13.sql \ +DATA = pageinspect--1.13--1.14.sql pageinspect--1.12--1.13.sql \ pageinspect--1.11--1.12.sql pageinspect--1.10--1.11.sql \ pageinspect--1.9--1.10.sql pageinspect--1.8--1.9.sql \ pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \ diff --git a/contrib/pageinspect/expected/gin.out b/contrib/pageinspect/expected/gin.out index ff1da6a5a17..97057e37529 100644 --- a/contrib/pageinspect/expected/gin.out +++ b/contrib/pageinspect/expected/gin.out @@ -1,6 +1,8 @@ -CREATE TABLE test1 (x int, y int[]); -INSERT INTO test1 VALUES (1, ARRAY[11, 111]); +CREATE TABLE test1 (x int, y int[], z text[]); +INSERT INTO test1 VALUES (1, ARRAY[11, 111], ARRAY['a', 'b', 'c']); CREATE INDEX test1_y_idx ON test1 USING gin (y) WITH (fastupdate = off); +CREATE INDEX test2_y_z_idx ON test1 USING gin (y, z) WITH (fastupdate = off); +CREATE INDEX test3_y_z_idx ON test1 USING gin (y, z) WITH (fastupdate = on); \x SELECT * FROM gin_metapage_info(get_raw_page('test1_y_idx', 0)); -[ RECORD 1 ]----+----------- @@ -27,6 +29,45 @@ flags | {leaf} SELECT * FROM gin_leafpage_items(get_raw_page('test1_y_idx', 1)); ERROR: input page is not a compressed GIN data leaf page DETAIL: Flags 0002, expected 0083 +SELECT * FROM gin_entrypage_items(get_raw_page('test1_y_idx', 1), 'test1_y_idx'::regclass); +-[ RECORD 1 ]-------------- +itemoffset | 1 +downlink | (2147483664,1) +tids | {"(0,1)"} +keys | y=11 +-[ RECORD 2 ]-------------- +itemoffset | 2 +downlink | (2147483664,1) +tids | {"(0,1)"} +keys | y=111 + +SELECT * FROM gin_entrypage_items(get_raw_page('test2_y_z_idx', 1), 'test2_y_z_idx'::regclass); +-[ RECORD 1 ]-------------- +itemoffset | 1 +downlink | (2147483664,1) +tids | {"(0,1)"} +keys | y=11 +-[ RECORD 2 ]-------------- +itemoffset | 2 +downlink | (2147483664,1) +tids | {"(0,1)"} +keys | y=111 +-[ RECORD 3 ]-------------- +itemoffset | 3 +downlink | (2147483664,1) +tids | {"(0,1)"} +keys | z=a +-[ RECORD 4 ]-------------- +itemoffset | 4 +downlink | (2147483664,1) +tids | {"(0,1)"} +keys | z=b +-[ RECORD 5 ]-------------- +itemoffset | 5 +downlink | (2147483664,1) +tids | {"(0,1)"} +keys | z=c + INSERT INTO test1 SELECT x, ARRAY[1,10] FROM generate_series(2,10000) x; SELECT COUNT(*) > 0 FROM gin_leafpage_items(get_raw_page('test1_y_idx', @@ -35,6 +76,23 @@ FROM gin_leafpage_items(get_raw_page('test1_y_idx', -[ RECORD 1 ] ?column? | t +-- Now test posting tree non-leaf page. +-- This requires inserting many tuples on a single leaf page to trigger page split. +CREATE TABLE test_data_page(i INT[]); +CREATE INDEX test_data_page_i_idx ON test_data_page USING gin(i) WITH (fastupdate = off); +INSERT INTO test_data_page SELECT ARRAY[1] FROM generate_series(1, 10000); +-- For this index, block 0 is metapage, block 1 is entry tree, block 2 is +-- posting tree non-leaf page and block 3 & 4 are compressed data leaf pages. +SELECT * FROM gin_datapage_items(get_raw_page('test_data_page_i_idx', 2)); +-[ RECORD 1 ]------- +itemoffset | 1 +downlink | 4 +item_tid | (44,83) +-[ RECORD 2 ]------- +itemoffset | 2 +downlink | 3 +item_tid | (0,0) + -- Failure with various modes. -- Suppress the DETAIL message, to allow the tests to work across various -- page sizes and architectures. @@ -54,12 +112,34 @@ ERROR: input page is not a valid GIN data leaf page SELECT * FROM gin_leafpage_items(get_raw_page('test1', 0)); ERROR: input page is not a valid GIN data leaf page \set VERBOSITY default +-- Reject unsupported page types in gin_entrypage_items. +SELECT * FROM gin_entrypage_items(get_raw_page('test2_y_z_idx', 0), 'test2_y_z_idx'::regclass); +ERROR: gin_entrypage_items is unsupported for metapage +-- Check the error message for the internal posting tree page. +SELECT * FROM gin_entrypage_items(get_raw_page('test_data_page_i_idx', 2), 'test_data_page_i_idx'::regclass); +ERROR: input page is not a GIN entry tree page +HINT: This appears to be a GIN posting tree page. Please use gin_datapage_items +-- insert new row to trigger new (fast-list) page allocation. +INSERT INTO test1 VALUES (1, ARRAY[11, 111], ARRAY['a', 'b', 'c']); +-- double check that the new page is fast-list. +SELECT * FROM gin_page_opaque_info(get_raw_page('test3_y_z_idx', 2)); +-[ RECORD 1 ]------------------ +rightlink | 3 +maxoff | 120 +flags | {list,list_fullrow} + +-- reject fast-list pages. +SELECT * FROM gin_entrypage_items(get_raw_page('test3_y_z_idx', 3), 'test3_y_z_idx'::regclass); +ERROR: gin_entrypage_items is unsupported for fast list pages -- Tests with all-zero pages. SHOW block_size \gset SELECT gin_leafpage_items(decode(repeat('00', :block_size), 'hex')); -[ RECORD 1 ]------+- gin_leafpage_items | +SELECT gin_datapage_items(decode(repeat('00', :block_size), 'hex')); +(0 rows) + SELECT gin_metapage_info(decode(repeat('00', :block_size), 'hex')); -[ RECORD 1 ]-----+- gin_metapage_info | diff --git a/contrib/pageinspect/ginfuncs.c b/contrib/pageinspect/ginfuncs.c index ebcc2b3db5c..0fb98c0efd6 100644 --- a/contrib/pageinspect/ginfuncs.c +++ b/contrib/pageinspect/ginfuncs.c @@ -11,18 +11,27 @@ #include "access/gin_private.h" #include "access/htup_details.h" +#include "access/relation.h" +#include "access/tupdesc.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "miscadmin.h" #include "pageinspect.h" #include "utils/array.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/ruleutils.h" PG_FUNCTION_INFO_V1(gin_metapage_info); PG_FUNCTION_INFO_V1(gin_page_opaque_info); +PG_FUNCTION_INFO_V1(gin_entrypage_items); PG_FUNCTION_INFO_V1(gin_leafpage_items); +PG_FUNCTION_INFO_V1(gin_datapage_items); +#define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX) +#define IS_GIN(r) ((r)->rd_rel->relam == GIN_AM_OID) Datum gin_metapage_info(PG_FUNCTION_ARGS) @@ -175,6 +184,334 @@ typedef struct gin_leafpage_items_state GinPostingList *lastseg; } gin_leafpage_items_state; +Datum +gin_entrypage_items(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Oid indexRelid = PG_GETARG_OID(1); + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + Relation indexRel; + OffsetNumber maxoff, offset; + TupleDesc tupdesc; + bool oneCol; + Page page; + GinPageOpaque opaq; + StringInfoData buf; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + maxoff = InvalidOffsetNumber; + + InitMaterializedSRF(fcinfo, 0); + + /* Open the relation */ + indexRel = index_open(indexRelid, AccessShareLock); + + if (!IS_INDEX(indexRel) || !IS_GIN(indexRel)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a %s index", + RelationGetRelationName(indexRel), "GIN"))); + + page = get_page_from_raw(raw_page); + + if (PageIsNew(page)) + { + index_close(indexRel, AccessShareLock); + PG_RETURN_NULL(); + } + + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a valid GIN entry tree page"), + errdetail("Expected special size %d, got %d.", + (int) MAXALIGN(sizeof(GinPageOpaqueData)), + (int) PageGetSpecialSize(page)))); + + opaq = GinPageGetOpaque(page); + + + /* we only support entry tree in this function, check that */ + if (opaq->flags & GIN_META) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("gin_entrypage_items is unsupported for metapage"))); + + + if (opaq->flags & (GIN_LIST | GIN_LIST_FULLROW)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("gin_entrypage_items is unsupported for fast list pages"))); + + + if (opaq->flags & GIN_DATA) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a GIN entry tree page"), + errhint("This appears to be a GIN posting tree page. Please use gin_datapage_items"))); + + maxoff = PageGetMaxOffsetNumber(page); + + tupdesc = RelationGetDescr(indexRel); + oneCol = tupdesc->natts == 1; + + for (offset = FirstOffsetNumber; + offset <= maxoff; + offset = OffsetNumberNext(offset)) + { + OffsetNumber indAtt; + Datum values[4]; + bool nulls[4]; + int ndecoded, i; + Datum *tids_datum; + ItemPointer items_orig; + bool free_items_orig; + Datum attrVal; + Oid foutoid; + bool typisvarlena; + Oid typoid; + char* value; + bool nq; + char* tmp; + bool isnull; + IndexTuple idxtuple; + ItemId iid = PageGetItemId(page, offset); + + if (!ItemIdIsValid(iid)) + elog(ERROR, "invalid ItemId"); + + idxtuple = (IndexTuple) PageGetItem(page, iid); + + memset(nulls, 0, sizeof(nulls)); + + values[0] = UInt16GetDatum(offset); + + if (oneCol) + { + indAtt = FirstOffsetNumber; + /* here we can safely reuse pg_class's tuple descriptor. */ + attrVal = index_getattr(idxtuple, FirstOffsetNumber, tupdesc, + &isnull); + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("invalud gin entry page tuple at offset %d", offset))); + } + else + { + TupleDesc tmpTupdesc; + Datum res; + Form_pg_attribute attr; + + /* orig tuple reuse is safe */ + + res = index_getattr(idxtuple, FirstOffsetNumber, tupdesc, + &isnull); + + /* we do not expect null for first attr in multi-column GIN */ + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("invalud gin entry page tuple at offset %d", offset))); + + indAtt = DatumGetUInt16(res); + + attr = TupleDescAttr(tupdesc, indAtt - 1); + + tmpTupdesc = CreateTemplateTupleDesc(2); + + TupleDescInitEntry(tmpTupdesc, (AttrNumber) 1, NULL, + INT2OID, -1, 0); + TupleDescInitEntry(tmpTupdesc, (AttrNumber) 2, NULL, + attr->atttypid, + attr->atttypmod, + attr->attndims); + TupleDescInitEntryCollation(tmpTupdesc, (AttrNumber) 2, + attr->attcollation); + + attrVal = index_getattr(idxtuple, OffsetNumberNext(FirstOffsetNumber), + tmpTupdesc, + &isnull); + + FreeTupleDesc(tmpTupdesc); + } + + initStringInfo(&buf); + appendStringInfo(&buf, "%s=", quote_identifier(TupleDescAttr(tupdesc, indAtt - 1)->attname.data)); + + if (!isnull) { + /* Most of this is copied from record_out(). */ + typoid = TupleDescAttr(tupdesc, indAtt - 1)->atttypid; + getTypeOutputInfo(typoid, &foutoid, &typisvarlena); + value = OidOutputFunctionCall(foutoid, attrVal); + + + /* Check whether we need double quotes for this value */ + nq = (value[0] == '\0'); /* force quotes for empty string */ + for (tmp = value; *tmp; tmp++) + { + char ch = *tmp; + + if (ch == '"' || ch == '\\' || + ch == '(' || ch == ')' || ch == ',' || + isspace((unsigned char) ch)) + { + nq = true; + break; + } + } + + /* And emit the string */ + if (nq) + appendStringInfoCharMacro(&buf, '"'); + for (tmp = value; *tmp; tmp++) + { + char ch = *tmp; + + if (ch == '"' || ch == '\\') + appendStringInfoCharMacro(&buf, ch); + appendStringInfoCharMacro(&buf, ch); + } + if (nq) + appendStringInfoCharMacro(&buf, '"'); + } + else + { + appendStringInfo(&buf, "NULL"); + } + + + values[3] = CStringGetTextDatum(buf.data); + resetStringInfo(&buf); + + if (GinIsPostingTree(idxtuple)) + { + values[1] = ItemPointerGetDatum(&idxtuple->t_tid); + nulls[2] = true; + } + else + { + values[1] = ItemPointerGetDatum(&idxtuple->t_tid); + /* Get list of item pointers from the tuple. */ + if (GinItupIsCompressed(idxtuple)) + { + items_orig = ginPostingListDecode((GinPostingList *) GinGetPosting(idxtuple), &ndecoded); + free_items_orig = true; + } + else + { + items_orig = (ItemPointer) GinGetPosting(idxtuple); + ndecoded = GinGetNPosting(idxtuple); + free_items_orig = false; + } + + tids_datum = (Datum *) palloc(ndecoded * sizeof(Datum)); + for (i = 0; i < ndecoded; i++) + tids_datum[i] = ItemPointerGetDatum(&items_orig[i]); + values[2] = PointerGetDatum(construct_array_builtin(tids_datum, ndecoded, TIDOID)); + + pfree(tids_datum); + + if (free_items_orig) + pfree(items_orig); + } + + /* Build and return the result tuple. */ + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + } + + relation_close(indexRel, AccessShareLock); + + return (Datum) 0; +} + + +Datum +gin_datapage_items(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + OffsetNumber maxoff, offset; + Page page; + GinPageOpaque opaq; + + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + + InitMaterializedSRF(fcinfo, 0); + page = get_page_from_raw(raw_page); + + if (PageIsNew(page)) + { + PG_RETURN_NULL(); + } + + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a valid GIN data leaf page"), + errdetail("Expected special size %d, got %d.", + (int) MAXALIGN(sizeof(GinPageOpaqueData)), + (int) PageGetSpecialSize(page)))); + + opaq = GinPageGetOpaque(page); + + + /* we only support posting tree non-leaf in this function, check that */ + + if (opaq->flags & (GIN_META)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("gin_datapage_items is unsupported for metapage"))); + + if (opaq->flags & (GIN_LIST | GIN_LIST_FULLROW)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("gin_datapage_items is unsupported for GIN fast update list"))); + + if (!(opaq->flags & GIN_DATA)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a GIN data tree page"))); + + if (opaq->flags & GIN_LEAF) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is a GIN data leaf tree page"))); + + maxoff = GinPageGetOpaque(page)->maxoff; + + for (offset = FirstOffsetNumber; + offset <= maxoff; + offset = OffsetNumberNext(offset)) + { + Datum values[3]; + bool nulls[3]; + PostingItem* item = GinDataPageGetPostingItem(page, offset); + + memset(nulls, 0, sizeof(nulls)); + + + values[0] = UInt16GetDatum(offset); + + values[1] = UInt32GetDatum(BlockIdGetBlockNumber(&item->child_blkno)); + values[2] = ItemPointerGetDatum(&item->key); + + /* Build and return the result tuple. */ + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + } + + return (Datum) 0; +} + Datum gin_leafpage_items(PG_FUNCTION_ARGS) { diff --git a/contrib/pageinspect/pageinspect--1.13--1.14.sql b/contrib/pageinspect/pageinspect--1.13--1.14.sql new file mode 100644 index 00000000000..72f5e9bbea7 --- /dev/null +++ b/contrib/pageinspect/pageinspect--1.13--1.14.sql @@ -0,0 +1,28 @@ +/* contrib/pageinspect/pageinspect--1.13--1.14.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.14'" to load this file. \quit + +-- +-- gin_entrypage_items() +-- +CREATE FUNCTION gin_entrypage_items(IN page bytea, IN reloid OID, + OUT itemoffset smallint, + OUT downlink tid, + OUT tids tid[], + OUT keys text) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gin_entrypage_items' +LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- gin_datapage_items() +-- +CREATE FUNCTION gin_datapage_items(IN page bytea, + OUT itemoffset smallint, + OUT downlink int, + OUT item_tid tid) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'gin_datapage_items' +LANGUAGE C STRICT PARALLEL SAFE; + diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control index cfc87feac03..aee3f598a9e 100644 --- a/contrib/pageinspect/pageinspect.control +++ b/contrib/pageinspect/pageinspect.control @@ -1,5 +1,5 @@ # pageinspect extension comment = 'inspect the contents of database pages at a low level' -default_version = '1.13' +default_version = '1.14' module_pathname = '$libdir/pageinspect' relocatable = true diff --git a/contrib/pageinspect/sql/gin.sql b/contrib/pageinspect/sql/gin.sql index b57466d7ebf..3e2738c71b0 100644 --- a/contrib/pageinspect/sql/gin.sql +++ b/contrib/pageinspect/sql/gin.sql @@ -1,6 +1,8 @@ -CREATE TABLE test1 (x int, y int[]); -INSERT INTO test1 VALUES (1, ARRAY[11, 111]); +CREATE TABLE test1 (x int, y int[], z text[]); +INSERT INTO test1 VALUES (1, ARRAY[11, 111], ARRAY['a', 'b', 'c']); CREATE INDEX test1_y_idx ON test1 USING gin (y) WITH (fastupdate = off); +CREATE INDEX test2_y_z_idx ON test1 USING gin (y, z) WITH (fastupdate = off); +CREATE INDEX test3_y_z_idx ON test1 USING gin (y, z) WITH (fastupdate = on); \x @@ -11,6 +13,10 @@ SELECT * FROM gin_page_opaque_info(get_raw_page('test1_y_idx', 1)); SELECT * FROM gin_leafpage_items(get_raw_page('test1_y_idx', 1)); +SELECT * FROM gin_entrypage_items(get_raw_page('test1_y_idx', 1), 'test1_y_idx'::regclass); + +SELECT * FROM gin_entrypage_items(get_raw_page('test2_y_z_idx', 1), 'test2_y_z_idx'::regclass); + INSERT INTO test1 SELECT x, ARRAY[1,10] FROM generate_series(2,10000) x; SELECT COUNT(*) > 0 @@ -18,6 +24,18 @@ FROM gin_leafpage_items(get_raw_page('test1_y_idx', (pg_relation_size('test1_y_idx') / current_setting('block_size')::bigint)::int - 1)); +-- Now test posting tree non-leaf page. +-- This requires inserting many tuples on a single leaf page to trigger page split. + +CREATE TABLE test_data_page(i INT[]); +CREATE INDEX test_data_page_i_idx ON test_data_page USING gin(i) WITH (fastupdate = off); + +INSERT INTO test_data_page SELECT ARRAY[1] FROM generate_series(1, 10000); + +-- For this index, block 0 is metapage, block 1 is entry tree, block 2 is +-- posting tree non-leaf page and block 3 & 4 are compressed data leaf pages. +SELECT * FROM gin_datapage_items(get_raw_page('test_data_page_i_idx', 2)); + -- Failure with various modes. -- Suppress the DETAIL message, to allow the tests to work across various -- page sizes and architectures. @@ -32,9 +50,21 @@ SELECT * FROM gin_page_opaque_info(get_raw_page('test1', 0)); SELECT * FROM gin_leafpage_items(get_raw_page('test1', 0)); \set VERBOSITY default +-- Reject unsupported page types in gin_entrypage_items. +SELECT * FROM gin_entrypage_items(get_raw_page('test2_y_z_idx', 0), 'test2_y_z_idx'::regclass); +-- Check the error message for the internal posting tree page. +SELECT * FROM gin_entrypage_items(get_raw_page('test_data_page_i_idx', 2), 'test_data_page_i_idx'::regclass); +-- insert new row to trigger new (fast-list) page allocation. +INSERT INTO test1 VALUES (1, ARRAY[11, 111], ARRAY['a', 'b', 'c']); +-- double check that the new page is fast-list. +SELECT * FROM gin_page_opaque_info(get_raw_page('test3_y_z_idx', 2)); +-- reject fast-list pages. +SELECT * FROM gin_entrypage_items(get_raw_page('test3_y_z_idx', 3), 'test3_y_z_idx'::regclass); + -- Tests with all-zero pages. SHOW block_size \gset SELECT gin_leafpage_items(decode(repeat('00', :block_size), 'hex')); +SELECT gin_datapage_items(decode(repeat('00', :block_size), 'hex')); SELECT gin_metapage_info(decode(repeat('00', :block_size), 'hex')); SELECT gin_page_opaque_info(decode(repeat('00', :block_size), 'hex')); -- 2.43.0 ^ permalink raw reply [nested|flat] 72+ messages in thread
end of thread, other threads:[~2026-01-02 19:10 UTC | newest] Thread overview: 72+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2021-01-26 07:43 [PATCH v3 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-05 12:59 [PATCH v4 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-02-13 05:11 [PATCH v5 2/5] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v8 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2021-03-07 12:56 [PATCH v7 1/4] Introduce a new syntax to add/drop publications Japin Li <[email protected]> 2026-01-01 14:00 Re: GIN pageinspect support for entry tree and posting tree Andrey Borodin <[email protected]> 2026-01-02 19:10 ` Re: GIN pageinspect support for entry tree and posting tree Kirill Reshke <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox