Skip to content

Commit 580d4ed

Browse files
author
Commitfest Bot
committed
[CF 6099] v31 - Support EXCEPT tables and EXCEPT (column-list) in publications
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/6099 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CANhcyEV_EVi5cgJ6WPvmeVAqjCS7Of+VAWuRHZtsVf8PQb_z7g@mail.gmail.com Author(s): Shlok Kyal
2 parents b4cbc10 + cce8659 commit 580d4ed

File tree

24 files changed

+768
-123
lines changed

24 files changed

+768
-123
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6581,6 +6581,15 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
65816581
if there is no publication qualifying condition.</para></entry>
65826582
</row>
65836583

6584+
<row>
6585+
<entry role="catalog_table_entry"><para role="column_definition">
6586+
<structfield>prexcept</structfield> <type>bool</type>
6587+
</para>
6588+
<para>
6589+
True if the relation must be excluded
6590+
</para></entry>
6591+
</row>
6592+
65846593
<row>
65856594
<entry role="catalog_table_entry"><para role="column_definition">
65866595
<structfield>prattrs</structfield> <type>int2vector</type>

doc/src/sgml/logical-replication.sgml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2550,10 +2550,12 @@ CONTEXT: processing remote data for replication origin "pg_16395" during "INSER
25502550
</para>
25512551

25522552
<para>
2553-
To add tables to a publication, the user must have ownership rights on the
2554-
table. To add all tables in schema to a publication, the user must be a
2555-
superuser. To create a publication that publishes all tables, all tables in
2556-
schema, or all sequences automatically, the user must be a superuser.
2553+
To create a publication using <literal>FOR ALL TABLES</literal>,
2554+
<literal>FOR ALL SEQUENCES</literal> or
2555+
<literal>FOR TABLES IN SCHEMA</literal>, the user must be a superuser. To add
2556+
<literal>ALL TABLES</literal> or <literal>TABLES IN SCHEMA</literal> to a
2557+
publication, the user must be a superuser. To add tables to a publication,
2558+
the user must have ownership rights on the table.
25572559
</para>
25582560

25592561
<para>

doc/src/sgml/ref/create_publication.sgml

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,16 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
3232

3333
<phrase>and <replaceable class="parameter">publication_all_object</replaceable> is one of:</phrase>
3434

35-
ALL TABLES
35+
ALL TABLES [ EXCEPT [ TABLE ] ( <replaceable class="parameter">table_exception_object</replaceable> [, ... ] ) ]
3636
ALL SEQUENCES
3737

3838
<phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase>
3939

4040
[ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE ( <replaceable class="parameter">expression</replaceable> ) ]
41+
42+
<phrase>where <replaceable class="parameter">table_exception_object</replaceable> is:</phrase>
43+
44+
[ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
4145
</synopsis>
4246
</refsynopsisdiv>
4347

@@ -164,7 +168,9 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
164168
<listitem>
165169
<para>
166170
Marks the publication as one that replicates changes for all tables in
167-
the database, including tables created in the future.
171+
the database, including tables created in the future. If
172+
<literal>EXCEPT TABLE</literal> is specified, then exclude replicating
173+
the changes for the specified tables.
168174
</para>
169175
</listitem>
170176
</varlistentry>
@@ -184,6 +190,35 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
184190
</listitem>
185191
</varlistentry>
186192

193+
<varlistentry id="sql-createpublication-params-for-except-table">
194+
<term><literal>EXCEPT TABLE</literal></term>
195+
<listitem>
196+
<para>
197+
This clause specifies a list of tables to be excluded from the
198+
publication. It can only be used with <literal>FOR ALL TABLES</literal>.
199+
If <literal>ONLY</literal> is specified before the table name, only
200+
that table is excluded from the publication. If <literal>ONLY</literal> is
201+
not specified, the table and all its descendant tables (if any) are
202+
excluded. Optionally, <literal>*</literal> can be specified after the
203+
table name to explicitly indicate that descendant tables are excluded.
204+
This does not apply to a partitioned table, however.
205+
</para>
206+
<para>
207+
The partitioned table or its partitions are excluded from the publication
208+
based on the parameter <literal>publish_via_partition_root</literal>.
209+
When <literal>publish_via_partition_root</literal> is set to
210+
<literal>true</literal>, specifying a root partitioned table in
211+
<literal>EXCEPT TABLE</literal> excludes it and all its partitions from
212+
replication. Specifying a leaf partition has no effect, as its changes are
213+
still replicated via the root partitioned table. When
214+
<literal>publish_via_partition_root</literal> is set to
215+
<literal>false</literal>, specifying a root partitioned table has no
216+
effect, as changes are replicated via the leaf partitions. Specifying a
217+
leaf partition excludes only that partition from replication.
218+
</para>
219+
</listitem>
220+
</varlistentry>
221+
187222
<varlistentry id="sql-createpublication-params-with">
188223
<term><literal>WITH ( <replaceable class="parameter">publication_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] )</literal></term>
189224
<listitem>
@@ -487,6 +522,23 @@ CREATE PUBLICATION all_sequences FOR ALL SEQUENCES;
487522
all sequences for synchronization:
488523
<programlisting>
489524
CREATE PUBLICATION all_tables_sequences FOR ALL TABLES, ALL SEQUENCES;
525+
</programlisting>
526+
</para>
527+
528+
<para>
529+
Create a publication that publishes all changes in all the tables except
530+
<structname>users</structname> and <structname>departments</structname>:
531+
<programlisting>
532+
CREATE PUBLICATION all_tables_except FOR ALL TABLES EXCEPT (users, departments);
533+
</programlisting>
534+
</para>
535+
536+
<para>
537+
Create a publication that publishes all changes in all sequences and all the
538+
tables except tables <structname>users</structname> and
539+
<structname>departments</structname>:
540+
<programlisting>
541+
CREATE PUBLICATION all_sequences_tables_except FOR ALL SEQUENCES, ALL TABLES EXCEPT (users, departments);
490542
</programlisting>
491543
</para>
492544
</refsect1>

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,8 +2103,9 @@ SELECT $1 \parse stmt1
21032103
listed.
21042104
If <literal>x</literal> is appended to the command name, the results
21052105
are displayed in expanded mode.
2106-
If <literal>+</literal> is appended to the command name, the tables and
2107-
schemas associated with each publication are shown as well.
2106+
If <literal>+</literal> is appended to the command name, the tables,
2107+
excluded tables, and schemas associated with each publication are shown
2108+
as well.
21082109
</para>
21092110
</listitem>
21102111
</varlistentry>

src/backend/catalog/pg_publication.c

Lines changed: 102 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,8 @@ GetPubPartitionOptionRelations(List *result, PublicationPartOpt pub_partopt,
354354
* ancestor is at the end of the list.
355355
*/
356356
Oid
357-
GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level)
357+
GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
358+
int *ancestor_level, bool puballtables)
358359
{
359360
ListCell *lc;
360361
Oid topmost_relid = InvalidOid;
@@ -366,32 +367,43 @@ GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level
366367
foreach(lc, ancestors)
367368
{
368369
Oid ancestor = lfirst_oid(lc);
369-
List *apubids = GetRelationPublications(ancestor);
370-
List *aschemaPubids = NIL;
370+
List *apubids = NIL;
371+
List *aexceptpubids = NIL;
372+
List *aschemapubids = NIL;
373+
bool set_top = false;
374+
375+
GetRelationPublications(ancestor, &apubids, &aexceptpubids);
371376

372377
level++;
373378

374-
if (list_member_oid(apubids, puboid))
379+
/* check if member of table publications */
380+
set_top = list_member_oid(apubids, puboid);
381+
if (!set_top)
375382
{
376-
topmost_relid = ancestor;
383+
aschemapubids = GetSchemaPublications(get_rel_namespace(ancestor));
377384

378-
if (ancestor_level)
379-
*ancestor_level = level;
385+
/* check if member of schema publications */
386+
set_top = list_member_oid(aschemapubids, puboid);
387+
388+
/*
389+
* If the publication is all tables publication and the table is
390+
* not part of exception tables.
391+
*/
392+
if (!set_top && puballtables)
393+
set_top = !list_member_oid(aexceptpubids, puboid);
380394
}
381-
else
395+
396+
if (set_top)
382397
{
383-
aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
384-
if (list_member_oid(aschemaPubids, puboid))
385-
{
386-
topmost_relid = ancestor;
398+
topmost_relid = ancestor;
387399

388-
if (ancestor_level)
389-
*ancestor_level = level;
390-
}
400+
if (ancestor_level)
401+
*ancestor_level = level;
391402
}
392403

393404
list_free(apubids);
394-
list_free(aschemaPubids);
405+
list_free(aschemapubids);
406+
list_free(aexceptpubids);
395407
}
396408

397409
return topmost_relid;
@@ -466,6 +478,26 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
466478
RelationGetRelationName(targetrel), pub->name)));
467479
}
468480

481+
/*
482+
* Check when a partition is excluded via EXCEPT TABLE while the
483+
* publication has publish_via_partition_root = true.
484+
*/
485+
if (pub->alltables && pub->pubviaroot && pri->except &&
486+
targetrel->rd_rel->relispartition)
487+
ereport(WARNING,
488+
(errmsg("partition \"%s\" might be replicated as publish_via_partition_root is \"%s\"",
489+
RelationGetRelationName(targetrel), "true")));
490+
491+
/*
492+
* Check when a partitioned table is excluded via EXCEPT TABLE while the
493+
* publication has publish_via_partition_root = false.
494+
*/
495+
if (pub->alltables && !pub->pubviaroot && pri->except &&
496+
targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
497+
ereport(WARNING,
498+
(errmsg("partitioned table \"%s\" might be replicated as publish_via_partition_root is \"%s\"",
499+
RelationGetRelationName(targetrel), "false")));
500+
469501
check_publication_add_relation(targetrel);
470502

471503
/* Validate and translate column names into a Bitmapset of attnums. */
@@ -482,6 +514,8 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
482514
ObjectIdGetDatum(pubid);
483515
values[Anum_pg_publication_rel_prrelid - 1] =
484516
ObjectIdGetDatum(relid);
517+
values[Anum_pg_publication_rel_prexcept - 1] =
518+
BoolGetDatum(pri->except);
485519

486520
/* Add qualifications, if available */
487521
if (pri->whereClause != NULL)
@@ -749,35 +783,59 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
749783
return myself;
750784
}
751785

752-
/* Gets list of publication oids for a relation */
753-
List *
754-
GetRelationPublications(Oid relid)
786+
/*
787+
* Get the list of publication oids associated with a specified relation.
788+
* pubids is filled with the list of publication oids the relation is part of.
789+
* except_pubids is filled with the list of publication oids the relation is
790+
* excluded from.
791+
*
792+
* This function returns true if the relation is part of any publication.
793+
*/
794+
bool
795+
GetRelationPublications(Oid relid, List **pubids, List **except_pubids)
755796
{
756-
List *result = NIL;
757797
CatCList *pubrellist;
758-
int i;
798+
bool found = false;
759799

760800
/* Find all publications associated with the relation. */
761801
pubrellist = SearchSysCacheList1(PUBLICATIONRELMAP,
762802
ObjectIdGetDatum(relid));
763-
for (i = 0; i < pubrellist->n_members; i++)
803+
for (int i = 0; i < pubrellist->n_members; i++)
764804
{
765805
HeapTuple tup = &pubrellist->members[i]->tuple;
766-
Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid;
806+
Form_pg_publication_rel pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
807+
Oid pubid = pubrel->prpubid;
767808

768-
result = lappend_oid(result, pubid);
809+
if (pubrel->prexcept)
810+
{
811+
if (except_pubids)
812+
*except_pubids = lappend_oid(*except_pubids, pubid);
813+
}
814+
else
815+
{
816+
if (pubids)
817+
*pubids = lappend_oid(*pubids, pubid);
818+
found = true;
819+
}
769820
}
770821

771822
ReleaseSysCacheList(pubrellist);
772823

773-
return result;
824+
return found;
774825
}
775826

776827
/*
777-
* Gets list of relation oids for a publication.
828+
* Return the list of relation OIDs for a publication.
829+
*
830+
* For a FOR ALL TABLES publication, this returns the list of tables that were
831+
* explicitly excluded via an EXCEPT TABLE clause.
832+
*
833+
* For a FOR TABLE publication, this returns the list of tables explicitly
834+
* included in the publication.
778835
*
779-
* This should only be used FOR TABLE publications, the FOR ALL TABLES/SEQUENCES
780-
* should use GetAllPublicationRelations().
836+
* Publications declared with FOR ALL TABLES or FOR ALL SEQUENCES should use
837+
* GetAllPublicationRelations() to obtain the complete set of tables covered by
838+
* the publication.
781839
*/
782840
List *
783841
GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
@@ -864,15 +922,23 @@ GetAllTablesPublications(void)
864922
* partitioned tables, we must exclude partitions in favor of including the
865923
* root partitioned tables. This is not applicable to FOR ALL SEQUENCES
866924
* publication.
925+
*
926+
* The list does not include relations that are explicitly excluded via the
927+
* EXCEPT TABLE clause of the publication specified by pubid.
867928
*/
868929
List *
869-
GetAllPublicationRelations(char relkind, bool pubviaroot)
930+
GetAllPublicationRelations(Oid pubid, char relkind, bool pubviaroot)
870931
{
871932
Relation classRel;
872933
ScanKeyData key[1];
873934
TableScanDesc scan;
874935
HeapTuple tuple;
875936
List *result = NIL;
937+
List *exceptlist;
938+
939+
exceptlist = GetPublicationRelations(pubid, pubviaroot ?
940+
PUBLICATION_PART_ALL :
941+
PUBLICATION_PART_ROOT);
876942

877943
Assert(!(relkind == RELKIND_SEQUENCE && pubviaroot));
878944

@@ -891,7 +957,8 @@ GetAllPublicationRelations(char relkind, bool pubviaroot)
891957
Oid relid = relForm->oid;
892958

893959
if (is_publishable_class(relid, relForm) &&
894-
!(relForm->relispartition && pubviaroot))
960+
!(relForm->relispartition && pubviaroot) &&
961+
!list_member_oid(exceptlist, relid))
895962
result = lappend_oid(result, relid);
896963
}
897964

@@ -912,7 +979,8 @@ GetAllPublicationRelations(char relkind, bool pubviaroot)
912979
Oid relid = relForm->oid;
913980

914981
if (is_publishable_class(relid, relForm) &&
915-
!relForm->relispartition)
982+
!relForm->relispartition &&
983+
!list_member_oid(exceptlist, relid))
916984
result = lappend_oid(result, relid);
917985
}
918986

@@ -1168,7 +1236,8 @@ pg_get_publication_tables(PG_FUNCTION_ARGS)
11681236
* those. Otherwise, get the partitioned table itself.
11691237
*/
11701238
if (pub_elem->alltables)
1171-
pub_elem_tables = GetAllPublicationRelations(RELKIND_RELATION,
1239+
pub_elem_tables = GetAllPublicationRelations(pub_elem->oid,
1240+
RELKIND_RELATION,
11721241
pub_elem->pubviaroot);
11731242
else
11741243
{
@@ -1367,7 +1436,7 @@ pg_get_publication_sequences(PG_FUNCTION_ARGS)
13671436
publication = GetPublicationByName(pubname, false);
13681437

13691438
if (publication->allsequences)
1370-
sequences = GetAllPublicationRelations(RELKIND_SEQUENCE, false);
1439+
sequences = GetAllPublicationRelations(publication->oid, RELKIND_SEQUENCE, false);
13711440

13721441
funcctx->user_fctx = sequences;
13731442

0 commit comments

Comments
 (0)