aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execReplication.c
diff options
context:
space:
mode:
authorAmit Kapila <akapila@postgresql.org>2024-12-04 09:45:18 +0530
committerAmit Kapila <akapila@postgresql.org>2024-12-04 09:45:18 +0530
commit87ce27de6963091f4a365f80bcdb06b9da098f00 (patch)
tree11927a574013d2b8edfd6f80298c4c6f23935580 /src/backend/executor/execReplication.c
parent77c189cdafe3873b7273149fbc490cc11c431cc3 (diff)
downloadpostgresql-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/executor/execReplication.c')
-rw-r--r--src/backend/executor/execReplication.c39
1 files changed, 31 insertions, 8 deletions
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 54025c9f150..cfdf2eedf4d 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -785,16 +785,27 @@ CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
return;
/*
- * It is only safe to execute UPDATE/DELETE when all columns, referenced
- * in the row filters from publications which the relation is in, are
- * valid - i.e. when all referenced columns are part of REPLICA IDENTITY
- * or the table does not publish UPDATEs or DELETEs.
+ * It is only safe to execute UPDATE/DELETE if the relation does not
+ * publish UPDATEs or DELETEs, or all the following conditions are
+ * satisfied:
+ *
+ * 1. All columns, referenced in the row filters from publications which
+ * the relation is in, are valid - i.e. when all referenced columns are
+ * part of REPLICA IDENTITY.
+ *
+ * 2. All columns, referenced in the column lists are valid - i.e. when
+ * all columns referenced in the REPLICA IDENTITY are covered by the
+ * column list.
+ *
+ * 3. All generated columns in REPLICA IDENTITY of the relation, are valid
+ * - i.e. when all these generated columns are published.
*
* XXX We could optimize it by first checking whether any of the
- * publications have a row filter for this relation. If not and relation
- * has replica identity then we can avoid building the descriptor but as
- * this happens only one time it doesn't seem worth the additional
- * complexity.
+ * publications have a row filter or column list for this relation, or if
+ * the relation contains a generated column. If none of these exist and
+ * the relation has replica identity then we can avoid building the
+ * descriptor but as this happens only one time it doesn't seem worth the
+ * additional complexity.
*/
RelationBuildPublicationDesc(rel, &pubdesc);
if (cmd == CMD_UPDATE && !pubdesc.rf_valid_for_update)
@@ -809,6 +820,12 @@ CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
errmsg("cannot update table \"%s\"",
RelationGetRelationName(rel)),
errdetail("Column list used by the publication does not cover the replica identity.")));
+ else if (cmd == CMD_UPDATE && !pubdesc.gencols_valid_for_update)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("cannot update table \"%s\"",
+ RelationGetRelationName(rel)),
+ errdetail("Replica identity consists of an unpublished generated column.")));
else if (cmd == CMD_DELETE && !pubdesc.rf_valid_for_delete)
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
@@ -821,6 +838,12 @@ CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
errmsg("cannot delete from table \"%s\"",
RelationGetRelationName(rel)),
errdetail("Column list used by the publication does not cover the replica identity.")));
+ else if (cmd == CMD_DELETE && !pubdesc.gencols_valid_for_delete)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("cannot delete from table \"%s\"",
+ RelationGetRelationName(rel)),
+ errdetail("Replica identity consists of an unpublished generated column.")));
/* If relation has replica identity we are always good. */
if (OidIsValid(RelationGetReplicaIndex(rel)))