1
0
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:
Peter Eisentraut
2018-04-07 11:24:53 -04:00
parent 5dfd1e5a66
commit 039eb6e92f
19 changed files with 572 additions and 111 deletions

View File

@ -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;

View 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');