diff options
author | Amit Kapila | 2022-06-02 03:01:50 +0000 |
---|---|---|
committer | Amit Kapila | 2022-06-02 03:01:50 +0000 |
commit | fd0b9dcebda7b931a41ce5c8e86d13f2efd0af2e (patch) | |
tree | 22533094decc38cd00734d5e6da15b016735a052 /src/backend/replication/pgoutput/pgoutput.c | |
parent | 99f6f19799edbba5e0a73702c0a1bd13bdd285a2 (diff) |
Prohibit combining publications with different column lists.
Currently, we simply combine the column lists when publishing tables on
multiple publications and that can sometimes lead to unexpected behavior.
Say, if a column is published in any row-filtered publication, then the
values for that column are sent to the subscriber even for rows that don't
match the row filter, as long as the row matches the row filter for any
other publication, even if that other publication doesn't include the
column.
The main purpose of introducing a column list is to have statically
different shapes on publisher and subscriber or hide sensitive column
data. In both cases, it doesn't seem to make sense to combine column
lists.
So, we disallow the cases where the column list is different for the same
table when combining publications. It can be later extended to combine the
column lists for selective cases where required.
Reported-by: Alvaro Herrera
Author: Hou Zhijie
Reviewed-by: Amit Kapila
Discussion: https://postgr.es/m/[email protected]
Diffstat (limited to 'src/backend/replication/pgoutput/pgoutput.c')
-rw-r--r-- | src/backend/replication/pgoutput/pgoutput.c | 80 |
1 files changed, 40 insertions, 40 deletions
diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index 42c06af2391..8deae571433 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -979,30 +979,31 @@ pgoutput_column_list_init(PGOutputData *data, List *publications, RelationSyncEntry *entry) { ListCell *lc; + bool first = true; + Relation relation = RelationIdGetRelation(entry->publish_as_relid); /* * Find if there are any column lists for this relation. If there are, - * build a bitmap merging all the column lists. - * - * All the given publication-table mappings must be checked. + * build a bitmap using the column lists. * * Multiple publications might have multiple column lists for this * relation. * + * Note that we don't support the case where the column list is different + * for the same table when combining publications. See comments atop + * fetch_table_list. But one can later change the publication so we still + * need to check all the given publication-table mappings and report an + * error if any publications have a different column list. + * * FOR ALL TABLES and FOR ALL TABLES IN SCHEMA implies "don't use column - * list" so it takes precedence. + * list". */ foreach(lc, publications) { Publication *pub = lfirst(lc); HeapTuple cftuple = NULL; Datum cfdatum = 0; - - /* - * Assume there's no column list. Only if we find pg_publication_rel - * entry with a column list we'll switch it to false. - */ - bool pub_no_list = true; + Bitmapset *cols = NULL; /* * If the publication is FOR ALL TABLES then it is treated the same as @@ -1011,6 +1012,8 @@ pgoutput_column_list_init(PGOutputData *data, List *publications, */ if (!pub->alltables) { + bool pub_no_list = true; + /* * Check for the presence of a column list in this publication. * @@ -1024,51 +1027,48 @@ pgoutput_column_list_init(PGOutputData *data, List *publications, if (HeapTupleIsValid(cftuple)) { - /* - * Lookup the column list attribute. - * - * Note: We update the pub_no_list value directly, because if - * the value is NULL, we have no list (and vice versa). - */ + /* Lookup the column list attribute. */ cfdatum = SysCacheGetAttr(PUBLICATIONRELMAP, cftuple, Anum_pg_publication_rel_prattrs, &pub_no_list); - /* - * Build the column list bitmap in the per-entry context. - * - * We need to merge column lists from all publications, so we - * update the same bitmapset. If the column list is null, we - * interpret it as replicating all columns. - */ + /* Build the column list bitmap in the per-entry context. */ if (!pub_no_list) /* when not null */ { pgoutput_ensure_entry_cxt(data, entry); - entry->columns = pub_collist_to_bitmapset(entry->columns, - cfdatum, - entry->entry_cxt); + cols = pub_collist_to_bitmapset(cols, cfdatum, + entry->entry_cxt); + + /* + * If column list includes all the columns of the table, + * set it to NULL. + */ + if (bms_num_members(cols) == RelationGetNumberOfAttributes(relation)) + { + bms_free(cols); + cols = NULL; + } } + + ReleaseSysCache(cftuple); } } - /* - * Found a publication with no column list, so we're done. But first - * discard column list we might have from preceding publications. - */ - if (pub_no_list) + if (first) { - if (cftuple) - ReleaseSysCache(cftuple); - - bms_free(entry->columns); - entry->columns = NULL; - - break; + entry->columns = cols; + first = false; } - - ReleaseSysCache(cftuple); + else if (!bms_equal(entry->columns, cols)) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot use different column lists for table \"%s.%s\" in different publications", + get_namespace_name(RelationGetNamespace(relation)), + RelationGetRelationName(relation))); } /* loop all subscribed publications */ + + RelationClose(relation); } /* |