Skip to content

Commit 5e97905

Browse files
author
Amit Kapila
committed
Fix double publish of child table's data.
We publish the child table's data twice for a publication that has both child and parent tables and is published with publish_via_partition_root as true. This happens because subscribers will initiate synchronization using both parent and child tables, since it gets both as separate tables in the initial table list. Ensure that pg_publication_tables returns only parent tables in such cases. Author: Hou Zhijie Reviewed-by: Greg Nancarrow, Amit Langote, Vignesh C, Amit Kapila Backpatch-through: 13 Discussion: https://postgr.es/m/OS0PR01MB57167F45D481F78CDC5986F794B99@OS0PR01MB5716.jpnprd01.prod.outlook.com
1 parent bcf6058 commit 5e97905

File tree

4 files changed

+40
-31
lines changed

4 files changed

+40
-31
lines changed

src/backend/catalog/pg_publication.c

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ is_publishable_class(Oid relid, Form_pg_class reltuple)
142142
* the publication.
143143
*/
144144
static List *
145-
filter_partitions(List *relids, List *schemarelids)
145+
filter_partitions(List *relids)
146146
{
147147
List *result = NIL;
148148
ListCell *lc;
@@ -161,16 +161,8 @@ filter_partitions(List *relids, List *schemarelids)
161161
{
162162
Oid ancestor = lfirst_oid(lc2);
163163

164-
/*
165-
* Check if the parent table exists in the published table list.
166-
*
167-
* XXX As of now, we do this if the partition relation or the
168-
* partition relation's ancestor is present in schema publication
169-
* relations.
170-
*/
171-
if (list_member_oid(relids, ancestor) &&
172-
(list_member_oid(schemarelids, relid) ||
173-
list_member_oid(schemarelids, ancestor)))
164+
/* Check if the parent table exists in the published table list. */
165+
if (list_member_oid(relids, ancestor))
174166
{
175167
skip = true;
176168
break;
@@ -913,22 +905,17 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
913905
PUBLICATION_PART_ROOT :
914906
PUBLICATION_PART_LEAF);
915907
tables = list_concat_unique_oid(relids, schemarelids);
916-
if (schemarelids && publication->pubviaroot)
917-
{
918-
/*
919-
* If the publication publishes partition changes via their
920-
* respective root partitioned tables, we must exclude
921-
* partitions in favor of including the root partitioned
922-
* tables. Otherwise, the function could return both the child
923-
* and parent tables which could cause data of the child table
924-
* to be double-published on the subscriber side.
925-
*
926-
* XXX As of now, we do this when a publication has associated
927-
* schema or for all tables publication. See
928-
* GetAllTablesPublicationRelations().
929-
*/
930-
tables = filter_partitions(tables, schemarelids);
931-
}
908+
909+
/*
910+
* If the publication publishes partition changes via their
911+
* respective root partitioned tables, we must exclude partitions
912+
* in favor of including the root partitioned tables. Otherwise,
913+
* the function could return both the child and parent tables
914+
* which could cause data of the child table to be
915+
* double-published on the subscriber side.
916+
*/
917+
if (publication->pubviaroot)
918+
tables = filter_partitions(tables);
932919
}
933920

934921
funcctx->user_fctx = (void *) tables;

src/test/regress/expected/publication.out

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,14 @@ SELECT * FROM pg_publication_tables;
829829
pub | sch2 | tbl1_part1
830830
(1 row)
831831

832+
-- Table publication that includes both the parent table and the child table
833+
ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
834+
SELECT * FROM pg_publication_tables;
835+
pubname | schemaname | tablename
836+
---------+------------+-----------
837+
pub | sch1 | tbl1
838+
(1 row)
839+
832840
DROP PUBLICATION pub;
833841
-- Schema publication that does not include the schema that has the parent table
834842
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=0);

src/test/regress/sql/publication.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,10 @@ DROP PUBLICATION pub;
474474
CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=1);
475475
SELECT * FROM pg_publication_tables;
476476

477+
-- Table publication that includes both the parent table and the child table
478+
ALTER PUBLICATION pub ADD TABLE sch1.tbl1;
479+
SELECT * FROM pg_publication_tables;
480+
477481
DROP PUBLICATION pub;
478482
-- Schema publication that does not include the schema that has the parent table
479483
CREATE PUBLICATION pub FOR ALL TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=0);

src/test/subscription/t/013_partition.pl

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use warnings;
77
use PostgreSQL::Test::Cluster;
88
use PostgreSQL::Test::Utils;
9-
use Test::More tests => 62;
9+
use Test::More tests => 63;
1010

1111
# setup
1212

@@ -412,11 +412,16 @@ BEGIN
412412
$node_publisher->safe_psql('postgres',
413413
"ALTER PUBLICATION pub_all SET (publish_via_partition_root = true)");
414414
# Note: tab3_1's parent is not in the publication, in which case its
415-
# changes are published using own identity.
415+
# changes are published using own identity. For tab2, even though both parent
416+
# and child tables are present but changes will be replicated via the parent's
417+
# identity and only once.
416418
$node_publisher->safe_psql('postgres',
417-
"CREATE PUBLICATION pub_viaroot FOR TABLE tab2, tab3_1 WITH (publish_via_partition_root = true)"
419+
"CREATE PUBLICATION pub_viaroot FOR TABLE tab2, tab2_1, tab3_1 WITH (publish_via_partition_root = true)"
418420
);
419421

422+
# prepare data for the initial sync
423+
$node_publisher->safe_psql('postgres', "INSERT INTO tab2 VALUES (1)");
424+
420425
# subscriber 1
421426
$node_subscriber1->safe_psql('postgres', "DROP SUBSCRIPTION sub1");
422427
$node_subscriber1->safe_psql('postgres',
@@ -468,12 +473,17 @@ BEGIN
468473
$node_subscriber2->poll_query_until('postgres', $synced_query)
469474
or die "Timed out while waiting for subscriber to synchronize data";
470475

476+
# check that data is synced correctly
477+
$result = $node_subscriber1->safe_psql('postgres',
478+
"SELECT c, a FROM tab2");
479+
is( $result, qq(sub1_tab2|1), 'initial data synced for pub_viaroot');
480+
471481
# insert
472482
$node_publisher->safe_psql('postgres', "INSERT INTO tab1 VALUES (1), (0)");
473483
$node_publisher->safe_psql('postgres', "INSERT INTO tab1_1 (a) VALUES (3)");
474484
$node_publisher->safe_psql('postgres', "INSERT INTO tab1_2 VALUES (5)");
475485
$node_publisher->safe_psql('postgres',
476-
"INSERT INTO tab2 VALUES (1), (0), (3), (5)");
486+
"INSERT INTO tab2 VALUES (0), (3), (5)");
477487
$node_publisher->safe_psql('postgres',
478488
"INSERT INTO tab3 VALUES (1), (0), (3), (5)");
479489

0 commit comments

Comments
 (0)