Skip to content

Commit 8c2bfd9

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 <euler@timbira.com.br> Discussion: https://www.postgresql.org/message-id/flat/21673e2d-597c-6afe-637e-e8b10425b240%402ndquadrant.com
1 parent 6c1860b commit 8c2bfd9

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
@@ -690,6 +690,7 @@ apply_handle_update(StringInfo s)
690690
bool has_oldtup;
691691
TupleTableSlot *localslot;
692692
TupleTableSlot *remoteslot;
693+
RangeTblEntry *target_rte;
693694
bool found;
694695
MemoryContext oldctx;
695696

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

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

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)