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

Commit d5daae4

Browse files
committed
Fix construction of updated-columns bitmap in logical replication.
Commit b9c130a failed to apply the publisher-to-subscriber column mapping while checking which columns were updated. Perhaps less significantly, it didn't exclude dropped columns either. This could result in an incorrect updated-columns bitmap and thus wrong decisions about whether to fire column-specific triggers on the subscriber while applying updates. In HEAD (since commit 9de77b5), it could also result in accesses off the end of the colstatus array, as detected by buildfarm member skink. Fix the logic, and adjust 003_constraints.pl so that the problem is exposed in unpatched code. In HEAD, also add some assertions to check that we don't access off the ends of these newly variable-sized arrays. Back-patch to v10, as b9c130a was. Discussion: https://postgr.es/m/CAH2-Wz=79hKQ4++c5A060RYbjTHgiYTHz=fw6mptCtgghH2gJA@mail.gmail.com
1 parent d98c08c commit d5daae4

File tree

4 files changed

+29
-9
lines changed

4 files changed

+29
-9
lines changed

src/backend/replication/logical/proto.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple)
548548
/* Allocate space for per-column values; zero out unused StringInfoDatas */
549549
tuple->colvalues = (StringInfoData *) palloc0(natts * sizeof(StringInfoData));
550550
tuple->colstatus = (char *) palloc(natts * sizeof(char));
551+
tuple->ncols = natts;
551552

552553
/* Read the data */
553554
for (i = 0; i < natts; i++)

src/backend/replication/logical/worker.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ slot_store_data(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
354354
{
355355
StringInfo colvalue = &tupleData->colvalues[remoteattnum];
356356

357+
Assert(remoteattnum < tupleData->ncols);
358+
357359
errarg.local_attnum = i;
358360
errarg.remote_attnum = remoteattnum;
359361

@@ -477,6 +479,8 @@ slot_modify_data(TupleTableSlot *slot, TupleTableSlot *srcslot,
477479
if (remoteattnum < 0)
478480
continue;
479481

482+
Assert(remoteattnum < tupleData->ncols);
483+
480484
if (tupleData->colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED)
481485
{
482486
StringInfo colvalue = &tupleData->colvalues[remoteattnum];
@@ -831,9 +835,17 @@ apply_handle_update(StringInfo s)
831835
target_rte = list_nth(estate->es_range_table, 0);
832836
for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++)
833837
{
834-
if (newtup.colstatus[i] != LOGICALREP_COLUMN_UNCHANGED)
835-
target_rte->updatedCols = bms_add_member(target_rte->updatedCols,
836-
i + 1 - FirstLowInvalidHeapAttributeNumber);
838+
Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i);
839+
int remoteattnum = rel->attrmap->attnums[i];
840+
841+
if (!att->attisdropped && remoteattnum >= 0)
842+
{
843+
Assert(remoteattnum < newtup.ncols);
844+
if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED)
845+
target_rte->updatedCols =
846+
bms_add_member(target_rte->updatedCols,
847+
i + 1 - FirstLowInvalidHeapAttributeNumber);
848+
}
837849
}
838850

839851
fill_extraUpdatedCols(target_rte, RelationGetDescr(rel->localrel));

src/include/replication/logicalproto.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,18 @@
2727
#define LOGICALREP_PROTO_MIN_VERSION_NUM 1
2828
#define LOGICALREP_PROTO_VERSION_NUM 1
2929

30-
/* Tuple coming via logical replication. */
30+
/*
31+
* This struct stores a tuple received via logical replication.
32+
* Keep in mind that the columns correspond to the *remote* table.
33+
*/
3134
typedef struct LogicalRepTupleData
3235
{
3336
/* Array of StringInfos, one per column; some may be unused */
3437
StringInfoData *colvalues;
3538
/* Array of markers for null/unchanged/text/binary, one per column */
3639
char *colstatus;
40+
/* Length of above arrays */
41+
int ncols;
3742
} LogicalRepTupleData;
3843

3944
/* Possible values for LogicalRepTupleData.colstatus[colnum] */

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919
$node_publisher->safe_psql('postgres',
2020
"CREATE TABLE tab_fk (bid int PRIMARY KEY);");
2121
$node_publisher->safe_psql('postgres',
22-
"CREATE TABLE tab_fk_ref (id int PRIMARY KEY, bid int REFERENCES tab_fk (bid));"
22+
"CREATE TABLE tab_fk_ref (id int PRIMARY KEY, junk text, bid int REFERENCES tab_fk (bid));"
2323
);
2424

25-
# Setup structure on subscriber
25+
# Setup structure on subscriber; column order intentionally different
2626
$node_subscriber->safe_psql('postgres',
2727
"CREATE TABLE tab_fk (bid int PRIMARY KEY);");
2828
$node_subscriber->safe_psql('postgres',
29-
"CREATE TABLE tab_fk_ref (id int PRIMARY KEY, bid int REFERENCES tab_fk (bid));"
29+
"CREATE TABLE tab_fk_ref (id int PRIMARY KEY, bid int REFERENCES tab_fk (bid), junk text);"
3030
);
3131

3232
# Setup logical replication
@@ -42,8 +42,10 @@
4242

4343
$node_publisher->safe_psql('postgres',
4444
"INSERT INTO tab_fk (bid) VALUES (1);");
45+
# "junk" value is meant to be large enough to force out-of-line storage
4546
$node_publisher->safe_psql('postgres',
46-
"INSERT INTO tab_fk_ref (id, bid) VALUES (1, 1);");
47+
"INSERT INTO tab_fk_ref (id, bid, junk) VALUES (1, 1, repeat(pi()::text,20000));"
48+
);
4749

4850
$node_publisher->wait_for_catchup('tap_sub');
4951

@@ -128,7 +130,7 @@ BEGIN
128130
$result = $node_subscriber->safe_psql('postgres',
129131
"SELECT count(*), min(id), max(id) FROM tab_fk_ref;");
130132
is($result, qq(2|1|2),
131-
'check column trigger applied on even for other column');
133+
'check column trigger applied even on update for other column');
132134

133135
$node_subscriber->stop('fast');
134136
$node_publisher->stop('fast');

0 commit comments

Comments
 (0)