diff options
author | Amit Kapila <akapila@postgresql.org> | 2024-12-04 09:45:18 +0530 |
---|---|---|
committer | Amit Kapila <akapila@postgresql.org> | 2024-12-04 09:45:18 +0530 |
commit | 87ce27de6963091f4a365f80bcdb06b9da098f00 (patch) | |
tree | 11927a574013d2b8edfd6f80298c4c6f23935580 /src/backend/utils/cache/relcache.c | |
parent | 77c189cdafe3873b7273149fbc490cc11c431cc3 (diff) | |
download | postgresql-87ce27de6963091f4a365f80bcdb06b9da098f00.tar.gz postgresql-87ce27de6963091f4a365f80bcdb06b9da098f00.zip |
Ensure stored generated columns must be published when required.
Ensure stored generated columns that are part of REPLICA IDENTITY must be
published explicitly for UPDATE and DELETE operations to be published. We
can publish generated columns by listing them in the column list or by
enabling the publish_generated_columns option.
This commit changes the behavior of the test added in commit adedf54e65 by
giving an ERROR for the UPDATE operation in such cases. There is no way to
trigger the bug reported in commit adedf54e65 but we didn't remove the
corresponding code change because it is still relevant when replicating
changes from a publisher with version less than 18.
We decided not to backpatch this behavior change to avoid the risk of
breaking existing output plugins that may be sending generated columns by
default although we are not aware of any such plugin. Also, we didn't see
any reports related to this on STABLE branches which is another reason not
to backpatch this change.
Author: Shlok Kyal, Hou Zhijie
Reviewed-by: Vignesh C, Amit Kapila
Discussion: https://postgr.es/m/CANhcyEVw4V2Awe2AB6i0E5AJLNdASShGfdBLbUd1XtWDboymCA@mail.gmail.com
Diffstat (limited to 'src/backend/utils/cache/relcache.c')
-rw-r--r-- | src/backend/utils/cache/relcache.c | 57 |
1 files changed, 45 insertions, 12 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index d0892cee24d..422509f18d7 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -5706,12 +5706,19 @@ RelationGetExclusionInfo(Relation indexRelation, * Get the publication information for the given relation. * * Traverse all the publications which the relation is in to get the - * publication actions and validate the row filter expressions for such - * publications if any. We consider the row filter expression as invalid if it - * references any column which is not part of REPLICA IDENTITY. + * publication actions and validate: + * 1. The row filter expressions for such publications if any. We consider the + * row filter expression as invalid if it references any column which is not + * part of REPLICA IDENTITY. + * 2. The column list for such publication if any. We consider the column list + * invalid if REPLICA IDENTITY contains any column that is not part of it. + * 3. The generated columns of the relation for such publications. We consider + * any reference of an unpublished generated column in REPLICA IDENTITY as + * invalid. * * To avoid fetching the publication information repeatedly, we cache the - * publication actions and row filter validation information. + * publication actions, row filter validation information, column list + * validation information, and generated column validation information. */ void RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc) @@ -5734,6 +5741,8 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc) pubdesc->rf_valid_for_delete = true; pubdesc->cols_valid_for_update = true; pubdesc->cols_valid_for_delete = true; + pubdesc->gencols_valid_for_update = true; + pubdesc->gencols_valid_for_delete = true; return; } @@ -5748,6 +5757,8 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc) pubdesc->rf_valid_for_delete = true; pubdesc->cols_valid_for_update = true; pubdesc->cols_valid_for_delete = true; + pubdesc->gencols_valid_for_update = true; + pubdesc->gencols_valid_for_delete = true; /* Fetch the publication membership info. */ puboids = GetRelationPublications(relid); @@ -5777,6 +5788,8 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc) Oid pubid = lfirst_oid(lc); HeapTuple tup; Form_pg_publication pubform; + bool invalid_column_list; + bool invalid_gen_col; tup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pubid)); @@ -5811,18 +5824,27 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc) /* * Check if all columns are part of the REPLICA IDENTITY index or not. * - * If the publication is FOR ALL TABLES then it means the table has no - * column list and we can skip the validation. + * Check if all generated columns included in the REPLICA IDENTITY are + * published. */ - if (!pubform->puballtables && - (pubform->pubupdate || pubform->pubdelete) && - pub_collist_contains_invalid_column(pubid, relation, ancestors, - pubform->pubviaroot)) + if ((pubform->pubupdate || pubform->pubdelete) && + pub_contains_invalid_column(pubid, relation, ancestors, + pubform->pubviaroot, + pubform->pubgencols, + &invalid_column_list, + &invalid_gen_col)) { if (pubform->pubupdate) - pubdesc->cols_valid_for_update = false; + { + pubdesc->cols_valid_for_update = !invalid_column_list; + pubdesc->gencols_valid_for_update = !invalid_gen_col; + } + if (pubform->pubdelete) - pubdesc->cols_valid_for_delete = false; + { + pubdesc->cols_valid_for_delete = !invalid_column_list; + pubdesc->gencols_valid_for_delete = !invalid_gen_col; + } } ReleaseSysCache(tup); @@ -5846,6 +5868,17 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc) pubdesc->pubactions.pubdelete && pubdesc->pubactions.pubtruncate && !pubdesc->cols_valid_for_update && !pubdesc->cols_valid_for_delete) break; + + /* + * If we know everything is replicated and replica identity has an + * unpublished generated column, there is no point to check for other + * publications. + */ + if (pubdesc->pubactions.pubinsert && pubdesc->pubactions.pubupdate && + pubdesc->pubactions.pubdelete && pubdesc->pubactions.pubtruncate && + !pubdesc->gencols_valid_for_update && + !pubdesc->gencols_valid_for_delete) + break; } if (relation->rd_pubdesc) |