Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3f244d0

Browse files
committed
Make new GENERATED-expressions code more bulletproof.
In commit 8bf6ec3 I assumed that no code path could reach ExecGetExtraUpdatedCols without having gone through ExecInitStoredGenerated. That turns out not to be the case in logical replication: if there's an ON UPDATE trigger on the target table, trigger.c will call this code before anybody has set up its generated columns. Having seen that, I don't have a lot of faith in there not being other such paths. ExecGetExtraUpdatedCols can call ExecInitStoredGenerated for itself, as long as we are willing to assume that it is only called in CMD_UPDATE operations, which on the whole seems like a safer leap of faith. Per report from Vitaly Davydov. Discussion: https://postgr.es/m/d259d69652b8c2ff50e14cda3c236c7f@postgrespro.ru
1 parent 1334b79 commit 3f244d0

File tree

4 files changed

+50
-17
lines changed

4 files changed

+50
-17
lines changed

src/backend/executor/execUtils.c

+4-9
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "access/transam.h"
5353
#include "executor/executor.h"
5454
#include "executor/execPartition.h"
55+
#include "executor/nodeModifyTable.h"
5556
#include "jit/jit.h"
5657
#include "mb/pg_wchar.h"
5758
#include "miscadmin.h"
@@ -1342,15 +1343,9 @@ ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate)
13421343
Bitmapset *
13431344
ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate)
13441345
{
1345-
#ifdef USE_ASSERT_CHECKING
1346-
/* Verify that ExecInitStoredGenerated has been called if needed. */
1347-
Relation rel = relinfo->ri_RelationDesc;
1348-
TupleDesc tupdesc = RelationGetDescr(rel);
1349-
1350-
if (tupdesc->constr && tupdesc->constr->has_generated_stored)
1351-
Assert(relinfo->ri_GeneratedExprs != NULL);
1352-
#endif
1353-
1346+
/* In some code paths we can reach here before initializing the info */
1347+
if (relinfo->ri_GeneratedExprs == NULL)
1348+
ExecInitStoredGenerated(relinfo, estate, CMD_UPDATE);
13541349
return relinfo->ri_extraUpdatedCols;
13551350
}
13561351

src/backend/executor/nodeModifyTable.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ ExecCheckTIDVisible(EState *estate,
360360
* fields. (Currently, ri_extraUpdatedCols is consulted only in UPDATE,
361361
* but we might as well fill it for INSERT too.)
362362
*/
363-
static void
363+
void
364364
ExecInitStoredGenerated(ResultRelInfo *resultRelInfo,
365365
EState *estate,
366366
CmdType cmdtype)

src/include/executor/nodeModifyTable.h

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515

1616
#include "nodes/execnodes.h"
1717

18+
extern void ExecInitStoredGenerated(ResultRelInfo *resultRelInfo,
19+
EState *estate,
20+
CmdType cmdtype);
21+
1822
extern void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
1923
EState *estate, TupleTableSlot *slot,
2024
CmdType cmdtype);

src/test/subscription/t/011_generated.pl

+41-7
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
);
2626

2727
$node_subscriber->safe_psql('postgres',
28-
"CREATE TABLE tab1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 22) STORED)"
28+
"CREATE TABLE tab1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 22) STORED, c int)"
2929
);
3030

3131
# data for initial sync
@@ -55,11 +55,45 @@
5555

5656
$node_publisher->wait_for_catchup('sub1');
5757

58-
$result = $node_subscriber->safe_psql('postgres', "SELECT a, b FROM tab1");
59-
is( $result, qq(1|22
60-
2|44
61-
3|66
62-
4|88
63-
6|132), 'generated columns replicated');
58+
$result = $node_subscriber->safe_psql('postgres', "SELECT * FROM tab1");
59+
is( $result, qq(1|22|
60+
2|44|
61+
3|66|
62+
4|88|
63+
6|132|), 'generated columns replicated');
64+
65+
# try it with a subscriber-side trigger
66+
67+
$node_subscriber->safe_psql(
68+
'postgres', q{
69+
CREATE FUNCTION tab1_trigger_func() RETURNS trigger
70+
LANGUAGE plpgsql AS $$
71+
BEGIN
72+
NEW.c := NEW.a + 10;
73+
RETURN NEW;
74+
END $$;
75+
76+
CREATE TRIGGER test1 BEFORE INSERT OR UPDATE ON tab1
77+
FOR EACH ROW
78+
EXECUTE PROCEDURE tab1_trigger_func();
79+
80+
ALTER TABLE tab1 ENABLE REPLICA TRIGGER test1;
81+
});
82+
83+
$node_publisher->safe_psql('postgres', "INSERT INTO tab1 VALUES (7), (8)");
84+
85+
$node_publisher->safe_psql('postgres', "UPDATE tab1 SET a = 9 WHERE a = 7");
86+
87+
$node_publisher->wait_for_catchup('sub1');
88+
89+
$result =
90+
$node_subscriber->safe_psql('postgres', "SELECT * FROM tab1 ORDER BY 1");
91+
is( $result, qq(1|22|
92+
2|44|
93+
3|66|
94+
4|88|
95+
6|132|
96+
8|176|18
97+
9|198|19), 'generated columns replicated with trigger');
6498

6599
done_testing();

0 commit comments

Comments
 (0)