From 91e46dd7c05adf3bef68d5f20a624e3e4a8ce545 Mon Sep 17 00:00:00 2001 From: Mikhail Kharitonov Date: Tue, 12 Aug 2025 14:16:10 +0300 Subject: [PATCH v3 2/2] tests: TAP for leaf based old tuple flag with publish_via_partition_root --- .../t/036_partition_replica_identity.pl | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100755 src/test/subscription/t/036_partition_replica_identity.pl diff --git a/src/test/subscription/t/036_partition_replica_identity.pl b/src/test/subscription/t/036_partition_replica_identity.pl new file mode 100755 index 00000000000..f69bca5ebe2 --- /dev/null +++ b/src/test/subscription/t/036_partition_replica_identity.pl @@ -0,0 +1,135 @@ +# Test logical replication with publish_via_partition_root, +# where the parent has REPLICA IDENTITY FULL, but one partition does not. +# +# Expected behavior: +# - For partitions with REPLICA IDENTITY FULL, old tuple must be marked as 'O' and contain full row. +# - For partitions with REPLICA IDENTITY DEFAULT, old tuple should be marked as 'K' and contain only key columns. + +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +sub log_state +{ + my ($node, $label) = @_; + + my $rows = $node->safe_psql('postgres', q{ + SELECT tableoid::regclass AS part, + id, + to_char(ts,'YYYY-MM-DD') AS ts, + load + FROM part_table + ORDER BY 1,2; + }); + diag "----- $label: rows -----\n$rows\n"; +} + +my $pub = PostgreSQL::Test::Cluster->new('publisher'); +$pub->init(allows_streaming => 'logical'); +$pub->start; + +my $sub = PostgreSQL::Test::Cluster->new('subscriber'); +$sub->init; +$sub->start; + +$pub->safe_psql('postgres', q{ +create table part_table( + id int generated always as identity, + ts timestamp, + load text, + constraint part_table_pk primary key(id, ts) +) partition by range(ts); + +create table part_table_sect_1 partition of part_table + for values from ('2000-01-01') to ('2024-01-01'); +create table part_table_sect_2 partition of part_table + for values from ('2024-01-01') to (maxvalue); + +alter table part_table replica identity full; +alter table part_table_sect_1 replica identity full; + +create publication pub_part_table + for table part_table + with (publish_via_partition_root = true); +}); + +$pub->safe_psql('postgres', + q{select pg_create_logical_replication_slot('slot_test', 'pgoutput');}); + +$sub->safe_psql('postgres', q{ +create table part_table( + id int, + ts timestamp, + load text, + constraint part_table_pk primary key(id, ts) +) partition by range(ts); + +create table part_table_sect_1 partition of part_table + for values from ('2000-01-01') to ('2024-01-01'); +create table part_table_sect_2 partition of part_table + for values from ('2024-01-01') to (maxvalue); +}); + +my $connstr = $pub->connstr . ' dbname=postgres'; +$sub->safe_psql('postgres', qq{ +create subscription sub_part + connection '$connstr application_name=sub_part' + publication pub_part_table;}); + +$sub->wait_for_subscription_sync($pub, 'sub_part'); + +$pub->safe_psql('postgres', q{ +insert into part_table values (default, '2020-01-01 00:00', 'first'); +insert into part_table values (default, '2025-01-01 00:00', 'second'); +}); +$sub->wait_for_subscription_sync($pub, 'sub_part'); +diag("\n"); +log_state($pub, 'publisher after insert'); +log_state($sub, 'subscriber after insert'); + +$pub->safe_psql('postgres', + q{update part_table set ts = ts + interval '1 day';}); +$sub->wait_for_subscription_sync($pub, 'sub_part'); + +log_state($pub, 'publisher after update'); +log_state($sub, 'subscriber after update'); + +$pub->safe_psql('postgres', q{delete from part_table;}); +$sub->wait_for_subscription_sync($pub, 'sub_part'); + +log_state($pub, 'publisher after delete'); +log_state($sub, 'subscriber after delete'); + +my $wal = $pub->safe_psql('postgres', q{ +select string_agg(encode(data, 'escape'),'') + from pg_logical_slot_get_binary_changes( + 'slot_test', null, null, + 'proto_version','1', + 'publication_names','pub_part_table'); +}); + +diag("---- WAL stream ----\n$wal\n"); + +# 1: first partition has REPLICA IDENTITY FULL - full old tuple +# (see - https://www.postgresql.org/docs/current/protocol-logicalrep-message-formats.html#PROTOCOL-LOGICALREP-MESSAGE-FORMATS-UPDATE) +like( + $wal, + qr/U.*O.*first.*first/s, + 'partition WITH REPLICA IDENTITY FULL contains full old tuple' +); + +# 2: second partition has REPLICA IDENTITY DEFAULT - only keys expected. +if ($wal =~ /U.*K.*second/s) +{ + pass("Tag K correctly used for partition with REPLICA IDENTITY DEFAULT"); +} +elsif ($wal =~ /(U.*O.*second)/s) +{ + my $blk = $1; + my $count = () = $blk =~ /second/g; + is($count, 2, "Tag O used but this partition with REPLICA IDENTITY DEFAULT"); +} + +done_testing(); -- 2.34.1