Thread

Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. Doc: Clarify that publication exclusions track table identity.

  1. Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-14T06:30:12Z

    Hi hackers,
    
    Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    publications, starting this thread to extend the same capability to
    schema-level publications (TABLES IN SCHEMA).
    
    Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    way to exclude a subset. Users who want to skip a few tables must
    switch to an explicit FOR TABLE list, which loses the convenience of
    schema-level publishing and requires ongoing maintenance as tables are
    added.
    
    Proposed syntax:
    ------------------------
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    
    Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    ambiguity and must belong to the published schema; otherwise, an error
    is raised.
    
    Rules and behavior:
    ----------------------------
    1) TABLES IN SCHEMA can be combined with FOR TABLE, but EXCEPT applies
    only to the schema clause and must appear immediately after it.
    
    Supported:
      CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), TABLE s2.t1;
      CREATE PUBLICATION pub FOR TABLE s2.t1, TABLES IN SCHEMA s1 EXCEPT (s1.t1);
      CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), s2
    EXCEPT (s2.t1)
    
    Not supported:
      CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1,  TABLE s2.t1 EXCEPT (s1.t1);
    
    -- This same rule applies to ALTER PUBLICATION ... ADD/SET.
    
    2) Conflicting definitions
    Specifying the same table both in the EXCEPT clause and explicitly in
    the TABLE clause results in an error, as this creates a conflicting
    definition for the publication.
    
    3) "ALTER PUBLICATION ... DROP TABLES IN SCHEMA" does not support
    EXCEPT clause. Whereas, dropping a schema also removes any associated
    entries from the EXCEPT list of the publication.
     -- To only remove/update except list entries, use SET instead.
    
    4) Consistency with ALL TABLES EXCEPT rules:
      4a) Excluding a partitioned root excludes all its partitions
      4b) Individual partitions cannot be excluded directly; exclude the root table.
      4c) Excluding an inheritance parent (without ONLY) also excludes its children.
    
    The patches are divided into three parts to simplify review:
      Patch-001: Basic framework to support EXCEPT in CREATE PUBLICATION
    ... TABLES IN SCHEMA
      Patch-002: Extend support to ALTER PUBLICATION ... ADD TABLES IN SCHEMA
      Patch-003: Extend support to ALTER PUBLICATION ... SET TABLES IN SCHEMA
    
    The patches are attached, feedback and suggestions are welcome.
    
    [1] https://www.postgresql.org/message-id/CALDaNm3%3DJrucjhiiwsYQw5-PGtBHFONa6F7hhWCXMsGvh%3DtamA%40mail.gmail.com
    
    --
    Thanks,
    Nisha
    
  2. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-04-14T07:32:31Z

    On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > Hi hackers,
    >
    > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > publications, starting this thread to extend the same capability to
    > schema-level publications (TABLES IN SCHEMA).
    
    Hi Nisha.
    
    +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    
    >
    > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > way to exclude a subset. Users who want to skip a few tables must
    > switch to an explicit FOR TABLE list, which loses the convenience of
    > schema-level publishing and requires ongoing maintenance as tables are
    > added.
    >
    > Proposed syntax:
    > ------------------------
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    >
    > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > ambiguity and must belong to the published schema; otherwise, an error
    > is raised.
    >
    
    The proposed syntax is almost, but not quite, what I was anticipating.
    IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    It can be *identical* to it. e.g., your examples are missing the
    'TABLE' keyword necessary to achieve the same command flexibility.
    
    Furthermore, what is the ambiguity referred to? An excluded table is
    clearly associated with the preceding schema. Can't the code infer the
    schema internally even when it is not provided by the user? Of course,
    the user *can* specify a schema-qualified name if they want to, but I
    didn't see why we are forcing that rule upon them.
    
    e.g.
    
    -- Syntax can be *identical* to the "EXCEPT (TABLE ...)" clause already pushed.
    -- Both of these below are equivalent.
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, t2, t3);
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, TABLE
    t2, TABLE t3);
    
    --  Below is an example of multiple schemas and multiple except clauses:
    -- publish all tables of schema s1 except s1.t1 and s1.t2
    -- publish all tables of schema s2
    -- publish all tables of schema s3 except table s3.t2 (how is this
    ambiguous with the excluded s1.t2?)
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, t2),
    s2, s3 EXCEPT (TABLE t2);
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  3. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Amit Kapila <amit.kapila16@gmail.com> — 2026-04-14T07:51:55Z

    On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    >
    > -- Syntax can be *identical* to the "EXCEPT (TABLE ...)" clause already pushed.
    > -- Both of these below are equivalent.
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, t2, t3);
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, TABLE
    > t2, TABLE t3);
    >
    > --  Below is an example of multiple schemas and multiple except clauses:
    > -- publish all tables of schema s1 except s1.t1 and s1.t2
    > -- publish all tables of schema s2
    > -- publish all tables of schema s3 except table s3.t2 (how is this
    > ambiguous with the excluded s1.t2?)
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, t2),
    > s2, s3 EXCEPT (TABLE t2);
    >
    
    We can go in the direction as proposed by you but my preference would
    be to avoid using EXCEPT keyword multiple times.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  4. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-04-14T08:09:42Z

    On Tue, Apr 14, 2026 at 5:52 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > >
    > > -- Syntax can be *identical* to the "EXCEPT (TABLE ...)" clause already pushed.
    > > -- Both of these below are equivalent.
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, t2, t3);
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, TABLE
    > > t2, TABLE t3);
    > >
    > > --  Below is an example of multiple schemas and multiple except clauses:
    > > -- publish all tables of schema s1 except s1.t1 and s1.t2
    > > -- publish all tables of schema s2
    > > -- publish all tables of schema s3 except table s3.t2 (how is this
    > > ambiguous with the excluded s1.t2?)
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, t2),
    > > s2, s3 EXCEPT (TABLE t2);
    > >
    >
    > We can go in the direction as proposed by you but my preference would
    > be to avoid using EXCEPT keyword multiple times.
    >
    > --
    
    Using the "EXCEPT keyword multiple times" wasn't anything different
    proposed by me. That was already part of the original post.
    
    e.g. in the 3rd example.
    
    ------
    Supported:
      CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), TABLE s2.t1;
      CREATE PUBLICATION pub FOR TABLE s2.t1, TABLES IN SCHEMA s1 EXCEPT (s1.t1);
      CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), s2
    EXCEPT (s2.t1);
    ------
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  5. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-04-14T08:34:55Z

    On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > Hi hackers,
    > >
    > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > publications, starting this thread to extend the same capability to
    > > schema-level publications (TABLES IN SCHEMA).
    >
    > Hi Nisha.
    >
    > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    >
    > >
    > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > way to exclude a subset. Users who want to skip a few tables must
    > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > schema-level publishing and requires ongoing maintenance as tables are
    > > added.
    > >
    > > Proposed syntax:
    > > ------------------------
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > >
    > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > ambiguity and must belong to the published schema; otherwise, an error
    > > is raised.
    > >
    >
    > The proposed syntax is almost, but not quite, what I was anticipating.
    > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > It can be *identical* to it. e.g., your examples are missing the
    > 'TABLE' keyword necessary to achieve the same command flexibility.
    > Furthermore, what is the ambiguity referred to? An excluded table is
    > clearly associated with the preceding schema. Can't the code infer the
    > schema internally even when it is not provided by the user? Of course,
    > the user *can* specify a schema-qualified name if they want to, but I
    > didn't see why we are forcing that rule upon them.
    
    +1. I also feel specifying only the table name is clear enough. Or are
    we referring to implementation complexity here?
    
    > e.g.
    >
    > -- Syntax can be *identical* to the "EXCEPT (TABLE ...)" clause already pushed.
    > -- Both of these below are equivalent.
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, t2, t3);
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, TABLE
    > t2, TABLE t3);
    >
    > --  Below is an example of multiple schemas and multiple except clauses:
    > -- publish all tables of schema s1 except s1.t1 and s1.t2
    > -- publish all tables of schema s2
    > -- publish all tables of schema s3 except table s3.t2 (how is this
    > ambiguous with the excluded s1.t2?)
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, t2),
    > s2, s3 EXCEPT (TABLE t2);
    >
    > ======
    > Kind Regards,
    > Peter Smith.
    > Fujitsu Australia.
    >
    >
    
    
    
    
  6. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Amit Kapila <amit.kapila16@gmail.com> — 2026-04-14T09:36:50Z

    On Tue, Apr 14, 2026 at 2:05 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > Hi hackers,
    > > >
    > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > publications, starting this thread to extend the same capability to
    > > > schema-level publications (TABLES IN SCHEMA).
    > >
    > > Hi Nisha.
    > >
    > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > >
    > > >
    > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > way to exclude a subset. Users who want to skip a few tables must
    > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > added.
    > > >
    > > > Proposed syntax:
    > > > ------------------------
    > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > >
    > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > is raised.
    > > >
    > >
    > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > It can be *identical* to it. e.g., your examples are missing the
    > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > Furthermore, what is the ambiguity referred to? An excluded table is
    > > clearly associated with the preceding schema. Can't the code infer the
    > > schema internally even when it is not provided by the user? Of course,
    > > the user *can* specify a schema-qualified name if they want to, but I
    > > didn't see why we are forcing that rule upon them.
    >
    > +1. I also feel specifying only the table name is clear enough. Or are
    > we referring to implementation complexity here?
    >
    
    I think it will add complexity. Consider an example:
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2, s3 EXCEPT (t1, t2);
    
    So, which schema's exclusion list will these tables should be
    considered for? Say, if table with name t1 is present in all schemas
    then shall we exclude from all schemas or just consider it excluded
    from the first one (s1)?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  7. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-14T09:45:18Z

    On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > Hi hackers,
    > >
    > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > publications, starting this thread to extend the same capability to
    > > schema-level publications (TABLES IN SCHEMA).
    >
    > Hi Nisha.
    >
    > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    >
    > >
    > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > way to exclude a subset. Users who want to skip a few tables must
    > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > schema-level publishing and requires ongoing maintenance as tables are
    > > added.
    > >
    > > Proposed syntax:
    > > ------------------------
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > >
    > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > ambiguity and must belong to the published schema; otherwise, an error
    > > is raised.
    > >
    >
    > The proposed syntax is almost, but not quite, what I was anticipating.
    > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > It can be *identical* to it. e.g., your examples are missing the
    > 'TABLE' keyword necessary to achieve the same command flexibility.
    >
    
    I intentionally didn’t use the TABLE keyword inside EXCEPT.
    IIUC, for FOR ALL TABLES EXCEPT, there may be a future need to
    distinguish object types (e.g., tables vs schemas), which is why
    specifying TABLE makes sense there.
    
    However, for TABLES IN SCHEMA, only tables can be specified, so
    omitting TABLE keeps the syntax simpler and more intuitive.
    
    Let's hear others’ opinions too on this and I'll adjust if there’s a
    preference for including it.
    
    --
    Thanks,
    Nisha
    
    
    
    
  8. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-04-14T09:52:42Z

    On Tue, Apr 14, 2026 at 3:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > Hi hackers,
    > > >
    > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > publications, starting this thread to extend the same capability to
    > > > schema-level publications (TABLES IN SCHEMA).
    > >
    > > Hi Nisha.
    > >
    > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > >
    > > >
    > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > way to exclude a subset. Users who want to skip a few tables must
    > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > added.
    > > >
    > > > Proposed syntax:
    > > > ------------------------
    > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > >
    > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > is raised.
    > > >
    > >
    > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > It can be *identical* to it. e.g., your examples are missing the
    > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > >
    >
    > I intentionally didn’t use the TABLE keyword inside EXCEPT.
    > IIUC, for FOR ALL TABLES EXCEPT, there may be a future need to
    > distinguish object types (e.g., tables vs schemas), which is why
    > specifying TABLE makes sense there.
    >
    > However, for TABLES IN SCHEMA, only tables can be specified, so
    > omitting TABLE keeps the syntax simpler and more intuitive.
    >
    > Let's hear others’ opinions too on this and I'll adjust if there’s a
    > preference for including it.
    >
    
    Nisha, please see pt 1 in [1]. If we plan to support that, then we
    need TABLE keyword, otherwise we are fine.
    
    [1]: https://www.postgresql.org/message-id/CAJpy0uB%3DJxTYXOB7VmrhVLR%2B1PG0%3DTtHuGekaqibOPpo2UBLiQ%40mail.gmail.com
    
    thanks
    Shveta
    
    
    
    
  9. Re: Support EXCEPT for TABLES IN SCHEMA publications

    vignesh C <vignesh21@gmail.com> — 2026-04-14T11:31:07Z

    On Tue, 14 Apr 2026 at 12:00, Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > Hi hackers,
    >
    > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > publications, starting this thread to extend the same capability to
    > schema-level publications (TABLES IN SCHEMA).
    >
    > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > way to exclude a subset. Users who want to skip a few tables must
    > switch to an explicit FOR TABLE list, which loses the convenience of
    > schema-level publishing and requires ongoing maintenance as tables are
    > added.
    >
    > Proposed syntax:
    > ------------------------
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    >
    > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > ambiguity and must belong to the published schema; otherwise, an error
    > is raised.
    >
    > Rules and behavior:
    > ----------------------------
    > 1) TABLES IN SCHEMA can be combined with FOR TABLE, but EXCEPT applies
    > only to the schema clause and must appear immediately after it.
    >
    > Supported:
    >   CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), TABLE s2.t1;
    >   CREATE PUBLICATION pub FOR TABLE s2.t1, TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    >   CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), s2
    > EXCEPT (s2.t1)
    >
    > Not supported:
    >   CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1,  TABLE s2.t1 EXCEPT (s1.t1);
    >
    > -- This same rule applies to ALTER PUBLICATION ... ADD/SET.
    >
    > 2) Conflicting definitions
    > Specifying the same table both in the EXCEPT clause and explicitly in
    > the TABLE clause results in an error, as this creates a conflicting
    > definition for the publication.
    >
    > 3) "ALTER PUBLICATION ... DROP TABLES IN SCHEMA" does not support
    > EXCEPT clause. Whereas, dropping a schema also removes any associated
    > entries from the EXCEPT list of the publication.
    >  -- To only remove/update except list entries, use SET instead.
    >
    > 4) Consistency with ALL TABLES EXCEPT rules:
    >   4a) Excluding a partitioned root excludes all its partitions
    >   4b) Individual partitions cannot be excluded directly; exclude the root table.
    >   4c) Excluding an inheritance parent (without ONLY) also excludes its children.
    >
    > The patches are divided into three parts to simplify review:
    >   Patch-001: Basic framework to support EXCEPT in CREATE PUBLICATION
    > ... TABLES IN SCHEMA
    >   Patch-002: Extend support to ALTER PUBLICATION ... ADD TABLES IN SCHEMA
    >   Patch-003: Extend support to ALTER PUBLICATION ... SET TABLES IN SCHEMA
    >
    > The patches are attached, feedback and suggestions are welcome.
    
    +1 for this.
    Few comments for the first patch:
    1) This should be collected only if except relation was specified, we
    can skip it if it is not specified:
    +                       /*
    +                        * Collect explicit table OIDs now, before we
    close the relation
    +                        * list, so that except-table validation below
    can check for
    +                        * contradictions without relying on a catalog
    scan that might not
    +                        * yet see the just-inserted rows.
    +                        */
    +                       foreach(lc, rels)
    +                       {
    +                               PublicationRelInfo *pri =
    (PublicationRelInfo *) lfirst(lc);
    +
    +                               explicitrelids = lappend_oid(explicitrelids,
    +
                      RelationGetRelid(pri->relation));
    +                       }
    
    2) Tab completion for except table of that particular schema lists
    other schema, but currently specifying other schema tables is not
    allowed:
    +       else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR",
    "TABLES", "IN", "SCHEMA", MatchAny, "EXCEPT", "("))
    +               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
    
    Can we list only the tables from the specified schema.
    
    3) Can this check be done at parser itself, it can be done in
    preprocess_pubobj_list parser function to detect the error early:
    +                               /*
    +                                * For TABLES IN SCHEMA publications,
    require schema-qualified
    +                                * names to avoid ambiguity when
    multiple schemas in the
    +                                * publication have identically-named tables.
    +                                */
    +                               foreach(lc, exceptrelations)
    +                               {
    +                                       PublicationTable *t =
    (PublicationTable *) lfirst(lc);
    +
    +                                       if (t->relation->schemaname == NULL)
    +                                               ereport(ERROR,
    +
    errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    +
    errmsg("table \"%s\" in EXCEPT clause must be schema-qualified",
    +
        t->relation->relname));
    +                               }
    
    4) This error message does not seems to be conveying the correct error message:
    postgres=# create publication pub1 for tables in schema sch3 except (
    sch3.t1 ) ;
    CREATE PUBLICATION
    postgres=# alter publication pub1 add table sch3.t1 ;
    ERROR:  relation "t1" is already member of publication "pub1"
    
    How about an error message like:
    ERROR: table "sch3.t1" cannot be added explicitly because it is listed
    in the EXCEPT clause of schema "sch3" in publication "pub1"
    
    5) This change is not related to this patch:
    @@ -5279,7 +5315,7 @@ foreach my $run (sort keys %pgdump_runs)
                    #
                    # Either "all_runs" should be set or there should be a
    "like" list,
                    # even if it is empty.  (This makes the test more
    self-documenting.)
    -               if (!defined($tests{$test}->{all_runs})
    +               if (   !defined($tests{$test}->{all_runs})
                            && !defined($tests{$test}->{like}))
                    {
                            die "missing \"like\" in test \"$test\"";
    
    6) There is an indentation issue here:
    -                               pub_except_obj_list opt_pub_except_clause
    +                               pub_except_obj_list pub_schema_except_obj_list
    +                       opt_pub_except_clause opt_pub_schema_except_clause
    
    Regards,
    Vignesh
    
    
    
    
  10. Re: Support EXCEPT for TABLES IN SCHEMA publications

    vignesh C <vignesh21@gmail.com> — 2026-04-14T15:16:35Z

    On Tue, 14 Apr 2026 at 12:00, Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > Hi hackers,
    >
    > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > publications, starting this thread to extend the same capability to
    > schema-level publications (TABLES IN SCHEMA).
    >
    > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > way to exclude a subset. Users who want to skip a few tables must
    > switch to an explicit FOR TABLE list, which loses the convenience of
    > schema-level publishing and requires ongoing maintenance as tables are
    > added.
    >
    > Proposed syntax:
    > ------------------------
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    >
    > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > ambiguity and must belong to the published schema; otherwise, an error
    > is raised.
    >
    > Rules and behavior:
    > ----------------------------
    > 1) TABLES IN SCHEMA can be combined with FOR TABLE, but EXCEPT applies
    > only to the schema clause and must appear immediately after it.
    >
    > Supported:
    >   CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), TABLE s2.t1;
    >   CREATE PUBLICATION pub FOR TABLE s2.t1, TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    >   CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), s2
    > EXCEPT (s2.t1)
    >
    > Not supported:
    >   CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1,  TABLE s2.t1 EXCEPT (s1.t1);
    >
    > -- This same rule applies to ALTER PUBLICATION ... ADD/SET.
    >
    > 2) Conflicting definitions
    > Specifying the same table both in the EXCEPT clause and explicitly in
    > the TABLE clause results in an error, as this creates a conflicting
    > definition for the publication.
    >
    > 3) "ALTER PUBLICATION ... DROP TABLES IN SCHEMA" does not support
    > EXCEPT clause. Whereas, dropping a schema also removes any associated
    > entries from the EXCEPT list of the publication.
    >  -- To only remove/update except list entries, use SET instead.
    >
    > 4) Consistency with ALL TABLES EXCEPT rules:
    >   4a) Excluding a partitioned root excludes all its partitions
    >   4b) Individual partitions cannot be excluded directly; exclude the root table.
    >   4c) Excluding an inheritance parent (without ONLY) also excludes its children.
    >
    > The patches are divided into three parts to simplify review:
    >   Patch-001: Basic framework to support EXCEPT in CREATE PUBLICATION
    > ... TABLES IN SCHEMA
    >   Patch-002: Extend support to ALTER PUBLICATION ... ADD TABLES IN SCHEMA
    >   Patch-003: Extend support to ALTER PUBLICATION ... SET TABLES IN SCHEMA
    >
    > The patches are attached, feedback and suggestions are welcome.
    
    When an EXCEPT table is specified together with TABLES IN SCHEMA sch1,
    the EXCEPT entry is correctly created:
    postgres=# create publication pub1 for tables in schema sch1 except (sch1.t1);
    CREATE PUBLICATION
    
    postgres=# \dRp+ pub1
                                                          Publication pub1
      Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Description
    ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
     vignesh | f          | f             | t       | t       | t       |
    t         | none              | f        |
    Tables from schemas:
        "sch1"
    Except tables:
        "sch1.t1"
    However, after dropping the schema from the publication, the
    previously recorded EXCEPT table entry is still retained:
    postgres=# alter publication pub1 drop TABLES IN SCHEMA sch1 ;
    ALTER PUBLICATION
    postgres=# \dRp+ pub1
                                                          Publication pub1
      Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Description
    ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
     vignesh | f          | f             | t       | t       | t       |
    t         | none              | f        |
    Except tables:
        "sch1.t1"
    
    This seems incorrect, because once sch1 is no longer part of the
    publication, retaining "sch1.t1" as an EXCEPT entry no longer has any
    semantic meaning and leaves behind stale catalog state.
    
    Regards,
    Vignesh
    
    
    
    
  11. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-04-15T02:05:31Z

    On Tue, Apr 14, 2026 at 7:37 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 2:05 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > >
    > > > > Hi hackers,
    > > > >
    > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > publications, starting this thread to extend the same capability to
    > > > > schema-level publications (TABLES IN SCHEMA).
    > > >
    > > > Hi Nisha.
    > > >
    > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > >
    > > > >
    > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > added.
    > > > >
    > > > > Proposed syntax:
    > > > > ------------------------
    > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > >
    > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > is raised.
    > > > >
    > > >
    > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > It can be *identical* to it. e.g., your examples are missing the
    > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > > Furthermore, what is the ambiguity referred to? An excluded table is
    > > > clearly associated with the preceding schema. Can't the code infer the
    > > > schema internally even when it is not provided by the user? Of course,
    > > > the user *can* specify a schema-qualified name if they want to, but I
    > > > didn't see why we are forcing that rule upon them.
    > >
    > > +1. I also feel specifying only the table name is clear enough. Or are
    > > we referring to implementation complexity here?
    > >
    >
    > I think it will add complexity. Consider an example:
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2, s3 EXCEPT (t1, t2);
    >
    > So, which schema's exclusion list will these tables should be
    > considered for? Say, if table with name t1 is present in all schemas
    > then shall we exclude from all schemas or just consider it excluded
    > from the first one (s1)?
    >
    
    The exact pattern is already in common usage for row-filters and
    column-lists, yet nobody is confused.
    
    -- all these tables have the same column names
    -- (analogous to multiple schemas having same table names)
    CREATE TABLE t1(c1 int, c2 int);
    CREATE TABLE t2(c1 int, c2 int);
    CREATE TABLE t3(c1 int, c2 int);
    
    -- all tables have a column c1
    -- but this c1 means t3.c1 because the column-list is only for the adjacent t3.
    CREATE PUBLICATION pub1 FOR TABLE t1, t2, t3 (c1);
    
    -- all tables have a column c2
    -- but this c2 means t3.c2 because the row-filter is only for the adjacent t3.
    CREATE PUBLICATION pub2 FOR TABLE t1, t2, t3 WHERE (c2 > 99);
    
    \dRp+
                                                           Publication pub1
      Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Description
    ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
     postgres | f          | f             | t       | t       | t       |
    t         | none              | f        |
    Tables:
        "public.t1"
        "public.t2"
        "public.t3" (c1)
    
                                                           Publication pub2
      Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Description
    ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
     postgres | f          | f             | t       | t       | t       |
    t         | none              | f        |
    Tables:
        "public.t1"
        "public.t2"
        "public.t3" WHERE (c2 > 99)
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  12. Re: Support EXCEPT for TABLES IN SCHEMA publications

    vignesh C <vignesh21@gmail.com> — 2026-04-15T05:48:07Z

    On Tue, 14 Apr 2026 at 12:00, Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > Hi hackers,
    >
    > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > publications, starting this thread to extend the same capability to
    > schema-level publications (TABLES IN SCHEMA).
    >
    > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > way to exclude a subset. Users who want to skip a few tables must
    > switch to an explicit FOR TABLE list, which loses the convenience of
    > schema-level publishing and requires ongoing maintenance as tables are
    > added.
    >
    > Proposed syntax:
    > ------------------------
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    >
    > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > ambiguity and must belong to the published schema; otherwise, an error
    > is raised.
    >
    > Rules and behavior:
    > ----------------------------
    > 1) TABLES IN SCHEMA can be combined with FOR TABLE, but EXCEPT applies
    > only to the schema clause and must appear immediately after it.
    >
    > Supported:
    >   CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), TABLE s2.t1;
    >   CREATE PUBLICATION pub FOR TABLE s2.t1, TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    >   CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1), s2
    > EXCEPT (s2.t1)
    >
    > Not supported:
    >   CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1,  TABLE s2.t1 EXCEPT (s1.t1);
    >
    > -- This same rule applies to ALTER PUBLICATION ... ADD/SET.
    >
    > 2) Conflicting definitions
    > Specifying the same table both in the EXCEPT clause and explicitly in
    > the TABLE clause results in an error, as this creates a conflicting
    > definition for the publication.
    >
    > 3) "ALTER PUBLICATION ... DROP TABLES IN SCHEMA" does not support
    > EXCEPT clause. Whereas, dropping a schema also removes any associated
    > entries from the EXCEPT list of the publication.
    >  -- To only remove/update except list entries, use SET instead.
    >
    > 4) Consistency with ALL TABLES EXCEPT rules:
    >   4a) Excluding a partitioned root excludes all its partitions
    >   4b) Individual partitions cannot be excluded directly; exclude the root table.
    >   4c) Excluding an inheritance parent (without ONLY) also excludes its children.
    >
    > The patches are divided into three parts to simplify review:
    >   Patch-001: Basic framework to support EXCEPT in CREATE PUBLICATION
    > ... TABLES IN SCHEMA
    >   Patch-002: Extend support to ALTER PUBLICATION ... ADD TABLES IN SCHEMA
    >   Patch-003: Extend support to ALTER PUBLICATION ... SET TABLES IN SCHEMA
    >
    > The patches are attached, feedback and suggestions are welcome.
    
    Few comments for the second patch:
    1) This patch adds support only for:
      ALTER PUBLICATION pub ADD TABLES IN SCHEMA s EXCEPT (s.t1, s.t2);
    
    But documentation mentions for both add and set:
    @@ -31,7 +31,7 @@ ALTER PUBLICATION <replaceable
    class="parameter">name</replaceable> RENAME TO <r
     <phrase>where <replaceable
    class="parameter">publication_object</replaceable> is one of:</phrase>
    
         TABLE <replaceable
    class="parameter">table_and_columns</replaceable> [, ... ]
    -    TABLES IN SCHEMA { <replaceable
    class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ...
    ]
    +    TABLES IN SCHEMA { <replaceable
    class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [
    EXCEPT ( <replaceable
    class="parameter">except_table_object</replaceable> [, ... ] ) ] [,
    ... ]
    
    2) Tab completion missing for:
    alter publication pub1 add TABLES IN SCHEMA sch1
    
    3) Currently Set tables in schema sch1 also works partially with the
    second patch without adding the except tables:
    postgres=# alter publication pub1 set tables in schema sch1 except (sch1.t1);
    ALTER PUBLICATION
    postgres=# \dRp+ pub1
                                                          Publication pub1
      Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Description
    ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
     vignesh | f          | f             | t       | t       | t       |
    t         | none              | f        |
    Tables from schemas:
        "sch1"
    
    Should there be a check here to throw an error:
    +static void
    +AlterPublicationExceptTables(AlterPublicationStmt *stmt,
    +                                                        HeapTuple
    tup, List *exceptrelations,
    +                                                        List *schemaidlist)
    +{
    +       Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
    +       Oid                     pubid = pubform->oid;
    +
    +       /*
    +        * Nothing to do if no EXCEPT entries.
    +        */
    +       if (!exceptrelations)
    +               return;
    +
    
    4) Can this check be done at parser itself, it can be done in
    preprocess_pubobj_list parser function to detect the error early:
    +               foreach(lc, exceptrelations)
    +               {
    +                       PublicationTable *t = (PublicationTable *) lfirst(lc);
    +
    +                       if (t->relation->schemaname == NULL)
    +                               ereport(ERROR,
    +
    errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    +                                               errmsg("table \"%s\"
    in EXCEPT clause must be schema-qualified",
    +
    t->relation->relname));
    +               }
    
    Regards,
    Vignesh
    
    
    
    
  13. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-15T08:52:04Z

    On Tue, Apr 14, 2026 at 3:22 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 3:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > >
    > > > > Hi hackers,
    > > > >
    > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > publications, starting this thread to extend the same capability to
    > > > > schema-level publications (TABLES IN SCHEMA).
    > > >
    > > > Hi Nisha.
    > > >
    > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > >
    > > > >
    > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > added.
    > > > >
    > > > > Proposed syntax:
    > > > > ------------------------
    > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > >
    > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > is raised.
    > > > >
    > > >
    > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > It can be *identical* to it. e.g., your examples are missing the
    > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > >
    > >
    > > I intentionally didn’t use the TABLE keyword inside EXCEPT.
    > > IIUC, for FOR ALL TABLES EXCEPT, there may be a future need to
    > > distinguish object types (e.g., tables vs schemas), which is why
    > > specifying TABLE makes sense there.
    > >
    > > However, for TABLES IN SCHEMA, only tables can be specified, so
    > > omitting TABLE keeps the syntax simpler and more intuitive.
    > >
    > > Let's hear others’ opinions too on this and I'll adjust if there’s a
    > > preference for including it.
    > >
    >
    > Nisha, please see pt 1 in [1]. If we plan to support that, then we
    > need TABLE keyword, otherwise we are fine.
    >
    > [1]: https://www.postgresql.org/message-id/CAJpy0uB%3DJxTYXOB7VmrhVLR%2B1PG0%3DTtHuGekaqibOPpo2UBLiQ%40mail.gmail.com
    >
    
    Thank you Shveta for pointing to the discussion.
    So, if we were to extend CREATE PUBLICATION to support something like:
     "CREATE PUBLICATION pub FOR TABLES, SEQUENCES IN SCHEMA sch1 EXCEPT
    (TABLE t1,  SEQUENCE s1);"
    then, introducing TABLE inside EXCEPT could make sense, but at this
    stage we’re not sure how the syntax for supporting SEQUENCES IN SCHEMA
    will evolve. The overall grammar may require broader changes rather
    than a simple extension. So, we can always introduce keywords like
    TABLE or SEQUENCE later, if and when they are actually needed. Just my
    thoughts.
    
    --
    Thanks,
    Nisha
    
    
    
    
  14. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-15T08:52:36Z

    On Tue, Apr 14, 2026 at 8:46 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > When an EXCEPT table is specified together with TABLES IN SCHEMA sch1,
    > the EXCEPT entry is correctly created:
    > postgres=# create publication pub1 for tables in schema sch1 except (sch1.t1);
    > CREATE PUBLICATION
    >
    > postgres=# \dRp+ pub1
    >                                                       Publication pub1
    >   Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root | Description
    > ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    >  vignesh | f          | f             | t       | t       | t       |
    > t         | none              | f        |
    > Tables from schemas:
    >     "sch1"
    > Except tables:
    >     "sch1.t1"
    > However, after dropping the schema from the publication, the
    > previously recorded EXCEPT table entry is still retained:
    > postgres=# alter publication pub1 drop TABLES IN SCHEMA sch1 ;
    > ALTER PUBLICATION
    > postgres=# \dRp+ pub1
    >                                                       Publication pub1
    >   Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root | Description
    > ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    >  vignesh | f          | f             | t       | t       | t       |
    > t         | none              | f        |
    > Except tables:
    >     "sch1.t1"
    >
    > This seems incorrect, because once sch1 is no longer part of the
    > publication, retaining "sch1.t1" as an EXCEPT entry no longer has any
    > semantic meaning and leaves behind stale catalog state.
    >
    
    This is handled in v1-003 (last) patch along with other ALTER
    PUBLICATION related modifications.
    
    --
    Thanks,
    Nisha
    
    
    
    
  15. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-04-15T10:15:43Z

    On Wed, Apr 15, 2026 at 2:22 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 3:22 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Tue, Apr 14, 2026 at 3:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > >
    > > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > >
    > > > > > Hi hackers,
    > > > > >
    > > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > > publications, starting this thread to extend the same capability to
    > > > > > schema-level publications (TABLES IN SCHEMA).
    > > > >
    > > > > Hi Nisha.
    > > > >
    > > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > > >
    > > > > >
    > > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > > added.
    > > > > >
    > > > > > Proposed syntax:
    > > > > > ------------------------
    > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > >
    > > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > > is raised.
    > > > > >
    > > > >
    > > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > > It can be *identical* to it. e.g., your examples are missing the
    > > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > > >
    > > >
    > > > I intentionally didn’t use the TABLE keyword inside EXCEPT.
    > > > IIUC, for FOR ALL TABLES EXCEPT, there may be a future need to
    > > > distinguish object types (e.g., tables vs schemas), which is why
    > > > specifying TABLE makes sense there.
    > > >
    > > > However, for TABLES IN SCHEMA, only tables can be specified, so
    > > > omitting TABLE keeps the syntax simpler and more intuitive.
    > > >
    > > > Let's hear others’ opinions too on this and I'll adjust if there’s a
    > > > preference for including it.
    > > >
    > >
    > > Nisha, please see pt 1 in [1]. If we plan to support that, then we
    > > need TABLE keyword, otherwise we are fine.
    > >
    > > [1]: https://www.postgresql.org/message-id/CAJpy0uB%3DJxTYXOB7VmrhVLR%2B1PG0%3DTtHuGekaqibOPpo2UBLiQ%40mail.gmail.com
    > >
    >
    > Thank you Shveta for pointing to the discussion.
    > So, if we were to extend CREATE PUBLICATION to support something like:
    >  "CREATE PUBLICATION pub FOR TABLES, SEQUENCES IN SCHEMA sch1 EXCEPT
    > (TABLE t1,  SEQUENCE s1);"
    > then, introducing TABLE inside EXCEPT could make sense, but at this
    > stage we’re not sure how the syntax for supporting SEQUENCES IN SCHEMA
    > will evolve. The overall grammar may require broader changes rather
    > than a simple extension. So, we can always introduce keywords like
    > TABLE or SEQUENCE later, if and when they are actually needed. Just my
    > thoughts.
    >
    
    It works for me Nisha. I also agree that the 'TABLE' keyword seems
    redundant in the case you proposed:
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    
    But I am just worried this might confuse users, as one command
    requires specifying the TABLE with EXCEPT, while another requires
    omitting it. But we can first focus on implementation and come back to
    it later when more people join and share their thoughts.
    
    thanks
    Shveta
    
    
    
    
  16. Re: Support EXCEPT for TABLES IN SCHEMA publications

    vignesh C <vignesh21@gmail.com> — 2026-04-15T13:08:01Z

    On Wed, 15 Apr 2026 at 14:22, Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 8:46 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > When an EXCEPT table is specified together with TABLES IN SCHEMA sch1,
    > > the EXCEPT entry is correctly created:
    > > postgres=# create publication pub1 for tables in schema sch1 except (sch1.t1);
    > > CREATE PUBLICATION
    > >
    > > postgres=# \dRp+ pub1
    > >                                                       Publication pub1
    > >   Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > > Truncates | Generated columns | Via root | Description
    > > ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > >  vignesh | f          | f             | t       | t       | t       |
    > > t         | none              | f        |
    > > Tables from schemas:
    > >     "sch1"
    > > Except tables:
    > >     "sch1.t1"
    > > However, after dropping the schema from the publication, the
    > > previously recorded EXCEPT table entry is still retained:
    > > postgres=# alter publication pub1 drop TABLES IN SCHEMA sch1 ;
    > > ALTER PUBLICATION
    > > postgres=# \dRp+ pub1
    > >                                                       Publication pub1
    > >   Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > > Truncates | Generated columns | Via root | Description
    > > ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > >  vignesh | f          | f             | t       | t       | t       |
    > > t         | none              | f        |
    > > Except tables:
    > >     "sch1.t1"
    > >
    > > This seems incorrect, because once sch1 is no longer part of the
    > > publication, retaining "sch1.t1" as an EXCEPT entry no longer has any
    > > semantic meaning and leaves behind stale catalog state.
    > >
    >
    > This is handled in v1-003 (last) patch along with other ALTER
    > PUBLICATION related modifications.
    
    I was reviewing the patches individually and noticed this. If it is
    handled in the 003 ignore that comment.
    
    Regards,
    Vignesh
    
    
    
    
  17. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-15T14:07:25Z

    On Tue, Apr 14, 2026 at 5:01 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > +1 for this.
    > Few comments for the first patch:
    
    Thank you Vignesh for the review.
    
    > 1) This should be collected only if except relation was specified, we
    > can skip it if it is not specified:
    > +                       /*
    > +                        * Collect explicit table OIDs now, before we
    > close the relation
    > +                        * list, so that except-table validation below
    > can check for
    > +                        * contradictions without relying on a catalog
    > scan that might not
    > +                        * yet see the just-inserted rows.
    > +                        */
    > +                       foreach(lc, rels)
    > +                       {
    > +                               PublicationRelInfo *pri =
    > (PublicationRelInfo *) lfirst(lc);
    > +
    > +                               explicitrelids = lappend_oid(explicitrelids,
    > +
    >                   RelationGetRelid(pri->relation));
    > +                       }
    >
    
    Fixed
    
    > 2) Tab completion for except table of that particular schema lists
    > other schema, but currently specifying other schema tables is not
    > allowed:
    > +       else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR",
    > "TABLES", "IN", "SCHEMA", MatchAny, "EXCEPT", "("))
    > +               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
    >
    > Can we list only the tables from the specified schema.
    >
    
    Fixed. Required a new query to fetch schema tables.
    
    > 3) Can this check be done at parser itself, it can be done in
    > preprocess_pubobj_list parser function to detect the error early:
    > +                               /*
    > +                                * For TABLES IN SCHEMA publications,
    > require schema-qualified
    > +                                * names to avoid ambiguity when
    > multiple schemas in the
    > +                                * publication have identically-named tables.
    > +                                */
    > +                               foreach(lc, exceptrelations)
    > +                               {
    > +                                       PublicationTable *t =
    > (PublicationTable *) lfirst(lc);
    > +
    > +                                       if (t->relation->schemaname == NULL)
    > +                                               ereport(ERROR,
    > +
    > errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    > +
    > errmsg("table \"%s\" in EXCEPT clause must be schema-qualified",
    > +
    >     t->relation->relname));
    > +                               }
    >
    
    Fixed
    
    > 4) This error message does not seems to be conveying the correct error message:
    > postgres=# create publication pub1 for tables in schema sch3 except (
    > sch3.t1 ) ;
    > CREATE PUBLICATION
    > postgres=# alter publication pub1 add table sch3.t1 ;
    > ERROR:  relation "t1" is already member of publication "pub1"
    >
    > How about an error message like:
    > ERROR: table "sch3.t1" cannot be added explicitly because it is listed
    > in the EXCEPT clause of schema "sch3" in publication "pub1"
    >
    
    Fixed with a bit short error message -
    postgres=# alter publication pub8 add table s1.t1;
    ERROR:  table "s1.t1" cannot be added because it is listed in EXCEPT
    clause of publication "pub8"
    
    > 5) This change is not related to this patch:
    > @@ -5279,7 +5315,7 @@ foreach my $run (sort keys %pgdump_runs)
    >                 #
    >                 # Either "all_runs" should be set or there should be a
    > "like" list,
    >                 # even if it is empty.  (This makes the test more
    > self-documenting.)
    > -               if (!defined($tests{$test}->{all_runs})
    > +               if (   !defined($tests{$test}->{all_runs})
    >                         && !defined($tests{$test}->{like}))
    >                 {
    >                         die "missing \"like\" in test \"$test\"";
    >
    
    Fixed
    
    > 6) There is an indentation issue here:
    > -                               pub_except_obj_list opt_pub_except_clause
    > +                               pub_except_obj_list pub_schema_except_obj_list
    > +                       opt_pub_except_clause opt_pub_schema_except_clause
    >
    
    Fixed
    
    Please find the updated patches (v2) attached.
    Patch-001: addressed above comments
    Patch-002: addressed Vignesh's comments from [1]
    Patch-003: updated the documentation and also added tab-completion for
    the SET TABLES IN SCHEMA EXCEPT
    
    [1] https://www.postgresql.org/message-id/CALDaNm3pBWnJJ9ynVj3KeZ%2BNQQXboU-goObvF9fZ0GYzyuJFhQ%40mail.gmail.com
    
    --
    Thanks,
    Nisha
    
  18. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-15T14:17:30Z

    On Wed, Apr 15, 2026 at 11:18 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Tue, 14 Apr 2026 at 12:00, Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    >
    > Few comments for the second patch:
    
    Thanks for the review.
    
    > 1) This patch adds support only for:
    >   ALTER PUBLICATION pub ADD TABLES IN SCHEMA s EXCEPT (s.t1, s.t2);
    >
    > But documentation mentions for both add and set:
    > @@ -31,7 +31,7 @@ ALTER PUBLICATION <replaceable
    > class="parameter">name</replaceable> RENAME TO <r
    >  <phrase>where <replaceable
    > class="parameter">publication_object</replaceable> is one of:</phrase>
    >
    >      TABLE <replaceable
    > class="parameter">table_and_columns</replaceable> [, ... ]
    > -    TABLES IN SCHEMA { <replaceable
    > class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ...
    > ]
    > +    TABLES IN SCHEMA { <replaceable
    > class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [
    > EXCEPT ( <replaceable
    > class="parameter">except_table_object</replaceable> [, ... ] ) ] [,
    > ... ]
    >
    
    Fixed the doc descriptions where SET was mentioned in patch-002.
    But for the synopsis, I feel it will be unnecessary to first separate
    the ADD and SET publication objects like "publication_add_object" and
    "publication_set_object" in patch-002 and then remove them in
    patch-003. Please let me know if you think otherwise.
    
    > 2) Tab completion missing for:
    > alter publication pub1 add TABLES IN SCHEMA sch1
    >
    
    Done.
    
    > 3) Currently Set tables in schema sch1 also works partially with the
    > second patch without adding the except tables:
    > postgres=# alter publication pub1 set tables in schema sch1 except (sch1.t1);
    > ALTER PUBLICATION
    > postgres=# \dRp+ pub1
    >                                                       Publication pub1
    >   Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root | Description
    > ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    >  vignesh | f          | f             | t       | t       | t       |
    > t         | none              | f        |
    > Tables from schemas:
    >     "sch1"
    >
    > Should there be a check here to throw an error:
    > +static void
    > +AlterPublicationExceptTables(AlterPublicationStmt *stmt,
    > +                                                        HeapTuple
    > tup, List *exceptrelations,
    > +                                                        List *schemaidlist)
    > +{
    > +       Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
    > +       Oid                     pubid = pubform->oid;
    > +
    > +       /*
    > +        * Nothing to do if no EXCEPT entries.
    > +        */
    > +       if (!exceptrelations)
    > +               return;
    > +
    >
    
    Fixed
    
    > 4) Can this check be done at parser itself, it can be done in
    > preprocess_pubobj_list parser function to detect the error early:
    > +               foreach(lc, exceptrelations)
    > +               {
    > +                       PublicationTable *t = (PublicationTable *) lfirst(lc);
    > +
    > +                       if (t->relation->schemaname == NULL)
    > +                               ereport(ERROR,
    > +
    > errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    > +                                               errmsg("table \"%s\"
    > in EXCEPT clause must be schema-qualified",
    > +
    > t->relation->relname));
    > +               }
    >
    
    Fixed.
    
    All above comments are addressed in v2 attached above.
    
    [1]
    --
    Thanks,
    Nisha
    
    
    
    
  19. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-04-16T00:13:42Z

    On Wed, Apr 15, 2026 at 8:15 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Apr 15, 2026 at 2:22 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > On Tue, Apr 14, 2026 at 3:22 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Tue, Apr 14, 2026 at 3:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > >
    > > > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > > >
    > > > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > > >
    > > > > > > Hi hackers,
    > > > > > >
    > > > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > > > publications, starting this thread to extend the same capability to
    > > > > > > schema-level publications (TABLES IN SCHEMA).
    > > > > >
    > > > > > Hi Nisha.
    > > > > >
    > > > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > > > >
    > > > > > >
    > > > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > > > added.
    > > > > > >
    > > > > > > Proposed syntax:
    > > > > > > ------------------------
    > > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > >
    > > > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > > > is raised.
    > > > > > >
    > > > > >
    > > > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > > > It can be *identical* to it. e.g., your examples are missing the
    > > > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > > > >
    > > > >
    > > > > I intentionally didn’t use the TABLE keyword inside EXCEPT.
    > > > > IIUC, for FOR ALL TABLES EXCEPT, there may be a future need to
    > > > > distinguish object types (e.g., tables vs schemas), which is why
    > > > > specifying TABLE makes sense there.
    > > > >
    > > > > However, for TABLES IN SCHEMA, only tables can be specified, so
    > > > > omitting TABLE keeps the syntax simpler and more intuitive.
    > > > >
    > > > > Let's hear others’ opinions too on this and I'll adjust if there’s a
    > > > > preference for including it.
    > > > >
    > > >
    > > > Nisha, please see pt 1 in [1]. If we plan to support that, then we
    > > > need TABLE keyword, otherwise we are fine.
    > > >
    > > > [1]: https://www.postgresql.org/message-id/CAJpy0uB%3DJxTYXOB7VmrhVLR%2B1PG0%3DTtHuGekaqibOPpo2UBLiQ%40mail.gmail.com
    > > >
    > >
    > > Thank you Shveta for pointing to the discussion.
    > > So, if we were to extend CREATE PUBLICATION to support something like:
    > >  "CREATE PUBLICATION pub FOR TABLES, SEQUENCES IN SCHEMA sch1 EXCEPT
    > > (TABLE t1,  SEQUENCE s1);"
    > > then, introducing TABLE inside EXCEPT could make sense, but at this
    > > stage we’re not sure how the syntax for supporting SEQUENCES IN SCHEMA
    > > will evolve. The overall grammar may require broader changes rather
    > > than a simple extension. So, we can always introduce keywords like
    > > TABLE or SEQUENCE later, if and when they are actually needed. Just my
    > > thoughts.
    > >
    >
    > It works for me Nisha. I also agree that the 'TABLE' keyword seems
    > redundant in the case you proposed:
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    >
    > But I am just worried this might confuse users, as one command
    > requires specifying the TABLE with EXCEPT, while another requires
    > omitting it. But we can first focus on implementation and come back to
    > it later when more people join and share their thoughts.
    >
    
    I agree with Shveta that inconsistency will confuse.
    
    We already have FOR ALL TABLES EXCEPT (TABLE ...) [1]
    And another place is currently introducing FOR ALL SEQUENCES EXCEPT
    (SEQUENCE ...) [2]
    IMO, this schema thread should match [1].
    
    I think syntax *consistency* should take priority here. While some
    keywords may end up being unnecessary in certain cases, having
    different rules for each EXCEPT variation seems harder to maintain and
    reason about.
    
    I also agree with you that we are not sure how the command syntax
    might evolve in future. That is another reason why requiring these
    keywords (like 'TABLE' and 'SEQUENCE') is the best/safest first
    approach, IMO, because it will be backward-compatible. For example, if
    it turns out we can make (some of) these keywords optional in future,
    then user code will still be fine, but if they are absent now, then we
    cannot make them mandatory in future PostgreSQL versions without
    breaking user code.
    
    ======
    [1] https://www.postgresql.org/docs/devel/sql-createpublication.html
    [2] https://www.postgresql.org/message-id/CANhcyEVSXyQkvmrsOWPdQqnm2J3GMyQQrKhyCJiBQzqs6AvSow%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  20. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-16T10:30:03Z

    On Thu, Apr 16, 2026 at 5:44 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Wed, Apr 15, 2026 at 8:15 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Wed, Apr 15, 2026 at 2:22 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > On Tue, Apr 14, 2026 at 3:22 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > > >
    > > > > On Tue, Apr 14, 2026 at 3:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > >
    > > > > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > > > >
    > > > > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > > > >
    > > > > > > > Hi hackers,
    > > > > > > >
    > > > > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > > > > publications, starting this thread to extend the same capability to
    > > > > > > > schema-level publications (TABLES IN SCHEMA).
    > > > > > >
    > > > > > > Hi Nisha.
    > > > > > >
    > > > > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > > > > >
    > > > > > > >
    > > > > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > > > > added.
    > > > > > > >
    > > > > > > > Proposed syntax:
    > > > > > > > ------------------------
    > > > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > >
    > > > > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > > > > is raised.
    > > > > > > >
    > > > > > >
    > > > > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > > > > It can be *identical* to it. e.g., your examples are missing the
    > > > > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > > > > >
    > > > > >
    > > > > > I intentionally didn’t use the TABLE keyword inside EXCEPT.
    > > > > > IIUC, for FOR ALL TABLES EXCEPT, there may be a future need to
    > > > > > distinguish object types (e.g., tables vs schemas), which is why
    > > > > > specifying TABLE makes sense there.
    > > > > >
    > > > > > However, for TABLES IN SCHEMA, only tables can be specified, so
    > > > > > omitting TABLE keeps the syntax simpler and more intuitive.
    > > > > >
    > > > > > Let's hear others’ opinions too on this and I'll adjust if there’s a
    > > > > > preference for including it.
    > > > > >
    > > > >
    > > > > Nisha, please see pt 1 in [1]. If we plan to support that, then we
    > > > > need TABLE keyword, otherwise we are fine.
    > > > >
    > > > > [1]: https://www.postgresql.org/message-id/CAJpy0uB%3DJxTYXOB7VmrhVLR%2B1PG0%3DTtHuGekaqibOPpo2UBLiQ%40mail.gmail.com
    > > > >
    > > >
    > > > Thank you Shveta for pointing to the discussion.
    > > > So, if we were to extend CREATE PUBLICATION to support something like:
    > > >  "CREATE PUBLICATION pub FOR TABLES, SEQUENCES IN SCHEMA sch1 EXCEPT
    > > > (TABLE t1,  SEQUENCE s1);"
    > > > then, introducing TABLE inside EXCEPT could make sense, but at this
    > > > stage we’re not sure how the syntax for supporting SEQUENCES IN SCHEMA
    > > > will evolve. The overall grammar may require broader changes rather
    > > > than a simple extension. So, we can always introduce keywords like
    > > > TABLE or SEQUENCE later, if and when they are actually needed. Just my
    > > > thoughts.
    > > >
    > >
    > > It works for me Nisha. I also agree that the 'TABLE' keyword seems
    > > redundant in the case you proposed:
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > >
    > > But I am just worried this might confuse users, as one command
    > > requires specifying the TABLE with EXCEPT, while another requires
    > > omitting it. But we can first focus on implementation and come back to
    > > it later when more people join and share their thoughts.
    > >
    >
    > I agree with Shveta that inconsistency will confuse.
    >
    > We already have FOR ALL TABLES EXCEPT (TABLE ...) [1]
    > And another place is currently introducing FOR ALL SEQUENCES EXCEPT
    > (SEQUENCE ...) [2]
    > IMO, this schema thread should match [1].
    >
    > I think syntax *consistency* should take priority here. While some
    > keywords may end up being unnecessary in certain cases, having
    > different rules for each EXCEPT variation seems harder to maintain and
    > reason about.
    >
    > I also agree with you that we are not sure how the command syntax
    > might evolve in future. That is another reason why requiring these
    > keywords (like 'TABLE' and 'SEQUENCE') is the best/safest first
    > approach, IMO, because it will be backward-compatible. For example, if
    > it turns out we can make (some of) these keywords optional in future,
    > then user code will still be fine, but if they are absent now, then we
    > cannot make them mandatory in future PostgreSQL versions without
    > breaking user code.
    >
    
    Okay, to maintain consistency in the EXCEPT syntax across publication
    commands and keeping future compatibility in mind, I’m fine updating
    the syntax.
    I’ll share an updated patch soon.
    
    --
    Thanks,
    Nisha
    
    
    
    
  21. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-16T10:54:05Z

    On Wed, Apr 15, 2026 at 7:35 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Tue, Apr 14, 2026 at 7:37 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Tue, Apr 14, 2026 at 2:05 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > >
    > > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > >
    > > > > > Hi hackers,
    > > > > >
    > > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > > publications, starting this thread to extend the same capability to
    > > > > > schema-level publications (TABLES IN SCHEMA).
    > > > >
    > > > > Hi Nisha.
    > > > >
    > > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > > >
    > > > > >
    > > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > > added.
    > > > > >
    > > > > > Proposed syntax:
    > > > > > ------------------------
    > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > >
    > > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > > is raised.
    > > > > >
    > > > >
    > > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > > It can be *identical* to it. e.g., your examples are missing the
    > > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > > > Furthermore, what is the ambiguity referred to? An excluded table is
    > > > > clearly associated with the preceding schema. Can't the code infer the
    > > > > schema internally even when it is not provided by the user? Of course,
    > > > > the user *can* specify a schema-qualified name if they want to, but I
    > > > > didn't see why we are forcing that rule upon them.
    > > >
    > > > +1. I also feel specifying only the table name is clear enough. Or are
    > > > we referring to implementation complexity here?
    > > >
    > >
    > > I think it will add complexity. Consider an example:
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2, s3 EXCEPT (t1, t2);
    > >
    > > So, which schema's exclusion list will these tables should be
    > > considered for? Say, if table with name t1 is present in all schemas
    > > then shall we exclude from all schemas or just consider it excluded
    > > from the first one (s1)?
    > >
    >
    > The exact pattern is already in common usage for row-filters and
    > column-lists, yet nobody is confused.
    >
    > -- all these tables have the same column names
    > -- (analogous to multiple schemas having same table names)
    > CREATE TABLE t1(c1 int, c2 int);
    > CREATE TABLE t2(c1 int, c2 int);
    > CREATE TABLE t3(c1 int, c2 int);
    >
    > -- all tables have a column c1
    > -- but this c1 means t3.c1 because the column-list is only for the adjacent t3.
    > CREATE PUBLICATION pub1 FOR TABLE t1, t2, t3 (c1);
    >
    > -- all tables have a column c2
    > -- but this c2 means t3.c2 because the row-filter is only for the adjacent t3.
    > CREATE PUBLICATION pub2 FOR TABLE t1, t2, t3 WHERE (c2 > 99);
    >
    > \dRp+
    >                                                        Publication pub1
    >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root | Description
    > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    >  postgres | f          | f             | t       | t       | t       |
    > t         | none              | f        |
    > Tables:
    >     "public.t1"
    >     "public.t2"
    >     "public.t3" (c1)
    >
    >                                                        Publication pub2
    >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root | Description
    > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    >  postgres | f          | f             | t       | t       | t       |
    > t         | none              | f        |
    > Tables:
    >     "public.t1"
    >     "public.t2"
    >     "public.t3" WHERE (c2 > 99)
    >
    
    My intention was to avoid potential ambiguity when tables are not
    schema-qualified, as mentioned by Amit [1].
    
    That said, PostgreSQL generally does not enforce schema qualification
    and relies on search_path [2][3] for object resolution. Users are
    typically familiar with this behavior.
    
    Thanks for the example. I’ve updated the syntax to allow tables in the
    EXCEPT list without schema qualification.
    If a table is specified without a schema, it is resolved against the
    immediately preceding schema clause.
    
    Few examples:
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE t1);
    -- table t1 is resolved in schema s2
    
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1), s2
    EXCEPT (TABLE t2);
    -- if s1 does not contain t1, an error is raised: "s1.t1" does not exist
    
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE s1.t1, s2.t1);
    -- it will create the publication with except list - (s1.t1, s2.t1)
    
    Attached v3 patches with the following updates:
    1) Updated syntax to include TABLE keyword as per discussion [1]
        CREATE/ALTER PUBLICATION ... FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, ...);
    2) Removed the requirement for schema-qualified table names in the EXCEPT list.
    
    [1] https://www.postgresql.org/message-id/CAA4eK1KbCWBmEXH-rhQjKgNwq%3DonZp8vRR-QkRhPpbKwL-kQdw%40mail.gmail.com
    [2] https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH
    [3] https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-SEARCH-PATH
    
    --
    Thanks,
    Nisha
    
  22. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-04-24T04:46:51Z

    On Thu, Apr 16, 2026 at 4:24 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Wed, Apr 15, 2026 at 7:35 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > On Tue, Apr 14, 2026 at 7:37 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > > On Tue, Apr 14, 2026 at 2:05 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > > >
    > > > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > > >
    > > > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > > >
    > > > > > > Hi hackers,
    > > > > > >
    > > > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > > > publications, starting this thread to extend the same capability to
    > > > > > > schema-level publications (TABLES IN SCHEMA).
    > > > > >
    > > > > > Hi Nisha.
    > > > > >
    > > > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > > > >
    > > > > > >
    > > > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > > > added.
    > > > > > >
    > > > > > > Proposed syntax:
    > > > > > > ------------------------
    > > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > >
    > > > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > > > is raised.
    > > > > > >
    > > > > >
    > > > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > > > It can be *identical* to it. e.g., your examples are missing the
    > > > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > > > > Furthermore, what is the ambiguity referred to? An excluded table is
    > > > > > clearly associated with the preceding schema. Can't the code infer the
    > > > > > schema internally even when it is not provided by the user? Of course,
    > > > > > the user *can* specify a schema-qualified name if they want to, but I
    > > > > > didn't see why we are forcing that rule upon them.
    > > > >
    > > > > +1. I also feel specifying only the table name is clear enough. Or are
    > > > > we referring to implementation complexity here?
    > > > >
    > > >
    > > > I think it will add complexity. Consider an example:
    > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2, s3 EXCEPT (t1, t2);
    > > >
    > > > So, which schema's exclusion list will these tables should be
    > > > considered for? Say, if table with name t1 is present in all schemas
    > > > then shall we exclude from all schemas or just consider it excluded
    > > > from the first one (s1)?
    > > >
    > >
    > > The exact pattern is already in common usage for row-filters and
    > > column-lists, yet nobody is confused.
    > >
    > > -- all these tables have the same column names
    > > -- (analogous to multiple schemas having same table names)
    > > CREATE TABLE t1(c1 int, c2 int);
    > > CREATE TABLE t2(c1 int, c2 int);
    > > CREATE TABLE t3(c1 int, c2 int);
    > >
    > > -- all tables have a column c1
    > > -- but this c1 means t3.c1 because the column-list is only for the adjacent t3.
    > > CREATE PUBLICATION pub1 FOR TABLE t1, t2, t3 (c1);
    > >
    > > -- all tables have a column c2
    > > -- but this c2 means t3.c2 because the row-filter is only for the adjacent t3.
    > > CREATE PUBLICATION pub2 FOR TABLE t1, t2, t3 WHERE (c2 > 99);
    > >
    > > \dRp+
    > >                                                        Publication pub1
    > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > Truncates | Generated columns | Via root | Description
    > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > >  postgres | f          | f             | t       | t       | t       |
    > > t         | none              | f        |
    > > Tables:
    > >     "public.t1"
    > >     "public.t2"
    > >     "public.t3" (c1)
    > >
    > >                                                        Publication pub2
    > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > Truncates | Generated columns | Via root | Description
    > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > >  postgres | f          | f             | t       | t       | t       |
    > > t         | none              | f        |
    > > Tables:
    > >     "public.t1"
    > >     "public.t2"
    > >     "public.t3" WHERE (c2 > 99)
    > >
    >
    > My intention was to avoid potential ambiguity when tables are not
    > schema-qualified, as mentioned by Amit [1].
    >
    > That said, PostgreSQL generally does not enforce schema qualification
    > and relies on search_path [2][3] for object resolution. Users are
    > typically familiar with this behavior.
    >
    > Thanks for the example. I’ve updated the syntax to allow tables in the
    > EXCEPT list without schema qualification.
    > If a table is specified without a schema, it is resolved against the
    > immediately preceding schema clause.
    >
    > Few examples:
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE t1);
    > -- table t1 is resolved in schema s2
    
    Okay, looks good.
    
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1), s2
    > EXCEPT (TABLE t2);
    > -- if s1 does not contain t1, an error is raised: "s1.t1" does not exist
    
    Okay, looks good.
    
    >
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE s1.t1, s2.t1);
    > -- it will create the publication with except list - (s1.t1, s2.t1)
    
    Okay, I thought we will not be supporting EXCEPT at the end and mixed
    schemas inside. What will happen if I give:
    
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE t1, t2);
    
    and t1, t2 are present in both the schemas?
    
    I feel just like we associate column lists and row filters directly
    with each table, rather than using a mixed style at the end; we should
    also allow EXCEPT to be specified alongside each schema. This would
    avoid added complexity and reduce potential confusion. Thoughts?
    
    thanks
    Shveta
    
    
    
    
  23. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-04-24T05:59:46Z

    On Fri, Apr 24, 2026 at 10:16 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Thu, Apr 16, 2026 at 4:24 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > On Wed, Apr 15, 2026 at 7:35 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > > > On Tue, Apr 14, 2026 at 7:37 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > > >
    > > > > On Tue, Apr 14, 2026 at 2:05 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > > > >
    > > > > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > > > >
    > > > > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > > > >
    > > > > > > > Hi hackers,
    > > > > > > >
    > > > > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > > > > publications, starting this thread to extend the same capability to
    > > > > > > > schema-level publications (TABLES IN SCHEMA).
    > > > > > >
    > > > > > > Hi Nisha.
    > > > > > >
    > > > > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > > > > >
    > > > > > > >
    > > > > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > > > > added.
    > > > > > > >
    > > > > > > > Proposed syntax:
    > > > > > > > ------------------------
    > > > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > >
    > > > > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > > > > is raised.
    > > > > > > >
    > > > > > >
    > > > > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > > > > It can be *identical* to it. e.g., your examples are missing the
    > > > > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > > > > > Furthermore, what is the ambiguity referred to? An excluded table is
    > > > > > > clearly associated with the preceding schema. Can't the code infer the
    > > > > > > schema internally even when it is not provided by the user? Of course,
    > > > > > > the user *can* specify a schema-qualified name if they want to, but I
    > > > > > > didn't see why we are forcing that rule upon them.
    > > > > >
    > > > > > +1. I also feel specifying only the table name is clear enough. Or are
    > > > > > we referring to implementation complexity here?
    > > > > >
    > > > >
    > > > > I think it will add complexity. Consider an example:
    > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2, s3 EXCEPT (t1, t2);
    > > > >
    > > > > So, which schema's exclusion list will these tables should be
    > > > > considered for? Say, if table with name t1 is present in all schemas
    > > > > then shall we exclude from all schemas or just consider it excluded
    > > > > from the first one (s1)?
    > > > >
    > > >
    > > > The exact pattern is already in common usage for row-filters and
    > > > column-lists, yet nobody is confused.
    > > >
    > > > -- all these tables have the same column names
    > > > -- (analogous to multiple schemas having same table names)
    > > > CREATE TABLE t1(c1 int, c2 int);
    > > > CREATE TABLE t2(c1 int, c2 int);
    > > > CREATE TABLE t3(c1 int, c2 int);
    > > >
    > > > -- all tables have a column c1
    > > > -- but this c1 means t3.c1 because the column-list is only for the adjacent t3.
    > > > CREATE PUBLICATION pub1 FOR TABLE t1, t2, t3 (c1);
    > > >
    > > > -- all tables have a column c2
    > > > -- but this c2 means t3.c2 because the row-filter is only for the adjacent t3.
    > > > CREATE PUBLICATION pub2 FOR TABLE t1, t2, t3 WHERE (c2 > 99);
    > > >
    > > > \dRp+
    > > >                                                        Publication pub1
    > > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > > Truncates | Generated columns | Via root | Description
    > > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > > >  postgres | f          | f             | t       | t       | t       |
    > > > t         | none              | f        |
    > > > Tables:
    > > >     "public.t1"
    > > >     "public.t2"
    > > >     "public.t3" (c1)
    > > >
    > > >                                                        Publication pub2
    > > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > > Truncates | Generated columns | Via root | Description
    > > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > > >  postgres | f          | f             | t       | t       | t       |
    > > > t         | none              | f        |
    > > > Tables:
    > > >     "public.t1"
    > > >     "public.t2"
    > > >     "public.t3" WHERE (c2 > 99)
    > > >
    > >
    > > My intention was to avoid potential ambiguity when tables are not
    > > schema-qualified, as mentioned by Amit [1].
    > >
    > > That said, PostgreSQL generally does not enforce schema qualification
    > > and relies on search_path [2][3] for object resolution. Users are
    > > typically familiar with this behavior.
    > >
    > > Thanks for the example. I’ve updated the syntax to allow tables in the
    > > EXCEPT list without schema qualification.
    > > If a table is specified without a schema, it is resolved against the
    > > immediately preceding schema clause.
    > >
    > > Few examples:
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE t1);
    > > -- table t1 is resolved in schema s2
    >
    > Okay, looks good.
    >
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1), s2
    > > EXCEPT (TABLE t2);
    > > -- if s1 does not contain t1, an error is raised: "s1.t1" does not exist
    >
    > Okay, looks good.
    >
    > >
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE s1.t1, s2.t1);
    > > -- it will create the publication with except list - (s1.t1, s2.t1)
    >
    > Okay, I thought we will not be supporting EXCEPT at the end and mixed
    > schemas inside. What will happen if I give:
    >
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE t1, t2);
    >
    > and t1, t2 are present in both the schemas?
    >
    > I feel just like we associate column lists and row filters directly
    > with each table, rather than using a mixed style at the end; we should
    > also allow EXCEPT to be specified alongside each schema. This would
    > avoid added complexity and reduce potential confusion. Thoughts?
    
    One correction: I meant, we should "only"  allow EXCEPT to be
    specified alongside each schema.
    
    thanks
    Shveta
    
    
    
    
  24. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-27T03:32:40Z

    On Fri, Apr 24, 2026 at 11:29 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Fri, Apr 24, 2026 at 10:16 AM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Thu, Apr 16, 2026 at 4:24 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > On Wed, Apr 15, 2026 at 7:35 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > >
    > > > > On Tue, Apr 14, 2026 at 7:37 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > > > >
    > > > > > On Tue, Apr 14, 2026 at 2:05 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > > > > >
    > > > > > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > > > > >
    > > > > > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > > > > >
    > > > > > > > > Hi hackers,
    > > > > > > > >
    > > > > > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > > > > > publications, starting this thread to extend the same capability to
    > > > > > > > > schema-level publications (TABLES IN SCHEMA).
    > > > > > > >
    > > > > > > > Hi Nisha.
    > > > > > > >
    > > > > > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > > > > > >
    > > > > > > > >
    > > > > > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > > > > > added.
    > > > > > > > >
    > > > > > > > > Proposed syntax:
    > > > > > > > > ------------------------
    > > > > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > > >
    > > > > > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > > > > > is raised.
    > > > > > > > >
    > > > > > > >
    > > > > > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > > > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > > > > > It can be *identical* to it. e.g., your examples are missing the
    > > > > > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > > > > > > Furthermore, what is the ambiguity referred to? An excluded table is
    > > > > > > > clearly associated with the preceding schema. Can't the code infer the
    > > > > > > > schema internally even when it is not provided by the user? Of course,
    > > > > > > > the user *can* specify a schema-qualified name if they want to, but I
    > > > > > > > didn't see why we are forcing that rule upon them.
    > > > > > >
    > > > > > > +1. I also feel specifying only the table name is clear enough. Or are
    > > > > > > we referring to implementation complexity here?
    > > > > > >
    > > > > >
    > > > > > I think it will add complexity. Consider an example:
    > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2, s3 EXCEPT (t1, t2);
    > > > > >
    > > > > > So, which schema's exclusion list will these tables should be
    > > > > > considered for? Say, if table with name t1 is present in all schemas
    > > > > > then shall we exclude from all schemas or just consider it excluded
    > > > > > from the first one (s1)?
    > > > > >
    > > > >
    > > > > The exact pattern is already in common usage for row-filters and
    > > > > column-lists, yet nobody is confused.
    > > > >
    > > > > -- all these tables have the same column names
    > > > > -- (analogous to multiple schemas having same table names)
    > > > > CREATE TABLE t1(c1 int, c2 int);
    > > > > CREATE TABLE t2(c1 int, c2 int);
    > > > > CREATE TABLE t3(c1 int, c2 int);
    > > > >
    > > > > -- all tables have a column c1
    > > > > -- but this c1 means t3.c1 because the column-list is only for the adjacent t3.
    > > > > CREATE PUBLICATION pub1 FOR TABLE t1, t2, t3 (c1);
    > > > >
    > > > > -- all tables have a column c2
    > > > > -- but this c2 means t3.c2 because the row-filter is only for the adjacent t3.
    > > > > CREATE PUBLICATION pub2 FOR TABLE t1, t2, t3 WHERE (c2 > 99);
    > > > >
    > > > > \dRp+
    > > > >                                                        Publication pub1
    > > > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > > > Truncates | Generated columns | Via root | Description
    > > > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > > > >  postgres | f          | f             | t       | t       | t       |
    > > > > t         | none              | f        |
    > > > > Tables:
    > > > >     "public.t1"
    > > > >     "public.t2"
    > > > >     "public.t3" (c1)
    > > > >
    > > > >                                                        Publication pub2
    > > > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > > > Truncates | Generated columns | Via root | Description
    > > > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > > > >  postgres | f          | f             | t       | t       | t       |
    > > > > t         | none              | f        |
    > > > > Tables:
    > > > >     "public.t1"
    > > > >     "public.t2"
    > > > >     "public.t3" WHERE (c2 > 99)
    > > > >
    > > >
    > > > My intention was to avoid potential ambiguity when tables are not
    > > > schema-qualified, as mentioned by Amit [1].
    > > >
    > > > That said, PostgreSQL generally does not enforce schema qualification
    > > > and relies on search_path [2][3] for object resolution. Users are
    > > > typically familiar with this behavior.
    > > >
    > > > Thanks for the example. I’ve updated the syntax to allow tables in the
    > > > EXCEPT list without schema qualification.
    > > > If a table is specified without a schema, it is resolved against the
    > > > immediately preceding schema clause.
    > > >
    > > > Few examples:
    > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE t1);
    > > > -- table t1 is resolved in schema s2
    > >
    > > Okay, looks good.
    > >
    > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1), s2
    > > > EXCEPT (TABLE t2);
    > > > -- if s1 does not contain t1, an error is raised: "s1.t1" does not exist
    > >
    > > Okay, looks good.
    > >
    > > >
    > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE s1.t1, s2.t1);
    > > > -- it will create the publication with except list - (s1.t1, s2.t1)
    > >
    > > Okay, I thought we will not be supporting EXCEPT at the end and mixed
    > > schemas inside. What will happen if I give:
    > >
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE t1, t2);
    > >
    > > and t1, t2 are present in both the schemas?
    > >
    
    If no schema is specified, tables are resolved against the immediate
    schema. In this case, s2.t1 and s2.t2 will be used in the EXCEPT list,
    even if they exist in both s1 and s2.
    
    > > I feel just like we associate column lists and row filters directly
    > > with each table, rather than using a mixed style at the end; we should
    > > also allow EXCEPT to be specified alongside each schema. This would
    > > avoid added complexity and reduce potential confusion. Thoughts?
    >
    > One correction: I meant, we should "only"  allow EXCEPT to be
    > specified alongside each schema.
    >
    
    I kept this for user convenience to specify the EXCEPT list in one go.
    But as you mentioned, supporting mixed style adds complexity, as
    currently, it checks tables against all schemas in the publication
    (not just those in the command).
    
    In further testing, I also noticed below confusing behavior due to
    allowed mixed style -
    
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1;
    ALTER PUBLICATION pub ADD TABLES IN SCHEMA s2 EXCEPT (TABLE s1.t1);
     -- here, s1.t1 is allowed in EXCEPT because s1 is already part of the
    publication, even though it’s not in the current command.
    
    Let me make the changes to restrict the mixed style. Will share the
    updated patch soon.
    
    --
    Thanks,
    Nisha
    
    
    
    
  25. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-04-27T03:36:11Z

    On Mon, Apr 27, 2026 at 9:02 AM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Fri, Apr 24, 2026 at 11:29 AM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Fri, Apr 24, 2026 at 10:16 AM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Thu, Apr 16, 2026 at 4:24 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > >
    > > > > On Wed, Apr 15, 2026 at 7:35 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > > >
    > > > > > On Tue, Apr 14, 2026 at 7:37 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > > > > >
    > > > > > > On Tue, Apr 14, 2026 at 2:05 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > > > > > >
    > > > > > > > On Tue, Apr 14, 2026 at 1:03 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > > > > > >
    > > > > > > > > On Tue, Apr 14, 2026 at 4:30 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > > > > > >
    > > > > > > > > > Hi hackers,
    > > > > > > > > >
    > > > > > > > > > Following earlier work to support EXCEPT for FOR ALL TABLES [1]
    > > > > > > > > > publications, starting this thread to extend the same capability to
    > > > > > > > > > schema-level publications (TABLES IN SCHEMA).
    > > > > > > > >
    > > > > > > > > Hi Nisha.
    > > > > > > > >
    > > > > > > > > +1 for adding this new kind of exclusion clause to CREATE PUBLICATION command.
    > > > > > > > >
    > > > > > > > > >
    > > > > > > > > > Currently, TABLES IN SCHEMA publishes all tables in a schema with no
    > > > > > > > > > way to exclude a subset. Users who want to skip a few tables must
    > > > > > > > > > switch to an explicit FOR TABLE list, which loses the convenience of
    > > > > > > > > > schema-level publishing and requires ongoing maintenance as tables are
    > > > > > > > > > added.
    > > > > > > > > >
    > > > > > > > > > Proposed syntax:
    > > > > > > > > > ------------------------
    > > > > > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (s1.t1, s1.t2);
    > > > > > > > > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > > > > ALTER PUBLICATION pub SET TABLES IN SCHEMA s1 EXCEPT (s1.t1);
    > > > > > > > > >
    > > > > > > > > > Note: Tables in the EXCEPT clause must be schema-qualified to avoid
    > > > > > > > > > ambiguity and must belong to the published schema; otherwise, an error
    > > > > > > > > > is raised.
    > > > > > > > > >
    > > > > > > > >
    > > > > > > > > The proposed syntax is almost, but not quite, what I was anticipating.
    > > > > > > > > IMO the syntax shouldn't just be similar to the FOR ALL TABLES EXCEPT;
    > > > > > > > > It can be *identical* to it. e.g., your examples are missing the
    > > > > > > > > 'TABLE' keyword necessary to achieve the same command flexibility.
    > > > > > > > > Furthermore, what is the ambiguity referred to? An excluded table is
    > > > > > > > > clearly associated with the preceding schema. Can't the code infer the
    > > > > > > > > schema internally even when it is not provided by the user? Of course,
    > > > > > > > > the user *can* specify a schema-qualified name if they want to, but I
    > > > > > > > > didn't see why we are forcing that rule upon them.
    > > > > > > >
    > > > > > > > +1. I also feel specifying only the table name is clear enough. Or are
    > > > > > > > we referring to implementation complexity here?
    > > > > > > >
    > > > > > >
    > > > > > > I think it will add complexity. Consider an example:
    > > > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2, s3 EXCEPT (t1, t2);
    > > > > > >
    > > > > > > So, which schema's exclusion list will these tables should be
    > > > > > > considered for? Say, if table with name t1 is present in all schemas
    > > > > > > then shall we exclude from all schemas or just consider it excluded
    > > > > > > from the first one (s1)?
    > > > > > >
    > > > > >
    > > > > > The exact pattern is already in common usage for row-filters and
    > > > > > column-lists, yet nobody is confused.
    > > > > >
    > > > > > -- all these tables have the same column names
    > > > > > -- (analogous to multiple schemas having same table names)
    > > > > > CREATE TABLE t1(c1 int, c2 int);
    > > > > > CREATE TABLE t2(c1 int, c2 int);
    > > > > > CREATE TABLE t3(c1 int, c2 int);
    > > > > >
    > > > > > -- all tables have a column c1
    > > > > > -- but this c1 means t3.c1 because the column-list is only for the adjacent t3.
    > > > > > CREATE PUBLICATION pub1 FOR TABLE t1, t2, t3 (c1);
    > > > > >
    > > > > > -- all tables have a column c2
    > > > > > -- but this c2 means t3.c2 because the row-filter is only for the adjacent t3.
    > > > > > CREATE PUBLICATION pub2 FOR TABLE t1, t2, t3 WHERE (c2 > 99);
    > > > > >
    > > > > > \dRp+
    > > > > >                                                        Publication pub1
    > > > > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > > > > Truncates | Generated columns | Via root | Description
    > > > > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > > > > >  postgres | f          | f             | t       | t       | t       |
    > > > > > t         | none              | f        |
    > > > > > Tables:
    > > > > >     "public.t1"
    > > > > >     "public.t2"
    > > > > >     "public.t3" (c1)
    > > > > >
    > > > > >                                                        Publication pub2
    > > > > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > > > > Truncates | Generated columns | Via root | Description
    > > > > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > > > > >  postgres | f          | f             | t       | t       | t       |
    > > > > > t         | none              | f        |
    > > > > > Tables:
    > > > > >     "public.t1"
    > > > > >     "public.t2"
    > > > > >     "public.t3" WHERE (c2 > 99)
    > > > > >
    > > > >
    > > > > My intention was to avoid potential ambiguity when tables are not
    > > > > schema-qualified, as mentioned by Amit [1].
    > > > >
    > > > > That said, PostgreSQL generally does not enforce schema qualification
    > > > > and relies on search_path [2][3] for object resolution. Users are
    > > > > typically familiar with this behavior.
    > > > >
    > > > > Thanks for the example. I’ve updated the syntax to allow tables in the
    > > > > EXCEPT list without schema qualification.
    > > > > If a table is specified without a schema, it is resolved against the
    > > > > immediately preceding schema clause.
    > > > >
    > > > > Few examples:
    > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE t1);
    > > > > -- table t1 is resolved in schema s2
    > > >
    > > > Okay, looks good.
    > > >
    > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1), s2
    > > > > EXCEPT (TABLE t2);
    > > > > -- if s1 does not contain t1, an error is raised: "s1.t1" does not exist
    > > >
    > > > Okay, looks good.
    > > >
    > > > >
    > > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE s1.t1, s2.t1);
    > > > > -- it will create the publication with except list - (s1.t1, s2.t1)
    > > >
    > > > Okay, I thought we will not be supporting EXCEPT at the end and mixed
    > > > schemas inside. What will happen if I give:
    > > >
    > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE t1, t2);
    > > >
    > > > and t1, t2 are present in both the schemas?
    > > >
    >
    > If no schema is specified, tables are resolved against the immediate
    > schema. In this case, s2.t1 and s2.t2 will be used in the EXCEPT list,
    > even if they exist in both s1 and s2.
    >
    > > > I feel just like we associate column lists and row filters directly
    > > > with each table, rather than using a mixed style at the end; we should
    > > > also allow EXCEPT to be specified alongside each schema. This would
    > > > avoid added complexity and reduce potential confusion. Thoughts?
    > >
    > > One correction: I meant, we should "only"  allow EXCEPT to be
    > > specified alongside each schema.
    > >
    >
    > I kept this for user convenience to specify the EXCEPT list in one go.
    > But as you mentioned, supporting mixed style adds complexity, as
    > currently, it checks tables against all schemas in the publication
    > (not just those in the command).
    >
    > In further testing, I also noticed below confusing behavior due to
    > allowed mixed style -
    >
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1;
    > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s2 EXCEPT (TABLE s1.t1);
    >  -- here, s1.t1 is allowed in EXCEPT because s1 is already part of the
    > publication, even though it’s not in the current command.
    
    This case seems further confusing.
    
    >
    > Let me make the changes to restrict the mixed style. Will share the
    > updated patch soon.
    >
    
    Yes, that will be better approach.
    
    thanks
    Shveta
    
    
    
    
  26. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-04-27T09:02:04Z

    On Mon, Apr 27, 2026 at 9:06 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Mon, Apr 27, 2026 at 9:02 AM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > On Fri, Apr 24, 2026 at 11:29 AM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > > I feel just like we associate column lists and row filters directly
    > > > > with each table, rather than using a mixed style at the end; we should
    > > > > also allow EXCEPT to be specified alongside each schema. This would
    > > > > avoid added complexity and reduce potential confusion. Thoughts?
    > > >
    > > > One correction: I meant, we should "only"  allow EXCEPT to be
    > > > specified alongside each schema.
    > > >
    > >
    > > I kept this for user convenience to specify the EXCEPT list in one go.
    > > But as you mentioned, supporting mixed style adds complexity, as
    > > currently, it checks tables against all schemas in the publication
    > > (not just those in the command).
    > >
    > > In further testing, I also noticed below confusing behavior due to
    > > allowed mixed style -
    > >
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1;
    > > ALTER PUBLICATION pub ADD TABLES IN SCHEMA s2 EXCEPT (TABLE s1.t1);
    > >  -- here, s1.t1 is allowed in EXCEPT because s1 is already part of the
    > > publication, even though it’s not in the current command.
    >
    > This case seems further confusing.
    >
    > >
    > > Let me make the changes to restrict the mixed style. Will share the
    > > updated patch soon.
    > >
    >
    > Yes, that will be better approach.
    >
    
    Please find the attached patches incorporating the above suggestion.
    The EXCEPT clause now accepts tables only of its adjacent schema and
    mixed style is not allowed.
    
    Few examples :
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1), s2
    EXCEPT (TABLE t1);
     -- both s1.t1 and s2.t1 will be added in except list
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1, s2 EXCEPT (TABLE s1.t1, s2.t1);
    -- ERROR:  table "s1.t1" in EXCEPT clause does not belong to schema "s2"
    
    --
    Thanks,
    Nisha
    
  27. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-04-29T05:45:57Z

    Hi Nisha.
    
    Some review comments for v4-0001 (docs only).
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    1.
    +    TABLES IN SCHEMA { <replaceable
    class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [
    EXCEPT ( <replaceable
    class="parameter">except_table_object</replaceable> [, ... ] ) ] [,
    ... ]
    
    It is ambiguous which part does that last ellipsis applies to?
    
    Consider instead:
    
    TABLES IN SCHEMA tables_in_schema [, ... ]
    
    and tables_in_schema is:
    { schema_name | CURRENT_SCHEMA } [ EXCEPT ( except_table_object [, ... ] ) ]
    
    ~~~
    
    2.
          <para>
           This clause specifies a list of tables to be excluded from the
    -      publication.
    +      publication. It can be used with both <literal>FOR ALL TABLES</literal>
    +      and <literal>FOR TABLES IN SCHEMA</literal>.
          </para>
    
    Saying "both" may imply they can coexist, but they cannot.
    
    SUGGESTION
    It can be used with <literal>FOR ALL TABLES</literal> or <literal>FOR
    TABLES IN SCHEMA</literal>.
    
    ~~~
    
    3.
    +  <para>
    +   Create a publication that publishes all changes for all the tables
    present in
    +   the schema <structname>sales</structname>, except
    +   <structname>sales.internal</structname> and
    <structname>sales.drafts</structname>:
    +<programlisting>
    +CREATE PUBLICATION sales_filtered FOR TABLES IN SCHEMA sales EXCEPT
    (TABLE sales.internal, sales.drafts);
    +</programlisting>
    +  </para>
    +
    
    AFAIK, the tables in the EXCEPT clause do not need to be
    schema-qualified, so they should not be written that way in the
    programlisting part of this example, because it might give the user
    the impression that schema-qualified names are required.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  28. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-04-29T06:32:12Z

    Hi Nisha.
    
    A couple of ad-hoc review comments for v4-0001...
    
    ======
    src/bin/pg_dump/pg_dump.c
    
    dumpPublicationNamespace:
    
    1.
    + if (!first_except)
    + appendPQExpBufferStr(query, ")");
    
    The logic seems ok, but the above seems a bit strange, checking
    'first_except'. Maybe renaming to 'has_except' (and some small
    refactoring) would read better?
    
    ~~~
    
    2.
    IIUC, the 'ALTER PUBLICATION' EXCEPT clause syntax change is not
    introduced until your patch 0003. So, how does this dump code even
    work when it relies on that ALTER PUBLICATION?
    
    Furthermore, the patch 0003 commit message says 'ALTER PUBLICATION pub
    SET TABLES', but this dump code is using 'ALTER PUBLICATION pub ADD
    TABLES' (note ADD v SET). Something seems suspicious...
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  29. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-04T12:05:39Z

    On Wed, Apr 29, 2026 at 11:16 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for v4-0001 (docs only).
    >
    
    Thank you Peter for the review.
    
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 1.
    > +    TABLES IN SCHEMA { <replaceable
    > class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [
    > EXCEPT ( <replaceable
    > class="parameter">except_table_object</replaceable> [, ... ] ) ] [,
    > ... ]
    >
    > It is ambiguous which part does that last ellipsis applies to?
    >
    > Consider instead:
    >
    > TABLES IN SCHEMA tables_in_schema [, ... ]
    >
    > and tables_in_schema is:
    > { schema_name | CURRENT_SCHEMA } [ EXCEPT ( except_table_object [, ... ] ) ]
    >
    > ~~~
    
    Fixed.
    
    >
    > 2.
    >       <para>
    >        This clause specifies a list of tables to be excluded from the
    > -      publication.
    > +      publication. It can be used with both <literal>FOR ALL TABLES</literal>
    > +      and <literal>FOR TABLES IN SCHEMA</literal>.
    >       </para>
    >
    > Saying "both" may imply they can coexist, but they cannot.
    >
    > SUGGESTION
    > It can be used with <literal>FOR ALL TABLES</literal> or <literal>FOR
    > TABLES IN SCHEMA</literal>.
    >
    > ~~~
    
    Fixed.
    
    >
    > 3.
    > +  <para>
    > +   Create a publication that publishes all changes for all the tables
    > present in
    > +   the schema <structname>sales</structname>, except
    > +   <structname>sales.internal</structname> and
    > <structname>sales.drafts</structname>:
    > +<programlisting>
    > +CREATE PUBLICATION sales_filtered FOR TABLES IN SCHEMA sales EXCEPT
    > (TABLE sales.internal, sales.drafts);
    > +</programlisting>
    > +  </para>
    > +
    >
    > AFAIK, the tables in the EXCEPT clause do not need to be
    > schema-qualified, so they should not be written that way in the
    > programlisting part of this example, because it might give the user
    > the impression that schema-qualified names are required.
    >
    
    Done, matched with nearby examples.
    
    All of the above comments are addressed in v5-0001 patch.
    
    --
    Thanks,
    Nisha
    
    
    
    
  30. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-04T12:05:48Z

    On Wed, Apr 29, 2026 at 12:02 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > A couple of ad-hoc review comments for v4-0001...
    >
    > ======
    > src/bin/pg_dump/pg_dump.c
    >
    > dumpPublicationNamespace:
    >
    > 1.
    > + if (!first_except)
    > + appendPQExpBufferStr(query, ")");
    >
    > The logic seems ok, but the above seems a bit strange, checking
    > 'first_except'. Maybe renaming to 'has_except' (and some small
    > refactoring) would read better?
    >
    > ~~~
    
    Thanks for pointing that out. This also needed updating after the
    syntax change to EXCEPT (TABLE ...).
    I’ve used the name "has_except", defaulting it to false for better readability.
    
    >
    > 2.
    > IIUC, the 'ALTER PUBLICATION' EXCEPT clause syntax change is not
    > introduced until your patch 0003. So, how does this dump code even
    > work when it relies on that ALTER PUBLICATION?
    >
    > Furthermore, the patch 0003 commit message says 'ALTER PUBLICATION pub
    > SET TABLES', but this dump code is using 'ALTER PUBLICATION pub ADD
    > TABLES' (note ADD v SET). Something seems suspicious...
    >
    
    I think there was a slight mix-up in reading the patch messages. The
    ALTER PUBLICATION ... EXCEPT syntax is introduced in patch-002 for
    ADD, not patch-003.
    
    That said, you’re right. The pg_dump part doesn’t work with patch-001
    alone. I missed moving it to patch-002 while splitting the patches.
    Thanks for catching that; it’s now fixed.
    
    Also, patch-003 is for SET (not ADD) and includes handling for cleanup
    of EXCEPT entries when a schema is dropped. I’ve updated the patch
    message for clarity.
    
    Attached v5 patches with these updates.
    
    --
    Thanks,
    Nisha
    
  31. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-05-15T07:19:03Z

    Hi Nisha.
    
    Some review comments for patch v5-0001.
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    (FOR TABLES IN SCHEMA)
    
    1.
    +      Tables listed in <literal>EXCEPT</literal> for a given schema are
    +      excluded from the publication.
    
    /Tables listed in EXCEPT.../Tables listed in the EXCEPT clause.../
    
    There is a similar problem with the existing *head* docs in the "FOR
    ALL TABLES" part. This should be fixed, too.
    /Tables listed in EXCEPT clause.../Tables listed in the EXCEPT clause.../
    
    ======
    src/backend/catalog/pg_publication.c
    
    publication_add_relation:
    
    2.
    + HeapTuple existing;
    
    Not sure if this is the best name. How about "tup"?
    
    ~~~
    
    3.
    + bool is_except = existing_form->prexcept;
    
    This variable is used only once. Not sure if that vindicates having it.
    
    ~~~
    
    4.
    + ereport(ERROR,
    + (errcode(ERRCODE_DUPLICATE_OBJECT),
    + errmsg("table \"%s.%s\" cannot be added because it is listed in
    EXCEPT clause of publication \"%s\"",
    + get_namespace_name(RelationGetNamespace(targetrel)),
    + RelationGetRelationName(targetrel),
    + pub->name)));
    
    4a.
    There is a recently committed function which will give the
    fully-qualified rel name, so you can use "%s" instead of "%s.%s".
    
    ~
    
    4b.
    The message seems a bit wordy.
    
    BEFORE
    "table \"%s.%s\" cannot be added because it is listed in EXCEPT clause
    of publication \"%s\""
    SUGGESTION
    "table \"%s\" cannot be added because it is excluded from publication \"%s\""
    
    ~~~
    
    5.
    + * For ALTER PUBLICATION, invalidation is needed when adding an EXCEPT
    + * table to either: - a FOR ALL TABLES publication (pub->alltables is
    + * true), or - a FOR TABLES IN SCHEMA publication (is_schema_publication
    + * is true).
      *
    - * For ALTER PUBLICATION, invalidation is needed only when adding an
    - * EXCEPT table to a publication already marked as ALL TABLES. For
    - * publications that were originally empty or defined as ALL SEQUENCES and
    - * are being converted to ALL TABLES, invalidation is skipped here, as
    - * AlterPublicationAllFlags() function invalidates all relations while
    - * marking the publication as ALL TABLES publication.
    + * The exception: when a publication is being converted to FOR ALL TABLES
    + * (pub->alltables is still false at this point),
    + * AlterPublicationAllFlags() will perform a full invalidation, so we skip
    + * it here.
      */
    - inval_except_table = (alter_stmt != NULL) && pub->alltables &&
    - (alter_stmt->for_all_tables && pri->except);
    + inval_except_table = (alter_stmt != NULL) && pri->except &&
    + (pub->alltables
    + ? alter_stmt->for_all_tables
    + : is_schema_publication(pubid));
    
    Is all this comment and logic about "ALTER PUBLICATION" a bit
    premature? AFAIK, patch 0001 is not yet doing anything for ALTER
    PUBLICATION, so it looks like all this belongs in a later patch.
    
    ~~~
    
    GetIncludedPublicationRelations:
    
    6.
    - * This should only be used FOR ALL TABLES publications.
    + * This is used for FOR ALL TABLES and FOR TABLES IN SCHEMA publications,
    + * both of which support EXCEPT TABLE.
    
    So, because it might come from 2 places, shouldn't the assert in this
    function be modified?
    
    Also, since the assert was not yet modified, how does this even pass
    the tests if 'alltables' was false?
    
    ~~~
    
    GetAllSchemaPublicationRelations:
    
    7.
    + /* get the list of tables excluded via EXCEPT TABLE for this publication */
    + if (pubschemalist != NIL)
    + exceptlist = get_publication_relations(pubid, pub_partopt, true);
    +
    
    Should this be calling 'GetExcludedPublicationTables' instead of
    directly calling get_publication_relations?
    
    ~~~
    
    8.
    + list_free(schemaRels);
    + }
    + else
    + result = list_concat(result, schemaRels);
    
    Why is 'schemaRels' only being freed when there is an EXCEPT?
    
    ======
    src/backend/commands/publicationcmds.c
    
    CreatePublication:
    
    9.
      List    *rels;
    + ListCell   *lc;
    
    Looks like this can be declared at a lower scope.
    
    ~~~
    
    10.
    + foreach(lc, rels)
    + {
    + PublicationRelInfo *pri = (PublicationRelInfo *) lfirst(lc);
    +
    + explicitrelids = lappend_oid(explicitrelids,
    + RelationGetRelid(pri->relation));
    + }
    
    Maybe this loop can be made neater using foreach_ptr().
    
    ~~~
    
    11.
    + foreach(lc, rels)
    + {
    + PublicationRelInfo *pri = (PublicationRelInfo *) lfirst(lc);
    + Oid relid = RelationGetRelid(pri->relation);
    + Oid relns = RelationGetNamespace(pri->relation);
    
    11a.
    Maybe this loop can be made neater using foreach_ptr().
    
    ~
    
    11b.
    Maybe 'except_relid' or 'excluded_relid' is a more meaningful varname.
    
    I also felt that some other names are a bit confusing.
    - e.g. 'exceptrelations' vs 'rels' ...
      IIUC, both of these are lists of excepted relations/tables, although
      1st is a list of PublicationTable and
      2nd is a list of PublicationRelInfo
    ~~~
    
    12.
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    + errmsg("table \"%s.%s\" cannot appear in both the table list and the
    EXCEPT clause",
    +    get_namespace_name(relns),
    +    RelationGetRelationName(pri->relation)));
    
    This can make use of a recently committed function that returns the
    schem-qualifier relname so you can use "%s" instead of "%s.%s".
    
    ======
    src/backend/parser/gram.y
    
    13.
    - * TABLES IN SCHEMA schema [, ...]
    + * TABLES IN SCHEMA schema [EXCEPT ( table [, ...] )] [, ...]
    
    In the comment just before this one, the FOR ALL TABLES EXCEPT was
    written as "[EXCEPT (TABLE table [, ...] )]". So, I think this should
    be the same for consistency.
    
    ~~~
    
    14.
    - | ColId opt_column_list OptWhereClause
    + | ColId opt_column_list OptWhereClause opt_pub_except_clause
      {
      $$ = makeNode(PublicationObjSpec);
      $$->pubobjtype = PUBLICATIONOBJ_CONTINUATION;
    + $$->except_tables = $4;
    
    This seems suspicious. You cannot have an EXCEPT clause when there is
    a column list or a WHERE clause, so what is this scenario? Maybe the
    "$$->except_tables = $4;" needs to be moved to the 'else' block?
    
    ~~~
    
    preprocess_pubobj_list:
    
    15.
    + /* Flatten EXCEPT entries into the top-level list */
    + foreach(lc, pubobj->except_tables)
    
    Maybe the loop can be made neater by using foreach_ptr.
    
    ~~~
    
    16.
    + if (pubobj->pubobjtype == PUBLICATIONOBJ_TABLES_IN_SCHEMA)
    + {
    + if (eobj->pubtable->relation->schemaname == NULL)
    + eobj->pubtable->relation->schemaname = pubobj->name;
    + else if (strcmp(eobj->pubtable->relation->schemaname,
    + pubobj->name) != 0)
    + ereport(ERROR,
    + errcode(ERRCODE_SYNTAX_ERROR),
    + errmsg("table \"%s.%s\" in EXCEPT clause does not belong to schema \"%s\"",
    +    eobj->pubtable->relation->schemaname,
    +    eobj->pubtable->relation->relname,
    +    pubobj->name),
    + parser_errposition(eobj->location));
    
    16a.
    Introducing some more variables (like 'eobj_schemaname' and
    'eobj_relname') would simplify this code quite a bit.
    
    ~
    
    16b.
    Should make use of the recently committed function that gets
    fully-qualified rel names so you can use "%s" instead of "%s.%s".
    
    ======
    src/backend/replication/pgoutput/pgoutput.c
    
    get_rel_sync_entry:
    
    17.
    + if (am_partition)
    + {
    + List    *pancestore = get_partition_ancestors(relid);
    +
    + schemaExceptPubids =
    + GetRelationExcludedPublications(llast_oid(pancestore));
    + list_free(pancestore);
    + }
    + else
    + schemaExceptPubids = GetRelationExcludedPublications(relid);
    
    17a.
    Typo? "pancestore" -- what's the 'e'?
    
    ~
    
    17b.
    Anyway, would it be simpler to just get the Oid up-front?
    
    e.g.
    Oid oid_root = llast_oid(get_partition_ancestors(relid));
    
    ~~~
    
    18.
    + /*
    + * The ancestor is only considered published if it is not
    + * in the EXCEPT clause of this schema publication.
    + * GetTopMostAncestorInPublication checks schema
    + * membership but does not account for the EXCEPT list, so
    + * we must filter that out here.
    + */
    
    So, then maybe the logic to account for EXCEPT TABLE *should* be
    encapsulated within the GetTopMostAncestorInPublication, instead of
    the tack-on extra filtering needed here?
    
    ======
    src/bin/psql/tab-complete.in.c
    
    19.
    + if (strchr(previous_words[3], ',') == NULL)
    + {
    + set_completion_reference(previous_words[3]);
    + COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_tables_in_schema);
    + }
    
    IIUC, you are not supposed to reference 'previous_words' directly like
    this, so maybe this should be 'prev4_wd'?
    
    ~~~
    
    20.
    + char    *schema_word = previous_words[previous_words_count - 8];
    
    This looks suspicious. I don't see similar code to this anywhere else. (??)
    
    ======
    src/include/nodes/parsenodes.h
    
    21.
    + List    *except_tables; /* tables to exclude (for TABLES IN SCHEMA) */
      ParseLoc location; /* token location, or -1 if unknown */
     } PublicationObjSpec;
    
    Maybe the wording should be worded more like the existing one in
    PublicationAllObjSpec.
    
    SUGGESTION
    tables specified in the EXCEPT clause (for TABLES IN SCHEMA)
    
    
    ======
    src/test/regress/sql/publication.sql
    
    22.
    +-- Exclude multiple tables using unqualified names (implicitly
    qualified with the schema)
    +CREATE PUBLICATION testpub_schema_except2
    +    FOR TABLES IN SCHEMA pub_test EXCEPT (TABLE testpub_nopk, testpub_tbl_s1);
    +\dRp+ testpub_schema_except2
    
    A slightly more sophisticated test might have those same excluded
    table names in the 'public' schema as well as in the schema with the
    EXCEPT clause, so then you can verify it is not getting them mixed up.
    
    ~~~
    
    23.
    +-- fail: EXCEPT is not allowed for FOR TABLE publications
    +CREATE PUBLICATION testpub_except_err
    +    FOR TABLE pub_test.testpub_tbl_s1, testpub_tbl_s2 EXCEPT (TABLE
    pub_test.testpub_nopk);
    
    Hmm. Doesn't this test belong elsewhere instead of in the "EXCEPT
    tests for TABLES IN SCHEMA" group?
    
    ~~~
    
    24.
    MORE TEST CASES (below are in no particular order)
    
    24a.
    fail - when a table in the schema EXCEPT clause is also specified by FOR TABLE
    
    ~
    
    24b.
    fail - when a non-existing table is specified in the EXCEPT clause.
    
    ~
    
    24c.
    fail - when trying to exclude partitions.
    
    ~
    
    24d.
    pass - publication with multiple schemas and multiple EXCEPT clauses
    
    ~
    
    24e.
    fail - when the TABLE keyword is omitted.
    
    
    ======
    src/test/subscription/t/037_except.pl
    
    25.
    +my $schema_pub =
    +  "CREATE PUBLICATION tap_pub_part FOR TABLES IN SCHEMA public EXCEPT
    (TABLE public.root1)";
    +test_except_root_partition('false',
    + "$schema_pub WITH (publish_via_partition_root = false)");
    +test_except_root_partition('true',
    + "$schema_pub WITH (publish_via_partition_root = true)");
    
    The "WITH (publish_via_partition_root = true/false)" part should be
    automatically appended within the subroutine, based on the other
    boolean parameter.
    
    ~~~
    
    26.
    + CREATE TABLE sch1.tab_pub AS SELECT generate_series(1,5) AS a;
    + CREATE TABLE sch1.tab_exc AS SELECT generate_series(1,5) AS a;
    + CREATE TABLE sch1.par (a int);
    + CREATE TABLE sch1.chi (b int) INHERITS (sch1.par);
    
    The abbreviated table names like 'tab_exc', 'par', 'chi' (instead of
    'tab_excluded', 'parent', 'child') are not saving much at all, but
    they are obfuscating the meaning. Descriptive names would be better,
    
    ~~~
    
    27.
    +# Truncate chi on the publisher so the next test starts with a clean slate.
    +# (The previous test inserted rows into chi that would otherwise be copied by
    +# the initial table sync of the next subscription.)
    
    The comment explains the setup, but does not actually say what the
    test does -- e.g. you are testing excluding ONLY the parent, so in
    this case the child is expected to be replicated.
    
    ~~~
    
    28.
    +$node_subscriber->safe_psql('postgres', 'DROP SUBSCRIPTION sch_sub');
    +$node_publisher->safe_psql('postgres', 'DROP PUBLICATION sch_pub');
    +$node_publisher->safe_psql('postgres',
    + 'TRUNCATE sch1.par, sch1.chi, sch1.tab_exc');
    +$node_subscriber->safe_psql('postgres',
    + 'TRUNCATE sch1.par, sch1.chi, sch1.tab_pub, sch1.tab_exc');
    
    Are those TRUNCATEs needed? Won't the tables get trashed anyway by
    CASCADE in the next ("Cleanup schema tables") part?
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  32. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-15T13:32:03Z

    On Fri, May 15, 2026 at 12:49 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for patch v5-0001.
    
    Thanks Peter for the review.
    
    >
    > ~~~
    >
    > GetIncludedPublicationRelations:
    >
    > 6.
    > - * This should only be used FOR ALL TABLES publications.
    > + * This is used for FOR ALL TABLES and FOR TABLES IN SCHEMA publications,
    > + * both of which support EXCEPT TABLE.
    >
    > So, because it might come from 2 places, shouldn't the assert in this
    > function be modified?
    >
    I think you meant GetExcludedPublicationTables.
    Patch-0003 updates the assert in this fuction for schema publications.
    
    > Also, since the assert was not yet modified, how does this even pass
    > the tests if 'alltables' was false?
    >
    
    As you pointed out in the next (7th) comment also, the first two
    patches are not calling GetExcludedPublicationTables(), but are using
    get_publication_relations() directly. Hence, the tests are passing
    even without the assert modification. But patch-0003 does call it, so
    the assert was updated there.
    
    > ~~~
    >
    > GetAllSchemaPublicationRelations:
    >
    > 7.
    > + /* get the list of tables excluded via EXCEPT TABLE for this publication */
    > + if (pubschemalist != NIL)
    > + exceptlist = get_publication_relations(pubid, pub_partopt, true);
    > +
    >
    > Should this be calling 'GetExcludedPublicationTables' instead of
    > directly calling get_publication_relations?
    >
    
    Agreed, I will make the change. With that, the above mentioned assert
    will also need to be updated in patch-1 itself.
    ~~~~
    
    I agree with the rest of the comments and will update and upload the
    patches soon.
    
    --
    Thanks,
    Nisha
    
    
    
    
  33. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-05-17T01:32:25Z

    On Fri, May 15, 2026 at 11:32 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    ...
    > > Also, since the assert was not yet modified, how does this even pass
    > > the tests if 'alltables' was false?
    > >
    >
    > As you pointed out in the next (7th) comment also, the first two
    > patches are not calling GetExcludedPublicationTables(), but are using
    > get_publication_relations() directly. Hence, the tests are passing
    > even without the assert modification. But patch-0003 does call it, so
    > the assert was updated there.
    >
    
    Ah, I missed that -- thanks for the explanation.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  34. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-19T11:14:09Z

    On Fri, May 15, 2026 at 12:49 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for patch v5-0001.
    >
    
    Thanks Peter, for the review.
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > publication_add_relation:
    >
    > 2.
    > + HeapTuple existing;
    >
    > Not sure if this is the best name. How about "tup"?
    >
    
    Noticed that a "HeapTuple tup;" is already declared, so we can use the
    existing one.
    
    > ~~~
    >
    > 3.
    > + bool is_except = existing_form->prexcept;
    >
    > This variable is used only once. Not sure if that vindicates having it.
    >
    
    the is_except value is being used after releasing the tuple and
    closing the table since the next step errors out. But I have now
    removed existing_form and directly extracted the value instead.
    
    > ~~~
    >
    > 8.
    > + list_free(schemaRels);
    > + }
    > + else
    > + result = list_concat(result, schemaRels);
    >
    > Why is 'schemaRels' only being freed when there is an EXCEPT?
    >
    
    IIUC, In the EXCEPT case, relid is an Oid, so lfirst_oid() copies the
    integer value from the cell, and lappend_oid() stores that value into
    a new cell in result. That means result does not reference schemaRels
    cells after the loop, so list_free(schemaRels) is safe.
    
    In the else branch, list_concat() directly transfers schemaRels cells
    into result. So freeing schemaRels there would corrupt the result.
    
    > ======
    > src/backend/parser/gram.y
    >
    > 14.
    > - | ColId opt_column_list OptWhereClause
    > + | ColId opt_column_list OptWhereClause opt_pub_except_clause
    >   {
    >   $$ = makeNode(PublicationObjSpec);
    >   $$->pubobjtype = PUBLICATIONOBJ_CONTINUATION;
    > + $$->except_tables = $4;
    >
    > This seems suspicious. You cannot have an EXCEPT clause when there is
    > a column list or a WHERE clause, so what is this scenario? Maybe the
    > "$$->except_tables = $4;" needs to be moved to the 'else' block?
    >
    
    This handles cases where multi-schema continuation is used along with
    an EXCEPT clause, e.g.:
      create publication pub1 for tables in schema public, s1 except (table t1);
    -- without the above, the EXCEPT clause would be silently ignored.
    
    Now, I agree that the above case can also be handled by moving
    "$$->except_tables = $4;" into the else branch. But then EXCEPT would
    again get silently ignored for table continuations with a column-list
    or where clause, e.g.,:
    postgres=# create publication pub2 for table t1, t2 (c1) except (table t1);
    CREATE PUBLICATION
    
    The idea here is to collect the EXCEPT list whenever it is specified
    for such continuation cases, and then explicitly raise an error in
    preprocess_pubobj_list() if a table publication object contains an
    EXCEPT list.
    
    > ~~~
    >
    > 16.
    > + if (pubobj->pubobjtype == PUBLICATIONOBJ_TABLES_IN_SCHEMA)
    > + {
    > + if (eobj->pubtable->relation->schemaname == NULL)
    > + eobj->pubtable->relation->schemaname = pubobj->name;
    > + else if (strcmp(eobj->pubtable->relation->schemaname,
    > + pubobj->name) != 0)
    > + ereport(ERROR,
    > + errcode(ERRCODE_SYNTAX_ERROR),
    > + errmsg("table \"%s.%s\" in EXCEPT clause does not belong to schema \"%s\"",
    > +    eobj->pubtable->relation->schemaname,
    > +    eobj->pubtable->relation->relname,
    > +    pubobj->name),
    > + parser_errposition(eobj->location));
    >
    > 16a.
    > Introducing some more variables (like 'eobj_schemaname' and
    > 'eobj_relname') would simplify this code quite a bit.
    >
    
    Done.
    
    > ~
    >
    > 16b.
    > Should make use of the recently committed function that gets
    > fully-qualified rel names so you can use "%s" instead of "%s.%s".
    >
    
    We cannot use RelationGetQualifiedRelationName() in the grammar, so I
    used quote_qualified_identifier() instead.
    ~~~~
    
    Addressed all other comments as suggested. Please find the updated v6
    patches attached.
    
    Patch-0001: updated as per the above comments.
    Patch-0002 and Patch-0003: adjusted for the Patch-0001 changes and
    some code simplification in tab-complete part.
    
    --
    Thanks,
    Nisha
    
  35. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-05-22T02:26:35Z

    Hi Nisha.
    
    Here are some review comments for patch v6-0001.
    
    ======
    src/backend/catalog/pg_publication.c
    
    GetTopMostAncestorInPublication:
    
    1.
    +GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
    + int *ancestor_level, List *except_pubids)
    
    I am having dificulty understanding this function. There needs to be a
    description what does the input parameter 'except_pubids' mean. The
    param name doesn't tell me anything much -- just that it is a list of
    pubids that "something" (what?) is excluded from. And how does that
    relate to the 'ancestors'?
    
    ~~~
    
    2.
      {
      aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    - if (list_member_oid(aschemaPubids, puboid))
    + if (list_member_oid(aschemaPubids, puboid) &&
    + !list_member_oid(except_pubids, puboid))
    
    Is this new code in the right place? I'm not 100% sure of the
    'except_pubids' details, but shouldn't it be checked sooner? e.g.  if
    we know already that this pubid is no good
    (!list_member_oid(except_pubids, puboid)) then what is the point to
    even assign/check aschemaPubids?
    
    ~~~
    
    3.
    + if (is_except)
    + ereport(ERROR,
    + (errcode(ERRCODE_DUPLICATE_OBJECT),
    + errmsg("table \"%s\" cannot be added because it is excluded from
    publication \"%s\"",
    + RelationGetQualifiedRelationName(targetrel),
    + pub->name)));
    + else
    + ereport(ERROR,
    + (errcode(ERRCODE_DUPLICATE_OBJECT),
    + errmsg("relation \"%s\" is already member of publication \"%s\"",
    + RelationGetRelationName(targetrel), pub->name)));
    
    Fully qualified 'targetrel' in the first error, but not in the second? Is it OK?
    
    ~~~
    
    GetAllSchemaPublicationRelations:
    
    4.
    + List    *exceptlist = NIL;
    
    The varname is a bit vague; it is a list of "what"? Maybe say
    'except_relids' or similar.
    
    ======
    src/backend/replication/pgoutput/pgoutput.c
    
    get_rel_sync_entry:
    
    5.
    + /*
    + * For the schema EXCEPT check, we must look up the top-most ancestor
    + * rather than the relation itself.  check_publication_add_relation()
    + * prevents individual partitions from appearing in the EXCEPT clause,
    + * so only a root (non-partition) table can have prexcept = true.
    + * Using the partition's own OID would always return NIL and miss the
    + * exclusion.
    + */
    + Oid root_relid = am_partition ?
    + llast_oid(get_partition_ancestors(relid)) : relid;
    +
    + schemaExceptPubids = GetRelationExcludedPublications(root_relid);
    
    5a.
    The varname 'schemaExceptPubids' seems ambiguous. It sounds like it is
    pubids that have EXCEPT SCHEMA. In the future the ALL TABLES may
    introduce "EXCEPT SCHEMA", but currently there is no such thing.
    Meanwhile, here I think it means "EXCEPT TABLE", so IMO that varname
    needs to indicate the meaning better.
    
    ~
    
    5b.
    Actually, this is becoming a GENERAL comment. There too many ways that
    these EXCEPT tables are getting named, and it is causing confusion:
    - except_pubids
    - exceptlist
    - exceptrelations
    - exceptrels
    - except_relid
    - except_tables
    - schemaExceptPubids
    
    Can we standardize on some common names, to make all the code more consistent?
    
    ~
    
    5c.
    Previously, the result of 'get_partition_ancestors' was being freed,
    but now it is not. I'm not sure how importatnt that is, because I
    found other examples in PG source code also not freeing...
    
    ======
    src/bin/psql/tab-complete.in.c
    
    6.
    + else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES",
    "IN", "SCHEMA", MatchAny, "EXCEPT", "(", "TABLE", MatchAnyN) &&
    ends_with(prev_wd, ','))
    + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
    
    I'm not sure if this is working as intended.
    
    When testing for multiple except tables I get results like:
    ----
    test_pub=# create publication pub1 for tables IN SCHEMA myschema <TAB>
    EXCEPT ( TABLE  WITH (
    test_pub=# create publication pub1 for tables IN SCHEMA myschema
    except ( table <TAB>
    test_pub=# create publication pub1 for tables IN SCHEMA myschema
    except ( table myschema.t<TAB>
    myschema.t1  myschema.t2  myschema.t3
    test_pub=# create publication pub1 for tables IN SCHEMA myschema
    except ( table myschema.t1,<TAB>
    information_schema.  myschema.            public.              t1
                 t2                   t3
    ----
    
    Note: it is offering suggstions for schema names outside of the
    "myschema". Should this code be calling
    Query_for_list_of_tables_in_schema instead of
    Query_for_list_of_tables?
    
    ======
    src/test/regress/sql/publication.sql
    
    7.
    ---- Tests for publications with SEQUENCES
    +---------------------------------------------
    +-- EXCEPT tests for TABLES IN SCHEMA
    +---------------------------------------------
    +SET client_min_messages = 'ERROR';
    
    It looks like a previous comment for the SEQUENCES tests has been
    accidentally removed.
    
    I should be put back, and made more prominent like the other big comments.
    e.g.
    ---------------------------------------------
    -- Tests for publications with SEQUENCES
    ---------------------------------------------
    
    ~~~
    
    8.
    +RESET client_min_messages;
    +DROP TABLE pub_test.testpub_tbl_s1, pub_test.testpub_tbl_s2;
    +DROP TABLE pub_test.testpub_parted_s CASCADE;
    +DROP TABLE testpub_nopk, testpub_tbl_s1;
    +DROP PUBLICATION testpub_schema_except1, testpub_schema_except2,
    testpub_schema_except_multi;
    +
    
    Add a "Cleanup" comment.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  36. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-05-22T05:30:12Z

    Hi Nisha.
    
    Here are some review comments for patch v6-0002.
    
    ======
    Commit message
    
    1.
    Extend the EXCEPT clause support to allow tables to be excluded when
    adding a schema to a publication via ALTER PUBLICATION ... ADD:
    
    ~
    
    /ADD:/ADD./
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    Synopsis.
    
    2.
    -    TABLES IN SCHEMA { <replaceable
    class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ...
    ]
    +    TABLES IN SCHEMA { <replaceable
    class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [
    EXCEPT ( <replaceable
    class="parameter">except_table_object</replaceable> [, ... ] ) ] [,
    ... ]
    
    ~
    
    Probably needs to change to introduce the 'tables_in_schema' part,
    same as in the CREATE PUBLICATION synopsis.
    
    ~~~
    
    3.
    +
    +<phrase>and <replaceable
    class="parameter">except_table_object</replaceable> is:</phrase>
    +
    +   [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    
    Something is wrong. Now the synopsis has 'except_table_object' 2x.
    
    ~~~
    
    4.
    +  <para>
    +   The <literal>EXCEPT</literal> clause can be used with
    +   <literal>ADD TABLES IN SCHEMA</literal> to exclude specific tables from a
    +   schema-level publication. <literal>EXCEPT</literal> is not supported with
    +   <literal>DROP TABLES IN SCHEMA</literal>; instead, dropping a schema from
    +   the publication automatically removes all of its associated
    +   <literal>EXCEPT</literal> entries.
    +  </para>
    
    4a.
    I didn't think you need to say it is a "schema-level" publication.
    That much is obvious because it already says "TABLES IN SCHEMA".
    
    ~
    
    4b.
    Maybe do not say "is not supported", because IMO that implies it will
    cause an error.
    
    SUGGESTION (or something like this)
    Using <literal>DROP TABLES IN SCHEMA</literal> on a publication will
    automatically also remove any associated <literal>EXCEPT</literal>
    entries.
    
    ~~~
    
    EXCEPT
    
    5.
    +   <varlistentry>
    +    <term><literal>EXCEPT ( <replaceable
    class="parameter">except_table_object</replaceable> [, ... ]
    )</literal></term>
    +    <listitem>
    +     <para>
    +      Specifies tables to be excluded from a schema-level publication entry.
    +      This clause may be used with <literal>ADD TABLES IN SCHEMA</literal>
    +      and not with <literal>DROP TABLES IN SCHEMA</literal>.  Each named
    +      table must belong to the schema specified in the same
    +      <literal>TABLES IN SCHEMA</literal> clause.  Table names may be
    +      schema-qualified or unqualified; unqualified names are implicitly
    +      qualified with the schema named in the same clause.  See
    +      <xref linkend="sql-createpublication"/> for further details on the
    +      semantics of <literal>EXCEPT</literal>.
    +     </para>
    +    </listitem>
    +   </varlistentry>
    +
    
    5a.
    Oh! If this EXCEPT part was previously missing even for the "FOR ALL
    TABLES", then IMO that is a separate bug that should be in another
    thread and patched/fixed asap, then your patch should just make small
    changes to to it.
    
    ~
    
    5b.
    I don't think you need to say "schema-level" here... Maybe reword like
    "When used with ADD TABLES IN SCHEMA...". Anyway, all this wording
    will need to change a bit after the aforementioned fix for "FOR ALL
    TABLES EXCEPT" patched/pushed.
    
    ~~~
    
    Examples
    
    6.
    +<programlisting>
    +ALTER PUBLICATION sales_publication ADD TABLES IN SCHEMA sales EXCEPT
    (TABLE sales.internal, sales.drafts);
    +</programlisting>
    
    It is OK left in the description, but IMO it is better if you don't
    use the schema-qualified name in the actual code fragment.
    
    ======
    src/backend/commands/publicationcmds.c
    
    AlterPublicationSchemas:
    
    7.
    + /*
    + * Increment the command counter so that is_schema_publication() in
    + * GetExcludedPublicationTables() can see the just-inserted schema
    + * rows when AlterPublicationExceptTables runs next.
    + */
    + CommandCounterIncrement();
    
    Should this cut/paste common code be done afterwards just if
    stmt->action == AP_AddObjects/SetObjects ?
    
    ~~~
    
    AlterPublicationExceptTables:
    
    8.
    + /*
    + * This function handles EXCEPT entries for schema-level publications
    + * only.  For FOR ALL TABLES publications, EXCEPT entries are already
    + * processed by AlterPublicationTables().
    + */
    + if (schemaidlist == NIL && !is_schema_publication(pubid))
    + return;
    
    Huh? It seems contrary to the function comment that was also talking
    about "FOR ALL TABLES".
    
    Should this function really be called something different like
    'AlterPublicationSchemaExceptTables'?
    
    ~~~
    
    9.
    + /*
    + * EXCEPT with SET is not supported: SET replaces the schema list but does
    + * not have a well-defined semantics for merging or replacing existing
    + * except entries.  Users should DROP and re-ADD the schema with the
    + * desired EXCEPT list instead.
    + */
    + if (stmt->action == AP_SetObjects)
    + ereport(ERROR,
    + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    + errmsg("EXCEPT clause is not supported with SET in ALTER PUBLICATION")));
    
    9a.
    Not sure about this comment saying "does not have a well-defined
    semantics". Should you instead just have XXX comment to simply say
    "Not yet implemented", because this is getting replaced later by your
    patch 0003 I think.
    
    SUGGESTION
    XXX EXCEPT with SET is not currently implemented. Workaround: Users
    should DROP and re-ADD the schema with the desired EXCEPT list.
    
    ~
    
    9b.
    The ereport should be temporarily (until patch 0003 is pushed) have
    using an errhint to say the workaround.
    
    ~~~
    
    10.
    + if (stmt->action == AP_AddObjects)
    + {
    + List    *rels;
    + List    *explicitrelids;
    + ListCell   *lc;
    +
    + rels = OpenTableList(exceptrelations);
    +
    + explicitrelids = GetIncludedPublicationRelations(pubid,
    + PUBLICATION_PART_ROOT);
    +
    + /*
    + * Validate that each excluded table is not also in the explicit table
    + * list (which would be contradictory).
    + */
    + foreach(lc, rels)
    
    Can tidy this using a foreach_ptr look instead of 'lc'.
    
    ~~~
    
    11.
    + if (list_member_oid(explicitrelids, relid))
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    + errmsg("table \"%s.%s\" cannot appear in both the table list and the
    EXCEPT clause",
    +    get_namespace_name(relns),
    +    RelationGetRelationName(pri->relation)));
    
    Make use of the new function to get fully qualified names and replace
    \"%s.%s\" with \"%s\".
    
    ~~~
    
    12.
    + /*
    + * For FOR ALL TABLES, EXCEPT entries are processed by
    + * AlterPublicationTables(), so merge them in.  For TABLES IN SCHEMA,
    + * they are handled separately by AlterPublicationExceptTables().
    + */
    + if (stmt->for_all_tables)
    + relations = list_concat(relations, exceptrelations);
      AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext,
         schemaidlist != NIL);
      AlterPublicationSchemas(stmt, tup, schemaidlist);
    + AlterPublicationExceptTables(stmt, tup, exceptrelations, schemaidlist);
    
    Would it be simpler if AlterPublicationExceptTables was merged (or
    delegated from) the AlterPublicationSchemas?
    
    ======
    src/bin/pg_dump/t/002_pg_dump.pl
    
    13.
    + 'CREATE PUBLICATION pub11' => {
    + create_order => 50,
    + create_sql =>
    +   'CREATE PUBLICATION pub11 FOR TABLES IN SCHEMA dump_test EXCEPT
    (TABLE dump_test.test_table);',
    + regexp => qr/^
    + \QCREATE PUBLICATION pub11 WITH (publish = 'insert, update, delete,
    truncate');\E
    + /xm,
    + like => { %full_runs, section_post_data => 1, },
    + },
    +
    + 'ALTER PUBLICATION pub11 ADD TABLES IN SCHEMA dump_test EXCEPT
    (dump_test.test_table)'
    +   => {
    + regexp => qr/^
    + \QALTER PUBLICATION pub11 ADD TABLES IN SCHEMA dump_test EXCEPT
    (TABLE ONLY test_table);\E
    + /xm,
    + like => { %full_runs, section_post_data => 1, },
    +   },
    +
    + 'CREATE PUBLICATION pub12' => {
    + create_order => 50,
    + create_sql =>
    +   'CREATE PUBLICATION pub12 FOR TABLES IN SCHEMA dump_test EXCEPT
    (TABLE dump_test.test_table, dump_test.test_second_table);',
    + regexp => qr/^
    + \QCREATE PUBLICATION pub12 WITH (publish = 'insert, update, delete,
    truncate');\E
    + /xm,
    + like => { %full_runs, section_post_data => 1, },
    + },
    +
    + 'ALTER PUBLICATION pub12 ADD TABLES IN SCHEMA dump_test EXCEPT
    (dump_test.test_table, dump_test.test_second_table)'
    +   => {
    + regexp => qr/^
    + \QALTER PUBLICATION pub12 ADD TABLES IN SCHEMA dump_test EXCEPT
    (TABLE ONLY test_table, TABLE ONLY test_second_table);\E
    + /xm,
    + like => { %full_runs, section_post_data => 1, },
    +   },
    +
    
    Should not need to specify schema-qualified names in the CREATE
    PUBLICATION or the ALTER PUBLICATION. I think a better test would have
    one of each (e.g. don't qualify the 'dump_test.test_table') in any of
    those SQL.
    
    ======
    src/bin/psql/tab-complete.in.c
    
    14.
    + else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD", "TABLES",
    "IN", "SCHEMA", MatchAny, "EXCEPT", "(", "TABLE", MatchAnyN) &&
    ends_with(prev_wd, ','))
    + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
    + else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD", "TABLES",
    "IN", "SCHEMA", MatchAny, "EXCEPT", "(", "TABLE", MatchAnyN) &&
    !ends_with(prev_wd, ','))
    + COMPLETE_WITH(")");
    
    I've not tested this, but the code looks the same. so I suspect this
    suffers the same problem where it lists potentially all tables instead
    of just the table of the current schema. Maybe this is an unavoidable
    quirk...
    
    ======
    src/test/regress/sql/publication.sql
    
    15.
    Should you add a some more test cases?
    
    e.g. Pass with EXCEPT without the schema-qualified name.
    e.g. Pass with multiple excepted tables.
    e.g. Fail because non-existing table name.
    e.g. Fail because table not in schema.
    e.g. Fail syntax because missing keyword TABLE.
    e.g. do a DROP TABLES IN SCHEMA to test that the except tables gove removed too
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  37. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-05-26T05:56:45Z

    Hi Nisha.
    
    Some review comments for patch v6-0003.
    
    ======
    Commit Message
    
    1.
    Extend AlterPublicationExceptTables() with the AP_SetObjects case,
    which redefine the publication and replaces the entire EXCEPT list.
    
    ~
    
    /redefine/redefines/
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    2.
    -
    -<phrase>and <replaceable
    class="parameter">except_table_object</replaceable> is:</phrase>
    -
    -   [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    
    IIUC this is just removing some duplicate entry in the synopsis that
    was not supposed to be there in the first place.
    
    ~~~
    
    3.
        The <literal>EXCEPT</literal> clause can be used with
    -   <literal>ADD TABLES IN SCHEMA</literal> to exclude specific tables from a
    +   <literal>ADD TABLES IN SCHEMA</literal> and
    +   <literal>SET TABLES IN SCHEMA</literal> to exclude specific tables from a
        schema-level publication. <literal>EXCEPT</literal> is not supported with
        <literal>DROP TABLES IN SCHEMA</literal>; instead, dropping a schema from
        the publication automatically removes all of its associated
    
    3a.
    This whole description section seems arranged in a confusing way IMO.
    Anyway, it is not all the fault of the current patch. But I don't
    think it should be talking about "SET TABLES IN SCHEMA" here because
    that was all mentioned already in the earlier "third variant"
    paragraph.
    
    ~
    
    3b.
    That last sentence all about EXCEPT with DROP TABLES IN SCHEMA seems
    redundant to me. It is not allowed by the synopsis, so there is no
    possible confusion about it being supported. Isn't it better to just
    say nothing?
    
    ~~~
    
    EXCEPT
    
    4.
          <para>
           Specifies tables to be excluded from a schema-level publication entry.
           This clause may be used with <literal>ADD TABLES IN SCHEMA</literal>
    -      and not with <literal>DROP TABLES IN SCHEMA</literal>.  Each named
    -      table must belong to the schema specified in the same
    -      <literal>TABLES IN SCHEMA</literal> clause.  Table names may be
    -      schema-qualified or unqualified; unqualified names are implicitly
    -      qualified with the schema named in the same clause.  See
    -      <xref linkend="sql-createpublication"/> for further details on the
    +      and <literal>SET TABLES IN SCHEMA</literal>, and not with
    +      <literal>DROP TABLES IN SCHEMA</literal>.  Each named table must belong
    +      to the schema specified in the same <literal>TABLES IN SCHEMA</literal>
    +      clause.  Table names may be schema-qualified or unqualified; unqualified
    +      names are implicitly qualified with the schema named in the same clause.
    +      See <xref linkend="sql-createpublication"/> for further details on the
           semantics of <literal>EXCEPT</literal>.
          </para>
    
    4a.
    IMO there is no reason to mention the "DROP TABLES IN SCHEMA" has no
    EXCEPT. That is not possible just by looking at the synopsis, so there
    is no ambiguity. Why say anything at all?
    
    ~
    
    4b.
    This description about EXCEPT is missing talking about FOR ALL TABLES
    EXCEPT, but IIRC I already reported that in a previous review.
    
    ~~~
    
    EXAMPLES
    
    5.
    +   Replace the schema list of <structname>sales_publication</structname> with
    +   <structname>sales</structname>, excluding only
    +   <structname>sales.drafts</structname> (any previously excluded tables for
    +   that schema are replaced, and schemas previously in the publication are
    +   removed):
    
    BEFORE
    (any previously excluded tables for that schema are replaced, and
    schemas previously in the publication are removed):
    
    SUGGESTION
    Other than sales.drafts, any previously excluded tables for schema
    sales are no longer excluded. Any schemas previously in the
    sales_publication are removed.
    
    ~~~
    
    6.
    +<programlisting>
    +ALTER PUBLICATION sales_publication SET TABLES IN SCHEMA sales EXCEPT
    (TABLE sales.drafts);
    +</programlisting>
    
    Don't fully qualify that table in the SQL example.
    
    ======
    src/backend/commands/publicationcmds.c
    
    AlterPublicationExceptTables:
    
    7.
    - * Nothing to do if no EXCEPT entries.
    + * Nothing to do if no EXCEPT entries, except in SET: for that it is quite
    + * possible that the user has removed all exceptions, in which case we
    + * need to drop any existing ones.
    
    Maybe reword this because it is a bit odd to have the word "except"
    with the keyword "EXCEPT".
    
    SUGGESTION
    Nothing to do if there are no EXCEPT entries, unless handling the SET
    command, because if the user has removed all exceptions we need to
    drop any existing ones.
    
    ~~~
    
    8.
    + {
    + List    *oldexceptrelids = NIL;
    + List    *newexceptrelids = NIL;
    + List    *delrelids = NIL;
    + List    *rels;
    + List    *explicitrelids;
    + ListCell   *lc;
    +
    + rels = OpenTableList(exceptrelations);
    +
    + /* Collect OIDs of the desired new except list. */
    + foreach(lc, rels)
    
    There are multiple foreach() loops which can probably be simplified
    all by using foreach_ptr(...) instead.
    
    ~~~
    
    9.
    + /* Collect OIDs of the desired new except list. */
    + foreach(lc, rels)
    
    /except/EXCEPT/
    
    ~~~
    
    10.
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    + errmsg("table \"%s.%s\" cannot appear in both the table list and the
    EXCEPT clause",
    +    get_namespace_name(relns),
    +    RelationGetRelationName(pri->relation)));
    
    Use the new function to get a fully qualified name and just substitute
    \"%s\" instead of \"%s.%s\".
    
    ~~~
    
    11.
    + /*
    + * Get the current set of except entries.  Only FOR ALL TABLES and
    + * schema-level publications can have except entries; for any other
    + * publication type oldexceptrelids stays NIL.
    + *
    + * Note: we check is_schema_publication() against the current catalog
    + * state (before AlterPublicationSchemas has run), so if the caller is
    + * doing SET TABLE t1 to convert a schema publication into a plain
    + * table publication, is_schema_publication() still returns true here.
    + * That is intentional: it lets us discover and clean up any stale
    + * except entries that belong to the old schema definition.
    + */
    + if (GetPublication(pubid)->alltables || is_schema_publication(pubid))
    + oldexceptrelids = GetExcludedPublicationTables(pubid,
    +    PUBLICATION_PART_ROOT);
    +
    + /* Build a list of old except entries not present in the new list. */
    + foreach(lc, oldexceptrelids)
    + {
    + Oid oldrelid = lfirst_oid(lc);
    +
    + if (!list_member_oid(newexceptrelids, oldrelid))
    + delrelids = lappend_oid(delrelids, oldrelid);
    + }
    +
    + /* Drop old except entries not present in the new list. */
    + foreach(lc, delrelids)
    
    There are multiple comments here mentioning "except entries", but I
    think they should say "EXCEPT entries".
    
    ~~~
    
    PublicationDropSchemas:
    
    12.
    + /*
    + * Collect prexcept rows for tables belonging to this schema before
    + * removing the schema entry.  GetExcludedPublicationTables relies on
    + * is_schema_publication(), which scans pg_publication_namespace; if
    + * this is the last schema in the publication, performDeletion() below
    + * would remove that row and make is_schema_publication() return
    + * false, tripping the assertion.
    + */
    
    What assertion?
    
    ~~~
    
    13.
    + foreach(elc, exceptoids)
    + {
    + Oid relid = lfirst_oid(elc);
    + Oid proid;
    
    Maybe this can be changed to be a foreach_oid(...) loop.
    
    ======
    src/bin/psql/tab-complete.in.c
    
    14.
    + else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET", "TABLES",
    "IN", "SCHEMA", MatchAny, "EXCEPT", "(", "TABLE", MatchAnyN) &&
    ends_with(prev_wd, ','))
    + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
    
    Not sure if this also ought to be
    'Query_for_list_of_tables_in_schema', instead of
    'Query_for_list_of_tables'.
    
    ======
    src/test/regress/sql/publication.sql
    
    15.
    +-- error: EXCEPT is not allowed with DROP
    
    I think we should keep all the SET tests together. The DROP test seems
    to be between other SET tests.
    
    ~~~
    
    16.
    Perhaps there should be some more tests -- eg. a test case to hit this
    new error "table \"%s.%s\" cannot appear in both the table list and
    the EXCEPT clause"
    
    ======
    src/test/subscription/t/037_except.pl
    
    17.
    +$node_publisher->safe_psql(
    + 'postgres', qq(
    + INSERT INTO sch1.tab_published VALUES (7);
    + INSERT INTO sch1.tab_excluded VALUES (7);
    +));
    +$node_publisher->wait_for_catchup('sch_sub');
    +
    +$result =
    +  $node_subscriber->safe_psql('postgres',
    + "SELECT count(*) FROM sch1.tab_excluded");
    +is($result, qq(7),
    + 'ALTER ... SET TABLES IN SCHEMA EXCEPT: newly included table is replicated'
    +);
    +$result =
    +  $node_subscriber->safe_psql('postgres',
    + "SELECT count(*) FROM sch1.tab_published");
    +is($result, qq(6),
    + 'ALTER ... SET TABLES IN SCHEMA EXCEPT: now-excluded table is not replicated'
    +);
    
    Instead of having to keep track of the running totals IMO it might be
    simpler if you just did "SELECT ... WHERE a = 7;" then the answer
    would be just 1 or 0 rows.
    
    ~~~
    
    18.
    +# SET without EXCEPT: clears the except list; both tables are now published.
    +# tab_published will be re-synced because REFRESH removed its entry when it was
    +# excluded.  Truncate the subscriber copy beforehand so the re-sync produces
    +# a predictable count: publisher has 7 rows (6 original + INSERT(7)), so the
    +# subscriber ends up with 7 after re-sync, then 8 after INSERT(8).
    
    This is similar to my previous comment. There is lots of tricky
    commentary here because you are trying to keep track of running totals
    of rows.
    
    I think most of this might not be needed if you changed the checks to
    do ""SELECT ... WHERE a = 8;", so then you are just expecting row
    count to be 1 for both tables.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  38. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-28T11:26:56Z

     On Fri, May 22, 2026 at 7:57 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Here are some review comments for patch v6-0001.
    >
    
    Thanks for the review. All comments are addressed in v7. Please find
    responses below for a few of the comments.
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > GetTopMostAncestorInPublication:
    >
    > 1.
    > +GetTopMostAncestorInPublication(Oid puboid, List *ancestors,
    > + int *ancestor_level, List *except_pubids)
    >
    > I am having dificulty understanding this function. There needs to be a
    > description what does the input parameter 'except_pubids' mean. The
    > param name doesn't tell me anything much -- just that it is a list of
    > pubids that "something" (what?) is excluded from. And how does that
    > relate to the 'ancestors'?
    >
    
    Updated the function comments to explain except_pubids.
    
    > ~~~
    >
    > 2.
    >   {
    >   aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > - if (list_member_oid(aschemaPubids, puboid))
    > + if (list_member_oid(aschemaPubids, puboid) &&
    > + !list_member_oid(except_pubids, puboid))
    >
    > Is this new code in the right place? I'm not 100% sure of the
    > 'except_pubids' details, but shouldn't it be checked sooner? e.g.  if
    > we know already that this pubid is no good
    > (!list_member_oid(except_pubids, puboid)) then what is the point to
    > even assign/check aschemaPubids?
    >
    
    except_pubids represents the set of publication OIDs from which the
    root relation has been explicitly excluded via EXCEPT (TABLE ...).
    But yes, we can check it before computing schemaPubids. Fixed.
    
    > ~~~
    >
    > 3.
    > + if (is_except)
    > + ereport(ERROR,
    > + (errcode(ERRCODE_DUPLICATE_OBJECT),
    > + errmsg("table \"%s\" cannot be added because it is excluded from
    > publication \"%s\"",
    > + RelationGetQualifiedRelationName(targetrel),
    > + pub->name)));
    > + else
    > + ereport(ERROR,
    > + (errcode(ERRCODE_DUPLICATE_OBJECT),
    > + errmsg("relation \"%s\" is already member of publication \"%s\"",
    > + RelationGetRelationName(targetrel), pub->name)));
    >
    > Fully qualified 'targetrel' in the first error, but not in the second? Is it OK?
    >
    
    IMO, we should use fully qualified names in both cases. Though the
    second error is not part of this patch, I’ve updated it as well.
    Please let me know if you think otherwise.
    
    > ======
    > src/backend/replication/pgoutput/pgoutput.c
    >
    > get_rel_sync_entry:
    >
    ...
    >
    > 5b.
    > Actually, this is becoming a GENERAL comment. There too many ways that
    > these EXCEPT tables are getting named, and it is causing confusion:
    > - except_pubids
    > - exceptlist
    > - exceptrelations
    > - exceptrels
    > - except_relid
    > - except_tables
    > - schemaExceptPubids
    >
    > Can we standardize on some common names, to make all the code more consistent?
    >
    
    I have now used the "except_" prefix consistently for all names to
    avoid confusion. Also updated the naming in all places as below:
    except_rel_names: parse-level table names in EXCEPT
    except_rels: internal relations in the EXCEPT list (opened/accessed)
    except_relids: list of relation OIDs wherever used
    except_pubids: publication OIDs from which a given relation (or its
    root) is excluded
    
    > ~
    >
    > 5c.
    > Previously, the result of 'get_partition_ancestors' was being freed,
    > but now it is not. I'm not sure how importatnt that is, because I
    > found other examples in PG source code also not freeing...
    >
    
    Sorry, this was mistakenly removed in v6 while cleaning up the code,
    and I did not verify it against the older version. Fixed now.
    
    > ======
    > src/bin/psql/tab-complete.in.c
    >
    > 6.
    > + else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES",
    > "IN", "SCHEMA", MatchAny, "EXCEPT", "(", "TABLE", MatchAnyN) &&
    > ends_with(prev_wd, ','))
    > + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
    >
    > I'm not sure if this is working as intended.
    >
    > When testing for multiple except tables I get results like:
    > ----
    > test_pub=# create publication pub1 for tables IN SCHEMA myschema <TAB>
    > EXCEPT ( TABLE  WITH (
    > test_pub=# create publication pub1 for tables IN SCHEMA myschema
    > except ( table <TAB>
    > test_pub=# create publication pub1 for tables IN SCHEMA myschema
    > except ( table myschema.t<TAB>
    > myschema.t1  myschema.t2  myschema.t3
    > test_pub=# create publication pub1 for tables IN SCHEMA myschema
    > except ( table myschema.t1,<TAB>
    > information_schema.  myschema.            public.              t1
    >              t2                   t3
    > ----
    >
    > Note: it is offering suggstions for schema names outside of the
    > "myschema". Should this code be calling
    > Query_for_list_of_tables_in_schema instead of
    > Query_for_list_of_tables?
    >
    
    For this case, I don't think it's really possible to keep suggesting
    after a comma. Even if we handle a fixed number of comma-separated
    entries, the same problem reappears with the next entry.
    Query_for_list_of_tables_in_schema needs a correct schema reference,
    but with each comma the relative offset changes, so there is no single
    fixed prev*_wd that can reliably point to the schema across all
    entries.
    
    But I see, the current call to Query_for_list_of_tables is clearly
    incorrect here. I have now suppressed suggestions after a comma,
    instead of showing incorrect suggestions.
    Thoughts?
    
    --
    Thanks,
    Nisha
    
    
    
    
  39. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-28T11:28:10Z

    On Fri, May 22, 2026 at 11:00 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Here are some review comments for patch v6-0002.
    >
    
    Thanks for the review. All comments are addressed in v7. Please find
    responses below for a few of the comments.
    
    >
    > EXCEPT
    >
    > 5.
    > +   <varlistentry>
    > +    <term><literal>EXCEPT ( <replaceable
    > class="parameter">except_table_object</replaceable> [, ... ]
    > )</literal></term>
    > +    <listitem>
    > +     <para>
    > +      Specifies tables to be excluded from a schema-level publication entry.
    > +      This clause may be used with <literal>ADD TABLES IN SCHEMA</literal>
    > +      and not with <literal>DROP TABLES IN SCHEMA</literal>.  Each named
    > +      table must belong to the schema specified in the same
    > +      <literal>TABLES IN SCHEMA</literal> clause.  Table names may be
    > +      schema-qualified or unqualified; unqualified names are implicitly
    > +      qualified with the schema named in the same clause.  See
    > +      <xref linkend="sql-createpublication"/> for further details on the
    > +      semantics of <literal>EXCEPT</literal>.
    > +     </para>
    > +    </listitem>
    > +   </varlistentry>
    > +
    >
    > 5a.
    > Oh! If this EXCEPT part was previously missing even for the "FOR ALL
    > TABLES", then IMO that is a separate bug that should be in another
    > thread and patched/fixed asap, then your patch should just make small
    > changes to to it.
    >
    
    Agree. I'll make a separate patch for it and start a thread.
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > AlterPublicationSchemas:
    >
    > 7.
    > + /*
    > + * Increment the command counter so that is_schema_publication() in
    > + * GetExcludedPublicationTables() can see the just-inserted schema
    > + * rows when AlterPublicationExceptTables runs next.
    > + */
    > + CommandCounterIncrement();
    >
    > Should this cut/paste common code be done afterwards just if
    > stmt->action == AP_AddObjects/SetObjects ?
    >
    
    Agree, fixed.
    
    > ~~~
    >
    > AlterPublicationExceptTables:
    >
    > 8.
    > + /*
    > + * This function handles EXCEPT entries for schema-level publications
    > + * only.  For FOR ALL TABLES publications, EXCEPT entries are already
    > + * processed by AlterPublicationTables().
    > + */
    > + if (schemaidlist == NIL && !is_schema_publication(pubid))
    > + return;
    >
    > Huh? It seems contrary to the function comment that was also talking
    > about "FOR ALL TABLES".
    >
    
    Corrected the function comments. It is for only schema publications.
    
    > Should this function really be called something different like
    > 'AlterPublicationSchemaExceptTables'?
    >
    
    Done. Renamed as suggested.
    
    > ~~~
    >
    > 12.
    > + /*
    > + * For FOR ALL TABLES, EXCEPT entries are processed by
    > + * AlterPublicationTables(), so merge them in.  For TABLES IN SCHEMA,
    > + * they are handled separately by AlterPublicationExceptTables().
    > + */
    > + if (stmt->for_all_tables)
    > + relations = list_concat(relations, exceptrelations);
    >   AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext,
    >      schemaidlist != NIL);
    >   AlterPublicationSchemas(stmt, tup, schemaidlist);
    > + AlterPublicationExceptTables(stmt, tup, exceptrelations, schemaidlist);
    >
    > Would it be simpler if AlterPublicationExceptTables was merged (or
    > delegated from) the AlterPublicationSchemas?
    >
    
    +1, fixed.
    
    --
    Thanks,
    Nisha
    
    
    
    
  40. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-28T11:28:31Z

    On Tue, May 26, 2026 at 11:27 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for patch v6-0003.
    >
    
    Thanks for the review. All comments are addressed in v7. Please find
    responses below for a few of the comments.
    
    >
    > 3.
    >     The <literal>EXCEPT</literal> clause can be used with
    > -   <literal>ADD TABLES IN SCHEMA</literal> to exclude specific tables from a
    > +   <literal>ADD TABLES IN SCHEMA</literal> and
    > +   <literal>SET TABLES IN SCHEMA</literal> to exclude specific tables from a
    >     schema-level publication. <literal>EXCEPT</literal> is not supported with
    >     <literal>DROP TABLES IN SCHEMA</literal>; instead, dropping a schema from
    >     the publication automatically removes all of its associated
    >
    > 3a.
    > This whole description section seems arranged in a confusing way IMO.
    > Anyway, it is not all the fault of the current patch. But I don't
    > think it should be talking about "SET TABLES IN SCHEMA" here because
    > that was all mentioned already in the earlier "third variant"
    > paragraph.
    >
    
    Right. it seems repeating. Removed "SET TABLES IN SCHEMA" related info.
    
    > ~
    >
    > 3b.
    > That last sentence all about EXCEPT with DROP TABLES IN SCHEMA seems
    > redundant to me. It is not allowed by the synopsis, so there is no
    > possible confusion about it being supported. Isn't it better to just
    > say nothing?
    >
    
    Okay, that makes sense. Fixed.
    
    > ~~~
    >
    > 4b.
    > This description about EXCEPT is missing talking about FOR ALL TABLES
    > EXCEPT, but IIRC I already reported that in a previous review.
    >
    
    Yes, we can handle this in a separate patch.
    
    > ~~~
    >
    > PublicationDropSchemas:
    >
    > 12.
    > + /*
    > + * Collect prexcept rows for tables belonging to this schema before
    > + * removing the schema entry.  GetExcludedPublicationTables relies on
    > + * is_schema_publication(), which scans pg_publication_namespace; if
    > + * this is the last schema in the publication, performDeletion() below
    > + * would remove that row and make is_schema_publication() return
    > + * false, tripping the assertion.
    > + */
    >
    > What assertion?
    >
    
    The assertion is Assert(GetPublication(pubid)->alltables ||
    is_schema_publication(pubid)) in GetExcludedPublicationTables().
    I’ve trimmed the comment a bit, as it felt slightly over-explained.
    ~~~~
    
    Please find the updated patch-set v7 attached.
    
    --
    Thanks,
    Nisha
    
  41. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-05-29T05:31:01Z

    Hi Nisha.
    
    Some review comments for patch v7-0001.
    
    ======
    src/backend/catalog/pg_publication.c
    
    GetTopMostAncestorInPublication:
    
    1.
    - else
    + else if (!list_member_oid(except_pubids, puboid))
      {
      aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    
    IIUC this `except_pubids` and `puboid` are not changing, so you do not
    need to be doing this member check every loop iteration. Is it better
    to assign some variable up-front?
    
    e.g.
    bool check_schemas = !list_member_oid(except_pubids, puboid);
    
    ~~~
    
    publication_add_relation:
    
    2.
    + if (is_except)
    + ereport(ERROR,
    + (errcode(ERRCODE_DUPLICATE_OBJECT),
    + errmsg("relation \"%s\" cannot be added because it is excluded from
    publication \"%s\"",
    + RelationGetQualifiedRelationName(targetrel),
    + pub->name)));
    
    I am unsure about the changed wording from "table" to "relation". IIUC
    we say "relation" when it could be either a table or a sequence. So
    maybe "table" is correct for your patch;l OTHOH this should change to
    "relation" by Shlok's EXCEPT SEQUENCE patch [1].
    
    ~~~
    
    3.
    + ereport(ERROR,
    + (errcode(ERRCODE_DUPLICATE_OBJECT),
    + errmsg("relation \"%s\" is already member of publication \"%s\"",
    + RelationGetQualifiedRelationName(targetrel), pub->name)));
    
    IMO making everything fully qualified like this would be a good
    change, but doing it here perhaps does not belong in your patch. I
    have resurrected this question in the other thread [2], which would
    affect not only this statement. but many others. Please post your
    opinion about this on that other thread.
    
    ======
    src/backend/commands/publicationcmds.c
    
    ObjectsInPublicationToOids:
    
    4.
     static void
     ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
    -    List **rels, List **exceptrels, List **schemas)
    +    List **rels, List **except_rel_names, List **schemas)
    
    Is `except_rel_names` an accurate name? IMO it makes it sound like
    it's a list of char* names, but IIUC that is not the case; aren't
    these PublicationTable objects? Would something like
    `except_pubtables` be more correct?
    
    ~~~
    
    CreatePublication:
    
    5.
    - List    *exceptrelations = NIL;
    + List    *except_rel_names = NIL;
    
    Same doubts about this `except_rel_names` variable name.
    
    ~~~
    
    AlterPublication:
    6.
      List    *relations = NIL;
    - List    *exceptrelations = NIL;
    + List    *except_rel_names = NIL;
    
    Same doubts about this `except_rel_names` variable name.
    
    ======
    src/backend/replication/pgoutput/pgoutput.c
    
    get_rel_sync_entry:
    
    7.
    + if (am_partition)
    + {
    + List    *part_ancestors = get_partition_ancestors(relid);
    +
    + root_relid = llast_oid(part_ancestors);
    + list_free(part_ancestors);
    
    I think just call this `ancestors` (not `part_ancestors`) for
    consistency with other code in the same function.
    
    ======
    src/bin/psql/tab-complete.in.c
    
    On Thu, May 28, 2026 at 9:27 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    >  On Fri, May 22, 2026 at 7:57 AM Peter Smith <smithpb2250@gmail.com> wrote:
    ...
    > > 6.
    > > + else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES",
    > > "IN", "SCHEMA", MatchAny, "EXCEPT", "(", "TABLE", MatchAnyN) &&
    > > ends_with(prev_wd, ','))
    > > + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
    > >
    > > I'm not sure if this is working as intended.
    > >
    > > When testing for multiple except tables I get results like:
    > > ----
    > > test_pub=# create publication pub1 for tables IN SCHEMA myschema <TAB>
    > > EXCEPT ( TABLE  WITH (
    > > test_pub=# create publication pub1 for tables IN SCHEMA myschema
    > > except ( table <TAB>
    > > test_pub=# create publication pub1 for tables IN SCHEMA myschema
    > > except ( table myschema.t<TAB>
    > > myschema.t1  myschema.t2  myschema.t3
    > > test_pub=# create publication pub1 for tables IN SCHEMA myschema
    > > except ( table myschema.t1,<TAB>
    > > information_schema.  myschema.            public.              t1
    > >              t2                   t3
    > > ----
    > >
    > > Note: it is offering suggstions for schema names outside of the
    > > "myschema". Should this code be calling
    > > Query_for_list_of_tables_in_schema instead of
    > > Query_for_list_of_tables?
    > >
    >
    > For this case, I don't think it's really possible to keep suggesting
    > after a comma. Even if we handle a fixed number of comma-separated
    > entries, the same problem reappears with the next entry.
    > Query_for_list_of_tables_in_schema needs a correct schema reference,
    > but with each comma the relative offset changes, so there is no single
    > fixed prev*_wd that can reliably point to the schema across all
    > entries.
    >
    > But I see, the current call to Query_for_list_of_tables is clearly
    > incorrect here. I have now suppressed suggestions after a comma,
    > instead of showing incorrect suggestions.
    > Thoughts?
    >
    
    8.
    REPLY: Yeah, I don't have any good ideas how to fix this, or if a fix
    is even possible, but I agree that doing nothing is better than doing
    the wrong thing.
    
    ~~~
    
    9.
    BTW, the current code is not able to handle multiple schemas.
    
    So, this works:
    test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA myschema <TAB>
    EXCEPT ( TABLE  WITH (
    
    but, this doesn't do anything:
    test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA public, myschema <TAB>
    
    ======
    [1] Shlok EXCEPT -
    https://www.postgresql.org/message-id/flat/CAHut%2BPsUrYmbZ996ZybjMWvpW_ufXB8WM94pdvAPyzQpoe%2BHRA%40mail.gmail.com#579d9b99c4f620602085cc59ff0e2b7d
    [2] schema-qualified messages -
    https://www.postgresql.org/message-id/CAHut%2BPvWoOyLKFb627Ch%2BXg3TYHuHdaOZ_XmxYgKVYdOzpqFsw%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  42. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-05-29T08:24:16Z

    Hi Nisha.
    
    Some review comments for patch v7-0002.
    
    ======
    src/backend/commands/publicationcmds.c
    
    1.
    +static void AlterPublicationSchemas(AlterPublicationStmt *stmt,
    + HeapTuple tup, List *schemaidlist,
    + List *except_rel_names);
    +static void AlterPublicationSchemaExceptTables(AlterPublicationStmt *stmt,
    +    HeapTuple tup,
    +    List *except_rel_names,
    +    List *schemaidlist);
    
    Maybe the same doubts about the `except_rel_names` parameter/variable
    are continued into this patch. See patch 0001 where I first queried
    this.
    
    ======
    src/bin/pg_dump/t/002_pg_dump.pl
    
    2.
    + 'CREATE PUBLICATION pub12' => {
    + create_order => 50,
    + create_sql =>
    +   'CREATE PUBLICATION pub12 FOR TABLES IN SCHEMA dump_test EXCEPT
    (TABLE test_table, dump_test.test_second_table);',
    + regexp => qr/^
    + \QCREATE PUBLICATION pub12 WITH (publish = 'insert, update, delete,
    truncate');\E
    + /xm,
    + like => { %full_runs, section_post_data => 1, },
    + },
    +
    + 'ALTER PUBLICATION pub12 ADD TABLES IN SCHEMA dump_test EXCEPT
    (TABLE test_table, dump_test.test_second_table)'
    +   => {
    + regexp => qr/^
    + \QALTER PUBLICATION pub12 ADD TABLES IN SCHEMA dump_test EXCEPT
    (TABLE ONLY test_table, TABLE ONLY test_second_table);\E
    + /xm,
    + like => { %full_runs, section_post_data => 1, },
    +   },
    
    I found those hard to read at first. How about just changing the test
    title of the ALTER parts
    
    BEFORE
    + 'ALTER PUBLICATION pub12 ADD TABLES IN SCHEMA dump_test EXCEPT
    (TABLE test_table, dump_test.test_second_table)'
    SUGGESTION
    + 'CREATE PUBLICATION pub12 test continues ...'
    
    (2 places like this)
    
    ======
    src/test/regress/expected/publication.out
    
    3.
    +-- DROP TABLES IN SCHEMA also removes associated EXCEPT entries
    +ALTER PUBLICATION testpub_alter_except DROP TABLES IN SCHEMA pub_test;
    +\dRp+ testpub_alter_except
    +                                                       Publication
    testpub_alter_except
    +          Owner           | All tables | All sequences | Inserts |
    Updates | Deletes | Truncates | Generated columns | Via root |
    Description
    +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    + regress_publication_user | f          | f             | t       | t
         | t       | t         | none              | f        |
    +Except tables:
    +    "pub_test.testpub_tbl_s1"
    +
    
    Isn't this showing a BUG, because after the DROP the "Except tables"
    are still listed.
    
    ~~~
    
    4.
    +-- ADD: unqualified name is implicitly qualified with the schema, not public
    +ALTER PUBLICATION testpub_alter_except ADD TABLES IN SCHEMA pub_test
    EXCEPT (TABLE testpub_tbl_s2);
    +\dRp+ testpub_alter_except
    +                                                       Publication
    testpub_alter_except
    +          Owner           | All tables | All sequences | Inserts |
    Updates | Deletes | Truncates | Generated columns | Via root |
    Description
    +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    + regress_publication_user | f          | f             | t       | t
         | t       | t         | none              | f        |
    +Tables from schemas:
    +    "pub_test"
    +Except tables:
    +    "pub_test.testpub_tbl_s1"
    +    "pub_test.testpub_tbl_s2"
    +
    
    Isn't this showing the same BUG as the previous test, because "s1" is
    still in the "Except tables" list, even though it was not part of the
    EXCEPT.
    
    ~~~
    
    5.
    +-- ADD: multiple excepted tables using unqualified names
    +ALTER PUBLICATION testpub_alter_except DROP TABLES IN SCHEMA pub_test;
    +ALTER PUBLICATION testpub_alter_except ADD TABLES IN SCHEMA pub_test
    EXCEPT (TABLE testpub_tbl_s1, testpub_tbl_s2);
    +ERROR:  relation "pub_test.testpub_tbl_s1" cannot be added because it
    is excluded from publication "testpub_alter_except"
    
    Isn't this showing the same BUG again, because "s1" was already in the
    EXCEPT list when it should not be.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  43. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-30T04:32:23Z

    On Fri, May 29, 2026 at 11:01 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for patch v7-0001.
    >
    
    Thanks for the review.
    
    >
    > publication_add_relation:
    >
    > 2.
    > + if (is_except)
    > + ereport(ERROR,
    > + (errcode(ERRCODE_DUPLICATE_OBJECT),
    > + errmsg("relation \"%s\" cannot be added because it is excluded from
    > publication \"%s\"",
    > + RelationGetQualifiedRelationName(targetrel),
    > + pub->name)));
    >
    > I am unsure about the changed wording from "table" to "relation". IIUC
    > we say "relation" when it could be either a table or a sequence. So
    > maybe "table" is correct for your patch;l OTHOH this should change to
    > "relation" by Shlok's EXCEPT SEQUENCE patch [1].
    >
    
    Okay, makes sense, changed back to "table".
    
    > ~~~
    >
    > 3.
    > + ereport(ERROR,
    > + (errcode(ERRCODE_DUPLICATE_OBJECT),
    > + errmsg("relation \"%s\" is already member of publication \"%s\"",
    > + RelationGetQualifiedRelationName(targetrel), pub->name)));
    >
    > IMO making everything fully qualified like this would be a good
    > change, but doing it here perhaps does not belong in your patch. I
    > have resurrected this question in the other thread [2], which would
    > affect not only this statement. but many others. Please post your
    > opinion about this on that other thread.
    >
    
    That is fine with me. I've reverted the change from my patch.
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > ObjectsInPublicationToOids:
    >
    > 4.
    >  static void
    >  ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
    > -    List **rels, List **exceptrels, List **schemas)
    > +    List **rels, List **except_rel_names, List **schemas)
    >
    > Is `except_rel_names` an accurate name? IMO it makes it sound like
    > it's a list of char* names, but IIUC that is not the case; aren't
    > these PublicationTable objects? Would something like
    > `except_pubtables` be more correct?
    >
    
    Yes these are PublicationTable objects, changed the name as sugegsted.
    
    > ~~~
    >
    > 9.
    > BTW, the current code is not able to handle multiple schemas.
    >
    > So, this works:
    > test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA myschema <TAB>
    > EXCEPT ( TABLE  WITH (
    >
    > but, this doesn't do anything:
    > test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA public, myschema <TAB>
    >
    
    I think the above preserves the existing behavior. Currently, we do
    not suggest "WITH (" after the second schema onwards. To support this
    properly, we would also need to handle "WITH (" suggestions for
    subsequent schema entries.
    
    I’ve created a top-up patch (patch-002) for this. I can merge it if we
    want to change the current behavior. Let me know your thoughts.
    ~~~~
    
    Attached is the updated v8 patch set.
    Addressed all of the above comments, along with the patch v7-0002
    comments from [1]. Patch-0003 has also been updated accordingly.
    
    [1] https://www.postgresql.org/message-id/CAHut%2BPuhL7Xj8UAK0yBmbbDsCC9xvRVmreCC_yxr%2BbMfc-dt5g%40mail.gmail.com
    
    --
    Thanks,
    Nisha
    
  44. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-30T04:32:41Z

    On Fri, May 29, 2026 at 1:54 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for patch v7-0002.
    >
    
    Thanks for the review. All comments are addressed in v8. Please find
    responses below for a few of the comments.
    
    > ======
    > src/bin/pg_dump/t/002_pg_dump.pl
    >
    > 2.
    > + 'CREATE PUBLICATION pub12' => {
    > + create_order => 50,
    > + create_sql =>
    > +   'CREATE PUBLICATION pub12 FOR TABLES IN SCHEMA dump_test EXCEPT
    > (TABLE test_table, dump_test.test_second_table);',
    > + regexp => qr/^
    > + \QCREATE PUBLICATION pub12 WITH (publish = 'insert, update, delete,
    > truncate');\E
    > + /xm,
    > + like => { %full_runs, section_post_data => 1, },
    > + },
    > +
    > + 'ALTER PUBLICATION pub12 ADD TABLES IN SCHEMA dump_test EXCEPT
    > (TABLE test_table, dump_test.test_second_table)'
    > +   => {
    > + regexp => qr/^
    > + \QALTER PUBLICATION pub12 ADD TABLES IN SCHEMA dump_test EXCEPT
    > (TABLE ONLY test_table, TABLE ONLY test_second_table);\E
    > + /xm,
    > + like => { %full_runs, section_post_data => 1, },
    > +   },
    >
    > I found those hard to read at first. How about just changing the test
    > title of the ALTER parts
    >
    > BEFORE
    > + 'ALTER PUBLICATION pub12 ADD TABLES IN SCHEMA dump_test EXCEPT
    > (TABLE test_table, dump_test.test_second_table)'
    > SUGGESTION
    > + 'CREATE PUBLICATION pub12 test continues ...'
    >
    > (2 places like this)
    >
    
    I don't see any existing "..test continues..." pattern, so I changed it as -
    'CREATE PUBLICATION pub11 - ADD TABLES IN SCHEMA EXCEPT dump'
    
    Thoughts?
    
    > ======
    > src/test/regress/expected/publication.out
    >
    > 3.
    > +-- DROP TABLES IN SCHEMA also removes associated EXCEPT entries
    > +ALTER PUBLICATION testpub_alter_except DROP TABLES IN SCHEMA pub_test;
    > +\dRp+ testpub_alter_except
    > +                                                       Publication
    > testpub_alter_except
    > +          Owner           | All tables | All sequences | Inserts |
    > Updates | Deletes | Truncates | Generated columns | Via root |
    > Description
    > +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > + regress_publication_user | f          | f             | t       | t
    >      | t       | t         | none              | f        |
    > +Except tables:
    > +    "pub_test.testpub_tbl_s1"
    > +
    >
    > Isn't this showing a BUG, because after the DROP the "Except tables"
    > are still listed.
    >
    
    DROP handling is part of Patch-0003, so the DROP-related tests belong
    there. I had added the test here only to verify the ADD scenarios, but
    I agree that it makes the coverage confusing and incorrect in its
    current placement.
    
    I’ve now corrected the tests accordingly.
    
    --
    Thanks,
    Nisha
    
    
    
    
  45. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-01T07:24:07Z

    Hi Nisha.
    
    Review comments for v8-0001 and v8-0002.
    
    ======
    git apply gives warnings.
    
    1.
    git apply ../patches_misc/v8-0001-Support-EXCEPT-clause-for-schema-level-publicatio.patch
    ../patches_misc/v8-0001-Support-EXCEPT-clause-for-schema-level-publicatio.patch:176:
    space before tab in indent.
                                             errmsg("relation \"%s\" is
    already member of publication \"%s\"",
    warning: 1 line adds whitespace errors.
    
    ======
    src/bin/psql/tab-complete.in.c
    
    On Sat, May 30, 2026 at 2:32 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    ...
    > > 9.
    > > BTW, the current code is not able to handle multiple schemas.
    > >
    > > So, this works:
    > > test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA myschema <TAB>
    > > EXCEPT ( TABLE  WITH (
    > >
    > > but, this doesn't do anything:
    > > test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA public, myschema <TAB>
    > >
    >
    > I think the above preserves the existing behavior. Currently, we do
    > not suggest "WITH (" after the second schema onwards. To support this
    > properly, we would also need to handle "WITH (" suggestions for
    > subsequent schema entries.
    >
    > I’ve created a top-up patch (patch-002) for this. I can merge it if we
    > want to change the current behavior. Let me know your thoughts.
    
    2.
    Some scenarios are improved, but others do not work (either newly
    broken or maybe they have been?).
    
    TBH, I am unsure if the added complexity of patch 0002 was worth it. I
    am going to pass on this for now and wait for other opinions.
    
    e.g.
    
    Good: (suggests schemas to use)
    test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA
    CURRENT_SCHEMA      information_schema  myschema            public
    
    Bad: (does not suggest more schema to use)
    test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA public, <TAB HERE>
    
    Good: (completes names of known schema)
    test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA my <TAB HERE>
    becomes
    test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA myschema
    
    Bad: (does not complete names of known schema)
    test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA public, my <TAB HERE>
    
    Good: (suggest EXCEPT with single schema)
    test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA myschema
    EXCEPT ( TABLE  WITH (
    
    Good: (suggest EXCEPT with multi schema)
    test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA public, myschema
    EXCEPT ( TABLE  WITH (
    
    Bad: (doesn't work if the FOR TABLE precedes TABLES IN SCHEMA)
    test_pub=# CREATE PUBLICATION pub1 FOR TABLE mytab, TABLES IN <TAB HERE>
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  46. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-01T07:26:14Z

    Hi Nisha.
    
    Some review comments for patch v8-0003.
    
    ======
    src/backend/commands/publicationcmds.c
    
    AlterPublicationSchemaExceptTables:
    
    1.
    + /*
    + * EXCEPT is not meaningful with DROP: dropping a schema from a
    + * publication already removes all its except entries via cascade, and
    + * there is no sensible interpretation of "drop only the except entry but
    + * keep the schema".
    + */
    
    Is that backwards? I think you mean :
    
    SUGGESTION
    * Dropping a schema from a publication removes all its EXCEPT entries via
    * cascade. The concept of "drop all schema tables from the publication EXCEPT
    * these ones" is not supported.
    ======
    src/bin/pg_dump/t/002_pg_dump.pl
    
    2.
    On Sat, May 30, 2026 at 2:32 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    ...
    > I don't see any existing "..test continues..." pattern, so I changed it as -
    > 'CREATE PUBLICATION pub11 - ADD TABLES IN SCHEMA EXCEPT dump'
    >
    > Thoughts?
    
    I've since found that there is a way to combine multiple regex within
    a single test. Doing it like below is a cleaner way to write these
    multi-statement tests.
    
    SUGGESTION (note /xms instead of /xm)
        'CREATE PUBLICATION pub11' => {
            create_order => 50,
            create_sql =>
              'CREATE PUBLICATION pub11 FOR TABLES IN SCHEMA dump_test
    EXCEPT (TABLE test_table);',
            regexp => qr/^
                \QCREATE PUBLICATION pub11 WITH (publish = 'insert,
    update, delete, truncate');\E
                .*?
                \QALTER PUBLICATION pub11 ADD TABLES IN SCHEMA dump_test
    EXCEPT (TABLE ONLY test_table);\E
                /xms,
            like => { %full_runs, section_post_data => 1, },
        },
    
    (ditto for the pub12 test)
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  47. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-01T07:28:53Z

    Hi Nisha.
    
    Some review comments for patch v8-0004.
    
    ======
    src/backend/commands/publicationcmds.c
    
    AlterPublicationSchemaExceptTables:
    
    1.
    + /* Collect OIDs of the desired new EXCEPT list. */
    + foreach_ptr(PublicationRelInfo, pri, rels)
    + {
    + newexceptrelids = lappend_oid(newexceptrelids,
    +   RelationGetRelid(pri->relation));
    + }
    
    Block braces {} not needed.
    
    ~~~
    
    2.
    + if (!OidIsValid(proid))
    + continue; /* already gone */
    +
    + ObjectAddressSet(obj, PublicationRelRelationId, proid);
    + performDeletion(&obj, DROP_CASCADE, 0);
    
    SUGGESTION
    if (OidIsValid(proid))
    {
      ObjectAddressSet(obj, PublicationRelRelationId, proid);
      performDeletion(&obj, DROP_CASCADE, 0);
    }
    
    ======
    src/test/subscription/t/037_except.pl
    
    3.
    I think you had used the SQL exactly as I previously suggested, but I
    made a mistake:
    It should say "SELECT count(*)" instead of "SELECT a".
    
    So it returns either 0 or 1 row.
    
    e.g. #1
    $result =
      $node_subscriber->safe_psql('postgres',
        "SELECT count(*) FROM sch1.tab_excluded WHERE a = 7");
    is($result, qq(1),
        'ALTER ... SET TABLES IN SCHEMA EXCEPT: newly included table is replicated'
    );
    $result =
      $node_subscriber->safe_psql('postgres',
        "SELECT count(*) FROM sch1.tab_published WHERE a = 7");
    is($result, qq(0),
        'ALTER ... SET TABLES IN SCHEMA EXCEPT: now-excluded table is not
    replicated'
    );
    
    e.g. #2
    $result =
      $node_subscriber->safe_psql('postgres',
        "SELECT count(*) FROM sch1.tab_published WHERE a = 8");
    is($result, qq(1),
        'ALTER ... SET TABLES IN SCHEMA (no EXCEPT): tab_published
    replicated after except list cleared'
    );
    $result =
      $node_subscriber->safe_psql('postgres',
        "SELECT count(*) FROM sch1.tab_excluded WHERE a = 8");
    is($result, qq(1),
        'ALTER ... SET TABLES IN SCHEMA (no EXCEPT): tab_excluded
    replicated after except list cleared'
    );
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  48. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-02T08:56:18Z

    On Mon, Jun 1, 2026 at 12:54 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Review comments for v8-0001 and v8-0002.
    >
    
    Thanks for the review.
    
    > ======
    > git apply gives warnings.
    >
    > 1.
    > git apply ../patches_misc/v8-0001-Support-EXCEPT-clause-for-schema-level-publicatio.patch
    > ../patches_misc/v8-0001-Support-EXCEPT-clause-for-schema-level-publicatio.patch:176:
    > space before tab in indent.
    >                                          errmsg("relation \"%s\" is
    > already member of publication \"%s\"",
    > warning: 1 line adds whitespace errors.
    >
    > ======
    > src/bin/psql/tab-complete.in.c
    >
    > On Sat, May 30, 2026 at 2:32 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > ...
    > > > 9.
    > > > BTW, the current code is not able to handle multiple schemas.
    > > >
    > > > So, this works:
    > > > test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA myschema <TAB>
    > > > EXCEPT ( TABLE  WITH (
    > > >
    > > > but, this doesn't do anything:
    > > > test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA public, myschema <TAB>
    > > >
    > >
    > > I think the above preserves the existing behavior. Currently, we do
    > > not suggest "WITH (" after the second schema onwards. To support this
    > > properly, we would also need to handle "WITH (" suggestions for
    > > subsequent schema entries.
    > >
    > > I’ve created a top-up patch (patch-002) for this. I can merge it if we
    > > want to change the current behavior. Let me know your thoughts.
    >
    > 2.
    > Some scenarios are improved, but others do not work (either newly
    > broken or maybe they have been?).
    >
    
    I tested these scenarios on HEAD (without this patch), and most of
    them already exist today. (See inline below).
    
    > TBH, I am unsure if the added complexity of patch 0002 was worth it. I
    > am going to pass on this for now and wait for other opinions.
    >
    > e.g.
    >
    > Good: (suggests schemas to use)
    > test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA
    > CURRENT_SCHEMA      information_schema  myschema            public
    >
    
    Not introduced by this patch; it is existing behavior.
    
    > Bad: (does not suggest more schema to use)
    > test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA public, <TAB HERE>
    >
    
    Not introduced by this patch; it is existing behavior.
    
    > Good: (completes names of known schema)
    > test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA my <TAB HERE>
    > becomes
    > test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA myschema
    >
    
    Not introduced by this patch; it is existing behavior.
    
    > Bad: (does not complete names of known schema)
    > test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA public, my <TAB HERE>
    >
    
    Not introduced by this patch; it is existing behavior.
    
    > Good: (suggest EXCEPT with single schema)
    > test_pub=# CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA myschema
    > EXCEPT ( TABLE  WITH (
    >
    
    This behavior is introduced by v8-0001.
    
    > Good: (suggest EXCEPT with multi schema)
    > test_pub=# CREATE PUBLICATION pub1 for TABLES IN SCHEMA public, myschema
    > EXCEPT ( TABLE  WITH (
    >
    
    This behavior is introduced by v8-0002.
    
    > Bad: (doesn't work if the FOR TABLE precedes TABLES IN SCHEMA)
    > test_pub=# CREATE PUBLICATION pub1 FOR TABLE mytab, TABLES IN <TAB HERE>
    
    Not introduced by this patch; it is existing behavior.
    ~~~
    
    All of the above behavior is consistent with HEAD. Only the "EXCEPT
    (TABLE" suggestions are introduced by patches 0001 and 0002.
    
    I'm also not sure the added complexity is justified just for the
    "EXCEPT (TABLE" suggestion, especially since suggestions after commas
    are generally not supported in most existing cases.
    I'll drop patch-0002 for now and we can revisit it later if others
    have opinions on it.
    
    --
    Thanks,
    Nisha
    
    
    
    
  49. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-02T08:56:49Z

    On Mon, Jun 1, 2026 at 12:56 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for patch v8-0003.
    >
    
    Thanks for the
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > AlterPublicationSchemaExceptTables:
    >
    > 1.
    > + /*
    > + * EXCEPT is not meaningful with DROP: dropping a schema from a
    > + * publication already removes all its except entries via cascade, and
    > + * there is no sensible interpretation of "drop only the except entry but
    > + * keep the schema".
    > + */
    >
    > Is that backwards? I think you mean :
    >
    > SUGGESTION
    > * Dropping a schema from a publication removes all its EXCEPT entries via
    > * cascade. The concept of "drop all schema tables from the publication EXCEPT
    > * these ones" is not supported.
    
    Fixed as suggested in v9.
    
    > ======
    > src/bin/pg_dump/t/002_pg_dump.pl
    >
    > 2.
    > On Sat, May 30, 2026 at 2:32 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > ...
    > > I don't see any existing "..test continues..." pattern, so I changed it as -
    > > 'CREATE PUBLICATION pub11 - ADD TABLES IN SCHEMA EXCEPT dump'
    > >
    > > Thoughts?
    >
    > I've since found that there is a way to combine multiple regex within
    > a single test. Doing it like below is a cleaner way to write these
    > multi-statement tests.
    >
    > SUGGESTION (note /xms instead of /xm)
    >     'CREATE PUBLICATION pub11' => {
    >         create_order => 50,
    >         create_sql =>
    >           'CREATE PUBLICATION pub11 FOR TABLES IN SCHEMA dump_test
    > EXCEPT (TABLE test_table);',
    >         regexp => qr/^
    >             \QCREATE PUBLICATION pub11 WITH (publish = 'insert,
    > update, delete, truncate');\E
    >             .*?
    >             \QALTER PUBLICATION pub11 ADD TABLES IN SCHEMA dump_test
    > EXCEPT (TABLE ONLY test_table);\E
    >             /xms,
    >         like => { %full_runs, section_post_data => 1, },
    >     },
    >
    > (ditto for the pub12 test)
    >
    
    Thanks for the suggestion. I've updated the test accordingly in v9.
    
    --
    Thanks,
    Nisha
    
    
    
    
  50. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-02T08:57:24Z

    On Mon, Jun 1, 2026 at 12:59 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for patch v8-0004.
    >
    
    Thanks for the review.
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > AlterPublicationSchemaExceptTables:
    >
    > 1.
    > + /* Collect OIDs of the desired new EXCEPT list. */
    > + foreach_ptr(PublicationRelInfo, pri, rels)
    > + {
    > + newexceptrelids = lappend_oid(newexceptrelids,
    > +   RelationGetRelid(pri->relation));
    > + }
    >
    > Block braces {} not needed.
    >
    
    Fixed.
    
    > ~~~
    >
    > 2.
    > + if (!OidIsValid(proid))
    > + continue; /* already gone */
    > +
    > + ObjectAddressSet(obj, PublicationRelRelationId, proid);
    > + performDeletion(&obj, DROP_CASCADE, 0);
    >
    > SUGGESTION
    > if (OidIsValid(proid))
    > {
    >   ObjectAddressSet(obj, PublicationRelRelationId, proid);
    >   performDeletion(&obj, DROP_CASCADE, 0);
    > }
    >
    
    Fixed. A similar pattern was also used in PublicationDropSchemas(),
    and I have fixed that as well.
    
    > ======
    > src/test/subscription/t/037_except.pl
    >
    > 3.
    > I think you had used the SQL exactly as I previously suggested, but I
    > made a mistake:
    > It should say "SELECT count(*)" instead of "SELECT a".
    >
    > So it returns either 0 or 1 row.
    >
    > e.g. #1
    > $result =
    >   $node_subscriber->safe_psql('postgres',
    >     "SELECT count(*) FROM sch1.tab_excluded WHERE a = 7");
    > is($result, qq(1),
    >     'ALTER ... SET TABLES IN SCHEMA EXCEPT: newly included table is replicated'
    > );
    > $result =
    >   $node_subscriber->safe_psql('postgres',
    >     "SELECT count(*) FROM sch1.tab_published WHERE a = 7");
    > is($result, qq(0),
    >     'ALTER ... SET TABLES IN SCHEMA EXCEPT: now-excluded table is not
    > replicated'
    > );
    >
    > e.g. #2
    > $result =
    >   $node_subscriber->safe_psql('postgres',
    >     "SELECT count(*) FROM sch1.tab_published WHERE a = 8");
    > is($result, qq(1),
    >     'ALTER ... SET TABLES IN SCHEMA (no EXCEPT): tab_published
    > replicated after except list cleared'
    > );
    > $result =
    >   $node_subscriber->safe_psql('postgres',
    >     "SELECT count(*) FROM sch1.tab_excluded WHERE a = 8");
    > is($result, qq(1),
    >     'ALTER ... SET TABLES IN SCHEMA (no EXCEPT): tab_excluded
    > replicated after except list cleared'
    > );
    >
    
    Thanks for pointing that out; I overlooked your earlier suggestion.
    I've now updated as suggested.
    
    Attached is the updated v9 patch set.
    
    --
    Thanks,
    Nisha
    
  51. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Zsolt Parragi <zsolt.parragi@percona.com> — 2026-06-02T21:21:48Z

    Hello
    
    In this simple script:
    
    CREATE SCHEMA s;
    CREATE TABLE s.a(i int);
    CREATE TABLE s.b(i int);
    CREATE PUBLICATION p FOR TABLES IN SCHEMA s EXCEPT (TABLE s.b);
    
    SELECT * FROM pg_publication_tables WHERE pubname = 'p';
    SELECT * FROM pg_get_publication_tables(ARRAY['p'], 's.b'::regclass);
    
    Shouldn't the second select return an empty list? Currently it returns
    one row for s.b.
    
    And I think there's also a bug with CREATE PUBLICATION with IN SCHEMA
    CURRENT_SCHEMA:
    
    CREATE SCHEMA s1;
    CREATE TABLE s1.a(i int);
    CREATE TABLE public.x(i int);
    CREATE TABLE public.onlypub(i int);   -- exists only in public, not in s1
    
    CREATE PUBLICATION pn FOR TABLES IN SCHEMA s1 EXCEPT (TABLE public.x);
    -- error out, good
    SET search_path = s1, public;
    CREATE PUBLICATION pn2 FOR TABLES IN SCHEMA s1 EXCEPT (TABLE onlypub);
    -- error out, good
    SET search_path = s1, public;
    SELECT current_schema(); -- s1
    CREATE PUBLICATION pc FOR TABLES IN SCHEMA CURRENT_SCHEMA EXCEPT
    (TABLE public.x); -- doesn't error out
    CREATE PUBLICATION pc4 FOR TABLES IN SCHEMA CURRENT_SCHEMA EXCEPT
    (TABLE onlypub); -- also doesn't error out
    
    
    
    
  52. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-05T11:41:34Z

    On Wed, Jun 3, 2026 at 2:51 AM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
    >
    > Hello
    >
    
    Hi, thanks for the tests.
    
    > In this simple script:
    >
    > CREATE SCHEMA s;
    > CREATE TABLE s.a(i int);
    > CREATE TABLE s.b(i int);
    > CREATE PUBLICATION p FOR TABLES IN SCHEMA s EXCEPT (TABLE s.b);
    >
    > SELECT * FROM pg_publication_tables WHERE pubname = 'p';
    > SELECT * FROM pg_get_publication_tables(ARRAY['p'], 's.b'::regclass);
    >
    > Shouldn't the second select return an empty list? Currently it returns
    > one row for s.b.
    >
    
    Yes, it is a bug.
    The issue was that pg_get_publication_tables() could not distinguish
    between prexcept=true and prexcept=false.
    
    A reference note: This worked fine for FOR ALL TABLES publications
    because entries in pg_publication_rel always had prexcept=false, so
    the presence of an entry implied the table was not published. With
    non-ALL TABLES publications, a table can now have both prexcept=true
    (explicitly excluded) and prexcept=false (excluded from a schema
    publication).
    
    Fixed.
    
    > And I think there's also a bug with CREATE PUBLICATION with IN SCHEMA
    > CURRENT_SCHEMA:
    >
    > CREATE SCHEMA s1;
    > CREATE TABLE s1.a(i int);
    > CREATE TABLE public.x(i int);
    > CREATE TABLE public.onlypub(i int);   -- exists only in public, not in s1
    >
    > CREATE PUBLICATION pn FOR TABLES IN SCHEMA s1 EXCEPT (TABLE public.x);
    > -- error out, good
    > SET search_path = s1, public;
    > CREATE PUBLICATION pn2 FOR TABLES IN SCHEMA s1 EXCEPT (TABLE onlypub);
    > -- error out, good
    > SET search_path = s1, public;
    > SELECT current_schema(); -- s1
    > CREATE PUBLICATION pc FOR TABLES IN SCHEMA CURRENT_SCHEMA EXCEPT
    > (TABLE public.x); -- doesn't error out
    > CREATE PUBLICATION pc4 FOR TABLES IN SCHEMA CURRENT_SCHEMA EXCEPT
    > (TABLE onlypub); -- also doesn't error out
    >
    
    Yes, this is a bug. The CURRENT_SCHEMA case was missed when the patch
    was updated to allow only immediate schema tables in the EXCEPT
    clause.
    
    Attached are v10 patches with fixes for both issues. I also added the
    missing tab-completion for CURRENT_SCHEMA and added regression tests
    in publication.sql covering both scenarios.
    
    --
    Thanks,
    Nisha
    
  53. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Zsolt Parragi <zsolt.parragi@percona.com> — 2026-06-06T20:39:18Z

    Hello!
    
    Thanks, I can confirm the fixes work.
    
    I did some more testing. I think I see two problems with ALTER TABLE
    ... SET SCHEMA:
    
    1.
    
    CREATE SCHEMA s;
    CREATE SCHEMA other;
    CREATE TABLE s.t(i int);
    CREATE PUBLICATION p FOR TABLES IN SCHEMA s EXCEPT (TABLE s.t);
    ALTER TABLE s.t SET SCHEMA other;
    ALTER PUBLICATION p ADD TABLES IN SCHEMA other;
    -- shouldn't s.t be there?
    SELECT schemaname, tablename FROM pg_publication_tables WHERE
    pubname='p' ORDER BY 1,2;
    
    
    2.
    
    CREATE SCHEMA s;
    CREATE SCHEMA other;
    CREATE TABLE s.t(i int);
    CREATE PUBLICATION p FOR TABLES IN SCHEMA s EXCEPT (TABLE s.t);
    ALTER TABLE s.t SET SCHEMA other;
    ALTER PUBLICATION p DROP TABLES IN SCHEMA s;
    -- should it still be there? it isn't without the alter set schema
    SELECT pr.prrelid::regclass AS rel, pub.pubname, pr.prexcept
    FROM pg_publication_rel pr JOIN pg_publication pub ON pub.oid=pr.prpubid;
    
    
    
    
  54. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-09T16:21:04Z

     On Sun, Jun 7, 2026 at 2:09 AM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
    >
    > Hello!
    >
    > Thanks, I can confirm the fixes work.
    >
    > I did some more testing. I think I see two problems with ALTER TABLE
    > ... SET SCHEMA:
    >
    
    Thanks for reporting this case.
    
    The current patches did not handle schema changes for excluded tables.
    Hou-san also reported the same design issue off-list to me.
    
    After considering, I chose to follow behavior similar to existing FOR
    ALL TABLES publications to handle schema-switch cases. Today, if a
    table excluded via EXCEPT is dropped, the corresponding prexcept entry
    is removed, and recreating a table with the same name does not
    automatically restore the exclusion.
    
    I applied the same principle to schema changes: once an excluded table
    moves out of the schema, the exclusion is removed.
    
    For example, consider the following cases:
    CREATE PUBLICATION p FOR TABLES IN SCHEMA s1 EXCEPT (TABLE s1.t);
    
    case-1: Table moves from s1 to s2:  ALTER TABLE s1.t SET SCHEMA s2;
    -- The exclusion entry s1.t is removed.
    
    case-2: Table moves back from s2 to s1:  ALTER TABLE s2.t SET SCHEMA s1;
    -- The table is published again. The exclusion is not restored
    automatically; the user must specify it again.
    
    case-3: If s2 is also part of publication p
     -- The behavior remains the same. Moving the table between schemas
    does not recreate the exclusion entry.
    
    IOW, once the exclusion is broken by a schema move (similar to a
    drop), it must be re-established explicitly by the user.
    
    The attached patch implements this behavior. I've also updated the
    docs to describe it.
    
    I also considered two alternatives:
    1) Reject the schema change: Error out if a table with a prexcept
    entry is moved between schemas. This feels overly restrictive.
    2) Make exclusions schema-aware: Add a prexceptschema column and store
    the schema OID along with the exclusion entry. The exclusion would
    only apply while the table remains in that schema, allowing it to be
    restored automatically if the table moves back later. IMO it
    complicates the design.
    
    Thoughts?
    
    > 1.
    >
    > CREATE SCHEMA s;
    > CREATE SCHEMA other;
    > CREATE TABLE s.t(i int);
    > CREATE PUBLICATION p FOR TABLES IN SCHEMA s EXCEPT (TABLE s.t);
    > ALTER TABLE s.t SET SCHEMA other;
    > ALTER PUBLICATION p ADD TABLES IN SCHEMA other;
    > -- shouldn't s.t be there?
    > SELECT schemaname, tablename FROM pg_publication_tables WHERE
    > pubname='p' ORDER BY 1,2;
    >
    
    Do you mean other.t should be there?
    This is now fixed. When t is moved to schema other, the exclusion is
    removed and the table is published through p.
    
    postgres=# SELECT schemaname, tablename FROM pg_publication_tables
    WHERE pubname='p' ORDER BY 1,2;
     schemaname | tablename
    ------------+-----------
     other      | t
    
    >
    > 2.
    >
    > CREATE SCHEMA s;
    > CREATE SCHEMA other;
    > CREATE TABLE s.t(i int);
    > CREATE PUBLICATION p FOR TABLES IN SCHEMA s EXCEPT (TABLE s.t);
    > ALTER TABLE s.t SET SCHEMA other;
    > ALTER PUBLICATION p DROP TABLES IN SCHEMA s;
    > -- should it still be there? it isn't without the alter set schema
    > SELECT pr.prrelid::regclass AS rel, pub.pubname, pr.prexcept
    > FROM pg_publication_rel pr JOIN pg_publication pub ON pub.oid=pr.prpubid;
    >
    
    Fixed.
    Now, when s.t is moved out of schema s, it is removed from publication
    p's exclusion list. Later, if schema s is dropped from publication p,
    the exclusion entries associated with s are removed as well, and s.t
    is no longer present there.
    
    Attached are the updated v11 patches.
    Patch 001 has the changes discussed above; patches 002 and 003 are unchanged.
    
    --
    Thanks,
    Nisha
    
  55. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Zsolt Parragi <zsolt.parragi@percona.com> — 2026-06-09T21:36:13Z

    > Do you mean other.t should be there?
    
    Yes, that was a typo in my example.
    
    > After considering, I chose to follow behavior similar to existing FOR
    > ALL TABLES publications to handle schema-switch cases. Today, if a
    > table excluded via EXCEPT is dropped, the corresponding prexcept entry
    > is removed, and recreating a table with the same name does not
    > automatically restore the exclusion.
    >
    > I applied the same principle to schema changes: once an excluded table
    > moves out of the schema, the exclusion is removed.
    
    I'm not that sure about the analogy. DROP TABLE is a destructive
    operation, executing DROP TABLE and then CREATE TABLE won't
    automatically bring back the data.
    
    With this approach, two cheap ALTER TABLE ... SET SCHEMA statements
    can clear an EXCEPT clause without the proper permissions.
    
    But I'm not sure what's the best solution for this. The v11 approach
    is at least more consistent than the previous behavior.
    
    > 1) Reject the schema change: Error out if a table with a prexcept
    > entry is moved between schemas. This feels overly restrictive.
    
    It is restrictive, but maybe it's the better solution? Or
    alternatively, maybe it should require proper permissions to remove
    the except clause?
    
    Another thing that could improve this if we would print out a warning
    that the statement caused a change in the publication? But then that's
    also a question for the preexisting drop table case.
    
    
    
    
  56. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-10T04:36:44Z

    Hi Nisha.
    
    Some review comments for v11-0001.
    
    (I had no new review comments for v11-0002, v11-0003)
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    1.
    +     <para>
    +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    +      <literal>EXCEPT</literal> clause is schema-scoped: the exclusion applies
    +      only in the context of the schema to which it was attached.  If a table
    +      listed in the <literal>EXCEPT</literal> clause is later moved to a
    +      different schema using <command>ALTER TABLE ... SET SCHEMA</command>,
    +      the exclusion is removed.  The table will then be published if its new
    +      schema is part of a publication.  If the table is moved back to
    +      the original schema, the exclusion is not restored; the user must
    +      re-establish it explicitly using <command>ALTER PUBLICATION</command>.
    +      Dropping a table always removes it from the <literal>EXCEPT</literal>
    +      list regardless of publication type.
    +     </para>
    
    1a.
    I felt this should be moved up to be the 2nd paragraph of the "EXCEPT"
    part. Subsequent information about
    inheritance/partitions/multi-publications is common for both EXCEPTS.
    
    ~
    
    1b.
    All that info about "If a table..." seemed more relevant to ALTER
    PUBLICATION than to CREATE PUBLICATION, so I didn't think we needed
    those details here.
    
    ======
    src/backend/commands/publicationcmds.c
    
    RemovePublicationExceptForRelation:
    
    2.
    +/*
    + * Remove any EXCEPT clause entries for a relation from schema publications.
    + * Called when a table changes schema (ALTER TABLE ... SET SCHEMA), so that
    + * a schema-scoped exclusion does not silently follow the table to its new
    + * schema.  FOR ALL TABLES publications are skipped because their EXCEPT
    + * clause is publication-scoped, not schema-scoped, so that exclusion should
    + * persist regardless of what schema the table is in.
    + */
    
    Instead of saying "FOR ALL TABLES publications are skipped", rephrase
    that to be something like: "This problem does not apply to FOR ALL
    TABLES publications because..."
    
    Anyway, I think you can remove that note from the function comment,
    and instead put it here:
    + if (!is_schema_publication(pubid))
    + continue;
    
    ~~~
    
    3.
    +{
    + List    *pubids;
    + ListCell   *lc;
    + ObjectAddress obj;
    +
    + pubids = GetRelationExcludedPublications(relid);
    +
    + foreach(lc, pubids)
    
    Using a `foreach_oid` loop might be tidier here.
    
    ======
    src/backend/commands/tablecmds.c
    
    4.
      table_close(classRel, RowExclusiveLock);
    +
    + /*
    + * Remove any EXCEPT clause entries for this relation from schema
    + * publications.  A schema-scoped exclusion is no longer meaningful once
    + * the table moves to a different schema.
    + */
    + if (rel->rd_rel->relkind == RELKIND_RELATION ||
    + rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    + RemovePublicationExceptForRelation(RelationGetRelid(rel));
    
    Should this code be put prior to the table_close, where the other
    dependent stuff is also removed?
    
    ======
    src/backend/parser/gram.y
    
    5.
    + /* For TABLES_IN_CUR_SCHEMA: leave except_tables for execution time */
    
    Isn't this repeating exactly what you already said in the other
    comment ("For TABLES_IN_CUR_SCHEMA the schema name is not yet
    known...")?
    
    ======
    src/test/regress/sql/publication.sql
    
    6.
    +-- test for EXCEPT clause with schema publication (bug: excluded
    table was incorrectly returned)
    +SELECT * FROM test_gpt(ARRAY['pub_schema_except'],
    'gpt_test_sch.tbl_sch'); -- no result (excluded)
    +SELECT * FROM test_gpt(ARRAY['pub_schema_except'],
    'gpt_test_sch.tbl_sch2'); -- one row (included via schema)
    +
    
    Is that "(bug: ...)" comment necessary?
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  57. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-10T09:03:04Z

    On Wed, Jun 10, 2026 at 3:06 AM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
    >
    > > Do you mean other.t should be there?
    >
    > Yes, that was a typo in my example.
    >
    > > After considering, I chose to follow behavior similar to existing FOR
    > > ALL TABLES publications to handle schema-switch cases. Today, if a
    > > table excluded via EXCEPT is dropped, the corresponding prexcept entry
    > > is removed, and recreating a table with the same name does not
    > > automatically restore the exclusion.
    > >
    > > I applied the same principle to schema changes: once an excluded table
    > > moves out of the schema, the exclusion is removed.
    >
    > I'm not that sure about the analogy. DROP TABLE is a destructive
    > operation, executing DROP TABLE and then CREATE TABLE won't
    > automatically bring back the data.
    >
    > With this approach, two cheap ALTER TABLE ... SET SCHEMA statements
    > can clear an EXCEPT clause without the proper permissions.
    >
    
    We already have similar behavior today. A non-publication owner can
    run ALTER TABLE ... SET SCHEMA and remove a table from a FOR TABLES IN
    SCHEMA publication without any warning or publication-level permission
    check.
    
    > But I'm not sure what's the best solution for this. The v11 approach
    > is at least more consistent than the previous behavior.
    >
    > > 1) Reject the schema change: Error out if a table with a prexcept
    > > entry is moved between schemas. This feels overly restrictive.
    >
    > It is restrictive, but maybe it's the better solution? Or
    > alternatively, maybe it should require proper permissions to remove
    > the except clause?
    >
    
    My concern is that introducing a permission check only for the EXCEPT
    case would create an inconsistency: one type of schema move affecting
    publication behavior would require publication ownership, while
    another would not.
    
    That said, I'm okay with restricting schema changes for tables in an
    EXCEPT list if others feel that's the right behavior. Let's wait for
    feedback from others.
    
    > Another thing that could improve this if we would print out a warning
    > that the statement caused a change in the publication? But then that's
    > also a question for the preexisting drop table case.
    >
    
    Right, As also mentioned above, ALTER TABLE changes that affect
    publication membership currently do not emit any notice or warning, so
    I'm not sure we need one here either.
    
    --
    Thanks,
    Nisha
    
    
    
    
  58. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-10T10:26:42Z

    On Wed, Jun 10, 2026 at 10:07 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for v11-0001.
    >
    > (I had no new review comments for v11-0002, v11-0003)
    >
    
    Thanks for the review. I've addressed all the comments as suggested
    and attached the updated v12 patch set.
    
    --
    Thanks,
    Nisha
    
  59. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Zsolt Parragi <zsolt.parragi@percona.com> — 2026-06-10T20:54:15Z

    > Let's wait for feedback from others.
    
    Yes, I think that's the best approach for this question.
    
    > Right, As also mentioned above, ALTER TABLE changes that affect
    > publication membership currently do not emit any notice or warning, so
    > I'm not sure we need one here either.
    
    My idea was to make this change both for existing cases and new cases
    in this patch, so it would be consistent, but that wasn't exactly
    clear in my previous email, sorry about that. Similarly to how cascade
    reports additional objects being dropped.
    
    
    
    
  60. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-11T06:47:17Z

    Hi Nisha.
    
    Some review comments for the v12 patches.
    
    //////
    v12-0001
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    1.
          <para>
           This clause specifies a list of tables to be excluded from the
    -      publication.
    +      publication. It can be used with <literal>FOR ALL TABLES</literal> or
    +      <literal>FOR TABLES IN SCHEMA</literal>.
    +     </para>
    +     <para>
    +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    +      <literal>EXCEPT</literal> clause is schema-scoped: the exclusion applies
    +      only within the schema to which it was attached.  For
    +      <literal>FOR ALL TABLES</literal> publications, the exclusion applies to
    +      the table regardless of the schema it is in.
          </para>
    
    For that 2nd para, I think it ought to avoid words like "attached"
    when talking about tables because attaching has a special meaning.
    
    Also, I don't really understand what the 2nd sentence is trying to
    say. FOR ALL TABLES exclusions will exclude the *named* tables. Yes,
    those named tables might be in any schema (by fully-qualifying the
    name), but that's not the same as saying "regardless of the schema".
    
    So, IMO we can throw away that 2nd sentence, and just keep the
    simplified 1st one. Since what remains is now very short, probably
    just combine both paragraphs.
    
    SUGGESTION
    This clause specifies a list of tables to be excluded from the
    publication. It can be used with <literal>FOR ALL TABLES</literal> or
    <literal>FOR TABLES IN SCHEMA</literal>. For <literal>FOR TABLES IN
    SCHEMA</literal>, the exclusion applies only to tables in the schema
    associated with the <literal>EXCEPT</literal>.
    
    ======
    src/backend/commands/publicationcmds.c
    
    RemovePublicationExceptForRelation:
    
    2.
    + if (OidIsValid(proid))
    + {
    + ObjectAddressSet(obj, PublicationRelRelationId, proid);
    + performDeletion(&obj, DROP_CASCADE, 0);
    + }
    
    Variable `obj` can be declared right here; it's the only place using it.
    
    //////////
    v12-0002
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    1.
    +     <para>
    +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    +      <literal>EXCEPT</literal> clause is schema-scoped.  If a table listed in
    +      the <literal>EXCEPT</literal> clause is later moved to a different schema
    +      using <command>ALTER TABLE ... SET SCHEMA</command>, the exclusion is
    +      removed; the table will then be published if its new schema is part of a
    +      publication.  If the table is subsequently moved back to the original
    +      schema, the exclusion is not restored, and must be re-established
    +      explicitly using <command>ALTER PUBLICATION</command>.  Dropping a table
    +      always removes it from the <literal>EXCEPT</literal> clause,
    regardless of
    +      publication type.
    +     </para>
    
    
    I think the sentence "If the table is subsequently moved back..." is
    overkill, and does not need to be said. The prior info "the exclusion
    is removed" already tells me the exclusion is gone, and I think is
    reasonable to assume "removed" means that it is gone for good, with no
    ambiguity that it might magically come back.
    
    YMMV. Leave it as-is if you prefer.
    
    //////////
    v12-0003
    
    No comments.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  61. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Amit Kapila <amit.kapila16@gmail.com> — 2026-06-15T06:08:16Z

    On Thu, Jun 11, 2026 at 2:24 AM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
    >
    > > Let's wait for feedback from others.
    >
    > Yes, I think that's the best approach for this question.
    >
    > > Right, As also mentioned above, ALTER TABLE changes that affect
    > > publication membership currently do not emit any notice or warning, so
    > > I'm not sure we need one here either.
    >
    > My idea was to make this change both for existing cases and new cases
    > in this patch, so it would be consistent, but that wasn't exactly
    > clear in my previous email, sorry about that. Similarly to how cascade
    > reports additional objects being dropped.
    >
    
    IIUC, the objects here we are talking about are removed because of
    their dependency type AUTO at least the existing case of 'drop table'.
    So, I think the current behavior suggested by Nisha sounds correct and
    consistent with the pre-existing 'drop table' case. Also, we display
    such case at DEBUG2 level, see following code in dependency.c:
    
    if (extra->flags & (DEPFLAG_AUTO |
    DEPFLAG_INTERNAL |
    DEPFLAG_PARTITION |
    DEPFLAG_EXTENSION))
    {
    /*
    * auto-cascades are reported at DEBUG2, not msglevel.  We don't
    * try to combine them with the regular message because the
    * results are too confusing when client_min_messages and
    * log_min_messages are different.
    */
    ereport(DEBUG2,
    (errmsg_internal("drop auto-cascades to %s",
    
    We can consider displaying such a message for schema cases, if not
    already there.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  62. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Amit Kapila <amit.kapila16@gmail.com> — 2026-06-15T06:19:57Z

    On Thu, Jun 11, 2026 at 12:17 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > //////////
    > v12-0002
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 1.
    > +     <para>
    > +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    > +      <literal>EXCEPT</literal> clause is schema-scoped.  If a table listed in
    > +      the <literal>EXCEPT</literal> clause is later moved to a different schema
    > +      using <command>ALTER TABLE ... SET SCHEMA</command>, the exclusion is
    > +      removed; the table will then be published if its new schema is part of a
    > +      publication.  If the table is subsequently moved back to the original
    > +      schema, the exclusion is not restored, and must be re-established
    > +      explicitly using <command>ALTER PUBLICATION</command>.  Dropping a table
    > +      always removes it from the <literal>EXCEPT</literal> clause,
    > regardless of
    > +      publication type.
    > +     </para>
    >
    >
    > I think the sentence "If the table is subsequently moved back..." is
    > overkill, and does not need to be said. The prior info "the exclusion
    > is removed" already tells me the exclusion is gone, and I think is
    > reasonable to assume "removed" means that it is gone for good, with no
    > ambiguity that it might magically come back.
    >
    > YMMV. Leave it as-is if you prefer.
    >
    
    I feel it is okay to keep the proposed sentence to avoid any ambiguity
    by the user to consider the schema-scope state is symmetric.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  63. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-15T11:30:42Z

    On Mon, Jun 15, 2026 at 11:38 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Thu, Jun 11, 2026 at 2:24 AM Zsolt Parragi <zsolt.parragi@percona.com> wrote:
    > >
    > > > Let's wait for feedback from others.
    > >
    > > Yes, I think that's the best approach for this question.
    > >
    > > > Right, As also mentioned above, ALTER TABLE changes that affect
    > > > publication membership currently do not emit any notice or warning, so
    > > > I'm not sure we need one here either.
    > >
    > > My idea was to make this change both for existing cases and new cases
    > > in this patch, so it would be consistent, but that wasn't exactly
    > > clear in my previous email, sorry about that. Similarly to how cascade
    > > reports additional objects being dropped.
    > >
    >
    > IIUC, the objects here we are talking about are removed because of
    > their dependency type AUTO at least the existing case of 'drop table'.
    > So, I think the current behavior suggested by Nisha sounds correct and
    > consistent with the pre-existing 'drop table' case. Also, we display
    > such case at DEBUG2 level, see following code in dependency.c:
    >
    > if (extra->flags & (DEPFLAG_AUTO |
    > DEPFLAG_INTERNAL |
    > DEPFLAG_PARTITION |
    > DEPFLAG_EXTENSION))
    > {
    > /*
    > * auto-cascades are reported at DEBUG2, not msglevel.  We don't
    > * try to combine them with the regular message because the
    > * results are too confusing when client_min_messages and
    > * log_min_messages are different.
    > */
    > ereport(DEBUG2,
    > (errmsg_internal("drop auto-cascades to %s",
    >
    > We can consider displaying such a message for schema cases, if not
    > already there.
    >
    
    The DROP TABLE case is handled automatically via dependency cascade,
    so we already get above DEBUG2 log. However, when a table is moved to
    a different schema, no such log is emitted because schema changes are
    outside the scope of dependency cascade processing.
    
    In v13, I added a DEBUG2 message when a table is removed from the
    exclusion list due to a schema change.
    
    --
    Thanks,
    Nisha
    
    
    
    
  64. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-15T11:30:50Z

    On Mon, Jun 15, 2026 at 11:50 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Thu, Jun 11, 2026 at 12:17 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > //////////
    > > v12-0002
    > >
    > > ======
    > > doc/src/sgml/ref/alter_publication.sgml
    > >
    > > 1.
    > > +     <para>
    > > +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    > > +      <literal>EXCEPT</literal> clause is schema-scoped.  If a table listed in
    > > +      the <literal>EXCEPT</literal> clause is later moved to a different schema
    > > +      using <command>ALTER TABLE ... SET SCHEMA</command>, the exclusion is
    > > +      removed; the table will then be published if its new schema is part of a
    > > +      publication.  If the table is subsequently moved back to the original
    > > +      schema, the exclusion is not restored, and must be re-established
    > > +      explicitly using <command>ALTER PUBLICATION</command>.  Dropping a table
    > > +      always removes it from the <literal>EXCEPT</literal> clause,
    > > regardless of
    > > +      publication type.
    > > +     </para>
    > >
    > >
    > > I think the sentence "If the table is subsequently moved back..." is
    > > overkill, and does not need to be said. The prior info "the exclusion
    > > is removed" already tells me the exclusion is gone, and I think is
    > > reasonable to assume "removed" means that it is gone for good, with no
    > > ambiguity that it might magically come back.
    > >
    > > YMMV. Leave it as-is if you prefer.
    > >
    >
    > I feel it is okay to keep the proposed sentence to avoid any ambiguity
    > by the user to consider the schema-scope state is symmetric.
    >
    
    Okay, I have kept this para as-it-is.
    
    The other comments on v12-0001 from [1] have also been addressed.
    
    Please find the updated v13 patches attached.
    
    [1] https://www.postgresql.org/message-id/CAHut%2BPv-GA1oGa6%2Bnwn_5AVhBg8NuJThQVUzqhQPXJge49jnew%40mail.gmail.com
    
    --
    Thanks,
    Nisha
    
  65. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Zsolt Parragi <zsolt.parragi@percona.com> — 2026-06-15T21:45:42Z

    > In v13, I added a DEBUG2 message when a table is removed from the
    > exclusion list due to a schema change.
    
    Thanks, the DEBUG2 message (and the v13 patch) looks good to me and
    this way it is consistent with the preexisting case. I have only a
    question about test coverage - should the tests include verification
    of the ALTER TABLE  ... SET SCHEMA behavior?
    
    
    
    
  66. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-16T01:21:51Z

    On Mon, Jun 15, 2026 at 9:31 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Mon, Jun 15, 2026 at 11:50 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Thu, Jun 11, 2026 at 12:17 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > > > //////////
    > > > v12-0002
    > > >
    > > > ======
    > > > doc/src/sgml/ref/alter_publication.sgml
    > > >
    > > > 1.
    > > > +     <para>
    > > > +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    > > > +      <literal>EXCEPT</literal> clause is schema-scoped.  If a table listed in
    > > > +      the <literal>EXCEPT</literal> clause is later moved to a different schema
    > > > +      using <command>ALTER TABLE ... SET SCHEMA</command>, the exclusion is
    > > > +      removed; the table will then be published if its new schema is part of a
    > > > +      publication.  If the table is subsequently moved back to the original
    > > > +      schema, the exclusion is not restored, and must be re-established
    > > > +      explicitly using <command>ALTER PUBLICATION</command>.  Dropping a table
    > > > +      always removes it from the <literal>EXCEPT</literal> clause,
    > > > regardless of
    > > > +      publication type.
    > > > +     </para>
    > > >
    > > >
    > > > I think the sentence "If the table is subsequently moved back..." is
    > > > overkill, and does not need to be said. The prior info "the exclusion
    > > > is removed" already tells me the exclusion is gone, and I think is
    > > > reasonable to assume "removed" means that it is gone for good, with no
    > > > ambiguity that it might magically come back.
    > > >
    > > > YMMV. Leave it as-is if you prefer.
    > > >
    > >
    > > I feel it is okay to keep the proposed sentence to avoid any ambiguity
    > > by the user to consider the schema-scope state is symmetric.
    > >
    >
    > Okay, I have kept this para as-it-is.
    >
    
    This discussion about the impact of ALTER TABLE ... SET SCHEMA made me
    wonder what happens for the existing PG19 case of FOR ALL TABLES
    EXCEPT (TABLE ...)
    
    It turns out to be quite different:
    
    ======
    
    test_pub=# CREATE SCHEMA myschema;
    CREATE SCHEMA
    test_pub=# CREATE TABLE t1(A INT);
    CREATE TABLE
    test_pub=# CREATE TABLE t2(A INT);
    CREATE TABLE
    test_pub=# CREATE TABLE myschema.myt1(A INT);
    CREATE TABLE
    test_pub=# CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT (TABLE myschema.myt1);
    CREATE PUBLICATION
    test_pub=# \d myschema.myt1
                   Table "myschema.myt1"
     Column |  Type   | Collation | Nullable | Default
    --------+---------+-----------+----------+---------
     a      | integer |           |          |
    Excluded from publications:
        "pub1"
    
    test_pub=# \dRp+ pub1
                                                           Publication pub1
      Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Descri
    ption
    ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------
    ------
     postgres | t          | f             | t       | t       | t       |
    t         | none              | f        |
    Except tables:
        "myschema.myt1"
    
    test_pub=# ALTER TABLE myschema.myt1 SET SCHEMA public;
    ALTER TABLE
    test_pub=# \d myt1
                    Table "public.myt1"
     Column |  Type   | Collation | Nullable | Default
    --------+---------+-----------+----------+---------
     a      | integer |           |          |
    Excluded from publications:
        "pub1"
    
    test_pub=# \dRp+ pub1
                                                           Publication pub1
      Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Descri
    ption
    ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------
    ------
     postgres | t          | f             | t       | t       | t       |
    t         | none              | f        |
    Except tables:
        "public.myt1"
    
    ======
    
    This experiment shows that moving the table did *not* remove the exclusion.
    
    It is kind of "explainable" in hindsight because the exclusion is by
    table OID, not name, so it follows the table around when it is moved.
    I don't think this is what a user would expect, given that they
    explicitly asked to exclude it from a different schema.
    
    Is it a PG19 exclusion bug?
    
    Is it behaviour that needs more documenting?
    
    ~
    
    IMO it seemed like a bug of the PG19 FOR ALL TABLES EXCEPT, because it
    is the opposite of what the new FOR TABLES IN SCHEMA EXCEPT patch
    does:
    "If a table listed in the <literal>EXCEPT</literal> clause is later
    moved to a different schema using  <command>ALTER TABLE ... SET
    SCHEMA</command>, the exclusion is removed;"
    
    Thoughts?
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  67. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Amit Kapila <amit.kapila16@gmail.com> — 2026-06-16T04:35:51Z

    On Tue, Jun 16, 2026 at 6:52 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > This discussion about the impact of ALTER TABLE ... SET SCHEMA made me
    > wonder what happens for the existing PG19 case of FOR ALL TABLES
    > EXCEPT (TABLE ...)
    >
    > It turns out to be quite different:
    >
    > ======
    >
    > test_pub=# CREATE SCHEMA myschema;
    > CREATE SCHEMA
    > test_pub=# CREATE TABLE t1(A INT);
    > CREATE TABLE
    > test_pub=# CREATE TABLE t2(A INT);
    > CREATE TABLE
    > test_pub=# CREATE TABLE myschema.myt1(A INT);
    > CREATE TABLE
    > test_pub=# CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT (TABLE myschema.myt1);
    > CREATE PUBLICATION
    > test_pub=# \d myschema.myt1
    >                Table "myschema.myt1"
    >  Column |  Type   | Collation | Nullable | Default
    > --------+---------+-----------+----------+---------
    >  a      | integer |           |          |
    > Excluded from publications:
    >     "pub1"
    >
    > test_pub=# \dRp+ pub1
    >                                                        Publication pub1
    >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root | Descri
    > ption
    > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------
    > ------
    >  postgres | t          | f             | t       | t       | t       |
    > t         | none              | f        |
    > Except tables:
    >     "myschema.myt1"
    >
    > test_pub=# ALTER TABLE myschema.myt1 SET SCHEMA public;
    > ALTER TABLE
    > test_pub=# \d myt1
    >                 Table "public.myt1"
    >  Column |  Type   | Collation | Nullable | Default
    > --------+---------+-----------+----------+---------
    >  a      | integer |           |          |
    > Excluded from publications:
    >     "pub1"
    >
    > test_pub=# \dRp+ pub1
    >                                                        Publication pub1
    >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root | Descri
    > ption
    > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------
    > ------
    >  postgres | t          | f             | t       | t       | t       |
    > t         | none              | f        |
    > Except tables:
    >     "public.myt1"
    >
    > ======
    >
    > This experiment shows that moving the table did *not* remove the exclusion.
    >
    > It is kind of "explainable" in hindsight because the exclusion is by
    > table OID, not name, so it follows the table around when it is moved.
    > I don't think this is what a user would expect, given that they
    > explicitly asked to exclude it from a different schema.
    >
    > Is it a PG19 exclusion bug?
    >
    > Is it behaviour that needs more documenting?
    >
    > ~
    >
    > IMO it seemed like a bug of the PG19 FOR ALL TABLES EXCEPT, because it
    > is the opposite of what the new FOR TABLES IN SCHEMA EXCEPT patch
    > does:
    > "If a table listed in the <literal>EXCEPT</literal> clause is later
    > moved to a different schema using  <command>ALTER TABLE ... SET
    > SCHEMA</command>, the exclusion is removed;"
    >
    
    No, I don't think this is a bug for PG19 and the new behaviour for
    PG20 is intentional and required because in this case, the EXCEPT
    clause is schema-scoped, so once schema is changed, the table should
    be removed from the exclusion list. OTOH, in PG19, the exclusion list
    follows the table-level exclusion based on its OID as we can see in
    the example provided by you. I think we can consider adding a line for
    this in docs if you and others feel that such explicit mention can
    avoid ambiguity around this. How about something like the following as
    a separate para in EXCEPT clause description: Once a table is
    excluded, the exclusion applies to that table itself, regardless of
    its name or schema. Renaming the table or moving it to another schema
    with <command>ALTER TABLE ... SET SCHEMA</command> does not cancel the
    exclusion."
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  68. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-16T06:36:10Z

    On Tue, Jun 16, 2026 at 2:36 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Jun 16, 2026 at 6:52 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > This discussion about the impact of ALTER TABLE ... SET SCHEMA made me
    > > wonder what happens for the existing PG19 case of FOR ALL TABLES
    > > EXCEPT (TABLE ...)
    > >
    > > It turns out to be quite different:
    > >
    > > ======
    > >
    > > test_pub=# CREATE SCHEMA myschema;
    > > CREATE SCHEMA
    > > test_pub=# CREATE TABLE t1(A INT);
    > > CREATE TABLE
    > > test_pub=# CREATE TABLE t2(A INT);
    > > CREATE TABLE
    > > test_pub=# CREATE TABLE myschema.myt1(A INT);
    > > CREATE TABLE
    > > test_pub=# CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT (TABLE myschema.myt1);
    > > CREATE PUBLICATION
    > > test_pub=# \d myschema.myt1
    > >                Table "myschema.myt1"
    > >  Column |  Type   | Collation | Nullable | Default
    > > --------+---------+-----------+----------+---------
    > >  a      | integer |           |          |
    > > Excluded from publications:
    > >     "pub1"
    > >
    > > test_pub=# \dRp+ pub1
    > >                                                        Publication pub1
    > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > Truncates | Generated columns | Via root | Descri
    > > ption
    > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------
    > > ------
    > >  postgres | t          | f             | t       | t       | t       |
    > > t         | none              | f        |
    > > Except tables:
    > >     "myschema.myt1"
    > >
    > > test_pub=# ALTER TABLE myschema.myt1 SET SCHEMA public;
    > > ALTER TABLE
    > > test_pub=# \d myt1
    > >                 Table "public.myt1"
    > >  Column |  Type   | Collation | Nullable | Default
    > > --------+---------+-----------+----------+---------
    > >  a      | integer |           |          |
    > > Excluded from publications:
    > >     "pub1"
    > >
    > > test_pub=# \dRp+ pub1
    > >                                                        Publication pub1
    > >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > > Truncates | Generated columns | Via root | Descri
    > > ption
    > > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------
    > > ------
    > >  postgres | t          | f             | t       | t       | t       |
    > > t         | none              | f        |
    > > Except tables:
    > >     "public.myt1"
    > >
    > > ======
    > >
    > > This experiment shows that moving the table did *not* remove the exclusion.
    > >
    > > It is kind of "explainable" in hindsight because the exclusion is by
    > > table OID, not name, so it follows the table around when it is moved.
    > > I don't think this is what a user would expect, given that they
    > > explicitly asked to exclude it from a different schema.
    > >
    > > Is it a PG19 exclusion bug?
    > >
    > > Is it behaviour that needs more documenting?
    > >
    > > ~
    > >
    > > IMO it seemed like a bug of the PG19 FOR ALL TABLES EXCEPT, because it
    > > is the opposite of what the new FOR TABLES IN SCHEMA EXCEPT patch
    > > does:
    > > "If a table listed in the <literal>EXCEPT</literal> clause is later
    > > moved to a different schema using  <command>ALTER TABLE ... SET
    > > SCHEMA</command>, the exclusion is removed;"
    > >
    >
    > No, I don't think this is a bug for PG19 and the new behaviour for
    > PG20 is intentional and required because in this case, the EXCEPT
    > clause is schema-scoped, so once schema is changed, the table should
    > be removed from the exclusion list. OTOH, in PG19, the exclusion list
    > follows the table-level exclusion based on its OID as we can see in
    > the example provided by you. I think we can consider adding a line for
    > this in docs if you and others feel that such explicit mention can
    > avoid ambiguity around this. How about something like the following as
    > a separate para in EXCEPT clause description: Once a table is
    > excluded, the exclusion applies to that table itself, regardless of
    > its name or schema. Renaming the table or moving it to another schema
    > with <command>ALTER TABLE ... SET SCHEMA</command> does not cancel the
    > exclusion."
    >
    
    OK. The explanatory text LGTM.
    
    What's the next step? Should I create a new thread/patch to deal with this?
    
    ======
    Kind Regards.
    Peter Smith
    Fujitsu Australia
    
    
    
    
  69. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-16T08:23:25Z

    Some review comments for v13-0001.
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    1.
          <para>
           Marks the publication as one that replicates changes for all tables in
           the specified list of schemas, including tables created in the future.
    +      Tables listed in the <literal>EXCEPT</literal> clause for a given schema
    +      are excluded from the publication.
          </para>
    
    Given Amit's suggestion [1] to modify the EXCEPT text for FOR ALL
    TABLES, perhaps there also needs to be an equivalent note for ALL
    TABLES IN SCHEMA. Maybe wait to see what happens [1], then you can use
    similar wording.
    
    ======
    src/backend/commands/publicationcmds.c
    
    RemovePublicationExceptForRelation:
    
    2.
    +/*
    + * Remove any EXCEPT clause entries for a relation from schema publications.
    + * Called when a table changes schema (ALTER TABLE ... SET SCHEMA), so that
    + * a schema-scoped exclusion does not silently follow the table to its new
    + * schema.
    + */
    +void
    +RemovePublicationExceptForRelation(Oid relid, Oid oldNspOid, Oid newNspOid)
    +{
    
    There's nothing about that function name to indicate it is only for
    SCHEMA publications.
    There must be a better -- e.g. 'MaybeRemoveExclusionFromSchemaPub', or
    whatever...
    
    ~~~
    
    3.
    + ereport(DEBUG2,
    + errmsg_internal("auto-drop exclusion of table %s from publication
    %s: table moved to schema %s",
    + quote_qualified_identifier(get_namespace_name(oldNspOid),
    +    get_rel_name(relid)),
    + get_publication_name(pubid, false),
    + quote_identifier(get_namespace_name(newNspOid))));
    
    Even though this is a debugging message, for consistency, we might as
    well quote everything the same as normal messages do:
    
    "auto-drop exclusion of table \"%s\" from publication \"%s\": table
    moved to schema \"%s\""
    
    ======
    [1] https://www.postgresql.org/message-id/CAA4eK1J_QP8CXCshaCy_01ALQQrDSzoTHfXKcAbjiEvV-RkOOw%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  70. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-16T10:44:09Z

    On Tue, Jun 16, 2026 at 1:53 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Some review comments for v13-0001.
    >
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 1.
    >       <para>
    >        Marks the publication as one that replicates changes for all tables in
    >        the specified list of schemas, including tables created in the future.
    > +      Tables listed in the <literal>EXCEPT</literal> clause for a given schema
    > +      are excluded from the publication.
    >       </para>
    >
    > Given Amit's suggestion [1] to modify the EXCEPT text for FOR ALL
    > TABLES, perhaps there also needs to be an equivalent note for ALL
    > TABLES IN SCHEMA. Maybe wait to see what happens [1], then you can use
    > similar wording.
    >
    
    Okay, noted. Let's first wait for the conclusion on this.
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > RemovePublicationExceptForRelation:
    >
    > 2.
    > +/*
    > + * Remove any EXCEPT clause entries for a relation from schema publications.
    > + * Called when a table changes schema (ALTER TABLE ... SET SCHEMA), so that
    > + * a schema-scoped exclusion does not silently follow the table to its new
    > + * schema.
    > + */
    > +void
    > +RemovePublicationExceptForRelation(Oid relid, Oid oldNspOid, Oid newNspOid)
    > +{
    >
    > There's nothing about that function name to indicate it is only for
    > SCHEMA publications.
    > There must be a better -- e.g. 'MaybeRemoveExclusionFromSchemaPub', or
    > whatever...
    >
    
    I've renamed it to RemoveSchemaPubExceptForRel to keep it consistent
    with the neighboring function names. Let me know if it works.
    
    > ~~~
    >
    > 3.
    > + ereport(DEBUG2,
    > + errmsg_internal("auto-drop exclusion of table %s from publication
    > %s: table moved to schema %s",
    > + quote_qualified_identifier(get_namespace_name(oldNspOid),
    > +    get_rel_name(relid)),
    > + get_publication_name(pubid, false),
    > + quote_identifier(get_namespace_name(newNspOid))));
    >
    > Even though this is a debugging message, for consistency, we might as
    > well quote everything the same as normal messages do:
    >
    > "auto-drop exclusion of table \"%s\" from publication \"%s\": table
    > moved to schema \"%s\""
    >
    
    Fixed.
    ~~~
    
    Also added a test as per Zsolt's suggestion at [1].
    I moved all documentation changes into a separate 0004 patch to make
    doc review easier.
    
    Please find the attached v14 patches.
    
    [1] https://www.postgresql.org/message-id/CAN4CZFPZnckA9-MPt6xj1QsRQTVKs3ZoKgdZ%2BQG1xa%3D%3DUD46Xw%40mail.gmail.com
    
    --
    Thanks,
    Nisha
    
  71. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Amit Kapila <amit.kapila16@gmail.com> — 2026-06-16T11:08:03Z

    On Tue, Jun 16, 2026 at 12:06 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Tue, Jun 16, 2026 at 2:36 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > No, I don't think this is a bug for PG19 and the new behaviour for
    > > PG20 is intentional and required because in this case, the EXCEPT
    > > clause is schema-scoped, so once schema is changed, the table should
    > > be removed from the exclusion list. OTOH, in PG19, the exclusion list
    > > follows the table-level exclusion based on its OID as we can see in
    > > the example provided by you. I think we can consider adding a line for
    > > this in docs if you and others feel that such explicit mention can
    > > avoid ambiguity around this. How about something like the following as
    > > a separate para in EXCEPT clause description: Once a table is
    > > excluded, the exclusion applies to that table itself, regardless of
    > > its name or schema. Renaming the table or moving it to another schema
    > > with <command>ALTER TABLE ... SET SCHEMA</command> does not cancel the
    > > exclusion."
    > >
    >
    > OK. The explanatory text LGTM.
    >
    > What's the next step? Should I create a new thread/patch to deal with this?
    >
    
    WFM.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  72. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-17T01:53:40Z

    Hi Nisha.
    
    Some review comments for v14-0001.
    
    ======
    src/test/regress/sql/publication.sql
    
    1.
    +-- ALTER TABLE ... SET SCHEMA on a table excluded by a schema publication:
    +-- the schema-scoped exclusion is no longer meaningful once the table moves
    +-- out of its schema, so the exclution is auto-dropped.
    
    1a.
    typo: /exclution/exclusion/
    
    ~
    
    1b.
    Exclusions cannot be "dropped" (at least, not by a user with the DROP
    command). So even though this is correct for what is happening
    internally, maybe from the user PoV it is better to say something like
    "auto-removed" or just "removed".
    
    (Same comment may also apply to the DEBUG logging when this happened).
    
    ~~~
    
    2.
    +-- Restore so the existing cleanup below still works
    +ALTER TABLE public.testpub_tbl_s2 SET SCHEMA pub_test;
    +DROP PUBLICATION testpub_schema_except_setsch;
    
    Why not just fix the cleanup code? It's not "existing cleanup" anyway
    -- it's part of this same patch.
    
    ////////////////////
    
    Some review comments for v14-0004.
    
    ======
    
    doc/src/sgml/ref/alter_publication.sgml:
    
    1.
        used with a publication defined with <literal>FOR TABLE</literal> or
        <literal>FOR TABLES IN SCHEMA</literal>, replaces the list of tables/schemas
        in the publication with the specified list; the existing tables or schemas
    -   that were present in the publication will be removed.
    +   that were present in the publication will be removed.  When
    +   <literal>SET TABLES IN SCHEMA</literal> is used with an
    +   <literal>EXCEPT</literal> clause, the excluded tables for each schema are
    +   replaced with the specified list; if <literal>EXCEPT</literal> is omitted
    +   for a schema, any existing exclusions for that schema are cleared.
    
    "are cleared" or "are removed"... not sure what is better. Maybe
    "cleared" is fine.
    
    ~~~
    
    2.
    +  <para>
    +   The <literal>EXCEPT</literal> clause can be used with
    +   <literal>ADD TABLES IN SCHEMA</literal> to exclude specific tables from a
    +   schema-level publication.
    +  </para>
    +
    
    This is talking about the 'ADD' variant. So really, it belongs as part
    of that 2nd para: "The first two variants modify..." because that is
    where ADD is discussed.
    
    ~~~
    
    3.
    +   <varlistentry>
    +    <term><literal>EXCEPT</literal></term>
    +    <listitem>
    +     <para>
    +      When used with <literal>ADD TABLES IN SCHEMA</literal>
    +      or <literal>SET TABLES IN SCHEMA</literal>, specifies
    +      tables to be excluded from the publication.  Each named
    +      table must belong to the schema specified in the same
    +      <literal>TABLES IN SCHEMA</literal> clause.  Table names may be
    +      schema-qualified or unqualified; unqualified names are implicitly
    +      qualified with the schema named in the same clause.  See
    +      <xref linkend="sql-createpublication"/> for further details on the
    +      semantics of <literal>EXCEPT</literal>.
    +     </para>
    +     <para>
    +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    +      <literal>EXCEPT</literal> clause is schema-scoped.  If a table listed in
    +      the <literal>EXCEPT</literal> clause is later moved to a different schema
    +      using <command>ALTER TABLE ... SET SCHEMA</command>, the exclusion is
    +      removed; the table will then be published if its new schema is part of a
    +      publication.  If the table is subsequently moved back to the original
    +      schema, the exclusion is not restored, and must be re-established
    +      explicitly using <command>ALTER PUBLICATION</command>.  Dropping a table
    +      always removes it from the <literal>EXCEPT</literal> clause,
    regardless of
    +      publication type.
    +     </para>
    +    </listitem>
    +   </varlistentry>
    
    3a.
    TBH, I lost track of what the final decision was about keeping this EXCEPT part.
    IIUC, you wanted to defer a decision until later, but I did not
    understand the plan:
    a) Should the EXCEPT part be present for now, but consider removing it later?
    b) Should the EXCEPT part be absent for now, but consider adding it later?
    
    I prefer what you have done, but either way, we need to ensure Shlok's
    thread uses the same approach.
    
    ~
    
    3b.
    I think that last "Dropping a table..." sentence should be a separate
    <para>, unrelated to renaming/moving tables. Also, you don't need to
    say "regardless of publication type" -- that would be the assumption
    unless you say otherwise.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  73. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-17T03:38:10Z

    On Tue, Jun 16, 2026 at 9:08 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Jun 16, 2026 at 12:06 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > On Tue, Jun 16, 2026 at 2:36 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > > No, I don't think this is a bug for PG19 and the new behaviour for
    > > > PG20 is intentional and required because in this case, the EXCEPT
    > > > clause is schema-scoped, so once schema is changed, the table should
    > > > be removed from the exclusion list. OTOH, in PG19, the exclusion list
    > > > follows the table-level exclusion based on its OID as we can see in
    > > > the example provided by you. I think we can consider adding a line for
    > > > this in docs if you and others feel that such explicit mention can
    > > > avoid ambiguity around this. How about something like the following as
    > > > a separate para in EXCEPT clause description: Once a table is
    > > > excluded, the exclusion applies to that table itself, regardless of
    > > > its name or schema. Renaming the table or moving it to another schema
    > > > with <command>ALTER TABLE ... SET SCHEMA</command> does not cancel the
    > > > exclusion."
    > > >
    > >
    > > OK. The explanatory text LGTM.
    > >
    > > What's the next step? Should I create a new thread/patch to deal with this?
    > >
    >
    > WFM.
    >
    
    Done. See new thread/patch [1].
    
    ======
    [1] https://www.postgresql.org/message-id/flat/CAHut%2BPvQ5BqnawCQd6r1tqqd%2BiAJC-CuRY8wscuXSrpHGUzofA%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  74. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-18T06:35:37Z

    On Wed, Jun 17, 2026 at 7:24 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for v14-0001.
    >
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 1.
    > +-- ALTER TABLE ... SET SCHEMA on a table excluded by a schema publication:
    > +-- the schema-scoped exclusion is no longer meaningful once the table moves
    > +-- out of its schema, so the exclution is auto-dropped.
    >
    > 1a.
    > typo: /exclution/exclusion/
    >
    > ~
    >
    > 1b.
    > Exclusions cannot be "dropped" (at least, not by a user with the DROP
    > command). So even though this is correct for what is happening
    > internally, maybe from the user PoV it is better to say something like
    > "auto-removed" or just "removed".
    >
    > (Same comment may also apply to the DEBUG logging when this happened).
    >
    > ~~~
    >
    > 2.
    > +-- Restore so the existing cleanup below still works
    > +ALTER TABLE public.testpub_tbl_s2 SET SCHEMA pub_test;
    > +DROP PUBLICATION testpub_schema_except_setsch;
    >
    > Why not just fix the cleanup code? It's not "existing cleanup" anyway
    > -- it's part of this same patch.
    >
    
    I restored it here because patches 002 and 003 add tests that depend
    on pub_test.testpub_tbl_s2.
    I agree the earlier comments were confusing and made it look like it
    was only for cleanup purposes. I've reworded the comments, but kept
    the restore so the ALTER PUBLICATION tests can be added smoothly.
    
    > ////////////////////
    >
    > Some review comments for v14-0004.
    >
    > ======
    >
    > doc/src/sgml/ref/alter_publication.sgml:
    >
    > 1.
    >     used with a publication defined with <literal>FOR TABLE</literal> or
    >     <literal>FOR TABLES IN SCHEMA</literal>, replaces the list of tables/schemas
    >     in the publication with the specified list; the existing tables or schemas
    > -   that were present in the publication will be removed.
    > +   that were present in the publication will be removed.  When
    > +   <literal>SET TABLES IN SCHEMA</literal> is used with an
    > +   <literal>EXCEPT</literal> clause, the excluded tables for each schema are
    > +   replaced with the specified list; if <literal>EXCEPT</literal> is omitted
    > +   for a schema, any existing exclusions for that schema are cleared.
    >
    > "are cleared" or "are removed"... not sure what is better. Maybe
    > "cleared" is fine.
    >
    
    okay, retained "cleared".
    
    > ~~~
    >
    > 2.
    > +  <para>
    > +   The <literal>EXCEPT</literal> clause can be used with
    > +   <literal>ADD TABLES IN SCHEMA</literal> to exclude specific tables from a
    > +   schema-level publication.
    > +  </para>
    > +
    >
    > This is talking about the 'ADD' variant. So really, it belongs as part
    > of that 2nd para: "The first two variants modify..." because that is
    > where ADD is discussed.
    >
    
    Done.
    
    > ~~~
    >
    > 3.
    > +   <varlistentry>
    > +    <term><literal>EXCEPT</literal></term>
    > +    <listitem>
    > +     <para>
    > +      When used with <literal>ADD TABLES IN SCHEMA</literal>
    > +      or <literal>SET TABLES IN SCHEMA</literal>, specifies
    > +      tables to be excluded from the publication.  Each named
    > +      table must belong to the schema specified in the same
    > +      <literal>TABLES IN SCHEMA</literal> clause.  Table names may be
    > +      schema-qualified or unqualified; unqualified names are implicitly
    > +      qualified with the schema named in the same clause.  See
    > +      <xref linkend="sql-createpublication"/> for further details on the
    > +      semantics of <literal>EXCEPT</literal>.
    > +     </para>
    > +     <para>
    > +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    > +      <literal>EXCEPT</literal> clause is schema-scoped.  If a table listed in
    > +      the <literal>EXCEPT</literal> clause is later moved to a different schema
    > +      using <command>ALTER TABLE ... SET SCHEMA</command>, the exclusion is
    > +      removed; the table will then be published if its new schema is part of a
    > +      publication.  If the table is subsequently moved back to the original
    > +      schema, the exclusion is not restored, and must be re-established
    > +      explicitly using <command>ALTER PUBLICATION</command>.  Dropping a table
    > +      always removes it from the <literal>EXCEPT</literal> clause,
    > regardless of
    > +      publication type.
    > +     </para>
    > +    </listitem>
    > +   </varlistentry>
    >
    > 3a.
    > TBH, I lost track of what the final decision was about keeping this EXCEPT part.
    > IIUC, you wanted to defer a decision until later, but I did not
    > understand the plan:
    > a) Should the EXCEPT part be present for now, but consider removing it later?
    > b) Should the EXCEPT part be absent for now, but consider adding it later?
    >
    > I prefer what you have done, but either way, we need to ensure Shlok's
    > thread uses the same approach.
    >
    
    I chose (a), keeping EXCEPT part for now so others can provide
    feedback and help us reach a consensus on how the documentation should
    be updated.
    
    > ~
    >
    > 3b.
    > I think that last "Dropping a table..." sentence should be a separate
    > <para>, unrelated to renaming/moving tables. Also, you don't need to
    > say "regardless of publication type" -- that would be the assumption
    > unless you say otherwise.
    >
    
    Fixed.
    ---
    
    Attached are the updated patches v15.
    
    --
    Thanks,
    Nisha
    
  75. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-22T02:16:17Z

    Hi Nisha.
    
    No further comments for patches v15-0001,0002,0003.
    
    But, here are some comments for v15-0004.
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    1.
    +     <para>
    +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    +      <literal>EXCEPT</literal> clause is schema-scoped.  If a table listed in
    +      the <literal>EXCEPT</literal> clause is later moved to a different schema
    +      using <command>ALTER TABLE ... SET SCHEMA</command>, the exclusion is
    +      removed; the table will then be published if its new schema is part of a
    +      publication.  If the table is subsequently moved back to the original
    +      schema, the exclusion is not restored, and must be re-established
    +      explicitly using <command>ALTER PUBLICATION</command>.
    +     </para>
    
    IIRC the recent commit 77b6dd9 that explained about moving tables to
    other schemas, was only on the CREATE PUBLICATION page; not on the
    ALTER PUBLICATION page.
    
    So either:
    (a) maybe this entire paragraph is not needed, or
    (b) keep it, but then the behaviour for FOR ALL TABLES also needs to
    be described here
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    2.
    Once a table is excluded, the exclusion applies to that table
    regardless of its name or schema. Renaming the table or moving it to
    another schema using ALTER TABLE ... SET SCHEMA does not remove the
    exclusion.
    
    ~~
    
    The recent commit 77b6dd9 added the above text to the CREATE
    PUBLICATION page. But, that was correct only for "FOR ALL TABLES" --
    after your patch we can't say that unconditionally anymore. Also,
    since that is mentioned on the CREATE PUBLICATION page, I guess we
    need to say something similar about ALL TABLES IN SCHEMA ... EXCEPT in
    the same paragraph.
    
    So, maybe something like below:
    
    SUGGESTION
    Once a table is excluded under <literal>FOR ALL TABLES</literal>, the
    exclusion applies to that table, the exclusion applies to that table
    regardless of its name or schema. Renaming the table or moving it to
    another schema using <command>ALTER TABLE ... SET SCHEMA</command>
    does not remove the exclusion. However, for <literal>FOR TABLES IN
    SCHEMA</literal>, because the <literal>EXCEPT</literal> is
    schema-scoped, moving a schema-excluded table to another schema does
    remove the exclusion.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  76. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-06-22T12:49:41Z

    On Mon, Jun 22, 2026 at 7:46 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > No further comments for patches v15-0001,0002,0003.
    >
    > But, here are some comments for v15-0004.
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 1.
    > +     <para>
    > +      For <literal>FOR TABLES IN SCHEMA</literal> publications, the
    > +      <literal>EXCEPT</literal> clause is schema-scoped.  If a table listed in
    > +      the <literal>EXCEPT</literal> clause is later moved to a different schema
    > +      using <command>ALTER TABLE ... SET SCHEMA</command>, the exclusion is
    > +      removed; the table will then be published if its new schema is part of a
    > +      publication.  If the table is subsequently moved back to the original
    > +      schema, the exclusion is not restored, and must be re-established
    > +      explicitly using <command>ALTER PUBLICATION</command>.
    > +     </para>
    >
    > IIRC the recent commit 77b6dd9 that explained about moving tables to
    > other schemas, was only on the CREATE PUBLICATION page; not on the
    > ALTER PUBLICATION page.
    >
    > So either:
    > (a) maybe this entire paragraph is not needed, or
    > (b) keep it, but then the behaviour for FOR ALL TABLES also needs to
    > be described here
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 2.
    > Once a table is excluded, the exclusion applies to that table
    > regardless of its name or schema. Renaming the table or moving it to
    > another schema using ALTER TABLE ... SET SCHEMA does not remove the
    > exclusion.
    >
    > ~~
    >
    > The recent commit 77b6dd9 added the above text to the CREATE
    > PUBLICATION page. But, that was correct only for "FOR ALL TABLES" --
    > after your patch we can't say that unconditionally anymore. Also,
    > since that is mentioned on the CREATE PUBLICATION page, I guess we
    > need to say something similar about ALL TABLES IN SCHEMA ... EXCEPT in
    > the same paragraph.
    >
    > So, maybe something like below:
    >
    > SUGGESTION
    > Once a table is excluded under <literal>FOR ALL TABLES</literal>, the
    > exclusion applies to that table, the exclusion applies to that table
    > regardless of its name or schema. Renaming the table or moving it to
    > another schema using <command>ALTER TABLE ... SET SCHEMA</command>
    > does not remove the exclusion. However, for <literal>FOR TABLES IN
    > SCHEMA</literal>, because the <literal>EXCEPT</literal> is
    > schema-scoped, moving a schema-excluded table to another schema does
    > remove the exclusion.
    >
    
    Based on the changes in commit 77b6dd9, I re-analyzed the pointed-out paragraph.
    
    On reconsideration, I think the schema-move behavior is really a
    property of EXCEPT itself rather than ALTER PUBLICATION. So we can
    keep it only in the CREATE PUBLICATION documentation and drop it from
    ALTER PUBLICATION to avoid duplication.
    We already have a cross-reference in ALTER PUBLICATION: "See CREATE
    PUBLICATION for further details on the semantics of EXCEPT", so users
    still have a clear path to the detailed behavior description.
    I modified your suggestion slightly to keep the information from the
    removed paragraph intact.
    
    Attached updated patches v16. No changes in 0001 to 0003.
    
    --
    Thanks,
    Nisha
    
  77. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-06-22T23:40:47Z

    On Tue, Jun 23, 2026 at 12:49 AM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    ...
    >
    > Based on the changes in commit 77b6dd9, I re-analyzed the pointed-out paragraph.
    >
    > On reconsideration, I think the schema-move behavior is really a
    > property of EXCEPT itself rather than ALTER PUBLICATION. So we can
    > keep it only in the CREATE PUBLICATION documentation and drop it from
    > ALTER PUBLICATION to avoid duplication.
    > We already have a cross-reference in ALTER PUBLICATION: "See CREATE
    > PUBLICATION for further details on the semantics of EXCEPT", so users
    > still have a clear path to the detailed behavior description.
    > I modified your suggestion slightly to keep the information from the
    > removed paragraph intact.
    >
    > Attached updated patches v16. No changes in 0001 to 0003.
    >
    
    Hi Nisha.
    
    Thanks for all the changes. I don't have any more review comments at this time.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  78. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-06-30T10:16:58Z

    On Mon, Jun 22, 2026 at 6:20 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > Attached updated patches v16. No changes in 0001 to 0003.
    >
    
    Thanks Nisha. I have resumed review of patch. I have not goen through
    complete patch yet, but please find a few initial comments:
    
    
    1)
    I get this during compilation:
    
    pgoutput.c:2232:45: warning: declaration of ‘except_pubids’ shadows a
    previous local [-Wshadow=compatible-local]
     2232 |                                 List       *except_pubids = NIL;
          |                                             ^~~~~~~~~~~~~
    pgoutput.c:2100:29: note: shadowed declaration is here
     2100 |                 List       *except_pubids;
    
    
    2)
    I am checking the flow:
    get_rel_sync_entry-->GetTopMostAncestorInPublication
    
    @@ -2267,7 +2290,8 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
    
      ancestor = GetTopMostAncestorInPublication(pub->oid,
         ancestors,
    -    &level);
    +    &level,
    +    except_pubids);
    
    a)
    For what all publications, flow will come to above point. I guess it
    can come for a explict table pub or a schema pub. For a schmea pub,
    except_pubids make sense but not for a explicit-table pub alone.
    Please add a comment atop GetTopMostAncestorInPublication call here to
    explain
    the scenario adn then the need of except_pubids.
    
    b)
    We have got except_pubids in the begining of the function as:
    except_pubids = GetRelationExcludedPublications(root_relid);
    
    Now inside GetTopMostAncestorInPublication(), we are keeping
    'except_pubids' as constant while moving through all the ancestors. We
    are fetching GetRelationIncludedPublications and GetSchemaPublications
    for each ancestor but are not computing
    GetRelationExcludedPublications for each ancestor. It may not be
    obvious (for many new readers) why 'except_pubids' is not fetched
    again for each ancestor. Please add a comment there (perhaps because
    EXCEPT list can not have any partition and can have only root)
    
    3)
    The error message below and its associated validation logic are
    duplicated in three places:
    
    "cannot appear in both the table list and the EXCEPT clause"
    
    Do you think we could factor this into a small helper function and
    reuse it everywhere? That would make the code more modular and
    simplify future changes to validation or error messages.
    
    thanks
    Shveta
    
    
    
    
  79. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-07-01T10:07:01Z

    On Tue, Jun 30, 2026 at 3:47 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Mon, Jun 22, 2026 at 6:20 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > Attached updated patches v16. No changes in 0001 to 0003.
    > >
    >
    > Thanks Nisha. I have resumed review of patch. I have not goen through
    > complete patch yet, but please find a few initial comments:
    >
    
    Thanks for review Shveta.
    
    >
    > 1)
    > I get this during compilation:
    >
    > pgoutput.c:2232:45: warning: declaration of ‘except_pubids’ shadows a
    > previous local [-Wshadow=compatible-local]
    >  2232 |                                 List       *except_pubids = NIL;
    >       |                                             ^~~~~~~~~~~~~
    > pgoutput.c:2100:29: note: shadowed declaration is here
    >  2100 |                 List       *except_pubids;
    >
    
    An existing variable exceptpubids was renamed to except_pubids by
    mistake while addressing comment #5 at [1].
    Fixed now.
    
    >
    > 2)
    > I am checking the flow:
    > get_rel_sync_entry-->GetTopMostAncestorInPublication
    >
    > @@ -2267,7 +2290,8 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
    >
    >   ancestor = GetTopMostAncestorInPublication(pub->oid,
    >      ancestors,
    > -    &level);
    > +    &level,
    > +    except_pubids);
    >
    > a)
    > For what all publications, flow will come to above point. I guess it
    > can come for a explict table pub or a schema pub. For a schmea pub,
    > except_pubids make sense but not for a explicit-table pub alone.
    > Please add a comment atop GetTopMostAncestorInPublication call here to
    > explain
    > the scenario adn then the need of except_pubids.
    >
    > b)
    > We have got except_pubids in the begining of the function as:
    > except_pubids = GetRelationExcludedPublications(root_relid);
    >
    > Now inside GetTopMostAncestorInPublication(), we are keeping
    > 'except_pubids' as constant while moving through all the ancestors. We
    > are fetching GetRelationIncludedPublications and GetSchemaPublications
    > for each ancestor but are not computing
    > GetRelationExcludedPublications for each ancestor. It may not be
    > obvious (for many new readers) why 'except_pubids' is not fetched
    > again for each ancestor. Please add a comment there (perhaps because
    > EXCEPT list can not have any partition and can have only root)
    >
    
    Added comments for both the cases.
    
    > 3)
    > The error message below and its associated validation logic are
    > duplicated in three places:
    >
    > "cannot appear in both the table list and the EXCEPT clause"
    >
    > Do you think we could factor this into a small helper function and
    > reuse it everywhere? That would make the code more modular and
    > simplify future changes to validation or error messages.
    >
    
    Done.
    ~~~
    
    Besides the above comments, I found and fixed a few additional issues
    during further testing:
    1) Fixed an issue where UPDATE/DELETE on an excluded table without
    replica identity was still blocked. e.g.,
    
    postgres=# create publication pub_h2 for tables in schema s1 except ( table t1);
    postgres=# update s1.t1 set c1=3 where c1=1;
    ERROR: cannot update table "t1" because it does not have a replica
    identity and publishes updates
    HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.
    
    Excluded tables should not be treated as published for update/delete checks.
    
    2) Fixed a bug in RemoveSchemaPubExceptForRel(), where a no-op command
    such as ALTER TABLE s.t SET SCHEMA s incorrectly removed the table
    from the EXCEPT list.
    
    3) Fixed is_table_publication() logic. It relied on the first
    pg_publication_rel row for a publication, which is no longer reliable
    because with this feature we can have multiple rows with different
    prexcept values for the same publication.
    A publication can have both FOR TABLE (prexcept =false) and FOR TABLES
    IN SCHEMA (prexcept=false) entries.
    
    4) Fixed a bug in PublicationDropTables() where "ALTER PUBLICATION p
    DROP TABLE t" could silently remove an EXCEPT entry. DROP TABLE should
    only apply to explicitly added FOR TABLE members; EXCEPT entries are
    not publication members and should not be removed through this path.
    For example: Before Fix:
      create publication pub1 for tables in schema s1 except ( table t1);
      alter publication pub1 drop table s1.t1;
      ALTER PUBLICATION
    -- the Except tables: entry gets deleted.
    
    After the fix (v17):
      alter publication pub1 drop table s1.t1;
      ERROR:  relation "t1" is not part of the publication
    ~~~
    
    Attached are the v17 patches.
    
    [1] https://www.postgresql.org/message-id/CAHut%2BPuiK_Pa%3DBkSgBxYzqf1PYh%2BmcUcUQCr8r1e69-y1r%2Bhhw%40mail.gmail.com
    --
    Thanks,
    Nisha
    
  80. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-07-01T10:12:38Z

    During tests, I found one behavior that needs discussion:
    
    Consider a partition hierarchy where the root table is in schema s2, a
    child partition is in schema s1, and the publication includes both
    schemas but excludes only the root table.
    Test case:
    -- Root of the partition tree lives in s2
      CREATE TABLE s2.parent (id int) PARTITION BY LIST (id);
    
    -- Child partition lives in s1
      CREATE TABLE s1.part PARTITION OF s2.parent FOR VALUES IN (1, 2, 3);
    
    -- Publication covers BOTH schemas, but EXCEPTs only s2.parent
      CREATE PUBLICATION p FOR TABLES IN SCHEMA s1, TABLES IN SCHEMA s2
    EXCEPT (TABLE s2.parent);
    
    Currently, if the root is excluded, all child partitions are also
    excluded, meaning s1.part is not published. However, s1 never excluded
    'part' explicitly, so the behavior may not be intuitive.
    
    We need to decide whether child partitions should always remain
    excluded when the root is excluded (regardless of schema), or whether
    schema membership should still allow publication.
    
    I've added an XXX comment in pg_publication.c to track this until we
    conclude on the expected behavior. Suggestions are welcome.
    
    --
    Thanks,
    Nisha
    
    
    
    
  81. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-07-01T10:16:43Z

    On Wed, Jul 1, 2026 at 3:37 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    >
    > Besides the above comments, I found and fixed a few additional issues
    > during further testing:
    > 1) Fixed an issue where UPDATE/DELETE on an excluded table without
    > replica identity was still blocked. e.g.,
    >
    > postgres=# create publication pub_h2 for tables in schema s1 except ( table t1);
    > postgres=# update s1.t1 set c1=3 where c1=1;
    > ERROR: cannot update table "t1" because it does not have a replica
    > identity and publishes updates
    > HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.
    >
    > Excluded tables should not be treated as published for update/delete checks.
    
    Nisha, this bug was introduced by your patch or does it exist in 'ALL
    TABLES Except TABLE' too?
    
    thanks
    Shveta
    
    
    
    
  82. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-07-01T10:24:19Z

    On Wed, Jul 1, 2026 at 3:42 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > During tests, I found one behavior that needs discussion:
    >
    > Consider a partition hierarchy where the root table is in schema s2, a
    > child partition is in schema s1, and the publication includes both
    > schemas but excludes only the root table.
    
    This is the exact scenario I had in mind when reviewing
    'get_rel_sync_entry-->GetTopMostAncestorInPublication' flow, thus I
    suggested adding comments about intentionally fetching the 'Exclude
    list' only for the Root. I was about to test it in my next review
    cycle.
    
    The first thought is that if root is excluded, the partition should
    also be excluded to maintain consistency with the 'ALL TABLES Except
    ROOT' behaviour. But then it needs to be documented too. This is my
    initial understanding, but I will think more when I resume the review.
    
    > Test case:
    > -- Root of the partition tree lives in s2
    >   CREATE TABLE s2.parent (id int) PARTITION BY LIST (id);
    >
    > -- Child partition lives in s1
    >   CREATE TABLE s1.part PARTITION OF s2.parent FOR VALUES IN (1, 2, 3);
    >
    > -- Publication covers BOTH schemas, but EXCEPTs only s2.parent
    >   CREATE PUBLICATION p FOR TABLES IN SCHEMA s1, TABLES IN SCHEMA s2
    > EXCEPT (TABLE s2.parent);
    >
    > Currently, if the root is excluded, all child partitions are also
    > excluded, meaning s1.part is not published. However, s1 never excluded
    > 'part' explicitly, so the behavior may not be intuitive.
    >
    > We need to decide whether child partitions should always remain
    > excluded when the root is excluded (regardless of schema), or whether
    > schema membership should still allow publication.
    >
    > I've added an XXX comment in pg_publication.c to track this until we
    > conclude on the expected behavior. Suggestions are welcome.
    >
    > --
    > Thanks,
    > Nisha
    
    
    
    
  83. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-07-02T04:51:18Z

    On Wed, Jul 1, 2026 at 3:46 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Jul 1, 2026 at 3:37 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > >
    > > Besides the above comments, I found and fixed a few additional issues
    > > during further testing:
    > > 1) Fixed an issue where UPDATE/DELETE on an excluded table without
    > > replica identity was still blocked. e.g.,
    > >
    > > postgres=# create publication pub_h2 for tables in schema s1 except ( table t1);
    > > postgres=# update s1.t1 set c1=3 where c1=1;
    > > ERROR: cannot update table "t1" because it does not have a replica
    > > identity and publishes updates
    > > HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.
    > >
    > > Excluded tables should not be treated as published for update/delete checks.
    >
    > Nisha, this bug was introduced by your patch or does it exist in 'ALL
    > TABLES Except TABLE' too?
    >
    
    The same case for 'ALL TABLES Except Table' was already handled
    correctly as part of that feature (Ref
    -RelationBuildPublicationDesc()). The bug was only in my patch where I
    missed handling it earlier.
    
    --
    Thanks,
    Nisha
    
    
    
    
  84. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-07-02T08:49:31Z

    On Thu, Jul 2, 2026 at 10:21 AM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Wed, Jul 1, 2026 at 3:46 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Wed, Jul 1, 2026 at 3:37 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > >
    > > > Besides the above comments, I found and fixed a few additional issues
    > > > during further testing:
    > > > 1) Fixed an issue where UPDATE/DELETE on an excluded table without
    > > > replica identity was still blocked. e.g.,
    > > >
    > > > postgres=# create publication pub_h2 for tables in schema s1 except ( table t1);
    > > > postgres=# update s1.t1 set c1=3 where c1=1;
    > > > ERROR: cannot update table "t1" because it does not have a replica
    > > > identity and publishes updates
    > > > HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.
    > > >
    > > > Excluded tables should not be treated as published for update/delete checks.
    > >
    > > Nisha, this bug was introduced by your patch or does it exist in 'ALL
    > > TABLES Except TABLE' too?
    > >
    >
    > The same case for 'ALL TABLES Except Table' was already handled
    > correctly as part of that feature (Ref
    > -RelationBuildPublicationDesc()). The bug was only in my patch where I
    > missed handling it earlier.
    >
    
    Okay, then we are good.
    
    A few comments:
    
    1)
    postgres=# alter publication pub1 add table s2.t1;
    ERROR:  table "s2.t1" cannot be added because it is excluded from
    publication "pub1"
    
    It will be good to add hint in above error.
    
    Suggestion:
    ERROR: cannot add table "s2.t1" to publication "pub1"
    DETAIL: Table "s2.t1" is currently in EXCEPT list
    HINT: Change EXCEPT list using ALTER PUBLICATION ..SET TABLES IN
    SCHEMA .. EXCEPT
    
    Please rephrase above as needed. Look earlier such messages/hints
    already added for ALL TABLES.
    
    From the user's perspective, it seems slightly odd that if he has
    excluded a table from publication, he can not add it back using 'add
    table'. Was this point discussed earlier?
    
    2)
    Currently:
    postgres=# alter publication pub1 drop table s2.t1;
    ERROR:  relation "t1" is not part of the publication
    
    s2.t1 is EXCEPT list here.
    
    Should we have below error for this case rather than above generic
    error, it will be more intuitive for user. Plus both add and drop
    table commands will have similar error-style for EXCEPT list.
    Thoughts?
    
    ERROR: cannot drop table "s2.t1" from publication "pub1"
    DETAIL: Table "s2.t1" is currently in EXCEPT list
    HINT: Change EXCEPT list using ALTER PUBLICATION ..SET TABLES IN
    SCHEMA .. EXCEPT
    
    3)
    Please see below test:
    
    --Setting Except list for pub1 which has all tables of s2 included:
    alter publication pub1 set tables in schema s2 EXCEPT ( TABLE t1, t2);
    
    --pg_publication_rel entries:
    pubname | schema_name | table_name | prexcept
    --------+-------------+------------+---------
    pub1    | s2          | t1         | t
    pub1    | s2          | t2         | t
    
    
    --Then added a partition:
    alter publication pub1 add table s2.tab_part_1_p2;
    
    --pg_publication_rel entries:
    pubname | schema_name |  table_name   | prexcept
    --------+-------------+---------------+---------
    pub1    | s2          | t1            | t
    pub1    | s2          | t2            | t
    pub1    | s2          | tab_part_1_p2 | f
    
    
    --Then changed pub1 to exclude tab_root (parent of tab_part_1_p2) as well:
    
    alter publication pub1 set tables in schema s2 EXCEPT ( TABLE t1, t2, tab_root);
    
    --pg_publication_rel entries:
    pubname | schema_name | table_name | prexcept
    --------+-------------+------------+---------
    pub1    | s2          | t1         | t
    pub1    | s2          | t2         | t
    pub1    | s2          | tab_root   | t
    
    Note theoutput above, tab_part_1_p2 is removed internally as part of
    earlier command since its parent tab_root is now excluded.
    
    --Now add parition back, it allowed to add it:
    alter publication pub1 add table s2.tab_part_1_p2;
    
    pg_publication_rel entries:
    pubname | schema_name |  table_name   | prexcept
    --------+-------------+---------------+---------
    pub1    | s2          | t1            | t
    pub1    | s2          | t2            | t
    pub1    | s2          | tab_root      | t
    pub1    | s2          | tab_part_1_p2 | f
    
    
    FOR ALL TABLES publicaiton, if root is excluded, there is no way
    publication can have parition included independently. Same should hold
    true here. Thoughts?
    
    thanks
    Shveta
    
    
    
    
  85. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Amit Kapila <amit.kapila16@gmail.com> — 2026-07-02T09:03:47Z

    On Wed, Jul 1, 2026 at 3:54 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Jul 1, 2026 at 3:42 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > During tests, I found one behavior that needs discussion:
    > >
    > > Consider a partition hierarchy where the root table is in schema s2, a
    > > child partition is in schema s1, and the publication includes both
    > > schemas but excludes only the root table.
    >
    > This is the exact scenario I had in mind when reviewing
    > 'get_rel_sync_entry-->GetTopMostAncestorInPublication' flow, thus I
    > suggested adding comments about intentionally fetching the 'Exclude
    > list' only for the Root. I was about to test it in my next review
    > cycle.
    >
    > The first thought is that if root is excluded, the partition should
    > also be excluded to maintain consistency with the 'ALL TABLES Except
    > ROOT' behaviour. But then it needs to be documented too.
    >
    
    +1. It is good to be consistent here with similar ALL TABLES case.
    BTW, as we can't exclude the child table explicitly, this anyway seems
    like the only option because otherwise, users won't have any way to
    exclude the child table.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  86. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-07-03T16:45:03Z

    On Thu, Jul 2, 2026 at 2:19 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > A few comments:
    >
    > 1)
    > postgres=# alter publication pub1 add table s2.t1;
    > ERROR:  table "s2.t1" cannot be added because it is excluded from
    > publication "pub1"
    >
    > It will be good to add hint in above error.
    >
    > Suggestion:
    > ERROR: cannot add table "s2.t1" to publication "pub1"
    > DETAIL: Table "s2.t1" is currently in EXCEPT list
    > HINT: Change EXCEPT list using ALTER PUBLICATION ..SET TABLES IN
    > SCHEMA .. EXCEPT
    >
    > Please rephrase above as needed. Look earlier such messages/hints
    > already added for ALL TABLES.
    >
    
    Updated the error as suggested, with a slight change in DETAIL message.
    
    > From the user's perspective, it seems slightly odd that if he has
    > excluded a table from publication, he can not add it back using 'add
    > table'. Was this point discussed earlier?
    >
    
    This hasn't been discussed yet.
    
    I kept the current behavior, as IMO, it is hard to tell whether the
    user is trying to re-include a table or whether the ADD is unintended
    and should be blocked. To avoid ambiguity, I think EXCEPT entries
    should be modified only through the EXCEPT (...) clause itself, such
    as ALTER PUBLICATION ... ADD/SET ... EXCEPT(TABLE ...), or by omitting
    EXCEPT in the SET case.
    
    This would also keep the behavior of ADD TABLE and DROP TABLE aligned,
    as both would operate only on the include list without touching the
    EXCEPT list.
    I think this also aligns with FOR ALL TABLES, where ADD TABLE is
    disallowed, so the EXCEPT list can only be modified through SET ..
    EXCEPT (...).
    
    Thoughts?
    
    > 2)
    > Currently:
    > postgres=# alter publication pub1 drop table s2.t1;
    > ERROR:  relation "t1" is not part of the publication
    >
    > s2.t1 is EXCEPT list here.
    >
    > Should we have below error for this case rather than above generic
    > error, it will be more intuitive for user. Plus both add and drop
    > table commands will have similar error-style for EXCEPT list.
    > Thoughts?
    >
    > ERROR: cannot drop table "s2.t1" from publication "pub1"
    > DETAIL: Table "s2.t1" is currently in EXCEPT list
    > HINT: Change EXCEPT list using ALTER PUBLICATION ..SET TABLES IN
    > SCHEMA .. EXCEPT
    >
    
    Makes sense. Fixed.
    
    > 3)
    > Please see below test:
    >
    > --Setting Except list for pub1 which has all tables of s2 included:
    > alter publication pub1 set tables in schema s2 EXCEPT ( TABLE t1, t2);
    >
    > --pg_publication_rel entries:
    > pubname | schema_name | table_name | prexcept
    > --------+-------------+------------+---------
    > pub1    | s2          | t1         | t
    > pub1    | s2          | t2         | t
    >
    >
    > --Then added a partition:
    > alter publication pub1 add table s2.tab_part_1_p2;
    >
    > --pg_publication_rel entries:
    > pubname | schema_name |  table_name   | prexcept
    > --------+-------------+---------------+---------
    > pub1    | s2          | t1            | t
    > pub1    | s2          | t2            | t
    > pub1    | s2          | tab_part_1_p2 | f
    >
    >
    > --Then changed pub1 to exclude tab_root (parent of tab_part_1_p2) as well:
    >
    > alter publication pub1 set tables in schema s2 EXCEPT ( TABLE t1, t2, tab_root);
    >
    > --pg_publication_rel entries:
    > pubname | schema_name | table_name | prexcept
    > --------+-------------+------------+---------
    > pub1    | s2          | t1         | t
    > pub1    | s2          | t2         | t
    > pub1    | s2          | tab_root   | t
    >
    > Note theoutput above, tab_part_1_p2 is removed internally as part of
    > earlier command since its parent tab_root is now excluded.
    >
    > --Now add parition back, it allowed to add it:
    > alter publication pub1 add table s2.tab_part_1_p2;
    >
    > pg_publication_rel entries:
    > pubname | schema_name |  table_name   | prexcept
    > --------+-------------+---------------+---------
    > pub1    | s2          | t1            | t
    > pub1    | s2          | t2            | t
    > pub1    | s2          | tab_root      | t
    > pub1    | s2          | tab_part_1_p2 | f
    >
    >
    > FOR ALL TABLES publicaiton, if root is excluded, there is no way
    > publication can have parition included independently. Same should hold
    > true here. Thoughts?
    
    Right, based on the discussion at [1], since we follow "all partitions
    are excluded if the root is excluded", a partition child should not be
    allowed to be added when its partition root is already excluded from
    the publication.
    Fixed. Such an operation will now raise an error:
    
    postgres=# alter publication pub2 add table t_part_p1;
    ERROR:  cannot add table "public.t_part_p1" to publication "pub2"
    DETAIL:  Partition ancestor "public.t_part" of table
    "public.t_part_p1" is currently listed in the EXCEPT clause of the
    publication.
    HINT:  Change EXCEPT list using ALTER PUBLICATION ... SET TABLES IN
    SCHEMA ... EXCEPT.
    ~~~
    
    Attached are the updated patches v18.
    
    [1] https://www.postgresql.org/message-id/CAA4eK1%2BNmQRjSHPLr0X8YBuC6joivFqgsY3_qJ5-RnuuwNGkRQ%40mail.gmail.com
    --
    Thanks,
    Nisha
    
  87. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-07-03T16:45:14Z

    On Thu, Jul 2, 2026 at 2:34 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, Jul 1, 2026 at 3:54 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Wed, Jul 1, 2026 at 3:42 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > During tests, I found one behavior that needs discussion:
    > > >
    > > > Consider a partition hierarchy where the root table is in schema s2, a
    > > > child partition is in schema s1, and the publication includes both
    > > > schemas but excludes only the root table.
    > >
    > > This is the exact scenario I had in mind when reviewing
    > > 'get_rel_sync_entry-->GetTopMostAncestorInPublication' flow, thus I
    > > suggested adding comments about intentionally fetching the 'Exclude
    > > list' only for the Root. I was about to test it in my next review
    > > cycle.
    > >
    > > The first thought is that if root is excluded, the partition should
    > > also be excluded to maintain consistency with the 'ALL TABLES Except
    > > ROOT' behaviour. But then it needs to be documented too.
    > >
    >
    > +1. It is good to be consistent here with similar ALL TABLES case.
    > BTW, as we can't exclude the child table explicitly, this anyway seems
    > like the only option because otherwise, users won't have any way to
    > exclude the child table.
    >
    
    I've updated the related comments in v18 and also updated the CREATE
    PUBLICATION docs to clarify this behavior.
    
    --
    Thanks,
    Nisha
    
    
    
    
  88. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-07-06T03:11:53Z

    On Fri, Jul 3, 2026 at 10:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Thu, Jul 2, 2026 at 2:19 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > A few comments:
    > >
    > > 1)
    > > postgres=# alter publication pub1 add table s2.t1;
    > > ERROR:  table "s2.t1" cannot be added because it is excluded from
    > > publication "pub1"
    > >
    > > It will be good to add hint in above error.
    > >
    > > Suggestion:
    > > ERROR: cannot add table "s2.t1" to publication "pub1"
    > > DETAIL: Table "s2.t1" is currently in EXCEPT list
    > > HINT: Change EXCEPT list using ALTER PUBLICATION ..SET TABLES IN
    > > SCHEMA .. EXCEPT
    > >
    > > Please rephrase above as needed. Look earlier such messages/hints
    > > already added for ALL TABLES.
    > >
    >
    > Updated the error as suggested, with a slight change in DETAIL message.
    >
    > > From the user's perspective, it seems slightly odd that if he has
    > > excluded a table from publication, he can not add it back using 'add
    > > table'. Was this point discussed earlier?
    > >
    >
    > This hasn't been discussed yet.
    >
    > I kept the current behavior, as IMO, it is hard to tell whether the
    > user is trying to re-include a table or whether the ADD is unintended
    > and should be blocked. To avoid ambiguity, I think EXCEPT entries
    > should be modified only through the EXCEPT (...) clause itself, such
    > as ALTER PUBLICATION ... ADD/SET ... EXCEPT(TABLE ...), or by omitting
    > EXCEPT in the SET case.
    >
    > This would also keep the behavior of ADD TABLE and DROP TABLE aligned,
    > as both would operate only on the include list without touching the
    > EXCEPT list.
    > I think this also aligns with FOR ALL TABLES, where ADD TABLE is
    > disallowed, so the EXCEPT list can only be modified through SET ..
    > EXCEPT (...).
    >
    > Thoughts?
    
    Okay. Makes sense.
    
    >
    > > 2)
    > > Currently:
    > > postgres=# alter publication pub1 drop table s2.t1;
    > > ERROR:  relation "t1" is not part of the publication
    > >
    > > s2.t1 is EXCEPT list here.
    > >
    > > Should we have below error for this case rather than above generic
    > > error, it will be more intuitive for user. Plus both add and drop
    > > table commands will have similar error-style for EXCEPT list.
    > > Thoughts?
    > >
    > > ERROR: cannot drop table "s2.t1" from publication "pub1"
    > > DETAIL: Table "s2.t1" is currently in EXCEPT list
    > > HINT: Change EXCEPT list using ALTER PUBLICATION ..SET TABLES IN
    > > SCHEMA .. EXCEPT
    > >
    >
    > Makes sense. Fixed.
    >
    > > 3)
    > > Please see below test:
    > >
    > > --Setting Except list for pub1 which has all tables of s2 included:
    > > alter publication pub1 set tables in schema s2 EXCEPT ( TABLE t1, t2);
    > >
    > > --pg_publication_rel entries:
    > > pubname | schema_name | table_name | prexcept
    > > --------+-------------+------------+---------
    > > pub1    | s2          | t1         | t
    > > pub1    | s2          | t2         | t
    > >
    > >
    > > --Then added a partition:
    > > alter publication pub1 add table s2.tab_part_1_p2;
    > >
    > > --pg_publication_rel entries:
    > > pubname | schema_name |  table_name   | prexcept
    > > --------+-------------+---------------+---------
    > > pub1    | s2          | t1            | t
    > > pub1    | s2          | t2            | t
    > > pub1    | s2          | tab_part_1_p2 | f
    > >
    > >
    > > --Then changed pub1 to exclude tab_root (parent of tab_part_1_p2) as well:
    > >
    > > alter publication pub1 set tables in schema s2 EXCEPT ( TABLE t1, t2, tab_root);
    > >
    > > --pg_publication_rel entries:
    > > pubname | schema_name | table_name | prexcept
    > > --------+-------------+------------+---------
    > > pub1    | s2          | t1         | t
    > > pub1    | s2          | t2         | t
    > > pub1    | s2          | tab_root   | t
    > >
    > > Note theoutput above, tab_part_1_p2 is removed internally as part of
    > > earlier command since its parent tab_root is now excluded.
    > >
    > > --Now add parition back, it allowed to add it:
    > > alter publication pub1 add table s2.tab_part_1_p2;
    > >
    > > pg_publication_rel entries:
    > > pubname | schema_name |  table_name   | prexcept
    > > --------+-------------+---------------+---------
    > > pub1    | s2          | t1            | t
    > > pub1    | s2          | t2            | t
    > > pub1    | s2          | tab_root      | t
    > > pub1    | s2          | tab_part_1_p2 | f
    > >
    > >
    > > FOR ALL TABLES publicaiton, if root is excluded, there is no way
    > > publication can have parition included independently. Same should hold
    > > true here. Thoughts?
    >
    > Right, based on the discussion at [1], since we follow "all partitions
    > are excluded if the root is excluded", a partition child should not be
    > allowed to be added when its partition root is already excluded from
    > the publication.
    > Fixed. Such an operation will now raise an error:
    >
    > postgres=# alter publication pub2 add table t_part_p1;
    > ERROR:  cannot add table "public.t_part_p1" to publication "pub2"
    > DETAIL:  Partition ancestor "public.t_part" of table
    > "public.t_part_p1" is currently listed in the EXCEPT clause of the
    > publication.
    > HINT:  Change EXCEPT list using ALTER PUBLICATION ... SET TABLES IN
    > SCHEMA ... EXCEPT.
    > ~~~
    
    Okay, looks good.
    
    thanks
    Shveta
    
    
    
    
  89. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-07-06T07:55:19Z

    Hi Nisha.
    
    Some review comments for v18.
    
    //////////
    Patch v18-0001
    //////////
    
    src/backend/catalog/pg_publication.c
    
    1.
    + /*
    + * A partition cannot be independently included when any of its partition
    + * ancestors is in this publication's EXCEPT list.  Doing so would leave
    + * the catalog inconsistent (root excluded, partition explicitly included)
    + * and the ancestor-cascade rule would silently override the include at
    + * replication time.  Individual partitions cannot appear in EXCEPT
    + * (rejected above), so this check only applies to non-EXCEPT adds.
    + */
    + if (!pri->except && targetrel->rd_rel->relispartition)
    
    It might be tidier to deal with partitions in one place.
    
    SUGGESTION
    if (targetrel->rd_rel->relispartition)
    {
      if (pri->except)
      {
        /* If in EXCEPT clause, must be root partitioned table */
        ...
      } else {
        /* If not in EXCEPT clause, check ancestors are not excluded */
        ...
      }
    }
    
    ~~~
    
    2.
    + errmsg("cannot add table \"%s\" to publication \"%s\"",
    + RelationGetQualifiedRelationName(targetrel),
    + get_publication_name(pubid, false)),
    + errdetail("Partition ancestor \"%s\" of table \"%s\" is currently
    listed in the EXCEPT clause of the publication.",
    
    "Partition ancestor .. of table" sounded odd.
    
    How about:
    errmsg:  cannot add partition \"%s\" to ...
    errdetail: Ancestor \"%s\" of partition \"%s\" is currently ...
    
    ~~~
    
    publication_add_relation:
    
    3.
    + ereport(ERROR,
    + (errcode(ERRCODE_DUPLICATE_OBJECT),
    + errmsg("cannot add table \"%s\" to publication \"%s\"",
    + RelationGetQualifiedRelationName(targetrel),
    + pub->name),
    + errdetail("Table \"%s\" is currently listed in the EXCEPT clause of
    the publication.",
    
    The table was already named in the errmsg. It seemed redundant to name
    it again in the errdetail.
    
    SUGGESTION
    errdetail("The table is currently listed in the EXCEPT clause of the
    publication.")
    
    ======
    src/backend/commands/publicationcmds.c
    
    4.
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    + errmsg("table \"%s\" cannot appear in both the table list and the
    EXCEPT clause",
    +    RelationGetQualifiedRelationName(pri->relation)));
    
    Do we need to name the clauses in this errmsg? Users can see what they typed.
    
    SUGGESTION
    errmsg("table \"%s\" cannot be both published and excluded")
    
    //////////
    Patch v18-0003
    //////////
    
    src/backend/commands/publicationcmds.c
    
    PublicationDropTables:
    
    1.
    + (errcode(ERRCODE_UNDEFINED_OBJECT),
    + errmsg("cannot drop table \"%s\" from publication \"%s\"",
    + RelationGetQualifiedRelationName(rel),
    + get_publication_name(pubid, false)),
    + errdetail("Table \"%s\" is currently listed in the EXCEPT clause of
    the publication.",
    +    RelationGetQualifiedRelationName(rel)),
    + errhint("Change EXCEPT list using ALTER PUBLICATION ... SET TABLES
    IN SCHEMA ... EXCEPT.")));
    
    1a.
    (Same as above review comment for patch 0001).
    
    The table was already named in the errmsg. It seemed redundant to name
    it again in the errdetail.
    
    SUGGESTION
    errdetail("The table is currently listed in the EXCEPT clause of the
    publication.")
    
    ~
    
    1b.
    Why hint to change the except list -- who says the EXCEPT list is wrong?
    
    It seemed more like a user error to me if they are trying to drop
    something from a publication when it is already excluded. Why does
    this need any hint at all?
    
    ======
    src/test/regress/sql/publication.sql
    
    2.
    I did not recognise the distinction in the test comments between
    "fail:" and "error:".
    
    ~~~
    
    3.
    +-- error: ALTER PUBLICATION ... DROP TABLE on an excluded table is rejected
    +-- with an EXCEPT-specific error; an EXCEPT entry is not a member.
    
    I don't think you need to say "an EXCEPT entry is not a member." --
    what does that mean, other than to redundantly say an excluded table
    is a table that is excluded?
    
    //////////
    Patch v18-0004
    //////////
    
    doc/src/sgml/ref/create_publication.sgml
    
    1.
          <para>
           For partitioned tables, only the root partitioned table may be specified
           in <literal>EXCEPT</literal>. Doing so excludes the root table and
    -      all of its partitions from replication. The optional
    +      all of its partitions from replication, even any partition that lives
    +      in a schema which is itself included in the publication.
    
    BEFORE
    For partitioned tables, only the root partitioned table may be
    specified in EXCEPT. Doing so excludes the root table and all of its
    partitions from replication, even any partition that lives in a schema
    which is itself included in the publication.
    
    SUGGESTION #1
    For partitioned tables, only the root partitioned table may be
    specified in EXCEPT. Excluding the root table automatically excludes
    all of its partitions from replication, including those in schemas
    that are otherwise included in the publication.
    
    SUGGESTION #2 (TI doubt you even needed to mention about other schemas)
    For partitioned tables, only the root partitioned table may be
    specified in EXCEPT. Excluding the root table automatically excludes
    all of its partitions from replication, including those that would
    otherwise be included in the publication.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  90. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Peter Smith <smithpb2250@gmail.com> — 2026-07-06T23:01:00Z

    Hi Nisha.
    
    Bug: The current patch is allowing conflicting exclusion lists...
    
    ======
    
    Background:
    
    It's always been possible for CREATE PUBLICATION to specify the same
    schema or same table multiple times.
    
    test_pub=# CREATE SCHEMA MYSCHEMA;
    CREATE SCHEMA
    test_pub=# CREATE TABLE T1(A INT);
    CREATE TABLE
    test_pub=# CREATE TABLE MYSCHEMA.T2(A INT);
    CREATE TABLE
    test_pub=# CREATE TABLE MYSCHEMA.T3(A INT);
    CREATE TABLE
    
    // same table multiple times is OK
    test_pub=# CREATE PUBLICATION PUB3 FOR TABLE T1,T1,T1,T1;
    CREATE PUBLICATION
    
    // same schema multiple times is OK
    test_pub=# CREATE PUBLICATION PUB1 FOR TABLES IN SCHEMA MYSCHEMA,
    MYSCHEMA, MYSCHEMA;
    CREATE PUBLICATION
    
    ~~~
    
    But, this behaviour is only allowed if there are no specification
    conflicts. e.g. You can't have multiple tables with column lists:
    
    test_pub=# CREATE PUBLICATION PUB4 FOR TABLE T1,T1(A),T1,T1;
    ERROR:  conflicting or redundant column lists for table "t1"
    
    ======
    
    Bug:
    
    IMO similar restrictions and a similar error message needs to happen
    for clashing schema exclusions. Specifying the same schema multiple
    times is fine, but it must be disallowed if there are conflicting
    exclusion lists.
    
    For example, the command below currently works, but it should not:
    
    test_pub=# CREATE PUBLICATION PUB2 FOR TABLES IN SCHEMA MYSCHEMA
    EXCEPT (TABLE T2), MYSCHEMA EXCEPT (TABLE T3), MYSCHEMA;
    CREATE PUBLICATION
    
    test_pub=# \dRp+ PUB2
                                                           Publication pub2
      Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Descri
    ption
    ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------
    ------
     postgres | f          | f             | t       | t       | t       |
    t         | none              | f        |
    Tables from schemas:
        "myschema"
    Except tables:
        "myschema.t2"
        "myschema.t3"
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  91. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-07-07T03:09:48Z

    On Tue, Jul 7, 2026 at 4:31 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Bug: The current patch is allowing conflicting exclusion lists...
    >
    > ======
    >
    > Background:
    >
    > It's always been possible for CREATE PUBLICATION to specify the same
    > schema or same table multiple times.
    >
    > test_pub=# CREATE SCHEMA MYSCHEMA;
    > CREATE SCHEMA
    > test_pub=# CREATE TABLE T1(A INT);
    > CREATE TABLE
    > test_pub=# CREATE TABLE MYSCHEMA.T2(A INT);
    > CREATE TABLE
    > test_pub=# CREATE TABLE MYSCHEMA.T3(A INT);
    > CREATE TABLE
    >
    > // same table multiple times is OK
    > test_pub=# CREATE PUBLICATION PUB3 FOR TABLE T1,T1,T1,T1;
    > CREATE PUBLICATION
    >
    > // same schema multiple times is OK
    > test_pub=# CREATE PUBLICATION PUB1 FOR TABLES IN SCHEMA MYSCHEMA,
    > MYSCHEMA, MYSCHEMA;
    > CREATE PUBLICATION
    >
    > ~~~
    >
    > But, this behaviour is only allowed if there are no specification
    > conflicts. e.g. You can't have multiple tables with column lists:
    >
    > test_pub=# CREATE PUBLICATION PUB4 FOR TABLE T1,T1(A),T1,T1;
    > ERROR:  conflicting or redundant column lists for table "t1"
    >
    > ======
    >
    > Bug:
    >
    > IMO similar restrictions and a similar error message needs to happen
    > for clashing schema exclusions. Specifying the same schema multiple
    > times is fine, but it must be disallowed if there are conflicting
    > exclusion lists.
    >
    
    +1. Good Catch.
    
    > For example, the command below currently works, but it should not:
    >
    > test_pub=# CREATE PUBLICATION PUB2 FOR TABLES IN SCHEMA MYSCHEMA
    > EXCEPT (TABLE T2), MYSCHEMA EXCEPT (TABLE T3), MYSCHEMA;
    > CREATE PUBLICATION
    >
    > test_pub=# \dRp+ PUB2
    >                                                        Publication pub2
    >   Owner   | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root | Descri
    > ption
    > ----------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------
    > ------
    >  postgres | f          | f             | t       | t       | t       |
    > t         | none              | f        |
    > Tables from schemas:
    >     "myschema"
    > Except tables:
    >     "myschema.t2"
    >     "myschema.t3"
    >
    > ======
    > Kind Regards,
    > Peter Smith.
    > Fujitsu Australia
    >
    >
    
    
    
    
  92. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-07-07T09:01:46Z

    On Fri, Jul 3, 2026 at 10:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    >
    > postgres=# alter publication pub2 add table t_part_p1;
    > ERROR:  cannot add table "public.t_part_p1" to publication "pub2"
    > DETAIL:  Partition ancestor "public.t_part" of table
    > "public.t_part_p1" is currently listed in the EXCEPT clause of the
    > publication.
    > HINT:  Change EXCEPT list using ALTER PUBLICATION ... SET TABLES IN
    > SCHEMA ... EXCEPT.
    > ~~~
    
    Thanks. Do you think we should restrict this case too? To me, this
    seems like another variation of the above where conflicting entries
    are given at the time of sub-creation itself. The root is excluded but
    parittion is included .
    
    postgres=# create publication pub1 for table s2.tab_part_1, tables in
    schema s2 except (table tab_root);
    CREATE PUBLICATION
    postgres=# \dRp+
                                                          Publication pub1
     Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Description
    --------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
     shveta | f          | f             | t       | t       | t       | t
            | none              | f        |
    Tables:
        "s2.tab_part_1"
    Tables from schemas:
        "s2"
    Except tables:
        "s2.tab_root"
    
    thanks
    Shveta
    
    
    
    
  93. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-07-07T10:43:17Z

    On Tue, Jul 7, 2026 at 2:31 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Fri, Jul 3, 2026 at 10:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > >
    > > postgres=# alter publication pub2 add table t_part_p1;
    > > ERROR:  cannot add table "public.t_part_p1" to publication "pub2"
    > > DETAIL:  Partition ancestor "public.t_part" of table
    > > "public.t_part_p1" is currently listed in the EXCEPT clause of the
    > > publication.
    > > HINT:  Change EXCEPT list using ALTER PUBLICATION ... SET TABLES IN
    > > SCHEMA ... EXCEPT.
    > > ~~~
    >
    > Thanks. Do you think we should restrict this case too? To me, this
    > seems like another variation of the above where conflicting entries
    > are given at the time of sub-creation itself. The root is excluded but
    > parittion is included .
    >
    > postgres=# create publication pub1 for table s2.tab_part_1, tables in
    > schema s2 except (table tab_root);
    > CREATE PUBLICATION
    > postgres=# \dRp+
    >                                                       Publication pub1
    >  Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root | Description
    > --------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    >  shveta | f          | f             | t       | t       | t       | t
    >         | none              | f        |
    > Tables:
    >     "s2.tab_part_1"
    > Tables from schemas:
    >     "s2"
    > Except tables:
    >     "s2.tab_root"
    >
    
    Since alter is blocked for above case while create-sub is not, I had a
    look at the new check added in check_publication_add_relation
    
    a) The check introduced in this function for partitions/ancestors is
    sufficient for cases where an except-list is already present, but not
    for cases where we are in the process of adding one. Thus the
    create-sub scenario above is not covered by this.
    
    b) Also the new check in check_publication_add_relation() gets and
    traverses complete list of ancestors
    
    + List    *ancestors = get_partition_ancestors(relid);
    +
    + foreach_oid(ancestor, ancestors)
    
    Isn't getting root (llast_oid(ancestors)) and checking that alone in
    EXCEPT list will suffice? Or am I missing something?
    
    thanks
    Shveta
    
    
    
    
  94. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Amit Kapila <amit.kapila16@gmail.com> — 2026-07-08T06:28:00Z

    On Tue, Jul 7, 2026 at 4:13 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Jul 7, 2026 at 2:31 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Fri, Jul 3, 2026 at 10:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > >
    > > > postgres=# alter publication pub2 add table t_part_p1;
    > > > ERROR:  cannot add table "public.t_part_p1" to publication "pub2"
    > > > DETAIL:  Partition ancestor "public.t_part" of table
    > > > "public.t_part_p1" is currently listed in the EXCEPT clause of the
    > > > publication.
    > > > HINT:  Change EXCEPT list using ALTER PUBLICATION ... SET TABLES IN
    > > > SCHEMA ... EXCEPT.
    > > > ~~~
    > >
    > > Thanks. Do you think we should restrict this case too? To me, this
    > > seems like another variation of the above where conflicting entries
    > > are given at the time of sub-creation itself. The root is excluded but
    > > parittion is included .
    > >
    > > postgres=# create publication pub1 for table s2.tab_part_1, tables in
    > > schema s2 except (table tab_root);
    > > CREATE PUBLICATION
    > > postgres=# \dRp+
    > >                                                       Publication pub1
    > >  Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > > Truncates | Generated columns | Via root | Description
    > > --------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > >  shveta | f          | f             | t       | t       | t       | t
    > >         | none              | f        |
    > > Tables:
    > >     "s2.tab_part_1"
    > > Tables from schemas:
    > >     "s2"
    > > Except tables:
    > >     "s2.tab_root"
    > >
    >
    > Since alter is blocked for above case while create-sub is not,
    >
    
    If a combination is not supported in ALTER, we should disallow the
    same in CREATE as well. So, +1 to restrict the case you reported.
    
    > I had a
    > look at the new check added in check_publication_add_relation
    >
    > a) The check introduced in this function for partitions/ancestors is
    > sufficient for cases where an except-list is already present, but not
    > for cases where we are in the process of adding one. Thus the
    > create-sub scenario above is not covered by this.
    >
    > b) Also the new check in check_publication_add_relation() gets and
    > traverses complete list of ancestors
    >
    > + List    *ancestors = get_partition_ancestors(relid);
    > +
    > + foreach_oid(ancestor, ancestors)
    >
    > Isn't getting root (llast_oid(ancestors)) and checking that alone in
    > EXCEPT list will suffice? Or am I missing something?
    >
    
    I think this should be possible because we allow only root tables in
    the EXCEPT list. If so, and we are planning to change as per your
    suggestion then we should add a comment as to why checking only last
    is okay here.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  95. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-07-08T08:52:31Z

    Nisha, the patch (testing wise) is okay, but some code-optimization
    might help. I am stuck on reveiw of GetTopMostAncestorInPublication()
    and all its callers.
    
    1)
    caller 1: pub_rf_contains_invalid_column():
    
    It is not understood why pub_rf_contains_invalid_column() does not
    pass 'exceptPubids' to GetTopMostAncestorInPublication(). Perhaps a
    comment will help. IIUC, a parittion (for which we are trying to
    verify row-filter expression) can not co-exist in a subscription where
    its ROOT is already excluded. This assumption will be true once you
    address the issue of CREATE SUB (in [1]) sent in my previous email.
    After that, this will still need a comment.
    
    2)
    Both caller 2 (get_rel_sync_entry) and caller 3
    (is_table_publishable_in_publication) are trying to see if partition
    should be considered as included in publication and thus are trying to
    see if ROOT is excluded. But the implementation is very different.
    
    The get_rel_sync_entry() does that by passing except_pubids to
    GetTopMostAncestorInPublication(). This except_pubids was computed for
    topmost ROOT of the parition in the caller and then it skips checking
    pg_pub_namepspace if given pubid is part of except_pubids arguement.
    
    While is_table_publishable_in_publication() does that by selecting
    topmost ancestor incuded in a given pubid using
    GetTopMostAncestorInPublication(), without consulting/computing
    except_pubids. And then later checks if the ancestor returned by
    GetTopMostAncestorInPublication() is in pg_publicaiton_rel with
    'prexcept' true.
    
    IMO, GetTopMostAncestorInPublication() should itself be inclusive of
    logic where it filters out the table (does not return it as result) if
    ROOT is excluded. And even we should not be passing an argument for
    that (this is my initial thought). By making such a logic, we need not
    to bother about all
    the callers to see if caller has correct logic to deal with output of
    GetTopMostAncestorInPublication(). Can you think on this line and
    check the feasibility.
    
    3)
    I have yet to reveiw pub_contains_invalid_column, but I have a initial
    understanding, that it will be similar to the case of
    pub_rf_contains_invalid_column. Please verify on your end.
    
    [1]: https://www.postgresql.org/message-id/CAJpy0uAuL7SqowL7T6HCELuVUr5ubwoGsjV1SQQRCXGme5L%3DpA%40mail.gmail.com
    
    thanks
    Shveta
    
    
    
    
  96. Re: Support EXCEPT for TABLES IN SCHEMA publications

    shveta malik <shveta.malik@gmail.com> — 2026-07-08T09:28:15Z

    On Wed, Jul 8, 2026 at 2:22 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > Nisha, the patch (testing wise) is okay, but some code-optimization
    > might help. I am stuck on reveiw of GetTopMostAncestorInPublication()
    > and all its callers.
    >
    > 1)
    > caller 1: pub_rf_contains_invalid_column():
    >
    > It is not understood why pub_rf_contains_invalid_column() does not
    > pass 'exceptPubids' to GetTopMostAncestorInPublication(). Perhaps a
    > comment will help. IIUC, a parittion (for which we are trying to
    > verify row-filter expression) can not co-exist in a subscription where
    > its ROOT is already excluded. This assumption will be true once you
    > address the issue of CREATE SUB (in [1]) sent in my previous email.
    > After that, this will still need a comment.
    >
    > 2)
    > Both caller 2 (get_rel_sync_entry) and caller 3
    > (is_table_publishable_in_publication) are trying to see if partition
    > should be considered as included in publication and thus are trying to
    > see if ROOT is excluded. But the implementation is very different.
    >
    > The get_rel_sync_entry() does that by passing except_pubids to
    > GetTopMostAncestorInPublication(). This except_pubids was computed for
    > topmost ROOT of the parition in the caller and then it skips checking
    > pg_pub_namepspace if given pubid is part of except_pubids arguement.
    >
    > While is_table_publishable_in_publication() does that by selecting
    > topmost ancestor incuded in a given pubid using
    > GetTopMostAncestorInPublication(), without consulting/computing
    > except_pubids. And then later checks if the ancestor returned by
    > GetTopMostAncestorInPublication() is in pg_publicaiton_rel with
    > 'prexcept' true.
    >
    > IMO, GetTopMostAncestorInPublication() should itself be inclusive of
    > logic where it filters out the table (does not return it as result) if
    > ROOT is excluded. And even we should not be passing an argument for
    > that (this is my initial thought). By making such a logic, we need not
    > to bother about all
    > the callers to see if caller has correct logic to deal with output of
    > GetTopMostAncestorInPublication(). Can you think on this line and
    > check the feasibility.
    >
    
    I thought more on this. Do you think we can do this?
    
    GetTopMostAncestorInPublication() is already accepting a list of
    ancestors in a ordered fashion, root at the end. We get root from this
    ancestor list, check if root is in pg_publication_rel with
    except=true. If so, we skip the rest of the logic and return
    InvalidOid from GetTopMostAncestorInPublication().
    
    The 'except_pubids' argument is not required. Callers need not to have
    special logic to send this arguement or to have extra-processing on
    output as done by is_table_publishable_in_publication() currently.
    Let me know if I have missed anything.
    
    thanks
    Shveta
    
    
    
    
  97. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-07-08T15:20:25Z

    On Tue, Jul 7, 2026 at 8:40 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Jul 7, 2026 at 4:31 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Hi Nisha.
    > >
    > > Bug: The current patch is allowing conflicting exclusion lists...
    > >
    > > ======
    > >
    > > Background:
    > >
    > > It's always been possible for CREATE PUBLICATION to specify the same
    > > schema or same table multiple times.
    > >
    > > test_pub=# CREATE SCHEMA MYSCHEMA;
    > > CREATE SCHEMA
    > > test_pub=# CREATE TABLE T1(A INT);
    > > CREATE TABLE
    > > test_pub=# CREATE TABLE MYSCHEMA.T2(A INT);
    > > CREATE TABLE
    > > test_pub=# CREATE TABLE MYSCHEMA.T3(A INT);
    > > CREATE TABLE
    > >
    > > // same table multiple times is OK
    > > test_pub=# CREATE PUBLICATION PUB3 FOR TABLE T1,T1,T1,T1;
    > > CREATE PUBLICATION
    > >
    > > // same schema multiple times is OK
    > > test_pub=# CREATE PUBLICATION PUB1 FOR TABLES IN SCHEMA MYSCHEMA,
    > > MYSCHEMA, MYSCHEMA;
    > > CREATE PUBLICATION
    > >
    > > ~~~
    > >
    > > But, this behaviour is only allowed if there are no specification
    > > conflicts. e.g. You can't have multiple tables with column lists:
    > >
    > > test_pub=# CREATE PUBLICATION PUB4 FOR TABLE T1,T1(A),T1,T1;
    > > ERROR:  conflicting or redundant column lists for table "t1"
    > >
    > > ======
    > >
    > > Bug:
    > >
    > > IMO similar restrictions and a similar error message needs to happen
    > > for clashing schema exclusions. Specifying the same schema multiple
    > > times is fine, but it must be disallowed if there are conflicting
    > > exclusion lists.
    > >
    >
    > +1. Good Catch.
    >
    
    Thanks for the bug report. I've fixed it and attached the fix as a
    separate top-up patch, v19-0002.
    
    Until v18, schema qualification checks for EXCEPT tables were split
    between two places:
    
    1) TABLES IN SCHEMA <schema-name> (EXCEPT TABLE <table-name>);
     -- the schema qualification check was performed in gram.y, since all
    required information was available during parsing.
    
    2) TABLES IN SCHEMA CURRENT_SCHEMA (EXCEPT TABLE <table-name>);
     -- CURRENT_SCHEMA is resolved only at execution time, so the schema
    qualification and eligibility checks were performed there.
    
    The reported bug also affects cases such as:
    CREATE PUBLICATION PUB2 FOR TABLES IN SCHEMA MYSCHEMA EXCEPT (TABLE
    T2), CURRENT_SCHEMA EXCEPT (TABLE T3);
     -- where CURRENT_SCHEMA = myschema. Correct handling of this case
    requires the checks to be performed after schema resolution, i.e., at
    execution time. To reduce duplicate code, I moved the schema
    qualification and eligibility checks for both cases to execution time
    and removed the corresponding processing from gram.y.
    
    Since this changes a fair amount of code, I've kept it as a separate
    patch (v19-0002) to make review easier. Depending on the feedback, we
    can optimize the implementation or move parts of it back into gram.y
    before merging it into patch 0001.
    ~~~
    
    Attached is the v19 patch set.
     -- Also Addressed all comments from Peter at [1] and Shveta at [2].
     -- The code optimization suggestions from Shveta at [3] are still
    pending. I'm evaluating them and plan to address them in the next
    version.
    ~~~
    
    [1] https://www.postgresql.org/message-id/CAHut%2BPt-%2B6Q4-kmhqCZ2_FpaNRR%3DhaJk-GK1eEyTXcTTDnRnXA%40mail.gmail.com
    [2] https://www.postgresql.org/message-id/CAJpy0uAuL7SqowL7T6HCELuVUr5ubwoGsjV1SQQRCXGme5L%3DpA%40mail.gmail.com
    [3] https://www.postgresql.org/message-id/CAJpy0uA_d-K-f94d-9Xw66Fqv7gdSGBYGzi9YcybtuEkYdQocQ%40mail.gmail.com
    
    --
    Thanks,
    Nisha
    
  98. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-07-08T15:22:17Z

    On Mon, Jul 6, 2026 at 1:25 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Nisha.
    >
    > Some review comments for v18.
    >
    > //////////
    > Patch v18-0001
    > //////////
    ...
    >
    > ~
    >
    > 1b.
    > Why hint to change the except list -- who says the EXCEPT list is wrong?
    >
    > It seemed more like a user error to me if they are trying to drop
    > something from a publication when it is already excluded. Why does
    > this need any hint at all?
    
    Okay, I see your point. The intent of the hint was to guide users who
    are actually trying to remove a table from the EXCEPT list by pointing
    them to SET.
    
    I've reworded it to:
    HINT: Use ALTER PUBLICATION ... SET TABLES IN SCHEMA ... EXCEPT to
    drop the table from the EXCEPT list.
    
    Hopefully this is clearer and more helpful. Let me know if this works,
    or if you still think no hint is the better option.
    
    >
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 2.
    > I did not recognise the distinction in the test comments between
    > "fail:" and "error:".
    >
    
    Fixed. I now use "fail" consistently throughout.
    
    ...
    >
    > //////////
    > Patch v18-0004
    > //////////
    >
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 1.
    >       <para>
    >        For partitioned tables, only the root partitioned table may be specified
    >        in <literal>EXCEPT</literal>. Doing so excludes the root table and
    > -      all of its partitions from replication. The optional
    > +      all of its partitions from replication, even any partition that lives
    > +      in a schema which is itself included in the publication.
    >
    > BEFORE
    > For partitioned tables, only the root partitioned table may be
    > specified in EXCEPT. Doing so excludes the root table and all of its
    > partitions from replication, even any partition that lives in a schema
    > which is itself included in the publication.
    >
    > SUGGESTION #1
    > For partitioned tables, only the root partitioned table may be
    > specified in EXCEPT. Excluding the root table automatically excludes
    > all of its partitions from replication, including those in schemas
    > that are otherwise included in the publication.
    >
    > SUGGESTION #2 (TI doubt you even needed to mention about other schemas)
    > For partitioned tables, only the root partitioned table may be
    > specified in EXCEPT. Excluding the root table automatically excludes
    > all of its partitions from replication, including those that would
    > otherwise be included in the publication.
    >
    
    I lean towards Suggestion #1, as not publishing a table/partition when
    its schema is published without any exception is a non-obvious case
    and should be stated explicitly.
    
    --
    Thanks,
    Nisha
    
    
    
    
  99. Re: Support EXCEPT for TABLES IN SCHEMA publications

    Nisha Moond <nisha.moond412@gmail.com> — 2026-07-08T15:22:45Z

    On Wed, Jul 8, 2026 at 11:58 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Jul 7, 2026 at 4:13 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Tue, Jul 7, 2026 at 2:31 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Fri, Jul 3, 2026 at 10:15 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > >
    > > > >
    > > > > postgres=# alter publication pub2 add table t_part_p1;
    > > > > ERROR:  cannot add table "public.t_part_p1" to publication "pub2"
    > > > > DETAIL:  Partition ancestor "public.t_part" of table
    > > > > "public.t_part_p1" is currently listed in the EXCEPT clause of the
    > > > > publication.
    > > > > HINT:  Change EXCEPT list using ALTER PUBLICATION ... SET TABLES IN
    > > > > SCHEMA ... EXCEPT.
    > > > > ~~~
    > > >
    > > > Thanks. Do you think we should restrict this case too? To me, this
    > > > seems like another variation of the above where conflicting entries
    > > > are given at the time of sub-creation itself. The root is excluded but
    > > > parittion is included .
    > > >
    > > > postgres=# create publication pub1 for table s2.tab_part_1, tables in
    > > > schema s2 except (table tab_root);
    > > > CREATE PUBLICATION
    > > > postgres=# \dRp+
    > > >                                                       Publication pub1
    > > >  Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > > > Truncates | Generated columns | Via root | Description
    > > > --------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
    > > >  shveta | f          | f             | t       | t       | t       | t
    > > >         | none              | f        |
    > > > Tables:
    > > >     "s2.tab_part_1"
    > > > Tables from schemas:
    > > >     "s2"
    > > > Except tables:
    > > >     "s2.tab_root"
    > > >
    > >
    > > Since alter is blocked for above case while create-sub is not,
    > >
    >
    > If a combination is not supported in ALTER, we should disallow the
    > same in CREATE as well. So, +1 to restrict the case you reported.
    >
    
    Fixed in v19.
    
    > > I had a
    > > look at the new check added in check_publication_add_relation
    > >
    > > a) The check introduced in this function for partitions/ancestors is
    > > sufficient for cases where an except-list is already present, but not
    > > for cases where we are in the process of adding one. Thus the
    > > create-sub scenario above is not covered by this.
    > >
    > > b) Also the new check in check_publication_add_relation() gets and
    > > traverses complete list of ancestors
    > >
    > > + List    *ancestors = get_partition_ancestors(relid);
    > > +
    > > + foreach_oid(ancestor, ancestors)
    > >
    > > Isn't getting root (llast_oid(ancestors)) and checking that alone in
    > > EXCEPT list will suffice? Or am I missing something?
    > >
    >
    > I think this should be possible because we allow only root tables in
    > the EXCEPT list. If so, and we are planning to change as per your
    > suggestion then we should add a comment as to why checking only last
    > is okay here.
    >
    
    Agreed; I implemented the suggested optimization and added comments
    explaining why checking only the last element is sufficient.
    
    --
    Thanks,
    Nisha