Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b9c130a

Browse files
committed
Have logical replication subscriber fire column triggers
The logical replication apply worker did not fire per-column update triggers because the updatedCols bitmap in the RTE was not populated. This fixes that. Reviewed-by: Euler Taveira <[email protected]> Discussion: https://www.postgresql.org/message-id/flat/21673e2d-597c-6afe-637e-e8b10425b240%402ndquadrant.com
1 parent 7b283d0 commit b9c130a

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

src/backend/replication/logical/worker.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ apply_handle_update(StringInfo s)
691691
bool has_oldtup;
692692
TupleTableSlot *localslot;
693693
TupleTableSlot *remoteslot;
694+
RangeTblEntry *target_rte;
694695
bool found;
695696
MemoryContext oldctx;
696697

@@ -721,6 +722,21 @@ apply_handle_update(StringInfo s)
721722
&estate->es_tupleTable);
722723
EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1);
723724

725+
/*
726+
* Populate updatedCols so that per-column triggers can fire. This could
727+
* include more columns than were actually changed on the publisher
728+
* because the logical replication protocol doesn't contain that
729+
* information. But it would for example exclude columns that only exist
730+
* on the subscriber, since we are not touching those.
731+
*/
732+
target_rte = list_nth(estate->es_range_table, 0);
733+
for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++)
734+
{
735+
if (newtup.changed[i])
736+
target_rte->updatedCols = bms_add_member(target_rte->updatedCols,
737+
i + 1 - FirstLowInvalidHeapAttributeNumber);
738+
}
739+
724740
PushActiveSnapshot(GetTransactionSnapshot());
725741
ExecOpenIndices(estate->es_result_relation_info, false);
726742

src/test/subscription/t/003_constraints.pl

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use warnings;
44
use PostgresNode;
55
use TestLib;
6-
use Test::More tests => 4;
6+
use Test::More tests => 6;
77

88
# Initialize publisher node
99
my $node_publisher = get_new_node('publisher');
@@ -81,14 +81,16 @@ BEGIN
8181
ELSE
8282
RETURN NULL;
8383
END IF;
84+
ELSIF (TG_OP = 'UPDATE') THEN
85+
RETURN NULL;
8486
ELSE
8587
RAISE WARNING 'Unknown action';
8688
RETURN NULL;
8789
END IF;
8890
END;
8991
\$\$ LANGUAGE plpgsql;
9092
CREATE TRIGGER filter_basic_dml_trg
91-
BEFORE INSERT ON tab_fk_ref
93+
BEFORE INSERT OR UPDATE OF bid ON tab_fk_ref
9294
FOR EACH ROW EXECUTE PROCEDURE filter_basic_dml_fn();
9395
ALTER TABLE tab_fk_ref ENABLE REPLICA TRIGGER filter_basic_dml_trg;
9496
});
@@ -99,10 +101,32 @@ BEGIN
99101

100102
$node_publisher->wait_for_catchup('tap_sub');
101103

102-
# The row should be skipped on subscriber
104+
# The trigger should cause the insert to be skipped on subscriber
105+
$result = $node_subscriber->safe_psql('postgres',
106+
"SELECT count(*), min(bid), max(bid) FROM tab_fk_ref;");
107+
is($result, qq(2|1|2), 'check replica insert trigger applied on subscriber');
108+
109+
# Update data
110+
$node_publisher->safe_psql('postgres',
111+
"UPDATE tab_fk_ref SET bid = 2 WHERE bid = 1;");
112+
113+
$node_publisher->wait_for_catchup('tap_sub');
114+
115+
# The trigger should cause the update to be skipped on subscriber
103116
$result = $node_subscriber->safe_psql('postgres',
104117
"SELECT count(*), min(bid), max(bid) FROM tab_fk_ref;");
105-
is($result, qq(2|1|2), 'check replica trigger applied on subscriber');
118+
is($result, qq(2|1|2), 'check replica update column trigger applied on subscriber');
119+
120+
# Update on a column not specified in the trigger, but it will trigger
121+
# anyway because logical replication ships all columns in an update.
122+
$node_publisher->safe_psql('postgres',
123+
"UPDATE tab_fk_ref SET id = 6 WHERE id = 1;");
124+
125+
$node_publisher->wait_for_catchup('tap_sub');
126+
127+
$result = $node_subscriber->safe_psql('postgres',
128+
"SELECT count(*), min(id), max(id) FROM tab_fk_ref;");
129+
is($result, qq(2|1|2), 'check column trigger applied on even for other column');
106130

107131
$node_subscriber->stop('fast');
108132
$node_publisher->stop('fast');

0 commit comments

Comments
 (0)