diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9705460ebda..cad3a7f9ada 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" @@ -1332,8 +1333,9 @@ ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { ListCell *lc; - /* Assert that ExecInitStoredGenerated has been called. */ - Assert(relinfo->ri_GeneratedExprs != NULL); + /* In some code paths we can reach here before initializing the info */ + if (relinfo->ri_GeneratedExprs == NULL) + ExecInitStoredGenerated(relinfo, estate, CMD_UPDATE); foreach(lc, estate->es_resultrelinfo_extra) { ResultRelInfoExtra *rextra = (ResultRelInfoExtra *) lfirst(lc); diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index d92bbd1c845..07805542466 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -260,7 +260,7 @@ ExecCheckTIDVisible(EState *estate, * (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 83e29655310..7b20f14b72e 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 0662c55f082..3a92b718776 100644 --- a/src/test/subscription/t/011_generated.pl +++ b/src/test/subscription/t/011_generated.pl @@ -6,7 +6,7 @@ use strict; use warnings; use PostgresNode; use TestLib; -use Test::More tests => 2; +use Test::More tests => 3; # setup @@ -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,9 +55,43 @@ $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');