Skip to content

Commit 40b1491

Browse files
author
Amit Kapila
committed
Fix incorrect output from pgoutput when using column lists.
For Updates and Deletes, we were not honoring the columns list for old tuple values while sending tuple data via pgoutput. This results in pgoutput emitting more columns than expected. This is not a problem for built-in logical replication as we simply ignore additional columns based on the relation information sent previously which didn't have those columns. However, some other users of pgoutput plugin may expect the columns as per the column list. Also, sending extra columns unnecessarily consumes network bandwidth defeating the purpose of the column list feature. Reported-by: Gunnar Morling Author: Hou Zhijie Reviewed-by: Amit Kapila Backpatch-through: 15 Discussion: https://postgr.es/m/CADGJaX9kiRZ-OH0EpWF5Fkyh1ZZYofoNRCrhapBfdk02tj5EKg@mail.gmail.com
1 parent 069de07 commit 40b1491

File tree

4 files changed

+42
-6
lines changed

4 files changed

+42
-6
lines changed

src/backend/replication/logical/proto.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ logicalrep_write_update(StringInfo out, TransactionId xid, Relation rel,
478478
pq_sendbyte(out, 'O'); /* old tuple follows */
479479
else
480480
pq_sendbyte(out, 'K'); /* old key follows */
481-
logicalrep_write_tuple(out, rel, oldslot, binary, NULL);
481+
logicalrep_write_tuple(out, rel, oldslot, binary, columns);
482482
}
483483

484484
pq_sendbyte(out, 'N'); /* new tuple follows */
@@ -531,7 +531,8 @@ logicalrep_read_update(StringInfo in, bool *has_oldtuple,
531531
*/
532532
void
533533
logicalrep_write_delete(StringInfo out, TransactionId xid, Relation rel,
534-
TupleTableSlot *oldslot, bool binary)
534+
TupleTableSlot *oldslot, bool binary,
535+
Bitmapset *columns)
535536
{
536537
Assert(rel->rd_rel->relreplident == REPLICA_IDENTITY_DEFAULT ||
537538
rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL ||
@@ -551,7 +552,7 @@ logicalrep_write_delete(StringInfo out, TransactionId xid, Relation rel,
551552
else
552553
pq_sendbyte(out, 'K'); /* old key follows */
553554

554-
logicalrep_write_tuple(out, rel, oldslot, binary, NULL);
555+
logicalrep_write_tuple(out, rel, oldslot, binary, columns);
555556
}
556557

557558
/*

src/backend/replication/pgoutput/pgoutput.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -1532,7 +1532,8 @@ pgoutput_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
15321532
break;
15331533
case REORDER_BUFFER_CHANGE_DELETE:
15341534
logicalrep_write_delete(ctx->out, xid, targetrel,
1535-
old_slot, data->binary);
1535+
old_slot, data->binary,
1536+
relentry->columns);
15361537
break;
15371538
default:
15381539
Assert(false);
@@ -1578,7 +1579,8 @@ pgoutput_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
15781579

15791580
OutputPluginPrepareWrite(ctx, true);
15801581
logicalrep_write_delete(ctx->out, xid, targetrel,
1581-
old_slot, data->binary);
1582+
old_slot, data->binary,
1583+
relentry->columns);
15821584
OutputPluginWrite(ctx, true);
15831585
}
15841586
else

src/include/replication/logicalproto.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ extern LogicalRepRelId logicalrep_read_update(StringInfo in,
220220
LogicalRepTupleData *newtup);
221221
extern void logicalrep_write_delete(StringInfo out, TransactionId xid,
222222
Relation rel, TupleTableSlot *oldslot,
223-
bool binary);
223+
bool binary, Bitmapset *columns);
224224
extern LogicalRepRelId logicalrep_read_delete(StringInfo in,
225225
LogicalRepTupleData *oldtup);
226226
extern void logicalrep_write_truncate(StringInfo out, TransactionId xid,

src/test/subscription/t/031_column_list.pl

+33
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,39 @@
11511151
4||),
11521152
'publication containing both parent and child relation');
11531153

1154+
# TEST: Only columns in the column list should exist in the old tuple of UPDATE
1155+
# and DELETE.
1156+
1157+
$node_publisher->safe_psql(
1158+
'postgres', qq(
1159+
CREATE TABLE test_oldtuple_col (a int PRIMARY KEY, b int, c int);
1160+
CREATE PUBLICATION pub_check_oldtuple FOR TABLE test_oldtuple_col (a, b);
1161+
INSERT INTO test_oldtuple_col VALUES(1, 2, 3);
1162+
SELECT * FROM pg_create_logical_replication_slot('test_slot', 'pgoutput');
1163+
UPDATE test_oldtuple_col SET a = 2;
1164+
DELETE FROM test_oldtuple_col;
1165+
));
1166+
1167+
1168+
# Check at 7th byte of binary data for the number of columns in the old tuple.
1169+
#
1170+
# 7 = 1 (count from 1) + 1 byte (message type) + 4 byte (relid) + 1 byte (flag
1171+
# for old key).
1172+
#
1173+
# The message type of UPDATE is 85('U').
1174+
# The message type of DELETE is 68('D').
1175+
$result = $node_publisher->safe_psql(
1176+
'postgres', qq(
1177+
SELECT substr(data, 7, 2) = int2send(2::smallint)
1178+
FROM pg_logical_slot_peek_binary_changes('test_slot', NULL, NULL,
1179+
'proto_version', '1',
1180+
'publication_names', 'pub_check_oldtuple')
1181+
WHERE get_byte(data, 0) = 85 OR get_byte(data, 0) = 68
1182+
));
1183+
1184+
is( $result, qq(t
1185+
t), 'check the number of columns in the old tuple');
1186+
11541187

11551188
# TEST: With a table included in multiple publications with different column
11561189
# lists, we should catch the error when creating the subscription.

0 commit comments

Comments
 (0)