diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 8c82c716752..c33a3c0bec7 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -52,6 +52,7 @@ #include "access/transam.h" #include "executor/executor.h" #include "executor/execPartition.h" +#include "executor/nodeModifyTable.h" #include "jit/jit.h" #include "mb/pg_wchar.h" #include "miscadmin.h" @@ -1342,15 +1343,9 @@ ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { -#ifdef USE_ASSERT_CHECKING - /* Verify that ExecInitStoredGenerated has been called if needed. */ - Relation rel = relinfo->ri_RelationDesc; - TupleDesc tupdesc = RelationGetDescr(rel); - - if (tupdesc->constr && tupdesc->constr->has_generated_stored) - Assert(relinfo->ri_GeneratedExprs != NULL); -#endif - + /* In some code paths we can reach here before initializing the info */ + if (relinfo->ri_GeneratedExprs == NULL) + ExecInitStoredGenerated(relinfo, estate, CMD_UPDATE); return relinfo->ri_extraUpdatedCols; } diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 687a5422eab..f419c47065a 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -360,7 +360,7 @@ ExecCheckTIDVisible(EState *estate, * fields. (Currently, ri_extraUpdatedCols is consulted only in UPDATE, * but we might as well fill it for INSERT too.) */ -static void +void ExecInitStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, CmdType cmdtype) diff --git a/src/include/executor/nodeModifyTable.h b/src/include/executor/nodeModifyTable.h index 37344141e0b..e7eeb307d0f 100644 --- a/src/include/executor/nodeModifyTable.h +++ b/src/include/executor/nodeModifyTable.h @@ -15,6 +15,10 @@ #include "nodes/execnodes.h" +extern void ExecInitStoredGenerated(ResultRelInfo *resultRelInfo, + EState *estate, + CmdType cmdtype); + extern void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype); diff --git a/src/test/subscription/t/011_generated.pl b/src/test/subscription/t/011_generated.pl index 0feed10158a..7711be295a0 100644 --- a/src/test/subscription/t/011_generated.pl +++ b/src/test/subscription/t/011_generated.pl @@ -25,7 +25,7 @@ $node_publisher->safe_psql('postgres', ); $node_subscriber->safe_psql('postgres', - "CREATE TABLE tab1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 22) STORED)" + "CREATE TABLE tab1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 22) STORED, c int)" ); # data for initial sync @@ -55,11 +55,45 @@ $node_publisher->safe_psql('postgres', "UPDATE tab1 SET a = 6 WHERE a = 5"); $node_publisher->wait_for_catchup('sub1'); -$result = $node_subscriber->safe_psql('postgres', "SELECT a, b FROM tab1"); -is( $result, qq(1|22 -2|44 -3|66 -4|88 -6|132), 'generated columns replicated'); +$result = $node_subscriber->safe_psql('postgres', "SELECT * FROM tab1"); +is( $result, qq(1|22| +2|44| +3|66| +4|88| +6|132|), 'generated columns replicated'); + +# try it with a subscriber-side trigger + +$node_subscriber->safe_psql( + 'postgres', q{ +CREATE FUNCTION tab1_trigger_func() RETURNS trigger +LANGUAGE plpgsql AS $$ +BEGIN + NEW.c := NEW.a + 10; + RETURN NEW; +END $$; + +CREATE TRIGGER test1 BEFORE INSERT OR UPDATE ON tab1 + FOR EACH ROW + EXECUTE PROCEDURE tab1_trigger_func(); + +ALTER TABLE tab1 ENABLE REPLICA TRIGGER test1; +}); + +$node_publisher->safe_psql('postgres', "INSERT INTO tab1 VALUES (7), (8)"); + +$node_publisher->safe_psql('postgres', "UPDATE tab1 SET a = 9 WHERE a = 7"); + +$node_publisher->wait_for_catchup('sub1'); + +$result = + $node_subscriber->safe_psql('postgres', "SELECT * FROM tab1 ORDER BY 1"); +is( $result, qq(1|22| +2|44| +3|66| +4|88| +6|132| +8|176|18 +9|198|19), 'generated columns replicated with trigger'); done_testing();