Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4548c76

Browse files
author
Amit Kapila
committed
Invalidate all partitions for a partitioned table in publication.
Updates/Deletes on a partition were allowed even without replica identity after the parent table was added to a publication. This would later lead to an error on subscribers. The reason was that we were not invalidating the partition's relcache and the publication information for partitions was not getting rebuilt. Similarly, we were not invalidating the partitions' relcache after dropping a partitioned table from a publication which will prohibit Updates/Deletes on its partition without replica identity even without any publication. Reported-by: Haiying Tang Author: Hou Zhijie and Vignesh C Reviewed-by: Vignesh C and Amit Kapila Backpatch-through: 13 Discussion: https://postgr.es/m/OS0PR01MB6113D77F583C922F1CEAA1C3FBD29@OS0PR01MB6113.jpnprd01.prod.outlook.com
1 parent 5e77625 commit 4548c76

File tree

6 files changed

+116
-55
lines changed

6 files changed

+116
-55
lines changed

src/backend/catalog/pg_publication.c

+53-29
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "catalog/pg_publication.h"
3232
#include "catalog/pg_publication_rel.h"
3333
#include "catalog/pg_type.h"
34+
#include "commands/publicationcmds.h"
3435
#include "funcapi.h"
3536
#include "miscadmin.h"
3637
#include "utils/array.h"
@@ -136,6 +137,42 @@ pg_relation_is_publishable(PG_FUNCTION_ARGS)
136137
PG_RETURN_BOOL(result);
137138
}
138139

140+
/*
141+
* Gets the relations based on the publication partition option for a specified
142+
* relation.
143+
*/
144+
List *
145+
GetPubPartitionOptionRelations(List *result, PublicationPartOpt pub_partopt,
146+
Oid relid)
147+
{
148+
if (get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE &&
149+
pub_partopt != PUBLICATION_PART_ROOT)
150+
{
151+
List *all_parts = find_all_inheritors(relid, NoLock,
152+
NULL);
153+
154+
if (pub_partopt == PUBLICATION_PART_ALL)
155+
result = list_concat(result, all_parts);
156+
else if (pub_partopt == PUBLICATION_PART_LEAF)
157+
{
158+
ListCell *lc;
159+
160+
foreach(lc, all_parts)
161+
{
162+
Oid partOid = lfirst_oid(lc);
163+
164+
if (get_rel_relkind(partOid) != RELKIND_PARTITIONED_TABLE)
165+
result = lappend_oid(result, partOid);
166+
}
167+
}
168+
else
169+
Assert(false);
170+
}
171+
else
172+
result = lappend_oid(result, relid);
173+
174+
return result;
175+
}
139176

140177
/*
141178
* Insert new publication / relation mapping.
@@ -153,6 +190,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *targetrel,
153190
Publication *pub = GetPublication(pubid);
154191
ObjectAddress myself,
155192
referenced;
193+
List *relids = NIL;
156194

157195
rel = table_open(PublicationRelRelationId, RowExclusiveLock);
158196

@@ -208,8 +246,18 @@ publication_add_relation(Oid pubid, PublicationRelInfo *targetrel,
208246
/* Close the table. */
209247
table_close(rel, RowExclusiveLock);
210248

211-
/* Invalidate relcache so that publication info is rebuilt. */
212-
CacheInvalidateRelcache(targetrel->relation);
249+
/*
250+
* Invalidate relcache so that publication info is rebuilt.
251+
*
252+
* For the partitioned tables, we must invalidate all partitions contained
253+
* in the respective partition hierarchies, not just the one explicitly
254+
* mentioned in the publication. This is required because we implicitly
255+
* publish the child tables when the parent table is published.
256+
*/
257+
relids = GetPubPartitionOptionRelations(relids, PUBLICATION_PART_ALL,
258+
relid);
259+
260+
InvalidatePublicationRels(relids);
213261

214262
return myself;
215263
}
@@ -241,7 +289,7 @@ GetRelationPublications(Oid relid)
241289
/*
242290
* Gets list of relation oids for a publication.
243291
*
244-
* This should only be used for normal publications, the FOR ALL TABLES
292+
* This should only be used FOR TABLE publications, the FOR ALL TABLES
245293
* should use GetAllTablesPublicationRelations().
246294
*/
247295
List *
@@ -270,32 +318,8 @@ GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
270318
Form_pg_publication_rel pubrel;
271319

272320
pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
273-
274-
if (get_rel_relkind(pubrel->prrelid) == RELKIND_PARTITIONED_TABLE &&
275-
pub_partopt != PUBLICATION_PART_ROOT)
276-
{
277-
List *all_parts = find_all_inheritors(pubrel->prrelid, NoLock,
278-
NULL);
279-
280-
if (pub_partopt == PUBLICATION_PART_ALL)
281-
result = list_concat(result, all_parts);
282-
else if (pub_partopt == PUBLICATION_PART_LEAF)
283-
{
284-
ListCell *lc;
285-
286-
foreach(lc, all_parts)
287-
{
288-
Oid partOid = lfirst_oid(lc);
289-
290-
if (get_rel_relkind(partOid) != RELKIND_PARTITIONED_TABLE)
291-
result = lappend_oid(result, partOid);
292-
}
293-
}
294-
else
295-
Assert(false);
296-
}
297-
else
298-
result = lappend_oid(result, pubrel->prrelid);
321+
result = GetPubPartitionOptionRelations(result, pub_partopt,
322+
pubrel->prrelid);
299323
}
300324

301325
systable_endscan(scan);

src/backend/commands/publicationcmds.c

+35-22
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@
4545
#include "utils/syscache.h"
4646
#include "utils/varlena.h"
4747

48-
/* Same as MAXNUMMESSAGES in sinvaladt.c */
49-
#define MAX_RELCACHE_INVAL_MSGS 4096
50-
5148
static List *OpenTableList(List *tables);
5249
static void CloseTableList(List *rels);
5350
static void PublicationAddTables(Oid pubid, List *rels, bool if_not_exists,
@@ -329,23 +326,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
329326
List *relids = GetPublicationRelations(pubform->oid,
330327
PUBLICATION_PART_ALL);
331328

332-
/*
333-
* We don't want to send too many individual messages, at some point
334-
* it's cheaper to just reset whole relcache.
335-
*/
336-
if (list_length(relids) < MAX_RELCACHE_INVAL_MSGS)
337-
{
338-
ListCell *lc;
339-
340-
foreach(lc, relids)
341-
{
342-
Oid relid = lfirst_oid(lc);
343-
344-
CacheInvalidateRelcacheByRelid(relid);
345-
}
346-
}
347-
else
348-
CacheInvalidateRelcacheAll();
329+
InvalidatePublicationRels(relids);
349330
}
350331

351332
ObjectAddressSet(obj, PublicationRelationId, pubform->oid);
@@ -355,6 +336,27 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
355336
InvokeObjectPostAlterHook(PublicationRelationId, pubform->oid, 0);
356337
}
357338

339+
/*
340+
* Invalidate the relations.
341+
*/
342+
void
343+
InvalidatePublicationRels(List *relids)
344+
{
345+
/*
346+
* We don't want to send too many individual messages, at some point it's
347+
* cheaper to just reset whole relcache.
348+
*/
349+
if (list_length(relids) < MAX_RELCACHE_INVAL_MSGS)
350+
{
351+
ListCell *lc;
352+
353+
foreach(lc, relids)
354+
CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
355+
}
356+
else
357+
CacheInvalidateRelcacheAll();
358+
}
359+
358360
/*
359361
* Add or remove table to/from publication.
360362
*/
@@ -488,6 +490,7 @@ RemovePublicationRelById(Oid proid)
488490
Relation rel;
489491
HeapTuple tup;
490492
Form_pg_publication_rel pubrel;
493+
List *relids = NIL;
491494

492495
rel = table_open(PublicationRelRelationId, RowExclusiveLock);
493496

@@ -499,8 +502,18 @@ RemovePublicationRelById(Oid proid)
499502

500503
pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
501504

502-
/* Invalidate relcache so that publication info is rebuilt. */
503-
CacheInvalidateRelcacheByRelid(pubrel->prrelid);
505+
/*
506+
* Invalidate relcache so that publication info is rebuilt.
507+
*
508+
* For the partitioned tables, we must invalidate all partitions contained
509+
* in the respective partition hierarchies, not just the one explicitly
510+
* mentioned in the publication. This is required because we implicitly
511+
* publish the child tables when the parent table is published.
512+
*/
513+
relids = GetPubPartitionOptionRelations(relids, PUBLICATION_PART_ALL,
514+
pubrel->prrelid);
515+
516+
InvalidatePublicationRels(relids);
504517

505518
CatalogTupleDelete(rel, &tup->t_self);
506519

src/include/catalog/pg_publication.h

+3
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ typedef enum PublicationPartOpt
111111
extern List *GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt);
112112
extern List *GetAllTablesPublications(void);
113113
extern List *GetAllTablesPublicationRelations(bool pubviaroot);
114+
extern List *GetPubPartitionOptionRelations(List *result,
115+
PublicationPartOpt pub_partopt,
116+
Oid relid);
114117

115118
extern bool is_publishable_relation(Relation rel);
116119
extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *targetrel,

src/include/commands/publicationcmds.h

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717

1818
#include "catalog/objectaddress.h"
1919
#include "parser/parse_node.h"
20+
#include "utils/inval.h"
21+
22+
/* Same as MAXNUMMESSAGES in sinvaladt.c */
23+
#define MAX_RELCACHE_INVAL_MSGS 4096
2024

2125
extern ObjectAddress CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt);
2226
extern void AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt);
@@ -25,5 +29,6 @@ extern void RemovePublicationRelById(Oid proid);
2529

2630
extern ObjectAddress AlterPublicationOwner(const char *name, Oid newOwnerId);
2731
extern void AlterPublicationOwner_oid(Oid pubid, Oid newOwnerId);
32+
extern void InvalidatePublicationRels(List *relids);
2833

2934
#endif /* PUBLICATIONCMDS_H */

src/test/regress/expected/publication.out

+11-2
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@ CREATE PUBLICATION testpub_forparted;
126126
CREATE PUBLICATION testpub_forparted1;
127127
RESET client_min_messages;
128128
CREATE TABLE testpub_parted1 (LIKE testpub_parted);
129+
CREATE TABLE testpub_parted2 (LIKE testpub_parted);
129130
ALTER PUBLICATION testpub_forparted1 SET (publish='insert');
131+
ALTER TABLE testpub_parted ATTACH PARTITION testpub_parted1 FOR VALUES IN (1);
132+
ALTER TABLE testpub_parted ATTACH PARTITION testpub_parted2 FOR VALUES IN (2);
130133
-- works despite missing REPLICA IDENTITY, because updates are not replicated
131134
UPDATE testpub_parted1 SET a = 1;
132-
ALTER TABLE testpub_parted ATTACH PARTITION testpub_parted1 FOR VALUES IN (1);
133135
-- only parent is listed as being in publication, not the partition
134136
ALTER PUBLICATION testpub_forparted ADD TABLE testpub_parted;
135137
\dRp+ testpub_forparted
@@ -156,7 +158,14 @@ ALTER PUBLICATION testpub_forparted SET (publish_via_partition_root = true);
156158
Tables:
157159
"public.testpub_parted"
158160

159-
DROP TABLE testpub_parted1;
161+
-- still fail, because parent's publication replicates updates
162+
UPDATE testpub_parted2 SET a = 2;
163+
ERROR: cannot update table "testpub_parted2" because it does not have a replica identity and publishes updates
164+
HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.
165+
ALTER PUBLICATION testpub_forparted DROP TABLE testpub_parted;
166+
-- works again, because update is no longer replicated
167+
UPDATE testpub_parted2 SET a = 2;
168+
DROP TABLE testpub_parted1, testpub_parted2;
160169
DROP PUBLICATION testpub_forparted, testpub_forparted1;
161170
-- Test cache invalidation FOR ALL TABLES publication
162171
SET client_min_messages = 'ERROR';

src/test/regress/sql/publication.sql

+9-2
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,12 @@ CREATE PUBLICATION testpub_forparted;
7676
CREATE PUBLICATION testpub_forparted1;
7777
RESET client_min_messages;
7878
CREATE TABLE testpub_parted1 (LIKE testpub_parted);
79+
CREATE TABLE testpub_parted2 (LIKE testpub_parted);
7980
ALTER PUBLICATION testpub_forparted1 SET (publish='insert');
81+
ALTER TABLE testpub_parted ATTACH PARTITION testpub_parted1 FOR VALUES IN (1);
82+
ALTER TABLE testpub_parted ATTACH PARTITION testpub_parted2 FOR VALUES IN (2);
8083
-- works despite missing REPLICA IDENTITY, because updates are not replicated
8184
UPDATE testpub_parted1 SET a = 1;
82-
ALTER TABLE testpub_parted ATTACH PARTITION testpub_parted1 FOR VALUES IN (1);
8385
-- only parent is listed as being in publication, not the partition
8486
ALTER PUBLICATION testpub_forparted ADD TABLE testpub_parted;
8587
\dRp+ testpub_forparted
@@ -90,7 +92,12 @@ ALTER TABLE testpub_parted DETACH PARTITION testpub_parted1;
9092
UPDATE testpub_parted1 SET a = 1;
9193
ALTER PUBLICATION testpub_forparted SET (publish_via_partition_root = true);
9294
\dRp+ testpub_forparted
93-
DROP TABLE testpub_parted1;
95+
-- still fail, because parent's publication replicates updates
96+
UPDATE testpub_parted2 SET a = 2;
97+
ALTER PUBLICATION testpub_forparted DROP TABLE testpub_parted;
98+
-- works again, because update is no longer replicated
99+
UPDATE testpub_parted2 SET a = 2;
100+
DROP TABLE testpub_parted1, testpub_parted2;
94101
DROP PUBLICATION testpub_forparted, testpub_forparted1;
95102

96103
-- Test cache invalidation FOR ALL TABLES publication

0 commit comments

Comments
 (0)