mirror of
https://github.com/postgres/postgres.git
synced 2025-07-20 05:03:10 +03:00
Logical replication support for TRUNCATE
Update the built-in logical replication system to make use of the previously added logical decoding for TRUNCATE support. Add the required truncate callback to pgoutput and a new logical replication protocol message. Publications get a new attribute to determine whether to replicate truncate actions. When updating a publication via pg_dump from an older version, this is not set, thus preserving the previous behavior. Author: Simon Riggs <simon@2ndquadrant.com> Author: Marco Nenciarini <marco.nenciarini@2ndquadrant.it> Author: Peter Eisentraut <peter.eisentraut@2ndquadrant.com> Reviewed-by: Petr Jelinek <petr.jelinek@2ndquadrant.com> Reviewed-by: Andres Freund <andres@anarazel.de> Reviewed-by: Alvaro Herrera <alvherre@alvh.no-ip.org>
This commit is contained in:
@ -21,20 +21,20 @@ ERROR: unrecognized publication parameter: foo
|
||||
CREATE PUBLICATION testpub_xxx WITH (publish = 'cluster, vacuum');
|
||||
ERROR: unrecognized "publish" value: "cluster"
|
||||
\dRp
|
||||
List of publications
|
||||
Name | Owner | All tables | Inserts | Updates | Deletes
|
||||
--------------------+--------------------------+------------+---------+---------+---------
|
||||
testpib_ins_trunct | regress_publication_user | f | t | f | f
|
||||
testpub_default | regress_publication_user | f | f | t | f
|
||||
List of publications
|
||||
Name | Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
--------------------+--------------------------+------------+---------+---------+---------+-----------
|
||||
testpib_ins_trunct | regress_publication_user | f | t | f | f | f
|
||||
testpub_default | regress_publication_user | f | f | t | f | f
|
||||
(2 rows)
|
||||
|
||||
ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete');
|
||||
\dRp
|
||||
List of publications
|
||||
Name | Owner | All tables | Inserts | Updates | Deletes
|
||||
--------------------+--------------------------+------------+---------+---------+---------
|
||||
testpib_ins_trunct | regress_publication_user | f | t | f | f
|
||||
testpub_default | regress_publication_user | f | t | t | t
|
||||
List of publications
|
||||
Name | Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
--------------------+--------------------------+------------+---------+---------+---------+-----------
|
||||
testpib_ins_trunct | regress_publication_user | f | t | f | f | f
|
||||
testpub_default | regress_publication_user | f | t | t | t | f
|
||||
(2 rows)
|
||||
|
||||
--- adding tables
|
||||
@ -76,10 +76,10 @@ Publications:
|
||||
"testpub_foralltables"
|
||||
|
||||
\dRp+ testpub_foralltables
|
||||
Publication testpub_foralltables
|
||||
Owner | All tables | Inserts | Updates | Deletes
|
||||
--------------------------+------------+---------+---------+---------
|
||||
regress_publication_user | t | t | t | f
|
||||
Publication testpub_foralltables
|
||||
Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
--------------------------+------------+---------+---------+---------+-----------
|
||||
regress_publication_user | t | t | t | f | f
|
||||
(1 row)
|
||||
|
||||
DROP TABLE testpub_tbl2;
|
||||
@ -89,19 +89,19 @@ CREATE TABLE testpub_tbl3a (b text) INHERITS (testpub_tbl3);
|
||||
CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3;
|
||||
CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3;
|
||||
\dRp+ testpub3
|
||||
Publication testpub3
|
||||
Owner | All tables | Inserts | Updates | Deletes
|
||||
--------------------------+------------+---------+---------+---------
|
||||
regress_publication_user | f | t | t | t
|
||||
Publication testpub3
|
||||
Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
--------------------------+------------+---------+---------+---------+-----------
|
||||
regress_publication_user | f | t | t | t | t
|
||||
Tables:
|
||||
"public.testpub_tbl3"
|
||||
"public.testpub_tbl3a"
|
||||
|
||||
\dRp+ testpub4
|
||||
Publication testpub4
|
||||
Owner | All tables | Inserts | Updates | Deletes
|
||||
--------------------------+------------+---------+---------+---------
|
||||
regress_publication_user | f | t | t | t
|
||||
Publication testpub4
|
||||
Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
--------------------------+------------+---------+---------+---------+-----------
|
||||
regress_publication_user | f | t | t | t | t
|
||||
Tables:
|
||||
"public.testpub_tbl3"
|
||||
|
||||
@ -119,10 +119,10 @@ ERROR: relation "testpub_tbl1" is already member of publication "testpub_fortbl
|
||||
CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1;
|
||||
ERROR: publication "testpub_fortbl" already exists
|
||||
\dRp+ testpub_fortbl
|
||||
Publication testpub_fortbl
|
||||
Owner | All tables | Inserts | Updates | Deletes
|
||||
--------------------------+------------+---------+---------+---------
|
||||
regress_publication_user | f | t | t | t
|
||||
Publication testpub_fortbl
|
||||
Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
--------------------------+------------+---------+---------+---------+-----------
|
||||
regress_publication_user | f | t | t | t | t
|
||||
Tables:
|
||||
"pub_test.testpub_nopk"
|
||||
"public.testpub_tbl1"
|
||||
@ -165,10 +165,10 @@ Publications:
|
||||
"testpub_fortbl"
|
||||
|
||||
\dRp+ testpub_default
|
||||
Publication testpub_default
|
||||
Owner | All tables | Inserts | Updates | Deletes
|
||||
--------------------------+------------+---------+---------+---------
|
||||
regress_publication_user | f | t | t | t
|
||||
Publication testpub_default
|
||||
Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
--------------------------+------------+---------+---------+---------+-----------
|
||||
regress_publication_user | f | t | t | t | f
|
||||
Tables:
|
||||
"pub_test.testpub_nopk"
|
||||
"public.testpub_tbl1"
|
||||
@ -210,10 +210,10 @@ DROP TABLE testpub_parted;
|
||||
DROP VIEW testpub_view;
|
||||
DROP TABLE testpub_tbl1;
|
||||
\dRp+ testpub_default
|
||||
Publication testpub_default
|
||||
Owner | All tables | Inserts | Updates | Deletes
|
||||
--------------------------+------------+---------+---------+---------
|
||||
regress_publication_user | f | t | t | t
|
||||
Publication testpub_default
|
||||
Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
--------------------------+------------+---------+---------+---------+-----------
|
||||
regress_publication_user | f | t | t | t | f
|
||||
(1 row)
|
||||
|
||||
-- fail - must be owner of publication
|
||||
@ -223,20 +223,20 @@ ERROR: must be owner of publication testpub_default
|
||||
RESET ROLE;
|
||||
ALTER PUBLICATION testpub_default RENAME TO testpub_foo;
|
||||
\dRp testpub_foo
|
||||
List of publications
|
||||
Name | Owner | All tables | Inserts | Updates | Deletes
|
||||
-------------+--------------------------+------------+---------+---------+---------
|
||||
testpub_foo | regress_publication_user | f | t | t | t
|
||||
List of publications
|
||||
Name | Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
-------------+--------------------------+------------+---------+---------+---------+-----------
|
||||
testpub_foo | regress_publication_user | f | t | t | t | f
|
||||
(1 row)
|
||||
|
||||
-- rename back to keep the rest simple
|
||||
ALTER PUBLICATION testpub_foo RENAME TO testpub_default;
|
||||
ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2;
|
||||
\dRp testpub_default
|
||||
List of publications
|
||||
Name | Owner | All tables | Inserts | Updates | Deletes
|
||||
-----------------+---------------------------+------------+---------+---------+---------
|
||||
testpub_default | regress_publication_user2 | f | t | t | t
|
||||
List of publications
|
||||
Name | Owner | All tables | Inserts | Updates | Deletes | Truncates
|
||||
-----------------+---------------------------+------------+---------+---------+---------+-----------
|
||||
testpub_default | regress_publication_user2 | f | t | t | t | f
|
||||
(1 row)
|
||||
|
||||
DROP PUBLICATION testpub_default;
|
||||
|
161
src/test/subscription/t/010_truncate.pl
Normal file
161
src/test/subscription/t/010_truncate.pl
Normal file
@ -0,0 +1,161 @@
|
||||
# Test TRUNCATE
|
||||
use strict;
|
||||
use warnings;
|
||||
use PostgresNode;
|
||||
use TestLib;
|
||||
use Test::More tests => 9;
|
||||
|
||||
# setup
|
||||
|
||||
my $node_publisher = get_new_node('publisher');
|
||||
$node_publisher->init(allows_streaming => 'logical');
|
||||
$node_publisher->start;
|
||||
|
||||
my $node_subscriber = get_new_node('subscriber');
|
||||
$node_subscriber->init(allows_streaming => 'logical');
|
||||
$node_subscriber->start;
|
||||
|
||||
my $publisher_connstr = $node_publisher->connstr . ' dbname=postgres';
|
||||
|
||||
$node_publisher->safe_psql('postgres',
|
||||
"CREATE TABLE tab1 (a int PRIMARY KEY)");
|
||||
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"CREATE TABLE tab1 (a int PRIMARY KEY)");
|
||||
|
||||
$node_publisher->safe_psql('postgres',
|
||||
"CREATE TABLE tab2 (a int PRIMARY KEY)");
|
||||
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"CREATE TABLE tab2 (a int PRIMARY KEY)");
|
||||
|
||||
$node_publisher->safe_psql('postgres',
|
||||
"CREATE TABLE tab3 (a int PRIMARY KEY)");
|
||||
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"CREATE TABLE tab3 (a int PRIMARY KEY)");
|
||||
|
||||
$node_publisher->safe_psql('postgres',
|
||||
"CREATE TABLE tab4 (x int PRIMARY KEY, y int REFERENCES tab3)");
|
||||
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"CREATE TABLE tab4 (x int PRIMARY KEY, y int REFERENCES tab3)");
|
||||
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"CREATE SEQUENCE seq1 OWNED BY tab1.a"
|
||||
);
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"ALTER SEQUENCE seq1 START 101"
|
||||
);
|
||||
|
||||
$node_publisher->safe_psql('postgres',
|
||||
"CREATE PUBLICATION pub1 FOR TABLE tab1");
|
||||
$node_publisher->safe_psql('postgres',
|
||||
"CREATE PUBLICATION pub2 FOR TABLE tab2 WITH (publish = insert)");
|
||||
$node_publisher->safe_psql('postgres',
|
||||
"CREATE PUBLICATION pub3 FOR TABLE tab3, tab4");
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"CREATE SUBSCRIPTION sub1 CONNECTION '$publisher_connstr application_name=sub1' PUBLICATION pub1");
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"CREATE SUBSCRIPTION sub2 CONNECTION '$publisher_connstr application_name=sub2' PUBLICATION pub2");
|
||||
$node_subscriber->safe_psql('postgres',
|
||||
"CREATE SUBSCRIPTION sub3 CONNECTION '$publisher_connstr application_name=sub3' PUBLICATION pub3");
|
||||
|
||||
$node_publisher->wait_for_catchup('sub1');
|
||||
|
||||
# insert data to truncate
|
||||
|
||||
$node_subscriber->safe_psql('postgres', "INSERT INTO tab1 VALUES (1), (2), (3)");
|
||||
|
||||
$node_publisher->wait_for_catchup('sub1');
|
||||
|
||||
# truncate and check
|
||||
|
||||
$node_publisher->safe_psql('postgres', "TRUNCATE tab1");
|
||||
|
||||
$node_publisher->wait_for_catchup('sub1');
|
||||
|
||||
my $result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT count(*), min(a), max(a) FROM tab1");
|
||||
is($result, qq(0||),
|
||||
'truncate replicated');
|
||||
|
||||
$result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT nextval('seq1')");
|
||||
is($result, qq(1),
|
||||
'sequence not restarted');
|
||||
|
||||
# truncate with restart identity
|
||||
|
||||
$node_publisher->safe_psql('postgres', "TRUNCATE tab1 RESTART IDENTITY");
|
||||
|
||||
$node_publisher->wait_for_catchup('sub1');
|
||||
|
||||
$result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT nextval('seq1')");
|
||||
is($result, qq(101),
|
||||
'truncate restarted identities');
|
||||
|
||||
# test publication that does not replicate truncate
|
||||
|
||||
$node_subscriber->safe_psql('postgres', "INSERT INTO tab2 VALUES (1), (2), (3)");
|
||||
|
||||
$node_publisher->safe_psql('postgres', "TRUNCATE tab2");
|
||||
|
||||
$node_publisher->wait_for_catchup('sub2');
|
||||
|
||||
$result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT count(*), min(a), max(a) FROM tab2");
|
||||
is($result, qq(3|1|3),
|
||||
'truncate not replicated');
|
||||
|
||||
$node_publisher->safe_psql('postgres',
|
||||
"ALTER PUBLICATION pub2 SET (publish = 'insert, truncate')");
|
||||
|
||||
$node_publisher->safe_psql('postgres', "TRUNCATE tab2");
|
||||
|
||||
$node_publisher->wait_for_catchup('sub2');
|
||||
|
||||
$result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT count(*), min(a), max(a) FROM tab2");
|
||||
is($result, qq(0||),
|
||||
'truncate replicated after publication change');
|
||||
|
||||
# test multiple tables connected by foreign keys
|
||||
|
||||
$node_subscriber->safe_psql('postgres', "INSERT INTO tab3 VALUES (1), (2), (3)");
|
||||
$node_subscriber->safe_psql('postgres', "INSERT INTO tab4 VALUES (11, 1), (111, 1), (22, 2)");
|
||||
|
||||
$node_publisher->safe_psql('postgres', "TRUNCATE tab3, tab4");
|
||||
|
||||
$node_publisher->wait_for_catchup('sub3');
|
||||
|
||||
$result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT count(*), min(a), max(a) FROM tab3");
|
||||
is($result, qq(0||),
|
||||
'truncate of multiple tables replicated');
|
||||
$result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT count(*), min(x), max(x) FROM tab4");
|
||||
is($result, qq(0||),
|
||||
'truncate of multiple tables replicated');
|
||||
|
||||
# test truncate of multiple tables, some of which are not published
|
||||
|
||||
$node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION sub2");
|
||||
$node_publisher->safe_psql('postgres', "DROP PUBLICATION pub2");
|
||||
|
||||
$node_subscriber->safe_psql('postgres', "INSERT INTO tab1 VALUES (1), (2), (3)");
|
||||
$node_subscriber->safe_psql('postgres', "INSERT INTO tab2 VALUES (1), (2), (3)");
|
||||
|
||||
$node_publisher->safe_psql('postgres', "TRUNCATE tab1, tab2");
|
||||
|
||||
$node_publisher->wait_for_catchup('sub1');
|
||||
|
||||
$result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT count(*), min(a), max(a) FROM tab1");
|
||||
is($result, qq(0||),
|
||||
'truncate of multiple tables some not published');
|
||||
$result = $node_subscriber->safe_psql('postgres',
|
||||
"SELECT count(*), min(a), max(a) FROM tab2");
|
||||
is($result, qq(3|1|3),
|
||||
'truncate of multiple tables some not published');
|
Reference in New Issue
Block a user