1294 lines
38 KiB
Perl
1294 lines
38 KiB
Perl
# Copyright (c) 2022-2023, PostgreSQL Global Development Group
|
|
|
|
# Test partial-column publication of tables
|
|
use strict;
|
|
use warnings;
|
|
use PostgreSQL::Test::Cluster;
|
|
use PostgreSQL::Test::Utils;
|
|
use Test::More;
|
|
|
|
# create publisher node
|
|
my $node_publisher = PostgreSQL::Test::Cluster->new('publisher');
|
|
$node_publisher->init(allows_streaming => 'logical');
|
|
$node_publisher->start;
|
|
|
|
# create subscriber node
|
|
my $node_subscriber = PostgreSQL::Test::Cluster->new('subscriber');
|
|
$node_subscriber->init(allows_streaming => 'logical');
|
|
$node_subscriber->append_conf('postgresql.conf',
|
|
qq(max_logical_replication_workers = 6));
|
|
$node_subscriber->start;
|
|
|
|
my $publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
|
|
my $offset = 0;
|
|
|
|
# setup tables on both nodes
|
|
|
|
# tab1: simple 1:1 replication
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab1 (a int PRIMARY KEY, "B" int, c int)
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab1 (a int PRIMARY KEY, "B" int, c int)
|
|
));
|
|
|
|
# tab2: replication from regular to table with fewer columns
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab2 (a int PRIMARY KEY, b varchar, c int);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab2 (a int PRIMARY KEY, b varchar)
|
|
));
|
|
|
|
# tab3: simple 1:1 replication with weird column names
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab3 ("a'" int PRIMARY KEY, "B" varchar, "c'" int)
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab3 ("a'" int PRIMARY KEY, "c'" int)
|
|
));
|
|
|
|
# test_part: partitioned tables, with partitioning (including multi-level
|
|
# partitioning, and fewer columns on the subscriber)
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part (a int PRIMARY KEY, b text, c timestamptz) PARTITION BY LIST (a);
|
|
CREATE TABLE test_part_1_1 PARTITION OF test_part FOR VALUES IN (1,2,3,4,5,6);
|
|
CREATE TABLE test_part_2_1 PARTITION OF test_part FOR VALUES IN (7,8,9,10,11,12) PARTITION BY LIST (a);
|
|
CREATE TABLE test_part_2_2 PARTITION OF test_part_2_1 FOR VALUES IN (7,8,9,10);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part (a int PRIMARY KEY, b text) PARTITION BY LIST (a);
|
|
CREATE TABLE test_part_1_1 PARTITION OF test_part FOR VALUES IN (1,2,3,4,5,6);
|
|
CREATE TABLE test_part_2_1 PARTITION OF test_part FOR VALUES IN (7,8,9,10,11,12) PARTITION BY LIST (a);
|
|
CREATE TABLE test_part_2_2 PARTITION OF test_part_2_1 FOR VALUES IN (7,8,9,10);
|
|
));
|
|
|
|
# tab4: table with user-defined enum types
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TYPE test_typ AS ENUM ('blue', 'red');
|
|
CREATE TABLE tab4 (a INT PRIMARY KEY, b test_typ, c int, d text);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TYPE test_typ AS ENUM ('blue', 'red');
|
|
CREATE TABLE tab4 (a INT PRIMARY KEY, b test_typ, d text);
|
|
));
|
|
|
|
|
|
# TEST: create publication and subscription for some of the tables with
|
|
# column lists
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE PUBLICATION pub1
|
|
FOR TABLE tab1 (a, "B"), tab3 ("a'", "c'"), test_part (a, b), tab4 (a, b, d)
|
|
WITH (publish_via_partition_root = 'true');
|
|
));
|
|
|
|
# check that we got the right prattrs values for the publication in the
|
|
# pg_publication_rel catalog (order by relname, to get stable ordering)
|
|
my $result = $node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
SELECT relname, prattrs
|
|
FROM pg_publication_rel pb JOIN pg_class pc ON(pb.prrelid = pc.oid)
|
|
ORDER BY relname
|
|
));
|
|
|
|
is( $result, qq(tab1|1 2
|
|
tab3|1 3
|
|
tab4|1 2 4
|
|
test_part|1 2), 'publication relation updated');
|
|
|
|
# TEST: insert data into the tables, create subscription and see if sync
|
|
# replicates the right columns
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab1 VALUES (1, 2, 3);
|
|
INSERT INTO tab1 VALUES (4, 5, 6);
|
|
));
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab3 VALUES (1, 2, 3);
|
|
INSERT INTO tab3 VALUES (4, 5, 6);
|
|
));
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab4 VALUES (1, 'red', 3, 'oh my');
|
|
INSERT INTO tab4 VALUES (2, 'blue', 4, 'hello');
|
|
));
|
|
|
|
# replication of partitioned table
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_part VALUES (1, 'abc', '2021-07-04 12:00:00');
|
|
INSERT INTO test_part VALUES (2, 'bcd', '2021-07-03 11:12:13');
|
|
INSERT INTO test_part VALUES (7, 'abc', '2021-07-04 12:00:00');
|
|
INSERT INTO test_part VALUES (8, 'bcd', '2021-07-03 11:12:13');
|
|
));
|
|
|
|
# create subscription for the publication, wait for sync to complete,
|
|
# then check the sync results
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub1
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
# tab1: only (a,b) is replicated
|
|
$result =
|
|
$node_subscriber->safe_psql('postgres', "SELECT * FROM tab1 ORDER BY a");
|
|
is( $result, qq(1|2|
|
|
4|5|), 'insert on column tab1.c is not replicated');
|
|
|
|
# tab3: only (a,c) is replicated
|
|
$result = $node_subscriber->safe_psql('postgres',
|
|
qq(SELECT * FROM tab3 ORDER BY "a'"));
|
|
is( $result, qq(1|3
|
|
4|6), 'insert on column tab3.b is not replicated');
|
|
|
|
# tab4: only (a,b,d) is replicated
|
|
$result =
|
|
$node_subscriber->safe_psql('postgres', "SELECT * FROM tab4 ORDER BY a");
|
|
is( $result, qq(1|red|oh my
|
|
2|blue|hello), 'insert on column tab4.c is not replicated');
|
|
|
|
# test_part: (a,b) is replicated
|
|
$result = $node_subscriber->safe_psql('postgres',
|
|
"SELECT * FROM test_part ORDER BY a");
|
|
is( $result, qq(1|abc
|
|
2|bcd
|
|
7|abc
|
|
8|bcd), 'insert on column test_part.c columns is not replicated');
|
|
|
|
|
|
# TEST: now insert more data into the tables, and wait until we replicate
|
|
# them (not by tablesync, but regular decoding and replication)
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab1 VALUES (2, 3, 4);
|
|
INSERT INTO tab1 VALUES (5, 6, 7);
|
|
));
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab3 VALUES (2, 3, 4);
|
|
INSERT INTO tab3 VALUES (5, 6, 7);
|
|
));
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab4 VALUES (3, 'red', 5, 'foo');
|
|
INSERT INTO tab4 VALUES (4, 'blue', 6, 'bar');
|
|
));
|
|
|
|
# replication of partitioned table
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_part VALUES (3, 'xxx', '2022-02-01 10:00:00');
|
|
INSERT INTO test_part VALUES (4, 'yyy', '2022-03-02 15:12:13');
|
|
INSERT INTO test_part VALUES (9, 'zzz', '2022-04-03 21:00:00');
|
|
INSERT INTO test_part VALUES (10, 'qqq', '2022-05-04 22:12:13');
|
|
));
|
|
|
|
# wait for catchup before checking the subscriber
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
# tab1: only (a,b) is replicated
|
|
$result =
|
|
$node_subscriber->safe_psql('postgres', "SELECT * FROM tab1 ORDER BY a");
|
|
is( $result, qq(1|2|
|
|
2|3|
|
|
4|5|
|
|
5|6|), 'insert on column tab1.c is not replicated');
|
|
|
|
# tab3: only (a,c) is replicated
|
|
$result = $node_subscriber->safe_psql('postgres',
|
|
qq(SELECT * FROM tab3 ORDER BY "a'"));
|
|
is( $result, qq(1|3
|
|
2|4
|
|
4|6
|
|
5|7), 'insert on column tab3.b is not replicated');
|
|
|
|
# tab4: only (a,b,d) is replicated
|
|
$result =
|
|
$node_subscriber->safe_psql('postgres', "SELECT * FROM tab4 ORDER BY a");
|
|
is( $result, qq(1|red|oh my
|
|
2|blue|hello
|
|
3|red|foo
|
|
4|blue|bar), 'insert on column tab4.c is not replicated');
|
|
|
|
# test_part: (a,b) is replicated
|
|
$result = $node_subscriber->safe_psql('postgres',
|
|
"SELECT * FROM test_part ORDER BY a");
|
|
is( $result, qq(1|abc
|
|
2|bcd
|
|
3|xxx
|
|
4|yyy
|
|
7|abc
|
|
8|bcd
|
|
9|zzz
|
|
10|qqq), 'insert on column test_part.c columns is not replicated');
|
|
|
|
|
|
# TEST: do some updates on some of the tables, both on columns included
|
|
# in the column list and other
|
|
|
|
# tab1: update of replicated column
|
|
$node_publisher->safe_psql('postgres',
|
|
qq(UPDATE tab1 SET "B" = 2 * "B" where a = 1));
|
|
|
|
# tab1: update of non-replicated column
|
|
$node_publisher->safe_psql('postgres',
|
|
qq(UPDATE tab1 SET c = 2*c where a = 4));
|
|
|
|
# tab3: update of non-replicated
|
|
$node_publisher->safe_psql('postgres',
|
|
qq(UPDATE tab3 SET "B" = "B" || ' updated' where "a'" = 4));
|
|
|
|
# tab3: update of replicated column
|
|
$node_publisher->safe_psql('postgres',
|
|
qq(UPDATE tab3 SET "c'" = 2 * "c'" where "a'" = 1));
|
|
|
|
# tab4
|
|
$node_publisher->safe_psql('postgres',
|
|
qq(UPDATE tab4 SET b = 'blue', c = c * 2, d = d || ' updated' where a = 1)
|
|
);
|
|
|
|
# tab4
|
|
$node_publisher->safe_psql('postgres',
|
|
qq(UPDATE tab4 SET b = 'red', c = c * 2, d = d || ' updated' where a = 2)
|
|
);
|
|
|
|
# wait for the replication to catch up, and check the UPDATE results got
|
|
# replicated correctly, with the right column list
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
$result =
|
|
$node_subscriber->safe_psql('postgres', qq(SELECT * FROM tab1 ORDER BY a));
|
|
is( $result,
|
|
qq(1|4|
|
|
2|3|
|
|
4|5|
|
|
5|6|), 'only update on column tab1.b is replicated');
|
|
|
|
$result = $node_subscriber->safe_psql('postgres',
|
|
qq(SELECT * FROM tab3 ORDER BY "a'"));
|
|
is( $result,
|
|
qq(1|6
|
|
2|4
|
|
4|6
|
|
5|7), 'only update on column tab3.c is replicated');
|
|
|
|
$result =
|
|
$node_subscriber->safe_psql('postgres', qq(SELECT * FROM tab4 ORDER BY a));
|
|
|
|
is( $result, qq(1|blue|oh my updated
|
|
2|red|hello updated
|
|
3|red|foo
|
|
4|blue|bar), 'update on column tab4.c is not replicated');
|
|
|
|
|
|
# TEST: add table with a column list, insert data, replicate
|
|
|
|
# insert some data before adding it to the publication
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab2 VALUES (1, 'abc', 3);
|
|
));
|
|
|
|
$node_publisher->safe_psql('postgres',
|
|
"ALTER PUBLICATION pub1 ADD TABLE tab2 (a, b)");
|
|
|
|
$node_subscriber->safe_psql('postgres',
|
|
"ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION");
|
|
|
|
# wait for the tablesync to complete, add a bit more data and then check
|
|
# the results of the replication
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab2 VALUES (2, 'def', 6);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
$result =
|
|
$node_subscriber->safe_psql('postgres', "SELECT * FROM tab2 ORDER BY a");
|
|
is( $result, qq(1|abc
|
|
2|def), 'insert on column tab2.c is not replicated');
|
|
|
|
# do a couple updates, check the correct stuff gets replicated
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
UPDATE tab2 SET c = 5 where a = 1;
|
|
UPDATE tab2 SET b = 'xyz' where a = 2;
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
$result =
|
|
$node_subscriber->safe_psql('postgres', "SELECT * FROM tab2 ORDER BY a");
|
|
is( $result, qq(1|abc
|
|
2|xyz), 'update on column tab2.c is not replicated');
|
|
|
|
|
|
# TEST: add a table to two publications with same column lists, and
|
|
# create a single subscription replicating both publications
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab5 (a int PRIMARY KEY, b int, c int, d int);
|
|
CREATE PUBLICATION pub2 FOR TABLE tab5 (a, b);
|
|
CREATE PUBLICATION pub3 FOR TABLE tab5 (a, b);
|
|
|
|
-- insert a couple initial records
|
|
INSERT INTO tab5 VALUES (1, 11, 111, 1111);
|
|
INSERT INTO tab5 VALUES (2, 22, 222, 2222);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab5 (a int PRIMARY KEY, b int, d int);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub2, pub3
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync($node_publisher, 'sub1');
|
|
|
|
# insert data and make sure the columns in column list get fully replicated
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab5 VALUES (3, 33, 333, 3333);
|
|
INSERT INTO tab5 VALUES (4, 44, 444, 4444);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM tab5 ORDER BY a"),
|
|
qq(1|11|
|
|
2|22|
|
|
3|33|
|
|
4|44|),
|
|
'overlapping publications with overlapping column lists');
|
|
|
|
|
|
# TEST: create a table with a column list, then change the replica
|
|
# identity by replacing a primary key (but use a different column in
|
|
# the column list)
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab6 (a int PRIMARY KEY, b int, c int, d int);
|
|
CREATE PUBLICATION pub4 FOR TABLE tab6 (a, b);
|
|
|
|
-- initial data
|
|
INSERT INTO tab6 VALUES (1, 22, 333, 4444);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab6 (a int PRIMARY KEY, b int, c int, d int);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub4
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab6 VALUES (2, 33, 444, 5555);
|
|
UPDATE tab6 SET b = b * 2, c = c * 3, d = d * 4;
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM tab6 ORDER BY a"),
|
|
qq(1|44||
|
|
2|66||), 'replication with the original primary key');
|
|
|
|
# now redefine the constraint - move the primary key to a different column
|
|
# (which is still covered by the column list, though)
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
ALTER TABLE tab6 DROP CONSTRAINT tab6_pkey;
|
|
ALTER TABLE tab6 ADD PRIMARY KEY (b);
|
|
));
|
|
|
|
# we need to do the same thing on the subscriber
|
|
# XXX What would happen if this happens before the publisher ALTER? Or
|
|
# interleaved, somehow? But that seems unrelated to column lists.
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
ALTER TABLE tab6 DROP CONSTRAINT tab6_pkey;
|
|
ALTER TABLE tab6 ADD PRIMARY KEY (b);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab6 VALUES (3, 55, 666, 8888);
|
|
UPDATE tab6 SET b = b * 2, c = c * 3, d = d * 4;
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM tab6 ORDER BY a"),
|
|
qq(1|88||
|
|
2|132||
|
|
3|110||),
|
|
'replication with the modified primary key');
|
|
|
|
|
|
# TEST: create a table with a column list, then change the replica
|
|
# identity by replacing a primary key with a key on multiple columns
|
|
# (all of them covered by the column list)
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab7 (a int PRIMARY KEY, b int, c int, d int);
|
|
CREATE PUBLICATION pub5 FOR TABLE tab7 (a, b);
|
|
|
|
-- some initial data
|
|
INSERT INTO tab7 VALUES (1, 22, 333, 4444);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE tab7 (a int PRIMARY KEY, b int, c int, d int);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub5
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab7 VALUES (2, 33, 444, 5555);
|
|
UPDATE tab7 SET b = b * 2, c = c * 3, d = d * 4;
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM tab7 ORDER BY a"),
|
|
qq(1|44||
|
|
2|66||), 'replication with the original primary key');
|
|
|
|
# now redefine the constraint - move the primary key to a different column
|
|
# (which is not covered by the column list)
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
ALTER TABLE tab7 DROP CONSTRAINT tab7_pkey;
|
|
ALTER TABLE tab7 ADD PRIMARY KEY (a, b);
|
|
));
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO tab7 VALUES (3, 55, 666, 7777);
|
|
UPDATE tab7 SET b = b * 2, c = c * 3, d = d * 4;
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM tab7 ORDER BY a"),
|
|
qq(1|88||
|
|
2|132||
|
|
3|110||),
|
|
'replication with the modified primary key');
|
|
|
|
# now switch the primary key again to another columns not covered by the
|
|
# column list, but also generate writes between the drop and creation
|
|
# of the new constraint
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
ALTER TABLE tab7 DROP CONSTRAINT tab7_pkey;
|
|
INSERT INTO tab7 VALUES (4, 77, 888, 9999);
|
|
-- update/delete is not allowed for tables without RI
|
|
ALTER TABLE tab7 ADD PRIMARY KEY (b, a);
|
|
UPDATE tab7 SET b = b * 2, c = c * 3, d = d * 4;
|
|
DELETE FROM tab7 WHERE a = 1;
|
|
));
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM tab7 ORDER BY a"),
|
|
qq(2|264||
|
|
3|220||
|
|
4|154||),
|
|
'replication with the modified primary key');
|
|
|
|
|
|
# TEST: partitioned tables (with publish_via_partition_root = false)
|
|
# and replica identity. The (leaf) partitions may have different RI, so
|
|
# we need to check the partition RI (with respect to the column list)
|
|
# while attaching the partition.
|
|
|
|
# First, let's create a partitioned table with two partitions, each with
|
|
# a different RI, but a column list not covering all those RI.
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part_a (a int, b int, c int) PARTITION BY LIST (a);
|
|
|
|
CREATE TABLE test_part_a_1 PARTITION OF test_part_a FOR VALUES IN (1,2,3,4,5);
|
|
ALTER TABLE test_part_a_1 ADD PRIMARY KEY (a);
|
|
ALTER TABLE test_part_a_1 REPLICA IDENTITY USING INDEX test_part_a_1_pkey;
|
|
|
|
CREATE TABLE test_part_a_2 PARTITION OF test_part_a FOR VALUES IN (6,7,8,9,10);
|
|
ALTER TABLE test_part_a_2 ADD PRIMARY KEY (b);
|
|
ALTER TABLE test_part_a_2 REPLICA IDENTITY USING INDEX test_part_a_2_pkey;
|
|
|
|
-- initial data, one row in each partition
|
|
INSERT INTO test_part_a VALUES (1, 3);
|
|
INSERT INTO test_part_a VALUES (6, 4);
|
|
));
|
|
|
|
# do the same thing on the subscriber (with the opposite column order)
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part_a (b int, a int) PARTITION BY LIST (a);
|
|
|
|
CREATE TABLE test_part_a_1 PARTITION OF test_part_a FOR VALUES IN (1,2,3,4,5);
|
|
ALTER TABLE test_part_a_1 ADD PRIMARY KEY (a);
|
|
ALTER TABLE test_part_a_1 REPLICA IDENTITY USING INDEX test_part_a_1_pkey;
|
|
|
|
CREATE TABLE test_part_a_2 PARTITION OF test_part_a FOR VALUES IN (6,7,8,9,10);
|
|
ALTER TABLE test_part_a_2 ADD PRIMARY KEY (b);
|
|
ALTER TABLE test_part_a_2 REPLICA IDENTITY USING INDEX test_part_a_2_pkey;
|
|
));
|
|
|
|
# create a publication replicating just the column "a", which is not enough
|
|
# for the second partition
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE PUBLICATION pub6 FOR TABLE test_part_a (b, a) WITH (publish_via_partition_root = true);
|
|
ALTER PUBLICATION pub6 ADD TABLE test_part_a_1 (a);
|
|
ALTER PUBLICATION pub6 ADD TABLE test_part_a_2 (b);
|
|
));
|
|
|
|
# add the publication to our subscription, wait for sync to complete
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub6
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_part_a VALUES (2, 5);
|
|
INSERT INTO test_part_a VALUES (7, 6);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT a, b FROM test_part_a ORDER BY a, b"),
|
|
qq(1|3
|
|
2|5
|
|
6|4
|
|
7|6),
|
|
'partitions with different replica identities not replicated correctly');
|
|
|
|
# This time start with a column list covering RI for all partitions, but
|
|
# then update the column list to not cover column "b" (needed by the
|
|
# second partition)
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part_b (a int, b int) PARTITION BY LIST (a);
|
|
|
|
CREATE TABLE test_part_b_1 PARTITION OF test_part_b FOR VALUES IN (1,2,3,4,5);
|
|
ALTER TABLE test_part_b_1 ADD PRIMARY KEY (a);
|
|
ALTER TABLE test_part_b_1 REPLICA IDENTITY USING INDEX test_part_b_1_pkey;
|
|
|
|
CREATE TABLE test_part_b_2 PARTITION OF test_part_b FOR VALUES IN (6,7,8,9,10);
|
|
ALTER TABLE test_part_b_2 ADD PRIMARY KEY (b);
|
|
ALTER TABLE test_part_b_2 REPLICA IDENTITY USING INDEX test_part_b_2_pkey;
|
|
|
|
-- initial data, one row in each partitions
|
|
INSERT INTO test_part_b VALUES (1, 1);
|
|
INSERT INTO test_part_b VALUES (6, 2);
|
|
));
|
|
|
|
# do the same thing on the subscriber
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part_b (a int, b int) PARTITION BY LIST (a);
|
|
|
|
CREATE TABLE test_part_b_1 PARTITION OF test_part_b FOR VALUES IN (1,2,3,4,5);
|
|
ALTER TABLE test_part_b_1 ADD PRIMARY KEY (a);
|
|
ALTER TABLE test_part_b_1 REPLICA IDENTITY USING INDEX test_part_b_1_pkey;
|
|
|
|
CREATE TABLE test_part_b_2 PARTITION OF test_part_b FOR VALUES IN (6,7,8,9,10);
|
|
ALTER TABLE test_part_b_2 ADD PRIMARY KEY (b);
|
|
ALTER TABLE test_part_b_2 REPLICA IDENTITY USING INDEX test_part_b_2_pkey;
|
|
));
|
|
|
|
# create a publication replicating both columns, which is sufficient for
|
|
# both partitions
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE PUBLICATION pub7 FOR TABLE test_part_b (a, b) WITH (publish_via_partition_root = true);
|
|
));
|
|
|
|
# add the publication to our subscription, wait for sync to complete
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub7
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_part_b VALUES (2, 3);
|
|
INSERT INTO test_part_b VALUES (7, 4);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT * FROM test_part_b ORDER BY a, b"),
|
|
qq(1|1
|
|
2|3
|
|
6|2
|
|
7|4),
|
|
'partitions with different replica identities not replicated correctly');
|
|
|
|
|
|
# TEST: This time start with a column list covering RI for all partitions,
|
|
# but then update RI for one of the partitions to not be covered by the
|
|
# column list anymore.
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part_c (a int, b int, c int) PARTITION BY LIST (a);
|
|
|
|
CREATE TABLE test_part_c_1 PARTITION OF test_part_c FOR VALUES IN (1,3);
|
|
ALTER TABLE test_part_c_1 ADD PRIMARY KEY (a);
|
|
ALTER TABLE test_part_c_1 REPLICA IDENTITY USING INDEX test_part_c_1_pkey;
|
|
|
|
CREATE TABLE test_part_c_2 PARTITION OF test_part_c FOR VALUES IN (2,4);
|
|
ALTER TABLE test_part_c_2 ADD PRIMARY KEY (b);
|
|
ALTER TABLE test_part_c_2 REPLICA IDENTITY USING INDEX test_part_c_2_pkey;
|
|
|
|
-- initial data, one row for each partition
|
|
INSERT INTO test_part_c VALUES (1, 3, 5);
|
|
INSERT INTO test_part_c VALUES (2, 4, 6);
|
|
));
|
|
|
|
# do the same thing on the subscriber
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part_c (a int, b int, c int) PARTITION BY LIST (a);
|
|
|
|
CREATE TABLE test_part_c_1 PARTITION OF test_part_c FOR VALUES IN (1,3);
|
|
ALTER TABLE test_part_c_1 ADD PRIMARY KEY (a);
|
|
ALTER TABLE test_part_c_1 REPLICA IDENTITY USING INDEX test_part_c_1_pkey;
|
|
|
|
CREATE TABLE test_part_c_2 PARTITION OF test_part_c FOR VALUES IN (2,4);
|
|
ALTER TABLE test_part_c_2 ADD PRIMARY KEY (b);
|
|
ALTER TABLE test_part_c_2 REPLICA IDENTITY USING INDEX test_part_c_2_pkey;
|
|
));
|
|
|
|
# create a publication replicating data through partition root, with a column
|
|
# list on the root, and then add the partitions one by one with separate
|
|
# column lists (but those are not applied)
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE PUBLICATION pub8 FOR TABLE test_part_c WITH (publish_via_partition_root = false);
|
|
ALTER PUBLICATION pub8 ADD TABLE test_part_c_1 (a,c);
|
|
ALTER PUBLICATION pub8 ADD TABLE test_part_c_2 (a,b);
|
|
));
|
|
|
|
# add the publication to our subscription, wait for sync to complete
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
DROP SUBSCRIPTION sub1;
|
|
CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub8;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_part_c VALUES (3, 7, 8);
|
|
INSERT INTO test_part_c VALUES (4, 9, 10);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT * FROM test_part_c ORDER BY a, b"),
|
|
qq(1||5
|
|
2|4|
|
|
3||8
|
|
4|9|),
|
|
'partitions with different replica identities not replicated correctly');
|
|
|
|
|
|
# create a publication not replicating data through partition root, without
|
|
# a column list on the root, and then add the partitions one by one with
|
|
# separate column lists
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
DROP PUBLICATION pub8;
|
|
CREATE PUBLICATION pub8 FOR TABLE test_part_c WITH (publish_via_partition_root = false);
|
|
ALTER PUBLICATION pub8 ADD TABLE test_part_c_1 (a);
|
|
ALTER PUBLICATION pub8 ADD TABLE test_part_c_2 (a,b);
|
|
));
|
|
|
|
# add the publication to our subscription, wait for sync to complete
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
|
|
TRUNCATE test_part_c;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
TRUNCATE test_part_c;
|
|
INSERT INTO test_part_c VALUES (1, 3, 5);
|
|
INSERT INTO test_part_c VALUES (2, 4, 6);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT * FROM test_part_c ORDER BY a, b"),
|
|
qq(1||
|
|
2|4|),
|
|
'partitions with different replica identities not replicated correctly');
|
|
|
|
|
|
# TEST: Start with a single partition, with RI compatible with the column
|
|
# list, and then attach a partition with incompatible RI.
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part_d (a int, b int) PARTITION BY LIST (a);
|
|
|
|
CREATE TABLE test_part_d_1 PARTITION OF test_part_d FOR VALUES IN (1,3);
|
|
ALTER TABLE test_part_d_1 ADD PRIMARY KEY (a);
|
|
ALTER TABLE test_part_d_1 REPLICA IDENTITY USING INDEX test_part_d_1_pkey;
|
|
|
|
INSERT INTO test_part_d VALUES (1, 2);
|
|
));
|
|
|
|
# do the same thing on the subscriber (in fact, create both partitions right
|
|
# away, no need to delay that)
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_part_d (a int, b int) PARTITION BY LIST (a);
|
|
|
|
CREATE TABLE test_part_d_1 PARTITION OF test_part_d FOR VALUES IN (1,3);
|
|
ALTER TABLE test_part_d_1 ADD PRIMARY KEY (a);
|
|
ALTER TABLE test_part_d_1 REPLICA IDENTITY USING INDEX test_part_d_1_pkey;
|
|
|
|
CREATE TABLE test_part_d_2 PARTITION OF test_part_d FOR VALUES IN (2,4);
|
|
ALTER TABLE test_part_d_2 ADD PRIMARY KEY (a);
|
|
ALTER TABLE test_part_d_2 REPLICA IDENTITY USING INDEX test_part_d_2_pkey;
|
|
));
|
|
|
|
# create a publication replicating both columns, which is sufficient for
|
|
# both partitions
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE PUBLICATION pub9 FOR TABLE test_part_d (a) WITH (publish_via_partition_root = true);
|
|
));
|
|
|
|
# add the publication to our subscription, wait for sync to complete
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub9
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_part_d VALUES (3, 4);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT * FROM test_part_d ORDER BY a, b"),
|
|
qq(1|
|
|
3|),
|
|
'partitions with different replica identities not replicated correctly');
|
|
|
|
|
|
# TEST: With a table included in the publications which is FOR ALL TABLES, it
|
|
# means replicate all columns.
|
|
|
|
# drop unnecessary tables, so as not to interfere with the FOR ALL TABLES
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
DROP TABLE tab1, tab2, tab3, tab4, tab5, tab6, tab7,
|
|
test_part, test_part_a, test_part_b, test_part_c, test_part_d;
|
|
));
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_mix_2 (a int PRIMARY KEY, b int, c int);
|
|
CREATE PUBLICATION pub_mix_3 FOR TABLE test_mix_2 (a, b, c);
|
|
CREATE PUBLICATION pub_mix_4 FOR ALL TABLES;
|
|
|
|
-- initial data
|
|
INSERT INTO test_mix_2 VALUES (1, 2, 3);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_mix_2 (a int PRIMARY KEY, b int, c int);
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub_mix_3, pub_mix_4;
|
|
ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_mix_2 VALUES (4, 5, 6);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM test_mix_2"),
|
|
qq(1|2|3
|
|
4|5|6),
|
|
'all columns should be replicated');
|
|
|
|
|
|
# TEST: With a table included in the publication which is FOR TABLES IN
|
|
# SCHEMA, it means replicate all columns.
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
DROP SUBSCRIPTION sub1;
|
|
CREATE TABLE test_mix_3 (a int PRIMARY KEY, b int, c int);
|
|
));
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
DROP TABLE test_mix_2;
|
|
CREATE TABLE test_mix_3 (a int PRIMARY KEY, b int, c int);
|
|
CREATE PUBLICATION pub_mix_5 FOR TABLE test_mix_3 (a, b, c);
|
|
CREATE PUBLICATION pub_mix_6 FOR TABLES IN SCHEMA public;
|
|
|
|
-- initial data
|
|
INSERT INTO test_mix_3 VALUES (1, 2, 3);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub_mix_5, pub_mix_6;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_mix_3 VALUES (4, 5, 6);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM test_mix_3"),
|
|
qq(1|2|3
|
|
4|5|6),
|
|
'all columns should be replicated');
|
|
|
|
|
|
# TEST: Check handling of publish_via_partition_root - if a partition is
|
|
# published through partition root, we should only apply the column list
|
|
# defined for the whole table (not the partitions) - both during the initial
|
|
# sync and when replicating changes. This is what we do for row filters.
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
DROP SUBSCRIPTION sub1;
|
|
|
|
CREATE TABLE test_root (a int PRIMARY KEY, b int, c int) PARTITION BY RANGE (a);
|
|
CREATE TABLE test_root_1 PARTITION OF test_root FOR VALUES FROM (1) TO (10);
|
|
CREATE TABLE test_root_2 PARTITION OF test_root FOR VALUES FROM (10) TO (20);
|
|
));
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_root (a int PRIMARY KEY, b int, c int) PARTITION BY RANGE (a);
|
|
CREATE TABLE test_root_1 PARTITION OF test_root FOR VALUES FROM (1) TO (10);
|
|
CREATE TABLE test_root_2 PARTITION OF test_root FOR VALUES FROM (10) TO (20);
|
|
|
|
CREATE PUBLICATION pub_test_root FOR TABLE test_root (a) WITH (publish_via_partition_root = true);
|
|
CREATE PUBLICATION pub_test_root_1 FOR TABLE test_root_1 (a, b);
|
|
|
|
-- initial data
|
|
INSERT INTO test_root VALUES (1, 2, 3);
|
|
INSERT INTO test_root VALUES (10, 20, 30);
|
|
));
|
|
|
|
# Subscribe to pub_test_root and pub_test_root_1 at the same time, which means
|
|
# that the initial data will be synced once, and only the column list of the
|
|
# parent table (test_root) in the publication pub_test_root will be used for
|
|
# both table sync and data replication.
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub_test_root, pub_test_root_1;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_root VALUES (2, 3, 4);
|
|
INSERT INTO test_root VALUES (11, 21, 31);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT * FROM test_root ORDER BY a, b, c"),
|
|
qq(1||
|
|
2||
|
|
10||
|
|
11||),
|
|
'publication via partition root applies column list');
|
|
|
|
|
|
# TEST: Multiple publications which publish schema of parent table and
|
|
# partition. The partition is published through two publications, once
|
|
# through a schema (so no column list) containing the parent, and then
|
|
# also directly (with all columns). The expected outcome is there is
|
|
# no column list.
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
DROP PUBLICATION pub1, pub2, pub3, pub4, pub5, pub6, pub7, pub8;
|
|
|
|
CREATE SCHEMA s1;
|
|
CREATE TABLE s1.t (a int, b int, c int) PARTITION BY RANGE (a);
|
|
CREATE TABLE t_1 PARTITION OF s1.t FOR VALUES FROM (1) TO (10);
|
|
|
|
CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA s1;
|
|
CREATE PUBLICATION pub2 FOR TABLE t_1(a, b, c);
|
|
|
|
-- initial data
|
|
INSERT INTO s1.t VALUES (1, 2, 3);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE SCHEMA s1;
|
|
CREATE TABLE s1.t (a int, b int, c int) PARTITION BY RANGE (a);
|
|
CREATE TABLE t_1 PARTITION OF s1.t FOR VALUES FROM (1) TO (10);
|
|
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub1, pub2;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO s1.t VALUES (4, 5, 6);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM s1.t ORDER BY a"),
|
|
qq(1|2|3
|
|
4|5|6),
|
|
'two publications, publishing the same relation');
|
|
|
|
# Now resync the subscription, but with publications in the opposite order.
|
|
# The result should be the same.
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
TRUNCATE s1.t;
|
|
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub2, pub1;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO s1.t VALUES (7, 8, 9);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql('postgres', "SELECT * FROM s1.t ORDER BY a"),
|
|
qq(7|8|9),
|
|
'two publications, publishing the same relation');
|
|
|
|
|
|
# TEST: One publication, containing both the parent and child relations.
|
|
# The expected outcome is list "a", because that's the column list defined
|
|
# for the top-most ancestor added to the publication.
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
DROP SCHEMA s1 CASCADE;
|
|
CREATE TABLE t (a int, b int, c int) PARTITION BY RANGE (a);
|
|
CREATE TABLE t_1 PARTITION OF t FOR VALUES FROM (1) TO (10)
|
|
PARTITION BY RANGE (a);
|
|
CREATE TABLE t_2 PARTITION OF t_1 FOR VALUES FROM (1) TO (10);
|
|
|
|
CREATE PUBLICATION pub3 FOR TABLE t_1 (a), t_2
|
|
WITH (PUBLISH_VIA_PARTITION_ROOT);
|
|
|
|
-- initial data
|
|
INSERT INTO t VALUES (1, 2, 3);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
DROP SCHEMA s1 CASCADE;
|
|
CREATE TABLE t (a int, b int, c int) PARTITION BY RANGE (a);
|
|
CREATE TABLE t_1 PARTITION OF t FOR VALUES FROM (1) TO (10)
|
|
PARTITION BY RANGE (a);
|
|
CREATE TABLE t_2 PARTITION OF t_1 FOR VALUES FROM (1) TO (10);
|
|
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub3;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO t VALUES (4, 5, 6);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT * FROM t ORDER BY a, b, c"),
|
|
qq(1||
|
|
4||),
|
|
'publication containing both parent and child relation');
|
|
|
|
|
|
# TEST: One publication, containing both the parent and child relations.
|
|
# The expected outcome is list "a", because that's the column list defined
|
|
# for the top-most ancestor added to the publication.
|
|
# Note: The difference from the preceding test is that in this case both
|
|
# relations have a column list defined.
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
DROP TABLE t;
|
|
CREATE TABLE t (a int, b int, c int) PARTITION BY RANGE (a);
|
|
CREATE TABLE t_1 PARTITION OF t FOR VALUES FROM (1) TO (10)
|
|
PARTITION BY RANGE (a);
|
|
CREATE TABLE t_2 PARTITION OF t_1 FOR VALUES FROM (1) TO (10);
|
|
|
|
CREATE PUBLICATION pub4 FOR TABLE t_1 (a), t_2 (b)
|
|
WITH (PUBLISH_VIA_PARTITION_ROOT);
|
|
|
|
-- initial data
|
|
INSERT INTO t VALUES (1, 2, 3);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
DROP TABLE t;
|
|
CREATE TABLE t (a int, b int, c int) PARTITION BY RANGE (a);
|
|
CREATE TABLE t_1 PARTITION OF t FOR VALUES FROM (1) TO (10)
|
|
PARTITION BY RANGE (a);
|
|
CREATE TABLE t_2 PARTITION OF t_1 FOR VALUES FROM (1) TO (10);
|
|
|
|
ALTER SUBSCRIPTION sub1 SET PUBLICATION pub4;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO t VALUES (4, 5, 6);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT * FROM t ORDER BY a, b, c"),
|
|
qq(1||
|
|
4||),
|
|
'publication containing both parent and child relation');
|
|
|
|
# TEST: Only columns in the column list should exist in the old tuple of UPDATE
|
|
# and DELETE.
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_oldtuple_col (a int PRIMARY KEY, b int, c int);
|
|
CREATE PUBLICATION pub_check_oldtuple FOR TABLE test_oldtuple_col (a, b);
|
|
INSERT INTO test_oldtuple_col VALUES(1, 2, 3);
|
|
SELECT * FROM pg_create_logical_replication_slot('test_slot', 'pgoutput');
|
|
UPDATE test_oldtuple_col SET a = 2;
|
|
DELETE FROM test_oldtuple_col;
|
|
));
|
|
|
|
|
|
# Check at 7th byte of binary data for the number of columns in the old tuple.
|
|
#
|
|
# 7 = 1 (count from 1) + 1 byte (message type) + 4 byte (relid) + 1 byte (flag
|
|
# for old key).
|
|
#
|
|
# The message type of UPDATE is 85('U').
|
|
# The message type of DELETE is 68('D').
|
|
$result = $node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
SELECT substr(data, 7, 2) = int2send(2::smallint)
|
|
FROM pg_logical_slot_peek_binary_changes('test_slot', NULL, NULL,
|
|
'proto_version', '1',
|
|
'publication_names', 'pub_check_oldtuple')
|
|
WHERE get_byte(data, 0) = 85 OR get_byte(data, 0) = 68
|
|
));
|
|
|
|
is( $result, qq(t
|
|
t), 'check the number of columns in the old tuple');
|
|
|
|
# TEST: Generated and dropped columns are not considered for the column list.
|
|
# So, the publication having a column list except for those columns and a
|
|
# publication without any column (aka all columns as part of the columns
|
|
# list) are considered to have the same column list.
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_mix_4 (a int PRIMARY KEY, b int, c int, d int GENERATED ALWAYS AS (a + 1) STORED);
|
|
ALTER TABLE test_mix_4 DROP COLUMN c;
|
|
|
|
CREATE PUBLICATION pub_mix_7 FOR TABLE test_mix_4 (a, b);
|
|
CREATE PUBLICATION pub_mix_8 FOR TABLE test_mix_4;
|
|
|
|
-- initial data
|
|
INSERT INTO test_mix_4 VALUES (1, 2);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
DROP SUBSCRIPTION sub1;
|
|
CREATE TABLE test_mix_4 (a int PRIMARY KEY, b int, c int, d int);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub_mix_7, pub_mix_8;
|
|
));
|
|
|
|
$node_subscriber->wait_for_subscription_sync;
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT * FROM test_mix_4 ORDER BY a"),
|
|
qq(1|2||),
|
|
'initial synchronization with multiple publications with the same column list'
|
|
);
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
INSERT INTO test_mix_4 VALUES (3, 4);
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
is( $node_subscriber->safe_psql(
|
|
'postgres', "SELECT * FROM test_mix_4 ORDER BY a"),
|
|
qq(1|2||
|
|
3|4||),
|
|
'replication with multiple publications with the same column list');
|
|
|
|
# TEST: With a table included in multiple publications with different column
|
|
# lists, we should catch the error when creating the subscription.
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
CREATE TABLE test_mix_1 (a int PRIMARY KEY, b int, c int);
|
|
CREATE PUBLICATION pub_mix_1 FOR TABLE test_mix_1 (a, b);
|
|
CREATE PUBLICATION pub_mix_2 FOR TABLE test_mix_1 (a, c);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
DROP SUBSCRIPTION sub1;
|
|
CREATE TABLE test_mix_1 (a int PRIMARY KEY, b int, c int);
|
|
));
|
|
|
|
my ($cmdret, $stdout, $stderr) = $node_subscriber->psql(
|
|
'postgres', qq(
|
|
CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub_mix_1, pub_mix_2;
|
|
));
|
|
|
|
ok( $stderr =~
|
|
qr/cannot use different column lists for table "public.test_mix_1" in different publications/,
|
|
'different column lists detected');
|
|
|
|
# TEST: If the column list is changed after creating the subscription, we
|
|
# should catch the error reported by walsender.
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
ALTER PUBLICATION pub_mix_1 SET TABLE test_mix_1 (a, c);
|
|
));
|
|
|
|
$node_subscriber->safe_psql(
|
|
'postgres', qq(
|
|
CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr' PUBLICATION pub_mix_1, pub_mix_2;
|
|
));
|
|
|
|
$node_publisher->wait_for_catchup('sub1');
|
|
|
|
$node_publisher->safe_psql(
|
|
'postgres', qq(
|
|
ALTER PUBLICATION pub_mix_1 SET TABLE test_mix_1 (a, b);
|
|
INSERT INTO test_mix_1 VALUES(1, 1, 1);
|
|
));
|
|
|
|
$offset = $node_publisher->wait_for_log(
|
|
qr/cannot use different column lists for table "public.test_mix_1" in different publications/,
|
|
$offset);
|
|
|
|
$node_subscriber->stop('fast');
|
|
$node_publisher->stop('fast');
|
|
|
|
done_testing();
|