Waiting for PostgreSQL 19 – Allow table exclusions in publications via EXCEPT TABLE.

On 4th of March 2026, Amit Kapila committed patch:

Allow table exclusions in publications via EXCEPT TABLE.
 
Extend CREATE PUBLICATION ... FOR ALL TABLES to support the EXCEPT TABLE
syntax. This allows one or more tables to be excluded. The publisher will
not send the data of excluded tables to the subscriber.
 
To support this, pg_publication_rel now includes a prexcept column to flag
excluded relations. For partitioned tables, the exclusion is applied at
the root level; specifying a root table excludes all current and future
partitions in that tree.
 
Follow-up work will implement ALTER PUBLICATION support for managing these
exclusions.
 
Author: vignesh C <vignesh21@gmail.com>
Author: Shlok Kyal <shlok.kyal.oss@gmail.com>
Reviewed-by: shveta malik <shveta.malik@gmail.com>
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
Reviewed-by: Peter Smith <smithpb2250@gmail.com>
Reviewed-by: Dilip Kumar <dilipbalaut@gmail.com>
Reviewed-by: Zhijie Hou <houzj.fnst@fujitsu.com>
Reviewed-by: Nisha Moond <nisha.moond412@gmail.com>
Reviewed-by: David G. Johnston <david.g.johnston@gmail.com>
Reviewed-by: Ashutosh Sharma <ashu.coek88@gmail.com>
Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Reviewed-by: Andrei Lepikhov <lepihov@gmail.com>
Discussion: https://postgr.es/m/CALDaNm3=JrucjhiiwsYQw5-PGtBHFONa6F7hhWCXMsGvh=tamA@mail.gmail.com

This is pretty cool. Let me try to explain why.

If you're doing any kind of logical replication, it's being done using publication, and subscription.

You start on source/primary, and you create a publication, and then you subscribe to it to get data. All clear. The problem is that, so far, there have been two types of publications:

  • for all tables – which included every user table in whole database
  • for tables in schema … – which included every table in given schema
  • for table … – which made subscription with just this table

I could have added tables later on to each publication (well, it doesn't really make sense to add table to for all tables – because, well, every table is automatically there.

For example, take a look at this:

=$ create schema s1;
=$ create schema s2;
=$ create table s1.t1 (x int4 primary key, y text);
=$ create table s1.t2 (x int4 primary key, y text);
=$ create table s2.t2 (x int4 primary key, y text);
=$ create table s2.t1 (x int4 primary key, y text);
=$ create table t5 (id serial primary key, payload text);

(last table is in public schema).

Now, when I'll make for all tables subscription, I will get every table:

=$ create publication p1 for all tables;
CREATE PUBLICATION
 
=$ select * from pg_publication_tables ;
 pubname │ schemaname │ tablename │   attnames   │ rowfilter 
─────────┼────────────┼───────────┼──────────────┼───────────
 p1      │ s1         │ t1        │ {x,y}[null]
 p1      │ s1         │ t2        │ {x,y}[null]
 p1      │ s2         │ t2        │ {x,y}[null]
 p1      │ s2         │ t1        │ {x,y}[null]
 p1      │ public     │ t5        │ {id,payload}[null]
(5 rows)

What's more, if I'll add new table to system, it will automatically get included in the publication:

=$ create table tx (id serial primary key, payl text);
CREATE TABLE
 
=$ select * from pg_publication_tables ;
 pubname │ schemaname │ tablename │   attnames   │ rowfilter 
─────────┼────────────┼───────────┼──────────────┼───────────
 p1      │ s1         │ t1        │ {x,y}[null]
 p1      │ s1         │ t2        │ {x,y}[null]
 p1      │ s2         │ t2        │ {x,y}[null]
 p1      │ s2         │ t1        │ {x,y}[null]
 p1      │ public     │ t5        │ {id,payload}[null]
 p1      │ public     │ tx        │ {id,payl}[null]
(6 rows)

Now, I can make also publication for single schema:

=$ create publication p2 for tables in schema s1;
CREATE PUBLICATION
 
=$ select * from pg_publication_tables where pubname = 'p2';
 pubname │ schemaname │ tablename │   attnames   │ rowfilter 
─────────┼────────────┼───────────┼──────────────┼───────────
 p2      │ s1         │ t1        │ {x,y}[null]
 p2      │ s1         │ t2        │ {x,y}[null]
(2 rows)

and of course I can add table there:

=$ alter publication p2 add table s2.t2;
ALTER PUBLICATION
 
=$ select * from pg_publication_tables where pubname = 'p2';
 pubname │ schemaname │ tablename │ attnames │ rowfilter 
─────────┼────────────┼───────────┼──────────┼───────────
 p2      │ s2         │ t2        │ {x,y}[null]
 p2      │ s1         │ t1        │ {x,y}[null]
 p2      │ s1         │ t2        │ {x,y}[null]
(3 rows)

The thing is – you can only add a table (or remove table that was specifically added), but you can't remove a table from for all tables.

So, if you had like bazillion tables, and wanter to replicate all of them, except some, you had to make a publication and list all tables you wanted.

Not so anymore.

=$ create publication p3 for all tables except table (public.t5, s1.t1);
CREATE PUBLICATION
 
=$ select * from pg_publication_tables where pubname = 'p3';
 pubname │ schemaname │ tablename │ attnames  │ rowfilter
─────────┼────────────┼───────────┼───────────┼───────────
 p3      │ s1         │ t2        │ {x,y}[null]
 p3      │ s2         │ t2        │ {x,y}[null]
 p3      │ s2         │ t1        │ {x,y}[null]
 p3      │ public     │ tx        │ {id,payl}[null]
(4 rows)

Unfortunately, at the moment, you can't alter existing for all tables publication to add except clause, and, of course, you can't drop a table from such publication:

=$ alter publication p1 drop table s1.t1;
ERROR:  publication "p1" is defined as FOR ALL TABLES
DETAIL:  Tables or sequences cannot be added to or dropped from FOR ALL TABLES publications.

But given that it's new feature, for future release, I don't think it's big problem.

Anyway – it's great addition, and I think many dbas will find it helpful. Thanks a lot, to everyone involved 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.