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. Fix miscellaneous issues in EXCEPT publication clause.

  2. Change syntax of EXCEPT TABLE clause in publication commands.

  3. Add support for EXCEPT TABLE in ALTER PUBLICATION.

  4. Allow table exclusions in publications via EXCEPT TABLE.

  5. Add wait_for_subscription_sync for TAP tests.

  1. Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-03-22T07:08:43Z

    Hi,
    
    This feature adds an option to skip changes of all tables in specified
    schema while creating publication.
    This feature is helpful for use cases where the user wants to
    subscribe to all the changes except for the changes present in a few
    schemas.
    Ex:
    CREATE PUBLICATION pub1 FOR ALL TABLES SKIP ALL TABLES IN SCHEMA s1,s2;
    OR
    ALTER PUBLICATION pub1 ADD SKIP ALL TABLES IN SCHEMA s1,s2;
    
    A new column pnskip is added to table "pg_publication_namespace", to
    maintain the schemas that the user wants to skip publishing through
    the publication. Modified the output plugin (pgoutput) to skip
    publishing the changes if the relation is part of skip schema
    publication.
    As a continuation to this, I will work on implementing skipping tables
    from all tables in schema and skipping tables from all tables
    publication.
    
    Attached patch has the implementation for this.
    This feature is for the pg16 version.
    Thoughts?
    
    Regards,
    Vignesh
    
  2. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-03-26T14:07:26Z

    On Tue, Mar 22, 2022 at 12:38 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Hi,
    >
    > This feature adds an option to skip changes of all tables in specified
    > schema while creating publication.
    > This feature is helpful for use cases where the user wants to
    > subscribe to all the changes except for the changes present in a few
    > schemas.
    > Ex:
    > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP ALL TABLES IN SCHEMA s1,s2;
    > OR
    > ALTER PUBLICATION pub1 ADD SKIP ALL TABLES IN SCHEMA s1,s2;
    >
    > A new column pnskip is added to table "pg_publication_namespace", to
    > maintain the schemas that the user wants to skip publishing through
    > the publication. Modified the output plugin (pgoutput) to skip
    > publishing the changes if the relation is part of skip schema
    > publication.
    > As a continuation to this, I will work on implementing skipping tables
    > from all tables in schema and skipping tables from all tables
    > publication.
    >
    > Attached patch has the implementation for this.
    
    The patch was not applying on top of HEAD because of the recent
    commits, attached patch is rebased on top of HEAD.
    
    Regards,
    Vignesh
    
  3. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-04-12T06:23:29Z

    On Sat, Mar 26, 2022 at 7:37 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Tue, Mar 22, 2022 at 12:38 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > This feature adds an option to skip changes of all tables in specified
    > > schema while creating publication.
    > > This feature is helpful for use cases where the user wants to
    > > subscribe to all the changes except for the changes present in a few
    > > schemas.
    > > Ex:
    > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP ALL TABLES IN SCHEMA s1,s2;
    > > OR
    > > ALTER PUBLICATION pub1 ADD SKIP ALL TABLES IN SCHEMA s1,s2;
    > >
    > > A new column pnskip is added to table "pg_publication_namespace", to
    > > maintain the schemas that the user wants to skip publishing through
    > > the publication. Modified the output plugin (pgoutput) to skip
    > > publishing the changes if the relation is part of skip schema
    > > publication.
    > > As a continuation to this, I will work on implementing skipping tables
    > > from all tables in schema and skipping tables from all tables
    > > publication.
    > >
    > > Attached patch has the implementation for this.
    >
    > The patch was not applying on top of HEAD because of the recent
    > commits, attached patch is rebased on top of HEAD.
    
    The patch does not apply on top of HEAD because of the recent commit,
    attached patch is rebased on top of HEAD.
    
    I have also included the implementation for skipping a few tables from
    all tables publication, the 0002 patch has the implementation for the
    same.
    This feature is helpful for use cases where the user wants to
    subscribe to all the changes except for the changes present in a few
    tables.
    Ex:
    CREATE PUBLICATION pub1 FOR ALL TABLES SKIP TABLE t1,t2;
    OR
    ALTER PUBLICATION pub1 ADD SKIP  TABLE t1,t2;
    
    Regards,
    Vignesh
    
  4. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-04-12T06:49:22Z

    On Tue, Apr 12, 2022 at 11:53 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Sat, Mar 26, 2022 at 7:37 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Tue, Mar 22, 2022 at 12:38 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > This feature adds an option to skip changes of all tables in specified
    > > > schema while creating publication.
    > > > This feature is helpful for use cases where the user wants to
    > > > subscribe to all the changes except for the changes present in a few
    > > > schemas.
    > > > Ex:
    > > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP ALL TABLES IN SCHEMA s1,s2;
    > > > OR
    > > > ALTER PUBLICATION pub1 ADD SKIP ALL TABLES IN SCHEMA s1,s2;
    > > >
    > > > A new column pnskip is added to table "pg_publication_namespace", to
    > > > maintain the schemas that the user wants to skip publishing through
    > > > the publication. Modified the output plugin (pgoutput) to skip
    > > > publishing the changes if the relation is part of skip schema
    > > > publication.
    > > > As a continuation to this, I will work on implementing skipping tables
    > > > from all tables in schema and skipping tables from all tables
    > > > publication.
    > > >
    > > > Attached patch has the implementation for this.
    > >
    > > The patch was not applying on top of HEAD because of the recent
    > > commits, attached patch is rebased on top of HEAD.
    >
    > The patch does not apply on top of HEAD because of the recent commit,
    > attached patch is rebased on top of HEAD.
    >
    > I have also included the implementation for skipping a few tables from
    > all tables publication, the 0002 patch has the implementation for the
    > same.
    > This feature is helpful for use cases where the user wants to
    > subscribe to all the changes except for the changes present in a few
    > tables.
    > Ex:
    > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP TABLE t1,t2;
    > OR
    > ALTER PUBLICATION pub1 ADD SKIP  TABLE t1,t2;
    >
    
    For the second syntax (Alter Publication ...), isn't it better to
    avoid using ADD? It looks odd to me because we are not adding anything
    in publication with this sytax.
    
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  5. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-04-12T10:46:53Z

    On Tue, Apr 12, 2022 at 12:19 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Apr 12, 2022 at 11:53 AM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Sat, Mar 26, 2022 at 7:37 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > On Tue, Mar 22, 2022 at 12:38 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > >
    > > > > Hi,
    > > > >
    > > > > This feature adds an option to skip changes of all tables in specified
    > > > > schema while creating publication.
    > > > > This feature is helpful for use cases where the user wants to
    > > > > subscribe to all the changes except for the changes present in a few
    > > > > schemas.
    > > > > Ex:
    > > > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP ALL TABLES IN SCHEMA s1,s2;
    > > > > OR
    > > > > ALTER PUBLICATION pub1 ADD SKIP ALL TABLES IN SCHEMA s1,s2;
    > > > >
    > > > > A new column pnskip is added to table "pg_publication_namespace", to
    > > > > maintain the schemas that the user wants to skip publishing through
    > > > > the publication. Modified the output plugin (pgoutput) to skip
    > > > > publishing the changes if the relation is part of skip schema
    > > > > publication.
    > > > > As a continuation to this, I will work on implementing skipping tables
    > > > > from all tables in schema and skipping tables from all tables
    > > > > publication.
    > > > >
    > > > > Attached patch has the implementation for this.
    > > >
    > > > The patch was not applying on top of HEAD because of the recent
    > > > commits, attached patch is rebased on top of HEAD.
    > >
    > > The patch does not apply on top of HEAD because of the recent commit,
    > > attached patch is rebased on top of HEAD.
    > >
    > > I have also included the implementation for skipping a few tables from
    > > all tables publication, the 0002 patch has the implementation for the
    > > same.
    > > This feature is helpful for use cases where the user wants to
    > > subscribe to all the changes except for the changes present in a few
    > > tables.
    > > Ex:
    > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP TABLE t1,t2;
    > > OR
    > > ALTER PUBLICATION pub1 ADD SKIP  TABLE t1,t2;
    > >
    >
    > For the second syntax (Alter Publication ...), isn't it better to
    > avoid using ADD? It looks odd to me because we are not adding anything
    > in publication with this sytax.
    
    I was thinking of the scenario where user initially creates the
    publication for all tables:
    CREATE PUBLICATION pub1 FOR ALL TABLES;
    
    After that user decides to skip few tables ex: t1, t2
     ALTER PUBLICATION pub1 ADD SKIP TABLE t1,t2;
    
    I thought of supporting this syntax if incase user decides to add the
    skipping of a few tables later.
    Thoughts?
    
    Regards,
    Vignesh
    
    
    
    
  6. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-04-12T11:16:28Z

    On Tue, Apr 12, 2022 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Tue, Apr 12, 2022 at 12:19 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > >
    > > For the second syntax (Alter Publication ...), isn't it better to
    > > avoid using ADD? It looks odd to me because we are not adding anything
    > > in publication with this sytax.
    >
    > I was thinking of the scenario where user initially creates the
    > publication for all tables:
    > CREATE PUBLICATION pub1 FOR ALL TABLES;
    >
    > After that user decides to skip few tables ex: t1, t2
    >  ALTER PUBLICATION pub1 ADD SKIP TABLE t1,t2;
    >
    > I thought of supporting this syntax if incase user decides to add the
    > skipping of a few tables later.
    >
    
    I understand that part but what I pointed out was that it might be
    better to avoid using ADD keyword in this syntax like: ALTER
    PUBLICATION pub1 SKIP TABLE t1,t2;
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  7. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-04-13T03:15:10Z

    On Tue, Apr 12, 2022 at 4:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Apr 12, 2022 at 4:17 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Tue, Apr 12, 2022 at 12:19 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > >
    > > > For the second syntax (Alter Publication ...), isn't it better to
    > > > avoid using ADD? It looks odd to me because we are not adding anything
    > > > in publication with this sytax.
    > >
    > > I was thinking of the scenario where user initially creates the
    > > publication for all tables:
    > > CREATE PUBLICATION pub1 FOR ALL TABLES;
    > >
    > > After that user decides to skip few tables ex: t1, t2
    > >  ALTER PUBLICATION pub1 ADD SKIP TABLE t1,t2;
    > >
    > > I thought of supporting this syntax if incase user decides to add the
    > > skipping of a few tables later.
    > >
    >
    > I understand that part but what I pointed out was that it might be
    > better to avoid using ADD keyword in this syntax like: ALTER
    > PUBLICATION pub1 SKIP TABLE t1,t2;
    
    Currently we are supporting Alter publication using the following syntax:
    ALTER PUBLICATION pub1 ADD TABLE t1;
    ALTER PUBLICATION pub1 SET TABLE t1;
    ALTER PUBLICATION pub1 DROP TABLE T1;
    ALTER PUBLICATION pub1 ADD ALL TABLES IN SCHEMA sch1;
    ALTER PUBLICATION pub1 SET ALL TABLES IN SCHEMA sch1;
    ALTER PUBLICATION pub1 DROP ALL TABLES IN SCHEMA sch1;
    
    I have extended the new syntax in similar lines:
    ALTER PUBLICATION pub1 ADD SKIP TABLE t1;
    ALTER PUBLICATION pub1 SET SKIP TABLE t1;
    ALTER PUBLICATION pub1 DROP SKIP TABLE T1;
    
    I did it like this to maintain consistency.
    But I'm fine doing it either way to keep it simple for the user.
    
    Regards,
    Vignesh
    
    
    
    
  8. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-04-13T04:39:51Z

    On Wed, Apr 13, 2022 at 8:45 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Tue, Apr 12, 2022 at 4:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > I understand that part but what I pointed out was that it might be
    > > better to avoid using ADD keyword in this syntax like: ALTER
    > > PUBLICATION pub1 SKIP TABLE t1,t2;
    >
    > Currently we are supporting Alter publication using the following syntax:
    > ALTER PUBLICATION pub1 ADD TABLE t1;
    > ALTER PUBLICATION pub1 SET TABLE t1;
    > ALTER PUBLICATION pub1 DROP TABLE T1;
    > ALTER PUBLICATION pub1 ADD ALL TABLES IN SCHEMA sch1;
    > ALTER PUBLICATION pub1 SET ALL TABLES IN SCHEMA sch1;
    > ALTER PUBLICATION pub1 DROP ALL TABLES IN SCHEMA sch1;
    >
    > I have extended the new syntax in similar lines:
    > ALTER PUBLICATION pub1 ADD SKIP TABLE t1;
    > ALTER PUBLICATION pub1 SET SKIP TABLE t1;
    > ALTER PUBLICATION pub1 DROP SKIP TABLE T1;
    >
    > I did it like this to maintain consistency.
    >
    
    What is the difference between ADD and SET variants? I understand we
    need some way to remove the SKIP table setting but not sure if DROP is
    the best alternative.
    
    The other ideas could be:
    To set skip tables: ALTER PUBLICATION pub1 SKIP TABLE t1, t2...;
    To reset skip tables: ALTER PUBLICATION pub1 SKIP TABLE; /* basically
    an empty list*/
    Yet another way to reset skip tables: ALTER PUBLICATION pub1 RESET
    SKIP TABLE; /* Here we need to introduce RESET. */
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  9. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-04-13T07:35:39Z

    On Wed, Apr 13, 2022 at 2:40 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, Apr 13, 2022 at 8:45 AM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Tue, Apr 12, 2022 at 4:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > > I understand that part but what I pointed out was that it might be
    > > > better to avoid using ADD keyword in this syntax like: ALTER
    > > > PUBLICATION pub1 SKIP TABLE t1,t2;
    > >
    > > Currently we are supporting Alter publication using the following syntax:
    > > ALTER PUBLICATION pub1 ADD TABLE t1;
    > > ALTER PUBLICATION pub1 SET TABLE t1;
    > > ALTER PUBLICATION pub1 DROP TABLE T1;
    > > ALTER PUBLICATION pub1 ADD ALL TABLES IN SCHEMA sch1;
    > > ALTER PUBLICATION pub1 SET ALL TABLES IN SCHEMA sch1;
    > > ALTER PUBLICATION pub1 DROP ALL TABLES IN SCHEMA sch1;
    > >
    > > I have extended the new syntax in similar lines:
    > > ALTER PUBLICATION pub1 ADD SKIP TABLE t1;
    > > ALTER PUBLICATION pub1 SET SKIP TABLE t1;
    > > ALTER PUBLICATION pub1 DROP SKIP TABLE T1;
    > >
    > > I did it like this to maintain consistency.
    > >
    >
    > What is the difference between ADD and SET variants? I understand we
    > need some way to remove the SKIP table setting but not sure if DROP is
    > the best alternative.
    >
    > The other ideas could be:
    > To set skip tables: ALTER PUBLICATION pub1 SKIP TABLE t1, t2...;
    > To reset skip tables: ALTER PUBLICATION pub1 SKIP TABLE; /* basically
    > an empty list*/
    > Yet another way to reset skip tables: ALTER PUBLICATION pub1 RESET
    > SKIP TABLE; /* Here we need to introduce RESET. */
    >
    
    When you were talking about SKIP TABLE then I liked the idea of:
    
    ALTER ... SET SKIP TABLE; /* empty list to reset the table skips */
    ALTER ... SET SKIP TABLE t1,t2; /* non-empty list to replace the table skips */
    
    But when you apply that rule to SKIP ALL TABLES IN SCHEMA, then the
    reset syntax looks too awkward.
    
    ALTER ... SET SKIP ALL TABLES IN SCHEMA; /* empty list to reset the
    schema skips */
    ALTER ... SET SKIP ALL TABLES IN SCHEMA s1,s2; /* non-empty list to
    replace the schema skips */
    
    ~~~
    
    IMO it might be simpler to do it like:
    
    ALTER ... DROP SKIP; /* reset/remove the skip */
    ALTER ... SET SKIP TABLE t1,t2; /* non-empty list to replace table skips */
    ALTER ... SET SKIP ALL TABLES IS SCHEMA s1,s2; /* non-empty list to
    replace schema skips */
    
    I don't really think that the ALTER ... SET SKIP empty list should be
    supported (because reason above)
    I don't really think that the ALTER ... ADD SKIP should be supported.
    
    ===
    
    More questions - What happens if the skip table or skip schema no
    longer exists exist?  Does that mean error?  Maybe there is a
    dependency on it but OTOH it might be annoying - e.g. to disallow a
    DROP TABLE when the only dependency was that the user wanted to SKIP
    it...
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  10. RE: Skipping schema changes in publication

    Yu Shi (Fujitsu) <shiy.fnst@fujitsu.com> — 2022-04-14T08:55:01Z

    On Tue, Apr 12, 2022 2:23 PM vignesh C <vignesh21@gmail.com> wrote:
    > 
    > The patch does not apply on top of HEAD because of the recent commit,
    > attached patch is rebased on top of HEAD.
    > 
    
    Thanks for your patch. Here are some comments for 0001 patch.
    
    1. doc/src/sgml/catalogs.sgml
    @@ -6438,6 +6438,15 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
            A null value indicates that all columns are published.
           </para></entry>
          </row>
    +
    +    <row>
    +      <entry role="catalog_table_entry"><para role="column_definition">
    +       <structfield>pnskip</structfield> <type>bool</type>
    +      </para>
    +      <para>
    +       True if the schema is skip schema
    +      </para></entry>
    +     </row>
         </tbody>
        </tgroup>
       </table>
    
    This change is added to pg_publication_rel, I think it should be added to
    pg_publication_namespace, right?
    
    2.
    postgres=# alter publication p1 add skip all tables in schema s1,s2;
    ERROR:  schema "s1" is already member of publication "p1"
    
    This error message seems odd to me, can we improve it? Something like:
    schema "s1" is already skipped in publication "p1"
    
    3.
    create table tbl (a int primary key);
    create schema s1;
    create schema s2;
    create table s1.tbl (a int);
    create publication p1 for all tables skip all tables in schema s1,s2;
    
    postgres=# \dRp+
                                   Publication p1
      Owner   | All tables | Inserts | Updates | Deletes | Truncates | Via root
    ----------+------------+---------+---------+---------+-----------+----------
     postgres | t          | t       | t       | t       | t         | f
    Skip tables from schemas:
        "s1"
        "s2"
    
    postgres=# select * from pg_publication_tables;
     pubname | schemaname | tablename
    ---------+------------+-----------
     p1      | public     | tbl
     p1      | s1         | tbl
    (2 rows)
    
    There shouldn't be a record of s1.tbl, since all tables in schema s1 are skipped.
    
    I found that it is caused by the following code:
    
    src/backend/catalog/pg_publication.c
    +	foreach(cell, pubschemalist)
    +	{
    +		PublicationSchInfo *pubsch = (PublicationSchInfo *) lfirst(cell);
    +
    +		skipschemaidlist = lappend_oid(result, pubsch->oid);
    +	}
    
    The first argument to append_oid() seems wrong, should it be:
    
    skipschemaidlist = lappend_oid(skipschemaidlist, pubsch->oid);
    
    
    4. src/backend/commands/publicationcmds.c
    
    /*
     * Convert the PublicationObjSpecType list into schema oid list and
     * PublicationTable list.
     */
    static void
    ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
    						   List **rels, List **schemas)
    
    Should we modify the comment of ObjectsInPublicationToOids()?
    "schema oid list" should be "PublicationSchInfo list".
    
    Regards,
    Shi yu
    
    
  11. RE: Skipping schema changes in publication

    Wei Wang (Fujitsu) <wangw.fnst@fujitsu.com> — 2022-04-14T09:33:15Z

    On Tue, Apr 12, 2022 at 2:23 PM vignesh C <vignesh21@gmail.com> wrote:
    > The patch does not apply on top of HEAD because of the recent commit,
    > attached patch is rebased on top of HEAD.
    Thanks for your patches.
    
    Here are some comments for v1-0001:
    1.
    I found the patch add the following two new functions in gram.y:
    preprocess_alltables_pubobj_list, check_skip_in_pubobj_list.
    These two functions look similar. So could we just add one new function?
    Besides, do we need the API `location` in new function
    preprocess_alltables_pubobj_list? It seems that "location" is not used in this
    new function.
    In addition, the location of error cursor in the messages seems has a little
    problem. For example:
    postgres=# create publication pub for all TABLES skip all tables in schema public, table test;
    ERROR:  only SKIP ALL TABLES IN SCHEMA or SKIP TABLE can be specified with ALL TABLES option
    LINE 1: create publication pub for all TABLES skip all tables in sch...
            ^
    (The location of error cursor is under 'create')
    
    2. I think maybe there is a minor missing in function
    preprocess_alltables_pubobj_list and check_skip_in_pubobj_list:
    We seem to be missing the CURRENT_SCHEMA case.
    For example(In function preprocess_alltables_pubobj_list) :
    +		/* Only SKIP ALL TABLES IN SCHEMA option supported with ALL TABLES */
    +		if (pubobj->pubobjtype != PUBLICATIONOBJ_TABLES_IN_SCHEMA ||
    +			!pubobj->skip)
    maybe need to be changed like this:
    +		/* Only SKIP ALL TABLES IN SCHEMA option supported with ALL TABLES */
    +		if ((pubobj->pubobjtype != PUBLICATIONOBJ_TABLES_IN_SCHEMA &&
    +		    pubobj->pubobjtype != PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA) &&
    +			pubobj->skip)
    
    3. I think maybe there are some minor missing in create_publication.sgml.
    +    [ FOR ALL TABLES [SKIP ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA }]
    maybe need to be changed to this:
    +    [ FOR ALL TABLES [SKIP ALL TABLES IN SCHEMA { <replaceable class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ... ]]
    
    4. The error message of function CreatePublication.
    Does the message below need to be modified like the comment?
    In addition, I think maybe "FOR/SKIP" is better.
    @@ -835,18 +843,21 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
    -		/* FOR ALL TABLES IN SCHEMA requires superuser */
    +		/* FOR [SKIP] ALL TABLES IN SCHEMA requires superuser */
     		if (list_length(schemaidlist) > 0 && !superuser())
     			ereport(ERROR,
     					errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     					errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
    
    5.
    I think there are some minor missing in tab-complete.c.
    +			 Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "SKIP", "ALL", "TABLES", "IN", "SCHEMA"))
    maybe need to be changed to this:
    +			 Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "SKIP", "ALL", "TABLES", "IN", "SCHEMA"))
    
    +			  Matches("CREATE", "PUBLICATION", MatchAny, "SKIP", "FOR", "ALL", "TABLES", "IN", "SCHEMA", MatchAny)) &&
    maybe need to be changed to this:
    +			  Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "SKIP", "ALL", "TABLES", "IN", "SCHEMA", MatchAny)) &&
    
    6.
    In function get_rel_sync_entry, do we need `if (!publish)` in below code?
    I think `publish` is always false here, as we delete the check for
    "pub->alltables".
    ```
    -			/*
    -			 * If this is a FOR ALL TABLES publication, pick the partition root
    -			 * and set the ancestor level accordingly.
    -			 */
    -			if (pub->alltables)
    -			{
    -				......
    -			}
    -
     			if (!publish)
    ```
    
    Regards,
    Wang wei
    
  12. Re: Skipping schema changes in publication

    Peter Eisentraut <peter.eisentraut@enterprisedb.com> — 2022-04-14T13:47:58Z

    On 12.04.22 08:23, vignesh C wrote:
    > I have also included the implementation for skipping a few tables from
    > all tables publication, the 0002 patch has the implementation for the
    > same.
    > This feature is helpful for use cases where the user wants to
    > subscribe to all the changes except for the changes present in a few
    > tables.
    > Ex:
    > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP TABLE t1,t2;
    > OR
    > ALTER PUBLICATION pub1 ADD SKIP  TABLE t1,t2;
    
    We have already allocated the "skip" terminology for skipping 
    transactions, which is a dynamic run-time action.  We are also using the 
    term "skip" elsewhere to skip locked rows, which is similarly a run-time 
    action.  I think it would be confusing to use the term SKIP for DDL 
    construction.
    
    Let's find another term like "omit", "except", etc.
    
    I would also think about this in broader terms.  For example, sometimes 
    people want features like "all columns except these" in certain places. 
    The syntax for those things should be similar.
    
    That said, I'm not sure this feature is worth the trouble.  If this is 
    useful, what about "whole database except these schemas"?  What about 
    "create this database from this template except these schemas".  This 
    could get out of hand.  I think we should encourage users to group their 
    object the way they want and not offer these complicated negative 
    selection mechanisms.
    
    
    
    
  13. Re: Skipping schema changes in publication

    Euler Taveira <euler@eulerto.com> — 2022-04-14T19:55:24Z

    On Thu, Apr 14, 2022, at 10:47 AM, Peter Eisentraut wrote:
    > On 12.04.22 08:23, vignesh C wrote:
    > > I have also included the implementation for skipping a few tables from
    > > all tables publication, the 0002 patch has the implementation for the
    > > same.
    > > This feature is helpful for use cases where the user wants to
    > > subscribe to all the changes except for the changes present in a few
    > > tables.
    > > Ex:
    > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP TABLE t1,t2;
    > > OR
    > > ALTER PUBLICATION pub1 ADD SKIP  TABLE t1,t2;
    > 
    > We have already allocated the "skip" terminology for skipping 
    > transactions, which is a dynamic run-time action.  We are also using the 
    > term "skip" elsewhere to skip locked rows, which is similarly a run-time 
    > action.  I think it would be confusing to use the term SKIP for DDL 
    > construction.
    I didn't like the SKIP choice too. We already have EXCEPT for IMPORT FOREIGN
    SCHEMA and if I were to suggest a keyword, it would be EXCEPT.
    
    > I would also think about this in broader terms.  For example, sometimes 
    > people want features like "all columns except these" in certain places. 
    > The syntax for those things should be similar.
    The questions are:
    What kind of issues does it solve?
    Do we have a workaround for it?
    
    > That said, I'm not sure this feature is worth the trouble.  If this is 
    > useful, what about "whole database except these schemas"?  What about 
    > "create this database from this template except these schemas".  This 
    > could get out of hand.  I think we should encourage users to group their 
    > object the way they want and not offer these complicated negative 
    > selection mechanisms.
    I have the same impression too. We already provide a way to:
    
    * include individual tables;
    * include all tables;
    * include all tables in a certain schema.
    
    Doesn't it cover the majority of the use cases? We don't need to cover all
    possible cases in one DDL command. IMO the current grammar for CREATE
    PUBLICATION is already complicated after the ALL TABLES IN SCHEMA. You are
    proposing to add "ALL TABLES SKIP ALL TABLES" that sounds repetitive but it is
    not; doesn't seem well-thought-out. I'm also concerned about possible gotchas
    for this proposal. The first command above suggests that it skips all tables in a
    certain schema. What happen if I decide to include a particular table of the
    skipped schema (second command)?
    
    ALTER PUBLICATION pub1 ADD SKIP ALL TABLES IN SCHEMA s1,s2;
    ALTER PUBLICATION pub1 ADD TABLE s1.foo;
    
    Having said that I'm not wedded to this proposal. Unless someone provides
    compelling use cases for this additional syntax, I think we should leave the
    publication syntax as is.
    
    
    --
    Euler Taveira
    EDB   https://www.enterprisedb.com/
    
  14. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-04-18T07:01:59Z

    On Fri, Apr 15, 2022 at 1:26 AM Euler Taveira <euler@eulerto.com> wrote:
    >
    > On Thu, Apr 14, 2022, at 10:47 AM, Peter Eisentraut wrote:
    >
    > On 12.04.22 08:23, vignesh C wrote:
    > > I have also included the implementation for skipping a few tables from
    > > all tables publication, the 0002 patch has the implementation for the
    > > same.
    > > This feature is helpful for use cases where the user wants to
    > > subscribe to all the changes except for the changes present in a few
    > > tables.
    > > Ex:
    > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP TABLE t1,t2;
    > > OR
    > > ALTER PUBLICATION pub1 ADD SKIP  TABLE t1,t2;
    >
    > We have already allocated the "skip" terminology for skipping
    > transactions, which is a dynamic run-time action.  We are also using the
    > term "skip" elsewhere to skip locked rows, which is similarly a run-time
    > action.  I think it would be confusing to use the term SKIP for DDL
    > construction.
    >
    > I didn't like the SKIP choice too. We already have EXCEPT for IMPORT FOREIGN
    > SCHEMA and if I were to suggest a keyword, it would be EXCEPT.
    >
    
    +1 for EXCEPT.
    
    > I would also think about this in broader terms.  For example, sometimes
    > people want features like "all columns except these" in certain places.
    > The syntax for those things should be similar.
    >
    > The questions are:
    > What kind of issues does it solve?
    
    As far as I understand, it is for usability, otherwise, users need to
    list all required columns' names even if they don't want to hide most
    of the columns in the table. Consider user doesn't want to publish the
    'salary' or other sensitive information of executives/employees but
    would like to publish all other columns. I feel in such cases it will
    be a lot of work for the user especially when the table has many
    columns. I see that Oracle has a similar feature [1]. I think without
    this it will be difficult for users to use this feature in some cases.
    
    > Do we have a workaround for it?
    >
    
    I can't think of any except the user needs to manually input all
    required columns. Can you think of any other workaround?
    
    > That said, I'm not sure this feature is worth the trouble.  If this is
    > useful, what about "whole database except these schemas"?  What about
    > "create this database from this template except these schemas".  This
    > could get out of hand.  I think we should encourage users to group their
    > object the way they want and not offer these complicated negative
    > selection mechanisms.
    >
    > I have the same impression too. We already provide a way to:
    >
    > * include individual tables;
    > * include all tables;
    > * include all tables in a certain schema.
    >
    > Doesn't it cover the majority of the use cases?
    >
    
    Similar to columns, the same applies to tables. Users need to manually
    add all tables for a database even when she wants to avoid only a
    handful of tables from the database say because they contain sensitive
    information or are not required. I think we don't need to cover all
    possible exceptions but a few where users can avoid some tables would
    be useful. If not, what kind of alternative do users have except for
    listing all columns or all tables that are required.
    
    
    [1] - https://docs.oracle.com/en/cloud/paas/goldengate-cloud/gwuad/selecting-columns.html#GUID-9A851C8B-48F7-43DF-8D98-D086BE069E20
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  15. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-04-18T09:40:46Z

    On Thu, Apr 14, 2022 at 7:18 PM Peter Eisentraut
    <peter.eisentraut@enterprisedb.com> wrote:
    >
    > On 12.04.22 08:23, vignesh C wrote:
    > > I have also included the implementation for skipping a few tables from
    > > all tables publication, the 0002 patch has the implementation for the
    > > same.
    > > This feature is helpful for use cases where the user wants to
    > > subscribe to all the changes except for the changes present in a few
    > > tables.
    > > Ex:
    > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP TABLE t1,t2;
    > > OR
    > > ALTER PUBLICATION pub1 ADD SKIP  TABLE t1,t2;
    >
    > We have already allocated the "skip" terminology for skipping
    > transactions, which is a dynamic run-time action.  We are also using the
    > term "skip" elsewhere to skip locked rows, which is similarly a run-time
    > action.  I think it would be confusing to use the term SKIP for DDL
    > construction.
    >
    > Let's find another term like "omit", "except", etc.
    
    +1 for Except
    
    > I would also think about this in broader terms.  For example, sometimes
    > people want features like "all columns except these" in certain places.
    > The syntax for those things should be similar.
    >
    > That said, I'm not sure this feature is worth the trouble.  If this is
    > useful, what about "whole database except these schemas"?  What about
    > "create this database from this template except these schemas".  This
    > could get out of hand.  I think we should encourage users to group their
    > object the way they want and not offer these complicated negative
    > selection mechanisms.
    
    I thought this feature would help when there are many many tables in
    the database and the user wants only certain confidential tables like
    credit card information. In this case instead of specifying the whole
    table list it will be better to specify "ALL TABLES EXCEPT
    cred_info_tbl".
    I had seen that mysql also has a similar option replicate-ignore-table
    to ignore the changes on specific tables as mentioned in [1].
    Similar use case exists in pg_dump too. pg_dump has an option
    exclude-table that will be used for not dumping any tables that are
    matching the table specified as in [2].
    
    [1] - https://dev.mysql.com/doc/refman/5.7/en/change-replication-filter.html
    [2] - https://www.postgresql.org/docs/devel/app-pgdump.html
    
    Regards,
    Vignesh
    
    
    
    
  16. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-04-21T03:15:07Z

    On Mon, Apr 18, 2022 at 12:32 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Apr 15, 2022 at 1:26 AM Euler Taveira <euler@eulerto.com> wrote:
    > >
    > > On Thu, Apr 14, 2022, at 10:47 AM, Peter Eisentraut wrote:
    > >
    > > On 12.04.22 08:23, vignesh C wrote:
    > > > I have also included the implementation for skipping a few tables from
    > > > all tables publication, the 0002 patch has the implementation for the
    > > > same.
    > > > This feature is helpful for use cases where the user wants to
    > > > subscribe to all the changes except for the changes present in a few
    > > > tables.
    > > > Ex:
    > > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP TABLE t1,t2;
    > > > OR
    > > > ALTER PUBLICATION pub1 ADD SKIP  TABLE t1,t2;
    > >
    > > We have already allocated the "skip" terminology for skipping
    > > transactions, which is a dynamic run-time action.  We are also using the
    > > term "skip" elsewhere to skip locked rows, which is similarly a run-time
    > > action.  I think it would be confusing to use the term SKIP for DDL
    > > construction.
    > >
    > > I didn't like the SKIP choice too. We already have EXCEPT for IMPORT FOREIGN
    > > SCHEMA and if I were to suggest a keyword, it would be EXCEPT.
    > >
    >
    > +1 for EXCEPT.
    
    Updated patch by changing the syntax to use EXCEPT instead of SKIP.
    
    Regards,
    Vignesh
    
  17. Re: Skipping schema changes in publication

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2022-04-22T16:09:24Z

    On Tue, Mar 22, 2022 at 12:39 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Hi,
    >
    > This feature adds an option to skip changes of all tables in specified
    > schema while creating publication.
    > This feature is helpful for use cases where the user wants to
    > subscribe to all the changes except for the changes present in a few
    > schemas.
    > Ex:
    > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP ALL TABLES IN SCHEMA s1,s2;
    > OR
    > ALTER PUBLICATION pub1 ADD SKIP ALL TABLES IN SCHEMA s1,s2;
    >
    > A new column pnskip is added to table "pg_publication_namespace", to
    > maintain the schemas that the user wants to skip publishing through
    > the publication. Modified the output plugin (pgoutput) to skip
    > publishing the changes if the relation is part of skip schema
    > publication.
    > As a continuation to this, I will work on implementing skipping tables
    > from all tables in schema and skipping tables from all tables
    > publication.
    >
    > Attached patch has the implementation for this.
    > This feature is for the pg16 version.
    > Thoughts?
    
    The feature seems to be useful especially when there are lots of
    schemas in a database. However, I don't quite like the syntax. Do we
    have 'SKIP' identifier in any of the SQL statements in SQL standard?
    Can we think of adding skip_schema_list as an option, something like
    below?
    
    CREATE PUBLICATION foo FOR ALL TABLES (skip_schema_list = 's1, s2');
    ALTER PUBLICATION foo SET (skip_schema_list = 's1, s2'); - to set
    ALTER PUBLICATION foo SET (skip_schema_list = ''); - to reset
    
    Regards,
    Bharath Rupireddy.
    
    
    
    
  18. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-04-26T01:55:21Z

    On Sat, Apr 23, 2022 at 2:09 AM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > On Tue, Mar 22, 2022 at 12:39 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > This feature adds an option to skip changes of all tables in specified
    > > schema while creating publication.
    > > This feature is helpful for use cases where the user wants to
    > > subscribe to all the changes except for the changes present in a few
    > > schemas.
    > > Ex:
    > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP ALL TABLES IN SCHEMA s1,s2;
    > > OR
    > > ALTER PUBLICATION pub1 ADD SKIP ALL TABLES IN SCHEMA s1,s2;
    > >
    > > A new column pnskip is added to table "pg_publication_namespace", to
    > > maintain the schemas that the user wants to skip publishing through
    > > the publication. Modified the output plugin (pgoutput) to skip
    > > publishing the changes if the relation is part of skip schema
    > > publication.
    > > As a continuation to this, I will work on implementing skipping tables
    > > from all tables in schema and skipping tables from all tables
    > > publication.
    > >
    > > Attached patch has the implementation for this.
    > > This feature is for the pg16 version.
    > > Thoughts?
    >
    > The feature seems to be useful especially when there are lots of
    > schemas in a database. However, I don't quite like the syntax. Do we
    > have 'SKIP' identifier in any of the SQL statements in SQL standard?
    > Can we think of adding skip_schema_list as an option, something like
    > below?
    >
    > CREATE PUBLICATION foo FOR ALL TABLES (skip_schema_list = 's1, s2');
    > ALTER PUBLICATION foo SET (skip_schema_list = 's1, s2'); - to set
    > ALTER PUBLICATION foo SET (skip_schema_list = ''); - to reset
    >
    
    I had been wondering for some time if there was any way to introduce a
    more flexible pattern matching into PUBLICATION but without bloating
    the syntax. Maybe your idea to use an option for the "skip" gives a
    way to do it...
    
    For example, if we could use regex (for <schemaname>.<tablename>
    patterns) for the option value then....
    
    ~~
    
    e.g.1. Exclude certain tables:
    
    // do NOT publish any tables of schemas s1,s2
    CREATE PUBLICATION foo FOR ALL TABLES (exclude_match = '(s1\..*)|(s2\..*)');
    
    // do NOT publish my secret tables (those called "mysecretXXX")
    CREATE PUBLICATION foo FOR ALL TABLES (exclude_match = '(.*\.mysecret.*)');
    
    ~~
    
    e.g.2. Only allow certain tables.
    
    // ONLY publish my tables (those called "mytableXXX")
    CREATE PUBLICATION foo FOR ALL TABLES (subset_match = '(.*\.mytable.*)');
    
    // So following is equivalent to FOR ALL TABLES IN SCHEMA s1
    CREATE PUBLICATION foo FOR ALL TABLES (subset_match = '(s1\..*)');
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  19. RE: Skipping schema changes in publication

    Takamichi Osumi (Fujitsu) <osumi.takamichi@fujitsu.com> — 2022-04-26T06:02:46Z

    On Thursday, April 21, 2022 12:15 PM vignesh C <vignesh21@gmail.com> wrote:
    > Updated patch by changing the syntax to use EXCEPT instead of SKIP.
    Hi
    
    
    This is my review comments on the v2 patch.
    
    (1) gram.y
    
    I think we can make a unified function that merges
    preprocess_alltables_pubobj_list with check_except_in_pubobj_list.
    
    With regard to preprocess_alltables_pubobj_list,
    we don't use the 2nd argument "location" in this function.
    
    (2) create_publication.sgml
    
    +  <para>
    +   Create a publication that publishes all changes in all the tables except for
    +   the changes of <structname>users</structname> and
    +   <structname>departments</structname> table;
    
    This sentence should end ":" not ";".
    
    (3) publication.out & publication.sql
    
    +-- fail - can't set except table to schema  publication
    +ALTER PUBLICATION testpub_forschema SET EXCEPT TABLE testpub_tbl1;
    
    There is one unnecessary space in the comment.
    Kindly change from "schema  publication" to "schema publication".
    
    (4) pg_dump.c & describe.c
    
    In your first email of this thread, you explained this feature
    is for PG16. Don't we need additional branch for PG16 ?
    
    @@ -6322,6 +6328,21 @@ describePublications(const char *pattern)
                            }
                    }
    
    +               if (pset.sversion >= 150000)
    +               {
    
    
    @@ -4162,7 +4164,7 @@ getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
            /* Collect all publication membership info. */
            if (fout->remoteVersion >= 150000)
                    appendPQExpBufferStr(query,
    -                                                        "SELECT tableoid, oid, prpubid, prrelid, "
    +                                                        "SELECT tableoid, oid, prpubid, prrelid, prexcept,"
    
    
    (5) psql-ref.sgml
    
    +        If <literal>+</literal> is appended to the command name, the tables,
    +        except tables and schemas associated with each publication are shown as
    +        well.
    
    I'm not sure if "except tables" is a good description.
    I suggest "excluded tables". This applies to the entire patch,
    in case if this is reasonable suggestion.
    
    
    Best Regards,
    	Takamichi Osumi
    
    
  20. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-04-27T12:50:11Z

    On Tue, Apr 26, 2022 at 11:32 AM osumi.takamichi@fujitsu.com
    <osumi.takamichi@fujitsu.com> wrote:
    >
    > On Thursday, April 21, 2022 12:15 PM vignesh C <vignesh21@gmail.com> wrote:
    > > Updated patch by changing the syntax to use EXCEPT instead of SKIP.
    > Hi
    >
    >
    > This is my review comments on the v2 patch.
    >
    > (1) gram.y
    >
    > I think we can make a unified function that merges
    > preprocess_alltables_pubobj_list with check_except_in_pubobj_list.
    >
    > With regard to preprocess_alltables_pubobj_list,
    > we don't use the 2nd argument "location" in this function.
    
    Removed location and made a unified function.
    
    > (2) create_publication.sgml
    >
    > +  <para>
    > +   Create a publication that publishes all changes in all the tables except for
    > +   the changes of <structname>users</structname> and
    > +   <structname>departments</structname> table;
    >
    > This sentence should end ":" not ";".
    
    Modified
    
    > (3) publication.out & publication.sql
    >
    > +-- fail - can't set except table to schema  publication
    > +ALTER PUBLICATION testpub_forschema SET EXCEPT TABLE testpub_tbl1;
    >
    > There is one unnecessary space in the comment.
    > Kindly change from "schema  publication" to "schema publication".
    
    Modified
    
    > (4) pg_dump.c & describe.c
    >
    > In your first email of this thread, you explained this feature
    > is for PG16. Don't we need additional branch for PG16 ?
    >
    > @@ -6322,6 +6328,21 @@ describePublications(const char *pattern)
    >                         }
    >                 }
    >
    > +               if (pset.sversion >= 150000)
    > +               {
    >
    >
    > @@ -4162,7 +4164,7 @@ getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
    >         /* Collect all publication membership info. */
    >         if (fout->remoteVersion >= 150000)
    >                 appendPQExpBufferStr(query,
    > -                                                        "SELECT tableoid, oid, prpubid, prrelid, "
    > +                                                        "SELECT tableoid, oid, prpubid, prrelid, prexcept,"
    >
    
    Modified by adding a comment saying "FIXME: 150000 should be changed
    to 160000 later for PG16."
    
    > (5) psql-ref.sgml
    >
    > +        If <literal>+</literal> is appended to the command name, the tables,
    > +        except tables and schemas associated with each publication are shown as
    > +        well.
    >
    > I'm not sure if "except tables" is a good description.
    > I suggest "excluded tables". This applies to the entire patch,
    > in case if this is reasonable suggestion.
    
    Modified it in most of the places where it was applicable. I felt the
    usage was ok in a few places.
    
    Thanks for the comments, the attached v3 patch has the changes for the same.
    
    Regards.
    Vignesh
    
  21. RE: Skipping schema changes in publication

    Takamichi Osumi (Fujitsu) <osumi.takamichi@fujitsu.com> — 2022-04-28T11:20:52Z

    On Wednesday, April 27, 2022 9:50 PM vignesh C <vignesh21@gmail.com> wrote:
    > Thanks for the comments, the attached v3 patch has the changes for the same.
    Hi
    
    Thank you for updating the patch. Several minor comments on v3.
    
    (1) commit message
    
    The new syntax allows specifying schemas. For example:
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    OR
    ALTER PUBLICATION pub1 ADD EXCEPT TABLE t1,t2;
    
    We have above sentence, but it looks better
    to make the description a bit more accurate.
    
    Kindly change
    From :
    "The new syntax allows specifying schemas"
    To :
    "The new syntax allows specifying excluded relations"
    
    Also, kindly change "OR" to "or",
    because this description is not syntax.
    
    (2) publication_add_relation
    
    @@ -396,6 +400,9 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
                    ObjectIdGetDatum(pubid);
            values[Anum_pg_publication_rel_prrelid - 1] =
                    ObjectIdGetDatum(relid);
    +       values[Anum_pg_publication_rel_prexcept - 1] =
    +               BoolGetDatum(pri->except);
    +
    
            /* Add qualifications, if available */
    
    It would be better to remove the blank line,
    because with this change, we'll have two blank
    lines in a row.
    
    (3) pg_dump.h & pg_dump_sort.c
    
    @@ -80,6 +80,7 @@ typedef enum
            DO_REFRESH_MATVIEW,
            DO_POLICY,
            DO_PUBLICATION,
    +       DO_PUBLICATION_EXCEPT_REL,
            DO_PUBLICATION_REL,
            DO_PUBLICATION_TABLE_IN_SCHEMA,
            DO_SUBSCRIPTION
    
    @@ -90,6 +90,7 @@ enum dbObjectTypePriorities
            PRIO_FK_CONSTRAINT,
            PRIO_POLICY,
            PRIO_PUBLICATION,
    +       PRIO_PUBLICATION_EXCEPT_REL,
            PRIO_PUBLICATION_REL,
            PRIO_PUBLICATION_TABLE_IN_SCHEMA,
            PRIO_SUBSCRIPTION,
    @@ -144,6 +145,7 @@ static const int dbObjectTypePriority[] =
            PRIO_REFRESH_MATVIEW,           /* DO_REFRESH_MATVIEW */
            PRIO_POLICY,                            /* DO_POLICY */
            PRIO_PUBLICATION,                       /* DO_PUBLICATION */
    +       PRIO_PUBLICATION_EXCEPT_REL,    /* DO_PUBLICATION_EXCEPT_REL */
            PRIO_PUBLICATION_REL,           /* DO_PUBLICATION_REL */
            PRIO_PUBLICATION_TABLE_IN_SCHEMA,       /* DO_PUBLICATION_TABLE_IN_SCHEMA */
            PRIO_SUBSCRIPTION                       /* DO_SUBSCRIPTION */
    
    How about having similar order between
    pg_dump.h and pg_dump_sort.c, like
    we'll add DO_PUBLICATION_EXCEPT_REL
    after DO_PUBLICATION_REL in pg_dump.h ?
    
    
    (4) GetAllTablesPublicationRelations
    
    +       /*
    +        * pg_publication_rel and pg_publication_namespace  will only have except
    +        * tables in case of all tables publication, no need to pass except flag
    +        * to get the relations.
    +        */
    +       List       *exceptpubtablelist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    +
    
    There is one unnecessary space in a comment
    "...pg_publication_namespace  will only have...". Kindly remove it.
    
    Then, how about diving the variable declaration and
    the insertion of the return value of GetPublicationRelations ?
    That might be aligned with other places in this file.
    
    (5) GetTopMostAncestorInPublication
    
    
    @@ -302,8 +303,9 @@ GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level
            foreach(lc, ancestors)
            {
                    Oid                     ancestor = lfirst_oid(lc);
    -               List       *apubids = GetRelationPublications(ancestor);
    +               List       *apubids = GetRelationPublications(ancestor, false);
                    List       *aschemaPubids = NIL;
    +               List       *aexceptpubids;
    
                    level++;
    
    @@ -317,7 +319,9 @@ GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level
                    else
                    {
                            aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    -                       if (list_member_oid(aschemaPubids, puboid))
    +                       aexceptpubids = GetRelationPublications(ancestor, true);
    +                       if (list_member_oid(aschemaPubids, puboid) ||
    +                               (puballtables && !list_member_oid(aexceptpubids, puboid)))
                            {
                                    topmost_relid = ancestor;
    
    It seems we forgot to call list_free for "aexceptpubids".
    
    
    Best Regards,
    	Takamichi Osumi
    
    
  22. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-04-28T11:31:53Z

    On Fri, Apr 22, 2022 at 9:39 PM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > On Tue, Mar 22, 2022 at 12:39 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > This feature adds an option to skip changes of all tables in specified
    > > schema while creating publication.
    > > This feature is helpful for use cases where the user wants to
    > > subscribe to all the changes except for the changes present in a few
    > > schemas.
    > > Ex:
    > > CREATE PUBLICATION pub1 FOR ALL TABLES SKIP ALL TABLES IN SCHEMA s1,s2;
    > > OR
    > > ALTER PUBLICATION pub1 ADD SKIP ALL TABLES IN SCHEMA s1,s2;
    > >
    >
    > The feature seems to be useful especially when there are lots of
    > schemas in a database. However, I don't quite like the syntax. Do we
    > have 'SKIP' identifier in any of the SQL statements in SQL standard?
    >
    
    After discussion, it seems EXCEPT is a preferred choice and the same
    is used in the other existing syntax as well.
    
    > Can we think of adding skip_schema_list as an option, something like
    > below?
    >
    > CREATE PUBLICATION foo FOR ALL TABLES (skip_schema_list = 's1, s2');
    > ALTER PUBLICATION foo SET (skip_schema_list = 's1, s2'); - to set
    > ALTER PUBLICATION foo SET (skip_schema_list = ''); - to reset
    >
    
    Yeah, that is also an option but it seems it will be difficult to
    extend if want to support "all columns except (c1, ..)" for the column
    list feature.
    
    The other thing to decide is for which all objects we want to support
    EXCEPT clause as it may not be useful for everything as indicated by
    Peter E. and Euler. We have seen that Oracle supports "all columns
    except (c1, ..)" [1] and MySQL seems to support for tables [2]. I
    guess we should restrict ourselves to those two cases for now and then
    we can extend it later for schemas if required or people agree. Also,
    we should see the syntax we choose here should be extendable.
    
    Another idea that occurred to me today for tables this is as follows:
    1. Allow to mention except during create publication ... For All Tables.
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    2. Allow to Reset it. This new syntax will reset all objects in the
    publications.
    Alter Publication ... RESET;
    3. Allow to add it to an existing publication
    Alter Publication ... Add ALL TABLES [EXCEPT TABLE t1,t2];
    
    I think it can be extended in a similar way for schema syntax as well.
    
    [1] - https://dev.mysql.com/doc/refman/5.7/en/change-replication-filter.html
    [2] - https://docs.oracle.com/en/cloud/paas/goldengate-cloud/gwuad/selecting-columns.html#GUID-9A851C8B-48F7-43DF-8D98-D086BE069E20
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  23. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-04-29T11:42:59Z

    On Thu, Apr 28, 2022 at 4:50 PM osumi.takamichi@fujitsu.com
    <osumi.takamichi@fujitsu.com> wrote:
    >
    > On Wednesday, April 27, 2022 9:50 PM vignesh C <vignesh21@gmail.com> wrote:
    > > Thanks for the comments, the attached v3 patch has the changes for the same.
    > Hi
    >
    > Thank you for updating the patch. Several minor comments on v3.
    >
    > (1) commit message
    >
    > The new syntax allows specifying schemas. For example:
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    > OR
    > ALTER PUBLICATION pub1 ADD EXCEPT TABLE t1,t2;
    >
    > We have above sentence, but it looks better
    > to make the description a bit more accurate.
    >
    > Kindly change
    > From :
    > "The new syntax allows specifying schemas"
    > To :
    > "The new syntax allows specifying excluded relations"
    >
    > Also, kindly change "OR" to "or",
    > because this description is not syntax.
    
    Slightly reworded and modified
    
    > (2) publication_add_relation
    >
    > @@ -396,6 +400,9 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
    >                 ObjectIdGetDatum(pubid);
    >         values[Anum_pg_publication_rel_prrelid - 1] =
    >                 ObjectIdGetDatum(relid);
    > +       values[Anum_pg_publication_rel_prexcept - 1] =
    > +               BoolGetDatum(pri->except);
    > +
    >
    >         /* Add qualifications, if available */
    >
    > It would be better to remove the blank line,
    > because with this change, we'll have two blank
    > lines in a row.
    
    Modified
    
    > (3) pg_dump.h & pg_dump_sort.c
    >
    > @@ -80,6 +80,7 @@ typedef enum
    >         DO_REFRESH_MATVIEW,
    >         DO_POLICY,
    >         DO_PUBLICATION,
    > +       DO_PUBLICATION_EXCEPT_REL,
    >         DO_PUBLICATION_REL,
    >         DO_PUBLICATION_TABLE_IN_SCHEMA,
    >         DO_SUBSCRIPTION
    >
    > @@ -90,6 +90,7 @@ enum dbObjectTypePriorities
    >         PRIO_FK_CONSTRAINT,
    >         PRIO_POLICY,
    >         PRIO_PUBLICATION,
    > +       PRIO_PUBLICATION_EXCEPT_REL,
    >         PRIO_PUBLICATION_REL,
    >         PRIO_PUBLICATION_TABLE_IN_SCHEMA,
    >         PRIO_SUBSCRIPTION,
    > @@ -144,6 +145,7 @@ static const int dbObjectTypePriority[] =
    >         PRIO_REFRESH_MATVIEW,           /* DO_REFRESH_MATVIEW */
    >         PRIO_POLICY,                            /* DO_POLICY */
    >         PRIO_PUBLICATION,                       /* DO_PUBLICATION */
    > +       PRIO_PUBLICATION_EXCEPT_REL,    /* DO_PUBLICATION_EXCEPT_REL */
    >         PRIO_PUBLICATION_REL,           /* DO_PUBLICATION_REL */
    >         PRIO_PUBLICATION_TABLE_IN_SCHEMA,       /* DO_PUBLICATION_TABLE_IN_SCHEMA */
    >         PRIO_SUBSCRIPTION                       /* DO_SUBSCRIPTION */
    >
    > How about having similar order between
    > pg_dump.h and pg_dump_sort.c, like
    > we'll add DO_PUBLICATION_EXCEPT_REL
    > after DO_PUBLICATION_REL in pg_dump.h ?
    >
    
    Modified
    
    > (4) GetAllTablesPublicationRelations
    >
    > +       /*
    > +        * pg_publication_rel and pg_publication_namespace  will only have except
    > +        * tables in case of all tables publication, no need to pass except flag
    > +        * to get the relations.
    > +        */
    > +       List       *exceptpubtablelist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    > +
    >
    > There is one unnecessary space in a comment
    > "...pg_publication_namespace  will only have...". Kindly remove it.
    >
    > Then, how about diving the variable declaration and
    > the insertion of the return value of GetPublicationRelations ?
    > That might be aligned with other places in this file.
    
    Modified
    
    > (5) GetTopMostAncestorInPublication
    >
    >
    > @@ -302,8 +303,9 @@ GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level
    >         foreach(lc, ancestors)
    >         {
    >                 Oid                     ancestor = lfirst_oid(lc);
    > -               List       *apubids = GetRelationPublications(ancestor);
    > +               List       *apubids = GetRelationPublications(ancestor, false);
    >                 List       *aschemaPubids = NIL;
    > +               List       *aexceptpubids;
    >
    >                 level++;
    >
    > @@ -317,7 +319,9 @@ GetTopMostAncestorInPublication(Oid puboid, List *ancestors, int *ancestor_level
    >                 else
    >                 {
    >                         aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > -                       if (list_member_oid(aschemaPubids, puboid))
    > +                       aexceptpubids = GetRelationPublications(ancestor, true);
    > +                       if (list_member_oid(aschemaPubids, puboid) ||
    > +                               (puballtables && !list_member_oid(aexceptpubids, puboid)))
    >                         {
    >                                 topmost_relid = ancestor;
    >
    > It seems we forgot to call list_free for "aexceptpubids".
    
    Modified
    
    The attached v4 patch has the changes for the same.
    
    Regards,
    Vignesh
    
  24. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-03T08:54:33Z

    On Thu, Apr 28, 2022 at 9:32 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    ...
    > Another idea that occurred to me today for tables this is as follows:
    > 1. Allow to mention except during create publication ... For All Tables.
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    > 2. Allow to Reset it. This new syntax will reset all objects in the
    > publications.
    > Alter Publication ... RESET;
    > 3. Allow to add it to an existing publication
    > Alter Publication ... Add ALL TABLES [EXCEPT TABLE t1,t2];
    >
    > I think it can be extended in a similar way for schema syntax as well.
    >
    
    Consider if the user does
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    ALTER PUBLICATION pub1 ADD ALL TABLES EXCEPT t3,t4;
    
    What does it mean?
    e.g. Is there only one exception list that is modified? Or did the ADD
    ALL TABLES override all meaning of the original list?
    e.g. Are we now skipping t1,t2,t3,t4, or are we now only skipping t3,t4?
    
    ~~~
    
    Here is a similar example, where the ADD TABLE seems confusing to me
    when it intersects with a prior EXCEPT
    e.g.
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT t1,t2; // ok
    ALTER PUBLICATION pub1 ADD TABLE t1; ???
    
    What does it mean?
    e.g. Does the explicit ADD TABLE override the original exception list?
    e.g. Is t1 published now or should that ALTER have caused an error?
    
    ~~
    
    It feels like there are too many tricky rules when using EXCEPT with
    ALTER PUBLICATION. I guess complexities can be described in the
    documentation but IMO it would be better if the ALTER syntax could be
    unambiguous in the first place. So perhaps the rules should be more
    restrictive (e.g. just disallow ALTER ... ADD any table that overlaps
    the existing EXCEPT list ??)
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  25. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-05-04T04:14:53Z

    On Tue, May 3, 2022 at 2:24 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Thu, Apr 28, 2022 at 9:32 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > ...
    > > Another idea that occurred to me today for tables this is as follows:
    > > 1. Allow to mention except during create publication ... For All Tables.
    > > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    > > 2. Allow to Reset it. This new syntax will reset all objects in the
    > > publications.
    > > Alter Publication ... RESET;
    > > 3. Allow to add it to an existing publication
    > > Alter Publication ... Add ALL TABLES [EXCEPT TABLE t1,t2];
    > >
    > > I think it can be extended in a similar way for schema syntax as well.
    > >
    >
    > Consider if the user does
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    > ALTER PUBLICATION pub1 ADD ALL TABLES EXCEPT t3,t4;
    >
    > What does it mean?
    > e.g. Is there only one exception list that is modified? Or did the ADD
    > ALL TABLES override all meaning of the original list?
    > e.g. Are we now skipping t1,t2,t3,t4, or are we now only skipping t3,t4?
    >
    
    This won't be allowed. We won't allow changing ALL TABLES publication
    unless the user first performs RESET. This is the purpose of providing
    the RESET variant.
    
    > ~~~
    >
    > Here is a similar example, where the ADD TABLE seems confusing to me
    > when it intersects with a prior EXCEPT
    > e.g.
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT t1,t2; // ok
    > ALTER PUBLICATION pub1 ADD TABLE t1; ???
    >
    > What does it mean?
    > e.g. Does the explicit ADD TABLE override the original exception list?
    > e.g. Is t1 published now or should that ALTER have caused an error?
    >
    
    This won't be allowed either. We don't allow to Add/Drop from All
    Tables publication unless the user performs a RESET. This is true even
    today except that we don't have a RESET syntax.
    
    > ~~
    >
    > It feels like there are too many tricky rules when using EXCEPT with
    > ALTER PUBLICATION. I guess complexities can be described in the
    > documentation but IMO it would be better if the ALTER syntax could be
    > unambiguous in the first place.
    >
    
    Agreed.
    
    > So perhaps the rules should be more
    > restrictive (e.g. just disallow ALTER ... ADD any table that overlaps
    > the existing EXCEPT list ??)
    >
    
    I think the current proposal seems to be restrictive enough to avoid
    any tricky issues. Do you see any other problem?
    
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  26. Re: Skipping schema changes in publication

    Peter Eisentraut <peter.eisentraut@enterprisedb.com> — 2022-05-04T13:34:54Z

    On 14.04.22 15:47, Peter Eisentraut wrote:
    > That said, I'm not sure this feature is worth the trouble.  If this is 
    > useful, what about "whole database except these schemas"?  What about 
    > "create this database from this template except these schemas".  This 
    > could get out of hand.  I think we should encourage users to group their 
    > object the way they want and not offer these complicated negative 
    > selection mechanisms.
    
    Another problem in general with this "all except these" way of 
    specifying things is that you need to track negative dependencies.
    
    For example, assume you can't add a table to a publication unless it has 
    a replica identity.  Now, if you have a publication p1 that says 
    includes "all tables except t1", you now have to check p1 whenever a new 
    table is created, even though the new table has no direct dependency 
    link with p1.  So in more general cases, you would have to check all 
    existing objects to see whether their specification is in conflict with 
    the new object being created.
    
    Now publications don't actually work that way, so it's not a real 
    problem right now, but similar things could work like that.  So I think 
    it's worth thinking this through a bit.
    
    
    
    
    
  27. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-05-05T03:50:36Z

    On Wed, May 4, 2022 at 7:05 PM Peter Eisentraut
    <peter.eisentraut@enterprisedb.com> wrote:
    >
    > On 14.04.22 15:47, Peter Eisentraut wrote:
    > > That said, I'm not sure this feature is worth the trouble.  If this is
    > > useful, what about "whole database except these schemas"?  What about
    > > "create this database from this template except these schemas".  This
    > > could get out of hand.  I think we should encourage users to group their
    > > object the way they want and not offer these complicated negative
    > > selection mechanisms.
    >
    > Another problem in general with this "all except these" way of
    > specifying things is that you need to track negative dependencies.
    >
    > For example, assume you can't add a table to a publication unless it has
    > a replica identity.  Now, if you have a publication p1 that says
    > includes "all tables except t1", you now have to check p1 whenever a new
    > table is created, even though the new table has no direct dependency
    > link with p1.  So in more general cases, you would have to check all
    > existing objects to see whether their specification is in conflict with
    > the new object being created.
    >
    
    Yes, I think we should avoid adding such negative dependencies. We
    have carefully avoided such dependencies during row filter, column
    list work where we don't try to perform DDL time verification.
    However, it is not clear to me how this proposal is related to this
    example or in general about tracking negative dependencies? AFAIR, we
    currently have such a check while changing persistence of logged table
    (logged to unlogged, see ATPrepChangePersistence) where we cannot
    allow changing persistence if that relation is part of some
    publication. But as per my understanding, this feature shouldn't add
    any such new dependencies. I agree that we have to ensure that
    existing checks shouldn't break due to this feature.
    
    > Now publications don't actually work that way, so it's not a real
    > problem right now, but similar things could work like that.  So I think
    > it's worth thinking this through a bit.
    >
    
    This is a good point and I agree that we should be careful to not add
    some new negative dependencies unless it is really required but I
    can't see how this proposal will make it more prone to such checks.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  28. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-05-05T04:12:32Z

    On Thu, May 5, 2022 at 9:20 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, May 4, 2022 at 7:05 PM Peter Eisentraut
    > <peter.eisentraut@enterprisedb.com> wrote:
    > >
    > > On 14.04.22 15:47, Peter Eisentraut wrote:
    > > > That said, I'm not sure this feature is worth the trouble.  If this is
    > > > useful, what about "whole database except these schemas"?  What about
    > > > "create this database from this template except these schemas".  This
    > > > could get out of hand.  I think we should encourage users to group their
    > > > object the way they want and not offer these complicated negative
    > > > selection mechanisms.
    > >
    > > Another problem in general with this "all except these" way of
    > > specifying things is that you need to track negative dependencies.
    > >
    > > For example, assume you can't add a table to a publication unless it has
    > > a replica identity.  Now, if you have a publication p1 that says
    > > includes "all tables except t1", you now have to check p1 whenever a new
    > > table is created, even though the new table has no direct dependency
    > > link with p1.  So in more general cases, you would have to check all
    > > existing objects to see whether their specification is in conflict with
    > > the new object being created.
    > >
    >
    > Yes, I think we should avoid adding such negative dependencies. We
    > have carefully avoided such dependencies during row filter, column
    > list work where we don't try to perform DDL time verification.
    > However, it is not clear to me how this proposal is related to this
    > example or in general about tracking negative dependencies?
    >
    
    I mean to say that even if we have such a restriction, it would apply
    to "for all tables" or other publications as well. In your example,
    consider one wants to Alter a table and remove its replica identity,
    we have to check whether the table is part of any publication similar
    to what we are doing for relation persistence in
    ATPrepChangePersistence.
    
    > AFAIR, we
    > currently have such a check while changing persistence of logged table
    > (logged to unlogged, see ATPrepChangePersistence) where we cannot
    > allow changing persistence if that relation is part of some
    > publication. But as per my understanding, this feature shouldn't add
    > any such new dependencies. I agree that we have to ensure that
    > existing checks shouldn't break due to this feature.
    >
    > > Now publications don't actually work that way, so it's not a real
    > > problem right now, but similar things could work like that.  So I think
    > > it's worth thinking this through a bit.
    > >
    >
    > This is a good point and I agree that we should be careful to not add
    > some new negative dependencies unless it is really required but I
    > can't see how this proposal will make it more prone to such checks.
    >
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  29. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-06T02:35:16Z

    On Thu, Apr 28, 2022 at 9:32 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    ...
    >
    > Another idea that occurred to me today for tables this is as follows:
    > 1. Allow to mention except during create publication ... For All Tables.
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    > 2. Allow to Reset it. This new syntax will reset all objects in the
    > publications.
    > Alter Publication ... RESET;
    > 3. Allow to add it to an existing publication
    > Alter Publication ... Add ALL TABLES [EXCEPT TABLE t1,t2];
    >
    > I think it can be extended in a similar way for schema syntax as well.
    >
    
    If the proposed syntax ALTER PUBLICATION ... RESET will reset all the
    objects in the publication then there still seems simple way to remove
    only the EXCEPT list but leave everything else intact. IIUC to clear
    just the EXCEPT list would require a 2 step process - 1) ALTER ...
    RESET then 2) ALTER ... ADD ALL TABLES again.
    
    I was wondering if it might be useful to have a variation that *only*
    resets the EXCEPT list, but still leaves everything else as-is?
    
    So, instead of:
    ALTER PUBLICATION pubname RESET
    
    use a syntax something like:
    ALTER PUBLICATION pubname RESET {ALL | EXCEPT}
    or
    ALTER PUBLICATION pubname RESET [EXCEPT]
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  30. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-10T03:38:48Z

    On Fri, May 6, 2022 at 8:05 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Thu, Apr 28, 2022 at 9:32 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > ...
    > >
    > > Another idea that occurred to me today for tables this is as follows:
    > > 1. Allow to mention except during create publication ... For All Tables.
    > > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    > > 2. Allow to Reset it. This new syntax will reset all objects in the
    > > publications.
    > > Alter Publication ... RESET;
    > > 3. Allow to add it to an existing publication
    > > Alter Publication ... Add ALL TABLES [EXCEPT TABLE t1,t2];
    > >
    > > I think it can be extended in a similar way for schema syntax as well.
    > >
    >
    > If the proposed syntax ALTER PUBLICATION ... RESET will reset all the
    > objects in the publication then there still seems simple way to remove
    > only the EXCEPT list but leave everything else intact. IIUC to clear
    > just the EXCEPT list would require a 2 step process - 1) ALTER ...
    > RESET then 2) ALTER ... ADD ALL TABLES again.
    >
    > I was wondering if it might be useful to have a variation that *only*
    > resets the EXCEPT list, but still leaves everything else as-is?
    >
    > So, instead of:
    > ALTER PUBLICATION pubname RESET
    
    +1 for this syntax as this syntax can be extendable to include options
    like (except/all/etc) later.
    Currently we can support this syntax and can be extended later based
    on the requirements.
    
    The new feature will handle the various use cases based on the
    behavior given below:
    -- CREATE Publication with EXCEPT TABLE syntax
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2; -- ok
    Alter Publication pub1 RESET;
    -- All Tables and options are reset similar to creating publication
    without any publication object and publication option (create
    publication pub1)
    \dRp+ pub1
    Publication pub2
    Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
    ---------+------------+---------+---------+---------+-----------+----------
    vignesh | f | t | t | t | t | f
    (1 row)
    
    -- Can add except table after reset of publication
    ALTER PUBLICATION pub1 Add ALL TABLES EXCEPT TABLE t1,t2; -- ok
    
    -- Cannot add except table without reset of publication
    ALTER PUBLICATION pub1 Add EXCEPT TABLE t3,t4; -- not ok, need to be reset
    
    Alter Publication pub1 RESET;
    -- Cannot add table to ALL TABLES Publication
    ALTER PUBLICATION pub1 Add ALL TABLES EXCEPT TABLE t1,t2, t3, t4,
    TABLE t5; -- not ok, ALL TABLES Publications does not support
    including of TABLES
    
    Alter Publication pub1 RESET;
    -- Cannot add table to ALL TABLES Publication
    ALTER PUBLICATION pub1 Add ALL TABLES TABLE t1,t2; -- not ok, ALL
    TABLES Publications does not support including of TABLES
    
    -- Cannot add ALL TABLES IN SCHEMA to ALL TABLES Publication
    ALTER PUBLICATION pub1 Add ALL TABLES ALL TABLES IN SCHEMA sch1, sch2;
    -- not ok, ALL TABLES Publications does not support including of ALL
    TABLES IN SCHEMA
    
    -- Existing syntax should work as it is
    CREATE PUBLICATION pub1 FOR TABLE t1;
    ALTER PUBLICATION pub1 ADD TABLE t1; -- ok, existing ALTER should work
    as it is (ok without reset)
    ALTER PUBLICATION pub1 ADD ALL TABLES IN SCHEMA sch1; -- ok, existing
    ALTER should work as it is (ok without reset)
    ALTER PUBLICATION pub1 DROP TABLE t1; -- ok, existing ALTER should
    work as it is (ok without reset)
    ALTER PUBLICATION pub1 DROP ALL TABLES IN SCHEMA sch1; -- ok, existing
    ALTER should work as it is (ok without reset)
    ALTER PUBLICATION pub1 SET TABLE t1; -- ok, existing ALTER should work
    as it is (ok without reset)
    ALTER PUBLICATION pub1 SET ALL TABLES IN SCHEMA sch1; -- ok, existing
    ALTER should work as it is (ok without reset)
    
    I will modify the patch to handle this.
    
    Regards,
    Vignesh
    
    
    
    
  31. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-12T04:24:39Z

    On Tue, May 10, 2022 at 9:08 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Fri, May 6, 2022 at 8:05 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > On Thu, Apr 28, 2022 at 9:32 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > ...
    > > >
    > > > Another idea that occurred to me today for tables this is as follows:
    > > > 1. Allow to mention except during create publication ... For All Tables.
    > > > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    > > > 2. Allow to Reset it. This new syntax will reset all objects in the
    > > > publications.
    > > > Alter Publication ... RESET;
    > > > 3. Allow to add it to an existing publication
    > > > Alter Publication ... Add ALL TABLES [EXCEPT TABLE t1,t2];
    > > >
    > > > I think it can be extended in a similar way for schema syntax as well.
    > > >
    > >
    > > If the proposed syntax ALTER PUBLICATION ... RESET will reset all the
    > > objects in the publication then there still seems simple way to remove
    > > only the EXCEPT list but leave everything else intact. IIUC to clear
    > > just the EXCEPT list would require a 2 step process - 1) ALTER ...
    > > RESET then 2) ALTER ... ADD ALL TABLES again.
    > >
    > > I was wondering if it might be useful to have a variation that *only*
    > > resets the EXCEPT list, but still leaves everything else as-is?
    > >
    > > So, instead of:
    > > ALTER PUBLICATION pubname RESET
    >
    > +1 for this syntax as this syntax can be extendable to include options
    > like (except/all/etc) later.
    > Currently we can support this syntax and can be extended later based
    > on the requirements.
    
    The attached patch has the implementation for "ALTER PUBLICATION
    pubname RESET". This command will reset the publication to default
    state which includes resetting the publication options, setting ALL
    TABLES option to false and dropping the relations and schemas that are
    associated with the publication.
    
    Regards,
    Vignesh
    
  32. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-13T04:07:17Z

    On Thu, May 12, 2022 at 2:24 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    ...
    > The attached patch has the implementation for "ALTER PUBLICATION
    > pubname RESET". This command will reset the publication to default
    > state which includes resetting the publication options, setting ALL
    > TABLES option to false and dropping the relations and schemas that are
    > associated with the publication.
    >
    
    Please see below my review comments for the v1-0001 (RESET) patch
    
    ======
    
    1. Commit message
    
    This patch adds a new RESET option to ALTER PUBLICATION which
    
    Wording: "RESET option" -> "RESET clause"
    
    ~~~
    
    2. doc/src/sgml/ref/alter_publication.sgml
    
    +  <para>
    +   The <literal>RESET</literal> clause will reset the publication to default
    +   state which includes resetting the publication options, setting
    +   <literal>ALL TABLES</literal> option to <literal>false</literal>
    and drop the
    +   relations and schemas that are associated with the publication.
       </para>
    
    2a. Wording: "to default state" -> "to the default state"
    
    2b. Wording: "and drop the relations..." -> "and dropping all relations..."
    
    ~~~
    
    3. doc/src/sgml/ref/alter_publication.sgml
    
    +   invoking user to be a superuser.  <literal>RESET</literal> of publication
    +   requires invoking user to be a superuser. To alter the owner, you must also
    
    Wording: "requires invoking user" -> "requires the invoking user"
    
    ~~~
    
    4. doc/src/sgml/ref/alter_publication.sgml - Example
    
    @@ -207,6 +220,12 @@ ALTER PUBLICATION sales_publication ADD ALL
    TABLES IN SCHEMA marketing, sales;
        <structname>production_publication</structname>:
     <programlisting>
     ALTER PUBLICATION production_publication ADD TABLE users,
    departments, ALL TABLES IN SCHEMA production;
    +</programlisting></para>
    +
    +  <para>
    +   Resetting the publication <structname>production_publication</structname>:
    +<programlisting>
    +ALTER PUBLICATION production_publication RESET;
    
    Wording: "Resetting the publication" -> "Reset the publication"
    
    ~~~
    
    5. src/backend/commands/publicationcmds.c
    
    + /* Check and reset the options */
    
    IMO the code can just reset all these options unconditionally. I did
    not see the point to check for existing option values first. I feel
    the simpler code outweighs any negligible performance difference in
    this case.
    
    ~~~
    
    6. src/backend/commands/publicationcmds.c
    
    + /* Check and reset the options */
    
    Somehow it seemed a pity having to hardcode all these default values
    true/false in multiple places; e.g. the same is already hardcoded in
    the parse_publication_options function.
    
    To avoid multiple hard coded bools you could just call the
    parse_publication_options with an empty options list. That would set
    the defaults which you can then use:
    values[Anum_pg_publication_pubinsert - 1] = BoolGetDatum(pubactiondefs->insert);
    
    Alternatively, maybe there should be #defines to use instead of having
    the scattered hardcoded bool defaults:
    #define PUBACTION_DEFAULT_INSERT true
    #define PUBACTION_DEFAULT_UPDATE true
    etc
    
    ~~~
    
    7. src/include/nodes/parsenodes.h
    
    @@ -4033,7 +4033,8 @@ typedef enum AlterPublicationAction
     {
      AP_AddObjects, /* add objects to publication */
      AP_DropObjects, /* remove objects from publication */
    - AP_SetObjects /* set list of objects */
    + AP_SetObjects, /* set list of objects */
    + AP_ReSetPublication /* reset the publication */
     } AlterPublicationAction;
    
    Unusual case: "AP_ReSetPublication" -> "AP_ResetPublication"
    
    ~~~
    
    8. src/test/regress/sql/publication.sql
    
    8a.
    +-- Test for RESET PUBLICATION
    SUGGESTED
    +-- Tests for ALTER PUBLICATION ... RESET
    
    8b.
    +-- Verify that 'ALL TABLES' option is reset
    SUGGESTED:
    +-- Verify that 'ALL TABLES' flag is reset
    
    8c.
    +-- Verify that publish option and publish via root option is reset
    SUGGESTED:
    +-- Verify that publish options and publish_via_partition_root option are reset
    
    8d.
    +-- Verify that only superuser can execute RESET publication
    SUGGESTED
    +-- Verify that only superuser can reset a publication
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  33. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-14T13:32:54Z

    On Fri, May 13, 2022 at 9:37 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Thu, May 12, 2022 at 2:24 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > ...
    > > The attached patch has the implementation for "ALTER PUBLICATION
    > > pubname RESET". This command will reset the publication to default
    > > state which includes resetting the publication options, setting ALL
    > > TABLES option to false and dropping the relations and schemas that are
    > > associated with the publication.
    > >
    >
    > Please see below my review comments for the v1-0001 (RESET) patch
    >
    > ======
    >
    > 1. Commit message
    >
    > This patch adds a new RESET option to ALTER PUBLICATION which
    >
    > Wording: "RESET option" -> "RESET clause"
    
    Modified
    
    > ~~~
    >
    > 2. doc/src/sgml/ref/alter_publication.sgml
    >
    > +  <para>
    > +   The <literal>RESET</literal> clause will reset the publication to default
    > +   state which includes resetting the publication options, setting
    > +   <literal>ALL TABLES</literal> option to <literal>false</literal>
    > and drop the
    > +   relations and schemas that are associated with the publication.
    >    </para>
    >
    > 2a. Wording: "to default state" -> "to the default state"
    
    Modified
    
    > 2b. Wording: "and drop the relations..." -> "and dropping all relations..."
    
    Modified
    
    > ~~~
    >
    > 3. doc/src/sgml/ref/alter_publication.sgml
    >
    > +   invoking user to be a superuser.  <literal>RESET</literal> of publication
    > +   requires invoking user to be a superuser. To alter the owner, you must also
    >
    > Wording: "requires invoking user" -> "requires the invoking user"
    
    Modified
    
    > ~~~
    >
    > 4. doc/src/sgml/ref/alter_publication.sgml - Example
    >
    > @@ -207,6 +220,12 @@ ALTER PUBLICATION sales_publication ADD ALL
    > TABLES IN SCHEMA marketing, sales;
    >     <structname>production_publication</structname>:
    >  <programlisting>
    >  ALTER PUBLICATION production_publication ADD TABLE users,
    > departments, ALL TABLES IN SCHEMA production;
    > +</programlisting></para>
    > +
    > +  <para>
    > +   Resetting the publication <structname>production_publication</structname>:
    > +<programlisting>
    > +ALTER PUBLICATION production_publication RESET;
    >
    > Wording: "Resetting the publication" -> "Reset the publication"
    
    Modified
    
    > ~~~
    >
    > 5. src/backend/commands/publicationcmds.c
    >
    > + /* Check and reset the options */
    >
    > IMO the code can just reset all these options unconditionally. I did
    > not see the point to check for existing option values first. I feel
    > the simpler code outweighs any negligible performance difference in
    > this case.
    
    Modified
    
    > ~~~
    >
    > 6. src/backend/commands/publicationcmds.c
    >
    > + /* Check and reset the options */
    >
    > Somehow it seemed a pity having to hardcode all these default values
    > true/false in multiple places; e.g. the same is already hardcoded in
    > the parse_publication_options function.
    >
    > To avoid multiple hard coded bools you could just call the
    > parse_publication_options with an empty options list. That would set
    > the defaults which you can then use:
    > values[Anum_pg_publication_pubinsert - 1] = BoolGetDatum(pubactiondefs->insert);
    >
    > Alternatively, maybe there should be #defines to use instead of having
    > the scattered hardcoded bool defaults:
    > #define PUBACTION_DEFAULT_INSERT true
    > #define PUBACTION_DEFAULT_UPDATE true
    > etc
    
    I have used #define for default value and used it in both the functions.
    
    > ~~~
    >
    > 7. src/include/nodes/parsenodes.h
    >
    > @@ -4033,7 +4033,8 @@ typedef enum AlterPublicationAction
    >  {
    >   AP_AddObjects, /* add objects to publication */
    >   AP_DropObjects, /* remove objects from publication */
    > - AP_SetObjects /* set list of objects */
    > + AP_SetObjects, /* set list of objects */
    > + AP_ReSetPublication /* reset the publication */
    >  } AlterPublicationAction;
    >
    > Unusual case: "AP_ReSetPublication" -> "AP_ResetPublication"
    
    Modified
    
    > ~~~
    >
    > 8. src/test/regress/sql/publication.sql
    >
    > 8a.
    > +-- Test for RESET PUBLICATION
    > SUGGESTED
    > +-- Tests for ALTER PUBLICATION ... RESET
    
    Modified
    
    > 8b.
    > +-- Verify that 'ALL TABLES' option is reset
    > SUGGESTED:
    > +-- Verify that 'ALL TABLES' flag is reset
    
    Modified
    
    > 8c.
    > +-- Verify that publish option and publish via root option is reset
    > SUGGESTED:
    > +-- Verify that publish options and publish_via_partition_root option are reset
    
    Modified
    
    > 8d.
    > +-- Verify that only superuser can execute RESET publication
    > SUGGESTED
    > +-- Verify that only superuser can reset a publication
    
    Modified
    
    Thanks for the comments, the attached v5 patch has the changes for the
    same. Also I have made the changes for SKIP Table based on the new
    syntax, the changes for the same are available in
    v5-0002-Skip-publishing-the-tables-specified-in-EXCEPT-TA.patch.
    
    Regards,
    Vignesh
    
  34. RE: Skipping schema changes in publication

    Takamichi Osumi (Fujitsu) <osumi.takamichi@fujitsu.com> — 2022-05-16T03:02:26Z

    On Saturday, May 14, 2022 10:33 PM vignesh C <vignesh21@gmail.com> wrote:
    > Thanks for the comments, the attached v5 patch has the changes for the same.
    > Also I have made the changes for SKIP Table based on the new syntax, the
    > changes for the same are available in
    > v5-0002-Skip-publishing-the-tables-specified-in-EXCEPT-TA.patch.
    Hi,
    
    
    Thank you for updating the patch.
    I'll share few minor review comments on v5-0001.
    
    
    (1) doc/src/sgml/ref/alter_publication.sgml
    
    @@ -73,12 +85,13 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
        Adding a table to a publication additionally requires owning that table.
        The <literal>ADD ALL TABLES IN SCHEMA</literal> and
        <literal>SET ALL TABLES IN SCHEMA</literal> to a publication requires the
    -   invoking user to be a superuser.  To alter the owner, you must also be a
    -   direct or indirect member of the new owning role. The new owner must have
    -   <literal>CREATE</literal> privilege on the database.  Also, the new owner
    -   of a <literal>FOR ALL TABLES</literal> or <literal>FOR ALL TABLES IN
    -   SCHEMA</literal> publication must be a superuser. However, a superuser can
    -   change the ownership of a publication regardless of these restrictions.
    +   invoking user to be a superuser.  <literal>RESET</literal> of publication
    +   requires the invoking user to be a superuser. To alter the owner, you must
    ...
    
    
    I suggest to combine the first part of your change with one existing sentence
    before your change, to make our description concise.
    
    FROM:
    "The <literal>ADD ALL TABLES IN SCHEMA</literal> and
    <literal>SET ALL TABLES IN SCHEMA</literal> to a publication requires the
    invoking user to be a superuser.  <literal>RESET</literal> of publication
    requires the invoking user to be a superuser."
    
    TO:
    "The <literal>ADD ALL TABLES IN SCHEMA</literal>,
    <literal>SET ALL TABLES IN SCHEMA</literal> to a publication and
    <literal>RESET</literal> of publication requires the invoking user to be a superuser."
    
    
    (2) typo
    
    +++ b/src/backend/commands/publicationcmds.c
    @@ -53,6 +53,13 @@
     #include "utils/syscache.h"
     #include "utils/varlena.h"
    
    +#define PUB_ATION_INSERT_DEFAULT true
    +#define PUB_ACTION_UPDATE_DEFAULT true
    
    
    Kindly change
    FROM:
    "PUB_ATION_INSERT_DEFAULT"
    TO:
    "PUB_ACTION_INSERT_DEFAULT"
    
    
    (3) src/test/regress/expected/publication.out
    
    +-- Verify that only superuser can reset a publication
    +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2;
    +SET ROLE regress_publication_user2;
    +ALTER PUBLICATION testpub_reset RESET; -- fail
    
    
    We have "-- fail" for one case in this patch.
    On the other hand, isn't better to add "-- ok" (or "-- success") for
    other successful statements,
    when we consider the entire tests description consistency ?
    
    
    Best Regards,
    	Takamichi Osumi
    
    
  35. RE: Skipping schema changes in publication

    Takamichi Osumi (Fujitsu) <osumi.takamichi@fujitsu.com> — 2022-05-16T08:29:58Z

    On Saturday, May 14, 2022 10:33 PM vignesh C <vignesh21@gmail.com> wrote:
    > Thanks for the comments, the attached v5 patch has the changes for the same.
    > Also I have made the changes for SKIP Table based on the new syntax, the
    > changes for the same are available in
    > v5-0002-Skip-publishing-the-tables-specified-in-EXCEPT-TA.patch.
    Hi,
    
    
    
    Several comments on v5-0002.
    
    (1) One unnecessary space before "except_pub_obj_list" syntax definition
    
    + except_pub_obj_list:  ExceptPublicationObjSpec
    +                                       { $$ = list_make1($1); }
    +                       | except_pub_obj_list ',' ExceptPublicationObjSpec
    +                                       { $$ = lappend($1, $3); }
    +                       |  /*EMPTY*/                                                            { $$ = NULL; }
    +       ;
    +
    
    From above part, kindly change
    FROM:
    " except_pub_obj_list:  ExceptPublicationObjSpec"
    TO:
    "except_pub_obj_list:  ExceptPublicationObjSpec"
    
    
    (2) doc/src/sgml/ref/create_publication.sgml
    
    (2-1)
    
    @@ -22,7 +22,7 @@ PostgreSQL documentation
      <refsynopsisdiv>
     <synopsis>
     CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
    -    [ FOR ALL TABLES
    +    [ FOR ALL TABLES [EXCEPT TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [, ... ]]
           | FOR <replaceable class="parameter">publication_object</replaceable> [, ... ] ]
         [ WITH ( <replaceable class="parameter">publication_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
    
    
    Here I think we need to add two more whitespaces around square brackets.
    Please change
    FROM:
    "[ FOR ALL TABLES [EXCEPT TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [, ... ]]"
    TO:
    "[ FOR ALL TABLES [ EXCEPT TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [, ... ] ]"
    
    When I check other documentations, I see whitespaces before/after square brackets.
    
    (2-2)
    This whitespace alignment applies to alter_publication.sgml as well.
    
    (3)
    
    
    @@ -156,6 +156,24 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
         </listitem>
        </varlistentry>
    
    +
    +   <varlistentry>
    +    <term><literal>EXCEPT TABLE</literal></term>
    +    <listitem>
    +     <para>
    +      Marks the publication as one that excludes replicating changes for the
    +      specified tables.
    +     </para>
    +
    +     <para>
    +      <literal>EXCEPT TABLE</literal> can be specified only for
    +      <literal>FOR ALL TABLES</literal> publication. It is not supported for
    +      <literal>FOR ALL TABLES IN SCHEMA </literal> publication and
    +      <literal>FOR TABLE</literal> publication.
    +     </para>
    +    </listitem>
    +   </varlistentry>
    +
    
    This EXCEPT TABLE clause is only for FOR ALL TABLES.
    So, how about extracting the main message from above part and
    moving it to an exising paragraph below, instead of having one independent paragraph ?
    
       <varlistentry>
        <term><literal>FOR ALL TABLES</literal></term>
        <listitem>
         <para>
          Marks the publication as one that replicates changes for all tables in
          the database, including tables created in the future.
         </para>
        </listitem>
       </varlistentry>
    
    Something like
    "Marks the publication as one that replicates changes for all tables in
    the database, including tables created in the future. EXCEPT TABLE indicates
    excluded tables for the defined publication.
    "
    
    
    (4) One minor confirmation about the syntax
    
    Currently, we allow one way of writing to indicate excluded tables like below.
    
    (example) CREATE PUBLICATION mypub FOR ALL TABLES EXCEPT TABLE tab3, tab4, EXCEPT TABLE tab5;
    
    This is because we define ExceptPublicationObjSpec with EXCEPT TABLE.
    Is it OK to have a room to write duplicate "EXCEPT TABLE" clauses ?
    I think there is no harm in having this,
    but I'd like to confirm whether this syntax might be better to be adjusted or not.
    
    
    (5) CheckAlterPublication
    
    +
    +       if (excepttable && !stmt->for_all_tables)
    +               ereport(ERROR,
    +                               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    +                                errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    +                                               NameStr(pubform->pubname)),
    +                                errdetail("except table cannot be added to, dropped from, or set on NON ALL TABLES publications.")));
    
    Could you please add a test for this ?
    
    
    
    Best Regards,
    	Takamichi Osumi
    
    
  36. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-16T09:23:14Z

    Below are my review comments for v5-0001.
    
    There is some overlap with comments recently posted by Osumi-san [1].
    
    (I also have review comments for v5-0002; will post them tomorrow)
    
    ======
    
    1. Commit message
    
    This patch adds a new RESET clause to ALTER PUBLICATION which will reset
    the publication to default state which includes resetting the publication
    options, setting ALL TABLES option to false and dropping the relations and
    schemas that are associated with the publication.
    
    SUGGEST
    "to default state" -> "to the default state"
    "ALL TABLES option" -> "ALL TABLES flag"
    
    ~~~
    
    2. doc/src/sgml/ref/alter_publication.sgml
    
    +  <para>
    +   The <literal>RESET</literal> clause will reset the publication to the
    +   default state which includes resetting the publication options, setting
    +   <literal>ALL TABLES</literal> option to <literal>false</literal> and
    +   dropping all relations and schemas that are associated with the publication.
       </para>
    
    "ALL TABLES option" -> "ALL TABLES flag"
    
    ~~~
    
    3. doc/src/sgml/ref/alter_publication.sgml
    
    +   invoking user to be a superuser.  <literal>RESET</literal> of publication
    +   requires the invoking user to be a superuser. To alter the owner, you must
    
    SUGGESTION
    To <literal>RESET</literal> a publication requires the invoking user
    to be a superuser.
    
    ~~~
    
    4. src/backend/commands/publicationcmds.c
    
    @@ -53,6 +53,13 @@
     #include "utils/syscache.h"
     #include "utils/varlena.h"
    
    +#define PUB_ATION_INSERT_DEFAULT true
    +#define PUB_ACTION_UPDATE_DEFAULT true
    +#define PUB_ACTION_DELETE_DEFAULT true
    +#define PUB_ACTION_TRUNCATE_DEFAULT true
    +#define PUB_VIA_ROOT_DEFAULT false
    +#define PUB_ALL_TABLES_DEFAULT false
    
    4a.
    Typo: "ATION" -> "ACTION"
    
    4b.
    I think these #defines deserve a 1 line comment.
    e.g.
    /* CREATE PUBLICATION default values for flags and options */
    
    4c.
    Since the "_DEFAULT" is a common part of all the names, maybe it is
    tidier if it comes first.
    e.g.
    #define PUB_DEFAULT_ACTION_INSERT true
    #define PUB_DEFAULT_ACTION_UPDATE true
    #define PUB_DEFAULT_ACTION_DELETE true
    #define PUB_DEFAULT_ACTION_TRUNCATE true
    #define PUB_DEFAULT_VIA_ROOT false
    #define PUB_DEFAULT_ALL_TABLES false
    
    ------
    [1] https://www.postgresql.org/message-id/TYCPR01MB8373C3120C2B3112001ED6F1EDCF9%40TYCPR01MB8373.jpnprd01.prod.outlook.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  37. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-17T02:04:57Z

    Below are my review comments for v5-0002.
    
    There may be an overlap with comments recently posted by Osumi-san [1].
    
    (I also have review comments for v5-0002; will post them tomorrow)
    
    ======
    
    1. General
    
    Is it really necessary to have to say "EXCEPT TABLE" instead of just
    "EXCEPT". It seems unnecessarily verbose and redundant when you write
    "FOR ALL TABLES EXCEPT TABLE...".
    
    If you want to keep this TABLE keyword (maybe you have plans for other
    kinds of except?) then IMO perhaps at least it can be the optional
    default except type. e.g. EXCEPT [TABLE].
    
    ~~~
    
    2. General
    
    (I was unsure whether to even mention this one).
    
    I understand the "EXCEPT" is chosen as the user-facing syntax, but it
    still seems strange when reading the patch to see attribute members
    and column names called 'except'. I think the problem is that "except"
    is not a verb, so saying except=t/f just does not make much sense.
    Sometimes I feel that for all the internal usage
    (code/comments/catalog) using "skip" and "skip-list" etc would be a
    much better choice of names. OTOH I can see that having consistency
    with the outside syntax might also be good. Anyway, please consider -
    maybe other people feel the same?
    
    ~~~
    
    3. General
    
    The ONLY keyword seems supported by the syntax for tables of the
    except-list (more on this in later comments) but:
    a) I am not sure if the patch code is accounting for that, and
    b) There are no test cases using ONLY.
    
    ~~~
    
    4. Commit message
    
    A new option "EXCEPT TABLE" in Create/Alter Publication allows
    one or more tables to be excluded, publisher will exclude sending the data
    of the excluded tables to the subscriber.
    
    SUGGESTION
    A new "EXCEPT TABLE" clause for CREATE/ALTER PUBLICATION allows one or
    more tables to be excluded. The publisher will not send the data of
    excluded tables to the subscriber.
    
    ~~
    
    5. Commit message
    
    The new syntax allows specifying exclude relations while creating a publication
    or exclude relations in alter publication. For example:
    
    SUGGESTION
    The new syntax allows specifying excluded relations when creating or
    altering a publication. For example:
    
    ~~~
    
    6. Commit message
    
    A new column prexcept is added to table "pg_publication_rel", to maintain
    the relations that the user wants to exclude publishing through the publication.
    
    SUGGESTION
    A new column "prexcept" is added to table "pg_publication_rel", to
    maintain the relations that the user wants to exclude from the
    publications.
    
    ~~~
    
    7. Commit message
    
    Modified the output plugin (pgoutput) to exclude publishing the changes of the
    excluded tables.
    
    I did not feel it was necessary to say this. It is already said above
    that the data is not sent, so that seems enough.
    
    ~~~
    
    8. Commit message
    
    Updates pg_dump to identify and dump the excluded tables of the publications.
    Updates the \d family of commands to display excluded tables of the
    publications and \dRp+ variant will now display associated except tables if any.
    
    SUGGESTION
    pg_dump is updated to identify and dump the excluded tables of the publications.
    
    The psql \d family of commands to display excluded tables. e.g. psql
    \dRp+ variant will now display associated "except tables" if any.
    
    ~~~
    
    9. doc/src/sgml/catalogs.sgml
    
    @@ -6426,6 +6426,15 @@ SCRAM-SHA-256$<replaceable>&lt;iteration
    count&gt;</replaceable>:<replaceable>&l
           if there is no publication qualifying condition.</para></entry>
          </row>
    
    +     <row>
    +      <entry role="catalog_table_entry"><para role="column_definition">
    +      <structfield>prexcept</structfield> <type>bool</type>
    +      </para>
    +      <para>
    +       True if the table must be excluded
    +      </para></entry>
    +     </row>
    
    Other descriptions on this page refer to "relation" instead of
    "table". Probably this should do the same to be consistent.
    
    ~~~
    
    10. doc/src/sgml/logical-replication.sgml
    
    @@ -1167,8 +1167,9 @@ CONTEXT:  processing remote data for replication
    origin "pg_16395" during "INSER
       <para>
        To add tables to a publication, the user must have ownership rights on the
        table. To add all tables in schema to a publication, the user must be a
    -   superuser. To create a publication that publishes all tables or
    all tables in
    -   schema automatically, the user must be a superuser.
    +   superuser. To add all tables to a publication, the user must be a superuser.
    +   To create a publication that publishes all tables or all tables in schema
    +   automatically, the user must be a superuser.
       </para>
    
    It seems like a valid change but how is this related to this EXCEPT
    patch. Maybe this fix should be patched separately?
    
    ~~~
    
    11. doc/src/sgml/ref/alter_publication.sgml
    
    @@ -22,6 +22,7 @@ PostgreSQL documentation
      <refsynopsisdiv>
     <synopsis>
     ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    ADD <replaceable class="parameter">publication_object</replaceable> [,
    ...]
    +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    ADD ALL TABLES [EXCEPT TABLE [ ONLY ] <replaceable
    class="parameter">table_name</replaceable> [ * ] [, ... ]]
    
    The [ONLY] looks misplaced when the syntax is described like this. For
    example, in practice it is possible to write "EXCEPT TABLE ONLY t1,
    ONLY t2, t3, ONLY t4" but it doesn't seem that way by looking at these
    PG DOCS.
    
    IMO would be better described like this:
    
    [ FOR ALL TABLES [ EXCEPT TABLE exception_object [,...] ]]
    
    where exception_object is:
    
        [ ONLY ] table_name [ * ]
    
    ~~~
    
    12. doc/src/sgml/ref/alter_publication.sgml
    
    @@ -82,8 +83,8 @@ ALTER PUBLICATION <replaceable
    class="parameter">name</replaceable> RESET
    
       <para>
        You must own the publication to use <command>ALTER PUBLICATION</command>.
    -   Adding a table to a publication additionally requires owning that table.
    -   The <literal>ADD ALL TABLES IN SCHEMA</literal> and
    +   Adding a table or excluding a table to a publication additionally requires
    +   owning that table. The <literal>ADD ALL TABLES IN SCHEMA</literal> and
    
    SUGGESTION
    Adding a table to or excluding a table from a publication additionally
    requires owning that table.
    
    ~~~
    
    13. doc/src/sgml/ref/alter_publication.sgml
    
    @@ -213,6 +214,14 @@ ALTER PUBLICATION sales_publication ADD ALL
    TABLES IN SCHEMA marketing, sales;
     </programlisting>
       </para>
    
    +  <para>
    +   Alter publication <structname>production_publication</structname> that
    +   publishes all tables except <structname>users</structname> and
    +   <structname>departments</structname> tables:
    +<programlisting>
    
    "that publishes" -> "to publish"
    
    ~~~
    
    14. doc/src/sgml/ref/create_publication.sgml
    
    (Same comment about the ONLY syntax as #11)
    
    ~~~
    
    15. doc/src/sgml/ref/create_publication.sgml
    
    +   <varlistentry>
    +    <term><literal>EXCEPT TABLE</literal></term>
    +    <listitem>
    +     <para>
    +      Marks the publication as one that excludes replicating changes for the
    +      specified tables.
    +     </para>
    +
    +     <para>
    +      <literal>EXCEPT TABLE</literal> can be specified only for
    +      <literal>FOR ALL TABLES</literal> publication. It is not supported for
    +      <literal>FOR ALL TABLES IN SCHEMA </literal> publication and
    +      <literal>FOR TABLE</literal> publication.
    +     </para>
    +    </listitem>
    +   </varlistentry>
    
    IMO you can remove all that "It is not supported for..." sentence. You
    don't need to spell that out again when it is already clear from the
    syntax.
    
    ~~~
    
    16. doc/src/sgml/ref/psql-ref.sgml
    
    @@ -1868,8 +1868,9 @@ testdb=&gt;
             If <replaceable class="parameter">pattern</replaceable> is
             specified, only those publications whose names match the pattern are
             listed.
    -        If <literal>+</literal> is appended to the command name, the tables and
    -        schemas associated with each publication are shown as well.
    +        If <literal>+</literal> is appended to the command name, the tables,
    +        excluded tables and schemas associated with each publication
    are shown as
    +        well.
             </para>
    
    Perhaps this is OK just as-is, but OTOH I felt that the change was
    almost unnecessary because saying it displays "the tables" kind of
    implies it would also have to account for the "excluded tables" too.
    
    ~~~
    
    17. src/backend/catalog/pg_publication.c - GetTopMostAncestorInPublication
    
    @@ -302,8 +303,9 @@ GetTopMostAncestorInPublication(Oid puboid, List
    *ancestors, int *ancestor_level
      foreach(lc, ancestors)
      {
      Oid ancestor = lfirst_oid(lc);
    - List    *apubids = GetRelationPublications(ancestor);
    + List    *apubids = GetRelationPublications(ancestor, false);
      List    *aschemaPubids = NIL;
    + List    *aexceptpubids = NIL;
    
    17a.
    I think the var "aschemaPubids" and "aexceptpubids" are only used in
    the 'else' block so it seems better they can be declared and freed in
    that block too instead of always.
    
    17b.
    Also, the camel-case of those variables is inconsistent so may fix
    that at the same time.
    
    ~~~
    
    18. src/backend/catalog/pg_publication.c - GetRelationPublications
    
    @@ -666,7 +673,7 @@ publication_add_schema(Oid pubid, Oid schemaid,
    bool if_not_exists)
    
     /* Gets list of publication oids for a relation */
     List *
    -GetRelationPublications(Oid relid)
    +GetRelationPublications(Oid relid, bool bexcept)
    
    18a.
    I felt that "except_flag" is a better name than "bexcept" for this param.
    
    18b.
    The function comment should be updated to say only relations matching
    this except_flag are returned in the list.
    
    ~~~
    
    19. src/backend/catalog/pg_publication.c - GetAllTablesPublicationRelations
    
    @@ -787,6 +795,15 @@ GetAllTablesPublicationRelations(bool pubviaroot)
      HeapTuple tuple;
      List    *result = NIL;
    
    + /*
    + * pg_publication_rel and pg_publication_namespace will only have excluded
    + * tables in case of all tables publication, no need to pass except flag
    + * to get the relations.
    + */
    + List    *exceptpubtablelist;
    +
    + exceptpubtablelist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    +
    
    19a.
    I wasn't very sure of the meaning/intent of the comment, but IIUC it
    seems to be explaining why it is not necessary to use an "except_flag"
    parameter in this code. Is it necessary/helpful to explain parameters
    that do NOT exist?
    
    19b.
    The var name "exceptpubtablelist" seems a bit overkill. (e.g.
    "excepttablelist" or "exceptlist" etc... are shorter but seem equally
    informative).
    
    ~~~
    
    20. src/backend/commands/publicationcmds.c  - CreatePublication
    
    @@ -843,54 +849,52 @@ CreatePublication(ParseState *pstate,
    CreatePublicationStmt *stmt)
      /* Make the changes visible. */
      CommandCounterIncrement();
    
    - /* Associate objects with the publication. */
    - if (stmt->for_all_tables)
    - {
    - /* Invalidate relcache so that publication info is rebuilt. */
    - CacheInvalidateRelcacheAll();
    - }
    - else
    - {
    - ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
    -    &schemaidlist);
    + ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
    + &schemaidlist);
    
    - /* FOR ALL TABLES IN SCHEMA requires superuser */
    - if (list_length(schemaidlist) > 0 && !superuser())
    - ereport(ERROR,
    - errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    - errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
    + /* FOR ALL TABLES IN SCHEMA requires superuser */
    + if (list_length(schemaidlist) > 0 && !superuser())
    + ereport(ERROR,
    + errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    + errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
    
    - if (list_length(relations) > 0)
    - {
    - List    *rels;
    + if (list_length(relations) > 0)
    + {
    + List    *rels;
    
    - rels = OpenTableList(relations);
    - CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
    -   PUBLICATIONOBJ_TABLE);
    + rels = OpenTableList(relations);
    + CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
    + PUBLICATIONOBJ_TABLE);
    
    - TransformPubWhereClauses(rels, pstate->p_sourcetext,
    - publish_via_partition_root);
    + TransformPubWhereClauses(rels, pstate->p_sourcetext,
    + publish_via_partition_root);
    
    - CheckPubRelationColumnList(rels, pstate->p_sourcetext,
    -    publish_via_partition_root);
    + CheckPubRelationColumnList(rels, pstate->p_sourcetext,
    + publish_via_partition_root);
    
    - PublicationAddTables(puboid, rels, true, NULL);
    - CloseTableList(rels);
    - }
    + PublicationAddTables(puboid, rels, true, NULL);
    + CloseTableList(rels);
    + }
    
    - if (list_length(schemaidlist) > 0)
    - {
    - /*
    - * Schema lock is held until the publication is created to prevent
    - * concurrent schema deletion.
    - */
    - LockSchemaList(schemaidlist);
    - PublicationAddSchemas(puboid, schemaidlist, true, NULL);
    - }
    + if (list_length(schemaidlist) > 0)
    + {
    + /*
    + * Schema lock is held until the publication is created to prevent
    + * concurrent schema deletion.
    + */
    + LockSchemaList(schemaidlist);
    + PublicationAddSchemas(puboid, schemaidlist, true, NULL);
      }
    
      table_close(rel, RowExclusiveLock);
    
    + /* Associate objects with the publication. */
    + if (stmt->for_all_tables)
    + {
    + /* Invalidate relcache so that publication info is rebuilt. */
    + CacheInvalidateRelcacheAll();
    + }
    +
    
    This function is refactored a lot to not use "if/else" as it did
    before. But AFAIK (maybe I misunderstood) this refactor doesn't seem
    to actually have anything to do with the EXCEPT patch. If it really is
    unrelated maybe it should not be part of this patch.
    
    ~~~
    
    21. src/backend/commands/publicationcmds.c - CheckPublicationDefValues
    
    + if (pubform->puballtables)
    + return false;
    +
    + if (!pubform->pubinsert || !pubform->pubupdate || !pubform->pubdelete ||
    + !pubform->pubtruncate || pubform->pubviaroot)
    + return false;
    
    Now you have all the #define for the PUB_DEFAULT_XXX values, perhaps
    this function should be using them instead of the hardcoded
    assumptions what the default values are.
    
    e.g.
    
    if (pubform->puballtables != PUB_DEFAULT_ALL_TABLES) return false;
    if (pubform->pubinsert != PUB_DEFAULT_ACTION_INSERT) return false;
    ...
    etc.
    
    ~~~
    
    22. src/backend/commands/publicationcmds.c -  CheckAlterPublication
    
    
    @@ -1442,6 +1516,19 @@ CheckAlterPublication(AlterPublicationStmt
    *stmt, HeapTuple tup,
        List *tables, List *schemaidlist)
     {
      Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
    + ListCell   *lc;
    + bool nonexcepttable = false;
    + bool excepttable = false;
    +
    + foreach(lc, tables)
    + {
    + PublicationTable *pub_table = lfirst_node(PublicationTable, lc);
    +
    + if (!pub_table->except)
    + nonexcepttable = true;
    + else
    + excepttable = true;
    + }
    
    22a.
    The names are very confusing. e.g. "nonexcepttable" is like a double-negative.
    
    SUGGEST:
    bool has_tables = false;
    bool has_except_tables = false;
    
    22b.
    Reverse the "if" condition to be positive instead of negative (remove !)
    e.g.
    if (pub_table->except)
    has_except_table = true;
    else
    has_table = true;
    
    ~~~
    
    23. src/backend/commands/publicationcmds.c -  CheckAlterPublication
    
    @@ -1461,12 +1548,19 @@ CheckAlterPublication(AlterPublicationStmt
    *stmt, HeapTuple tup,
      errdetail("Tables from schema cannot be added to, dropped from, or
    set on FOR ALL TABLES publications.")));
    
      /* Check that user is allowed to manipulate the publication tables. */
    - if (tables && pubform->puballtables)
    + if (nonexcepttable && tables && pubform->puballtables)
      ereport(ERROR,
    
    Seems no reason for "tables" to be in the condition since
    "nonexcepttable" can't be true if "tables" is NIL.
    
    ~~~
    
    24. src/backend/commands/publicationcmds.c -  CheckAlterPublication
    
    +
    + if (excepttable && !stmt->for_all_tables)
    + ereport(ERROR,
    + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    + NameStr(pubform->pubname)),
    + errdetail("except table cannot be added to, dropped from, or set on
    NON ALL TABLES publications.")));
    
    The errdetail message seems over-complex.
    
    SUGGESTION
    "EXCEPT TABLE clause is only allowed for FOR ALL TABLES publications."
    
    ~~~
    
    25. src/backend/commands/publicationcmds.c - AlterPublication
    
    @@ -1500,6 +1594,20 @@ AlterPublication(ParseState *pstate,
    AlterPublicationStmt *stmt)
      aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_PUBLICATION,
         stmt->pubname);
    
    + if (stmt->for_all_tables)
    + {
    + bool isdefault = CheckPublicationDefValues(tup);
    +
    + if (!isdefault)
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    + errmsg("Setting ALL TABLES requires publication \"%s\" to have
    default values",
    +    stmt->pubname),
    + errhint("Either the publication has tables/schemas associated or
    does not have default publication options or ALL TABLES option is
    set."));
    
    The errhint message seems over-complex.
    
    SUGGESTION
    "Use ALTER PUBLICATION ... RESET"
    
    ~~~
    
    26. src/bin/pg_dump/pg_dump.c - dumpPublication
    
    @@ -3980,8 +3982,34 @@ dumpPublication(Archive *fout, const
    PublicationInfo *pubinfo)
        qpubname);
    
      if (pubinfo->puballtables)
    + {
    + SimplePtrListCell *cell;
    + bool first = true;
      appendPQExpBufferStr(query, " FOR ALL TABLES");
    
    + /* Include exception tables if the publication has except tables */
    + for (cell = exceptinfo.head; cell; cell = cell->next)
    + {
    + PublicationRelInfo *pubrinfo = (PublicationRelInfo *) cell->ptr;
    + PublicationInfo *relpubinfo = pubrinfo->publication;
    + TableInfo  *tbinfo;
    +
    + if (pubinfo == relpubinfo)
    + {
    + tbinfo = pubrinfo->pubtable;
    +
    + if (first)
    + {
    + appendPQExpBufferStr(query, " EXCEPT TABLE ONLY");
    + first = false;
    + }
    + else
    + appendPQExpBufferStr(query, ", ");
    + appendPQExpBuffer(query, " %s", fmtQualifiedDumpable(tbinfo));
    + }
    + }
    + }
    +
    
    IIUC this usage of ONLY looks incorrect.
    
    26a.
    Firstly, if you want to hardwire ONLY then shouldn't it apply to every
    of the except-list table, not just the first one? e.g. "EXCEPT TABLE
    ONLY t1, ONLY t2, ONLY t3..."
    
    26b.
    Secondly, is it even correct to unconditionally hardwire the ONLY? How
    do you know that is how the user wanted it?
    
    ~~~
    
    27. src/bin/pg_dump/pg_dump.c
    
    @@ -127,6 +127,8 @@ static SimpleOidList foreign_servers_include_oids
    = {NULL, NULL};
     static SimpleStringList extension_include_patterns = {NULL, NULL};
     static SimpleOidList extension_include_oids = {NULL, NULL};
    
    +static SimplePtrList exceptinfo = {NULL, NULL};
    +
    
    Probably I just did not understand how this logic works, but how does
    this static work properly if there are multiple publications and 2
    different EXCEPT lists? E.g. where is it clearing the "exceptinfo" so
    that multiple EXCEPT TABLE lists don't become muddled?
    
    ~~~
    
    28. src/bin/pg_dump/pg_dump.c - dumpPublicationTable
    
    @@ -4330,8 +4378,11 @@ dumpPublicationTable(Archive *fout, const
    PublicationRelInfo *pubrinfo)
    
      query = createPQExpBuffer();
    
    - appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
    + appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD ",
        fmtId(pubinfo->dobj.name));
    +
    + appendPQExpBufferStr(query, "TABLE ONLY");
    +
    
    That code refactor does not seem necessary for this patch.
    
    ~~~
    
    29. src/bin/pg_dump/pg_dump_sort.c
    
    @@ -90,6 +90,7 @@ enum dbObjectTypePriorities
      PRIO_FK_CONSTRAINT,
      PRIO_POLICY,
      PRIO_PUBLICATION,
    + PRIO_PUBLICATION_EXCEPT_REL,
      PRIO_PUBLICATION_REL,
      PRIO_PUBLICATION_TABLE_IN_SCHEMA,
      PRIO_SUBSCRIPTION,
    
    I'm not sure how this enum is used (so perhaps this makes no
    difference) but judging by the enum comment why did you put the sort
    priority order PRIO_PUBLICATION_EXCEPT_REL before
    PRIO_PUBLICATION_REL. Wouldn’t it make more sense the other way
    around?
    
    ~~~
    
    30. src/bin/psql/describe.c
    
    @@ -2950,17 +2950,34 @@ describeOneTableDetails(const char *schemaname,
        "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
        "        ELSE NULL END) "
        "FROM pg_catalog.pg_publication p\n"
    -   "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
    -   "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    -   "WHERE pr.prrelid = '%s'\n"
    -   "UNION\n"
    +   " JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
    +   " JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    +   "WHERE pr.prrelid = '%s'",
    +   oid, oid, oid);
    
    I feel that trailing "\n" ("WHERE pr.prrelid = '%s'\n") should not
    have been removed.
    
    ~~~
    
    31. src/bin/psql/describe.c
    
    + /* FIXME: 150000 should be changed to 160000 later for PG16. */
    + if (pset.sversion >= 150000)
    + appendPQExpBufferStr(&buf, " AND pr.prexcept = 'f'\n");
    +
    + appendPQExpBuffer(&buf, "UNION\n"
    
    The "UNION\n" param might be better wrapped onto the next line like it
    used to be.
    
    ~~~
    
    32. src/bin/psql/describe.c
    
    + /* FIXME: 150000 should be changed to 160000 later for PG16. */
    + if (pset.sversion >= 150000)
    + appendPQExpBuffer(&buf,
    +   " AND NOT EXISTS (SELECT 1\n"
    +   " FROM pg_catalog.pg_publication_rel pr\n"
    +   " JOIN pg_catalog.pg_class pc\n"
    +   "   ON pr.prrelid = pc.oid\n"
    +   " WHERE pr.prrelid = '%s' AND pr.prpubid = p.oid)\n",
    +   oid);
    
    The whitespace indents in the SQL seem excessive here.
    
    ~~~
    
    33. src/bin/psql/describe.c - describePublications
    
    @@ -6322,6 +6344,22 @@ describePublications(const char *pattern)
      }
      }
    
    + /* FIXME: 150000 should be changed to 160000 later for PG16. */
    + if (pset.sversion >= 150000)
    + {
    + /* Get the excluded tables for the specified publication */
    + printfPQExpBuffer(&buf,
    +   "SELECT concat(c.relnamespace::regnamespace, '.', c.relname)\n"
    +   "FROM pg_catalog.pg_class c\n"
    +   "     JOIN pg_catalog.pg_publication_rel pr ON c.oid = pr.prrelid\n"
    +   "WHERE pr.prpubid = '%s'\n"
    +   "  AND pr.prexcept = 't'\n"
    +   "ORDER BY 1", pubid);
    + if (!addFooterToPublicationDesc(&buf, "Except tables:",
    + true, &cont))
    + goto error_return;
    + }
    +
    
    I think this code is misplaced. Shouldn't it be if/else and be above
    the other 150000 check, otherwise when you change this to PG16 it may
    not work as expected?
    
    ~~~
    
    34. src/bin/psql/describe.c - describePublications
    
    + if (!addFooterToPublicationDesc(&buf, "Except tables:",
    + true, &cont))
    + goto error_return;
    + }
    
    Should this be using the _T() macros same as the other prompts for translation?
    
    ~~~
    
    35. src/include/catalog/pg_publication.h
    
    I thought the param "bexpect" should be "except_flag".
    
    (same comment as #18a)
    
    ~~~
    
    36. src/include/catalog/pg_publication_rel.h
    
    @@ -31,6 +31,7 @@ CATALOG(pg_publication_rel,6106,PublicationRelRelationId)
      Oid oid; /* oid */
      Oid prpubid BKI_LOOKUP(pg_publication); /* Oid of the publication */
      Oid prrelid BKI_LOOKUP(pg_class); /* Oid of the relation */
    + bool prexcept BKI_DEFAULT(f); /* except the relation */
    
    SUGGEST (comment)
    /* skip the relation */
    
    ~~~
    
    37. src/include/commands/publicationcmds.h
    
    @@ -32,8 +32,8 @@ extern ObjectAddress AlterPublicationOwner(const
    char *name, Oid newOwnerId);
     extern void AlterPublicationOwner_oid(Oid pubid, Oid newOwnerId);
     extern void InvalidatePublicationRels(List *relids);
     extern bool pub_rf_contains_invalid_column(Oid pubid, Relation relation,
    -    List *ancestors, bool pubviaroot);
    +    List *ancestors, bool pubviaroot, bool alltables);
     extern bool pub_collist_contains_invalid_column(Oid pubid, Relation relation,
    - List *ancestors, bool pubviaroot);
    + List *ancestors, bool pubviaroot, bool alltables);
    
    Elsewhere in this patch, a similarly added param is called
    "puballtables" (not "alltables"). Please check all places and use a
    consistent param name for all of them.
    
    ~~~
    
    38. src/test/regress/sql/publication.sql
    
    There don't seem to be any tests for more than one EXCEPT TABLE (e.g.
    no list tests?)
    
    ~~~
    
    38. src/test/regress/sql/publication.sql
    
    Maybe adjust all the below comments (a-d) to say "EXCEPT TABLES"
    intead of "except tables"
    
    38a.
    +-- can't add except table to 'FOR ALL TABLES' publication
    
    38b.
    +-- can't add except table to 'FOR TABLE' publication
    
    38c.
    +-- can't add except table to 'FOR ALL TABLES IN SCHEMA' publication
    
    38d.
    +-- can't add except table when publish_via_partition_root option does not
    +-- have default value
    
    38e.
    +-- can't add except table when the publication options does not have default
    +-- values
    
    SUGGESTION
    can't add EXCEPT TABLE when the publication options are not the default values
    
    ~~~
    
    39. .../t/032_rep_changes_except_table.pl
    
    39a.
    +# Check the table data does not sync for excluded table
    +my $result = $node_subscriber->safe_psql('postgres',
    + "SELECT count(*), min(a), max(a) FROM sch1.tab1");
    +is($result, qq(0||), 'check tablesync is excluded for excluded tables');
    
    Maybe the "is" message should say "check there is no initial data
    copied for the excluded table"
    
    ~~~
    
    
    40 .../t/032_rep_changes_except_table.pl
    
    +# Insert some data into few tables and verify that inserted data is not
    +# replicated
    +$node_publisher->safe_psql('postgres',
    + "INSERT INTO sch1.tab1 VALUES(generate_series(11,20))");
    
    The comment is not quite correct. You are inserting into only one
    table here - not "few tables".
    
    ~~~
    
    41. .../t/032_rep_changes_except_table.pl
    
    +# Alter publication to exclude data changes in public.tab1 and verify that
    +# subscriber does not get the new table data.
    
    "new table data" -> "changed data for this table"
    
    ------
    [1] https://www.postgresql.org/message-id/TYCPR01MB83737C28187A6E0BADAE98F0EDCF9%40TYCPR01MB8373.jpnprd01.prod.outlook.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  38. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-05-17T03:56:28Z

    On Tue, May 17, 2022 at 7:35 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Below are my review comments for v5-0002.
    >
    > There may be an overlap with comments recently posted by Osumi-san [1].
    >
    > (I also have review comments for v5-0002; will post them tomorrow)
    >
    > ======
    >
    > 1. General
    >
    > Is it really necessary to have to say "EXCEPT TABLE" instead of just
    > "EXCEPT". It seems unnecessarily verbose and redundant when you write
    > "FOR ALL TABLES EXCEPT TABLE...".
    >
    > If you want to keep this TABLE keyword (maybe you have plans for other
    > kinds of except?)
    >
    
    I don't think there is an immediate plan but one can imagine using
    EXCEPT SCHEMA. Then for column lists, one may want to use the syntax
    Create Publication pub1 For Table t1 Except Cols (c1, ..);
    
    > then IMO perhaps at least it can be the optional
    > default except type. e.g. EXCEPT [TABLE].
    >
    
    Yeah, that might be okay, so, even if we plan to extend this in the
    future, by default we will consider the list of tables after EXCEPT
    but if the user mentions EXCEPT SCHEMA or something else then we can
    use a different object. Is that sound okay?
    
    >
    > 3. General
    >
    > The ONLY keyword seems supported by the syntax for tables of the
    > except-list (more on this in later comments) but:
    > a) I am not sure if the patch code is accounting for that, and
    > b) There are no test cases using ONLY.
    >
    > ~~~
    >
    
    Isn't it better to map ONLY with the way it can already be specified
    in CREATE PUBLICATION? I am not sure what exactly is proposed and what
    is your suggestion? Can you please explain if it is different from the
    way we use it for CREATE PUBLICATION?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  39. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-17T04:29:33Z

    On Tue, May 17, 2022 at 1:56 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, May 17, 2022 at 7:35 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Below are my review comments for v5-0002.
    > >
    > > There may be an overlap with comments recently posted by Osumi-san [1].
    > >
    > > (I also have review comments for v5-0002; will post them tomorrow)
    > >
    > > ======
    > >
    > > 1. General
    > >
    > > Is it really necessary to have to say "EXCEPT TABLE" instead of just
    > > "EXCEPT". It seems unnecessarily verbose and redundant when you write
    > > "FOR ALL TABLES EXCEPT TABLE...".
    > >
    > > If you want to keep this TABLE keyword (maybe you have plans for other
    > > kinds of except?)
    > >
    >
    > I don't think there is an immediate plan but one can imagine using
    > EXCEPT SCHEMA. Then for column lists, one may want to use the syntax
    > Create Publication pub1 For Table t1 Except Cols (c1, ..);
    >
    > > then IMO perhaps at least it can be the optional
    > > default except type. e.g. EXCEPT [TABLE].
    > >
    >
    > Yeah, that might be okay, so, even if we plan to extend this in the
    > future, by default we will consider the list of tables after EXCEPT
    > but if the user mentions EXCEPT SCHEMA or something else then we can
    > use a different object. Is that sound okay?
    
    Yes. That is what I meant.
    
    >
    > >
    > > 3. General
    > >
    > > The ONLY keyword seems supported by the syntax for tables of the
    > > except-list (more on this in later comments) but:
    > > a) I am not sure if the patch code is accounting for that, and
    > > b) There are no test cases using ONLY.
    > >
    > > ~~~
    > >
    >
    > Isn't it better to map ONLY with the way it can already be specified
    > in CREATE PUBLICATION? I am not sure what exactly is proposed and what
    > is your suggestion? Can you please explain if it is different from the
    > way we use it for CREATE PUBLICATION?
    >
    
    Yes, I am not proposing anything different to how ONLY already works
    for published tables. I was only questioning whether the patch behaves
    correctly when ONLY is specified for the tables of the EXCEPT list. I
    had some doubt about it because there are a few other review comments
    I wrote (e.g. in pg_dump.c),  and also I did not find any ONLY tests,
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  40. RE: Skipping schema changes in publication

    Yu Shi (Fujitsu) <shiy.fnst@fujitsu.com> — 2022-05-18T03:00:38Z

    On Sat, May 14, 2022 9:33 PM vignesh C <vignesh21@gmail.com> wrote:
    > 
    > Thanks for the comments, the attached v5 patch has the changes for the
    > same. Also I have made the changes for SKIP Table based on the new
    > syntax, the changes for the same are available in
    > v5-0002-Skip-publishing-the-tables-specified-in-EXCEPT-TA.patch.
    >
    
    Thanks for your patch. Here are some comments on v5-0001 patch.
    
    +		Oid			relid = lfirst_oid(lc);
    +
    +		prid = GetSysCacheOid2(PUBLICATIONRELMAP, Anum_pg_publication_rel_oid,
    +							   ObjectIdGetDatum(relid),
    +							   ObjectIdGetDatum(pubid));
    +		if (!OidIsValid(prid))
    +			ereport(ERROR,
    +					(errcode(ERRCODE_UNDEFINED_OBJECT),
    +					 errmsg("relation \"%s\" is not part of the publication",
    +							RelationGetRelationName(rel))));
    
    I think the relation in the error message should be the one whose oid is
    "relid", instead of relation "rel".
    
    Besides, I think it might be better not to report an error in this case. If
    "prid" is invalid, just ignore this relation. Because in RESET cases, we want to
    drop all tables in the publications, and there is no specific table.
    (If you agree with that, similarly missing_ok should be set to true when calling
    PublicationDropSchemas().)
    
    Regards,
    Shi yu
    
  41. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-18T17:45:08Z

    On Mon, May 16, 2022 at 8:32 AM osumi.takamichi@fujitsu.com
    <osumi.takamichi@fujitsu.com> wrote:
    >
    > On Saturday, May 14, 2022 10:33 PM vignesh C <vignesh21@gmail.com> wrote:
    > > Thanks for the comments, the attached v5 patch has the changes for the same.
    > > Also I have made the changes for SKIP Table based on the new syntax, the
    > > changes for the same are available in
    > > v5-0002-Skip-publishing-the-tables-specified-in-EXCEPT-TA.patch.
    > Hi,
    >
    >
    > Thank you for updating the patch.
    > I'll share few minor review comments on v5-0001.
    >
    >
    > (1) doc/src/sgml/ref/alter_publication.sgml
    >
    > @@ -73,12 +85,13 @@ ALTER PUBLICATION <replaceable class="parameter">name</replaceable> RENAME TO <r
    >     Adding a table to a publication additionally requires owning that table.
    >     The <literal>ADD ALL TABLES IN SCHEMA</literal> and
    >     <literal>SET ALL TABLES IN SCHEMA</literal> to a publication requires the
    > -   invoking user to be a superuser.  To alter the owner, you must also be a
    > -   direct or indirect member of the new owning role. The new owner must have
    > -   <literal>CREATE</literal> privilege on the database.  Also, the new owner
    > -   of a <literal>FOR ALL TABLES</literal> or <literal>FOR ALL TABLES IN
    > -   SCHEMA</literal> publication must be a superuser. However, a superuser can
    > -   change the ownership of a publication regardless of these restrictions.
    > +   invoking user to be a superuser.  <literal>RESET</literal> of publication
    > +   requires the invoking user to be a superuser. To alter the owner, you must
    > ...
    >
    >
    > I suggest to combine the first part of your change with one existing sentence
    > before your change, to make our description concise.
    >
    > FROM:
    > "The <literal>ADD ALL TABLES IN SCHEMA</literal> and
    > <literal>SET ALL TABLES IN SCHEMA</literal> to a publication requires the
    > invoking user to be a superuser.  <literal>RESET</literal> of publication
    > requires the invoking user to be a superuser."
    >
    > TO:
    > "The <literal>ADD ALL TABLES IN SCHEMA</literal>,
    > <literal>SET ALL TABLES IN SCHEMA</literal> to a publication and
    > <literal>RESET</literal> of publication requires the invoking user to be a superuser."
    
    Modified
    
    >
    > (2) typo
    >
    > +++ b/src/backend/commands/publicationcmds.c
    > @@ -53,6 +53,13 @@
    >  #include "utils/syscache.h"
    >  #include "utils/varlena.h"
    >
    > +#define PUB_ATION_INSERT_DEFAULT true
    > +#define PUB_ACTION_UPDATE_DEFAULT true
    >
    >
    > Kindly change
    > FROM:
    > "PUB_ATION_INSERT_DEFAULT"
    > TO:
    > "PUB_ACTION_INSERT_DEFAULT"
    
    Modified
    
    >
    > (3) src/test/regress/expected/publication.out
    >
    > +-- Verify that only superuser can reset a publication
    > +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2;
    > +SET ROLE regress_publication_user2;
    > +ALTER PUBLICATION testpub_reset RESET; -- fail
    >
    >
    > We have "-- fail" for one case in this patch.
    > On the other hand, isn't better to add "-- ok" (or "-- success") for
    > other successful statements,
    > when we consider the entire tests description consistency ?
    
    We generally do not mention success comments for all the success cases
    as that might be an overkill. I felt it is better to keep it as it is.
    Thoughts?
    
    The attached v6 patch has the changes for the same.
    
    Regards,
    Vignesh
    
  42. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-18T17:47:27Z

    On Mon, May 16, 2022 at 2:53 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Below are my review comments for v5-0001.
    >
    > There is some overlap with comments recently posted by Osumi-san [1].
    >
    > (I also have review comments for v5-0002; will post them tomorrow)
    >
    > ======
    >
    > 1. Commit message
    >
    > This patch adds a new RESET clause to ALTER PUBLICATION which will reset
    > the publication to default state which includes resetting the publication
    > options, setting ALL TABLES option to false and dropping the relations and
    > schemas that are associated with the publication.
    >
    > SUGGEST
    > "to default state" -> "to the default state"
    > "ALL TABLES option" -> "ALL TABLES flag"
    
    Modified
    
    > ~~~
    >
    > 2. doc/src/sgml/ref/alter_publication.sgml
    >
    > +  <para>
    > +   The <literal>RESET</literal> clause will reset the publication to the
    > +   default state which includes resetting the publication options, setting
    > +   <literal>ALL TABLES</literal> option to <literal>false</literal> and
    > +   dropping all relations and schemas that are associated with the publication.
    >    </para>
    >
    > "ALL TABLES option" -> "ALL TABLES flag"
    
    Modified
    
    > ~~~
    >
    > 3. doc/src/sgml/ref/alter_publication.sgml
    >
    > +   invoking user to be a superuser.  <literal>RESET</literal> of publication
    > +   requires the invoking user to be a superuser. To alter the owner, you must
    >
    > SUGGESTION
    > To <literal>RESET</literal> a publication requires the invoking user
    > to be a superuser.
    
     I have combined it with the earlier sentence.
    
    > ~~~
    >
    > 4. src/backend/commands/publicationcmds.c
    >
    > @@ -53,6 +53,13 @@
    >  #include "utils/syscache.h"
    >  #include "utils/varlena.h"
    >
    > +#define PUB_ATION_INSERT_DEFAULT true
    > +#define PUB_ACTION_UPDATE_DEFAULT true
    > +#define PUB_ACTION_DELETE_DEFAULT true
    > +#define PUB_ACTION_TRUNCATE_DEFAULT true
    > +#define PUB_VIA_ROOT_DEFAULT false
    > +#define PUB_ALL_TABLES_DEFAULT false
    >
    > 4a.
    > Typo: "ATION" -> "ACTION"
    
    Modified
    
    > 4b.
    > I think these #defines deserve a 1 line comment.
    > e.g.
    > /* CREATE PUBLICATION default values for flags and options */
    
    Added comment
    
    > 4c.
    > Since the "_DEFAULT" is a common part of all the names, maybe it is
    > tidier if it comes first.
    > e.g.
    > #define PUB_DEFAULT_ACTION_INSERT true
    > #define PUB_DEFAULT_ACTION_UPDATE true
    > #define PUB_DEFAULT_ACTION_DELETE true
    > #define PUB_DEFAULT_ACTION_TRUNCATE true
    > #define PUB_DEFAULT_VIA_ROOT false
    > #define PUB_DEFAULT_ALL_TABLES false
    
    Modified
    
    The v6 patch attached at [1] has the changes for the same.
    [1] - https://www.postgresql.org/message-id/CALDaNm0iZZDB300Dez_97S8G6_RW5QpQ8ef6X3wq8tyK-8wnXQ%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  43. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-18T17:49:01Z

    On Wed, May 18, 2022 at 8:30 AM shiy.fnst@fujitsu.com
    <shiy.fnst@fujitsu.com> wrote:
    >
    > On Sat, May 14, 2022 9:33 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Thanks for the comments, the attached v5 patch has the changes for the
    > > same. Also I have made the changes for SKIP Table based on the new
    > > syntax, the changes for the same are available in
    > > v5-0002-Skip-publishing-the-tables-specified-in-EXCEPT-TA.patch.
    > >
    >
    > Thanks for your patch. Here are some comments on v5-0001 patch.
    >
    > +               Oid                     relid = lfirst_oid(lc);
    > +
    > +               prid = GetSysCacheOid2(PUBLICATIONRELMAP, Anum_pg_publication_rel_oid,
    > +                                                          ObjectIdGetDatum(relid),
    > +                                                          ObjectIdGetDatum(pubid));
    > +               if (!OidIsValid(prid))
    > +                       ereport(ERROR,
    > +                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
    > +                                        errmsg("relation \"%s\" is not part of the publication",
    > +                                                       RelationGetRelationName(rel))));
    >
    > I think the relation in the error message should be the one whose oid is
    > "relid", instead of relation "rel".
    
    Modified it
    
    > Besides, I think it might be better not to report an error in this case. If
    > "prid" is invalid, just ignore this relation. Because in RESET cases, we want to
    > drop all tables in the publications, and there is no specific table.
    > (If you agree with that, similarly missing_ok should be set to true when calling
    > PublicationDropSchemas().)
    
    Ideally this scenario should not happen, but if it happens I felt we
    should throw an error in this case.
    
    The v6 patch attached at [1] has the changes for the same.
    [1] - https://www.postgresql.org/message-id/CALDaNm0iZZDB300Dez_97S8G6_RW5QpQ8ef6X3wq8tyK-8wnXQ%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  44. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-18T17:54:02Z

    On Mon, May 16, 2022 at 2:00 PM osumi.takamichi@fujitsu.com
    <osumi.takamichi@fujitsu.com> wrote:
    >
    > On Saturday, May 14, 2022 10:33 PM vignesh C <vignesh21@gmail.com> wrote:
    > > Thanks for the comments, the attached v5 patch has the changes for the same.
    > > Also I have made the changes for SKIP Table based on the new syntax, the
    > > changes for the same are available in
    > > v5-0002-Skip-publishing-the-tables-specified-in-EXCEPT-TA.patch.
    > Hi,
    >
    >
    >
    > Several comments on v5-0002.
    >
    > (1) One unnecessary space before "except_pub_obj_list" syntax definition
    >
    > + except_pub_obj_list:  ExceptPublicationObjSpec
    > +                                       { $$ = list_make1($1); }
    > +                       | except_pub_obj_list ',' ExceptPublicationObjSpec
    > +                                       { $$ = lappend($1, $3); }
    > +                       |  /*EMPTY*/                                                            { $$ = NULL; }
    > +       ;
    > +
    >
    > From above part, kindly change
    > FROM:
    > " except_pub_obj_list:  ExceptPublicationObjSpec"
    > TO:
    > "except_pub_obj_list:  ExceptPublicationObjSpec"
    >
    
    Modified
    
    > (2) doc/src/sgml/ref/create_publication.sgml
    >
    > (2-1)
    >
    > @@ -22,7 +22,7 @@ PostgreSQL documentation
    >   <refsynopsisdiv>
    >  <synopsis>
    >  CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
    > -    [ FOR ALL TABLES
    > +    [ FOR ALL TABLES [EXCEPT TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [, ... ]]
    >        | FOR <replaceable class="parameter">publication_object</replaceable> [, ... ] ]
    >      [ WITH ( <replaceable class="parameter">publication_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
    >
    >
    > Here I think we need to add two more whitespaces around square brackets.
    > Please change
    > FROM:
    > "[ FOR ALL TABLES [EXCEPT TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [, ... ]]"
    > TO:
    > "[ FOR ALL TABLES [ EXCEPT TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [, ... ] ]"
    >
    > When I check other documentations, I see whitespaces before/after square brackets.
    >
    > (2-2)
    > This whitespace alignment applies to alter_publication.sgml as well.
    
    Modified
    
    > (3)
    >
    >
    > @@ -156,6 +156,24 @@ CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
    >      </listitem>
    >     </varlistentry>
    >
    > +
    > +   <varlistentry>
    > +    <term><literal>EXCEPT TABLE</literal></term>
    > +    <listitem>
    > +     <para>
    > +      Marks the publication as one that excludes replicating changes for the
    > +      specified tables.
    > +     </para>
    > +
    > +     <para>
    > +      <literal>EXCEPT TABLE</literal> can be specified only for
    > +      <literal>FOR ALL TABLES</literal> publication. It is not supported for
    > +      <literal>FOR ALL TABLES IN SCHEMA </literal> publication and
    > +      <literal>FOR TABLE</literal> publication.
    > +     </para>
    > +    </listitem>
    > +   </varlistentry>
    > +
    >
    > This EXCEPT TABLE clause is only for FOR ALL TABLES.
    > So, how about extracting the main message from above part and
    > moving it to an exising paragraph below, instead of having one independent paragraph ?
    >
    >    <varlistentry>
    >     <term><literal>FOR ALL TABLES</literal></term>
    >     <listitem>
    >      <para>
    >       Marks the publication as one that replicates changes for all tables in
    >       the database, including tables created in the future.
    >      </para>
    >     </listitem>
    >    </varlistentry>
    >
    > Something like
    > "Marks the publication as one that replicates changes for all tables in
    > the database, including tables created in the future. EXCEPT TABLE indicates
    > excluded tables for the defined publication.
    > "
    >
    
    Modified
    
    > (4) One minor confirmation about the syntax
    >
    > Currently, we allow one way of writing to indicate excluded tables like below.
    >
    > (example) CREATE PUBLICATION mypub FOR ALL TABLES EXCEPT TABLE tab3, tab4, EXCEPT TABLE tab5;
    >
    > This is because we define ExceptPublicationObjSpec with EXCEPT TABLE.
    > Is it OK to have a room to write duplicate "EXCEPT TABLE" clauses ?
    > I think there is no harm in having this,
    > but I'd like to confirm whether this syntax might be better to be adjusted or not.
    
    Changed it to allow except table only once
    
    >
    > (5) CheckAlterPublication
    >
    > +
    > +       if (excepttable && !stmt->for_all_tables)
    > +               ereport(ERROR,
    > +                               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > +                                errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    > +                                               NameStr(pubform->pubname)),
    > +                                errdetail("except table cannot be added to, dropped from, or set on NON ALL TABLES publications.")));
    >
    > Could you please add a test for this ?
    
    This code can be removed because of grammar optimization, it will not
    allow tables without "ALL TABLES". Removed this code
    
    The v6 patch attached at [1] has the changes for the same.
    [1] - https://www.postgresql.org/message-id/CALDaNm0iZZDB300Dez_97S8G6_RW5QpQ8ef6X3wq8tyK-8wnXQ%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  45. RE: Skipping schema changes in publication

    Takamichi Osumi (Fujitsu) <osumi.takamichi@fujitsu.com> — 2022-05-19T01:58:27Z

    On Thursday, May 19, 2022 2:45 AM vignesh C <vignesh21@gmail.com> wrote:
    > On Mon, May 16, 2022 at 8:32 AM osumi.takamichi@fujitsu.com
    > <osumi.takamichi@fujitsu.com> wrote:
    > > (3) src/test/regress/expected/publication.out
    > >
    > > +-- Verify that only superuser can reset a publication ALTER
    > > +PUBLICATION testpub_reset OWNER TO regress_publication_user2; SET
    > > +ROLE regress_publication_user2; ALTER PUBLICATION testpub_reset
    > > +RESET; -- fail
    > >
    > >
    > > We have "-- fail" for one case in this patch.
    > > On the other hand, isn't better to add "-- ok" (or "-- success") for
    > > other successful statements, when we consider the entire tests
    > > description consistency ?
    > 
    > We generally do not mention success comments for all the success cases as
    > that might be an overkill. I felt it is better to keep it as it is.
    > Thoughts?
    Thank you for updating the patches !
    
    In terms of this point,
    I meant to say we add "-- ok" for each successful
    "ALTER PUBLICATION testpub_reset RESET;" statement.
    That means, we'll have just three places to add "--ok"
    and I thought this was not an overkill.
    
    *But*, I'm also OK with your idea.
    Please don't change the comments
    and keep them as it is like v6.
    
    
    Best Regards,
    	Takamichi Osumi
    
    
  46. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-19T02:57:02Z

    On Tue, May 17, 2022 at 7:35 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Below are my review comments for v5-0002.
    >
    > There may be an overlap with comments recently posted by Osumi-san [1].
    >
    > (I also have review comments for v5-0002; will post them tomorrow)
    >
    > ======
    >
    > 1. General
    >
    > Is it really necessary to have to say "EXCEPT TABLE" instead of just
    > "EXCEPT". It seems unnecessarily verbose and redundant when you write
    > "FOR ALL TABLES EXCEPT TABLE...".
    >
    > If you want to keep this TABLE keyword (maybe you have plans for other
    > kinds of except?) then IMO perhaps at least it can be the optional
    > default except type. e.g. EXCEPT [TABLE].
    
    I have made TABLE optional.
    
    > ~~~
    >
    > 2. General
    >
    > (I was unsure whether to even mention this one).
    >
    > I understand the "EXCEPT" is chosen as the user-facing syntax, but it
    > still seems strange when reading the patch to see attribute members
    > and column names called 'except'. I think the problem is that "except"
    > is not a verb, so saying except=t/f just does not make much sense.
    > Sometimes I feel that for all the internal usage
    > (code/comments/catalog) using "skip" and "skip-list" etc would be a
    > much better choice of names. OTOH I can see that having consistency
    > with the outside syntax might also be good. Anyway, please consider -
    > maybe other people feel the same?
    
    Earlier we had discussed whether to use SKIP, but felt SKIP was not
    appropriate and planned to use except as in [1]. Let's use except
    unless we find a better alternative.
    
    > ~~~
    >
    > 3. General
    >
    > The ONLY keyword seems supported by the syntax for tables of the
    > except-list (more on this in later comments) but:
    > a) I am not sure if the patch code is accounting for that, and
    
    I have kept the behavior similar to FOR TABLE
    
    > b) There are no test cases using ONLY.
    
    Added tests for the same
    
    > ~~~
    >
    > 4. Commit message
    >
    > A new option "EXCEPT TABLE" in Create/Alter Publication allows
    > one or more tables to be excluded, publisher will exclude sending the data
    > of the excluded tables to the subscriber.
    >
    > SUGGESTION
    > A new "EXCEPT TABLE" clause for CREATE/ALTER PUBLICATION allows one or
    > more tables to be excluded. The publisher will not send the data of
    > excluded tables to the subscriber.
    
    Modified
    
    > ~~
    >
    > 5. Commit message
    >
    > The new syntax allows specifying exclude relations while creating a publication
    > or exclude relations in alter publication. For example:
    >
    > SUGGESTION
    > The new syntax allows specifying excluded relations when creating or
    > altering a publication. For example:
    
    Modified
    
    > ~~~
    >
    > 6. Commit message
    >
    > A new column prexcept is added to table "pg_publication_rel", to maintain
    > the relations that the user wants to exclude publishing through the publication.
    >
    > SUGGESTION
    > A new column "prexcept" is added to table "pg_publication_rel", to
    > maintain the relations that the user wants to exclude from the
    > publications.
    
    Modified
    
    > ~~~
    >
    > 7. Commit message
    >
    > Modified the output plugin (pgoutput) to exclude publishing the changes of the
    > excluded tables.
    >
    > I did not feel it was necessary to say this. It is already said above
    > that the data is not sent, so that seems enough.
    
    Modified
    
    > ~~~
    >
    > 8. Commit message
    >
    > Updates pg_dump to identify and dump the excluded tables of the publications.
    > Updates the \d family of commands to display excluded tables of the
    > publications and \dRp+ variant will now display associated except tables if any.
    >
    > SUGGESTION
    > pg_dump is updated to identify and dump the excluded tables of the publications.
    >
    > The psql \d family of commands to display excluded tables. e.g. psql
    > \dRp+ variant will now display associated "except tables" if any.
    
    Modified
    
    > ~~~
    >
    > 9. doc/src/sgml/catalogs.sgml
    >
    > @@ -6426,6 +6426,15 @@ SCRAM-SHA-256$<replaceable>&lt;iteration
    > count&gt;</replaceable>:<replaceable>&l
    >        if there is no publication qualifying condition.</para></entry>
    >       </row>
    >
    > +     <row>
    > +      <entry role="catalog_table_entry"><para role="column_definition">
    > +      <structfield>prexcept</structfield> <type>bool</type>
    > +      </para>
    > +      <para>
    > +       True if the table must be excluded
    > +      </para></entry>
    > +     </row>
    >
    > Other descriptions on this page refer to "relation" instead of
    > "table". Probably this should do the same to be consistent.
    
    Modified
    
    > ~~~
    >
    > 10. doc/src/sgml/logical-replication.sgml
    >
    > @@ -1167,8 +1167,9 @@ CONTEXT:  processing remote data for replication
    > origin "pg_16395" during "INSER
    >    <para>
    >     To add tables to a publication, the user must have ownership rights on the
    >     table. To add all tables in schema to a publication, the user must be a
    > -   superuser. To create a publication that publishes all tables or
    > all tables in
    > -   schema automatically, the user must be a superuser.
    > +   superuser. To add all tables to a publication, the user must be a superuser.
    > +   To create a publication that publishes all tables or all tables in schema
    > +   automatically, the user must be a superuser.
    >    </para>
    >
    > It seems like a valid change but how is this related to this EXCEPT
    > patch. Maybe this fix should be patched separately?
    
    Earlier we were not allowed to add ALL TABLES, while altering
    publication. This is mentioned in this patch as we suport:
    ALTER PUBLICATION pubname ADD ALL TABLES syntax.
    
    > ~~~
    >
    > 11. doc/src/sgml/ref/alter_publication.sgml
    >
    > @@ -22,6 +22,7 @@ PostgreSQL documentation
    >   <refsynopsisdiv>
    >  <synopsis>
    >  ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > ADD <replaceable class="parameter">publication_object</replaceable> [,
    > ...]
    > +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > ADD ALL TABLES [EXCEPT TABLE [ ONLY ] <replaceable
    > class="parameter">table_name</replaceable> [ * ] [, ... ]]
    >
    > The [ONLY] looks misplaced when the syntax is described like this. For
    > example, in practice it is possible to write "EXCEPT TABLE ONLY t1,
    > ONLY t2, t3, ONLY t4" but it doesn't seem that way by looking at these
    > PG DOCS.
    >
    > IMO would be better described like this:
    >
    > [ FOR ALL TABLES [ EXCEPT TABLE exception_object [,...] ]]
    >
    > where exception_object is:
    >
    >     [ ONLY ] table_name [ * ]
    
    Modified
    
    > ~~~
    >
    > 12. doc/src/sgml/ref/alter_publication.sgml
    >
    > @@ -82,8 +83,8 @@ ALTER PUBLICATION <replaceable
    > class="parameter">name</replaceable> RESET
    >
    >    <para>
    >     You must own the publication to use <command>ALTER PUBLICATION</command>.
    > -   Adding a table to a publication additionally requires owning that table.
    > -   The <literal>ADD ALL TABLES IN SCHEMA</literal> and
    > +   Adding a table or excluding a table to a publication additionally requires
    > +   owning that table. The <literal>ADD ALL TABLES IN SCHEMA</literal> and
    >
    > SUGGESTION
    > Adding a table to or excluding a table from a publication additionally
    > requires owning that table.
    
    Modified
    
    > ~~~
    >
    > 13. doc/src/sgml/ref/alter_publication.sgml
    >
    > @@ -213,6 +214,14 @@ ALTER PUBLICATION sales_publication ADD ALL
    > TABLES IN SCHEMA marketing, sales;
    >  </programlisting>
    >    </para>
    >
    > +  <para>
    > +   Alter publication <structname>production_publication</structname> that
    > +   publishes all tables except <structname>users</structname> and
    > +   <structname>departments</structname> tables:
    > +<programlisting>
    >
    > "that publishes" -> "to publish"
    
    Modified
    
    > ~~~
    >
    > 14. doc/src/sgml/ref/create_publication.sgml
    >
    > (Same comment about the ONLY syntax as #11)
    
    Modified
    
    > ~~~
    >
    > 15. doc/src/sgml/ref/create_publication.sgml
    >
    > +   <varlistentry>
    > +    <term><literal>EXCEPT TABLE</literal></term>
    > +    <listitem>
    > +     <para>
    > +      Marks the publication as one that excludes replicating changes for the
    > +      specified tables.
    > +     </para>
    > +
    > +     <para>
    > +      <literal>EXCEPT TABLE</literal> can be specified only for
    > +      <literal>FOR ALL TABLES</literal> publication. It is not supported for
    > +      <literal>FOR ALL TABLES IN SCHEMA </literal> publication and
    > +      <literal>FOR TABLE</literal> publication.
    > +     </para>
    > +    </listitem>
    > +   </varlistentry>
    >
    > IMO you can remove all that "It is not supported for..." sentence. You
    > don't need to spell that out again when it is already clear from the
    > syntax.
    
    Modified
    
    > ~~~
    >
    > 16. doc/src/sgml/ref/psql-ref.sgml
    >
    > @@ -1868,8 +1868,9 @@ testdb=&gt;
    >          If <replaceable class="parameter">pattern</replaceable> is
    >          specified, only those publications whose names match the pattern are
    >          listed.
    > -        If <literal>+</literal> is appended to the command name, the tables and
    > -        schemas associated with each publication are shown as well.
    > +        If <literal>+</literal> is appended to the command name, the tables,
    > +        excluded tables and schemas associated with each publication
    > are shown as
    > +        well.
    >          </para>
    >
    > Perhaps this is OK just as-is, but OTOH I felt that the change was
    > almost unnecessary because saying it displays "the tables" kind of
    > implies it would also have to account for the "excluded tables" too.
    
    I mentioned it that way so that it is clearer and to avoid confusions
    to be pointed out by other members later. I felt let's keep it this
    way.
    
    > ~~~
    >
    > 17. src/backend/catalog/pg_publication.c - GetTopMostAncestorInPublication
    >
    > @@ -302,8 +303,9 @@ GetTopMostAncestorInPublication(Oid puboid, List
    > *ancestors, int *ancestor_level
    >   foreach(lc, ancestors)
    >   {
    >   Oid ancestor = lfirst_oid(lc);
    > - List    *apubids = GetRelationPublications(ancestor);
    > + List    *apubids = GetRelationPublications(ancestor, false);
    >   List    *aschemaPubids = NIL;
    > + List    *aexceptpubids = NIL;
    >
    > 17a.
    > I think the var "aschemaPubids" and "aexceptpubids" are only used in
    > the 'else' block so it seems better they can be declared and freed in
    > that block too instead of always.
    
    Modified
    
    > 17b.
    > Also, the camel-case of those variables is inconsistent so may fix
    > that at the same time.
    
    Modified
    
    > ~~~
    >
    > 18. src/backend/catalog/pg_publication.c - GetRelationPublications
    >
    > @@ -666,7 +673,7 @@ publication_add_schema(Oid pubid, Oid schemaid,
    > bool if_not_exists)
    >
    >  /* Gets list of publication oids for a relation */
    >  List *
    > -GetRelationPublications(Oid relid)
    > +GetRelationPublications(Oid relid, bool bexcept)
    >
    > 18a.
    > I felt that "except_flag" is a better name than "bexcept" for this param.
    
    Modified
    
    > 18b.
    > The function comment should be updated to say only relations matching
    > this except_flag are returned in the list.
    
    Modified
    
    > ~~~
    >
    > 19. src/backend/catalog/pg_publication.c - GetAllTablesPublicationRelations
    >
    > @@ -787,6 +795,15 @@ GetAllTablesPublicationRelations(bool pubviaroot)
    >   HeapTuple tuple;
    >   List    *result = NIL;
    >
    > + /*
    > + * pg_publication_rel and pg_publication_namespace will only have excluded
    > + * tables in case of all tables publication, no need to pass except flag
    > + * to get the relations.
    > + */
    > + List    *exceptpubtablelist;
    > +
    > + exceptpubtablelist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    > +
    >
    > 19a.
    > I wasn't very sure of the meaning/intent of the comment, but IIUC it
    > seems to be explaining why it is not necessary to use an "except_flag"
    > parameter in this code. Is it necessary/helpful to explain parameters
    > that do NOT exist?
    
    I have removed it
    
    > 19b.
    > The var name "exceptpubtablelist" seems a bit overkill. (e.g.
    > "excepttablelist" or "exceptlist" etc... are shorter but seem equally
    > informative).
    
    Modified
    
    > ~~~
    >
    > 20. src/backend/commands/publicationcmds.c  - CreatePublication
    >
    > @@ -843,54 +849,52 @@ CreatePublication(ParseState *pstate,
    > CreatePublicationStmt *stmt)
    >   /* Make the changes visible. */
    >   CommandCounterIncrement();
    >
    > - /* Associate objects with the publication. */
    > - if (stmt->for_all_tables)
    > - {
    > - /* Invalidate relcache so that publication info is rebuilt. */
    > - CacheInvalidateRelcacheAll();
    > - }
    > - else
    > - {
    > - ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
    > -    &schemaidlist);
    > + ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
    > + &schemaidlist);
    >
    > - /* FOR ALL TABLES IN SCHEMA requires superuser */
    > - if (list_length(schemaidlist) > 0 && !superuser())
    > - ereport(ERROR,
    > - errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > - errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
    > + /* FOR ALL TABLES IN SCHEMA requires superuser */
    > + if (list_length(schemaidlist) > 0 && !superuser())
    > + ereport(ERROR,
    > + errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > + errmsg("must be superuser to create FOR ALL TABLES IN SCHEMA publication"));
    >
    > - if (list_length(relations) > 0)
    > - {
    > - List    *rels;
    > + if (list_length(relations) > 0)
    > + {
    > + List    *rels;
    >
    > - rels = OpenTableList(relations);
    > - CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
    > -   PUBLICATIONOBJ_TABLE);
    > + rels = OpenTableList(relations);
    > + CheckObjSchemaNotAlreadyInPublication(rels, schemaidlist,
    > + PUBLICATIONOBJ_TABLE);
    >
    > - TransformPubWhereClauses(rels, pstate->p_sourcetext,
    > - publish_via_partition_root);
    > + TransformPubWhereClauses(rels, pstate->p_sourcetext,
    > + publish_via_partition_root);
    >
    > - CheckPubRelationColumnList(rels, pstate->p_sourcetext,
    > -    publish_via_partition_root);
    > + CheckPubRelationColumnList(rels, pstate->p_sourcetext,
    > + publish_via_partition_root);
    >
    > - PublicationAddTables(puboid, rels, true, NULL);
    > - CloseTableList(rels);
    > - }
    > + PublicationAddTables(puboid, rels, true, NULL);
    > + CloseTableList(rels);
    > + }
    >
    > - if (list_length(schemaidlist) > 0)
    > - {
    > - /*
    > - * Schema lock is held until the publication is created to prevent
    > - * concurrent schema deletion.
    > - */
    > - LockSchemaList(schemaidlist);
    > - PublicationAddSchemas(puboid, schemaidlist, true, NULL);
    > - }
    > + if (list_length(schemaidlist) > 0)
    > + {
    > + /*
    > + * Schema lock is held until the publication is created to prevent
    > + * concurrent schema deletion.
    > + */
    > + LockSchemaList(schemaidlist);
    > + PublicationAddSchemas(puboid, schemaidlist, true, NULL);
    >   }
    >
    >   table_close(rel, RowExclusiveLock);
    >
    > + /* Associate objects with the publication. */
    > + if (stmt->for_all_tables)
    > + {
    > + /* Invalidate relcache so that publication info is rebuilt. */
    > + CacheInvalidateRelcacheAll();
    > + }
    > +
    >
    > This function is refactored a lot to not use "if/else" as it did
    > before. But AFAIK (maybe I misunderstood) this refactor doesn't seem
    > to actually have anything to do with the EXCEPT patch. If it really is
    > unrelated maybe it should not be part of this patch.
    
    Earlier tables cannot be specified with all tables, now except tables
    can be specified with all tables, except tables should be added to
    pg_publication_rel, to handle it the code changes are required.
    
    > ~~~
    >
    > 21. src/backend/commands/publicationcmds.c - CheckPublicationDefValues
    >
    > + if (pubform->puballtables)
    > + return false;
    > +
    > + if (!pubform->pubinsert || !pubform->pubupdate || !pubform->pubdelete ||
    > + !pubform->pubtruncate || pubform->pubviaroot)
    > + return false;
    >
    > Now you have all the #define for the PUB_DEFAULT_XXX values, perhaps
    > this function should be using them instead of the hardcoded
    > assumptions what the default values are.
    >
    > e.g.
    >
    > if (pubform->puballtables != PUB_DEFAULT_ALL_TABLES) return false;
    > if (pubform->pubinsert != PUB_DEFAULT_ACTION_INSERT) return false;
    > ...
    > etc.
    
    Modified
    
    > ~~~
    >
    > 22. src/backend/commands/publicationcmds.c -  CheckAlterPublication
    >
    >
    > @@ -1442,6 +1516,19 @@ CheckAlterPublication(AlterPublicationStmt
    > *stmt, HeapTuple tup,
    >     List *tables, List *schemaidlist)
    >  {
    >   Form_pg_publication pubform = (Form_pg_publication) GETSTRUCT(tup);
    > + ListCell   *lc;
    > + bool nonexcepttable = false;
    > + bool excepttable = false;
    > +
    > + foreach(lc, tables)
    > + {
    > + PublicationTable *pub_table = lfirst_node(PublicationTable, lc);
    > +
    > + if (!pub_table->except)
    > + nonexcepttable = true;
    > + else
    > + excepttable = true;
    > + }
    >
    > 22a.
    > The names are very confusing. e.g. "nonexcepttable" is like a double-negative.
    >
    > SUGGEST:
    > bool has_tables = false;
    > bool has_except_tables = false;
    >
    > 22b.
    > Reverse the "if" condition to be positive instead of negative (remove !)
    > e.g.
    > if (pub_table->except)
    > has_except_table = true;
    > else
    > has_table = true;
    
    This code can be removed because of grammar optimization, it will not
    allow except table without "ALL TABLES". Removed these changes.
    
    > ~~~
    >
    > 23. src/backend/commands/publicationcmds.c -  CheckAlterPublication
    >
    > @@ -1461,12 +1548,19 @@ CheckAlterPublication(AlterPublicationStmt
    > *stmt, HeapTuple tup,
    >   errdetail("Tables from schema cannot be added to, dropped from, or
    > set on FOR ALL TABLES publications.")));
    >
    >   /* Check that user is allowed to manipulate the publication tables. */
    > - if (tables && pubform->puballtables)
    > + if (nonexcepttable && tables && pubform->puballtables)
    >   ereport(ERROR,
    >
    > Seems no reason for "tables" to be in the condition since
    > "nonexcepttable" can't be true if "tables" is NIL.
    
    This code can be removed because of grammar optimization, it will not
    allow except table without "ALL TABLES". Removed these changes.
    
    > ~~~
    >
    > 24. src/backend/commands/publicationcmds.c -  CheckAlterPublication
    >
    > +
    > + if (excepttable && !stmt->for_all_tables)
    > + ereport(ERROR,
    > + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    > + NameStr(pubform->pubname)),
    > + errdetail("except table cannot be added to, dropped from, or set on
    > NON ALL TABLES publications.")));
    >
    > The errdetail message seems over-complex.
    >
    > SUGGESTION
    > "EXCEPT TABLE clause is only allowed for FOR ALL TABLES publications."
    
    This code can be removed because of grammar optimization, it will not
    allow except table without "ALL TABLES". Removed this code
    
    > ~~~
    >
    > 25. src/backend/commands/publicationcmds.c - AlterPublication
    >
    > @@ -1500,6 +1594,20 @@ AlterPublication(ParseState *pstate,
    > AlterPublicationStmt *stmt)
    >   aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_PUBLICATION,
    >      stmt->pubname);
    >
    > + if (stmt->for_all_tables)
    > + {
    > + bool isdefault = CheckPublicationDefValues(tup);
    > +
    > + if (!isdefault)
    > + ereport(ERROR,
    > + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > + errmsg("Setting ALL TABLES requires publication \"%s\" to have
    > default values",
    > +    stmt->pubname),
    > + errhint("Either the publication has tables/schemas associated or
    > does not have default publication options or ALL TABLES option is
    > set."));
    >
    > The errhint message seems over-complex.
    >
    > SUGGESTION
    > "Use ALTER PUBLICATION ... RESET"
    
    Modified
    
    > ~~~
    >
    > 26. src/bin/pg_dump/pg_dump.c - dumpPublication
    >
    > @@ -3980,8 +3982,34 @@ dumpPublication(Archive *fout, const
    > PublicationInfo *pubinfo)
    >     qpubname);
    >
    >   if (pubinfo->puballtables)
    > + {
    > + SimplePtrListCell *cell;
    > + bool first = true;
    >   appendPQExpBufferStr(query, " FOR ALL TABLES");
    >
    > + /* Include exception tables if the publication has except tables */
    > + for (cell = exceptinfo.head; cell; cell = cell->next)
    > + {
    > + PublicationRelInfo *pubrinfo = (PublicationRelInfo *) cell->ptr;
    > + PublicationInfo *relpubinfo = pubrinfo->publication;
    > + TableInfo  *tbinfo;
    > +
    > + if (pubinfo == relpubinfo)
    > + {
    > + tbinfo = pubrinfo->pubtable;
    > +
    > + if (first)
    > + {
    > + appendPQExpBufferStr(query, " EXCEPT TABLE ONLY");
    > + first = false;
    > + }
    > + else
    > + appendPQExpBufferStr(query, ", ");
    > + appendPQExpBuffer(query, " %s", fmtQualifiedDumpable(tbinfo));
    > + }
    > + }
    > + }
    > +
    >
    > IIUC this usage of ONLY looks incorrect.
    >
    > 26a.
    > Firstly, if you want to hardwire ONLY then shouldn't it apply to every
    > of the except-list table, not just the first one? e.g. "EXCEPT TABLE
    > ONLY t1, ONLY t2, ONLY t3..."
    
    Modified, included ONLY for all the tables
    
    > 26b.
    > Secondly, is it even correct to unconditionally hardwire the ONLY? How
    > do you know that is how the user wanted it?
    
    The table ONLY selection is handled appropriately while creating
    publication and stored in pg_publication_rel. When we dump all the
    parent and child table will be included specifying ONLY will handle
    both scenarios with and without ONLY. This is the same behavior as in
    FOR TABLE publication
    
    > ~~~
    >
    > 27. src/bin/pg_dump/pg_dump.c
    >
    > @@ -127,6 +127,8 @@ static SimpleOidList foreign_servers_include_oids
    > = {NULL, NULL};
    >  static SimpleStringList extension_include_patterns = {NULL, NULL};
    >  static SimpleOidList extension_include_oids = {NULL, NULL};
    >
    > +static SimplePtrList exceptinfo = {NULL, NULL};
    > +
    >
    > Probably I just did not understand how this logic works, but how does
    > this static work properly if there are multiple publications and 2
    > different EXCEPT lists? E.g. where is it clearing the "exceptinfo" so
    > that multiple EXCEPT TABLE lists don't become muddled?
    
    Currently exceptinfo holds all the exception tables and the
    corresponding publications. When we dump the publication it will
    select the appropriate exception tables that correspond to the
    publication and dump the exception tables associated for this
    publication. Since this is a special syntax "CREATE PUBLICATION FOR
    ALL TABLES EXCEPT TABLE tb1 .." all the except tables should be
    specified in a single statement unlike the other publication objects.
    
    > ~~~
    >
    > 28. src/bin/pg_dump/pg_dump.c - dumpPublicationTable
    >
    > @@ -4330,8 +4378,11 @@ dumpPublicationTable(Archive *fout, const
    > PublicationRelInfo *pubrinfo)
    >
    >   query = createPQExpBuffer();
    >
    > - appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
    > + appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD ",
    >     fmtId(pubinfo->dobj.name));
    > +
    > + appendPQExpBufferStr(query, "TABLE ONLY");
    > +
    >
    > That code refactor does not seem necessary for this patch.
    
    Modified
    
    > ~~~
    >
    > 29. src/bin/pg_dump/pg_dump_sort.c
    >
    > @@ -90,6 +90,7 @@ enum dbObjectTypePriorities
    >   PRIO_FK_CONSTRAINT,
    >   PRIO_POLICY,
    >   PRIO_PUBLICATION,
    > + PRIO_PUBLICATION_EXCEPT_REL,
    >   PRIO_PUBLICATION_REL,
    >   PRIO_PUBLICATION_TABLE_IN_SCHEMA,
    >   PRIO_SUBSCRIPTION,
    >
    > I'm not sure how this enum is used (so perhaps this makes no
    > difference) but judging by the enum comment why did you put the sort
    > priority order PRIO_PUBLICATION_EXCEPT_REL before
    > PRIO_PUBLICATION_REL. Wouldn’t it make more sense the other way
    > around?
    
    This order does not matter, since the new syntax is like "CREATE
    PUBLICATION.. FOR ALL TABLES EXCEPT TABLE ....", all the except tables
    need to be accumulated and handled during dump publication. This code
    changes take care of accumulating the exception table which will be
    used later by dump publication
    
    > ~~~
    >
    > 30. src/bin/psql/describe.c
    >
    > @@ -2950,17 +2950,34 @@ describeOneTableDetails(const char *schemaname,
    >     "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
    >     "        ELSE NULL END) "
    >     "FROM pg_catalog.pg_publication p\n"
    > -   "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
    > -   "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    > -   "WHERE pr.prrelid = '%s'\n"
    > -   "UNION\n"
    > +   " JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
    > +   " JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    > +   "WHERE pr.prrelid = '%s'",
    > +   oid, oid, oid);
    >
    > I feel that trailing "\n" ("WHERE pr.prrelid = '%s'\n") should not
    > have been removed.
    
    Modified
    
    > ~~~
    >
    > 31. src/bin/psql/describe.c
    >
    > + /* FIXME: 150000 should be changed to 160000 later for PG16. */
    > + if (pset.sversion >= 150000)
    > + appendPQExpBufferStr(&buf, " AND pr.prexcept = 'f'\n");
    > +
    > + appendPQExpBuffer(&buf, "UNION\n"
    >
    > The "UNION\n" param might be better wrapped onto the next line like it
    > used to be.
    
    Modified
    
    > ~~~
    >
    > 32. src/bin/psql/describe.c
    >
    > + /* FIXME: 150000 should be changed to 160000 later for PG16. */
    > + if (pset.sversion >= 150000)
    > + appendPQExpBuffer(&buf,
    > +   " AND NOT EXISTS (SELECT 1\n"
    > +   " FROM pg_catalog.pg_publication_rel pr\n"
    > +   " JOIN pg_catalog.pg_class pc\n"
    > +   "   ON pr.prrelid = pc.oid\n"
    > +   " WHERE pr.prrelid = '%s' AND pr.prpubid = p.oid)\n",
    > +   oid);
    >
    > The whitespace indents in the SQL seem excessive here.
    
    Modified
    
    > ~~~
    >
    > 33. src/bin/psql/describe.c - describePublications
    >
    > @@ -6322,6 +6344,22 @@ describePublications(const char *pattern)
    >   }
    >   }
    >
    > + /* FIXME: 150000 should be changed to 160000 later for PG16. */
    > + if (pset.sversion >= 150000)
    > + {
    > + /* Get the excluded tables for the specified publication */
    > + printfPQExpBuffer(&buf,
    > +   "SELECT concat(c.relnamespace::regnamespace, '.', c.relname)\n"
    > +   "FROM pg_catalog.pg_class c\n"
    > +   "     JOIN pg_catalog.pg_publication_rel pr ON c.oid = pr.prrelid\n"
    > +   "WHERE pr.prpubid = '%s'\n"
    > +   "  AND pr.prexcept = 't'\n"
    > +   "ORDER BY 1", pubid);
    > + if (!addFooterToPublicationDesc(&buf, "Except tables:",
    > + true, &cont))
    > + goto error_return;
    > + }
    > +
    >
    > I think this code is misplaced. Shouldn't it be if/else and be above
    > the other 150000 check, otherwise when you change this to PG16 it may
    > not work as expected?
    
    I moved this to else. I felt this is applicable only for all tables
    publication. Just keeping in else is fine.
    
    > ~~~
    >
    > 34. src/bin/psql/describe.c - describePublications
    >
    > + if (!addFooterToPublicationDesc(&buf, "Except tables:",
    > + true, &cont))
    > + goto error_return;
    > + }
    >
    > Should this be using the _T() macros same as the other prompts for translation?
    
    Modified
    
    > ~~~
    >
    > 35. src/include/catalog/pg_publication.h
    >
    > I thought the param "bexpect" should be "except_flag".
    >
    > (same comment as #18a)
    
    Modified
    
    > ~~~
    >
    > 36. src/include/catalog/pg_publication_rel.h
    >
    > @@ -31,6 +31,7 @@ CATALOG(pg_publication_rel,6106,PublicationRelRelationId)
    >   Oid oid; /* oid */
    >   Oid prpubid BKI_LOOKUP(pg_publication); /* Oid of the publication */
    >   Oid prrelid BKI_LOOKUP(pg_class); /* Oid of the relation */
    > + bool prexcept BKI_DEFAULT(f); /* except the relation */
    >
    > SUGGEST (comment)
    > /* skip the relation */
    
    Changed it to exclude the relation
    
    > ~~~
    >
    > 37. src/include/commands/publicationcmds.h
    >
    > @@ -32,8 +32,8 @@ extern ObjectAddress AlterPublicationOwner(const
    > char *name, Oid newOwnerId);
    >  extern void AlterPublicationOwner_oid(Oid pubid, Oid newOwnerId);
    >  extern void InvalidatePublicationRels(List *relids);
    >  extern bool pub_rf_contains_invalid_column(Oid pubid, Relation relation,
    > -    List *ancestors, bool pubviaroot);
    > +    List *ancestors, bool pubviaroot, bool alltables);
    >  extern bool pub_collist_contains_invalid_column(Oid pubid, Relation relation,
    > - List *ancestors, bool pubviaroot);
    > + List *ancestors, bool pubviaroot, bool alltables);
    >
    > Elsewhere in this patch, a similarly added param is called
    > "puballtables" (not "alltables"). Please check all places and use a
    > consistent param name for all of them.
    
    Modified
    
    > ~~~
    >
    > 38. src/test/regress/sql/publication.sql
    >
    > There don't seem to be any tests for more than one EXCEPT TABLE (e.g.
    > no list tests?)
    
    Modified
    
    > ~~~
    >
    > 38. src/test/regress/sql/publication.sql
    >
    > Maybe adjust all the below comments (a-d) to say "EXCEPT TABLES"
    > intead of "except tables"
    >
    > 38a.
    > +-- can't add except table to 'FOR ALL TABLES' publication
    >
    > 38b.
    > +-- can't add except table to 'FOR TABLE' publication
    >
    > 38c.
    > +-- can't add except table to 'FOR ALL TABLES IN SCHEMA' publication
    >
    > 38d.
    > +-- can't add except table when publish_via_partition_root option does not
    > +-- have default value
    >
    > 38e.
    > +-- can't add except table when the publication options does not have default
    > +-- values
    >
    > SUGGESTION
    > can't add EXCEPT TABLE when the publication options are not the default values
    
    Modified
    > ~~~
    >
    > 39. .../t/032_rep_changes_except_table.pl
    >
    > 39a.
    > +# Check the table data does not sync for excluded table
    > +my $result = $node_subscriber->safe_psql('postgres',
    > + "SELECT count(*), min(a), max(a) FROM sch1.tab1");
    > +is($result, qq(0||), 'check tablesync is excluded for excluded tables');
    >
    > Maybe the "is" message should say "check there is no initial data
    > copied for the excluded table"
    
    Modified
    
    > ~~~
    >
    >
    > 40 .../t/032_rep_changes_except_table.pl
    >
    > +# Insert some data into few tables and verify that inserted data is not
    > +# replicated
    > +$node_publisher->safe_psql('postgres',
    > + "INSERT INTO sch1.tab1 VALUES(generate_series(11,20))");
    >
    > The comment is not quite correct. You are inserting into only one
    > table here - not "few tables".
    
    Modified
    
    > ~~~
    >
    > 41. .../t/032_rep_changes_except_table.pl
    >
    > +# Alter publication to exclude data changes in public.tab1 and verify that
    > +# subscriber does not get the new table data.
    >
    > "new table data" -> "changed data for this table"
    
    Modified
    
    Thanks for the comments, the v6 patch attached at [2] has the changes
    for the same.
    [1] - https://www.postgresql.org/message-id/a2004f08-eb2f-b124-115c-f8f18667e585%40enterprisedb.com
    [2] - https://www.postgresql.org/message-id/CALDaNm0iZZDB300Dez_97S8G6_RW5QpQ8ef6X3wq8tyK-8wnXQ%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  47. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-19T08:19:19Z

    Below are my review comments for v6-0001.
    
    ======
    
    1. General.
    
    The patch failed 'publication' tests in the make check phase.
    
    Please add this work to the commit-fest so that the 'cfbot' can report
    such errors sooner.
    
    ~~~
    
    2. src/backend/commands/publicationcmds.c - AlterPublicationReset
    
    +/*
    + * Reset the publication.
    + *
    + * Reset the publication options, publication relations and
    publication schemas.
    + */
    +static void
    +AlterPublicationReset(ParseState *pstate, AlterPublicationStmt *stmt,
    + Relation rel, HeapTuple tup)
    
    SUGGESTION (Make the comment similar to the sgml text instead of
    repeating "publication" 4x !)
    /*
     * Reset the publication options, set the ALL TABLES flag to false, and
     * drop all relations and schemas that are associated with the publication.
     */
    
    ~~~
    
    3. src/test/regress/expected/publication.out
    
    make check failed. The diff is below:
    
    @@ -1716,7 +1716,7 @@
     -- Verify that only superuser can reset a publication
     ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2;
     SET ROLE regress_publication_user2;
    -ALTER PUBLICATION testpub_reset RESET; -- fail
    +ALTER PUBLICATION testpub_reset RESET; -- fail - must be superuser
     ERROR:  must be superuser to RESET publication
     SET ROLE regress_publication_user;
     DROP PUBLICATION testpub_reset;
    
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  48. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-20T00:19:13Z

    FYI, although the v6-0002 patch applied cleanly, I found that the SGML
    was malformed and so the pg docs build fails.
    
    ~~~
    e.g.
    
    [postgres@CentOS7-x64 sgml]$ make STYLE=website html
    { \
      echo "<!ENTITY version \"15beta1\">"; \
      echo "<!ENTITY majorversion \"15\">"; \
    } > version.sgml
    '/usr/bin/perl' ./mk_feature_tables.pl YES
    ../../../src/backend/catalog/sql_feature_packages.txt
    ../../../src/backend/catalog/sql_features.txt >
    features-supported.sgml
    '/usr/bin/perl' ./mk_feature_tables.pl NO
    ../../../src/backend/catalog/sql_feature_packages.txt
    ../../../src/backend/catalog/sql_features.txt >
    features-unsupported.sgml
    '/usr/bin/perl' ./generate-errcodes-table.pl
    ../../../src/backend/utils/errcodes.txt > errcodes-table.sgml
    '/usr/bin/perl' ./generate-keywords-table.pl . > keywords-table.sgml
    /usr/bin/xmllint --path . --noout --valid postgres.sgml
    ref/create_publication.sgml:171: parser error : Opening and ending tag
    mismatch: varlistentry line 166 and listitem
        </listitem>
                   ^
    ref/create_publication.sgml:172: parser error : Opening and ending tag
    mismatch: variablelist line 60 and varlistentry
       </varlistentry>
                      ^
    ref/create_publication.sgml:226: parser error : Opening and ending tag
    mismatch: refsect1 line 57 and variablelist
      </variablelist>
                     ^
    ...
    
    I will work around it locally, but for future patches please check the
    SGML builds ok before posting.
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  49. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-20T01:20:47Z

    On Fri, May 20, 2022 at 10:19 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > FYI, although the v6-0002 patch applied cleanly, I found that the SGML
    > was malformed and so the pg docs build fails.
    >
    > ~~~
    > e.g.
    >
    > [postgres@CentOS7-x64 sgml]$ make STYLE=website html
    > { \
    >   echo "<!ENTITY version \"15beta1\">"; \
    >   echo "<!ENTITY majorversion \"15\">"; \
    > } > version.sgml
    > '/usr/bin/perl' ./mk_feature_tables.pl YES
    > ../../../src/backend/catalog/sql_feature_packages.txt
    > ../../../src/backend/catalog/sql_features.txt >
    > features-supported.sgml
    > '/usr/bin/perl' ./mk_feature_tables.pl NO
    > ../../../src/backend/catalog/sql_feature_packages.txt
    > ../../../src/backend/catalog/sql_features.txt >
    > features-unsupported.sgml
    > '/usr/bin/perl' ./generate-errcodes-table.pl
    > ../../../src/backend/utils/errcodes.txt > errcodes-table.sgml
    > '/usr/bin/perl' ./generate-keywords-table.pl . > keywords-table.sgml
    > /usr/bin/xmllint --path . --noout --valid postgres.sgml
    > ref/create_publication.sgml:171: parser error : Opening and ending tag
    > mismatch: varlistentry line 166 and listitem
    >     </listitem>
    >                ^
    > ref/create_publication.sgml:172: parser error : Opening and ending tag
    > mismatch: variablelist line 60 and varlistentry
    >    </varlistentry>
    >                   ^
    > ref/create_publication.sgml:226: parser error : Opening and ending tag
    > mismatch: refsect1 line 57 and variablelist
    >   </variablelist>
    >                  ^
    > ...
    >
    > I will work around it locally, but for future patches please check the
    > SGML builds ok before posting.
    
    FYI, I rewrote the bad SGML fragment like this:
    
       <varlistentry>
        <term><literal>EXCEPT TABLE</literal></term>
        <listitem>
         <para>
          This clause specifies a list of tables to exclude from the publication. It
          can only be used with <literal>FOR ALL TABLES</literal>.
         </para>
        </listitem>
       </varlistentry>
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  50. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-20T05:53:12Z

    Below are my review comments for v6-0002.
    
    ======
    
    1. Commit message.
    The psql \d family of commands to display excluded tables.
    
    SUGGESTION
    The psql \d family of commands can now display excluded tables.
    
    ~~~
    
    2. doc/src/sgml/ref/alter_publication.sgml
    
    @@ -22,6 +22,7 @@ PostgreSQL documentation
      <refsynopsisdiv>
     <synopsis>
     ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    ADD <replaceable class="parameter">publication_object</replaceable> [,
    ...]
    +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    ADD ALL TABLES [ EXCEPT [ TABLE ] exception_object [, ... ] ]
    
    The "exception_object" font is wrong. Should look the same as
    "publication_object"
    
    ~~~
    
    3. doc/src/sgml/ref/alter_publication.sgml - Examples
    
    @@ -214,6 +220,14 @@ ALTER PUBLICATION sales_publication ADD ALL
    TABLES IN SCHEMA marketing, sales;
     </programlisting>
       </para>
    
    +  <para>
    +   Alter publication <structname>production_publication</structname> to publish
    +   all tables except <structname>users</structname> and
    +   <structname>departments</structname> tables:
    +<programlisting>
    +ALTER PUBLICATION production_publication ADD ALL TABLES EXCEPT TABLE
    users, departments;
    +</programlisting></para>
    
    Consider using "EXCEPT" instead of "EXCEPT TABLE" because that will
    show TABLE keyword is optional.
    
    ~~~
    
    4. doc/src/sgml/ref/create_publication.sgml
    
    An SGML tag error caused building the docs to fail. My fix was
    previously reported [1].
    
    ~~~
    
    5. doc/src/sgml/ref/create_publication.sgml
    
    @@ -22,7 +22,7 @@ PostgreSQL documentation
      <refsynopsisdiv>
     <synopsis>
     CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
    -    [ FOR ALL TABLES
    +    [ FOR ALL TABLES [ EXCEPT [ TABLE ] exception_object [, ... ] ]
    
    The "exception_object" font is wrong. Should look the same as
    "publication_object"
    
    ~~~
    
    6. doc/src/sgml/ref/create_publication.sgml - Examples
    
    @@ -351,6 +366,15 @@ CREATE PUBLICATION production_publication FOR
    TABLE users, departments, ALL TABL
     CREATE PUBLICATION sales_publication FOR ALL TABLES IN SCHEMA marketing, sales;
     </programlisting></para>
    
    +  <para>
    +   Create a publication that publishes all changes in all the tables except for
    +   the changes of <structname>users</structname> and
    +   <structname>departments</structname> table:
    +<programlisting>
    +CREATE PUBLICATION mypublication FOR ALL TABLE EXCEPT TABLE users, departments;
    +</programlisting>
    +  </para>
    +
    
    6a.
    Typo: "FOR ALL TABLE" -> "FOR ALL TABLES"
    
    6b.
    Consider using "EXCEPT" instead of "EXCEPT TABLE" because that will
    show TABLE keyword is optional.
    
    ~~~
    
    7. src/backend/catalog/pg_publication.c - GetTopMostAncestorInPublication
    
    @@ -316,18 +316,25 @@ GetTopMostAncestorInPublication(Oid puboid, List
    *ancestors, int *ancestor_level
      }
      else
      {
    - aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    - if (list_member_oid(aschemaPubids, puboid))
    + List    *aschemapubids = NIL;
    + List    *aexceptpubids = NIL;
    +
    + aschemapubids = GetSchemaPublications(get_rel_namespace(ancestor));
    + aexceptpubids = GetRelationPublications(ancestor, true);
    + if (list_member_oid(aschemapubids, puboid) ||
    + (puballtables && !list_member_oid(aexceptpubids, puboid)))
      {
    
    You could re-write this as multiple conditions instead of one. That
    could avoid always assigning the 'aexceptpubids', so it might be a
    more efficient way to write this logic.
    
    ~~~
    
    8. src/backend/catalog/pg_publication.c - CheckPublicationDefValues
    
    +/*
    + * Check if the publication has default values
    + *
    + * Check the following:
    + * Publication is having default options
    + *  Publication is not associated with relations
    + *  Publication is not associated with schemas
    + *  Publication is not set with "FOR ALL TABLES"
    + */
    +static bool
    +CheckPublicationDefValues(HeapTuple tup)
    
    8a.
    Remove the tab. Replace with spaces.
    
    8b.
    It might be better if this comment order is the same as the logic order.
    e.g.
    
    * Check the following:
    *  Publication is not set with "FOR ALL TABLES"
    *  Publication is having default options
    *  Publication is not associated with schemas
    *  Publication is not associated with relations
    
    ~~~
    
    9. src/backend/catalog/pg_publication.c - AlterPublicationSetAllTables
    
    +/*
    + * Reset the publication.
    + *
    + * Reset the publication options, publication relations and
    publication schemas.
    + */
    +static void
    +AlterPublicationSetAllTables(Relation rel, HeapTuple tup)
    
    The function comment and the function name do not seem to match here;
    something looks like a cut/paste error ??
    
    ~~~
    
    10. src/backend/catalog/pg_publication.c - AlterPublicationSetAllTables
    
    + /* set all tables option */
    + values[Anum_pg_publication_puballtables - 1] = BoolGetDatum(true);
    + replaces[Anum_pg_publication_puballtables - 1] = true;
    
    SUGGEST (comment)
    /* set all ALL TABLES flag */
    
    ~~~
    
    11. src/backend/catalog/pg_publication.c - AlterPublication
    
    @@ -1501,6 +1579,20 @@ AlterPublication(ParseState *pstate,
    AlterPublicationStmt *stmt)
      aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_PUBLICATION,
         stmt->pubname);
    
    + if (stmt->for_all_tables)
    + {
    + bool isdefault = CheckPublicationDefValues(tup);
    +
    + if (!isdefault)
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    + errmsg("Setting ALL TABLES requires publication \"%s\" to have
    default values",
    +    stmt->pubname),
    + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    
    The errmsg should start with a lowercase letter.
    
    ~~~
    
    12. src/backend/catalog/pg_publication.c - AlterPublication
    
    @@ -1501,6 +1579,20 @@ AlterPublication(ParseState *pstate,
    AlterPublicationStmt *stmt)
      aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_PUBLICATION,
         stmt->pubname);
    
    + if (stmt->for_all_tables)
    + {
    + bool isdefault = CheckPublicationDefValues(tup);
    +
    + if (!isdefault)
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    + errmsg("Setting ALL TABLES requires publication \"%s\" to have
    default values",
    +    stmt->pubname),
    + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    
    Example test:
    
    postgres=# create table t1(a int);
    CREATE TABLE
    postgres=# create publication p1 for table t1;
    CREATE PUBLICATION
    postgres=# alter publication p1 add all tables except t1;
    2022-05-20 14:34:49.301 AEST [21802] ERROR:  Setting ALL TABLES
    requires publication "p1" to have default values
    2022-05-20 14:34:49.301 AEST [21802] HINT:  Use ALTER PUBLICATION ...
    RESET to reset the publication
    2022-05-20 14:34:49.301 AEST [21802] STATEMENT:  alter publication p1
    add all tables except t1;
    ERROR:  Setting ALL TABLES requires publication "p1" to have default values
    HINT:  Use ALTER PUBLICATION ... RESET to reset the publication
    postgres=# alter publication p1 set all tables except t1;
    
    That error message does not quite match what the user was doing.
    Firstly, they were adding the ALL TABLES, not setting it. Secondly,
    all the values of the publication were already defaults (only there
    was an existing table t1 in the publication). Maybe some minor changes
    to the message wording can be a better reflect what the user is doing
    here.
    
    ~~~
    
    13. src/backend/parser/gram.y
    
    @@ -10410,7 +10411,7 @@ AlterOwnerStmt: ALTER AGGREGATE
    aggregate_with_argtypes OWNER TO RoleSpec
      *
      * CREATE PUBLICATION name [WITH options]
      *
    - * CREATE PUBLICATION FOR ALL TABLES [WITH options]
    + * CREATE PUBLICATION FOR ALL TABLES [EXCEPT TABLE table [, ...]]
    [WITH options]
    
    Comment should show the "TABLE" keyword is optional
    
    ~~~
    
    14. src/bin/pg_dump/pg_dump.c - dumpPublicationTable
    
    @@ -4332,6 +4380,7 @@ dumpPublicationTable(Archive *fout, const
    PublicationRelInfo *pubrinfo)
    
      appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
        fmtId(pubinfo->dobj.name));
    +
      appendPQExpBuffer(query, " %s",
        fmtQualifiedDumpable(tbinfo));
    
    This additional whitespace seems unrelated to this patch
    
    ~~~
    
    15. src/include/nodes/parsenodes.h
    
    15a.
    @@ -3999,6 +3999,7 @@ typedef struct PublicationTable
      RangeVar   *relation; /* relation to be published */
      Node    *whereClause; /* qualifications */
      List    *columns; /* List of columns in a publication table */
    + bool except; /* except relation */
     } PublicationTable;
    
    Maybe the comment should be more like similar ones:
    /* exclude the relation */
    
    15b.
    @@ -4007,6 +4008,7 @@ typedef struct PublicationTable
     typedef enum PublicationObjSpecType
     {
      PUBLICATIONOBJ_TABLE, /* A table */
    + PUBLICATIONOBJ_EXCEPT_TABLE, /* An Except table */
      PUBLICATIONOBJ_TABLES_IN_SCHEMA, /* All tables in schema */
      PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA, /* All tables in first element of
    
    Maybe the comment should be more like:
    /* A table to be excluded */
    
    ~~~
    
    16. src/test/regress/sql/publication.sql
    
    I did not see any test cases using EXCEPT when the optional TABLE
    keyword is omitted.
    
    
    ------
    [1] https://www.postgresql.org/message-id/CAHut%2BPtZDfBJ1d%3D3kSexgM5m%2BP_ok8sdsJXKimsXycaMyqXsNA%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  51. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-21T05:30:52Z

    On Thu, May 19, 2022 at 1:49 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Below are my review comments for v6-0001.
    >
    > ======
    >
    > 1. General.
    >
    > The patch failed 'publication' tests in the make check phase.
    >
    > Please add this work to the commit-fest so that the 'cfbot' can report
    > such errors sooner.
    
    Added commitfest entry
    
    > ~~~
    >
    > 2. src/backend/commands/publicationcmds.c - AlterPublicationReset
    >
    > +/*
    > + * Reset the publication.
    > + *
    > + * Reset the publication options, publication relations and
    > publication schemas.
    > + */
    > +static void
    > +AlterPublicationReset(ParseState *pstate, AlterPublicationStmt *stmt,
    > + Relation rel, HeapTuple tup)
    >
    > SUGGESTION (Make the comment similar to the sgml text instead of
    > repeating "publication" 4x !)
    > /*
    >  * Reset the publication options, set the ALL TABLES flag to false, and
    >  * drop all relations and schemas that are associated with the publication.
    >  */
    
    Modified
    
    > ~~~
    >
    > 3. src/test/regress/expected/publication.out
    >
    > make check failed. The diff is below:
    >
    > @@ -1716,7 +1716,7 @@
    >  -- Verify that only superuser can reset a publication
    >  ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2;
    >  SET ROLE regress_publication_user2;
    > -ALTER PUBLICATION testpub_reset RESET; -- fail
    > +ALTER PUBLICATION testpub_reset RESET; -- fail - must be superuser
    >  ERROR:  must be superuser to RESET publication
    >  SET ROLE regress_publication_user;
    >  DROP PUBLICATION testpub_reset;
    
    It passed for me locally because the change was present in the 002
    patch. I have moved the change to 001.
    
    The attached v7 patch has the changes for the same.
    [1] - https://commitfest.postgresql.org/38/3646/
    
    Regards,
    Vignesh
    
  52. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-21T05:32:45Z

    On Fri, May 20, 2022 at 5:49 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > FYI, although the v6-0002 patch applied cleanly, I found that the SGML
    > was malformed and so the pg docs build fails.
    >
    > ~~~
    > e.g.
    >
    > [postgres@CentOS7-x64 sgml]$ make STYLE=website html
    > { \
    >   echo "<!ENTITY version \"15beta1\">"; \
    >   echo "<!ENTITY majorversion \"15\">"; \
    > } > version.sgml
    > '/usr/bin/perl' ./mk_feature_tables.pl YES
    > ../../../src/backend/catalog/sql_feature_packages.txt
    > ../../../src/backend/catalog/sql_features.txt >
    > features-supported.sgml
    > '/usr/bin/perl' ./mk_feature_tables.pl NO
    > ../../../src/backend/catalog/sql_feature_packages.txt
    > ../../../src/backend/catalog/sql_features.txt >
    > features-unsupported.sgml
    > '/usr/bin/perl' ./generate-errcodes-table.pl
    > ../../../src/backend/utils/errcodes.txt > errcodes-table.sgml
    > '/usr/bin/perl' ./generate-keywords-table.pl . > keywords-table.sgml
    > /usr/bin/xmllint --path . --noout --valid postgres.sgml
    > ref/create_publication.sgml:171: parser error : Opening and ending tag
    > mismatch: varlistentry line 166 and listitem
    >     </listitem>
    >                ^
    > ref/create_publication.sgml:172: parser error : Opening and ending tag
    > mismatch: variablelist line 60 and varlistentry
    >    </varlistentry>
    >                   ^
    > ref/create_publication.sgml:226: parser error : Opening and ending tag
    > mismatch: refsect1 line 57 and variablelist
    >   </variablelist>
    >                  ^
    > ...
    >
    > I will work around it locally, but for future patches please check the
    > SGML builds ok before posting.
    
    Thanks for reporting this, I have made the changes for this.
    The v7 patch attached at [1] has the changes for the same.
    
    [1] - https://www.postgresql.org/message-id/CALDaNm3EpX3%2BRu%3DSNaYi%3DUW5ZLE6nNhGRHZ7a8-fXPZ_-gLdxQ%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  53. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-21T05:36:16Z

    On Fri, May 20, 2022 at 11:23 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Below are my review comments for v6-0002.
    >
    > ======
    >
    > 1. Commit message.
    > The psql \d family of commands to display excluded tables.
    >
    > SUGGESTION
    > The psql \d family of commands can now display excluded tables.
    
    Modified
    
    > ~~~
    >
    > 2. doc/src/sgml/ref/alter_publication.sgml
    >
    > @@ -22,6 +22,7 @@ PostgreSQL documentation
    >   <refsynopsisdiv>
    >  <synopsis>
    >  ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > ADD <replaceable class="parameter">publication_object</replaceable> [,
    > ...]
    > +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > ADD ALL TABLES [ EXCEPT [ TABLE ] exception_object [, ... ] ]
    >
    > The "exception_object" font is wrong. Should look the same as
    > "publication_object"
    
    Modified
    
    > ~~~
    >
    > 3. doc/src/sgml/ref/alter_publication.sgml - Examples
    >
    > @@ -214,6 +220,14 @@ ALTER PUBLICATION sales_publication ADD ALL
    > TABLES IN SCHEMA marketing, sales;
    >  </programlisting>
    >    </para>
    >
    > +  <para>
    > +   Alter publication <structname>production_publication</structname> to publish
    > +   all tables except <structname>users</structname> and
    > +   <structname>departments</structname> tables:
    > +<programlisting>
    > +ALTER PUBLICATION production_publication ADD ALL TABLES EXCEPT TABLE
    > users, departments;
    > +</programlisting></para>
    >
    > Consider using "EXCEPT" instead of "EXCEPT TABLE" because that will
    > show TABLE keyword is optional.
    
    Modified
    
    > ~~~
    >
    > 4. doc/src/sgml/ref/create_publication.sgml
    >
    > An SGML tag error caused building the docs to fail. My fix was
    > previously reported [1].
    
    Modified
    
    > ~~~
    >
    > 5. doc/src/sgml/ref/create_publication.sgml
    >
    > @@ -22,7 +22,7 @@ PostgreSQL documentation
    >   <refsynopsisdiv>
    >  <synopsis>
    >  CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
    > -    [ FOR ALL TABLES
    > +    [ FOR ALL TABLES [ EXCEPT [ TABLE ] exception_object [, ... ] ]
    >
    > The "exception_object" font is wrong. Should look the same as
    > "publication_object"
    
    Modified
    
    > ~~~
    >
    > 6. doc/src/sgml/ref/create_publication.sgml - Examples
    >
    > @@ -351,6 +366,15 @@ CREATE PUBLICATION production_publication FOR
    > TABLE users, departments, ALL TABL
    >  CREATE PUBLICATION sales_publication FOR ALL TABLES IN SCHEMA marketing, sales;
    >  </programlisting></para>
    >
    > +  <para>
    > +   Create a publication that publishes all changes in all the tables except for
    > +   the changes of <structname>users</structname> and
    > +   <structname>departments</structname> table:
    > +<programlisting>
    > +CREATE PUBLICATION mypublication FOR ALL TABLE EXCEPT TABLE users, departments;
    > +</programlisting>
    > +  </para>
    > +
    >
    > 6a.
    > Typo: "FOR ALL TABLE" -> "FOR ALL TABLES"
    
    Modified
    
    > 6b.
    > Consider using "EXCEPT" instead of "EXCEPT TABLE" because that will
    > show TABLE keyword is optional.
    
    Modified
    
    > ~~~
    >
    > 7. src/backend/catalog/pg_publication.c - GetTopMostAncestorInPublication
    >
    > @@ -316,18 +316,25 @@ GetTopMostAncestorInPublication(Oid puboid, List
    > *ancestors, int *ancestor_level
    >   }
    >   else
    >   {
    > - aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > - if (list_member_oid(aschemaPubids, puboid))
    > + List    *aschemapubids = NIL;
    > + List    *aexceptpubids = NIL;
    > +
    > + aschemapubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > + aexceptpubids = GetRelationPublications(ancestor, true);
    > + if (list_member_oid(aschemapubids, puboid) ||
    > + (puballtables && !list_member_oid(aexceptpubids, puboid)))
    >   {
    >
    > You could re-write this as multiple conditions instead of one. That
    > could avoid always assigning the 'aexceptpubids', so it might be a
    > more efficient way to write this logic.
    
    Modified
    
    > ~~~
    >
    > 8. src/backend/catalog/pg_publication.c - CheckPublicationDefValues
    >
    > +/*
    > + * Check if the publication has default values
    > + *
    > + * Check the following:
    > + * Publication is having default options
    > + *  Publication is not associated with relations
    > + *  Publication is not associated with schemas
    > + *  Publication is not set with "FOR ALL TABLES"
    > + */
    > +static bool
    > +CheckPublicationDefValues(HeapTuple tup)
    >
    > 8a.
    > Remove the tab. Replace with spaces.
    
    Modified
    
    > 8b.
    > It might be better if this comment order is the same as the logic order.
    > e.g.
    >
    > * Check the following:
    > *  Publication is not set with "FOR ALL TABLES"
    > *  Publication is having default options
    > *  Publication is not associated with schemas
    > *  Publication is not associated with relations
    
    Modified
    
    > ~~~
    >
    > 9. src/backend/catalog/pg_publication.c - AlterPublicationSetAllTables
    >
    > +/*
    > + * Reset the publication.
    > + *
    > + * Reset the publication options, publication relations and
    > publication schemas.
    > + */
    > +static void
    > +AlterPublicationSetAllTables(Relation rel, HeapTuple tup)
    >
    > The function comment and the function name do not seem to match here;
    > something looks like a cut/paste error ??
    
    Modified
    
    > ~~~
    >
    > 10. src/backend/catalog/pg_publication.c - AlterPublicationSetAllTables
    >
    > + /* set all tables option */
    > + values[Anum_pg_publication_puballtables - 1] = BoolGetDatum(true);
    > + replaces[Anum_pg_publication_puballtables - 1] = true;
    >
    > SUGGEST (comment)
    > /* set all ALL TABLES flag */
    
    Modified
    
    > ~~~
    >
    > 11. src/backend/catalog/pg_publication.c - AlterPublication
    >
    > @@ -1501,6 +1579,20 @@ AlterPublication(ParseState *pstate,
    > AlterPublicationStmt *stmt)
    >   aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_PUBLICATION,
    >      stmt->pubname);
    >
    > + if (stmt->for_all_tables)
    > + {
    > + bool isdefault = CheckPublicationDefValues(tup);
    > +
    > + if (!isdefault)
    > + ereport(ERROR,
    > + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > + errmsg("Setting ALL TABLES requires publication \"%s\" to have
    > default values",
    > +    stmt->pubname),
    > + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    >
    > The errmsg should start with a lowercase letter.
    
    Modified
    
    > ~~~
    >
    > 12. src/backend/catalog/pg_publication.c - AlterPublication
    >
    > @@ -1501,6 +1579,20 @@ AlterPublication(ParseState *pstate,
    > AlterPublicationStmt *stmt)
    >   aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_PUBLICATION,
    >      stmt->pubname);
    >
    > + if (stmt->for_all_tables)
    > + {
    > + bool isdefault = CheckPublicationDefValues(tup);
    > +
    > + if (!isdefault)
    > + ereport(ERROR,
    > + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > + errmsg("Setting ALL TABLES requires publication \"%s\" to have
    > default values",
    > +    stmt->pubname),
    > + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    >
    > Example test:
    >
    > postgres=# create table t1(a int);
    > CREATE TABLE
    > postgres=# create publication p1 for table t1;
    > CREATE PUBLICATION
    > postgres=# alter publication p1 add all tables except t1;
    > 2022-05-20 14:34:49.301 AEST [21802] ERROR:  Setting ALL TABLES
    > requires publication "p1" to have default values
    > 2022-05-20 14:34:49.301 AEST [21802] HINT:  Use ALTER PUBLICATION ...
    > RESET to reset the publication
    > 2022-05-20 14:34:49.301 AEST [21802] STATEMENT:  alter publication p1
    > add all tables except t1;
    > ERROR:  Setting ALL TABLES requires publication "p1" to have default values
    > HINT:  Use ALTER PUBLICATION ... RESET to reset the publication
    > postgres=# alter publication p1 set all tables except t1;
    >
    > That error message does not quite match what the user was doing.
    > Firstly, they were adding the ALL TABLES, not setting it. Secondly,
    > all the values of the publication were already defaults (only there
    > was an existing table t1 in the publication). Maybe some minor changes
    > to the message wording can be a better reflect what the user is doing
    > here.
    
    Modified
    
    > ~~~
    >
    > 13. src/backend/parser/gram.y
    >
    > @@ -10410,7 +10411,7 @@ AlterOwnerStmt: ALTER AGGREGATE
    > aggregate_with_argtypes OWNER TO RoleSpec
    >   *
    >   * CREATE PUBLICATION name [WITH options]
    >   *
    > - * CREATE PUBLICATION FOR ALL TABLES [WITH options]
    > + * CREATE PUBLICATION FOR ALL TABLES [EXCEPT TABLE table [, ...]]
    > [WITH options]
    >
    > Comment should show the "TABLE" keyword is optional
    
    Modified
    
    > ~~~
    >
    > 14. src/bin/pg_dump/pg_dump.c - dumpPublicationTable
    >
    > @@ -4332,6 +4380,7 @@ dumpPublicationTable(Archive *fout, const
    > PublicationRelInfo *pubrinfo)
    >
    >   appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
    >     fmtId(pubinfo->dobj.name));
    > +
    >   appendPQExpBuffer(query, " %s",
    >     fmtQualifiedDumpable(tbinfo));
    >
    > This additional whitespace seems unrelated to this patch
    
    Modified
    
    > ~~~
    >
    > 15. src/include/nodes/parsenodes.h
    >
    > 15a.
    > @@ -3999,6 +3999,7 @@ typedef struct PublicationTable
    >   RangeVar   *relation; /* relation to be published */
    >   Node    *whereClause; /* qualifications */
    >   List    *columns; /* List of columns in a publication table */
    > + bool except; /* except relation */
    >  } PublicationTable;
    >
    > Maybe the comment should be more like similar ones:
    > /* exclude the relation */
    
    Modified
    
    > 15b.
    > @@ -4007,6 +4008,7 @@ typedef struct PublicationTable
    >  typedef enum PublicationObjSpecType
    >  {
    >   PUBLICATIONOBJ_TABLE, /* A table */
    > + PUBLICATIONOBJ_EXCEPT_TABLE, /* An Except table */
    >   PUBLICATIONOBJ_TABLES_IN_SCHEMA, /* All tables in schema */
    >   PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA, /* All tables in first element of
    >
    > Maybe the comment should be more like:
    > /* A table to be excluded */
    
    Modified
    
    > ~~~
    >
    > 16. src/test/regress/sql/publication.sql
    >
    > I did not see any test cases using EXCEPT when the optional TABLE
    > keyword is omitted.
    
    Added a test
    
    Thanks for the comments, the v7 patch attached at [1] has the changes
    for the same.
    [1] - https://www.postgresql.org/message-id/CALDaNm3EpX3%2BRu%3DSNaYi%3DUW5ZLE6nNhGRHZ7a8-fXPZ_-gLdxQ%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  54. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-05-23T05:13:03Z

    On Sat, May 21, 2022 at 11:06 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Fri, May 20, 2022 at 11:23 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Below are my review comments for v6-0002.
    > >
    > > ======
    > >
    > > 1. Commit message.
    > > The psql \d family of commands to display excluded tables.
    > >
    > > SUGGESTION
    > > The psql \d family of commands can now display excluded tables.
    >
    > Modified
    >
    > > ~~~
    > >
    > > 2. doc/src/sgml/ref/alter_publication.sgml
    > >
    > > @@ -22,6 +22,7 @@ PostgreSQL documentation
    > >   <refsynopsisdiv>
    > >  <synopsis>
    > >  ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > > ADD <replaceable class="parameter">publication_object</replaceable> [,
    > > ...]
    > > +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > > ADD ALL TABLES [ EXCEPT [ TABLE ] exception_object [, ... ] ]
    > >
    > > The "exception_object" font is wrong. Should look the same as
    > > "publication_object"
    >
    > Modified
    >
    > > ~~~
    > >
    > > 3. doc/src/sgml/ref/alter_publication.sgml - Examples
    > >
    > > @@ -214,6 +220,14 @@ ALTER PUBLICATION sales_publication ADD ALL
    > > TABLES IN SCHEMA marketing, sales;
    > >  </programlisting>
    > >    </para>
    > >
    > > +  <para>
    > > +   Alter publication <structname>production_publication</structname> to publish
    > > +   all tables except <structname>users</structname> and
    > > +   <structname>departments</structname> tables:
    > > +<programlisting>
    > > +ALTER PUBLICATION production_publication ADD ALL TABLES EXCEPT TABLE
    > > users, departments;
    > > +</programlisting></para>
    > >
    > > Consider using "EXCEPT" instead of "EXCEPT TABLE" because that will
    > > show TABLE keyword is optional.
    >
    > Modified
    >
    > > ~~~
    > >
    > > 4. doc/src/sgml/ref/create_publication.sgml
    > >
    > > An SGML tag error caused building the docs to fail. My fix was
    > > previously reported [1].
    >
    > Modified
    >
    > > ~~~
    > >
    > > 5. doc/src/sgml/ref/create_publication.sgml
    > >
    > > @@ -22,7 +22,7 @@ PostgreSQL documentation
    > >   <refsynopsisdiv>
    > >  <synopsis>
    > >  CREATE PUBLICATION <replaceable class="parameter">name</replaceable>
    > > -    [ FOR ALL TABLES
    > > +    [ FOR ALL TABLES [ EXCEPT [ TABLE ] exception_object [, ... ] ]
    > >
    > > The "exception_object" font is wrong. Should look the same as
    > > "publication_object"
    >
    > Modified
    >
    > > ~~~
    > >
    > > 6. doc/src/sgml/ref/create_publication.sgml - Examples
    > >
    > > @@ -351,6 +366,15 @@ CREATE PUBLICATION production_publication FOR
    > > TABLE users, departments, ALL TABL
    > >  CREATE PUBLICATION sales_publication FOR ALL TABLES IN SCHEMA marketing, sales;
    > >  </programlisting></para>
    > >
    > > +  <para>
    > > +   Create a publication that publishes all changes in all the tables except for
    > > +   the changes of <structname>users</structname> and
    > > +   <structname>departments</structname> table:
    > > +<programlisting>
    > > +CREATE PUBLICATION mypublication FOR ALL TABLE EXCEPT TABLE users, departments;
    > > +</programlisting>
    > > +  </para>
    > > +
    > >
    > > 6a.
    > > Typo: "FOR ALL TABLE" -> "FOR ALL TABLES"
    >
    > Modified
    >
    > > 6b.
    > > Consider using "EXCEPT" instead of "EXCEPT TABLE" because that will
    > > show TABLE keyword is optional.
    >
    > Modified
    >
    > > ~~~
    > >
    > > 7. src/backend/catalog/pg_publication.c - GetTopMostAncestorInPublication
    > >
    > > @@ -316,18 +316,25 @@ GetTopMostAncestorInPublication(Oid puboid, List
    > > *ancestors, int *ancestor_level
    > >   }
    > >   else
    > >   {
    > > - aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > > - if (list_member_oid(aschemaPubids, puboid))
    > > + List    *aschemapubids = NIL;
    > > + List    *aexceptpubids = NIL;
    > > +
    > > + aschemapubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > > + aexceptpubids = GetRelationPublications(ancestor, true);
    > > + if (list_member_oid(aschemapubids, puboid) ||
    > > + (puballtables && !list_member_oid(aexceptpubids, puboid)))
    > >   {
    > >
    > > You could re-write this as multiple conditions instead of one. That
    > > could avoid always assigning the 'aexceptpubids', so it might be a
    > > more efficient way to write this logic.
    >
    > Modified
    >
    > > ~~~
    > >
    > > 8. src/backend/catalog/pg_publication.c - CheckPublicationDefValues
    > >
    > > +/*
    > > + * Check if the publication has default values
    > > + *
    > > + * Check the following:
    > > + * Publication is having default options
    > > + *  Publication is not associated with relations
    > > + *  Publication is not associated with schemas
    > > + *  Publication is not set with "FOR ALL TABLES"
    > > + */
    > > +static bool
    > > +CheckPublicationDefValues(HeapTuple tup)
    > >
    > > 8a.
    > > Remove the tab. Replace with spaces.
    >
    > Modified
    >
    > > 8b.
    > > It might be better if this comment order is the same as the logic order.
    > > e.g.
    > >
    > > * Check the following:
    > > *  Publication is not set with "FOR ALL TABLES"
    > > *  Publication is having default options
    > > *  Publication is not associated with schemas
    > > *  Publication is not associated with relations
    >
    > Modified
    >
    > > ~~~
    > >
    > > 9. src/backend/catalog/pg_publication.c - AlterPublicationSetAllTables
    > >
    > > +/*
    > > + * Reset the publication.
    > > + *
    > > + * Reset the publication options, publication relations and
    > > publication schemas.
    > > + */
    > > +static void
    > > +AlterPublicationSetAllTables(Relation rel, HeapTuple tup)
    > >
    > > The function comment and the function name do not seem to match here;
    > > something looks like a cut/paste error ??
    >
    > Modified
    >
    > > ~~~
    > >
    > > 10. src/backend/catalog/pg_publication.c - AlterPublicationSetAllTables
    > >
    > > + /* set all tables option */
    > > + values[Anum_pg_publication_puballtables - 1] = BoolGetDatum(true);
    > > + replaces[Anum_pg_publication_puballtables - 1] = true;
    > >
    > > SUGGEST (comment)
    > > /* set all ALL TABLES flag */
    >
    > Modified
    >
    > > ~~~
    > >
    > > 11. src/backend/catalog/pg_publication.c - AlterPublication
    > >
    > > @@ -1501,6 +1579,20 @@ AlterPublication(ParseState *pstate,
    > > AlterPublicationStmt *stmt)
    > >   aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_PUBLICATION,
    > >      stmt->pubname);
    > >
    > > + if (stmt->for_all_tables)
    > > + {
    > > + bool isdefault = CheckPublicationDefValues(tup);
    > > +
    > > + if (!isdefault)
    > > + ereport(ERROR,
    > > + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > > + errmsg("Setting ALL TABLES requires publication \"%s\" to have
    > > default values",
    > > +    stmt->pubname),
    > > + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    > >
    > > The errmsg should start with a lowercase letter.
    >
    > Modified
    >
    > > ~~~
    > >
    > > 12. src/backend/catalog/pg_publication.c - AlterPublication
    > >
    > > @@ -1501,6 +1579,20 @@ AlterPublication(ParseState *pstate,
    > > AlterPublicationStmt *stmt)
    > >   aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_PUBLICATION,
    > >      stmt->pubname);
    > >
    > > + if (stmt->for_all_tables)
    > > + {
    > > + bool isdefault = CheckPublicationDefValues(tup);
    > > +
    > > + if (!isdefault)
    > > + ereport(ERROR,
    > > + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > > + errmsg("Setting ALL TABLES requires publication \"%s\" to have
    > > default values",
    > > +    stmt->pubname),
    > > + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    > >
    > > Example test:
    > >
    > > postgres=# create table t1(a int);
    > > CREATE TABLE
    > > postgres=# create publication p1 for table t1;
    > > CREATE PUBLICATION
    > > postgres=# alter publication p1 add all tables except t1;
    > > 2022-05-20 14:34:49.301 AEST [21802] ERROR:  Setting ALL TABLES
    > > requires publication "p1" to have default values
    > > 2022-05-20 14:34:49.301 AEST [21802] HINT:  Use ALTER PUBLICATION ...
    > > RESET to reset the publication
    > > 2022-05-20 14:34:49.301 AEST [21802] STATEMENT:  alter publication p1
    > > add all tables except t1;
    > > ERROR:  Setting ALL TABLES requires publication "p1" to have default values
    > > HINT:  Use ALTER PUBLICATION ... RESET to reset the publication
    > > postgres=# alter publication p1 set all tables except t1;
    > >
    > > That error message does not quite match what the user was doing.
    > > Firstly, they were adding the ALL TABLES, not setting it. Secondly,
    > > all the values of the publication were already defaults (only there
    > > was an existing table t1 in the publication). Maybe some minor changes
    > > to the message wording can be a better reflect what the user is doing
    > > here.
    >
    > Modified
    >
    > > ~~~
    > >
    > > 13. src/backend/parser/gram.y
    > >
    > > @@ -10410,7 +10411,7 @@ AlterOwnerStmt: ALTER AGGREGATE
    > > aggregate_with_argtypes OWNER TO RoleSpec
    > >   *
    > >   * CREATE PUBLICATION name [WITH options]
    > >   *
    > > - * CREATE PUBLICATION FOR ALL TABLES [WITH options]
    > > + * CREATE PUBLICATION FOR ALL TABLES [EXCEPT TABLE table [, ...]]
    > > [WITH options]
    > >
    > > Comment should show the "TABLE" keyword is optional
    >
    > Modified
    >
    > > ~~~
    > >
    > > 14. src/bin/pg_dump/pg_dump.c - dumpPublicationTable
    > >
    > > @@ -4332,6 +4380,7 @@ dumpPublicationTable(Archive *fout, const
    > > PublicationRelInfo *pubrinfo)
    > >
    > >   appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
    > >     fmtId(pubinfo->dobj.name));
    > > +
    > >   appendPQExpBuffer(query, " %s",
    > >     fmtQualifiedDumpable(tbinfo));
    > >
    > > This additional whitespace seems unrelated to this patch
    >
    > Modified
    >
    > > ~~~
    > >
    > > 15. src/include/nodes/parsenodes.h
    > >
    > > 15a.
    > > @@ -3999,6 +3999,7 @@ typedef struct PublicationTable
    > >   RangeVar   *relation; /* relation to be published */
    > >   Node    *whereClause; /* qualifications */
    > >   List    *columns; /* List of columns in a publication table */
    > > + bool except; /* except relation */
    > >  } PublicationTable;
    > >
    > > Maybe the comment should be more like similar ones:
    > > /* exclude the relation */
    >
    > Modified
    >
    > > 15b.
    > > @@ -4007,6 +4008,7 @@ typedef struct PublicationTable
    > >  typedef enum PublicationObjSpecType
    > >  {
    > >   PUBLICATIONOBJ_TABLE, /* A table */
    > > + PUBLICATIONOBJ_EXCEPT_TABLE, /* An Except table */
    > >   PUBLICATIONOBJ_TABLES_IN_SCHEMA, /* All tables in schema */
    > >   PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA, /* All tables in first element of
    > >
    > > Maybe the comment should be more like:
    > > /* A table to be excluded */
    >
    > Modified
    >
    > > ~~~
    > >
    > > 16. src/test/regress/sql/publication.sql
    > >
    > > I did not see any test cases using EXCEPT when the optional TABLE
    > > keyword is omitted.
    >
    > Added a test
    >
    > Thanks for the comments, the v7 patch attached at [1] has the changes
    > for the same.
    > [1] - https://www.postgresql.org/message-id/CALDaNm3EpX3%2BRu%3DSNaYi%3DUW5ZLE6nNhGRHZ7a8-fXPZ_-gLdxQ%40mail.gmail.com
    
    Attached v7 patch which fixes the buildfarm warning for an unused
    warning in release mode as in  [1].
    [1] - https://cirrus-ci.com/task/6220288017825792
    
    Regards,
    Vignesh
    
  55. RE: Skipping schema changes in publication

    Takamichi Osumi (Fujitsu) <osumi.takamichi@fujitsu.com> — 2022-05-26T13:34:33Z

    On Monday, May 23, 2022 2:13 PM vignesh C <vignesh21@gmail.com> wrote:
    > Attached v7 patch which fixes the buildfarm warning for an unused warning in
    > release mode as in  [1].
    Hi, thank you for the patches.
    
    
    I'll share several review comments.
    
    For v7-0001.
    
    (1) I'll suggest some minor rewording.
    
    +  <para>
    +   The <literal>RESET</literal> clause will reset the publication to the
    +   default state which includes resetting the publication options, setting
    +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    +   dropping all relations and schemas that are associated with the publication.
    
    My suggestion is
    "The RESET clause will reset the publication to the
    default state. It resets the publication operations,
    sets ALL TABLES flag to false and drops all relations
    and schemas associated with the publication."
    
    (2) typo and rewording
    
    +/*
    + * Reset the publication.
    + *
    + * Reset the publication options, setting ALL TABLES flag to false and drop
    + * all relations and schemas that are associated with the publication.
    + */
    
    The "setting" in this sentence should be "set".
    
    How about changing like below ?
    FROM:
    "Reset the publication options, setting ALL TABLES flag to false and drop
    all relations and schemas that are associated with the publication."
    TO:
    "Reset the publication operations, set ALL TABLES flag to false and drop
    all relations and schemas associated with the publication."
    
    (3) AlterPublicationReset
    
    Do we need to call CacheInvalidateRelcacheAll() or
    InvalidatePublicationRels() at the end of
    AlterPublicationReset() like AlterPublicationOptions() ?
    
    
    For v7-0002.
    
    (4)
    
    +       if (stmt->for_all_tables)
    +       {
    +               bool            isdefault = CheckPublicationDefValues(tup);
    +
    +               if (!isdefault)
    +                       ereport(ERROR,
    +                                       errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    +                                       errmsg("adding ALL TABLES requires the publication to have default publication options, no tables/....
    +                                       errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    
    
    The errmsg string has three messages for user and is a bit long
    (we have two sentences there connected by 'and').
    Can't we make it concise and split it into a couple of lines for code readability ?
    
    I'll suggest a change below.
    FROM:
    "adding ALL TABLES requires the publication to have default publication options, no tables/schemas associated and ALL TABLES flag should not be set"
    TO:
    "adding ALL TABLES requires the publication defined not for ALL TABLES"
    "to have default publish actions without any associated tables/schemas"
    
    (5) typo
    
       <varlistentry>
    +    <term><literal>EXCEPT TABLE</literal></term>
    +    <listitem>
    +     <para>
    +      This clause specifies a list of tables to exclude from the publication.
    +      It can only be used with <literal>FOR ALL TABLES</literal>.
    +     </para>
    +    </listitem>
    +   </varlistentry>
    +
    
    Kindly change
    FROM:
    This clause specifies a list of tables to exclude from the publication.
    TO:
    This clause specifies a list of tables to be excluded from the publication.
    or
    This clause specifies a list of tables excluded from the publication.
    
    (6) Minor suggestion for an expression change
    
           Marks the publication as one that replicates changes for all tables in
    -      the database, including tables created in the future.
    +      the database, including tables created in the future. If
    +      <literal>EXCEPT TABLE</literal> is specified, then exclude replicating
    +      the changes for the specified tables.
    
    
    I'll suggest a minor rewording.
    FROM:
    ...exclude replicating the changes for the specified tables
    TO:
    ...exclude replication changes for the specified tables
    
    (7)
    (7-1)
    
    +/*
    + * Check if the publication has default values
    + *
    + * Check the following:
    + * a) Publication is not set with "FOR ALL TABLES"
    + * b) Publication is having default options
    + * c) Publication is not associated with schemas
    + * d) Publication is not associated with relations
    + */
    +static bool
    +CheckPublicationDefValues(HeapTuple tup)
    
    
    I think this header comment can be improved.
    FROM:
    Check the following:
    TO:
    Returns true if the publication satisfies all the following conditions:
    
    (7-2)
    
    b) should be changed as well
    FROM:
    Publication is having default options
    TO:
    Publication has the default publish operations
    
    
    
    Best Regards,
    	Takamichi Osumi
    
    
  56. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-30T08:21:36Z

    Here are some minor review comments for v7-0001.
    
    ======
    
    1. General
    
    Probably the commit message and all the PG docs and code comments
    should be changed to refer to "publication parameters" instead of
    (currently) "publication options". This is because these things are
    really called "publication_parameters" in the PG docs [1].
    
    All the following review comments are just examples of this suggestion.
    
    ~~~
    
    2. Commit message
    
    "includes resetting the publication options," -> "includes resetting
    the publication parameters,"
    
    ~~~
    
    3. doc/src/sgml/ref/alter_publication.sgml
    
    +  <para>
    +   The <literal>RESET</literal> clause will reset the publication to the
    +   default state which includes resetting the publication options, setting
    +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    +   dropping all relations and schemas that are associated with the publication.
       </para>
    
    
    "resetting the publication options," -> "resetting the publication parameters,"
    
    ~~~
    
    4. src/backend/commands/publicationcmds.c
    
    @@ -53,6 +53,14 @@
     #include "utils/syscache.h"
     #include "utils/varlena.h"
    
    +/* CREATE PUBLICATION default values for flags and options */
    +#define PUB_DEFAULT_ACTION_INSERT true
    +#define PUB_DEFAULT_ACTION_UPDATE true
    +#define PUB_DEFAULT_ACTION_DELETE true
    +#define PUB_DEFAULT_ACTION_TRUNCATE true
    +#define PUB_DEFAULT_VIA_ROOT false
    +#define PUB_DEFAULT_ALL_TABLES false
    
    "flags and options" -> "flags and publication parameters"
    
    ~~~
    
    5. src/backend/commands/publicationcmds.c
    
    +/*
    + * Reset the publication.
    + *
    + * Reset the publication options, setting ALL TABLES flag to false and drop
    + * all relations and schemas that are associated with the publication.
    + */
    +static void
    +AlterPublicationReset(ParseState *pstate, AlterPublicationStmt *stmt,
    +   Relation rel, HeapTuple tup)
    
    "Reset the publication options," -> "Reset the publication parameters,"
    
    ~~~
    
    6. src/test/regress/sql/publication.sql
    
    +-- Verify that publish options and publish_via_partition_root option are reset
    +\dRp+ testpub_reset
    +ALTER PUBLICATION testpub_reset RESET;
    +\dRp+ testpub_reset
    
    SUGGESTION
    -- Verify that 'publish' and 'publish_via_partition_root' publication
    parameters are reset
    
    ------
    [1] https://www.postgresql.org/docs/current/sql-createpublication.html
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  57. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2022-05-31T06:20:48Z

    Here are my review comments for patch v7-0002.
    
    ======
    
    1. doc/src/sgml/logical-replication.sgml
    
    @@ -1167,8 +1167,9 @@ CONTEXT:  processing remote data for replication
    origin "pg_16395" during "INSER
       <para>
        To add tables to a publication, the user must have ownership rights on the
        table. To add all tables in schema to a publication, the user must be a
    -   superuser. To create a publication that publishes all tables or
    all tables in
    -   schema automatically, the user must be a superuser.
    +   superuser. To add all tables to a publication, the user must be a superuser.
    +   To create a publication that publishes all tables or all tables in schema
    +   automatically, the user must be a superuser.
       </para>
    
    I felt that maybe this whole paragraph should be rearranged. Put the
    "create publication" parts before the "alter publication" parts;
    Re-word the sentences more similarly. I also felt the ALL TABLES and
    ALL TABLES IN SCHEMA etc should be written uppercase/literal since
    that is what was meant.
    
    SUGGESTION
    To create a publication using FOR ALL TABLES or FOR ALL TABLES IN
    SCHEMA, the user must be a superuser. To add ALL TABLES or ALL TABLES
    IN SCHEMA to a publication, the user must be a superuser. To add
    tables to a publication, the user must have ownership rights on the
    table.
    
    ~~~
    
    2. doc/src/sgml/ref/alter_publication.sgml
    
    @@ -82,8 +88,8 @@ ALTER PUBLICATION <replaceable
    class="parameter">name</replaceable> RESET
    
       <para>
        You must own the publication to use <command>ALTER PUBLICATION</command>.
    -   Adding a table to a publication additionally requires owning that table.
    -   The <literal>ADD ALL TABLES IN SCHEMA</literal>,
    +   Adding a table to or excluding a table from a publication additionally
    +   requires owning that table. The <literal>ADD ALL TABLES IN SCHEMA</literal>,
        <literal>SET ALL TABLES IN SCHEMA</literal> to a publication and
    
    Isn't this missing some information that says ADD ALL TABLES requires
    the invoking user to be a superuser?
    
    ~~~
    
    3. doc/src/sgml/ref/alter_publication.sgml - examples
    
    +  <para>
    +   Alter publication <structname>production_publication</structname> to publish
    +   all tables except <structname>users</structname> and
    +   <structname>departments</structname> tables:
    +<programlisting>
    +ALTER PUBLICATION production_publication ADD ALL TABLES EXCEPT users,
    departments;
    +</programlisting></para>
    +
    
    I didn't think it needs to say "tables" 2x (e.g. remove the last "tables")
    
    ~~~
    
    4. doc/src/sgml/ref/create_publication.sgml - examples
    
    +  <para>
    +   Create a publication that publishes all changes in all the tables except for
    +   the changes of <structname>users</structname> and
    +   <structname>departments</structname> tables:
    +<programlisting>
    +CREATE PUBLICATION mypublication FOR ALL TABLES EXCEPT users, departments;
    +</programlisting>
    +  </para>
    
    I didn't think it needs to say "tables" 2x (e.g. remove the last "tables")
    
    ~~~
    
    5. src/backend/catalog/pg_publication.c
    
      foreach(lc, ancestors)
      {
      Oid ancestor = lfirst_oid(lc);
    - List    *apubids = GetRelationPublications(ancestor);
    - List    *aschemaPubids = NIL;
    + List    *apubids = GetRelationPublications(ancestor, false);
    + List    *aschemapubids = NIL;
    + List    *aexceptpubids = NIL;
    
      level++;
    
    - if (list_member_oid(apubids, puboid))
    + /* check if member of table publications */
    + if (!list_member_oid(apubids, puboid))
      {
    - topmost_relid = ancestor;
    -
    - if (ancestor_level)
    - *ancestor_level = level;
    - }
    - else
    - {
    - aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    - if (list_member_oid(aschemaPubids, puboid))
    + /* check if member of schema publications */
    + aschemapubids = GetSchemaPublications(get_rel_namespace(ancestor));
    + if (!list_member_oid(aschemapubids, puboid))
      {
    - topmost_relid = ancestor;
    -
    - if (ancestor_level)
    - *ancestor_level = level;
    + /*
    + * If the publication is all tables publication and the table
    + * is not part of exception tables.
    + */
    + if (puballtables)
    + {
    + aexceptpubids = GetRelationPublications(ancestor, true);
    + if (list_member_oid(aexceptpubids, puboid))
    + goto next;
    + }
    + else
    + goto next;
      }
      }
    
    + topmost_relid = ancestor;
    +
    + if (ancestor_level)
    + *ancestor_level = level;
    +
    +next:
      list_free(apubids);
    - list_free(aschemaPubids);
    + list_free(aschemapubids);
    + list_free(aexceptpubids);
      }
    
    
    I felt those negative (!) conditions and those goto are making this
    logic hard to understand. Can’t it be simplified more than this? Even
    just having another bool flag might help make it easier.
    
    e.g. Perhaps something a bit like this (but add some comments)
    
    foreach(lc, ancestors)
    {
    Oid ancestor = lfirst_oid(lc);
    List    *apubids = GetRelationPublications(ancestor);
    List    *aschemaPubids = NIL;
    List    *aexceptpubids = NIL;
    bool set_top = false;
    level++;
    
    set_top = list_member_oid(apubids, puboid);
    if (!set_top)
    {
    aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    set_top = list_member_oid(aschemaPubids, puboid);
    
    if (!set_top && puballtables)
    {
    aexceptpubids = GetRelationPublications(ancestor, true);
    set_top = !list_member_oid(aexceptpubids, puboid);
    }
    }
    if (set_top)
    {
    topmost_relid = ancestor;
    
    if (ancestor_level)
    *ancestor_level = level;
    }
    
    list_free(apubids);
    list_free(aschemapubids);
    list_free(aexceptpubids);
    }
    
    ------
    
    6. src/backend/commands/publicationcmds.c - CheckPublicationDefValues
    
    +/*
    + * Check if the publication has default values
    + *
    + * Check the following:
    + * a) Publication is not set with "FOR ALL TABLES"
    + * b) Publication is having default options
    + * c) Publication is not associated with schemas
    + * d) Publication is not associated with relations
    + */
    +static bool
    +CheckPublicationDefValues(HeapTuple tup)
    
    I think Osumi-san already gave a review [1] about this same comment.
    
    So I only wanted to add that it should not say "options" here:
    "default options" -> "default publication parameter values"
    
    ~~~
    
    7. src/backend/commands/publicationcmds.c - AlterPublicationSetAllTables
    
    +#ifdef USE_ASSERT_CHECKING
    + Assert(!pubform->puballtables);
    +#endif
    
    Why is this #ifdef needed? Isn't that logic built into the Assert macro already?
    
    ~~~
    
    8. src/backend/commands/publicationcmds.c - AlterPublicationSetAllTables
    
    + /* set ALL TABLES flag */
    
    Use uppercase 'S' to match other comments.
    
    ~~~
    
    9. src/backend/commands/publicationcmds.c - AlterPublication
    
    + if (!isdefault)
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    + errmsg("adding ALL TABLES requires the publication to have default
    publication options, no tables/schemas associated and ALL TABLES flag
    should not be set"),
    + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    
    IMO this errmsg text is not very good but I think Osumi-san [1] has
    also given a review comment about the same errmsg.
    
    So I only wanted to add that should not say "options" here:
    "default publication options" -> "default publication parameter values"
    
    ~~~
    
    10. src/backend/parser/gram.y
    
    /*****************************************************************************
     *
     * ALTER PUBLICATION name SET ( options )
     *
     * ALTER PUBLICATION name ADD pub_obj [, ...]
     *
     * ALTER PUBLICATION name DROP pub_obj [, ...]
     *
     * ALTER PUBLICATION name SET pub_obj [, ...]
     *
     * ALTER PUBLICATION name RESET
     *
     * pub_obj is one of:
     *
     * TABLE table_name [, ...]
     * ALL TABLES IN SCHEMA schema_name [, ...]
     *
     *****************************************************************************/
    
    -
    
     Should the above comment be updated to mention also ADD ALL TABLES
    ... EXCEPT [TABLE] ...
    
    ~~~
    
    11. src/bin/pg_dump/pg_dump.c - dumpPublication
    
    + /* Include exception tables if the publication has except tables */
    + for (cell = exceptinfo.head; cell; cell = cell->next)
    + {
    + PublicationRelInfo *pubrinfo = (PublicationRelInfo *) cell->ptr;
    + PublicationInfo *relpubinfo = pubrinfo->publication;
    + TableInfo  *tbinfo;
    +
    + if (pubinfo == relpubinfo)
    
    I am unsure if that variable 'relpubinfo' is of much use; it is only
    used one time.
    
    ~~~
    
    12. src/bin/pg_dump/t/002_pg_dump.pl
    
    I think there should be more test cases here:
    
    E.g.1. EXCEPT TABLE should also test a list of tables
    
    E.g.2. EXCEPT with optional TABLE keyword ommitted
    
    ~~~
    
    13. src/bin/psql/describe.c - question about the SQL
    
    Since the new 'except' is a boolean column, wouldn't it be more
    natural if all the SQL was treating it as one?
    
    e.g. should the SQL be saying "IS preexpect", "IS NOT prexcept";
    instead of comparing preexpect to 't' and 'f' character.
    
    ~~~
    
    14. .../t/032_rep_changes_except_table.pl
    
    +# Test replication with publications created using FOR ALL TABLES EXCEPT TABLE
    +# option.
    +# Create schemas and tables on publisher
    
    "option" -> "clause"
    
    ------
    [1] https://www.postgresql.org/message-id/TYCPR01MB83730A2F1D6A5303E9C1416AEDD99%40TYCPR01MB8373.jpnprd01.prod.outlook.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  58. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-06-03T10:06:51Z

    On Thu, May 26, 2022 at 7:04 PM osumi.takamichi@fujitsu.com
    <osumi.takamichi@fujitsu.com> wrote:
    >
    > On Monday, May 23, 2022 2:13 PM vignesh C <vignesh21@gmail.com> wrote:
    > > Attached v7 patch which fixes the buildfarm warning for an unused warning in
    > > release mode as in  [1].
    > Hi, thank you for the patches.
    >
    >
    > I'll share several review comments.
    >
    > For v7-0001.
    >
    > (1) I'll suggest some minor rewording.
    >
    > +  <para>
    > +   The <literal>RESET</literal> clause will reset the publication to the
    > +   default state which includes resetting the publication options, setting
    > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > +   dropping all relations and schemas that are associated with the publication.
    >
    > My suggestion is
    > "The RESET clause will reset the publication to the
    > default state. It resets the publication operations,
    > sets ALL TABLES flag to false and drops all relations
    > and schemas associated with the publication."
    
    I felt the existing looks better. I would prefer to keep it that way.
    
    > (2) typo and rewording
    >
    > +/*
    > + * Reset the publication.
    > + *
    > + * Reset the publication options, setting ALL TABLES flag to false and drop
    > + * all relations and schemas that are associated with the publication.
    > + */
    >
    > The "setting" in this sentence should be "set".
    >
    > How about changing like below ?
    > FROM:
    > "Reset the publication options, setting ALL TABLES flag to false and drop
    > all relations and schemas that are associated with the publication."
    > TO:
    > "Reset the publication operations, set ALL TABLES flag to false and drop
    > all relations and schemas associated with the publication."
    
     I felt the existing looks better. I would prefer to keep it that way.
    
    > (3) AlterPublicationReset
    >
    > Do we need to call CacheInvalidateRelcacheAll() or
    > InvalidatePublicationRels() at the end of
    > AlterPublicationReset() like AlterPublicationOptions() ?
    
    CacheInvalidateRelcacheAll should be called if we change all tables
    from true to false, else the cache will not be invalidated. Modified
    
    >
    > For v7-0002.
    >
    > (4)
    >
    > +       if (stmt->for_all_tables)
    > +       {
    > +               bool            isdefault = CheckPublicationDefValues(tup);
    > +
    > +               if (!isdefault)
    > +                       ereport(ERROR,
    > +                                       errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > +                                       errmsg("adding ALL TABLES requires the publication to have default publication options, no tables/....
    > +                                       errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    >
    >
    > The errmsg string has three messages for user and is a bit long
    > (we have two sentences there connected by 'and').
    > Can't we make it concise and split it into a couple of lines for code readability ?
    >
    > I'll suggest a change below.
    > FROM:
    > "adding ALL TABLES requires the publication to have default publication options, no tables/schemas associated and ALL TABLES flag should not be set"
    > TO:
    > "adding ALL TABLES requires the publication defined not for ALL TABLES"
    > "to have default publish actions without any associated tables/schemas"
    
    Added errdetail and split it
    
    > (5) typo
    >
    >    <varlistentry>
    > +    <term><literal>EXCEPT TABLE</literal></term>
    > +    <listitem>
    > +     <para>
    > +      This clause specifies a list of tables to exclude from the publication.
    > +      It can only be used with <literal>FOR ALL TABLES</literal>.
    > +     </para>
    > +    </listitem>
    > +   </varlistentry>
    > +
    >
    > Kindly change
    > FROM:
    > This clause specifies a list of tables to exclude from the publication.
    > TO:
    > This clause specifies a list of tables to be excluded from the publication.
    > or
    > This clause specifies a list of tables excluded from the publication.
    
    Modified
    
    > (6) Minor suggestion for an expression change
    >
    >        Marks the publication as one that replicates changes for all tables in
    > -      the database, including tables created in the future.
    > +      the database, including tables created in the future. If
    > +      <literal>EXCEPT TABLE</literal> is specified, then exclude replicating
    > +      the changes for the specified tables.
    >
    >
    > I'll suggest a minor rewording.
    > FROM:
    > ...exclude replicating the changes for the specified tables
    > TO:
    > ...exclude replication changes for the specified tables
    
    I felt the existing is better.
    
    > (7)
    > (7-1)
    >
    > +/*
    > + * Check if the publication has default values
    > + *
    > + * Check the following:
    > + * a) Publication is not set with "FOR ALL TABLES"
    > + * b) Publication is having default options
    > + * c) Publication is not associated with schemas
    > + * d) Publication is not associated with relations
    > + */
    > +static bool
    > +CheckPublicationDefValues(HeapTuple tup)
    >
    >
    > I think this header comment can be improved.
    > FROM:
    > Check the following:
    > TO:
    > Returns true if the publication satisfies all the following conditions:
    
    Modified
    
    > (7-2)
    >
    > b) should be changed as well
    > FROM:
    > Publication is having default options
    > TO:
    > Publication has the default publish operations
    
    Changed it to "Publication is having default publication parameter values"
    
    Thanks for the comments, the attached v8 patch has the changes for the same.
    
    Regards,
    Vignesh
    
  59. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-06-03T10:10:55Z

    'On Mon, May 30, 2022 at 1:51 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Here are some minor review comments for v7-0001.
    >
    > ======
    >
    > 1. General
    >
    > Probably the commit message and all the PG docs and code comments
    > should be changed to refer to "publication parameters" instead of
    > (currently) "publication options". This is because these things are
    > really called "publication_parameters" in the PG docs [1].
    >
    > All the following review comments are just examples of this suggestion.
    
    Modified
    
    > ~~~
    >
    > 2. Commit message
    >
    > "includes resetting the publication options," -> "includes resetting
    > the publication parameters,"
    
    Modified
    
    > ~~~
    >
    > 3. doc/src/sgml/ref/alter_publication.sgml
    >
    > +  <para>
    > +   The <literal>RESET</literal> clause will reset the publication to the
    > +   default state which includes resetting the publication options, setting
    > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > +   dropping all relations and schemas that are associated with the publication.
    >    </para>
    >
    >
    > "resetting the publication options," -> "resetting the publication parameters,"
    
    Modified
    
    > ~~~
    >
    > 4. src/backend/commands/publicationcmds.c
    >
    > @@ -53,6 +53,14 @@
    >  #include "utils/syscache.h"
    >  #include "utils/varlena.h"
    >
    > +/* CREATE PUBLICATION default values for flags and options */
    > +#define PUB_DEFAULT_ACTION_INSERT true
    > +#define PUB_DEFAULT_ACTION_UPDATE true
    > +#define PUB_DEFAULT_ACTION_DELETE true
    > +#define PUB_DEFAULT_ACTION_TRUNCATE true
    > +#define PUB_DEFAULT_VIA_ROOT false
    > +#define PUB_DEFAULT_ALL_TABLES false
    >
    > "flags and options" -> "flags and publication parameters"
    
    Modified
    
    > ~~~
    >
    > 5. src/backend/commands/publicationcmds.c
    >
    > +/*
    > + * Reset the publication.
    > + *
    > + * Reset the publication options, setting ALL TABLES flag to false and drop
    > + * all relations and schemas that are associated with the publication.
    > + */
    > +static void
    > +AlterPublicationReset(ParseState *pstate, AlterPublicationStmt *stmt,
    > +   Relation rel, HeapTuple tup)
    >
    > "Reset the publication options," -> "Reset the publication parameters,"
    
    Modified
    
    > ~~~
    >
    > 6. src/test/regress/sql/publication.sql
    >
    > +-- Verify that publish options and publish_via_partition_root option are reset
    > +\dRp+ testpub_reset
    > +ALTER PUBLICATION testpub_reset RESET;
    > +\dRp+ testpub_reset
    >
    > SUGGESTION
    > -- Verify that 'publish' and 'publish_via_partition_root' publication
    > parameters are reset
    
    Modified, I have split this into two tests as it will help the 0002
    patch to add few tests with the existing steps for  'publish' and
    'publish_via_partition_root' publication parameter.
    
    Thanks for the comments. the v8 patch attached at [1] has the fixes
    for the same.
    [1] - https://www.postgresql.org/message-id/CALDaNm0sAU4s1KTLOEWv%3DrYo5dQK6uFTJn_0FKj3XG1Nv4D-qw%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  60. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-06-03T10:20:08Z

    On Tue, May 31, 2022 at 11:51 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Here are my review comments for patch v7-0002.
    >
    > ======
    >
    > 1. doc/src/sgml/logical-replication.sgml
    >
    > @@ -1167,8 +1167,9 @@ CONTEXT:  processing remote data for replication
    > origin "pg_16395" during "INSER
    >    <para>
    >     To add tables to a publication, the user must have ownership rights on the
    >     table. To add all tables in schema to a publication, the user must be a
    > -   superuser. To create a publication that publishes all tables or
    > all tables in
    > -   schema automatically, the user must be a superuser.
    > +   superuser. To add all tables to a publication, the user must be a superuser.
    > +   To create a publication that publishes all tables or all tables in schema
    > +   automatically, the user must be a superuser.
    >    </para>
    >
    > I felt that maybe this whole paragraph should be rearranged. Put the
    > "create publication" parts before the "alter publication" parts;
    > Re-word the sentences more similarly. I also felt the ALL TABLES and
    > ALL TABLES IN SCHEMA etc should be written uppercase/literal since
    > that is what was meant.
    >
    > SUGGESTION
    > To create a publication using FOR ALL TABLES or FOR ALL TABLES IN
    > SCHEMA, the user must be a superuser. To add ALL TABLES or ALL TABLES
    > IN SCHEMA to a publication, the user must be a superuser. To add
    > tables to a publication, the user must have ownership rights on the
    > table.
    
    Modified
    
    > ~~~
    >
    > 2. doc/src/sgml/ref/alter_publication.sgml
    >
    > @@ -82,8 +88,8 @@ ALTER PUBLICATION <replaceable
    > class="parameter">name</replaceable> RESET
    >
    >    <para>
    >     You must own the publication to use <command>ALTER PUBLICATION</command>.
    > -   Adding a table to a publication additionally requires owning that table.
    > -   The <literal>ADD ALL TABLES IN SCHEMA</literal>,
    > +   Adding a table to or excluding a table from a publication additionally
    > +   requires owning that table. The <literal>ADD ALL TABLES IN SCHEMA</literal>,
    >     <literal>SET ALL TABLES IN SCHEMA</literal> to a publication and
    >
    > Isn't this missing some information that says ADD ALL TABLES requires
    > the invoking user to be a superuser?
    
    Modified
    
    > ~~~
    >
    > 3. doc/src/sgml/ref/alter_publication.sgml - examples
    >
    > +  <para>
    > +   Alter publication <structname>production_publication</structname> to publish
    > +   all tables except <structname>users</structname> and
    > +   <structname>departments</structname> tables:
    > +<programlisting>
    > +ALTER PUBLICATION production_publication ADD ALL TABLES EXCEPT users,
    > departments;
    > +</programlisting></para>
    > +
    >
    > I didn't think it needs to say "tables" 2x (e.g. remove the last "tables")
    
    Modified
    
    > ~~~
    >
    > 4. doc/src/sgml/ref/create_publication.sgml - examples
    >
    > +  <para>
    > +   Create a publication that publishes all changes in all the tables except for
    > +   the changes of <structname>users</structname> and
    > +   <structname>departments</structname> tables:
    > +<programlisting>
    > +CREATE PUBLICATION mypublication FOR ALL TABLES EXCEPT users, departments;
    > +</programlisting>
    > +  </para>
    >
    > I didn't think it needs to say "tables" 2x (e.g. remove the last "tables")
    
    Modified
    
    > ~~~
    >
    > 5. src/backend/catalog/pg_publication.c
    >
    >   foreach(lc, ancestors)
    >   {
    >   Oid ancestor = lfirst_oid(lc);
    > - List    *apubids = GetRelationPublications(ancestor);
    > - List    *aschemaPubids = NIL;
    > + List    *apubids = GetRelationPublications(ancestor, false);
    > + List    *aschemapubids = NIL;
    > + List    *aexceptpubids = NIL;
    >
    >   level++;
    >
    > - if (list_member_oid(apubids, puboid))
    > + /* check if member of table publications */
    > + if (!list_member_oid(apubids, puboid))
    >   {
    > - topmost_relid = ancestor;
    > -
    > - if (ancestor_level)
    > - *ancestor_level = level;
    > - }
    > - else
    > - {
    > - aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > - if (list_member_oid(aschemaPubids, puboid))
    > + /* check if member of schema publications */
    > + aschemapubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > + if (!list_member_oid(aschemapubids, puboid))
    >   {
    > - topmost_relid = ancestor;
    > -
    > - if (ancestor_level)
    > - *ancestor_level = level;
    > + /*
    > + * If the publication is all tables publication and the table
    > + * is not part of exception tables.
    > + */
    > + if (puballtables)
    > + {
    > + aexceptpubids = GetRelationPublications(ancestor, true);
    > + if (list_member_oid(aexceptpubids, puboid))
    > + goto next;
    > + }
    > + else
    > + goto next;
    >   }
    >   }
    >
    > + topmost_relid = ancestor;
    > +
    > + if (ancestor_level)
    > + *ancestor_level = level;
    > +
    > +next:
    >   list_free(apubids);
    > - list_free(aschemaPubids);
    > + list_free(aschemapubids);
    > + list_free(aexceptpubids);
    >   }
    >
    >
    > I felt those negative (!) conditions and those goto are making this
    > logic hard to understand. Can’t it be simplified more than this? Even
    > just having another bool flag might help make it easier.
    >
    > e.g. Perhaps something a bit like this (but add some comments)
    >
    > foreach(lc, ancestors)
    > {
    > Oid ancestor = lfirst_oid(lc);
    > List    *apubids = GetRelationPublications(ancestor);
    > List    *aschemaPubids = NIL;
    > List    *aexceptpubids = NIL;
    > bool set_top = false;
    > level++;
    >
    > set_top = list_member_oid(apubids, puboid);
    > if (!set_top)
    > {
    > aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > set_top = list_member_oid(aschemaPubids, puboid);
    >
    > if (!set_top && puballtables)
    > {
    > aexceptpubids = GetRelationPublications(ancestor, true);
    > set_top = !list_member_oid(aexceptpubids, puboid);
    > }
    > }
    > if (set_top)
    > {
    > topmost_relid = ancestor;
    >
    > if (ancestor_level)
    > *ancestor_level = level;
    > }
    >
    > list_free(apubids);
    > list_free(aschemapubids);
    > list_free(aexceptpubids);
    > }
    
    Modified
    
    > ------
    >
    > 6. src/backend/commands/publicationcmds.c - CheckPublicationDefValues
    >
    > +/*
    > + * Check if the publication has default values
    > + *
    > + * Check the following:
    > + * a) Publication is not set with "FOR ALL TABLES"
    > + * b) Publication is having default options
    > + * c) Publication is not associated with schemas
    > + * d) Publication is not associated with relations
    > + */
    > +static bool
    > +CheckPublicationDefValues(HeapTuple tup)
    >
    > I think Osumi-san already gave a review [1] about this same comment.
    >
    > So I only wanted to add that it should not say "options" here:
    > "default options" -> "default publication parameter values"
    
    Modified
    
    > ~~~
    >
    > 7. src/backend/commands/publicationcmds.c - AlterPublicationSetAllTables
    >
    > +#ifdef USE_ASSERT_CHECKING
    > + Assert(!pubform->puballtables);
    > +#endif
    >
    > Why is this #ifdef needed? Isn't that logic built into the Assert macro already?
    
    pubform is used only for assert case. If we don't use it within #ifdef
    or PG_USED_FOR_ASSERTS_ONLY, it will throw a unused variable error
    without --enable-cassert like:
    
    publicationcmds.c: In function ‘AlterPublicationSetAllTables’:
    publicationcmds.c:1250:29: error: unused variable ‘pubform’
    [-Werror=unused-variable]
     1250 |         Form_pg_publication pubform = (Form_pg_publication)
    GETSTRUCT(tup);
          |                             ^~~~~~~
    cc1: all warnings being treated as errors
    
    > ~~~
    >
    > 8. src/backend/commands/publicationcmds.c - AlterPublicationSetAllTables
    >
    > + /* set ALL TABLES flag */
    >
    > Use uppercase 'S' to match other comments.
    
    Modified
    
    > ~~~
    >
    > 9. src/backend/commands/publicationcmds.c - AlterPublication
    >
    > + if (!isdefault)
    > + ereport(ERROR,
    > + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > + errmsg("adding ALL TABLES requires the publication to have default
    > publication options, no tables/schemas associated and ALL TABLES flag
    > should not be set"),
    > + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    >
    > IMO this errmsg text is not very good but I think Osumi-san [1] has
    > also given a review comment about the same errmsg.
    >
    > So I only wanted to add that should not say "options" here:
    > "default publication options" -> "default publication parameter values"
    
    Modified
    
    > ~~~
    >
    > 10. src/backend/parser/gram.y
    >
    > /*****************************************************************************
    >  *
    >  * ALTER PUBLICATION name SET ( options )
    >  *
    >  * ALTER PUBLICATION name ADD pub_obj [, ...]
    >  *
    >  * ALTER PUBLICATION name DROP pub_obj [, ...]
    >  *
    >  * ALTER PUBLICATION name SET pub_obj [, ...]
    >  *
    >  * ALTER PUBLICATION name RESET
    >  *
    >  * pub_obj is one of:
    >  *
    >  * TABLE table_name [, ...]
    >  * ALL TABLES IN SCHEMA schema_name [, ...]
    >  *
    >  *****************************************************************************/
    >
    > -
    >
    >  Should the above comment be updated to mention also ADD ALL TABLES
    > ... EXCEPT [TABLE] ...
    
    Modified
    
    > ~~~
    >
    > 11. src/bin/pg_dump/pg_dump.c - dumpPublication
    >
    > + /* Include exception tables if the publication has except tables */
    > + for (cell = exceptinfo.head; cell; cell = cell->next)
    > + {
    > + PublicationRelInfo *pubrinfo = (PublicationRelInfo *) cell->ptr;
    > + PublicationInfo *relpubinfo = pubrinfo->publication;
    > + TableInfo  *tbinfo;
    > +
    > + if (pubinfo == relpubinfo)
    >
    > I am unsure if that variable 'relpubinfo' is of much use; it is only
    > used one time.
    
    Removed relpubinfo
    
    > ~~~
    >
    > 12. src/bin/pg_dump/t/002_pg_dump.pl
    >
    > I think there should be more test cases here:
    >
    > E.g.1. EXCEPT TABLE should also test a list of tables
    >
    > E.g.2. EXCEPT with optional TABLE keyword ommitted
    
    Added a test for list of tables and modified one of the test to remove TABLE.
    
    > ~~~
    >
    > 13. src/bin/psql/describe.c - question about the SQL
    >
    > Since the new 'except' is a boolean column, wouldn't it be more
    > natural if all the SQL was treating it as one?
    >
    > e.g. should the SQL be saying "IS preexpect", "IS NOT prexcept";
    > instead of comparing preexpect to 't' and 'f' character.
    
    modified
    
    > ~~~
    >
    > 14. .../t/032_rep_changes_except_table.pl
    >
    > +# Test replication with publications created using FOR ALL TABLES EXCEPT TABLE
    > +# option.
    > +# Create schemas and tables on publisher
    >
    > "option" -> "clause"
    
    Modified.
    
    Thanks for the comments. The v8 patch attached at [1] has the fixes
    for the same.
    [1] - https://www.postgresql.org/message-id/CALDaNm0sAU4s1KTLOEWv%3DrYo5dQK6uFTJn_0FKj3XG1Nv4D-qw%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  61. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-06-08T11:04:05Z

    On Fri, Jun 3, 2022 at 3:37 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Thanks for the comments, the attached v8 patch has the changes for the same.
    >
    
    AFAICS, the summary of this proposal is that we want to support
    exclude of certain objects from publication with two kinds of
    variants. The first variant is to add support to exclude specific
    tables from ALL TABLES PUBLICATION. Without this feature, users need
    to manually add all tables for a database even when she wants to avoid
    only a handful of tables from the database say because they contain
    sensitive information or are not required. We have seen that other
    database like MySQL also provides similar feature [1] (See
    REPLICATE_WILD_IGNORE_TABLE). The proposed syntax for this is as
    follows:
    
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    or
    ALTER PUBLICATION pub1 ADD ALL TABLES EXCEPT TABLE t1,t2;
    
    This will allow us to publish all the tables in the current database
    except t1 and t2. Now, I see that pg_dump has a similar option
    provided by switch --exclude-table but that allows tables matching
    patterns which is not the case here. I am not sure if we need a
    similar variant here.
    
    Then users will be allowed to reset the publication by:
    ALTER PUBLICATION pub1 RESET;
    
    This will reset the publication to the default state which includes
    resetting the publication parameters, setting the ALL TABLES flag to
    false, and dropping the relations and schemas that are associated with
    the publication. I don't know if we want to go further with allowing
    to RESET specific parameters and if so which parameters and what would
    its syntax be?
    
    The second variant is to add support to exclude certain columns of a
    table while publishing a particular table. Currently, users need to
    list all required columns' names even if they don't want to hide most
    of the columns in the table (for example Create Publication pub For
    Table t1 (c1, c2)). Consider user doesn't want to publish the 'salary'
    or other sensitive information of executives/employees but would like
    to publish all other columns. I feel in such cases it will be a lot of
    work for the user especially when the table has many columns. I see
    that Oracle has a similar feature [2]. I think without this it will be
    difficult for users to use this feature in some cases. The patch for
    this is not proposed but I would imagine syntax for it to be something
    like "Create Publication pub For Table t1 Except (c3)" and similar
    variants for Alter Publication.
    
    Have I missed anything?
    
    Thoughts on the proposal/syntax would be appreciated?
    
    [1] - https://dev.mysql.com/doc/refman/5.7/en/change-replication-filter.html
    [2] - https://docs.oracle.com/en/cloud/paas/goldengate-cloud/gwuad/selecting-columns.html#GUID-9A851C8B-48F7-43DF-8D98-D086BE069E20
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  62. RE: Skipping schema changes in publication

    Zhijie Hou (Fujitsu) <houzj.fnst@fujitsu.com> — 2022-06-14T03:40:42Z

    On Wednesday, June 8, 2022 7:04 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > 
    > On Fri, Jun 3, 2022 at 3:37 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Thanks for the comments, the attached v8 patch has the changes for the
    > same.
    > >
    > 
    > AFAICS, the summary of this proposal is that we want to support
    > exclude of certain objects from publication with two kinds of
    > variants. The first variant is to add support to exclude specific
    > tables from ALL TABLES PUBLICATION. Without this feature, users need
    > to manually add all tables for a database even when she wants to avoid
    > only a handful of tables from the database say because they contain
    > sensitive information or are not required. We have seen that other
    > database like MySQL also provides similar feature [1] (See
    > REPLICATE_WILD_IGNORE_TABLE). The proposed syntax for this is as
    > follows:
    > 
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    > or
    > ALTER PUBLICATION pub1 ADD ALL TABLES EXCEPT TABLE t1,t2;
    > 
    > This will allow us to publish all the tables in the current database
    > except t1 and t2. Now, I see that pg_dump has a similar option
    > provided by switch --exclude-table but that allows tables matching
    > patterns which is not the case here. I am not sure if we need a
    > similar variant here.
    > 
    > Then users will be allowed to reset the publication by:
    > ALTER PUBLICATION pub1 RESET;
    > 
    > This will reset the publication to the default state which includes
    > resetting the publication parameters, setting the ALL TABLES flag to
    > false, and dropping the relations and schemas that are associated with
    > the publication. I don't know if we want to go further with allowing
    > to RESET specific parameters and if so which parameters and what would
    > its syntax be?
    > 
    > The second variant is to add support to exclude certain columns of a
    > table while publishing a particular table. Currently, users need to
    > list all required columns' names even if they don't want to hide most
    > of the columns in the table (for example Create Publication pub For
    > Table t1 (c1, c2)). Consider user doesn't want to publish the 'salary'
    > or other sensitive information of executives/employees but would like
    > to publish all other columns. I feel in such cases it will be a lot of
    > work for the user especially when the table has many columns. I see
    > that Oracle has a similar feature [2]. I think without this it will be
    > difficult for users to use this feature in some cases. The patch for
    > this is not proposed but I would imagine syntax for it to be something
    > like "Create Publication pub For Table t1 Except (c3)" and similar
    > variants for Alter Publication.
    
    I think the feature to exclude certain columns of a table would be useful.
    
    In some production scenarios, we usually do not want to replicate
    sensitive fields(column) in the table. Although we already can achieve
    this by specify all replicated columns in the list[1], but that seems a
    hard work when the table has hundreds of columns.
    
    [1]
    CREATE TABLE test(a int, b int, c int,..., sensitive text);
    CRAETE PUBLICATION pub FOR TABLE test(a,b,c,...);
    
    In addition, it's not easy to maintain the column list like above. Because
    we sometimes need to add new fields or delete fields due to business
    needs. Every time we add a column(or delete a column in column list), we
    need to update the column list.
    
    If we support Except:
    CRAETE PUBLICATION pub FOR TABLE test EXCEPT (sensitive);
    
    We don't need to update the column list in most cases.
    
    Thanks for "hametan" for providing the use case off-list.
    
    Best regards,
    Hou zj
    
    
    
    
  63. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2022-06-16T04:04:54Z

    On Tue, Jun 14, 2022 at 9:10 AM houzj.fnst@fujitsu.com
    <houzj.fnst@fujitsu.com> wrote:
    >
    > On Wednesday, June 8, 2022 7:04 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Fri, Jun 3, 2022 at 3:37 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > Thanks for the comments, the attached v8 patch has the changes for the
    > > same.
    > > >
    > >
    > > AFAICS, the summary of this proposal is that we want to support
    > > exclude of certain objects from publication with two kinds of
    > > variants. The first variant is to add support to exclude specific
    > > tables from ALL TABLES PUBLICATION. Without this feature, users need
    > > to manually add all tables for a database even when she wants to avoid
    > > only a handful of tables from the database say because they contain
    > > sensitive information or are not required. We have seen that other
    > > database like MySQL also provides similar feature [1] (See
    > > REPLICATE_WILD_IGNORE_TABLE). The proposed syntax for this is as
    > > follows:
    > >
    > > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE t1,t2;
    > > or
    > > ALTER PUBLICATION pub1 ADD ALL TABLES EXCEPT TABLE t1,t2;
    > >
    > > This will allow us to publish all the tables in the current database
    > > except t1 and t2. Now, I see that pg_dump has a similar option
    > > provided by switch --exclude-table but that allows tables matching
    > > patterns which is not the case here. I am not sure if we need a
    > > similar variant here.
    > >
    > > Then users will be allowed to reset the publication by:
    > > ALTER PUBLICATION pub1 RESET;
    > >
    > > This will reset the publication to the default state which includes
    > > resetting the publication parameters, setting the ALL TABLES flag to
    > > false, and dropping the relations and schemas that are associated with
    > > the publication. I don't know if we want to go further with allowing
    > > to RESET specific parameters and if so which parameters and what would
    > > its syntax be?
    > >
    > > The second variant is to add support to exclude certain columns of a
    > > table while publishing a particular table. Currently, users need to
    > > list all required columns' names even if they don't want to hide most
    > > of the columns in the table (for example Create Publication pub For
    > > Table t1 (c1, c2)). Consider user doesn't want to publish the 'salary'
    > > or other sensitive information of executives/employees but would like
    > > to publish all other columns. I feel in such cases it will be a lot of
    > > work for the user especially when the table has many columns. I see
    > > that Oracle has a similar feature [2]. I think without this it will be
    > > difficult for users to use this feature in some cases. The patch for
    > > this is not proposed but I would imagine syntax for it to be something
    > > like "Create Publication pub For Table t1 Except (c3)" and similar
    > > variants for Alter Publication.
    >
    > I think the feature to exclude certain columns of a table would be useful.
    >
    > In some production scenarios, we usually do not want to replicate
    > sensitive fields(column) in the table. Although we already can achieve
    > this by specify all replicated columns in the list[1], but that seems a
    > hard work when the table has hundreds of columns.
    >
    > [1]
    > CREATE TABLE test(a int, b int, c int,..., sensitive text);
    > CRAETE PUBLICATION pub FOR TABLE test(a,b,c,...);
    >
    > In addition, it's not easy to maintain the column list like above. Because
    > we sometimes need to add new fields or delete fields due to business
    > needs. Every time we add a column(or delete a column in column list), we
    > need to update the column list.
    >
    > If we support Except:
    > CRAETE PUBLICATION pub FOR TABLE test EXCEPT (sensitive);
    >
    > We don't need to update the column list in most cases.
    >
    
    Right, this is a valid point and I think it makes sense for me to
    support such a feature for column list and also to exclude a
    particular table(s) from the ALL TABLES publication.
    
    Peter E., Euler, and others, do you have any objections to supporting
    the above-mentioned two cases?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  64. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-08-08T07:16:39Z

    On Fri, Jun 3, 2022 at 3:36 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Thu, May 26, 2022 at 7:04 PM osumi.takamichi@fujitsu.com
    > <osumi.takamichi@fujitsu.com> wrote:
    > >
    > > On Monday, May 23, 2022 2:13 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > Attached v7 patch which fixes the buildfarm warning for an unused warning in
    > > > release mode as in  [1].
    > > Hi, thank you for the patches.
    > >
    > >
    > > I'll share several review comments.
    > >
    > > For v7-0001.
    > >
    > > (1) I'll suggest some minor rewording.
    > >
    > > +  <para>
    > > +   The <literal>RESET</literal> clause will reset the publication to the
    > > +   default state which includes resetting the publication options, setting
    > > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > > +   dropping all relations and schemas that are associated with the publication.
    > >
    > > My suggestion is
    > > "The RESET clause will reset the publication to the
    > > default state. It resets the publication operations,
    > > sets ALL TABLES flag to false and drops all relations
    > > and schemas associated with the publication."
    >
    > I felt the existing looks better. I would prefer to keep it that way.
    >
    > > (2) typo and rewording
    > >
    > > +/*
    > > + * Reset the publication.
    > > + *
    > > + * Reset the publication options, setting ALL TABLES flag to false and drop
    > > + * all relations and schemas that are associated with the publication.
    > > + */
    > >
    > > The "setting" in this sentence should be "set".
    > >
    > > How about changing like below ?
    > > FROM:
    > > "Reset the publication options, setting ALL TABLES flag to false and drop
    > > all relations and schemas that are associated with the publication."
    > > TO:
    > > "Reset the publication operations, set ALL TABLES flag to false and drop
    > > all relations and schemas associated with the publication."
    >
    >  I felt the existing looks better. I would prefer to keep it that way.
    >
    > > (3) AlterPublicationReset
    > >
    > > Do we need to call CacheInvalidateRelcacheAll() or
    > > InvalidatePublicationRels() at the end of
    > > AlterPublicationReset() like AlterPublicationOptions() ?
    >
    > CacheInvalidateRelcacheAll should be called if we change all tables
    > from true to false, else the cache will not be invalidated. Modified
    >
    > >
    > > For v7-0002.
    > >
    > > (4)
    > >
    > > +       if (stmt->for_all_tables)
    > > +       {
    > > +               bool            isdefault = CheckPublicationDefValues(tup);
    > > +
    > > +               if (!isdefault)
    > > +                       ereport(ERROR,
    > > +                                       errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > > +                                       errmsg("adding ALL TABLES requires the publication to have default publication options, no tables/....
    > > +                                       errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    > >
    > >
    > > The errmsg string has three messages for user and is a bit long
    > > (we have two sentences there connected by 'and').
    > > Can't we make it concise and split it into a couple of lines for code readability ?
    > >
    > > I'll suggest a change below.
    > > FROM:
    > > "adding ALL TABLES requires the publication to have default publication options, no tables/schemas associated and ALL TABLES flag should not be set"
    > > TO:
    > > "adding ALL TABLES requires the publication defined not for ALL TABLES"
    > > "to have default publish actions without any associated tables/schemas"
    >
    > Added errdetail and split it
    >
    > > (5) typo
    > >
    > >    <varlistentry>
    > > +    <term><literal>EXCEPT TABLE</literal></term>
    > > +    <listitem>
    > > +     <para>
    > > +      This clause specifies a list of tables to exclude from the publication.
    > > +      It can only be used with <literal>FOR ALL TABLES</literal>.
    > > +     </para>
    > > +    </listitem>
    > > +   </varlistentry>
    > > +
    > >
    > > Kindly change
    > > FROM:
    > > This clause specifies a list of tables to exclude from the publication.
    > > TO:
    > > This clause specifies a list of tables to be excluded from the publication.
    > > or
    > > This clause specifies a list of tables excluded from the publication.
    >
    > Modified
    >
    > > (6) Minor suggestion for an expression change
    > >
    > >        Marks the publication as one that replicates changes for all tables in
    > > -      the database, including tables created in the future.
    > > +      the database, including tables created in the future. If
    > > +      <literal>EXCEPT TABLE</literal> is specified, then exclude replicating
    > > +      the changes for the specified tables.
    > >
    > >
    > > I'll suggest a minor rewording.
    > > FROM:
    > > ...exclude replicating the changes for the specified tables
    > > TO:
    > > ...exclude replication changes for the specified tables
    >
    > I felt the existing is better.
    >
    > > (7)
    > > (7-1)
    > >
    > > +/*
    > > + * Check if the publication has default values
    > > + *
    > > + * Check the following:
    > > + * a) Publication is not set with "FOR ALL TABLES"
    > > + * b) Publication is having default options
    > > + * c) Publication is not associated with schemas
    > > + * d) Publication is not associated with relations
    > > + */
    > > +static bool
    > > +CheckPublicationDefValues(HeapTuple tup)
    > >
    > >
    > > I think this header comment can be improved.
    > > FROM:
    > > Check the following:
    > > TO:
    > > Returns true if the publication satisfies all the following conditions:
    >
    > Modified
    >
    > > (7-2)
    > >
    > > b) should be changed as well
    > > FROM:
    > > Publication is having default options
    > > TO:
    > > Publication has the default publish operations
    >
    > Changed it to "Publication is having default publication parameter values"
    >
    > Thanks for the comments, the attached v8 patch has the changes for the same.
    
    The patch needed to be rebased on top of HEAD because of commit
    "0c20dd33db1607d6a85ffce24238c1e55e384b49", attached a rebased v8
    version for the changes of the same.
    
    Regards,
    Vignesh
    
  65. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-08-08T09:23:28Z

    On Mon, Aug 8, 2022 at 12:46 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Fri, Jun 3, 2022 at 3:36 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Thu, May 26, 2022 at 7:04 PM osumi.takamichi@fujitsu.com
    > > <osumi.takamichi@fujitsu.com> wrote:
    > > >
    > > > On Monday, May 23, 2022 2:13 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > > Attached v7 patch which fixes the buildfarm warning for an unused warning in
    > > > > release mode as in  [1].
    > > > Hi, thank you for the patches.
    > > >
    > > >
    > > > I'll share several review comments.
    > > >
    > > > For v7-0001.
    > > >
    > > > (1) I'll suggest some minor rewording.
    > > >
    > > > +  <para>
    > > > +   The <literal>RESET</literal> clause will reset the publication to the
    > > > +   default state which includes resetting the publication options, setting
    > > > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > > > +   dropping all relations and schemas that are associated with the publication.
    > > >
    > > > My suggestion is
    > > > "The RESET clause will reset the publication to the
    > > > default state. It resets the publication operations,
    > > > sets ALL TABLES flag to false and drops all relations
    > > > and schemas associated with the publication."
    > >
    > > I felt the existing looks better. I would prefer to keep it that way.
    > >
    > > > (2) typo and rewording
    > > >
    > > > +/*
    > > > + * Reset the publication.
    > > > + *
    > > > + * Reset the publication options, setting ALL TABLES flag to false and drop
    > > > + * all relations and schemas that are associated with the publication.
    > > > + */
    > > >
    > > > The "setting" in this sentence should be "set".
    > > >
    > > > How about changing like below ?
    > > > FROM:
    > > > "Reset the publication options, setting ALL TABLES flag to false and drop
    > > > all relations and schemas that are associated with the publication."
    > > > TO:
    > > > "Reset the publication operations, set ALL TABLES flag to false and drop
    > > > all relations and schemas associated with the publication."
    > >
    > >  I felt the existing looks better. I would prefer to keep it that way.
    > >
    > > > (3) AlterPublicationReset
    > > >
    > > > Do we need to call CacheInvalidateRelcacheAll() or
    > > > InvalidatePublicationRels() at the end of
    > > > AlterPublicationReset() like AlterPublicationOptions() ?
    > >
    > > CacheInvalidateRelcacheAll should be called if we change all tables
    > > from true to false, else the cache will not be invalidated. Modified
    > >
    > > >
    > > > For v7-0002.
    > > >
    > > > (4)
    > > >
    > > > +       if (stmt->for_all_tables)
    > > > +       {
    > > > +               bool            isdefault = CheckPublicationDefValues(tup);
    > > > +
    > > > +               if (!isdefault)
    > > > +                       ereport(ERROR,
    > > > +                                       errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > > > +                                       errmsg("adding ALL TABLES requires the publication to have default publication options, no tables/....
    > > > +                                       errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    > > >
    > > >
    > > > The errmsg string has three messages for user and is a bit long
    > > > (we have two sentences there connected by 'and').
    > > > Can't we make it concise and split it into a couple of lines for code readability ?
    > > >
    > > > I'll suggest a change below.
    > > > FROM:
    > > > "adding ALL TABLES requires the publication to have default publication options, no tables/schemas associated and ALL TABLES flag should not be set"
    > > > TO:
    > > > "adding ALL TABLES requires the publication defined not for ALL TABLES"
    > > > "to have default publish actions without any associated tables/schemas"
    > >
    > > Added errdetail and split it
    > >
    > > > (5) typo
    > > >
    > > >    <varlistentry>
    > > > +    <term><literal>EXCEPT TABLE</literal></term>
    > > > +    <listitem>
    > > > +     <para>
    > > > +      This clause specifies a list of tables to exclude from the publication.
    > > > +      It can only be used with <literal>FOR ALL TABLES</literal>.
    > > > +     </para>
    > > > +    </listitem>
    > > > +   </varlistentry>
    > > > +
    > > >
    > > > Kindly change
    > > > FROM:
    > > > This clause specifies a list of tables to exclude from the publication.
    > > > TO:
    > > > This clause specifies a list of tables to be excluded from the publication.
    > > > or
    > > > This clause specifies a list of tables excluded from the publication.
    > >
    > > Modified
    > >
    > > > (6) Minor suggestion for an expression change
    > > >
    > > >        Marks the publication as one that replicates changes for all tables in
    > > > -      the database, including tables created in the future.
    > > > +      the database, including tables created in the future. If
    > > > +      <literal>EXCEPT TABLE</literal> is specified, then exclude replicating
    > > > +      the changes for the specified tables.
    > > >
    > > >
    > > > I'll suggest a minor rewording.
    > > > FROM:
    > > > ...exclude replicating the changes for the specified tables
    > > > TO:
    > > > ...exclude replication changes for the specified tables
    > >
    > > I felt the existing is better.
    > >
    > > > (7)
    > > > (7-1)
    > > >
    > > > +/*
    > > > + * Check if the publication has default values
    > > > + *
    > > > + * Check the following:
    > > > + * a) Publication is not set with "FOR ALL TABLES"
    > > > + * b) Publication is having default options
    > > > + * c) Publication is not associated with schemas
    > > > + * d) Publication is not associated with relations
    > > > + */
    > > > +static bool
    > > > +CheckPublicationDefValues(HeapTuple tup)
    > > >
    > > >
    > > > I think this header comment can be improved.
    > > > FROM:
    > > > Check the following:
    > > > TO:
    > > > Returns true if the publication satisfies all the following conditions:
    > >
    > > Modified
    > >
    > > > (7-2)
    > > >
    > > > b) should be changed as well
    > > > FROM:
    > > > Publication is having default options
    > > > TO:
    > > > Publication has the default publish operations
    > >
    > > Changed it to "Publication is having default publication parameter values"
    > >
    > > Thanks for the comments, the attached v8 patch has the changes for the same.
    >
    > The patch needed to be rebased on top of HEAD because of commit
    > "0c20dd33db1607d6a85ffce24238c1e55e384b49", attached a rebased v8
    > version for the changes of the same.
    
    I had missed attaching one of the changes that was present locally.
    The updated patch has the changes for the same.
    
    Regards,
    Vignesh
    
  66. Re: Skipping schema changes in publication

    Nitin Jadhav <nitinjadhavpostgres@gmail.com> — 2022-08-18T07:02:31Z

    I spent some time on understanding the proposal and the patch. Here
    are a few comments wrt the test cases.
    
    > +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1;
    > +
    > +-- Verify that tables associated with the publication are dropped after RESET
    > +\dRp+ testpub_reset
    > +ALTER PUBLICATION testpub_reset RESET;
    > +\dRp+ testpub_reset
    >
    > +ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA public;
    > +
    > +-- Verify that schemas associated with the publication are dropped after RESET
    > +\dRp+ testpub_reset
    > +ALTER PUBLICATION testpub_reset RESET;
    > +\dRp+ testpub_reset
    
    The results for the above two cases are the same before and after the
    reset. Is there any way to verify that?
    ---
    
    > +-- Can't add EXCEPT TABLE to 'FOR ALL TABLES' publication
    > +ALTER PUBLICATION testpub_reset ADD ALL TABLES EXCEPT TABLE pub_sch1.tbl1;
    > +
    >
    > +-- Can't add EXCEPT TABLE to 'FOR TABLE' publication
    > +ALTER PUBLICATION testpub_reset ADD ALL TABLES EXCEPT TABLE pub_sch1.tbl1;
    > +
    >
    > +-- Can't add EXCEPT TABLE to 'FOR ALL TABLES IN SCHEMA' publication
    > +ALTER PUBLICATION testpub_reset ADD ALL TABLES EXCEPT TABLE pub_sch1.tbl1;
    > +
    
    I did not understand the objective of these tests. I think we need to
    improve the comments.
    
    Thanks & Regards,
    
    
    
    On Mon, Aug 8, 2022 at 2:53 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Mon, Aug 8, 2022 at 12:46 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Fri, Jun 3, 2022 at 3:36 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > On Thu, May 26, 2022 at 7:04 PM osumi.takamichi@fujitsu.com
    > > > <osumi.takamichi@fujitsu.com> wrote:
    > > > >
    > > > > On Monday, May 23, 2022 2:13 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > > > Attached v7 patch which fixes the buildfarm warning for an unused warning in
    > > > > > release mode as in  [1].
    > > > > Hi, thank you for the patches.
    > > > >
    > > > >
    > > > > I'll share several review comments.
    > > > >
    > > > > For v7-0001.
    > > > >
    > > > > (1) I'll suggest some minor rewording.
    > > > >
    > > > > +  <para>
    > > > > +   The <literal>RESET</literal> clause will reset the publication to the
    > > > > +   default state which includes resetting the publication options, setting
    > > > > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > > > > +   dropping all relations and schemas that are associated with the publication.
    > > > >
    > > > > My suggestion is
    > > > > "The RESET clause will reset the publication to the
    > > > > default state. It resets the publication operations,
    > > > > sets ALL TABLES flag to false and drops all relations
    > > > > and schemas associated with the publication."
    > > >
    > > > I felt the existing looks better. I would prefer to keep it that way.
    > > >
    > > > > (2) typo and rewording
    > > > >
    > > > > +/*
    > > > > + * Reset the publication.
    > > > > + *
    > > > > + * Reset the publication options, setting ALL TABLES flag to false and drop
    > > > > + * all relations and schemas that are associated with the publication.
    > > > > + */
    > > > >
    > > > > The "setting" in this sentence should be "set".
    > > > >
    > > > > How about changing like below ?
    > > > > FROM:
    > > > > "Reset the publication options, setting ALL TABLES flag to false and drop
    > > > > all relations and schemas that are associated with the publication."
    > > > > TO:
    > > > > "Reset the publication operations, set ALL TABLES flag to false and drop
    > > > > all relations and schemas associated with the publication."
    > > >
    > > >  I felt the existing looks better. I would prefer to keep it that way.
    > > >
    > > > > (3) AlterPublicationReset
    > > > >
    > > > > Do we need to call CacheInvalidateRelcacheAll() or
    > > > > InvalidatePublicationRels() at the end of
    > > > > AlterPublicationReset() like AlterPublicationOptions() ?
    > > >
    > > > CacheInvalidateRelcacheAll should be called if we change all tables
    > > > from true to false, else the cache will not be invalidated. Modified
    > > >
    > > > >
    > > > > For v7-0002.
    > > > >
    > > > > (4)
    > > > >
    > > > > +       if (stmt->for_all_tables)
    > > > > +       {
    > > > > +               bool            isdefault = CheckPublicationDefValues(tup);
    > > > > +
    > > > > +               if (!isdefault)
    > > > > +                       ereport(ERROR,
    > > > > +                                       errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > > > > +                                       errmsg("adding ALL TABLES requires the publication to have default publication options, no tables/....
    > > > > +                                       errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    > > > >
    > > > >
    > > > > The errmsg string has three messages for user and is a bit long
    > > > > (we have two sentences there connected by 'and').
    > > > > Can't we make it concise and split it into a couple of lines for code readability ?
    > > > >
    > > > > I'll suggest a change below.
    > > > > FROM:
    > > > > "adding ALL TABLES requires the publication to have default publication options, no tables/schemas associated and ALL TABLES flag should not be set"
    > > > > TO:
    > > > > "adding ALL TABLES requires the publication defined not for ALL TABLES"
    > > > > "to have default publish actions without any associated tables/schemas"
    > > >
    > > > Added errdetail and split it
    > > >
    > > > > (5) typo
    > > > >
    > > > >    <varlistentry>
    > > > > +    <term><literal>EXCEPT TABLE</literal></term>
    > > > > +    <listitem>
    > > > > +     <para>
    > > > > +      This clause specifies a list of tables to exclude from the publication.
    > > > > +      It can only be used with <literal>FOR ALL TABLES</literal>.
    > > > > +     </para>
    > > > > +    </listitem>
    > > > > +   </varlistentry>
    > > > > +
    > > > >
    > > > > Kindly change
    > > > > FROM:
    > > > > This clause specifies a list of tables to exclude from the publication.
    > > > > TO:
    > > > > This clause specifies a list of tables to be excluded from the publication.
    > > > > or
    > > > > This clause specifies a list of tables excluded from the publication.
    > > >
    > > > Modified
    > > >
    > > > > (6) Minor suggestion for an expression change
    > > > >
    > > > >        Marks the publication as one that replicates changes for all tables in
    > > > > -      the database, including tables created in the future.
    > > > > +      the database, including tables created in the future. If
    > > > > +      <literal>EXCEPT TABLE</literal> is specified, then exclude replicating
    > > > > +      the changes for the specified tables.
    > > > >
    > > > >
    > > > > I'll suggest a minor rewording.
    > > > > FROM:
    > > > > ...exclude replicating the changes for the specified tables
    > > > > TO:
    > > > > ...exclude replication changes for the specified tables
    > > >
    > > > I felt the existing is better.
    > > >
    > > > > (7)
    > > > > (7-1)
    > > > >
    > > > > +/*
    > > > > + * Check if the publication has default values
    > > > > + *
    > > > > + * Check the following:
    > > > > + * a) Publication is not set with "FOR ALL TABLES"
    > > > > + * b) Publication is having default options
    > > > > + * c) Publication is not associated with schemas
    > > > > + * d) Publication is not associated with relations
    > > > > + */
    > > > > +static bool
    > > > > +CheckPublicationDefValues(HeapTuple tup)
    > > > >
    > > > >
    > > > > I think this header comment can be improved.
    > > > > FROM:
    > > > > Check the following:
    > > > > TO:
    > > > > Returns true if the publication satisfies all the following conditions:
    > > >
    > > > Modified
    > > >
    > > > > (7-2)
    > > > >
    > > > > b) should be changed as well
    > > > > FROM:
    > > > > Publication is having default options
    > > > > TO:
    > > > > Publication has the default publish operations
    > > >
    > > > Changed it to "Publication is having default publication parameter values"
    > > >
    > > > Thanks for the comments, the attached v8 patch has the changes for the same.
    > >
    > > The patch needed to be rebased on top of HEAD because of commit
    > > "0c20dd33db1607d6a85ffce24238c1e55e384b49", attached a rebased v8
    > > version for the changes of the same.
    >
    > I had missed attaching one of the changes that was present locally.
    > The updated patch has the changes for the same.
    >
    > Regards,
    > Vignesh
    
    
    
    
  67. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-08-18T14:26:54Z

    On Thu, Aug 18, 2022 at 12:33 PM Nitin Jadhav
    <nitinjadhavpostgres@gmail.com> wrote:
    >
    > I spent some time on understanding the proposal and the patch. Here
    > are a few comments wrt the test cases.
    >
    > > +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1;
    > > +
    > > +-- Verify that tables associated with the publication are dropped after RESET
    > > +\dRp+ testpub_reset
    > > +ALTER PUBLICATION testpub_reset RESET;
    > > +\dRp+ testpub_reset
    > >
    > > +ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA public;
    > > +
    > > +-- Verify that schemas associated with the publication are dropped after RESET
    > > +\dRp+ testpub_reset
    > > +ALTER PUBLICATION testpub_reset RESET;
    > > +\dRp+ testpub_reset
    >
    > The results for the above two cases are the same before and after the
    > reset. Is there any way to verify that?
    
    If you see the expected, first \dRp+ command includes:
    +Tables:
    +    "pub_sch1.tbl1"
    The second \dRp+ does not include the Tables.
    We are trying to verify that after reset, the tables will be removed
    from the publication.
    The second test is similar to the first, the only difference here is
    that we test schema instead of tables. i.e we verify that the schemas
    will be removed from the publication.
    
    > ---
    >
    > > +-- Can't add EXCEPT TABLE to 'FOR ALL TABLES' publication
    > > +ALTER PUBLICATION testpub_reset ADD ALL TABLES EXCEPT TABLE pub_sch1.tbl1;
    > > +
    > >
    > > +-- Can't add EXCEPT TABLE to 'FOR TABLE' publication
    > > +ALTER PUBLICATION testpub_reset ADD ALL TABLES EXCEPT TABLE pub_sch1.tbl1;
    > > +
    > >
    > > +-- Can't add EXCEPT TABLE to 'FOR ALL TABLES IN SCHEMA' publication
    > > +ALTER PUBLICATION testpub_reset ADD ALL TABLES EXCEPT TABLE pub_sch1.tbl1;
    > > +
    >
    > I did not understand the objective of these tests. I think we need to
    > improve the comments.
    
    There are different publications like "ALL TABLES", "TABLE", "ALL
    TABLES IN SCHEMA" publications. Here we are trying to verify that
    except tables cannot be added to "ALL TABLES", "TABLE", "ALL TABLES IN
    SCHEMA" publications.
    If you see the expected file, you will see the following error:
    +-- Can't add EXCEPT TABLE to 'FOR ALL TABLES' publication
    +ALTER PUBLICATION testpub_reset ADD ALL TABLES EXCEPT TABLE pub_sch1.tbl1;
    +ERROR:  adding ALL TABLES requires the publication to have default
    publication parameter values
    +DETAIL:  ALL TABLES flag should not be set and no tables/schemas
    should be associated.
    +HINT:  Use ALTER PUBLICATION ... RESET to reset the publication
    
    I felt the existing comment is ok. Let me know if you still feel any
    change is required.
    
    Regards,
    Vignesh
    
    
    
    
  68. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-08-18T17:41:30Z

    On Mon, Aug 8, 2022 at 2:53 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Mon, Aug 8, 2022 at 12:46 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Fri, Jun 3, 2022 at 3:36 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > On Thu, May 26, 2022 at 7:04 PM osumi.takamichi@fujitsu.com
    > > > <osumi.takamichi@fujitsu.com> wrote:
    > > > >
    > > > > On Monday, May 23, 2022 2:13 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > > > Attached v7 patch which fixes the buildfarm warning for an unused warning in
    > > > > > release mode as in  [1].
    > > > > Hi, thank you for the patches.
    > > > >
    > > > >
    > > > > I'll share several review comments.
    > > > >
    > > > > For v7-0001.
    > > > >
    > > > > (1) I'll suggest some minor rewording.
    > > > >
    > > > > +  <para>
    > > > > +   The <literal>RESET</literal> clause will reset the publication to the
    > > > > +   default state which includes resetting the publication options, setting
    > > > > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > > > > +   dropping all relations and schemas that are associated with the publication.
    > > > >
    > > > > My suggestion is
    > > > > "The RESET clause will reset the publication to the
    > > > > default state. It resets the publication operations,
    > > > > sets ALL TABLES flag to false and drops all relations
    > > > > and schemas associated with the publication."
    > > >
    > > > I felt the existing looks better. I would prefer to keep it that way.
    > > >
    > > > > (2) typo and rewording
    > > > >
    > > > > +/*
    > > > > + * Reset the publication.
    > > > > + *
    > > > > + * Reset the publication options, setting ALL TABLES flag to false and drop
    > > > > + * all relations and schemas that are associated with the publication.
    > > > > + */
    > > > >
    > > > > The "setting" in this sentence should be "set".
    > > > >
    > > > > How about changing like below ?
    > > > > FROM:
    > > > > "Reset the publication options, setting ALL TABLES flag to false and drop
    > > > > all relations and schemas that are associated with the publication."
    > > > > TO:
    > > > > "Reset the publication operations, set ALL TABLES flag to false and drop
    > > > > all relations and schemas associated with the publication."
    > > >
    > > >  I felt the existing looks better. I would prefer to keep it that way.
    > > >
    > > > > (3) AlterPublicationReset
    > > > >
    > > > > Do we need to call CacheInvalidateRelcacheAll() or
    > > > > InvalidatePublicationRels() at the end of
    > > > > AlterPublicationReset() like AlterPublicationOptions() ?
    > > >
    > > > CacheInvalidateRelcacheAll should be called if we change all tables
    > > > from true to false, else the cache will not be invalidated. Modified
    > > >
    > > > >
    > > > > For v7-0002.
    > > > >
    > > > > (4)
    > > > >
    > > > > +       if (stmt->for_all_tables)
    > > > > +       {
    > > > > +               bool            isdefault = CheckPublicationDefValues(tup);
    > > > > +
    > > > > +               if (!isdefault)
    > > > > +                       ereport(ERROR,
    > > > > +                                       errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > > > > +                                       errmsg("adding ALL TABLES requires the publication to have default publication options, no tables/....
    > > > > +                                       errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    > > > >
    > > > >
    > > > > The errmsg string has three messages for user and is a bit long
    > > > > (we have two sentences there connected by 'and').
    > > > > Can't we make it concise and split it into a couple of lines for code readability ?
    > > > >
    > > > > I'll suggest a change below.
    > > > > FROM:
    > > > > "adding ALL TABLES requires the publication to have default publication options, no tables/schemas associated and ALL TABLES flag should not be set"
    > > > > TO:
    > > > > "adding ALL TABLES requires the publication defined not for ALL TABLES"
    > > > > "to have default publish actions without any associated tables/schemas"
    > > >
    > > > Added errdetail and split it
    > > >
    > > > > (5) typo
    > > > >
    > > > >    <varlistentry>
    > > > > +    <term><literal>EXCEPT TABLE</literal></term>
    > > > > +    <listitem>
    > > > > +     <para>
    > > > > +      This clause specifies a list of tables to exclude from the publication.
    > > > > +      It can only be used with <literal>FOR ALL TABLES</literal>.
    > > > > +     </para>
    > > > > +    </listitem>
    > > > > +   </varlistentry>
    > > > > +
    > > > >
    > > > > Kindly change
    > > > > FROM:
    > > > > This clause specifies a list of tables to exclude from the publication.
    > > > > TO:
    > > > > This clause specifies a list of tables to be excluded from the publication.
    > > > > or
    > > > > This clause specifies a list of tables excluded from the publication.
    > > >
    > > > Modified
    > > >
    > > > > (6) Minor suggestion for an expression change
    > > > >
    > > > >        Marks the publication as one that replicates changes for all tables in
    > > > > -      the database, including tables created in the future.
    > > > > +      the database, including tables created in the future. If
    > > > > +      <literal>EXCEPT TABLE</literal> is specified, then exclude replicating
    > > > > +      the changes for the specified tables.
    > > > >
    > > > >
    > > > > I'll suggest a minor rewording.
    > > > > FROM:
    > > > > ...exclude replicating the changes for the specified tables
    > > > > TO:
    > > > > ...exclude replication changes for the specified tables
    > > >
    > > > I felt the existing is better.
    > > >
    > > > > (7)
    > > > > (7-1)
    > > > >
    > > > > +/*
    > > > > + * Check if the publication has default values
    > > > > + *
    > > > > + * Check the following:
    > > > > + * a) Publication is not set with "FOR ALL TABLES"
    > > > > + * b) Publication is having default options
    > > > > + * c) Publication is not associated with schemas
    > > > > + * d) Publication is not associated with relations
    > > > > + */
    > > > > +static bool
    > > > > +CheckPublicationDefValues(HeapTuple tup)
    > > > >
    > > > >
    > > > > I think this header comment can be improved.
    > > > > FROM:
    > > > > Check the following:
    > > > > TO:
    > > > > Returns true if the publication satisfies all the following conditions:
    > > >
    > > > Modified
    > > >
    > > > > (7-2)
    > > > >
    > > > > b) should be changed as well
    > > > > FROM:
    > > > > Publication is having default options
    > > > > TO:
    > > > > Publication has the default publish operations
    > > >
    > > > Changed it to "Publication is having default publication parameter values"
    > > >
    > > > Thanks for the comments, the attached v8 patch has the changes for the same.
    > >
    > > The patch needed to be rebased on top of HEAD because of commit
    > > "0c20dd33db1607d6a85ffce24238c1e55e384b49", attached a rebased v8
    > > version for the changes of the same.
    >
    > I had missed attaching one of the changes that was present locally.
    > The updated patch has the changes for the same.
    
    The patch needed to be rebased on top of HEAD because of a recent
    commit. The updated v8 patch has the changes for the same.
    
    Regards,
    Vignesh
    
  69. Re: Skipping schema changes in publication

    Ian Lawrence Barwick <barwick@gmail.com> — 2022-11-04T02:49:46Z

    2022年8月19日(金) 2:41 vignesh C <vignesh21@gmail.com>:
    >
    > On Mon, Aug 8, 2022 at 2:53 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Mon, Aug 8, 2022 at 12:46 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > On Fri, Jun 3, 2022 at 3:36 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > >
    > > > > On Thu, May 26, 2022 at 7:04 PM osumi.takamichi@fujitsu.com
    > > > > <osumi.takamichi@fujitsu.com> wrote:
    > > > > >
    > > > > > On Monday, May 23, 2022 2:13 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > > > > Attached v7 patch which fixes the buildfarm warning for an unused warning in
    > > > > > > release mode as in  [1].
    > > > > > Hi, thank you for the patches.
    > > > > >
    > > > > >
    > > > > > I'll share several review comments.
    > > > > >
    > > > > > For v7-0001.
    > > > > >
    > > > > > (1) I'll suggest some minor rewording.
    > > > > >
    > > > > > +  <para>
    > > > > > +   The <literal>RESET</literal> clause will reset the publication to the
    > > > > > +   default state which includes resetting the publication options, setting
    > > > > > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > > > > > +   dropping all relations and schemas that are associated with the publication.
    > > > > >
    > > > > > My suggestion is
    > > > > > "The RESET clause will reset the publication to the
    > > > > > default state. It resets the publication operations,
    > > > > > sets ALL TABLES flag to false and drops all relations
    > > > > > and schemas associated with the publication."
    > > > >
    > > > > I felt the existing looks better. I would prefer to keep it that way.
    > > > >
    > > > > > (2) typo and rewording
    > > > > >
    > > > > > +/*
    > > > > > + * Reset the publication.
    > > > > > + *
    > > > > > + * Reset the publication options, setting ALL TABLES flag to false and drop
    > > > > > + * all relations and schemas that are associated with the publication.
    > > > > > + */
    > > > > >
    > > > > > The "setting" in this sentence should be "set".
    > > > > >
    > > > > > How about changing like below ?
    > > > > > FROM:
    > > > > > "Reset the publication options, setting ALL TABLES flag to false and drop
    > > > > > all relations and schemas that are associated with the publication."
    > > > > > TO:
    > > > > > "Reset the publication operations, set ALL TABLES flag to false and drop
    > > > > > all relations and schemas associated with the publication."
    > > > >
    > > > >  I felt the existing looks better. I would prefer to keep it that way.
    > > > >
    > > > > > (3) AlterPublicationReset
    > > > > >
    > > > > > Do we need to call CacheInvalidateRelcacheAll() or
    > > > > > InvalidatePublicationRels() at the end of
    > > > > > AlterPublicationReset() like AlterPublicationOptions() ?
    > > > >
    > > > > CacheInvalidateRelcacheAll should be called if we change all tables
    > > > > from true to false, else the cache will not be invalidated. Modified
    > > > >
    > > > > >
    > > > > > For v7-0002.
    > > > > >
    > > > > > (4)
    > > > > >
    > > > > > +       if (stmt->for_all_tables)
    > > > > > +       {
    > > > > > +               bool            isdefault = CheckPublicationDefValues(tup);
    > > > > > +
    > > > > > +               if (!isdefault)
    > > > > > +                       ereport(ERROR,
    > > > > > +                                       errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > > > > > +                                       errmsg("adding ALL TABLES requires the publication to have default publication options, no tables/....
    > > > > > +                                       errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    > > > > >
    > > > > >
    > > > > > The errmsg string has three messages for user and is a bit long
    > > > > > (we have two sentences there connected by 'and').
    > > > > > Can't we make it concise and split it into a couple of lines for code readability ?
    > > > > >
    > > > > > I'll suggest a change below.
    > > > > > FROM:
    > > > > > "adding ALL TABLES requires the publication to have default publication options, no tables/schemas associated and ALL TABLES flag should not be set"
    > > > > > TO:
    > > > > > "adding ALL TABLES requires the publication defined not for ALL TABLES"
    > > > > > "to have default publish actions without any associated tables/schemas"
    > > > >
    > > > > Added errdetail and split it
    > > > >
    > > > > > (5) typo
    > > > > >
    > > > > >    <varlistentry>
    > > > > > +    <term><literal>EXCEPT TABLE</literal></term>
    > > > > > +    <listitem>
    > > > > > +     <para>
    > > > > > +      This clause specifies a list of tables to exclude from the publication.
    > > > > > +      It can only be used with <literal>FOR ALL TABLES</literal>.
    > > > > > +     </para>
    > > > > > +    </listitem>
    > > > > > +   </varlistentry>
    > > > > > +
    > > > > >
    > > > > > Kindly change
    > > > > > FROM:
    > > > > > This clause specifies a list of tables to exclude from the publication.
    > > > > > TO:
    > > > > > This clause specifies a list of tables to be excluded from the publication.
    > > > > > or
    > > > > > This clause specifies a list of tables excluded from the publication.
    > > > >
    > > > > Modified
    > > > >
    > > > > > (6) Minor suggestion for an expression change
    > > > > >
    > > > > >        Marks the publication as one that replicates changes for all tables in
    > > > > > -      the database, including tables created in the future.
    > > > > > +      the database, including tables created in the future. If
    > > > > > +      <literal>EXCEPT TABLE</literal> is specified, then exclude replicating
    > > > > > +      the changes for the specified tables.
    > > > > >
    > > > > >
    > > > > > I'll suggest a minor rewording.
    > > > > > FROM:
    > > > > > ...exclude replicating the changes for the specified tables
    > > > > > TO:
    > > > > > ...exclude replication changes for the specified tables
    > > > >
    > > > > I felt the existing is better.
    > > > >
    > > > > > (7)
    > > > > > (7-1)
    > > > > >
    > > > > > +/*
    > > > > > + * Check if the publication has default values
    > > > > > + *
    > > > > > + * Check the following:
    > > > > > + * a) Publication is not set with "FOR ALL TABLES"
    > > > > > + * b) Publication is having default options
    > > > > > + * c) Publication is not associated with schemas
    > > > > > + * d) Publication is not associated with relations
    > > > > > + */
    > > > > > +static bool
    > > > > > +CheckPublicationDefValues(HeapTuple tup)
    > > > > >
    > > > > >
    > > > > > I think this header comment can be improved.
    > > > > > FROM:
    > > > > > Check the following:
    > > > > > TO:
    > > > > > Returns true if the publication satisfies all the following conditions:
    > > > >
    > > > > Modified
    > > > >
    > > > > > (7-2)
    > > > > >
    > > > > > b) should be changed as well
    > > > > > FROM:
    > > > > > Publication is having default options
    > > > > > TO:
    > > > > > Publication has the default publish operations
    > > > >
    > > > > Changed it to "Publication is having default publication parameter values"
    > > > >
    > > > > Thanks for the comments, the attached v8 patch has the changes for the same.
    > > >
    > > > The patch needed to be rebased on top of HEAD because of commit
    > > > "0c20dd33db1607d6a85ffce24238c1e55e384b49", attached a rebased v8
    > > > version for the changes of the same.
    > >
    > > I had missed attaching one of the changes that was present locally.
    > > The updated patch has the changes for the same.
    >
    > The patch needed to be rebased on top of HEAD because of a recent
    > commit. The updated v8 patch has the changes for the same.
    
    Hi
    
    cfbot reports the patch no longer applies [1]. As CommitFest 2022-11 is
    currently underway, this would be an excellent time to update the patch.
    
    [1] http://cfbot.cputube.org/patch_40_3646.log
    
    Thanks
    
    Ian Barwick
    
    
    
    
  70. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-11-07T13:39:41Z

    On Fri, 4 Nov 2022 at 08:19, Ian Lawrence Barwick <barwick@gmail.com> wrote:
    >
    > Hi
    >
    > cfbot reports the patch no longer applies [1]. As CommitFest 2022-11 is
    > currently underway, this would be an excellent time to update the patch.
    >
    > [1] http://cfbot.cputube.org/patch_40_3646.log
    
    Here is an updated patch which is rebased on top of HEAD.
    
    Regards,
    Vignesh
    
  71. Re: Skipping schema changes in publication

    Ian Lawrence Barwick <barwick@gmail.com> — 2022-11-16T04:04:18Z

    2022年11月7日(月) 22:39 vignesh C <vignesh21@gmail.com>:
    >
    > On Fri, 4 Nov 2022 at 08:19, Ian Lawrence Barwick <barwick@gmail.com> wrote:
    > >
    > > Hi
    > >
    > > cfbot reports the patch no longer applies [1]. As CommitFest 2022-11 is
    > > currently underway, this would be an excellent time to update the patch.
    > >
    > > [1] http://cfbot.cputube.org/patch_40_3646.log
    >
    > Here is an updated patch which is rebased on top of HEAD.
    
    Thanks for the updated patch.
    
    While reviewing the patch backlog, we have determined that this patch adds
    one or more TAP tests but has not added the test to the "meson.build" file.
    
    To do this, locate the relevant "meson.build" file for each test and add it
    in the 'tests' dictionary, which will look something like this:
    
      'tap': {
        'tests': [
          't/001_basic.pl',
        ],
      },
    
    For some additional details please see this Wiki article:
    
      https://wiki.postgresql.org/wiki/Meson_for_patch_authors
    
    For more information on the meson build system for PostgreSQL see:
    
      https://wiki.postgresql.org/wiki/Meson
    
    
    Regards
    
    Ian Barwick
    
    
    
    
  72. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2022-11-16T10:05:31Z

    On Wed, 16 Nov 2022 at 09:34, Ian Lawrence Barwick <barwick@gmail.com> wrote:
    >
    > 2022年11月7日(月) 22:39 vignesh C <vignesh21@gmail.com>:
    > >
    > > On Fri, 4 Nov 2022 at 08:19, Ian Lawrence Barwick <barwick@gmail.com> wrote:
    > > >
    > > > Hi
    > > >
    > > > cfbot reports the patch no longer applies [1]. As CommitFest 2022-11 is
    > > > currently underway, this would be an excellent time to update the patch.
    > > >
    > > > [1] http://cfbot.cputube.org/patch_40_3646.log
    > >
    > > Here is an updated patch which is rebased on top of HEAD.
    >
    > Thanks for the updated patch.
    >
    > While reviewing the patch backlog, we have determined that this patch adds
    > one or more TAP tests but has not added the test to the "meson.build" file.
    
    Thanks, I have updated the meson.build to include the TAP test. The
    attached patch has the changes for the same.
    
    Regards,
    Vignesh
    
  73. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2023-01-20T10:00:54Z

    On Wed, 16 Nov 2022 at 15:35, vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Wed, 16 Nov 2022 at 09:34, Ian Lawrence Barwick <barwick@gmail.com> wrote:
    > >
    > > 2022年11月7日(月) 22:39 vignesh C <vignesh21@gmail.com>:
    > > >
    > > > On Fri, 4 Nov 2022 at 08:19, Ian Lawrence Barwick <barwick@gmail.com> wrote:
    > > > >
    > > > > Hi
    > > > >
    > > > > cfbot reports the patch no longer applies [1]. As CommitFest 2022-11 is
    > > > > currently underway, this would be an excellent time to update the patch.
    > > > >
    > > > > [1] http://cfbot.cputube.org/patch_40_3646.log
    > > >
    > > > Here is an updated patch which is rebased on top of HEAD.
    > >
    > > Thanks for the updated patch.
    > >
    > > While reviewing the patch backlog, we have determined that this patch adds
    > > one or more TAP tests but has not added the test to the "meson.build" file.
    >
    > Thanks, I have updated the meson.build to include the TAP test. The
    > attached patch has the changes for the same.
    
    The patch was not applying on top of HEAD, attached a rebased version.
    
    Regards,
    Vignesh
    
  74. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2024-01-09T06:32:04Z

    On Fri, 20 Jan 2023 at 15:30, vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Wed, 16 Nov 2022 at 15:35, vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Wed, 16 Nov 2022 at 09:34, Ian Lawrence Barwick <barwick@gmail.com> wrote:
    > > >
    > > > 2022年11月7日(月) 22:39 vignesh C <vignesh21@gmail.com>:
    > > > >
    > > > > On Fri, 4 Nov 2022 at 08:19, Ian Lawrence Barwick <barwick@gmail.com> wrote:
    > > > > >
    > > > > > Hi
    > > > > >
    > > > > > cfbot reports the patch no longer applies [1]. As CommitFest 2022-11 is
    > > > > > currently underway, this would be an excellent time to update the patch.
    > > > > >
    > > > > > [1] http://cfbot.cputube.org/patch_40_3646.log
    > > > >
    > > > > Here is an updated patch which is rebased on top of HEAD.
    > > >
    > > > Thanks for the updated patch.
    > > >
    > > > While reviewing the patch backlog, we have determined that this patch adds
    > > > one or more TAP tests but has not added the test to the "meson.build" file.
    > >
    > > Thanks, I have updated the meson.build to include the TAP test. The
    > > attached patch has the changes for the same.
    >
    > The patch was not applying on top of HEAD, attached a rebased version.
    
    As I did not see much interest from others, I'm withdrawing this patch
    for now. But if there is any interest others in future, I would be
    more than happy to work on this feature.
    
    Regards,
    Vignesh
    
    
    
    
  75. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2025-04-10T11:24:57Z

    On Tue, Jan 9, 2024 at 12:02 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > As I did not see much interest from others, I'm withdrawing this patch
    > for now. But if there is any interest others in future, I would be
    > more than happy to work on this feature.
    >
    
    Just FYI, I noticed a use case for this patch in email [1]. Users
    would like to replicate all except a few columns having sensitive
    information. The challenge with current column list features is that
    adding new tables to columns would lead users to change the respective
    publications as well.
    
    [1] - https://www.postgresql.org/message-id/tencent_DCDF626FCD4A556C51BE270FDC3047540208%40qq.com
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  76. RE: Skipping schema changes in publication

    Zhijie Hou (Fujitsu) <houzj.fnst@fujitsu.com> — 2025-04-16T02:52:29Z

    On Thu, Apr 10, 2025 at 7:25 PM Amit Kapila wrote:
    > 
    > On Tue, Jan 9, 2024 at 12:02 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > As I did not see much interest from others, I'm withdrawing this patch
    > > for now. But if there is any interest others in future, I would be
    > > more than happy to work on this feature.
    > >
    > 
    > Just FYI, I noticed a use case for this patch in email [1]. Users would like to
    > replicate all except a few columns having sensitive information. The challenge
    > with current column list features is that adding new tables to columns would
    > lead users to change the respective publications as well.
    > 
    > [1] -
    > https://www.postgresql.org/message-id/tencent_DCDF626FCD4A556C51BE
    > 270FDC3047540208%40qq.com
    
    BTW, I noticed that debezium, an open source distributed platform for change
    data capture that replies on logical decoding, also support specifying the
    column exclusion list[1]. So, this indicates that there could be some use cases
    for this feature.
    
    https://debezium.io/documentation/reference/stable/connectors/postgresql.html#postgresql-property-column-exclude-list
    
    Best Regards,
    Hou zj
    
  77. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2025-04-17T03:42:22Z

    On Wed, Apr 16, 2025 at 8:22 AM Zhijie Hou (Fujitsu)
    <houzj.fnst@fujitsu.com> wrote:
    >
    > On Thu, Apr 10, 2025 at 7:25 PM Amit Kapila wrote:
    > >
    > > On Tue, Jan 9, 2024 at 12:02 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > As I did not see much interest from others, I'm withdrawing this patch
    > > > for now. But if there is any interest others in future, I would be
    > > > more than happy to work on this feature.
    > > >
    > >
    > > Just FYI, I noticed a use case for this patch in email [1]. Users would like to
    > > replicate all except a few columns having sensitive information. The challenge
    > > with current column list features is that adding new tables to columns would
    > > lead users to change the respective publications as well.
    > >
    > > [1] -
    > > https://www.postgresql.org/message-id/tencent_DCDF626FCD4A556C51BE
    > > 270FDC3047540208%40qq.com
    >
    > BTW, I noticed that debezium, an open source distributed platform for change
    > data capture that replies on logical decoding, also support specifying the
    > column exclusion list[1]. So, this indicates that there could be some use cases
    > for this feature.
    >
    
    Thanks for sharing the link. I see that they support both the include
    and exclude lists for columns and tables.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  78. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-06-11T14:07:49Z

    On Thu, 17 Apr 2025 at 09:12, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, Apr 16, 2025 at 8:22 AM Zhijie Hou (Fujitsu)
    > <houzj.fnst@fujitsu.com> wrote:
    > >
    > > On Thu, Apr 10, 2025 at 7:25 PM Amit Kapila wrote:
    > > >
    > > > On Tue, Jan 9, 2024 at 12:02 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > >
    > > > > As I did not see much interest from others, I'm withdrawing this patch
    > > > > for now. But if there is any interest others in future, I would be
    > > > > more than happy to work on this feature.
    > > > >
    > > >
    > > > Just FYI, I noticed a use case for this patch in email [1]. Users would like to
    > > > replicate all except a few columns having sensitive information. The challenge
    > > > with current column list features is that adding new tables to columns would
    > > > lead users to change the respective publications as well.
    > > >
    > > > [1] -
    > > > https://www.postgresql.org/message-id/tencent_DCDF626FCD4A556C51BE
    > > > 270FDC3047540208%40qq.com
    > >
    > > BTW, I noticed that debezium, an open source distributed platform for change
    > > data capture that replies on logical decoding, also support specifying the
    > > column exclusion list[1]. So, this indicates that there could be some use cases
    > > for this feature.
    > >
    >
    > Thanks for sharing the link. I see that they support both the include
    > and exclude lists for columns and tables.
    >
    
    Hi Hackers,
    
    I see there is some interest in the functionality added by this patch.
    I have rebased the patches in [1]. I saw a new column 'pubgencols' was
    added in pg_publication in PG 18. So, I have modified v11-0001 to
    RESET this as well.
    I am also working on creating a patch to exclude columns in
    publication as per suggestion in [2].
    
    [1]: https://www.postgresql.org/message-id/CALDaNm3dWZCYDih55qTNAYsjCvYXMFv%3D46UsDWmfCnXMt3kPCg%40mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAA4eK1KRdAPC%3D5%3D7tQ1GW0cRwD%3DzaDMi%2BT4u_k4GxPhPY6e8BQ%40mail.gmail.com
    
    Thanks and Regards,
    Shlok Kyal
    
  79. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-06-17T07:41:34Z

    On Wed, 11 Jun 2025 at 19:37, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Thu, 17 Apr 2025 at 09:12, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Wed, Apr 16, 2025 at 8:22 AM Zhijie Hou (Fujitsu)
    > > <houzj.fnst@fujitsu.com> wrote:
    > > >
    > > > On Thu, Apr 10, 2025 at 7:25 PM Amit Kapila wrote:
    > > > >
    > > > > On Tue, Jan 9, 2024 at 12:02 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > > >
    > > > > > As I did not see much interest from others, I'm withdrawing this patch
    > > > > > for now. But if there is any interest others in future, I would be
    > > > > > more than happy to work on this feature.
    > > > > >
    > > > >
    > > > > Just FYI, I noticed a use case for this patch in email [1]. Users would like to
    > > > > replicate all except a few columns having sensitive information. The challenge
    > > > > with current column list features is that adding new tables to columns would
    > > > > lead users to change the respective publications as well.
    > > > >
    > > > > [1] -
    > > > > https://www.postgresql.org/message-id/tencent_DCDF626FCD4A556C51BE
    > > > > 270FDC3047540208%40qq.com
    > > >
    > > > BTW, I noticed that debezium, an open source distributed platform for change
    > > > data capture that replies on logical decoding, also support specifying the
    > > > column exclusion list[1]. So, this indicates that there could be some use cases
    > > > for this feature.
    > > >
    > >
    > > Thanks for sharing the link. I see that they support both the include
    > > and exclude lists for columns and tables.
    > >
    >
    > Hi Hackers,
    >
    > I see there is some interest in the functionality added by this patch.
    > I have rebased the patches in [1]. I saw a new column 'pubgencols' was
    > added in pg_publication in PG 18. So, I have modified v11-0001 to
    > RESET this as well.
    > I am also working on creating a patch to exclude columns in
    > publication as per suggestion in [2].
    >
    > [1]: https://www.postgresql.org/message-id/CALDaNm3dWZCYDih55qTNAYsjCvYXMFv%3D46UsDWmfCnXMt3kPCg%40mail.gmail.com
    > [2]: https://www.postgresql.org/message-id/CAA4eK1KRdAPC%3D5%3D7tQ1GW0cRwD%3DzaDMi%2BT4u_k4GxPhPY6e8BQ%40mail.gmail.com
    >
    
    I have attached a patch support excluding columns for publication.
    
    I have added a syntax: "FOR TABLE table_name EXCEPT (c1, c2, ..)"
    It can be used with CREATE or ALTER PUBLICATION.
    
    v12-0003 patch contains the changes for the same.
    
    Thanks and Regards,
    Shlok Kyal
    
  80. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-06-18T01:04:31Z

    On Tue, Jun 17, 2025 at 5:41 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    ...
    > I have attached a patch support excluding columns for publication.
    >
    > I have added a syntax: "FOR TABLE table_name EXCEPT (c1, c2, ..)"
    > It can be used with CREATE or ALTER PUBLICATION.
    >
    > v12-0003 patch contains the changes for the same.
    >
    
    Hi Shlok,
    
    I was interested in your new EXCEPT (col-list) so I had a quick look
    at your patch v12-0003 (only looked at the documentation).
    
    Below are some comments:
    
    ======
    
    1. Chapter 29.5 "Column Lists".
    
    I think new EXCEPT syntax needs a mention here as well.
    
    ======
    
    doc/src/sgml/catalogs.sgml
    
    2.
    +      <para>
    +       This is an array of values that indicates which table columns are
    +       excluded from the publication.  For example, a value of
    +       <literal>1 3</literal> would mean that the columns except the first and
    +       the third columns are published.
    +       A null value indicates that no columns are excluded from being
    published.
    +      </para></entry>
    
    The sentence "A null value indicates that no columns are excluded from
    being published" seems kind of confusing, because if the user has a
    "normal" column-list  although nothing was being *explicitly* excluded
    (using EXCEPT), any columns not named are *implicitly* excluded from
    being published.
    
    ~
    
    3.
    TBH, I was wondering why a new catalog attribute was necessary...
    
    Can't you simply re-use the existing attribute "prattrs" attribute.
    e.g. let's just define negative means exclude.
    
    e.g. a value of 1 3 means only the 1st and 3rd columns are published
    e.g. a value of -1 -3 means all columns except 1st and 3rd columns are published
    e.g. a value of null mean all columns are published
    
    (mixes of negative and positive will not be possible)
    
    ======
    
    doc/src/sgml/ref/alter_publication.sgml
    
    4. ALTER PUBLICATION syntax
    
    The syntax is currently written as:
    TABLE [ ONLY ] table_name [ * ] { [ [ ( column_name [, ... ] ) ] | [
    EXCEPT ( column_name [, ... ] ) ] ] } [ WHERE ( expression ) ] [, ...
    ]
    
    Can't this be more simply written as:
    TABLE [ ONLY ] table_name [ * ] [ [ EXCEPT ] ( column_name [, ... ] )
    ] [ WHERE ( expression ) ] [, ... ]
    
    ~~~
    
    5.
    +  <para>
    +   Alter publication <structname>mypublication</structname> to add table
    +   <structname>users</structname> except column
    +   <structname>security_pin</structname>:
    +<programlisting>
    +ALTER PUBLICATION production_publication ADD TABLE users EXCEPT (security_pin);
    
    Those tags don't seem correct. e.g. "users" and "security_pin" are not
    <structname> (???).
    
    Perhaps, every other example here is wrong too and you just copied
    them? Anyway, something here looks wrong to me.
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    6. CREATE PUBLICATION syntax
    
    The syntax is currently written as:
    TABLE [ ONLY ] table_name [ * ] { [ [ ( column_name [, ... ] ) ] | [
    EXCEPT ( column_name [, ... ] ) ] ] } [ WHERE ( expression ) ] [, ...
    ]
    
    Can't this be more simply written as:
    TABLE [ ONLY ] table_name [ * ] [ [ EXCEPT ] ( column_name [, ... ] )
    ] [ WHERE ( expression ) ] [, ... ]
    
    ~~~
    
    7.
    +     <para>
    +      When a column list is specified with EXCEPT, the named columns are not
    +      replicated. The excluded column list cannot contain generated
    columns. The
    +      column list and excluded column list cannot be specified together.
    +      Specifying a column list has no effect on <literal>TRUNCATE</literal>
    +      commands.
    +     </para>
    
    IMO you don't need to say "The column list and excluded column list
    cannot be specified together." because AFAIK the syntax makes that
    impossible to do anyhow.
    
    ~~~
    
    8.
    +  <para>
    +   Create a publication that publishes all changes for table
    <structname>users</structname>
    +   except changes for columns <structname>security_pin</structname>:
    +<programlisting>
    +CREATE PUBLICATION users_safe FOR TABLE users EXCEPT (security_pin);
    +</programlisting>
    +  </para>
    
    8a.
    Same review comment as previously -- Those tags don't seem correct.
    e.g. "users" and "security_pin" are not <structname> (???).
    Again, are all the other existing tags also wrong? Maybe a new thread
    needed to address these?
    
    ~
    
    8b.
    Plural?  /except changes for columns/except changes for column/
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  81. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-06-19T06:41:48Z

    On Wed, 18 Jun 2025 at 06:34, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Tue, Jun 17, 2025 at 5:41 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > ...
    > > I have attached a patch support excluding columns for publication.
    > >
    > > I have added a syntax: "FOR TABLE table_name EXCEPT (c1, c2, ..)"
    > > It can be used with CREATE or ALTER PUBLICATION.
    > >
    > > v12-0003 patch contains the changes for the same.
    > >
    >
    > Hi Shlok,
    >
    > I was interested in your new EXCEPT (col-list) so I had a quick look
    > at your patch v12-0003 (only looked at the documentation).
    >
    > Below are some comments:
    >
    > ======
    >
    > 1. Chapter 29.5 "Column Lists".
    >
    > I think new EXCEPT syntax needs a mention here as well.
    >
    Added
    
    > ======
    >
    > doc/src/sgml/catalogs.sgml
    >
    > 2.
    > +      <para>
    > +       This is an array of values that indicates which table columns are
    > +       excluded from the publication.  For example, a value of
    > +       <literal>1 3</literal> would mean that the columns except the first and
    > +       the third columns are published.
    > +       A null value indicates that no columns are excluded from being
    > published.
    > +      </para></entry>
    >
    > The sentence "A null value indicates that no columns are excluded from
    > being published" seems kind of confusing, because if the user has a
    > "normal" column-list  although nothing was being *explicitly* excluded
    > (using EXCEPT), any columns not named are *implicitly* excluded from
    > being published.
    >
    I have removed this line.
    
    > ~
    >
    > 3.
    > TBH, I was wondering why a new catalog attribute was necessary...
    >
    > Can't you simply re-use the existing attribute "prattrs" attribute.
    > e.g. let's just define negative means exclude.
    >
    > e.g. a value of 1 3 means only the 1st and 3rd columns are published
    > e.g. a value of -1 -3 means all columns except 1st and 3rd columns are published
    > e.g. a value of null mean all columns are published
    >
    > (mixes of negative and positive will not be possible)
    >
    
    Currently I have added a new attribute 'prexcludeattrs' in
    pg_publication_rel table. I used this approach because it will be
    easier for user to get the exclude column list, in code no extra
    processing is required to get the exclude column list.
    
    For an approach to use negative numbers for exclude columns. I see an
    advantage that we do not need to introduce a new column for
    pg_publication_rel. But in code, each time we want to get a column
    list or exclude column list we need an extra processing of 'prattrs'
    columns. Also I don't see any existing catalog table using a negative
    attribute for column list.
    
    Based on above observations, I feel that the current is better.
    
    Please correct me if I missed an advantage for the approach you suggested.
    
    > ======
    >
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 4. ALTER PUBLICATION syntax
    >
    > The syntax is currently written as:
    > TABLE [ ONLY ] table_name [ * ] { [ [ ( column_name [, ... ] ) ] | [
    > EXCEPT ( column_name [, ... ] ) ] ] } [ WHERE ( expression ) ] [, ...
    > ]
    >
    > Can't this be more simply written as:
    > TABLE [ ONLY ] table_name [ * ] [ [ EXCEPT ] ( column_name [, ... ] )
    > ] [ WHERE ( expression ) ] [, ... ]
    >
    > ~~~
    Fixed
    
    >
    > 5.
    > +  <para>
    > +   Alter publication <structname>mypublication</structname> to add table
    > +   <structname>users</structname> except column
    > +   <structname>security_pin</structname>:
    > +<programlisting>
    > +ALTER PUBLICATION production_publication ADD TABLE users EXCEPT (security_pin);
    >
    > Those tags don't seem correct. e.g. "users" and "security_pin" are not
    > <structname> (???).
    >
    > Perhaps, every other example here is wrong too and you just copied
    > them? Anyway, something here looks wrong to me.
    >
    I saw different documents and usage of tags seems not well defined.
    For example for table we are using tags in document
    create_publication.sgml, update.sgml <structname> is used, in document
    table.sgml, advanced.sgml <classname> is used, and in
    logical-replication.sgml <literal>  is used. Similarly for column
    names <structname>, <structfield> or <literal> are used in different
    parts of the document.
    
    I kept the changed tag to <structfield> for the column for this patch.
    Do you have any suggestions?
    
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 6. CREATE PUBLICATION syntax
    >
    > The syntax is currently written as:
    > TABLE [ ONLY ] table_name [ * ] { [ [ ( column_name [, ... ] ) ] | [
    > EXCEPT ( column_name [, ... ] ) ] ] } [ WHERE ( expression ) ] [, ...
    > ]
    >
    > Can't this be more simply written as:
    > TABLE [ ONLY ] table_name [ * ] [ [ EXCEPT ] ( column_name [, ... ] )
    > ] [ WHERE ( expression ) ] [, ... ]
    >
    > ~~~
    Fixed
    
    >
    > 7.
    > +     <para>
    > +      When a column list is specified with EXCEPT, the named columns are not
    > +      replicated. The excluded column list cannot contain generated
    > columns. The
    > +      column list and excluded column list cannot be specified together.
    > +      Specifying a column list has no effect on <literal>TRUNCATE</literal>
    > +      commands.
    > +     </para>
    >
    > IMO you don't need to say "The column list and excluded column list
    > cannot be specified together." because AFAIK the syntax makes that
    > impossible to do anyhow.
    >
    Removed this line
    
    > ~~~
    >
    > 8.
    > +  <para>
    > +   Create a publication that publishes all changes for table
    > <structname>users</structname>
    > +   except changes for columns <structname>security_pin</structname>:
    > +<programlisting>
    > +CREATE PUBLICATION users_safe FOR TABLE users EXCEPT (security_pin);
    > +</programlisting>
    > +  </para>
    >
    > 8a.
    > Same review comment as previously -- Those tags don't seem correct.
    > e.g. "users" and "security_pin" are not <structname> (???).
    > Again, are all the other existing tags also wrong? Maybe a new thread
    > needed to address these?
    >
    > ~
    Same as point 5.
    I also feel this should be addressed in a new thread.
    
    > 8b.
    > Plural?  /except changes for columns/except changes for column/
    Fixed
    
    Also in this patch I added displaying "EXCEPT (column_list)" for \dRp+
    and \d table_name psql commands.
    
    Thanks and Regards,
    Shlok Kyal
    
  82. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-06-20T03:57:39Z

    On Thu, Jun 19, 2025 at 4:42 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    ...
    > > 3.
    > > TBH, I was wondering why a new catalog attribute was necessary...
    > >
    > > Can't you simply re-use the existing attribute "prattrs" attribute.
    > > e.g. let's just define negative means exclude.
    > >
    > > e.g. a value of 1 3 means only the 1st and 3rd columns are published
    > > e.g. a value of -1 -3 means all columns except 1st and 3rd columns are published
    > > e.g. a value of null mean all columns are published
    > >
    > > (mixes of negative and positive will not be possible)
    > >
    >
    > Currently I have added a new attribute 'prexcludeattrs' in
    > pg_publication_rel table. I used this approach because it will be
    > easier for user to get the exclude column list, in code no extra
    > processing is required to get the exclude column list.
    >
    > For an approach to use negative numbers for exclude columns. I see an
    > advantage that we do not need to introduce a new column for
    > pg_publication_rel. But in code, each time we want to get a column
    > list or exclude column list we need an extra processing of 'prattrs'
    > columns. Also I don't see any existing catalog table using a negative
    > attribute for column list.
    >
    > Based on above observations, I feel that the current is better.
    >
    > Please correct me if I missed an advantage for the approach you suggested.
    >
    
    OK. Maybe using negative numbers was a bridge too far...
    
    But IMO it is not good to have 2 separate attributes for the lists.
    Doing so implies they can coexist, but that is not true. I felt there
    are not really 2 "kinds" of columns list anyway -- there is just a
    "column list" which defines columns that are either included or
    excluded from the publication determined by EXCEPT.
    
    Having  dual lists gets weird/confusing to describe them -- you end up
    continually having to refer to the other one to clarify behaviour.
    
    e.g. Does 'prattrs' value NULL mean publish everything? Well, no...
    that depends if there is a non null 'prexcludeattrs'
    e.g. Does 'prexcludeattrs' value NULL mean publish everything? Well,
    no... that depends if there is a non null 'prattrs'
    
    Furthermore, all the code is doubling up referring to "column list"
    and "exclude column list"  -- code / docs / comments / error messages.
    There are quite a lot of places the patch touches that I thought were
    not really needed if you don't have 2 different kinds of column-lists.
    
    To summarise, I felt it would be better to just keep the existing
    'prattrs' as the one-and-only column list, but add another BOOLEAN
    attribute to flag whether 'prattrs' columns should be included or
    excluded.
    
    prattrs;   prattrs_exclude;  Means
    --------------------------------------------
    1 2 3     f                          only cols 1,2,3 will be published
    4 5 6     t                          only cols 4,5,6 will NOT be published
    null       f                          all cols are published (flag is ignored)
    null       t                          all cols are published (flag is ignored)
    
    > > 5.
    > > +  <para>
    > > +   Alter publication <structname>mypublication</structname> to add table
    > > +   <structname>users</structname> except column
    > > +   <structname>security_pin</structname>:
    > > +<programlisting>
    > > +ALTER PUBLICATION production_publication ADD TABLE users EXCEPT (security_pin);
    > >
    > > Those tags don't seem correct. e.g. "users" and "security_pin" are not
    > > <structname> (???).
    > >
    > > Perhaps, every other example here is wrong too and you just copied
    > > them? Anyway, something here looks wrong to me.
    > >
    > I saw different documents and usage of tags seems not well defined.
    > For example for table we are using tags in document
    > create_publication.sgml, update.sgml <structname> is used, in document
    > table.sgml, advanced.sgml <classname> is used, and in
    > logical-replication.sgml <literal>  is used. Similarly for column
    > names <structname>, <structfield> or <literal> are used in different
    > parts of the document.
    >
    > I kept the changed tag to <structfield> for the column for this patch.
    > Do you have any suggestions?
    
    No, for this patch I think it is best that you just follow nearby code
    (as you are already doing). I plan to raise another thread to ask what
    are the guidelines for this  sort of markup which is currently used
    inconsistently in different places.
    
    //////////
    
    Below are a few more review comments for v13-0003
    
    ======
    Commit message
    
    1.
    Typo /THe/The/
    
    ~~~
    
    2.
    The new syntax allows specifying excluded column list when creating or
    altering a publication. For example:
    CREATE PUBLICATION pubname FOR TABLE tabname EXCEPT (exclude_column_list)
    or
    ALTER PUBLICATION pubname ADD TABLE tabname EXCEPT (exclude_column_list)
    
    ~
    
    I felt since you say these "For example:" it would be better to give
    real examples.
    e.g. say "(col1,col2,col3)" instead of "(exclude_column_list)".
    
    ~~~
    
    3.
    Typo /family of command/family of commands/
    
    ======
    doc/src/sgml/logical-replication.sgml
    
    4.
    I am not sure that it was a good idea to be making a new term called
    an "exclude column list"... because in introduces a new concept of
    something that sounds like it is a different kind of list, and now you
    have to keep referring everywhere to both to "column list" versus
    "exclude column list". All the doubling up add more complication I
    think.
    
    IMO really there is just a "column list". Whether that list is for
    exclusion or not just depends on the presence of EXCEPT. So I felt
    maybe all places mentioning "exclude column list" could be rephrased.
    
    ======
    src/backend/catalog/pg_publication.c
    
    5.
    +/*
    + * Returns true if the relation has exluded column list associated with the
    + * publication, false otherwise.
    + *
    + * If a exclude column list is found, the corresponding bitmap is returned
    + * through the cols parameter, if provided. The bitmap is constructed
    within the
    + * given memory context (mcxt).
    + */
    +
    
    Typo /exluded column/an excluded column/
    Typo /exclude column list/excluded column list/
    
    ~~~
    
    6.
    +/*
    + * pub_exclude_collist_validate
    + * Process and validate the 'excluded columns' list and ensure the columns
    + * are all valid to exclude from publication.  Checks for and raises an
    + * ERROR for any unknown columns, system columns, duplicate columns, or
    + * generated columns.
    + *
    
    Why can't you exclude generated columns?
    
    e.g. Maybe PUBLICATION says publish_generated_columns=stored and there
    are 100s of such columns, but the user just wants to exclude one of
    them. Why say they cannot do that? Hmm. Perhaps this is being already
    handled elsewhere, in which case this comment still seems misleading.
    
    ======
    src/backend/commands/publicationcmds.c
    
    7.
    + * With REPLICA IDENTITY FULL, no column list and no excluded column
    + * list is allowed.
    
    Really, just "no column list is allowed." same as it said before.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  83. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-06-23T00:54:10Z

    > > > 5.
    > > > +  <para>
    > > > +   Alter publication <structname>mypublication</structname> to add table
    > > > +   <structname>users</structname> except column
    > > > +   <structname>security_pin</structname>:
    > > > +<programlisting>
    > > > +ALTER PUBLICATION production_publication ADD TABLE users EXCEPT (security_pin);
    > > >
    > > > Those tags don't seem correct. e.g. "users" and "security_pin" are not
    > > > <structname> (???).
    > > >
    > > > Perhaps, every other example here is wrong too and you just copied
    > > > them? Anyway, something here looks wrong to me.
    > > >
    > > I saw different documents and usage of tags seems not well defined.
    > > For example for table we are using tags in document
    > > create_publication.sgml, update.sgml <structname> is used, in document
    > > table.sgml, advanced.sgml <classname> is used, and in
    > > logical-replication.sgml <literal>  is used. Similarly for column
    > > names <structname>, <structfield> or <literal> are used in different
    > > parts of the document.
    > >
    > > I kept the changed tag to <structfield> for the column for this patch.
    > > Do you have any suggestions?
    >
    > No, for this patch I think it is best that you just follow nearby code
    > (as you are already doing). I plan to raise another thread to ask what
    > are the guidelines for this  sort of markup which is currently used
    > inconsistently in different places.
    >
    
    FYI - I created a new thread asking this markup question [1].
    
    ======
    [1] https://www.postgresql.org/message-id/CAHut%2BPvtf24r%2BbdPgBind84dBLPvgNL7aB%2B%3DHxAUupdPuo2gRg%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  84. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-06-24T04:17:53Z

    On Fri, 20 Jun 2025 at 09:28, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Thu, Jun 19, 2025 at 4:42 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > ...
    > > > 3.
    > > > TBH, I was wondering why a new catalog attribute was necessary...
    > > >
    > > > Can't you simply re-use the existing attribute "prattrs" attribute.
    > > > e.g. let's just define negative means exclude.
    > > >
    > > > e.g. a value of 1 3 means only the 1st and 3rd columns are published
    > > > e.g. a value of -1 -3 means all columns except 1st and 3rd columns are published
    > > > e.g. a value of null mean all columns are published
    > > >
    > > > (mixes of negative and positive will not be possible)
    > > >
    > >
    > > Currently I have added a new attribute 'prexcludeattrs' in
    > > pg_publication_rel table. I used this approach because it will be
    > > easier for user to get the exclude column list, in code no extra
    > > processing is required to get the exclude column list.
    > >
    > > For an approach to use negative numbers for exclude columns. I see an
    > > advantage that we do not need to introduce a new column for
    > > pg_publication_rel. But in code, each time we want to get a column
    > > list or exclude column list we need an extra processing of 'prattrs'
    > > columns. Also I don't see any existing catalog table using a negative
    > > attribute for column list.
    > >
    > > Based on above observations, I feel that the current is better.
    > >
    > > Please correct me if I missed an advantage for the approach you suggested.
    > >
    >
    > OK. Maybe using negative numbers was a bridge too far...
    >
    > But IMO it is not good to have 2 separate attributes for the lists.
    > Doing so implies they can coexist, but that is not true. I felt there
    > are not really 2 "kinds" of columns list anyway -- there is just a
    > "column list" which defines columns that are either included or
    > excluded from the publication determined by EXCEPT.
    >
    > Having  dual lists gets weird/confusing to describe them -- you end up
    > continually having to refer to the other one to clarify behaviour.
    >
    > e.g. Does 'prattrs' value NULL mean publish everything? Well, no...
    > that depends if there is a non null 'prexcludeattrs'
    > e.g. Does 'prexcludeattrs' value NULL mean publish everything? Well,
    > no... that depends if there is a non null 'prattrs'
    >
    > Furthermore, all the code is doubling up referring to "column list"
    > and "exclude column list"  -- code / docs / comments / error messages.
    > There are quite a lot of places the patch touches that I thought were
    > not really needed if you don't have 2 different kinds of column-lists.
    >
    > To summarise, I felt it would be better to just keep the existing
    > 'prattrs' as the one-and-only column list, but add another BOOLEAN
    > attribute to flag whether 'prattrs' columns should be included or
    > excluded.
    >
    > prattrs;   prattrs_exclude;  Means
    > --------------------------------------------
    > 1 2 3     f                          only cols 1,2,3 will be published
    > 4 5 6     t                          only cols 4,5,6 will NOT be published
    > null       f                          all cols are published (flag is ignored)
    > null       t                          all cols are published (flag is ignored)
    >
    
    I agree with your point and also it would be a better approach. In
    patch 0001 an column 'prexcept' was added in pg_publication_rel. We
    use that only for publication with all tables. I have reused this
    column for patch 0003. If publication is not for all tables and the
    'prexcept' flag is true, it implies that the columns in 'prattrs' are
    to be excluded from being published. I have included the changes for
    it in v14-0003 patch.
    
    > > > 5.
    > > > +  <para>
    > > > +   Alter publication <structname>mypublication</structname> to add table
    > > > +   <structname>users</structname> except column
    > > > +   <structname>security_pin</structname>:
    > > > +<programlisting>
    > > > +ALTER PUBLICATION production_publication ADD TABLE users EXCEPT (security_pin);
    > > >
    > > > Those tags don't seem correct. e.g. "users" and "security_pin" are not
    > > > <structname> (???).
    > > >
    > > > Perhaps, every other example here is wrong too and you just copied
    > > > them? Anyway, something here looks wrong to me.
    > > >
    > > I saw different documents and usage of tags seems not well defined.
    > > For example for table we are using tags in document
    > > create_publication.sgml, update.sgml <structname> is used, in document
    > > table.sgml, advanced.sgml <classname> is used, and in
    > > logical-replication.sgml <literal>  is used. Similarly for column
    > > names <structname>, <structfield> or <literal> are used in different
    > > parts of the document.
    > >
    > > I kept the changed tag to <structfield> for the column for this patch.
    > > Do you have any suggestions?
    >
    > No, for this patch I think it is best that you just follow nearby code
    > (as you are already doing). I plan to raise another thread to ask what
    > are the guidelines for this  sort of markup which is currently used
    > inconsistently in different places.
    Thanks for starting a thread for it.
    
    >
    > //////////
    >
    > Below are a few more review comments for v13-0003
    >
    > ======
    > Commit message
    >
    > 1.
    > Typo /THe/The/
    >
    > ~~~
    Fixed
    
    > 2.
    > The new syntax allows specifying excluded column list when creating or
    > altering a publication. For example:
    > CREATE PUBLICATION pubname FOR TABLE tabname EXCEPT (exclude_column_list)
    > or
    > ALTER PUBLICATION pubname ADD TABLE tabname EXCEPT (exclude_column_list)
    >
    > ~
    >
    > I felt since you say these "For example:" it would be better to give
    > real examples.
    > e.g. say "(col1,col2,col3)" instead of "(exclude_column_list)".
    >
    Fixed
    
    > ~~~
    >
    > 3.
    > Typo /family of command/family of commands/
    >
    > ======
    > doc/src/sgml/logical-replication.sgml
    >
    > 4.
    > I am not sure that it was a good idea to be making a new term called
    > an "exclude column list"... because in introduces a new concept of
    > something that sounds like it is a different kind of list, and now you
    > have to keep referring everywhere to both to "column list" versus
    > "exclude column list". All the doubling up add more complication I
    > think.
    >
    > IMO really there is just a "column list". Whether that list is for
    > exclusion or not just depends on the presence of EXCEPT. So I felt
    > maybe all places mentioning "exclude column list" could be rephrased.
    >
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > 5.
    > +/*
    > + * Returns true if the relation has exluded column list associated with the
    > + * publication, false otherwise.
    > + *
    > + * If a exclude column list is found, the corresponding bitmap is returned
    > + * through the cols parameter, if provided. The bitmap is constructed
    > within the
    > + * given memory context (mcxt).
    > + */
    > +
    >
    > Typo /exluded column/an excluded column/
    > Typo /exclude column list/excluded column list/
    >
    updated the comment according to latest implementation
    
    > ~~~
    >
    > 6.
    > +/*
    > + * pub_exclude_collist_validate
    > + * Process and validate the 'excluded columns' list and ensure the columns
    > + * are all valid to exclude from publication.  Checks for and raises an
    > + * ERROR for any unknown columns, system columns, duplicate columns, or
    > + * generated columns.
    > + *
    >
    > Why can't you exclude generated columns?
    >
    > e.g. Maybe PUBLICATION says publish_generated_columns=stored and there
    > are 100s of such columns, but the user just wants to exclude one of
    > them. Why say they cannot do that? Hmm. Perhaps this is being already
    > handled elsewhere, in which case this comment still seems misleading.
    >
    I have removed this restriction. Now we can specify stored generated
    columns in EXCEPT (column_list) when we use the
    'publish_generated_columns' flag.
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > 7.
    > + * With REPLICA IDENTITY FULL, no column list and no excluded column
    > + * list is allowed.
    >
    > Really, just "no column list is allowed." same as it said before.
    >
    > ======
    Fixed
    
    Thanks and Regards,
    Shlok Kyal
    
  85. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-06-26T03:36:10Z

    Hi Shlok.
    
    Below are some review comments for v14-0003
    
    ======
    1. GENERAL
    
    Since the new syntax uses EXCEPT, then, in my opinion, you should try
    to use that same term where possible when describing things. I
    understand it is hard to do this in text and I agree often it makes
    more sense to say "exclude" columns etc, but OTOH in the code there
    are lots of places where you could have named vars/params differently:
    e.g. 'except_collist' instead of 'exclude_collist' might have been
    better.
    
    ======
    Commit message
    
    2.
    Column list specifed with EXCEPT is stored in column "prattrs" in table
    "pg_publication_rel" and also column "prexcept" is set to "true", to maintain
    the column list that user wants to exclude from the publication.
    
    ~
    
    That paragraph could do with some rewording. For example, AFAIK,
    "prattrs" is for all column lists -- not just except col-lists, but
    the way it is described here sounds different.
    
    Also, /specifed/specified/
    
    ======
    doc/src/sgml/catalogs.sgml
    
    3. (52.42. pg_publication_rel)
    
           <para>
    -       True if the relation must be excluded
    +       True if the relation or column list must be excluded. If publication is
    +       created <literal>FOR ALL TABLES</literal> and it is specified as true,
    +       the relation should be excluded. Else if it is true the columns in
    +       <literal>prattrs</literal> should be excluded from being published.
           </para></entry>
    
    I felt this could be expressed more simply without mentioning anything
    about FOR ALL TABLES.
    
    SUGGESTION
    True if the column list or relation must be excluded from publication.
    If a column list is specified in <literal>prattrs</literal>, then
    exclude only those columns. If <literal>prattrs</literal> is NULL,
    then exclude the entire relation.
    
    ======
    doc/src/sgml/logical-replication.sgml
    
    4. (29.5. Column Lists)
    
       <para>
    -   Each publication can optionally specify which columns of each table are
    -   replicated to subscribers. The table on the subscriber side must have at
    -   least all the columns that are published. If no column list is specified,
    -   then all columns on the publisher are replicated.
    +   Each publication can optionally specify which columns of each
    table should be
    +   replicated or excluded from replication. On the subscriber side, the table
    +   must include at least all the columns that are published. If no column list
    +   is provided, all columns from the publisher are replicated by default.
        See <xref linkend="sql-createpublication"/> for details on the syntax.
       </para>
    
    I felt this patch may have changed too much text. IMO, you only needed
    to say "... are replicated or excluded from replication.". The other
    changes did not seem necessary.
    
    ~~~
    
    5.
       <para>
    -   If no column list is specified, any columns added to the table later are
    -   automatically replicated. This means that having a column list which names
    -   all columns is not the same as having no column list at all.
    +   If no column list or a column list with EXCEPT is specified, any columns
    +   added to the table later are automatically replicated. This means
    that having
    +   a column list which names all columns is not the same as having no
    +   column list at all. If an column list is specified, any columns added to the
    +   table later are automatically replicated.
       </para>
    
    5a.
    "This means that having a column list which names all columns is not
    the same as having no column list at all." -- That note does not make
    sense when you say EXCEPT. I think some rewording is needed here.
    
    ~
    
    5b.
    "If an column list is specified, any columns added to the table later
    are automatically replicated.".
    
    This made no sense -- some words missing?
    
    ~~~
    
    6.
        Generated columns can also be specified in a column list. This allows
        generated columns to be published, regardless of the publication parameter
        <link linkend="sql-createpublication-params-with-publish-generated-columns">
    -   <literal>publish_generated_columns</literal></link>. See
    -   <xref linkend="logical-replication-gencols"/> for details.
    +   <literal>publish_generated_columns</literal></link>. Generated columns can
    +   be included in column list specified with EXCEPT clause if publication
    +   parameter
    +   <link linkend="sql-createpublication-params-with-publish-generated-columns">
    +   <literal>publish_generated_columns</literal></link> is not set to
    +   <literal>none</literal>. Specified generated columns will not be published.
    +   See <xref linkend="logical-replication-gencols"/> for details.
       </para>
    
    I am not so sure about this. It seemed overly strict to me.
    
    Why can't it simply say:
    "Generated columns can also be specified in a column list. This allows
    generated columns to be published or excluded, regardless of the
    publication parameter..."
    
    Specifically, I don't know why you need to say:
    Generated columns can be included in column list specified with EXCEPT
    clause if publication parameter publish_generated_columns is not set
    to none. Specified generated columns will not be published.
    
    IIUC, then EXCEPT (gencol1, gencol2) is saying to exclude the named
    cols. So if param is "stored", then the named cols will be excluded.
    OTOH, if param is "none" then all generated cols will be excluded
    anyway, so why not just allow the EXCEPT (gencol,gencol2) here as
    well, because the result will be the same.
    
    
    ~~~
    
    7. (29.5.1. Examples)
    
        <para>
    -    Create a table <literal>t1</literal> to be used in the following example.
    +    Create tables <literal>t1</literal>, <literal>t2</literal> to be
    used in the
    +    following example.
    
    /Create tables t1, t2/Create tables t1 and t2/
    
    ~~~
    
    8.
        <para>
         Create a publication <literal>p1</literal>. A column list is defined for
    -    table <literal>t1</literal> to reduce the number of columns that will be
    -    replicated. Notice that the order of column names in the column list does
    -    not matter.
    +    table <literal>t1</literal> and a column list is defined for table
    +    <literal>t2</literal> with EXCEPT clause to reduce the number of
    columns that will be
    +    replicated. Notice that the order of column names in the column
    lists does not matter.
    
    BEFORE
    A column list is defined for table t1 and a column list is defined for
    table t2...
    
    SUGGESTION (added comma, etc.)
    A column list is defined for table t1, and another column list is
    defined for table t2...
    
    ~~~
    
    9.
    The final example still says:
    "Only data from the column list of publication p1 is replicated."
    
    That doesn't seem quite appropriate now that you also have an EXCEPT
    column list.
    
    SUGGESTION:
    Only data specified by the column lists of publication p1 is replicated.
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    10.
    +     <para>
    +      When a column list is specified with EXCEPT, the named columns are not
    +      replicated. Specifying a column list has no effect on
    +      <literal>TRUNCATE</literal> commands.
    +     </para>
    
    I felt that to be clearer the preceding paragraph should be changed as follows:
    
    /When a column list is specified, only the named columns are
    replicated./When a column list without EXCEPT is specified, only the
    named columns are replicated./
    
    ~~~
    
    11. CREATE PUBLICATION (NOTES section)
    
    11a.
    The NOTES talk about replica identity columns -- should you mention EXCEPT here?
    
    ~
    
    11b.
    The NOTES talk about generated columns -- should you mention EXCEPT here?
    
    ======
    src/backend/catalog/pg_publication.c
    
    12. check_and_fetch_column_list
    
    + if (!isnull)
    + except = DatumGetBool(cfdatum);
    +
    + *except_columns = except && !pub->alltables;
    
    AFAICT, you can Assert(!pub->alltables) because you already checked
    that earlier up front.
    So you don't need 'except' var either. Just assign *except_cols up
    front and then overwrite it later if true.
    
    SUGGESTION:
    
    *except_cols = false;
    
    if (pub->alltables)
      return false;
    ...
    if (!isnull)
     *except_cols = DatumGetBool(cfdatum);
    
    ~~~
    
    13. publication_add_relation
    
      /* Validate and translate column names into a Bitmapset of attnums. */
    - attnums = pub_collist_validate(pri->relation, pri->columns);
    + attnums = pub_collist_validate(pri->relation, pri->columns,
    +    pri->except && !pub->alltables,
    +    pub->pubgencols_type);
    
    
    I am wondering why we are even calling a function to validate column
    lists if pub->alltables was true. AFAIK, that combination of
    column-lists and FOR ALL TABLES is not even possible, so the code
    seems strange.
    
    ~~~
    
    14. pub_exclude_collist_validate
    .
    + /*
    + * Check if column list specified with EXCEPT have any stored
    + * generated column and 'publish_generated_columns' is not set to
    + * 'stored'.
    + */
    + if (except_columns &&
    + TupleDescAttr(tupdesc, attnum - 1)->attgenerated ==
    ATTRIBUTE_GENERATED_STORED &&
    + pubgencols_type != PUBLISH_GENCOLS_STORED)
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
    + errmsg("cannot use stored generated column \"%s\" in publication
    column list specified with EXCEPT when \"%s\" set to \"%s\"",
    +    colname, "publish_generated_columns", "stored"));
    
    As mentioned in the above DOCS comments, I was having doubts about why
    we have this error.
    
    If the parameter says "none", then generated columns will not be
    replicated, so why should we care if the user also says
    EXCEPT(gencol1,gencol2). Either way, the result will be the same; the
    generated column will not be published.
    
    ~~~
    
    15. GetRelationPublications
    
      {
      HeapTuple tup = &pubrellist->members[i]->tuple;
      Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid;
    + HeapTuple pubtup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pubid));
    + bool is_table_excluded = ((Form_pg_publication)
    GETSTRUCT(pubtup))->puballtables &&
    + ((Form_pg_publication_rel) GETSTRUCT(tup))->prexcept;
    
    - if (except_flag == ((Form_pg_publication_rel) GETSTRUCT(tup))->prexcept)
    + if (except_flag == is_table_excluded)
      result = lappend_oid(result, pubid);
    +
    + ReleaseS
    
    
    I'm not 100% sure you need the additional 'pubtup'... Can't you just
    look at the "prattrs" field to see if a column-list was specified? If
    "prattrs" is null and "prexcept" is true, isn't that the same
    combination as what you are looking for here?
    
    ~~~
    
    16. pg_get_publication_tables
    
    + columnsDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
    +    Anum_pg_publication_rel_prattrs,
    +    &(nulls[2]));
    +
    + /* if column list is specified with EXCEPT */
    + if (!pub->alltables && except)
    + columns = pub_collist_to_bitmapset(NULL, columnsDatum, NULL);
    + else
    + values[2] = columnsDatum;
    
    16a.
    Something seems fishy here. Isn't there a pathway where you missed
    assigning value[2] to anything?
    
    ~
    
    16b.
    Also, I feel there should be some other boolean variable used here
    instead of checking bot (!pub->alltables && except) in multiple
    places.
    
    
    ======
    src/backend/replication/pgoutput/pgoutput.c
    
    17. RelationSyncEntry
    +
    + /* Indicate if no column is included in the publication */
    + bool no_cols_published;
    
    Maybe this can have a more explanatory comment to explain why it is needed?
    
    ~~~
    
    18. check_and_init_gencol
    
    + bool found = false;
    + bool except_columns = false;
    +
    + found = check_and_fetch_column_list(pub, entry->publish_as_relid, NULL,
    + NULL, &except_columns);
    +
      /*
      * The column list takes precedence over the
      * 'publish_generated_columns' parameter. Those will be checked later,
    - * see pgoutput_column_list_init.
    + * see pgoutput_column_list_init. But when a column list is specified
    + * with EXCEPT, it should be checked.
      */
    - if (check_and_fetch_column_list(pub, entry->publish_as_relid, NULL, NULL))
    + if (found && !except_columns)
      continue;
    
    The variable 'found' seems a poor name; how about 'has_column_list' or similar?
    
    ~~~
    
    19. pgoutput_change
    
    + /*
    + * If all columns of a table is present in column list specified with
    + * EXCEPT, skip publishing the changes.
    + */
    + if (relentry->no_cols_published)
    + return;
    
    /is present/are present/
    
    ======
    src/bin/pg_dump/pg_dump.c
    
    20. getPublicationTables
    
    + if (strcmp(prexcept, "t") == 0 && PQgetisnull(res, i, i_prattrs))
      pubrinfo[j].dobj.objType = DO_PUBLICATION_EXCEPT_REL;
    + else
    + pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
    
      pubrinfo[j].dobj.catId.tableoid =
      atooid(PQgetvalue(res, i, i_tableoid));
    @@ -4797,6 +4797,7 @@ getPublicationTables(Archive *fout, TableInfo
    tblinfo[], int numTables)
      pubrinfo[j].pubrelqual = NULL;
      else
      pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
    + pubrinfo[j].pubexcept = (strcmp(prexcept, "t") == 0);
    
    
    Why not assign pubrinfo[j].pubexcept earlier so you don't have to
    repeat the strcmp?
    
    ~~~
    
    21.
    - if (strcmp(prexcept, "t") == 0)
    + if (strcmp(prexcept, "t") == 0 && PQgetisnull(res, i, i_prattrs))
      simple_ptr_list_append(&exceptinfo, &pubrinfo[j]);
    
    Why not assign pubrinfo[j].pubexcept earlier so you don't have to
    repeat the strcmp? Same also for the PQgetisnull(res, i,
    i_prattrs))...
    
    ~~~
    
    22. dumpPublicationTable
    
      if (pubrinfo->pubrattrs)
    - appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
    + {
    + if (pubrinfo->pubexcept)
    + appendPQExpBuffer(query, " EXCEPT (%s)", pubrinfo->pubrattrs);
    + else
    + appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
    + }
    
    SUGGESTION
    {
      if (pubrinfo->pubexcept)
        appendPQExpBuffer(query, " EXCEPT");
    
      appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
    }
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  86. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-06-26T09:57:36Z

    On Tue, Jun 24, 2025 at 9:48 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    >  I have included the changes for
    > it in v14-0003 patch.
    >
    Thanks for the patches. I have reviewed patch001 alone, please find
    few comments:
    
    1)
    +  <para>
    +   The <literal>RESET</literal> clause will reset the publication to the
    +   default state which includes resetting the publication parameters, setting
    +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    +   dropping all relations and schemas that are associated with the
    +   publication.
       </para>
    
    It is misleading, as far as I have understood, we do not drop the
    tables or schemas associated with the pub; we just remove those from
    the publication's object list. See previous doc:
    "The ADD and DROP clauses will add and remove one or more
    tables/schemas from the publication"
    
    Perhaps we want to say the same thing when we speak about the 'drop'
    aspect of RESET.
    
    2)
    AlterPublicationReset():
    
    + if (!OidIsValid(prid))
    + ereport(ERROR,
    + (errcode(ERRCODE_UNDEFINED_OBJECT),
    + errmsg("relation \"%s\" is not part of the publication",
    + get_rel_name(relid))));
    
    Can you please help me understand which scenario will give this error?
    
    Another question is do we really need this error? IIUC, we generally
    give errors if a user has explicitly called out a name of an object
    and that object is not found. Example:
    
    postgres=# alter publication pubnew drop table t1,tab2;
    ERROR:  relation "t1" is not part of the publication
    
    While in a few other cases, we pass missing_okay as true and do not
    give errors. Please see other callers of performDeletion in
    publicationcmds.c itself. There we have usage of missing_okay=true. I
    have not researched myself, but please analyze the cases where
    missing_okay is passed as true to figure out if those match our RESET
    case. Try to reproduce if possible and then take a call.
    
    3)
    +ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA public;
    +ERROR:  syntax error at or near "ALL"
    +LINE 1: ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA pub...
    
    There is a problem in syntax, I think the intention of testcase was to
    run this query successfully.
    
    thanks
    Shveta
    
    
    
    
  87. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-06-27T10:11:17Z

    On Thu, 26 Jun 2025 at 09:06, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Below are some review comments for v14-0003
    >
    > ======
    > 1. GENERAL
    >
    > Since the new syntax uses EXCEPT, then, in my opinion, you should try
    > to use that same term where possible when describing things. I
    > understand it is hard to do this in text and I agree often it makes
    > more sense to say "exclude" columns etc, but OTOH in the code there
    > are lots of places where you could have named vars/params differently:
    > e.g. 'except_collist' instead of 'exclude_collist' might have been
    > better.
    >
    Fixed the variable names.
    
    > ======
    > Commit message
    >
    > 2.
    > Column list specifed with EXCEPT is stored in column "prattrs" in table
    > "pg_publication_rel" and also column "prexcept" is set to "true", to maintain
    > the column list that user wants to exclude from the publication.
    >
    > ~
    >
    > That paragraph could do with some rewording. For example, AFAIK,
    > "prattrs" is for all column lists -- not just except col-lists, but
    > the way it is described here sounds different.
    >
    > Also, /specifed/specified/
    >
    Reworded the paragraph
    
    > ======
    > doc/src/sgml/catalogs.sgml
    >
    > 3. (52.42. pg_publication_rel)
    >
    >        <para>
    > -       True if the relation must be excluded
    > +       True if the relation or column list must be excluded. If publication is
    > +       created <literal>FOR ALL TABLES</literal> and it is specified as true,
    > +       the relation should be excluded. Else if it is true the columns in
    > +       <literal>prattrs</literal> should be excluded from being published.
    >        </para></entry>
    >
    > I felt this could be expressed more simply without mentioning anything
    > about FOR ALL TABLES.
    >
    > SUGGESTION
    > True if the column list or relation must be excluded from publication.
    > If a column list is specified in <literal>prattrs</literal>, then
    > exclude only those columns. If <literal>prattrs</literal> is NULL,
    > then exclude the entire relation.
    >
    Fixed
    
    > ======
    > doc/src/sgml/logical-replication.sgml
    >
    > 4. (29.5. Column Lists)
    >
    >    <para>
    > -   Each publication can optionally specify which columns of each table are
    > -   replicated to subscribers. The table on the subscriber side must have at
    > -   least all the columns that are published. If no column list is specified,
    > -   then all columns on the publisher are replicated.
    > +   Each publication can optionally specify which columns of each
    > table should be
    > +   replicated or excluded from replication. On the subscriber side, the table
    > +   must include at least all the columns that are published. If no column list
    > +   is provided, all columns from the publisher are replicated by default.
    >     See <xref linkend="sql-createpublication"/> for details on the syntax.
    >    </para>
    >
    > I felt this patch may have changed too much text. IMO, you only needed
    > to say "... are replicated or excluded from replication.". The other
    > changes did not seem necessary.
    >
    > ~~~
    Fixed
    
    > 5.
    >    <para>
    > -   If no column list is specified, any columns added to the table later are
    > -   automatically replicated. This means that having a column list which names
    > -   all columns is not the same as having no column list at all.
    > +   If no column list or a column list with EXCEPT is specified, any columns
    > +   added to the table later are automatically replicated. This means
    > that having
    > +   a column list which names all columns is not the same as having no
    > +   column list at all. If an column list is specified, any columns added to the
    > +   table later are automatically replicated.
    >    </para>
    >
    > 5a.
    > "This means that having a column list which names all columns is not
    > the same as having no column list at all." -- That note does not make
    > sense when you say EXCEPT. I think some rewording is needed here.
    >
    Fixed
    
    > ~
    >
    > 5b.
    > "If an column list is specified, any columns added to the table later
    > are automatically replicated.".
    >
    > This made no sense -- some words missing?
    >
    This change was done by mistake. Removed it.
    
    > ~~~
    >
    > 6.
    >     Generated columns can also be specified in a column list. This allows
    >     generated columns to be published, regardless of the publication parameter
    >     <link linkend="sql-createpublication-params-with-publish-generated-columns">
    > -   <literal>publish_generated_columns</literal></link>. See
    > -   <xref linkend="logical-replication-gencols"/> for details.
    > +   <literal>publish_generated_columns</literal></link>. Generated columns can
    > +   be included in column list specified with EXCEPT clause if publication
    > +   parameter
    > +   <link linkend="sql-createpublication-params-with-publish-generated-columns">
    > +   <literal>publish_generated_columns</literal></link> is not set to
    > +   <literal>none</literal>. Specified generated columns will not be published.
    > +   See <xref linkend="logical-replication-gencols"/> for details.
    >    </para>
    >
    > I am not so sure about this. It seemed overly strict to me.
    >
    > Why can't it simply say:
    > "Generated columns can also be specified in a column list. This allows
    > generated columns to be published or excluded, regardless of the
    > publication parameter..."
    >
    > Specifically, I don't know why you need to say:
    > Generated columns can be included in column list specified with EXCEPT
    > clause if publication parameter publish_generated_columns is not set
    > to none. Specified generated columns will not be published.
    >
    > IIUC, then EXCEPT (gencol1, gencol2) is saying to exclude the named
    > cols. So if param is "stored", then the named cols will be excluded.
    > OTOH, if param is "none" then all generated cols will be excluded
    > anyway, so why not just allow the EXCEPT (gencol,gencol2) here as
    > well, because the result will be the same.
    >
    >
    I have removed this change. And allowed specifying generated columns
    in EXCEPT column list as well irrespective of value of
    ‘publish_generated_columns’.
    
    > ~~~
    >
    > 7. (29.5.1. Examples)
    >
    >     <para>
    > -    Create a table <literal>t1</literal> to be used in the following example.
    > +    Create tables <literal>t1</literal>, <literal>t2</literal> to be
    > used in the
    > +    following example.
    >
    > /Create tables t1, t2/Create tables t1 and t2/
    >
    Fixed
    
    > ~~~
    >
    > 8.
    >     <para>
    >      Create a publication <literal>p1</literal>. A column list is defined for
    > -    table <literal>t1</literal> to reduce the number of columns that will be
    > -    replicated. Notice that the order of column names in the column list does
    > -    not matter.
    > +    table <literal>t1</literal> and a column list is defined for table
    > +    <literal>t2</literal> with EXCEPT clause to reduce the number of
    > columns that will be
    > +    replicated. Notice that the order of column names in the column
    > lists does not matter.
    >
    > BEFORE
    > A column list is defined for table t1 and a column list is defined for
    > table t2...
    >
    > SUGGESTION (added comma, etc.)
    > A column list is defined for table t1, and another column list is
    > defined for table t2...
    >
    Fixed
    
    > ~~~
    >
    > 9.
    > The final example still says:
    > "Only data from the column list of publication p1 is replicated."
    >
    > That doesn't seem quite appropriate now that you also have an EXCEPT
    > column list.
    >
    > SUGGESTION:
    > Only data specified by the column lists of publication p1 is replicated.
    >
    Fixed
    
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 10.
    > +     <para>
    > +      When a column list is specified with EXCEPT, the named columns are not
    > +      replicated. Specifying a column list has no effect on
    > +      <literal>TRUNCATE</literal> commands.
    > +     </para>
    >
    > I felt that to be clearer the preceding paragraph should be changed as follows:
    >
    > /When a column list is specified, only the named columns are
    > replicated./When a column list without EXCEPT is specified, only the
    > named columns are replicated./
    >
    Fixed
    
    > ~~~
    >
    > 11. CREATE PUBLICATION (NOTES section)
    >
    > 11a.
    > The NOTES talk about replica identity columns -- should you mention EXCEPT here?
    >
    Added notes for EXCEPT
    
    > ~
    >
    > 11b.
    > The NOTES talk about generated columns -- should you mention EXCEPT here?
    >
    I felt it is not needed.
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > 12. check_and_fetch_column_list
    >
    > + if (!isnull)
    > + except = DatumGetBool(cfdatum);
    > +
    > + *except_columns = except && !pub->alltables;
    >
    > AFAICT, you can Assert(!pub->alltables) because you already checked
    > that earlier up front.
    > So you don't need 'except' var either. Just assign *except_cols up
    > front and then overwrite it later if true.
    >
    > SUGGESTION:
    >
    > *except_cols = false;
    >
    > if (pub->alltables)
    >   return false;
    > ...
    > if (!isnull)
    >  *except_cols = DatumGetBool(cfdatum);
    >
    Fixed
    
    > ~~~
    >
    > 13. publication_add_relation
    >
    >   /* Validate and translate column names into a Bitmapset of attnums. */
    > - attnums = pub_collist_validate(pri->relation, pri->columns);
    > + attnums = pub_collist_validate(pri->relation, pri->columns,
    > +    pri->except && !pub->alltables,
    > +    pub->pubgencols_type);
    >
    >
    > I am wondering why we are even calling a function to validate column
    > lists if pub->alltables was true. AFAIK, that combination of
    > column-lists and FOR ALL TABLES is not even possible, so the code
    > seems strange.
    >
    Fixed
    
    > ~~~
    >
    > 14. pub_exclude_collist_validate
    > .
    > + /*
    > + * Check if column list specified with EXCEPT have any stored
    > + * generated column and 'publish_generated_columns' is not set to
    > + * 'stored'.
    > + */
    > + if (except_columns &&
    > + TupleDescAttr(tupdesc, attnum - 1)->attgenerated ==
    > ATTRIBUTE_GENERATED_STORED &&
    > + pubgencols_type != PUBLISH_GENCOLS_STORED)
    > + ereport(ERROR,
    > + errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
    > + errmsg("cannot use stored generated column \"%s\" in publication
    > column list specified with EXCEPT when \"%s\" set to \"%s\"",
    > +    colname, "publish_generated_columns", "stored"));
    >
    > As mentioned in the above DOCS comments, I was having doubts about why
    > we have this error.
    >
    > If the parameter says "none", then generated columns will not be
    > replicated, so why should we care if the user also says
    > EXCEPT(gencol1,gencol2). Either way, the result will be the same; the
    > generated column will not be published.
    >
    Removed this restriction.
    
    > ~~~
    >
    > 15. GetRelationPublications
    >
    >   {
    >   HeapTuple tup = &pubrellist->members[i]->tuple;
    >   Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid;
    > + HeapTuple pubtup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pubid));
    > + bool is_table_excluded = ((Form_pg_publication)
    > GETSTRUCT(pubtup))->puballtables &&
    > + ((Form_pg_publication_rel) GETSTRUCT(tup))->prexcept;
    >
    > - if (except_flag == ((Form_pg_publication_rel) GETSTRUCT(tup))->prexcept)
    > + if (except_flag == is_table_excluded)
    >   result = lappend_oid(result, pubid);
    > +
    > + ReleaseS
    >
    >
    > I'm not 100% sure you need the additional 'pubtup'... Can't you just
    > look at the "prattrs" field to see if a column-list was specified? If
    > "prattrs" is null and "prexcept" is true, isn't that the same
    > combination as what you are looking for here?
    >
    Yes, we can use this combination as well. Fixed it in latest patch.
    
    > ~~~
    >
    > 16. pg_get_publication_tables
    >
    > + columnsDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
    > +    Anum_pg_publication_rel_prattrs,
    > +    &(nulls[2]));
    > +
    > + /* if column list is specified with EXCEPT */
    > + if (!pub->alltables && except)
    > + columns = pub_collist_to_bitmapset(NULL, columnsDatum, NULL);
    > + else
    > + values[2] = columnsDatum;
    >
    > 16a.
    > Something seems fishy here. Isn't there a pathway where you missed
    > assigning value[2] to anything?
    >
    Modified this change.
    
    > ~
    >
    > 16b.
    > Also, I feel there should be some other boolean variable used here
    > instead of checking bot (!pub->alltables && except) in multiple
    > places.
    >
    Fixed
    >
    > ======
    > src/backend/replication/pgoutput/pgoutput.c
    >
    > 17. RelationSyncEntry
    > +
    > + /* Indicate if no column is included in the publication */
    > + bool no_cols_published;
    >
    > Maybe this can have a more explanatory comment to explain why it is needed?
    >
    Fixed
    
    > ~~~
    >
    > 18. check_and_init_gencol
    >
    > + bool found = false;
    > + bool except_columns = false;
    > +
    > + found = check_and_fetch_column_list(pub, entry->publish_as_relid, NULL,
    > + NULL, &except_columns);
    > +
    >   /*
    >   * The column list takes precedence over the
    >   * 'publish_generated_columns' parameter. Those will be checked later,
    > - * see pgoutput_column_list_init.
    > + * see pgoutput_column_list_init. But when a column list is specified
    > + * with EXCEPT, it should be checked.
    >   */
    > - if (check_and_fetch_column_list(pub, entry->publish_as_relid, NULL, NULL))
    > + if (found && !except_columns)
    >   continue;
    >
    > The variable 'found' seems a poor name; how about 'has_column_list' or similar?
    >
    Fixed
    
    > ~~~
    >
    > 19. pgoutput_change
    >
    > + /*
    > + * If all columns of a table is present in column list specified with
    > + * EXCEPT, skip publishing the changes.
    > + */
    > + if (relentry->no_cols_published)
    > + return;
    >
    > /is present/are present/
    >
    fixed
    
    > ======
    > src/bin/pg_dump/pg_dump.c
    >
    > 20. getPublicationTables
    >
    > + if (strcmp(prexcept, "t") == 0 && PQgetisnull(res, i, i_prattrs))
    >   pubrinfo[j].dobj.objType = DO_PUBLICATION_EXCEPT_REL;
    > + else
    > + pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
    >
    >   pubrinfo[j].dobj.catId.tableoid =
    >   atooid(PQgetvalue(res, i, i_tableoid));
    > @@ -4797,6 +4797,7 @@ getPublicationTables(Archive *fout, TableInfo
    > tblinfo[], int numTables)
    >   pubrinfo[j].pubrelqual = NULL;
    >   else
    >   pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
    > + pubrinfo[j].pubexcept = (strcmp(prexcept, "t") == 0);
    >
    >
    > Why not assign pubrinfo[j].pubexcept earlier so you don't have to
    > repeat the strcmp?
    >
    Fixed
    
    > ~~~
    >
    > 21.
    > - if (strcmp(prexcept, "t") == 0)
    > + if (strcmp(prexcept, "t") == 0 && PQgetisnull(res, i, i_prattrs))
    >   simple_ptr_list_append(&exceptinfo, &pubrinfo[j]);
    >
    > Why not assign pubrinfo[j].pubexcept earlier so you don't have to
    > repeat the strcmp? Same also for the PQgetisnull(res, i,
    > i_prattrs))...
    >
    Fixed
    
    > ~~~
    >
    > 22. dumpPublicationTable
    >
    >   if (pubrinfo->pubrattrs)
    > - appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
    > + {
    > + if (pubrinfo->pubexcept)
    > + appendPQExpBuffer(query, " EXCEPT (%s)", pubrinfo->pubrattrs);
    > + else
    > + appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
    > + }
    >
    > SUGGESTION
    > {
    >   if (pubrinfo->pubexcept)
    >     appendPQExpBuffer(query, " EXCEPT");
    >
    >   appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
    > }
    Fixed
    
    I have addressed the comments shared by you and shared the updated v15
    patch set here.
    
    Thanks and Regards,
    Shlok Kyal
    
  88. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-06-27T10:14:14Z

    On Thu, 26 Jun 2025 at 15:27, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Jun 24, 2025 at 9:48 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > >  I have included the changes for
    > > it in v14-0003 patch.
    > >
    > Thanks for the patches. I have reviewed patch001 alone, please find
    > few comments:
    >
    > 1)
    > +  <para>
    > +   The <literal>RESET</literal> clause will reset the publication to the
    > +   default state which includes resetting the publication parameters, setting
    > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > +   dropping all relations and schemas that are associated with the
    > +   publication.
    >    </para>
    >
    > It is misleading, as far as I have understood, we do not drop the
    > tables or schemas associated with the pub; we just remove those from
    > the publication's object list. See previous doc:
    > "The ADD and DROP clauses will add and remove one or more
    > tables/schemas from the publication"
    >
    > Perhaps we want to say the same thing when we speak about the 'drop'
    > aspect of RESET.
    I have updated the document.
    
    > 2)
    > AlterPublicationReset():
    >
    > + if (!OidIsValid(prid))
    > + ereport(ERROR,
    > + (errcode(ERRCODE_UNDEFINED_OBJECT),
    > + errmsg("relation \"%s\" is not part of the publication",
    > + get_rel_name(relid))));
    >
    > Can you please help me understand which scenario will give this error?
    >
    > Another question is do we really need this error? IIUC, we generally
    > give errors if a user has explicitly called out a name of an object
    > and that object is not found. Example:
    >
    > postgres=# alter publication pubnew drop table t1,tab2;
    > ERROR:  relation "t1" is not part of the publication
    >
    > While in a few other cases, we pass missing_okay as true and do not
    > give errors. Please see other callers of performDeletion in
    > publicationcmds.c itself. There we have usage of missing_okay=true. I
    > have not researched myself, but please analyze the cases where
    > missing_okay is passed as true to figure out if those match our RESET
    > case. Try to reproduce if possible and then take a call.
    I thought about the above point and I also think this check is not
    required. Also, the function was calling PublicationDropSchemas with
    missing_ok as false. I have changed it to be true.
    
    > 3)
    > +ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA public;
    > +ERROR:  syntax error at or near "ALL"
    > +LINE 1: ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA pub...
    >
    > There is a problem in syntax, I think the intention of testcase was to
    > run this query successfully.
    
    I have fixed it.
    
    Thanks Shveta for reviewing the patch. I have addressed the comments
    and posted an updated version v15 in [1].
    
    [1]: https://www.postgresql.org/message-id/CANhcyEU%2BaPu6iAH2cTA0cDtn3pd6c_njBONCt3FubYZoEEnm8Q%40mail.gmail.com
    
    Thanks and Regards,
    Shlok Kyal
    
    
    
    
  89. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-06-30T06:07:15Z

    Hi Shlok.
    
    Some review comments for v15-0003.
    
    ======
    doc/src/sgml/catalogs.sgml
    
    1.
           <para>
    -       True if the relation must be excluded
    +       True if the column list or relation must be excluded from publication.
    +       If a column list is specified in <literal>prattrs</literal>, then
    +       exclude only those columns. If <literal>prattrs</literal> is NULL,
    +       then exclude the entire relation.
           </para></entry>
    
    I noticed other fields on this page say "null" instead of "NULL". It
    seems like "null" is more conventional.
    
    ======
    doc/src/sgml/logical-replication.sgml
    
    2.
       <para>
        If no column list is specified, any columns added to the table later are
        automatically replicated. This means that having a column list which names
    -   all columns is not the same as having no column list at all.
    +   all columns is not the same as having no column list at all.
    Similarly, if an
    +   column list is specified with EXCEPT, any columns added to the table later
    +   are also replicated automatically.
       </para>
    
    2a.
    CURRENTLY
    If no column list or a column list with EXCEPT is specified, any
    columns added to the table later are automatically replicated. This
    means that having a column list which names all columns is not the
    same as having no column list at all. If an column list is specified,
    any columns added to the table later are automatically replicated.
    
    ~
    
    That still doesn't quite make sense. I think instead of saying "This
    means..." it needs to say something a bit like below:
    
    However, a normal column list (without EXCEPT) only replicates the
    specified columns and no more. Therefore, having a column list that
    names all columns is not the same as having no column list at all, as
    more columns may be added to the table later.
    
    ~
    
    2b.
    And the final sentence "If an column list..." looks like a cut/paste error (??)
    
    ~
    
    2c.
    Maybe here EXCEPT should be written as <literal>EXCEPT</literal>
    
    ~~~
    
    2.5A.
    The description about generated columns still says this:
    
    CURRENT:
    Generated columns can also be specified in a column list. This allows
    generated columns to be published, regardless of the publication
    parameter publish_generated_columns. See Section 29.6 for details.
    
    ~
    
    But I don't think it is quite correct. IMO gencols behaviour is much
    more subtle...
    
    e.g.
    
    a) Normal collist - these named cols are published REGARDLESS of the
    'publish_generated_cols' parameter (same as before)
    
    b) EXCEPT collist - you can specify gencols in the list REGARDLESS of
    the 'publish_generated_cols' parameter, because since they are named
    as "except" then they will not be published anyhow....
    
    c) BUT for EXCEPT collist case, I think any gencols that are *not*
    covered by that EXCEPT collist should follow the rules according to
    the 'publish_generated_cols' parameter.
    
    So, it is much more tricky than the docs currently say:
    
    Also
    
    2.5B.
    - The text says "See Section 29.6 for details," but there are no
    examples of these combinations (e.g. EXCEPT collist and diff parameter
    setting)
    
    2.5C,
    - The regression tests also need to be more complex to cover these
    
    2.5D.
    - You might need to add something in the CREATE PUBLICATION "NOTES"
    section after all -- even if it just refers to here.
    
    ~~~
    
    3.
        <para>
         Create a publication <literal>p1</literal>. A column list is defined for
    -    table <literal>t1</literal> to reduce the number of columns that will be
    -    replicated. Notice that the order of column names in the column list does
    -    not matter.
    +    table <literal>t1</literal>, and another column list is defined for table
    +    <literal>t2</literal> using the EXCEPT clause to reduce the number of
    +    columns that will be replicated. Note that the order of column names in
    +    the column lists does not matter.
     <programlisting>
    -/* pub # */ CREATE PUBLICATION p1 FOR TABLE t1 (id, b, a, d);
    +/* pub # */ CREATE PUBLICATION p1 FOR TABLE t1 (id, b, a, d), t2 EXCEPT (d, a);
     </programlisting></para>
    
    Maybe here EXCEPT should be written as <literal>EXCEPT</literal>
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    4.
          <para>
    -      When a column list is specified, only the named columns are replicated.
    -      The column list can contain stored generated columns as well. If the
    -      column list is omitted, the publication will replicate all non-generated
    -      columns (including any added in the future) by default. Stored generated
    -      columns can also be replicated if
    <literal>publish_generated_columns</literal>
    -      is set to <literal>stored</literal>. Specifying a column list has no
    -      effect on <literal>TRUNCATE</literal> commands. See
    +      When a column list without EXCEPT is specified, only the named
    columns are
    +      replicated. The column list can contain stored generated columns as well.
    +      If the column list is omitted, the publication will replicate
    +      all non-generated columns (including any added in the future) by default.
    +      Stored generated columns can also be replicated if
    +      <literal>publish_generated_columns</literal> is set to
    +      <literal>stored</literal>. Specifying a column list has no effect on
    +      <literal>TRUNCATE</literal> commands. See
           <xref linkend="logical-replication-col-lists"/> for details about column
           lists.
          </para>
    
    Maybe here EXCEPT should be written as <literal>EXCEPT</literal>
    
    ~~~
    
    5.
    +     <para>
    +      When a column list is specified with EXCEPT, the named columns are not
    +      replicated. Specifying a column list has no effect on
    +      <literal>TRUNCATE</literal> commands.
    +     </para>
    
    Maybe here EXCEPT should be written as <literal>EXCEPT</literal>.
    
    ** Note all the extra subtleties that I mentioned in the review
    comment #2.5 above --- e.g. IMO any *un-listed* gencols still should
    follow the parameter rules.
    
    ~~~
    
    6.
       <para>
        Any column list must include the <literal>REPLICA IDENTITY</literal> columns
    -   in order for <command>UPDATE</command> or <command>DELETE</command>
    -   operations to be published. There are no column list restrictions if the
    -   publication publishes only <command>INSERT</command> operations.
    +   and any column list specified with EXCEPT must not include the
    +   <literal>REPLICA IDENTITY</literal> columns in order for
    +   <command>UPDATE</command> or <command>DELETE</command> operations to be
    +   published. There are no column list restrictions if the
    publication publishes
    +   only <command>INSERT</command> operations.
       </para>
    
    6a.
    CURRENT:
    Any column list must include the REPLICA IDENTITY columns, and any
    column list specified with EXCEPT must not include the REPLICA
    IDENTITY columns in order for UPDATE or DELETE operations to be
    published.
    
    ~
    
    I felt that might be better expressed the other way around. Also, it
    might be better to say "not name" instead of "not include" because
    EXCEPT + include seemed a bit contrary.
    
    
    SUGGESTION (maybe like this)
    In order for UPDATE or DELETE operations to work, all the REPLICA
    IDENTITY columns must be published. So, any column list must name all
    REPLICA IDENTITY columns, and any EXCEPT column list must not name any
    REPLICA IDENTITY columns.
    
    ~~
    
    6b.
    Maybe here EXCEPT should be written as <literal>EXCEPT</literal>
    
    ======
    src/backend/catalog/pg_publication.c
    
    check_and_fetch_column_list:
    
    7.
    + /* Lookup the except attribute */
    + cfdatum = SysCacheGetAttr(PUBLICATIONRELMAP, cftuple,
    +   Anum_pg_publication_rel_prexcept, &isnull);
    +
    + if (!isnull)
    + {
    + Assert(!pub->alltables);
    + *except_columns = DatumGetBool(cfdatum);
    + }
    +
    
    I felt it would be safer to also assign *except_columns = false;
    up-front so the caller could be sure this flag was meaningful on
    return.
    
    ~~~
    
    pub_form_cols_map:
    
    8.
    Maybe use snake case like for other params, so /excepcols/except_cols/
    
    ~~~
    
    pg_get_publication_tables:
    
    9.
    
    I felt all the logic in this function maybe can be simpler:
    
    e.g. If you just have "Bitmapset *except_columns = NULL;" then null
    nmeans there is no except columns; otherwise there is. This means you
    don't need a separate 'bool except_column' variable.
    
    e.g. Assign the Bitmapset *except_columns after you already have the
    values[2], instead of doing it later.
    
    e.g. The skip code if (except_columns && bms_is_member(att->attnum,
    columns)) could just check the list member, I think, without the
    additional bool.
    
    ~~~
    
    10.
    + /*
    + * We fetch pubtuple if publication is not FOR ALL TABLES and not
    + * FOR TABLES IN SCHEMA. So if prexcept is true, it indicate that
    + * prattrs contains columns to be excluded for replication.
    + */
    + if (!isnull)
    + except_columns = DatumGetBool(exceptDatum);
    
    
    /indicate/indicates/
    
    ======
    src/backend/parser/gram.y
    
    11.
    + | TABLE relation_expr EXCEPT opt_except_column_list OptWhereClause
    + {
    + $$ = makeNode(PublicationObjSpec);
    + $$->pubobjtype = PUBLICATIONOBJ_TABLE;
    + $$->pubtable = makeNode(PublicationTable);
    + $$->pubtable->relation = $2;
    + $$->pubtable->columns = $4;
    + $$->pubtable->whereClause = $5;
    + $$->pubtable->except = true;
    + $$->location = @1;
    + }
    
    I wasn't expecting you would need another 'opt_except_column_list' and
    all the code duplication that causes. AFAIK, the syntax is identical
    for 'opt_column_list' apart from the preceding EXCEPT so I thought all
    you need is to allow the 'opt_column_list' to have an optional EXCEPT
    qualifier.
    
    ======
    src/backend/replication/pgoutput/pgoutput.c
    
    12.
    +
    + /*
    + * Indicates whether no columns are published for a given relation. With
    + * the introduction of the EXCEPT clause in column lists, it is now
    + * possible to define a publication that excludes all columns of a table.
    + * However, the 'columns' attribute cannot represent this case, since a
    + * NULL value implies that all columns are published. To distinguish this
    + * scenario, the 'no_cols_published' flag is introduced.
    + */
    + bool no_cols_published;
     } RelationSyncEntry;
    
    But, what about when Bitmapset *columns is not null, but has no bits
    set -- doesn't that mean the same as "no columns"?
    
    ======
    src/include/catalog/pg_publication.h
    
    13.
     extern Bitmapset *pub_form_cols_map(Relation relation,
    - PublishGencolsType include_gencols_type);
    + PublishGencolsType include_gencols_type,
    + Bitmapset *exceptcols);
    
    Maybe snake-case like the other params: /exceptcols/except_cols/
    
    ======
    src/test/regress/sql/publication.sql
    
    14.
    +-- Verify that publication is created with EXCEPT
    +CREATE PUBLICATION testpub_except FOR TABLE pub_test_except1,
    pub_sch1.pub_test_except2 EXCEPT (b, c);
    +SELECT * FROM pg_publication_tables WHERE pubname = 'testpub_except';
    +
    
    I think tests should also use psql \dRp+ commands in places to show
    that the "describe" stuff is working correctly.
    
    ~~~
    
    15.
    +-- Check for invalid cases
    +CREATE PUBLICATION testpub_except2 FOR TABLES IN SCHEMA pub_sch1,
    TABLE pub_test_except1 EXCEPT (b, c);
    +CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except1 EXCEPT;
    
    Should explain more about what you are testing here:
    a) cannot use EXCEPT col-lists combined with TABLES IN SCHEMA
    b) syntax error EXCEPT without a col-list
    
    ~~~
    
    16.
    +-- Verify that publication can be altered with EXCEPT
    +ALTER PUBLICATION testpub_except SET TABLE pub_test_except1 EXCEPT
    (a, b), pub_sch1.pub_test_except2;
    +SELECT * FROM pg_publication_tables WHERE pubname = 'testpub_except';
    
    The comment is a bit misleading because there are many kinds of
    "alter". Maybe say more like
    Verify ok - ALTER PUBLICATION ... SET ... EXCEPT (col-list)
    
    ~~~
    
    17.
    +-- Verify ALTER PUBLICATION ... DROP
    +ALTER PUBLICATION testpub_except DROP TABLE pub_test_except1 EXCEPT (a, b);
    +ALTER PUBLICATION testpub_except DROP TABLE pub_test_except1;
    
    Should explain more:
    +-- Verify fails - ALTER PUBLICATION ... DROP ... EXCEPT (col-list)
    +-- Verify ok - ALTER PUBLICATION ... DROP ...
    
    ~~~
    
    18.
    +ALTER PUBLICATION testpub_except ADD TABLE pub_test_except1 EXCEPT (c, d);
    +SELECT * FROM pg_publication_tables WHERE pubname = 'testpub_except';
    
    Missing comment:
    +-- Verify ok - ALTER PUBLICATION ... ADD ... EXCEPT (col-list)
    
    ~~~
    
    19.
    +-- Verify excluded columns cannot be part of REPLICA IDENTITY
    +ALTER TABLE pub_test_except1 REPLICA IDENTITY FULL;
    +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    
    +CREATE UNIQUE INDEX pub_test_except1_a_idx ON pub_test_except1 (a, c);
    +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    pub_test_except1_a_idx;
    +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    
    +DROP INDEX pub_test_except1_a_idx;
    +CREATE UNIQUE INDEX pub_test_except1_a_idx ON pub_test_except1 (a);
    +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    pub_test_except1_a_idx;
    +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    +
    +DROP INDEX pub_test_except1_a_idx;
    
    19a.
    IIUC, really there are multiple tests here, so I think it should all
    be split and commented separately.
    
    a) Verify that EXCEPT col-list cannot contain RI cols (when using RI FULL)
    b) Verify that EXCEPT col-list cannot contain RI cols (when using INDEX)
    c) Verify that so long as no clash between RI cols and the EXCEPT
    col-list, then it is ok
    
    ~
    
    19b.
    IMO, some index names could be better:
    
    CREATE UNIQUE INDEX pub_test_except1_a_idx ON pub_test_except1 (a, c);
    How about 'pub_test_except1_ac_idx'?
    
    ~~~
    
    20.
    +DROP PUBLICATION testpub_except;
    +DROP TABLE pub_test_except1;
    +DROP TABLE pub_sch1.pub_test_except2;
    
    Add a "cleanup" comment.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  90. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-06-30T06:23:44Z

    Hi Shlok,
    
    One more thing, I noticed there is no tab-completion code yet for this
    new EXCEPT (column_list) syntax.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  91. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-06-30T06:58:25Z

    On Fri, Jun 27, 2025 at 3:44 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Thu, 26 Jun 2025 at 15:27, shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Tue, Jun 24, 2025 at 9:48 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > >  I have included the changes for
    > > > it in v14-0003 patch.
    > > >
    > > Thanks for the patches. I have reviewed patch001 alone, please find
    > > few comments:
    > >
    > > 1)
    > > +  <para>
    > > +   The <literal>RESET</literal> clause will reset the publication to the
    > > +   default state which includes resetting the publication parameters, setting
    > > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > > +   dropping all relations and schemas that are associated with the
    > > +   publication.
    > >    </para>
    > >
    > > It is misleading, as far as I have understood, we do not drop the
    > > tables or schemas associated with the pub; we just remove those from
    > > the publication's object list. See previous doc:
    > > "The ADD and DROP clauses will add and remove one or more
    > > tables/schemas from the publication"
    > >
    > > Perhaps we want to say the same thing when we speak about the 'drop'
    > > aspect of RESET.
    > I have updated the document.
    >
    > > 2)
    > > AlterPublicationReset():
    > >
    > > + if (!OidIsValid(prid))
    > > + ereport(ERROR,
    > > + (errcode(ERRCODE_UNDEFINED_OBJECT),
    > > + errmsg("relation \"%s\" is not part of the publication",
    > > + get_rel_name(relid))));
    > >
    > > Can you please help me understand which scenario will give this error?
    > >
    > > Another question is do we really need this error? IIUC, we generally
    > > give errors if a user has explicitly called out a name of an object
    > > and that object is not found. Example:
    > >
    > > postgres=# alter publication pubnew drop table t1,tab2;
    > > ERROR:  relation "t1" is not part of the publication
    > >
    > > While in a few other cases, we pass missing_okay as true and do not
    > > give errors. Please see other callers of performDeletion in
    > > publicationcmds.c itself. There we have usage of missing_okay=true. I
    > > have not researched myself, but please analyze the cases where
    > > missing_okay is passed as true to figure out if those match our RESET
    > > case. Try to reproduce if possible and then take a call.
    > I thought about the above point and I also think this check is not
    > required. Also, the function was calling PublicationDropSchemas with
    > missing_ok as false. I have changed it to be true.
    >
    
    Okay. Is there a reason for not using PublicationDropTables() here? We
    have rewritten similar code in the Reset flow.
    
    > > 3)
    > > +ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA public;
    > > +ERROR:  syntax error at or near "ALL"
    > > +LINE 1: ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA pub...
    > >
    > > There is a problem in syntax, I think the intention of testcase was to
    > > run this query successfully.
    >
    > I have fixed it.
    >
    > Thanks Shveta for reviewing the patch. I have addressed the comments
    > and posted an updated version v15 in [1].
    
    Thanks for the patches. My review is in progress but please find few
    comments on 002:
    
    1)
    where exception_object is:
        [ ONLY ] table_name [ * ]
    
    We have the above in CREATE and ALTER pub docs, but we do not explain
    ONLY with EXCEPT. We do have an explanation of ONLY under 'FOR TABLE'.
    But since 'FOR TABLE' and 'EXCEPT' do not go together, it is somewhat
    difficult to connect the dots and find the information ONLY in the
    context of EXCEPT. We shall have ONLY explained for EXCEPT as well. Or
    we can have ONLY defined in a way that both 'FOR TABLE' and 'EXCEPT'
    can refer to it.
    
    2)
    We get tab-completion options in this command:
    postgres=# create publication pub5 for TABLE tab1 W
    WHERE (  WITH (
    
    Similarly in this command:
    create publication pub5 for TABLES IN SCHEMA s1
    
    But once we have 'EXCEPT TABLE', we do not get further tab-completion
    option like WITH(...)
    create publication pub5 for ALL TABLES EXCEPT TABLE tab1
    
    3)
    During tab-expansion, 'EXCEPT TABLE' and  'WITH (' in the below
    command looks like they are connecting words. Can the gap be increased
    similar to tab-expansion of next command shown below:
    
    postgres=# create publication pub4 for ALL TABLES
    EXCEPT TABLE  WITH (
    
    postgres=# create publication pub4 for
    ALL TABLES        TABLE             TABLES IN SCHEMA
    
    4)
    alter_publication.sgml.orig is a left-over in patch002.
    
    thanks
    Shveta
    
    
    
    
  92. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-06-30T10:55:18Z

    Few more comments on 002:
    
    5)
    +GetAllTablesPublicationRelations(Oid pubid, bool pubviaroot)
     {
    
    + List    *exceptlist;
    +
    + exceptlist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    
    
    a) Here, we are assuming that the list provided by
    GetPublicationRelations() will be except-tables list only, but there
    is no validation of that.
    b) We are using GetPublicationRelations() to get the relations which
    are excluded from the publication. The name of function and comments
    atop function are not in alignment with this usage.
    
    Suggestion:
    We can have a new GetPublicationExcludeRelations() function for the
    concerned usage. The existing logic of GetPublicationRelations() can
    be shifted to a new internal-logic function which will accept a
    'except-flag' as well. Both GetPublicationRelations() and
    GetPublicationExcludeRelations() can call that new function by passing
    'except-flag' as false and true respectively. The new internal
    function will validate 'prexcept' against that except-flag passed and
    will return the results.
    
    6)
    Before your patch002, GetTopMostAncestorInPublication() was checking
    pg_publication_rel and pg_publication_namespace to find out if the
    table in the ancestor-list is part of a given particular. Both
    pg_publication_rel and pg_publication_namespace did not have the entry
    "for all tables" publications. That means
    GetTopMostAncestorInPublication() was originally not checking whether
    the given puboid is an "for all tables" publication to see if a rel
    belongs to that particular pub or not. I
    
    But now with the current change, we do check if pub is all-tables pub,
    if so, return relid and mark ancestor_level (provided table is not
    part of the except list).  IIUC, the result in 2 cases may be
    different. Is that the intention? Let me know if my understanding is
    wrong.
    
    thanks
    Shveta
    
    
    
    
  93. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-07-19T10:44:03Z

    On Mon, 30 Jun 2025 at 11:37, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Some review comments for v15-0003.
    >
    > ======
    > doc/src/sgml/catalogs.sgml
    >
    > 1.
    >        <para>
    > -       True if the relation must be excluded
    > +       True if the column list or relation must be excluded from publication.
    > +       If a column list is specified in <literal>prattrs</literal>, then
    > +       exclude only those columns. If <literal>prattrs</literal> is NULL,
    > +       then exclude the entire relation.
    >        </para></entry>
    >
    > I noticed other fields on this page say "null" instead of "NULL". It
    > seems like "null" is more conventional.
    >
    Fixed
    
    > ======
    > doc/src/sgml/logical-replication.sgml
    >
    > 2.
    >    <para>
    >     If no column list is specified, any columns added to the table later are
    >     automatically replicated. This means that having a column list which names
    > -   all columns is not the same as having no column list at all.
    > +   all columns is not the same as having no column list at all.
    > Similarly, if an
    > +   column list is specified with EXCEPT, any columns added to the table later
    > +   are also replicated automatically.
    >    </para>
    >
    > 2a.
    > CURRENTLY
    > If no column list or a column list with EXCEPT is specified, any
    > columns added to the table later are automatically replicated. This
    > means that having a column list which names all columns is not the
    > same as having no column list at all. If an column list is specified,
    > any columns added to the table later are automatically replicated.
    >
    > ~
    >
    > That still doesn't quite make sense. I think instead of saying "This
    > means..." it needs to say something a bit like below:
    >
    > However, a normal column list (without EXCEPT) only
    > specified columns and no more. Therefore, having a column list that
    > names all columns is not the same as having no column list at all, as
    > more columns may be added to the table later.
    >
    Fixed
    
    > ~
    >
    > 2b.
    > And the final sentence "If an column list..." looks like a cut/paste error (??)
    >
    Yes it was a mistake.
    
    > ~
    >
    > 2c.
    > Maybe here EXCEPT should be written as <literal>EXCEPT</literal>
    >
    Fixed.
    
    > ~~~
    >
    > 2.5A.
    > The description about generated columns still says this:
    >
    > CURRENT:
    > Generated columns can also be specified in a column list. This allows
    > generated columns to be published, regardless of the publication
    > parameter publish_generated_columns. See Section 29.6 for details.
    >
    > ~
    >
    > But I don't think it is quite correct. IMO gencols behaviour is much
    > more subtle...
    >
    > e.g.
    >
    > a) Normal collist - these named cols are published REGARDLESS of the
    > 'publish_generated_cols' parameter (same as before)
    >
    > b) EXCEPT collist - you can specify gencols in the list REGARDLESS of
    > the 'publish_generated_cols' parameter, because since they are named
    > as "except" then they will not be published anyhow....
    >
    > c) BUT for EXCEPT collist case, I think any gencols that are *not*
    > covered by that EXCEPT collist should follow the rules according to
    > the 'publish_generated_cols' parameter.
    >
    > So, it is much more tricky than the docs currently say:
    >
    Modified the documentation
    
    > Also
    >
    > 2.5B.
    > - The text says "See Section 29.6 for details," but there are no
    > examples of these combinations (e.g. EXCEPT collist and diff parameter
    > setting)
    >
    Added documentation.
    
    > 2.5C,
    > - The regression tests also need to be more complex to cover these
    >
    Added tests related to these
    
    > 2.5D.
    > - You might need to add something in the CREATE PUBLICATION "NOTES"
    > section after all -- even if it just refers to here.
    >
    Added documentation
    
    > ~~~
    >
    > 3.
    >     <para>
    >      Create a publication <literal>p1</literal>. A column list is defined for
    > -    table <literal>t1</literal> to reduce the number of columns that will be
    > -    replicated. Notice that the order of column names in the column list does
    > -    not matter.
    > +    table <literal>t1</literal>, and another column list is defined for table
    > +    <literal>t2</literal> using the EXCEPT clause to reduce the number of
    > +    columns that will be replicated. Note that the order of column names in
    > +    the column lists does not matter.
    >  <programlisting>
    > -/* pub # */ CREATE PUBLICATION p1 FOR TABLE t1 (id, b, a, d);
    > +/* pub # */ CREATE PUBLICATION p1 FOR TABLE t1 (id, b, a, d), t2 EXCEPT (d, a);
    >  </programlisting></para>
    >
    > Maybe here EXCEPT should be written as <literal>EXCEPT</literal>
    >
    Fixed
    
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 4.
    >       <para>
    > -      When a column list is specified, only the named columns are replicated.
    > -      The column list can contain stored generated columns as well. If the
    > -      column list is omitted, the publication will replicate all non-generated
    > -      columns (including any added in the future) by default. Stored generated
    > -      columns can also be replicated if
    > <literal>publish_generated_columns</literal>
    > -      is set to <literal>stored</literal>. Specifying a column list has no
    > -      effect on <literal>TRUNCATE</literal> commands. See
    > +      When a column list without EXCEPT is specified, only the named
    > columns are
    > +      replicated. The column list can contain stored generated columns as well.
    > +      If the column list is omitted, the publication will replicate
    > +      all non-generated columns (including any added in the future) by default.
    > +      Stored generated columns can also be replicated if
    > +      <literal>publish_generated_columns</literal> is set to
    > +      <literal>stored</literal>. Specifying a column list has no effect on
    > +      <literal>TRUNCATE</literal> commands. See
    >        <xref linkend="logical-replication-col-lists"/> for details about column
    >        lists.
    >       </para>
    >
    > Maybe here EXCEPT should be written as <literal>EXCEPT</literal>
    >
    Fixed
    > ~~~
    >
    > 5.
    > +     <para>
    > +      When a column list is specified with EXCEPT, the named columns are not
    > +      replicated. Specifying a column list has no effect on
    > +      <literal>TRUNCATE</literal> commands.
    > +     </para>
    >
    > Maybe here EXCEPT should be written as <literal>EXCEPT</literal>.
    >
    Fixed
    
    > ** Note all the extra subtleties that I mentioned in the review
    > comment #2.5 above --- e.g. IMO any *un-listed* gencols still should
    > follow the parameter rules.
    >
    > ~~~
    >
    > 6.
    >    <para>
    >     Any column list must include the <literal>REPLICA IDENTITY</literal> columns
    > -   in order for <command>UPDATE</command> or <command>DELETE</command>
    > -   operations to be published. There are no column list restrictions if the
    > -   publication publishes only <command>INSERT</command> operations.
    > +   and any column list specified with EXCEPT must not include the
    > +   <literal>REPLICA IDENTITY</literal> columns in order for
    > +   <command>UPDATE</command> or <command>DELETE</command> operations to be
    > +   published. There are no column list restrictions if the
    > publication publishes
    > +   only <command>INSERT</command> operations.
    >    </para>
    >
    > 6a.
    > CURRENT:
    > Any column list must include the REPLICA IDENTITY columns, and any
    > column list specified with EXCEPT must not include the REPLICA
    > IDENTITY columns in order for UPDATE or DELETE operations to be
    > published.
    >
    > ~
    >
    > I felt that might be better expressed the other way around. Also, it
    > might be better to say "not name" instead of "not include" because
    > EXCEPT + include seemed a bit contrary.
    >
    >
    > SUGGESTION (maybe like this)
    > In order for UPDATE or DELETE operations to work, all the REPLICA
    > IDENTITY columns must be published. So, any column list must name all
    > REPLICA IDENTITY columns, and any EXCEPT column list must not name any
    > REPLICA IDENTITY columns.
    >
    Fixed
    
    > ~~
    >
    > 6b.
    > Maybe here EXCEPT should be written as <literal>EXCEPT</literal>
    >
    Fixed
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > check_and_fetch_column_list:
    >
    > 7.
    > + /* Lookup the except attribute */
    > + cfdatum = SysCacheGetAttr(PUBLICATIONRELMAP, cftuple,
    > +   Anum_pg_publication_rel_prexcept, &isnull);
    > +
    > + if (!isnull)
    > + {
    > + Assert(!pub->alltables);
    > + *except_columns = DatumGetBool(cfdatum);
    > + }
    > +
    >
    > I felt it would be safer to also assign *except_columns = false;
    > up-front so the caller could be sure this flag was meaningful on
    > return.
    >
    Fixed
    
    > ~~~
    >
    > pub_form_cols_map:
    >
    > 8.
    > Maybe use snake case like for other params, so /excepcols/except_cols/
    >
    Fixed
    
    > ~~~
    >
    > pg_get_publication_tables:
    >
    > 9.
    >
    > I felt all the logic in this function maybe can be simpler:
    >
    > e.g. If you just have "Bitmapset *except_columns = NULL;" then null
    > nmeans there is no except columns; otherwise there is. This means you
    > don't need a separate 'bool except_column' variable.
    >
    > e.g. Assign the Bitmapset *except_columns after you already have the
    > values[2], instead of doing it later.
    >
    > e.g. The skip code if (except_columns && bms_is_member(att->attnum,
    > columns)) could just check the list member, I think, without the
    > additional bool.
    >
    > ~~~
    >
    Fixed
    
    > 10.
    > + /*
    > + * We fetch pubtuple if publication is not FOR ALL TABLES and not
    > + * FOR TABLES IN SCHEMA. So if prexcept is true, it indicate that
    > + * prattrs contains columns to be excluded for replication.
    > + */
    > + if (!isnull)
    > + except_columns = DatumGetBool(exceptDatum);
    >
    >
    > /indicate/indicates/
    >
    Fixed
    
    > ======
    > src/backend/parser/gram.y
    >
    > 11.
    > + | TABLE relation_expr EXCEPT opt_except_column_list OptWhereClause
    > + {
    > + $$ = makeNode(PublicationObjSpec);
    > + $$->pubobjtype = PUBLICATIONOBJ_TABLE;
    > + $$->pubtable = makeNode(PublicationTable);
    > + $$->pubtable->relation = $2;
    > + $$->pubtable->columns = $4;
    > + $$->pubtable->whereClause = $5;
    > + $$->pubtable->except = true;
    > + $$->location = @1;
    > + }
    >
    > I wasn't expecting you would need another 'opt_except_column_list' and
    > all the code duplication that causes. AFAIK, the syntax is identical
    > for 'opt_column_list' apart from the preceding EXCEPT so I thought all
    > you need is to allow the 'opt_column_list' to have an optional EXCEPT
    > qualifier.
    >
    The main reason I used a separate 'opt_except_column_list' is because
    'opt_column_list' can also be NULL. But the column list specified with
    EXCEPT not be NULL. So, 'opt_except_column_list' is defined such that
    it cannot be null.
    
    > ======
    > src/backend/replication/pgoutput/pgoutput.c
    >
    > 12.
    > +
    > + /*
    > + * Indicates whether no columns are published for a given relation. With
    > + * the introduction of the EXCEPT clause in column lists, it is now
    > + * possible to define a publication that excludes all columns of a table.
    > + * However, the 'columns' attribute cannot represent this case, since a
    > + * NULL value implies that all columns are published. To distinguish this
    > + * scenario, the 'no_cols_published' flag is introduced.
    > + */
    > + bool no_cols_published;
    >  } RelationSyncEntry;
    >
    > But, what about when Bitmapset *columns is not null, but has no bits
    > set -- doesn't that mean the same as "no columns"?
    >
    I think this is possible. A bitmapset which has no set bit is NULL. I
    saw following comment in bitmapset.c
    "By convention, we always represent a set with
     * the minimum possible number of words, i.e, there are never any trailing
     * zero words.  Enforcing this requires that an empty set is represented as
     * NULL.  Because an empty Bitmapset is represented as NULL, a non-NULL
     * Bitmapset always has at least 1 Bitmapword."
    
    > ======
    > src/include/catalog/pg_publication.h
    >
    > 13.
    >  extern Bitmapset *pub_form_cols_map(Relation relation,
    > - PublishGencolsType include_gencols_type);
    > + PublishGencolsType include_gencols_type,
    > + Bitmapset *exceptcols);
    >
    > Maybe snake-case like the other params: /exceptcols/except_cols/
    >
    Fixed
    
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 14.
    > +-- Verify that publication is created with EXCEPT
    > +CREATE PUBLICATION testpub_except FOR TABLE pub_test_except1,
    > pub_sch1.pub_test_except2 EXCEPT (b, c);
    > +SELECT * FROM pg_publication_tables WHERE pubname = 'testpub_except';
    > +
    >
    > I think tests should also use psql \dRp+ commands in places to show
    > that the "describe" stuff is working correctly.
    >
    > ~~~
    Fixed
    
    >
    > 15.
    > +-- Check for invalid cases
    > +CREATE PUBLICATION testpub_except2 FOR TABLES IN SCHEMA pub_sch1,
    > TABLE pub_test_except1 EXCEPT (b, c);
    > +CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except1 EXCEPT;
    >
    > Should explain more about what you are testing here:
    > a) cannot use EXCEPT col-lists combined with TABLES IN SCHEMA
    > b) syntax error EXCEPT without a col-list
    >
    > ~~~
    fixed
    
    >
    > 16.
    > +-- Verify that publication can be altered with EXCEPT
    > +ALTER PUBLICATION testpub_except SET TABLE pub_test_except1 EXCEPT
    > (a, b), pub_sch1.pub_test_except2;
    > +SELECT * FROM pg_publication_tables WHERE pubname = 'testpub_except';
    >
    > The comment is a bit misleading because there are many kinds of
    > "alter". Maybe say more like
    > Verify ok - ALTER PUBLICATION ... SET ... EXCEPT (col-list)
    >
    > ~~~
    Fixed
    
    >
    > 17.
    > +-- Verify ALTER PUBLICATION ... DROP
    > +ALTER PUBLICATION testpub_except DROP TABLE pub_test_except1 EXCEPT (a, b);
    > +ALTER PUBLICATION testpub_except DROP TABLE pub_test_except1;
    >
    > Should explain more:
    > +-- Verify fails - ALTER PUBLICATION ... DROP ... EXCEPT (col-list)
    > +-- Verify ok - ALTER PUBLICATION ... DROP ...
    >
    > ~~~
    Fixed
    
    >
    > 18.
    > +ALTER PUBLICATION testpub_except ADD TABLE pub_test_except1 EXCEPT (c, d);
    > +SELECT * FROM pg_publication_tables WHERE pubname = 'testpub_except';
    >
    > Missing comment:
    > +-- Verify ok - ALTER PUBLICATION ... ADD ... EXCEPT (col-list)
    >
    > ~~~
    Fixed
    
    >
    > 19.
    > +-- Verify excluded columns cannot be part of REPLICA IDENTITY
    > +ALTER TABLE pub_test_except1 REPLICA IDENTITY FULL;
    > +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    >
    > +CREATE UNIQUE INDEX pub_test_except1_a_idx ON pub_test_except1 (a, c);
    > +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    > pub_test_except1_a_idx;
    > +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    >
    > +DROP INDEX pub_test_except1_a_idx;
    > +CREATE UNIQUE INDEX pub_test_except1_a_idx ON pub_test_except1 (a);
    > +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    > pub_test_except1_a_idx;
    > +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    > +
    > +DROP INDEX pub_test_except1_a_idx;
    >
    > 19a.
    > IIUC, really there are multiple tests here, so I think it should all
    > be split and commented separately.
    >
    > a) Verify that EXCEPT col-list cannot contain RI cols (when using RI FULL)
    > b) Verify that EXCEPT col-list cannot contain RI cols (when using INDEX)
    > c) Verify that so long as no clash between RI cols and the EXCEPT
    > col-list, then it is ok
    >
    > ~
    Fixed
    
    >
    > 19b.
    > IMO, some index names could be better:
    >
    > CREATE UNIQUE INDEX pub_test_except1_a_idx ON pub_test_except1 (a, c);
    > How about 'pub_test_except1_ac_idx'?
    >
    > ~~~
    >
    Fixed
    
    > 20.
    > +DROP PUBLICATION testpub_except;
    > +DROP TABLE pub_test_except1;
    > +DROP TABLE pub_sch1.pub_test_except2;
    >
    > Add a "cleanup" comment.
    >
    Added
    
    I have addressed the comments and added the latest v16.
    
    Thanks and Regards,
    Shlok Kyal
    
  94. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-07-19T10:44:51Z

    On Mon, 30 Jun 2025 at 12:28, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Fri, Jun 27, 2025 at 3:44 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Thu, 26 Jun 2025 at 15:27, shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Tue, Jun 24, 2025 at 9:48 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > > >
    > > > >  I have included the changes for
    > > > > it in v14-0003 patch.
    > > > >
    > > > Thanks for the patches. I have reviewed patch001 alone, please find
    > > > few comments:
    > > >
    > > > 1)
    > > > +  <para>
    > > > +   The <literal>RESET</literal> clause will reset the publication to the
    > > > +   default state which includes resetting the publication parameters, setting
    > > > +   <literal>ALL TABLES</literal> flag to <literal>false</literal> and
    > > > +   dropping all relations and schemas that are associated with the
    > > > +   publication.
    > > >    </para>
    > > >
    > > > It is misleading, as far as I have understood, we do not drop the
    > > > tables or schemas associated with the pub; we just remove those from
    > > > the publication's object list. See previous doc:
    > > > "The ADD and DROP clauses will add and remove one or more
    > > > tables/schemas from the publication"
    > > >
    > > > Perhaps we want to say the same thing when we speak about the 'drop'
    > > > aspect of RESET.
    > > I have updated the document.
    > >
    > > > 2)
    > > > AlterPublicationReset():
    > > >
    > > > + if (!OidIsValid(prid))
    > > > + ereport(ERROR,
    > > > + (errcode(ERRCODE_UNDEFINED_OBJECT),
    > > > + errmsg("relation \"%s\" is not part of the publication",
    > > > + get_rel_name(relid))));
    > > >
    > > > Can you please help me understand which scenario will give this error?
    > > >
    > > > Another question is do we really need this error? IIUC, we generally
    > > > give errors if a user has explicitly called out a name of an object
    > > > and that object is not found. Example:
    > > >
    > > > postgres=# alter publication pubnew drop table t1,tab2;
    > > > ERROR:  relation "t1" is not part of the publication
    > > >
    > > > While in a few other cases, we pass missing_okay as true and do not
    > > > give errors. Please see other callers of performDeletion in
    > > > publicationcmds.c itself. There we have usage of missing_okay=true. I
    > > > have not researched myself, but please analyze the cases where
    > > > missing_okay is passed as true to figure out if those match our RESET
    > > > case. Try to reproduce if possible and then take a call.
    > > I thought about the above point and I also think this check is not
    > > required. Also, the function was calling PublicationDropSchemas with
    > > missing_ok as false. I have changed it to be true.
    > >
    >
    > Okay. Is there a reason for not using PublicationDropTables() here? We
    > have rewritten similar code in the Reset flow.
    >
    I feel it's better to use the function PublicationDropTables(). Also
    proper locking would be required on tables while dropping them from
    publication.
    Made changes for the same.
    
    > > > 3)
    > > > +ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA public;
    > > > +ERROR:  syntax error at or near "ALL"
    > > > +LINE 1: ALTER PUBLICATION testpub_reset ADD ALL TABLES IN SCHEMA pub...
    > > >
    > > > There is a problem in syntax, I think the intention of testcase was to
    > > > run this query successfully.
    > >
    > > I have fixed it.
    > >
    > > Thanks Shveta for reviewing the patch. I have addressed the comments
    > > and posted an updated version v15 in [1].
    >
    > Thanks for the patches. My review is in progress but please find few
    > comments on 002:
    >
    > 1)
    > where exception_object is:
    >     [ ONLY ] table_name [ * ]
    >
    > We have the above in CREATE and ALTER pub docs, but we do not explain
    > ONLY with EXCEPT. We do have an explanation of ONLY under 'FOR TABLE'.
    > But since 'FOR TABLE' and 'EXCEPT' do not go together, it is somewhat
    > difficult to connect the dots and find the information ONLY in the
    > context of EXCEPT. We shall have ONLY explained for EXCEPT as well. Or
    > we can have ONLY defined in a way that both 'FOR TABLE' and 'EXCEPT'
    > can refer to it.
    >
    In create_publication.sgml, added it under "EXCEPT_TABLE'. In
    alter_publication.sgml, modified the document under item 'table_name'
    under "<title>Parameters</title>"
    
    > 2)
    > We get tab-completion options in this command:
    > postgres=# create publication pub5 for TABLE tab1 W
    > WHERE (  WITH (
    >
    > Similarly in this command:
    > create publication pub5 for TABLES IN SCHEMA s1
    >
    > But once we have 'EXCEPT TABLE', we do not get further tab-completion
    > option like WITH(...)
    > create publication pub5 for ALL TABLES EXCEPT TABLE tab1
    Fixed
    
    > 3)
    > During tab-expansion, 'EXCEPT TABLE' and  'WITH (' in the below
    > command looks like they are connecting words. Can the gap be increased
    > similar to tab-expansion of next command shown below:
    >
    > postgres=# create publication pub4 for ALL TABLES
    > EXCEPT TABLE  WITH (
    >
    I did not find a place to add any custom space. It is default
    behaviour to add 2 spaces between different words. See similar:
    postgres=# CREATE PUBLICATION pub1 FOR TABLE t1 W
    WHERE (  WITH (
    
    > postgres=# create publication pub4 for
    > ALL TABLES        TABLE             TABLES IN SCHEMA
    >
    I observed that the space between word is dependent on the length of
    longest word. Here the longest word is "TABLES IN SCHEMA". The space
    between the words are quite noticeable.
    
    > 4)
    > alter_publication.sgml.orig is a left-over in patch002.
    Fixed
    
    I have added the changes in the latest v16 patch [1].
    [1]: https://www.postgresql.org/message-id/CANhcyEW2LK4diNeCG862DE40yQoV3VAgf59kXUq2TuR8fnw5vQ%40mail.gmail.com
    
    Thanks and Regards,
    Shlok Kyal
    
    
    
    
  95. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-07-19T10:45:42Z

    On Mon, 30 Jun 2025 at 11:54, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok,
    >
    > One more thing, I noticed there is no tab-completion code yet for this
    > new EXCEPT (column_list) syntax.
    >
    
    I have added the tab-completion code in the latest v16 patch [1].
    [1]: https://www.postgresql.org/message-id/CANhcyEW2LK4diNeCG862DE40yQoV3VAgf59kXUq2TuR8fnw5vQ%40mail.gmail.com
    
    Thanks and Regards,
    Shlok Kyal
    
    
    
    
  96. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-07-19T10:47:06Z

    On Mon, 30 Jun 2025 at 16:25, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > Few more comments on 002:
    >
    > 5)
    > +GetAllTablesPublicationRelations(Oid pubid, bool pubviaroot)
    >  {
    >
    > + List    *exceptlist;
    > +
    > + exceptlist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    >
    >
    > a) Here, we are assuming that the list provided by
    > GetPublicationRelations() will be except-tables list only, but there
    > is no validation of that.
    > b) We are using GetPublicationRelations() to get the relations which
    > are excluded from the publication. The name of function and comments
    > atop function are not in alignment with this usage.
    >
    > Suggestion:
    > We can have a new GetPublicationExcludeRelations() function for the
    > concerned usage. The existing logic of GetPublicationRelations() can
    > be shifted to a new internal-logic function which will accept a
    > 'except-flag' as well. Both GetPublicationRelations() and
    > GetPublicationExcludeRelations() can call that new function by passing
    > 'except-flag' as false and true respectively. The new internal
    > function will validate 'prexcept' against that except-flag passed and
    > will return the results.
    >
    I have made the above change.
    
    
    > 6)
    > Before your patch002, GetTopMostAncestorInPublication() was checking
    > pg_publication_rel and pg_publication_namespace to find out if the
    > table in the ancestor-list is part of a given particular. Both
    > pg_publication_rel and pg_publication_namespace did not have the entry
    > "for all tables" publications. That means
    > GetTopMostAncestorInPublication() was originally not checking whether
    > the given puboid is an "for all tables" publication to see if a rel
    > belongs to that particular pub or not. I
    >
    > But now with the current change, we do check if pub is all-tables pub,
    > if so, return relid and mark ancestor_level (provided table is not
    > part of the except list).  IIUC, the result in 2 cases may be
    > different. Is that the intention? Let me know if my understanding is
    > wrong.
    >
    This is intentional, in function get_rel_sync_entry, we are setting
    pub_relid to the topmost published ancestor. In HEAD we are directly
    setting using:
                /*
                 * If this is a FOR ALL TABLES publication, pick the partition
                 * root and set the ancestor level accordingly.
                 */
                if (pub->alltables)
                {
                    publish = true;
                    if (pub->pubviaroot && am_partition)
                    {
                        List       *ancestors = get_partition_ancestors(relid);
    
                        pub_relid = llast_oid(ancestors);
                        ancestor_level = list_length(ancestors);
                    }
                }
    In HEAD, we can directly use 'llast_oid(ancestors)' to get the topmost
    ancestor for case of FOR ALL TABLES.
    But with this proposal. This change will no longer be valid as the
    'llast_oid(ancestors)' may be excluded in the publication. So, to
    handle this change was made in GetTopMostAncestorInPublication.
    
    
    Also, during testing with the partitioned table and
    publish_via_partition_root the behaviour of the current patch is  as
    below:
    For example we have a partitioned table t1. It has partitions part1
    and part2. Now consider the following cases:
    1. with publish_via_partition_root = true
         I. If we create publication on all tables with EXCEPT t1, no data
    for t1, part1 or part2 is replicated.
         II.  If we create publication on all tables with EXCEPT part1,
    data for all tables t1, part1 and part2 is replicated.
    2. with publish_via_partition_root = false
         I. If we create publication on all tables with EXCEPT t1, no data
    for t1, part1 or part2 is replicated.
         II. If we create publication on all tables with EXCEPT part1,
    data for part1 is not replicated
    
    Is this behaviour fine?
    I checked for other databases such as MySQL, SQL Server. In that we do
    not have such cases as either we replicate the whole partitioned table
    or we not replicated at all. We do not have partition level control.
    For Oracle, I found that we can include or exclude partitions using
    'PARTITIONEXCLUDE' [2], but did not find something similar to
    publish_via_partition_root or where partitions are published as
    separate tables.
    What are your thoughts on the above behaviour?
    
    I have addressed the comments and added the changes in the latest v16 patch [1].
    [1]:https://www.postgresql.org/message-id/CANhcyEW2LK4diNeCG862DE40yQoV3VAgf59kXUq2TuR8fnw5vQ%40mail.gmail.com
    [2]: https://docs.oracle.com/en/middleware/goldengate/core/23/reference/partition-partitionexclude.html
    Thanks,
    Shlok Kyal
    
    
    
    
  97. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-07-21T06:46:48Z

    Hi Shlok.
    
    Some review comments for patch v16-0003.
    
    ======
    Commit message
    
    1.
    The column "prexcept" of system catalog "pg_publication_rel" is set to
    "true" when publication is created with EXCEPT table or EXCEPT column
    list. If column "prattrs" of system catalog "pg_publication_rel" is also
    set or column "puballtables" of system catalog "pg_publication" is
    "false", it indicates the column list is specified with EXCEPT clause
    and columns in "prattrs" are excluded from being published.
    
    ~
    
    Somehow, this seems to contain too much information, making it a bit
    confusing. Can't you chop this down to something like below?
    
    SUGESTION
    When column "prexcept" of system catalog "pg_publication_rel" is set
    to "true", and column "prattrs" of system catalog "pg_publication_rel"
    is not NULL, that means the publication was created with "EXCEPT
    (column-list)", and the columns in "prattrs" will be excluded from
    being published.
    
    ======
    doc/src/sgml/logical-replication.sgml
    
    2.
        Generated columns can also be specified in a column list. This allows
        generated columns to be published, regardless of the publication parameter
        <link linkend="sql-createpublication-params-with-publish-generated-columns">
    +   <literal>publish_generated_columns</literal></link>. Generated
    columns can be
    +   specified in a column list using the <literal>EXCEPT</literal> clause. This
    +   excludes the specified generated columns from being published, regardless of
    +   the <link linkend="sql-createpublication-params-with-publish-generated-columns">
    +   <literal>publish_generated_columns</literal></link> setting. However, for
    +   generated columns that are not listed in the <literal>EXCEPT</literal>
    +   clause, whether they are published or not still depends on the value of
    +   <link linkend="sql-createpublication-params-with-publish-generated-columns">
        <literal>publish_generated_columns</literal></link>. See
        <xref linkend="logical-replication-gencols"/> for details.
       </para>
    
    ~~
    
    For this part:
    
    "Generated columns can be specified in a column list using the
    <literal>EXCEPT</literal> clause. This excludes the specified
    generated columns from being published, regardless of..."
    
    I think the whole paragraph already said "Generated columns can also
    be specified in a column list", so you don't need to repeat it.
    Instead, maybe say something like below.
    
    SUGGESTION
    Specifying generated columns in a column list using the
    <literal>EXCEPT</literal> clause excludes those columns from being
    published, regardless of...
    
    ~~~
    
    3.
    -                               Publication p1
    -  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Via root
    -----------+------------+---------+---------+---------+-----------+----------
    - postgres | f          | t       | t       | t       | t         | f
    +                                        Publication p1
    + Owner  | All tables | Inserts | Updates | Deletes | Truncates |
    Generated columns | Via root
    +--------+------------+---------+---------+---------+-----------+-------------------+----------
    + ubuntu | f          | t       | t       | t       | t         | none
                 | f
     Tables:
         "public.t1" (id, a, b, d)
    +    "public.t2" EXCEPT (a, d)
     </programlisting></para>
    
    
    I noticed the Owner changed from "postgres" to "ubuntu". Do you think
    it is better to keep this as "postgres" for the example?
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    4.
    The tables added to a publication that publishes UPDATE and/or DELETE
    operations must have REPLICA IDENTITY defined. Otherwise those
    operations will be disallowed on those tables.
    
    In order for UPDATE or DELETE operations to work, all the REPLICA
    IDENTITY columns must be published. So, any column list must name all
    REPLICA IDENTITY columns, and any EXCEPT column list must not name any
    REPLICA IDENTITY columns.
    
    A row filter expression (i.e., the WHERE clause) must contain only
    columns that are covered by the REPLICA IDENTITY, in order for UPDATE
    and DELETE operations to be published. For publication of INSERT
    operations, any column may be used in the WHERE expression. The row
    filter allows simple expressions that don't have user-defined
    functions, user-defined operators, user-defined types, user-defined
    collations, non-immutable built-in functions, or references to system
    columns.
    
    The generated columns that are part of the column list specified with
    the EXCEPT clause are not published, regardless of the
    publish_generated_columns option. However, generated columns that are
    not part of the column list specified with the EXCEPT clause are
    published according to the value of the publish_generated_columns
    option. See Section 29.6 for details.
    
    The generated columns that are part of REPLICA IDENTITY must be
    published explicitly either by listing them in the column list or by
    enabling the publish_generated_columns option, in order for UPDATE and
    DELETE operations to be published.
    
    ~~
    
    Notice all those 5 paragraphs (above) are talking about REPLICA
    IDENTITY, except the 4th paragraph. Maybe the 4th paragraph should be
    moved to last, to keep all the REPLICA IDENTITY stuff together.
    
    ======
    src/backend/catalog/pg_publication.c
    
    5. pub_form_cols_map
    
      * Returns a bitmap representing the columns of the specified table.
      *
      * Generated columns are included if include_gencols_type is
    - * PUBLISH_GENCOLS_STORED.
    + * PUBLISH_GENCOLS_STORED. Columns that are in the exceptcols are excluded from
    + * the column list.
      */
     Bitmapset *
    -pub_form_cols_map(Relation relation, PublishGencolsType include_gencols_type)
    +pub_form_cols_map(Relation relation, PublishGencolsType include_gencols_type,
    +   Bitmapset *except_cols)
    
    Forgot to add the underscore in the function comment.
    
    /exceptcols/except_cols/
    
    ~~~
    
    6. pg_get_publication_tables
    
    +
    + /*
    + * We fetch pubtuple if publication is not FOR ALL TABLES and not
    + * FOR TABLES IN SCHEMA. So if prexcept is true, it indicates that
    + * prattrs contains columns to be excluded for replication.
    + */
    + exceptDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
    +   Anum_pg_publication_rel_prexcept,
    +   &isnull);
    +
    + if (!isnull && DatumGetBool(exceptDatum) && !nulls[2])
    + except_columns = pub_collist_to_bitmapset(NULL, values[2], NULL);
    
    But, you cannot have EXCEPT for null column list, so shouldn't the
    !nulls[2] check be done to also guard the SysCacheGetAttr call?
    
    ======
    src/backend/parser/gram.y
    
    7.
    
    Shlok wrote [1-reply #11]
    The main reason I used a separate 'opt_except_column_list' is because
    'opt_column_list' can also be NULL. But the column list specified with
    EXCEPT not be NULL. So, 'opt_except_column_list' is defined such that
    it cannot be null.
    
    ~
    
    Yeah, but IMO that leads to excessive duplicated code. I think the
    code can perhaps be a lot simpler if the grammar is written more like
    the synopsis:
    
    e.g. TABLE name opt_EXCEPT opt_column_list
    
    where - opt_EXCEPT is null, and opt_column_list is null... means no col list
    where - opt_EXCEPT is null, and opt_column_list is not null... means
    normal col list
    where - opt_EXCEPT is not null, and opt_column_list not null... means
    EXCEPT col list
    where - opt_EXCEPT is not null, and opt_column_list null... SYNTAX ERROR
    
    So code it something like this (just adding opt_EXCEPT to the existing
    productions)
    
    %type <boolean> opt_ordinality opt_without_overlaps opt_EXCEPT
    ...
    opt_EXCEPT:
    EXCEPT { $$ = true; }
    | /*EMPTY*/ { $$ = false; }
    ;
    ...
    TABLE relation_expr opt_EXCEPT opt_column_list OptWhereClause
    {
      $$ = makeNode(PublicationObjSpec);
      $$->pubobjtype = PUBLICATIONOBJ_TABLE;
      $$->pubtable = makeNode(PublicationTable);
      $$->pubtable->relation = $2;
      $$->pubtable->except = $3;
      $$->pubtable->columns = $4;
      if ($3 && !$4)
        ereport(ERROR,
          (errcode(ERRCODE_SYNTAX_ERROR),
          errmsg("EXCEPT without column list"),
          parser_errposition(@3)));
      $$->pubtable->whereClause = $5;
      $$->location = @1;
    }
    
    etc.
    
    ======
    src/bin/psql/describe.c
    
    8.
      if (!PQgetisnull(res, i, 3))
    + {
    + if (!PQgetisnull(res, i, 4) && strcmp(PQgetvalue(res, i, 4), "t") == 0)
    + appendPQExpBuffer(buf, " EXCEPT");
      appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
    + }
    
    This growing list of columns makes it hard to understand this function
    without looking back at the caller all the time. Maybe you can add a
    function comment that at least explains what those attributes 1,2,3,4
    represent?
    
    ======
    src/bin/psql/tab-complete.in.c
    
    9.
    + else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET",
    "TABLE", MatchAny))
    + COMPLETE_WITH("EXCEPT");
    
    Since it is not allowed to have an EXCEPT with no column list,
    shouldn't this say "EXCEPT ("?
    
    ~~~
    
    10.
      else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE",
    MatchAny) && !ends_with(prev_wd, ','))
    - COMPLETE_WITH("WHERE (", "WITH (");
    + COMPLETE_WITH("EXCEPT", "WHERE (", "WITH (");
    
    Ditto. Since it is not allowed to have an EXCEPT with no column list,
    shouldn't this say "EXCEPT ("?
    
    
    ======
    src/test/regress/expected/publication.out
    
    11.
    +-- Verify that EXCEPT col-list cannot contain RI cols (when using INDEX)
    +CREATE UNIQUE INDEX pub_test_except1_ac_idx ON pub_test_except1 (a, c);
    +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    pub_test_except1_a_idx;
    +ERROR:  index "pub_test_except1_a_idx" for table "pub_test_except1"
    does not exist
    +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    +ERROR:  cannot update table "pub_test_except1"
    +DETAIL:  Column list used by the publication does not cover the
    replica identity.
    +DROP INDEX pub_test_except1_ac_idx;
    
    
    What's happening here? I'm not sure these are the kind of errors you
    were trying to cause.
    
    ======
    src/test/regress/sql/publication.sql
    
    12.
    +-- Verify that EXCEPT col-list cannot contain RI cols (when using RI FULL)
    +ALTER TABLE pub_test_except1 REPLICA IDENTITY FULL;
    +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    
    
    SUGGESTION. Change that comment to:
    Verify fails - EXCEPT col-list cannot...
    
    ~~~
    
    13.
    +-- Verify that EXCEPT col-list cannot contain RI cols (when using INDEX)
    +CREATE UNIQUE INDEX pub_test_except1_ac_idx ON pub_test_except1 (a, c);
    +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    pub_test_except1_a_idx;
    +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    +DROP INDEX pub_test_except1_ac_idx;
    
    SUGGESTION. Change that comment to:
    Verify fails - EXCEPT col-list cannot...
    
    ~~~
    
    14.
    +-- Verify that so long as no clash between RI cols and the EXCEPT
    +CREATE UNIQUE INDEX pub_test_except1_a_idx ON pub_test_except1 (a);
    +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    pub_test_except1_a_idx;
    +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    +
    
    That comment doesn't make sense. Missing words?
    
    ======
    .../t/036_rep_changes_except_table.pl
    
    15.
    (I haven't reviewed this file in detail yet, but here is a general comment)
    
    I know this patch currently lives in the same thread as all the EXCEPT
    TABLE stuff, but that seems just happenstance to me. IMO, this is a
    separate enhancement that just shares the keyword EXCEPT. So, I felt
    it should have quite separate tests too.
    
    e.g. How about: 037_rep_changes_except_collist.pl
    
    ======
    [1] https://www.postgresql.org/message-id/CANhcyEW2LK4diNeCG862DE40yQoV3VAgf59kXUq2TuR8fnw5vQ%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  98. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-07-21T10:52:42Z

    On Sat, Jul 19, 2025 at 4:17 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Mon, 30 Jun 2025 at 16:25, shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > Few more comments on 002:
    > >
    > > 5)
    > > +GetAllTablesPublicationRelations(Oid pubid, bool pubviaroot)
    > >  {
    > >
    > > + List    *exceptlist;
    > > +
    > > + exceptlist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    > >
    > >
    > > a) Here, we are assuming that the list provided by
    > > GetPublicationRelations() will be except-tables list only, but there
    > > is no validation of that.
    > > b) We are using GetPublicationRelations() to get the relations which
    > > are excluded from the publication. The name of function and comments
    > > atop function are not in alignment with this usage.
    > >
    > > Suggestion:
    > > We can have a new GetPublicationExcludeRelations() function for the
    > > concerned usage. The existing logic of GetPublicationRelations() can
    > > be shifted to a new internal-logic function which will accept a
    > > 'except-flag' as well. Both GetPublicationRelations() and
    > > GetPublicationExcludeRelations() can call that new function by passing
    > > 'except-flag' as false and true respectively. The new internal
    > > function will validate 'prexcept' against that except-flag passed and
    > > will return the results.
    > >
    > I have made the above change.
    >
    >
    > > 6)
    > > Before your patch002, GetTopMostAncestorInPublication() was checking
    > > pg_publication_rel and pg_publication_namespace to find out if the
    > > table in the ancestor-list is part of a given particular. Both
    > > pg_publication_rel and pg_publication_namespace did not have the entry
    > > "for all tables" publications. That means
    > > GetTopMostAncestorInPublication() was originally not checking whether
    > > the given puboid is an "for all tables" publication to see if a rel
    > > belongs to that particular pub or not. I
    > >
    > > But now with the current change, we do check if pub is all-tables pub,
    > > if so, return relid and mark ancestor_level (provided table is not
    > > part of the except list).  IIUC, the result in 2 cases may be
    > > different. Is that the intention? Let me know if my understanding is
    > > wrong.
    > >
    > This is intentional, in function get_rel_sync_entry, we are setting
    > pub_relid to the topmost published ancestor. In HEAD we are directly
    > setting using:
    >             /*
    >              * If this is a FOR ALL TABLES publication, pick the partition
    >              * root and set the ancestor level accordingly.
    >              */
    >             if (pub->alltables)
    >             {
    >                 publish = true;
    >                 if (pub->pubviaroot && am_partition)
    >                 {
    >                     List       *ancestors = get_partition_ancestors(relid);
    >
    >                     pub_relid = llast_oid(ancestors);
    >                     ancestor_level = list_length(ancestors);
    >                 }
    >             }
    > In HEAD, we can directly use 'llast_oid(ancestors)' to get the topmost
    > ancestor for case of FOR ALL TABLES.
    > But with this proposal. This change will no longer be valid as the
    > 'llast_oid(ancestors)' may be excluded in the publication. So, to
    > handle this change was made in GetTopMostAncestorInPublication.
    >
    >
    > Also, during testing with the partitioned table and
    > publish_via_partition_root the behaviour of the current patch is  as
    > below:
    > For example we have a partitioned table t1. It has partitions part1
    > and part2. Now consider the following cases:
    > 1. with publish_via_partition_root = true
    >      I. If we create publication on all tables with EXCEPT t1, no data
    > for t1, part1 or part2 is replicated.
    >      II.  If we create publication on all tables with EXCEPT part1,
    > data for all tables t1, part1 and part2 is replicated.
    > 2. with publish_via_partition_root = false
    >      I. If we create publication on all tables with EXCEPT t1, no data
    > for t1, part1 or part2 is replicated.
    >      II. If we create publication on all tables with EXCEPT part1,
    > data for part1 is not replicated
    >
    > Is this behaviour fine?
    > I checked for other databases such as MySQL, SQL Server. In that we do
    > not have such cases as either we replicate the whole partitioned table
    > or we not replicated at all. We do not have partition level control.
    > For Oracle, I found that we can include or exclude partitions using
    > 'PARTITIONEXCLUDE' [2], but did not find something similar to
    > publish_via_partition_root or where partitions are published as
    > separate tables.
    > What are your thoughts on the above behaviour?
    >
    
    Thank You for the details. I will review this behaviour soon and will
    let you know my comments. Meanwhile, please find a few comments on
    v16-0001:
    
    1)
    we do LockSchemaList() everywhere before we call
    PublicationDropSchemas() to prevent concurrent schema deletion. Do we
    need that in reset flow as well?
    
    2)
    + /* Drop the schemas associated with the publication */
    + schemas = GetPublicationSchemas(pubid);
    + PublicationDropSchemas(pubid, schemas, true);
    +
    + /* Get all relations associated with the publication */
    + relids = GetPublicationRelations(pubid, PUBLICATION_PART_ROOT);
    
    We can rename schemas to schemaids similar to relids, as
    GetPublicationSchemas return oids.
    
    3)
    + /* Drop the relations associated with the publication */
    + PublicationDropTables(pubform->oid, rels, true);
    
    we can pass 'pubid' here instead of pubform->oid
    
    4)
    Shall we modify the comments:
    'Drop the relations associated with the publication' to 'Remove the
    associated relations from the publication'
    'Drop the schemas associated with the publication'  to 'Remove the
    associated schemas from the publication'
    
    Similar changes can be done in test file's comments as well
    --Verify that tables associated with the publication are dropped after
    RESET
    --Verify that schemas associated with the publication are dropped after RESET
    
    thanks
    Shveta
    
    
    
    
  99. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-07-21T19:33:37Z

    On Mon, 21 Jul 2025 at 12:17, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Some review comments for patch v16-0003.
    >
    > ======
    > Commit message
    >
    > 1.
    > The column "prexcept" of system catalog "pg_publication_rel" is set to
    > "true" when publication is created with EXCEPT table or EXCEPT column
    > list. If column "prattrs" of system catalog "pg_publication_rel" is also
    > set or column "puballtables" of system catalog "pg_publication" is
    > "false", it indicates the column list is specified with EXCEPT clause
    > and columns in "prattrs" are excluded from being published.
    >
    > ~
    >
    > Somehow, this seems to contain too much information, making it a bit
    > confusing. Can't you chop this down to something like below?
    >
    > SUGESTION
    > When column "prexcept" of system catalog "pg_publication_rel" is set
    > to "true", and column "prattrs" of system catalog "pg_publication_rel"
    > is not NULL, that means the publication was created with "EXCEPT
    > (column-list)", and the columns in "prattrs" will be excluded from
    > being published.
    >
    Modified the commit message as per suggestion.
    
    > ======
    > doc/src/sgml/logical-replication.sgml
    >
    > 2.
    >     Generated columns can also be specified in a column list. This allows
    >     generated columns to be published, regardless of the publication parameter
    >     <link linkend="sql-createpublication-params-with-publish-generated-columns">
    > +   <literal>publish_generated_columns</literal></link>. Generated
    > columns can be
    > +   specified in a column list using the <literal>EXCEPT</literal> clause. This
    > +   excludes the specified generated columns from being published, regardless of
    > +   the <link linkend="sql-createpublication-params-with-publish-generated-columns">
    > +   <literal>publish_generated_columns</literal></link> setting. However, for
    > +   generated columns that are not listed in the <literal>EXCEPT</literal>
    > +   clause, whether they are published or not still depends on the value of
    > +   <link linkend="sql-createpublication-params-with-publish-generated-columns">
    >     <literal>publish_generated_columns</literal></link>. See
    >     <xref linkend="logical-replication-gencols"/> for details.
    >    </para>
    >
    > ~~
    >
    > For this part:
    >
    > "Generated columns can be specified in a column list using the
    > <literal>EXCEPT</literal> clause. This excludes the specified
    > generated columns from being published, regardless of..."
    >
    > I think the whole paragraph already said "Generated columns can also
    > be specified in a column list", so you don't need to repeat it.
    > Instead, maybe say something like below.
    >
    > SUGGESTION
    > Specifying generated columns in a column list using the
    > <literal>EXCEPT</literal> clause excludes those columns from being
    > published, regardless of...
    >
    > ~~~
    >
    Modified
    
    > 3.
    > -                               Publication p1
    > -  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Via root
    > -----------+------------+---------+---------+---------+-----------+----------
    > - postgres | f          | t       | t       | t       | t         | f
    > +                                        Publication p1
    > + Owner  | All tables | Inserts | Updates | Deletes | Truncates |
    > Generated columns | Via root
    > +--------+------------+---------+---------+---------+-----------+-------------------+----------
    > + ubuntu | f          | t       | t       | t       | t         | none
    >              | f
    >  Tables:
    >      "public.t1" (id, a, b, d)
    > +    "public.t2" EXCEPT (a, d)
    >  </programlisting></para>
    >
    >
    > I noticed the Owner changed from "postgres" to "ubuntu". Do you think
    > it is better to keep this as "postgres" for the example?
    I agree that it is better to keep "postgres". I have reverted back to
    the use "postgres"..
    
    >
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 4.
    > The tables added to a publication that publishes UPDATE and/or DELETE
    > operations must have REPLICA IDENTITY defined. Otherwise those
    > operations will be disallowed on those tables.
    >
    > In order for UPDATE or DELETE operations to work, all the REPLICA
    > IDENTITY columns must be published. So, any column list must name all
    > REPLICA IDENTITY columns, and any EXCEPT column list must not name any
    > REPLICA IDENTITY columns.
    >
    > A row filter expression (i.e., the WHERE clause) must contain only
    > columns that are covered by the REPLICA IDENTITY, in order for UPDATE
    > and DELETE operations to be published. For publication of INSERT
    > operations, any column may be used in the WHERE expression. The row
    > filter allows simple expressions that don't have user-defined
    > functions, user-defined operators, user-defined types, user-defined
    > collations, non-immutable built-in functions, or references to system
    > columns.
    >
    > The generated columns that are part of the column list specified with
    > the EXCEPT clause are not published, regardless of the
    > publish_generated_columns option. However, generated columns that are
    > not part of the column list specified with the EXCEPT clause are
    > published according to the value of the publish_generated_columns
    > option. See Section 29.6 for details.
    >
    > The generated columns that are part of REPLICA IDENTITY must be
    > published explicitly either by listing them in the column list or by
    > enabling the publish_generated_columns option, in order for UPDATE and
    > DELETE operations to be published.
    >
    > ~~
    >
    > Notice all those 5 paragraphs (above) are talking about REPLICA
    > IDENTITY, except the 4th paragraph. Maybe the 4th paragraph should be
    > moved to last, to keep all the REPLICA IDENTITY stuff together.
    >
    Fixed
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > 5. pub_form_cols_map
    >
    >   * Returns a bitmap representing the columns of the specified table.
    >   *
    >   * Generated columns are included if include_gencols_type is
    > - * PUBLISH_GENCOLS_STORED.
    > + * PUBLISH_GENCOLS_STORED. Columns that are in the exceptcols are excluded from
    > + * the column list.
    >   */
    >  Bitmapset *
    > -pub_form_cols_map(Relation relation, PublishGencolsType include_gencols_type)
    > +pub_form_cols_map(Relation relation, PublishGencolsType include_gencols_type,
    > +   Bitmapset *except_cols)
    >
    > Forgot to add the underscore in the function comment.
    >
    > /exceptcols/except_cols/
    >
    Fixed
    
    > ~~~
    >
    > 6. pg_get_publication_tables
    >
    > +
    > + /*
    > + * We fetch pubtuple if publication is not FOR ALL TABLES and not
    > + * FOR TABLES IN SCHEMA. So if prexcept is true, it indicates that
    > + * prattrs contains columns to be excluded for replication.
    > + */
    > + exceptDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
    > +   Anum_pg_publication_rel_prexcept,
    > +   &isnull);
    > +
    > + if (!isnull && DatumGetBool(exceptDatum) && !nulls[2])
    > + except_columns = pub_collist_to_bitmapset(NULL, values[2], NULL);
    >
    > But, you cannot have EXCEPT for null column list, so shouldn't the
    > !nulls[2] check be done to also guard the SysCacheGetAttr call?
    >
    Fixed
    
    > ======
    > src/backend/parser/gram.y
    >
    > 7.
    >
    > Shlok wrote [1-reply #11]
    > The main reason I used a separate 'opt_except_column_list' is because
    > 'opt_column_list' can also be NULL. But the column list specified with
    > EXCEPT not be NULL. So, 'opt_except_column_list' is defined such that
    > it cannot be null.
    >
    > ~
    >
    > Yeah, but IMO that leads to excessive duplicated code. I think the
    > code can perhaps be a lot simpler if the grammar is written more like
    > the synopsis:
    >
    > e.g. TABLE name opt_EXCEPT opt_column_list
    >
    > where - opt_EXCEPT is null, and opt_column_list is null... means no col list
    > where - opt_EXCEPT is null, and opt_column_list is not null... means
    > normal col list
    > where - opt_EXCEPT is not null, and opt_column_list not null... means
    > EXCEPT col list
    > where - opt_EXCEPT is not null, and opt_column_list null... SYNTAX ERROR
    >
    > So code it something like this (just adding opt_EXCEPT to the existing
    > productions)
    >
    > %type <boolean> opt_ordinality opt_without_overlaps opt_EXCEPT
    > ...
    > opt_EXCEPT:
    > EXCEPT { $$ = true; }
    > | /*EMPTY*/ { $$ = false; }
    > ;
    > ...
    > TABLE relation_expr opt_EXCEPT opt_column_list OptWhereClause
    > {
    >   $$ = makeNode(PublicationObjSpec);
    >   $$->pubobjtype = PUBLICATIONOBJ_TABLE;
    >   $$->pubtable = makeNode(PublicationTable);
    >   $$->pubtable->relation = $2;
    >   $$->pubtable->except = $3;
    >   $$->pubtable->columns = $4;
    >   if ($3 && !$4)
    >     ereport(ERROR,
    >       (errcode(ERRCODE_SYNTAX_ERROR),
    >       errmsg("EXCEPT without column list"),
    >       parser_errposition(@3)));
    >   $$->pubtable->whereClause = $5;
    >   $$->location = @1;
    > }
    >
    > etc.
    >
    I have modified it. I have created a function 'check_except_collist'
    to throw error, to avoid duplication code for error message.
    
    > ======
    > src/bin/psql/describe.c
    >
    > 8.
    >   if (!PQgetisnull(res, i, 3))
    > + {
    > + if (!PQgetisnull(res, i, 4) && strcmp(PQgetvalue(res, i, 4), "t") == 0)
    > + appendPQExpBuffer(buf, " EXCEPT");
    >   appendPQExpBuffer(buf, " (%s)", PQgetvalue(res, i, 3));
    > + }
    >
    > This growing list of columns makes it hard to understand this function
    > without looking back at the caller all the time. Maybe you can add a
    > function comment that at least explains what those attributes 1,2,3,4
    > represent?
    >
    Added a comment
    
    > ======
    > src/bin/psql/tab-complete.in.c
    >
    > 9.
    > + else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET",
    > "TABLE", MatchAny))
    > + COMPLETE_WITH("EXCEPT");
    >
    > Since it is not allowed to have an EXCEPT with no column list,
    > shouldn't this say "EXCEPT ("?
    >
    Fixed
    
    > ~~~
    >
    > 10.
    >   else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE",
    > MatchAny) && !ends_with(prev_wd, ','))
    > - COMPLETE_WITH("WHERE (", "WITH (");
    > + COMPLETE_WITH("EXCEPT", "WHERE (", "WITH (");
    >
    > Ditto. Since it is not allowed to have an EXCEPT with no column list,
    > shouldn't this say "EXCEPT ("?
    >
    Fixed
    
    >
    > ======
    > src/test/regress/expected/publication.out
    >
    > 11.
    > +-- Verify that EXCEPT col-list cannot contain RI cols (when using INDEX)
    > +CREATE UNIQUE INDEX pub_test_except1_ac_idx ON pub_test_except1 (a, c);
    > +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    > pub_test_except1_a_idx;
    > +ERROR:  index "pub_test_except1_a_idx" for table "pub_test_except1"
    > does not exist
    > +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    > +ERROR:  cannot update table "pub_test_except1"
    > +DETAIL:  Column list used by the publication does not cover the
    > replica identity.
    > +DROP INDEX pub_test_except1_ac_idx;
    >
    >
    > What's happening here? I'm not sure these are the kind of errors you
    > were trying to cause.
    >
    Yes, it is not the error I was trying to cause. I have modified it.
    
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 12.
    > +-- Verify that EXCEPT col-list cannot contain RI cols (when using RI FULL)
    > +ALTER TABLE pub_test_except1 REPLICA IDENTITY FULL;
    > +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    >
    >
    > SUGGESTION. Change that comment to:
    > Verify fails - EXCEPT col-list cannot...
    >
    Fixed
    
    > ~~~
    >
    > 13.
    > +-- Verify that EXCEPT col-list cannot contain RI cols (when using INDEX)
    > +CREATE UNIQUE INDEX pub_test_except1_ac_idx ON pub_test_except1 (a, c);
    > +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    > pub_test_except1_a_idx;
    > +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    > +DROP INDEX pub_test_except1_ac_idx;
    >
    > SUGGESTION. Change that comment to:
    > Verify fails - EXCEPT col-list cannot...
    >
    Fixed
    
    > ~~~
    >
    > 14.
    > +-- Verify that so long as no clash between RI cols and the EXCEPT
    > +CREATE UNIQUE INDEX pub_test_except1_a_idx ON pub_test_except1 (a);
    > +ALTER TABLE pub_test_except1 REPLICA IDENTITY USING INDEX
    > pub_test_except1_a_idx;
    > +UPDATE pub_test_except1 SET a = 3 WHERE a = 1;
    > +
    >
    > That comment doesn't make sense. Missing words?
    >
    Fixed
    
    > ======
    > .../t/036_rep_changes_except_table.pl
    >
    > 15.
    > (I haven't reviewed this file in detail yet, but here is a general comment)
    >
    > I know this patch currently lives in the same thread as all the EXCEPT
    > TABLE stuff, but that seems just happenstance to me. IMO, this is a
    > separate enhancement that just shares the keyword EXCEPT. So, I felt
    > it should have quite separate tests too.
    >
    > e.g. How about: 037_rep_changes_except_collist.pl
    >
    Modified
    
    > ======
    > [1] https://www.postgresql.org/message-id/CANhcyEW2LK4diNeCG862DE40yQoV3VAgf59kXUq2TuR8fnw5vQ%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  100. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-07-21T19:35:40Z

    On Mon, 21 Jul 2025 at 16:22, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Sat, Jul 19, 2025 at 4:17 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Mon, 30 Jun 2025 at 16:25, shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > Few more comments on 002:
    > > >
    > > > 5)
    > > > +GetAllTablesPublicationRelations(Oid pubid, bool pubviaroot)
    > > >  {
    > > >
    > > > + List    *exceptlist;
    > > > +
    > > > + exceptlist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    > > >
    > > >
    > > > a) Here, we are assuming that the list provided by
    > > > GetPublicationRelations() will be except-tables list only, but there
    > > > is no validation of that.
    > > > b) We are using GetPublicationRelations() to get the relations which
    > > > are excluded from the publication. The name of function and comments
    > > > atop function are not in alignment with this usage.
    > > >
    > > > Suggestion:
    > > > We can have a new GetPublicationExcludeRelations() function for the
    > > > concerned usage. The existing logic of GetPublicationRelations() can
    > > > be shifted to a new internal-logic function which will accept a
    > > > 'except-flag' as well. Both GetPublicationRelations() and
    > > > GetPublicationExcludeRelations() can call that new function by passing
    > > > 'except-flag' as false and true respectively. The new internal
    > > > function will validate 'prexcept' against that except-flag passed and
    > > > will return the results.
    > > >
    > > I have made the above change.
    > >
    > >
    > > > 6)
    > > > Before your patch002, GetTopMostAncestorInPublication() was checking
    > > > pg_publication_rel and pg_publication_namespace to find out if the
    > > > table in the ancestor-list is part of a given particular. Both
    > > > pg_publication_rel and pg_publication_namespace did not have the entry
    > > > "for all tables" publications. That means
    > > > GetTopMostAncestorInPublication() was originally not checking whether
    > > > the given puboid is an "for all tables" publication to see if a rel
    > > > belongs to that particular pub or not. I
    > > >
    > > > But now with the current change, we do check if pub is all-tables pub,
    > > > if so, return relid and mark ancestor_level (provided table is not
    > > > part of the except list).  IIUC, the result in 2 cases may be
    > > > different. Is that the intention? Let me know if my understanding is
    > > > wrong.
    > > >
    > > This is intentional, in function get_rel_sync_entry, we are setting
    > > pub_relid to the topmost published ancestor. In HEAD we are directly
    > > setting using:
    > >             /*
    > >              * If this is a FOR ALL TABLES publication, pick the partition
    > >              * root and set the ancestor level accordingly.
    > >              */
    > >             if (pub->alltables)
    > >             {
    > >                 publish = true;
    > >                 if (pub->pubviaroot && am_partition)
    > >                 {
    > >                     List       *ancestors = get_partition_ancestors(relid);
    > >
    > >                     pub_relid = llast_oid(ancestors);
    > >                     ancestor_level = list_length(ancestors);
    > >                 }
    > >             }
    > > In HEAD, we can directly use 'llast_oid(ancestors)' to get the topmost
    > > ancestor for case of FOR ALL TABLES.
    > > But with this proposal. This change will no longer be valid as the
    > > 'llast_oid(ancestors)' may be excluded in the publication. So, to
    > > handle this change was made in GetTopMostAncestorInPublication.
    > >
    > >
    > > Also, during testing with the partitioned table and
    > > publish_via_partition_root the behaviour of the current patch is  as
    > > below:
    > > For example we have a partitioned table t1. It has partitions part1
    > > and part2. Now consider the following cases:
    > > 1. with publish_via_partition_root = true
    > >      I. If we create publication on all tables with EXCEPT t1, no data
    > > for t1, part1 or part2 is replicated.
    > >      II.  If we create publication on all tables with EXCEPT part1,
    > > data for all tables t1, part1 and part2 is replicated.
    > > 2. with publish_via_partition_root = false
    > >      I. If we create publication on all tables with EXCEPT t1, no data
    > > for t1, part1 or part2 is replicated.
    > >      II. If we create publication on all tables with EXCEPT part1,
    > > data for part1 is not replicated
    > >
    > > Is this behaviour fine?
    > > I checked for other databases such as MySQL, SQL Server. In that we do
    > > not have such cases as either we replicate the whole partitioned table
    > > or we not replicated at all. We do not have partition level control.
    > > For Oracle, I found that we can include or exclude partitions using
    > > 'PARTITIONEXCLUDE' [2], but did not find something similar to
    > > publish_via_partition_root or where partitions are published as
    > > separate tables.
    > > What are your thoughts on the above behaviour?
    > >
    >
    > Thank You for the details. I will review this behaviour soon and will
    > let you know my comments. Meanwhile, please find a few comments on
    > v16-0001:
    >
    > 1)
    > we do LockSchemaList() everywhere before we call
    > PublicationDropSchemas() to prevent concurrent schema deletion. Do we
    > need that in reset flow as well?
    Added
    
    >
    > 2)
    > + /* Drop the schemas associated with the publication */
    > + schemas = GetPublicationSchemas(pubid);
    > + PublicationDropSchemas(pubid, schemas, true);
    > +
    > + /* Get all relations associated with the publication */
    > + relids = GetPublicationRelations(pubid, PUBLICATION_PART_ROOT);
    >
    > We can rename schemas to schemaids similar to relids, as
    > GetPublicationSchemas return oids.
    >
    Fixed
    
    > 3)
    > + /* Drop the relations associated with the publication */
    > + PublicationDropTables(pubform->oid, rels, true);
    >
    > we can pass 'pubid' here instead of pubform->oid
    >
    Modified
    
    > 4)
    > Shall we modify the comments:
    > 'Drop the relations associated with the publication' to 'Remove the
    > associated relations from the publication'
    > 'Drop the schemas associated with the publication'  to 'Remove the
    > associated schemas from the publication'
    >
    > Similar changes can be done in test file's comments as well
    > --Verify that tables associated with the publication are dropped after
    > RESET
    > --Verify that schemas associated with the publication are dropped after RESET
    >
    Fixed
    
    I have made the changes in the latest v17 patch [1].
    [1]: https://www.postgresql.org/message-id/CANhcyEUtYV-9ujtxLasnxN_peT%2B3LuZjcRx1xUECh1CCmANB8w%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  101. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-07-22T01:58:29Z

    Hi Shlok,
    
    Some review comments for patch v17-0003. I also checked the TAP test this time.
    
    ======
    doc/src/sgml/logical-replication.sgml
    
    1.
    +   <literal>publish_generated_columns</literal></link>. Specifying generated
    +   columns in a column list using the <literal>EXCEPT</literal> clause excludes
    +   the specified generated columns from being published, regardless of the
    +   <link linkend="sql-createpublication-params-with-publish-generated-columns">
    +   <literal>publish_generated_columns</literal></link> setting. However, for
    
    I think that is not quite the same wording I had previously suggested.
    It sounds a bit odd/redundant saying "Specifying" and "specified" in
    the same sentence.
    
    ======
    src/backend/parser/gram.y
    
    2. check_except_collist
    
    I'm wondering if this checking should be done within the existing
    preprocess_pubobj_list() function, alongside all the other ERROR
    checking. Care needs to be taken to make sure the pubtable->except is
    referring to an EXCEPT (col-list), instead of the other kind of EXCEPT
    tables, but in general I think it is better to keep all the
    publication combinations checking errors like this in one place.
    
    
    ======
    src/bin/psql/describe.c
    
    3. addFooterToPublicationDesc
    
    - appendPQExpBuffer(&buf, " (%s)",
    -   PQgetvalue(result, i, 2));
    + {
    + if (!PQgetisnull(result, i, 3) &&
    + strcmp(PQgetvalue(result, i, 3), "t") == 0)
    + appendPQExpBuffer(&buf, " EXCEPT (%s)",
    +   PQgetvalue(result, i, 2));
    + else
    + appendPQExpBuffer(&buf, " (%s)",
    +   PQgetvalue(result, i, 2));
    + }
    
    Do you really need to check !PQgetisnull(result, i, 3) here?  (e.g.
    The comment does not say that this attribute can be NULL)
    
    ======
    .../t/037_rep_changes_except_collist.pl
    
    4.
    +# Copyright (c) 2021-2025, PostgreSQL Global Development Group
    +
    +# Logical replication tests for except table publications
    
    Comment is wrong. These tests are for EXCEPT (column-list)
    
    ~~~
    
    5.
    +# Test for except column publications
    +# Initial setup
    +$node_publisher->safe_psql('postgres', "CREATE SCHEMA sch1");
    +$node_publisher->safe_psql('postgres',
    + "CREATE TABLE tab2 (a int, b int NOT NULL, c int)");
    +$node_publisher->safe_psql('postgres',
    + "CREATE TABLE sch1.tab2 (a int, b int, c int)");
    +$node_publisher->safe_psql('postgres',
    + "CREATE TABLE tab3 (a int, b int, c int)");
    +$node_publisher->safe_psql('postgres',
    + "CREATE TABLE tab4 (a int, b int GENERATED ALWAYS AS (a * 2) STORED,
    c int GENERATED ALWAYS AS (a * 3) STORED)"
    +);
    +$node_publisher->safe_psql('postgres',
    + "CREATE TABLE tab5 (a int, b int GENERATED ALWAYS AS (a * 2) STORED,
    c int GENERATED ALWAYS AS (a * 3) STORED)"
    +);
    +$node_publisher->safe_psql('postgres', "INSERT INTO tab2 VALUES (1, 2, 3)");
    +$node_publisher->safe_psql('postgres',
    + "INSERT INTO sch1.tab2 VALUES (1, 2, 3)");
    +$node_publisher->safe_psql('postgres',
    + "CREATE PUBLICATION tap_pub_col FOR TABLE tab2 EXCEPT (a), sch1.tab2
    EXCEPT (b, c)"
    +);
    
    5a.
    I think you don't need to say "Test for except column publications",
    because that is the purpose of thie entire file.
    
    ~
    
    5b.
    You can combine multiple of these safe_psql calls together
    
    ~
    
    5c.
    It might help make tests easier to read if you named those generated
    columns 'b', 'c' cols as 'bgen', 'cgen' instead.
    
    ~
    
    5d.
    The table names are strange, because why does it start at tab2 when
    there is not a tab1?
    
    ~~~
    
    6.
    +$node_subscriber->safe_psql('postgres', "CREATE SCHEMA sch1");
    +$node_subscriber->safe_psql('postgres',
    + "CREATE TABLE tab2 (a int, b int NOT NULL, c int)");
    +$node_subscriber->safe_psql('postgres',
    + "CREATE TABLE sch1.tab2 (a int, b int, c int)");
    +$node_subscriber->safe_psql('postgres',
    + "CREATE TABLE tab3 (a int, b int, c int)");
    +$node_subscriber->safe_psql('postgres',
    + "CREATE TABLE tab4 (a int, b int, c int)");
    +$node_subscriber->safe_psql('postgres',
    + "CREATE TABLE tab5 (a int, b int, c int)");
    
    You can combine multiple of these safe_psql calls together
    
    ~~~
    
    7.
    +# Test initial sync
    +my $result = $node_subscriber->safe_psql('postgres', "SELECT * FROM tab2");
    +is($result, qq(|2|3),
    + 'check that initial sync for except column publication');
    
    The message seems strange. Do you mean "check initial sync for an
    'EXCEPT (column-list)' publication"
    
    NOTE: There are many other messages where you wrote "for except column
    publication" but I think maybe all of those can be improved a bit like
    above.
    
    ~~~
    
    8.
    +$node_publisher->safe_psql('postgres', "INSERT INTO tab2 VALUES (4, 5, 6)");
    +$node_publisher->safe_psql('postgres',
    + "INSERT INTO sch1.tab2 VALUES (4, 5, 6)");
    +$node_publisher->wait_for_catchup('tap_sub_col');
    
    8a.
    You can combine multiple of these safe_psql calls together.
    
    NOTE: I won't keep repeating this review comment but I think maybe
    there are lots more places where the safe_psql can all be combined to
    expected multiple statements.
    
    ~
    
    8b.
    I felt all those commands should be under the "Test incremental
    changes" comment.
    
    ~~~
    
    9.
    +is($result, qq(1||3), 'check alter publication with EXCEPT');
    
    Maybe that should've said with 'EXCEPT (column-list)'
    
    ~~~
    
    10.
    +# Test for publication created with publish_generated_columns as true on table
    +# with generated columns and column list specified with EXCEPT
    +$node_publisher->safe_psql('postgres', "INSERT INTO tab4 VALUES (1)");
    +$node_publisher->safe_psql('postgres',
    + "ALTER PUBLICATION tap_pub_col SET (publish_generated_columns)");
    +$node_publisher->safe_psql('postgres',
    + "ALTER PUBLICATION tap_pub_col SET TABLE tab4 EXCEPT(b)");
    +$node_subscriber->safe_psql('postgres',
    + "ALTER SUBSCRIPTION tap_sub_col REFRESH PUBLICATION");
    +$node_subscriber->wait_for_subscription_sync($node_publisher, 'tap_sub_col');
    
    10a.
    I felt the test comments for both those generated columns parameter
    test should give more explanation to say what is the expected result
    and why.
    
    ~
    
    10b.
    How does "ALTER PUBLICATION tap_pub_col SET
    (publish_generated_columns)" even work? I thought the
    "pubish_generated_columns" is an enum but you did not specify any enum
    value here (???)
    
    ~~~
    
    11.
    + 'check publication(publish_generated_columns as false) with
    generated columns and EXCEPT'
    
    Hmm. I thought there is no such thing as "publish_generated_columns as
    false", and also the EXCEPT should say 'EXCEPT (column-list)'
    
    ~~~
    
    12.
    I wonder if there should be another boundary condition test case as follows:
    - have some table with cols a,b,c.
    - create a publication 'EXCEPT (a,b,c)', so you don't publish anything at all.
    - then ALTER the TABLE to add a column 'd'.
    - now the publication should publish only 'd'.
    
    ======
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  102. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-07-22T08:59:45Z

    On Sat, Jul 19, 2025 at 4:17 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Mon, 30 Jun 2025 at 16:25, shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > Few more comments on 002:
    > >
    > > 5)
    > > +GetAllTablesPublicationRelations(Oid pubid, bool pubviaroot)
    > >  {
    > >
    > > + List    *exceptlist;
    > > +
    > > + exceptlist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    > >
    > >
    > > a) Here, we are assuming that the list provided by
    > > GetPublicationRelations() will be except-tables list only, but there
    > > is no validation of that.
    > > b) We are using GetPublicationRelations() to get the relations which
    > > are excluded from the publication. The name of function and comments
    > > atop function are not in alignment with this usage.
    > >
    > > Suggestion:
    > > We can have a new GetPublicationExcludeRelations() function for the
    > > concerned usage. The existing logic of GetPublicationRelations() can
    > > be shifted to a new internal-logic function which will accept a
    > > 'except-flag' as well. Both GetPublicationRelations() and
    > > GetPublicationExcludeRelations() can call that new function by passing
    > > 'except-flag' as false and true respectively. The new internal
    > > function will validate 'prexcept' against that except-flag passed and
    > > will return the results.
    > >
    > I have made the above change.
    
    Thank You for the changes.
    
    1)
    But on rethinking, shall we make GetPublicationRelations() similar to :
    
    /* Gets list of publication oids for a relation that matches the except_flag */
    GetRelationPublications(Oid relid, bool except_flag)
    
    i.e. we can have a single function GetPublicationRelations() taking
    except_flag and comment can say: 'Gets list of relation oids for a
    publication that matches the except_flag.'
    
    We can get rid of GetPubIncludedOrExcludedRels() and
    GetPublicationExcludeRelations().
    
    Thoughts?
    
    
    2)
    we can rename except_table to except_flag to be consistent with
    GetRelationPublications()
    
    3)
    + if ((except_table && pubrel->prexcept) || !except_table)
    + result = GetPubPartitionOptionRelations(result, pub_partopt,
    + pubrel->prrelid);
    
    3a)
    In the case of '!except_table', we are not matching it with
    'pubrel->prexcept', is that intentional?
    
    3 b)
    Shall we simplify this similar to the changes in GetRelationPublications() i.e.
    if (except_table/flag == pubrel->prexcept)
       result = GetPubPartitionOptionRelations(...)
    
    
    >
    > > 6)
    > > Before your patch002, GetTopMostAncestorInPublication() was checking
    > > pg_publication_rel and pg_publication_namespace to find out if the
    > > table in the ancestor-list is part of a given particular. Both
    > > pg_publication_rel and pg_publication_namespace did not have the entry
    > > "for all tables" publications. That means
    > > GetTopMostAncestorInPublication() was originally not checking whether
    > > the given puboid is an "for all tables" publication to see if a rel
    > > belongs to that particular pub or not. I
    > >
    > > But now with the current change, we do check if pub is all-tables pub,
    > > if so, return relid and mark ancestor_level (provided table is not
    > > part of the except list).  IIUC, the result in 2 cases may be
    > > different. Is that the intention? Let me know if my understanding is
    > > wrong.
    > >
    > This is intentional, in function get_rel_sync_entry, we are setting
    > pub_relid to the topmost published ancestor. In HEAD we are directly
    > setting using:
    >             /*
    >              * If this is a FOR ALL TABLES publication, pick the partition
    >              * root and set the ancestor level accordingly.
    >              */
    >             if (pub->alltables)
    >             {
    >                 publish = true;
    >                 if (pub->pubviaroot && am_partition)
    >                 {
    >                     List       *ancestors = get_partition_ancestors(relid);
    >
    >                     pub_relid = llast_oid(ancestors);
    >                     ancestor_level = list_length(ancestors);
    >                 }
    >             }
    > In HEAD, we can directly use 'llast_oid(ancestors)' to get the topmost
    > ancestor for case of FOR ALL TABLES.
    > But with this proposal. This change will no longer be valid as the
    > 'llast_oid(ancestors)' may be excluded in the publication. So, to
    > handle this change was made in GetTopMostAncestorInPublication.
    >
    >
    > Also, during testing with the partitioned table and
    > publish_via_partition_root the behaviour of the current patch is  as
    > below:
    > For example we have a partitioned table t1. It has partitions part1
    > and part2. Now consider the following cases:
    > 1. with publish_via_partition_root = true
    >      I. If we create publication on all tables with EXCEPT t1, no data
    > for t1, part1 or part2 is replicated.
    
    Okay. Agreed.
    
    >      II.  If we create publication on all tables with EXCEPT part1,
    > data for all tables t1, part1 and part2 is replicated.
    
    Okay. Is this because part1 changes are replicated through t1 and
    since t1 changes are not restricted, part1 changes will also not be
    restricted? In other words, part1 was never published directly in the
    first place and thus 'EXCEPT part1' has no meaning when
    'publish_via_partition_root' = true? IMO, it is in alignment with the
    'publish_via_partition_root' definition but it might not be that
    intuitive for users. So shall we emit a WARNING:
    
    WARNING: Partition "part1" is excluded, but publish_via_partition_root
    = true, so this will have no effect.
    Thoughts?
    
    > 2. with publish_via_partition_root = false
    >      I. If we create publication on all tables with EXCEPT t1, no data
    > for t1, part1 or part2 is replicated.
    
    I think we shall still publish partitions here. Since
    publish_via_partition_root is false, part1 and part2 are published
    individually and thus shall we allow publishing of part1 and part 2
    here? Thoughts?
    
    >      II. If we create publication on all tables with EXCEPT part1,
    > data for part1 is not replicated
    >
    
    Agreed.
    
    thanks
    Shveta
    
    
    
    
  103. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-07-22T10:27:34Z

    Shlok, I was trying to validate the interaction of
    'publish_via_partition_root' with 'EXCEPT". Found some unexpected
    behaviour, can you please review:
    
    Pub:
    ---------
    CREATE TABLE tab_root (range_col int,i int,j int) PARTITION BY RANGE
    (range_col);
    CREATE TABLE tab_part_1 PARTITION OF tab_root FOR VALUES FROM (1) to (1000);
    CREATE TABLE tab_part_2 PARTITION OF tab_root FOR VALUES FROM (1000) to (2000);
    create publication pub2 for all tables except tab_part_2 WITH
    (publish_via_partition_root=true);
    
    Sub (tables without partition):
    --------
    CREATE TABLE tab_root (range_col int,i int,j int);
    CREATE TABLE tab_part_1(range_col int,i int,j int);
    CREATE TABLE tab_part_2(range_col int,i int,j int);
    create subscription sub2 connection '...' publication pub2;
    
    Pub:
    --------
    insert into tab_part_2 values(1001,1,1);
    
    On Sub, the above row is replicated as expected in tab_root due to
    publish_via_partition_root=true on pub.
    
    Now on Pub:
    --------
    alter publication pub2 set (publish_via_partition_root=false);
    insert into tab_part_2 values(1002,2,2);
    
    Now with publish_via_partition_root=false and 'except tab_part_2', the
    above row is correctly ignored and not replicated on sub.
    
    But when I try this:
    insert into tab_part_1 values(1,1,1);
    insert into tab_root values(5,5,5);
    
    Expectation was that the above rows are replicated but that is not the
    case. Can you please review? Please let me know if my understanding is
    wrong.
    
    thanks
    Shveta
    
    
    
    
  104. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-07-23T04:38:43Z

    I further tested inherited tables flow as well wrt ONLY and EXCEPT, it
    works well. But while reading docs for the saem, I have few concerns.
    
    1)
    While explaining ONLY for EXCEPT, create-publication doc says this
    
    +      This does not apply to a partitioned table, however.  The partitions of
    +      a partitioned table are always implicitly considered part of the
    +      publication, so they are never explicitly excluded from the publication.
    
    I do not understand the last line: "so they are never explicitly
    excluded from the publication" . But we can explicitly exclude them
    using EXCEPT <partition_name>. Do you mean to say something else here?
    
    2)
    alter-publication doc says (in context of EXCEPT):
    
    "If ONLY is specified before the table name, only that table is
    affected. If ONLY is not specified, the table and all its descendant
    tables (if any) are affected. Optionally, * can be specified after
    the table name to explicitly indicate that descendant tables are
    affected."
    
    But it does not mention anything for partitions. I think we shall
    mention here as well that this does not apply to a partitioned table.
    (I tested ONLY and EXCEPT for partition-root. UNLIKE inherited tables,
    ONLY has no impact on partitioned tables.)
    
    3)
    Shall we explain the relation of 'publish_via_partition_root' with
    EXCEPT briefly in docs(once we conclude that design)?
    
    Please note that I have performed all the tests (mentioned here and in
    previous emails) on patch001 and patch002. patch003 is not applied in
    these tests.
    
    thanks
    Shveta
    
    
    
    
  105. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-08-03T16:07:00Z

    On Tue, 22 Jul 2025 at 07:28, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok,
    >
    > Some review comments for patch v17-0003. I also checked the TAP test this time.
    >
    > ======
    > doc/src/sgml/logical-replication.sgml
    >
    > 1.
    > +   <literal>publish_generated_columns</literal></link>. Specifying generated
    > +   columns in a column list using the <literal>EXCEPT</literal> clause excludes
    > +   the specified generated columns from being published, regardless of the
    > +   <link linkend="sql-createpublication-params-with-publish-generated-columns">
    > +   <literal>publish_generated_columns</literal></link> setting. However, for
    >
    > I think that is not quite the same wording I had previously suggested.
    > It sounds a bit odd/redundant saying "Specifying" and "specified" in
    > the same sentence.
    >
    > ======
    > src/backend/parser/gram.y
    >
    > 2. check_except_collist
    >
    > I'm wondering if this checking should be done within the existing
    > preprocess_pubobj_list() function, alongside all the other ERROR
    > checking. Care needs to be taken to make sure the pubtable->except is
    > referring to an EXCEPT (col-list), instead of the other kind of EXCEPT
    > tables, but in general I think it is better to keep all the
    > publication combinations checking errors like this in one place.
    >
    Added the check in preprocess_pubobj_list(). I checked the syntaxes
    and found that this function is not called for "FOR ALL TABLES" cases
    and EXCEPT tables can only be used with "FOR ALL TABLES" publications.
    So, I think handling for "EXCEPT tables" will not be required in the
    function preprocess_pubobj_list()
    
    >
    > ======
    > src/bin/psql/describe.c
    >
    > 3. addFooterToPublicationDesc
    >
    > - appendPQExpBuffer(&buf, " (%s)",
    > -   PQgetvalue(result, i, 2));
    > + {
    > + if (!PQgetisnull(result, i, 3) &&
    > + strcmp(PQgetvalue(result, i, 3), "t") == 0)
    > + appendPQExpBuffer(&buf, " EXCEPT (%s)",
    > +   PQgetvalue(result, i, 2));
    > + else
    > + appendPQExpBuffer(&buf, " (%s)",
    > +   PQgetvalue(result, i, 2));
    > + }
    >
    > Do you really need to check !PQgetisnull(result, i, 3) here?  (e.g.
    > The comment does not say that this attribute can be NULL)
    >
    > ======
    > .../t/037_rep_changes_except_collist.pl
    >
    > 4.
    > +# Copyright (c) 2021-2025, PostgreSQL Global Development Group
    > +
    > +# Logical replication tests for except table publications
    >
    > Comment is wrong. These tests are for EXCEPT (column-list)
    >
    > ~~~
    >
    > 5.
    > +# Test for except column publications
    > +# Initial setup
    > +$node_publisher->safe_psql('postgres', "CREATE SCHEMA sch1");
    > +$node_publisher->safe_psql('postgres',
    > + "CREATE TABLE tab2 (a int, b int NOT NULL, c int)");
    > +$node_publisher->safe_psql('postgres',
    > + "CREATE TABLE sch1.tab2 (a int, b int, c int)");
    > +$node_publisher->safe_psql('postgres',
    > + "CREATE TABLE tab3 (a int, b int, c int)");
    > +$node_publisher->safe_psql('postgres',
    > + "CREATE TABLE tab4 (a int, b int GENERATED ALWAYS AS (a * 2) STORED,
    > c int GENERATED ALWAYS AS (a * 3) STORED)"
    > +);
    > +$node_publisher->safe_psql('postgres',
    > + "CREATE TABLE tab5 (a int, b int GENERATED ALWAYS AS (a * 2) STORED,
    > c int GENERATED ALWAYS AS (a * 3) STORED)"
    > +);
    > +$node_publisher->safe_psql('postgres', "INSERT INTO tab2 VALUES (1, 2, 3)");
    > +$node_publisher->safe_psql('postgres',
    > + "INSERT INTO sch1.tab2 VALUES (1, 2, 3)");
    > +$node_publisher->safe_psql('postgres',
    > + "CREATE PUBLICATION tap_pub_col FOR TABLE tab2 EXCEPT (a), sch1.tab2
    > EXCEPT (b, c)"
    > +);
    >
    > 5a.
    > I think you don't need to say "Test for except column publications",
    > because that is the purpose of thie entire file.
    >
    > ~
    >
    > 5b.
    > You can combine multiple of these safe_psql calls together
    >
    > ~
    >
    > 5c.
    > It might help make tests easier to read if you named those generated
    > columns 'b', 'c' cols as 'bgen', 'cgen' instead.
    >
    > ~
    > 5d.
    > The table names are strange, because why does it start at tab2 when
    > there is not a tab1?
    > ~~~
    >
    > 6.
    > +$node_subscriber->safe_psql('postgres', "CREATE SCHEMA sch1");
    > +$node_subscriber->safe_psql('postgres',
    > + "CREATE TABLE tab2 (a int, b int NOT NULL, c int)");
    > +$node_subscriber->safe_psql('postgres',
    > + "CREATE TABLE sch1.tab2 (a int, b int, c int)");
    > +$node_subscriber->safe_psql('postgres',
    > + "CREATE TABLE tab3 (a int, b int, c int)");
    > +$node_subscriber->safe_psql('postgres',
    > + "CREATE TABLE tab4 (a int, b int, c int)");
    > +$node_subscriber->safe_psql('postgres',
    > + "CREATE TABLE tab5 (a int, b int, c int)");
    >
    > You can combine multiple of these safe_psql calls together
    >
    > ~~~
    >
    > 7.
    > +# Test initial sync
    > +my $result = $node_subscriber->safe_psql('postgres', "SELECT * FROM tab2");
    > +is($result, qq(|2|3),
    > + 'check that initial sync for except column publication');
    >
    > The message seems strange. Do you mean "check initial sync for an
    > 'EXCEPT (column-list)' publication"
    >
    > NOTE: There are many other messages where you wrote "for except column
    > publication" but I think maybe all of those can be improved a bit like
    > above.
    >
    > ~~~
    >
    > 8.
    > +$node_publisher->safe_psql('postgres', "INSERT INTO tab2 VALUES (4, 5, 6)");
    > +$node_publisher->safe_psql('postgres',
    > + "INSERT INTO sch1.tab2 VALUES (4, 5, 6)");
    > +$node_publisher->wait_for_catchup('tap_sub_col');
    >
    > 8a.
    > You can combine multiple of these safe_psql calls together.
    >
    > NOTE: I won't keep repeating this review comment but I think maybe
    > there are lots more places where the safe_psql can all be combined to
    > expected multiple statements.
    >
    > ~
    >
    > 8b.
    > I felt all those commands should be under the "Test incremental
    > changes" comment.
    >
    > ~~~
    >
    > 9.
    > +is($result, qq(1||3), 'check alter publication with EXCEPT');
    >
    > Maybe that should've said with 'EXCEPT (column-list)'
    >
    > ~~~
    >
    > 10.
    > +# Test for publication created with publish_generated_columns as true on table
    > +# with generated columns and column list specified with EXCEPT
    > +$node_publisher->safe_psql('postgres', "INSERT INTO tab4 VALUES (1)");
    > +$node_publisher->safe_psql('postgres',
    > + "ALTER PUBLICATION tap_pub_col SET (publish_generated_columns)");
    > +$node_publisher->safe_psql('postgres',
    > + "ALTER PUBLICATION tap_pub_col SET TABLE tab4 EXCEPT(b)");
    > +$node_subscriber->safe_psql('postgres',
    > + "ALTER SUBSCRIPTION tap_sub_col REFRESH PUBLICATION");
    > +$node_subscriber->wait_for_subscription_sync($node_publisher, 'tap_sub_col');
    >
    > 10a.
    > I felt the test comments for both those generated columns parameter
    > test should give more explanation to say what is the expected result
    > and why.
    >
    > ~
    >
    > 10b.
    > How does "ALTER PUBLICATION tap_pub_col SET
    > (publish_generated_columns)" even work? I thought the
    > "pubish_generated_columns" is an enum but you did not specify any enum
    > value here (???)
    >
    > ~~~
    Yes, it works. It works equivalent to publish_generated_columns = stored.
    Eg:
    postgres=# CREATE PUBLICATION pub1 FOR TABLE t1 with
    (publish_generated_columns);
    CREATE PUBLICATION
    postgres=# select * from pg_publication;
      oid  | pubname | pubowner | puballtables | pubinsert | pubupdate |
    pubdelete | pubtruncate | pubviaroot | pubgencols
    -------+---------+----------+--------------+-----------+-----------+-----------+-------------+------------+------------
     16395 | pub1    |       10 | f            | t         | t         | t
            | t           | f          | s
    (1 row)
    
    For this patch, I have modified the test to use
    'publish_generated_columns = stored'.
    
    >
    > 11.
    > + 'check publication(publish_generated_columns as false) with
    > generated columns and EXCEPT'
    >
    > Hmm. I thought there is no such thing as "publish_generated_columns as
    > false", and also the EXCEPT should say 'EXCEPT (column-list)'
    >
    > ~~~
    >
    > 12.
    > I wonder if there should be another boundary condition test case as follows:
    > - have some table with cols a,b,c.
    > - create a publication 'EXCEPT (a,b,c)', so you don't publish anything at all.
    > - then ALTER the TABLE to add a column 'd'.
    > - now the publication should publish only 'd'.
    > ======
    
    I have fixed all the comments and added the changes in the latest v18 patch.
    
    Thanks,
    Shlok Kyal
    
  106. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-08-03T16:10:22Z

    On Tue, 22 Jul 2025 at 14:29, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Sat, Jul 19, 2025 at 4:17 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Mon, 30 Jun 2025 at 16:25, shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > Few more comments on 002:
    > > >
    > > > 5)
    > > > +GetAllTablesPublicationRelations(Oid pubid, bool pubviaroot)
    > > >  {
    > > >
    > > > + List    *exceptlist;
    > > > +
    > > > + exceptlist = GetPublicationRelations(pubid, PUBLICATION_PART_ALL);
    > > >
    > > >
    > > > a) Here, we are assuming that the list provided by
    > > > GetPublicationRelations() will be except-tables list only, but there
    > > > is no validation of that.
    > > > b) We are using GetPublicationRelations() to get the relations which
    > > > are excluded from the publication. The name of function and comments
    > > > atop function are not in alignment with this usage.
    > > >
    > > > Suggestion:
    > > > We can have a new GetPublicationExcludeRelations() function for the
    > > > concerned usage. The existing logic of GetPublicationRelations() can
    > > > be shifted to a new internal-logic function which will accept a
    > > > 'except-flag' as well. Both GetPublicationRelations() and
    > > > GetPublicationExcludeRelations() can call that new function by passing
    > > > 'except-flag' as false and true respectively. The new internal
    > > > function will validate 'prexcept' against that except-flag passed and
    > > > will return the results.
    > > >
    > > I have made the above change.
    >
    > Thank You for the changes.
    >
    > 1)
    > But on rethinking, shall we make GetPublicationRelations() similar to :
    >
    > /* Gets list of publication oids for a relation that matches the except_flag */
    > GetRelationPublications(Oid relid, bool except_flag)
    >
    > i.e. we can have a single function GetPublicationRelations() taking
    > except_flag and comment can say: 'Gets list of relation oids for a
    > publication that matches the except_flag.'
    >
    > We can get rid of GetPubIncludedOrExcludedRels() and
    > GetPublicationExcludeRelations().
    >
    > Thoughts?
    >
    This seems reasonable to me. I have made the changes for the same.
    
    >
    > 2)
    > we can rename except_table to except_flag to be consistent with
    > GetRelationPublications()
    >
    > 3)
    > + if ((except_table && pubrel->prexcept) || !except_table)
    > + result = GetPubPartitionOptionRelations(result, pub_partopt,
    > + pubrel->prrelid);
    >
    > 3a)
    > In the case of '!except_table', we are not matching it with
    > 'pubrel->prexcept', is that intentional?
    >
    > 3 b)
    > Shall we simplify this similar to the changes in GetRelationPublications() i.e.
    > if (except_table/flag == pubrel->prexcept)
    >    result = GetPubPartitionOptionRelations(...)
    >
    >
    > >
    > > > 6)
    > > > Before your patch002, GetTopMostAncestorInPublication() was checking
    > > > pg_publication_rel and pg_publication_namespace to find out if the
    > > > table in the ancestor-list is part of a given particular. Both
    > > > pg_publication_rel and pg_publication_namespace did not have the entry
    > > > "for all tables" publications. That means
    > > > GetTopMostAncestorInPublication() was originally not checking whether
    > > > the given puboid is an "for all tables" publication to see if a rel
    > > > belongs to that particular pub or not. I
    > > >
    > > > But now with the current change, we do check if pub is all-tables pub,
    > > > if so, return relid and mark ancestor_level (provided table is not
    > > > part of the except list).  IIUC, the result in 2 cases may be
    > > > different. Is that the intention? Let me know if my understanding is
    > > > wrong.
    > > >
    > > This is intentional, in function get_rel_sync_entry, we are setting
    > > pub_relid to the topmost published ancestor. In HEAD we are directly
    > > setting using:
    > >             /*
    > >              * If this is a FOR ALL TABLES publication, pick the partition
    > >              * root and set the ancestor level accordingly.
    > >              */
    > >             if (pub->alltables)
    > >             {
    > >                 publish = true;
    > >                 if (pub->pubviaroot && am_partition)
    > >                 {
    > >                     List       *ancestors = get_partition_ancestors(relid);
    > >
    > >                     pub_relid = llast_oid(ancestors);
    > >                     ancestor_level = list_length(ancestors);
    > >                 }
    > >             }
    > > In HEAD, we can directly use 'llast_oid(ancestors)' to get the topmost
    > > ancestor for case of FOR ALL TABLES.
    > > But with this proposal. This change will no longer be valid as the
    > > 'llast_oid(ancestors)' may be excluded in the publication. So, to
    > > handle this change was made in GetTopMostAncestorInPublication.
    > >
    > >
    > > Also, during testing with the partitioned table and
    > > publish_via_partition_root the behaviour of the current patch is  as
    > > below:
    > > For example we have a partitioned table t1. It has partitions part1
    > > and part2. Now consider the following cases:
    > > 1. with publish_via_partition_root = true
    > >      I. If we create publication on all tables with EXCEPT t1, no data
    > > for t1, part1 or part2 is replicated.
    >
    > Okay. Agreed.
    >
    > >      II.  If we create publication on all tables with EXCEPT part1,
    > > data for all tables t1, part1 and part2 is replicated.
    >
    > Okay. Is this because part1 changes are replicated through t1 and
    > since t1 changes are not restricted, part1 changes will also not be
    > restricted? In other words, part1 was never published directly in the
    > first place and thus 'EXCEPT part1' has no meaning when
    > 'publish_via_partition_root' = true? IMO, it is in alignment with the
    > 'publish_via_partition_root' definition but it might not be that
    > intuitive for users. So shall we emit a WARNING:
    >
    > WARNING: Partition "part1" is excluded, but publish_via_partition_root
    > = true, so this will have no effect.
    > Thoughts?
    Your understanding is correct. I have added a WARNING for this case
    
    >
    > > 2. with publish_via_partition_root = false
    > >      I. If we create publication on all tables with EXCEPT t1, no data
    > > for t1, part1 or part2 is replicated.
    >
    > I think we shall still publish partitions here. Since
    > publish_via_partition_root is false, part1 and part2 are published
    > individually and thus shall we allow publishing of part1 and part 2
    > here? Thoughts?
    I made a mistake in explaining this point. Yes your point is correct.
    Changes for partitions part1 and part2 will be replicated.
    I have documented the behaviour in the docs.
    
    >
    > >      II. If we create publication on all tables with EXCEPT part1,
    > > data for part1 is not replicated
    > >
    >
    > Agreed.
    >
    
    I have addressed the comments and have attached the updated patch in [1].
    [1]: https://www.postgresql.org/message-id/CANhcyEXkeg3sjkS3DS9yU1ckz4ozUBNZ%2BRmrWaRNSSVCR8RquA%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  107. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-08-03T16:11:24Z

    On Tue, 22 Jul 2025 at 15:57, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > Shlok, I was trying to validate the interaction of
    > 'publish_via_partition_root' with 'EXCEPT". Found some unexpected
    > behaviour, can you please review:
    >
    > Pub:
    > ---------
    > CREATE TABLE tab_root (range_col int,i int,j int) PARTITION BY RANGE
    > (range_col);
    > CREATE TABLE tab_part_1 PARTITION OF tab_root FOR VALUES FROM (1) to (1000);
    > CREATE TABLE tab_part_2 PARTITION OF tab_root FOR VALUES FROM (1000) to (2000);
    > create publication pub2 for all tables except tab_part_2 WITH
    > (publish_via_partition_root=true);
    >
    > Sub (tables without partition):
    > --------
    > CREATE TABLE tab_root (range_col int,i int,j int);
    > CREATE TABLE tab_part_1(range_col int,i int,j int);
    > CREATE TABLE tab_part_2(range_col int,i int,j int);
    > create subscription sub2 connection '...' publication pub2;
    >
    > Pub:
    > --------
    > insert into tab_part_2 values(1001,1,1);
    >
    > On Sub, the above row is replicated as expected in tab_root due to
    > publish_via_partition_root=true on pub.
    >
    > Now on Pub:
    > --------
    > alter publication pub2 set (publish_via_partition_root=false);
    > insert into tab_part_2 values(1002,2,2);
    >
    > Now with publish_via_partition_root=false and 'except tab_part_2', the
    > above row is correctly ignored and not replicated on sub.
    >
    > But when I try this:
    > insert into tab_part_1 values(1,1,1);
    > insert into tab_root values(5,5,5);
    >
    > Expectation was that the above rows are replicated but that is not the
    > case. Can you please review? Please let me know if my understanding is
    > wrong.
    
    Hi Shveta,
    
    I checked this behaviour on HEAD and found that it is the same
    behaviour as HEAD. I think if we alter the parameter
    'publish_via_partition_root', we should do ALTER SUBSCRIPTION ..
    REFRESH PUBLICATION on subscriber.
    I reviewed your behaviour and saw that after the 'alter publication
    pub2 set (publish_via_partition_root=false)', the changes are still
    being replicated to 'tab_root' on subscriber. And this behaviour is
    similar to HEAD.
    
    For example:
    Pub:
    ---------
    CREATE TABLE tab_root (range_col int,i int,j int) PARTITION BY RANGE
    (range_col);
    CREATE TABLE tab_part_1 PARTITION OF tab_root FOR VALUES FROM (1) to (1000);
    CREATE TABLE tab_part_2 PARTITION OF tab_root FOR VALUES FROM (1000) to (2000);
    create publication pub2 for table tab_root WITH
    (publish_via_partition_root=true);
    
    Sub (tables without partition):
    --------
    CREATE TABLE tab_root (range_col int,i int,j int);
    CREATE TABLE tab_part_1(range_col int,i int,j int);
    CREATE TABLE tab_part_2(range_col int,i int,j int);
    create subscription sub2 connection '...' publication pub2;
    
    Pub:
    --------
    insert into tab_part_2 values(1001,1,1);
    
    On Sub, the above row is replicated as expected in tab_root.
    
    Now on Pub:
    --------
    alter publication pub2 set (publish_via_partition_root=false);
    
    when I try this the data:
    insert into tab_part_2 values(1002,2,2);
    insert into tab_part_1 values(1,1,1);
    insert into tab_root values(5,5,5);
    
    The data is being replicated to tab_root on the subscriber.
    
    After I do ALTER SUBSCRIPTION .. REFRESH PUBLICATION on subscriber,
    replication happens as expected.
    
    Also I found following documentation:
    "Altering the <literal>publish_via_partition_root</literal> parameter can
    lead to data loss or duplication at the subscriber because it changes
    the identity and schema of the published tables. Note this happens only
    when a partition root table is specified as the replication target."
    
    Thanks,
    Shlok Kyal
    
    
    
    
  108. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-08-03T16:12:16Z

    On Wed, 23 Jul 2025 at 10:08, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > I further tested inherited tables flow as well wrt ONLY and EXCEPT, it
    > works well. But while reading docs for the saem, I have few concerns.
    >
    > 1)
    > While explaining ONLY for EXCEPT, create-publication doc says this
    >
    > +      This does not apply to a partitioned table, however.  The partitions of
    > +      a partitioned table are always implicitly considered part of the
    > +      publication, so they are never explicitly excluded from the publication.
    >
    > I do not understand the last line: "so they are never explicitly
    > excluded from the publication" . But we can explicitly exclude them
    > using EXCEPT <partition_name>. Do you mean to say something else here?
    >
    > 2)
    > alter-publication doc says (in context of EXCEPT):
    >
    > "If ONLY is specified before the table name, only that table is
    > affected. If ONLY is not specified, the table and all its descendant
    > tables (if any) are affected. Optionally, * can be specified after
    > the table name to explicitly indicate that descendant tables are
    > affected."
    >
    > But it does not mention anything for partitions. I think we shall
    > mention here as well that this does not apply to a partitioned table.
    > (I tested ONLY and EXCEPT for partition-root. UNLIKE inherited tables,
    > ONLY has no impact on partitioned tables.)
    >
    > 3)
    > Shall we explain the relation of 'publish_via_partition_root' with
    > EXCEPT briefly in docs(once we conclude that design)?
    >
    > Please note that I have performed all the tests (mentioned here and in
    > previous emails) on patch001 and patch002. patch003 is not applied in
    > these tests.
    >
    I have added/ modified the documentations as per the comments. The
    changes are present in patch [1].
    [1]: https://www.postgresql.org/message-id/CANhcyEXkeg3sjkS3DS9yU1ckz4ozUBNZ%2BRmrWaRNSSVCR8RquA%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  109. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-08-04T07:33:21Z

    On Mon, Aug 4, 2025 at 2:07 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    ...
    > > 10b.
    > > How does "ALTER PUBLICATION tap_pub_col SET
    > > (publish_generated_columns)" even work? I thought the
    > > "pubish_generated_columns" is an enum but you did not specify any enum
    > > value here (???)
    > >
    > > ~~~
    > Yes, it works. It works equivalent to publish_generated_columns = stored.
    > Eg:
    > postgres=# CREATE PUBLICATION pub1 FOR TABLE t1 with
    > (publish_generated_columns);
    > CREATE PUBLICATION
    > postgres=# select * from pg_publication;
    >   oid  | pubname | pubowner | puballtables | pubinsert | pubupdate |
    > pubdelete | pubtruncate | pubviaroot | pubgencols
    > -------+---------+----------+--------------+-----------+-----------+-----------+-------------+------------+------------
    >  16395 | pub1    |       10 | f            | t         | t         | t
    >         | t           | f          | s
    > (1 row)
    >
    
    Hmm -- it's not documented to behave like that, so I've created
    another thread for getting to the bottom of this topic.
    
    ~~~
    
    Meanwhile, here are my review comments for patch v18-0003
    
    ======
    src/backend/catalog/pg_publication.c
    
    pg_get_publication_tables:
    
    1.
    if (nattnums > 0)
    {
    values[2] = PointerGetDatum(buildint2vector(attnums, nattnums));
    nulls[2] = false;
    }
    else
    nulls[2] = true;
    
    Is there any possibility that values[2] might not be null, but then
    nattrnums skips some cols so remains 0? Then the final values[2] would
    conflict with nulls[2], which seems strange. Maybe it is safer to also
    assign values[2] = null in the else.
    
    ======
    src/backend/replication/logical/tablesync.c
    
    fetch_remote_table_info:
    
    2.
     static void
     fetch_remote_table_info(char *nspname, char *relname, LogicalRepRelation *lrel,
    - List **qual, bool *gencol_published)
    + List **qual, bool *gencol_published,
    + bool *no_cols_published)
    
    This new parameter should be documented in the function comment.
    
    ~~~
    
    3.
    + if (server_version >= 190000)
    + *no_cols_published = DatumGetBool(slot_getattr(tslot, 2, &isnull));
    +
    
    It seems that *no_cols_published (and *gencol_published) are assigned
    false by the caller. I had to go looking for that, so IMO it would be
    better to put Assert at the top of here so it is self-documenting
    
    Assert(*gencol_published == false);
    Assert(*no_cols_published == false);
    
    ======
    src/backend/replication/pgoutput/pgoutput.c
    
    4.
    + /*
    + * Indicates whether no columns are published for a given relation. With
    + * the introduction of the EXCEPT clause in column lists, it is now
    + * possible to define a publication that excludes all columns of a table.
    + * However, the 'columns' attribute cannot represent this case, since a
    + * NULL value implies that all columns are published. To distinguish this
    + * scenario, the 'no_cols_published' flag is introduced.
    + */
    + bool no_cols_published;
    
    The wording of the comment seems a bit strange -- EXCEPT is not a clause.
    
    BEFORE:
    the introduction of the EXCEPT clause in column lists, ...
    
    SUGGESTION
    the introduction of the EXCEPT qualifier for column lists, ....
    
    ~~~
    
    5.
      Bitmapset  *cols = NULL;
    + bool except_columns = false;
    + bool no_col_published = false;
    
    There are multiple places in this patch that say:
    
    'no_col_published'
    or 'no_cols_published'
    
    I felt this var name can be misunderstood because it is easy to read
    "no" as meaning "no." (aka number), and then misinterpret as
    "number_of_cols_published".
    
    Maybe an unambiguous name can be found, like
    - 'zero_cols_published' or
    - 'nothing_published' or
    - really make it 'num_cols_published' and check for 0.
    
    (so this comment applies to multiple places in the patch)
    
    ~~
    
    6.
      * of the table (including generated columns when
      * 'publish_generated_columns' parameter is true).
      */
    - if (!cols)
    + if (!no_col_published && !cols)
      {
    
    The existing comment above this code fragment also needs to mention
    "EXCEPT (column-list)" where all the columns are excluded
    
    ======
    src/bin/psql/describe.c
    
    describeOneTableDetails:
    
    7.
      /* column list (if any) */
      if (!PQgetisnull(result, i, 2))
    - appendPQExpBuffer(&buf, " (%s)",
    -   PQgetvalue(result, i, 2));
    + {
    + if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
    + appendPQExpBuffer(&buf, " EXCEPT (%s)",
    +   PQgetvalue(result, i, 2));
    + else
    + appendPQExpBuffer(&buf, " (%s)",
    +   PQgetvalue(result, i, 2));
    + }
    
    Isn't this code fragment (and also surrounding code) using the same
    logic as what is already encapsulated in the function
    addFooterToPublicationDesc()?
    Superficially, it seems like a large chunk can all be replaced with a
    single call to the existing function.
    
    ======
    src/test/regress/expected/publication.out
    
    8.
    +-- Syntax error EXCEPT without a col-list
    +CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except1 EXCEPT;
    +ERROR:  EXCEPT clause not allowed for table without column list
    +LINE 1: CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except...
    +                                               ^
    
    Is that a bad syntax position marker (^)? e.g. Why is it pointed at
    the word "TABLE" instead of "EXCEPT"?
    
    ======
    .../t/037_rep_changes_except_collist.pl
    
    9.
    +# Test initial sync
    +my $result = $node_subscriber->safe_psql('postgres', "SELECT * FROM tab1");
    +is($result, qq(|2|3),
    + 'check that initial sync for EXCEPT (column-list) publication');
    +$result = $node_subscriber->safe_psql('postgres', "SELECT * FROM sch1.tab1");
    +is($result, qq(1||),
    + 'check that initial sync for EXCEPT (column-list) publication');
    
    These messages still seem to have missing or extra words: "check that
    initial sync" (??). Maybe just remove the word 'that'?
    
    ~~~
    
    10.
    # Test for update
    $node_subscriber->safe_psql(
    'postgres', qq(
    CREATE UNIQUE INDEX b_idx ON tab1 (b);
    ALTER TABLE tab1 REPLICA IDENTITY USING INDEX b_idx;
    ));
    $node_publisher->safe_psql(
    'postgres', qq(
    CREATE UNIQUE INDEX b_idx ON tab1 (b);
    ALTER TABLE tab1 REPLICA IDENTITY USING INDEX b_idx;
    UPDATE tab1 SET a = 3, b = 4, c = 5 WHERE a = 1;
    ));
    $node_publisher->wait_for_catchup('tap_sub_col');
    $result = $node_subscriber->safe_psql('postgres', "SELECT * FROM tab1");
    is( $result, qq(|5|6
    |4|5),
    'check update for EXCEPT (column-list) publication');
    
    ~
    
    10a.
    I think the test is OK, but your chosen numbers like 1,2,3, then 4,5,6
    and then updating to 1,2,3 to 3,4,5 make it quite hard to review.
    Maybe use easier numbers that are more identifiable, e.g. update 1,2,3
    => 991,992,993 or something like that.
    
    ~
    
    10b.
    You may need to put some ORDER BY in all these queries just to make
    sure they are always reproducible, giving rows in the expected order.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  110. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-08-06T13:10:55Z

    On Mon, 4 Aug 2025 at 13:03, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Mon, Aug 4, 2025 at 2:07 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > ...
    > > > 10b.
    > > > How does "ALTER PUBLICATION tap_pub_col SET
    > > > (publish_generated_columns)" even work? I thought the
    > > > "pubish_generated_columns" is an enum but you did not specify any enum
    > > > value here (???)
    > > >
    > > > ~~~
    > > Yes, it works. It works equivalent to publish_generated_columns = stored.
    > > Eg:
    > > postgres=# CREATE PUBLICATION pub1 FOR TABLE t1 with
    > > (publish_generated_columns);
    > > CREATE PUBLICATION
    > > postgres=# select * from pg_publication;
    > >   oid  | pubname | pubowner | puballtables | pubinsert | pubupdate |
    > > pubdelete | pubtruncate | pubviaroot | pubgencols
    > > -------+---------+----------+--------------+-----------+-----------+-----------+-------------+------------+------------
    > >  16395 | pub1    |       10 | f            | t         | t         | t
    > >         | t           | f          | s
    > > (1 row)
    > >
    >
    > Hmm -- it's not documented to behave like that, so I've created
    > another thread for getting to the bottom of this topic.
    >
    > ~~~
    >
    > Meanwhile, here are my review comments for patch v18-0003
    >
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > pg_get_publication_tables:
    >
    > 1.
    > if (nattnums > 0)
    > {
    > values[2] = PointerGetDatum(buildint2vector(attnums, nattnums));
    > nulls[2] = false;
    > }
    > else
    > nulls[2] = true;
    >
    > Is there any possibility that values[2] might not be null, but then
    > nattrnums skips some cols so remains 0? Then the final values[2] would
    > conflict with nulls[2], which seems strange. Maybe it is safer to also
    > assign values[2] = null in the else.
    >
    Yes, When all the columns of a table are present in 'EXCEPT
    (column-list)'. Then effectively no column should be replicated. In
    such cases we should mark nulls[2] as true.
    I agree with your point that values[2] should be made null. I have
    used '(Datum) 0', in accordance with other places.
    
    > ======
    > src/backend/replication/logical/tablesync.c
    >
    > fetch_remote_table_info:
    >
    > 2.
    >  static void
    >  fetch_remote_table_info(char *nspname, char *relname, LogicalRepRelation *lrel,
    > - List **qual, bool *gencol_published)
    > + List **qual, bool *gencol_published,
    > + bool *no_cols_published)
    >
    > This new parameter should be documented in the function comment.
    >
    > ~~~
    >
    > 3.
    > + if (server_version >= 190000)
    > + *no_cols_published = DatumGetBool(slot_getattr(tslot, 2, &isnull));
    > +
    >
    > It seems that *no_cols_published (and *gencol_published) are assigned
    > false by the caller. I had to go looking for that, so IMO it would be
    > better to put Assert at the top of here so it is self-documenting
    >
    > Assert(*gencol_published == false);
    > Assert(*no_cols_published == false);
    >
    > ======
    > src/backend/replication/pgoutput/pgoutput.c
    >
    > 4.
    > + /*
    > + * Indicates whether no columns are published for a given relation. With
    > + * the introduction of the EXCEPT clause in column lists, it is now
    > + * possible to define a publication that excludes all columns of a table.
    > + * However, the 'columns' attribute cannot represent this case, since a
    > + * NULL value implies that all columns are published. To distinguish this
    > + * scenario, the 'no_cols_published' flag is introduced.
    > + */
    > + bool no_cols_published;
    >
    > The wording of the comment seems a bit strange -- EXCEPT is not a clause.
    >
    > BEFORE:
    > the introduction of the EXCEPT clause in column lists, ...
    >
    > SUGGESTION
    > the introduction of the EXCEPT qualifier for column lists, ....
    >
    > ~~~
    >
    > 5.
    >   Bitmapset  *cols = NULL;
    > + bool except_columns = false;
    > + bool no_col_published = false;
    >
    > There are multiple places in this patch that say:
    >
    > 'no_col_published'
    > or 'no_cols_published'
    >
    > I felt this var name can be misunderstood because it is easy to read
    > "no" as meaning "no." (aka number), and then misinterpret as
    > "number_of_cols_published".
    >
    > Maybe an unambiguous name can be found, like
    > - 'zero_cols_published' or
    > - 'nothing_published' or
    > - really make it 'num_cols_published' and check for 0.
    >
    > (so this comment applies to multiple places in the patch)
    >
    How about 'all_cols_excluded'? Or 'has_published_cols'?
    I have used 'all_cols_excluded' in this patch. Thoughts?
    
    > ~~
    >
    > 6.
    >   * of the table (including generated columns when
    >   * 'publish_generated_columns' parameter is true).
    >   */
    > - if (!cols)
    > + if (!no_col_published && !cols)
    >   {
    >
    > The existing comment above this code fragment also needs to mention
    > "EXCEPT (column-list)" where all the columns are excluded
    >
    > ======
    > src/bin/psql/describe.c
    >
    > describeOneTableDetails:
    >
    > 7.
    >   /* column list (if any) */
    >   if (!PQgetisnull(result, i, 2))
    > - appendPQExpBuffer(&buf, " (%s)",
    > -   PQgetvalue(result, i, 2));
    > + {
    > + if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
    > + appendPQExpBuffer(&buf, " EXCEPT (%s)",
    > +   PQgetvalue(result, i, 2));
    > + else
    > + appendPQExpBuffer(&buf, " (%s)",
    > +   PQgetvalue(result, i, 2));
    > + }
    >
    > Isn't this code fragment (and also surrounding code) using the same
    > logic as what is already encapsulated in the function
    > addFooterToPublicationDesc()?
    > Superficially, it seems like a large chunk can all be replaced with a
    > single call to the existing function.
    >
    'addFooterToPublicationDesc' is called when we use \dRp+ and print in format:
    "schema_name.table_name" EXCEPT (column-list)
    Whereas code pasted above is executed when we use \d+ table_name and
    the output is the format:
    "publication_name" EXCEPT (column-list)
    
    These pieces of code are used to print different info. One is used to
    print info related to tables and the other is used to print info
    related to publication.
    Should we use a common function for this?
    
    > ======
    > src/test/regress/expected/publication.out
    >
    > 8.
    > +-- Syntax error EXCEPT without a col-list
    > +CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except1 EXCEPT;
    > +ERROR:  EXCEPT clause not allowed for table without column list
    > +LINE 1: CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except...
    > +                                               ^
    >
    > Is that a bad syntax position marker (^)? e.g. Why is it pointed at
    > the word "TABLE" instead of "EXCEPT"?
    >
    In function 'preprocess_pubobj_list' the position of position marker
    (^) is decided by "pubobj->location". Function handles multiple errors
    and setting "$$->location" only specific to EXCEPT qualifier would not
    be appropriate. One solution I feel is to not show "position marker
    (^)" in the case of EXCEPT. Or maybe we can add a new variable to
    'PublicationTable' for except_location but I think we should not do
    that. Thoughts?
    
    For this version of patch, I have removed the "position marker (^)" in
    the case of EXCEPT.
    
    > ======
    > .../t/037_rep_changes_except_collist.pl
    >
    > 9.
    > +# Test initial sync
    > +my $result = $node_subscriber->safe_psql('postgres', "SELECT * FROM tab1");
    > +is($result, qq(|2|3),
    > + 'check that initial sync for EXCEPT (column-list) publication');
    > +$result = $node_subscriber->safe_psql('postgres', "SELECT * FROM sch1.tab1");
    > +is($result, qq(1||),
    > + 'check that initial sync for EXCEPT (column-list) publication');
    >
    > These messages still seem to have missing or extra words: "check that
    > initial sync" (??). Maybe just remove the word 'that'?
    >
    > ~~~
    >
    > 10.
    > # Test for update
    > $node_subscriber->safe_psql(
    > 'postgres', qq(
    > CREATE UNIQUE INDEX b_idx ON tab1 (b);
    > ALTER TABLE tab1 REPLICA IDENTITY USING INDEX b_idx;
    > ));
    > $node_publisher->safe_psql(
    > 'postgres', qq(
    > CREATE UNIQUE INDEX b_idx ON tab1 (b);
    > ALTER TABLE tab1 REPLICA IDENTITY USING INDEX b_idx;
    > UPDATE tab1 SET a = 3, b = 4, c = 5 WHERE a = 1;
    > ));
    > $node_publisher->wait_for_catchup('tap_sub_col');
    > $result = $node_subscriber->safe_psql('postgres', "SELECT * FROM tab1");
    > is( $result, qq(|5|6
    > |4|5),
    > 'check update for EXCEPT (column-list) publication');
    >
    > ~
    >
    > 10a.
    > I think the test is OK, but your chosen numbers like 1,2,3, then 4,5,6
    > and then updating to 1,2,3 to 3,4,5 make it quite hard to review.
    > Maybe use easier numbers that are more identifiable, e.g. update 1,2,3
    > => 991,992,993 or something like that.
    >
    > ~
    >
    > 10b.
    > You may need to put some ORDER BY in all these queries just to make
    > sure they are always reproducible, giving rows in the expected order.
    >
    
    I have also addressed the remaining comments and attached the latest
    v19 patches.
    
    Thanks,
    Shlok Kyal
    
  111. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-08-11T08:25:12Z

    Hi Shlok.
    
    On Wed, Aug 6, 2025 at 11:11 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    ...
    > > 5.
    > >   Bitmapset  *cols = NULL;
    > > + bool except_columns = false;
    > > + bool no_col_published = false;
    > >
    > > There are multiple places in this patch that say:
    > >
    > > 'no_col_published'
    > > or 'no_cols_published'
    > >
    > > I felt this var name can be misunderstood because it is easy to read
    > > "no" as meaning "no." (aka number), and then misinterpret as
    > > "number_of_cols_published".
    > >
    > > Maybe an unambiguous name can be found, like
    > > - 'zero_cols_published' or
    > > - 'nothing_published' or
    > > - really make it 'num_cols_published' and check for 0.
    > >
    > > (so this comment applies to multiple places in the patch)
    > >
    > How about 'all_cols_excluded'? Or 'has_published_cols'?
    > I have used 'all_cols_excluded' in this patch. Thoughts?
    
    The new name is good.
    
    > > ======
    > > src/bin/psql/describe.c
    > >
    > > describeOneTableDetails:
    > >
    > > 7.
    > >   /* column list (if any) */
    > >   if (!PQgetisnull(result, i, 2))
    > > - appendPQExpBuffer(&buf, " (%s)",
    > > -   PQgetvalue(result, i, 2));
    > > + {
    > > + if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
    > > + appendPQExpBuffer(&buf, " EXCEPT (%s)",
    > > +   PQgetvalue(result, i, 2));
    > > + else
    > > + appendPQExpBuffer(&buf, " (%s)",
    > > +   PQgetvalue(result, i, 2));
    > > + }
    > >
    > > Isn't this code fragment (and also surrounding code) using the same
    > > logic as what is already encapsulated in the function
    > > addFooterToPublicationDesc()?
    > > Superficially, it seems like a large chunk can all be replaced with a
    > > single call to the existing function.
    > >
    > 'addFooterToPublicationDesc' is called when we use \dRp+ and print in format:
    > "schema_name.table_name" EXCEPT (column-list)
    > Whereas code pasted above is executed when we use \d+ table_name and
    > the output is the format:
    > "publication_name" EXCEPT (column-list)
    >
    > These pieces of code are used to print different info. One is used to
    > print info related to tables and the other is used to print info
    > related to publication.
    > Should we use a common function for this?
    
    It still seems like quite a lot of overlap. e.g. I thought there were
    ~30 lines common. OTOH, perhaps you'll need to pass another boolean to
    the function to indicate it is a "Publication:" footer. I guess you'd
    have to try it out first to see if the changes required to save those
    30 LOC are worthwhile or not.
    
    >
    > > ======
    > > src/test/regress/expected/publication.out
    > >
    > > 8.
    > > +-- Syntax error EXCEPT without a col-list
    > > +CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except1 EXCEPT;
    > > +ERROR:  EXCEPT clause not allowed for table without column list
    > > +LINE 1: CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except...
    > > +                                               ^
    > >
    > > Is that a bad syntax position marker (^)? e.g. Why is it pointed at
    > > the word "TABLE" instead of "EXCEPT"?
    > >
    > In function 'preprocess_pubobj_list' the position of position marker
    > (^) is decided by "pubobj->location". Function handles multiple errors
    > and setting "$$->location" only specific to EXCEPT qualifier would not
    > be appropriate. One solution I feel is to not show "position marker
    > (^)" in the case of EXCEPT. Or maybe we can add a new variable to
    > 'PublicationTable' for except_location but I think we should not do
    > that. Thoughts?
    
    In the review comments below, I suggest putting this location back,
    but changing the message.
    
    >
    > For this version of patch, I have removed the "position marker (^)" in
    > the case of EXCEPT.
    >
    
    //////
    
    Here are my review comments for the patch v19-0003.
    
    ======
    1. General - SGML tags in docs for table/column names.
    
    There is nothing to change just yet, but keep an eye on the thread
    [1],  because if/when that gets pushed, then there will several tags
    in this patch for table/column names that will need to be updated for
    consistency.
    
    ======
    src/backend/catalog/pg_publication.c
    
    pg_get_publication_tables:
    
    2.
    +
    + if (!nulls[2])
    + {
    + Datum exceptDatum;
    + bool isnull;
    +
    + /*
    + * We fetch pubtuple if publication is not FOR ALL TABLES and
    + * not FOR TABLES IN SCHEMA. So if prexcept is true, it
    + * indicates that prattrs contains columns to be excluded for
    + * replication.
    + */
    + exceptDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
    +   Anum_pg_publication_rel_prexcept,
    +   &isnull);
    +
    + if (!isnull && DatumGetBool(exceptDatum))
    + except_columns = pub_collist_to_bitmapset(NULL, values[2], NULL);
    + }
    
    Maybe this should be done a few lines earlier, to keep all the
    values[2]/nulls[2] code together, ahead of the values[3]/nulls[3]
    code. Indeed, there is lots of other values[2]/nulls[2] logic that
    comes later in this function, so maybe it is better to do all of that
    first, instead of mingling it with values[3]/nulls[3].
    
    ======
    src/backend/commands/publicationcmds.c
    
    pub_contains_invalid_column:
    
    3.
      * 1. Ensures that all columns referenced in the REPLICA IDENTITY are covered
    - *    by the column list. If any column is missing, *invalid_column_list is set
    + *    by the column list and are not part of column list specified with EXCEPT.
    + *   If any column is missing, *invalid_column_list is set
      *    to true.
    
    Whitespace problem here; there is some tab instead of space in this comment.
    
    Also /part of column list/part of the column list/
    
    ~~~
    
    AlterPublicationTables:
    
    4.
      bool isnull = true;
      Datum whereClauseDatum;
      Datum columnListDatum;
    + Datum exceptDatum;
    
    It's not necessary to have all these different Datum variables; they
    are only temporary storage. It might be simpler to use a single "Datum
    datum;" which is reused 3x.
    
    ~
    
    5.
    + exceptDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    +   Anum_pg_publication_rel_prexcept,
    +   &isnull);
    +
    + if (!isnull)
    + oldexcept = DatumGetBool(exceptDatum);
    +
    
    Isn't the 'prexcept' also used for EXCEPT TABLE as well as EXCEPT
    (column-list)? In other words, should the change to this function be
    done already in one of the earlier patches?
    
    ~
    
    6.
      if (equal(oldrelwhereclause, newpubrel->whereClause) &&
    - bms_equal(oldcolumns, newcolumns))
    + bms_equal(oldcolumns, newcolumns) &&
    + oldexcept == newpubrel->except)
    
    The code comment about this code fragment should also mention EXCEPT.
    
    ======
    src/backend/parser/gram.y
    
    preprocess_pubobj_list:
    
    7.
    + if (pubobj->pubtable && pubobj->pubtable->except &&
    + pubobj->pubtable->columns == NULL)
    + ereport(ERROR,
    + errcode(ERRCODE_SYNTAX_ERROR),
    + errmsg("EXCEPT clause not allowed for table without column list"));
    +
    
    Having the syntax error location (like before in v18) might be better,
    but since that location is associated with the TABLE, then the error
    message should also be reworded so the subject is the table.
    
    SUGGESTION
    errmsg("table without column list cannot use EXCEPT clause")
    
    ======
    src/bin/psql/describe.c
    
    describeOneTableDetails:
    
    8.
    - if (pset.sversion >= 150000)
    + if (pset.sversion >= 190000)
      {
      printfPQExpBuffer(&buf,
        "SELECT pubname\n"
        "     , NULL\n"
        "     , NULL\n"
    +   " , NULL\n"
        "FROM pg_catalog.pg_publication p\n"
        "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
        "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
    @@ -3038,35 +3039,62 @@ describeOneTableDetails(const char *schemaname,
        "                pg_catalog.pg_attribute\n"
        "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
        "        ELSE NULL END) "
    +   " , prexcept "
        "FROM pg_catalog.pg_publication p\n"
        " JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
        " JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    -   "WHERE pr.prrelid = '%s'\n",
    -   oid, oid, oid);
    -
    - if (pset.sversion >= 190000)
    - appendPQExpBufferStr(&buf, " AND NOT pr.prexcept\n");
    +   "WHERE pr.prrelid = '%s' "
    +   "AND  c.relnamespace NOT IN (\n "
    +   " SELECT pnnspid FROM\n"
    +   " pg_catalog.pg_publication_namespace)\n"
    
    - appendPQExpBuffer(&buf,
        "UNION\n"
        "SELECT pubname\n"
        " , NULL\n"
        " , NULL\n"
    +   " , NULL\n"
        "FROM pg_catalog.pg_publication p\n"
    -   "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n",
    -   oid);
    -
    - if (pset.sversion >= 190000)
    - appendPQExpBuffer(&buf,
    -   "     AND NOT EXISTS (\n"
    -   " SELECT 1\n"
    -   " FROM pg_catalog.pg_publication_rel pr\n"
    -   " JOIN pg_catalog.pg_class pc\n"
    -   " ON pr.prrelid = pc.oid\n"
    -   " WHERE pr.prrelid = '%s' AND pr.prpubid = p.oid)\n",
    -   oid);
    -
    - appendPQExpBufferStr(&buf, "ORDER BY 1;");
    +   "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
    +   "     AND NOT EXISTS (\n"
    +   " SELECT 1\n"
    +   " FROM pg_catalog.pg_publication_rel pr\n"
    +   " JOIN pg_catalog.pg_class pc\n"
    +   " ON pr.prrelid = pc.oid\n"
    +   " WHERE pr.prrelid = '%s' AND pr.prpubid = p.oid)\n"
    +   "ORDER BY 1;",
    +   oid, oid, oid, oid, oid);
    + }
    + else if (pset.sversion >= 150000)
    + {
    + printfPQExpBuffer(&buf,
    +   "SELECT pubname\n"
    +   "     , NULL\n"
    +   "     , NULL\n"
    +   "FROM pg_catalog.pg_publication p\n"
    +   "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
    +   "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
    +   "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
    +   "UNION\n"
    +   "SELECT pubname\n"
    +   "     , pg_get_expr(pr.prqual, c.oid)\n"
    +   "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
    +   "         (SELECT string_agg(attname, ', ')\n"
    +   "           FROM pg_catalog.generate_series(0,
    pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
    +   "                pg_catalog.pg_attribute\n"
    +   "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
    +   "        ELSE NULL END) "
    +   "FROM pg_catalog.pg_publication p\n"
    +   "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
    +   "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    +   "WHERE pr.prrelid = '%s'\n"
    +   "UNION\n"
    +   "SELECT pubname\n"
    +   "     , NULL\n"
    +   "     , NULL\n"
    +   "FROM pg_catalog.pg_publication p\n"
    +   "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
    +   "ORDER BY 1;",
    +   oid, oid, oid, oid);
    
    I found these large SQL selects with 3x UNIONs are difficult to read.
    Maybe you can add more comments to describe the intention of each of
    the UNION SELECTs?
    
    ~~~
    
    9.
      /* column list (if any) */
      if (!PQgetisnull(result, i, 2))
    - appendPQExpBuffer(&buf, " (%s)",
    -   PQgetvalue(result, i, 2));
    + {
    + if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
    + appendPQExpBuffer(&buf, " EXCEPT");
    + appendPQExpBuffer(&buf, " (%s)", PQgetvalue(result, i, 2));
    + }
    
    I did not find any regression test case where the "EXCEPT" col-list is
    getting output for a "Publications:" footer.
    
    ======
    [1] https://www.postgresql.org/message-id/aIELRMAviNiUL1ie%40momjian.us
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  112. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-08-13T09:21:42Z

    On Mon, 11 Aug 2025 at 13:55, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > On Wed, Aug 6, 2025 at 11:11 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > ...
    > > > 5.
    > > >   Bitmapset  *cols = NULL;
    > > > + bool except_columns = false;
    > > > + bool no_col_published = false;
    > > >
    > > > There are multiple places in this patch that say:
    > > >
    > > > 'no_col_published'
    > > > or 'no_cols_published'
    > > >
    > > > I felt this var name can be misunderstood because it is easy to read
    > > > "no" as meaning "no." (aka number), and then misinterpret as
    > > > "number_of_cols_published".
    > > >
    > > > Maybe an unambiguous name can be found, like
    > > > - 'zero_cols_published' or
    > > > - 'nothing_published' or
    > > > - really make it 'num_cols_published' and check for 0.
    > > >
    > > > (so this comment applies to multiple places in the patch)
    > > >
    > > How about 'all_cols_excluded'? Or 'has_published_cols'?
    > > I have used 'all_cols_excluded' in this patch. Thoughts?
    >
    > The new name is good.
    >
    > > > ======
    > > > src/bin/psql/describe.c
    > > >
    > > > describeOneTableDetails:
    > > >
    > > > 7.
    > > >   /* column list (if any) */
    > > >   if (!PQgetisnull(result, i, 2))
    > > > - appendPQExpBuffer(&buf, " (%s)",
    > > > -   PQgetvalue(result, i, 2));
    > > > + {
    > > > + if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
    > > > + appendPQExpBuffer(&buf, " EXCEPT (%s)",
    > > > +   PQgetvalue(result, i, 2));
    > > > + else
    > > > + appendPQExpBuffer(&buf, " (%s)",
    > > > +   PQgetvalue(result, i, 2));
    > > > + }
    > > >
    > > > Isn't this code fragment (and also surrounding code) using the same
    > > > logic as what is already encapsulated in the function
    > > > addFooterToPublicationDesc()?
    > > > Superficially, it seems like a large chunk can all be replaced with a
    > > > single call to the existing function.
    > > >
    > > 'addFooterToPublicationDesc' is called when we use \dRp+ and print in format:
    > > "schema_name.table_name" EXCEPT (column-list)
    > > Whereas code pasted above is executed when we use \d+ table_name and
    > > the output is the format:
    > > "publication_name" EXCEPT (column-list)
    > >
    > > These pieces of code are used to print different info. One is used to
    > > print info related to tables and the other is used to print info
    > > related to publication.
    > > Should we use a common function for this?
    >
    > It still seems like quite a lot of overlap. e.g. I thought there were
    > ~30 lines common. OTOH, perhaps you'll need to pass another boolean to
    > the function to indicate it is a "Publication:" footer. I guess you'd
    > have to try it out first to see if the changes required to save those
    > 30 LOC are worthwhile or not.
    >
    I have added the code changes for the same in this patch.
    
    > >
    > > > ======
    > > > src/test/regress/expected/publication.out
    > > >
    > > > 8.
    > > > +-- Syntax error EXCEPT without a col-list
    > > > +CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except1 EXCEPT;
    > > > +ERROR:  EXCEPT clause not allowed for table without column list
    > > > +LINE 1: CREATE PUBLICATION testpub_except2 FOR TABLE pub_test_except...
    > > > +                                               ^
    > > >
    > > > Is that a bad syntax position marker (^)? e.g. Why is it pointed at
    > > > the word "TABLE" instead of "EXCEPT"?
    > > >
    > > In function 'preprocess_pubobj_list' the position of position marker
    > > (^) is decided by "pubobj->location". Function handles multiple errors
    > > and setting "$$->location" only specific to EXCEPT qualifier would not
    > > be appropriate. One solution I feel is to not show "position marker
    > > (^)" in the case of EXCEPT. Or maybe we can add a new variable to
    > > 'PublicationTable' for except_location but I think we should not do
    > > that. Thoughts?
    >
    > In the review comments below, I suggest putting this location back,
    > but changing the message.
    >
    > >
    > > For this version of patch, I have removed the "position marker (^)" in
    > > the case of EXCEPT.
    > >
    >
    > //////
    >
    > Here are my review comments for the patch v19-0003.
    >
    > ======
    > 1. General - SGML tags in docs for table/column names.
    >
    > There is nothing to change just yet, but keep an eye on the thread
    > [1],  because if/when that gets pushed, then there will several tags
    > in this patch for table/column names that will need to be updated for
    > consistency.
    >
    Noted
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > pg_get_publication_tables:
    >
    > 2.
    > +
    > + if (!nulls[2])
    > + {
    > + Datum exceptDatum;
    > + bool isnull;
    > +
    > + /*
    > + * We fetch pubtuple if publication is not FOR ALL TABLES and
    > + * not FOR TABLES IN SCHEMA. So if prexcept is true, it
    > + * indicates that prattrs contains columns to be excluded for
    > + * replication.
    > + */
    > + exceptDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubtuple,
    > +   Anum_pg_publication_rel_prexcept,
    > +   &isnull);
    > +
    > + if (!isnull && DatumGetBool(exceptDatum))
    > + except_columns = pub_collist_to_bitmapset(NULL, values[2], NULL);
    > + }
    >
    > Maybe this should be done a few lines earlier, to keep all the
    > values[2]/nulls[2] code together, ahead of the values[3]/nulls[3]
    > code. Indeed, there is lots of other values[2]/nulls[2] logic that
    > comes later in this function, so maybe it is better to do all of that
    > first, instead of mingling it with values[3]/nulls[3].
    >
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > pub_contains_invalid_column:
    >
    > 3.
    >   * 1. Ensures that all columns referenced in the REPLICA IDENTITY are covered
    > - *    by the column list. If any column is missing, *invalid_column_list is set
    > + *    by the column list and are not part of column list specified with EXCEPT.
    > + *   If any column is missing, *invalid_column_list is set
    >   *    to true.
    >
    > Whitespace problem here; there is some tab instead of space in this comment.
    >
    > Also /part of column list/part of the column list/
    >
    > ~~~
    >
    > AlterPublicationTables:
    >
    > 4.
    >   bool isnull = true;
    >   Datum whereClauseDatum;
    >   Datum columnListDatum;
    > + Datum exceptDatum;
    >
    > It's not necessary to have all these different Datum variables; they
    > are only temporary storage. It might be simpler to use a single "Datum
    > datum;" which is reused 3x.
    >
    > ~
    >
    > 5.
    > + exceptDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > +   Anum_pg_publication_rel_prexcept,
    > +   &isnull);
    > +
    > + if (!isnull)
    > + oldexcept = DatumGetBool(exceptDatum);
    > +
    >
    > Isn't the 'prexcept' also used for EXCEPT TABLE as well as EXCEPT
    > (column-list)? In other words, should the change to this function be
    > done already in one of the earlier patches?
    >
    > ~
    This code path is only executed when running ALTER PUBLICATION ... SET
    TABLE and running this command on a  ALL TABLES publication throws an
    error due to check by function 'CheckAlterPublication' . And EXCEPT
    TABLE can only be used for ALL TABLES publications, I think it doesn’t
    need to be moved to the 0002 patch.
    
    >
    > 6.
    >   if (equal(oldrelwhereclause, newpubrel->whereClause) &&
    > - bms_equal(oldcolumns, newcolumns))
    > + bms_equal(oldcolumns, newcolumns) &&
    > + oldexcept == newpubrel->except)
    >
    > The code comment about this code fragment should also mention EXCEPT.
    >
    > ======
    > src/backend/parser/gram.y
    >
    > preprocess_pubobj_list:
    >
    > 7.
    > + if (pubobj->pubtable && pubobj->pubtable->except &&
    > + pubobj->pubtable->columns == NULL)
    > + ereport(ERROR,
    > + errcode(ERRCODE_SYNTAX_ERROR),
    > + errmsg("EXCEPT clause not allowed for table without column list"));
    > +
    >
    > Having the syntax error location (like before in v18) might be better,
    > but since that location is associated with the TABLE, then the error
    > message should also be reworded so the subject is the table.
    >
    > SUGGESTION
    > errmsg("table without column list cannot use EXCEPT clause")
    >
    > ======
    > src/bin/psql/describe.c
    >
    > describeOneTableDetails:
    >
    > 8.
    > - if (pset.sversion >= 150000)
    > + if (pset.sversion >= 190000)
    >   {
    >   printfPQExpBuffer(&buf,
    >     "SELECT pubname\n"
    >     "     , NULL\n"
    >     "     , NULL\n"
    > +   " , NULL\n"
    >     "FROM pg_catalog.pg_publication p\n"
    >     "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
    >     "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
    > @@ -3038,35 +3039,62 @@ describeOneTableDetails(const char *schemaname,
    >     "                pg_catalog.pg_attribute\n"
    >     "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
    >     "        ELSE NULL END) "
    > +   " , prexcept "
    >     "FROM pg_catalog.pg_publication p\n"
    >     " JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
    >     " JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    > -   "WHERE pr.prrelid = '%s'\n",
    > -   oid, oid, oid);
    > -
    > - if (pset.sversion >= 190000)
    > - appendPQExpBufferStr(&buf, " AND NOT pr.prexcept\n");
    > +   "WHERE pr.prrelid = '%s' "
    > +   "AND  c.relnamespace NOT IN (\n "
    > +   " SELECT pnnspid FROM\n"
    > +   " pg_catalog.pg_publication_namespace)\n"
    >
    > - appendPQExpBuffer(&buf,
    >     "UNION\n"
    >     "SELECT pubname\n"
    >     " , NULL\n"
    >     " , NULL\n"
    > +   " , NULL\n"
    >     "FROM pg_catalog.pg_publication p\n"
    > -   "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n",
    > -   oid);
    > -
    > - if (pset.sversion >= 190000)
    > - appendPQExpBuffer(&buf,
    > -   "     AND NOT EXISTS (\n"
    > -   " SELECT 1\n"
    > -   " FROM pg_catalog.pg_publication_rel pr\n"
    > -   " JOIN pg_catalog.pg_class pc\n"
    > -   " ON pr.prrelid = pc.oid\n"
    > -   " WHERE pr.prrelid = '%s' AND pr.prpubid = p.oid)\n",
    > -   oid);
    > -
    > - appendPQExpBufferStr(&buf, "ORDER BY 1;");
    > +   "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
    > +   "     AND NOT EXISTS (\n"
    > +   " SELECT 1\n"
    > +   " FROM pg_catalog.pg_publication_rel pr\n"
    > +   " JOIN pg_catalog.pg_class pc\n"
    > +   " ON pr.prrelid = pc.oid\n"
    > +   " WHERE pr.prrelid = '%s' AND pr.prpubid = p.oid)\n"
    > +   "ORDER BY 1;",
    > +   oid, oid, oid, oid, oid);
    > + }
    > + else if (pset.sversion >= 150000)
    > + {
    > + printfPQExpBuffer(&buf,
    > +   "SELECT pubname\n"
    > +   "     , NULL\n"
    > +   "     , NULL\n"
    > +   "FROM pg_catalog.pg_publication p\n"
    > +   "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
    > +   "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
    > +   "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
    > +   "UNION\n"
    > +   "SELECT pubname\n"
    > +   "     , pg_get_expr(pr.prqual, c.oid)\n"
    > +   "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
    > +   "         (SELECT string_agg(attname, ', ')\n"
    > +   "           FROM pg_catalog.generate_series(0,
    > pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
    > +   "                pg_catalog.pg_attribute\n"
    > +   "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
    > +   "        ELSE NULL END) "
    > +   "FROM pg_catalog.pg_publication p\n"
    > +   "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
    > +   "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    > +   "WHERE pr.prrelid = '%s'\n"
    > +   "UNION\n"
    > +   "SELECT pubname\n"
    > +   "     , NULL\n"
    > +   "     , NULL\n"
    > +   "FROM pg_catalog.pg_publication p\n"
    > +   "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
    > +   "ORDER BY 1;",
    > +   oid, oid, oid, oid);
    >
    > I found these large SQL selects with 3x UNIONs are difficult to read.
    > Maybe you can add more comments to describe the intention of each of
    > the UNION SELECTs?
    >
    > ~~~
    >
    > 9.
    >   /* column list (if any) */
    >   if (!PQgetisnull(result, i, 2))
    > - appendPQExpBuffer(&buf, " (%s)",
    > -   PQgetvalue(result, i, 2));
    > + {
    > + if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
    > + appendPQExpBuffer(&buf, " EXCEPT");
    > + appendPQExpBuffer(&buf, " (%s)", PQgetvalue(result, i, 2));
    > + }
    >
    > I did not find any regression test case where the "EXCEPT" col-list is
    > getting output for a "Publications:" footer.
    >
    > ======
    > [1] https://www.postgresql.org/message-id/aIELRMAviNiUL1ie%40momjian.us
    >
    
    I have addressed the comments and the changes in v20 patch.
    
    Thanks,
    Shlok Kyal
    
  113. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-08-15T00:53:10Z

    Hi Shlok,
    
    Here are some review comments for v20-0003.
    
    ======
    src/backend/commands/publicationcmds.c
    
    AlterPublicationTables:
    
    1.
      bool isnull = true;
    - Datum whereClauseDatum;
    - Datum columnListDatum;
    + Datum datum;
    
    I know you did not write the code, but that "isnull = true" is
    redundant, and seems kind of misleading because it will always be
    re-assigned before it is used.
    
    ~~~
    
    2.
      /* Load the WHERE clause for this table. */
    - whereClauseDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    -    Anum_pg_publication_rel_prqual,
    -    &isnull);
    + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    + Anum_pg_publication_rel_prqual,
    + &isnull);
      if (!isnull)
    - oldrelwhereclause = stringToNode(TextDatumGetCString(whereClauseDatum));
    + oldrelwhereclause = stringToNode(TextDatumGetCString(datum));
    
      /* Transform the int2vector column list to a bitmap. */
    - columnListDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    -   Anum_pg_publication_rel_prattrs,
    -   &isnull);
    + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    + Anum_pg_publication_rel_prattrs,
    + &isnull);
    +
    + if (!isnull)
    + oldcolumns = pub_collist_to_bitmapset(NULL, datum, NULL);
    +
    + /* Load the prexcept flag for this table. */
    + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    + Anum_pg_publication_rel_prexcept,
    + &isnull);
    
      if (!isnull)
    - oldcolumns = pub_collist_to_bitmapset(NULL, columnListDatum, NULL);
    + oldexcept = DatumGetBool(datum);
    
    Use consistent spacing. Either do or don't (I prefer don't) put a
    blank line between the pairs of "datum =" and "if (!isnull)". Avoid
    having a mixture.
    
    ======
    src/bin/psql/describe.c
    
    addFooterToPublicationOrTableDesc:
    
    3.
    +/*
    + * If is_tbl_desc is true add footer to table description else add footer to
    + * publication description.
    + */
    +static bool
    +addFooterToPublicationOrTableDesc(PQExpBuffer buf, const char *footermsg,
    +   bool as_schema, printTableContent *const cont,
    +   bool is_tbl_desc)
    
    3a.
    Since you are changing this anyway, I think it would be better to keep
    those boolean params together (at the end).
    
    ~
    
    3b.
    It seems a bit mixed up calling this addFooterToPublicationOrTableDesc
    but having the variable 'is_tbl_desc', because it seems more natural
    to me to read left to right, so the logical order of everything here
    should be pub desc then table desc. In other words, use boolean
    'is_pub_desc' instead of 'is_tbl_desc'. Also, I think that 'as_schema'
    thing is kind of a *subset* of the publication description, so it
    makes more sense for that to come last too.
    
    e.g.
    CURRENT
    addFooterToPublicationOrTableDesc(buf, footermsg, as_schema, cont, is_tbl_desc)
    SUGGESTION
    addFooterToPublicationOrTableDesc(buf, cont, footermsg, is_pub_desc, as_schema)
    
    ~
    
    3c
    While you are changing things, maybe also consider changing that
    'as_schema' name because I did not understand what "as" means. Perhaps
    rename like 'pub_schemas', or 'only_show_schemas' or something better
    (???).
    
    ~~~
    
    4.
    + PGresult   *res;
    + int count = 0;
    + int i = 0;
    + int col = is_tbl_desc ? 0 : 1;
    +
    + res = PSQLexec(buf->data);
    + if (!res)
    + return false;
    + else
    + count = PQntuples(res);
    +
    
    4a.
    Assignment count = 0 is redundant.
    
    ~
    
    4b.
    Remove the 'i' declaration here. Declare it in the "for" loop later.
    
    ~
    
    4c.
    The "else" is not required. If 'res' was not good, you already returned.
    
    ~~~
    
    5.
    + if (as_schema)
    + printfPQExpBuffer(buf, "    \"%s\"", PQgetvalue(res, i, 0));
    + else
    + {
    + if (is_tbl_desc)
    + printfPQExpBuffer(buf, "    \"%s\"", PQgetvalue(res, i, col));
    + else
    + printfPQExpBuffer(buf, "    \"%s.%s\"", PQgetvalue(res, i, 0),
    +   PQgetvalue(res, i, col));
    
    This function is basically either (a) a footer for a table description
    or (b) a footer for a publication description. And that all hinges on
    the boolean 'is_tbl_desc'. Therefore, it seems more natural for the
    main condition to be "if (is_tbl_desc)" here.
    
    This turned everything inside out. PSA: a top-up patch to show a way
    to do this. Perhaps my implementation is a bit verbose, but OTOH it
    seems easier to understand. Anyway, see what you think...
    
    ~~~
    
    6.
    + /*---------------------------------------------------
    + * Publication/ table description columns:
    + * [0]: schema name (nspname)
    + * [col]: table name (relname) / publication name (pubname)
    + * [col + 1]: row filter expression (prqual), may be NULL
    + * [col + 2]: column list (comma-separated), may be NULL
    + * [col + 3]: except flag ("t" if EXCEPT, else "f")
    + *---------------------------------------------------
    
    I've modified this comment slightly so I could understand it better.
    See if you agree.
    
    SUGGESTION
    /*---------------------------------------------------
     * Description columns:
     * PUB      TBL
     * [0]      -      : schema name (nspname)
     * [col]    -      : table name (relname)
     * -        [col]  : publication name (pubname)
     * [col+1]  [col+1]: row filter expression (prqual), may be NULL
     * [col+2]  [col+1]: column list (comma-separated), may be NULL
     * [col+3]  [col+1]: except flag ("t" if EXCEPT, else "f")
     *---------------------------------------------------
     */
    
    ~~~
    
    describeOneTableDetails:
    
    7.
    + else if (pset.sversion >= 150000)
    + {
    + printfPQExpBuffer(&buf,
    +   "SELECT pubname\n"
    +   "     , NULL\n"
    +   "     , NULL\n"
    +   "FROM pg_catalog.pg_publication p\n"
    +   "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
    +   "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
    +   "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
    +   "UNION\n"
    +   "SELECT pubname\n"
    +   "     , pg_get_expr(pr.prqual, c.oid)\n"
    +   "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
    +   "         (SELECT string_agg(attname, ', ')\n"
    +   "           FROM pg_catalog.generate_series(0,
    pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
    +   "                pg_catalog.pg_attribute\n"
    +   "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
    +   "        ELSE NULL END) "
    +   "FROM pg_catalog.pg_publication p\n"
    +   "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
    +   "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    +   "WHERE pr.prrelid = '%s'\n"
    +   "UNION\n"
    +   "SELECT pubname\n"
    +   "     , NULL\n"
    +   "     , NULL\n"
    +   "FROM pg_catalog.pg_publication p\n"
    +   "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
    +   "ORDER BY 1;",
    +   oid, oid, oid, oid);
    
    AFAICT, that >= 150000 code seems to have added another UNION at the
    end that was not previously there. What's that about? How is that
    related to EXCEPT (column-list)?
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
  114. Re: Skipping schema changes in publication

    Kirill Reshke <reshkekirill@gmail.com> — 2025-08-15T06:16:32Z

    Hi
    
    On Fri, 15 Aug 2025 at 05:53, Peter Smith <smithpb2250@gmail.com> wrote:
    
    > 1.
    >   bool isnull = true;
    > - Datum whereClauseDatum;
    > - Datum columnListDatum;
    > + Datum datum;
    >
    > I know you did not write the code, but that "isnull = true" is
    > redundant, and seems kind of misleading because it will always be
    > re-assigned before it is used.
    
    People are not generally excited about refactoring code they did not
    change. This makes patch to have more review cycles, and less probable
    to actually being committed. If we are really wedded with this change,
    this could be a separate thread.
    
    
    > ~~~
    >
    > 2.
    >   /* Load the WHERE clause for this table. */
    > - whereClauseDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > -    Anum_pg_publication_rel_prqual,
    > -    &isnull);
    > + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > + Anum_pg_publication_rel_prqual,
    > + &isnull);
    >   if (!isnull)
    > - oldrelwhereclause = stringToNode(TextDatumGetCString(whereClauseDatum));
    > + oldrelwhereclause = stringToNode(TextDatumGetCString(datum));
    >
    >   /* Transform the int2vector column list to a bitmap. */
    > - columnListDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > -   Anum_pg_publication_rel_prattrs,
    > -   &isnull);
    > + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > + Anum_pg_publication_rel_prattrs,
    > + &isnull);
    > +
    > + if (!isnull)
    > + oldcolumns = pub_collist_to_bitmapset(NULL, datum, NULL);
    > +
    > + /* Load the prexcept flag for this table. */
    > + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > + Anum_pg_publication_rel_prexcept,
    > + &isnull);
    >
    >   if (!isnull)
    > - oldcolumns = pub_collist_to_bitmapset(NULL, columnListDatum, NULL);
    > + oldexcept = DatumGetBool(datum);
    >
    > Use consistent spacing. Either do or don't (I prefer don't) put a
    > blank line between the pairs of "datum =" and "if (!isnull)". Avoid
    > having a mixture.
    >
    > ======
    > src/bin/psql/describe.c
    >
    > addFooterToPublicationOrTableDesc:
    >
    > 3.
    > +/*
    > + * If is_tbl_desc is true add footer to table description else add footer to
    > + * publication description.
    > + */
    > +static bool
    > +addFooterToPublicationOrTableDesc(PQExpBuffer buf, const char *footermsg,
    > +   bool as_schema, printTableContent *const cont,
    > +   bool is_tbl_desc)
    >
    > 3a.
    > Since you are changing this anyway, I think it would be better to keep
    > those boolean params together (at the end).
    >
    > ~
    >
    > 3b.
    > It seems a bit mixed up calling this addFooterToPublicationOrTableDesc
    > but having the variable 'is_tbl_desc', because it seems more natural
    > to me to read left to right, so the logical order of everything here
    > should be pub desc then table desc. In other words, use boolean
    > 'is_pub_desc' instead of 'is_tbl_desc'. Also, I think that 'as_schema'
    > thing is kind of a *subset* of the publication description, so it
    > makes more sense for that to come last too.
    >
    > e.g.
    > CURRENT
    > addFooterToPublicationOrTableDesc(buf, footermsg, as_schema, cont, is_tbl_desc)
    > SUGGESTION
    > addFooterToPublicationOrTableDesc(buf, cont, footermsg, is_pub_desc, as_schema)
    >
    > ~
    >
    > 3c
    > While you are changing things, maybe also consider changing that
    > 'as_schema' name because I did not understand what "as" means. Perhaps
    > rename like 'pub_schemas', or 'only_show_schemas' or something better
    > (???).
    >
    > ~~~
    >
    > 4.
    > + PGresult   *res;
    > + int count = 0;
    > + int i = 0;
    > + int col = is_tbl_desc ? 0 : 1;
    > +
    > + res = PSQLexec(buf->data);
    > + if (!res)
    > + return false;
    > + else
    > + count = PQntuples(res);
    > +
    >
    > 4a.
    > Assignment count = 0 is redundant.
    >
    > ~
    >
    > 4b.
    > Remove the 'i' declaration here. Declare it in the "for" loop later.
    >
    > ~
    >
    > 4c.
    > The "else" is not required. If 'res' was not good, you already returned.
    >
    > ~~~
    >
    > 5.
    > + if (as_schema)
    > + printfPQExpBuffer(buf, "    \"%s\"", PQgetvalue(res, i, 0));
    > + else
    > + {
    > + if (is_tbl_desc)
    > + printfPQExpBuffer(buf, "    \"%s\"", PQgetvalue(res, i, col));
    > + else
    > + printfPQExpBuffer(buf, "    \"%s.%s\"", PQgetvalue(res, i, 0),
    > +   PQgetvalue(res, i, col));
    >
    > This function is basically either (a) a footer for a table description
    > or (b) a footer for a publication description. And that all hinges on
    > the boolean 'is_tbl_desc'. Therefore, it seems more natural for the
    > main condition to be "if (is_tbl_desc)" here.
    >
    > This turned everything inside out. PSA: a top-up patch to show a way
    > to do this. Perhaps my implementation is a bit verbose, but OTOH it
    > seems easier to understand. Anyway, see what you think...
    >
    
    + 1
    
    >
    > 6.
    > + /*---------------------------------------------------
    > + * Publication/ table description columns:
    > + * [0]: schema name (nspname)
    > + * [col]: table name (relname) / publication name (pubname)
    > + * [col + 1]: row filter expression (prqual), may be NULL
    > + * [col + 2]: column list (comma-separated), may be NULL
    > + * [col + 3]: except flag ("t" if EXCEPT, else "f")
    > + *---------------------------------------------------
    >
    > I've modified this comment slightly so I could understand it better.
    > See if you agree.
    
    For me that's equal. lets see what other people think
    
    
    -- 
    Best regards,
    Kirill Reshke
    
    
    
    
  115. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-08-20T09:00:36Z

    On Fri, 15 Aug 2025 at 06:23, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok,
    >
    > Here are some review comments for v20-0003.
    >
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > AlterPublicationTables:
    >
    > 1.
    >   bool isnull = true;
    > - Datum whereClauseDatum;
    > - Datum columnListDatum;
    > + Datum datum;
    >
    > I know you did not write the code, but that "isnull = true" is
    > redundant, and seems kind of misleading because it will always be
    > re-assigned before it is used.
    >
    Since this is part of already existing code, I think this should be a
    new thread. I have created a new thread for this. See [1].
    
    > ~~~
    >
    > 2.
    >   /* Load the WHERE clause for this table. */
    > - whereClauseDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > -    Anum_pg_publication_rel_prqual,
    > -    &isnull);
    > + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > + Anum_pg_publication_rel_prqual,
    > + &isnull);
    >   if (!isnull)
    > - oldrelwhereclause = stringToNode(TextDatumGetCString(whereClauseDatum));
    > + oldrelwhereclause = stringToNode(TextDatumGetCString(datum));
    >
    >   /* Transform the int2vector column list to a bitmap. */
    > - columnListDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > -   Anum_pg_publication_rel_prattrs,
    > -   &isnull);
    > + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > + Anum_pg_publication_rel_prattrs,
    > + &isnull);
    > +
    > + if (!isnull)
    > + oldcolumns = pub_collist_to_bitmapset(NULL, datum, NULL);
    > +
    > + /* Load the prexcept flag for this table. */
    > + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > + Anum_pg_publication_rel_prexcept,
    > + &isnull);
    >
    >   if (!isnull)
    > - oldcolumns = pub_collist_to_bitmapset(NULL, columnListDatum, NULL);
    > + oldexcept = DatumGetBool(datum);
    >
    > Use consistent spacing. Either do or don't (I prefer don't) put a
    > blank line between the pairs of "datum =" and "if (!isnull)". Avoid
    > having a mixture.
    >
    > ======
    > src/bin/psql/describe.c
    >
    > addFooterToPublicationOrTableDesc:
    >
    > 3.
    > +/*
    > + * If is_tbl_desc is true add footer to table description else add footer to
    > + * publication description.
    > + */
    > +static bool
    > +addFooterToPublicationOrTableDesc(PQExpBuffer buf, const char *footermsg,
    > +   bool as_schema, printTableContent *const cont,
    > +   bool is_tbl_desc)
    >
    > 3a.
    > Since you are changing this anyway, I think it would be better to keep
    > those boolean params together (at the end).
    >
    > ~
    >
    > 3b.
    > It seems a bit mixed up calling this addFooterToPublicationOrTableDesc
    > but having the variable 'is_tbl_desc', because it seems more natural
    > to me to read left to right, so the logical order of everything here
    > should be pub desc then table desc. In other words, use boolean
    > 'is_pub_desc' instead of 'is_tbl_desc'. Also, I think that 'as_schema'
    > thing is kind of a *subset* of the publication description, so it
    > makes more sense for that to come last too.
    >
    > e.g.
    > CURRENT
    > addFooterToPublicationOrTableDesc(buf, footermsg, as_schema, cont, is_tbl_desc)
    > SUGGESTION
    > addFooterToPublicationOrTableDesc(buf, cont, footermsg, is_pub_desc, as_schema)
    >
    > ~
    >
    > 3c
    > While you are changing things, maybe also consider changing that
    > 'as_schema' name because I did not understand what "as" means. Perhaps
    > rename like 'pub_schemas', or 'only_show_schemas' or something better
    > (???).
    >
    I have used pub_schemas.
    > ~~~
    >
    > 4.
    > + PGresult   *res;
    > + int count = 0;
    > + int i = 0;
    > + int col = is_tbl_desc ? 0 : 1;
    > +
    > + res = PSQLexec(buf->data);
    > + if (!res)
    > + return false;
    > + else
    > + count = PQntuples(res);
    > +
    >
    > 4a.
    > Assignment count = 0 is redundant.
    >
    > ~
    >
    > 4b.
    > Remove the 'i' declaration here. Declare it in the "for" loop later.
    >
    > ~
    >
    > 4c.
    > The "else" is not required. If 'res' was not good, you already returned.
    >
    > ~~~
    >
    > 5.
    > + if (as_schema)
    > + printfPQExpBuffer(buf, "    \"%s\"", PQgetvalue(res, i, 0));
    > + else
    > + {
    > + if (is_tbl_desc)
    > + printfPQExpBuffer(buf, "    \"%s\"", PQgetvalue(res, i, col));
    > + else
    > + printfPQExpBuffer(buf, "    \"%s.%s\"", PQgetvalue(res, i, 0),
    > +   PQgetvalue(res, i, col));
    >
    > This function is basically either (a) a footer for a table description
    > or (b) a footer for a publication description. And that all hinges on
    > the boolean 'is_tbl_desc'. Therefore, it seems more natural for the
    > main condition to be "if (is_tbl_desc)" here.
    >
    > This turned everything inside out. PSA: a top-up patch to show a way
    > to do this. Perhaps my implementation is a bit verbose, but OTOH it
    > seems easier to understand. Anyway, see what you think...
    >
    I have also used the patch with minor changes.
    
    > ~~~
    >
    > 6.
    > + /*---------------------------------------------------
    > + * Publication/ table description columns:
    > + * [0]: schema name (nspname)
    > + * [col]: table name (relname) / publication name (pubname)
    > + * [col + 1]: row filter expression (prqual), may be NULL
    > + * [col + 2]: column list (comma-separated), may be NULL
    > + * [col + 3]: except flag ("t" if EXCEPT, else "f")
    > + *---------------------------------------------------
    >
    > I've modified this comment slightly so I could understand it better.
    > See if you agree.
    >
    > SUGGESTION
    > /*---------------------------------------------------
    >  * Description columns:
    >  * PUB      TBL
    >  * [0]      -      : schema name (nspname)
    >  * [col]    -      : table name (relname)
    >  * -        [col]  : publication name (pubname)
    >  * [col+1]  [col+1]: row filter expression (prqual), may be NULL
    >  * [col+2]  [col+1]: column list (comma-separated), may be NULL
    >  * [col+3]  [col+1]: except flag ("t" if EXCEPT, else "f")
    >  *---------------------------------------------------
    >  */
    >
    > ~~~
    >
    I have used the suggested description with some modifications.
    
    > describeOneTableDetails:
    >
    > 7.
    > + else if (pset.sversion >= 150000)
    > + {
    > + printfPQExpBuffer(&buf,
    > +   "SELECT pubname\n"
    > +   "     , NULL\n"
    > +   "     , NULL\n"
    > +   "FROM pg_catalog.pg_publication p\n"
    > +   "     JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n"
    > +   "     JOIN pg_catalog.pg_class pc ON pc.relnamespace = pn.pnnspid\n"
    > +   "WHERE pc.oid ='%s' and pg_catalog.pg_relation_is_publishable('%s')\n"
    > +   "UNION\n"
    > +   "SELECT pubname\n"
    > +   "     , pg_get_expr(pr.prqual, c.oid)\n"
    > +   "     , (CASE WHEN pr.prattrs IS NOT NULL THEN\n"
    > +   "         (SELECT string_agg(attname, ', ')\n"
    > +   "           FROM pg_catalog.generate_series(0,
    > pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
    > +   "                pg_catalog.pg_attribute\n"
    > +   "          WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
    > +   "        ELSE NULL END) "
    > +   "FROM pg_catalog.pg_publication p\n"
    > +   "     JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n"
    > +   "     JOIN pg_catalog.pg_class c ON c.oid = pr.prrelid\n"
    > +   "WHERE pr.prrelid = '%s'\n"
    > +   "UNION\n"
    > +   "SELECT pubname\n"
    > +   "     , NULL\n"
    > +   "     , NULL\n"
    > +   "FROM pg_catalog.pg_publication p\n"
    > +   "WHERE p.puballtables AND pg_catalog.pg_relation_is_publishable('%s')\n"
    > +   "ORDER BY 1;",
    > +   oid, oid, oid, oid);
    >
    > AFAICT, that >= 150000 code seems to have added another UNION at the
    > end that was not previously there. What's that about? How is that
    > related to EXCEPT (column-list)?
    >
    This patch does not add any new code to  >= 150000. It is the same as
    HEAD. This diff appears because of changes in 0002 patchset. In patch
    0002, I did not create a separate full query for >= 190000 due to
    small changes.
    
    I have addressed the rest of the comments and added the changes in the
    latest v21 patchset.
    
    
    [1]: https://www.postgresql.org/message-id/CANhcyEXHiCbk2q8%3Dbq3boQDyc8ac9fjgK-kkp5PdTYLcAOq80Q%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  116. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-08-20T09:02:38Z

    Hi Kirill,
    
    Thanks for reviewing the patch.
    
    On Fri, 15 Aug 2025 at 11:46, Kirill Reshke <reshkekirill@gmail.com> wrote:
    >
    > Hi
    >
    > On Fri, 15 Aug 2025 at 05:53, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > > 1.
    > >   bool isnull = true;
    > > - Datum whereClauseDatum;
    > > - Datum columnListDatum;
    > > + Datum datum;
    > >
    > > I know you did not write the code, but that "isnull = true" is
    > > redundant, and seems kind of misleading because it will always be
    > > re-assigned before it is used.
    >
    > People are not generally excited about refactoring code they did not
    > change. This makes patch to have more review cycles, and less probable
    > to actually being committed. If we are really wedded with this change,
    > this could be a separate thread.
    >
    I also feel that we should create a new thread for the same. I have
    created a new thread. See [1].
    
    >
    > > ~~~
    > >
    > > 2.
    > >   /* Load the WHERE clause for this table. */
    > > - whereClauseDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > > -    Anum_pg_publication_rel_prqual,
    > > -    &isnull);
    > > + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > > + Anum_pg_publication_rel_prqual,
    > > + &isnull);
    > >   if (!isnull)
    > > - oldrelwhereclause = stringToNode(TextDatumGetCString(whereClauseDatum));
    > > + oldrelwhereclause = stringToNode(TextDatumGetCString(datum));
    > >
    > >   /* Transform the int2vector column list to a bitmap. */
    > > - columnListDatum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > > -   Anum_pg_publication_rel_prattrs,
    > > -   &isnull);
    > > + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > > + Anum_pg_publication_rel_prattrs,
    > > + &isnull);
    > > +
    > > + if (!isnull)
    > > + oldcolumns = pub_collist_to_bitmapset(NULL, datum, NULL);
    > > +
    > > + /* Load the prexcept flag for this table. */
    > > + datum = SysCacheGetAttr(PUBLICATIONRELMAP, rftuple,
    > > + Anum_pg_publication_rel_prexcept,
    > > + &isnull);
    > >
    > >   if (!isnull)
    > > - oldcolumns = pub_collist_to_bitmapset(NULL, columnListDatum, NULL);
    > > + oldexcept = DatumGetBool(datum);
    > >
    > > Use consistent spacing. Either do or don't (I prefer don't) put a
    > > blank line between the pairs of "datum =" and "if (!isnull)". Avoid
    > > having a mixture.
    > >
    > > ======
    > > src/bin/psql/describe.c
    > >
    > > addFooterToPublicationOrTableDesc:
    > >
    > > 3.
    > > +/*
    > > + * If is_tbl_desc is true add footer to table description else add footer to
    > > + * publication description.
    > > + */
    > > +static bool
    > > +addFooterToPublicationOrTableDesc(PQExpBuffer buf, const char *footermsg,
    > > +   bool as_schema, printTableContent *const cont,
    > > +   bool is_tbl_desc)
    > >
    > > 3a.
    > > Since you are changing this anyway, I think it would be better to keep
    > > those boolean params together (at the end).
    > >
    > > ~
    > >
    > > 3b.
    > > It seems a bit mixed up calling this addFooterToPublicationOrTableDesc
    > > but having the variable 'is_tbl_desc', because it seems more natural
    > > to me to read left to right, so the logical order of everything here
    > > should be pub desc then table desc. In other words, use boolean
    > > 'is_pub_desc' instead of 'is_tbl_desc'. Also, I think that 'as_schema'
    > > thing is kind of a *subset* of the publication description, so it
    > > makes more sense for that to come last too.
    > >
    > > e.g.
    > > CURRENT
    > > addFooterToPublicationOrTableDesc(buf, footermsg, as_schema, cont, is_tbl_desc)
    > > SUGGESTION
    > > addFooterToPublicationOrTableDesc(buf, cont, footermsg, is_pub_desc, as_schema)
    > >
    > > ~
    > >
    > > 3c
    > > While you are changing things, maybe also consider changing that
    > > 'as_schema' name because I did not understand what "as" means. Perhaps
    > > rename like 'pub_schemas', or 'only_show_schemas' or something better
    > > (???).
    > >
    > > ~~~
    > >
    > > 4.
    > > + PGresult   *res;
    > > + int count = 0;
    > > + int i = 0;
    > > + int col = is_tbl_desc ? 0 : 1;
    > > +
    > > + res = PSQLexec(buf->data);
    > > + if (!res)
    > > + return false;
    > > + else
    > > + count = PQntuples(res);
    > > +
    > >
    > > 4a.
    > > Assignment count = 0 is redundant.
    > >
    > > ~
    > >
    > > 4b.
    > > Remove the 'i' declaration here. Declare it in the "for" loop later.
    > >
    > > ~
    > >
    > > 4c.
    > > The "else" is not required. If 'res' was not good, you already returned.
    > >
    > > ~~~
    > >
    > > 5.
    > > + if (as_schema)
    > > + printfPQExpBuffer(buf, "    \"%s\"", PQgetvalue(res, i, 0));
    > > + else
    > > + {
    > > + if (is_tbl_desc)
    > > + printfPQExpBuffer(buf, "    \"%s\"", PQgetvalue(res, i, col));
    > > + else
    > > + printfPQExpBuffer(buf, "    \"%s.%s\"", PQgetvalue(res, i, 0),
    > > +   PQgetvalue(res, i, col));
    > >
    > > This function is basically either (a) a footer for a table description
    > > or (b) a footer for a publication description. And that all hinges on
    > > the boolean 'is_tbl_desc'. Therefore, it seems more natural for the
    > > main condition to be "if (is_tbl_desc)" here.
    > >
    > > This turned everything inside out. PSA: a top-up patch to show a way
    > > to do this. Perhaps my implementation is a bit verbose, but OTOH it
    > > seems easier to understand. Anyway, see what you think...
    > >
    >
    > + 1
    >
    Included these changes in the latest patch [2].
    
    > >
    > > 6.
    > > + /*---------------------------------------------------
    > > + * Publication/ table description columns:
    > > + * [0]: schema name (nspname)
    > > + * [col]: table name (relname) / publication name (pubname)
    > > + * [col + 1]: row filter expression (prqual), may be NULL
    > > + * [col + 2]: column list (comma-separated), may be NULL
    > > + * [col + 3]: except flag ("t" if EXCEPT, else "f")
    > > + *---------------------------------------------------
    > >
    > > I've modified this comment slightly so I could understand it better.
    > > See if you agree.
    >
    > For me that's equal. lets see what other people think
    >
    For now I have used the version shared by Peter. I felt it was more descriptive.
    
    [1] : https://www.postgresql.org/message-id/CANhcyEXHiCbk2q8%3Dbq3boQDyc8ac9fjgK-kkp5PdTYLcAOq80Q%40mail.gmail.com
    [2] : https://www.postgresql.org/message-id/CANhcyEUEMWSkTfGc7Q3B%2BUiOzSiOmOGLgK-%2BC5DXwtCGOnDBhg%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  117. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-08-21T00:03:31Z

    Hi Shlok,
    
    I reviewed your latest v20-0003 patch and have no more comments at
    this time; I only found one trivial typo.
    
    ======
    src/bin/psql/describe.c
    
    1.
    + /*
    + * Footers entries for a publication description or a table
    + * description
    + */
    
    Typo. /Footers entries/Footer entries/
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  118. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-08-25T08:08:24Z

    On Thu, 21 Aug 2025 at 05:33, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok,
    >
    > I reviewed your latest v20-0003 patch and have no more comments at
    > this time; I only found one trivial typo.
    >
    > ======
    > src/bin/psql/describe.c
    >
    > 1.
    > + /*
    > + * Footers entries for a publication description or a table
    > + * description
    > + */
    >
    > Typo. /Footers entries/Footer entries/
    >
    
    I have fixed it and attached the updated patches
    
    Thanks,
    Shlok Kyal
    
  119. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-09-05T06:27:23Z

    On Mon, 25 Aug 2025 at 13:38, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Thu, 21 Aug 2025 at 05:33, Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Hi Shlok,
    > >
    > > I reviewed your latest v20-0003 patch and have no more comments at
    > > this time; I only found one trivial typo.
    > >
    > > ======
    > > src/bin/psql/describe.c
    > >
    > > 1.
    > > + /*
    > > + * Footers entries for a publication description or a table
    > > + * description
    > > + */
    > >
    > > Typo. /Footers entries/Footer entries/
    > >
    >
    > I have fixed it and attached the updated patches
    >
    The patches were not applying on HEAD and needed a Rebase. Here is the
    rebased patches
    
    Thanks,
    Shlok Kyal
    
  120. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2025-09-25T08:48:04Z

    On Fri, 5 Sept 2025 at 11:57, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Mon, 25 Aug 2025 at 13:38, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Thu, 21 Aug 2025 at 05:33, Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > > > Hi Shlok,
    > > >
    > > > I reviewed your latest v20-0003 patch and have no more comments at
    > > > this time; I only found one trivial typo.
    > > >
    > > > ======
    > > > src/bin/psql/describe.c
    > > >
    > > > 1.
    > > > + /*
    > > > + * Footers entries for a publication description or a table
    > > > + * description
    > > > + */
    > > >
    > > > Typo. /Footers entries/Footer entries/
    > > >
    > >
    > > I have fixed it and attached the updated patches
    > >
    > The patches were not applying on HEAD and needed a Rebase. Here is the
    > rebased patches
    
    Consider the following scenario:
    create table t1(c1 int, c2 int);
    create publication pub1 for table t1 except (c1, c2);
    
    In this case, the publication is created in such a way that no columns
    are included, so effectively no data will be replicated to the
    subscriber.
    However, when attempting an UPDATE, the following error occurs:
    postgres=# update t1 set c1 = 2;
    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.
    
    Is this behavior expected?
    
    Regards,
    Vignesh
    
    
    
    
  121. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2025-09-25T11:08:54Z

    On Fri, 5 Sept 2025 at 11:57, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Mon, 25 Aug 2025 at 13:38, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Thu, 21 Aug 2025 at 05:33, Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > > > Hi Shlok,
    > > >
    > > > I reviewed your latest v20-0003 patch and have no more comments at
    > > > this time; I only found one trivial typo.
    > > >
    > > > ======
    > > > src/bin/psql/describe.c
    > > >
    > > > 1.
    > > > + /*
    > > > + * Footers entries for a publication description or a table
    > > > + * description
    > > > + */
    > > >
    > > > Typo. /Footers entries/Footer entries/
    > > >
    > >
    > > I have fixed it and attached the updated patches
    > >
    > The patches were not applying on HEAD and needed a Rebase. Here is the
    > rebased patches
    
    Few comments:
    1) Currently from pg_publication_tables it is not clear if it is
    replicating column list or replicating exclude column, can we indicate
    if it is exclude or not:
    create publication pub1 for table t1(c1);
    create publication pub2 for  table t1 except ( c1);
    
    postgres=# select * from pg_publication_tables;
     pubname | schemaname | tablename | attnames | rowfilter
    ---------+------------+-----------+----------+-----------
     pub1    | public     | t1        | {c1}     |
     pub2    | public     | t1        | {c2}     |
    (2 rows)
    
    2) Tab completion is not correct in this case:
    postgres=# alter publication pub3 add table t2 EXCEPT (
    ,        WHERE (
    
    3) tab6 is not used anywhere, it can be removed:
    +       CREATE TABLE tab5 (a int, b int, c int);
    +       CREATE TABLE tab6 (agen int GENERATED ALWAYS AS (1) STORED,
    bgen int GENERATED ALWAYS AS (2) STORED);
    +       INSERT INTO tab1 VALUES (1, 2, 3);
    
    4) both these tests are using same message:
    +  $node_subscriber->safe_psql('postgres', "SELECT * FROM tab1 ORDER BY a");
    +is( $result, qq(|2|3
    +|5|6),
    +       'check incremental insert for EXCEPT (column-list) publication');
    +$result = $node_subscriber->safe_psql('postgres',
    +       "SELECT * FROM sch1.tab1 ORDER BY a");
    +is( $result, qq(1||
    +4||), 'check incremental insert for EXCEPT (column-list) publication');
    
    we can include table name here to differentiate the test that will
    help in identifying test failure easily
    
    5) /newly added column are is replicated/ should be "newly added
    column is replicated"
    is($result, qq(|||10), 'newly added column are is replicated');
    
    Regards,
    Vignesh
    
    
    
    
  122. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-09-26T18:53:58Z

    On Thu, 25 Sept 2025 at 14:18, vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Fri, 5 Sept 2025 at 11:57, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Mon, 25 Aug 2025 at 13:38, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > On Thu, 21 Aug 2025 at 05:33, Peter Smith <smithpb2250@gmail.com> wrote:
    > > > >
    > > > > Hi Shlok,
    > > > >
    > > > > I reviewed your latest v20-0003 patch and have no more comments at
    > > > > this time; I only found one trivial typo.
    > > > >
    > > > > ======
    > > > > src/bin/psql/describe.c
    > > > >
    > > > > 1.
    > > > > + /*
    > > > > + * Footers entries for a publication description or a table
    > > > > + * description
    > > > > + */
    > > > >
    > > > > Typo. /Footers entries/Footer entries/
    > > > >
    > > >
    > > > I have fixed it and attached the updated patches
    > > >
    > > The patches were not applying on HEAD and needed a Rebase. Here is the
    > > rebased patches
    >
    > Consider the following scenario:
    > create table t1(c1 int, c2 int);
    > create publication pub1 for table t1 except (c1, c2);
    >
    > In this case, the publication is created in such a way that no columns
    > are included, so effectively no data will be replicated to the
    > subscriber.
    > However, when attempting an UPDATE, the following error occurs:
    > postgres=# update t1 set c1 = 2;
    > 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.
    >
    > Is this behavior expected?
    
    Hi Vignesh,
    
    I think this behaviour is same as other similar cases like:
    
    1. publication on empty table:
    CREATE TABLE t1();
    CREATE PUBLICATION pub1 FOR TABLE t1;
    
    postgres=# DELETE FROM t1;
    ERROR:  cannot delete from table "t1" because it does not have a
    replica identity and publishes deletes
    HINT:  To enable deleting from the table, set REPLICA IDENTITY using
    ALTER TABLE.
    
    2. All the columns in a table is a generated column:
     CREATE TABLE t2(a int GENERATED ALWAYS AS (2*2) STORED);
    CREATE PUBLICATION pub2 FOR TABLE t2 WITH (publish_generated_columns='none');
    
    In this case since "publish_generated_columns=none", should not
    publish changes for table t2. But we get following:
    postgres=# DELETE FROM t2;
    ERROR:  cannot delete from table "t2" because it does not have a
    replica identity and publishes deletes
    HINT:  To enable deleting from the table, set REPLICA IDENTITY using
    ALTER TABLE.
    
    In above cases as well no columns are published but we have the similar error.
    Given these behaviours in HEAD I think it is okay for EXCEPT
    column_list to have the similar behaviour when all columns are
    excluded. Thought?
    
    Thanks,
    Shlok Kyal
    
    
    
    
  123. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-09-26T19:50:31Z

    On Thu, 25 Sept 2025 at 16:39, vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Fri, 5 Sept 2025 at 11:57, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Mon, 25 Aug 2025 at 13:38, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > On Thu, 21 Aug 2025 at 05:33, Peter Smith <smithpb2250@gmail.com> wrote:
    > > > >
    > > > > Hi Shlok,
    > > > >
    > > > > I reviewed your latest v20-0003 patch and have no more comments at
    > > > > this time; I only found one trivial typo.
    > > > >
    > > > > ======
    > > > > src/bin/psql/describe.c
    > > > >
    > > > > 1.
    > > > > + /*
    > > > > + * Footers entries for a publication description or a table
    > > > > + * description
    > > > > + */
    > > > >
    > > > > Typo. /Footers entries/Footer entries/
    > > > >
    > > >
    > > > I have fixed it and attached the updated patches
    > > >
    > > The patches were not applying on HEAD and needed a Rebase. Here is the
    > > rebased patches
    >
    > Few comments:
    > 1) Currently from pg_publication_tables it is not clear if it is
    > replicating column list or replicating exclude column, can we indicate
    > if it is exclude or not:
    > create publication pub1 for table t1(c1);
    > create publication pub2 for  table t1 except ( c1);
    >
    > postgres=# select * from pg_publication_tables;
    >  pubname | schemaname | tablename | attnames | rowfilter
    > ---------+------------+-----------+----------+-----------
    >  pub1    | public     | t1        | {c1}     |
    >  pub2    | public     | t1        | {c2}     |
    > (2 rows)
    >
    > 2) Tab completion is not correct in this case:
    > postgres=# alter publication pub3 add table t2 EXCEPT (
    > ,        WHERE (
    >
    > 3) tab6 is not used anywhere, it can be removed:
    > +       CREATE TABLE tab5 (a int, b int, c int);
    > +       CREATE TABLE tab6 (agen int GENERATED ALWAYS AS (1) STORED,
    > bgen int GENERATED ALWAYS AS (2) STORED);
    > +       INSERT INTO tab1 VALUES (1, 2, 3);
    >
    > 4) both these tests are using same message:
    > +  $node_subscriber->safe_psql('postgres', "SELECT * FROM tab1 ORDER BY a");
    > +is( $result, qq(|2|3
    > +|5|6),
    > +       'check incremental insert for EXCEPT (column-list) publication');
    > +$result = $node_subscriber->safe_psql('postgres',
    > +       "SELECT * FROM sch1.tab1 ORDER BY a");
    > +is( $result, qq(1||
    > +4||), 'check incremental insert for EXCEPT (column-list) publication');
    >
    > we can include table name here to differentiate the test that will
    > help in identifying test failure easily
    >
    > 5) /newly added column are is replicated/ should be "newly added
    > column is replicated"
    > is($result, qq(|||10), 'newly added column are is replicated');
    Hi Vignesh,
    
    Thanks for reviewing the patch.
    I have addressed the comments and attached the updated version.
    
    Thanks,
    Shlok Kyal
    
  124. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-09-29T03:28:12Z

    Hi Shlok,
    
    I was looking at the recent v24 changes.
    
    ======
    GENERAL.
    
    I saw that you modified the system view to add a new flag:
    
    +     <row>
    +      <entry role="catalog_table_entry"><para role="column_definition">
    +       <structfield>exceptcol</structfield> <type>bool</type>
    +      </para>
    +      <para>
    +       True if a column list with <literal>EXCEPT</literal> clause is specified
    +       for the table in the publication.
    +      </para></entry>
    +     </row>
    
    So output now might look like this:
    
    +CREATE TABLE pub_test_except1 (a int NOT NULL, b int, c int NOT NULL, d int);
    +CREATE PUBLICATION testpub_except FOR TABLE pub_test_except1,
    pub_sch1.pub_test_except2 EXCEPT (b, c);
    +SELECT * FROM pg_publication_tables WHERE pubname = 'testpub_except';
    +    pubname     | schemaname |    tablename     | attnames  |
    rowfilter | exceptcol
    +----------------+------------+------------------+-----------+-----------+-----------
    + testpub_except | public     | pub_test_except1 | {a,b,c,d} |           | f
    + testpub_except | pub_sch1   | pub_test_except2 | {a,d}     |           | t
    +(2 rows)
    
    ~~~
    
    I think this was done in response to a comment from Vignesh [1], but
    it did not get implemented in the way that I had imagined. e.g. I
    imagined the view might be more like this:
    
    +    pubname     | schemaname |    tablename     | attnames  |
    rowfilter | exceptcols
    +----------------+------------+------------------+-----------+-----------+-----------
    + testpub_except | public     | pub_test_except1 | {a,b,c,d} |           |
    + testpub_except | pub_sch1   | pub_test_except2 | {a,d}     |           | {b,c}
    
    I don't know if broadcasting to the user what the unpublished/hidden
    columns' names are is very wise (e.g. "{password,internal_notes,
    salary}", but OTOH just having a boolean flag saying that "something"
    was excluded ddin't seem useful.
    
    ~
    
    Furthermore, having a Boolean seemed strangely incompatible with a
    normal column list. e.g. Lets say there is a table T1 with cols
    c1,c2,c3,c4.
    
    I could publish that as "FOR TABLE T1(c1,c2,c3)"
    Or as "FOR TABLE T1 EXCEPT (c4)"
    
    In the v24 implementation, AFAIK, the view will show those as
    "attnames = {c1,c2,c3}", and except will be both "f" and "t". It
    seemed odd to.
    
    ~
    
    Lastly,  I think the EXCEPT (col-list) feature was mostly added just
    to help users with 100s of columns to write their CREATE PUBLICATION
    statement more easily.  Since the view already shows all the columns
    that will be published. So, I'm kind of -0.5 on this idea of changing
    the view to show how they typed their statement.
    
    ======
    [1] https://www.postgresql.org/message-id/CALDaNm32XQDR4qsOhPQeophVbZ8r%2BShJSSssoVfdPcwG6joPHQ%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  125. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2025-09-29T15:30:14Z

    On Sat, 27 Sept 2025 at 01:20, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > Thanks for reviewing the patch.
    > I have addressed the comments and attached the updated version.
    
    If all columns are excluded, we do not publish the changes. However,
    when a table has no columns, the data is still replicated. Should we
    make this behavior consistent?
    @@ -1482,6 +1525,13 @@ pgoutput_change(LogicalDecodingContext *ctx,
    ReorderBufferTXN *txn,
            relentry = get_rel_sync_entry(data, relation);
    +       /*
    +        * If all columns of a table are present in column list specified with
    +        * EXCEPT, skip publishing the changes.
    +        */
    +       if (relentry->all_cols_excluded)
    +               return;
    
    Steps to check the above issue:
    -- pub
    create table t1();
    create table t2(c1 int, c2 int);
    create publication pub1 FOR table t1;
    create publication pub2 FOR table t2 except(c1, c2);
    
    --sub
    create table t1(c1 int);
    create table t2(c1 int, c2 int);
    create subscription sub1 connection 'dbname=postgres host=localhost
    port=5432' publication pub1,pub2;
    
    --pub
    postgres=# insert into t1 default values ;
    INSERT 0 1
    postgres=# insert into t2 default values;
    INSERT 0 1
    
    --sub
    -- In case of table having no columns, data is replicated
    postgres=# select * from t1;
     c1
    ----
    
    (1 row)
    
    -- In case of table having all columns excluded, data is not replicated
    postgres=# select * from t2;
     c1 | c2
    ----+----
    (0 rows)
    
    Thoughts?
    
    Regards,
    Vignesh
    
    
    
    
  126. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2025-09-30T04:57:48Z

    On Mon, 29 Sept 2025 at 08:58, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok,
    >
    > I was looking at the recent v24 changes.
    >
    > ======
    > GENERAL.
    >
    > I saw that you modified the system view to add a new flag:
    >
    > +     <row>
    > +      <entry role="catalog_table_entry"><para role="column_definition">
    > +       <structfield>exceptcol</structfield> <type>bool</type>
    > +      </para>
    > +      <para>
    > +       True if a column list with <literal>EXCEPT</literal> clause is specified
    > +       for the table in the publication.
    > +      </para></entry>
    > +     </row>
    >
    > So output now might look like this:
    >
    > +CREATE TABLE pub_test_except1 (a int NOT NULL, b int, c int NOT NULL, d int);
    > +CREATE PUBLICATION testpub_except FOR TABLE pub_test_except1,
    > pub_sch1.pub_test_except2 EXCEPT (b, c);
    > +SELECT * FROM pg_publication_tables WHERE pubname = 'testpub_except';
    > +    pubname     | schemaname |    tablename     | attnames  |
    > rowfilter | exceptcol
    > +----------------+------------+------------------+-----------+-----------+-----------
    > + testpub_except | public     | pub_test_except1 | {a,b,c,d} |           | f
    > + testpub_except | pub_sch1   | pub_test_except2 | {a,d}     |           | t
    > +(2 rows)
    >
    > ~~~
    >
    > I think this was done in response to a comment from Vignesh [1], but
    > it did not get implemented in the way that I had imagined. e.g. I
    > imagined the view might be more like this:
    >
    > +    pubname     | schemaname |    tablename     | attnames  |
    > rowfilter | exceptcols
    > +----------------+------------+------------------+-----------+-----------+-----------
    > + testpub_except | public     | pub_test_except1 | {a,b,c,d} |           |
    > + testpub_except | pub_sch1   | pub_test_except2 | {a,d}     |           | {b,c}
    >
    > I don't know if broadcasting to the user what the unpublished/hidden
    > columns' names are is very wise (e.g. "{password,internal_notes,
    > salary}", but OTOH just having a boolean flag saying that "something"
    > was excluded ddin't seem useful.
    >
    > ~
    >
    > Furthermore, having a Boolean seemed strangely incompatible with a
    > normal column list. e.g. Lets say there is a table T1 with cols
    > c1,c2,c3,c4.
    >
    > I could publish that as "FOR TABLE T1(c1,c2,c3)"
    > Or as "FOR TABLE T1 EXCEPT (c4)"
    >
    > In the v24 implementation, AFAIK, the view will show those as
    > "attnames = {c1,c2,c3}", and except will be both "f" and "t". It
    > seemed odd to.
    >
    > ~
    >
    > Lastly,  I think the EXCEPT (col-list) feature was mostly added just
    > to help users with 100s of columns to write their CREATE PUBLICATION
    > statement more easily.  Since the view already shows all the columns
    > that will be published. So, I'm kind of -0.5 on this idea of changing
    > the view to show how they typed their statement.
    
    On further consideration, I’m ok with removing this column to avoid
    potential confusion.
    
    Regards,
    Vignesh
    
    
    
    
  127. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-10-24T01:11:20Z

    Hi Vignesh,
    
    I had a look at patch v24-0001.
    
    FYI -- a rebase is needed
    
    [postgres@CentOS7-x64 oss_postgres_misc]$ git apply
    ../patches_misc/v24-0001-Add-RESET-clause-to-Alter-Publication-which-will.patch
    error: patch failed: doc/src/sgml/ref/alter_publication.sgml:69
    error: doc/src/sgml/ref/alter_publication.sgml: patch does not apply
    
    Here are some other review comments:
    
    ======
    
    1.
    There seems to be some basic omission of the ALTER PUBLICATION in that
    it does not support "ALL TABLES" as a publication_object.
    
    Therefore, if you have:
    CREATE PUBLICATION mypub FOR ALL TABLES;
    
    and then you do:
    ALTER PUBLICATION mypub RESET;
    
    There seems to be no way to restore mpub to be an ALL TABLES publication again!
    
    ~~~
    
    I think if you are going to implement a RESET, then you also need a
    way to get back to what you had before you did the reset. So you'll
    also need to implement the ALTER PUBLICATION mypub SET ALL TABLES;
    
    IMO, supporting  "SET ALL TABLES" should be your new 0001 patch
    because AFAIK, this situation already exists if the user had created
    an "empty" publication:
    CREATE PUBLICATION myemptypub;
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    2.
    Probably need to mention ALL SEQUENCES now too?
    
    ======
    src/backend/commands/publicationcmds.c
    
    3.
    +/* CREATE PUBLICATION default values for flags and publication parameters */
    +#define PUB_DEFAULT_ACTION_INSERT true
    +#define PUB_DEFAULT_ACTION_UPDATE true
    +#define PUB_DEFAULT_ACTION_DELETE true
    +#define PUB_DEFAULT_ACTION_TRUNCATE true
    +#define PUB_DEFAULT_VIA_ROOT false
    +#define PUB_DEFAULT_ALL_TABLES false
    +#define PUB_DEFAULT_GENCOLS PUBLISH_GENCOLS_NONE
    +
    
    Is it better to put all these in the catalog/pg_publication.h where
    the catalog was defined?
    
    ~~~
    
    AlterPublicationReset:
    
    4.
    + /* Set ALL TABLES flag to false */
    + if (pubform->puballtables)
    + {
    + values[Anum_pg_publication_puballtables - 1] =
    BoolGetDatum(PUB_DEFAULT_ALL_TABLES);
    + replaces[Anum_pg_publication_puballtables - 1] = true;
    + CacheInvalidateRelcacheAll();
    + }
    
    Why not just do this anyway without the condition?
    
    ======
    src/backend/parser/gram.y
    
    6.
    It would be nicer if all these grammar productions were coded in the
    same order as the comment above them.
    
    ======
    src/include/nodes/parsenodes.h
    
    7.
      AP_AddObjects, /* add objects to publication */
      AP_DropObjects, /* remove objects from publication */
      AP_SetObjects, /* set list of objects */
    + AP_ResetPublication, /* reset the publication */
     } AlterPublicationAction;
    
    It is already called "AlterPublicationAction", so maybe the enum value
    only needs to be AP_Reset instead of AP_ResetPublication.
    
    
    ======
    src/test/regress/expected/publication.out
    
    8.
    Expected output all needs rebasing now that there is a new "All
    sequences" column.
    
    ======
    src/test/regress/sql/publication.sql
    
    9.
    +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1;
    +
    +-- Verify that associated tables are removed from the publication after RESET
    +\dRp+ testpub_reset
    +ALTER PUBLICATION testpub_reset RESET;
    +\dRp+ testpub_reset
    +
    
    I felt the ADD TABLE should be after the comment.
    
    And ditto for all the other test cases -- should be that same pattern too.
    
    # comment about test
    ALTER .. do something
    \dRp+ pub
    ALTER .. RESET
    \dRp+ pub
    
    ~~~
    
    10.
    +-- Verify that associated schemas are reomved from the publication after RESET
    
    typo: /reomved/removed/
    
    ~~~
    
    11.
    +-- Verify that only superuser can reset a publication
    +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2;
    +SET ROLE regress_publication_user2;
    +ALTER PUBLICATION testpub_reset RESET; -- fail - must be superuser
    +SET ROLE regress_publication_user;
    +
    
    Perhaps this should be the first test?
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  128. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-10-30T06:04:05Z

    Hi Vignesh
    
    Here are some review comments for the patch v24-0002.
    
    These comments are just for the SGML docs. The patch needs a rebase so
    I was unable to review the code.
    
    ======
    Commit message
    
    1.
    A new column "prexcept" is added to table "pg_publication_rel", to maintain
    the relations that the user wants to exclude from the publications.
    
    ~
    
    /to maintain/to flag/
    
    ======
    doc/src/sgml/logical-replication.sgml
    
    2.
       <para>
    -   To add tables to a publication, the user must have ownership rights on the
    -   table. To add all tables in schema to a publication, the user must be a
    -   superuser. To create a publication that publishes all tables or
    all tables in
    -   schema automatically, the user must be a superuser.
    +   To create a publication using FOR ALL TABLES or FOR ALL TABLES IN SCHEMA,
    +   the user must be a superuser. To add ALL TABLES or ALL TABLES IN SCHEMA to a
    +   publication, the user must be a superuser. To add tables to a publication,
    +   the user must have ownership rights on the table.
       </para>
    
    Those "FOR ALL TABLES" etc are missing SGML markup.
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    3.
    +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    ADD ALL TABLES [ EXCEPT [ TABLE ] <replaceable
    class="parameter">exception_object</replaceable> [, ... ] ]
    
    and
    
    +
    +<phrase>where <replaceable
    class="parameter">exception_object</replaceable> is:</phrase>
    +
    +    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    +
    
    It is not clear from the syntax which of these is possible.
    
    ... ADD ALL TABLES EXCEPT TABLE t1,t2,t3
    ... ADD ALL TABLES EXCEPT TABLE t1, TABLE t2, TABLES t3
    
    IMO it is best put the "[TABLE]" within the exception_object:
    [ TABLE ] [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    
    Then both are possible, which is consistent with how "FOR TABLE" syntax works.
    
    Furthermore, you might want later to say EXCLUDE SEQUENCE, so doing it
    this way makes that possible.
    
    ~~~
    
    4.
    -   Adding a table to a publication additionally requires owning that table.
    -   The <literal>ADD TABLES IN SCHEMA</literal>,
    +   Adding a table to or excluding a table from a publication additionally
    +   requires owning that table. The <literal>ADD ALL TABLES</literal>,
    
    This wording seems a bit awkward. How are re-phrasing like:
    
    SUGGESTION
    Adding or excluding a table from a publication requires ownership of that table.
    
    ~~~
    
    5.
    -      name to explicitly indicate that descendant tables are included.
    +      name to explicitly indicate that descendant tables are affected. For
    +      partitioned tables, <literal>ONLY</literal> donot have any effect.
    
    typo: /donot/does not/
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    6.
    -    [ FOR ALL TABLES
    +    [ FOR ALL TABLES [ EXCEPT [ TABLE ] <replaceable
    class="parameter">exception_object</replaceable> [, ... ] ]
           | FOR <replaceable
    class="parameter">publication_object</replaceable> [, ... ] ]
         [ WITH ( <replaceable
    class="parameter">publication_parameter</replaceable> [= <replaceable
    class="parameter">value</replaceable>] [, ... ] ) ]
    
    @@ -30,6 +30,10 @@ CREATE PUBLICATION <replaceable
    class="parameter">name</replaceable>
    
         TABLE [ ONLY ] <replaceable
    class="parameter">table_name</replaceable> [ * ] [ ( <replaceable
    class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE (
    <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
         TABLES IN SCHEMA { <replaceable
    class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ...
    ]
    +
    +<phrase>where <replaceable
    class="parameter">exception_object</replaceable> is:</phrase>
    +
    +    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    
    Same review comment as #3 before.
    
    I think it is clearer (and more flexible) to change the
    exception_object to include [TABLE].
    [ TABLE ] [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    
    It also helps pave the way for any future EXCLUDE SEQUENCE feature.
    
    ~~~
    
    7.
    +     <para>
    +      This clause specifies a list of tables to be excluded from the
    +      publication. It can only be used with <literal>FOR ALL TABLES</literal>.
    +      If <literal>ONLY</literal> is specified before the table name, only
    +      that table is excluded from the publication. If
    <literal>ONLY</literal> is
    +      not specified, the table and all its descendant tables (if any) are
    +      excluded. Optionally, <literal>*</literal> can be specified after the
    +      table name to explicitly indicate that descendant tables are excluded.
    +      This does not apply to a partitioned table, however.  The partitioned
    +      table or its partitions are excluded from the publication based on the
    +      parameter <literal>publish_via_partition_root</literal>.
    +     </para>
    +     <para>
    +      When <literal>publish_via_partition_root</literal> is set to
    +      <literal>true</literal>, specifying a root partitioned table in
    +      <literal>EXCEPT TABLE</literal> excludes it and all its partitions from
    +      replication. Specifying a leaf partition has no effect, as its
    changes are
    +      still replicated via the root partitioned table. When
    +      <literal>publish_via_partition_root</literal> is set to
    +      <literal>false</literal>, specifying a partitioned table or non-leaf
    +      partition has no effect, as changes are replicated via the leaf
    +      partitions. Specifying a leaf partition excludes only that partition from
    +      replication.
    +     </para>
    
    I felt that the second paragraph should be started with the sentence
    "The partitioned table or its partitions are excluded...", so then
    everything related to "publish_via_partition_root" is kept together.
    
    ~~~
    
    8.
    +  <para>
    +   Create a publication that publishes all changes in all the tables except for
    +   the changes of <structname>users</structname> and
    +   <structname>departments</structname>:
    +<programlisting>
    +CREATE PUBLICATION mypublication FOR ALL TABLES EXCEPT users, departments;
    +</programlisting>
    +  </para>
    
    The words "the changes of" are not needed, and you did not use that
    wording in the ALTER PUBLICATION example.
    
    ======
    doc/src/sgml/ref/psql-ref.sgml
    
    9.
             If <literal>x</literal> is appended to the command name, the results
             are displayed in expanded mode.
    -        If <literal>+</literal> is appended to the command name, the tables and
    -        schemas associated with each publication are shown as well.
    +        If <literal>+</literal> is appended to the command name, the tables,
    +        excluded tables and schemas associated with each publication
    are shown as
    +        well.
             </para>
    
    /excluded tables and schemas/excluded tables, and schemas/
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  129. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-11-06T11:16:48Z

    On Thu, 30 Oct 2025 at 11:34, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Vignesh
    >
    > Here are some review comments for the patch v24-0002.
    >
    > These comments are just for the SGML docs. The patch needs a rebase so
    > I was unable to review the code.
    >
    > ======
    > Commit message
    >
    > 1.
    > A new column "prexcept" is added to table "pg_publication_rel", to maintain
    > the relations that the user wants to exclude from the publications.
    >
    > ~
    >
    > /to maintain/to flag/
    >
    > ======
    > doc/src/sgml/logical-replication.sgml
    >
    > 2.
    >    <para>
    > -   To add tables to a publication, the user must have ownership rights on the
    > -   table. To add all tables in schema to a publication, the user must be a
    > -   superuser. To create a publication that publishes all tables or
    > all tables in
    > -   schema automatically, the user must be a superuser.
    > +   To create a publication using FOR ALL TABLES or FOR ALL TABLES IN SCHEMA,
    > +   the user must be a superuser. To add ALL TABLES or ALL TABLES IN SCHEMA to a
    > +   publication, the user must be a superuser. To add tables to a publication,
    > +   the user must have ownership rights on the table.
    >    </para>
    >
    > Those "FOR ALL TABLES" etc are missing SGML markup.
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 3.
    > +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > ADD ALL TABLES [ EXCEPT [ TABLE ] <replaceable
    > class="parameter">exception_object</replaceable> [, ... ] ]
    >
    > and
    >
    > +
    > +<phrase>where <replaceable
    > class="parameter">exception_object</replaceable> is:</phrase>
    > +
    > +    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    > +
    >
    > It is not clear from the syntax which of these is possible.
    >
    > ... ADD ALL TABLES EXCEPT TABLE t1,t2,t3
    > ... ADD ALL TABLES EXCEPT TABLE t1, TABLE t2, TABLES t3
    >
    > IMO it is best put the "[TABLE]" within the exception_object:
    > [ TABLE ] [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > Then both are possible, which is consistent with how "FOR TABLE" syntax works.
    >
    > Furthermore, you might want later to say EXCLUDE SEQUENCE, so doing it
    > this way makes that possible.
    >
    > ~~~
    >
    > 4.
    > -   Adding a table to a publication additionally requires owning that table.
    > -   The <literal>ADD TABLES IN SCHEMA</literal>,
    > +   Adding a table to or excluding a table from a publication additionally
    > +   requires owning that table. The <literal>ADD ALL TABLES</literal>,
    >
    > This wording seems a bit awkward. How are re-phrasing like:
    >
    > SUGGESTION
    > Adding or excluding a table from a publication requires ownership of that table.
    >
    > ~~~
    >
    > 5.
    > -      name to explicitly indicate that descendant tables are included.
    > +      name to explicitly indicate that descendant tables are affected. For
    > +      partitioned tables, <literal>ONLY</literal> donot have any effect.
    >
    > typo: /donot/does not/
    >
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 6.
    > -    [ FOR ALL TABLES
    > +    [ FOR ALL TABLES [ EXCEPT [ TABLE ] <replaceable
    > class="parameter">exception_object</replaceable> [, ... ] ]
    >        | FOR <replaceable
    > class="parameter">publication_object</replaceable> [, ... ] ]
    >      [ WITH ( <replaceable
    > class="parameter">publication_parameter</replaceable> [= <replaceable
    > class="parameter">value</replaceable>] [, ... ] ) ]
    >
    > @@ -30,6 +30,10 @@ CREATE PUBLICATION <replaceable
    > class="parameter">name</replaceable>
    >
    >      TABLE [ ONLY ] <replaceable
    > class="parameter">table_name</replaceable> [ * ] [ ( <replaceable
    > class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE (
    > <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
    >      TABLES IN SCHEMA { <replaceable
    > class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ...
    > ]
    > +
    > +<phrase>where <replaceable
    > class="parameter">exception_object</replaceable> is:</phrase>
    > +
    > +    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > Same review comment as #3 before.
    >
    > I think it is clearer (and more flexible) to change the
    > exception_object to include [TABLE].
    > [ TABLE ] [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > It also helps pave the way for any future EXCLUDE SEQUENCE feature.
    >
    > ~~~
    >
    > 7.
    > +     <para>
    > +      This clause specifies a list of tables to be excluded from the
    > +      publication. It can only be used with <literal>FOR ALL TABLES</literal>.
    > +      If <literal>ONLY</literal> is specified before the table name, only
    > +      that table is excluded from the publication. If
    > <literal>ONLY</literal> is
    > +      not specified, the table and all its descendant tables (if any) are
    > +      excluded. Optionally, <literal>*</literal> can be specified after the
    > +      table name to explicitly indicate that descendant tables are excluded.
    > +      This does not apply to a partitioned table, however.  The partitioned
    > +      table or its partitions are excluded from the publication based on the
    > +      parameter <literal>publish_via_partition_root</literal>.
    > +     </para>
    > +     <para>
    > +      When <literal>publish_via_partition_root</literal> is set to
    > +      <literal>true</literal>, specifying a root partitioned table in
    > +      <literal>EXCEPT TABLE</literal> excludes it and all its partitions from
    > +      replication. Specifying a leaf partition has no effect, as its
    > changes are
    > +      still replicated via the root partitioned table. When
    > +      <literal>publish_via_partition_root</literal> is set to
    > +      <literal>false</literal>, specifying a partitioned table or non-leaf
    > +      partition has no effect, as changes are replicated via the leaf
    > +      partitions. Specifying a leaf partition excludes only that partition from
    > +      replication.
    > +     </para>
    >
    > I felt that the second paragraph should be started with the sentence
    > "The partitioned table or its partitions are excluded...", so then
    > everything related to "publish_via_partition_root" is kept together.
    >
    > ~~~
    >
    > 8.
    > +  <para>
    > +   Create a publication that publishes all changes in all the tables except for
    > +   the changes of <structname>users</structname> and
    > +   <structname>departments</structname>:
    > +<programlisting>
    > +CREATE PUBLICATION mypublication FOR ALL TABLES EXCEPT users, departments;
    > +</programlisting>
    > +  </para>
    >
    > The words "the changes of" are not needed, and you did not use that
    > wording in the ALTER PUBLICATION example.
    >
    > ======
    > doc/src/sgml/ref/psql-ref.sgml
    >
    > 9.
    >          If <literal>x</literal> is appended to the command name, the results
    >          are displayed in expanded mode.
    > -        If <literal>+</literal> is appended to the command name, the tables and
    > -        schemas associated with each publication are shown as well.
    > +        If <literal>+</literal> is appended to the command name, the tables,
    > +        excluded tables and schemas associated with each publication
    > are shown as
    > +        well.
    >          </para>
    >
    > /excluded tables and schemas/excluded tables, and schemas/
    >
    Hi Peter, Vignesh
    
    Thanks for reviewing the patches.
    I have rebased the patches. I have modified the syntax for EXCEPT
    TABLE (002) patch.
    For example, now to exclude a table we need to specify like:
    CREATE PUBLICATION pub1 FOR ALL TABLE EXCEPT TABLE (t1, t2);
    We need to specify '()' around the table list.
    
    This patchset is the only rebased version. I will address all the
    comments in the next version of patch.
    
    Thanks,
    Shlok Kyal
    
  130. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-07T03:41:24Z

    Hi Shlok.
    
    Here are some review comments for the patch v25-0001 (RESET).
    
    I belatedly saw that you said this is a rebase *only*, so does not yet
    address any of the earlier review comments [1]. Anyway, below are a
    few more comments that I did not report previously.
    
    ======
    Commit message
    
    1.
    This patch adds a new RESET clause to ALTER PUBLICATION which will reset
    the publication to the default state which includes resetting the publication
    parameters, setting ALL TABLES flag to false and dropping the relations and
    schemas that are associated with the publication.
    
    ~
    
    1a.
    /which includes.../. This includes...
    
    ~
    
    1b.
    Needs to also mention about ALL SEQUENCES
    
    ======
    src/backend/commands/publicationcmds.c
    
    AlterPublicationReset:
    
    2.
    + /* Set ALL TABLES flag to false */
    + if (pubform->puballsequences)
    + {
    + values[Anum_pg_publication_puballsequences - 1] =
    BoolGetDatum(PUB_DEFAULT_ALL_SEQUENCES);
    + replaces[Anum_pg_publication_puballsequences - 1] = true;
    + }
    
    The comment should say ALL SEQUENCES.
    
    ======
    [1] my review of v24-0001
    https://www.postgresql.org/message-id/CAHut%2BPvoOVo%3D_O-sG8wNaLRBPSD%2B6S%3D4PXOH2r%3DyKTxbpAbHkg%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  131. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-07T04:04:29Z

    Hi Shlok.
    
    This is a general comment about the content of these patches.
    
    IIUC, the v25* patches currently are currently arranged like this:
    
    0001
     - New command ALTER PUBLICATION pubname RESET;
    0002
    - Add new command: ALTER PUBLICATION pub_name ADD ALL TABLES;
    - Enhance existing CREATE and the new ALTER syntax for EXCEPT tables
    0003
    - Enhance existing CREATE and ALTER syntax for EXCEPT col_list
    
    ~~~
    
    IMO it is a bug that the ALTER PUBLICATION pub_name ADD/SET ALL TABLES
    command does not already exist as a supported command. And, that is
    independent of anything else you are implementing here like RESET or
    EXCEPT.
    
    Therefore, I think that one should be 1st in your patchset; The EXCEPT
    stuff then just becomes enhancements to existing syntax, which would
    give a cleaner separation of logic.
    
    So, I am suggesting there should be 4 patches instead of 3. e.g.
    
    SUGGESTION
    0001 - New command: ALTER PUBLICATION pub_name ADD/SET ALL TABLES;
    0002 - New command: ALTER PUBLICATION pubname RESET;
    0003 - Enhance existing CREATE/ALTER syntax for EXCEPT tables
    0004 - Enhance existing CREATE/ALTER syntax for EXCEPT col_list
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  132. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-07T06:06:04Z

    Hi Shlok.
    
    Some questions for the patch v25-0002 (EXCEPT tables)
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    1.
    +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    ADD ALL TABLES [ EXCEPT [ TABLE ] ( <replaceable
    class="parameter">exception_object</replaceable> [, ... ] ) ]
    
    You can do both ADD/SET the <publication_object>, so really there
    should be an ADD/SET ALL TABLES command as well, right?
    
    ~~~
    
    2.
    What was your reason for changing the syntax?
    AFAICT those added "( )" are not strictly necessary, so I just
    wondered your reason.
    
    For example, we do not have any "( )" for <publication_object> [,...].
    It is: ALTER PUBLICATION name ADD publication_object [, ...]
    Not:   ALTER PUBLICATION name ADD (publication_object [, ...])
    
    So in the same way we could have EXCEPT syntax like that:
    ALTER PUBLICATION name ADD ALL TABLES [EXCEPT <table_exception_object> [, ...]]
    Where table_exception_object is: [ TABLE ] [ ONLY ] table_name [ * ]
    
    Currently, if the user just wants to exclude a single table they must do:
    ALTER PUBLICATION name ADD ALL TABLES EXCEPT (t1);
    instead of just ALTER PUBLICATION name ADD ALL TABLES EXCEPT t1;
    
    ~~~
    
    3.
    BTW, I think you may need to consider a <table_exception_object>
    instead of a generic name like <exception_object>, because in the
    future if we EXCEPT SEQUENCES the <exception_object> name may be not
    appropriate because things like [ONLY] and [*] are not applicable for
    sequences.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  133. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-11-11T10:20:36Z

    On Fri, 7 Nov 2025 at 11:36, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Some questions for the patch v25-0002 (EXCEPT tables)
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 1.
    > +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > ADD ALL TABLES [ EXCEPT [ TABLE ] ( <replaceable
    > class="parameter">exception_object</replaceable> [, ... ] ) ]
    >
    > You can do both ADD/SET the <publication_object>, so really there
    > should be an ADD/SET ALL TABLES command as well, right?
    >
    These patches only added the ADD ALL TABLES command. I think once the
    ADD ALL TABLES patch is committed, we can add the syntax SET ALL
    TABLES.
    
    > ~~~
    >
    > 2.
    > What was your reason for changing the syntax?
    > AFAICT those added "( )" are not strictly necessary, so I just
    > wondered your reason.
    >
    > For example, we do not have any "( )" for <publication_object> [,...].
    > It is: ALTER PUBLICATION name ADD publication_object [, ...]
    > Not:   ALTER PUBLICATION name ADD (publication_object [, ...])
    >
    > So in the same way we could have EXCEPT syntax like that:
    > ALTER PUBLICATION name ADD ALL TABLES [EXCEPT <table_exception_object> [, ...]]
    > Where table_exception_object is: [ TABLE ] [ ONLY ] table_name [ * ]
    >
    > Currently, if the user just wants to exclude a single table they must do:
    > ALTER PUBLICATION name ADD ALL TABLES EXCEPT (t1);
    > instead of just ALTER PUBLICATION name ADD ALL TABLES EXCEPT t1;
    >
    With recent commit now we support
    CREATE PUBLICATION .. FOR ALL TABLES, ALL SEQUENCES.
    
    Now when I am trying to support "FOR ALL TABLE EXCEPT t1, t2" , I am
    getting a conflict when compiling this grammar.
    For example
    CREATE PUBLICATION .. FOR ALL TABLES EXCEPT t1, ...
    After this comma, bison is giving conflict because it is not able to
    figure whether to pick
    ExceptPublicationObjSpec or a PublicationAllObjSpec.
    So to handle this I introduced brackets around the table list.
    And to make ALTER PUBLICATION similar to CREATE PUBLICATION, I have
    added the same syntax for it.
    
    So current syntax for CREATE/ALTER PUBLICATION is like:
    CREATE PUBLICATION ... ALL TABLES EXCEPT TABLE(t1, t2, t3);
    ALTER PUBLICATION ... ADD ALL TABLES EXCEPT TABLE(t1, t2, t3);
    
    > ~~~
    >
    > 3.
    > BTW, I think you may need to consider a <table_exception_object>
    > instead of a generic name like <exception_object>, because in the
    > future if we EXCEPT SEQUENCES the <exception_object> name may be not
    > appropriate because things like [ONLY] and [*] are not applicable for
    > sequences.
    Fixed
    
    I have attached the latest patch here.
    I have also addressed the comments for [1], [2].
    
    [1]: https://www.postgresql.org/message-id/CALDaNm0xDv96F%2B5LzcJYV6RC3Jg%2BRtdUqpQ-zoauwq3woTFzmQ%40mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAHut+PsRD8ybC7MDBNBXXs=J2DuGiOc8kSePRyZc0s63U5f7tw@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  134. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-11-11T10:22:01Z

    On Fri, 7 Nov 2025 at 09:34, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > This is a general comment about the content of these patches.
    >
    > IIUC, the v25* patches currently are currently arranged like this:
    >
    > 0001
    >  - New command ALTER PUBLICATION pubname RESET;
    > 0002
    > - Add new command: ALTER PUBLICATION pub_name ADD ALL TABLES;
    > - Enhance existing CREATE and the new ALTER syntax for EXCEPT tables
    > 0003
    > - Enhance existing CREATE and ALTER syntax for EXCEPT col_list
    >
    > ~~~
    >
    > IMO it is a bug that the ALTER PUBLICATION pub_name ADD/SET ALL TABLES
    > command does not already exist as a supported command. And, that is
    > independent of anything else you are implementing here like RESET or
    > EXCEPT.
    >
    > Therefore, I think that one should be 1st in your patchset; The EXCEPT
    > stuff then just becomes enhancements to existing syntax, which would
    > give a cleaner separation of logic.
    >
    > So, I am suggesting there should be 4 patches instead of 3. e.g.
    >
    > SUGGESTION
    > 0001 - New command: ALTER PUBLICATION pub_name ADD/SET ALL TABLES;
    > 0002 - New command: ALTER PUBLICATION pubname RESET;
    > 0003 - Enhance existing CREATE/ALTER syntax for EXCEPT tables
    > 0004 - Enhance existing CREATE/ALTER syntax for EXCEPT col_list
    >
    I read the previous conversation in the thread. And got an
    understanding that RESET was introduced so that we can have a way to
    remove 'EXCEPT TABLE' from a publication and after RESET we can use
    'ADD ALL TABLES [EXCEPT]' to alter the list of EXCEPT TABLE. So I
    prefer to keep 'ALTER PUBLICATION .. RESET' as the first patch.
    I think since 'ADD ALL TABLES' serves our current purpose. We can add
    the syntax 'SET ALL TABLES' once 'ADD ALL TABLES' is in committed or
    in committable shape.
    
    Thanks,
    Shlok Kyal
    
    
    
    
  135. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-11-11T10:24:06Z

    On Fri, 24 Oct 2025 at 06:41, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Vignesh,
    >
    > I had a look at patch v24-0001.
    >
    > FYI -- a rebase is needed
    >
    > [postgres@CentOS7-x64 oss_postgres_misc]$ git apply
    > ../patches_misc/v24-0001-Add-RESET-clause-to-Alter-Publication-which-will.patch
    > error: patch failed: doc/src/sgml/ref/alter_publication.sgml:69
    > error: doc/src/sgml/ref/alter_publication.sgml: patch does not apply
    >
    > Here are some other review comments:
    >
    > ======
    >
    > 1.
    > There seems to be some basic omission of the ALTER PUBLICATION in that
    > it does not support "ALL TABLES" as a publication_object.
    >
    > Therefore, if you have:
    > CREATE PUBLICATION mypub FOR ALL TABLES;
    >
    > and then you do:
    > ALTER PUBLICATION mypub RESET;
    >
    > There seems to be no way to restore mpub to be an ALL TABLES publication again!
    >
    > ~~~
    >
    > I think if you are going to implement a RESET, then you also need a
    > way to get back to what you had before you did the reset. So you'll
    > also need to implement the ALTER PUBLICATION mypub SET ALL TABLES;
    >
    > IMO, supporting  "SET ALL TABLES" should be your new 0001 patch
    > because AFAIK, this situation already exists if the user had created
    > an "empty" publication:
    > CREATE PUBLICATION myemptypub;
    >
    With current patches we can add ALL TABLES to a publication using "ADD
    ALL TABLES"
    I think once the ADD ALL TABLES patch is committed, we can add SET ALL TABLES.
    
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 2.
    > Probably need to mention ALL SEQUENCES now too?
    >
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > 3.
    > +/* CREATE PUBLICATION default values for flags and publication parameters */
    > +#define PUB_DEFAULT_ACTION_INSERT true
    > +#define PUB_DEFAULT_ACTION_UPDATE true
    > +#define PUB_DEFAULT_ACTION_DELETE true
    > +#define PUB_DEFAULT_ACTION_TRUNCATE true
    > +#define PUB_DEFAULT_VIA_ROOT false
    > +#define PUB_DEFAULT_ALL_TABLES false
    > +#define PUB_DEFAULT_GENCOLS PUBLISH_GENCOLS_NONE
    > +
    >
    > Is it better to put all these in the catalog/pg_publication.h where
    > the catalog was defined?
    >
    > ~~~
    >
    > AlterPublicationReset:
    >
    > 4.
    > + /* Set ALL TABLES flag to false */
    > + if (pubform->puballtables)
    > + {
    > + values[Anum_pg_publication_puballtables - 1] =
    > BoolGetDatum(PUB_DEFAULT_ALL_TABLES);
    > + replaces[Anum_pg_publication_puballtables - 1] = true;
    > + CacheInvalidateRelcacheAll();
    > + }
    >
    > Why not just do this anyway without the condition?
    >
    > ======
    > src/backend/parser/gram.y
    >
    > 6.
    > It would be nicer if all these grammar productions were coded in the
    > same order as the comment above them.
    >
    > ======
    > src/include/nodes/parsenodes.h
    >
    > 7.
    >   AP_AddObjects, /* add objects to publication */
    >   AP_DropObjects, /* remove objects from publication */
    >   AP_SetObjects, /* set list of objects */
    > + AP_ResetPublication, /* reset the publication */
    >  } AlterPublicationAction;
    >
    > It is already called "AlterPublicationAction", so maybe the enum value
    > only needs to be AP_Reset instead of AP_ResetPublication.
    >
    >
    > ======
    > src/test/regress/expected/publication.out
    >
    > 8.
    > Expected output all needs rebasing now that there is a new "All
    > sequences" column.
    >
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 9.
    > +ALTER PUBLICATION testpub_reset ADD TABLE pub_sch1.tbl1;
    > +
    > +-- Verify that associated tables are removed from the publication after RESET
    > +\dRp+ testpub_reset
    > +ALTER PUBLICATION testpub_reset RESET;
    > +\dRp+ testpub_reset
    > +
    >
    > I felt the ADD TABLE should be after the comment.
    >
    > And ditto for all the other test cases -- should be that same pattern too.
    >
    > # comment about test
    > ALTER .. do something
    > \dRp+ pub
    > ALTER .. RESET
    > \dRp+ pub
    >
    > ~~~
    >
    > 10.
    > +-- Verify that associated schemas are reomved from the publication after RESET
    >
    > typo: /reomved/removed/
    >
    > ~~~
    >
    > 11.
    > +-- Verify that only superuser can reset a publication
    > +ALTER PUBLICATION testpub_reset OWNER TO regress_publication_user2;
    > +SET ROLE regress_publication_user2;
    > +ALTER PUBLICATION testpub_reset RESET; -- fail - must be superuser
    > +SET ROLE regress_publication_user;
    > +
    >
    > Perhaps this should be the first test?
    >
    > ======
    
    I have addressed the remaining comments in the latest v26 patch [1].
    [1]: https://www.postgresql.org/message-id/CANhcyEWGiWwGt1-v6d9bCAae9Np7cNPt%2BiRV9PXBZ0z%3D75XEVw%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  136. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-11-11T10:24:29Z

    On Thu, 30 Oct 2025 at 11:34, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Vignesh
    >
    > Here are some review comments for the patch v24-0002.
    >
    > These comments are just for the SGML docs. The patch needs a rebase so
    > I was unable to review the code.
    >
    > ======
    > Commit message
    >
    > 1.
    > A new column "prexcept" is added to table "pg_publication_rel", to maintain
    > the relations that the user wants to exclude from the publications.
    >
    > ~
    >
    > /to maintain/to flag/
    >
    > ======
    > doc/src/sgml/logical-replication.sgml
    >
    > 2.
    >    <para>
    > -   To add tables to a publication, the user must have ownership rights on the
    > -   table. To add all tables in schema to a publication, the user must be a
    > -   superuser. To create a publication that publishes all tables or
    > all tables in
    > -   schema automatically, the user must be a superuser.
    > +   To create a publication using FOR ALL TABLES or FOR ALL TABLES IN SCHEMA,
    > +   the user must be a superuser. To add ALL TABLES or ALL TABLES IN SCHEMA to a
    > +   publication, the user must be a superuser. To add tables to a publication,
    > +   the user must have ownership rights on the table.
    >    </para>
    >
    > Those "FOR ALL TABLES" etc are missing SGML markup.
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 3.
    > +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > ADD ALL TABLES [ EXCEPT [ TABLE ] <replaceable
    > class="parameter">exception_object</replaceable> [, ... ] ]
    >
    > and
    >
    > +
    > +<phrase>where <replaceable
    > class="parameter">exception_object</replaceable> is:</phrase>
    > +
    > +    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    > +
    >
    > It is not clear from the syntax which of these is possible.
    >
    > ... ADD ALL TABLES EXCEPT TABLE t1,t2,t3
    > ... ADD ALL TABLES EXCEPT TABLE t1, TABLE t2, TABLES t3
    >
    > IMO it is best put the "[TABLE]" within the exception_object:
    > [ TABLE ] [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > Then both are possible, which is consistent with how "FOR TABLE" syntax works.
    >
    > Furthermore, you might want later to say EXCLUDE SEQUENCE, so doing it
    > this way makes that possible.
    >
    Recently a commit was pushed which allowed use of ALL SEQUENCES
    syntax. Due to it, I have updated the syntax to
    CREATE PUBLICATION ... ALL TABLES EXCEPT TABLE(t1, t2, t3);
    
    For ALTER PUBLICATION to have a similar syntax. I have updated it to
    have syntax like:
    ALTER PUBLICATION ... ADD ALL TABLES EXCEPT TABLE(t1, t2, t3);
    
    I think in the future we can extend the syntax for sequences like:
    ALL SEQUENCES EXCEPT(s1, s2, s3).
    
    See [1] for more info.
    
    > ~~~
    >
    > 4.
    > -   Adding a table to a publication additionally requires owning that table.
    > -   The <literal>ADD TABLES IN SCHEMA</literal>,
    > +   Adding a table to or excluding a table from a publication additionally
    > +   requires owning that table. The <literal>ADD ALL TABLES</literal>,
    >
    > This wording seems a bit awkward. How are re-phrasing like:
    >
    > SUGGESTION
    > Adding or excluding a table from a publication requires ownership of that table.
    >
    > ~~~
    >
    > 5.
    > -      name to explicitly indicate that descendant tables are included.
    > +      name to explicitly indicate that descendant tables are affected. For
    > +      partitioned tables, <literal>ONLY</literal> donot have any effect.
    >
    > typo: /donot/does not/
    >
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 6.
    > -    [ FOR ALL TABLES
    > +    [ FOR ALL TABLES [ EXCEPT [ TABLE ] <replaceable
    > class="parameter">exception_object</replaceable> [, ... ] ]
    >        | FOR <replaceable
    > class="parameter">publication_object</replaceable> [, ... ] ]
    >      [ WITH ( <replaceable
    > class="parameter">publication_parameter</replaceable> [= <replaceable
    > class="parameter">value</replaceable>] [, ... ] ) ]
    >
    > @@ -30,6 +30,10 @@ CREATE PUBLICATION <replaceable
    > class="parameter">name</replaceable>
    >
    >      TABLE [ ONLY ] <replaceable
    > class="parameter">table_name</replaceable> [ * ] [ ( <replaceable
    > class="parameter">column_name</replaceable> [, ... ] ) ] [ WHERE (
    > <replaceable class="parameter">expression</replaceable> ) ] [, ... ]
    >      TABLES IN SCHEMA { <replaceable
    > class="parameter">schema_name</replaceable> | CURRENT_SCHEMA } [, ...
    > ]
    > +
    > +<phrase>where <replaceable
    > class="parameter">exception_object</replaceable> is:</phrase>
    > +
    > +    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > Same review comment as #3 before.
    >
    > I think it is clearer (and more flexible) to change the
    > exception_object to include [TABLE].
    > [ TABLE ] [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > It also helps pave the way for any future EXCLUDE SEQUENCE feature.
    >
    See #3 before
    .
    > ~~~
    >
    > 7.
    > +     <para>
    > +      This clause specifies a list of tables to be excluded from the
    > +      publication. It can only be used with <literal>FOR ALL TABLES</literal>.
    > +      If <literal>ONLY</literal> is specified before the table name, only
    > +      that table is excluded from the publication. If
    > <literal>ONLY</literal> is
    > +      not specified, the table and all its descendant tables (if any) are
    > +      excluded. Optionally, <literal>*</literal> can be specified after the
    > +      table name to explicitly indicate that descendant tables are excluded.
    > +      This does not apply to a partitioned table, however.  The partitioned
    > +      table or its partitions are excluded from the publication based on the
    > +      parameter <literal>publish_via_partition_root</literal>.
    > +     </para>
    > +     <para>
    > +      When <literal>publish_via_partition_root</literal> is set to
    > +      <literal>true</literal>, specifying a root partitioned table in
    > +      <literal>EXCEPT TABLE</literal> excludes it and all its partitions from
    > +      replication. Specifying a leaf partition has no effect, as its
    > changes are
    > +      still replicated via the root partitioned table. When
    > +      <literal>publish_via_partition_root</literal> is set to
    > +      <literal>false</literal>, specifying a partitioned table or non-leaf
    > +      partition has no effect, as changes are replicated via the leaf
    > +      partitions. Specifying a leaf partition excludes only that partition from
    > +      replication.
    > +     </para>
    >
    > I felt that the second paragraph should be started with the sentence
    > "The partitioned table or its partitions are excluded...", so then
    > everything related to "publish_via_partition_root" is kept together.
    >
    > ~~~
    >
    > 8.
    > +  <para>
    > +   Create a publication that publishes all changes in all the tables except for
    > +   the changes of <structname>users</structname> and
    > +   <structname>departments</structname>:
    > +<programlisting>
    > +CREATE PUBLICATION mypublication FOR ALL TABLES EXCEPT users, departments;
    > +</programlisting>
    > +  </para>
    >
    > The words "the changes of" are not needed, and you did not use that
    > wording in the ALTER PUBLICATION example.
    >
    > ======
    > doc/src/sgml/ref/psql-ref.sgml
    >
    > 9.
    >          If <literal>x</literal> is appended to the command name, the results
    >          are displayed in expanded mode.
    > -        If <literal>+</literal> is appended to the command name, the tables and
    > -        schemas associated with each publication are shown as well.
    > +        If <literal>+</literal> is appended to the command name, the tables,
    > +        excluded tables and schemas associated with each publication
    > are shown as
    > +        well.
    >          </para>
    >
    > /excluded tables and schemas/excluded tables, and schemas/
    >
    
    I addressed the remaining comments in the latest v26 patch [1].
    [1]:https://www.postgresql.org/message-id/CANhcyEWGiWwGt1-v6d9bCAae9Np7cNPt%2BiRV9PXBZ0z%3D75XEVw%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  137. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-11-13T06:08:17Z

    On Tue, 11 Nov 2025 at 15:50, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Fri, 7 Nov 2025 at 11:36, Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Hi Shlok.
    > >
    > > Some questions for the patch v25-0002 (EXCEPT tables)
    > >
    > > ======
    > > doc/src/sgml/ref/alter_publication.sgml
    > >
    > > 1.
    > > +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > > ADD ALL TABLES [ EXCEPT [ TABLE ] ( <replaceable
    > > class="parameter">exception_object</replaceable> [, ... ] ) ]
    > >
    > > You can do both ADD/SET the <publication_object>, so really there
    > > should be an ADD/SET ALL TABLES command as well, right?
    > >
    > These patches only added the ADD ALL TABLES command. I think once the
    > ADD ALL TABLES patch is committed, we can add the syntax SET ALL
    > TABLES.
    >
    > > ~~~
    > >
    > > 2.
    > > What was your reason for changing the syntax?
    > > AFAICT those added "( )" are not strictly necessary, so I just
    > > wondered your reason.
    > >
    > > For example, we do not have any "( )" for <publication_object> [,...].
    > > It is: ALTER PUBLICATION name ADD publication_object [, ...]
    > > Not:   ALTER PUBLICATION name ADD (publication_object [, ...])
    > >
    > > So in the same way we could have EXCEPT syntax like that:
    > > ALTER PUBLICATION name ADD ALL TABLES [EXCEPT <table_exception_object> [, ...]]
    > > Where table_exception_object is: [ TABLE ] [ ONLY ] table_name [ * ]
    > >
    > > Currently, if the user just wants to exclude a single table they must do:
    > > ALTER PUBLICATION name ADD ALL TABLES EXCEPT (t1);
    > > instead of just ALTER PUBLICATION name ADD ALL TABLES EXCEPT t1;
    > >
    > With recent commit now we support
    > CREATE PUBLICATION .. FOR ALL TABLES, ALL SEQUENCES.
    >
    > Now when I am trying to support "FOR ALL TABLE EXCEPT t1, t2" , I am
    > getting a conflict when compiling this grammar.
    > For example
    > CREATE PUBLICATION .. FOR ALL TABLES EXCEPT t1, ...
    > After this comma, bison is giving conflict because it is not able to
    > figure whether to pick
    > ExceptPublicationObjSpec or a PublicationAllObjSpec.
    > So to handle this I introduced brackets around the table list.
    > And to make ALTER PUBLICATION similar to CREATE PUBLICATION, I have
    > added the same syntax for it.
    >
    > So current syntax for CREATE/ALTER PUBLICATION is like:
    > CREATE PUBLICATION ... ALL TABLES EXCEPT TABLE(t1, t2, t3);
    > ALTER PUBLICATION ... ADD ALL TABLES EXCEPT TABLE(t1, t2, t3);
    >
    > > ~~~
    > >
    > > 3.
    > > BTW, I think you may need to consider a <table_exception_object>
    > > instead of a generic name like <exception_object>, because in the
    > > future if we EXCEPT SEQUENCES the <exception_object> name may be not
    > > appropriate because things like [ONLY] and [*] are not applicable for
    > > sequences.
    > Fixed
    >
    > I have attached the latest patch here.
    > I have also addressed the comments for [1], [2].
    >
    > [1]: https://www.postgresql.org/message-id/CALDaNm0xDv96F%2B5LzcJYV6RC3Jg%2BRtdUqpQ-zoauwq3woTFzmQ%40mail.gmail.com
    > [2]: https://www.postgresql.org/message-id/CAHut+PsRD8ybC7MDBNBXXs=J2DuGiOc8kSePRyZc0s63U5f7tw@mail.gmail.com
    >
    
    The patches needed a rebase. Here are the rebased patches.
    
    Thanks,
    Shlok Kyal
    
  138. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-14T06:44:43Z

    Hi Shlok.
    
    Some review comments for patch v27-0001.
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    1.
    +  <para>
    +   The <literal>RESET</literal> clause will reset the publication to
    the default
    +   state. This includes resetting all publication parameters, setting the
    +   <literal>ALL TABLES</literal> and <literal>ALL SEQUENCES</literal> flags to
    +   <literal>false</literal>, and removing all associated tables and
    schemas from
    +   the publication.
       </para>
    
    It would be better to give references to the actual
    pg_publication.puballtables and .puballsequences flag fields [1]
    instead of vaguely calling them the "<literal>ALL TABLES</literal> and
    <literal>ALL SEQUENCES</literal> flags".
    
    ======
    src/backend/commands/publicationcmds.c
    
    AlterPublicationReset:
    
    2.
    + if (pubform->puballtables)
    + CacheInvalidateRelcacheAll();
    
    Does that also need to check ->puballsequences?
    
    ======
    src/test/regress/sql/publication.sql
    
    3.
    If you want to, you can easily combine many of these test cases and
    verify them in one go instead of separate ALTER/RESET for every kind
    of flag.
    
    ~~~
    
    4.
    +-- Verify that 'ALL TABLES' flag is reset
    
    Missing test to check the 'ALL SEQUENCES' flag gets reset?
    
    ======
    [1] https://www.postgresql.org/docs/devel/catalog-pg-publication.html
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  139. Re: Skipping schema changes in publication

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

    On Tue, Nov 11, 2025 at 9:22 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Fri, 7 Nov 2025 at 09:34, Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Hi Shlok.
    > >
    > > This is a general comment about the content of these patches.
    > >
    > > IIUC, the v25* patches currently are currently arranged like this:
    > >
    > > 0001
    > >  - New command ALTER PUBLICATION pubname RESET;
    > > 0002
    > > - Add new command: ALTER PUBLICATION pub_name ADD ALL TABLES;
    > > - Enhance existing CREATE and the new ALTER syntax for EXCEPT tables
    > > 0003
    > > - Enhance existing CREATE and ALTER syntax for EXCEPT col_list
    > >
    > > ~~~
    > >
    > > IMO it is a bug that the ALTER PUBLICATION pub_name ADD/SET ALL TABLES
    > > command does not already exist as a supported command. And, that is
    > > independent of anything else you are implementing here like RESET or
    > > EXCEPT.
    > >
    > > Therefore, I think that one should be 1st in your patchset; The EXCEPT
    > > stuff then just becomes enhancements to existing syntax, which would
    > > give a cleaner separation of logic.
    > >
    > > So, I am suggesting there should be 4 patches instead of 3. e.g.
    > >
    > > SUGGESTION
    > > 0001 - New command: ALTER PUBLICATION pub_name ADD/SET ALL TABLES;
    > > 0002 - New command: ALTER PUBLICATION pubname RESET;
    > > 0003 - Enhance existing CREATE/ALTER syntax for EXCEPT tables
    > > 0004 - Enhance existing CREATE/ALTER syntax for EXCEPT col_list
    > >
    > I read the previous conversation in the thread. And got an
    > understanding that RESET was introduced so that we can have a way to
    > remove 'EXCEPT TABLE' from a publication and after RESET we can use
    > 'ADD ALL TABLES [EXCEPT]' to alter the list of EXCEPT TABLE. So I
    > prefer to keep 'ALTER PUBLICATION .. RESET' as the first patch.
    > I think since 'ADD ALL TABLES' serves our current purpose. We can add
    > the syntax 'SET ALL TABLES' once 'ADD ALL TABLES' is in committed or
    > in committable shape.
    >
    
    Sure, you can defer the ALTER PUBLICATION ... SET ALL TABLES.
    
    However, I still think that 'ALTER PUBLICATION ... ADD ALL TABLES' is
    a self-contained new command that deserves to have its own *separate*
    patch and tests and docs, etc.
    
    IMO, patch 0002 is doing too much at once. It would be tidier (and
    smaller and easier to review, etc) if you split 0002 to implement the
    new 'ALTER PUBLICATION ... ADD ALL TABLES' separately, before
    expanding on that to implement the EXCEPT part: 'ALTER PUBLICATION ...
    ADD ALL TABLES [EXCEPT ...]'.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  140. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-17T06:12:24Z

    Hi Shlok,
    
    Here are a few ad-hoc comments for patch v27-0002.
    
    ======
    doc/src/sgml/logical-replication.sgml
    
    1.
       <para>
    -   To add tables to a publication, the user must have ownership rights on the
    -   table. To add all tables in schema to a publication, the user must be a
    -   superuser. To create a publication that publishes all tables, all tables in
    -   schema, or all sequences automatically, the user must be a superuser.
    +   To create a publication using <literal>FOR ALL TABLES</literal>,
    +   <literal>FOR ALL SEQUENCES or <literal>FOR TABLES IN SCHEMA</literal>, the
    +   user must be a superuser. To add <literal>ALL TABLES</literal> or
    +   <literal>TABLES IN SCHEMA</literal> to a publication, the user must be a
    +   superuser. To add tables to a publication, the user must have ownership
    +   rights on the table.
       </para>
    
    Typo: Mismatched tags. "<literal>FOR ALL SEQUENCES or <literal>FOR
    TABLES IN SCHEMA</literal>"
    
    (The docs in v27-0002 cannot  build because of this error)
    
    ======
    src/backend/commands/publicationcmds.c
    
    CheckPublicationDefValues:
    
    2.
    +/*
    + * Check if the publication has default values.
    + *
    + * Returns true if the publication satisfies all the following conditions:
    + * a) Publication is not set with "FOR ALL TABLES"
    + * b) Publication is having default publication parameter values
    + * c) Publication is not associated with schemas
    + * d) Publication is not associated with relations
    + */
    +static bool
    +CheckPublicationDefValues(HeapTuple tup)
    
    Should a) also say "FOR ALL SEQUENCES"?
    
    ~~~
    
    3.
    What about checking defaults of other publication parameters, e.g.
    publish_via_parttion_root and  publish_generated_columns?
    
    ======
    src/backend/parser/gram.y
    
    4.
      * TABLE table_name [, ...]
      * TABLES IN SCHEMA schema_name [, ...]
      *
    + * ALTER PUBLICATION name ADD ALL TABLES EXCEPT [TABLE] (table_name [, ...])
    + *
      * ALTER PUBLICATION name RESET
    
    The EXCEPT clause part should be optional.
    
    ======
    src/bin/pg_dump/t/002_pg_dump.pl
    
    5.
      #
      # 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})
    
    Is this just an accidental whitespace change?
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  141. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-17T07:51:57Z

    Hi Shlok,
    
    A comment about patch v27-0003
    
    ======
    doc/src/sgml/logical-replication.sgml
    
    All references to tables "t1" and "t2" should use the <structname> tag
    in the SGML. The current patch has broken this in multiple places:
    
    e.g.
    -    Create a table <structname>t1</structname> to be used in the
    following example.
    +    Create tables <literal>t1</literal> and <literal>t2</literal> to be used in
    +    the following example.
    
    ~~
    
    e.g.
    -    table <structname>t1</structname> to reduce the number of columns
    that will be
    -    replicated. Notice that the order of column names in the column list does
    -    not matter.
    +    table <literal>t1</literal>, and another column list is defined for table
    +    <literal>t2</literal> using the <literal>EXCEPT</literal> clause to reduce
    +    the number of columns that will be replicated. Note that the
    order of column
    +    names in the column lists does not matter.
    
    ~~
    
    e.g.
    -     On the subscriber node, create a table
    <structname>t1</structname> which now
    -     only needs a subset of the columns that were on the publisher table
    -     <structname>t1</structname>, and also create the subscription
    +     On the subscriber node, create tables <literal>t1</literal> and
    +     <literal>t2</literal> which now only needs a subset of the columns that
    +     were on the publisher tables <literal>t1</literal> and
    +     <literal>t2</literal>, and also create the subscription
    
    ~~
    
    e.g.
    -     On the publisher node, insert some rows to table
    <structname>t1</structname>.
    +     On the publisher node, insert some rows to tables <literal>t1</literal>
    +     and <literal>t2</literal>
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  142. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-11-19T10:04:04Z

    On Fri, 14 Nov 2025 at 12:15, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Some review comments for patch v27-0001.
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 1.
    > +  <para>
    > +   The <literal>RESET</literal> clause will reset the publication to
    > the default
    > +   state. This includes resetting all publication parameters, setting the
    > +   <literal>ALL TABLES</literal> and <literal>ALL SEQUENCES</literal> flags to
    > +   <literal>false</literal>, and removing all associated tables and
    > schemas from
    > +   the publication.
    >    </para>
    >
    > It would be better to give references to the actual
    > pg_publication.puballtables and .puballsequences flag fields [1]
    > instead of vaguely calling them the "<literal>ALL TABLES</literal> and
    > <literal>ALL SEQUENCES</literal> flags".
    >
    Fixed
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > AlterPublicationReset:
    >
    > 2.
    > + if (pubform->puballtables)
    > + CacheInvalidateRelcacheAll();
    >
    > Does that also need to check ->puballsequences?
    >
    I think we call CacheInvalidateRelcacheAll to invalide the relsync
    cache for the case of ALTER Publication. For sequences we do not build
    RelSyncEntry.
    Also I see there are other similar occurrences (such as
    RemovePublicationById, AlterPublicationOptions) where we do not
    invalidate cache if we modify all sequence publications.
    So, I think we do not require this check for puballsequences.
    
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 3.
    > If you want to, you can easily combine many of these test cases and
    > verify them in one go instead of separate ALTER/RESET for every kind
    > of flag.
    >
    > ~~~
    >
    I agree. I have made the changes in the latest patch.
    
    > 4.
    > +-- Verify that 'ALL TABLES' flag is reset
    >
    > Missing test to check the 'ALL SEQUENCES' flag gets reset?
    >
    Added the test.
    
    > ======
    > [1] https://www.postgresql.org/docs/devel/catalog-pg-publication.html
    >
    
    I have also addressed the comments in [1], [2].
    
    [1]: https://www.postgresql.org/message-id/CAHut%2BPtRzCD4-0894cutkU_h8cPNtosN0_oSHn2iAKEfg2ENOQ%40mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAHut+PuHn-hohA4OdEJz+Zfukfr41TvMTeTH7NwJ=wg1+94uNA@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  143. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-11-19T10:05:05Z

    On Mon, 17 Nov 2025 at 11:35, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Tue, Nov 11, 2025 at 9:22 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Fri, 7 Nov 2025 at 09:34, Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > > > Hi Shlok.
    > > >
    > > > This is a general comment about the content of these patches.
    > > >
    > > > IIUC, the v25* patches currently are currently arranged like this:
    > > >
    > > > 0001
    > > >  - New command ALTER PUBLICATION pubname RESET;
    > > > 0002
    > > > - Add new command: ALTER PUBLICATION pub_name ADD ALL TABLES;
    > > > - Enhance existing CREATE and the new ALTER syntax for EXCEPT tables
    > > > 0003
    > > > - Enhance existing CREATE and ALTER syntax for EXCEPT col_list
    > > >
    > > > ~~~
    > > >
    > > > IMO it is a bug that the ALTER PUBLICATION pub_name ADD/SET ALL TABLES
    > > > command does not already exist as a supported command. And, that is
    > > > independent of anything else you are implementing here like RESET or
    > > > EXCEPT.
    > > >
    > > > Therefore, I think that one should be 1st in your patchset; The EXCEPT
    > > > stuff then just becomes enhancements to existing syntax, which would
    > > > give a cleaner separation of logic.
    > > >
    > > > So, I am suggesting there should be 4 patches instead of 3. e.g.
    > > >
    > > > SUGGESTION
    > > > 0001 - New command: ALTER PUBLICATION pub_name ADD/SET ALL TABLES;
    > > > 0002 - New command: ALTER PUBLICATION pubname RESET;
    > > > 0003 - Enhance existing CREATE/ALTER syntax for EXCEPT tables
    > > > 0004 - Enhance existing CREATE/ALTER syntax for EXCEPT col_list
    > > >
    > > I read the previous conversation in the thread. And got an
    > > understanding that RESET was introduced so that we can have a way to
    > > remove 'EXCEPT TABLE' from a publication and after RESET we can use
    > > 'ADD ALL TABLES [EXCEPT]' to alter the list of EXCEPT TABLE. So I
    > > prefer to keep 'ALTER PUBLICATION .. RESET' as the first patch.
    > > I think since 'ADD ALL TABLES' serves our current purpose. We can add
    > > the syntax 'SET ALL TABLES' once 'ADD ALL TABLES' is in committed or
    > > in committable shape.
    > >
    >
    > Sure, you can defer the ALTER PUBLICATION ... SET ALL TABLES.
    >
    > However, I still think that 'ALTER PUBLICATION ... ADD ALL TABLES' is
    > a self-contained new command that deserves to have its own *separate*
    > patch and tests and docs, etc.
    >
    > IMO, patch 0002 is doing too much at once. It would be tidier (and
    > smaller and easier to review, etc) if you split 0002 to implement the
    > new 'ALTER PUBLICATION ... ADD ALL TABLES' separately, before
    > expanding on that to implement the EXCEPT part: 'ALTER PUBLICATION ...
    > ADD ALL TABLES [EXCEPT ...]'.
    
    I agree with you.
    I have split the 0002 patch. Now we have following patches
    
    0001 - Add RESET clause to Alter Publication
    0002 - Support ADD ALL TABLES in ALTER PUBLICATION
    0003 - Skip publishing the tables specified in EXCEPT TABLE
    0004 - Skip publishing the columns specified in FOR TABLE EXCEPT
    
    I have attached the updated patch in [1].
    [1]: https://www.postgresql.org/message-id/CANhcyEXCKPCAdoqBLAhxt64Nwf%2B7T52dd8daE3qvhBNTvro13Q%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  144. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-20T01:55:16Z

    Hi Shlok,
    
    Some review comments for patch v28-0001.
    
    ======
    src/backend/commands/publicationcmds.c
    
    AlterPublicationReset:
    1.
    + values[Anum_pg_publication_puballtables - 1] =
    BoolGetDatum(PUB_DEFAULT_ALL_TABLES);
    + replaces[Anum_pg_publication_puballtables - 1] = true;
    +
    + values[Anum_pg_publication_puballsequences - 1] =
    BoolGetDatum(PUB_DEFAULT_ALL_SEQUENCES);
    + replaces[Anum_pg_publication_puballsequences - 1] = true;
    
    The PUB_DEFAULT_xxx made sense for all the publication parameters, but
    these are just flags that say if the ALL TABLES or ALL SEQUENCES
    clauses are present in the command, so I'm not sure that "default"
    macros are appropriate here.
    
    e.g. it seems better to reset those using hardwired booleans:
    
    values[Anum_pg_publication_puballtables - 1] = BoolGetDatum(false);
    values[Anum_pg_publication_puballsequences - 1] = BoolGetDatum(false);
    
    (This would also be consistent with what the function comment is saying)
    
    ======
    src/include/catalog/pg_publication.h
    
    2.
    +/* default values for flags and publication parameters */
    +#define PUB_DEFAULT_ACTION_INSERT true
    +#define PUB_DEFAULT_ACTION_UPDATE true
    +#define PUB_DEFAULT_ACTION_DELETE true
    +#define PUB_DEFAULT_ACTION_TRUNCATE true
    +#define PUB_DEFAULT_VIA_ROOT false
    +#define PUB_DEFAULT_ALL_TABLES false
    +#define PUB_DEFAULT_ALL_SEQUENCES false
    +#define PUB_DEFAULT_GENCOLS PUBLISH_GENCOLS_NONE
    
    As in the previous comment #1, I don't think we should define
    PUB_DEFAULT_ALL_TABLES and PUB_DEFAULT_ALL_SEQUENCES. Also, change the
    comment to remove the words "flags and".
    
    ======
    src/test/regress/sql/publication.sql
    
    3.
    I don't think the parameter names should be written in uppercase
    because that's not how they normally appear in the docs and examples.
    
    ~~~
    
    4.
    +-- Verify that 'ALL TABLES', 'ALL SEQUENCES' flag is reset
    
    typo: /flag is reset/flags are reset/
    
    ~~~
    
    5.
    +-- Verify that associated tables, schemas and the publication parameters
    +-- 'PUBLISH', 'PUBLISH_VIA_PARTITION_ROOT', and 'PUBLISH_GENERATED_COLUMNS'
    +-- are removed from the publication after RESET
    
    The comment makes it sound like parameters are "removed". Maybe some
    rewording is needed.
    
    SUGGESTION
    Verify that a publication RESET removes the associated tables and
    schemas, and sets default values for publication parameters 'publish',
    'publish_via_partition_root', and 'publish_generated_columns'.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  145. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-20T06:24:12Z

    Hi Shlok.
    
    Thanks for splitting the patches.
    
    Here are some review comments for the new patch v28-0002 (ADD ALL TABLES).
    
    ======
    Commit Message
    
    1.
    This patch adds support for using ADD ALL TABLES in ALTER PUBLICATION,
    allowing an existing publication to be changed into an ALL TABLES
    publication. This command is permitted only when the publication is
    in its default state, meaning it has no tables or schemas added, its
    ALL TABLES and ALL SEQUENCES flags are not set, and publication
    options such as publish_via_root_partition, publish_generated_columns,
    and publish are at their default values.
    
    ~
    
    IMO, the restrictions for this new command are too severe:
    
    e.g. If I already have a FOR ALL SEQUENCES publication, then I
    expected it should be possible to ADD ALL TABLES to that as well,
    right?
    
    Likewise, why are we enforcing that the publication parameters must be
    defaults? IOW, why is (i) below disallowed, but (ii) is allowed?
    
    (i)
    ALTER PUBLICATION pub SET (publish_generated_columns=stored);
    ALTER PUBLICATION pub ADD ALL TABLES;
    
    (ii)
    ALTER PUBLICATION pub ADD ALL TABLES;
    ALTER PUBLICATION pub SET (publish_generated_columns=stored);
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    Description:
    
    2.
    The "Description" part of this page is confusing because it was
    referring to "The first three variants" and later "The fourth
    variant".  Now that the "ADD ALL TABLES" variant has been added, I
    have lost track of what "variants" this description is talking about.
    Those words should be replaced by something clearer. This could be an
    ongoing issue if it is not worded differently because the same problem
    will happen again, e.g. when more syntax gets added for ALL SEQUENCES,
    etc.
    
    ~~~
    
    3.
    Note also that DROP TABLES IN SCHEMA will not drop any schema tables
    that were specified using FOR TABLE/ ADD TABLE.
    
    ~
    
    That sentence (above) is from the docs. Does that also need updating
    now that there is ADD ALL TABLES?
    
    ======
    src/backend/commands/publicationcmds.c
    
    CheckPublicationDefValues:
    
    4.
    Is this function needed?
    
    ~~~
    
    AlterPublication:
    
    5.
    + if (stmt->for_all_tables)
    + {
    + bool isdefault = CheckPublicationDefValues(tup);
    +
    + if (!isdefault)
    + ereport(ERROR,
    + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    + errmsg("adding ALL TABLES requires the publication to have default
    publication parameter values"),
    + errdetail("ALL TABLES or ALL SEQUENCES flag should not be set and no
    tables/schemas should be associated."),
    + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    +
    + AlterPublicationSetAllTables(rel, tup);
    + }
    +
    
    Why do we need this self-imposed restriction?
    
    ======
    src/include/nodes/parsenodes.h
    
    6.
      List    *pubobjects; /* Optional list of publication objects */
    + bool for_all_tables; /* Special publication for all tables in db */
      AlterPublicationAction action; /* What action to perform with the given
      * objects */
     } AlterPublicationStmt;
    
    
    There is no such "FOR" syntax like ALTER PUBLICATION ... FOR ALL
    TABLES, so I felt just 'puballtables' might be a better member name.
    
    ======
    src/test/regress/sql/publication.sql
    
    7.
    Don't uppercase any of the publication parameters because they never
    appear in the docs/examples like that.
    
    ~
    
    8.
    So that the last command is the one being tested, I felt that all the
    test cases should be doing RESET *first* instead of last.
    
    ~~~
    
    9.
    You don't always need to use RESET. There should also be some tests
    using an "empty" publication just to be sure it works. e.g
    
    CREATE PUBLICATION pub_empty;
    ALTER PUBLICATION pub_empty ADD ALL TABLES;
    
    ~~~
    
    10.
    As commented earlier, I felt the rules were too restrictive. So I
    think some test cases can be removed.
    
    ~~~
    
    11.
    +-- Tests for ALTER PUBLICATION ... ADD ALL TABLES
    
    ~
    
    I noticed there is a "--
    ======================================================" separator
    between the major groups of tests.
    
    11a. Should use this separator in patch 0001 for the RESET group of tests
    
    11b. Should use this separator in patch 0002 for the ADD ALL TABLES
    groups of tests
    
    ~~~
    
    12.
    +-- Can't add ALL TABLES to 'ALL TABLES' publication
    +ALTER PUBLICATION testpub_reset ADD ALL TABLES;
    +
    
    This test case seems to belong earlier, near the 'FOR TABLE' and the
    'TABLES IN SCHEMA' tests.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  146. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-21T06:55:37Z

    Hi Shlok.
    
    Here are some review comments for your patch v28-0003 (EXCEPT TABLE ...).
    
    The review of this patch is a WIP. In this post I only looked at the test code.
    
    ======
    .../t/037_rep_changes_except_table.pl
    
    1.
    +
    +# Copyright (c) 2021-2025, PostgreSQL Global Development Group
    +
    +# Logical replication tests for except table publications
    
    Use uppercase: /except table/EXCEPT TABLE/
    
    ~~~
    
    2.
    There are lots of test cases dedicated to partiion-table testing. I
    felt a bigger comment separating these major groups might be helpful.
    
    Something like:
    
    -- ============================================
    -- EXCEPT TABLE test cases for normal tables
    -- ============================================
    
    and
    
    -- ============================================
    -- EXCEPT TABLE test cases for partition tables
    -- ============================================
    
    ~~~
    
    3.
    +# Initialize publisher node
    ...
    +# Create subscriber node
    
    Those 2 comments should be almost alike -- e.g. both should say
    "Initialize" or both should say "Create".
    
    ~~~
    
    4.
    +# Test replication with publications created using FOR ALL TABLES EXCEPT TABLE
    +# clause.
    +# Create schemas and tables on publisher
    +$node_publisher->safe_psql(
    + 'postgres', qq(
    + CREATE SCHEMA sch1;
    + CREATE TABLE sch1.tab1 AS SELECT generate_series(1,10) AS a;
    + CREATE TABLE public.tab1(a int);
    +));
    +
    
    That first sentence ("Test replication with ...") is not needed here.
    The is just repeating the purpose of the entire file, so that comment
    can replace the one at the top of this file.
    
    ~~~
    
    5.
    +# Insert some data and verify that inserted data is not replicated
    
    Be explicit that we are referring to the excluded table.
    
    SUGGESTION (e.g.)
    Verify that data inserted to the excluded table is not replcated.
    
    ~~~
    
    6.
    +# Alter publication to exclude data changes in public.tab1 and verify that
    +# subscriber does not get the changed data for this table.
    +$node_publisher->safe_psql(
    + 'postgres', qq(
    + ALTER PUBLICATION tap_pub_schema RESET;
    + ALTER PUBLICATION tap_pub_schema ADD ALL TABLES EXCEPT TABLE
    (sch1.tab1, public.tab1);
    + INSERT INTO public.tab1 VALUES(generate_series(1,10));
    +));
    +$node_publisher->wait_for_catchup('tap_sub_schema');
    +
    
    It is not strictly needed for these tests, but do you think it makes
    more sense to also do an ALTER SUBSCRIPTION ... REFRESH PUBLICATION;
    whenever you change the publications?
    
    ~~~
    
    7.
    +# cleanup
    +$node_publisher->safe_psql('postgres', "DROP PUBLICATION tap_pub_schema");
    +$node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION tap_sub_schema");
    +
    +
    
    double-blank lines.
    
    ~~~
    
    8.
    I think it would be more helpful if the partition table test cases say
    (in their comments) a lot more about the steps they are doing, and
    what they expect the result to be. Sure, I can read all the code to
    figure it out for each case, but it is better to know the test
    intentions/expectations then verify they are doing the right thing.
    
    ~~~
    
    9.
    + CREATE TABLE sch1.t1(a int) PARTITION BY RANGE(a);
    + CREATE TABLE sch1.part1 PARTITION OF sch1.t1 FOR VALUES FROM (0) TO (5);
    
    Maybe create this table to have *multiple* partitions. It might be
    interesting later to see what happens when you try to EXCEPT only one
    of the partitions.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  147. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-24T07:32:39Z

    On Fri, Nov 21, 2025 at 5:55 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Here are some review comments for your patch v28-0003 (EXCEPT TABLE ...).
    >
    > The review of this patch is a WIP. In this post I only looked at the test code.
    >
    
    Here are my remaining review comments for patch v28-0003 (EXCEPT TABLE ...).
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    1.
    -ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    ADD ALL TABLES
    +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    ADD ALL TABLES [ EXCEPT [ TABLE ] ( <replaceable
    class="parameter">table_exception_object</replaceable> [, ... ] ) ]
    
    Why is that optional [TABLE] keyword needed?
    
    I know PostGres commands sometimes have "noise" words in the syntax so
    the command can be more English-like, but in this case, the
    publication is a FOR ALL *TABLES* anyway, so I am not sure what the
    benefit is of the user being able to say TABLE a 2nd time?
    
    ======
    src/backend/catalog/pg_publication.c
    
    2.
    + /*
    + * Check for partitions of partitioned table which are specified with
    + * EXCEPT clause and partitioned table is published with
    + * publish_via_partition_root = true.
    + */
    
    I think you can just say "partitions" or "table partitions", but
    "partitions of [a] partitioned table" seems overkill.
    
    Also, "... and partitioned table is published with
    publish_via_partition_root = true." seems too wordy. Isn't that just
    the same as "... and publish_via_partition_root = true"
    
    SUGGESTION
    Check for when the publication says "EXCEPT TABLE (partition)" but
    publish_via_partition_root = true.
    
    ~~~
    
    3.
    -/* Gets list of publication oids for a relation */
    +/* Gets list of publication oids for a relation that matches the except_flag */
     List *
    -GetRelationPublications(Oid relid)
    +GetRelationPublications(Oid relid, bool except_flag)
     {
      List    *result = NIL;
      CatCList   *pubrellist;
    @@ -765,7 +791,8 @@ GetRelationPublications(Oid relid)
      HeapTuple tup = &pubrellist->members[i]->tuple;
      Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid;
    
    - result = lappend_oid(result, pubid);
    + if (except_flag == ((Form_pg_publication_rel) GETSTRUCT(tup))->prexcept)
    + result = lappend_oid(result, pubid);
      }
    
    I was wondering if it might be better to return 2 lists from this
    function (e.g. an included-list, and an excluded-list) instead of
    passing the 'except_flag' like the current code. IIUC, you are mostly
    calling this function twice to get 2 lists anyway, but returning 2
    lists instead of 1, this function might be more efficient since it
    will only process the publication loop once.
    
    ~~~
    
    4.
    /*
     * Gets list of relation oids for a publication that matches the except_flag.
     *
     * This should only be used FOR TABLE publications, the FOR ALL TABLES/SEQUENCES
     * should use GetAllPublicationRelations().
     */
    List *
    GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt,
    bool except_flag)
    Something doesn't seem right -- the function comment says we shouldn't
    be calling the function for FOR ALL TABLES, but meanwhile, EXCEPT
    TABLE is currently only implemented via FOR ALL TABLES. So it feels
    contradictory. Maybe it is just the comment that needs updating?
    
    ~~~
    
    5.
    /*
     * Gets list of relation oids for a publication that matches the except_flag.
     *
     * This should only be used FOR TABLE publications, the FOR ALL TABLES/SEQUENCES
     * should use GetAllPublicationRelations().
     */
    List *
    GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt,
    bool except_flag)
    {
    List    *result;
    Relation pubrelsrel;
    ScanKeyData scankey;
    SysScanDesc scan;
    HeapTuple tup;
    
    /* Find all publications associated with the relation. */
    pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
    
    Existing bug? Isn't this a bogus comment?
    /* Find all publications associated with the relation. */
    
    Was that meant to be the other way around? -- e.g. Find all the
    relations associated with the specified publication.
    
    ======
    src/backend/commands/publicationcmds.c
    
    6.
    + default:
    + /* shouldn't happen */
    + elog(ERROR, "invalid publication object type %d",
    + puballobj->pubobjtype);
    + break;
    
    I think the ERROR is enough of a clue that it shouldn't happen. I felt
    the comment was redundant.
    
    ~~~
    
    ObjectsInPublicationToOids:
    
    7.
      case PUBLICATIONOBJ_TABLE:
    + pubobj->pubtable->except = false;
    + *rels = lappend(*rels, pubobj->pubtable);
    + break;
    + case PUBLICATIONOBJ_EXCEPT_TABLE:
    + pubobj->pubtable->except = true;
      *rels = lappend(*rels, pubobj->pubtable);
      break;
    Those are very similar. How about combining like below?
    
    case PUBLICATIONOBJ_TABLE:
    case PUBLICATIONOBJ_EXCEPT_TABLE:
      pubobj->pubtable->except = (pubobj->pubobjtype ==
    PUBLICATIONOBJ_EXCEPT_TABLE);
      *rels = lappend(*rels, pubobj->pubtable);
      break;
    
    ~~
    
    pub_contains_invalid_column:
    
    8.
     pub_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
      bool pubviaroot, char pubgencols_type,
    - bool *invalid_column_list,
    + bool puballtables, bool *invalid_column_list,
      bool *invalid_gen_col)
    
    The 'pub_via_root' and 'pubgencols_type' are parameters. Somehow it
    seems more natural for the 'puballtables' to be passed before those,
    because FOR ALL TABLES comes before WITH in the syntax.
    
    ~~~
    
    CreatePublication:
    
    9.
      else if (!stmt->for_all_sequences)
    - {
      ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
         &schemaidlist);
    
    AFAICT, this function is refactored a lot because of the removal of
    that '{'. It looks like mostly whitespace, but really, I think the
    logic is quite different. I wasn't sure what that was about. Is it
    related to this patch, or some other bugfix in passing or what?
    
    ======
    src/backend/commands/tablecmds.c
    
    ATPrepChangePersistence:
    
    10.
    - GetRelationPublications(RelationGetRelid(rel)) != NIL)
    + list_length(GetRelationPublications(RelationGetRelid(rel), false)) > 0)
    
    Isn't an empty List the same as a NIL list? Maybe that list_length()
    change was not really needed.
    
    ======
    src/backend/parser/gram.y
    
    11.
      drop_option_list pub_obj_list pub_all_obj_type_list
    + except_pub_obj_list opt_except_clause
    
    Is this name consistent with the others? Should it be pub_except_obj_list?
    
    ~~~
    
    12.
     %type <publicationobjectspec> PublicationObjSpec
    +%type <publicationobjectspec> ExceptPublicationObjSpec
     %type <publicationallobjectspec> PublicationAllObjSpec
    
    Is this name consistent with the others? Should it be PublicationExceptObjSpec?
    
    ~~~
    
    CreatePublicationStmt:
    
    13.
      n->pubname = $3;
    + n->pubobjects = $5;
    
    I noticed that sometimes there is a cast (List *) and other times
    there is not. e.g. none here, but cast in AlterPublicationStmt. Why
    the differences?
    
    ~~~
    
    PublicationObjSpec:
    
    14.
    The comment for 'PublicationObjSpec' says "FOR TABLE and FOR TABLES IN
    SCHEMA specifications". If that comment is correct, then why is this
    patch changing this code? OTOH, if the code is correct, then does the
    comment need updating?
    
    ======
    src/bin/pg_dump/pg_dump.c
    
    15.
    Shouldn't there have already been some ALTER ... ADD ALL TABLE dump
    code and test code implemented back in patch 0002?
    
    ~~~
    
    dumpPublication:
    
    16.
      else if (pubinfo->puballtables)
    + {
    + SimplePtrListCell *cell;
    +
      appendPQExpBufferStr(query, " FOR ALL TABLES");
    +
    + /* Include exception tables if the publication has except tables */
    + for (cell = exceptinfo.head; cell; cell = cell->next)
    + {
    + PublicationRelInfo *pubrinfo = (PublicationRelInfo *) cell->ptr;
    + TableInfo  *tbinfo;
    +
    + if (pubinfo == pubrinfo->publication)
    + {
    + tbinfo = pubrinfo->pubtable;
    +
    + if (first)
    + {
    + appendPQExpBufferStr(query, " EXCEPT TABLE (");
    + first = false;
    + }
    + else
    + appendPQExpBufferStr(query, ", ");
    + appendPQExpBuffer(query, "ONLY %s", fmtQualifiedDumpable(tbinfo));
    + }
    + }
    + if (!first)
    + appendPQExpBufferStr(query, ")");
    + }
    
    16a.
    SimplePtrListCell *cell can be declared as a for-loop variable.
    
    ~
    
    16b.
    The comment should say "EXCEPT TABLES" in uppercase.
    
    ~
    
    16c.
    I am not convinced you can use that 'first' flag like you are doing.
    Isn't that interfering with the existing usage of that flag? Perhaps
    another boolean just for this EXCEPT loop is needed.
    
    ~~~
    
    getPublicationTables:
    
    17.
    + if (strcmp(prexcept, "f") == 0)
    + pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
    + else
    + pubrinfo[j].dobj.objType = DO_PUBLICATION_EXCEPT_REL;
    +
    
    ...
    
    + if (strcmp(prexcept, "t") == 0)
    + simple_ptr_list_append(&exceptinfo, &pubrinfo[j]);
    +
    
    Here you are comparing the same 'prexcept' flag for both "f" and "t".
    
    I felt it was better if both comparisons are the same (e.g. both "t").
    
    Or better still, assign a new boolean and avoid that 2nd strcmp
    entirely -- e.g. except_flag = (strcmp(prexcept, "t") == 0);
    
    ======
    src/bin/pg_dump/pg_dump_sort.c
    
    DOTypeNameCompare:
    
    18.
    + else if (obj1->objType == DO_PUBLICATION_EXCEPT_REL)
    + {
    + PublicationRelInfo *probj1 = *(PublicationRelInfo *const *) p1;
    + PublicationRelInfo *probj2 = *(PublicationRelInfo *const *) p2;
    +
    + /* Sort by publication name, since (namespace, name) match the rel */
    + cmpval = strcmp(probj1->publication->dobj.name,
    + probj2->publication->dobj.name);
    + if (cmpval != 0)
    + return cmpval;
    + }
    
    Isn't this identical to the previous code block? So can't you just add
     DO_PUBLICATION_EXCEPT_REL to that condition?
    
    ======
    src/bin/pg_dump/t/002_pg_dump.pl
    
    19.
    Missing test cases for ALTER? But also.
    
    ~~~
    
    20.
    Missing test cases for EXCEPT for INHERITED tables?
    
    ======
    src/bin/psql/describe.c
    
    describeOneTableDetails:
    
    21.
    I was wondering if the "describe" for tables (e.g. \d+) should also
    show the publications where the table is an ECEPT TABLE? How else is
    the user going to know it has been excluded by some publication?
    
    ======
    src/bin/psql/tab-complete.in.c
    
    ALTER PUBLICATION:
    
    22.
    The tab completion does not seem as good as it could be. e.g, there is
    missing '(' and the for EXCEPT TABLE
    
    ~~~
    
    CREATE PUBLICATION:
    
    23.
    The tab completion does not seem as good as it could be. e.g, there is
    missing '(' and the for EXCEPT TABLE
    
    ======
    src/test/regress/sql/publication.sql
    
    24.
    +\dRp+ testpub_foralltables_excepttable
    +\dRp+ testpub_foralltables_excepttable1
    
    As well as doing the "describes" for the publication, I think we need
    to see the test cases for the describes of those excluded tables. e.g.
    I imagine that they should also list the publications that they are
    *excluded* from, right?
    
    ~~~
    
    25.
    +CREATE PUBLICATION testpub5 FOR ALL TABLES EXCEPT TABLE (testpub_tbl3);
    +CREATE PUBLICATION testpub6 FOR ALL TABLES EXCEPT TABLE (ONLY testpub_tbl3);
    
    25a.
    Needs some explanatory comments here saying these are for testing the
    EXCEPT with inherited tables (e.g. ONLY versus not).
    
    ~
    
    25b.
    I think you should be testing the '*' syntax here too.
    
    ~~~
    
    26.
    +CREATE TABLE pub_sch1.tbl2 (a int);
     SET client_min_messages = 'ERROR';
     CREATE PUBLICATION testpub_reset FOR ALL TABLES, ALL SEQUENCES;
     RESET client_min_messages;
    @@ -1344,9 +1358,15 @@ ALTER PUBLICATION testpub_reset ADD ALL TABLES;
    
     -- Can't add ALL TABLES to 'ALL TABLES' publication
     ALTER PUBLICATION testpub_reset ADD ALL TABLES;
    +ALTER PUBLICATION testpub_reset RESET;
    +
    +-- Verify adding EXCEPT TABLE
    +ALTER PUBLICATION testpub_reset ADD ALL TABLES EXCEPT TABLE
    (pub_sch1.tbl1, pub_sch1.tbl2);
    +\dRp+ testpub_reset
    
     DROP PUBLICATION testpub_reset;
     DROP TABLE pub_sch1.tbl1;
    +DROP TABLE pub_sch1.tbl2;
     DROP SCHEMA pub_sch1;
    
    It looks like that added CREATE TABLE (and RESET?) belongs more
    appropriately within the scope of the new test "Verify adding EXCEPT
    TABLE".
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  148. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-11-24T21:41:05Z

    Hi Shlok.
    
    I checked the latest v28-0004 "EXCEPT (col-list)" patch. I have no
    code review comments, but I do have one syntax question.
    
    ======
    
    The result of the current patch proposed syntax is like:
    
    CREATE PUBLICATION pubname FOR TABLE tabname EXCEPT (col1, col2, col3)
    ALTER PUBLICATION pubname ADD TABLE tabname EXCEPT (col1, col2, col3)
    
    ~~
    
    In the previous patch v28-0003 (FOR ALL TABLES EXCEPT [TABLE]), I
    thought the optional noise-word TABLE did not have any user benefit
    because TABLE was already obvious.
    
    OTOH, here in patch v28-0004, it might be helpful to have an
    *optional* [COLUMN] part. e.g., I felt  "EXCEPT [COLUMN]" would
    improve the readability of these commands.
    
    Compare:
    CREATE PUBLICATION pubname FOR TABLE tabname EXCEPT (col1, col2, col3)
    CREATE PUBLICATION pubname FOR TABLE tabname EXCEPT COLUMN (col1, col2, col3)
    
    Compare:
    ALTER PUBLICATION pubname ADD TABLE tabname EXCEPT (col1, col2, col3)
    ALTER PUBLICATION pubname ADD TABLE tabname EXCEPT COLUMN (col1, col2, col3)
    
    
    This is similar to the optional "[COLUMN]" keyword used here [1].
    
    Thoughts?
    
    ======
    [1] https://www.postgresql.org/docs/devel/sql-altertable.html
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  149. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-12-03T03:26:01Z

    On Wed, Nov 19, 2025 at 3:34 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    >
    > I have also addressed the comments in [1], [2].
    >
    > [1]: https://www.postgresql.org/message-id/CAHut%2BPtRzCD4-0894cutkU_h8cPNtosN0_oSHn2iAKEfg2ENOQ%40mail.gmail.com
    > [2]: https://www.postgresql.org/message-id/CAHut+PuHn-hohA4OdEJz+Zfukfr41TvMTeTH7NwJ=wg1+94uNA@mail.gmail.com
    >
    
    Thanks for the patch. Please find a few comments on 001:
    
    1)
    + bool nulls[Natts_pg_publication];
    + bool replaces[Natts_pg_publication];
    + Datum values[Natts_pg_publication];
    
    + memset(values, 0, sizeof(values));
    + memset(nulls, false, sizeof(nulls));
    + memset(replaces, false, sizeof(replaces));
    
    Can we initialize all 3 like below? Then we do not need memset.
    bool nulls[Natts_pg_publication] = {0};
    
    2)
    AlterPublicationReset():
    Can we reset the columns in same sequence as they are originally
    defined (see pg_publication_d.h), it makes it easy to map when
    comparing/reviewing that we do not have missed any.
    
    3)
    +/* default values for flags and publication parameters */
    ...
    +#define PUB_DEFAULT_VIA_ROOT false
    +#define PUB_DEFAULT_ALL_TABLES false
    +#define PUB_DEFAULT_ALL_SEQUENCES false
    +#define PUB_DEFAULT_GENCOLS PUBLISH_GENCOLS_NONE
    
    These too we can rearrange as per order in pg_publication_d.h
    
    4)
    + COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "RESET", "SET");
    
    Is it supposed to be in alphabetical order? Looking at others, I do
    not think so. If not, then I feel 'SET' first followed by  'RESET'
    seems a more obvious choice to me. See similar (ENABLE followed by
    DISABLE):
    COMPLETE_WITH("CONNECTION", "ENABLE", "DISABLE", "OWNER TO",
    
    5)
    +DROP TABLE pub_sch1.tbl1;
    +DROP SCHEMA pub_sch1;
    
    We can use 'DROP SCHEMA pub_sch1 CASCADE'. Then we need not to worry
    about dropping the associated table(s) (even if we create more in
    future in this schema).
    
    thanks
    Shveta
    
    
    
    
  150. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-12-04T04:41:44Z

    On Wed, Dec 3, 2025 at 8:56 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Nov 19, 2025 at 3:34 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > >
    > > I have also addressed the comments in [1], [2].
    > >
    > > [1]: https://www.postgresql.org/message-id/CAHut%2BPtRzCD4-0894cutkU_h8cPNtosN0_oSHn2iAKEfg2ENOQ%40mail.gmail.com
    > > [2]: https://www.postgresql.org/message-id/CAHut+PuHn-hohA4OdEJz+Zfukfr41TvMTeTH7NwJ=wg1+94uNA@mail.gmail.com
    > >
    >
    > Thanks for the patch. Please find a few comments on 001:
    >
    > 1)
    > + bool nulls[Natts_pg_publication];
    > + bool replaces[Natts_pg_publication];
    > + Datum values[Natts_pg_publication];
    >
    > + memset(values, 0, sizeof(values));
    > + memset(nulls, false, sizeof(nulls));
    > + memset(replaces, false, sizeof(replaces));
    >
    > Can we initialize all 3 like below? Then we do not need memset.
    > bool nulls[Natts_pg_publication] = {0};
    >
    > 2)
    > AlterPublicationReset():
    > Can we reset the columns in same sequence as they are originally
    > defined (see pg_publication_d.h), it makes it easy to map when
    > comparing/reviewing that we do not have missed any.
    >
    > 3)
    > +/* default values for flags and publication parameters */
    > ...
    > +#define PUB_DEFAULT_VIA_ROOT false
    > +#define PUB_DEFAULT_ALL_TABLES false
    > +#define PUB_DEFAULT_ALL_SEQUENCES false
    > +#define PUB_DEFAULT_GENCOLS PUBLISH_GENCOLS_NONE
    >
    > These too we can rearrange as per order in pg_publication_d.h
    >
    > 4)
    > + COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "RESET", "SET");
    >
    > Is it supposed to be in alphabetical order? Looking at others, I do
    > not think so. If not, then I feel 'SET' first followed by  'RESET'
    > seems a more obvious choice to me. See similar (ENABLE followed by
    > DISABLE):
    > COMPLETE_WITH("CONNECTION", "ENABLE", "DISABLE", "OWNER TO",
    >
    > 5)
    > +DROP TABLE pub_sch1.tbl1;
    > +DROP SCHEMA pub_sch1;
    >
    > We can use 'DROP SCHEMA pub_sch1 CASCADE'. Then we need not to worry
    > about dropping the associated table(s) (even if we create more in
    > future in this schema).
    >
    
    6)
    Just before AlterPublicationReset-->LockSchemaList does
    SearchSysCacheExists1(), I dropped the schema concurrently, which
    resulted in below error:
    
    postgres=# alter publication pub1 reset;
    ERROR:  schema with OID 16393 does not exist
    
    I do not think the user should be seeing this error. For
    CreatePublication and AlterPublicationSchemas, it makes sense to give
    an error when it calls LockSchemaList as user has given the schema
    names himself. But here since schemas are internally fetched, I think
    it will be logical to skip the
    error if it gets dropped concurrently. Thoughts?
    
    If we plan to skip error, we can do so by passing the flag
    missing_okay=true. See RangeVarGetRelid and other such functions using
    such a flag.
    
    thanks
    Shveta
    
    
    
    
  151. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-12-04T11:50:54Z

    On Thu, 20 Nov 2025 at 11:54, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Thanks for splitting the patches.
    >
    > Here are some review comments for the new patch v28-0002 (ADD ALL TABLES).
    >
    > ======
    > Commit Message
    >
    > 1.
    > This patch adds support for using ADD ALL TABLES in ALTER PUBLICATION,
    > allowing an existing publication to be changed into an ALL TABLES
    > publication. This command is permitted only when the publication is
    > in its default state, meaning it has no tables or schemas added, its
    > ALL TABLES and ALL SEQUENCES flags are not set, and publication
    > options such as publish_via_root_partition, publish_generated_columns,
    > and publish are at their default values.
    >
    > ~
    >
    > IMO, the restrictions for this new command are too severe:
    >
    > e.g. If I already have a FOR ALL SEQUENCES publication, then I
    > expected it should be possible to ADD ALL TABLES to that as well,
    > right?
    >
    > Likewise, why are we enforcing that the publication parameters must be
    > defaults? IOW, why is (i) below disallowed, but (ii) is allowed?
    >
    > (i)
    > ALTER PUBLICATION pub SET (publish_generated_columns=stored);
    > ALTER PUBLICATION pub ADD ALL TABLES;
    >
    > (ii)
    > ALTER PUBLICATION pub ADD ALL TABLES;
    > ALTER PUBLICATION pub SET (publish_generated_columns=stored);
    >
    I agree that the current restrictions were too strict. With the latest
    patch we avoid adding ALL TABLES only when we have an existing list of
    tables or schemas in a publication.
    
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > Description:
    >
    > 2.
    > The "Description" part of this page is confusing because it was
    > referring to "The first three variants" and later "The fourth
    > variant".  Now that the "ADD ALL TABLES" variant has been added, I
    > have lost track of what "variants" this description is talking about.
    > Those words should be replaced by something clearer. This could be an
    > ongoing issue if it is not worded differently because the same problem
    > will happen again, e.g. when more syntax gets added for ALL SEQUENCES,
    > etc.
    >
    > ~~~
    >
    I have updated the description to avoid the wording "The first three
    variants". Instead I have added a list to describe each command
    separately. Similar to ALTER TABLE [1].
    
    > 3.
    > Note also that DROP TABLES IN SCHEMA will not drop any schema tables
    > that were specified using FOR TABLE/ ADD TABLE.
    >
    > ~
    >
    > That sentence (above) is from the docs. Does that also need updating
    > now that there is ADD ALL TABLES?
    >
    When we create a publication on a schema, we can also add specific
    tables using FOR TABLE/ADD TABLE.
    But in case of ALL TABLES publication we are not allowed to include
    tables using FOR TABLE/ADD TABLE.
    
    So for ALL TABLES case this wording is not required.
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > CheckPublicationDefValues:
    >
    > 4.
    > Is this function needed?
    >
    It is not needed. Modified the function to give proper error messages
    for each case.
    
    > ~~~
    >
    > AlterPublication:
    >
    > 5.
    > + if (stmt->for_all_tables)
    > + {
    > + bool isdefault = CheckPublicationDefValues(tup);
    > +
    > + if (!isdefault)
    > + ereport(ERROR,
    > + errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    > + errmsg("adding ALL TABLES requires the publication to have default
    > publication parameter values"),
    > + errdetail("ALL TABLES or ALL SEQUENCES flag should not be set and no
    > tables/schemas should be associated."),
    > + errhint("Use ALTER PUBLICATION ... RESET to reset the publication"));
    > +
    > + AlterPublicationSetAllTables(rel, tup);
    > + }
    > +
    >
    > Why do we need this self-imposed restriction?
    >
    See reply to comment 1.
    
    > ======
    > src/include/nodes/parsenodes.h
    >
    > 6.
    >   List    *pubobjects; /* Optional list of publication objects */
    > + bool for_all_tables; /* Special publication for all tables in db */
    >   AlterPublicationAction action; /* What action to perform with the given
    >   * objects */
    >  } AlterPublicationStmt;
    >
    >
    > There is no such "FOR" syntax like ALTER PUBLICATION ... FOR ALL
    > TABLES, so I felt just 'puballtables' might be a better member name.
    >
    We have the same variable name in CreatePublicationStmt. I feel
    keeping the name as 'for_all_tables' will keep it consistent and
    easier to understand.
    
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 7.
    > Don't uppercase any of the publication parameters because they never
    > appear in the docs/examples like that.
    >
    > ~
    >
    > 8.
    > So that the last command is the one being tested, I felt that all the
    > test cases should be doing RESET *first* instead of last.
    >
    > ~~~
    >
    > 9.
    > You don't always need to use RESET. There should also be some tests
    > using an "empty" publication just to be sure it works. e.g
    >
    > CREATE PUBLICATION pub_empty;
    > ALTER PUBLICATION pub_empty ADD ALL TABLES;
    >
    > ~~~
    >
    > 10.
    > As commented earlier, I felt the rules were too restrictive. So I
    > think some test cases can be removed.
    >
    > ~~~
    >
    > 11.
    > +-- Tests for ALTER PUBLICATION ... ADD ALL TABLES
    >
    > ~
    >
    > I noticed there is a "--
    > ======================================================" separator
    > between the major groups of tests.
    >
    > 11a. Should use this separator in patch 0001 for the RESET group of tests
    >
    > 11b. Should use this separator in patch 0002 for the ADD ALL TABLES
    > groups of tests
    >
    > ~~~
    >
    > 12.
    > +-- Can't add ALL TABLES to 'ALL TABLES' publication
    > +ALTER PUBLICATION testpub_reset ADD ALL TABLES;
    > +
    >
    > This test case seems to belong earlier, near the 'FOR TABLE' and the
    > 'TABLES IN SCHEMA' tests.
    >
    I saw the patch needed a rebase. I have rebased it.
    I have also addressed the remaining comments in this email and
    comments in the email [2].
    
    While addressing the comments I saw there were a couple of race
    conditions when we run 'ALTER PUBLICATION ... RESET and ALTER
    PUBLICATION ... ADD TABLE concurrently' and
     'ALTER PUBLICATION ... ADD ALL TABLES and ALTER PUBLICATION ... ADD
    TABLE concurrently'
    I have addressed these in the v29 patch.
    Will address comments for 0003 and 0004 patch by Peter and comments by
    Shveta in next version.
    
    [1]: https://www.postgresql.org/docs/current/sql-altertable.html
    [2]: https://www.postgresql.org/message-id/CAHut%2BPv4d9EAjDQiOHiu2BrYP3ZA-oJgsgGZdygBaZnWDR7sDA%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  152. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-12-08T05:46:56Z

    Hi Shlok. Some review comments for v29-0001 (ALTER PUBLICATION RESET)
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    Description:
    
    1.
       <para>
        The command <command>ALTER PUBLICATION</command> can change the attributes
    -   of a publication.
    -  </para>
    +   of a publication. There are several subforms described below.
    
    IMO, you don't need to say that sentence "There are several subforms
    described below," because it is obvious from the context.
    
    (I guess you copied it from ALTER TABLE, but that doesn't change my opinion)
    
    ~~~
    
    <General>
    
    2.
    The new description list entries all say:
    
    "This form adds..."
    "This form replaces..."
    "This for removes..."
    etc.
    
    IMO, the words "This form" are all unnecessary here. Instead, just say
    "Adds...", "Replaces...", "Removes...", etc.
    
    (I guess you copied it from ALTER TABLE, but that doesn't change my opinion)
    
    ~~~
    
    DROP:
    
    3.
    +   <varlistentry>
    +    <term><literal>DROP <replaceable
    class="parameter">publication_object</replaceable> [,
    ...]</literal></term>
    +    <listitem>
    
    The replacement name here should be "publication_drop_object".
    
    ~~~
    
    SET:
    
    4.
    +     <para>
    +      This form can change all of the publication properties specified in
    +      <xref linkend="sql-createpublication"/>. Properties not mentioned in the
    +      command retain their previous settings. It is not applicable to
    +      sequences.
    +     </para>
    
    For "can change all of"; maybe that should be "can change any of".
    Also, for "It is not applicable." -- saying "it" seemed awkward.
    
    IMO the current master text (shown below) seemed OK as-is; At least I
    thought it was better than the patch replacement text:
    This clause alters publication parameters originally set by CREATE
    PUBLICATION. See there for more information. This clause is not
    applicable to sequences.
    
    ~~~
    
    5.
    +      <para>
    +       This problem can be avoided by refraining from modifying partition leaf
    +       tables after the <command>ALTER PUBLICATION ... SET</command> until the
    +       <link linkend="sql-altersubscription"><command>ALTER
    SUBSCRIPTION ... REFRESH PUBLICATION</command></link>
    +       is executed and by only refreshing using the
    <literal>copy_data = off</literal>
    +       option.
    +      </para>
    
    This link would be better if it referenced the actual REFRESH
    PUBLICATION. Currently, it just goes to the top of the ALTER
    SUBSCRIPTION page.
    
    ~~~
    
    6.
       <para>
        You must own the publication to use <command>ALTER PUBLICATION</command>.
        Adding a table to a publication additionally requires owning that table.
    -   The <literal>ADD TABLES IN SCHEMA</literal> and
    -   <literal>SET TABLES IN SCHEMA</literal> to a publication requires the
    -   invoking user to be a superuser.
    -   To alter the owner, you must be able to <literal>SET ROLE</literal> to the
    -   new owning role, and that role must have <literal>CREATE</literal>
    -   privilege on the database.
    +   The <literal>ADD TABLES IN SCHEMA</literal>,
    +   <literal>SET TABLES IN SCHEMA</literal> to a publication and
    +   <literal>RESET</literal> of publication requires the invoking user to be a
    +   superuser. To alter the owner, you must be able to
    +   <literal>SET ROLE</literal> to the new owning role, and that role must have
    +   <literal>CREATE</literal> privilege on the database.
        Also, the new owner of a
        <link linkend="sql-createpublication-params-for-tables-in-schema"><literal>FOR
    TABLES IN SCHEMA</literal></link>
        or <link linkend="sql-createpublication-params-for-all-tables"><literal>FOR
    ALL TABLES</literal></link>
    
    
    I felt that those sentences:
    "To alter the owner, you must ...", and
    "Also, the new owner of a ..."
    really belonged in the ALTER OWNER TO description part.
    
    src/test/regress/sql/publication.sql
    ======
    
    7.
    +ALTER PUBLICATION testpub_reset SET (publish_via_partition_root = 'true');
    
    It's a bit strange to say 'true' in quotes. Indeed, you shouldn't need
    to give any value here -- just say "SET (publish_via_partition_root)"
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  153. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-12-08T11:57:27Z

    On Thu, Dec 4, 2025 at 5:21 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > I have addressed these in the v29 patch.
    > Will address comments for 0003 and 0004 patch by Peter and comments by
    > Shveta in next version.
    >
    
    Thanks for the patch.
    
    I believe patch 003 (EXCEPT table) and 004 (EXCEPT column_list) should
    be the primary focus. The other patches, RESET and ADD ALL tables are
    additional features and may or may not be necessary depending on
    further input.
    
    I suggest making 003 and 004 as 001 and 002 respectively, and
    prioritizing these first.  The RESET and ADD ALL tables patches can
    become 003 and 004, and we can review them after the first 2 patches
    are fully evaluated. The documentation formatting changes for ALTER
    PUBLICATION are not required in this patch and, if needed, can be
    handled separately.
    
    thanks
    Shveta
    
    
    
    
  154. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2025-12-08T12:14:19Z

    On Mon, Dec 8, 2025 at 5:27 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Thu, Dec 4, 2025 at 5:21 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > I have addressed these in the v29 patch.
    > > Will address comments for 0003 and 0004 patch by Peter and comments by
    > > Shveta in next version.
    > >
    >
    > Thanks for the patch.
    >
    > I believe patch 003 (EXCEPT table) and 004 (EXCEPT column_list) should
    > be the primary focus.
    >
    
    +1. We should first try to make 0003 RFC before going further.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  155. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-12-09T17:46:59Z

    On Mon, 8 Dec 2025 at 17:44, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Mon, Dec 8, 2025 at 5:27 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Thu, Dec 4, 2025 at 5:21 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > I have addressed these in the v29 patch.
    > > > Will address comments for 0003 and 0004 patch by Peter and comments by
    > > > Shveta in next version.
    > > >
    > >
    > > Thanks for the patch.
    > >
    > > I believe patch 003 (EXCEPT table) and 004 (EXCEPT column_list) should
    > > be the primary focus.
    > >
    >
    > +1. We should first try to make 0003 RFC before going further.
    >
    I have removed the 0001 0002 and 0004 patches for now. Will post them
    once 0003 patch is RFC.
    Here is the update patch for "EXCEPT TABLE".
    
    Thanks,
    Shlok Kyal
    
  156. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-12-09T17:48:48Z

    On Mon, 24 Nov 2025 at 13:03, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Fri, Nov 21, 2025 at 5:55 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Hi Shlok.
    > >
    > > Here are some review comments for your patch v28-0003 (EXCEPT TABLE ...).
    > >
    > > The review of this patch is a WIP. In this post I only looked at the test code.
    > >
    >
    > Here are my remaining review comments for patch v28-0003 (EXCEPT TABLE ...).
    >
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 1.
    > -ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > ADD ALL TABLES
    > +ALTER PUBLICATION <replaceable class="parameter">name</replaceable>
    > ADD ALL TABLES [ EXCEPT [ TABLE ] ( <replaceable
    > class="parameter">table_exception_object</replaceable> [, ... ] ) ]
    >
    > Why is that optional [TABLE] keyword needed?
    >
    > I know PostGres commands sometimes have "noise" words in the syntax so
    > the command can be more English-like, but in this case, the
    > publication is a FOR ALL *TABLES* anyway, so I am not sure what the
    > benefit is of the user being able to say TABLE a 2nd time?
    >
    I think this feature can be extended to EXCEPT SCHEMA etc. So I think
    it is necessary for clarity.
    There is already a discussion [1].
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > 2.
    > + /*
    > + * Check for partitions of partitioned table which are specified with
    > + * EXCEPT clause and partitioned table is published with
    > + * publish_via_partition_root = true.
    > + */
    >
    > I think you can just say "partitions" or "table partitions", but
    > "partitions of [a] partitioned table" seems overkill.
    >
    > Also, "... and partitioned table is published with
    > publish_via_partition_root = true." seems too wordy. Isn't that just
    > the same as "... and publish_via_partition_root = true"
    >
    > SUGGESTION
    > Check for when the publication says "EXCEPT TABLE (partition)" but
    > publish_via_partition_root = true.
    >
    > ~~~
    >
    Modified
    
    > 3.
    > -/* Gets list of publication oids for a relation */
    > +/* Gets list of publication oids for a relation that matches the except_flag */
    >  List *
    > -GetRelationPublications(Oid relid)
    > +GetRelationPublications(Oid relid, bool except_flag)
    >  {
    >   List    *result = NIL;
    >   CatCList   *pubrellist;
    > @@ -765,7 +791,8 @@ GetRelationPublications(Oid relid)
    >   HeapTuple tup = &pubrellist->members[i]->tuple;
    >   Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid;
    >
    > - result = lappend_oid(result, pubid);
    > + if (except_flag == ((Form_pg_publication_rel) GETSTRUCT(tup))->prexcept)
    > + result = lappend_oid(result, pubid);
    >   }
    >
    > I was wondering if it might be better to return 2 lists from this
    > function (e.g. an included-list, and an excluded-list) instead of
    > passing the 'except_flag' like the current code. IIUC, you are mostly
    > calling this function twice to get 2 lists anyway, but returning 2
    > lists instead of 1, this function might be more efficient since it
    > will only process the publication loop once.
    >
    Modified
    
    > ~~~
    >
    > 4.
    > /*
    >  * Gets list of relation oids for a publication that matches the except_flag.
    >  *
    >  * This should only be used FOR TABLE publications, the FOR ALL TABLES/SEQUENCES
    >  * should use GetAllPublicationRelations().
    >  */
    > List *
    > GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt,
    > bool except_flag)
    > Something doesn't seem right -- the function comment says we shouldn't
    > be calling the function for FOR ALL TABLES, but meanwhile, EXCEPT
    > TABLE is currently only implemented via FOR ALL TABLES. So it feels
    > contradictory. Maybe it is just the comment that needs updating?
    >
    I thought more about this function and found that we can remove the
    'except_flag' variable.
    
    Since we can only use EXCEPT TABLE clause for ALL TABLES publication
    and we cannot use FOR TABLE clause with ALL TABLES.
    If for ALL TABLES publication we call this function, we will return an
    except table list.
    Else we will return a list of table to be included in publication.
    
    I have added a comment to this behaviour.
    
    > ~~~
    >
    > 5.
    > /*
    >  * Gets list of relation oids for a publication that matches the except_flag.
    >  *
    >  * This should only be used FOR TABLE publications, the FOR ALL TABLES/SEQUENCES
    >  * should use GetAllPublicationRelations().
    >  */
    > List *
    > GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt,
    > bool except_flag)
    > {
    > List    *result;
    > Relation pubrelsrel;
    > ScanKeyData scankey;
    > SysScanDesc scan;
    > HeapTuple tup;
    >
    > /* Find all publications associated with the relation. */
    > pubrelsrel = table_open(PublicationRelRelationId, AccessShareLock);
    >
    > Existing bug? Isn't this a bogus comment?
    > /* Find all publications associated with the relation. */
    >
    > Was that meant to be the other way around? -- e.g. Find all the
    > relations associated with the specified publication.
    >
    I think you are correct. I will create a separate thread for this change.
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > 6.
    > + default:
    > + /* shouldn't happen */
    > + elog(ERROR, "invalid publication object type %d",
    > + puballobj->pubobjtype);
    > + break;
    >
    > I think the ERROR is enough of a clue that it shouldn't happen. I felt
    > the comment was redundant.
    >
    There are multiple similar occurrences. See functions
    'ObjectsInPublicationToOids', in publicationcmds.c.
    There are some occurrences in the openssl.c file as well. But I also
    think this comment is redundant.
    I have removed the comment.
    
    > ~~~
    >
    > ObjectsInPublicationToOids:
    >
    > 7.
    >   case PUBLICATIONOBJ_TABLE:
    > + pubobj->pubtable->except = false;
    > + *rels = lappend(*rels, pubobj->pubtable);
    > + break;
    > + case PUBLICATIONOBJ_EXCEPT_TABLE:
    > + pubobj->pubtable->except = true;
    >   *rels = lappend(*rels, pubobj->pubtable);
    >   break;
    > Those are very similar. How about combining like below?
    >
    > case PUBLICATIONOBJ_TABLE:
    > case PUBLICATIONOBJ_EXCEPT_TABLE:
    >   pubobj->pubtable->except = (pubobj->pubobjtype ==
    > PUBLICATIONOBJ_EXCEPT_TABLE);
    >   *rels = lappend(*rels, pubobj->pubtable);
    >   break;
    >
    Modified
    
    > ~~
    >
    > pub_contains_invalid_column:
    >
    > 8.
    >  pub_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
    >   bool pubviaroot, char pubgencols_type,
    > - bool *invalid_column_list,
    > + bool puballtables, bool *invalid_column_list,
    >   bool *invalid_gen_col)
    >
    > The 'pub_via_root' and 'pubgencols_type' are parameters. Somehow it
    > seems more natural for the 'puballtables' to be passed before those,
    > because FOR ALL TABLES comes before WITH in the syntax.
    >
    Modified
    
    > ~~~
    >
    > CreatePublication:
    >
    > 9.
    >   else if (!stmt->for_all_sequences)
    > - {
    >   ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
    >      &schemaidlist);
    >
    > AFAICT, this function is refactored a lot because of the removal of
    > that '{'. It looks like mostly whitespace, but really, I think the
    > logic is quite different. I wasn't sure what that was about. Is it
    > related to this patch, or some other bugfix in passing or what?
    >
    This change is part of this patch.
    This change is required to get a list of tables which are excluded
    (for ALL TABLES publication).
    
    I think the code related to schema should be inside the condition
    'else if (!stmt->for_all_sequences)'
    I have made the change for the same and also added a comment.
    
    > ======
    > src/backend/commands/tablecmds.c
    >
    > ATPrepChangePersistence:
    >
    > 10.
    > - GetRelationPublications(RelationGetRelid(rel)) != NIL)
    > + list_length(GetRelationPublications(RelationGetRelid(rel), false)) > 0)
    >
    > Isn't an empty List the same as a NIL list? Maybe that list_length()
    > change was not really needed.
    >
    Modified
    
    > ======
    > src/backend/parser/gram.y
    >
    > 11.
    >   drop_option_list pub_obj_list pub_all_obj_type_list
    > + except_pub_obj_list opt_except_clause
    >
    > Is this name consistent with the others? Should it be pub_except_obj_list?
    >
    I think pub_except_obj_list is consistent with others. Modified.
    
    > ~~~
    >
    > 12.
    >  %type <publicationobjectspec> PublicationObjSpec
    > +%type <publicationobjectspec> ExceptPublicationObjSpec
    >  %type <publicationallobjectspec> PublicationAllObjSpec
    >
    > Is this name consistent with the others? Should it be PublicationExceptObjSpec?
    >
    I agree. Modified.
    
    > ~~~
    >
    > CreatePublicationStmt:
    >
    > 13.
    >   n->pubname = $3;
    > + n->pubobjects = $5;
    >
    > I noticed that sometimes there is a cast (List *) and other times
    > there is not. e.g. none here, but cast in AlterPublicationStmt. Why
    > the differences?
    >
    This change is not required in the latest patch. Due to discussion in [2].
    
    > ~~~
    >
    > PublicationObjSpec:
    >
    > 14.
    > The comment for 'PublicationObjSpec' says "FOR TABLE and FOR TABLES IN
    > SCHEMA specifications". If that comment is correct, then why is this
    > patch changing this code? OTOH, if the code is correct, then does the
    > comment need updating?
    >
    We have only added "$$->location = @1;" for PublicationObjSpec.
    I define the location of the '^' indicator while throwing an error. I
    think we don't need to update comments for it?
    
    > ======
    > src/bin/pg_dump/pg_dump.c
    >
    > 15.
    > Shouldn't there have already been some ALTER ... ADD ALL TABLE dump
    > code and test code implemented back in patch 0002?
    >
    This change is not required in the latest patch. Due to discussion in [2].
    
    > ~~~
    >
    > dumpPublication:
    >
    > 16.
    >   else if (pubinfo->puballtables)
    > + {
    > + SimplePtrListCell *cell;
    > +
    >   appendPQExpBufferStr(query, " FOR ALL TABLES");
    > +
    > + /* Include exception tables if the publication has except tables */
    > + for (cell = exceptinfo.head; cell; cell = cell->next)
    > + {
    > + PublicationRelInfo *pubrinfo = (PublicationRelInfo *) cell->ptr;
    > + TableInfo  *tbinfo;
    > +
    > + if (pubinfo == pubrinfo->publication)
    > + {
    > + tbinfo = pubrinfo->pubtable;
    > +
    > + if (first)
    > + {
    > + appendPQExpBufferStr(query, " EXCEPT TABLE (");
    > + first = false;
    > + }
    > + else
    > + appendPQExpBufferStr(query, ", ");
    > + appendPQExpBuffer(query, "ONLY %s", fmtQualifiedDumpable(tbinfo));
    > + }
    > + }
    > + if (!first)
    > + appendPQExpBufferStr(query, ")");
    > + }
    >
    > 16a.
    > SimplePtrListCell *cell can be declared as a for-loop variable.
    >
    > ~
    >
    > 16b.
    > The comment should say "EXCEPT TABLES" in uppercase.
    >
    > ~
    >
    > 16c.
    > I am not convinced you can use that 'first' flag like you are doing.
    > Isn't that interfering with the existing usage of that flag? Perhaps
    > another boolean just for this EXCEPT loop is needed.
    >
    > ~~~
    >
    Modified
    
    > getPublicationTables:
    >
    > 17.
    > + if (strcmp(prexcept, "f") == 0)
    > + pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
    > + else
    > + pubrinfo[j].dobj.objType = DO_PUBLICATION_EXCEPT_REL;
    > +
    >
    > ...
    >
    > + if (strcmp(prexcept, "t") == 0)
    > + simple_ptr_list_append(&exceptinfo, &pubrinfo[j]);
    > +
    >
    > Here you are comparing the same 'prexcept' flag for both "f" and "t".
    >
    > I felt it was better if both comparisons are the same (e.g. both "t").
    >
    > Or better still, assign a new boolean and avoid that 2nd strcmp
    > entirely -- e.g. except_flag = (strcmp(prexcept, "t") == 0);
    >
    Modified
    
    > ======
    > src/bin/pg_dump/pg_dump_sort.c
    >
    > DOTypeNameCompare:
    >
    > 18.
    > + else if (obj1->objType == DO_PUBLICATION_EXCEPT_REL)
    > + {
    > + PublicationRelInfo *probj1 = *(PublicationRelInfo *const *) p1;
    > + PublicationRelInfo *probj2 = *(PublicationRelInfo *const *) p2;
    > +
    > + /* Sort by publication name, since (namespace, name) match the rel */
    > + cmpval = strcmp(probj1->publication->dobj.name,
    > + probj2->publication->dobj.name);
    > + if (cmpval != 0)
    > + return cmpval;
    > + }
    >
    > Isn't this identical to the previous code block? So can't you just add
    >  DO_PUBLICATION_EXCEPT_REL to that condition?
    >
    Modified
    
    > ======
    > src/bin/pg_dump/t/002_pg_dump.pl
    >
    > 19.
    > Missing test cases for ALTER? But also.
    >
    This change is not required in the latest patch. Due to discussion in [2].
    
    > ~~~
    >
    > 20.
    > Missing test cases for EXCEPT for INHERITED tables?
    >
    > ======
    > src/bin/psql/describe.c
    >
    > describeOneTableDetails:
    >
    > 21.
    > I was wondering if the "describe" for tables (e.g. \d+) should also
    > show the publications where the table is an ECEPT TABLE? How else is
    > the user going to know it has been excluded by some publication?
    >
    I thought it would be sufficient to show only the list of
    publications, the table is part of.
    Users can check the excluded tables by checking the description of the
    publication using \dRp+.
    Will it be not sufficient?
    I am not sure why we should show a list of publications which it is not part of?
    Am I missing something thoughts?
    
    > ======
    > src/bin/psql/tab-complete.in.c
    >
    > ALTER PUBLICATION:
    >
    > 22.
    > The tab completion does not seem as good as it could be. e.g, there is
    > missing '(' and the for EXCEPT TABLE
    >
    > ~~~
    >
    Modified
    
    > CREATE PUBLICATION:
    >
    > 23.
    > The tab completion does not seem as good as it could be. e.g, there is
    > missing '(' and the for EXCEPT TABLE
    >
    Modified
    
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 24.
    > +\dRp+ testpub_foralltables_excepttable
    > +\dRp+ testpub_foralltables_excepttable1
    >
    > As well as doing the "describes" for the publication, I think we need
    > to see the test cases for the describes of those excluded tables. e.g.
    > I imagine that they should also list the publications that they are
    > *excluded* from, right?
    >
    See Reply to comment 21.
    
    > ~~~
    >
    > 25.
    > +CREATE PUBLICATION testpub5 FOR ALL TABLES EXCEPT TABLE (testpub_tbl3);
    > +CREATE PUBLICATION testpub6 FOR ALL TABLES EXCEPT TABLE (ONLY testpub_tbl3);
    >
    > 25a.
    > Needs some explanatory comments here saying these are for testing the
    > EXCEPT with inherited tables (e.g. ONLY versus not).
    >
    > ~
    >
    > 25b.
    > I think you should be testing the '*' syntax here too.
    >
    > ~~~
    >
    I agree. Made the changes.
    
    > 26.
    > +CREATE TABLE pub_sch1.tbl2 (a int);
    >  SET client_min_messages = 'ERROR';
    >  CREATE PUBLICATION testpub_reset FOR ALL TABLES, ALL SEQUENCES;
    >  RESET client_min_messages;
    > @@ -1344,9 +1358,15 @@ ALTER PUBLICATION testpub_reset ADD ALL TABLES;
    >
    >  -- Can't add ALL TABLES to 'ALL TABLES' publication
    >  ALTER PUBLICATION testpub_reset ADD ALL TABLES;
    > +ALTER PUBLICATION testpub_reset RESET;
    > +
    > +-- Verify adding EXCEPT TABLE
    > +ALTER PUBLICATION testpub_reset ADD ALL TABLES EXCEPT TABLE
    > (pub_sch1.tbl1, pub_sch1.tbl2);
    > +\dRp+ testpub_reset
    >
    >  DROP PUBLICATION testpub_reset;
    >  DROP TABLE pub_sch1.tbl1;
    > +DROP TABLE pub_sch1.tbl2;
    >  DROP SCHEMA pub_sch1;
    >
    > It looks like that added CREATE TABLE (and RESET?) belongs more
    > appropriately within the scope of the new test "Verify adding EXCEPT
    > TABLE".
    >
    This change is not required in the latest patch. Due to discussion in [2].
    
    I have addressed the comments and attached the latest patch.
    As per suggestion by Shveta and Amit, I have omitted the patches 0001,
    0002, and 0004 (as per [2]). Will post these patches once 0003 patch
    is RFC.
    
    The new 0001 patch is to support EXCEPT TABLE for CREATE PUBLICATION
    .. FOR ALL TABLES syntax. I have attached in [3].
    
    [1]: https://www.postgresql.org/message-id/CAA4eK1JEKs8qwwhRb1BCiMNduJ5ePUtFnTscrZt86UKWBkLxwg%40mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAA4eK1KZ1Sb0soHp3HH2htwJ3%3Dqka-eQjW35vOW3%2B4VeWw4VoQ%40mail.gmail.com
    [3]: https://www.postgresql.org/message-id/CANhcyEXwLrQsec6g%2B1dqWTKyJQMQMh%3Dgetj28C%2BzLL14BjuumA%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  157. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-12-09T17:49:26Z

    On Fri, 21 Nov 2025 at 12:26, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Here are some review comments for your patch v28-0003 (EXCEPT TABLE ...).
    >
    > The review of this patch is a WIP. In this post I only looked at the test code.
    >
    > ======
    > .../t/037_rep_changes_except_table.pl
    >
    > 1.
    > +
    > +# Copyright (c) 2021-2025, PostgreSQL Global Development Group
    > +
    > +# Logical replication tests for except table publications
    >
    > Use uppercase: /except table/EXCEPT TABLE/
    >
    > ~~~
    >
    > 2.
    > There are lots of test cases dedicated to partiion-table testing. I
    > felt a bigger comment separating these major groups might be helpful.
    >
    > Something like:
    >
    > -- ============================================
    > -- EXCEPT TABLE test cases for normal tables
    > -- ============================================
    >
    > and
    >
    > -- ============================================
    > -- EXCEPT TABLE test cases for partition tables
    > -- ============================================
    >
    > ~~~
    >
    > 3.
    > +# Initialize publisher node
    > ...
    > +# Create subscriber node
    >
    > Those 2 comments should be almost alike -- e.g. both should say
    > "Initialize" or both should say "Create".
    >
    > ~~~
    >
    > 4.
    > +# Test replication with publications created using FOR ALL TABLES EXCEPT TABLE
    > +# clause.
    > +# Create schemas and tables on publisher
    > +$node_publisher->safe_psql(
    > + 'postgres', qq(
    > + CREATE SCHEMA sch1;
    > + CREATE TABLE sch1.tab1 AS SELECT generate_series(1,10) AS a;
    > + CREATE TABLE public.tab1(a int);
    > +));
    > +
    >
    > That first sentence ("Test replication with ...") is not needed here.
    > The is just repeating the purpose of the entire file, so that comment
    > can replace the one at the top of this file.
    >
    > ~~~
    >
    > 5.
    > +# Insert some data and verify that inserted data is not replicated
    >
    > Be explicit that we are referring to the excluded table.
    >
    > SUGGESTION (e.g.)
    > Verify that data inserted to the excluded table is not replcated.
    >
    > ~~~
    >
    > 6.
    > +# Alter publication to exclude data changes in public.tab1 and verify that
    > +# subscriber does not get the changed data for this table.
    > +$node_publisher->safe_psql(
    > + 'postgres', qq(
    > + ALTER PUBLICATION tap_pub_schema RESET;
    > + ALTER PUBLICATION tap_pub_schema ADD ALL TABLES EXCEPT TABLE
    > (sch1.tab1, public.tab1);
    > + INSERT INTO public.tab1 VALUES(generate_series(1,10));
    > +));
    > +$node_publisher->wait_for_catchup('tap_sub_schema');
    > +
    >
    > It is not strictly needed for these tests, but do you think it makes
    > more sense to also do an ALTER SUBSCRIPTION ... REFRESH PUBLICATION;
    > whenever you change the publications?
    >
    > ~~~
    >
    > 7.
    > +# cleanup
    > +$node_publisher->safe_psql('postgres', "DROP PUBLICATION tap_pub_schema");
    > +$node_subscriber->safe_psql('postgres', "DROP SUBSCRIPTION tap_sub_schema");
    > +
    > +
    >
    > double-blank lines.
    >
    > ~~~
    >
    > 8.
    > I think it would be more helpful if the partition table test cases say
    > (in their comments) a lot more about the steps they are doing, and
    > what they expect the result to be. Sure, I can read all the code to
    > figure it out for each case, but it is better to know the test
    > intentions/expectations then verify they are doing the right thing.
    >
    > ~~~
    >
    > 9.
    > + CREATE TABLE sch1.t1(a int) PARTITION BY RANGE(a);
    > + CREATE TABLE sch1.part1 PARTITION OF sch1.t1 FOR VALUES FROM (0) TO (5);
    >
    > Maybe create this table to have *multiple* partitions. It might be
    > interesting later to see what happens when you try to EXCEPT only one
    > of the partitions.
    >
    I have addressed all the comments
    Please find the updated patch in [1].
    
    [1]: https://www.postgresql.org/message-id/CANhcyEXwLrQsec6g%2B1dqWTKyJQMQMh%3Dgetj28C%2BzLL14BjuumA%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  158. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-12-10T05:50:59Z

    On Tue, Dec 9, 2025 at 11:17 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > >
    > I have removed the 0001 0002 and 0004 patches for now. Will post them
    > once 0003 patch is RFC.
    > Here is the update patch for "EXCEPT TABLE".
    >
    
    Thanks, I have not looked at new patch yet, but here are few comments
    for v29-003:
    
    1)
    create_publication.sgml:
    Please add one more example in the example section for EXCEPT using
    'all tables, and all sequences' after the last existing one. This is
    needed to show that ALL TABLES EXCEPT() and ALL SEQ are still possible
    in single command.
    
    2)
    +      excluded. Optionally, <literal>*</literal> can be specified after the
    +      table name to explicitly indicate that descendant tables are excluded.
    +     </para>
    
    We may add: This does not apply to a partitioned table, however.
    (this will make it more clear similar to how existing doc has it for
    'FOR TABLE' clause ). And then start details on partition.
    
    3)
    When
    +      <literal>publish_via_partition_root</literal> is set to
    +      <literal>false</literal>, specifying a partitioned table or non-leaf
    +      partition has no effect
    
    Can we simply say 'specifying a root partitioned table has no effect'.
    This will make it consistent as the previous sentence also uses the
    same term rather than 'non-leaf'.
    
    4)
    tab_root is a partitioned table with tab_part_1 and tab_part_2 as its
    partitions.
    In the first case, I receive a WARNING because the user excluded
    tab_part_2 but its data will still be replicated through the root
    table:
    
    postgres=# create publication pub3 for all tables except (tab_part_2)
    WITH (publish_via_partition_root=true);
    WARNING:  partition "tab_part_2" will be replicated as
    publish_via_partition_root is "true"
    
    But in the following case, no WARNING is shown:
    postgres=# create publication pub4 for all tables except (tab_root)
    WITH (publish_via_partition_root=false);
    CREATE PUBLICATION
    
    In this scenario, the user has excluded the root table, yet its data
    will still be replicated because publish_via_partition_root = false.
    Should we emit a warning in this case as well? Thoughts?
    
    5)
    publication_add_relation:
    + if (pub->alltables && pri->except && targetrel->rd_rel->relispartition &&
    + pub->pubviaroot)
    
    Can we please bring both the 'pub' conditions together, as that seems
    more understandable:
    if (pub->alltables && pub->pubviaroot &&...)
    
    6)
    We have added pubid as argument to GetAllPublicationRelations to
    exclude except-list tables.
    We should change comment atop GetAllPublicationRelations() to indicate
    the same. We should extend
    this existing comment to say about except-list exclusion also.
    
     * If the publication publishes partition changes via their respective root
     * partitioned tables, we must exclude partitions in favor of including the
     * root partitioned tables.
    
    thanks
    Shveta
    
    
    
    
  159. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-12-10T10:21:53Z

    On Wed, 10 Dec 2025 at 11:21, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Dec 9, 2025 at 11:17 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > >
    > > I have removed the 0001 0002 and 0004 patches for now. Will post them
    > > once 0003 patch is RFC.
    > > Here is the update patch for "EXCEPT TABLE".
    > >
    >
    > Thanks, I have not looked at new patch yet, but here are few comments
    > for v29-003:
    >
    > 1)
    > create_publication.sgml:
    > Please add one more example in the example section for EXCEPT using
    > 'all tables, and all sequences' after the last existing one. This is
    > needed to show that ALL TABLES EXCEPT() and ALL SEQ are still possible
    > in single command.
    >
    > 2)
    > +      excluded. Optionally, <literal>*</literal> can be specified after the
    > +      table name to explicitly indicate that descendant tables are excluded.
    > +     </para>
    >
    > We may add: This does not apply to a partitioned table, however.
    > (this will make it more clear similar to how existing doc has it for
    > 'FOR TABLE' clause ). And then start details on partition.
    >
    > 3)
    > When
    > +      <literal>publish_via_partition_root</literal> is set to
    > +      <literal>false</literal>, specifying a partitioned table or non-leaf
    > +      partition has no effect
    >
    > Can we simply say 'specifying a root partitioned table has no effect'.
    > This will make it consistent as the previous sentence also uses the
    > same term rather than 'non-leaf'.
    >
    > 4)
    > tab_root is a partitioned table with tab_part_1 and tab_part_2 as its
    > partitions.
    > In the first case, I receive a WARNING because the user excluded
    > tab_part_2 but its data will still be replicated through the root
    > table:
    >
    > postgres=# create publication pub3 for all tables except (tab_part_2)
    > WITH (publish_via_partition_root=true);
    > WARNING:  partition "tab_part_2" will be replicated as
    > publish_via_partition_root is "true"
    >
    > But in the following case, no WARNING is shown:
    > postgres=# create publication pub4 for all tables except (tab_root)
    > WITH (publish_via_partition_root=false);
    > CREATE PUBLICATION
    >
    > In this scenario, the user has excluded the root table, yet its data
    > will still be replicated because publish_via_partition_root = false.
    > Should we emit a warning in this case as well? Thoughts?
    >
    > 5)
    > publication_add_relation:
    > + if (pub->alltables && pri->except && targetrel->rd_rel->relispartition &&
    > + pub->pubviaroot)
    >
    > Can we please bring both the 'pub' conditions together, as that seems
    > more understandable:
    > if (pub->alltables && pub->pubviaroot &&...)
    >
    > 6)
    > We have added pubid as argument to GetAllPublicationRelations to
    > exclude except-list tables.
    > We should change comment atop GetAllPublicationRelations() to indicate
    > the same. We should extend
    > this existing comment to say about except-list exclusion also.
    >
    >  * If the publication publishes partition changes via their respective root
    >  * partitioned tables, we must exclude partitions in favor of including the
    >  * root partitioned tables.
    >
    Hi Shveta,
    
    I have addressed the above comments and attached the updated patch.
    I have also addressed a comment by Peter (comment no. 20 in [1]) which
    I missed in the earlier version.
    
    [1]: https://www.postgresql.org/message-id/CAHut%2BPudi%2B9ssBR_Q_Fd29aGEu8s18OyKUGo5w5aKJK-2_c%2B8g%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  160. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-12-10T22:39:33Z

    Hi Shlok -
    
    Here are some review comments for v31-0001 (EXCEPT (tablelist))
    
    ======
    Commit message
    
    1.
    The new syntax allows specifying excluded relations when creating or altering
    a publication. For example:
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (t1,t2);
    
    ~
    
    In v30, you removed all the ALTER PUBLICATION changes, so the "or
    altering" in the message above also needs to be removed.
    
    ======
    doc/src/sgml/logical-replication.sgml
    
    2.
       <para>
    -   To add tables to a publication, the user must have ownership rights on the
    -   table. To add all tables in schema to a publication, the user must be a
    -   superuser. To create a publication that publishes all tables, all tables in
    -   schema, or all sequences automatically, the user must be a superuser.
    +   To create a publication using <literal>FOR ALL TABLES</literal>,
    +   <literal>FOR ALL SEQUENCES</literal> or
    +   <literal>FOR TABLES IN SCHEMA</literal>, the user must be a
    superuser. To add
    +   <literal>ALL TABLES</literal> or <literal>TABLES IN SCHEMA</literal> to a
    +   publication, the user must be a superuser. To add tables to a publication,
    +   the user must have ownership rights on the table.
       </para>
    
    This is a good improvement, but I was not sure why it is in this
    patch. Should it be a separate thread for a docs improvement?
    
    ======
    src/backend/catalog/pg_publication.c
    
    GetTopMostAncestorInPublication:
    
    3.
    {
      Oid ancestor = lfirst_oid(lc);
    - List    *apubids = GetRelationPublications(ancestor);
    - List    *aschemaPubids = NIL;
    + List    *apubids = NIL;
    + List    *aexceptpubids = NIL;
    + List    *aschemapubids = NIL;
    + bool set_top = false;
    +
    + GetRelationPublications(ancestor, &apubids, &aexceptpubids);
    
      level++;
    
    - if (list_member_oid(apubids, puboid))
    + /* check if member of table publications */
    + set_top = list_member_oid(apubids, puboid);
    + if (!set_top)
      {
    - topmost_relid = ancestor;
    + aschemapubids = GetSchemaPublications(get_rel_namespace(ancestor));
    
    - if (ancestor_level)
    - *ancestor_level = level;
    + /* check if member of schema publications */
    + set_top = list_member_oid(aschemapubids, puboid);
    +
    + /*
    + * If the publication is all tables publication and the table is
    + * not part of exception tables.
    + */
    + if (!set_top && puballtables)
    + set_top = !list_member_oid(aexceptpubids, puboid);
      }
    - else
    +
    + if (set_top)
      {
    - aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    - if (list_member_oid(aschemaPubids, puboid))
    - {
    - topmost_relid = ancestor;
    + topmost_relid = ancestor;
    
    - if (ancestor_level)
    - *ancestor_level = level;
    - }
    + if (ancestor_level)
    + *ancestor_level = level;
      }
    
      list_free(apubids);
    - list_free(aschemaPubids);
    + list_free(aschemapubids);
    + list_free(aexceptpubids);
      }
    
    That 'aschemapubids' can be declared and freed within the if block.
    
    ~~~
    
    publication_add_relation:
    
    4.
    + /*
    + * Check when a partition is excluded via EXCEPT TABLE while the
    + * publication has publish_via_partition_root = true.
    + */
    + if (pub->alltables && pub->pubviaroot && pri->except &&
    + targetrel->rd_rel->relispartition)
    + ereport(WARNING,
    
    
    This comment doesn't sound quite right:
    
    SUGGESTION
    Handle the case where a partition is excluded by EXCEPT TABLE while
    publish_via_partition_root = true.
    
    ~~~
    
    5.
    + /*
    + * Check when a partitioned table is excluded via EXCEPT TABLE while the
    + * publication has publish_via_partition_root = false.
    + */
    + if (pub->alltables && !pub->pubviaroot && pri->except &&
    + targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    + ereport(WARNING,
    
    Ditto. Reword like suggested in the previous review comment.
    
    ~~~
    
    6.
    +/*
    + * Get the list of publication oids associated with a specified relation.
    + * pubids is filled with the list of publication oids the relation is part of.
    + * except_pubids is filled with the list of publication oids the relation is
    + * excluded from.
    + *
    + * This function returns true if the relation is part of any publication.
    + */
    
    Maybe putting 'pubids' and 'except_pubids' in single quotes will help
    readability of this comment?
    
    Also, these are already Lists, so they are not filled with lists.
    
    SUGGESTION
    Parameter 'pubids' returns the OIDs of the publications the relation is part of.
    Parameter 'except_pubids' returns the OIDs of publications the
    relation is excluded from.
    
    ~~~
    
    GetPublicationRelations:
    
    7.
     /*
    - * Gets list of relation oids for a publication.
    + * Return the list of relation OIDs for a publication.
    + *
    + * For a FOR ALL TABLES publication, this returns the list of tables that were
    + * explicitly excluded via an EXCEPT TABLE clause.
    + *
    + * For a FOR TABLE publication, this returns the list of tables explicitly
    + * included in the publication.
      *
    - * This should only be used FOR TABLE publications, the FOR ALL
    TABLES/SEQUENCES
    - * should use GetAllPublicationRelations().
    + * Publications declared with FOR ALL TABLES or FOR ALL SEQUENCES should use
    + * GetAllPublicationRelations() to obtain the complete set of tables covered by
    + * the publication.
      */
     List *
     GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
    
    7a.
    The function is called 'GetPublicationRelations', so it seems
    unintuitive that it sometimes returns the list of all the tables that
    are *excluded* from the publication. If you are going to have one
    single function that does everything, then IMO it might be better to
    hide that behind some wrapper functions like:
    GetPublicationMemberRelations
    GetPublicationExcludedRelations
    
    Consider also that all these assumptions might be OK today but they
    won't be OK in the future. e.g. One day, when named FOR SEQUENCE
    sq1,sq2 are supported then you will be alble to write a command like
    FOR ALL TABLES EXCEPT (t1), FOR SEQUENCE sq1,sq2. That's going to be a
    muddle of some included and some excluded relations. So, it is better
    to cater for that scenario now, rather than have to rewrite all of
    this function again in the future. e.g. Maybe instead of this function
    returning one list it is better to return included/excluded Lists or
    relations as output parameters?
    
    ~
    
    7b.
    Also, comments like "Publications declared with FOR ALL TABLES or FOR
    ALL SEQUENCES should use..." seems like too many assumptions are being
    made. It would be better to enforce the calling requirements using
    parameter checking and Asserts instead instead of hoping that callers
    are going to abide by the comments.
    
    ~~~
    
    GetAllPublicationRelations:
    
    8.
    + exceptlist = GetPublicationRelations(pubid, pubviaroot ?
    + PUBLICATION_PART_ALL :
    + PUBLICATION_PART_ROOT);
    
    This is similar to the above review comment. I'm not sure how you can
    just assume that this must be the "except list" -- AFAICT this assumes
    that 'GetAllPublicationRelations' can only be called by FOR ALL TABLES
    (???). Seems like a lot of assumptions, that would be much better to
    be enforced by Asserts in the code.
    
    ======
    src/backend/commands/publicationcmds.c
    
    pub_rf_contains_invalid_column:
    
    9.
     bool
     pub_rf_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
    -    bool pubviaroot)
    +    bool pubviaroot, bool puballtables)
    
    I felt that 'puballtables' is more "important" than 'pubviaroot' so
    maybe it should come earlier in the parameter list. (e.g. make it more
    similar to 'pub_contains_invalid_column')
    
    ======
    src/backend/parser/gram.y
    
    10.
    + pub_except_obj_list opt_except_clause
    
    I felt that 'opt_except_clause' should better be called
    'opt_pub_except_clause' or 'pub_opt_except_clause' because without
    'pub' it is a bit vague.
    
    ~~~
    
    11.
    +/*
    + * ALL TABLES EXCEPT ( table_name [, ...] ) specification
    + */
    
    11a
    This comment should be up where all the other CREATE PUBLICATION
    syntax is commented.
    
    ~
    
    11b.
    Also, there is a missing optional "[TABLE]" part.
    
    ~~~
    
    12.
    +pub_except_obj_list: PublicationExceptObjSpec
    + { $$ = list_make1($1); }
    + | pub_except_obj_list ',' PublicationExceptObjSpec
    + { $$ = lappend($1, $3); }
    + ;
    +
    +opt_except_clause:
    + EXCEPT opt_table '(' pub_except_obj_list ')' { $$ = $4; }
    + | /*EMPTY*/ { $$ = NIL; }
    + ;
    
    I felt the clause should be defined before the obj list because that
    seems the natural order to read these.
    
    ======
    src/bin/pg_dump/pg_dump.c
    
    13.
    +static SimplePtrList exceptinfo = {NULL, NULL};
    
    Having this as global seems a bit hacky. It has nothing in common with
    all the other nearby lists, which are commented as being based on
    "patterns given by command-line switches"
    
    ~~~
    
    dumpPublication:
    
    14.
    + /* Include exception tables if the publication has EXCEPT TABLEs */
    + for (SimplePtrListCell *cell = exceptinfo.head; cell; cell = cell->next)
    + {
    + PublicationRelInfo *pubrinfo = (PublicationRelInfo *) cell->ptr;
    + TableInfo  *tbinfo;
    +
    + if (pubinfo == pubrinfo->publication)
    + {
    + tbinfo = pubrinfo->pubtable;
    
    That 'tbinfo' can be declared within the "if".
    
    ~~~
    
    15.
    + appendPQExpBuffer(query, "ONLY %s", fmtQualifiedDumpable(tbinfo));
    
    ONLY is not the default. How did you decide that "ONLY" is the correct
    thing to do here?
    
    ~~~
    
    getPublicationTables:
    
    16.
    - pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
    + if (prexcept)
    + pubrinfo[j].dobj.objType = DO_PUBLICATION_EXCEPT_REL;
    + else
    + pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
    +
    
    Would a single assignment (ternary) make this code simpler and easier to read?
    
    SUGGESTION
    pubrinfo[j].dobj.objType = prexcept ?
      DO_PUBLICATION_EXCEPT_REL :
      DO_PUBLICATION_REL;
    
    ======
    src/bin/pg_dump/t/002_pg_dump.pl
    
    17.
    + 'CREATE PUBLICATION pub10' => {
    + create_order => 50,
    + create_sql =>
    +   'CREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT TABLE
    (dump_test.test_table_generated);',
    + regexp => qr/^
    + \QCREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT TABLE (ONLY
    dump_test.test_table_generated, ONLY
    dump_test.test_table_generated_child2, ONLY
    dump_test.test_table_generated_child1) WITH (publish = 'insert,
    update, delete, truncate');\E
    + /xm,
    + like => { %full_runs, section_post_data => 1, },
    + },
    +
    
    These "generated" names seem unusual. I saw there are some other
    tables like 'dump_test.test_inheritance_child' and
    'dump_test.test_inheritance_parent'. Can you use those more normal
    table names instead?
    
    Also curious - does the order of the tests matter? I saw that the
    CREATE TABLE tests seem to be coming after the CREATE PUBLICATION
    tests that are using them.
    
    ~~~
    18.
    - if (!defined($tests{$test}->{all_runs})
    + if (   !defined($tests{$test}->{all_runs})
    
    Why add this whitespace?
    
    ======
    src/include/nodes/parsenodes.h
    
    19.
      AP_SetObjects, /* set list of objects */
    + AP_Reset, /* reset the publication */
     } AlterPublicationAction;
    
    AFAIK, you removed all ALTER command changes from v30-0001. So this
    should not be here.
    
    ~~~
    
    20.
    + bool for_all_tables; /* Special publication for all tables in db */
      AlterPublicationAction action; /* What action to perform with the given
      * objects */
     } AlterPublicationStmt;
    
    AFAIK, you removed all ALTER command changes from v30-0001. So this
    should not be here.
    
    ======
    src/test/regress/sql/publication.sql
    
    21.
    +SET client_min_messages = 'ERROR';
    +CREATE PUBLICATION testpub_foralltables_excepttable FOR ALL TABLES
    EXCEPT TABLE (testpub_tbl1, testpub_tbl2);
    +-- specify EXCEPT without TABLE
    +CREATE PUBLICATION testpub_foralltables_excepttable1 FOR ALL TABLES
    EXCEPT (testpub_tbl1);
    
    Should be 2 comments here for the 2x CREATE:
    
    # Exclude tables using FOR ALL TABLES EXCEPT TABLE (tablelist)
    
    # Exclude tables using FOR ALL TABLES EXCEPT (tablelist)
    
    ~~~
    
    22.
     CREATE TABLE testpub_tbl3 (a int);
     CREATE TABLE testpub_tbl3a (b text) INHERITS (testpub_tbl3);
    
    If you rename these tables like 'testpub_tbl_parent' and
    'testpub_tbl_child', it will be much easier to see what is going on.
    
    ~~~
    
    23.
    +CREATE PUBLICATION testpub5 FOR ALL TABLES EXCEPT TABLE (testpub_tbl3);
    
    Missing comment -- something like:
    # Exclude parent table, omitting both of 'ONLY' and '*'
    
    ~~~
    
    24.
    +-- EXCEPT with wildcard: exclude table and all descendants
    +CREATE PUBLICATION testpub6 FOR ALL TABLES EXCEPT TABLE (testpub_tbl3*);
    
    24a.
    TBH, I don't think this is a "wildcard" -- it is not doing any pattern
    matching. IMO just call it an "asterisk" or a "star".
    
    ~
    
    24b.
    And put a space before the '*' here.
    
    ======
    .../t/037_rep_changes_except_table.pl
    
    25.
    +# ============================================
    +# EXCEPT TABLE test cases for partition tables
    +# ============================================
    +# Check behavior of EXCEPT TABLE together with publish_via_partition_root
    +# when applied to a partitioned table and its partitions.
    
    
    Really, that "Check behavior" sentence is generic for all of the
    following tests, so it should also be (within the "=======" of the
    previous comment)
    
    ~~~
    
    26.
    +$node_publisher->safe_psql(
    + 'postgres', qq(
    + CREATE TABLE sch1.t1(a int) PARTITION BY RANGE(a);
    + CREATE TABLE sch1.part1 PARTITION OF sch1.t1 FOR VALUES FROM (0) TO (5);
    + CREATE TABLE sch1.part2 PARTITION OF sch1.t1 FOR VALUES FROM (6) TO (10);
    + INSERT INTO sch1.t1 VALUES (1), (6);
    +));
    +
    +$node_subscriber->safe_psql(
    + 'postgres', qq(
    + CREATE TABLE sch1.t1(a int);
    + CREATE TABLE sch1.part1(a int);
    + CREATE TABLE sch1.part2(a int);
    +));
    
    26a.
    There should be a comment for this part that just says something like
    "Setup partition table and partitions on the publisher that map to
    normal tables on the subscriber"
    
    ~
    
    26b.
    The INSERT should be done later, after the CREATE PUBLICATION but
    before the CREATE SUBSCRIPTION. The pattern will be the same for all
    the test cases.
    
    ~~~
    
    27.
    +$node_publisher->safe_psql('postgres',
    + "CREATE PUBLICATION tap_pub_part FOR ALL TABLES EXCEPT TABLE (sch1.part1)"
    +);
    
    Even though the publish_via_partition_root is 'false' by default, I
    think you should spell it out explicitly here for clarity.
    
    ~~~
    
    28.
    +# EXCEPT TABLE (sch1.t1) with publish_via_partition_root = false
    +# Excluding the partitioned table while publish_via_partition_root = false
    +# still allows rows inserted into its partitions to be replicated.
    
    I felt you should word this differently. I don't think you should say
    "inserted into its partitions" because actually, you inserted into the
    partition table, and the data just ends up in the partitions.
    
    ~~~
    
    29.
    +$node_publisher->safe_psql(
    + 'postgres', qq(
    + CREATE PUBLICATION tap_pub_part FOR ALL TABLES EXCEPT TABLE (sch1.t1);
    + INSERT INTO sch1.t1 VALUES (1), (6);
    +));
    
    Ditto earlier comment. Better to explicitly say
    "publish_via_partition_root=false".
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  161. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-12-10T23:01:13Z

    On Wed, Dec 10, 2025 at 4:49 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Mon, 24 Nov 2025 at 13:03, Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    ...
    > > 21.
    > > I was wondering if the "describe" for tables (e.g. \d+) should also
    > > show the publications where the table is an ECEPT TABLE? How else is
    > > the user going to know it has been excluded by some publication?
    > >
    > I thought it would be sufficient to show only the list of
    > publications, the table is part of.
    > Users can check the excluded tables by checking the description of the
    > publication using \dRp+.
    > Will it be not sufficient?
    > I am not sure why we should show a list of publications which it is not part of?
    > Am I missing something thoughts?
    
    For this comment, I was imagining a scenario where there are dozens of
    publications, and the user is wondering why their table is not being
    replicated to the subscriber like they expected it would be.
    
    Yes, they could use \dRs+ to identify the publications excluding it,
    but that will be quite painful if there are very many publications
    they have to check. IIUC, there is no other way to check it without
    digging into System Catalogs.
    
    That's why I thought it might be useful if the \d+ could also show
    publications where the table was named in an EXCEPT TABLE clause.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  162. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-12-16T09:20:41Z

    On Thu, 11 Dec 2025 at 04:10, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok -
    >
    > Here are some review comments for v31-0001 (EXCEPT (tablelist))
    >
    > ======
    > Commit message
    >
    > 1.
    > The new syntax allows specifying excluded relations when creating or altering
    > a publication. For example:
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (t1,t2);
    >
    > ~
    >
    > In v30, you removed all the ALTER PUBLICATION changes, so the "or
    > altering" in the message above also needs to be removed.
    >
    > ======
    > doc/src/sgml/logical-replication.sgml
    >
    > 2.
    >    <para>
    > -   To add tables to a publication, the user must have ownership rights on the
    > -   table. To add all tables in schema to a publication, the user must be a
    > -   superuser. To create a publication that publishes all tables, all tables in
    > -   schema, or all sequences automatically, the user must be a superuser.
    > +   To create a publication using <literal>FOR ALL TABLES</literal>,
    > +   <literal>FOR ALL SEQUENCES</literal> or
    > +   <literal>FOR TABLES IN SCHEMA</literal>, the user must be a
    > superuser. To add
    > +   <literal>ALL TABLES</literal> or <literal>TABLES IN SCHEMA</literal> to a
    > +   publication, the user must be a superuser. To add tables to a publication,
    > +   the user must have ownership rights on the table.
    >    </para>
    >
    > This is a good improvement, but I was not sure why it is in this
    > patch. Should it be a separate thread for a docs improvement?
    >
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > GetTopMostAncestorInPublication:
    >
    > 3.
    > {
    >   Oid ancestor = lfirst_oid(lc);
    > - List    *apubids = GetRelationPublications(ancestor);
    > - List    *aschemaPubids = NIL;
    > + List    *apubids = NIL;
    > + List    *aexceptpubids = NIL;
    > + List    *aschemapubids = NIL;
    > + bool set_top = false;
    > +
    > + GetRelationPublications(ancestor, &apubids, &aexceptpubids);
    >
    >   level++;
    >
    > - if (list_member_oid(apubids, puboid))
    > + /* check if member of table publications */
    > + set_top = list_member_oid(apubids, puboid);
    > + if (!set_top)
    >   {
    > - topmost_relid = ancestor;
    > + aschemapubids = GetSchemaPublications(get_rel_namespace(ancestor));
    >
    > - if (ancestor_level)
    > - *ancestor_level = level;
    > + /* check if member of schema publications */
    > + set_top = list_member_oid(aschemapubids, puboid);
    > +
    > + /*
    > + * If the publication is all tables publication and the table is
    > + * not part of exception tables.
    > + */
    > + if (!set_top && puballtables)
    > + set_top = !list_member_oid(aexceptpubids, puboid);
    >   }
    > - else
    > +
    > + if (set_top)
    >   {
    > - aschemaPubids = GetSchemaPublications(get_rel_namespace(ancestor));
    > - if (list_member_oid(aschemaPubids, puboid))
    > - {
    > - topmost_relid = ancestor;
    > + topmost_relid = ancestor;
    >
    > - if (ancestor_level)
    > - *ancestor_level = level;
    > - }
    > + if (ancestor_level)
    > + *ancestor_level = level;
    >   }
    >
    >   list_free(apubids);
    > - list_free(aschemaPubids);
    > + list_free(aschemapubids);
    > + list_free(aexceptpubids);
    >   }
    >
    > That 'aschemapubids' can be declared and freed within the if block.
    >
    > ~~~
    >
    > publication_add_relation:
    >
    > 4.
    > + /*
    > + * Check when a partition is excluded via EXCEPT TABLE while the
    > + * publication has publish_via_partition_root = true.
    > + */
    > + if (pub->alltables && pub->pubviaroot && pri->except &&
    > + targetrel->rd_rel->relispartition)
    > + ereport(WARNING,
    >
    >
    > This comment doesn't sound quite right:
    >
    > SUGGESTION
    > Handle the case where a partition is excluded by EXCEPT TABLE while
    > publish_via_partition_root = true.
    >
    > ~~~
    >
    > 5.
    > + /*
    > + * Check when a partitioned table is excluded via EXCEPT TABLE while the
    > + * publication has publish_via_partition_root = false.
    > + */
    > + if (pub->alltables && !pub->pubviaroot && pri->except &&
    > + targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    > + ereport(WARNING,
    >
    > Ditto. Reword like suggested in the previous review comment.
    >
    > ~~~
    >
    > 6.
    > +/*
    > + * Get the list of publication oids associated with a specified relation.
    > + * pubids is filled with the list of publication oids the relation is part of.
    > + * except_pubids is filled with the list of publication oids the relation is
    > + * excluded from.
    > + *
    > + * This function returns true if the relation is part of any publication.
    > + */
    >
    > Maybe putting 'pubids' and 'except_pubids' in single quotes will help
    > readability of this comment?
    >
    > Also, these are already Lists, so they are not filled with lists.
    >
    > SUGGESTION
    > Parameter 'pubids' returns the OIDs of the publications the relation is part of.
    > Parameter 'except_pubids' returns the OIDs of publications the
    > relation is excluded from.
    >
    > ~~~
    >
    > GetPublicationRelations:
    >
    > 7.
    >  /*
    > - * Gets list of relation oids for a publication.
    > + * Return the list of relation OIDs for a publication.
    > + *
    > + * For a FOR ALL TABLES publication, this returns the list of tables that were
    > + * explicitly excluded via an EXCEPT TABLE clause.
    > + *
    > + * For a FOR TABLE publication, this returns the list of tables explicitly
    > + * included in the publication.
    >   *
    > - * This should only be used FOR TABLE publications, the FOR ALL
    > TABLES/SEQUENCES
    > - * should use GetAllPublicationRelations().
    > + * Publications declared with FOR ALL TABLES or FOR ALL SEQUENCES should use
    > + * GetAllPublicationRelations() to obtain the complete set of tables covered by
    > + * the publication.
    >   */
    >  List *
    >  GetPublicationRelations(Oid pubid, PublicationPartOpt pub_partopt)
    >
    > 7a.
    > The function is called 'GetPublicationRelations', so it seems
    > unintuitive that it sometimes returns the list of all the tables that
    > are *excluded* from the publication. If you are going to have one
    > single function that does everything, then IMO it might be better to
    > hide that behind some wrapper functions like:
    > GetPublicationMemberRelations
    > GetPublicationExcludedRelations
    >
    > Consider also that all these assumptions might be OK today but they
    > won't be OK in the future. e.g. One day, when named FOR SEQUENCE
    > sq1,sq2 are supported then you will be alble to write a command like
    > FOR ALL TABLES EXCEPT (t1), FOR SEQUENCE sq1,sq2. That's going to be a
    > muddle of some included and some excluded relations. So, it is better
    > to cater for that scenario now, rather than have to rewrite all of
    > this function again in the future. e.g. Maybe instead of this function
    > returning one list it is better to return included/excluded Lists or
    > relations as output parameters?
    >
    > ~
    >
    > 7b.
    > Also, comments like "Publications declared with FOR ALL TABLES or FOR
    > ALL SEQUENCES should use..." seems like too many assumptions are being
    > made. It would be better to enforce the calling requirements using
    > parameter checking and Asserts instead instead of hoping that callers
    > are going to abide by the comments.
    >
    > ~~~
    >
    > GetAllPublicationRelations:
    >
    > 8.
    > + exceptlist = GetPublicationRelations(pubid, pubviaroot ?
    > + PUBLICATION_PART_ALL :
    > + PUBLICATION_PART_ROOT);
    >
    > This is similar to the above review comment. I'm not sure how you can
    > just assume that this must be the "except list" -- AFAICT this assumes
    > that 'GetAllPublicationRelations' can only be called by FOR ALL TABLES
    > (???). Seems like a lot of assumptions, that would be much better to
    > be enforced by Asserts in the code.
    >
    I agree with comments 7 and 8. I have added two functions
    'GetPublicationIncludedRelations' and
    'GetPublicationExcludedRelations'. To get Relations which are included
    or excluded in a publication.
    Both functions will call 'GetPublicationRelationsInternal' function. I
    have also reintroduced the 'except_flag' variable
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > pub_rf_contains_invalid_column:
    >
    > 9.
    >  bool
    >  pub_rf_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
    > -    bool pubviaroot)
    > +    bool pubviaroot, bool puballtables)
    >
    > I felt that 'puballtables' is more "important" than 'pubviaroot' so
    > maybe it should come earlier in the parameter list. (e.g. make it more
    > similar to 'pub_contains_invalid_column')
    >
    > ======
    > src/backend/parser/gram.y
    >
    > 10.
    > + pub_except_obj_list opt_except_clause
    >
    > I felt that 'opt_except_clause' should better be called
    > 'opt_pub_except_clause' or 'pub_opt_except_clause' because without
    > 'pub' it is a bit vague.
    >
    I agree. I prefer 'opt_pub_except_clause'. By looking at other
    variables it better make sense to start the variable name with 'opt_'
    as it indicates that it is optional.
    Made changes for the same.
    
    > ~~~
    >
    > 11.
    > +/*
    > + * ALL TABLES EXCEPT ( table_name [, ...] ) specification
    > + */
    >
    > 11a
    > This comment should be up where all the other CREATE PUBLICATION
    > syntax is commented.
    >
    > ~
    >
    > 11b.
    > Also, there is a missing optional "[TABLE]" part.
    >
    > ~~~
    >
    > 12.
    > +pub_except_obj_list: PublicationExceptObjSpec
    > + { $$ = list_make1($1); }
    > + | pub_except_obj_list ',' PublicationExceptObjSpec
    > + { $$ = lappend($1, $3); }
    > + ;
    > +
    > +opt_except_clause:
    > + EXCEPT opt_table '(' pub_except_obj_list ')' { $$ = $4; }
    > + | /*EMPTY*/ { $$ = NIL; }
    > + ;
    >
    > I felt the clause should be defined before the obj list because that
    > seems the natural order to read these.
    >
    > ======
    > src/bin/pg_dump/pg_dump.c
    >
    > 13.
    > +static SimplePtrList exceptinfo = {NULL, NULL};
    >
    > Having this as global seems a bit hacky. It has nothing in common with
    > all the other nearby lists, which are commented as being based on
    > "patterns given by command-line switches"
    >
    I agree, I have added it in the PublicationInfo struct and made the
    corresponding code changes.
    
    > ~~~
    >
    > dumpPublication:
    >
    > 14.
    > + /* Include exception tables if the publication has EXCEPT TABLEs */
    > + for (SimplePtrListCell *cell = exceptinfo.head; cell; cell = cell->next)
    > + {
    > + PublicationRelInfo *pubrinfo = (PublicationRelInfo *) cell->ptr;
    > + TableInfo  *tbinfo;
    > +
    > + if (pubinfo == pubrinfo->publication)
    > + {
    > + tbinfo = pubrinfo->pubtable;
    >
    > That 'tbinfo' can be declared within the "if".
    >
    > ~~~
    >
    > 15.
    > + appendPQExpBuffer(query, "ONLY %s", fmtQualifiedDumpable(tbinfo));
    >
    > ONLY is not the default. How did you decide that "ONLY" is the correct
    > thing to do here?
    >
    For pg_dump for publication we use "ONLY" by default while specifying the table
    
    For Alter publication we use similar thing:
    ```
      appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
                fmtId(pubinfo->dobj.name));
    ```
    
    Also if we specify a parent table in a publication(without ONLY) all
    its child tables are also added to the pg_publication_rel table.
    So when we dump such a publication we get something like:
    .... EXCEPT TABLE(ONLY parent_table, ONLY child_table)...
    
    > ~~~
    >
    > getPublicationTables:
    >
    > 16.
    > - pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
    > + if (prexcept)
    > + pubrinfo[j].dobj.objType = DO_PUBLICATION_EXCEPT_REL;
    > + else
    > + pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
    > +
    >
    > Would a single assignment (ternary) make this code simpler and easier to read?
    >
    > SUGGESTION
    > pubrinfo[j].dobj.objType = prexcept ?
    >   DO_PUBLICATION_EXCEPT_REL :
    >   DO_PUBLICATION_REL;
    >
    > ======
    > src/bin/pg_dump/t/002_pg_dump.pl
    >
    > 17.
    > + 'CREATE PUBLICATION pub10' => {
    > + create_order => 50,
    > + create_sql =>
    > +   'CREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT TABLE
    > (dump_test.test_table_generated);',
    > + regexp => qr/^
    > + \QCREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT TABLE (ONLY
    > dump_test.test_table_generated, ONLY
    > dump_test.test_table_generated_child2, ONLY
    > dump_test.test_table_generated_child1) WITH (publish = 'insert,
    > update, delete, truncate');\E
    > + /xm,
    > + like => { %full_runs, section_post_data => 1, },
    > + },
    > +
    >
    > These "generated" names seem unusual. I saw there are some other
    > tables like 'dump_test.test_inheritance_child' and
    > 'dump_test.test_inheritance_parent'. Can you use those more normal
    > table names instead?
    >
    > Also curious - does the order of the tests matter? I saw that the
    > CREATE TABLE tests seem to be coming after the CREATE PUBLICATION
    > tests that are using them.
    >
    I looked into it and came to the conclusion that this is controlled
    using 'create_order' while specifying the tests.
    Tests with a lower create_order value are executed earlier.
    So to ensure 'CREATE PUBLICATION' runs correctly we have to make sure
    the 'create_order' of these statements is higher than that of the
    respective 'CREATE TABLE' statement.
    
    > ~~~
    > 18.
    > - if (!defined($tests{$test}->{all_runs})
    > + if (   !defined($tests{$test}->{all_runs})
    >
    > Why add this whitespace?
    >
    pg_perltidy makes this change. I have reverted it.
    
    > ======
    > src/include/nodes/parsenodes.h
    >
    > 19.
    >   AP_SetObjects, /* set list of objects */
    > + AP_Reset, /* reset the publication */
    >  } AlterPublicationAction;
    >
    > AFAIK, you removed all ALTER command changes from v30-0001. So this
    > should not be here.
    >
    > ~~~
    >
    > 20.
    > + bool for_all_tables; /* Special publication for all tables in db */
    >   AlterPublicationAction action; /* What action to perform with the given
    >   * objects */
    >  } AlterPublicationStmt;
    >
    > AFAIK, you removed all ALTER command changes from v30-0001. So this
    > should not be here.
    >
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 21.
    > +SET client_min_messages = 'ERROR';
    > +CREATE PUBLICATION testpub_foralltables_excepttable FOR ALL TABLES
    > EXCEPT TABLE (testpub_tbl1, testpub_tbl2);
    > +-- specify EXCEPT without TABLE
    > +CREATE PUBLICATION testpub_foralltables_excepttable1 FOR ALL TABLES
    > EXCEPT (testpub_tbl1);
    >
    > Should be 2 comments here for the 2x CREATE:
    >
    > # Exclude tables using FOR ALL TABLES EXCEPT TABLE (tablelist)
    >
    > # Exclude tables using FOR ALL TABLES EXCEPT (tablelist)
    >
    > ~~~
    >
    > 22.
    >  CREATE TABLE testpub_tbl3 (a int);
    >  CREATE TABLE testpub_tbl3a (b text) INHERITS (testpub_tbl3);
    >
    > If you rename these tables like 'testpub_tbl_parent' and
    > 'testpub_tbl_child', it will be much easier to see what is going on.
    >
    > ~~~
    >
    > 23.
    > +CREATE PUBLICATION testpub5 FOR ALL TABLES EXCEPT TABLE (testpub_tbl3);
    >
    > Missing comment -- something like:
    > # Exclude parent table, omitting both of 'ONLY' and '*'
    >
    > ~~~
    >
    > 24.
    > +-- EXCEPT with wildcard: exclude table and all descendants
    > +CREATE PUBLICATION testpub6 FOR ALL TABLES EXCEPT TABLE (testpub_tbl3*);
    >
    > 24a.
    > TBH, I don't think this is a "wildcard" -- it is not doing any pattern
    > matching. IMO just call it an "asterisk" or a "star".
    >
    > ~
    >
    > 24b.
    > And put a space before the '*' here.
    >
    > ======
    > .../t/037_rep_changes_except_table.pl
    >
    > 25.
    > +# ============================================
    > +# EXCEPT TABLE test cases for partition tables
    > +# ============================================
    > +# Check behavior of EXCEPT TABLE together with publish_via_partition_root
    > +# when applied to a partitioned table and its partitions.
    >
    >
    > Really, that "Check behavior" sentence is generic for all of the
    > following tests, so it should also be (within the "=======" of the
    > previous comment)
    >
    > ~~~
    >
    > 26.
    > +$node_publisher->safe_psql(
    > + 'postgres', qq(
    > + CREATE TABLE sch1.t1(a int) PARTITION BY RANGE(a);
    > + CREATE TABLE sch1.part1 PARTITION OF sch1.t1 FOR VALUES FROM (0) TO (5);
    > + CREATE TABLE sch1.part2 PARTITION OF sch1.t1 FOR VALUES FROM (6) TO (10);
    > + INSERT INTO sch1.t1 VALUES (1), (6);
    > +));
    > +
    > +$node_subscriber->safe_psql(
    > + 'postgres', qq(
    > + CREATE TABLE sch1.t1(a int);
    > + CREATE TABLE sch1.part1(a int);
    > + CREATE TABLE sch1.part2(a int);
    > +));
    >
    > 26a.
    > There should be a comment for this part that just says something like
    > "Setup partition table and partitions on the publisher that map to
    > normal tables on the subscriber"
    >
    > ~
    >
    > 26b.
    > The INSERT should be done later, after the CREATE PUBLICATION but
    > before the CREATE SUBSCRIPTION. The pattern will be the same for all
    > the test cases.
    >
    > ~~~
    >
    > 27.
    > +$node_publisher->safe_psql('postgres',
    > + "CREATE PUBLICATION tap_pub_part FOR ALL TABLES EXCEPT TABLE (sch1.part1)"
    > +);
    >
    > Even though the publish_via_partition_root is 'false' by default, I
    > think you should spell it out explicitly here for clarity.
    >
    > ~~~
    >
    > 28.
    > +# EXCEPT TABLE (sch1.t1) with publish_via_partition_root = false
    > +# Excluding the partitioned table while publish_via_partition_root = false
    > +# still allows rows inserted into its partitions to be replicated.
    >
    > I felt you should word this differently. I don't think you should say
    > "inserted into its partitions" because actually, you inserted into the
    > partition table, and the data just ends up in the partitions.
    >
    > ~~~
    >
    > 29.
    > +$node_publisher->safe_psql(
    > + 'postgres', qq(
    > + CREATE PUBLICATION tap_pub_part FOR ALL TABLES EXCEPT TABLE (sch1.t1);
    > + INSERT INTO sch1.t1 VALUES (1), (6);
    > +));
    >
    > Ditto earlier comment. Better to explicitly say
    > "publish_via_partition_root=false".
    >
    I have also addressed the remaining comments and attached the latest patch.
    
    Thanks,
    Shlok Kyal
    
  163. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-12-16T09:21:18Z

    On Thu, 11 Dec 2025 at 04:31, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Wed, Dec 10, 2025 at 4:49 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Mon, 24 Nov 2025 at 13:03, Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > ...
    > > > 21.
    > > > I was wondering if the "describe" for tables (e.g. \d+) should also
    > > > show the publications where the table is an ECEPT TABLE? How else is
    > > > the user going to know it has been excluded by some publication?
    > > >
    > > I thought it would be sufficient to show only the list of
    > > publications, the table is part of.
    > > Users can check the excluded tables by checking the description of the
    > > publication using \dRp+.
    > > Will it be not sufficient?
    > > I am not sure why we should show a list of publications which it is not part of?
    > > Am I missing something thoughts?
    >
    > For this comment, I was imagining a scenario where there are dozens of
    > publications, and the user is wondering why their table is not being
    > replicated to the subscriber like they expected it would be.
    >
    > Yes, they could use \dRs+ to identify the publications excluding it,
    > but that will be quite painful if there are very many publications
    > they have to check. IIUC, there is no other way to check it without
    > digging into System Catalogs.
    >
    > That's why I thought it might be useful if the \d+ could also show
    > publications where the table was named in an EXCEPT TABLE clause.
    >
    I thought more about this point and it can be useful. I have added the
    changes for the same in the latest patch in [1].
    
    [1]: https://www.postgresql.org/message-id/CANhcyEWg2WbEW_fFwk0D3J2KBrUF7th6VrE%2BgvESgkUKP9VpZg%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  164. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-12-17T05:54:23Z

    On Tue, Dec 16, 2025 at 2:50 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > I have also addressed the remaining comments and attached the latest patch.
    >
    
    Thanks. A few comments:
    
    1)
    + if (!set_top && puballtables)
    + set_top = !list_member_oid(aexceptpubids, puboid);
    
    In GetTopMostAncestorInPublication(), we have made the above change
    which will now get ancestor from all-tables publication as well,
    provided table is not part of 'except' List. Earlier this function was
    only checking pg_subscription_rel and pg_publication_namespace which
    does not include all-tables publication. Won't it change the
    result-set for callers?
    
    2)
    + * Publications declared with FOR ALL TABLES or FOR ALL SEQUENCES should use
    + * GetAllPublicationRelations() to obtain the complete set of tables covered by
    + * the publication.
    + */
    +List *
    +GetPublicationIncludedRelations(Oid pubid, PublicationPartOpt pub_partopt)
    +{
    + return GetPublicationRelationsInternal(pubid, pub_partopt, false);
    +}
    
    We can have an Assert here that pubid passed is not for ALL-Tables or
    ALL-sequences
    
    3)
    GetAllPublicationRelations:
     * If the publication publishes partition changes via their respective root
     * partitioned tables, we must exclude partitions in favor of including the
     * root partitioned tables. This is not applicable to FOR ALL SEQUENCES
     * publication.
    
    + * The list does not include relations that are explicitly excluded via the
    + * EXCEPT TABLE clause of the publication specified by pubid.
    
    Suggestion:
    /*
     * If the publication publishes partition changes via their respective root
     * partitioned tables, we must exclude partitions in favor of including the
     * root partitioned tables. The list also excludes tables that are
     * explicitly excluded via the EXCEPT TABLE clause of the publication
     * identified by pubid. Neither of these rules applies to FOR ALL SEQUENCES
     * publications.
     */
    
    4)
    GetAllPublicationRelations:
    + if (relkind == RELKIND_RELATION)
    + exceptlist = GetPublicationExcludedRelations(pubid, pubviaroot ?
    + PUBLICATION_PART_ALL :
    + PUBLICATION_PART_ROOT);
    
      Assert(!(relkind == RELKIND_SEQUENCE && pubviaroot));
    
    Generally we keep such parameters' sanity checks as the first step. We
    can add new code after Assert.
    
    5)
    ObjectsInAllPublicationToOids() only has one caller which calls it
    under check: 'if (stmt->for_all_tables)'
    
    Thus IMO, we do not need a switch-case in
    ObjectsInAllPublicationToOids(). We can simply have a sanity check to
    see it is 'PUBLICATION_ALL_TABLES' and then do the needed operation
    for this pub-type.
    
    6)
    CreatePublication():
    /*
    * If publication is for ALL TABLES and relations is not empty, it means
    * that there are some relations to be excluded from the publication.
    * Else, relations is the list of relations to be added to the
    * publication.
    */
    
    Shall we rephrase slightly to:
    
    /*
     * If the publication is for ALL TABLES and 'relations' is not empty,
     * it indicates that some relations should be excluded from the publication.
     * Add those excluded relations to the publication with 'prexcept' set to true.
     * Otherwise, 'relations' contains the list of relations to be explicitly
     * included in the publication.
     */
    
    7)
    + /* Associate objects with the publication. */
    + if (stmt->for_all_tables)
    + {
    + /* Invalidate relcache so that publication info is rebuilt. */
    + CacheInvalidateRelcacheAll();
    + }
    
    I think this comment is misplaced. We shall have it at previous place, atop:
    if (stmt->for_all_tables)
    This is because here we are just trying to invalidate cache while at
    previous place we are trying to associate.
    
    thanks
    Shveta
    
    
    
    
  165. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-12-18T06:00:30Z

    On Wed, Dec 17, 2025 at 11:24 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Dec 16, 2025 at 2:50 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > I have also addressed the remaining comments and attached the latest patch.
    > >
    >
    > Thanks. A few comments:
    >
    > 1)
    > + if (!set_top && puballtables)
    > + set_top = !list_member_oid(aexceptpubids, puboid);
    >
    > In GetTopMostAncestorInPublication(), we have made the above change
    > which will now get ancestor from all-tables publication as well,
    > provided table is not part of 'except' List. Earlier this function was
    > only checking pg_subscription_rel and pg_publication_namespace which
    > does not include all-tables publication. Won't it change the
    > result-set for callers?
    >
    > 2)
    > + * Publications declared with FOR ALL TABLES or FOR ALL SEQUENCES should use
    > + * GetAllPublicationRelations() to obtain the complete set of tables covered by
    > + * the publication.
    > + */
    > +List *
    > +GetPublicationIncludedRelations(Oid pubid, PublicationPartOpt pub_partopt)
    > +{
    > + return GetPublicationRelationsInternal(pubid, pub_partopt, false);
    > +}
    >
    > We can have an Assert here that pubid passed is not for ALL-Tables or
    > ALL-sequences
    >
    > 3)
    > GetAllPublicationRelations:
    >  * If the publication publishes partition changes via their respective root
    >  * partitioned tables, we must exclude partitions in favor of including the
    >  * root partitioned tables. This is not applicable to FOR ALL SEQUENCES
    >  * publication.
    >
    > + * The list does not include relations that are explicitly excluded via the
    > + * EXCEPT TABLE clause of the publication specified by pubid.
    >
    > Suggestion:
    > /*
    >  * If the publication publishes partition changes via their respective root
    >  * partitioned tables, we must exclude partitions in favor of including the
    >  * root partitioned tables. The list also excludes tables that are
    >  * explicitly excluded via the EXCEPT TABLE clause of the publication
    >  * identified by pubid. Neither of these rules applies to FOR ALL SEQUENCES
    >  * publications.
    >  */
    >
    > 4)
    > GetAllPublicationRelations:
    > + if (relkind == RELKIND_RELATION)
    > + exceptlist = GetPublicationExcludedRelations(pubid, pubviaroot ?
    > + PUBLICATION_PART_ALL :
    > + PUBLICATION_PART_ROOT);
    >
    >   Assert(!(relkind == RELKIND_SEQUENCE && pubviaroot));
    >
    > Generally we keep such parameters' sanity checks as the first step. We
    > can add new code after Assert.
    >
    > 5)
    > ObjectsInAllPublicationToOids() only has one caller which calls it
    > under check: 'if (stmt->for_all_tables)'
    >
    > Thus IMO, we do not need a switch-case in
    > ObjectsInAllPublicationToOids(). We can simply have a sanity check to
    > see it is 'PUBLICATION_ALL_TABLES' and then do the needed operation
    > for this pub-type.
    >
    > 6)
    > CreatePublication():
    > /*
    > * If publication is for ALL TABLES and relations is not empty, it means
    > * that there are some relations to be excluded from the publication.
    > * Else, relations is the list of relations to be added to the
    > * publication.
    > */
    >
    > Shall we rephrase slightly to:
    >
    > /*
    >  * If the publication is for ALL TABLES and 'relations' is not empty,
    >  * it indicates that some relations should be excluded from the publication.
    >  * Add those excluded relations to the publication with 'prexcept' set to true.
    >  * Otherwise, 'relations' contains the list of relations to be explicitly
    >  * included in the publication.
    >  */
    >
    > 7)
    > + /* Associate objects with the publication. */
    > + if (stmt->for_all_tables)
    > + {
    > + /* Invalidate relcache so that publication info is rebuilt. */
    > + CacheInvalidateRelcacheAll();
    > + }
    >
    > I think this comment is misplaced. We shall have it at previous place, atop:
    > if (stmt->for_all_tables)
    > This is because here we are just trying to invalidate cache while at
    > previous place we are trying to associate.
    >
    
    Few more:
    
    8)
    get_rel_sync_entry()
    + List    *exceptTablePubids = NIL;
    
    At all other places, we are using exceptpubids, shall we use the same here?
    
    9)
    ObjectsInPublicationToOids()
    
      case PUBLICATIONOBJ_TABLE:
    + case PUBLICATIONOBJ_EXCEPT_TABLE:
    + pubobj->pubtable->except = (pubobj->pubobjtype ==
    PUBLICATIONOBJ_EXCEPT_TABLE);
      *rels = lappend(*rels, pubobj->pubtable);
      break;
    
    It looks slightly odd that for pubobjtype case
    'PUBLICATIONOBJ_EXCEPT_TABLE', we have to check pubobjtype against
    PUBLICATIONOBJ_EXCEPT_TABLE itself.
    
    Shall we make it:
    case PUBLICATIONOBJ_EXCEPT_TABLE:
        pubobj->pubtable->except = true;
        /* fall through */
    case PUBLICATIONOBJ_TABLE:
        *rels = lappend(*rels, pubobj->pubtable);
        break;
    
    10)
    I want to understand the usage of DO_PUBLICATION_EXCEPT_REL. Can you
    give a scenario where its usage in DOTypeNameCompare() will be hit?
    Its all other usages too need some analysis and validation.
    
    11)
    + List    *except_objects; /* List of publication object to be excluded */
    
    object --> objects
    Currently since we exclude only tables, does it make sense to name it
    as except_tables?
    
    thanks
    Shveta
    
    
    
    
  166. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-12-18T11:45:06Z

    On Thu, 18 Dec 2025 at 11:30, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Dec 17, 2025 at 11:24 AM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Tue, Dec 16, 2025 at 2:50 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > I have also addressed the remaining comments and attached the latest patch.
    > > >
    > >
    > > Thanks. A few comments:
    > >
    > > 1)
    > > + if (!set_top && puballtables)
    > > + set_top = !list_member_oid(aexceptpubids, puboid);
    > >
    > > In GetTopMostAncestorInPublication(), we have made the above change
    > > which will now get ancestor from all-tables publication as well,
    > > provided table is not part of 'except' List. Earlier this function was
    > > only checking pg_subscription_rel and pg_publication_namespace which
    > > does not include all-tables publication. Won't it change the
    > > result-set for callers?
    > >
    It can change the result set of callers. I analysed more and saw that
    GetTopMostAncestorInPublication is called from 3 places.
    1. pub_rf_contains_invalid_column: it is called when publication is
    not ALL TABLES. It will have no impact with the change.
    2. pub_contains_invalid_column : it is called for all type of
    publication. it calls GetTopMostAncestorInPublication like:
    ```
        if (pubviaroot && relation->rd_rel->relispartition)
      {
        publish_as_relid = GetTopMostAncestorInPublication(pubid, ancestors,
                                   NULL, puballtables);
    
        if (!OidIsValid(publish_as_relid))
          publish_as_relid = relid;
      }
    ```
    In HEAD for ALL TABLES publication GetTopMostAncestorInPublication
    will always return InvalidOid. With this patch it can have some value.
    So there is a difference in behaviour.
    
    3. get_rel_sync_entry
    in HEAD we had
    ```
    if (pub->alltables)
          {
            publish = true;
            if (pub->pubviaroot && am_partition)
            {
              List     *ancestors = get_partition_ancestors(relid);
    
              pub_relid = llast_oid(ancestors);
              ancestor_level = list_length(ancestors);
            }
          }
    ```
    With patch this condition is not valid because we cannot set
    'pub_relid = llast_oid(ancestors);' directly as the table can be
    excluded.
    So, the change in GetTopMostAncestorInPublication will even handle the
    case of "ALL TABLES" publication.
    
    Since we have a behaviour difference for the 2nd function, I have
    removed the changes for 'ALL TABLES' from
    GetTopMostAncestorInPublication and added it separately
    'get_rel_sync_entry'. Thoughts?
    
    > > 2)
    > > + * Publications declared with FOR ALL TABLES or FOR ALL SEQUENCES should use
    > > + * GetAllPublicationRelations() to obtain the complete set of tables covered by
    > > + * the publication.
    > > + */
    > > +List *
    > > +GetPublicationIncludedRelations(Oid pubid, PublicationPartOpt pub_partopt)
    > > +{
    > > + return GetPublicationRelationsInternal(pubid, pub_partopt, false);
    > > +}
    > >
    > > We can have an Assert here that pubid passed is not for ALL-Tables or
    > > ALL-sequences
    > >
    Added assert for all tables. I found during testing that this function
    can be called for ALL SEQUENCES in HEAD. So I have not added an
    assertion for it.
    I think it is a bug and shared the same in [1]. Will add assert for
    all sequences as well once the bug is fixed.
    
    > > 3)
    > > GetAllPublicationRelations:
    > >  * If the publication publishes partition changes via their respective root
    > >  * partitioned tables, we must exclude partitions in favor of including the
    > >  * root partitioned tables. This is not applicable to FOR ALL SEQUENCES
    > >  * publication.
    > >
    > > + * The list does not include relations that are explicitly excluded via the
    > > + * EXCEPT TABLE clause of the publication specified by pubid.
    > >
    > > Suggestion:
    > > /*
    > >  * If the publication publishes partition changes via their respective root
    > >  * partitioned tables, we must exclude partitions in favor of including the
    > >  * root partitioned tables. The list also excludes tables that are
    > >  * explicitly excluded via the EXCEPT TABLE clause of the publication
    > >  * identified by pubid. Neither of these rules applies to FOR ALL SEQUENCES
    > >  * publications.
    > >  */
    > >
    > > 4)
    > > GetAllPublicationRelations:
    > > + if (relkind == RELKIND_RELATION)
    > > + exceptlist = GetPublicationExcludedRelations(pubid, pubviaroot ?
    > > + PUBLICATION_PART_ALL :
    > > + PUBLICATION_PART_ROOT);
    > >
    > >   Assert(!(relkind == RELKIND_SEQUENCE && pubviaroot));
    > >
    > > Generally we keep such parameters' sanity checks as the first step. We
    > > can add new code after Assert.
    > >
    > > 5)
    > > ObjectsInAllPublicationToOids() only has one caller which calls it
    > > under check: 'if (stmt->for_all_tables)'
    > >
    > > Thus IMO, we do not need a switch-case in
    > > ObjectsInAllPublicationToOids(). We can simply have a sanity check to
    > > see it is 'PUBLICATION_ALL_TABLES' and then do the needed operation
    > > for this pub-type.
    > >
    > > 6)
    > > CreatePublication():
    > > /*
    > > * If publication is for ALL TABLES and relations is not empty, it means
    > > * that there are some relations to be excluded from the publication.
    > > * Else, relations is the list of relations to be added to the
    > > * publication.
    > > */
    > >
    > > Shall we rephrase slightly to:
    > >
    > > /*
    > >  * If the publication is for ALL TABLES and 'relations' is not empty,
    > >  * it indicates that some relations should be excluded from the publication.
    > >  * Add those excluded relations to the publication with 'prexcept' set to true.
    > >  * Otherwise, 'relations' contains the list of relations to be explicitly
    > >  * included in the publication.
    > >  */
    > >
    > > 7)
    > > + /* Associate objects with the publication. */
    > > + if (stmt->for_all_tables)
    > > + {
    > > + /* Invalidate relcache so that publication info is rebuilt. */
    > > + CacheInvalidateRelcacheAll();
    > > + }
    > >
    > > I think this comment is misplaced. We shall have it at previous place, atop:
    > > if (stmt->for_all_tables)
    > > This is because here we are just trying to invalidate cache while at
    > > previous place we are trying to associate.
    > >
    >
    > Few more:
    >
    > 8)
    > get_rel_sync_entry()
    > + List    *exceptTablePubids = NIL;
    >
    > At all other places, we are using exceptpubids, shall we use the same here?
    >
    > 9)
    > ObjectsInPublicationToOids()
    >
    >   case PUBLICATIONOBJ_TABLE:
    > + case PUBLICATIONOBJ_EXCEPT_TABLE:
    > + pubobj->pubtable->except = (pubobj->pubobjtype ==
    > PUBLICATIONOBJ_EXCEPT_TABLE);
    >   *rels = lappend(*rels, pubobj->pubtable);
    >   break;
    >
    > It looks slightly odd that for pubobjtype case
    > 'PUBLICATIONOBJ_EXCEPT_TABLE', we have to check pubobjtype against
    > PUBLICATIONOBJ_EXCEPT_TABLE itself.
    >
    > Shall we make it:
    > case PUBLICATIONOBJ_EXCEPT_TABLE:
    >     pubobj->pubtable->except = true;
    >     /* fall through */
    > case PUBLICATIONOBJ_TABLE:
    >     *rels = lappend(*rels, pubobj->pubtable);
    >     break;
    >
    We should also make pubobj->pubtable->except = false for PUBLICATIONOBJ_TABLE?
    Updated the condition like:
          case PUBLICATIONOBJ_EXCEPT_TABLE:
            pubobj->pubtable->except = true;
            *rels = lappend(*rels, pubobj->pubtable);
            break;
          case PUBLICATIONOBJ_TABLE:
            pubobj->pubtable->except = false;
            *rels = lappend(*rels, pubobj->pubtable);
            break;
    
    > 10)
    > I want to understand the usage of DO_PUBLICATION_EXCEPT_REL. Can you
    > give a scenario where its usage in DOTypeNameCompare() will be hit?
    > Its all other usages too need some analysis and validation.
    >
    In the current patch we are not setting an objecttype to
    DO_PUBLICATION_EXCEPT_REL.
    We are storing the list of except tables in 'pubinfo[i].excepttbls'
    list in function getPublications and "pubinfo[i].dobj.objType =
    DO_PUBLICATION". So, I don't see any requirement of
    DO_PUBLICATION_EXCEPT_REL now. I have removed it.
    
    > 11)
    > + List    *except_objects; /* List of publication object to be excluded */
    >
    > object --> objects
    > Currently since we exclude only tables, does it make sense to name it
    > as except_tables?
    >
    I have also addressed the remaining comments and attached the updated v33 patch.
    [1]: https://www.postgresql.org/message-id/CALDaNm0qoNtsX%2B9KPug6qb%3DuC-H2iPMYW%2BgL%3DHehx%2BNgOxga6w%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  167. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-12-22T05:12:33Z

    On Thu, Dec 18, 2025 at 5:15 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Thu, 18 Dec 2025 at 11:30, shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Wed, Dec 17, 2025 at 11:24 AM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Tue, Dec 16, 2025 at 2:50 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > > >
    > > > > I have also addressed the remaining comments and attached the latest patch.
    > > > >
    > > >
    > > > Thanks. A few comments:
    > > >
    > > > 1)
    > > > + if (!set_top && puballtables)
    > > > + set_top = !list_member_oid(aexceptpubids, puboid);
    > > >
    > > > In GetTopMostAncestorInPublication(), we have made the above change
    > > > which will now get ancestor from all-tables publication as well,
    > > > provided table is not part of 'except' List. Earlier this function was
    > > > only checking pg_subscription_rel and pg_publication_namespace which
    > > > does not include all-tables publication. Won't it change the
    > > > result-set for callers?
    > > >
    > It can change the result set of callers. I analysed more and saw that
    > GetTopMostAncestorInPublication is called from 3 places.
    > 1. pub_rf_contains_invalid_column: it is called when publication is
    > not ALL TABLES. It will have no impact with the change.
    > 2. pub_contains_invalid_column : it is called for all type of
    > publication. it calls GetTopMostAncestorInPublication like:
    > ```
    >     if (pubviaroot && relation->rd_rel->relispartition)
    >   {
    >     publish_as_relid = GetTopMostAncestorInPublication(pubid, ancestors,
    >                                NULL, puballtables);
    >
    >     if (!OidIsValid(publish_as_relid))
    >       publish_as_relid = relid;
    >   }
    > ```
    > In HEAD for ALL TABLES publication GetTopMostAncestorInPublication
    > will always return InvalidOid. With this patch it can have some value.
    > So there is a difference in behaviour.
    >
    > 3. get_rel_sync_entry
    > in HEAD we had
    > ```
    > if (pub->alltables)
    >       {
    >         publish = true;
    >         if (pub->pubviaroot && am_partition)
    >         {
    >           List     *ancestors = get_partition_ancestors(relid);
    >
    >           pub_relid = llast_oid(ancestors);
    >           ancestor_level = list_length(ancestors);
    >         }
    >       }
    > ```
    > With patch this condition is not valid because we cannot set
    > 'pub_relid = llast_oid(ancestors);' directly as the table can be
    > excluded.
    > So, the change in GetTopMostAncestorInPublication will even handle the
    > case of "ALL TABLES" publication.
    >
    > Since we have a behaviour difference for the 2nd function, I have
    > removed the changes for 'ALL TABLES' from
    > GetTopMostAncestorInPublication and added it separately
    > 'get_rel_sync_entry'. Thoughts?
    
    I find the current implementation better, the previous one was
    impacting the results of different paths.
    
    Regarding:
    + if (list_member_oid(aexceptpubids, puboid))
    + {
    + list_free(aexceptpubids);
    + continue;
    + }
    
    IMO, if puboid is part of apubids, that check is enough. This is
    because aexceptpubids and apubids are mutually exclusive lists for a
    particular 'ancestor'. But if we want to have it to avoid
    schma-mapping check later, we should add a comment. How about:
    
    This step isn’t strictly necessary, but we keep it so we can skip the
    table if it appears in the EXCEPT list, avoiding an expensive
    schema-mapping check later.
    
    >
    > > > 2)
    > > > + * Publications declared with FOR ALL TABLES or FOR ALL SEQUENCES should use
    > > > + * GetAllPublicationRelations() to obtain the complete set of tables covered by
    > > > + * the publication.
    > > > + */
    > > > +List *
    > > > +GetPublicationIncludedRelations(Oid pubid, PublicationPartOpt pub_partopt)
    > > > +{
    > > > + return GetPublicationRelationsInternal(pubid, pub_partopt, false);
    > > > +}
    > > >
    > > > We can have an Assert here that pubid passed is not for ALL-Tables or
    > > > ALL-sequences
    > > >
    > Added assert for all tables. I found during testing that this function
    > can be called for ALL SEQUENCES in HEAD. So I have not added an
    > assertion for it.
    > I think it is a bug and shared the same in [1]. Will add assert for
    > all sequences as well once the bug is fixed.
    >
    
    Okay.
    
    > > > 3)
    > > > GetAllPublicationRelations:
    > > >  * If the publication publishes partition changes via their respective root
    > > >  * partitioned tables, we must exclude partitions in favor of including the
    > > >  * root partitioned tables. This is not applicable to FOR ALL SEQUENCES
    > > >  * publication.
    > > >
    > > > + * The list does not include relations that are explicitly excluded via the
    > > > + * EXCEPT TABLE clause of the publication specified by pubid.
    > > >
    > > > Suggestion:
    > > > /*
    > > >  * If the publication publishes partition changes via their respective root
    > > >  * partitioned tables, we must exclude partitions in favor of including the
    > > >  * root partitioned tables. The list also excludes tables that are
    > > >  * explicitly excluded via the EXCEPT TABLE clause of the publication
    > > >  * identified by pubid. Neither of these rules applies to FOR ALL SEQUENCES
    > > >  * publications.
    > > >  */
    > > >
    > > > 4)
    > > > GetAllPublicationRelations:
    > > > + if (relkind == RELKIND_RELATION)
    > > > + exceptlist = GetPublicationExcludedRelations(pubid, pubviaroot ?
    > > > + PUBLICATION_PART_ALL :
    > > > + PUBLICATION_PART_ROOT);
    > > >
    > > >   Assert(!(relkind == RELKIND_SEQUENCE && pubviaroot));
    > > >
    > > > Generally we keep such parameters' sanity checks as the first step. We
    > > > can add new code after Assert.
    > > >
    > > > 5)
    > > > ObjectsInAllPublicationToOids() only has one caller which calls it
    > > > under check: 'if (stmt->for_all_tables)'
    > > >
    > > > Thus IMO, we do not need a switch-case in
    > > > ObjectsInAllPublicationToOids(). We can simply have a sanity check to
    > > > see it is 'PUBLICATION_ALL_TABLES' and then do the needed operation
    > > > for this pub-type.
    > > >
    > > > 6)
    > > > CreatePublication():
    > > > /*
    > > > * If publication is for ALL TABLES and relations is not empty, it means
    > > > * that there are some relations to be excluded from the publication.
    > > > * Else, relations is the list of relations to be added to the
    > > > * publication.
    > > > */
    > > >
    > > > Shall we rephrase slightly to:
    > > >
    > > > /*
    > > >  * If the publication is for ALL TABLES and 'relations' is not empty,
    > > >  * it indicates that some relations should be excluded from the publication.
    > > >  * Add those excluded relations to the publication with 'prexcept' set to true.
    > > >  * Otherwise, 'relations' contains the list of relations to be explicitly
    > > >  * included in the publication.
    > > >  */
    > > >
    > > > 7)
    > > > + /* Associate objects with the publication. */
    > > > + if (stmt->for_all_tables)
    > > > + {
    > > > + /* Invalidate relcache so that publication info is rebuilt. */
    > > > + CacheInvalidateRelcacheAll();
    > > > + }
    > > >
    > > > I think this comment is misplaced. We shall have it at previous place, atop:
    > > > if (stmt->for_all_tables)
    > > > This is because here we are just trying to invalidate cache while at
    > > > previous place we are trying to associate.
    > > >
    > >
    > > Few more:
    > >
    > > 8)
    > > get_rel_sync_entry()
    > > + List    *exceptTablePubids = NIL;
    > >
    > > At all other places, we are using exceptpubids, shall we use the same here?
    > >
    > > 9)
    > > ObjectsInPublicationToOids()
    > >
    > >   case PUBLICATIONOBJ_TABLE:
    > > + case PUBLICATIONOBJ_EXCEPT_TABLE:
    > > + pubobj->pubtable->except = (pubobj->pubobjtype ==
    > > PUBLICATIONOBJ_EXCEPT_TABLE);
    > >   *rels = lappend(*rels, pubobj->pubtable);
    > >   break;
    > >
    > > It looks slightly odd that for pubobjtype case
    > > 'PUBLICATIONOBJ_EXCEPT_TABLE', we have to check pubobjtype against
    > > PUBLICATIONOBJ_EXCEPT_TABLE itself.
    > >
    > > Shall we make it:
    > > case PUBLICATIONOBJ_EXCEPT_TABLE:
    > >     pubobj->pubtable->except = true;
    > >     /* fall through */
    > > case PUBLICATIONOBJ_TABLE:
    > >     *rels = lappend(*rels, pubobj->pubtable);
    > >     break;
    > >
    > We should also make pubobj->pubtable->except = false for PUBLICATIONOBJ_TABLE?
    
    yes, right.
    
    > Updated the condition like:
    >       case PUBLICATIONOBJ_EXCEPT_TABLE:
    >         pubobj->pubtable->except = true;
    >         *rels = lappend(*rels, pubobj->pubtable);
    >         break;
    >       case PUBLICATIONOBJ_TABLE:
    >         pubobj->pubtable->except = false;
    >         *rels = lappend(*rels, pubobj->pubtable);
    >         break;
    >
    
    Looks good.
    
    > > 10)
    > > I want to understand the usage of DO_PUBLICATION_EXCEPT_REL. Can you
    > > give a scenario where its usage in DOTypeNameCompare() will be hit?
    > > Its all other usages too need some analysis and validation.
    > >
    > In the current patch we are not setting an objecttype to
    > DO_PUBLICATION_EXCEPT_REL.
    > We are storing the list of except tables in 'pubinfo[i].excepttbls'
    > list in function getPublications and "pubinfo[i].dobj.objType =
    > DO_PUBLICATION". So, I don't see any requirement of
    > DO_PUBLICATION_EXCEPT_REL now. I have removed it.
    >
    
    Yes, that was my initial thought as well, that we might not need it.
    But I’ll review it further and let you know.
    
    > > 11)
    > > + List    *except_objects; /* List of publication object to be excluded */
    > >
    > > object --> objects
    > > Currently since we exclude only tables, does it make sense to name it
    > > as except_tables?
    > >
    > I have also addressed the remaining comments and attached the updated v33 patch.
    > [1]: https://www.postgresql.org/message-id/CALDaNm0qoNtsX%2B9KPug6qb%3DuC-H2iPMYW%2BgL%3DHehx%2BNgOxga6w%40mail.gmail.com
    >
    
    Thanks, will review.
    
    thanks
    Shveta
    
    
    
    
  168. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-12-22T06:07:27Z

    Hi Shlok.
    
    Some review comments for patch v33-0001 (code part)
    
    ======
    src/backend/catalog/pg_publication.c
    
    GetPublicationRelationsInternal:
    
    1.
    Static function names should be snake_case.
    
    ~~~
    
    GetPublicationIncludedRelations:
    
    2.
    +/*
    + * Return the list of relation OIDs for a publication.
    + *
    + * For a FOR TABLE publication, this returns the list of relations explicitly
    + * included in the publication.
    + *
    + * Publications declared with FOR ALL TABLES or FOR ALL SEQUENCES should use
    + * GetAllPublicationRelations() to obtain the complete set of tables covered by
    + * the publication.
    + */
    +List *
    +GetPublicationIncludedRelations(Oid pubid, PublicationPartOpt pub_partopt)
    +{
    + Assert(!GetPublication(pubid)->alltables);
    +
    + return GetPublicationRelationsInternal(pubid, pub_partopt, false);
    +}
    
    Why isn't the Assert also saying something about puballsequences, as
    mentioned in the function comment?
    
    ~~~
    
    GetAllPublicationRelations:
    
    3.
    + * root partitioned tables. The list also excludes tables that are
    + * explicitly excluded via the EXCEPT TABLE clause of the publication
    + * identified by pubid. Neither of these rules applies to FOR ALL SEQUENCES
    + * publications.
    
    3.
    It seems wrong to say "FOR ALL SEQUENCES" ... that seems to assume the
    "FOR ALL SEQUENCES" and "FOR ALL TABLES" cannot co-exist. Did you mean
    "Neither of ... to published sequences"?
    
    ~
    
    4.
    -GetAllPublicationRelations(char relkind, bool pubviaroot)
    +GetAllPublicationRelations(Oid pubid, char relkind, bool pubviaroot)
    
    There are tricky rules about relation vs sequences and the
    publish_via_partition_root parameter value. It would be better if you
    encapsulate all this within this function. Specifically, it would be
    simpler if you passed the 'Publication' arg instead of the pubid. Then
    you can get the pubviaroot value from that (within the function)
    instead of passing around "fake" values of false when you are looking
    at RELKIND_SEQUENCE.
    ======
    src/backend/commands/publicationcmds.c
    
    ObjectsInAllPublicationToOids:
    
    5.
    + foreach_ptr(PublicationAllObjSpec, puballobj, puballobjspec_list)
    + {
    + if (puballobj->pubobjtype != PUBLICATION_ALL_TABLES)
    + continue;
    +
    + foreach_ptr(PublicationObjSpec, pubobj, puballobj->except_tables)
    + {
    + pubobj->pubtable->except = true;
    + *rels = lappend(*rels, pubobj->pubtable);
    + }
    + }
    
    I think it's tidier to code this like below:
    
    if (puballobj->pubobjtype == PUBLICATION_ALL_TABLES)
    {
      foreach_ptr...
    }
    
    ~~~
    
    pub_contains_invalid_column:
    
    6.
     bool
     pub_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
      bool pubviaroot, char pubgencols_type,
    - bool *invalid_column_list,
    - bool *invalid_gen_col)
    + bool *invalid_column_list, bool *invalid_gen_col)
    
    Why does this change even exist at all in this patch?
    
    ~~~
    
    CreatePublication:
    
    7.
    + /*
    + * If the publication is for ALL TABLES and 'relations' is not empty, it
    + * indicates that some relations should be excluded from the publication.
    + * Add those excluded relations to the publication with 'prexcept' set to
    + * true. Otherwise, 'relations' contains the list of relations to be
    + * explicitly included in the publication.
    + */
    + if (relations != NIL)
    + {
    + List    *rels;
    +
    + rels = OpenTableList(relations);
    + TransformPubWhereClauses(rels, pstate->p_sourcetext,
    + publish_via_partition_root);
    +
    + CheckPubRelationColumnList(stmt->pubname, rels,
    +    schemaidlist != NIL,
    +    publish_via_partition_root);
    +
    + PublicationAddTables(puboid, rels, true, NULL);
    + CloseTableList(rels);
    + }
    +
    
    The comment and the code don't match. The comment is talking about
    rules for FOR ALL TABLES, but puballtables is not part of any
    condition here (??). Was all this supposed to be within the "if
    (stmt->for_all_tables)" code block?
    
    ======
    src/bin/pg_dump/pg_dump.c
    
    8.
    - "SELECT tableoid, oid, prpubid, prrelid, "
    + "SELECT tableoid, oid, prpubid, prrelid,\n"
      "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
      "(CASE\n"
      "  WHEN pr.prattrs IS NOT NULL THEN\n"
    @@ -4868,6 +4929,9 @@ getPublicationTables(Archive *fout, TableInfo
    tblinfo[], int numTables)
      "      WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
      "  ELSE NULL END) prattrs "
      "FROM pg_catalog.pg_publication_rel pr");
    + if (fout->remoteVersion >= 190000)
    + appendPQExpBufferStr(query, " WHERE prexcept = false");
    
    8a
    Isn't it better to qualify everything here with the alias 'pr'?
    
    ~
    
    8b.
    Also "WHERE NOT pr.prexcept;" might be more conssitent with other code
    I saw in describe.c
    
    ======
    src/bin/pg_dump/pg_dump.h
    
    9.
      PublishGencolsType pubgencols_type;
    + SimplePtrList excepttbls;
     } PublicationInfo;
    
    How about "tables instead of "tbls" (e.g. "excepttables" or
    "except_tables") here? That would also be more consistent with the
    other puballtables member.
    
    ======
    src/test/regress/sql/publication.sql
    
    10.
     RESET client_min_messages;
     \dRp+ testpub3
     \dRp+ testpub4
    +\dRp+ testpub5
    +\dRp+ testpub6
    +\dRp+ testpub7
    
    
    I feel it would be better to keep each \dRp+ together with the test it
    belongs to, rather than have a bunch of different tests which are then
    followed by a bunch of different \dRp+. Note: this same comment
    applies to other place of places -- not just here. Check everywhere
    you do \dRp+
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  169. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2025-12-23T06:32:49Z

    On Mon, 22 Dec 2025 at 11:37, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Some review comments for patch v33-0001 (code part)
    >
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > GetPublicationRelationsInternal:
    >
    > 1.
    > Static function names should be snake_case.
    >
    > ~~~
    >
    > GetPublicationIncludedRelations:
    >
    > 2.
    > +/*
    > + * Return the list of relation OIDs for a publication.
    > + *
    > + * For a FOR TABLE publication, this returns the list of relations explicitly
    > + * included in the publication.
    > + *
    > + * Publications declared with FOR ALL TABLES or FOR ALL SEQUENCES should use
    > + * GetAllPublicationRelations() to obtain the complete set of tables covered by
    > + * the publication.
    > + */
    > +List *
    > +GetPublicationIncludedRelations(Oid pubid, PublicationPartOpt pub_partopt)
    > +{
    > + Assert(!GetPublication(pubid)->alltables);
    > +
    > + return GetPublicationRelationsInternal(pubid, pub_partopt, false);
    > +}
    >
    > Why isn't the Assert also saying something about puballsequences, as
    > mentioned in the function comment?
    >
    I reported a similar kind of issue in HEAD in [1].
    As per the latest discussion, I understood that it is ok to call this
    function for ALL SEQUENCES.
    I have updated the comment.
    
    > ~~~
    >
    > GetAllPublicationRelations:
    >
    > 3.
    > + * root partitioned tables. The list also excludes tables that are
    > + * explicitly excluded via the EXCEPT TABLE clause of the publication
    > + * identified by pubid. Neither of these rules applies to FOR ALL SEQUENCES
    > + * publications.
    >
    > 3.
    > It seems wrong to say "FOR ALL SEQUENCES" ... that seems to assume the
    > "FOR ALL SEQUENCES" and "FOR ALL TABLES" cannot co-exist. Did you mean
    > "Neither of ... to published sequences"?
    >
    I have modified the comment.
    
    > ~
    >
    > 4.
    > -GetAllPublicationRelations(char relkind, bool pubviaroot)
    > +GetAllPublicationRelations(Oid pubid, char relkind, bool pubviaroot)
    >
    > There are tricky rules about relation vs sequences and the
    > publish_via_partition_root parameter value. It would be better if you
    > encapsulate all this within this function. Specifically, it would be
    > simpler if you passed the 'Publication' arg instead of the pubid. Then
    > you can get the pubviaroot value from that (within the function)
    > instead of passing around "fake" values of false when you are looking
    > at RELKIND_SEQUENCE.
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > ObjectsInAllPublicationToOids:
    >
    > 5.
    > + foreach_ptr(PublicationAllObjSpec, puballobj, puballobjspec_list)
    > + {
    > + if (puballobj->pubobjtype != PUBLICATION_ALL_TABLES)
    > + continue;
    > +
    > + foreach_ptr(PublicationObjSpec, pubobj, puballobj->except_tables)
    > + {
    > + pubobj->pubtable->except = true;
    > + *rels = lappend(*rels, pubobj->pubtable);
    > + }
    > + }
    >
    > I think it's tidier to code this like below:
    >
    > if (puballobj->pubobjtype == PUBLICATION_ALL_TABLES)
    > {
    >   foreach_ptr...
    > }
    >
    > ~~~
    >
    > pub_contains_invalid_column:
    >
    > 6.
    >  bool
    >  pub_contains_invalid_column(Oid pubid, Relation relation, List *ancestors,
    >   bool pubviaroot, char pubgencols_type,
    > - bool *invalid_column_list,
    > - bool *invalid_gen_col)
    > + bool *invalid_column_list, bool *invalid_gen_col)
    >
    > Why does this change even exist at all in this patch?
    This change is not required. I have reverted it.
    
    >
    > ~~~
    >
    > CreatePublication:
    >
    > 7.
    > + /*
    > + * If the publication is for ALL TABLES and 'relations' is not empty, it
    > + * indicates that some relations should be excluded from the publication.
    > + * Add those excluded relations to the publication with 'prexcept' set to
    > + * true. Otherwise, 'relations' contains the list of relations to be
    > + * explicitly included in the publication.
    > + */
    > + if (relations != NIL)
    > + {
    > + List    *rels;
    > +
    > + rels = OpenTableList(relations);
    > + TransformPubWhereClauses(rels, pstate->p_sourcetext,
    > + publish_via_partition_root);
    > +
    > + CheckPubRelationColumnList(stmt->pubname, rels,
    > +    schemaidlist != NIL,
    > +    publish_via_partition_root);
    > +
    > + PublicationAddTables(puboid, rels, true, NULL);
    > + CloseTableList(rels);
    > + }
    > +
    >
    > The comment and the code don't match. The comment is talking about
    > rules for FOR ALL TABLES, but puballtables is not part of any
    > condition here (??). Was all this supposed to be within the "if
    > (stmt->for_all_tables)" code block?
    >
    For both ALL TABLES publication and non-ALL TABLES publication we need
    the same code block.
    Setting of prexcept flag will be handled in PublicationAddTables.
    This comment clarifies what the list 'relations' would mean for ALL
    TABLES publication and non-ALL TABLES publication
    
    > ======
    > src/bin/pg_dump/pg_dump.c
    >
    > 8.
    > - "SELECT tableoid, oid, prpubid, prrelid, "
    > + "SELECT tableoid, oid, prpubid, prrelid,\n"
    >   "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
    >   "(CASE\n"
    >   "  WHEN pr.prattrs IS NOT NULL THEN\n"
    > @@ -4868,6 +4929,9 @@ getPublicationTables(Archive *fout, TableInfo
    > tblinfo[], int numTables)
    >   "      WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
    >   "  ELSE NULL END) prattrs "
    >   "FROM pg_catalog.pg_publication_rel pr");
    > + if (fout->remoteVersion >= 190000)
    > + appendPQExpBufferStr(query, " WHERE prexcept = false");
    >
    > 8a
    > Isn't it better to qualify everything here with the alias 'pr'?
    >
    It is an existing code. So I prefer not to modify it in this patch. I
    have added the alias for the column added by this patch.
    
    > ~
    >
    > 8b.
    > Also "WHERE NOT pr.prexcept;" might be more conssitent with other code
    > I saw in describe.c
    >
    > ======
    > src/bin/pg_dump/pg_dump.h
    >
    > 9.
    >   PublishGencolsType pubgencols_type;
    > + SimplePtrList excepttbls;
    >  } PublicationInfo;
    >
    > How about "tables instead of "tbls" (e.g. "excepttables" or
    > "except_tables") here? That would also be more consistent with the
    > other puballtables member.
    >
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 10.
    >  RESET client_min_messages;
    >  \dRp+ testpub3
    >  \dRp+ testpub4
    > +\dRp+ testpub5
    > +\dRp+ testpub6
    > +\dRp+ testpub7
    >
    >
    > I feel it would be better to keep each \dRp+ together with the test it
    > belongs to, rather than have a bunch of different tests which are then
    > followed by a bunch of different \dRp+. Note: this same comment
    > applies to other place of places -- not just here. Check everywhere
    > you do \dRp+
    >
    
    I have addressed the remaining comments, did some cosmetic changes and
    addressed the comment shared by Shveta in [2].
    [1]: https://www.postgresql.org/message-id/CAA4eK1+rnjBOvkiQC2r4LuTwuje653iVPPAXcmJZXPpKvsNbOQ@mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAJpy0uCf5tXvqyVS3GQzU9J5HdSLAxX6Lxt1UKY4HJ8qnimCAw%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  170. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2025-12-24T03:12:31Z

    Hi Shlok
    
    Some review comments for patch v34-0001 (code)
    
    ======
    src/backend/catalog/pg_publication.c
    
    1.
    +static List *
    +get_publication_relations_internal(Oid pubid, PublicationPartOpt pub_partopt,
    +    bool except_flag)
    
    No need to name this function as "_internal"; the snake_case name and
    static already indicate it is internal.
    
    ======
    src/bin/pg_dump/pg_dump.c
    
    getPublications:
    
    2.
    + if (fout->remoteVersion >= 190000)
    + {
    + int ntbls;
    + PGresult   *res_tbls;
    +
    + resetPQExpBuffer(query);
    + appendPQExpBuffer(query,
    +   "SELECT prrelid\n"
    +   "FROM pg_catalog.pg_publication_rel\n"
    +   "WHERE prpubid = %u and prexcept = true",
    +   pubinfo[i].dobj.catId.oid);
    +
    + res_tbls = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
    +
    + ntbls = PQntuples(res_tbls);
    + if (ntbls == 0)
    + continue;
    +
    + for (int j = 0; j < ntbls; j++)
    + {
    + Oid prrelid;
    + TableInfo  *tbinfo;
    +
    + prrelid = atooid(PQgetvalue(res_tbls, j, 0));
    +
    + tbinfo = findTableByOid(prrelid);
    + if (tbinfo == NULL)
    + continue;
    +
    + simple_ptr_list_append(&pubinfo[i].except_tables, tbinfo);
    + }
    +
    + PQclear(res_tbls);
    + }
    
    2a.
    I suppose this code is for populating the list of all tables except
    those excluded, but there is no explanatory comment stating the
    purpose of all this.
    
    ~
    
    2b.
    BEFORE
    "WHERE prpubid = %u and prexcept = true"
    
    SUGGESTION
    "WHERE prpubid = %u AND prexcept"
    
    ~~~
    
    dumpPublication:
    
    3.
    + {
    + bool first_tbl = true;
    +
      appendPQExpBufferStr(query, " FOR ALL TABLES");
    +
    + /* Include exception tables if the publication has EXCEPT TABLEs */
    + for (SimplePtrListCell *cell = pubinfo->except_tables.head; cell;
    cell = cell->next)
    + {
    + TableInfo  *tbinfo = (TableInfo *) cell->ptr;
    +
    + if (first_tbl)
    + {
    + appendPQExpBufferStr(query, " EXCEPT TABLE (");
    + first_tbl = false;
    + }
    + else
    + appendPQExpBufferStr(query, ", ");
    + appendPQExpBuffer(query, "ONLY %s", fmtQualifiedDumpable(tbinfo));
    + }
    + if (!first_tbl)
    + appendPQExpBufferStr(query, ")");
    + }
    
    3a.
    That code comment seems backwards.
    
    BEFORE
    /* Include exception tables if the publication has EXCEPT TABLEs */
    
    SUGGESTION
    /* Include EXCEPT TABLE clause if there are except_tables. */
    
    ~~~
    
    3b.
    Although it works OK, I felt the following looked strange:
    + if (!first_tbl)
    + appendPQExpBufferStr(query, ")");
    
    IMO it would be better implemented as a counter:
    
    Replace
    bool first_tbl = true;
    with
    int n_excluded = 0;
    
    Then,
    + if (first_tbl)
    + {
    + appendPQExpBufferStr(query, " EXCEPT TABLE (");
    + first_tbl = false;
    + }
    becomes
    + if (++n_excluded == 1)
    + appendPQExpBufferStr(query, " EXCEPT TABLE (");
    
    And,
    + if (!first_tbl)
    + appendPQExpBufferStr(query, ")");
    becomes
    + if (n_excluded > 0)
    + appendPQExpBufferStr(query, ")");
    
    ======
    src/bin/psql/describe.c
    
    describeOneTableDetails:
    
    4.
    + /* Print publication the relation is excluded explicitly */
    + if (pset.sversion >= 190000)
    
    The comment doesn't seem right:
    
    SUGGESTION
    Print publications that the table is explicitly excluded from
    
    ======
    src/test/regress/sql/publication.sql
    
    5.
    Missing tests.
    
    There are no test cases to show that \d is working for printing the
    "Except Publications:".
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  171. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2025-12-26T09:57:22Z

    On Tue, Dec 23, 2025 at 12:03 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    >
    > I have addressed the remaining comments, did some cosmetic changes and
    > addressed the comment shared by Shveta in [2].
    > [1]: https://www.postgresql.org/message-id/CAA4eK1+rnjBOvkiQC2r4LuTwuje653iVPPAXcmJZXPpKvsNbOQ@mail.gmail.com
    > [2]: https://www.postgresql.org/message-id/CAJpy0uCf5tXvqyVS3GQzU9J5HdSLAxX6Lxt1UKY4HJ8qnimCAw%40mail.gmail.com
    >
    
    Thank You for the patch. Please find a few comments:
    
    1)
    GetTopMostAncestorInPublication():
    
    + if (list_member_oid(aexceptpubids, puboid))
    + {
    + list_free(aexceptpubids);
    + continue;
    + }
    
    We need to do 'list_free(apubids)' as well here.
    
    2)
    GetTopMostAncestorInPublication(). Currently it has:
    
    if (list_member_oid(aexceptpubids, puboid))
    ...
    if (list_member_oid(apubids, puboid))
    ...
    else
    ...schema mapping check
    
    IMO more natural order of checks will be
    
    if (list_member_oid(apubids, puboid))
    ..
    else if (list_member_oid(aexceptpubids, puboid))
    ...
    else
    ...schema mapping check
    
    3)
    +/*
    + * Return the list of relation OIDs excluded from a publication.
    + * This is only applicable for FOR ALL TABLES publications.
    + */
    +List *
    +GetPublicationExcludedRelations(Oid pubid, PublicationPartOpt pub_partopt)
    
    a) Since now 'Relations' term means both tables and sequences, but
    here we mean only Tables, we can rename it to have 'Tables' rather
    than 'Relations'
    
    b) Similar to GetAllPublicationRelations which is for 'ALL Tables'
    pub, we can rename it to have 'All'
    
    So the name can be 'GetAllPublicationExcludedTables' to be more clear.
    
    Also we can move this function close to GetAllPublicationRelations as
    it is more related to that.
    
    4)
    ObjectsInPublicationToOids()
    + case PUBLICATIONOBJ_EXCEPT_TABLE:
    + pubobj->pubtable->except = true;
    + *rels = lappend(*rels, pubobj->pubtable);
    + break;
    
    Let me know when this will be hit when we already have
    'ObjectsInAllPublicationToOids' in place?
    
    5)
    get_rel_sync_entry():
    + level++;
    + GetRelationPublications(ancestor, NULL, &aexceptpubids);
    +
    + if (!list_member_oid(aexceptpubids, pub->oid))
    + {
    + pub_relid = ancestor;
    + ancestor_level = level;
    + }
    + }
    
    Consider the following table structure:
    t1 has a partition p1, which in turn has a child partition
    child_part1. When publish_via_partition_root is set to true, any
    changes made to child_part1 are replicated through t1. If we add t1 to
    the EXCEPT list, get_rel_sync_entry() still marks p1 as an ancestor to
    publish changes or child_part1. Is it correct?
    
    6)
    RelationBuildPublicationDesc() also needs some more analysis about
    getting and setting ancestor part for above case.
    
    7)
    Currently the way we deal with the except table in pg_dump.c differs
    from how we deal with included-table. To explain the same, how about
    adding below comment in getPublications() just before we fetch
    except-list:
    
    We process EXCEPT TABLES here instead of in getPublicationTables(),
    and output them directly in dumpPublication(). This differs from the
    approach used in dumpPublicationTable() and
    dumpPublicationNamespace(). Following that approach would require
    dumping table additions later as ALTER PUBLICATION … ADD EXCEPT, which
    is currently not supported.
    
    thanks
    Shveta
    
    
    
    
  172. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-01-06T12:43:17Z

    On Fri, 26 Dec 2025 at 15:27, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Dec 23, 2025 at 12:03 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > >
    > > I have addressed the remaining comments, did some cosmetic changes and
    > > addressed the comment shared by Shveta in [2].
    > > [1]: https://www.postgresql.org/message-id/CAA4eK1+rnjBOvkiQC2r4LuTwuje653iVPPAXcmJZXPpKvsNbOQ@mail.gmail.com
    > > [2]: https://www.postgresql.org/message-id/CAJpy0uCf5tXvqyVS3GQzU9J5HdSLAxX6Lxt1UKY4HJ8qnimCAw%40mail.gmail.com
    > >
    >
    > Thank You for the patch. Please find a few comments:
    >
    > 1)
    > GetTopMostAncestorInPublication():
    >
    > + if (list_member_oid(aexceptpubids, puboid))
    > + {
    > + list_free(aexceptpubids);
    > + continue;
    > + }
    >
    > We need to do 'list_free(apubids)' as well here.
    >
    > 2)
    > GetTopMostAncestorInPublication(). Currently it has:
    >
    > if (list_member_oid(aexceptpubids, puboid))
    > ...
    > if (list_member_oid(apubids, puboid))
    > ...
    > else
    > ...schema mapping check
    >
    > IMO more natural order of checks will be
    >
    > if (list_member_oid(apubids, puboid))
    > ..
    > else if (list_member_oid(aexceptpubids, puboid))
    > ...
    > else
    > ...schema mapping check
    >
    While analyzing this behavior, I noticed that changes to tables listed
    in the EXCEPT clause were still being published. However, those
    changes were not replicated, because pg_get_publication_tables()
    correctly excluded those tables, and consequently pg_subscription_rel
    was populated correctly on the subscriber side.
    Even though replication was prevented downstream, we should not
    publish changes for tables in the EXCEPT list in the first place.
    Publishing them is unnecessary and inconsistent with the publication
    definition. This issue has been addressed in the latest version of the
    patch
    And the above two changes are not required.
    
    > 3)
    > +/*
    > + * Return the list of relation OIDs excluded from a publication.
    > + * This is only applicable for FOR ALL TABLES publications.
    > + */
    > +List *
    > +GetPublicationExcludedRelations(Oid pubid, PublicationPartOpt pub_partopt)
    >
    > a) Since now 'Relations' term means both tables and sequences, but
    > here we mean only Tables, we can rename it to have 'Tables' rather
    > than 'Relations'
    >
    > b) Similar to GetAllPublicationRelations which is for 'ALL Tables'
    > pub, we can rename it to have 'All'
    >
    > So the name can be 'GetAllPublicationExcludedTables' to be more clear.
    >
    > Also we can move this function close to GetAllPublicationRelations as
    > it is more related to that.
    >
    > 4)
    > ObjectsInPublicationToOids()
    > + case PUBLICATIONOBJ_EXCEPT_TABLE:
    > + pubobj->pubtable->except = true;
    > + *rels = lappend(*rels, pubobj->pubtable);
    > + break;
    >
    > Let me know when this will be hit when we already have
    > 'ObjectsInAllPublicationToOids' in place?
    I analysed it and found that we can eliminate the use of
    'ObjectsInAllPublicationToOids'. This requires a change in gram.y.
    Made the changes in the latest patch.
    
    >
    > 5)
    > get_rel_sync_entry():
    > + level++;
    > + GetRelationPublications(ancestor, NULL, &aexceptpubids);
    > +
    > + if (!list_member_oid(aexceptpubids, pub->oid))
    > + {
    > + pub_relid = ancestor;
    > + ancestor_level = level;
    > + }
    > + }
    >
    > Consider the following table structure:
    > t1 has a partition p1, which in turn has a child partition
    > child_part1. When publish_via_partition_root is set to true, any
    > changes made to child_part1 are replicated through t1. If we add t1 to
    > the EXCEPT list, get_rel_sync_entry() still marks p1 as an ancestor to
    > publish changes or child_part1. Is it correct?
    >
    This change is not required. See reply to comment 1 and 2.
    
    > 6)
    > RelationBuildPublicationDesc() also needs some more analysis about
    > getting and setting ancestor part for above case.
    >
    The case for partition tables can be tricky. Currently I am analysing
    the behaviour of partitioned tables with the EXCEPT TABLE option in
    general.
    Will address it in the next version upon further analysing.
    
    > 7)
    > Currently the way we deal with the except table in pg_dump.c differs
    > from how we deal with included-table. To explain the same, how about
    > adding below comment in getPublications() just before we fetch
    > except-list:
    >
    > We process EXCEPT TABLES here instead of in getPublicationTables(),
    > and output them directly in dumpPublication(). This differs from the
    > approach used in dumpPublicationTable() and
    > dumpPublicationNamespace(). Following that approach would require
    > dumping table additions later as ALTER PUBLICATION … ADD EXCEPT, which
    > is currently not supported.
    
    Also addressed the remaining comments. I have also addressed the
    comments by Peter in [1]. I have also done some minor cosmetic
    changes.
    [1]: https://www.postgresql.org/message-id/CAHut+PuZX_7Ot-oh5iqGLBRrZBS5ewDnHa91mJi2Y09uCRfixg@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  173. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-01-08T04:20:51Z

    Hi Shlok.
    
    Some review comments for the v35-0001 patch (code)
    
    ======
    src/backend/replication/pgoutput/pgoutput.c
    
    get_rel_sync_entry:
    
    1.
      /*
      * If this is a FOR ALL TABLES publication, pick the partition
      * root and set the ancestor level accordingly.
    + *
    + * If this is a FOR ALL TABLES publication and it has an EXCEPT
    + * TABLE list:
    + *
    + * 1. If pubviaroot is set and the relation is a partition, check
    + * whether the partition root is included in the EXCEPT TABLE
    + * list. If so, do not publish the change.
    + *
    + * 2. If pubviaroot is not set, check whether the relation itself
    + * is included in the EXCEPT TABLE list. If so, do not publish the
    + * change.
    + *
    + * This is achieved by keeping the variable "publish" set to
    + * false. And eventually, entry->pubactions will remain all false
    + * for this publication.
      */
    
    For that last para ("This is achieved by..."), it is unclear what
    "This" is referring to. I think you mean like below:
    
    SUGGESTION
    Note - "do not publish the change" is achieved by...
    
    ======
    src/bin/pg_dump/pg_dump.c
    
    getPublications:
    
    2.
    + ntbls = PQntuples(res_tbls);
    + if (ntbls == 0)
    + continue;
    +
    + for (int j = 0; j < ntbls; j++)
    + {
    + Oid prrelid;
    + TableInfo  *tbinfo;
    +
    + prrelid = atooid(PQgetvalue(res_tbls, j, 0));
    +
    + tbinfo = findTableByOid(prrelid);
    + if (tbinfo == NULL)
    + continue;
    +
    + simple_ptr_list_append(&pubinfo[i].except_tables, tbinfo);
    + }
    +
    + PQclear(res_tbls);
    
    2a.
    That first condition with 'continue' looks like it would be better
    just to remove it. Otherwise, the PQclear(res_tbls) may leak. Anyway,
    the loop will never iterate if ntbls is 0, so where is the harm in
    removing this?
    
    ~
    
    2b.
    Also, that "if (tbinfo == NULL)" seems overkill because it is only
    avoiding the final statement of the loop. It might be better to
    replace this like below:
    
    if (tblinfo != NULL)
     simple_ptr_list_append(...);
    
    ~~~
    
    dumpPublication:
    
    3.
    + appendPQExpBuffer(query, "ONLY %s", fmtQualifiedDumpable(tbinfo));
    
    I think that unconditionally choosing "ONLY" here may not be the right
    thing to do, particularly when the excluded table is a partitioned
    table. (e.g. this is related to off-list discussions about how to
    EXCEPT partition tables).
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  174. Re: Skipping schema changes in publication

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

    On Tue, Jan 6, 2026 at 6:13 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > Also addressed the remaining comments. I have also addressed the
    > comments by Peter in [1]. I have also done some minor cosmetic
    > changes.
    >
    
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (t1,t2);
    
    Did we consider using EXCLUDE instead of EXCEPT? In another similar
    feature being discussed, the community is proposing to use EXCLUDE to
    SQL Standard, so won't it be better to use EXCLUDE here as well?
    
    [1] - https://www.postgresql.org/message-id/63e1587b-4258-41de-b823-948f8cc692d9%40eisentraut.org
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  175. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-01-19T09:38:17Z

    Approaches for Supporting EXCEPT in Partitioned Tables
    ------------------------------------------------------------------------
    
    In an offline discussion with Peter Smith, Amit, and Shlok, we
    identified several approaches for supporting EXCEPT with partitioned
    tables and their partitions. I’d like to hear others’ opinions on
    these approaches.
    
    Consider the following partition hierarchy:
    tab_root
      ├─ tab_part_1
      │   ├─ tab_part_1_p1
      │   └─ tab_part_1_p2
      └─ tab_part_2
          ├─ tab_part_2_p1
          └─ tab_part_2_p2
    
    
    Approach 1:
    ---------------------------------
    If we exclude a table, then the data in that table and all of its
    partitions (i.e., the entire subtree under that table) should not be
    replicated.
    
    For example EXCEPT (tab_part_1) skips replication of tab_part_1 and
    all of its partitions.
    
    This behaviour remains the same with or without
    publish_via_partition_root. The publish_via_partition_root flag only
    affects publish_via_relid, i.e., the relation through which data is
    published.
    
    This approach involves certain implementation challenges. For brevity,
    these are documented in the attached 'Approach1_challenges' document.
    
    Approach 2:
    ---------------------------------------------------
    Assign meaning to ONLY and '*' for partition tables in the EXCEPT
    list. In HEAD, ONLY and '*' do not have any meaning for partitioned
    tables or partitions, and these keywords are currently ignored.
    
    Examples:
    1. EXCEPT (ONLY tab_part_1) skips replication of only the table
    tab_part_1. Changes for tab_root, tab_part_1_p1, and tab_part_1_p2 are
    still replicated.
    
    ii. EXCEPT (tab_part_1*) skips replication of tables tab_part_1,
    tab_part_1_p1, and tab_part_1_p2
    
    The challenges described in Approach 1, particularly around tablesync
    handling and COPY behaviour, would still need to be addressed under
    this approach as well. ONLY or '*' with partitioned tables is not
    supported in HEAD, supporting it specifically for ALL TABLES EXCEPT
    may introduce additional confusion for users.
    
    Approach 3:
    ----------------
    Do not allow partitions to be specified in the EXCEPT clause.
    
    Only EXCEPT (tab_root) is supported, which excludes tab_root and all
    of its partitions. Specifying EXCEPT (tab_part_1) or EXCEPT
    (tab_part_1_p1) will result in an error.
    
    ~~
    
    While Approach 1 and Approach 2 offer more flexibility to the user
    compared to Approach 3, they also introduce additional design
    complexity which does not seem simpler to address.
    
    Approach 3, OTOH, provides a simpler solution and removes ambiguity
    around the meaning of EXCEPT. It also aligns, to some extent, with the
    behavior of ALTER PUBLICATION … DROP TABLE. For example, when a
    publication is created explicitly for a ROOT partitioned table,
    ALTER PUBLICATION DROP TABLE is not allowed for a partition,
    regardless of the publish_via_partition_root setting. This behavior is
    demonstrated in the attached AlterPubDropTable file.
    
    That said, the behaviors of DROP TABLE and EXCEPT (table) are not
    directly comparable. EXCEPT (table) is supported only for ALL TABLES
    publications,  whereas DROP TABLE is not supported in that context.
    Similarly, when a publication is created explicitly for a partition,
    ALTER PUBLICATION does allow dropping that partition.
    
    The key point here is that 'publish_via_partition_root' does not
    influence what can or cannot be dropped using ALTER PUBLICATION … DROP
    TABLE. This approach follows the same principle.
    
    OTOH when we see row-filter behaviour, publish_via_partition_root
    plays a role in determining whether a row filter can be created
    itself.  This is demonstrated in the attached RowFilterBehaviour
    experiment.
    
    This leads us to Approach 4.
    
    Approach 4:
    ----------------
    Do not allow partitions to be specified in the EXCEPT clause when
    publish_via_partition_root is true, but allow them when
    publish_via_partition_root is false.
    
    When publish_via_partition_root = true:
    EXCEPT (tab_part_1_p1) results in an error.
    EXCEPT (tab_part_1) results in an error.
    EXCEPT (tab_root) excludes the entire hierarchy under the root table.
    
    When publish_via_partition_root = false:
    EXCEPT (tab_part_1_p1) skips replication of that partition.
    EXCEPT (tab_part_1) skips replication of that partition and all of its
    child partitions.
    EXCEPT (tab_root) skips replication of the partitioned table and all
    partitions beneath it.
    
    This design is guided by the way row filters behave. It offers more
    flexibility than Approach 3 while avoiding the complexity of
    Approaches 1 and 2.  However, it may cause some user confusion since
    EXCEPT behavior depends on publish_via_partition_root.
    
    Given this, the current plan is to implement Approach 3 as the initial
    patch, and then implement the changes required for Approach 4 in a
    top-up patch.  This will allow us to evaluate if at all Approach 4
    causes any implementation complexities.
    
    Please share your thoughts on the approaches described.
    
    thanks
    Shveta
    
  176. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-01-21T06:05:18Z

    On Mon, Jan 19, 2026 at 3:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > Approaches for Supporting EXCEPT in Partitioned Tables
    > ------------------------------------------------------------------------
    >
    > In an offline discussion with Peter Smith, Amit, and Shlok, we
    > identified several approaches for supporting EXCEPT with partitioned
    > tables and their partitions. I’d like to hear others’ opinions on
    > these approaches.
    >
    > Consider the following partition hierarchy:
    > tab_root
    >   ├─ tab_part_1
    >   │   ├─ tab_part_1_p1
    >   │   └─ tab_part_1_p2
    >   └─ tab_part_2
    >       ├─ tab_part_2_p1
    >       └─ tab_part_2_p2
    >
    >
    > Approach 1:
    > ---------------------------------
    > If we exclude a table, then the data in that table and all of its
    > partitions (i.e., the entire subtree under that table) should not be
    > replicated.
    >
    > For example EXCEPT (tab_part_1) skips replication of tab_part_1 and
    > all of its partitions.
    >
    > This behaviour remains the same with or without
    > publish_via_partition_root. The publish_via_partition_root flag only
    > affects publish_via_relid, i.e., the relation through which data is
    > published.
    >
    > This approach involves certain implementation challenges. For brevity,
    > these are documented in the attached 'Approach1_challenges' document.
    >
    > Approach 2:
    > ---------------------------------------------------
    > Assign meaning to ONLY and '*' for partition tables in the EXCEPT
    > list. In HEAD, ONLY and '*' do not have any meaning for partitioned
    > tables or partitions, and these keywords are currently ignored.
    >
    > Examples:
    > 1. EXCEPT (ONLY tab_part_1) skips replication of only the table
    > tab_part_1. Changes for tab_root, tab_part_1_p1, and tab_part_1_p2 are
    > still replicated.
    >
    > ii. EXCEPT (tab_part_1*) skips replication of tables tab_part_1,
    > tab_part_1_p1, and tab_part_1_p2
    >
    > The challenges described in Approach 1, particularly around tablesync
    > handling and COPY behaviour, would still need to be addressed under
    > this approach as well. ONLY or '*' with partitioned tables is not
    > supported in HEAD, supporting it specifically for ALL TABLES EXCEPT
    > may introduce additional confusion for users.
    >
    > Approach 3:
    > ----------------
    > Do not allow partitions to be specified in the EXCEPT clause.
    >
    > Only EXCEPT (tab_root) is supported, which excludes tab_root and all
    > of its partitions. Specifying EXCEPT (tab_part_1) or EXCEPT
    > (tab_part_1_p1) will result in an error.
    >
    > ~~
    >
    > While Approach 1 and Approach 2 offer more flexibility to the user
    > compared to Approach 3, they also introduce additional design
    > complexity which does not seem simpler to address.
    
    Thanks for explaining this, overall I like the Approach 1, and I also
    see the problem when publish via root is given in that case COPY FROM
    is executed on the root and it would be hard to exclude specific
    partitions.  What is the behavior when root of partition tree is added
    but publish via root is not true, it doesn't add any relation to
    publication rel or how does it manage to not copy data from
    partitions?
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  177. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-01-21T06:32:53Z

    On Fri, Jan 16, 2026 at 4:59 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Jan 6, 2026 at 6:13 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > Also addressed the remaining comments. I have also addressed the
    > > comments by Peter in [1]. I have also done some minor cosmetic
    > > changes.
    > >
    >
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (t1,t2);
    >
    > Did we consider using EXCLUDE instead of EXCEPT? In another similar
    > feature being discussed, the community is proposing to use EXCLUDE to
    > SQL Standard, so won't it be better to use EXCLUDE here as well?
    >
    > [1] - https://www.postgresql.org/message-id/63e1587b-4258-41de-b823-948f8cc692d9%40eisentraut.org
    >
    
    I am listing all current usages of EXCEPT and EXCLUDE to decide us better:
    
    EXCEPT:
    1. Set Operator (EXCEPT / EXCEPT ALL) (docs at [1])
    
    Syntax: query1 EXCEPT [ALL] query2.
    
    EXCEPT returns all rows that are in the result of query1 but not in
    the result of query2.
    
    Example: SELECT id, name FROM employees EXCEPT SELECT id, name FROM contractors;
    
    
    2. IMPORT FOREIGN SCHEMA … EXCEPT (docs at [2])
    
    Syntax:
    IMPORT FOREIGN SCHEMA remote_schema
      [ { LIMIT TO | EXCEPT } ( table_list ) ]
      FROM SERVER server_name INTO local_schema
      [ OPTIONS (...) ];
    
    LIMIT TO (table_list) → import only the listed tables.
    EXCEPT (table_list) → import all tables except the listed ones.
    
    Example:
    IMPORT FOREIGN SCHEMA public EXCEPT (audit_log)
       FROM SERVER remote_pg INTO remote_import;
    
    ~~
    
    EXCLUDE:
    1. Exclusion Constraints (docs at [3])
    Syntax:
    CREATE TABLE table_name
     ( column_name data_type,
      EXCLUDE USING index_method ( column_name WITH operator [, ...] ) );
    
    Prevents rows from violating a condition defined by operators.
    
    Example:
    CREATE TABLE circles ( c circle, EXCLUDE USING gist (c WITH &&) );
    Or
    CREATE TABLE foo (x int, EXCLUDE (x WITH =)); --This is essentially
    like a UNIQUE constraint but defined via EXCLUDE.
    
    2.
    In Window Function Calls  (docs at [4])
    We use EXCLUDE in the window frame exclusion clause.
    In SQL, when we use OVER (...) with ROWS or RANGE, we can also specify
    EXCLUDE to control which rows are considered in the window frame.
    
    Options explained:
    EXCLUDE CURRENT ROW → exclude the current row from the frame.
    EXCLUDE GROUP → excludes the current row and its ordering peers from the frame.
    EXCLUDE TIES →  excludes any peers of the current row from the frame,
    but not the current row itself
    EXCLUDE NO OTHERS → include everything (default).
    
    Example:
    SELECT id, value, SUM(value)
      OVER ( ORDER BY id ROWS
             BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
             EXCLUDE CURRENT ROW )
      AS running_sum_excluding_current FROM test;
    
    ~~
    
    IMO, we use EXCEPT in postgres when we want to filter a set of objects
    from all selected objects and we use EXCLUDE mostly in terms of rows
    based constraints/exclusion.
    
    And our example of :
    Create publication pub1 for all tables EXCEPT/EXCLUDE tab1,tab2;
    
    has more resemblance to:
    
    IMPORT FOREIGN SCHEMA public EXCEPT (audit_log) FROM SERVER remote_pg
    INTO remote_import;
    
    So based on above, +1 for EXCEPT for our case.
    ~~
    
    [1]: https://www.postgresql.org/docs/current/queries-union.html
    [2]: https://www.postgresql.org/docs/current/sql-importforeignschema.html
    [3]: https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-EXCLUSION
    [4]: https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-WINDOW-FUNCTIONS
    
    thanks
    Shveta
    
    
    
    
  178. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-01-21T07:21:15Z

    On Wed, Jan 21, 2026 at 12:03 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Fri, Jan 16, 2026 at 4:59 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Tue, Jan 6, 2026 at 6:13 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > Also addressed the remaining comments. I have also addressed the
    > > > comments by Peter in [1]. I have also done some minor cosmetic
    > > > changes.
    > > >
    > >
    > > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (t1,t2);
    > >
    > > Did we consider using EXCLUDE instead of EXCEPT? In another similar
    > > feature being discussed, the community is proposing to use EXCLUDE to
    > > SQL Standard, so won't it be better to use EXCLUDE here as well?
    > >
    > > [1] - https://www.postgresql.org/message-id/63e1587b-4258-41de-b823-948f8cc692d9%40eisentraut.org
    > >
    >
    > I am listing all current usages of EXCEPT and EXCLUDE to decide us better:
    >
    > EXCEPT:
    > 1. Set Operator (EXCEPT / EXCEPT ALL) (docs at [1])
    >
    > Syntax: query1 EXCEPT [ALL] query2.
    >
    > EXCEPT returns all rows that are in the result of query1 but not in
    > the result of query2.
    >
    > Example: SELECT id, name FROM employees EXCEPT SELECT id, name FROM contractors;
    >
    >
    > 2. IMPORT FOREIGN SCHEMA … EXCEPT (docs at [2])
    >
    > Syntax:
    > IMPORT FOREIGN SCHEMA remote_schema
    >   [ { LIMIT TO | EXCEPT } ( table_list ) ]
    >   FROM SERVER server_name INTO local_schema
    >   [ OPTIONS (...) ];
    >
    > LIMIT TO (table_list) → import only the listed tables.
    > EXCEPT (table_list) → import all tables except the listed ones.
    >
    > Example:
    > IMPORT FOREIGN SCHEMA public EXCEPT (audit_log)
    >    FROM SERVER remote_pg INTO remote_import;
    >
    > ~~
    >
    > EXCLUDE:
    > 1. Exclusion Constraints (docs at [3])
    > Syntax:
    > CREATE TABLE table_name
    >  ( column_name data_type,
    >   EXCLUDE USING index_method ( column_name WITH operator [, ...] ) );
    >
    > Prevents rows from violating a condition defined by operators.
    >
    > Example:
    > CREATE TABLE circles ( c circle, EXCLUDE USING gist (c WITH &&) );
    > Or
    > CREATE TABLE foo (x int, EXCLUDE (x WITH =)); --This is essentially
    > like a UNIQUE constraint but defined via EXCLUDE.
    >
    > 2.
    > In Window Function Calls  (docs at [4])
    > We use EXCLUDE in the window frame exclusion clause.
    > In SQL, when we use OVER (...) with ROWS or RANGE, we can also specify
    > EXCLUDE to control which rows are considered in the window frame.
    >
    > Options explained:
    > EXCLUDE CURRENT ROW → exclude the current row from the frame.
    > EXCLUDE GROUP → excludes the current row and its ordering peers from the frame.
    > EXCLUDE TIES →  excludes any peers of the current row from the frame,
    > but not the current row itself
    > EXCLUDE NO OTHERS → include everything (default).
    >
    > Example:
    > SELECT id, value, SUM(value)
    >   OVER ( ORDER BY id ROWS
    >          BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    >          EXCLUDE CURRENT ROW )
    >   AS running_sum_excluding_current FROM test;
    >
    > ~~
    >
    > IMO, we use EXCEPT in postgres when we want to filter a set of objects
    > from all selected objects and we use EXCLUDE mostly in terms of rows
    > based constraints/exclusion.
    >
    > And our example of :
    > Create publication pub1 for all tables EXCEPT/EXCLUDE tab1,tab2;
    >
    > has more resemblance to:
    >
    > IMPORT FOREIGN SCHEMA public EXCEPT (audit_log) FROM SERVER remote_pg
    > INTO remote_import;
    >
    > So based on above, +1 for EXCEPT for our case.
    
    I agree based on existing examples EXCEPT seems more relevant for our
    use case. So +1 for EXCEPT.
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  179. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-01-21T11:27:29Z

    On Wed, Jan 21, 2026 at 11:35 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:
    >
    > Thanks for explaining this, overall I like the Approach 1, and I also
    > see the problem when publish via root is given in that case COPY FROM
    > is executed on the root and it would be hard to exclude specific
    > partitions.  What is the behavior when root of partition tree is added
    > but publish via root is not true, it doesn't add any relation to
    > publication rel or how does it manage to not copy data from
    > partitions?
    >
    
    So, I believe you are asking about the behavior of COPY on HEAD for
    the following case:
    
    CREATE PUBLICATION pub1 FOR TABLE tab_root WITH
    (publish_via_partition_root = false);
    
    In this scenario, pg_publication_rel contains an entry for tab_root,
    while pg_publication_tables contains all leaf partitions (because
    publish_via_partition_root = false). Consequently,
    pg_subscription_rel, which is derived from pg_publication_tables, also
    contains all corresponding leaf partitions.  As a result, on HEAD, a
    separate tablesync worker is launched for each leaf partition, and
    each leaf partition is copied independently.
    
    ~~
    
    Now, in Approach 4, when publish_via_partition_root is set to false,
    we propose avoiding the inclusion of leaf partitions in
    pg_publication_tables if their parent appears in the EXCEPT list.
    Given the table hierarchy described in Approach1_challenges:
    
    tab_root
    ├── tab_part_1
    │   ├── tab_part_1_1
    │   │   ├── tab_part_1_1_1
    │   │   │   └── tab_part_1_1_1_1
    │   │   └── tab_part_1_1_2
    │   └── tab_part_1_2
    │       ├── tab_part_1_2_1
    │       └── tab_part_1_2_2
    └── tab_part_2
    
    If tab_part_1_1 is specified in the EXCEPT list, then
    pg_publication_tables will include only those leaf partitions that are
    not in the partition-chain of tab_part_1_1. As a result, both
    pg_publication_tables and pg_subscription_rel (which is built from
    pg_publication_tables via fetch_relation_list) will contain:
    
    tab_part_1_2_1
    tab_part_1_2_2
    tab_part_2
    
    With this setup, any INSERT into tab_part_1 or tab_root that routes
    rows to tab_part_1_1_1_1 or tab_part_1_1_2 will not be replicated.
    However, rows routed to any of the three leaf partitions listed above
    will be replicated.
    
    I hope it answers your query. If we have to go by Approach1, then do
    you see any simpler way to overcome the challenges we mention for
    publish_via_partition_root=true case. Or any other approach
    altogether?
    
    thanks
    Shveta
    
    
    
    
  180. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-01-21T11:41:31Z

    On Wed, Jan 21, 2026 at 4:57 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Jan 21, 2026 at 11:35 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:
    > >
    > > Thanks for explaining this, overall I like the Approach 1, and I also
    > > see the problem when publish via root is given in that case COPY FROM
    > > is executed on the root and it would be hard to exclude specific
    > > partitions.  What is the behavior when root of partition tree is added
    > > but publish via root is not true, it doesn't add any relation to
    > > publication rel or how does it manage to not copy data from
    > > partitions?
    > >
    >
    > So, I believe you are asking about the behavior of COPY on HEAD for
    > the following case:
    >
    > CREATE PUBLICATION pub1 FOR TABLE tab_root WITH
    > (publish_via_partition_root = false);
    >
    > In this scenario, pg_publication_rel contains an entry for tab_root,
    > while pg_publication_tables contains all leaf partitions (because
    > publish_via_partition_root = false). Consequently,
    > pg_subscription_rel, which is derived from pg_publication_tables, also
    > contains all corresponding leaf partitions.  As a result, on HEAD, a
    > separate tablesync worker is launched for each leaf partition, and
    > each leaf partition is copied independently.
    >
    > ~~
    >
    > Now, in Approach 4, when publish_via_partition_root is set to false,
    > we propose avoiding the inclusion of leaf partitions in
    > pg_publication_tables if their parent appears in the EXCEPT list.
    > Given the table hierarchy described in Approach1_challenges:
    >
    > tab_root
    > ├── tab_part_1
    > │   ├── tab_part_1_1
    > │   │   ├── tab_part_1_1_1
    > │   │   │   └── tab_part_1_1_1_1
    > │   │   └── tab_part_1_1_2
    > │   └── tab_part_1_2
    > │       ├── tab_part_1_2_1
    > │       └── tab_part_1_2_2
    > └── tab_part_2
    >
    > If tab_part_1_1 is specified in the EXCEPT list, then
    > pg_publication_tables will include only those leaf partitions that are
    > not in the partition-chain of tab_part_1_1. As a result, both
    > pg_publication_tables and pg_subscription_rel (which is built from
    > pg_publication_tables via fetch_relation_list) will contain:
    >
    > tab_part_1_2_1
    > tab_part_1_2_2
    > tab_part_2
    >
    > With this setup, any INSERT into tab_part_1 or tab_root that routes
    > rows to tab_part_1_1_1_1 or tab_part_1_1_2 will not be replicated.
    > However, rows routed to any of the three leaf partitions listed above
    > will be replicated.
    >
    > I hope it answers your query. If we have to go by Approach1, then do
    > you see any simpler way to overcome the challenges we mention for
    > publish_via_partition_root=true case. Or any other approach
    > altogether?
    
    Thanks for the explanation, that clears it up. I agree that Approach 3
    is the right path forward. And it makes sense to extend this with
    Approach 4. Logically, I think it's reasonable to say that if a user
    chooses to partition via the root, they are treating the entire
    partition tree as a single entity. Therefore, it makes sense to
    disallow the exclusion of individual child partitions in that context.
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  181. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-01-22T12:04:25Z

    On Wed, 21 Jan 2026 at 17:11, Dilip Kumar <dilipbalaut@gmail.com> wrote:
    >
    > On Wed, Jan 21, 2026 at 4:57 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Wed, Jan 21, 2026 at 11:35 AM Dilip Kumar <dilipbalaut@gmail.com> wrote:
    > > >
    > > > Thanks for explaining this, overall I like the Approach 1, and I also
    > > > see the problem when publish via root is given in that case COPY FROM
    > > > is executed on the root and it would be hard to exclude specific
    > > > partitions.  What is the behavior when root of partition tree is added
    > > > but publish via root is not true, it doesn't add any relation to
    > > > publication rel or how does it manage to not copy data from
    > > > partitions?
    > > >
    > >
    > > So, I believe you are asking about the behavior of COPY on HEAD for
    > > the following case:
    > >
    > > CREATE PUBLICATION pub1 FOR TABLE tab_root WITH
    > > (publish_via_partition_root = false);
    > >
    > > In this scenario, pg_publication_rel contains an entry for tab_root,
    > > while pg_publication_tables contains all leaf partitions (because
    > > publish_via_partition_root = false). Consequently,
    > > pg_subscription_rel, which is derived from pg_publication_tables, also
    > > contains all corresponding leaf partitions.  As a result, on HEAD, a
    > > separate tablesync worker is launched for each leaf partition, and
    > > each leaf partition is copied independently.
    > >
    > > ~~
    > >
    > > Now, in Approach 4, when publish_via_partition_root is set to false,
    > > we propose avoiding the inclusion of leaf partitions in
    > > pg_publication_tables if their parent appears in the EXCEPT list.
    > > Given the table hierarchy described in Approach1_challenges:
    > >
    > > tab_root
    > > ├── tab_part_1
    > > │   ├── tab_part_1_1
    > > │   │   ├── tab_part_1_1_1
    > > │   │   │   └── tab_part_1_1_1_1
    > > │   │   └── tab_part_1_1_2
    > > │   └── tab_part_1_2
    > > │       ├── tab_part_1_2_1
    > > │       └── tab_part_1_2_2
    > > └── tab_part_2
    > >
    > > If tab_part_1_1 is specified in the EXCEPT list, then
    > > pg_publication_tables will include only those leaf partitions that are
    > > not in the partition-chain of tab_part_1_1. As a result, both
    > > pg_publication_tables and pg_subscription_rel (which is built from
    > > pg_publication_tables via fetch_relation_list) will contain:
    > >
    > > tab_part_1_2_1
    > > tab_part_1_2_2
    > > tab_part_2
    > >
    > > With this setup, any INSERT into tab_part_1 or tab_root that routes
    > > rows to tab_part_1_1_1_1 or tab_part_1_1_2 will not be replicated.
    > > However, rows routed to any of the three leaf partitions listed above
    > > will be replicated.
    > >
    > > I hope it answers your query. If we have to go by Approach1, then do
    > > you see any simpler way to overcome the challenges we mention for
    > > publish_via_partition_root=true case. Or any other approach
    > > altogether?
    >
    > Thanks for the explanation, that clears it up. I agree that Approach 3
    > is the right path forward. And it makes sense to extend this with
    > Approach 4. Logically, I think it's reasonable to say that if a user
    > chooses to partition via the root, they are treating the entire
    > partition tree as a single entity. Therefore, it makes sense to
    > disallow the exclusion of individual child partitions in that context.
    >
    Hi,
    
    I have prepared a patch for Approach-3. We are also checking the
    feasibility of other approaches.
    
    Thanks,
    Shlok Kyal
    
  182. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-01-23T05:43:21Z

    Hi Shlok.
    
    Some review comments for v36-0001.
    
    ======
    1.
    It seems that most of my v35 public/internal review comments from
    around 8/JAN have not been addressed in v36.
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    EXCEPT TABLE
    
    2.
    +     <para>
    +      For partitioned tables, only the root partitioned table may be specified
    +      in <literal>EXCEPT TABLE</literal>. Doing so excludes the root table and
    +      all of its partitions from replication, regardless of the value of
    +      <literal>publish_via_partition_root</literal>. The optional
    +      <literal>*</literal> has no effect for partitioned tables.
    +     </para>
    
    2a.
    AFAIK, the 'publish_via_partition_root' value has nothing to do with
    EXCEPT(partition) in this patch, but why was it really necessary to
    actually say that?
    
    ~
    
    2b.
    Should this be saying that "ONLY" also does not mean anything when a
    partitioned table is specified?
    
    ======
    src/backend/catalog/pg_publication.c
    
    publication_add_relation:
    
    3.
    + /*
    + * Handle the case where a partition is excluded by EXCEPT TABLE
    + */
    + if (pub->alltables && pri->except && targetrel->rd_rel->relispartition)
    + ereport(ERROR,
    + (errmsg("partition \"%s\" cannot be excluded using EXCEPT TABLE",
    + RelationGetRelationName(targetrel))));
    
    Should we have a hintmsg here to ask/tell the user that they might
    need to try EXCEPT the partitioned root instead?
    
    ======
    src/backend/commands/tablecmds.c
    
    ATExecAttachPartition:
    
    4.
    + /* Check if the partiton is part of EXCEPT list of any publication */
    + GetRelationPublications(RelationGetRelid(attachrel), NULL, &except_pubids);
    + if (except_pubids != NIL)
    + ereport(ERROR,
    + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("cannot attach relation \"%s\" as partition because it is
    part of EXCEPT list in publication",
    + RelationGetRelationName(attachrel))));
    
    Maybe reword the message
    
    SUGGESTION
    ... because it is named in a publication EXCEPT TABLE list
    
    ======
    src/backend/utils/cache/relcache.c
    
    RelationBuildPublicationDesc:
    
    5.
    + /*
    + * Only the topmost ancestor of a partitioned table can be specified
    + * in EXCEPT TABLES clause of a FOR ALL TABLES publication. So fetch
    + * the publications excluding the topmost ancestor only.
    + */
    + GetRelationPublications(llast_oid(ancestors), NULL, &exceptpuboids);
    +
    
    I found that "So fetch..." sentence to be quite ambiguous.
    
    SUGGESTION
    ... , so we only need to check the topmost ancestor.
    
    ======
    .../t/037_rep_changes_except_table.pl
    
    6.
    + ALTER TABLE sch1.t1 ATTACH PARTITION sch1.part2 FOR VALUES FROM
    (101) TO (200);
    
    Was there any reason to do this ALTER?
    AFAIK, you could've said both PARTITION BY and PARTITION OF in the
    original CREATE TABLE.
    
    ~~~
    
    7.
    +# Partititions cannot be excluded using EXCEPT TABLE
    
    7a.
    typo: /Partititions/Partitions/
    
    ~
    
    7b.
    TBH, I don't know if it was necessary to repeat these partition tests
    all the time with/without publish_via_partition_root set, but if you
    still think it is needed, then the comment should say that all
    combinations are tested to demonstrate that the parameter has no
    effect.
    
    Meanwhile, the same goes for all the other tests too ... Given that
    'publish_via_partition_root' has no impact on EXCEPT TABLE, then why
    do we need so many combinations of tests to show that it has no
    effect? IOW, many other parameters also have nothing to do with EXCEPT
    TABLE, but we don't test those.
    
    ~~~
    
    8.
    +# Cannot attach partition that is part of EXCEPT list in publication
    
    SUGGESTION
    Cannot attach a partition  that is named in the EXCEPT list of any publication
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  183. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-01-23T13:11:09Z

    On Wed, 21 Jan 2026 at 11:35, Dilip Kumar <dilipbalaut@gmail.com> wrote:
    >
    > On Mon, Jan 19, 2026 at 3:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > Approaches for Supporting EXCEPT in Partitioned Tables
    > > ------------------------------------------------------------------------
    > >
    > > In an offline discussion with Peter Smith, Amit, and Shlok, we
    > > identified several approaches for supporting EXCEPT with partitioned
    > > tables and their partitions. I’d like to hear others’ opinions on
    > > these approaches.
    > >
    > > Consider the following partition hierarchy:
    > > tab_root
    > >   ├─ tab_part_1
    > >   │   ├─ tab_part_1_p1
    > >   │   └─ tab_part_1_p2
    > >   └─ tab_part_2
    > >       ├─ tab_part_2_p1
    > >       └─ tab_part_2_p2
    > >
    > >
    > > Approach 1:
    > > ---------------------------------
    > > If we exclude a table, then the data in that table and all of its
    > > partitions (i.e., the entire subtree under that table) should not be
    > > replicated.
    > >
    > > For example EXCEPT (tab_part_1) skips replication of tab_part_1 and
    > > all of its partitions.
    > >
    > > This behaviour remains the same with or without
    > > publish_via_partition_root. The publish_via_partition_root flag only
    > > affects publish_via_relid, i.e., the relation through which data is
    > > published.
    > >
    > > This approach involves certain implementation challenges. For brevity,
    > > these are documented in the attached 'Approach1_challenges' document.
    > >
    > > Approach 2:
    > > ---------------------------------------------------
    > > Assign meaning to ONLY and '*' for partition tables in the EXCEPT
    > > list. In HEAD, ONLY and '*' do not have any meaning for partitioned
    > > tables or partitions, and these keywords are currently ignored.
    > >
    > > Examples:
    > > 1. EXCEPT (ONLY tab_part_1) skips replication of only the table
    > > tab_part_1. Changes for tab_root, tab_part_1_p1, and tab_part_1_p2 are
    > > still replicated.
    > >
    > > ii. EXCEPT (tab_part_1*) skips replication of tables tab_part_1,
    > > tab_part_1_p1, and tab_part_1_p2
    > >
    > > The challenges described in Approach 1, particularly around tablesync
    > > handling and COPY behaviour, would still need to be addressed under
    > > this approach as well. ONLY or '*' with partitioned tables is not
    > > supported in HEAD, supporting it specifically for ALL TABLES EXCEPT
    > > may introduce additional confusion for users.
    > >
    > > Approach 3:
    > > ----------------
    > > Do not allow partitions to be specified in the EXCEPT clause.
    > >
    > > Only EXCEPT (tab_root) is supported, which excludes tab_root and all
    > > of its partitions. Specifying EXCEPT (tab_part_1) or EXCEPT
    > > (tab_part_1_p1) will result in an error.
    > >
    > > ~~
    > >
    > > While Approach 1 and Approach 2 offer more flexibility to the user
    > > compared to Approach 3, they also introduce additional design
    > > complexity which does not seem simpler to address.
    >
    > Thanks for explaining this, overall I like the Approach 1, and I also
    > see the problem when publish via root is given in that case COPY FROM
    > is executed on the root and it would be hard to exclude specific
    > partitions.
    
    Regarding the above issue which is also mentioned in
    Approach1_challenges at [1]:
    When a publication is created with publish_via_partition_root = true
    and a specific partition(tab_part_1_1) is excluded, the expected
    behavior is that changes from non-excluded partitions (for example,
    tab_part_2 and tab_part_1_2 and their descendants) are replicated,
    while changes from the excluded partition (tab_part_1_1 and its
    subtree) are not.
    tab_root
    ├── tab_part_1
    │   ├── tab_part_1_1        (except)
    │   │   ├── tab_part_1_1_1
    │   │   │   └── tab_part_1_1_1_1
    │   │   └── tab_part_1_1_2
    │   └── tab_part_1_2
    │       ├── tab_part_1_2_1
    │       └── tab_part_1_2_2
    └── tab_part_2
    
    In this situation, replication cannot be performed purely via the
    partition root (tab_root), because doing so would implicitly include
    data from the excluded child partitions.
    
    To address this, the publication creation should explicitly record the
    excluded partition(tab_part_1_1) in pg_publication_rel with an
    excluded = true flag. The publish_via_partition_root setting remains
    stored at the publication level, as it is today. With
    publish_via_partition_root = true, the publisher–subscriber mapping is
    not partition-to-partition. Instead, all eligible data is mapped to
    the subscriber’s partition root. Therefore,
    pg_get_publication_tables() should return only the top-level root
    table (tab_root) to the subscriber for table synchronization. During
    initial table sync, when the tablesync worker prepares the COPY
    command, it can query the publisher to determine the effective set of
    tables that belong to the publication after applying the exclusion
    rules. Based on this resolved table list, the tablesync worker can
    construct a COPY query that unions data only from the non-excluded
    partitions, for example:
    COPY (
        SELECT * FROM tab_part_1_2_1
        UNION ALL
        SELECT * FROM tab_part_1_2_2
        UNION ALL
        SELECT * FROM tab_part_2
    )
    
    This ensures that only non-excluded data is copied and applied to
    tab_root on the subscriber, while preserving the semantics of
    publish_via_partition_root = true.
    Thoughts?
    
    [1] - https://www.postgresql.org/message-id/CAJpy0uD81HRrMYr7S-6AV4W2PtbGKM-nf2D89zsoMHJ9jZssUg%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  184. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-01-27T14:37:01Z

    On Fri, 23 Jan 2026 at 18:41, vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Wed, 21 Jan 2026 at 11:35, Dilip Kumar <dilipbalaut@gmail.com> wrote:
    > >
    > > On Mon, Jan 19, 2026 at 3:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > Approaches for Supporting EXCEPT in Partitioned Tables
    > > > ------------------------------------------------------------------------
    > > >
    > > > In an offline discussion with Peter Smith, Amit, and Shlok, we
    > > > identified several approaches for supporting EXCEPT with partitioned
    > > > tables and their partitions. I’d like to hear others’ opinions on
    > > > these approaches.
    > > >
    > > > Consider the following partition hierarchy:
    > > > tab_root
    > > >   ├─ tab_part_1
    > > >   │   ├─ tab_part_1_p1
    > > >   │   └─ tab_part_1_p2
    > > >   └─ tab_part_2
    > > >       ├─ tab_part_2_p1
    > > >       └─ tab_part_2_p2
    > > >
    > > >
    > > > Approach 1:
    > > > ---------------------------------
    > > > If we exclude a table, then the data in that table and all of its
    > > > partitions (i.e., the entire subtree under that table) should not be
    > > > replicated.
    > > >
    > > > For example EXCEPT (tab_part_1) skips replication of tab_part_1 and
    > > > all of its partitions.
    > > >
    > > > This behaviour remains the same with or without
    > > > publish_via_partition_root. The publish_via_partition_root flag only
    > > > affects publish_via_relid, i.e., the relation through which data is
    > > > published.
    > > >
    > > > This approach involves certain implementation challenges. For brevity,
    > > > these are documented in the attached 'Approach1_challenges' document.
    > > >
    > > > Approach 2:
    > > > ---------------------------------------------------
    > > > Assign meaning to ONLY and '*' for partition tables in the EXCEPT
    > > > list. In HEAD, ONLY and '*' do not have any meaning for partitioned
    > > > tables or partitions, and these keywords are currently ignored.
    > > >
    > > > Examples:
    > > > 1. EXCEPT (ONLY tab_part_1) skips replication of only the table
    > > > tab_part_1. Changes for tab_root, tab_part_1_p1, and tab_part_1_p2 are
    > > > still replicated.
    > > >
    > > > ii. EXCEPT (tab_part_1*) skips replication of tables tab_part_1,
    > > > tab_part_1_p1, and tab_part_1_p2
    > > >
    > > > The challenges described in Approach 1, particularly around tablesync
    > > > handling and COPY behaviour, would still need to be addressed under
    > > > this approach as well. ONLY or '*' with partitioned tables is not
    > > > supported in HEAD, supporting it specifically for ALL TABLES EXCEPT
    > > > may introduce additional confusion for users.
    > > >
    > > > Approach 3:
    > > > ----------------
    > > > Do not allow partitions to be specified in the EXCEPT clause.
    > > >
    > > > Only EXCEPT (tab_root) is supported, which excludes tab_root and all
    > > > of its partitions. Specifying EXCEPT (tab_part_1) or EXCEPT
    > > > (tab_part_1_p1) will result in an error.
    > > >
    > > > ~~
    > > >
    > > > While Approach 1 and Approach 2 offer more flexibility to the user
    > > > compared to Approach 3, they also introduce additional design
    > > > complexity which does not seem simpler to address.
    > >
    > > Thanks for explaining this, overall I like the Approach 1, and I also
    > > see the problem when publish via root is given in that case COPY FROM
    > > is executed on the root and it would be hard to exclude specific
    > > partitions.
    >
    > Regarding the above issue which is also mentioned in
    > Approach1_challenges at [1]:
    > When a publication is created with publish_via_partition_root = true
    > and a specific partition(tab_part_1_1) is excluded, the expected
    > behavior is that changes from non-excluded partitions (for example,
    > tab_part_2 and tab_part_1_2 and their descendants) are replicated,
    > while changes from the excluded partition (tab_part_1_1 and its
    > subtree) are not.
    > tab_root
    > ├── tab_part_1
    > │   ├── tab_part_1_1        (except)
    > │   │   ├── tab_part_1_1_1
    > │   │   │   └── tab_part_1_1_1_1
    > │   │   └── tab_part_1_1_2
    > │   └── tab_part_1_2
    > │       ├── tab_part_1_2_1
    > │       └── tab_part_1_2_2
    > └── tab_part_2
    >
    > In this situation, replication cannot be performed purely via the
    > partition root (tab_root), because doing so would implicitly include
    > data from the excluded child partitions.
    >
    > To address this, the publication creation should explicitly record the
    > excluded partition(tab_part_1_1) in pg_publication_rel with an
    > excluded = true flag. The publish_via_partition_root setting remains
    > stored at the publication level, as it is today. With
    > publish_via_partition_root = true, the publisher–subscriber mapping is
    > not partition-to-partition. Instead, all eligible data is mapped to
    > the subscriber’s partition root. Therefore,
    > pg_get_publication_tables() should return only the top-level root
    > table (tab_root) to the subscriber for table synchronization. During
    > initial table sync, when the tablesync worker prepares the COPY
    > command, it can query the publisher to determine the effective set of
    > tables that belong to the publication after applying the exclusion
    > rules. Based on this resolved table list, the tablesync worker can
    > construct a COPY query that unions data only from the non-excluded
    > partitions, for example:
    > COPY (
    >     SELECT * FROM tab_part_1_2_1
    >     UNION ALL
    >     SELECT * FROM tab_part_1_2_2
    >     UNION ALL
    >     SELECT * FROM tab_part_2
    > )
    >
    > This ensures that only non-excluded data is copied and applied to
    > tab_root on the subscriber, while preserving the semantics of
    > publish_via_partition_root = true.
    
    Here is a patch which has the changes to handle the same.
    
    Regards,
    Vignesh
    
  185. Re: Skipping schema changes in publication

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

    On Tue, Jan 27, 2026 at 8:25 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Fri, 23 Jan 2026 at 18:41, vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Wed, 21 Jan 2026 at 11:35, Dilip Kumar <dilipbalaut@gmail.com> wrote:
    > > >
    > > > On Mon, Jan 19, 2026 at 3:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > > >
    > > > > Approaches for Supporting EXCEPT in Partitioned Tables
    > > > > ------------------------------------------------------------------------
    > > > >
    > > > > In an offline discussion with Peter Smith, Amit, and Shlok, we
    > > > > identified several approaches for supporting EXCEPT with partitioned
    > > > > tables and their partitions. I’d like to hear others’ opinions on
    > > > > these approaches.
    > > > >
    > > > > Consider the following partition hierarchy:
    > > > > tab_root
    > > > >   ├─ tab_part_1
    > > > >   │   ├─ tab_part_1_p1
    > > > >   │   └─ tab_part_1_p2
    > > > >   └─ tab_part_2
    > > > >       ├─ tab_part_2_p1
    > > > >       └─ tab_part_2_p2
    > > > >
    > > > >
    > > > > Approach 1:
    > > > > ---------------------------------
    > > > > If we exclude a table, then the data in that table and all of its
    > > > > partitions (i.e., the entire subtree under that table) should not be
    > > > > replicated.
    > > > >
    > > > > For example EXCEPT (tab_part_1) skips replication of tab_part_1 and
    > > > > all of its partitions.
    > > > >
    > > > > This behaviour remains the same with or without
    > > > > publish_via_partition_root. The publish_via_partition_root flag only
    > > > > affects publish_via_relid, i.e., the relation through which data is
    > > > > published.
    > > > >
    > > > > This approach involves certain implementation challenges. For brevity,
    > > > > these are documented in the attached 'Approach1_challenges' document.
    > > > >
    > > > > Approach 2:
    > > > > ---------------------------------------------------
    > > > > Assign meaning to ONLY and '*' for partition tables in the EXCEPT
    > > > > list. In HEAD, ONLY and '*' do not have any meaning for partitioned
    > > > > tables or partitions, and these keywords are currently ignored.
    > > > >
    > > > > Examples:
    > > > > 1. EXCEPT (ONLY tab_part_1) skips replication of only the table
    > > > > tab_part_1. Changes for tab_root, tab_part_1_p1, and tab_part_1_p2 are
    > > > > still replicated.
    > > > >
    > > > > ii. EXCEPT (tab_part_1*) skips replication of tables tab_part_1,
    > > > > tab_part_1_p1, and tab_part_1_p2
    > > > >
    > > > > The challenges described in Approach 1, particularly around tablesync
    > > > > handling and COPY behaviour, would still need to be addressed under
    > > > > this approach as well. ONLY or '*' with partitioned tables is not
    > > > > supported in HEAD, supporting it specifically for ALL TABLES EXCEPT
    > > > > may introduce additional confusion for users.
    > > > >
    > > > > Approach 3:
    > > > > ----------------
    > > > > Do not allow partitions to be specified in the EXCEPT clause.
    > > > >
    > > > > Only EXCEPT (tab_root) is supported, which excludes tab_root and all
    > > > > of its partitions. Specifying EXCEPT (tab_part_1) or EXCEPT
    > > > > (tab_part_1_p1) will result in an error.
    > > > >
    > > > > ~~
    > > > >
    > > > > While Approach 1 and Approach 2 offer more flexibility to the user
    > > > > compared to Approach 3, they also introduce additional design
    > > > > complexity which does not seem simpler to address.
    > > >
    > > > Thanks for explaining this, overall I like the Approach 1, and I also
    > > > see the problem when publish via root is given in that case COPY FROM
    > > > is executed on the root and it would be hard to exclude specific
    > > > partitions.
    > >
    > > Regarding the above issue which is also mentioned in
    > > Approach1_challenges at [1]:
    > > When a publication is created with publish_via_partition_root = true
    > > and a specific partition(tab_part_1_1) is excluded, the expected
    > > behavior is that changes from non-excluded partitions (for example,
    > > tab_part_2 and tab_part_1_2 and their descendants) are replicated,
    > > while changes from the excluded partition (tab_part_1_1 and its
    > > subtree) are not.
    > > tab_root
    > > ├── tab_part_1
    > > │   ├── tab_part_1_1        (except)
    > > │   │   ├── tab_part_1_1_1
    > > │   │   │   └── tab_part_1_1_1_1
    > > │   │   └── tab_part_1_1_2
    > > │   └── tab_part_1_2
    > > │       ├── tab_part_1_2_1
    > > │       └── tab_part_1_2_2
    > > └── tab_part_2
    > >
    > > In this situation, replication cannot be performed purely via the
    > > partition root (tab_root), because doing so would implicitly include
    > > data from the excluded child partitions.
    > >
    > > To address this, the publication creation should explicitly record the
    > > excluded partition(tab_part_1_1) in pg_publication_rel with an
    > > excluded = true flag. The publish_via_partition_root setting remains
    > > stored at the publication level, as it is today. With
    > > publish_via_partition_root = true, the publisher–subscriber mapping is
    > > not partition-to-partition. Instead, all eligible data is mapped to
    > > the subscriber’s partition root. Therefore,
    > > pg_get_publication_tables() should return only the top-level root
    > > table (tab_root) to the subscriber for table synchronization. During
    > > initial table sync, when the tablesync worker prepares the COPY
    > > command, it can query the publisher to determine the effective set of
    > > tables that belong to the publication after applying the exclusion
    > > rules. Based on this resolved table list, the tablesync worker can
    > > construct a COPY query that unions data only from the non-excluded
    > > partitions, for example:
    > > COPY (
    > >     SELECT * FROM tab_part_1_2_1
    > >     UNION ALL
    > >     SELECT * FROM tab_part_1_2_2
    > >     UNION ALL
    > >     SELECT * FROM tab_part_2
    > > )
    > >
    > > This ensures that only non-excluded data is copied and applied to
    > > tab_root on the subscriber, while preserving the semantics of
    > > publish_via_partition_root = true.
    
    I agree with the suggested changes in tablesync. It will be good if we
    can add these details in the commit-msg section of the patch. Also
    please mention how increment replication is impacted (or supposed to
    work) with Approach1.
    
    > Here is a patch which has the changes to handle the same.
    >
    
    Thank You for the patch.
    
    1)
    There are certain parts of Approach 3 still present in Approach 1, as
    an example:
    
    1a)
    +      For partitioned tables, only the root partitioned table may be specified
    +      in <literal>EXCEPT TABLE</literal>.
    
    1b)
    + /*
    + * Only the topmost ancestor of a partitioned table can be specified
    + * in EXCEPT TABLES clause of a FOR ALL TABLES publication. So fetch
    + * the publications excluding the topmost ancestor only.
    + */
    + GetRelationPublications(llast_oid(ancestors), NULL, &exceptpuboids);
    +
    
    1c)
    + /* Check if the partiton is part of EXCEPT list of any publication */
    + GetRelationPublications(RelationGetRelid(attachrel), NULL, &except_pubids);
    + if (except_pubids != NIL)
    + ereport(ERROR,
    + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("cannot attach relation \"%s\" as partition because it is
    part of EXCEPT list in publication",
    + RelationGetRelationName(attachrel))));
    +
    
    Overall, please take a diff of v35 and v37 to find such parts and
    please correct these and others (if any).
    
    2)
    Also I don't think if below is correct statement for Approach 1:
    
    + * 2. For a partition, if the topmost ancestor is part of
    + *   the EXCEPT TABLE list, we don't publish it.
    
    Even if any ancestor is part of EXECPT list (not only top most) we
    should not publish that partition, isn't it?
    
    3)
    I tried a scenario and found that incremental replication is not
    working correctly. Attached the failing test as Approach1_v37_fail.txt
    
    Once these basic things are corrected, I can review further.
    
    thanks
    Shveta
    
  186. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-01-29T15:11:26Z

    On Wed, 28 Jan 2026 at 10:46, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > Thank You for the patch.
    >
    > 1)
    > There are certain parts of Approach 3 still present in Approach 1, as
    > an example:
    >
    > 1a)
    > +      For partitioned tables, only the root partitioned table may be specified
    > +      in <literal>EXCEPT TABLE</literal>.
    >
    > 1b)
    > + /*
    > + * Only the topmost ancestor of a partitioned table can be specified
    > + * in EXCEPT TABLES clause of a FOR ALL TABLES publication. So fetch
    > + * the publications excluding the topmost ancestor only.
    > + */
    > + GetRelationPublications(llast_oid(ancestors), NULL, &exceptpuboids);
    > +
    >
    > 1c)
    > + /* Check if the partiton is part of EXCEPT list of any publication */
    > + GetRelationPublications(RelationGetRelid(attachrel), NULL, &except_pubids);
    > + if (except_pubids != NIL)
    > + ereport(ERROR,
    > + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("cannot attach relation \"%s\" as partition because it is
    > part of EXCEPT list in publication",
    > + RelationGetRelationName(attachrel))));
    > +
    >
    > Overall, please take a diff of v35 and v37 to find such parts and
    > please correct these and others (if any).
    >
    > 2)
    > Also I don't think if below is correct statement for Approach 1:
    >
    > + * 2. For a partition, if the topmost ancestor is part of
    > + *   the EXCEPT TABLE list, we don't publish it.
    >
    > Even if any ancestor is part of EXECPT list (not only top most) we
    > should not publish that partition, isn't it?
    >
    > 3)
    > I tried a scenario and found that incremental replication is not
    > working correctly. Attached the failing test as Approach1_v37_fail.txt
    >
    > Once these basic things are corrected, I can review further.
    
    These comments are addressed in the v38 version patch attached.
    Currently the approach-3 changes is present separately in
    v38-0002-Restrict-EXCEPT-TABLE-to-root-partitioned-tables-apporach-3.patch
    which can be applied on top of
    v38-0001-Skip-publishing-the-tables-specified-in-EXCEPT-T.patch.
    Similarly the approach-1 changes is present separately in
    v38-0002-handle-EXCEPT-TABLE-correctly-with-partitioned-approach-1.patch
    which can be applied on top of
    v38-0001-Skip-publishing-the-tables-specified-in-EXCEPT-T.patch.
    Currently few of the query are logged as LOG messages, I will reduce
    the log level for these queries once few rounds of review are
    completed on the queries.
    
    Regards,
    Vignesh
    
  187. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-01-29T21:36:53Z

    Hi Vignesh/Shlok.
    
    1.
    I've changed the name of the CF entry [1] for this because EXCEPT
    (column-list) is no longer part of the current focus.
    
    BEFORE
    Support EXCEPT tables and EXCEPT (column-list) in publications
    AFTER
    Support EXCEPT tables in publications
    
    2.
    I understand the latest patch structure to be like:
    
    0001 + 0002 (approach #1)
    or
    0001 + 0002 (approach #3)
    
    AFAICT, this is breaking the CF bot, which attempts to apply all
    patches at once [2]. IMO, next time you should change the patch suffix
    of "0002 (approach #3)" to something like ".txt" or ".nocfbot", so
    CFbot won't see it, and we can continue to get CI results for the
    other patches.
    
    ======
    [1] https://commitfest.postgresql.org/patch/6099/
    [2] https://cfbot.cputube.org/patch_6099.log
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  188. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-01-30T08:16:13Z

    Hi Vignesh.
    
    Some review comments for v38-0001.
    
    ======
    
    1. General. Patch structure
    
    This patch set structure seems muddled to me; I think patch 0001
    requires proper handling for EXCEPT (partitions), otherwise, it cannot
    be independently committed.
    
    It's my understanding that the goal is to try approach #1, but if that
    proves too difficult, then the fallback would be approach #3. Yet
    AFAICT this patch 0001 is neither -- I have no idea anymore what patch
    0001 does for partitions; IIUC it looks like just old partition logic
    from a few versions back (???).
    
    Personally, I felt it would be better to combine 0001 + 0002 (approach
    #3), then 0002 would be a patch that *replaces* approach #3 logic with
    approach #1 logic. That way, 0001 is self-contained, and 0002 is an
    evolution of the feature.
    
    OTOH if the plan is that 0001 and (one of the) 0002 *must* be merged
    prior to commit, then I would have expected that the 0001 has no
    EXCEPT(partition) logic and not tests at all -- just maybe a bunch of
    placeholders that patch 0002 would flesh out.
    
    Anyway, the following review comments are based on the patch set as it
    currently stands.
    
    ~~~
    
    2. Still unaddressed comments for 3 weeks?
    
    It seems most (all?) of my review comments from 8/January remain
    unaddressed in this 0001 patch. Multiple versions have come and gone,
    but the following review comments are still pending, according to my
    records:
    
    8/1 #1 (code review)
    8/1 #3 (code review)
    8/1 #1 (internal review)
    8/1 #2a (internal review)
    8/1 #2b (internal review)
    8/1 #C2 (cosmetic review)
    8/1 #C3 (cosmetic review)
    8/1 #C4 (cosmetic review)
    etc..
    
    ======
    Commit Message
    
    3.
    The message needs to explain what this patch is doing regarding
    partition tables and partitions. Currently, I have no idea. It seems
    just old logic that is neither approach #1 nor approach #3.
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    4.
    +     <para>
    +      For partitioned tables, when
    <literal>publish_via_partition_root</literal>
    +      is set to <literal>true</literal>, specifying a root partitioned table in
    +      <literal>EXCEPT TABLE</literal> excludes it and all its partitions from
    +      replication. Specifying a leaf partition has no effect, as its
    changes are
    +      still replicated via the root partitioned table. When
    +      <literal>publish_via_partition_root</literal> is set to
    +      <literal>false</literal>, specifying a root partitioned table has no
    +      effect, as changes are replicated via the leaf partitions. Specifying a
    +      leaf partition excludes only that partition from replication.
    The optional
    +      <literal>*</literal> has no meaning for partitioned tables.
    +     </para>
    
    4a.
    Huh? This is not at all my understanding of how either approach #1 or
    approach #3 would work.
    
    ~
    
    4b.
    I thought the patch set is currently arranged to handle partitions in
    the alternate patch 0002s.
    
    So, I expected this doc fragment to be more like:
    <para>
     For partitioned tables, TBA.
    </para>
    
    Later, your subsequent patch 0002 should replace this "TBA" part
    according to approach #1 or #3 implementation.
    
    ======
    src/backend/catalog/pg_publication.c
    
    publication_add_relation:
    
    5.
    + /*
    + * Handle the case where a partition is excluded by EXCEPT TABLE while
    + * publish_via_partition_root = true.
    + */
    + if (pub->alltables && pub->pubviaroot && pri->except &&
    + targetrel->rd_rel->relispartition)
    + ereport(WARNING,
    + (errmsg("partition \"%s\" might be replicated as
    publish_via_partition_root is \"%s\"",
    + RelationGetRelationName(targetrel), "true")));
    +
    + /*
    + * Handle the case where a partitioned table is excluded by EXCEPT TABLE
    + * while publish_via_partition_root = false.
    + */
    + if (pub->alltables && !pub->pubviaroot && pri->except &&
    + targetrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    + ereport(WARNING,
    + (errmsg("partitioned table \"%s\" might be replicated as
    publish_via_partition_root is \"%s\"",
    + RelationGetRelationName(targetrel), "false")));
    +
    
    Huh? IIUC, this is neither approach #1 nor approach #3.
    
    ======
    src/backend/replication/pgoutput/pgoutput.c
    
    get_rel_sync_entry:
    
    6.
    + *
    + * If this is a FOR ALL TABLES publication and it has an EXCEPT
    + * TABLE list:
    + *
    + * 1. If pubviaroot is set and the relation is a partition, check
    + * whether the partition root is included in the EXCEPT TABLE
    + * list. If so, do not publish the change.
    + *
    + * 2. If pubviaroot is not set, check whether the relation itself
    + * is included in the EXCEPT TABLE list. If so, do not publish the
    + * change.
    + *
    + * This is achieved by keeping the variable "publish" set to
    + * false. And eventually, entry->pubactions will remain all false
    + * for this publication.
    
    Again, this logic seems to describe neither approach #1 nor approach
    #3. So, what is it (???)
    
    ======
    src/test/subscription/t/037_rep_changes_except_table.pl
    
    7.
    IIUC, the patch set is arranged so that the 2 x patch 00002
    implementations are for the alternate partition handling. So I was not
    expecting patch 0001 to have any partition tests at all.
    
    If 0001 is not implementing approach #1 or approach #3 logic, then
    what are these tests testing?
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  189. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-01-30T09:12:29Z

    On Thu, Jan 29, 2026 at 8:41 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Wed, 28 Jan 2026 at 10:46, shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > Thank You for the patch.
    > >
    > > 1)
    > > There are certain parts of Approach 3 still present in Approach 1, as
    > > an example:
    > >
    > > 1a)
    > > +      For partitioned tables, only the root partitioned table may be specified
    > > +      in <literal>EXCEPT TABLE</literal>.
    > >
    > > 1b)
    > > + /*
    > > + * Only the topmost ancestor of a partitioned table can be specified
    > > + * in EXCEPT TABLES clause of a FOR ALL TABLES publication. So fetch
    > > + * the publications excluding the topmost ancestor only.
    > > + */
    > > + GetRelationPublications(llast_oid(ancestors), NULL, &exceptpuboids);
    > > +
    > >
    > > 1c)
    > > + /* Check if the partiton is part of EXCEPT list of any publication */
    > > + GetRelationPublications(RelationGetRelid(attachrel), NULL, &except_pubids);
    > > + if (except_pubids != NIL)
    > > + ereport(ERROR,
    > > + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > > + errmsg("cannot attach relation \"%s\" as partition because it is
    > > part of EXCEPT list in publication",
    > > + RelationGetRelationName(attachrel))));
    > > +
    > >
    > > Overall, please take a diff of v35 and v37 to find such parts and
    > > please correct these and others (if any).
    > >
    > > 2)
    > > Also I don't think if below is correct statement for Approach 1:
    > >
    > > + * 2. For a partition, if the topmost ancestor is part of
    > > + *   the EXCEPT TABLE list, we don't publish it.
    > >
    > > Even if any ancestor is part of EXECPT list (not only top most) we
    > > should not publish that partition, isn't it?
    > >
    > > 3)
    > > I tried a scenario and found that incremental replication is not
    > > working correctly. Attached the failing test as Approach1_v37_fail.txt
    > >
    > > Once these basic things are corrected, I can review further.
    >
    > These comments are addressed in the v38 version patch attached.
    > Currently the approach-3 changes is present separately in
    > v38-0002-Restrict-EXCEPT-TABLE-to-root-partitioned-tables-apporach-3.patch
    > which can be applied on top of
    > v38-0001-Skip-publishing-the-tables-specified-in-EXCEPT-T.patch.
    > Similarly the approach-1 changes is present separately in
    > v38-0002-handle-EXCEPT-TABLE-correctly-with-partitioned-approach-1.patch
    > which can be applied on top of
    > v38-0001-Skip-publishing-the-tables-specified-in-EXCEPT-T.patch.
    > Currently few of the query are logged as LOG messages, I will reduce
    > the log level for these queries once few rounds of review are
    > completed on the queries.
    >
    
    I was trying to test Approach 1 and haven’t fully reviewed or
    validated it yet. A few initial observations:
    
    
    postgres=# CREATE PUBLICATION pub1 for all tables
    EXCEPT(tab_part_1,tab_part_2_p2) WITH
    (PUBLISH_VIA_PARTITION_ROOT=true);
    WARNING:  partition "tab_part_1" might be replicated as
    publish_via_partition_root is "true"
    WARNING:  partition "tab_part_2_p2" might be replicated as
    publish_via_partition_root is "true"
    CREATE PUBLICATION
    
    postgres=# CREATE PUBLICATION pub1 for all tables
    EXCEPT(tab_part_1,tab_part_2_p2) WITH
    (PUBLISH_VIA_PARTITION_ROOT=false);
    WARNING:  partitioned table "tab_part_1" might be replicated as
    publish_via_partition_root is "false"
    CREATE PUBLICATION
    
    IIUC, these Warnings are not expected in Approach1.
    
    thanks
    Shveta
    
    
    
    
  190. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-02T05:31:52Z

    On Fri, Jan 30, 2026 at 1:46 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Vignesh.
    >
    > Some review comments for v38-0001.
    >
    > ======
    >
    > 1. General. Patch structure
    >
    > This patch set structure seems muddled to me; I think patch 0001
    > requires proper handling for EXCEPT (partitions), otherwise, it cannot
    > be independently committed.
    >
    > It's my understanding that the goal is to try approach #1, but if that
    > proves too difficult, then the fallback would be approach #3. Yet
    > AFAICT this patch 0001 is neither -- I have no idea anymore what patch
    > 0001 does for partitions; IIUC it looks like just old partition logic
    > from a few versions back (???).
    >
    > Personally, I felt it would be better to combine 0001 + 0002 (approach
    > #3), then 0002 would be a patch that *replaces* approach #3 logic with
    > approach #1 logic. That way, 0001 is self-contained, and 0002 is an
    > evolution of the feature.
    >
    
    I agree that the current patch structure is hard to follow and doesn’t
    add much value, since patch001 isn’t aligned with a specific approach.
    How about this patch structure:
    
    patch001: Implement EXCEPT syntax and CREATE PUBLICATION changes. This
    includes all changes required to correctly populate
    pg_publication_tables and pg_publication_rel.
    
    Expectation for patch001:
    When this patch is applied on its own, CREATE PUBLICATION command with
    the EXCEPT clause should work, and the appropriate entries should
    appear in pg_publication_tables/pg_publication_rel with the except
    flag set correctly. No publishing expected in this patch.
    
    patch002: Enable publishing and subscription support.
    All changes required to make subscriptions work should go here, including:
    --table sync and other subscriber-side changes
    --pgoutput logic to determine which tables are published
    
    Expectation for patch002:
    Changes should be published and replicated correctly to the subscriber.
    
    patch003: pg_dump, tests, documentation, etc.
    
    All of the above changes/patches are intended to support Approach001.
    
    Approach003 has a limitation in that it allows 'only' ROOT table to be
    specified in the EXCEPT clause. If tab_root is listed in EXCEPT and a
    user later attempts to attach it as a partition of another table
    using:
    'ALTER TABLE root ATTACH PARTITION tab_root', we would need to block
    the ATTACH PARTITION command. Given this, we should first try to
    implement Approach001 and evaluate its feasibility. If it turns out to
    be impractical,
    we can fall back to Approach003 or consider other alternatives. For
    now, the Approach003 patch can remain on hold.
    
    thanks
    Shveta
    
    
    
    
  191. Re: Skipping schema changes in publication

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

    On Mon, Feb 2, 2026 at 4:32 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Fri, Jan 30, 2026 at 1:46 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Hi Vignesh.
    > >
    > > Some review comments for v38-0001.
    > >
    > > ======
    > >
    > > 1. General. Patch structure
    > >
    > > This patch set structure seems muddled to me; I think patch 0001
    > > requires proper handling for EXCEPT (partitions), otherwise, it cannot
    > > be independently committed.
    > >
    > > It's my understanding that the goal is to try approach #1, but if that
    > > proves too difficult, then the fallback would be approach #3. Yet
    > > AFAICT this patch 0001 is neither -- I have no idea anymore what patch
    > > 0001 does for partitions; IIUC it looks like just old partition logic
    > > from a few versions back (???).
    > >
    > > Personally, I felt it would be better to combine 0001 + 0002 (approach
    > > #3), then 0002 would be a patch that *replaces* approach #3 logic with
    > > approach #1 logic. That way, 0001 is self-contained, and 0002 is an
    > > evolution of the feature.
    > >
    >
    > I agree that the current patch structure is hard to follow and doesn’t
    > add much value, since patch001 isn’t aligned with a specific approach.
    > How about this patch structure:
    >
    > patch001: Implement EXCEPT syntax and CREATE PUBLICATION changes. This
    > includes all changes required to correctly populate
    > pg_publication_tables and pg_publication_rel.
    >
    > Expectation for patch001:
    > When this patch is applied on its own, CREATE PUBLICATION command with
    > the EXCEPT clause should work, and the appropriate entries should
    > appear in pg_publication_tables/pg_publication_rel with the except
    > flag set correctly. No publishing expected in this patch.
    >
    > patch002: Enable publishing and subscription support.
    > All changes required to make subscriptions work should go here, including:
    > --table sync and other subscriber-side changes
    > --pgoutput logic to determine which tables are published
    >
    > Expectation for patch002:
    > Changes should be published and replicated correctly to the subscriber.
    >
    > patch003: pg_dump, tests, documentation, etc.
    >
    > All of the above changes/patches are intended to support Approach001.
    >
    > Approach003 has a limitation in that it allows 'only' ROOT table to be
    > specified in the EXCEPT clause. If tab_root is listed in EXCEPT and a
    > user later attempts to attach it as a partition of another table
    > using:
    > 'ALTER TABLE root ATTACH PARTITION tab_root', we would need to block
    > the ATTACH PARTITION command. Given this, we should first try to
    > implement Approach001 and evaluate its feasibility. If it turns out to
    > be impractical,
    > we can fall back to Approach003 or consider other alternatives. For
    > now, the Approach003 patch can remain on hold.
    >
    
    Hi Shveta,
    
    Your split proposal sounds like an improvement.
    
    Personally, I feel each patch should be *self-contained*, not only
    because then it could safely be pushed independently, but also because
    I normally work 1 patch at a time
    - apply 0001 / review 0001
    - apply 0002 / review 0002
    - etc
    
    Tests:
    IMO if patch 0001 does something then it should also include the
    necessary tests for that "something". Then patch 0002 adds something
    more so the 0002 tests should evolve to add a few more tests to what
    were there for 0001 etc.
    IOW, I don't really want to be forced to apply everything in order to
    run the tests for 0001.
    
    Docs:
    Again personally I prefer everything *self-contained* for 0001 would
    have docs for 001 etc, so I can focus on just one improvement at a
    time, but I'm OK with docs being kept separated if others prefer it
    that way.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  192. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-02T06:12:21Z

    On Mon, Feb 2, 2026 at 11:27 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Mon, Feb 2, 2026 at 4:32 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Fri, Jan 30, 2026 at 1:46 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > > > Hi Vignesh.
    > > >
    > > > Some review comments for v38-0001.
    > > >
    > > > ======
    > > >
    > > > 1. General. Patch structure
    > > >
    > > > This patch set structure seems muddled to me; I think patch 0001
    > > > requires proper handling for EXCEPT (partitions), otherwise, it cannot
    > > > be independently committed.
    > > >
    > > > It's my understanding that the goal is to try approach #1, but if that
    > > > proves too difficult, then the fallback would be approach #3. Yet
    > > > AFAICT this patch 0001 is neither -- I have no idea anymore what patch
    > > > 0001 does for partitions; IIUC it looks like just old partition logic
    > > > from a few versions back (???).
    > > >
    > > > Personally, I felt it would be better to combine 0001 + 0002 (approach
    > > > #3), then 0002 would be a patch that *replaces* approach #3 logic with
    > > > approach #1 logic. That way, 0001 is self-contained, and 0002 is an
    > > > evolution of the feature.
    > > >
    > >
    > > I agree that the current patch structure is hard to follow and doesn’t
    > > add much value, since patch001 isn’t aligned with a specific approach.
    > > How about this patch structure:
    > >
    > > patch001: Implement EXCEPT syntax and CREATE PUBLICATION changes. This
    > > includes all changes required to correctly populate
    > > pg_publication_tables and pg_publication_rel.
    > >
    > > Expectation for patch001:
    > > When this patch is applied on its own, CREATE PUBLICATION command with
    > > the EXCEPT clause should work, and the appropriate entries should
    > > appear in pg_publication_tables/pg_publication_rel with the except
    > > flag set correctly. No publishing expected in this patch.
    > >
    > > patch002: Enable publishing and subscription support.
    > > All changes required to make subscriptions work should go here, including:
    > > --table sync and other subscriber-side changes
    > > --pgoutput logic to determine which tables are published
    > >
    > > Expectation for patch002:
    > > Changes should be published and replicated correctly to the subscriber.
    > >
    > > patch003: pg_dump, tests, documentation, etc.
    > >
    > > All of the above changes/patches are intended to support Approach001.
    > >
    > > Approach003 has a limitation in that it allows 'only' ROOT table to be
    > > specified in the EXCEPT clause. If tab_root is listed in EXCEPT and a
    > > user later attempts to attach it as a partition of another table
    > > using:
    > > 'ALTER TABLE root ATTACH PARTITION tab_root', we would need to block
    > > the ATTACH PARTITION command. Given this, we should first try to
    > > implement Approach001 and evaluate its feasibility. If it turns out to
    > > be impractical,
    > > we can fall back to Approach003 or consider other alternatives. For
    > > now, the Approach003 patch can remain on hold.
    > >
    >
    > Hi Shveta,
    >
    > Your split proposal sounds like an improvement.
    >
    > Personally, I feel each patch should be *self-contained*, not only
    > because then it could safely be pushed independently, but also because
    > I normally work 1 patch at a time
    > - apply 0001 / review 0001
    > - apply 0002 / review 0002
    > - etc
    >
    > Tests:
    > IMO if patch 0001 does something then it should also include the
    > necessary tests for that "something". Then patch 0002 adds something
    > more so the 0002 tests should evolve to add a few more tests to what
    > were there for 0001 etc.
    > IOW, I don't really want to be forced to apply everything in order to
    > run the tests for 0001.
    
    +1. we should be adding tests in the concerned patch
    
    >
    > Docs:
    > Again personally I prefer everything *self-contained* for 0001 would
    > have docs for 001 etc, so I can focus on just one improvement at a
    > time, but I'm OK with docs being kept separated if others prefer it
    > that way.
    
    I think splitting the doccould be challenging. We can start by keeping
    it in patch003, and once we have better clarity, we can consider
    splitting the doc if needed.
    OTOH, each patch’s commit message should clearly state its purpose, so
    the EXCEPT concept can be understood without referring to the doc
    coming in later patch.
    
    thanks
    Shveta
    
    
    
    
  193. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-02-02T11:48:45Z

    On Thu, 29 Jan 2026 at 20:41, vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Wed, 28 Jan 2026 at 10:46, shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > Thank You for the patch.
    > >
    > > 1)
    > > There are certain parts of Approach 3 still present in Approach 1, as
    > > an example:
    > >
    > > 1a)
    > > +      For partitioned tables, only the root partitioned table may be specified
    > > +      in <literal>EXCEPT TABLE</literal>.
    > >
    > > 1b)
    > > + /*
    > > + * Only the topmost ancestor of a partitioned table can be specified
    > > + * in EXCEPT TABLES clause of a FOR ALL TABLES publication. So fetch
    > > + * the publications excluding the topmost ancestor only.
    > > + */
    > > + GetRelationPublications(llast_oid(ancestors), NULL, &exceptpuboids);
    > > +
    > >
    > > 1c)
    > > + /* Check if the partiton is part of EXCEPT list of any publication */
    > > + GetRelationPublications(RelationGetRelid(attachrel), NULL, &except_pubids);
    > > + if (except_pubids != NIL)
    > > + ereport(ERROR,
    > > + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > > + errmsg("cannot attach relation \"%s\" as partition because it is
    > > part of EXCEPT list in publication",
    > > + RelationGetRelationName(attachrel))));
    > > +
    > >
    > > Overall, please take a diff of v35 and v37 to find such parts and
    > > please correct these and others (if any).
    > >
    > > 2)
    > > Also I don't think if below is correct statement for Approach 1:
    > >
    > > + * 2. For a partition, if the topmost ancestor is part of
    > > + *   the EXCEPT TABLE list, we don't publish it.
    > >
    > > Even if any ancestor is part of EXECPT list (not only top most) we
    > > should not publish that partition, isn't it?
    > >
    > > 3)
    > > I tried a scenario and found that incremental replication is not
    > > working correctly. Attached the failing test as Approach1_v37_fail.txt
    > >
    > > Once these basic things are corrected, I can review further.
    >
    > These comments are addressed in the v38 version patch attached.
    > Currently the approach-3 changes is present separately in
    > v38-0002-Restrict-EXCEPT-TABLE-to-root-partitioned-tables-apporach-3.patch
    > which can be applied on top of
    > v38-0001-Skip-publishing-the-tables-specified-in-EXCEPT-T.patch.
    > Similarly the approach-1 changes is present separately in
    > v38-0002-handle-EXCEPT-TABLE-correctly-with-partitioned-approach-1.patch
    > which can be applied on top of
    > v38-0001-Skip-publishing-the-tables-specified-in-EXCEPT-T.patch.
    > Currently few of the query are logged as LOG messages, I will reduce
    > the log level for these queries once few rounds of review are
    > completed on the queries.
    
    Hi Vignesh,
    
    I reviewed v38-0002-handle-EXCEPT-TABLE-correctly-with-partitioned-approach-1.patch
    patch. Here are my comments:
    
    1. in pgoutput.c:
    @@ -2228,6 +2228,11 @@ get_rel_sync_entry(PGOutputData *data, Relation relation)
      {
      List    *ancestors = get_partition_ancestors(relid);
    
    + GetRelationPublications(relid, NULL, &exceptpubids);
    +
    + foreach_oid(ancestor, ancestors)
    + GetRelationPublications(ancestor, NULL, &exceptpubids);
    +
      pub_relid = llast_oid(ancestors);
      ancestor_level = list_length(ancestors);
      }
    
    when we create a publication on a partitioned table and
    publish_via_partition_root is false,
    the changes of the partitions should not be published as discussed.
    But, while testing I found that the changes are being published.
    
    Test:
    CREATE SCHEMA sc1;
    CREATE TABLE sc1.t1(id int) PARTITION BY RANGE(id);
    CREATE TABLE sc1.child1(id int)  PARTITION BY RANGE(id);
    CREATE TABLE sc1.child2 PARTITION OF sc1.t1 FOR VALUES FROM (101) TO (200);
    CREATE TABLE sc1.child1_1 PARTITION OF sc1.child1 FOR VALUES FROM (0) TO (50);
    CREATE TABLE sc1.child1_2 PARTITION OF sc1.child1 FOR VALUES FROM (51) TO (100);
    ALTER TABLE sc1.t1 ATTACH PARTITION sc1.child1 FOR VALUES FROM (0) TO (100);
    
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (sc1.t1);
    insert into sc1. t1 values (1), (60), (150);
    
    I get following output:
    postgres=# SELECT * FROM pg_logical_slot_get_binary_changes(
        's1',
        NULL,
        NULL,
        'proto_version', '1',
        'publication_names', 'pub1'
    );
        lsn     | xid |                                 data
    ------------+-----+----------------------------------------------------------------------
     0/0175A1A0 | 777 | \x42000000000175a3f80002eccfc016338000000309
     0/0175A1A0 | 777 |
    \x520000400a736331006368696c64315f31006400010069640000000017ffffffff
     0/0175A1A0 | 777 | \x490000400a4e0001740000000131
     0/0175A268 | 777 |
    \x520000400d736331006368696c64315f32006400010069640000000017ffffffff
     0/0175A268 | 777 | \x490000400d4e000174000000023630
     0/0175A330 | 777 |
    \x5200004007736331006368696c6432006400010069640000000017ffffffff
     0/0175A330 | 777 | \x49000040074e00017400000003313530
     0/0175A428 | 777 | \x4300000000000175a3f8000000000175a4280002eccfc0163380
    (8 rows)
    
    I think this check should be done irrespective of the value of
    publish_via_partition_root.
    + foreach_oid(ancestor, ancestors)
    + GetRelationPublications(ancestor, NULL, &exceptpubids);
    
    2. In get_rel_sync_entry we have a comment:
    *
    * 2. If pubviaroot is not set, check whether the relation itself
    * is included in the EXCEPT TABLE list. If so, do not publish the
    * change.
    *
    This comment does not seem correct. Even if publish_via_partition_root
    is false, we should not publish changes if any of its ancestors is
    excluded.
    This is my understanding of approach 1 [1]. Correct me if I am wrong.
    
    3. In tablesync.c:
    
    + /*
    + * Store the tables as a list of schemaname and tablename.
    + */
    + natt = 0;
    this assignment 'natt = 0', is not required
    
    4. While testing, I noticed that the new query introduced in tablesync
    can be invoked for "FOR TABLE". SHould we only call it for "ALL
    TABLES" publications?
    + if (server_version >= 190000 && !is_partition &&
    + lrel->relkind == RELKIND_PARTITIONED_TABLE)
    + {
    + resetStringInfo(&cmd);
    +
    + /*
    + * This query recursively traverses the inheritance (partition) tree
    + * starting from the given table OID and determines which leaf
    + * relations should be included for replication. Exclusion propagates
    + * from parent to child, and a relation is also treated as excluded if
    + * it is explicitly marked with prexcept = true in pg_publication_rel
    + * for the specified publications. The final result returns only
    + * non excluded leaf relations.
    + */
    Test:
    Create publication for table sc1.t1 using (publish_via_partition_root
    = true) and create subscription on it. In subscriber logs we can see
    the logs for the new query.
    sc1.t1 has the same structure as in comment 1.
    
    5. I was testing some scenarios and found a difference in behaviour of
    tablesync and incremental changes.
    Consider the same table structure as comment1. But we have two publications as:
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (sc1.child1) WITH
    (publish_via_partition_root = true);
    CREATE PUBLICATION pub2 FOR ALL TABLES WITH (publish_via_partition_root = true);
    
    And subscription is created
    CREATE SUBSCRIPTION sub1 CONNECTION 'host=localhost port=5432
    dbname=postgres' PUBLICATION pub1, pub2;
    
    Before creating the subscription I did a insert on publisher node:
    INSERT INTO sc1.t1 VALUES(1), (51), (101);
    
    Output after tablesync:
    postgres=# select * from sc1.t1;
     id
    -----
     101
    (1 row)
    
    Now, I again did a insert on publisher node:
    INSERT INTO sc1.t1 VALUES(1), (51), (101);
    
    Output after incremental sync:
    postgres=# select * from sc1.t1;
     id
    -----
     101
       1
      51
     101
    (4 rows)
    
    What should be the behaviour if a partitioned table is published by a
    publication and excluded by another publication?
    
    I checked the behaviour of publication with row_filter for similar conditions:
    CREATE PUBLICATION pub1 FOR TABLE sc1.t1 WHERE (id > 100) WITH
    (publish_via_partition_root = true);
    CREATE PUBLICATION pub2 FOR TABLE sc1.t1 WITH
    (publish_via_partition_root = true);
    
    Here is the behaviour:
    Before creating the publication I did a insert on publisher node:
    INSERT INTO sc1.t1 VALUES(1), (51), (101);
    
    Output after tablesync:
    postgres=# select * from sc1.t1;
     id
    -----
     101
       1
      51
    (3 rows)
    
    Now, I again did a insert on publisher node:
    INSERT INTO sc1.t1 VALUES(1), (51), (101);
    
    Output after incremental sync:
    postgres=# select * from sc1.t1;
     id
    -----
     101
       1
      51
       1
      51
     101
    (6 rows)
    
    So, I think in the EXCEPT table case we should follow the same
    behaviour with tablesync i.e. publish the change if any of the
    publications publishes it . Thoughts?
    
    [1]: https://www.postgresql.org/message-id/CAJpy0uD81HRrMYr7S-6AV4W2PtbGKM-nf2D89zsoMHJ9jZssUg@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  194. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-03T16:52:52Z

    On Mon, 2 Feb 2026 at 17:18, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > I reviewed v38-0002-handle-EXCEPT-TABLE-correctly-with-partitioned-approach-1.patch
    > patch. Here are my comments:
    >
    > 4. While testing, I noticed that the new query introduced in tablesync
    > can be invoked for "FOR TABLE". SHould we only call it for "ALL
    > TABLES" publications?
    > + if (server_version >= 190000 && !is_partition &&
    > + lrel->relkind == RELKIND_PARTITIONED_TABLE)
    > + {
    > + resetStringInfo(&cmd);
    > +
    > + /*
    > + * This query recursively traverses the inheritance (partition) tree
    > + * starting from the given table OID and determines which leaf
    > + * relations should be included for replication. Exclusion propagates
    > + * from parent to child, and a relation is also treated as excluded if
    > + * it is explicitly marked with prexcept = true in pg_publication_rel
    > + * for the specified publications. The final result returns only
    > + * non excluded leaf relations.
    > + */
    > Test:
    > Create publication for table sc1.t1 using (publish_via_partition_root
    > = true) and create subscription on it. In subscriber logs we can see
    > the logs for the new query.
    > sc1.t1 has the same structure as in comment 1.
    
    We will not know if it is a table publication or all tables
    publication from here. Also there can be a possibility of multiple
    publications.  We will invoke it and handle it from the publisher to
    return appropriate tables.
    
    Thanks for the review and detailed comments. All remaining feedback
    has been addressed in the attached v39 patch.
    
    In addition, the SQL query previously used to compute the set of
    effective tables has been replaced with a C implementation. The SQL
    approach had become increasingly complex and difficult to reason
    about, especially as more publication combinations were added.
    Implementing this logic in C significantly improves readability and
    maintainability, and makes it easier to handle complex scenarios, such
    as:
    a) Multiple publications where one publication has no EXCEPT tables
    and another does.
    b) Multiple publications where one publication is an ALL TABLES
    publication with EXCEPT, while another is a table-specific
    publication.
    c) Multiple publications where none of the publications define any
    EXCEPT tables.
    
    Peter's comments from [1] and Shveta's comments form [2] will be
    addressed in the next version.
    [1] - https://www.postgresql.org/message-id/CAHut%2BPsiWwmNSuCXTWM0iPDm3yGskLts-fukELTB__rbBids-A%40mail.gmail.com
    [2] - https://www.postgresql.org/message-id/CAJpy0uAOvtMBP-oV9Tgoznt5-UsE2dzAjZW3eJmgKcU-X-vEzg%40mail.gmail.com
    
    Regards,
    Vignesh
    
  195. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-02-04T08:16:06Z

    On Tue, 3 Feb 2026 at 22:23, vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Mon, 2 Feb 2026 at 17:18, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > I reviewed v38-0002-handle-EXCEPT-TABLE-correctly-with-partitioned-approach-1.patch
    > > patch. Here are my comments:
    > >
    > > 4. While testing, I noticed that the new query introduced in tablesync
    > > can be invoked for "FOR TABLE". SHould we only call it for "ALL
    > > TABLES" publications?
    > > + if (server_version >= 190000 && !is_partition &&
    > > + lrel->relkind == RELKIND_PARTITIONED_TABLE)
    > > + {
    > > + resetStringInfo(&cmd);
    > > +
    > > + /*
    > > + * This query recursively traverses the inheritance (partition) tree
    > > + * starting from the given table OID and determines which leaf
    > > + * relations should be included for replication. Exclusion propagates
    > > + * from parent to child, and a relation is also treated as excluded if
    > > + * it is explicitly marked with prexcept = true in pg_publication_rel
    > > + * for the specified publications. The final result returns only
    > > + * non excluded leaf relations.
    > > + */
    > > Test:
    > > Create publication for table sc1.t1 using (publish_via_partition_root
    > > = true) and create subscription on it. In subscriber logs we can see
    > > the logs for the new query.
    > > sc1.t1 has the same structure as in comment 1.
    >
    > We will not know if it is a table publication or all tables
    > publication from here. Also there can be a possibility of multiple
    > publications.  We will invoke it and handle it from the publisher to
    > return appropriate tables.
    >
    > Thanks for the review and detailed comments. All remaining feedback
    > has been addressed in the attached v39 patch.
    >
    > In addition, the SQL query previously used to compute the set of
    > effective tables has been replaced with a C implementation. The SQL
    > approach had become increasingly complex and difficult to reason
    > about, especially as more publication combinations were added.
    > Implementing this logic in C significantly improves readability and
    > maintainability, and makes it easier to handle complex scenarios, such
    > as:
    > a) Multiple publications where one publication has no EXCEPT tables
    > and another does.
    > b) Multiple publications where one publication is an ALL TABLES
    > publication with EXCEPT, while another is a table-specific
    > publication.
    > c) Multiple publications where none of the publications define any
    > EXCEPT tables.
    >
    > Peter's comments from [1] and Shveta's comments form [2] will be
    > addressed in the next version.
    > [1] - https://www.postgresql.org/message-id/CAHut%2BPsiWwmNSuCXTWM0iPDm3yGskLts-fukELTB__rbBids-A%40mail.gmail.com
    > [2] - https://www.postgresql.org/message-id/CAJpy0uAOvtMBP-oV9Tgoznt5-UsE2dzAjZW3eJmgKcU-X-vEzg%40mail.gmail.com
    >
    Hi Vignesh,
    
    Thanks for providing the updated patch.
    While testing the patch I have found a small bug:
    ```
    +               if (am_partition)
                    {
                        List       *ancestors = get_partition_ancestors(relid);
    
    -                   pub_relid = llast_oid(ancestors);
    -                   ancestor_level = list_length(ancestors);
    +                   foreach_oid(ancestor, ancestors)
    +                       GetRelationPublications(ancestor, NULL, &exceptpubids);
    +
    +                   if (pub->pubviaroot)
    +                   {
    +                       pub_relid = llast_oid(ancestors);
    +                       ancestor_level = list_length(ancestors);
    +                   }
                    }
    
                    GetRelationPublications(pub_relid, NULL, &exceptpubids);
    ```
    Here at last "GetRelationPublications" should be called for 'relid',
    instead of 'pub_relid'. The call for function
    "GetRelationPublications" can be skipped for table which is not root
    partitioned but is specified explicitly in the EXCEPT TABLE list.
    Test:
    The partition structure is as:
    sc1.t1
        - sc1.child1
              - sc1.child1_1
              - sc1.child1_2
        - sc1.child2
    
    Suppose we create two publications.
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (sc1.child2) WITH
    (publish_via_partition_root = true);
    CREATE PUBLICATION pub2 FOR TABLE sc1.child1_1 WITH
    (publish_via_partition_root = true);
    And on subscriber node we create a subscriber as:
    CREATE SUBSCRIPTION sub1 CONNECTION 'host=localhost port=5432
    dbname=postgres' PUBLICATION pub1, pub2;
    
    Ideally changes of 'sc1.child2' should not be replicated, but
    increment sync replicates the data for 'sc1.child2'.
    I have fixed this in the latest version of patch.
    I have also added extended tests for this patch as 0003 patch. The
    last test in the file tests the above issue.
    
    I have also addressed Peter's comment in [1].
    For comment:
    > 3.
    > + appendPQExpBuffer(query, "ONLY %s", fmtQualifiedDumpable(tbinfo));
    >
    > I think that unconditionally choosing "ONLY" here may not be the right
    > thing to do, particularly when the excluded table is a partitioned
    > table. (e.g. this is related to off-list discussions about how to
    > EXCEPT partition tables).
    The ONLY clause has no effect with partition tables but ONLY clause is
    required for inherited tables.
    And ALTER PUBLICATION ... ADD TABLE also uses ONLY clause
    unconditionally. So, I think this behaviour is consistent with ALTER
    PUBLICATION ... ADD TABLE
    According to the latest discussion, I think we are proceeding with
    Approach 1 in [2]. And EXCEPT TABLE is independent of ONLY. So, I
    think we can keep it.
    
    [1]: https://www.postgresql.org/message-id/CAHut+PvmCPdbScDoGV3jX42STm2F3DUWbj7nnbn5Y_zs6w8XWA@mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAJpy0uD81HRrMYr7S-6AV4W2PtbGKM-nf2D89zsoMHJ9jZssUg@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  196. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-02-05T05:29:06Z

    On Tue, 3 Feb 2026 at 22:23, vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Mon, 2 Feb 2026 at 17:18, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > I reviewed v38-0002-handle-EXCEPT-TABLE-correctly-with-partitioned-approach-1.patch
    > > patch. Here are my comments:
    > >
    > > 4. While testing, I noticed that the new query introduced in tablesync
    > > can be invoked for "FOR TABLE". SHould we only call it for "ALL
    > > TABLES" publications?
    > > + if (server_version >= 190000 && !is_partition &&
    > > + lrel->relkind == RELKIND_PARTITIONED_TABLE)
    > > + {
    > > + resetStringInfo(&cmd);
    > > +
    > > + /*
    > > + * This query recursively traverses the inheritance (partition) tree
    > > + * starting from the given table OID and determines which leaf
    > > + * relations should be included for replication. Exclusion propagates
    > > + * from parent to child, and a relation is also treated as excluded if
    > > + * it is explicitly marked with prexcept = true in pg_publication_rel
    > > + * for the specified publications. The final result returns only
    > > + * non excluded leaf relations.
    > > + */
    > > Test:
    > > Create publication for table sc1.t1 using (publish_via_partition_root
    > > = true) and create subscription on it. In subscriber logs we can see
    > > the logs for the new query.
    > > sc1.t1 has the same structure as in comment 1.
    >
    > We will not know if it is a table publication or all tables
    > publication from here. Also there can be a possibility of multiple
    > publications.  We will invoke it and handle it from the publisher to
    > return appropriate tables.
    >
    > Thanks for the review and detailed comments. All remaining feedback
    > has been addressed in the attached v39 patch.
    >
    > In addition, the SQL query previously used to compute the set of
    > effective tables has been replaced with a C implementation. The SQL
    > approach had become increasingly complex and difficult to reason
    > about, especially as more publication combinations were added.
    > Implementing this logic in C significantly improves readability and
    > maintainability, and makes it easier to handle complex scenarios, such
    > as:
    > a) Multiple publications where one publication has no EXCEPT tables
    > and another does.
    > b) Multiple publications where one publication is an ALL TABLES
    > publication with EXCEPT, while another is a table-specific
    > publication.
    > c) Multiple publications where none of the publications define any
    > EXCEPT tables.
    >
    > Peter's comments from [1] and Shveta's comments form [2] will be
    > addressed in the next version.
    > [1] - https://www.postgresql.org/message-id/CAHut%2BPsiWwmNSuCXTWM0iPDm3yGskLts-fukELTB__rbBids-A%40mail.gmail.com
    > [2] - https://www.postgresql.org/message-id/CAJpy0uAOvtMBP-oV9Tgoznt5-UsE2dzAjZW3eJmgKcU-X-vEzg%40mail.gmail.com
    
    Hi Vignesh,
    
    I found one bug.
    
    For Partition structure:
    sc1.t1
        - sc1.child1
            - sc1.child1_1
            - sc1.child1_2
        - sc1.child2
    
    If we specify sc1.child1 and sc1.child2 in the EXCEPT list and
    publish_via_partition_root = true,
    tablesync publish all the changes. It is wrong.
    Behavour of incremental sync is correct and it donot publish any changes.
    Publication:
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (sc1.child1,
    sc1.child2) WITH (publish_via_partition_root = true);
    
    I checked, pg_get_publication_effective_tables is not returning any row:
    postgres=# SELECT schemaname, relname FROM
    pg_get_publication_effective_tables(16385, ARRAY['pub1']);
     schemaname | relname
    ------------+---------
    (0 rows)
    
    But the COPY command in tablesync is formed as:
    COPY sc1.t1 (id) TO STDOUT
    
    Same behaviour when publication is:
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLE (sc1.child1_1,
    sc1.child1_2, sc1.child2) WITH (publish_via_partition_root = true);
    
    I think it is because, when we specify all the partitions of a
    partitioned table in the EXCEPT list, the function
    'fetch_remote_table_info' will have 'effective_relations' as an empty
    list.
    So, instead of the condition 'if (effective_relations &&
    list_length(effective_relations))' it will go inside the 'else if'
    condition and form the above COPY command.
    
    One possible solution I can think of is if all the partitions of a
    partitioned table is excluded using EXCEPT TABLE, we can avoid having
    the partitioned table in 'pg_publication_tables' and hence the
    pg_subsciption_rel will also not have the corresponding entry.
    So, the issue can be avoided.
    
    I have added the fix of the same in the latest v41 patch and added the
    corresponding test in 101_test.pl file.
    I have also merged the v40-0001 and v40-0002 patches  to form v41-0001
    patch and v41-0002 has the extended tests.
    
    Thanks,
    Shlok Kyal
    
  197. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-06T10:23:57Z

    On Thu, Feb 5, 2026 at 10:59 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > I have added the fix of the same in the latest v41 patch and added the
    > corresponding test in 101_test.pl file.
    > I have also merged the v40-0001 and v40-0002 patches  to form v41-0001
    > patch and v41-0002 has the extended tests.
    >
    
    Thank You for the patched Shlok. While testing I found a case where
    table-sync and incremental-sync are not replicating same set of
    tables.
    
    I have attached the test-case and results in DifferentPubViaRoot.txt
    
    The problem happens when we have a subscriber subscribing to multiple
    pubs with different EXCEPT and different PUBLISH_VIA_PARTITION_ROOT
    value. Example:
    
    CREATE PUBLICATION pub1 for ALL TABLES EXCEPT table (tab_part_1_p1,
    tab_part_2_p2) WITH (PUBLISH_VIA_PARTITION_ROOT=true);
    CREATE PUBLICATION pub2 for ALL TABLES EXCEPT table (tab_part_2) WITH
    (PUBLISH_VIA_PARTITION_ROOT=false);
    
    We need to decide the behaviour of such a case for Apporach1. It will
    be good to analyze row-filter and column-list behaviour for equivalent
    cases i.e. publication publishing the same set of tables with
    different row-filters and different PUBLISH_VIA_PARTITION_ROOT value.
    Once we have clarity on that, we can decide about the behaviour here.
    
    thanks
    Shveta
    
  198. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-08T10:20:58Z

    On Fri, Feb 6, 2026 at 3:54 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Thu, Feb 5, 2026 at 10:59 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > I have added the fix of the same in the latest v41 patch and added the
    > > corresponding test in 101_test.pl file.
    > > I have also merged the v40-0001 and v40-0002 patches  to form v41-0001
    > > patch and v41-0002 has the extended tests.
    > >
    >
    > Thank You for the patched Shlok. While testing I found a case where
    > table-sync and incremental-sync are not replicating same set of
    > tables.
    >
    > I have attached the test-case and results in DifferentPubViaRoot.txt
    >
    > The problem happens when we have a subscriber subscribing to multiple
    > pubs with different EXCEPT and different PUBLISH_VIA_PARTITION_ROOT
    > value. Example:
    >
    > CREATE PUBLICATION pub1 for ALL TABLES EXCEPT table (tab_part_1_p1,
    > tab_part_2_p2) WITH (PUBLISH_VIA_PARTITION_ROOT=true);
    > CREATE PUBLICATION pub2 for ALL TABLES EXCEPT table (tab_part_2) WITH
    > (PUBLISH_VIA_PARTITION_ROOT=false);
    >
    > We need to decide the behaviour of such a case for Apporach1.
    >
    
    It is better to disallow such combinations where combining
    publications could lead to contradictory behavior. For example, pub1:
    FOR ALL Tables EXCEPT (tab1) and pub2: FOR TABLE tab1. Now, combining
    pub1 and pub2 via subscription should result in an ERROR. We have
    similar restrictions for column lists. See section: "Warning:
    Combining Column Lists from Multiple Publications" in docs [1]. Does
    that sound reasonable to you?
    
    [1] - https://www.postgresql.org/docs/devel/logical-replication-col-lists.html
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  199. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-02-09T01:10:52Z

    On Sun, Feb 8, 2026 at 9:21 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Feb 6, 2026 at 3:54 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Thu, Feb 5, 2026 at 10:59 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > I have added the fix of the same in the latest v41 patch and added the
    > > > corresponding test in 101_test.pl file.
    > > > I have also merged the v40-0001 and v40-0002 patches  to form v41-0001
    > > > patch and v41-0002 has the extended tests.
    > > >
    > >
    > > Thank You for the patched Shlok. While testing I found a case where
    > > table-sync and incremental-sync are not replicating same set of
    > > tables.
    > >
    > > I have attached the test-case and results in DifferentPubViaRoot.txt
    > >
    > > The problem happens when we have a subscriber subscribing to multiple
    > > pubs with different EXCEPT and different PUBLISH_VIA_PARTITION_ROOT
    > > value. Example:
    > >
    > > CREATE PUBLICATION pub1 for ALL TABLES EXCEPT table (tab_part_1_p1,
    > > tab_part_2_p2) WITH (PUBLISH_VIA_PARTITION_ROOT=true);
    > > CREATE PUBLICATION pub2 for ALL TABLES EXCEPT table (tab_part_2) WITH
    > > (PUBLISH_VIA_PARTITION_ROOT=false);
    > >
    > > We need to decide the behaviour of such a case for Apporach1.
    > >
    >
    > It is better to disallow such combinations where combining
    > publications could lead to contradictory behavior. For example, pub1:
    > FOR ALL Tables EXCEPT (tab1) and pub2: FOR TABLE tab1. Now, combining
    > pub1 and pub2 via subscription should result in an ERROR. We have
    > similar restrictions for column lists. See section: "Warning:
    > Combining Column Lists from Multiple Publications" in docs [1]. Does
    > that sound reasonable to you?
    >
    > [1] - https://www.postgresql.org/docs/devel/logical-replication-col-lists.html
    >
    
    Hi Amit.
    
    I understand there can be some tricky scenarios where partitions are
    involved, but I was not sure why "pub1: FOR ALL Tables EXCEPT (tab1)
    and pub2: FOR TABLE tab1" is an example of contradictory behaviour.
    
    Consider if the publisher has 3 tables tab1,tab2,tab3:
    Here, "pub1: FOR ALL Tables EXCEPT (tab1)" is like a shorthand for
    saying "pub1: FOR TABLE tab2,tab3"
    So what's wrong for the subscriber to combine pub1 and pub2 in this case?
    
    Indeed, if the user wants ALL TABLES, but they want a row filter
    applied *only* for tab1, isn't this combination probably a good way to
    achieve that? e.g. "pub1: FOR ALL Tables EXCEPT (tab1) and pub2: FOR
    TABLE tab1 WHERE (c == 99)".
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  200. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-09T05:36:03Z

    On Mon, Feb 9, 2026 at 6:41 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Sun, Feb 8, 2026 at 9:21 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Fri, Feb 6, 2026 at 3:54 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Thu, Feb 5, 2026 at 10:59 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > > >
    > > > > I have added the fix of the same in the latest v41 patch and added the
    > > > > corresponding test in 101_test.pl file.
    > > > > I have also merged the v40-0001 and v40-0002 patches  to form v41-0001
    > > > > patch and v41-0002 has the extended tests.
    > > > >
    > > >
    > > > Thank You for the patched Shlok. While testing I found a case where
    > > > table-sync and incremental-sync are not replicating same set of
    > > > tables.
    > > >
    > > > I have attached the test-case and results in DifferentPubViaRoot.txt
    > > >
    > > > The problem happens when we have a subscriber subscribing to multiple
    > > > pubs with different EXCEPT and different PUBLISH_VIA_PARTITION_ROOT
    > > > value. Example:
    > > >
    > > > CREATE PUBLICATION pub1 for ALL TABLES EXCEPT table (tab_part_1_p1,
    > > > tab_part_2_p2) WITH (PUBLISH_VIA_PARTITION_ROOT=true);
    > > > CREATE PUBLICATION pub2 for ALL TABLES EXCEPT table (tab_part_2) WITH
    > > > (PUBLISH_VIA_PARTITION_ROOT=false);
    > > >
    > > > We need to decide the behaviour of such a case for Apporach1.
    > > >
    > >
    > > It is better to disallow such combinations where combining
    > > publications could lead to contradictory behavior. For example, pub1:
    > > FOR ALL Tables EXCEPT (tab1) and pub2: FOR TABLE tab1. Now, combining
    > > pub1 and pub2 via subscription should result in an ERROR. We have
    > > similar restrictions for column lists. See section: "Warning:
    > > Combining Column Lists from Multiple Publications" in docs [1]. Does
    > > that sound reasonable to you?
    > >
    > > [1] - https://www.postgresql.org/docs/devel/logical-replication-col-lists.html
    > >
    >
    > Hi Amit.
    >
    > I understand there can be some tricky scenarios where partitions are
    > involved, but I was not sure why "pub1: FOR ALL Tables EXCEPT (tab1)
    > and pub2: FOR TABLE tab1" is an example of contradictory behaviour.
    >
    > Consider if the publisher has 3 tables tab1,tab2,tab3:
    > Here, "pub1: FOR ALL Tables EXCEPT (tab1)" is like a shorthand for
    > saying "pub1: FOR TABLE tab2,tab3"
    > So what's wrong for the subscriber to combine pub1 and pub2 in this case?
    >
    
    It is because one of the publications (pub2) indicates to include a
    particular table tab1 and the other one (pub1) to exclude the same
    table. And things become much more complex when the Except list
    contains partitions as shown in Shveta's example. So, I think it makes
    sense to keep things simple at least for the first version, we can
    consider to uplift this restriction if we see some use cases from the
    field.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  201. Re: Skipping schema changes in publication

    David G. Johnston <david.g.johnston@gmail.com> — 2026-02-09T06:22:55Z

    On Sunday, February 8, 2026, Amit Kapila <amit.kapila16@gmail.com> wrote:
    
    > On Mon, Feb 9, 2026 at 6:41 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > > Hi Amit.
    > >
    > > I understand there can be some tricky scenarios where partitions are
    > > involved, but I was not sure why "pub1: FOR ALL Tables EXCEPT (tab1)
    > > and pub2: FOR TABLE tab1" is an example of contradictory behaviour.
    > >
    > > Consider if the publisher has 3 tables tab1,tab2,tab3:
    > > Here, "pub1: FOR ALL Tables EXCEPT (tab1)" is like a shorthand for
    > > saying "pub1: FOR TABLE tab2,tab3"
    > > So what's wrong for the subscriber to combine pub1 and pub2 in this case?
    > >
    >
    > It is because one of the publications (pub2) indicates to include a
    > particular table tab1 and the other one (pub1) to exclude the same
    > table. And things become much more complex when the Except list
    > contains partitions as shown in Shveta's example. So, I think it makes
    > sense to keep things simple at least for the first version, we can
    > consider to uplift this restriction if we see some use cases from the
    > field.
    >
    >
    >
    I’m with Peter here - I do not think it is wise to expose the exception
    listing outside the publication.  Publication combinations should be purely
    additive in much the same way grants are in the system.  Except lists are
    internal shorthand for describing the positive list of tables a publication
    makes available - all tables except.
    
    David J.
    
  202. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-10T06:05:13Z

    On Mon, Feb 9, 2026 at 11:52 AM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    >
    > On Sunday, February 8, 2026, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >>
    >> On Mon, Feb 9, 2026 at 6:41 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >>
    >> > Hi Amit.
    >> >
    >> > I understand there can be some tricky scenarios where partitions are
    >> > involved, but I was not sure why "pub1: FOR ALL Tables EXCEPT (tab1)
    >> > and pub2: FOR TABLE tab1" is an example of contradictory behaviour.
    >> >
    >> > Consider if the publisher has 3 tables tab1,tab2,tab3:
    >> > Here, "pub1: FOR ALL Tables EXCEPT (tab1)" is like a shorthand for
    >> > saying "pub1: FOR TABLE tab2,tab3"
    >> > So what's wrong for the subscriber to combine pub1 and pub2 in this case?
    >> >
    >>
    >> It is because one of the publications (pub2) indicates to include a
    >> particular table tab1 and the other one (pub1) to exclude the same
    >> table. And things become much more complex when the Except list
    >> contains partitions as shown in Shveta's example. So, I think it makes
    >> sense to keep things simple at least for the first version, we can
    >> consider to uplift this restriction if we see some use cases from the
    >> field.
    >>
    >>
    >
    > I’m with Peter here - I do not think it is wise to expose the exception listing outside the publication.  Publication combinations should be purely additive in much the same way grants are in the system.  Except lists are internal shorthand for describing the positive list of tables a publication makes available - all tables except.
    >
    
    The earlier case -
    pub1: FOR ALL TABLES EXCEPT (tab1)
    pub2: FOR TABLE tab1 WHERE (c = 99)
    
    seems a valid scenario, and we are currently evaluating its
    implementation feasibility under Approach 1.
    
    OTOH, subscribing to two different publications that are both defined
    as 'FOR ALL TABLES' but have different EXCEPT lists introduces
    unnecessary implementation complexity without a clear business use
    case. This becomes especially complex when the publications exclude
    different partitions of the same partitioned table. For example:
    
    pub1: FOR ALL TABLES EXCEPT (part1, part2) WITH
    (publish_via_partition_root=true)
    pub2: FOR ALL TABLES EXCEPT (part7) WITH
    (publish_via_partition_root=false)
    
    IMO, there is no clear need for a user to create multiple 'ALL TABLES'
    publications with different EXCEPT lists and then combine them at the
    subscriber level. Given this, to keep the patch simpler, we plan to
    emit an error for this scenario (multi-pub EXCEPTs case) for now. If a
    valid requirement emerges in the future, we can revisit and consider
    supporting it.
    
    thanks
    Shveta
    
    
    
    
  203. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-10T17:41:03Z

    On Tue, 10 Feb 2026 at 11:35, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Mon, Feb 9, 2026 at 11:52 AM David G. Johnston
    > <david.g.johnston@gmail.com> wrote:
    > >
    > > On Sunday, February 8, 2026, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >>
    > >> On Mon, Feb 9, 2026 at 6:41 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >>
    > >> > Hi Amit.
    > >> >
    > >> > I understand there can be some tricky scenarios where partitions are
    > >> > involved, but I was not sure why "pub1: FOR ALL Tables EXCEPT (tab1)
    > >> > and pub2: FOR TABLE tab1" is an example of contradictory behaviour.
    > >> >
    > >> > Consider if the publisher has 3 tables tab1,tab2,tab3:
    > >> > Here, "pub1: FOR ALL Tables EXCEPT (tab1)" is like a shorthand for
    > >> > saying "pub1: FOR TABLE tab2,tab3"
    > >> > So what's wrong for the subscriber to combine pub1 and pub2 in this case?
    > >> >
    > >>
    > >> It is because one of the publications (pub2) indicates to include a
    > >> particular table tab1 and the other one (pub1) to exclude the same
    > >> table. And things become much more complex when the Except list
    > >> contains partitions as shown in Shveta's example. So, I think it makes
    > >> sense to keep things simple at least for the first version, we can
    > >> consider to uplift this restriction if we see some use cases from the
    > >> field.
    > >>
    > >>
    > >
    > > I’m with Peter here - I do not think it is wise to expose the exception listing outside the publication.  Publication combinations should be purely additive in much the same way grants are in the system.  Except lists are internal shorthand for describing the positive list of tables a publication makes available - all tables except.
    > >
    >
    > The earlier case -
    > pub1: FOR ALL TABLES EXCEPT (tab1)
    > pub2: FOR TABLE tab1 WHERE (c = 99)
    >
    > seems a valid scenario, and we are currently evaluating its
    > implementation feasibility under Approach 1.
    >
    > OTOH, subscribing to two different publications that are both defined
    > as 'FOR ALL TABLES' but have different EXCEPT lists introduces
    > unnecessary implementation complexity without a clear business use
    > case. This becomes especially complex when the publications exclude
    > different partitions of the same partitioned table. For example:
    >
    > pub1: FOR ALL TABLES EXCEPT (part1, part2) WITH
    > (publish_via_partition_root=true)
    > pub2: FOR ALL TABLES EXCEPT (part7) WITH
    > (publish_via_partition_root=false)
    >
    > IMO, there is no clear need for a user to create multiple 'ALL TABLES'
    > publications with different EXCEPT lists and then combine them at the
    > subscriber level. Given this, to keep the patch simpler, we plan to
    > emit an error for this scenario (multi-pub EXCEPTs case) for now. If a
    > valid requirement emerges in the future, we can revisit and consider
    > supporting it.
    
    I agree with this. This has been handled.
    Additionally it also resolves conflicts between overlapping
    publications with differing publish_via_partition_root settings. When
    a partition is excluded from a "via root" publication but included in
    another table publication with "via root" as false, then it is
    published through its root ancestor by default to give priority to the
    root table.
    Ex:
    -- Publisher tables
    CREATE TABLE tab_root (id int, range_col int) PARTITION BY RANGE (range_col);
    CREATE TABLE tab_part_1 PARTITION OF tab_root FOR VALUES FROM (1) TO
    (1000) PARTITION BY RANGE (range_col);
    CREATE TABLE tab_part_2 PARTITION OF tab_root FOR VALUES FROM (1000)
    TO (2000) PARTITION BY RANGE (range_col);
    CREATE TABLE tab_part_1_p1 PARTITION OF tab_part_1 FOR VALUES FROM (1) TO (500);
    CREATE TABLE tab_part_1_p2 PARTITION OF tab_part_1 FOR VALUES FROM
    (500) TO (1000);
    CREATE TABLE tab_part_2_p1 PARTITION OF tab_part_2 FOR VALUES FROM
    (1000) TO (1500);
    CREATE TABLE tab_part_2_p2 PARTITION OF tab_part_2 FOR VALUES FROM
    (1500) TO (2000);
    
    -- Publication
    CREATE PUBLICATION pub1 for ALL TABLES EXCEPT table (tab_part_1_p1,
    tab_part_1_p2) WITH (PUBLISH_VIA_PARTITION_ROOT=true);
    CREATE PUBLICATION pub2 for table tab_part_1_p2 WITH
    (PUBLISH_VIA_PARTITION_ROOT=false);
    
    -- Subscriber tables
    CREATE TABLE tab_root (id int, range_col int);
    CREATE TABLE tab_part_1 (id int, range_col int);
    CREATE TABLE tab_part_2 (id int, range_col int);
    CREATE TABLE tab_part_1_p1 (id int, range_col int);
    CREATE TABLE tab_part_1_p2 (id int, range_col int);
    CREATE TABLE tab_part_2_p1 (id int, range_col int);
    CREATE TABLE tab_part_2_p2 (id int, range_col int);
    
    Subscription with both pub1 and pub2 publications.:
    create subscription sub1 connection 'dbname=postgres host=localhost
    port=5432' publication pub1,pub2;
    
    Consider the insert statements:
    insert into tab_part_1_p1 values(1,100);
    insert into tab_part_1_p2 values(2,600);
    insert into tab_part_2_p1 values(3,1100);
    insert into tab_part_2_p2 values(4,1600);
    
    The tab_part_2_p2 table's  row is replicated to the subscriber’s
    tab_root table, since root table publications take precedence.
    postgres=# select * from tab_root ;
     id | range_col
    ----+-----------
      2 |       600
      3 |      1100
      4 |      1600
    (3 rows)
    
    The attached v42 version patch has the changes for the same.
    
    Regards,
    Vignesh
    
  204. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-11T05:17:43Z

    On Tue, Feb 10, 2026 at 11:11 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Tue, 10 Feb 2026 at 11:35, shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Mon, Feb 9, 2026 at 11:52 AM David G. Johnston
    > > <david.g.johnston@gmail.com> wrote:
    > > >
    > > > On Sunday, February 8, 2026, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >>
    > > >> On Mon, Feb 9, 2026 at 6:41 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > > >>
    > > >> > Hi Amit.
    > > >> >
    > > >> > I understand there can be some tricky scenarios where partitions are
    > > >> > involved, but I was not sure why "pub1: FOR ALL Tables EXCEPT (tab1)
    > > >> > and pub2: FOR TABLE tab1" is an example of contradictory behaviour.
    > > >> >
    > > >> > Consider if the publisher has 3 tables tab1,tab2,tab3:
    > > >> > Here, "pub1: FOR ALL Tables EXCEPT (tab1)" is like a shorthand for
    > > >> > saying "pub1: FOR TABLE tab2,tab3"
    > > >> > So what's wrong for the subscriber to combine pub1 and pub2 in this case?
    > > >> >
    > > >>
    > > >> It is because one of the publications (pub2) indicates to include a
    > > >> particular table tab1 and the other one (pub1) to exclude the same
    > > >> table. And things become much more complex when the Except list
    > > >> contains partitions as shown in Shveta's example. So, I think it makes
    > > >> sense to keep things simple at least for the first version, we can
    > > >> consider to uplift this restriction if we see some use cases from the
    > > >> field.
    > > >>
    > > >>
    > > >
    > > > I’m with Peter here - I do not think it is wise to expose the exception listing outside the publication.  Publication combinations should be purely additive in much the same way grants are in the system.  Except lists are internal shorthand for describing the positive list of tables a publication makes available - all tables except.
    > > >
    > >
    > > The earlier case -
    > > pub1: FOR ALL TABLES EXCEPT (tab1)
    > > pub2: FOR TABLE tab1 WHERE (c = 99)
    > >
    > > seems a valid scenario, and we are currently evaluating its
    > > implementation feasibility under Approach 1.
    > >
    > > OTOH, subscribing to two different publications that are both defined
    > > as 'FOR ALL TABLES' but have different EXCEPT lists introduces
    > > unnecessary implementation complexity without a clear business use
    > > case. This becomes especially complex when the publications exclude
    > > different partitions of the same partitioned table. For example:
    > >
    > > pub1: FOR ALL TABLES EXCEPT (part1, part2) WITH
    > > (publish_via_partition_root=true)
    > > pub2: FOR ALL TABLES EXCEPT (part7) WITH
    > > (publish_via_partition_root=false)
    > >
    > > IMO, there is no clear need for a user to create multiple 'ALL TABLES'
    > > publications with different EXCEPT lists and then combine them at the
    > > subscriber level. Given this, to keep the patch simpler, we plan to
    > > emit an error for this scenario (multi-pub EXCEPTs case) for now. If a
    > > valid requirement emerges in the future, we can revisit and consider
    > > supporting it.
    >
    > I agree with this. This has been handled.
    > Additionally it also resolves conflicts between overlapping
    > publications with differing publish_via_partition_root settings. When
    > a partition is excluded from a "via root" publication but included in
    > another table publication with "via root" as false, then it is
    > published through its root ancestor by default to give priority to the
    > root table.
    
    Okay, this behaviour is the same as that of HEAD. I think the patch
    has not changed anything in that.
    
    So will it be right to conclude this:
    
    In the case where a subscriber subscribes to multiple publications:
    
    a) If a table or partition is included in one publication and excluded
    in another, we treat it as included.
    
    b) If any publisher has publish_via_partition_root = true, we consider
    this setting to apply to all publishers in that subscription. In other
    words, all changes are published through the topmost ancestor included
    in the publications. This behavior is the same as in HEAD; nothing has
    changed here.
    
    c) If the publications have different EXCEPT lists, this scenario is
    currently not supported (for the reasons stated in [1]). In such
    cases, we raise an error.
    
    [1]: https://www.postgresql.org/message-id/CAJpy0uC_0uvhmXyWegKGRozhpyoLGHwHrUAK%3DWk%2BbSmzqLMoSw%40mail.gmail.com
    
    
    > Ex:
    > -- Publisher tables
    > CREATE TABLE tab_root (id int, range_col int) PARTITION BY RANGE (range_col);
    > CREATE TABLE tab_part_1 PARTITION OF tab_root FOR VALUES FROM (1) TO
    > (1000) PARTITION BY RANGE (range_col);
    > CREATE TABLE tab_part_2 PARTITION OF tab_root FOR VALUES FROM (1000)
    > TO (2000) PARTITION BY RANGE (range_col);
    > CREATE TABLE tab_part_1_p1 PARTITION OF tab_part_1 FOR VALUES FROM (1) TO (500);
    > CREATE TABLE tab_part_1_p2 PARTITION OF tab_part_1 FOR VALUES FROM
    > (500) TO (1000);
    > CREATE TABLE tab_part_2_p1 PARTITION OF tab_part_2 FOR VALUES FROM
    > (1000) TO (1500);
    > CREATE TABLE tab_part_2_p2 PARTITION OF tab_part_2 FOR VALUES FROM
    > (1500) TO (2000);
    >
    > -- Publication
    > CREATE PUBLICATION pub1 for ALL TABLES EXCEPT table (tab_part_1_p1,
    > tab_part_1_p2) WITH (PUBLISH_VIA_PARTITION_ROOT=true);
    > CREATE PUBLICATION pub2 for table tab_part_1_p2 WITH
    > (PUBLISH_VIA_PARTITION_ROOT=false);
    >
    > -- Subscriber tables
    > CREATE TABLE tab_root (id int, range_col int);
    > CREATE TABLE tab_part_1 (id int, range_col int);
    > CREATE TABLE tab_part_2 (id int, range_col int);
    > CREATE TABLE tab_part_1_p1 (id int, range_col int);
    > CREATE TABLE tab_part_1_p2 (id int, range_col int);
    > CREATE TABLE tab_part_2_p1 (id int, range_col int);
    > CREATE TABLE tab_part_2_p2 (id int, range_col int);
    >
    > Subscription with both pub1 and pub2 publications.:
    > create subscription sub1 connection 'dbname=postgres host=localhost
    > port=5432' publication pub1,pub2;
    >
    > Consider the insert statements:
    > insert into tab_part_1_p1 values(1,100);
    > insert into tab_part_1_p2 values(2,600);
    > insert into tab_part_2_p1 values(3,1100);
    > insert into tab_part_2_p2 values(4,1600);
    >
    > The tab_part_2_p2 table's  row is replicated to the subscriber’s
    > tab_root table, since root table publications take precedence.
    
    I think your intent was to focus on results for tab_part_1_p2 as that
    was included in pub2 but excluded in pub1.
    
    > postgres=# select * from tab_root ;
    >  id | range_col
    > ----+-----------
    >   2 |       600
    >   3 |      1100
    >   4 |      1600
    > (3 rows)
    >
    > The attached v42 version patch has the changes for the same.
    >
    
    Thanks, will review.
    
    thanks
    Shveta
    
    
    
    
  205. Re: Skipping schema changes in publication

    David G. Johnston <david.g.johnston@gmail.com> — 2026-02-11T06:02:50Z

    On Tuesday, February 10, 2026, shveta malik <shveta.malik@gmail.com> wrote:
    
    > >
    > > The attached v42 version patch has the changes for the same.
    > >
    >
    > Thanks, will review.
    >
    
    Publication names are unique within the database.  In
    check_publications_except_list, the use of “select distinct” when pulling
    from pg_publication by name is unnecessary.  Furthermore, if we only have a
    single publication name it is unnecessary to perform the check and thus no
    need to even run the query.
    
    David J.
    
    
    +check_publications_except_list(WalReceiverConn *wrconn, List *publications)
    +{
    + WalRcvExecResult *res;
    + StringInfoData cmd;
    + TupleTableSlot *slot;
    + Oid tableRow[1] = {TEXTOID};
    + List   *except_publications = NIL;
    +
    + initStringInfo(&cmd);
    + appendStringInfoString(&cmd,
    +   "SELECT DISTINCT p.pubname\n"
    +   "  FROM pg_catalog.pg_publication p\n"
    +   " WHERE p.pubname IN (");
    +
    + GetPublicationsStr(publications, &cmd, true);
    +
    + appendStringInfoString(&cmd,
    +   ")\n"
    +   "   AND EXISTS (SELECT 1\n"
    +   "                 FROM pg_catalog.pg_publication_rel pr\n"
    +   "                WHERE pr.prpubid = p.oid\n"
    +   "                  AND pr.prexcept IS TRUE)");
    
  206. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-11T06:28:12Z

    On Wed, 11 Feb 2026 at 11:32, David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    >
    > On Tuesday, February 10, 2026, shveta malik <shveta.malik@gmail.com> wrote:
    >>
    >> >
    >> > The attached v42 version patch has the changes for the same.
    >> >
    >>
    >> Thanks, will review.
    >
    >
    > Publication names are unique within the database.  In check_publications_except_list, the use of “select distinct” when pulling from pg_publication by name is unnecessary.  Furthermore, if we only have a single publication name it is unnecessary to perform the check and thus no need to even run the query.
    
    Thanks, we will handle this in the next version.
    
    Regards,
    Vignesh
    
    
    
    
  207. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-02-12T06:58:28Z

    On Wed, 11 Feb 2026 at 11:32, David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    >
    > On Tuesday, February 10, 2026, shveta malik <shveta.malik@gmail.com> wrote:
    >>
    >> >
    >> > The attached v42 version patch has the changes for the same.
    >> >
    >>
    >> Thanks, will review.
    >
    >
    > Publication names are unique within the database.  In check_publications_except_list, the use of “select distinct” when pulling from pg_publication by name is unnecessary.  Furthermore, if we only have a single publication name it is unnecessary to perform the check and thus no need to even run the query.
    >
    > David J.
    >
    >
    > +check_publications_except_list(WalReceiverConn *wrconn, List *publications)
    > +{
    > + WalRcvExecResult *res;
    > + StringInfoData cmd;
    > + TupleTableSlot *slot;
    > + Oid tableRow[1] = {TEXTOID};
    > + List   *except_publications = NIL;
    > +
    > + initStringInfo(&cmd);
    > + appendStringInfoString(&cmd,
    > +   "SELECT DISTINCT p.pubname\n"
    > +   "  FROM pg_catalog.pg_publication p\n"
    > +   " WHERE p.pubname IN (");
    > +
    > + GetPublicationsStr(publications, &cmd, true);
    > +
    > + appendStringInfoString(&cmd,
    > +   ")\n"
    > +   "   AND EXISTS (SELECT 1\n"
    > +   "                 FROM pg_catalog.pg_publication_rel pr\n"
    > +   "                WHERE pr.prpubid = p.oid\n"
    > +   "                  AND pr.prexcept IS TRUE)");
    
    Hi David,
    
    We have addressed the comments in the latest v43 patch.
    Also we have refactored the code in
    'pg_get_publication_effective_tables' and added comments to make it
    simpler to understand.
    
    Thanks,
    Shlok Kyal
    
  208. Re: Skipping schema changes in publication

    David G. Johnston <david.g.johnston@gmail.com> — 2026-02-12T08:28:16Z

    On Wed, Feb 11, 2026 at 11:58 PM Shlok Kyal <shlok.kyal.oss@gmail.com>
    wrote:
    
    >
    > We have addressed the comments in the latest v43 patch.
    >
    
    Non-comprehensive review.
    
    Shouldn't the early exits look like:
    
    <begin function>
    if (list_length(publications) < 2)
        return;
    
    perform query here, capture except_publications
    
    if (list_length(except_publications) < 2)
        return;
    
    construct error message and ereport
    <end function>
    
    
    + if (publication_has_any_exception(puboid))
    + {
    + except_pub_names = lappend(except_pub_names,
    +   makeString(pubform->pubname.data));
    + has_any_exclusion = true;
    + except_pub_id = pubform->oid;
    + }
    
    Either rename has_any_exclusion to has_any_exception or, given how
    ambiguous that reads in code, maybe standardize on calling these exclusions
    throughout the code and either just accept we've chosen EXCEPT for the
    syntax for good reasons or consider whether to EXCLUDING (table1, table2)
    would be a better choice.
    
    + errmsg("could not get non excluded table list for table \"%s.%s\" from
    publisher: %s", -- triple negative; try to avoid "non excluded" as a term -
    those are "included".
    
    pg_get_publication_effective_tables - the second input argument is an array
    of publication names so the singular form here is a bit misleading.  Should
    this be subscription-oriented?  Otherwise, pg_get_tables_from_publications
    seems like a more accurate name.  "effective" or "only the included ones"
    seems reasonable to imply.
    
    Related, the check in there for "does at least one publication have
    an exclusion list" makes sense but feels awfully similar to the check for
    "at most one publication has an exclusion list"...too late for me to figure
    out what if anything to do make/do about it though.
    
    David J.
    
  209. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-13T11:41:08Z

    On Thu, Feb 12, 2026 at 1:58 PM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    >
    > On Wed, Feb 11, 2026 at 11:58 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >>
    >>
    >> We have addressed the comments in the latest v43 patch.
    >
    >
    > Non-comprehensive review.
    
    Thanks, we will review and address these.
    
    > Shouldn't the early exits look like:
    >
    > <begin function>
    > if (list_length(publications) < 2)
    >     return;
    >
    > perform query here, capture except_publications
    >
    > if (list_length(except_publications) < 2)
    >     return;
    >
    > construct error message and ereport
    > <end function>
    >
    >
    > + if (publication_has_any_exception(puboid))
    > + {
    > + except_pub_names = lappend(except_pub_names,
    > +   makeString(pubform->pubname.data));
    > + has_any_exclusion = true;
    > + except_pub_id = pubform->oid;
    > + }
    >
    > Either rename has_any_exclusion to has_any_exception or, given how ambiguous that reads in code, maybe standardize on calling these exclusions throughout the code and either just accept we've chosen EXCEPT for the syntax for good reasons or consider whether to EXCLUDING (table1, table2) would be a better choice.
    
    We have chosen 'EXCEPT' based on [1]. There were agreements on this,
    see [2]. We will modify function names and comments to be in sync with
    the 'EXCEPT' keyword.
    
    [1]: https://www.postgresql.org/message-id/CAJpy0uCM40%2Bcu8va2HBqE-pazPjhNdTDAQqK3nWbs4%2B5fs4Mxw%40mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAFiTN-vMpoX%3DTnxZq2_CmAH_k_DnJLp1Tu7-YT%2BuWvNGoPzfxg%40mail.gmail.com
    
    thanks
    Shveta
    
    
    
    
  210. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-16T03:20:36Z

    On Thu, 12 Feb 2026 at 13:58, David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    >
    > On Wed, Feb 11, 2026 at 11:58 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >>
    >>
    >> We have addressed the comments in the latest v43 patch.
    >
    >
    > Non-comprehensive review.
    >
    > Shouldn't the early exits look like:
    >
    > <begin function>
    > if (list_length(publications) < 2)
    >     return;
    >
    > perform query here, capture except_publications
    >
    > if (list_length(except_publications) < 2)
    >     return;
    >
    > construct error message and ereport
    > <end function>
    
    Modified
    
    >
    > + if (publication_has_any_exception(puboid))
    > + {
    > + except_pub_names = lappend(except_pub_names,
    > +   makeString(pubform->pubname.data));
    > + has_any_exclusion = true;
    > + except_pub_id = pubform->oid;
    > + }
    >
    > Either rename has_any_exclusion to has_any_exception or, given how ambiguous that reads in code, maybe standardize on calling these exclusions throughout the code and either just accept we've chosen EXCEPT for the syntax for good reasons or consider whether to EXCLUDING (table1, table2) would be a better choice.
    
    Changed it to has_any_exception
    
    > + errmsg("could not get non excluded table list for table \"%s.%s\" from publisher: %s", -- triple negative; try to avoid "non excluded" as a term - those are "included".
    >
    > pg_get_publication_effective_tables - the second input argument is an array of publication names so the singular form here is a bit misleading.  Should this be subscription-oriented?  Otherwise, pg_get_tables_from_publications seems like a more accurate name.  "effective" or "only the included ones" seems reasonable to imply.
    
    We have used similarly in the existing pg_get_publication_tables, I
    felt the existing might be ok to keep it consistent unless there is a
    better consistent name.
    
    > Related, the check in there for "does at least one publication have an exclusion list" makes sense but feels awfully similar to the check for "at most one publication has an exclusion list"...too late for me to figure out what if anything to do make/do about it though.
    
    Ideally this is not expected to happen. But under rare cases it can
    happen if the publication is dropped/recreated with except after
    creating a subscription. It is better to throw an error in these cases
    as we do not allow multiple publications except tables.
    
    Thanks for the comments, the attached v44 version patch has the
    changes for the same.
    
    Regards,
    Vignesh
    
  211. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-02-16T04:36:58Z

    On Mon, Feb 16, 2026 at 8:50 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Thu, 12 Feb 2026 at 13:58, David G. Johnston
    > <david.g.johnston@gmail.com> wrote:
    > >
    > > On Wed, Feb 11, 2026 at 11:58 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >>
    > >>
    > >> We have addressed the comments in the latest v43 patch.
    > >
    > >
    > > Non-comprehensive review.
    > >
    > > Shouldn't the early exits look like:
    > >
    > > <begin function>
    > > if (list_length(publications) < 2)
    > >     return;
    > >
    > > perform query here, capture except_publications
    > >
    > > if (list_length(except_publications) < 2)
    > >     return;
    > >
    > > construct error message and ereport
    > > <end function>
    >
    > Modified
    >
    > >
    > > + if (publication_has_any_exception(puboid))
    > > + {
    > > + except_pub_names = lappend(except_pub_names,
    > > +   makeString(pubform->pubname.data));
    > > + has_any_exclusion = true;
    > > + except_pub_id = pubform->oid;
    > > + }
    > >
    > > Either rename has_any_exclusion to has_any_exception or, given how ambiguous that reads in code, maybe standardize on calling these exclusions throughout the code and either just accept we've chosen EXCEPT for the syntax for good reasons or consider whether to EXCLUDING (table1, table2) would be a better choice.
    >
    > Changed it to has_any_exception
    >
    > > + errmsg("could not get non excluded table list for table \"%s.%s\" from publisher: %s", -- triple negative; try to avoid "non excluded" as a term - those are "included".
    > >
    > > pg_get_publication_effective_tables - the second input argument is an array of publication names so the singular form here is a bit misleading.  Should this be subscription-oriented?  Otherwise, pg_get_tables_from_publications seems like a more accurate name.  "effective" or "only the included ones" seems reasonable to imply.
    >
    > We have used similarly in the existing pg_get_publication_tables, I
    > felt the existing might be ok to keep it consistent unless there is a
    > better consistent name.
    >
    > > Related, the check in there for "does at least one publication have an exclusion list" makes sense but feels awfully similar to the check for "at most one publication has an exclusion list"...too late for me to figure out what if anything to do make/do about it though.
    >
    > Ideally this is not expected to happen. But under rare cases it can
    > happen if the publication is dropped/recreated with except after
    > creating a subscription. It is better to throw an error in these cases
    > as we do not allow multiple publications except tables.
    >
    > Thanks for the comments, the attached v44 version patch has the
    > changes for the same.
    
    I started looking into the patch, I have a few comments/questions
    
    1.
    +     <para>
    +      Similarly, if another publication includes the same table with a row
    +      filter, but it is also covered by a
    +      <literal>FOR ALL TABLES ... EXCEPT</literal> publication, the table is
    +      published without applying the row filter, since row filters cannot be
    +      specified for <literal>FOR ALL TABLES ... EXCEPT</literal> publications
    +      and such publications are therefore treated as having no row filter.
    +     </para>
    
    I did not understand what this paragraph is trying to explain?  what
    do you mean by it is covered by  FOR ALL TABLES ... EXCEPT, does it
    mean the relation is not excluded by EXCEPT?
    
    2.
    +  <para>
    +   Create a publication that publishes all sequences for synchronization, and
    +   all changes in all tables except <structname>users</structname> and
    +   <structname>departments</structname>:
    +<programlisting>
    +CREATE PUBLICATION all_sequences_tables_except FOR ALL SEQUENCES, ALL
    TABLES EXCEPT (users, departments);
    
    Do we support EXCEPT SEQUENCES as well or that's for the future?
    
    3.
    +/* Helper: Check syscache for prexcept flag */
    +bool
    +is_relid_published_as_except(Oid relid, Oid pubid)
    +{
    
    IMHO the function name doesn't look great for its purpose, we just
    want to check whether the relid is excluded, published_as_except seems
    confusing, no?
    
    4.
    +/*
    + * Throws an ERROR if multiple publications with exceptions are found.
    + *
    + * This check is mandatory because logical replication currently does not
    + * support subscribing to multiple publications if more than one of them
    + * uses an exclusion list.
    + */
    
    IMHO explaining the reason why this is not supported or giving
    reference to any other comments where it is already explained would be
    useful.
    
    5.
    + /*
    + * If the root ancestor is covered by a FOR ALL TABLES
    + * EXCEPT publication that uses
    + * publish_via_partition_root, we must publish changes via
    + * the root identity.
    + */
    + if (root_published_via_exceptpub)
    + {
    + pub_relid = root_ancestor;
    + ancestor_level = list_length(ancestors);
    + }
    
    I find this comment a bit confusing, what does "covered by a FOR ALL
    TABLES EXCEPT publication" means? It means relation is not in the
    EXCEPT list or in the EXCEPT list?
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  212. Re: Skipping schema changes in publication

    David G. Johnston <david.g.johnston@gmail.com> — 2026-02-16T06:24:27Z

    On Sunday, February 15, 2026, Dilip Kumar <dilipbalaut@gmail.com> wrote:
    
    > On Mon, Feb 16, 2026 at 8:50 AM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > I started looking into the patch, I have a few comments/questions
    >
    > 1.
    > +     <para>
    > +      Similarly, if another publication includes the same table with a row
    > +      filter, but it is also covered by a
    > +      <literal>FOR ALL TABLES ... EXCEPT</literal> publication, the table
    > is
    > +      published without applying the row filter, since row filters cannot
    > be
    > +      specified for <literal>FOR ALL TABLES ... EXCEPT</literal>
    > publications
    > +      and such publications are therefore treated as having no row filter.
    > +     </para>
    >
    > I did not understand what this paragraph is trying to explain?  what
    > do you mean by it is covered by  FOR ALL TABLES ... EXCEPT, does it
    > mean the relation is not excluded by EXCEPT?
    
    
    I concur the wording is tough to parse.  “Covered” is probably not a good
    word to use.  Stick with either included or excluded.  In this case, the
    point being communicated is if two publications include a table and one
    doesn’t have a where clause a combining subscription will consider the
    where clause to be a constant true when combining the where clauses using
    OR.
    
    This wording basically already exists in create subscription.  This
    paragraph and the preceding one, which discuss “if another publication
    exists”, seems out of place in the create publication documentation.  This
    page should be restricted to only those behaviors that manifest when
    dealing with a single publication, detailing what it produces.
    
    
    >
    > 4.
    > +/*
    > + * Throws an ERROR if multiple publications with exceptions are found.
    > + *
    > + * This check is mandatory because logical replication currently does not
    > + * support subscribing to multiple publications if more than one of them
    > + * uses an exclusion list.
    > + */
    >
    > IMHO explaining the reason why this is not supported or giving
    > reference to any other comments where it is already explained would be
    > useful.
    
    
    The word “exceptions” needs to be turned into “except clauses” or
    “exclusions”.
    
    
    >
    > 5.
    > + /*
    > + * If the root ancestor is covered by a FOR ALL TABLES
    > + * EXCEPT publication that uses
    > + * publish_via_partition_root, we must publish changes via
    > + * the root identity.
    > + */
    > + if (root_published_via_exceptpub)
    > + {
    > + pub_relid = root_ancestor;
    > + ancestor_level = list_length(ancestors);
    > + }
    >
    > I find this comment a bit confusing, what does "covered by a FOR ALL
    > TABLES EXCEPT publication" means?
    
    
    
    root_published_via_exceptpub … shouldn’t this just  be
    “root_published_via_alltables? It’s the all table clause that prohibits "
    where" and accepts "except".  IOW, it's sufficient to drop mention of
    except because the nature of the boolean means that 'false == except'.
    
    Something Like:
    When dealing with multiple publications in a subscription, a
    publish_via_partition_root attribute on an ALL TABLES publication causes
    the system to direct the data for all subscribed partitions to the
    partition root, even if other publications do not specify
    publish_via_partition_root (so long as the partition root in question
    hasn't been excluded from the ALL TABLES publication).
    
    Note: all tables + publish_via_partition_root = false comes to mind here
    but I'm unable to dive into the sidebar at the moment. It seems like
    root_published_via_alltables being false covers that case sufficiently
    though since in effect the root isn't published in that scenario.
    
    + * If the relation is a partition, check whether the current
    + * relation or any of the ancestors is included in the EXCEPT
    + * TABLE list. If so, do not publish the change. This is done
    + * irrespective of pubviaroot setting.
    
    I don't understand the motivation for pointing out pubviaroot here.  Of
    course a table that is not published doesn't care what 'pub'viaroot says.
    
    + * If the top-most ancestor is covered by a 'FOR ALL TABLES
    + * EXCEPT' publication, we don't need to track per-relation
    + * metadata here. These publications apply globally and do not
    + * support row filters or column lists that would require
    + * specific tracking for this partition.
    
    Suggestion:
    /* Partitions whose top-most ancestor is being published via an ALL TABLES
    publication need not be individually published via any other publication.
    Repeated occurrences of a partition take the least restricted definition,
    which the ALL TABLES publication always provides. I.e., all columns, all
    rows, no children left behind. */
    if (root_published_via_alltables)
        continue;
    
    The last two sentences are inferrable (from the definition/description of
    the rules of publication combining) and probably should be excluded.
    
    If the root is either "except'ed or there is no "publish via partition
    root" this is false and the subsequent lappend happens per usual.  That
    seems correct.
    
    
    + * explicitly specified in the EXCEPT claus of the given publication.
    s.b., "clause"
    
    David J.
    
  213. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-16T07:14:53Z

    On Mon, Feb 16, 2026 at 11:55 AM David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    >
    > On Sunday, February 15, 2026, Dilip Kumar <dilipbalaut@gmail.com> wrote:
    >>
    >> On Mon, Feb 16, 2026 at 8:50 AM vignesh C <vignesh21@gmail.com> wrote:
    >> >
    >> I started looking into the patch, I have a few comments/questions
    >>
    >> 1.
    >> +     <para>
    >> +      Similarly, if another publication includes the same table with a row
    >> +      filter, but it is also covered by a
    >> +      <literal>FOR ALL TABLES ... EXCEPT</literal> publication, the table is
    >> +      published without applying the row filter, since row filters cannot be
    >> +      specified for <literal>FOR ALL TABLES ... EXCEPT</literal> publications
    >> +      and such publications are therefore treated as having no row filter.
    >> +     </para>
    >>
    >> I did not understand what this paragraph is trying to explain?  what
    >> do you mean by it is covered by  FOR ALL TABLES ... EXCEPT, does it
    >> mean the relation is not excluded by EXCEPT?
    >
    >
    > I concur the wording is tough to parse.  “Covered” is probably not a good word to use.  Stick with either included or excluded.  In this case, the point being communicated is if two publications include a table and one doesn’t have a where clause a combining subscription will consider the where clause to be a constant true when combining the where clauses using OR.
    >
    > This wording basically already exists in create subscription.
    
    --yes, it exists already in doc ([1]) in the row-filter page.
    
    > This paragraph and the preceding one, which discuss “if another publication exists”, seems out of place in the create publication documentation.  This page should be restricted to only those behaviors that manifest when dealing with a single publication, detailing what it produces.
    
    If we want to mention it in brief, I still feel CREATE-PUBLICATION
    page is a good place. SImilar to how we mention in
    'publish_via_partition_root' section. See 'There can be a case where a
    subscription combines multiple publications.' in [2].
    
    Overall, I think this complete paragraph could be removed from
    CREATE-PUBLICATION. I find it over-detailed.
    
    +     <para>
    +      When a partition is listed in the <literal>EXCEPT</literal> clause of a
    +      <literal>FOR ALL TABLES</literal> publication with
    +      <literal>publish_via_partition_root</literal> set to
    +      <literal>true</literal>, the root partitioned table remains included in
    +      the publication. If that partition is explicitly included by another
    +      publication with <literal>publish_via_partition_root</literal> set to
    +      <literal>false</literal>, its changes are still replicated. In this
    +      case, the changes are published using the identity of the root
    +      partitioned table, since it is the top-most ancestor included in the
    +      <literal>FOR ALL TABLES</literal> publication, thereby ensuring
    +      consistent routing of changes within the partition hierarchy.
    +     </para>
    +     <para>
    +      Similarly, if another publication includes the same table with a row
    +      filter, but it is also covered by a
    +      <literal>FOR ALL TABLES ... EXCEPT</literal> publication, the table is
    +      published without applying the row filter, since row filters cannot be
    +      specified for <literal>FOR ALL TABLES ... EXCEPT</literal> publications
    +      and such publications are therefore treated as having no row filter.
    +     </para>
    
    Instead we can have something like below:
    
    There can be a case where a subscription includes multiple
    publications. In such a case, a table or partition that is included in
    one publication and listed in the EXCEPT TABLE clause of another is
    considered included for replication. Subscribing to multiple
    publications with different EXCEPT TABLE lists is currently not
    supported.
    
    ~~
    
    I feel it is unnecessary to mention row filters or column lists
    specifically in the context of the EXCEPT TABLE clause because IIUC:
    
    --They do not apply to FOR ALL TABLES publications, which is where
    EXCEPT TABLE is relevant.
    --In the case of multiple publications, the behavior of row filters
    and column lists remains unchanged from the HEAD behavior. The EXCEPT
    clause simply excludes the specified table; rest of the behaviour
    remains the same.
    
    Thoughts?
    
    ~~
    
    
    Few other comments on doc:
    
    1)
    catalogs.sgml:
    prexcept:
    +       True if the relation is excluded from the publication.
    
    We shall use the term 'table' instead of 'relation' to refer clearly
    that it is a table and not a sequence.
    
    2)
    logical-replication.sgml:
    +   <xref linkend="logical-replication-sequences"/>. When a publication is
    +   created with <literal>FOR ALL TABLES</literal>, tables can be explicitly
    +   excluded from publication using the
    +   <link linkend="sql-createpublication-params-for-except-table"><literal>EXCEPT
    TABLE</literal></link>
    +   clause.
    
    Suggestion:
    'tables can be explicitly' --> 'a table or set of tables can be explicitly'
    
    3)
    create_publication.sgml:
    
    +      For inherited tables, if <literal>ONLY</literal> is specified before the
    +      table name, only that table is excluded from the publication. If
    +      <literal>ONLY</literal> is not specified, the table and all its
    descendant
    +      tables (if any) are excluded. Optionally, <literal>*</literal> can be
    +      specified after the table name to explicitly indicate that descendant
    +      tables are excluded.
    +     </para>
    +     <para>
    +      For partitioned tables, when a table is specified in EXCEPT TABLE, then
    +      changes to that table and all of its partitions (that is, the entire
    +      partition subtree rooted at that table) are not replicated. This behavior
    +      is the same regardless of whether
    +      <literal>publish_via_partition_root</literal> is set to
    +      <literal>true</literal> or <literal>false</literal>. The
    +      <literal>publish_via_partition_root</literal> setting only determines
    +      which relation is used as the publishing relation for replicated changes,
    +      and does not affect exclusion semantics.
    
    a)
    Can we make the tone of both paragraphs the same? Suggestion:
    For partitioned tables, if a table is specified in EXCEPT TABLE, that
    table and all of its partitions (that is, the entire partition subtree
    rooted at that table) are excluded from the publication.
    
    b)
    This behavior
    +      is the same regardless of whether
    +      <literal>publish_via_partition_root</literal> is set to
    +      <literal>true</literal> or <literal>false</literal>. The
    +      <literal>publish_via_partition_root</literal> setting only determines
    +      which relation is used as the publishing relation for replicated changes,
    +      and does not affect exclusion semantics.
    
    I think there is no need to explicitly mention the
    'publish_via_partition_root' thing here, unless others think
    otherwise.
    
    ~~
    
    
    [1]: https://www.postgresql.org/docs/current/logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-COMBINING
    [2]: https://www.postgresql.org/docs/current/sql-createpublication.html#SQL-CREATEPUBLICATION-PARAMS-WITH-PUBLISH-VIA-PARTITION-ROOT
    
    thanks
    Shveta
    
    
    
    
  214. Re: Skipping schema changes in publication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-02-16T12:41:16Z

    On Mon, Feb 16, 2026 at 8:50 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Thanks for the comments, the attached v44 version patch has the
    > changes for the same.
    >
    I’ve started reviewing/testing the patch. Few comments:
    
    1) File - /doc/src/sgml/catalogs.sgml
           if there is no publication qualifying condition.</para></entry>
          </row>
    
    +     <row>
    +      <entry role="catalog_table_entry"><para role="column_definition">
    +      <structfield>prexcept</structfield> <type>bool</type>
    +      </para>
    
    Should the column order in the doc match the order in
    pg_publication_rel? In the table, the "prexcept" column appears before
    "prqual", whereas in the doc it is listed after.
    
    pg_publication.c: GetAllPublicationExcludedTables()
    2) There may be a scope for optimization in how relids are processed.
    Each time a new parentid is appended to the relids list, the first for
    loop:
    
    + while (is_parent_in_except)
    + {
    + List    *parentids = NIL;
    + List    *curr = NIL;
    +
    + foreach_oid(relid, relids)     <<< this
    + {
    + Oid parentid;
    +
    + if (!get_rel_relispartition(relid))
    + continue;
    +
    
    reprocesses all existing relids. Could we restrict it to check only
    the newly added relids and avoid redundant work? Does this make sense?
    
    I also feel the function would benefit from a few brief comments to
    clarify the logic.
    
    The attached file has the changes for the suggested optimization and
    few comment suggestions for the function, if they seem suitable.
    
    --
    Thanks,
    Nisha
    
  215. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-17T05:43:32Z

    On Mon, 16 Feb 2026 at 12:45, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Mon, Feb 16, 2026 at 11:55 AM David G. Johnston
    > <david.g.johnston@gmail.com> wrote:
    > >
    > > On Sunday, February 15, 2026, Dilip Kumar <dilipbalaut@gmail.com> wrote:
    > >>
    > >> On Mon, Feb 16, 2026 at 8:50 AM vignesh C <vignesh21@gmail.com> wrote:
    > >> >
    > >> I started looking into the patch, I have a few comments/questions
    > >>
    > >> 1.
    > >> +     <para>
    > >> +      Similarly, if another publication includes the same table with a row
    > >> +      filter, but it is also covered by a
    > >> +      <literal>FOR ALL TABLES ... EXCEPT</literal> publication, the table is
    > >> +      published without applying the row filter, since row filters cannot be
    > >> +      specified for <literal>FOR ALL TABLES ... EXCEPT</literal> publications
    > >> +      and such publications are therefore treated as having no row filter.
    > >> +     </para>
    > >>
    > >> I did not understand what this paragraph is trying to explain?  what
    > >> do you mean by it is covered by  FOR ALL TABLES ... EXCEPT, does it
    > >> mean the relation is not excluded by EXCEPT?
    > >
    > >
    > > I concur the wording is tough to parse.  “Covered” is probably not a good word to use.  Stick with either included or excluded.  In this case, the point being communicated is if two publications include a table and one doesn’t have a where clause a combining subscription will consider the where clause to be a constant true when combining the where clauses using OR.
    > >
    > > This wording basically already exists in create subscription.
    >
    > --yes, it exists already in doc ([1]) in the row-filter page.
    >
    > > This paragraph and the preceding one, which discuss “if another publication exists”, seems out of place in the create publication documentation.  This page should be restricted to only those behaviors that manifest when dealing with a single publication, detailing what it produces.
    >
    > If we want to mention it in brief, I still feel CREATE-PUBLICATION
    > page is a good place. SImilar to how we mention in
    > 'publish_via_partition_root' section. See 'There can be a case where a
    > subscription combines multiple publications.' in [2].
    >
    > Overall, I think this complete paragraph could be removed from
    > CREATE-PUBLICATION. I find it over-detailed.
    >
    > +     <para>
    > +      When a partition is listed in the <literal>EXCEPT</literal> clause of a
    > +      <literal>FOR ALL TABLES</literal> publication with
    > +      <literal>publish_via_partition_root</literal> set to
    > +      <literal>true</literal>, the root partitioned table remains included in
    > +      the publication. If that partition is explicitly included by another
    > +      publication with <literal>publish_via_partition_root</literal> set to
    > +      <literal>false</literal>, its changes are still replicated. In this
    > +      case, the changes are published using the identity of the root
    > +      partitioned table, since it is the top-most ancestor included in the
    > +      <literal>FOR ALL TABLES</literal> publication, thereby ensuring
    > +      consistent routing of changes within the partition hierarchy.
    > +     </para>
    > +     <para>
    > +      Similarly, if another publication includes the same table with a row
    > +      filter, but it is also covered by a
    > +      <literal>FOR ALL TABLES ... EXCEPT</literal> publication, the table is
    > +      published without applying the row filter, since row filters cannot be
    > +      specified for <literal>FOR ALL TABLES ... EXCEPT</literal> publications
    > +      and such publications are therefore treated as having no row filter.
    > +     </para>
    >
    > Instead we can have something like below:
    >
    > There can be a case where a subscription includes multiple
    > publications. In such a case, a table or partition that is included in
    > one publication and listed in the EXCEPT TABLE clause of another is
    > considered included for replication. Subscribing to multiple
    > publications with different EXCEPT TABLE lists is currently not
    > supported.
    
    Updated
    
    > ~~
    >
    > I feel it is unnecessary to mention row filters or column lists
    > specifically in the context of the EXCEPT TABLE clause because IIUC:
    >
    > --They do not apply to FOR ALL TABLES publications, which is where
    > EXCEPT TABLE is relevant.
    > --In the case of multiple publications, the behavior of row filters
    > and column lists remains unchanged from the HEAD behavior. The EXCEPT
    > clause simply excludes the specified table; rest of the behaviour
    > remains the same.
    >
    > Thoughts?
    
    Agree, removed these
    
    > ~~
    >
    >
    > Few other comments on doc:
    >
    > 1)
    > catalogs.sgml:
    > prexcept:
    > +       True if the relation is excluded from the publication.
    >
    > We shall use the term 'table' instead of 'relation' to refer clearly
    > that it is a table and not a sequence.
    
    Modified
    
    > 2)
    > logical-replication.sgml:
    > +   <xref linkend="logical-replication-sequences"/>. When a publication is
    > +   created with <literal>FOR ALL TABLES</literal>, tables can be explicitly
    > +   excluded from publication using the
    > +   <link linkend="sql-createpublication-params-for-except-table"><literal>EXCEPT
    > TABLE</literal></link>
    > +   clause.
    >
    > Suggestion:
    > 'tables can be explicitly' --> 'a table or set of tables can be explicitly'
    
    Modified
    
    > 3)
    > create_publication.sgml:
    >
    > +      For inherited tables, if <literal>ONLY</literal> is specified before the
    > +      table name, only that table is excluded from the publication. If
    > +      <literal>ONLY</literal> is not specified, the table and all its
    > descendant
    > +      tables (if any) are excluded. Optionally, <literal>*</literal> can be
    > +      specified after the table name to explicitly indicate that descendant
    > +      tables are excluded.
    > +     </para>
    > +     <para>
    > +      For partitioned tables, when a table is specified in EXCEPT TABLE, then
    > +      changes to that table and all of its partitions (that is, the entire
    > +      partition subtree rooted at that table) are not replicated. This behavior
    > +      is the same regardless of whether
    > +      <literal>publish_via_partition_root</literal> is set to
    > +      <literal>true</literal> or <literal>false</literal>. The
    > +      <literal>publish_via_partition_root</literal> setting only determines
    > +      which relation is used as the publishing relation for replicated changes,
    > +      and does not affect exclusion semantics.
    >
    > a)
    > Can we make the tone of both paragraphs the same? Suggestion:
    > For partitioned tables, if a table is specified in EXCEPT TABLE, that
    > table and all of its partitions (that is, the entire partition subtree
    > rooted at that table) are excluded from the publication.
    >
    > b)
    > This behavior
    > +      is the same regardless of whether
    > +      <literal>publish_via_partition_root</literal> is set to
    > +      <literal>true</literal> or <literal>false</literal>. The
    > +      <literal>publish_via_partition_root</literal> setting only determines
    > +      which relation is used as the publishing relation for replicated changes,
    > +      and does not affect exclusion semantics.
    >
    > I think there is no need to explicitly mention the
    > 'publish_via_partition_root' thing here, unless others think
    > otherwise.
    
    Removed
    
    Thanks for the comments. The attached v45 version patch has the
    changes for the same.
    
    Regards,
    Vignesh
    
  216. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-17T06:23:00Z

    On Mon, 16 Feb 2026 at 11:55, David G. Johnston
    <david.g.johnston@gmail.com> wrote:
    >
    > On Sunday, February 15, 2026, Dilip Kumar <dilipbalaut@gmail.com> wrote:
    >>
    >> On Mon, Feb 16, 2026 at 8:50 AM vignesh C <vignesh21@gmail.com> wrote:
    >> >
    >> I started looking into the patch, I have a few comments/questions
    >>
    >> 1.
    >> +     <para>
    >> +      Similarly, if another publication includes the same table with a row
    >> +      filter, but it is also covered by a
    >> +      <literal>FOR ALL TABLES ... EXCEPT</literal> publication, the table is
    >> +      published without applying the row filter, since row filters cannot be
    >> +      specified for <literal>FOR ALL TABLES ... EXCEPT</literal> publications
    >> +      and such publications are therefore treated as having no row filter.
    >> +     </para>
    >>
    >> I did not understand what this paragraph is trying to explain?  what
    >> do you mean by it is covered by  FOR ALL TABLES ... EXCEPT, does it
    >> mean the relation is not excluded by EXCEPT?
    >
    >
    > I concur the wording is tough to parse.  “Covered” is probably not a good word to use.  Stick with either included or excluded.  In this case, the point being communicated is if two publications include a table and one doesn’t have a where clause a combining subscription will consider the where clause to be a constant true when combining the where clauses using OR.
    >
    > This wording basically already exists in create subscription.  This paragraph and the preceding one, which discuss “if another publication exists”, seems out of place in the create publication documentation.  This page should be restricted to only those behaviors that manifest when dealing with a single publication, detailing what it produces.
    
    I have removed these as they follow the existing row filter rules
    already mentioned.
    
    >>
    >>
    >> 4.
    >> +/*
    >> + * Throws an ERROR if multiple publications with exceptions are found.
    >> + *
    >> + * This check is mandatory because logical replication currently does not
    >> + * support subscribing to multiple publications if more than one of them
    >> + * uses an exclusion list.
    >> + */
    >>
    >> IMHO explaining the reason why this is not supported or giving
    >> reference to any other comments where it is already explained would be
    >> useful.
    >
    >
    > The word “exceptions” needs to be turned into “except clauses” or “exclusions”.
    
    Modified
    
    >>
    >>
    >> 5.
    >> + /*
    >> + * If the root ancestor is covered by a FOR ALL TABLES
    >> + * EXCEPT publication that uses
    >> + * publish_via_partition_root, we must publish changes via
    >> + * the root identity.
    >> + */
    >> + if (root_published_via_exceptpub)
    >> + {
    >> + pub_relid = root_ancestor;
    >> + ancestor_level = list_length(ancestors);
    >> + }
    >>
    >> I find this comment a bit confusing, what does "covered by a FOR ALL
    >> TABLES EXCEPT publication" means?
    >
    >
    >
    > root_published_via_exceptpub … shouldn’t this just  be “root_published_via_alltables? It’s the all table clause that prohibits "where" and accepts "except".  IOW, it's sufficient to drop mention of except because the nature of the boolean means that 'false == except'.
    >
    > Something Like:
    > When dealing with multiple publications in a subscription, a publish_via_partition_root attribute on an ALL TABLES publication causes the system to direct the data for all subscribed partitions to the partition root, even if other publications do not specify publish_via_partition_root (so long as the partition root in question hasn't been excluded from the ALL TABLES publication).
    
    Updated with few changes. Let me know if it did not convey the same
    meaning, I will use your exact wording.
    
    > Note: all tables + publish_via_partition_root = false comes to mind here but I'm unable to dive into the sidebar at the moment. It seems like root_published_via_alltables being false covers that case sufficiently though since in effect the root isn't published in that scenario.
    >
    > + * If the relation is a partition, check whether the current
    > + * relation or any of the ancestors is included in the EXCEPT
    > + * TABLE list. If so, do not publish the change. This is done
    > + * irrespective of pubviaroot setting.
    >
    > I don't understand the motivation for pointing out pubviaroot here.  Of course a table that is not published doesn't care what 'pub'viaroot says.
    
    Removed this to avoid confusion.
    
    > + * If the top-most ancestor is covered by a 'FOR ALL TABLES
    > + * EXCEPT' publication, we don't need to track per-relation
    > + * metadata here. These publications apply globally and do not
    > + * support row filters or column lists that would require
    > + * specific tracking for this partition.
    >
    > Suggestion:
    > /* Partitions whose top-most ancestor is being published via an ALL TABLES publication need not be individually published via any other publication.  Repeated occurrences of a partition take the least restricted definition, which the ALL TABLES publication always provides. I.e., all columns, all rows, no children left behind. */
    > if (root_published_via_alltables)
    >     continue;
    
    Modified, I have left the no children left behind as the child
    specified in the except clause will be skipped if it is not included
    via another publication.
    
    > The last two sentences are inferrable (from the definition/description of the rules of publication combining) and probably should be excluded.
    >
    > If the root is either "except'ed or there is no "publish via partition root" this is false and the subsequent lappend happens per usual.  That seems correct.
    
    I have removed the newly added comment i.e.:
    * If the relation is part of EXCEPT TABLE list of a publication,
    * the 'publish' variable is already set to false.
    I have retained the other one as it was an existing comment.
    
    > + * explicitly specified in the EXCEPT claus of the given publication.
    > s.b., "clause"
    
    Fixed this.
    
    Thanks for the comments, these are addressed in the v45 version posted at [1].
    [1] - https://www.postgresql.org/message-id/CALDaNm11LKbC2epEyHOvD6H_ONqLqhDQk7sXWwcneyhrTbFaTw%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  217. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-17T06:31:53Z

    On Tue, Feb 17, 2026 at 11:13 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Thanks for the comments. The attached v45 version patch has the
    > changes for the same.
    >
    
    Thanks Vignesh, please find a few comments from v44 itself. Please
    ignore if already addressed. Will switch to v45 now.
    
    
    1)
    publication_has_any_exception:
    + ScanKeyInit(&skey,
    + Anum_pg_publication_rel_prpubid,
    + BTEqualStrategyNumber, F_OIDEQ,
    + ObjectIdGetDatum(pubid));
    +
    + scan = systable_beginscan(pubRel,
    +   PublicationRelPrrelidPrpubidIndexId,
    +   true, NULL, 1, &skey);
    
    PublicationRelPrrelidPrpubidIndexId is index on relid and pubid, I
    don't think it will be used in this case where we have only pubid key.
    Shall we use PublicationRelPrpubidIndexId instead?
    
    2)
    +/*
    + * is_relid_published_explicitly
    + *
    + * Checks if the given relation OID is explicitly part of the publication.
    + * This corresponds to the 'FOR TABLE' syntax.
    + */
    +static bool
    +is_relid_published_explicitly(Oid relid, Oid pubid)
    +{
    + /*
    + * Search the syscache for pg_publication_rel using the (relid, pubid)
    + * index.
    + */
    + return SearchSysCacheExists2(PUBLICATIONRELMAP,
    + ObjectIdGetDatum(relid),
    + ObjectIdGetDatum(pubid));
    +}
    
    How are we ensuring that it is not fetching the one with except-flag
    as true? Shall we assert when pub is all-tables to rule out that
    case/mistake? Or if we code-flow is expected to come to this function
    even for 'all-tables' pub (it appears to me t by looking at the
    caller), we shall return in such a case instead of Assert.
    
    
    3)
    Shall we rename these:
    'is_relid_published_as_except' as 'is_relid_excepted'.
    'is_relid_published_as_except_with_ancestors' as 'is_relid_or_ancestor_excepted'
    
    These will be to match tones of:
    is_schema_published
    is_relid_published_explicitly
    
    I feel even for 'is_relid_published_explicitly', we can simply say
    'is_relid_published'. Comments (and assert/checks) can explain that it
    is for FOR-TABLE pub.
    
    4)
    + List    *except_leaves = NIL;
    + List    *allowed_leaves = NIL;
    
    Similar to allowed_leaves, shall we have excepted_leaves instead of
    except_leaves?
    
    5)
    pg_get_publication_effective_tables():
    
    +
    + /*
    + * Check whether the table itself or its schema is
    + * included in this publication.
    + */
    + if (is_relid_published_explicitly(curr_relid, pubid) ||
    + is_schema_published(get_rel_namespace(curr_relid), pubid))
    + {
    + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    + }
    + else
    + {
    + List    *ancestors = get_partition_ancestors(curr_relid);
    +
    + /*
    + * Check whether any ancestor, or its schema, is
    + * included in this publication.
    + */
    + foreach_oid(anc_oid, ancestors)
    + {
    + if (is_relid_published_explicitly(anc_oid, pubid) ||
    + is_schema_published(get_rel_namespace(anc_oid), pubid))
    + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    + }
    + }
    
    Do you think we can convert this code part to
    is_relid_or_ancestor_published(), similar to
    is_relid_published_as_except_with_ancestors()?
    
    is_relid_or_ancestor_published() can check both pg_publication_rel and
    pg_publication_namespace. I do not see individual use-case scenarios
    for is_relid_published_explicitly() and is_schema_published() and thus
    it is better to club these in one function. So at the end we will be
    left with 2 such functions:
    
    is_relid_or_ancestor_published
    is_relid_or_ancestor_excepted/excluded (choose the name based on
    others comments too)
    
    thanks
    Shveta
    
    
    
    
  218. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-17T06:35:47Z

    On Mon, 16 Feb 2026 at 10:07, Dilip Kumar <dilipbalaut@gmail.com> wrote:
    >
    > I started looking into the patch, I have a few comments/questions
    >
    > 1.
    > +     <para>
    > +      Similarly, if another publication includes the same table with a row
    > +      filter, but it is also covered by a
    > +      <literal>FOR ALL TABLES ... EXCEPT</literal> publication, the table is
    > +      published without applying the row filter, since row filters cannot be
    > +      specified for <literal>FOR ALL TABLES ... EXCEPT</literal> publications
    > +      and such publications are therefore treated as having no row filter.
    > +     </para>
    >
    > I did not understand what this paragraph is trying to explain?  what
    > do you mean by it is covered by  FOR ALL TABLES ... EXCEPT, does it
    > mean the relation is not excluded by EXCEPT?
    
    I have removed these as they follow the existing row filter rules
    already mentioned.
    
    > 2.
    > +  <para>
    > +   Create a publication that publishes all sequences for synchronization, and
    > +   all changes in all tables except <structname>users</structname> and
    > +   <structname>departments</structname>:
    > +<programlisting>
    > +CREATE PUBLICATION all_sequences_tables_except FOR ALL SEQUENCES, ALL
    > TABLES EXCEPT (users, departments);
    >
    > Do we support EXCEPT SEQUENCES as well or that's for the future?
    
    We don't support sequences, it will be handled later.
    
    > 3.
    > +/* Helper: Check syscache for prexcept flag */
    > +bool
    > +is_relid_published_as_except(Oid relid, Oid pubid)
    > +{
    >
    > IMHO the function name doesn't look great for its purpose, we just
    > want to check whether the relid is excluded, published_as_except seems
    > confusing, no?
    
    Modified it to is_relation_excluded
    
    > 4.
    > +/*
    > + * Throws an ERROR if multiple publications with exceptions are found.
    > + *
    > + * This check is mandatory because logical replication currently does not
    > + * support subscribing to multiple publications if more than one of them
    > + * uses an exclusion list.
    > + */
    >
    > IMHO explaining the reason why this is not supported or giving
    > reference to any other comments where it is already explained would be
    > useful.
    
    Updated comments
    
    > 5.
    > + /*
    > + * If the root ancestor is covered by a FOR ALL TABLES
    > + * EXCEPT publication that uses
    > + * publish_via_partition_root, we must publish changes via
    > + * the root identity.
    > + */
    > + if (root_published_via_exceptpub)
    > + {
    > + pub_relid = root_ancestor;
    > + ancestor_level = list_length(ancestors);
    > + }
    >
    > I find this comment a bit confusing, what does "covered by a FOR ALL
    > TABLES EXCEPT publication" means? It means relation is not in the
    > EXCEPT list or in the EXCEPT list?
    
    Updated comments to:
    /*
    * If the root ancestor is effectively published by an
    * ALL TABLES publication with publish_via_partition_root,
    * then changes for its partitions must be published
    * using the root identity.
    *
    * This applies even if other publications do not specify
    * publish_via_partition_root, provided the root is not
    * excluded from that ALL TABLES publication.
    */
    
    Thanks for the comments, these are addressed in the v45 version posted at [1].
    Additionally the v45 version also addresses Nisha's comments from [2].
    
    [1] - https://www.postgresql.org/message-id/CALDaNm11LKbC2epEyHOvD6H_ONqLqhDQk7sXWwcneyhrTbFaTw%40mail.gmail.com
    [2] - https://www.postgresql.org/message-id/CABdArM4KucFdHYPFCcpvpN7_OVnV5rpnDY7Daz7Cjn6ZuN-dkg%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  219. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-17T11:38:32Z

    On Tue, Feb 17, 2026 at 12:01 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Feb 17, 2026 at 11:13 AM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Thanks for the comments. The attached v45 version patch has the
    > > changes for the same.
    > >
    >
    > Thanks Vignesh, please find a few comments from v44 itself. Please
    > ignore if already addressed. Will switch to v45 now.
    >
    >
    > 1)
    > publication_has_any_exception:
    > + ScanKeyInit(&skey,
    > + Anum_pg_publication_rel_prpubid,
    > + BTEqualStrategyNumber, F_OIDEQ,
    > + ObjectIdGetDatum(pubid));
    > +
    > + scan = systable_beginscan(pubRel,
    > +   PublicationRelPrrelidPrpubidIndexId,
    > +   true, NULL, 1, &skey);
    >
    > PublicationRelPrrelidPrpubidIndexId is index on relid and pubid, I
    > don't think it will be used in this case where we have only pubid key.
    > Shall we use PublicationRelPrpubidIndexId instead?
    >
    > 2)
    > +/*
    > + * is_relid_published_explicitly
    > + *
    > + * Checks if the given relation OID is explicitly part of the publication.
    > + * This corresponds to the 'FOR TABLE' syntax.
    > + */
    > +static bool
    > +is_relid_published_explicitly(Oid relid, Oid pubid)
    > +{
    > + /*
    > + * Search the syscache for pg_publication_rel using the (relid, pubid)
    > + * index.
    > + */
    > + return SearchSysCacheExists2(PUBLICATIONRELMAP,
    > + ObjectIdGetDatum(relid),
    > + ObjectIdGetDatum(pubid));
    > +}
    >
    > How are we ensuring that it is not fetching the one with except-flag
    > as true? Shall we assert when pub is all-tables to rule out that
    > case/mistake? Or if we code-flow is expected to come to this function
    > even for 'all-tables' pub (it appears to me t by looking at the
    > caller), we shall return in such a case instead of Assert.
    >
    >
    > 3)
    > Shall we rename these:
    > 'is_relid_published_as_except' as 'is_relid_excepted'.
    > 'is_relid_published_as_except_with_ancestors' as 'is_relid_or_ancestor_excepted'
    >
    > These will be to match tones of:
    > is_schema_published
    > is_relid_published_explicitly
    >
    > I feel even for 'is_relid_published_explicitly', we can simply say
    > 'is_relid_published'. Comments (and assert/checks) can explain that it
    > is for FOR-TABLE pub.
    >
    > 4)
    > + List    *except_leaves = NIL;
    > + List    *allowed_leaves = NIL;
    >
    > Similar to allowed_leaves, shall we have excepted_leaves instead of
    > except_leaves?
    >
    > 5)
    > pg_get_publication_effective_tables():
    >
    > +
    > + /*
    > + * Check whether the table itself or its schema is
    > + * included in this publication.
    > + */
    > + if (is_relid_published_explicitly(curr_relid, pubid) ||
    > + is_schema_published(get_rel_namespace(curr_relid), pubid))
    > + {
    > + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    > + }
    > + else
    > + {
    > + List    *ancestors = get_partition_ancestors(curr_relid);
    > +
    > + /*
    > + * Check whether any ancestor, or its schema, is
    > + * included in this publication.
    > + */
    > + foreach_oid(anc_oid, ancestors)
    > + {
    > + if (is_relid_published_explicitly(anc_oid, pubid) ||
    > + is_schema_published(get_rel_namespace(anc_oid), pubid))
    > + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    > + }
    > + }
    >
    > Do you think we can convert this code part to
    > is_relid_or_ancestor_published(), similar to
    > is_relid_published_as_except_with_ancestors()?
    >
    > is_relid_or_ancestor_published() can check both pg_publication_rel and
    > pg_publication_namespace. I do not see individual use-case scenarios
    > for is_relid_published_explicitly() and is_schema_published() and thus
    > it is better to club these in one function. So at the end we will be
    > left with 2 such functions:
    >
    > is_relid_or_ancestor_published
    > is_relid_or_ancestor_excepted/excluded (choose the name based on
    > others comments too)
    >
    
    A few more:
    
    6)
    postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab1);
    ERROR:  cannot add relation "tab1" to publication
    DETAIL:  This operation is not supported for temporary tables.
    
    postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab2);
    ERROR:  cannot add relation "tab2" to publication
    DETAIL:  This operation is not supported for unlogged tables.
    
    Shall we change the error message here as we are not trying to add
    relation here.
    
    7)
    Currently the error i s:
    
    postgres=# create subscription sub1 connection '...' publication pub1,pub2,pub3;
    ERROR:  publications "pub1", "pub2", "pub3" are defined with EXCEPT TABLE
    HINT:  Subscription cannot be created using multiple publications that
    specify EXCEPT TABLE.
    
    Hint looks more like DETAIL. Shall we have this:
    
    ERROR:  cannot create subscription with multiple publications that
    specify EXCEPT TABLE
    DETAIL:  The publications "pub1", "pub2", and "pub3" define EXCEPT
    TABLE clauses.
    
    8)
    pg_get_publication_effective_tables():
    
    + /* Return root immediately if no filtering logic is needed */
    + if (has_clean_all_tables_pub || !has_any_except)
    
    I think we do not need any additional boolean 'has_any_except' for
    this purpose. We can simply rely on 'except_pub_names' being Nil.
    
    9)
    + /* Return root immediately if no filtering logic is needed */
    + if (has_clean_all_tables_pub || !has_any_except)
    + {
    + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    + final_output = list_make1_oid(root_relid);
    + MemoryContextSwitchTo(oldcontext);
    + }
    + else
    
    Can we please write a comment atop 'else' block to say what it is
    going to attempt? This is because there is a big chunk of code in
    'else' block and it is difficult to construct our logic by reading
    each part of it.
    
    10)
    CreatePublication() has changed the way we process publications.
    Earlier, we had explicit checks for publication types such as
    'for_all_tables' and 'for_all_sequences' etc, which made the code
    easier to follow. That differentiation based on publication type is no
    longer there. As an example,
    we now invoke functions like TransformPubWhereClauses() and
    CheckPubRelationColumnList() even for FOR ALL TABLES ... EXCEPT
    publications, which are not needed. We could consider restoring the
    previous structure, where logic was clearly separated based on
    publication type.
    
    thanks
    Shveta
    
    
    
    
  220. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-18T06:11:38Z

    On Tue, Feb 17, 2026 at 5:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Feb 17, 2026 at 12:01 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Tue, Feb 17, 2026 at 11:13 AM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > Thanks for the comments. The attached v45 version patch has the
    > > > changes for the same.
    > > >
    > >
    > > Thanks Vignesh, please find a few comments from v44 itself. Please
    > > ignore if already addressed. Will switch to v45 now.
    > >
    > >
    > > 1)
    > > publication_has_any_exception:
    > > + ScanKeyInit(&skey,
    > > + Anum_pg_publication_rel_prpubid,
    > > + BTEqualStrategyNumber, F_OIDEQ,
    > > + ObjectIdGetDatum(pubid));
    > > +
    > > + scan = systable_beginscan(pubRel,
    > > +   PublicationRelPrrelidPrpubidIndexId,
    > > +   true, NULL, 1, &skey);
    > >
    > > PublicationRelPrrelidPrpubidIndexId is index on relid and pubid, I
    > > don't think it will be used in this case where we have only pubid key.
    > > Shall we use PublicationRelPrpubidIndexId instead?
    > >
    > > 2)
    > > +/*
    > > + * is_relid_published_explicitly
    > > + *
    > > + * Checks if the given relation OID is explicitly part of the publication.
    > > + * This corresponds to the 'FOR TABLE' syntax.
    > > + */
    > > +static bool
    > > +is_relid_published_explicitly(Oid relid, Oid pubid)
    > > +{
    > > + /*
    > > + * Search the syscache for pg_publication_rel using the (relid, pubid)
    > > + * index.
    > > + */
    > > + return SearchSysCacheExists2(PUBLICATIONRELMAP,
    > > + ObjectIdGetDatum(relid),
    > > + ObjectIdGetDatum(pubid));
    > > +}
    > >
    > > How are we ensuring that it is not fetching the one with except-flag
    > > as true? Shall we assert when pub is all-tables to rule out that
    > > case/mistake? Or if we code-flow is expected to come to this function
    > > even for 'all-tables' pub (it appears to me t by looking at the
    > > caller), we shall return in such a case instead of Assert.
    > >
    > >
    > > 3)
    > > Shall we rename these:
    > > 'is_relid_published_as_except' as 'is_relid_excepted'.
    > > 'is_relid_published_as_except_with_ancestors' as 'is_relid_or_ancestor_excepted'
    > >
    > > These will be to match tones of:
    > > is_schema_published
    > > is_relid_published_explicitly
    > >
    > > I feel even for 'is_relid_published_explicitly', we can simply say
    > > 'is_relid_published'. Comments (and assert/checks) can explain that it
    > > is for FOR-TABLE pub.
    > >
    > > 4)
    > > + List    *except_leaves = NIL;
    > > + List    *allowed_leaves = NIL;
    > >
    > > Similar to allowed_leaves, shall we have excepted_leaves instead of
    > > except_leaves?
    > >
    > > 5)
    > > pg_get_publication_effective_tables():
    > >
    > > +
    > > + /*
    > > + * Check whether the table itself or its schema is
    > > + * included in this publication.
    > > + */
    > > + if (is_relid_published_explicitly(curr_relid, pubid) ||
    > > + is_schema_published(get_rel_namespace(curr_relid), pubid))
    > > + {
    > > + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    > > + }
    > > + else
    > > + {
    > > + List    *ancestors = get_partition_ancestors(curr_relid);
    > > +
    > > + /*
    > > + * Check whether any ancestor, or its schema, is
    > > + * included in this publication.
    > > + */
    > > + foreach_oid(anc_oid, ancestors)
    > > + {
    > > + if (is_relid_published_explicitly(anc_oid, pubid) ||
    > > + is_schema_published(get_rel_namespace(anc_oid), pubid))
    > > + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    > > + }
    > > + }
    > >
    > > Do you think we can convert this code part to
    > > is_relid_or_ancestor_published(), similar to
    > > is_relid_published_as_except_with_ancestors()?
    > >
    > > is_relid_or_ancestor_published() can check both pg_publication_rel and
    > > pg_publication_namespace. I do not see individual use-case scenarios
    > > for is_relid_published_explicitly() and is_schema_published() and thus
    > > it is better to club these in one function. So at the end we will be
    > > left with 2 such functions:
    > >
    > > is_relid_or_ancestor_published
    > > is_relid_or_ancestor_excepted/excluded (choose the name based on
    > > others comments too)
    > >
    >
    > A few more:
    >
    > 6)
    > postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab1);
    > ERROR:  cannot add relation "tab1" to publication
    > DETAIL:  This operation is not supported for temporary tables.
    >
    > postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab2);
    > ERROR:  cannot add relation "tab2" to publication
    > DETAIL:  This operation is not supported for unlogged tables.
    >
    > Shall we change the error message here as we are not trying to add
    > relation here.
    >
    
    But aren't these existing messages? As these are not added by this
    patch and equally apply to existing code, so, isn't it better to
    discuss these separately if you think these are not suitable?
    
    > 7)
    > Currently the error i s:
    >
    > postgres=# create subscription sub1 connection '...' publication pub1,pub2,pub3;
    > ERROR:  publications "pub1", "pub2", "pub3" are defined with EXCEPT TABLE
    > HINT:  Subscription cannot be created using multiple publications that
    > specify EXCEPT TABLE.
    >
    > Hint looks more like DETAIL. Shall we have this:
    >
    > ERROR:  cannot create subscription with multiple publications that
    > specify EXCEPT TABLE
    > DETAIL:  The publications "pub1", "pub2", and "pub3" define EXCEPT
    > TABLE clauses.
    >
    
    So, you are talking about below error:
    + ereport(ERROR,
    + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("publications %s are defined with EXCEPT TABLE", pubnames.data),
    + errhint("Subscription cannot be created using multiple publications
    that specify EXCEPT TABLE."));
    
    Apart from the message, the error code here should be
    ERRCODE_FEATURE_NOT_SUPPORTED. How about an errmsg like: "cannot
    combine publications %s with EXCEPT TABLE clauses",
    except_publications? If we choose a message like that then we don't
    need a hint. I suggest you can refer to the following column list
    message to form a message for the except clause.
    
    ereport(ERROR,
    errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    errmsg("cannot use different column lists for table \"%s.%s\" in
    different publications",
       nspname, relname));
    
    Few other minor comments:
    1.
    + * Combining different exclusion lists with varying settings for
    + * 'publish_via_partition_root' can result in ambiguous publication
    + * identities.
    
    It would be better if you give one such example case so that it is
    easy for reader to understand why we don't support this combination.
    
    2.
    + *
    + * Publications declared with FOR ALL TABLES should use
    + * GetAllPublicationRelations() to obtain the complete set of tables covered by
    + * the publication.
    + */
    +List *
    +GetPublicationIncludedRelations(Oid pubid, PublicationPartOpt pub_partopt)
    
    Shouldn't this mention the ALL SEQUENCES case as well in the comments
    as we were previously doing for GetPublicationRelations.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  221. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-18T06:50:23Z

    On Tue, Feb 17, 2026 at 5:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > 10)
    > CreatePublication() has changed the way we process publications.
    > Earlier, we had explicit checks for publication types such as
    > 'for_all_tables' and 'for_all_sequences' etc, which made the code
    > easier to follow. That differentiation based on publication type is no
    > longer there. As an example,
    > we now invoke functions like TransformPubWhereClauses() and
    > CheckPubRelationColumnList() even for FOR ALL TABLES ... EXCEPT
    > publications, which are not needed. We could consider restoring the
    > previous structure, where logic was clearly separated based on
    > publication type.
    >
    
    Yeah, we can do that if it doesn't add more complexity w.r.t except
    clause because the proposed approach tries to unify the code path
    where we need to add relations to pg_publication_rel. I think we are
    doing some of the new stuff even for all sequences case as well which
    is not required for the patch. If we want we can keep a flag
    indicating the presence of the except flag in CreatePublicationStmt to
    simplify the handling. BTW, the patch doesn't implement EXCEPT clause
    for ALL SEQUENCES, is it because we want to deal with that separately
    to avoid additional complexity in the patch? Otherwise, I think that
    is a natural extension of this work.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  222. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-18T06:57:55Z

    On Wed, Feb 18, 2026 at 11:41 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Feb 17, 2026 at 5:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Tue, Feb 17, 2026 at 12:01 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Tue, Feb 17, 2026 at 11:13 AM vignesh C <vignesh21@gmail.com> wrote:
    > > > >
    > > > > Thanks for the comments. The attached v45 version patch has the
    > > > > changes for the same.
    > > > >
    > > >
    > > > Thanks Vignesh, please find a few comments from v44 itself. Please
    > > > ignore if already addressed. Will switch to v45 now.
    > > >
    > > >
    > > > 1)
    > > > publication_has_any_exception:
    > > > + ScanKeyInit(&skey,
    > > > + Anum_pg_publication_rel_prpubid,
    > > > + BTEqualStrategyNumber, F_OIDEQ,
    > > > + ObjectIdGetDatum(pubid));
    > > > +
    > > > + scan = systable_beginscan(pubRel,
    > > > +   PublicationRelPrrelidPrpubidIndexId,
    > > > +   true, NULL, 1, &skey);
    > > >
    > > > PublicationRelPrrelidPrpubidIndexId is index on relid and pubid, I
    > > > don't think it will be used in this case where we have only pubid key.
    > > > Shall we use PublicationRelPrpubidIndexId instead?
    > > >
    > > > 2)
    > > > +/*
    > > > + * is_relid_published_explicitly
    > > > + *
    > > > + * Checks if the given relation OID is explicitly part of the publication.
    > > > + * This corresponds to the 'FOR TABLE' syntax.
    > > > + */
    > > > +static bool
    > > > +is_relid_published_explicitly(Oid relid, Oid pubid)
    > > > +{
    > > > + /*
    > > > + * Search the syscache for pg_publication_rel using the (relid, pubid)
    > > > + * index.
    > > > + */
    > > > + return SearchSysCacheExists2(PUBLICATIONRELMAP,
    > > > + ObjectIdGetDatum(relid),
    > > > + ObjectIdGetDatum(pubid));
    > > > +}
    > > >
    > > > How are we ensuring that it is not fetching the one with except-flag
    > > > as true? Shall we assert when pub is all-tables to rule out that
    > > > case/mistake? Or if we code-flow is expected to come to this function
    > > > even for 'all-tables' pub (it appears to me t by looking at the
    > > > caller), we shall return in such a case instead of Assert.
    > > >
    > > >
    > > > 3)
    > > > Shall we rename these:
    > > > 'is_relid_published_as_except' as 'is_relid_excepted'.
    > > > 'is_relid_published_as_except_with_ancestors' as 'is_relid_or_ancestor_excepted'
    > > >
    > > > These will be to match tones of:
    > > > is_schema_published
    > > > is_relid_published_explicitly
    > > >
    > > > I feel even for 'is_relid_published_explicitly', we can simply say
    > > > 'is_relid_published'. Comments (and assert/checks) can explain that it
    > > > is for FOR-TABLE pub.
    > > >
    > > > 4)
    > > > + List    *except_leaves = NIL;
    > > > + List    *allowed_leaves = NIL;
    > > >
    > > > Similar to allowed_leaves, shall we have excepted_leaves instead of
    > > > except_leaves?
    > > >
    > > > 5)
    > > > pg_get_publication_effective_tables():
    > > >
    > > > +
    > > > + /*
    > > > + * Check whether the table itself or its schema is
    > > > + * included in this publication.
    > > > + */
    > > > + if (is_relid_published_explicitly(curr_relid, pubid) ||
    > > > + is_schema_published(get_rel_namespace(curr_relid), pubid))
    > > > + {
    > > > + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    > > > + }
    > > > + else
    > > > + {
    > > > + List    *ancestors = get_partition_ancestors(curr_relid);
    > > > +
    > > > + /*
    > > > + * Check whether any ancestor, or its schema, is
    > > > + * included in this publication.
    > > > + */
    > > > + foreach_oid(anc_oid, ancestors)
    > > > + {
    > > > + if (is_relid_published_explicitly(anc_oid, pubid) ||
    > > > + is_schema_published(get_rel_namespace(anc_oid), pubid))
    > > > + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    > > > + }
    > > > + }
    > > >
    > > > Do you think we can convert this code part to
    > > > is_relid_or_ancestor_published(), similar to
    > > > is_relid_published_as_except_with_ancestors()?
    > > >
    > > > is_relid_or_ancestor_published() can check both pg_publication_rel and
    > > > pg_publication_namespace. I do not see individual use-case scenarios
    > > > for is_relid_published_explicitly() and is_schema_published() and thus
    > > > it is better to club these in one function. So at the end we will be
    > > > left with 2 such functions:
    > > >
    > > > is_relid_or_ancestor_published
    > > > is_relid_or_ancestor_excepted/excluded (choose the name based on
    > > > others comments too)
    > > >
    > >
    > > A few more:
    > >
    > > 6)
    > > postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab1);
    > > ERROR:  cannot add relation "tab1" to publication
    > > DETAIL:  This operation is not supported for temporary tables.
    > >
    > > postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab2);
    > > ERROR:  cannot add relation "tab2" to publication
    > > DETAIL:  This operation is not supported for unlogged tables.
    > >
    > > Shall we change the error message here as we are not trying to add
    > > relation here.
    > >
    >
    > But aren't these existing messages? As these are not added by this
    > patch and equally apply to existing code, so, isn't it better to
    > discuss these separately if you think these are not suitable?
    >
    
     I do not have strong opinion here but this is what I originally had in mind:
    
    1) 'cannot add relation .. to publication' is confusing when the user
    is actually trying to exclude them.
    2) Since these tables are already excluded because of their unlogged
    and temporary nature, the error is further misleading.
    
    Perhaps we shall have:
    ERROR:  cannot specify relation "tab1" in publication
    DETAIL:  Unlogged tables are automatically excluded from publication.
    
    Or something better? Or do you think we are okay as is?
    
    > > 7)
    > > Currently the error i s:
    > >
    > > postgres=# create subscription sub1 connection '...' publication pub1,pub2,pub3;
    > > ERROR:  publications "pub1", "pub2", "pub3" are defined with EXCEPT TABLE
    > > HINT:  Subscription cannot be created using multiple publications that
    > > specify EXCEPT TABLE.
    > >
    > > Hint looks more like DETAIL. Shall we have this:
    > >
    > > ERROR:  cannot create subscription with multiple publications that
    > > specify EXCEPT TABLE
    > > DETAIL:  The publications "pub1", "pub2", and "pub3" define EXCEPT
    > > TABLE clauses.
    > >
    >
    > So, you are talking about below error:
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("publications %s are defined with EXCEPT TABLE", pubnames.data),
    > + errhint("Subscription cannot be created using multiple publications
    > that specify EXCEPT TABLE."));
    >
    > Apart from the message, the error code here should be
    > ERRCODE_FEATURE_NOT_SUPPORTED. How about an errmsg like: "cannot
    > combine publications %s with EXCEPT TABLE clauses",
    > except_publications? If we choose a message like that then we don't
    > need a hint. I suggest you can refer to the following column list
    > message to form a message for the except clause.
    
    The proposed message looks good.
    
    thanks
    Shveta
    
    
    
    
  223. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-18T07:39:08Z

    On Wed, Feb 18, 2026 at 12:28 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Feb 18, 2026 at 11:41 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Tue, Feb 17, 2026 at 5:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > A few more:
    > > >
    > > > 6)
    > > > postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab1);
    > > > ERROR:  cannot add relation "tab1" to publication
    > > > DETAIL:  This operation is not supported for temporary tables.
    > > >
    > > > postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab2);
    > > > ERROR:  cannot add relation "tab2" to publication
    > > > DETAIL:  This operation is not supported for unlogged tables.
    > > >
    > > > Shall we change the error message here as we are not trying to add
    > > > relation here.
    > > >
    > >
    > > But aren't these existing messages? As these are not added by this
    > > patch and equally apply to existing code, so, isn't it better to
    > > discuss these separately if you think these are not suitable?
    > >
    >
    >  I do not have strong opinion here but this is what I originally had in mind:
    >
    > 1) 'cannot add relation .. to publication' is confusing when the user
    > is actually trying to exclude them.
    > 2) Since these tables are already excluded because of their unlogged
    > and temporary nature, the error is further misleading.
    >
    > Perhaps we shall have:
    > ERROR:  cannot specify relation "tab1" in publication
    > DETAIL:  Unlogged tables are automatically excluded from publication.
    >
    
    Your proposed message for this case sounds good to me.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  224. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-18T11:04:07Z

    On Wed, Feb 18, 2026 at 11:41 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Feb 17, 2026 at 5:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    >
    > > 7)
    > > Currently the error i s:
    > >
    > > postgres=# create subscription sub1 connection '...' publication pub1,pub2,pub3;
    > > ERROR:  publications "pub1", "pub2", "pub3" are defined with EXCEPT TABLE
    > > HINT:  Subscription cannot be created using multiple publications that
    > > specify EXCEPT TABLE.
    > >
    > > Hint looks more like DETAIL. Shall we have this:
    > >
    > > ERROR:  cannot create subscription with multiple publications that
    > > specify EXCEPT TABLE
    > > DETAIL:  The publications "pub1", "pub2", and "pub3" define EXCEPT
    > > TABLE clauses.
    > >
    >
    > So, you are talking about below error:
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("publications %s are defined with EXCEPT TABLE", pubnames.data),
    > + errhint("Subscription cannot be created using multiple publications
    > that specify EXCEPT TABLE."));
    >
    > Apart from the message, the error code here should be
    > ERRCODE_FEATURE_NOT_SUPPORTED. How about an errmsg like: "cannot
    > combine publications %s with EXCEPT TABLE clauses",
    > except_publications? If we choose a message like that then we don't
    > need a hint. I suggest you can refer to the following column list
    > message to form a message for the except clause.
    >
    > ereport(ERROR,
    > errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    > errmsg("cannot use different column lists for table \"%s.%s\" in
    > different publications",
    >    nspname, relname));
    >
    
    Based on the above, the following message should also be changed:
    + ereport(ERROR,
    + errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    + errmsg("cannot use multiple publications with EXCEPT TABLE lists"),
    + errdetail("The following publications have exceptions: %s.",
    +   pub_names_str.data));
    
    Few more minor comments:
    1.
    + elog(LOG, "fetch_relation_list: executing query to fetch
    effectiverelations: \n%s",
    + cmd.data);
    
    space is required between effective and relations.
    
    2.
    @@ -10858,6 +10862,7 @@ PublicationObjSpec:
                         $$->pubtable->relation = $2;
                         $$->pubtable->columns = $3;
                         $$->pubtable->whereClause = $4;
    +                    $$->location = @1;
    what is purpose of this change in PublicationObjSpec?
    
    3.
    + elog(LOG, "Executing query to get the tables:\n%s", cmd.data);
    
    This can be more specific. How about "Executing query to get the
    partition tables to be copied" or something like that?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  225. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-02-19T04:43:30Z

    On Tue, 17 Feb 2026 at 17:08, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Feb 17, 2026 at 12:01 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Tue, Feb 17, 2026 at 11:13 AM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > Thanks for the comments. The attached v45 version patch has the
    > > > changes for the same.
    > > >
    > >
    > > Thanks Vignesh, please find a few comments from v44 itself. Please
    > > ignore if already addressed. Will switch to v45 now.
    > >
    > >
    > > 1)
    > > publication_has_any_exception:
    > > + ScanKeyInit(&skey,
    > > + Anum_pg_publication_rel_prpubid,
    > > + BTEqualStrategyNumber, F_OIDEQ,
    > > + ObjectIdGetDatum(pubid));
    > > +
    > > + scan = systable_beginscan(pubRel,
    > > +   PublicationRelPrrelidPrpubidIndexId,
    > > +   true, NULL, 1, &skey);
    > >
    > > PublicationRelPrrelidPrpubidIndexId is index on relid and pubid, I
    > > don't think it will be used in this case where we have only pubid key.
    > > Shall we use PublicationRelPrpubidIndexId instead?
    > >
    > > 2)
    > > +/*
    > > + * is_relid_published_explicitly
    > > + *
    > > + * Checks if the given relation OID is explicitly part of the publication.
    > > + * This corresponds to the 'FOR TABLE' syntax.
    > > + */
    > > +static bool
    > > +is_relid_published_explicitly(Oid relid, Oid pubid)
    > > +{
    > > + /*
    > > + * Search the syscache for pg_publication_rel using the (relid, pubid)
    > > + * index.
    > > + */
    > > + return SearchSysCacheExists2(PUBLICATIONRELMAP,
    > > + ObjectIdGetDatum(relid),
    > > + ObjectIdGetDatum(pubid));
    > > +}
    > >
    > > How are we ensuring that it is not fetching the one with except-flag
    > > as true? Shall we assert when pub is all-tables to rule out that
    > > case/mistake? Or if we code-flow is expected to come to this function
    > > even for 'all-tables' pub (it appears to me t by looking at the
    > > caller), we shall return in such a case instead of Assert.
    > >
    > >
    > > 3)
    > > Shall we rename these:
    > > 'is_relid_published_as_except' as 'is_relid_excepted'.
    > > 'is_relid_published_as_except_with_ancestors' as 'is_relid_or_ancestor_excepted'
    > >
    > > These will be to match tones of:
    > > is_schema_published
    > > is_relid_published_explicitly
    > >
    > > I feel even for 'is_relid_published_explicitly', we can simply say
    > > 'is_relid_published'. Comments (and assert/checks) can explain that it
    > > is for FOR-TABLE pub.
    > >
    > > 4)
    > > + List    *except_leaves = NIL;
    > > + List    *allowed_leaves = NIL;
    > >
    > > Similar to allowed_leaves, shall we have excepted_leaves instead of
    > > except_leaves?
    > >
    > > 5)
    > > pg_get_publication_effective_tables():
    > >
    > > +
    > > + /*
    > > + * Check whether the table itself or its schema is
    > > + * included in this publication.
    > > + */
    > > + if (is_relid_published_explicitly(curr_relid, pubid) ||
    > > + is_schema_published(get_rel_namespace(curr_relid), pubid))
    > > + {
    > > + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    > > + }
    > > + else
    > > + {
    > > + List    *ancestors = get_partition_ancestors(curr_relid);
    > > +
    > > + /*
    > > + * Check whether any ancestor, or its schema, is
    > > + * included in this publication.
    > > + */
    > > + foreach_oid(anc_oid, ancestors)
    > > + {
    > > + if (is_relid_published_explicitly(anc_oid, pubid) ||
    > > + is_schema_published(get_rel_namespace(anc_oid), pubid))
    > > + allowed_leaves = lappend_oid(allowed_leaves, curr_relid);
    > > + }
    > > + }
    > >
    > > Do you think we can convert this code part to
    > > is_relid_or_ancestor_published(), similar to
    > > is_relid_published_as_except_with_ancestors()?
    > >
    > > is_relid_or_ancestor_published() can check both pg_publication_rel and
    > > pg_publication_namespace. I do not see individual use-case scenarios
    > > for is_relid_published_explicitly() and is_schema_published() and thus
    > > it is better to club these in one function. So at the end we will be
    > > left with 2 such functions:
    > >
    > > is_relid_or_ancestor_published
    > > is_relid_or_ancestor_excepted/excluded (choose the name based on
    > > others comments too)
    > >
    >
    > A few more:
    >
    > 6)
    > postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab1);
    > ERROR:  cannot add relation "tab1" to publication
    > DETAIL:  This operation is not supported for temporary tables.
    >
    > postgres=# CREATE PUBLICATION pub4 for ALL TABLES  EXCEPT TABLE (tab2);
    > ERROR:  cannot add relation "tab2" to publication
    > DETAIL:  This operation is not supported for unlogged tables.
    >
    > Shall we change the error message here as we are not trying to add
    > relation here.
    >
    Based on the discussion in [1]. We are still analysing this change.
    Will address it in the next version of patch.
    
    > 7)
    > Currently the error i s:
    >
    > postgres=# create subscription sub1 connection '...' publication pub1,pub2,pub3;
    > ERROR:  publications "pub1", "pub2", "pub3" are defined with EXCEPT TABLE
    > HINT:  Subscription cannot be created using multiple publications that
    > specify EXCEPT TABLE.
    >
    > Hint looks more like DETAIL. Shall we have this:
    >
    > ERROR:  cannot create subscription with multiple publications that
    > specify EXCEPT TABLE
    > DETAIL:  The publications "pub1", "pub2", and "pub3" define EXCEPT
    > TABLE clauses.
    >
    Modified it as per Amit's suggestion in [2].
    
    > 8)
    > pg_get_publication_effective_tables():
    >
    > + /* Return root immediately if no filtering logic is needed */
    > + if (has_clean_all_tables_pub || !has_any_except)
    >
    > I think we do not need any additional boolean 'has_any_except' for
    > this purpose. We can simply rely on 'except_pub_names' being Nil.
    >
    > 9)
    > + /* Return root immediately if no filtering logic is needed */
    > + if (has_clean_all_tables_pub || !has_any_except)
    > + {
    > + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    > + final_output = list_make1_oid(root_relid);
    > + MemoryContextSwitchTo(oldcontext);
    > + }
    > + else
    >
    > Can we please write a comment atop 'else' block to say what it is
    > going to attempt? This is because there is a big chunk of code in
    > 'else' block and it is difficult to construct our logic by reading
    > each part of it.
    >
    > 10)
    > CreatePublication() has changed the way we process publications.
    > Earlier, we had explicit checks for publication types such as
    > 'for_all_tables' and 'for_all_sequences' etc, which made the code
    > easier to follow. That differentiation based on publication type is no
    > longer there. As an example,
    > we now invoke functions like TransformPubWhereClauses() and
    > CheckPubRelationColumnList() even for FOR ALL TABLES ... EXCEPT
    > publications, which are not needed. We could consider restoring the
    > previous structure, where logic was clearly separated based on
    > publication type.
    >
    
    Thanks for reviewing the patch. I have addressed the remaining
    comments in the v46 patch..
    
    [1]: https://www.postgresql.org/message-id/CAA4eK1J%3D41eienWGzgAnB_-fAGjZdB2daP_N%3D84NSm1QT56YVQ%40mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAA4eK1KWqttt3UMdR8P0wYyqDO6cuLhuvGb5cDpuctG8F10EFA%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  226. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-02-19T04:45:06Z

    On Wed, 18 Feb 2026 at 16:34, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, Feb 18, 2026 at 11:41 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Tue, Feb 17, 2026 at 5:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > >
    > > > 7)
    > > > Currently the error i s:
    > > >
    > > > postgres=# create subscription sub1 connection '...' publication pub1,pub2,pub3;
    > > > ERROR:  publications "pub1", "pub2", "pub3" are defined with EXCEPT TABLE
    > > > HINT:  Subscription cannot be created using multiple publications that
    > > > specify EXCEPT TABLE.
    > > >
    > > > Hint looks more like DETAIL. Shall we have this:
    > > >
    > > > ERROR:  cannot create subscription with multiple publications that
    > > > specify EXCEPT TABLE
    > > > DETAIL:  The publications "pub1", "pub2", and "pub3" define EXCEPT
    > > > TABLE clauses.
    > > >
    > >
    > > So, you are talking about below error:
    > > + ereport(ERROR,
    > > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > > + errmsg("publications %s are defined with EXCEPT TABLE", pubnames.data),
    > > + errhint("Subscription cannot be created using multiple publications
    > > that specify EXCEPT TABLE."));
    > >
    > > Apart from the message, the error code here should be
    > > ERRCODE_FEATURE_NOT_SUPPORTED. How about an errmsg like: "cannot
    > > combine publications %s with EXCEPT TABLE clauses",
    > > except_publications? If we choose a message like that then we don't
    > > need a hint. I suggest you can refer to the following column list
    > > message to form a message for the except clause.
    > >
    > > ereport(ERROR,
    > > errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    > > errmsg("cannot use different column lists for table \"%s.%s\" in
    > > different publications",
    > >    nspname, relname));
    > >
    >
    > Based on the above, the following message should also be changed:
    > + ereport(ERROR,
    > + errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    > + errmsg("cannot use multiple publications with EXCEPT TABLE lists"),
    > + errdetail("The following publications have exceptions: %s.",
    > +   pub_names_str.data));
    >
    > Few more minor comments:
    > 1.
    > + elog(LOG, "fetch_relation_list: executing query to fetch
    > effectiverelations: \n%s",
    > + cmd.data);
    >
    > space is required between effective and relations.
    >
    > 2.
    > @@ -10858,6 +10862,7 @@ PublicationObjSpec:
    >                      $$->pubtable->relation = $2;
    >                      $$->pubtable->columns = $3;
    >                      $$->pubtable->whereClause = $4;
    > +                    $$->location = @1;
    > what is purpose of this change in PublicationObjSpec?
    >
    > 3.
    > + elog(LOG, "Executing query to get the tables:\n%s", cmd.data);
    >
    > This can be more specific. How about "Executing query to get the
    > partition tables to be copied" or something like that?
    >
    Hi Amit,
    
    Thanks for reviewing the patch. I have addressed the above comments
    and the minor comments in [1] in v46 patch.
    I have shared v46 patch in [2].
    
    [1]: https://www.postgresql.org/message-id/CAA4eK1KWqttt3UMdR8P0wYyqDO6cuLhuvGb5cDpuctG8F10EFA%40mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CANhcyEXBw0NeCmrbzSEQ3bBHzzEwvyLo-rOx0migTtfm-H4sNw%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  227. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-02-19T04:45:24Z

    On Wed, 18 Feb 2026 at 12:20, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Feb 17, 2026 at 5:08 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > 10)
    > > CreatePublication() has changed the way we process publications.
    > > Earlier, we had explicit checks for publication types such as
    > > 'for_all_tables' and 'for_all_sequences' etc, which made the code
    > > easier to follow. That differentiation based on publication type is no
    > > longer there. As an example,
    > > we now invoke functions like TransformPubWhereClauses() and
    > > CheckPubRelationColumnList() even for FOR ALL TABLES ... EXCEPT
    > > publications, which are not needed. We could consider restoring the
    > > previous structure, where logic was clearly separated based on
    > > publication type.
    > >
    >
    > Yeah, we can do that if it doesn't add more complexity w.r.t except
    > clause because the proposed approach tries to unify the code path
    > where we need to add relations to pg_publication_rel. I think we are
    > doing some of the new stuff even for all sequences case as well which
    > is not required for the patch. If we want we can keep a flag
    > indicating the presence of the except flag in CreatePublicationStmt to
    > simplify the handling. BTW, the patch doesn't implement EXCEPT clause
    > for ALL SEQUENCES, is it because we want to deal with that separately
    > to avoid additional complexity in the patch? Otherwise, I think that
    > is a natural extension of this work.
    >
    I have made the change as per Shveta's suggestion in the v46 patch [1].
    
    We did not implement the EXCEPT clause for ALL SEQUENCES to avoid
    additional complexity in the patch. And thought of implementing it
    after this patch gets committed.
    What do you suggest? Is it ok or should we also implement EXCEPT
    clause for ALL SEQUENCES for this patch itself?
    
    [1]: https://www.postgresql.org/message-id/CANhcyEXBw0NeCmrbzSEQ3bBHzzEwvyLo-rOx0migTtfm-H4sNw%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  228. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-19T04:52:56Z

    On Thu, Feb 19, 2026 at 10:15 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > We did not implement the EXCEPT clause for ALL SEQUENCES to avoid
    > additional complexity in the patch.
    >
    
    Fair enough.
    
    > And thought of implementing it
    > after this patch gets committed.
    > What do you suggest? Is it ok or should we also implement EXCEPT
    > clause for ALL SEQUENCES for this patch itself?
    >
    
    I am fine with doing it as a separate patch but let's try to get that
    done immediately after the main patch (FOR ALL TABLES EXCEPT ...) is
    committed.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  229. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-19T10:36:46Z

    On Thu, Feb 19, 2026 at 10:13 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > Thanks for reviewing the patch. I have addressed the remaining
    > comments in the v46 patch..
    >
    
    Can we think of some ideas to split this patch? One possibility is
    that in the first patch we give an ERROR, if a non-root partitioned
    table is present in EXCEPT Clause. I see that a lot of code is written
    to handle partitions in EXCEPT clause. I feel such a split will make
    code easier to review and validate.
    
    Few other comments:
    =================
    1.
      if (stmt->for_all_tables)
      {
    + /* Process EXCEPT table list */
    + if (relations != NIL)
    + {
    + Assert(rels != NIL);
    + PublicationAddTables(puboid, rels, true, NULL);
    + }
    +
      /*
      * Invalidate relcache so that publication info is rebuilt. Sequences
      * publication doesn't require invalidation, as replica identity
    CacheInvalidateRelcacheAll();
    
    Here, the relations listed in the except table list will be
    invalidated twice, once inside
    PublicationAddTables->publication_add_relation, and second time via
    CacheInvalidateRelcacheAll. Can we avoid that by adding a parameter to
    PublicationAddTables?
    
    2.
    - root_relids = GetPublicationRelations(pubform->oid,
    -   PUBLICATION_PART_ROOT);
    + root_relids = GetIncludedRelationsByPub(pubform->oid,
    + PUBLICATION_PART_ROOT);
    
    To follow the previous function naming pattern, can we rename
    GetIncludedRelationsByPub to GetIncludedPublicationRelations? If we
    agree to this then rename the excluded version of the function as
    well.
    
    3.
    +/*
    + * Return the list of relation Oids for a publication.
    + *
    + * For a FOR TABLE publication, this returns the list of relations explicitly
    + * included in the publication.
    + *
    + * Publications declared with FOR ALL TABLES/SEQUENCES should use
    + * GetAllPublicationRelations() to obtain the complete set of tables/sequences
    + * covered by the publication.
    + */
    +List *
    +GetIncludedRelationsByPub(Oid pubid, PublicationPartOpt pub_partopt)
    
    This is equivalent to the existing function GetPublicationRelations()
    which has more precise comments atop. We can use the same comments
    unless there is any functionality difference.
    
    4.
    --- a/src/backend/catalog/pg_publication.c
    +++ b/src/backend/catalog/pg_publication.c
    @@ -29,6 +29,7 @@
     #include "catalog/pg_publication.h"
     #include "catalog/pg_publication_namespace.h"
     #include "catalog/pg_publication_rel.h"
    +#include "catalog/pg_subscription.h"
    
    It appears odd to include pg_subscription.h in the publication code.
    Is there a reason for the same? If not then avoid it.
    
    Apart from above, a few cosmetic changes are attached.
    
    -- 
    With Regards,
    Amit Kapila.
    
  230. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-02-19T10:38:01Z

    On Thu, Feb 19, 2026 at 10:13 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > Thanks for reviewing the patch. I have addressed the remaining
    > comments in the v46 patch..
    >
    I am looking at the latest version some questions/comments
    
    1.
    GetRelationPublications()
    {
    ..
    for (int i = 0; i < pubrellist->n_members; i++)
    {
    + if (pubrel->prexcept)
    + {
    + if (except_pubids)
    + *except_pubids = lappend_oid(*except_pubids, pubid);
    + }
    + else
    + {
    + if (pubids)
    + *pubids = lappend_oid(*pubids, pubid);
    + found = true;
    + }
    }
    
    Can we simplify this multi-level check to something simple by using
    some temporary variable?
    Suggestions:
    
    for (int i = 0; i < pubrellist->n_members; i++)
    {
        List **target_list = pubrel->prexcept ? except_pubids : pubids;
    
      if (target_list)
         *target_list = lappend_oid(*target_list, pubid);
      if (!pubrel->prexcept)
        found = true;
    }
    
    2.
    -extern List *GetPublicationRelations(Oid pubid, PublicationPartOpt
    pub_partopt);
    +extern List *GetIncludedRelationsByPub(Oid pubid,
    +    PublicationPartOpt pub_partopt);
    
    I don't really like renaming this function, IMHO the
    GetPublicationRelations() means all the published relation, we don't
    need to be explicit about *Included* relation.  Then I see we have
    another function to fetch the excluded relation i.e.
    GetExcludedTablesByPub() so maybe this makes sense considering we have
    a requirement for fetching included as well as excluded.
    
    3. Question, what would be the behavior of
    pg_get_publication_tables(), if we have created PUBLICATION FOR ALL
    TABLES with EXCEPT, I assume it will give all table even in the EXCEPT
    list as we have another function to get effective tables i.e.
    pg_get_publication_effective_tables(), so my question is are we
    planning to add an additional field in pg_get_publication_tables() to
    explicitly mention that the table is EXCLUDED?
    
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  231. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-19T11:51:50Z

    On Thu, Feb 19, 2026 at 10:13 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    >
    > Thanks for reviewing the patch. I have addressed the remaining
    > comments in the v46 patch..
    >
    
    Thanks. Please find a few comments:
    
    1)
    pg_get_publication_effective_tables has
    + ereport(ERROR,
    + errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    + errmsg("cannot use multiple publications with EXCEPT TABLE lists"),
    + errdetail("The following publications have exceptions: %s.",
    +   pub_names->data));
    
    LoadPublications has:
    + ereport(ERROR,
    + errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    + errmsg("cannot use multiple publications with EXCEPT TABLE lists"),
    + errdetail("Publications (%s) define EXCEPT TABLE clauses.",
    +   pub_names_str.data));
    
    check_publications_except_list has:
    + ereport(ERROR,
    + errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    + errmsg("cannot combine publications %s with EXCEPT TABLE clauses",
    pubnames.data));
    
    Can we make all three messages the same based on which format was
    decided earlier.
    
    2)
    +/* Helper: Check syscache for prexcept flag */
    +bool
    +is_relid_excepted(Oid relid, Oid pubid)
    
    Can we move this function just before 'is_relid_or_ancestor_excepted'
    as they are related functions? Can we also change the comments as well
    similar to other functions?
    
    3)
    I see that is_schema_published() is only needed by
    is_relid_or_ancestor_published() which calls is_relid_published()
    already. Shall we make logic of 'is_schema_published' as part of
    'is_relid_published' itself i.e. we will consider relid published if
    it is published explicitly or as part of schema? Thoughts? This will
    help us to avoid having 'is_schema_published' function separately when
    its usage is quite limited.
    
    4)
    pg_get_publication_effective_tables() has neighboring comments:
    
    + /* Check whether this publication defines any EXCEPT entries */
    
    + /*
    + * This publication includes all tables without except.
    + */
    
    We can choose the same comment style at both places. Please check the
    rest of the function too.
    
    thanks
    Shveta
    
    
    
    
  232. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-19T12:24:00Z

    On Thu, Feb 19, 2026 at 4:08 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
    >
    > 3. Question, what would be the behavior of
    > pg_get_publication_tables(), if we have created PUBLICATION FOR ALL
    > TABLES with EXCEPT, I assume it will give all table even in the EXCEPT
    > list
    >
    
    No, it won't return the tables mentioned in the except list and I
    think that is what we expect from this function.
    
    > as we have another function to get effective tables i.e.
    > pg_get_publication_effective_tables(),
    >
    
    This function is for a different purpose and I think its name can be
    improved, how about pg_get_publication_partitions() or something on
    those lines?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  233. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-02-20T00:28:56Z

    A few of the recent function name change suggestions have originated
    from my internal review comments. But, these changes are not very
    effective unless they are done across existing functions as well to
    keep everything consistent.
    
    Background
    ----------
    TBH, I always found many of the current HEAD names to be confusing. I
    am often having to double-check back with the function comment to find
    what was the intention.
    
    e.g. Here is a list of some existing function names of pg_publication.c.
    GetPubPartitionOptionRelations
    GetRelationPublications
    GetPublicationRelations
    GetAllTablesPublications
    GetAllPublicationRelations
    GetPublicationSchemas
    GetSchemaPublications
    GetSchemaPublicationRelations
    GetAllSchemaPublicationRelations
    GetPublication
    GetPublicationByName
    
    At the core of my confusion is that the pattern GetYyyyXxxx means 2
    different things:
    
    - Sometimes the Yyyy is a search condition or "criteria" for what you
    are getting. e.g. GetRelationPublications means "Get publications that
    the specified relid is a member of"
    
    - sometimes the Yyyy is more like an "attribute" for what you are
    getting. e.g. GetAllTablesPublications means "Get all of the FOR ALL
    TABLES publications"
    
    Often there is some ambiguity:
    
    e.g GetAllPublicationRelations
    - does it mean "Get relations list equivalent to a FOR ALL TABLES publication"?
    - does it mean "Get relations that are members of all the specified
    publications"?
    
    e.g. GetSchemaPublications
    - does it mean "Get publications marked as FOR TABLES IN SCHEMA"?
    - does it mean "Get publications for the specified schemaid"?
    
    etc.
    
    ~~~
    
    This thread's EXCEPT patch is not causing new problems, it's just
    adding yet more functions to the long list of those that I felt are
    ambiguous.
    
    IMO, when a function returns XXX, then the function name should be
    prefixed GetXXX.
    
    So, I would advocate for existing function names to change; something
    like below.
    
    GetPubPartitionOptionRelations  -> GetRelsByRelidAndPartitionOpt
    GetRelationPublications -> GetPubsByRelid
    GetPublicationRelations -> GetRelsByPubidMarkedForTable
    GetAllTablesPublications -> GetPubsMarkedForAllTables
    GetAllPublicationRelations -> GetRelsOfPubsMarkedForAll
    GetPublicationSchemas -> GetSchemasByPubidMarkedForTablesInSchema
    GetSchemaPublications -> GetPubsMarkedForTablesInSchemaBySchemaid
    GetSchemaPublicationRelations -> GetPublishedRelsBySchemaid
    GetAllSchemaPublicationRelations -> GetRelsOfPubsMarkedForTablesInSchema
    GetPublication -> GetPubByPubid
    GetPublicationByName -> GetPubByName
    
    Then, any new (non-static) functions for this EXCEPT patch would be
    named similarly.
    
    ~
    
    I understand that maybe everyone else feels the current names are fine
    as-is and not confusing at all.
    Or, maybe the confusion is recognised, but now is just not a good time
    of year to change everything.
    
    Anyway, I just wanted to give the background, and explain reasons for
    those function name suggestions.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  234. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-02-20T05:16:02Z

    On Thu, Feb 19, 2026 at 5:54 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Thu, Feb 19, 2026 at 4:08 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
    > >
    > > 3. Question, what would be the behavior of
    > > pg_get_publication_tables(), if we have created PUBLICATION FOR ALL
    > > TABLES with EXCEPT, I assume it will give all table even in the EXCEPT
    > > list
    > >
    >
    > No, it won't return the tables mentioned in the except list and I
    > think that is what we expect from this function.
    
    Yeah logically that makes sense, I got confused because of the another
    function with name 'pg_get_publication_effective_tables'
    
    > > as we have another function to get effective tables i.e.
    > > pg_get_publication_effective_tables(),
    > >
    >
    > This function is for a different purpose and I think its name can be
    > improved, how about pg_get_publication_partitions() or something on
    > those lines?
    
    I think the current name is confusing, not sure about what would be a
    better name, IMHO pg_get_publication_partitions() is not confusing at
    least.
    
    And also ,regarding the naming of is_relid_excepted() and
    is_relid_or_ancestor_excepted(), although they accurately reflect the
    EXCEPT keyword, "excepted" feels a bit awkward in code. I'd suggest
    changing these to is_relid_excluded() and
    is_relid_or_ancestor_excluded() for better readability.  But if others
    think excepted also looks fine and it's just me who doesn't like it
    then feel free to ignore this suggestion.
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  235. Re: Skipping schema changes in publication

    Ashutosh Sharma <ashu.coek88@gmail.com> — 2026-02-20T05:26:09Z

    On Fri, Feb 20, 2026 at 5:59 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > A few of the recent function name change suggestions have originated
    > from my internal review comments. But, these changes are not very
    > effective unless they are done across existing functions as well to
    > keep everything consistent.
    >
    > Background
    > ----------
    > TBH, I always found many of the current HEAD names to be confusing. I
    > am often having to double-check back with the function comment to find
    > what was the intention.
    >
    > e.g. Here is a list of some existing function names of pg_publication.c.
    > GetPubPartitionOptionRelations
    > GetRelationPublications
    > GetPublicationRelations
    > GetAllTablesPublications
    > GetAllPublicationRelations
    > GetPublicationSchemas
    > GetSchemaPublications
    > GetSchemaPublicationRelations
    > GetAllSchemaPublicationRelations
    > GetPublication
    > GetPublicationByName
    >
    > At the core of my confusion is that the pattern GetYyyyXxxx means 2
    > different things:
    >
    > - Sometimes the Yyyy is a search condition or "criteria" for what you
    > are getting. e.g. GetRelationPublications means "Get publications that
    > the specified relid is a member of"
    >
    > - sometimes the Yyyy is more like an "attribute" for what you are
    > getting. e.g. GetAllTablesPublications means "Get all of the FOR ALL
    > TABLES publications"
    >
    > Often there is some ambiguity:
    >
    > e.g GetAllPublicationRelations
    > - does it mean "Get relations list equivalent to a FOR ALL TABLES publication"?
    > - does it mean "Get relations that are members of all the specified
    > publications"?
    >
    > e.g. GetSchemaPublications
    > - does it mean "Get publications marked as FOR TABLES IN SCHEMA"?
    > - does it mean "Get publications for the specified schemaid"?
    >
    > etc.
    >
    > ~~~
    >
    > This thread's EXCEPT patch is not causing new problems, it's just
    > adding yet more functions to the long list of those that I felt are
    > ambiguous.
    >
    > IMO, when a function returns XXX, then the function name should be
    > prefixed GetXXX.
    >
    > So, I would advocate for existing function names to change; something
    > like below.
    >
    > GetPubPartitionOptionRelations  -> GetRelsByRelidAndPartitionOpt
    > GetRelationPublications -> GetPubsByRelid
    > GetPublicationRelations -> GetRelsByPubidMarkedForTable
    > GetAllTablesPublications -> GetPubsMarkedForAllTables
    > GetAllPublicationRelations -> GetRelsOfPubsMarkedForAll
    > GetPublicationSchemas -> GetSchemasByPubidMarkedForTablesInSchema
    > GetSchemaPublications -> GetPubsMarkedForTablesInSchemaBySchemaid
    > GetSchemaPublicationRelations -> GetPublishedRelsBySchemaid
    > GetAllSchemaPublicationRelations -> GetRelsOfPubsMarkedForTablesInSchema
    > GetPublication -> GetPubByPubid
    > GetPublicationByName -> GetPubByName
    >
    > Then, any new (non-static) functions for this EXCEPT patch would be
    > named similarly.
    >
    > ~
    >
    > I understand that maybe everyone else feels the current names are fine
    > as-is and not confusing at all.
    > Or, maybe the confusion is recognised, but now is just not a good time
    > of year to change everything.
    >
    > Anyway, I just wanted to give the background, and explain reasons for
    > those function name suggestions.
    >
    
    Few more comments on top of these:
    
    + elog(LOG, "fetch_relation_list: executing query to fetch
    effectiverelations: \n%s",
    + cmd.data);
    
    This looks like a debugging log message, did you add it for debugging
    purposes but later forgot to change its elevel or probably clean up?
    
    --
    
    +relation_is_effectively_excluded(Oid relid, List *exceptlist)
    +{
    + List    *leaftables = NIL;
    +
    + if (exceptlist == NIL)
    + return false;
    +
    + /* Explicitly excluded */
    + if (list_member_oid(exceptlist, relid))
    + return true;
    +
    + /* Get all leaf partitions of relid */
    + leaftables = GetPubPartitionOptionRelations(leaftables,
    + PUBLICATION_PART_LEAF,
    +
    
    "leaftables" are never freed on either return path. Since this
    function is called in a loop from GetAllPublicationRelations() during
    catalog scans, this has the potential to accumulate memory.
    
    --
    
    + res = walrcv_exec(LogRepWorkerWalRcvConn, cmd.data,
    +   lengthof(filtertableRow), filtertableRow);
    
    
    Seems like we are missing "walrcv_clear_result()" for this.
    
    --
    
    +/* Helper: Check syscache for prexcept flag */
    +bool
    +is_relid_excepted(Oid relid, Oid pubid)
    
    The comment atop this function needs some improvement it seems. It is
    trying to explain how it is implemented, rather than its purpose.
    
    --
    With Regards,
    Ashutosh Sharma,
    
    
    
    
  236. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-20T08:38:21Z

    On Thu, 19 Feb 2026 at 16:06, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Thu, Feb 19, 2026 at 10:13 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > Thanks for reviewing the patch. I have addressed the remaining
    > > comments in the v46 patch..
    > >
    >
    > Can we think of some ideas to split this patch? One possibility is
    > that in the first patch we give an ERROR, if a non-root partitioned
    > table is present in EXCEPT Clause. I see that a lot of code is written
    > to handle partitions in EXCEPT clause. I feel such a split will make
    > code easier to review and validate.
    
    Split it accordingly.
    
    > Few other comments:
    > =================
    > 1.
    >   if (stmt->for_all_tables)
    >   {
    > + /* Process EXCEPT table list */
    > + if (relations != NIL)
    > + {
    > + Assert(rels != NIL);
    > + PublicationAddTables(puboid, rels, true, NULL);
    > + }
    > +
    >   /*
    >   * Invalidate relcache so that publication info is rebuilt. Sequences
    >   * publication doesn't require invalidation, as replica identity
    > CacheInvalidateRelcacheAll();
    >
    > Here, the relations listed in the except table list will be
    > invalidated twice, once inside
    > PublicationAddTables->publication_add_relation, and second time via
    > CacheInvalidateRelcacheAll. Can we avoid that by adding a parameter to
    > PublicationAddTables?
    
    Fixed this
    
    > 2.
    > - root_relids = GetPublicationRelations(pubform->oid,
    > -   PUBLICATION_PART_ROOT);
    > + root_relids = GetIncludedRelationsByPub(pubform->oid,
    > + PUBLICATION_PART_ROOT);
    >
    > To follow the previous function naming pattern, can we rename
    > GetIncludedRelationsByPub to GetIncludedPublicationRelations? If we
    > agree to this then rename the excluded version of the function as
    > well.
    
    Modified
    
    > 3.
    > +/*
    > + * Return the list of relation Oids for a publication.
    > + *
    > + * For a FOR TABLE publication, this returns the list of relations explicitly
    > + * included in the publication.
    > + *
    > + * Publications declared with FOR ALL TABLES/SEQUENCES should use
    > + * GetAllPublicationRelations() to obtain the complete set of tables/sequences
    > + * covered by the publication.
    > + */
    > +List *
    > +GetIncludedRelationsByPub(Oid pubid, PublicationPartOpt pub_partopt)
    >
    > This is equivalent to the existing function GetPublicationRelations()
    > which has more precise comments atop. We can use the same comments
    > unless there is any functionality difference.
    
    Updated it
    
    > 4.
    > --- a/src/backend/catalog/pg_publication.c
    > +++ b/src/backend/catalog/pg_publication.c
    > @@ -29,6 +29,7 @@
    >  #include "catalog/pg_publication.h"
    >  #include "catalog/pg_publication_namespace.h"
    >  #include "catalog/pg_publication_rel.h"
    > +#include "catalog/pg_subscription.h"
    >
    > It appears odd to include pg_subscription.h in the publication code.
    > Is there a reason for the same? If not then avoid it.
    
    This was required because we now started using GetPublicationsStr. I
    have moved GetPublicationsStr to logical.c from pg_subscription which
    is common to both pub and sub.
    
    > Apart from above, a few cosmetic changes are attached.
    
    Merged them.
    
    The attached v47 version patch has the changes for the same.
    
    Regards,
    Vignesh
    
  237. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-20T09:08:03Z

    On Thu, 19 Feb 2026 at 17:22, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Thu, Feb 19, 2026 at 10:13 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > >
    > > Thanks for reviewing the patch. I have addressed the remaining
    > > comments in the v46 patch..
    > >
    >
    > Thanks. Please find a few comments:
    >
    > 1)
    > pg_get_publication_effective_tables has
    > + ereport(ERROR,
    > + errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    > + errmsg("cannot use multiple publications with EXCEPT TABLE lists"),
    > + errdetail("The following publications have exceptions: %s.",
    > +   pub_names->data));
    >
    > LoadPublications has:
    > + ereport(ERROR,
    > + errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    > + errmsg("cannot use multiple publications with EXCEPT TABLE lists"),
    > + errdetail("Publications (%s) define EXCEPT TABLE clauses.",
    > +   pub_names_str.data));
    >
    > check_publications_except_list has:
    > + ereport(ERROR,
    > + errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    > + errmsg("cannot combine publications %s with EXCEPT TABLE clauses",
    > pubnames.data));
    >
    > Can we make all three messages the same based on which format was
    > decided earlier.
    >
    > 2)
    > +/* Helper: Check syscache for prexcept flag */
    > +bool
    > +is_relid_excepted(Oid relid, Oid pubid)
    >
    > Can we move this function just before 'is_relid_or_ancestor_excepted'
    > as they are related functions? Can we also change the comments as well
    > similar to other functions?
    >
    > 3)
    > I see that is_schema_published() is only needed by
    > is_relid_or_ancestor_published() which calls is_relid_published()
    > already. Shall we make logic of 'is_schema_published' as part of
    > 'is_relid_published' itself i.e. we will consider relid published if
    > it is published explicitly or as part of schema? Thoughts? This will
    > help us to avoid having 'is_schema_published' function separately when
    > its usage is quite limited.
    >
    > 4)
    > pg_get_publication_effective_tables() has neighboring comments:
    >
    > + /* Check whether this publication defines any EXCEPT entries */
    >
    > + /*
    > + * This publication includes all tables without except.
    > + */
    >
    > We can choose the same comment style at both places. Please check the
    > rest of the function too.
    
    Thanks for the comments. These comments are addressed in the v47
    version patch posted at [1].
    [1] - https://www.postgresql.org/message-id/CALDaNm3X24fJznRUFh6NVhY1SDzgY9Aie1Ks%3Db6YqmAx-Z4H7Q%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  238. Re: Skipping schema changes in publication

    Ashutosh Sharma <ashu.coek88@gmail.com> — 2026-02-20T12:46:53Z

    Hi,
    
    On Fri, Feb 20, 2026 at 2:08 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Thu, 19 Feb 2026 at 16:06, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Thu, Feb 19, 2026 at 10:13 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > Thanks for reviewing the patch. I have addressed the remaining
    > > > comments in the v46 patch..
    > > >
    > >
    > > Can we think of some ideas to split this patch? One possibility is
    > > that in the first patch we give an ERROR, if a non-root partitioned
    > > table is present in EXCEPT Clause. I see that a lot of code is written
    > > to handle partitions in EXCEPT clause. I feel such a split will make
    > > code easier to review and validate.
    >
    > Split it accordingly.
    >
    > > Few other comments:
    > > =================
    > > 1.
    > >   if (stmt->for_all_tables)
    > >   {
    > > + /* Process EXCEPT table list */
    > > + if (relations != NIL)
    > > + {
    > > + Assert(rels != NIL);
    > > + PublicationAddTables(puboid, rels, true, NULL);
    > > + }
    > > +
    > >   /*
    > >   * Invalidate relcache so that publication info is rebuilt. Sequences
    > >   * publication doesn't require invalidation, as replica identity
    > > CacheInvalidateRelcacheAll();
    > >
    > > Here, the relations listed in the except table list will be
    > > invalidated twice, once inside
    > > PublicationAddTables->publication_add_relation, and second time via
    > > CacheInvalidateRelcacheAll. Can we avoid that by adding a parameter to
    > > PublicationAddTables?
    >
    > Fixed this
    >
    > > 2.
    > > - root_relids = GetPublicationRelations(pubform->oid,
    > > -   PUBLICATION_PART_ROOT);
    > > + root_relids = GetIncludedRelationsByPub(pubform->oid,
    > > + PUBLICATION_PART_ROOT);
    > >
    > > To follow the previous function naming pattern, can we rename
    > > GetIncludedRelationsByPub to GetIncludedPublicationRelations? If we
    > > agree to this then rename the excluded version of the function as
    > > well.
    >
    > Modified
    >
    > > 3.
    > > +/*
    > > + * Return the list of relation Oids for a publication.
    > > + *
    > > + * For a FOR TABLE publication, this returns the list of relations explicitly
    > > + * included in the publication.
    > > + *
    > > + * Publications declared with FOR ALL TABLES/SEQUENCES should use
    > > + * GetAllPublicationRelations() to obtain the complete set of tables/sequences
    > > + * covered by the publication.
    > > + */
    > > +List *
    > > +GetIncludedRelationsByPub(Oid pubid, PublicationPartOpt pub_partopt)
    > >
    > > This is equivalent to the existing function GetPublicationRelations()
    > > which has more precise comments atop. We can use the same comments
    > > unless there is any functionality difference.
    >
    > Updated it
    >
    > > 4.
    > > --- a/src/backend/catalog/pg_publication.c
    > > +++ b/src/backend/catalog/pg_publication.c
    > > @@ -29,6 +29,7 @@
    > >  #include "catalog/pg_publication.h"
    > >  #include "catalog/pg_publication_namespace.h"
    > >  #include "catalog/pg_publication_rel.h"
    > > +#include "catalog/pg_subscription.h"
    > >
    > > It appears odd to include pg_subscription.h in the publication code.
    > > Is there a reason for the same? If not then avoid it.
    >
    > This was required because we now started using GetPublicationsStr. I
    > have moved GetPublicationsStr to logical.c from pg_subscription which
    > is common to both pub and sub.
    >
    > > Apart from above, a few cosmetic changes are attached.
    >
    > Merged them.
    >
    > The attached v47 version patch has the changes for the same.
    >
    
    SIGABRT with normal tables:
    --------------------------------------
    
    ashutosh@test=# select
    pg_get_publication_effective_tables('t1'::regclass,
    '{all_pub}'::text[]);
    server closed the connection unexpectedly
            This probably means the server terminated abnormally
            before or while processing the request.
    The connection to the server was lost. Attempting reset: Failed.
    
    Since the function is user-facing, I think we should have test coverage for it.
    
    --
    With Regards,
    Ashutosh Sharma.
    
    
    
    
  239. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-22T11:20:57Z

    On Fri, Feb 20, 2026 at 2:08 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > The attached v47 version patch has the changes for the same.
    >
    
    Few comments on 0001:
    1.
    - * publish the child tables when the parent table is published.
    + * No need to invalidate the tables listed in the EXCEPT clause, because
    + * in that case all tables are invalidated anyway.
    
    Give the reference of a function where all tables are invalidated.
    
    2.
     List *
    -GetAllPublicationRelations(char relkind, bool pubviaroot)
    +GetAllPublicationRelations(Publication *pub, char relkind)
    ...
    + if (relkind == RELKIND_RELATION)
    + exceptlist = GetExcludedPublicationTables(pubid, pubviaroot ?
    +   PUBLICATION_PART_ROOT :
    +   PUBLICATION_PART_LEAF);
    
    Can you add a comment why the patch forms exceptlist only for RELKIND_RELATION?
    
    3.
    + errmsg("cannot combine publications that define EXCEPT TABLE clauses"),
    + errdetail("The following publications define EXCEPT TABLE clauses: %s.",
    
    Can you see a similar style of error message formation at any other
    place in code? As per my understanding, in such cases, we display
    required objects in errmsg itself. For example, "publications %s do
    not exist on the publisher". Also, I think the errmsg should end with
    a clause instead of caluses.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  240. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-02-23T06:07:35Z

    On Fri, 20 Feb 2026 at 18:17, Ashutosh Sharma <ashu.coek88@gmail.com> wrote:
    >
    > Hi,
    >
    > On Fri, Feb 20, 2026 at 2:08 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Thu, 19 Feb 2026 at 16:06, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > > On Thu, Feb 19, 2026 at 10:13 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > > >
    > > > > Thanks for reviewing the patch. I have addressed the remaining
    > > > > comments in the v46 patch..
    > > > >
    > > >
    > > > Can we think of some ideas to split this patch? One possibility is
    > > > that in the first patch we give an ERROR, if a non-root partitioned
    > > > table is present in EXCEPT Clause. I see that a lot of code is written
    > > > to handle partitions in EXCEPT clause. I feel such a split will make
    > > > code easier to review and validate.
    > >
    > > Split it accordingly.
    > >
    > > > Few other comments:
    > > > =================
    > > > 1.
    > > >   if (stmt->for_all_tables)
    > > >   {
    > > > + /* Process EXCEPT table list */
    > > > + if (relations != NIL)
    > > > + {
    > > > + Assert(rels != NIL);
    > > > + PublicationAddTables(puboid, rels, true, NULL);
    > > > + }
    > > > +
    > > >   /*
    > > >   * Invalidate relcache so that publication info is rebuilt. Sequences
    > > >   * publication doesn't require invalidation, as replica identity
    > > > CacheInvalidateRelcacheAll();
    > > >
    > > > Here, the relations listed in the except table list will be
    > > > invalidated twice, once inside
    > > > PublicationAddTables->publication_add_relation, and second time via
    > > > CacheInvalidateRelcacheAll. Can we avoid that by adding a parameter to
    > > > PublicationAddTables?
    > >
    > > Fixed this
    > >
    > > > 2.
    > > > - root_relids = GetPublicationRelations(pubform->oid,
    > > > -   PUBLICATION_PART_ROOT);
    > > > + root_relids = GetIncludedRelationsByPub(pubform->oid,
    > > > + PUBLICATION_PART_ROOT);
    > > >
    > > > To follow the previous function naming pattern, can we rename
    > > > GetIncludedRelationsByPub to GetIncludedPublicationRelations? If we
    > > > agree to this then rename the excluded version of the function as
    > > > well.
    > >
    > > Modified
    > >
    > > > 3.
    > > > +/*
    > > > + * Return the list of relation Oids for a publication.
    > > > + *
    > > > + * For a FOR TABLE publication, this returns the list of relations explicitly
    > > > + * included in the publication.
    > > > + *
    > > > + * Publications declared with FOR ALL TABLES/SEQUENCES should use
    > > > + * GetAllPublicationRelations() to obtain the complete set of tables/sequences
    > > > + * covered by the publication.
    > > > + */
    > > > +List *
    > > > +GetIncludedRelationsByPub(Oid pubid, PublicationPartOpt pub_partopt)
    > > >
    > > > This is equivalent to the existing function GetPublicationRelations()
    > > > which has more precise comments atop. We can use the same comments
    > > > unless there is any functionality difference.
    > >
    > > Updated it
    > >
    > > > 4.
    > > > --- a/src/backend/catalog/pg_publication.c
    > > > +++ b/src/backend/catalog/pg_publication.c
    > > > @@ -29,6 +29,7 @@
    > > >  #include "catalog/pg_publication.h"
    > > >  #include "catalog/pg_publication_namespace.h"
    > > >  #include "catalog/pg_publication_rel.h"
    > > > +#include "catalog/pg_subscription.h"
    > > >
    > > > It appears odd to include pg_subscription.h in the publication code.
    > > > Is there a reason for the same? If not then avoid it.
    > >
    > > This was required because we now started using GetPublicationsStr. I
    > > have moved GetPublicationsStr to logical.c from pg_subscription which
    > > is common to both pub and sub.
    > >
    > > > Apart from above, a few cosmetic changes are attached.
    > >
    > > Merged them.
    > >
    > > The attached v47 version patch has the changes for the same.
    > >
    >
    > SIGABRT with normal tables:
    > --------------------------------------
    >
    > ashutosh@test=# select
    > pg_get_publication_effective_tables('t1'::regclass,
    > '{all_pub}'::text[]);
    > server closed the connection unexpectedly
    >         This probably means the server terminated abnormally
    >         before or while processing the request.
    > The connection to the server was lost. Attempting reset: Failed.
    >
    > Since the function is user-facing, I think we should have test coverage for it.
    >
    Hi Ashutosh,
    Thanks for reviewing the patch.
    
    We have fixed this issue.
    This function is not user facing. We have a similar function
    'pg_get_publication_tables'. We do not have documentation and test
    coverage for this function.
    I think 'pg_get_publication_effective_tables' is a similar function,
    so we should not add documentation or tests for it.
    
    We have also addressed the comments in [1]. I have also done minor
    code and comment modifications
    I have also modified the error message as suggested by Shveta in [2].
    Attached the latest v48 patch.
    
    [1]: https://www.postgresql.org/message-id/CAE9k0P=SfawUYBoSmK9Zxwey774OJw5g4Gb7+fA3CQ22j83a7Q@mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAJpy0uAHr8=YUKSyEBs8G4P0zGGb6mcUr-YJdB56tdYMF9yoKA@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  241. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-23T11:16:10Z

    On Mon, Feb 23, 2026 at 11:37 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > I have also modified the error message as suggested by Shveta in [2].
    > Attached the latest v48 patch.
    >
    
    I see that the second patch (0002) brings complexity in the patch to
    deal with following points: (a) The first complexity is if one of the
    partitions is specified then how to compute the initial set of
    relations to copy when pubviaroot is true. This is complex because we
    need to exclude the partitions specified. (b) The other complexity is
    combining Except list containing partitions and other publications
    specifying partitions or partitioned tables both during replication
    and probably during initial sync.
    
    I think it will be better if for the first version, we allow only root
    partitioned table to be specified in the Except Table list. This would
    mean that if the user tries to attach that root partition table to
    another root then we should give an error. If we go via this route, it
    will be important to allow users to remove some tables from the Except
    list, so we can provide Alter Publication <pub_name> Set Except Table
    (table_names).
    
    I think excluding specific partitions may also have some use cases but
    adding additional complexity and maintenance effort is worth, if there
    is a real field demand for such a feature.
    
    Thoughts?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  242. Re: Skipping schema changes in publication

    Ashutosh Sharma <ashu.coek88@gmail.com> — 2026-02-23T11:26:46Z

    Hi,
    
    On Mon, Feb 23, 2026 at 11:37 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Fri, 20 Feb 2026 at 18:17, Ashutosh Sharma <ashu.coek88@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > On Fri, Feb 20, 2026 at 2:08 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > On Thu, 19 Feb 2026 at 16:06, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > > >
    > > > > On Thu, Feb 19, 2026 at 10:13 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > > > >
    > > > > > Thanks for reviewing the patch. I have addressed the remaining
    > > > > > comments in the v46 patch..
    > > > > >
    > > > >
    > > > > Can we think of some ideas to split this patch? One possibility is
    > > > > that in the first patch we give an ERROR, if a non-root partitioned
    > > > > table is present in EXCEPT Clause. I see that a lot of code is written
    > > > > to handle partitions in EXCEPT clause. I feel such a split will make
    > > > > code easier to review and validate.
    > > >
    > > > Split it accordingly.
    > > >
    > > > > Few other comments:
    > > > > =================
    > > > > 1.
    > > > >   if (stmt->for_all_tables)
    > > > >   {
    > > > > + /* Process EXCEPT table list */
    > > > > + if (relations != NIL)
    > > > > + {
    > > > > + Assert(rels != NIL);
    > > > > + PublicationAddTables(puboid, rels, true, NULL);
    > > > > + }
    > > > > +
    > > > >   /*
    > > > >   * Invalidate relcache so that publication info is rebuilt. Sequences
    > > > >   * publication doesn't require invalidation, as replica identity
    > > > > CacheInvalidateRelcacheAll();
    > > > >
    > > > > Here, the relations listed in the except table list will be
    > > > > invalidated twice, once inside
    > > > > PublicationAddTables->publication_add_relation, and second time via
    > > > > CacheInvalidateRelcacheAll. Can we avoid that by adding a parameter to
    > > > > PublicationAddTables?
    > > >
    > > > Fixed this
    > > >
    > > > > 2.
    > > > > - root_relids = GetPublicationRelations(pubform->oid,
    > > > > -   PUBLICATION_PART_ROOT);
    > > > > + root_relids = GetIncludedRelationsByPub(pubform->oid,
    > > > > + PUBLICATION_PART_ROOT);
    > > > >
    > > > > To follow the previous function naming pattern, can we rename
    > > > > GetIncludedRelationsByPub to GetIncludedPublicationRelations? If we
    > > > > agree to this then rename the excluded version of the function as
    > > > > well.
    > > >
    > > > Modified
    > > >
    > > > > 3.
    > > > > +/*
    > > > > + * Return the list of relation Oids for a publication.
    > > > > + *
    > > > > + * For a FOR TABLE publication, this returns the list of relations explicitly
    > > > > + * included in the publication.
    > > > > + *
    > > > > + * Publications declared with FOR ALL TABLES/SEQUENCES should use
    > > > > + * GetAllPublicationRelations() to obtain the complete set of tables/sequences
    > > > > + * covered by the publication.
    > > > > + */
    > > > > +List *
    > > > > +GetIncludedRelationsByPub(Oid pubid, PublicationPartOpt pub_partopt)
    > > > >
    > > > > This is equivalent to the existing function GetPublicationRelations()
    > > > > which has more precise comments atop. We can use the same comments
    > > > > unless there is any functionality difference.
    > > >
    > > > Updated it
    > > >
    > > > > 4.
    > > > > --- a/src/backend/catalog/pg_publication.c
    > > > > +++ b/src/backend/catalog/pg_publication.c
    > > > > @@ -29,6 +29,7 @@
    > > > >  #include "catalog/pg_publication.h"
    > > > >  #include "catalog/pg_publication_namespace.h"
    > > > >  #include "catalog/pg_publication_rel.h"
    > > > > +#include "catalog/pg_subscription.h"
    > > > >
    > > > > It appears odd to include pg_subscription.h in the publication code.
    > > > > Is there a reason for the same? If not then avoid it.
    > > >
    > > > This was required because we now started using GetPublicationsStr. I
    > > > have moved GetPublicationsStr to logical.c from pg_subscription which
    > > > is common to both pub and sub.
    > > >
    > > > > Apart from above, a few cosmetic changes are attached.
    > > >
    > > > Merged them.
    > > >
    > > > The attached v47 version patch has the changes for the same.
    > > >
    > >
    > > SIGABRT with normal tables:
    > > --------------------------------------
    > >
    > > ashutosh@test=# select
    > > pg_get_publication_effective_tables('t1'::regclass,
    > > '{all_pub}'::text[]);
    > > server closed the connection unexpectedly
    > >         This probably means the server terminated abnormally
    > >         before or while processing the request.
    > > The connection to the server was lost. Attempting reset: Failed.
    > >
    > > Since the function is user-facing, I think we should have test coverage for it.
    > >
    > Hi Ashutosh,
    > Thanks for reviewing the patch.
    >
    > We have fixed this issue.
    > This function is not user facing. We have a similar function
    > 'pg_get_publication_tables'. We do not have documentation and test
    > coverage for this function.
    > I think 'pg_get_publication_effective_tables' is a similar function,
    > so we should not add documentation or tests for it.
    >
    > We have also addressed the comments in [1]. I have also done minor
    > code and comment modifications
    > I have also modified the error message as suggested by Shveta in [2].
    > Attached the latest v48 patch.
    >
    > [1]: https://www.postgresql.org/message-id/CAE9k0P=SfawUYBoSmK9Zxwey774OJw5g4Gb7+fA3CQ22j83a7Q@mail.gmail.com
    > [2]: https://www.postgresql.org/message-id/CAJpy0uAHr8=YUKSyEBs8G4P0zGGb6mcUr-YJdB56tdYMF9yoKA@mail.gmail.com
    >
    
    Thanks for the fix. A couple of quick observations:
    
    + list_free_deep(pubnames_with_except);
    + pfree(except_publications);
    
    The memory cleanup is inconsistent - some lists are freed with
    list_free_deep while others use a plain pfree. Could you clarify the
    reasoning, or make this consistent?
    --
    
    + /* walrcv_clear_result(res); */
    + ExecDropSingleTupleTableSlot(slot);
    
    walrcv_clear_result(res) appears to have been commented out — was this
    intentional? If so, what's the reason?
    
    --
    
    There are multiple places (at least 3) where we have multi-pub
    conflict checks to ensure not more than one pub with except clause
    exist, it doesn't seem like we can make them common, but since they
    are somewhat duplicate its possible that when we make changes at one
    place, some other place where the similar change is required gets
    missed. So if possible (and if it makes sense to you) I think it would
    be good to have some kind of comments saying "this is one of N places
    that enforces this constraint, see also X and Y.
    
    --
    With Regards,
    Ashutosh Sharma.
    
    
    
    
  243. RE: Skipping schema changes in publication

    Zhijie Hou (Fujitsu) <houzj.fnst@fujitsu.com> — 2026-02-25T06:33:10Z

    On Monday, February 23, 2026 7:16 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > 
    > On Mon, Feb 23, 2026 at 11:37 AM Shlok Kyal <shlok.kyal.oss@gmail.com>
    > wrote:
    > >
    > > I have also modified the error message as suggested by Shveta in [2].
    > > Attached the latest v48 patch.
    > >
    > 
    > I see that the second patch (0002) brings complexity in the patch to
    > deal with following points: (a) The first complexity is if one of the
    > partitions is specified then how to compute the initial set of
    > relations to copy when pubviaroot is true. This is complex because we
    > need to exclude the partitions specified. (b) The other complexity is
    > combining Except list containing partitions and other publications
    > specifying partitions or partitioned tables both during replication
    > and probably during initial sync.
    > 
    > I think it will be better if for the first version, we allow only root
    > partitioned table to be specified in the Except Table list. This would
    > mean that if the user tries to attach that root partition table to
    > another root then we should give an error. If we go via this route, it
    > will be important to allow users to remove some tables from the Except
    > list, so we can provide Alter Publication <pub_name> Set Except Table
    > (table_names).
    > 
    > I think excluding specific partitions may also have some use cases but
    > adding additional complexity and maintenance effort is worth, if there
    > is a real field demand for such a feature.
    
    I agree that supporting exclusion of a partition while publishing its
    parent would add significant complexity and could be achieved independently.
    
    Another perspective I saw this is that the ability to exclude only a specific
    partition from a partition tree while continuing to publish changes via the root
    table introduces an entirely new mode that users couldn't achieve before this
    patch. Previously, once a parent table was added to a publication, all its
    partitions were published regardless of their schema, so users cannot exclude a
    partition either via FOR TABLE or FOR TABLES IN SCHEMA. Therefore, disallowing
    partition exclusion doesn't remove any existing capability, which seems
    acceptable.
    
    The original use case in this thread also doesn't mention partitioned tables, so
    I think EXCEPT TABLE provides value even without implementing this new ability.
    Given the amount of code required to support partition exclusion, it makes sense
    to me to treat this as an independent feature that can be pursued later based
    on user feedback.
    
    Best Regards,
    Hou zj
    
  244. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-25T07:04:41Z

    On Mon, 23 Feb 2026 at 16:46, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Mon, Feb 23, 2026 at 11:37 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > I have also modified the error message as suggested by Shveta in [2].
    > > Attached the latest v48 patch.
    > >
    >
    > I see that the second patch (0002) brings complexity in the patch to
    > deal with following points: (a) The first complexity is if one of the
    > partitions is specified then how to compute the initial set of
    > relations to copy when pubviaroot is true. This is complex because we
    > need to exclude the partitions specified. (b) The other complexity is
    > combining Except list containing partitions and other publications
    > specifying partitions or partitioned tables both during replication
    > and probably during initial sync.
    >
    > I think it will be better if for the first version, we allow only root
    > partitioned table to be specified in the Except Table list. This would
    > mean that if the user tries to attach that root partition table to
    > another root then we should give an error. If we go via this route, it
    > will be important to allow users to remove some tables from the Except
    > list, so we can provide Alter Publication <pub_name> Set Except Table
    > (table_names).
    
    The attached patch has the changes for the same i.e.a) Raises an error
    when attempting to attach a partition to a root partitioned table if
    that table is referenced in an EXCEPT clause of any publication. b)
    Adds support for dropping excluded tables using: ALTER PUBLICATION ...
    DROP EXCEPT TABLE. c) Adds support for replacing the exclusion list
    using  ALTER PUBLICATION ... SET EXCEPT TABLE.
    The changes related to DROP EXCEPT TABLE and SET EXCEPT TABLE have
    been kept separately into patch 0002 for easier review.
    
    The comments from [1] are also addressed in the attached patch.
    [1] - https://www.postgresql.org/message-id/CAA4eK1KESu4%3DW6j4CQkKv5nzNJgtJyYBsg3E5K%2BLcwOr3t0WKw%40mail.gmail.com
    
    Regards,
    Vignesh
    
  245. Re: Skipping schema changes in publication

    Chao Li <li.evan.chao@gmail.com> — 2026-02-25T08:22:37Z

    
    > On Feb 25, 2026, at 15:04, vignesh C <vignesh21@gmail.com> wrote:
    > 
    > On Mon, 23 Feb 2026 at 16:46, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >> 
    >> On Mon, Feb 23, 2026 at 11:37 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >>> 
    >>> I have also modified the error message as suggested by Shveta in [2].
    >>> Attached the latest v48 patch.
    >>> 
    >> 
    >> I see that the second patch (0002) brings complexity in the patch to
    >> deal with following points: (a) The first complexity is if one of the
    >> partitions is specified then how to compute the initial set of
    >> relations to copy when pubviaroot is true. This is complex because we
    >> need to exclude the partitions specified. (b) The other complexity is
    >> combining Except list containing partitions and other publications
    >> specifying partitions or partitioned tables both during replication
    >> and probably during initial sync.
    >> 
    >> I think it will be better if for the first version, we allow only root
    >> partitioned table to be specified in the Except Table list. This would
    >> mean that if the user tries to attach that root partition table to
    >> another root then we should give an error. If we go via this route, it
    >> will be important to allow users to remove some tables from the Except
    >> list, so we can provide Alter Publication <pub_name> Set Except Table
    >> (table_names).
    > 
    > The attached patch has the changes for the same i.e.a) Raises an error
    > when attempting to attach a partition to a root partitioned table if
    > that table is referenced in an EXCEPT clause of any publication. b)
    > Adds support for dropping excluded tables using: ALTER PUBLICATION ...
    > DROP EXCEPT TABLE. c) Adds support for replacing the exclusion list
    > using  ALTER PUBLICATION ... SET EXCEPT TABLE.
    > The changes related to DROP EXCEPT TABLE and SET EXCEPT TABLE have
    > been kept separately into patch 0002 for easier review.
    > 
    > The comments from [1] are also addressed in the attached patch.
    > [1] - https://www.postgresql.org/message-id/CAA4eK1KESu4%3DW6j4CQkKv5nzNJgtJyYBsg3E5K%2BLcwOr3t0WKw%40mail.gmail.com
    > 
    > Regards,
    > Vignesh
    > <v49-0001-Skip-publishing-the-tables-specified-in-EXCEPT-T.patch><v49-0002-Support-DROP-and-SET-EXCEPT-TABLE-in-ALTER-PUBLI.patch>
    
    I quickly went through the code change and haven’t actually played the new feature yet. A few small comments:
    
    1 - 0001 - subscriptioncmds.c
    ```
    +	GetPublicationsStr(except_publications, &pubnames, false);
    +
    +	pfree(except_publications);
    ```
    
    except_publications is a list, so we should use list_free(except_publications) instead of pfree().
    
    2 - 0002
    ```
    +   will be removed.  The <literal>ADD</literal> clauses will add one or more
    +   tables/schemas from the publication.  The <literal>DROP</literal> clauses
    ```
    
    I guess “from the publication” should be “to the publication”.
    
    Best regards,
    --
    Chao Li (Evan)
    HighGo Software Co., Ltd.
    https://www.highgo.com/
    
    
    
    
    
    
    
    
  246. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-25T10:31:34Z

    On Mon, Feb 23, 2026 at 4:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Mon, Feb 23, 2026 at 11:37 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > I have also modified the error message as suggested by Shveta in [2].
    > > Attached the latest v48 patch.
    > >
    >
    > I see that the second patch (0002) brings complexity in the patch to
    > deal with following points: (a) The first complexity is if one of the
    > partitions is specified then how to compute the initial set of
    > relations to copy when pubviaroot is true. This is complex because we
    > need to exclude the partitions specified. (b) The other complexity is
    > combining Except list containing partitions and other publications
    > specifying partitions or partitioned tables both during replication
    > and probably during initial sync.
    >
    > I think it will be better if for the first version, we allow only root
    > partitioned table to be specified in the Except Table list. This would
    > mean that if the user tries to attach that root partition table to
    > another root then we should give an error. If we go via this route, it
    > will be important to allow users to remove some tables from the Except
    > list, so we can provide Alter Publication <pub_name> Set Except Table
    > (table_names).
    >
    > I think excluding specific partitions may also have some use cases but
    > adding additional complexity and maintenance effort is worth, if there
    > is a real field demand for such a feature.
    >
    > Thoughts?
    >
    
    I fully agree. The additional complexity does not appear to be worth
    the value right now, unless we receive meaningful user feedback that
    justifies implementing it.
    
    Thanks Vignesh for the patch supporting this approach. Please find a
    few comments on v49-001:
    
    1)
    +      in <literal>EXCEPT TABLE</literal>. Doing so excludes the root table and
    +      all of its partitions from replication, regardless of the value of
    +      <literal>publish_via_partition_root</literal>. The optional
    +      <literal>*</literal> has no effect for partitioned tables.
    +     </para>
    
    a)
    I feel it is not needed to mention 'regardless of
    publish_via_partition_root'  as generally that never decides what is
    included and what is not. Same is the case here.
    
    b)
    We shall mention both ONLY and *. Infact it is 'ONLY' which has no
    effect. * is the default in this case. But still to make it a more
    generic statement, we shall mention both for partitioned tables.
    
    2)
    +  <para>
    +   Create a publication that publishes all sequences for synchronization, and
    +   all changes in all tables except <structname>users</structname> and
    +   <structname>departments</structname>:
    +<programlisting>
    +CREATE PUBLICATION all_sequences_tables_except FOR ALL SEQUENCES, ALL
    TABLES EXCEPT (users, departments);
    
    If I run the command given in example, it asserts.
    
    TRAP: failed Assert("!stmt->for_all_sequences"), File:
    "publicationcmds.c", Line: 942, PID: 70620
    postgres: shveta postgres [local] CREATE
    PUBLICATION(ExceptionalCondition+0xbb)[0x611280ed8c48]
    
    3)
    -check_publication_add_relation(Relation targetrel)
    +check_publication_add_relation(Relation targetrel, PublicationRelInfo *pri)
    
    We can avoid passing 'targetrel' now. PublicationRelInfo has that info inside.
    
    4)
    postgres=# create publication pub2 for all tables except (tab_part_1);
    ERROR:  partition "tab_part_1" cannot be excluded using EXCEPT TABLE
    
    Other errors are in active voice (eg: cannot use system column \"%s\"
    in publication column list, cannot use publication EXCEPT clause for
    relation \"%s\).
    
    Shall we also keep this the same way? I think we can mimic the error
    for tmp and unlogged tables which is:
    
    postgres=# create publication pub2 for all tables except (tmp1);
    ERROR:  cannot use publication EXCEPT clause for relation "tmp1"
    DETAIL:  This operation is not supported for temporary tables.
    
    We can convert concerned one to:
    
    postgres=# create publication pub2 for all tables except (tab_part_1);
    ERROR: cannot use publication EXCEPT clause for partition "tab_part_1"
    DETAIL:  This operation is not supported for individual partitions.
    
    
    5)
    + if (pub->alltables && pri->except && targetrel->rd_rel->relispartition)
    + ereport(ERROR,
    + errmsg("partition \"%s\" cannot be excluded using EXCEPT TABLE",
    +    RelationGetRelationName(targetrel)));
    +
    + check_publication_add_relation(targetrel, pri);
    
    I feel we can push the partition check and error to
    check_publication_add_relation() itself. If we do so, we can retain
    the exactly same errormsg for parition case as well which is:
     errormsg = gettext_noop("cannot use publication EXCEPT clause for
    relation \"%s\"");
    
    And we can just change DETAIL as suggested in pt 4.
    
    6)
    +++ b/src/backend/catalog/pg_subscription.c
    -/*
    - * Add a comma-separated list of publication names to the 'dest' string.
    - */
    -void
    -GetPublicationsStr(List *publications, StringInfo dest, bool quote_literal)
    -{
    
    Do we still need to move GetPublicationsStr() to logical.c? I see the
    only extra call is now in pgoutput.c which already includes
    catalog/pg_subscription.h
    ~~
    
    Reviewing further.
    
    thanks
    Shveta
    
    
    
    
  247. Re: Skipping schema changes in publication

    Andrei Lepikhov <lepihov@gmail.com> — 2026-02-25T13:39:18Z

    On 25/2/26 08:04, vignesh C wrote:
    > On Mon, 23 Feb 2026 at 16:46, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > The attached patch has the changes for the same i.e.a) Raises an error
    > when attempting to attach a partition to a root partitioned table if
    > that table is referenced in an EXCEPT clause of any publication. b)
    > Adds support for dropping excluded tables using: ALTER PUBLICATION ...
    > DROP EXCEPT TABLE. c) Adds support for replacing the exclusion list
    > using  ALTER PUBLICATION ... SET EXCEPT TABLE.
    > The changes related to DROP EXCEPT TABLE and SET EXCEPT TABLE have
    > been kept separately into patch 0002 for easier review.
    
    I discovered this patch, maybe not deeply enough. But one question raised.
    
    I usually work with multiple tables (sometimes hundreds, if not
    thousands). EXCEPT clause might be quite rare. Some commands want to
    extract only excepted tables from the publication using the following
    pattern:
    
    'SELECT ... FROM pg_publication_rel WHERE prpubid = <X> AND pr.prexcept'
    
    - check_publications_except_list()
    - describePublications()
    - getPublications()
    - LoadPublications() / publication_has_any_except_table()
    
    That means pass through all the publication tables to find (usually)
    nothing. It might be a costly operation, especially in the
    LoadPublications routine. Maybe slightly change index
    pg_publication_rel_prpubid_index a little and add 'prexcept' as a second
    column there? It might avoid performance issues that may arise during
    the upgrade.
    
    
    --
    regards, Andrei Lepikhov,
    pgEdge
    
    
    
    
  248. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-02-25T17:13:10Z

    On Wed, 25 Feb 2026 at 16:01, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Mon, Feb 23, 2026 at 4:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Mon, Feb 23, 2026 at 11:37 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > I have also modified the error message as suggested by Shveta in [2].
    > > > Attached the latest v48 patch.
    > > >
    > >
    > > I see that the second patch (0002) brings complexity in the patch to
    > > deal with following points: (a) The first complexity is if one of the
    > > partitions is specified then how to compute the initial set of
    > > relations to copy when pubviaroot is true. This is complex because we
    > > need to exclude the partitions specified. (b) The other complexity is
    > > combining Except list containing partitions and other publications
    > > specifying partitions or partitioned tables both during replication
    > > and probably during initial sync.
    > >
    > > I think it will be better if for the first version, we allow only root
    > > partitioned table to be specified in the Except Table list. This would
    > > mean that if the user tries to attach that root partition table to
    > > another root then we should give an error. If we go via this route, it
    > > will be important to allow users to remove some tables from the Except
    > > list, so we can provide Alter Publication <pub_name> Set Except Table
    > > (table_names).
    > >
    > > I think excluding specific partitions may also have some use cases but
    > > adding additional complexity and maintenance effort is worth, if there
    > > is a real field demand for such a feature.
    > >
    > > Thoughts?
    > >
    >
    > I fully agree. The additional complexity does not appear to be worth
    > the value right now, unless we receive meaningful user feedback that
    > justifies implementing it.
    >
    > Thanks Vignesh for the patch supporting this approach. Please find a
    > few comments on v49-001:
    >
    > 1)
    > +      in <literal>EXCEPT TABLE</literal>. Doing so excludes the root table and
    > +      all of its partitions from replication, regardless of the value of
    > +      <literal>publish_via_partition_root</literal>. The optional
    > +      <literal>*</literal> has no effect for partitioned tables.
    > +     </para>
    >
    > a)
    > I feel it is not needed to mention 'regardless of
    > publish_via_partition_root'  as generally that never decides what is
    > included and what is not. Same is the case here.
    >
    > b)
    > We shall mention both ONLY and *. Infact it is 'ONLY' which has no
    > effect. * is the default in this case. But still to make it a more
    > generic statement, we shall mention both for partitioned tables.
    >
    > 2)
    > +  <para>
    > +   Create a publication that publishes all sequences for synchronization, and
    > +   all changes in all tables except <structname>users</structname> and
    > +   <structname>departments</structname>:
    > +<programlisting>
    > +CREATE PUBLICATION all_sequences_tables_except FOR ALL SEQUENCES, ALL
    > TABLES EXCEPT (users, departments);
    >
    > If I run the command given in example, it asserts.
    >
    > TRAP: failed Assert("!stmt->for_all_sequences"), File:
    > "publicationcmds.c", Line: 942, PID: 70620
    > postgres: shveta postgres [local] CREATE
    > PUBLICATION(ExceptionalCondition+0xbb)[0x611280ed8c48]
    >
    > 3)
    > -check_publication_add_relation(Relation targetrel)
    > +check_publication_add_relation(Relation targetrel, PublicationRelInfo *pri)
    >
    > We can avoid passing 'targetrel' now. PublicationRelInfo has that info inside.
    >
    > 4)
    > postgres=# create publication pub2 for all tables except (tab_part_1);
    > ERROR:  partition "tab_part_1" cannot be excluded using EXCEPT TABLE
    >
    > Other errors are in active voice (eg: cannot use system column \"%s\"
    > in publication column list, cannot use publication EXCEPT clause for
    > relation \"%s\).
    >
    > Shall we also keep this the same way? I think we can mimic the error
    > for tmp and unlogged tables which is:
    >
    > postgres=# create publication pub2 for all tables except (tmp1);
    > ERROR:  cannot use publication EXCEPT clause for relation "tmp1"
    > DETAIL:  This operation is not supported for temporary tables.
    >
    > We can convert concerned one to:
    >
    > postgres=# create publication pub2 for all tables except (tab_part_1);
    > ERROR: cannot use publication EXCEPT clause for partition "tab_part_1"
    > DETAIL:  This operation is not supported for individual partitions.
    >
    >
    > 5)
    > + if (pub->alltables && pri->except && targetrel->rd_rel->relispartition)
    > + ereport(ERROR,
    > + errmsg("partition \"%s\" cannot be excluded using EXCEPT TABLE",
    > +    RelationGetRelationName(targetrel)));
    > +
    > + check_publication_add_relation(targetrel, pri);
    >
    > I feel we can push the partition check and error to
    > check_publication_add_relation() itself. If we do so, we can retain
    > the exactly same errormsg for parition case as well which is:
    >  errormsg = gettext_noop("cannot use publication EXCEPT clause for
    > relation \"%s\"");
    >
    > And we can just change DETAIL as suggested in pt 4.
    >
    > 6)
    > +++ b/src/backend/catalog/pg_subscription.c
    > -/*
    > - * Add a comma-separated list of publication names to the 'dest' string.
    > - */
    > -void
    > -GetPublicationsStr(List *publications, StringInfo dest, bool quote_literal)
    > -{
    >
    > Do we still need to move GetPublicationsStr() to logical.c? I see the
    > only extra call is now in pgoutput.c which already includes
    > catalog/pg_subscription.h
    > ~~
    Hi Shveta, Ashutosh, Chao-san,
    
    Thanks for reviewing the patch.
    I have addressed the above comments and the comments in [1] and [2].
    Attached is the latest v50 version.
    
    I have also split the patch 0002 to 0002 and 0003
    0001 - Support CREATE PUBLICATION ... EXCEPT TABLE syntax
    0002 - Support ALTER PUBLICATION ... SET EXCEPT TABLE .. syntax
    0003 - Support ALTER PUBLICATION ... DROP EXCEPT TABLE .. syntax
    
    [1]: https://www.postgresql.org/message-id/CAE9k0P=wzzOXykzgj-7Dw7E9r8k0PX633RY3TEVDpwLue004tg@mail.gmail.com
    [2]: https://www.postgresql.org/message-id/0DDF38E7-3033-47E1-98C1-49A1378526B3@gmail.com
    
    Thanks,
    Shlok Kyal
    
  249. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-26T02:56:38Z

    On Wed, Feb 25, 2026 at 7:09 PM Andrei Lepikhov <lepihov@gmail.com> wrote:
    >
    > On 25/2/26 08:04, vignesh C wrote:
    > > On Mon, 23 Feb 2026 at 16:46, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > The attached patch has the changes for the same i.e.a) Raises an error
    > > when attempting to attach a partition to a root partitioned table if
    > > that table is referenced in an EXCEPT clause of any publication. b)
    > > Adds support for dropping excluded tables using: ALTER PUBLICATION ...
    > > DROP EXCEPT TABLE. c) Adds support for replacing the exclusion list
    > > using  ALTER PUBLICATION ... SET EXCEPT TABLE.
    > > The changes related to DROP EXCEPT TABLE and SET EXCEPT TABLE have
    > > been kept separately into patch 0002 for easier review.
    >
    > I discovered this patch, maybe not deeply enough. But one question raised.
    >
    > I usually work with multiple tables (sometimes hundreds, if not
    > thousands). EXCEPT clause might be quite rare.
    >
    
    I think it will be useful for users using ALL TABLES IN SCHEMA and ALL
    TABLES publications where they don't want to replicate the entire
    schema or database.
    
    > Some commands want to
    > extract only excepted tables from the publication using the following
    > pattern:
    >
    > 'SELECT ... FROM pg_publication_rel WHERE prpubid = <X> AND pr.prexcept'
    >
    > - check_publications_except_list()
    > - describePublications()
    > - getPublications()
    > - LoadPublications() / publication_has_any_except_table()
    >
    > That means pass through all the publication tables to find (usually)
    > nothing. It might be a costly operation, especially in the
    > LoadPublications routine. Maybe slightly change index
    > pg_publication_rel_prpubid_index a little and add 'prexcept' as a second
    > column there? It might avoid performance issues that may arise during
    > the upgrade.
    >
    
    Why do you think it can hurt when used via LoadPublications? That is
    done only one time when the first relsync entry is built, after that
    it is reused unless there is some DDL on the publication. Also, we are
    not expecting thousands of tables in the except list for a
    publication. So can traversing that few entries and that too first
    time when cache is built can impact performance in a noticeable way?
    If you think so, we can modify the index as you are suggesting but I
    was thinking if the number of entries is not large per-publication
    then why to permanently increase the size of the index.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  250. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-02-26T04:14:47Z

    On Wed, Feb 25, 2026 at 4:01 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Mon, Feb 23, 2026 at 4:46 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Mon, Feb 23, 2026 at 11:37 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > I have also modified the error message as suggested by Shveta in [2].
    > > > Attached the latest v48 patch.
    > > >
    > >
    > > I see that the second patch (0002) brings complexity in the patch to
    > > deal with following points: (a) The first complexity is if one of the
    > > partitions is specified then how to compute the initial set of
    > > relations to copy when pubviaroot is true. This is complex because we
    > > need to exclude the partitions specified. (b) The other complexity is
    > > combining Except list containing partitions and other publications
    > > specifying partitions or partitioned tables both during replication
    > > and probably during initial sync.
    > >
    > > I think it will be better if for the first version, we allow only root
    > > partitioned table to be specified in the Except Table list. This would
    > > mean that if the user tries to attach that root partition table to
    > > another root then we should give an error. If we go via this route, it
    > > will be important to allow users to remove some tables from the Except
    > > list, so we can provide Alter Publication <pub_name> Set Except Table
    > > (table_names).
    > >
    > > I think excluding specific partitions may also have some use cases but
    > > adding additional complexity and maintenance effort is worth, if there
    > > is a real field demand for such a feature.
    > >
    > > Thoughts?
    > >
    >
    > I fully agree. The additional complexity does not appear to be worth
    > the value right now, unless we receive meaningful user feedback that
    > justifies implementing it.
    >
    
    I’m on the same page. The benefit probably doesn't justify the added
    complexity and the long-term code maintenance overhead this would
    introduce.
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  251. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-26T04:56:46Z

    On Thu, Feb 26, 2026 at 8:26 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, Feb 25, 2026 at 7:09 PM Andrei Lepikhov <lepihov@gmail.com> wrote:
    > >
    > > On 25/2/26 08:04, vignesh C wrote:
    > > > On Mon, 23 Feb 2026 at 16:46, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > > The attached patch has the changes for the same i.e.a) Raises an error
    > > > when attempting to attach a partition to a root partitioned table if
    > > > that table is referenced in an EXCEPT clause of any publication. b)
    > > > Adds support for dropping excluded tables using: ALTER PUBLICATION ...
    > > > DROP EXCEPT TABLE. c) Adds support for replacing the exclusion list
    > > > using  ALTER PUBLICATION ... SET EXCEPT TABLE.
    > > > The changes related to DROP EXCEPT TABLE and SET EXCEPT TABLE have
    > > > been kept separately into patch 0002 for easier review.
    > >
    > > I discovered this patch, maybe not deeply enough. But one question raised.
    > >
    > > I usually work with multiple tables (sometimes hundreds, if not
    > > thousands). EXCEPT clause might be quite rare.
    > >
    >
    > I think it will be useful for users using ALL TABLES IN SCHEMA and ALL
    > TABLES publications where they don't want to replicate the entire
    > schema or database.
    >
    > > Some commands want to
    > > extract only excepted tables from the publication using the following
    > > pattern:
    > >
    > > 'SELECT ... FROM pg_publication_rel WHERE prpubid = <X> AND pr.prexcept'
    > >
    
    I agree with Amit's point. I would like to add one more point here.
    
    pg_publication_rel will not contain any regular table entries for a
    given pubid when EXCEPT is used. Except is for ALL-TABLES pub alone
    for which base-table entries are stored in pg_publication_tables. Thus
    for an EXCEPT pub, anything we fetch from pg_publication_rel using the
    pubid index
    would necessarily be an EXCEPT entry. The scenario where we retrieve,
    say, 1000 table entries for a pubid and only 1–2 of them are EXCEPT
    entries will not occur. Given this, the need for a composite index on
    (prpubid, prexcept) does not appear to be strongly justified to me.
    Please ignore my comment if you are already aware of this situation.
    
    thanks
    Shveta
    
    
    
    
  252. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-26T06:08:42Z

    On Thu, Feb 26, 2026 at 10:26 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Thu, Feb 26, 2026 at 8:26 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Wed, Feb 25, 2026 at 7:09 PM Andrei Lepikhov <lepihov@gmail.com> wrote:
    > > >
    > > > On 25/2/26 08:04, vignesh C wrote:
    > > > > On Mon, 23 Feb 2026 at 16:46, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > > > The attached patch has the changes for the same i.e.a) Raises an error
    > > > > when attempting to attach a partition to a root partitioned table if
    > > > > that table is referenced in an EXCEPT clause of any publication. b)
    > > > > Adds support for dropping excluded tables using: ALTER PUBLICATION ...
    > > > > DROP EXCEPT TABLE. c) Adds support for replacing the exclusion list
    > > > > using  ALTER PUBLICATION ... SET EXCEPT TABLE.
    > > > > The changes related to DROP EXCEPT TABLE and SET EXCEPT TABLE have
    > > > > been kept separately into patch 0002 for easier review.
    > > >
    > > > I discovered this patch, maybe not deeply enough. But one question raised.
    > > >
    > > > I usually work with multiple tables (sometimes hundreds, if not
    > > > thousands). EXCEPT clause might be quite rare.
    > > >
    > >
    > > I think it will be useful for users using ALL TABLES IN SCHEMA and ALL
    > > TABLES publications where they don't want to replicate the entire
    > > schema or database.
    > >
    > > > Some commands want to
    > > > extract only excepted tables from the publication using the following
    > > > pattern:
    > > >
    > > > 'SELECT ... FROM pg_publication_rel WHERE prpubid = <X> AND pr.prexcept'
    > > >
    >
    > I agree with Amit's point. I would like to add one more point here.
    >
    > pg_publication_rel will not contain any regular table entries for a
    > given pubid when EXCEPT is used. Except is for ALL-TABLES pub alone
    > for which base-table entries are stored in pg_publication_tables.
    
    One correction here:  Except is for ALL-TABLES pub alone for which
    base-table entries are "fetched" from pg_publication_tables view
    instead of catalog pg_publication_rel.
    
    > Thus
    > for an EXCEPT pub, anything we fetch from pg_publication_rel using the
    > pubid index
    > would necessarily be an EXCEPT entry. The scenario where we retrieve,
    > say, 1000 table entries for a pubid and only 1–2 of them are EXCEPT
    > entries will not occur. Given this, the need for a composite index on
    > (prpubid, prexcept) does not appear to be strongly justified to me.
    > Please ignore my comment if you are already aware of this situation.
    >
    > thanks
    > Shveta
    
    
    
    
  253. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-26T06:12:01Z

    On Wed, Feb 25, 2026 at 10:43 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > Hi Shveta, Ashutosh, Chao-san,
    >
    > Thanks for reviewing the patch.
    > I have addressed the above comments and the comments in [1] and [2].
    > Attached is the latest v50 version.
    >
    
    Thank You Shlok. Please find a few comments on v50-001.
    
    1)
    -check_publication_add_relation(Relation targetrel)
    +check_publication_add_relation(PublicationRelInfo *pri, bool puballtables)
    
    Why do we need argument  puballtables? I think we can give partition
    related error even without checking 'puballtables', like we are giving
    for temp and unlogged table error in EXCEPT list without checking
    puballtables. Or let me know if I am missing the point here?
    
    2)
    publication_has_any_except_table(): Shall we optimize it:
    a) if it is not all-table pub, return false there itself.
    b) if it is all table, proceed with fetching tuples. At first tuple we
    can break the loop irrespective of check 'pubrel->prexcept', as there
    is no other feasibility. But we shall Assert (pubrel->prexcept) .
    Thoughts?
    
    3)
    + /* Process EXCEPT table list */
    + if (relations != NIL)
    + {
    + Assert(rels != NIL);
    + PublicationAddTables(puboid, rels, true, NULL);
    + }
    
    We can remove Assert from here too as we don't have it in the later
    part of code where we use rels. I feel it is okay to not have Assert
    as we are fetching rels in nearby logic only.
    
    4)
    CheckPublicationsForExceptClauses() frees except_publications list
    only in case where it has to emit ERROR. It does not free it for a
    case where there is only one element in it. Also one of the callers
    free the list while another does not. Is it by design? Shall we make
    the behaviour more consistent for callers?
    
    5)
    postgres=# ALTER TABLE tab_top_root ATTACH PARTITION tab_root FOR
    VALUES FROM (0) TO (2000);
    ERROR:  cannot attach table "tab_root" as partition because it is
    referenced in a publication EXCEPT clause
    DETAIL:  Tables excluded from publications cannot be attached as partitions.
    HINT:  Remove the table from the publication EXCEPT list before attaching it.
    
    a) It will be good to list pubnames here for which we are referring EXCEPT list.
    
    b) Also I feel, instead of emphasising on 'cannot attach table as
    partition' in DETAIL, we should emphasise on 'partition cannot be part
    of EXCEPT'. How about?
    DETAIL: The publication EXCEPT clause cannot contain tables that are partitions.
    
    6)
    I see a few error-messages mentioning EXCEPT 'clause' while others
    mention EXCEPT 'list'. We can make the wording the same throughout.
    
    7)
    + /*
    + * Check that the table is not part of any publication when changing to
    + * UNLOGGED, as UNLOGGED tables can't be published.
    + */
    + GetRelationPublications(RelationGetRelid(attachrel), NULL, &exceptpuboids);
    + if (exceptpuboids != NIL)
    + ereport(ERROR,
    + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("cannot attach table \"%s\" as partition because it is
    referenced in a publication EXCEPT clause",
    
    Comments need to be corrected. Copy/paste issue.
    
    8)
    tablesync.c:
    +#include "replication/logical.h"
    
    This inclusion is not needed now.
    
    Reviewing further.
    
    thanks
    Shveta
    
    
    
    
  254. Re: Skipping schema changes in publication

    Andrei Lepikhov <lepihov@gmail.com> — 2026-02-26T09:38:33Z

    On 26/2/26 05:56, shveta malik wrote:
    > On Thu, Feb 26, 2026 at 8:26 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >> On Wed, Feb 25, 2026 at 7:09 PM Andrei Lepikhov <lepihov@gmail.com> wrote:
    > pg_publication_rel will not contain any regular table entries for a
    > given pubid when EXCEPT is used. Except is for ALL-TABLES pub alone
    > for which base-table entries are stored in pg_publication_tables. Thus
    > for an EXCEPT pub, anything we fetch from pg_publication_rel using the
    > pubid index
    > would necessarily be an EXCEPT entry. The scenario where we retrieve,
    > say, 1000 table entries for a pubid and only 1–2 of them are EXCEPT
    > entries will not occur. Given this, the need for a composite index on
    > (prpubid, prexcept) does not appear to be strongly justified to me.
    > Please ignore my comment if you are already aware of this situation.
    Thanks, I missed the fact that pg_publication_rel doesn't contain 
    records for each table in the ALL-TABLES case. Case solved.
    
    -- 
    regards, Andrei Lepikhov,
    pgEdge
    
    
    
    
  255. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-02-26T09:52:41Z

    On Thu, Feb 26, 2026 at 11:42 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Feb 25, 2026 at 10:43 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > Hi Shveta, Ashutosh, Chao-san,
    > >
    > > Thanks for reviewing the patch.
    > > I have addressed the above comments and the comments in [1] and [2].
    > > Attached is the latest v50 version.
    > >
    >
    > Thank You Shlok. Please find a few comments on v50-001.
    >
    > 1)
    > -check_publication_add_relation(Relation targetrel)
    > +check_publication_add_relation(PublicationRelInfo *pri, bool puballtables)
    >
    > Why do we need argument  puballtables? I think we can give partition
    > related error even without checking 'puballtables', like we are giving
    > for temp and unlogged table error in EXCEPT list without checking
    > puballtables. Or let me know if I am missing the point here?
    >
    > 2)
    > publication_has_any_except_table(): Shall we optimize it:
    > a) if it is not all-table pub, return false there itself.
    > b) if it is all table, proceed with fetching tuples. At first tuple we
    > can break the loop irrespective of check 'pubrel->prexcept', as there
    > is no other feasibility. But we shall Assert (pubrel->prexcept) .
    > Thoughts?
    >
    > 3)
    > + /* Process EXCEPT table list */
    > + if (relations != NIL)
    > + {
    > + Assert(rels != NIL);
    > + PublicationAddTables(puboid, rels, true, NULL);
    > + }
    >
    > We can remove Assert from here too as we don't have it in the later
    > part of code where we use rels. I feel it is okay to not have Assert
    > as we are fetching rels in nearby logic only.
    >
    > 4)
    > CheckPublicationsForExceptClauses() frees except_publications list
    > only in case where it has to emit ERROR. It does not free it for a
    > case where there is only one element in it. Also one of the callers
    > free the list while another does not. Is it by design? Shall we make
    > the behaviour more consistent for callers?
    >
    > 5)
    > postgres=# ALTER TABLE tab_top_root ATTACH PARTITION tab_root FOR
    > VALUES FROM (0) TO (2000);
    > ERROR:  cannot attach table "tab_root" as partition because it is
    > referenced in a publication EXCEPT clause
    > DETAIL:  Tables excluded from publications cannot be attached as partitions.
    > HINT:  Remove the table from the publication EXCEPT list before attaching it.
    >
    > a) It will be good to list pubnames here for which we are referring EXCEPT list.
    >
    > b) Also I feel, instead of emphasising on 'cannot attach table as
    > partition' in DETAIL, we should emphasise on 'partition cannot be part
    > of EXCEPT'. How about?
    > DETAIL: The publication EXCEPT clause cannot contain tables that are partitions.
    >
    > 6)
    > I see a few error-messages mentioning EXCEPT 'clause' while others
    > mention EXCEPT 'list'. We can make the wording the same throughout.
    >
    > 7)
    > + /*
    > + * Check that the table is not part of any publication when changing to
    > + * UNLOGGED, as UNLOGGED tables can't be published.
    > + */
    > + GetRelationPublications(RelationGetRelid(attachrel), NULL, &exceptpuboids);
    > + if (exceptpuboids != NIL)
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("cannot attach table \"%s\" as partition because it is
    > referenced in a publication EXCEPT clause",
    >
    > Comments need to be corrected. Copy/paste issue.
    >
    > 8)
    > tablesync.c:
    > +#include "replication/logical.h"
    >
    > This inclusion is not needed now.
    >
    > Reviewing further.
    >
    
    Few more comments on v50-001:
    
    9)
    + *   pub1:
    + *     FOR ALL TABLES EXCEPT (part1, part2)
    + *       WITH (publish_via_partition_root = true)
    
    This example atop check_publications_except_list() needs to be changed
    as now we can not specify partitions in EXCEPT.
    
    10)
    In get_rel_sync_entry(), for the code under:
    /*
    * If this is a FOR ALL TABLES publication, pick the partition
    * root and set the ancestor level accordingly.
    */
    
    Earlier when 'if (pub->alltables)' was true, 'publish=true' was set
    unconditionally, that means it will never go to the subsequent 'if
    (!publish)' block. I think that subsequent block was implicitly meant
    for a pub which is not for alltables. But now it will go to that block
    even for all-tables if we set publish as false because ROOT was
    excluded. This unnecessary work should be avoided. A pub can either be
    ALL-TABLE (with or without EXCEPT) or schema-pub at a time, but not
    both.
    
    11)
    get_rel_sync_entry():
    
    + GetRelationPublications(pub_relid, NULL, &exceptpubids);
    +
    + if (!list_member_oid(exceptpubids, pub->oid))
    + publish = true;
    +
    + list_free(exceptpubids);
    
    We should add a brief comment as to why we are checking only the last
    ancestor in exclude-list. How about this:
    
    Check exclusion of top-most ancestor. Currently, only the top-most
    ancestor can be added to the EXCEPT list, which means a partition will
    not be published if its top-most ancestor is excluded.
    
    <please rephrase if needed>
    
    12) pg_dump dumped "ONLY" along with ROOT name in EXCEPT. I think it
    should be avoided.
    pg_dump output:
    CREATE PUBLICATION pub4 FOR ALL TABLES EXCEPT TABLE (ONLY
    public.tab_root) WITH (publish = 'insert, update, delete, truncate');
    
    13)
    pg_dump did not dump EXCEPT list for combination of allseq, all tables pub.
    
    original:
    CREATE PUBLICATION pub0 FOR ALL SEQUENCES, ALL TABLES EXCEPT (users,
    departments);
    postgres=# \dRp+ pub0
    Owner  | All tables | All sequences |
    --------+------------+---------------
    shveta | t          | t             |
    Except tables:
        "public.departments"
        "public. Users"
    
    pg_dump output:
    CREATE PUBLICATION pub0 FOR ALL TABLES, ALL SEQUENCES WITH (publish =
    'insert, update, delete, truncate');
    
    ~~
    
    Reviewing further.
    
    thanks
    Shveta
    
    
    
    
  256. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-26T10:37:10Z

    On Thu, Feb 26, 2026 at 3:22 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Thu, Feb 26, 2026 at 11:42 AM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Wed, Feb 25, 2026 at 10:43 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > Hi Shveta, Ashutosh, Chao-san,
    > > >
    > > > Thanks for reviewing the patch.
    > > > I have addressed the above comments and the comments in [1] and [2].
    > > > Attached is the latest v50 version.
    > > >
    > >
    > > Thank You Shlok. Please find a few comments on v50-001.
    > >
    > > 1)
    > > -check_publication_add_relation(Relation targetrel)
    > > +check_publication_add_relation(PublicationRelInfo *pri, bool puballtables)
    > >
    > > Why do we need argument  puballtables? I think we can give partition
    > > related error even without checking 'puballtables', like we are giving
    > > for temp and unlogged table error in EXCEPT list without checking
    > > puballtables. Or let me know if I am missing the point here?
    > >
    
    One more point related to this part of code:
    + if (pri->except)
    + errormsg = gettext_noop("cannot use publication EXCEPT clause for
    relation \"%s\"");
    + else
    + errormsg = gettext_noop("cannot add relation \"%s\" to publication");
    +
    + /* If in EXCEPT clause, must be root partitioned table */
    + if (puballtables && pri->except && targetrel->rd_rel->relispartition)
    + ereport(ERROR,
    + (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    + errmsg(errormsg, RelationGetRelationName(targetrel)),
    + errdetail("This operation is not supported for individual partitions.")));
    +
    
    Why can't we directly use the required error message in errmsg?
    
    > > 2)
    > > publication_has_any_except_table(): Shall we optimize it:
    > > a) if it is not all-table pub, return false there itself.
    > > b) if it is all table, proceed with fetching tuples. At first tuple we
    > > can break the loop irrespective of check 'pubrel->prexcept', as there
    > > is no other feasibility. But we shall Assert (pubrel->prexcept) .
    > > Thoughts?
    > >
    
    Hmm, let's not add such optimization checks here. It's not worth
    making it specific to all_tables because tomorrow we will need to
    support all_tables_in_schema case as well and we won't gain much with
    such optimizations.
    ...
    
    > >
    > > 4)
    > > CheckPublicationsForExceptClauses() frees except_publications list
    > > only in case where it has to emit ERROR. It does not free it for a
    > > case where there is only one element in it. Also one of the callers
    > > free the list while another does not. Is it by design? Shall we make
    > > the behaviour more consistent for callers?
    > >
    
    It should be consistent. I think in the ERROR case, most probably, it
    will be freed by the containing memory context. I suggest check which
    memory context's are used in all cases including when the
    LoadPuclications() could be invoked via decoding APIs.
    
    > > 5)
    > > postgres=# ALTER TABLE tab_top_root ATTACH PARTITION tab_root FOR
    > > VALUES FROM (0) TO (2000);
    > > ERROR:  cannot attach table "tab_root" as partition because it is
    > > referenced in a publication EXCEPT clause
    > > DETAIL:  Tables excluded from publications cannot be attached as partitions.
    > > HINT:  Remove the table from the publication EXCEPT list before attaching it.
    > >
    > > a) It will be good to list pubnames here for which we are referring EXCEPT list.
    > >
    > > b) Also I feel, instead of emphasising on 'cannot attach table as
    > > partition' in DETAIL, we should emphasise on 'partition cannot be part
    > > of EXCEPT'. How about?
    > > DETAIL: The publication EXCEPT clause cannot contain tables that are partitions.
    > >
    
    +1.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  257. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-26T14:11:31Z

    On Wed, Feb 25, 2026 at 10:43 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > I have also split the patch 0002 to 0002 and 0003
    > 0001 - Support CREATE PUBLICATION ... EXCEPT TABLE syntax
    >
    
    Few comments:
    =============
    *
      * pub_all_obj_type is one of:
      *
    - * TABLES
    + * TABLES [EXCEPT [TABLE] ( table [, ...] )]
    As per above syntax in gram.y, [TABLE] is optional but won't it be
    confusing when we also support sequences? Then we need to identify
    whether the object given in EXCEPT list is table or sequence. So, I
    think it is better to keep TABLE as non-optional.
    
    * Can we have one test related to INHERITS for actual replication
    unless it is already there?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  258. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-27T05:33:33Z

    On Thu, Feb 26, 2026 at 3:22 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > Few more comments on v50-001:
    >
    > 9)
    > + *   pub1:
    > + *     FOR ALL TABLES EXCEPT (part1, part2)
    > + *       WITH (publish_via_partition_root = true)
    >
    > This example atop check_publications_except_list() needs to be changed
    > as now we can not specify partitions in EXCEPT.
    >
    
    Now that we don't support partitions in EXCEPT clauses, won't it be
    possible to allow combining two publications with except clauses?
    
    Few other comments:
    =================
    1.
    +/*
    + * Get the list of publication oids associated with a specified relation.
    + *
    + * 'pubids' returns the Oids of the publications the relation is part of.
    + *
    + * 'except_pubids' returns the Oids of publications the relation is excluded
    + * from.
    + *
    + * This function returns true if the relation is part of any publication.
    + */
    +bool
    +GetRelationPublications(Oid relid, List **pubids, List **except_pubids)
    
    Isn't it better to have two wrapper functions for included and
    excluded RelationPublications? The current API signature doesn't look
    clean to me as it combines two different things.
    
    2.
    The following is from 0001
    --- a/src/backend/commands/publicationcmds.c
    +++ b/src/backend/commands/publicationcmds.c
    @@ -198,7 +198,12 @@ ObjectsInPublicationToOids(List *pubobjspec_list,
    ParseState *pstate,
    
      switch (pubobj->pubobjtype)
      {
    + case PUBLICATIONOBJ_EXCEPT_TABLE:
    + pubobj->pubtable->except = true;
    + *rels = lappend(*rels, pubobj->pubtable);
    + break;
      case PUBLICATIONOBJ_TABLE:
    + pubobj->pubtable->except = false;
      *rels = lappend(*rels, pubobj->pubtable);
    ....
    
    The following is from 0002:
     static void
     ObjectsInPublicationToOids(List *pubobjspec_list, ParseState *pstate,
    -    List **rels, List **schemas)
    +    List **rels, List **exceptrels, List **schemas)
     {
      ListCell   *cell;
      PublicationObjSpec *pubobj;
    @@ -200,7 +200,7 @@ ObjectsInPublicationToOids(List *pubobjspec_list,
    ParseState *pstate,
      {
      case PUBLICATIONOBJ_EXCEPT_TABLE:
      pubobj->pubtable->except = true;
    - *rels = lappend(*rels, pubobj->pubtable);
    + *exceptrels = lappend(*exceptrels, pubobj->pubtable);
    
    I think it is better to have a separate exceptlist from patch-1 itself
    as that keeps code easier to understand for 0001.
    
    3.
    +$node_publisher->safe_psql('postgres',
    + "SELECT slot_name FROM pg_replication_slot_advance('test_slot',
    pg_current_wal_lsn());"
    +);
    
    At above and other places where it uses slot_advance, write a comment
    to explain why the same is required.
    
    4.
    + # When the root partitioned table is listed in the EXCEPT clause,
    + # all its partitions are not published.
    + $node_publisher->safe_psql(
    
    Can we change the comment to the following: "If the root partitioned
    table is in the EXCEPT clause, all its partitions are excluded from
    publication, regardless of the publish_via_partition_root setting."?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  259. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-27T06:39:59Z

    On Thu, 26 Feb 2026 at 11:42, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Feb 25, 2026 at 10:43 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > Hi Shveta, Ashutosh, Chao-san,
    > >
    > > Thanks for reviewing the patch.
    > > I have addressed the above comments and the comments in [1] and [2].
    > > Attached is the latest v50 version.
    > >
    >
    > Thank You Shlok. Please find a few comments on v50-001.
    >
    > 1)
    > -check_publication_add_relation(Relation targetrel)
    > +check_publication_add_relation(PublicationRelInfo *pri, bool puballtables)
    >
    > Why do we need argument  puballtables? I think we can give partition
    > related error even without checking 'puballtables', like we are giving
    > for temp and unlogged table error in EXCEPT list without checking
    > puballtables. Or let me know if I am missing the point here?
    
    This is required as this is a common function used by both all tables
    and table publication,  since we allow child tables in case of table
    publication, alltables check is required.
    
    > 2)
    > publication_has_any_except_table(): Shall we optimize it:
    > a) if it is not all-table pub, return false there itself.
    > b) if it is all table, proceed with fetching tuples. At first tuple we
    > can break the loop irrespective of check 'pubrel->prexcept', as there
    > is no other feasibility. But we shall Assert (pubrel->prexcept) .
    > Thoughts?
    
    As Amit is also not in favor of this at [1], I'm not doing any change for this.
    
    Rest of the comments are addressed in the attached v51 version.
    
    Regarding the 12 comment from [2]:
    > 12) pg_dump dumped "ONLY" along with ROOT name in EXCEPT. I think it
    > should be avoided.
    >  pg_dump output:
    > CREATE PUBLICATION pub4 FOR ALL TABLES EXCEPT TABLE (ONLY
    > public.tab_root) WITH (publish = 'insert, update, delete, truncate');
    
    I felt this is ok, we dump similarly in case of table publications too
    like for the below publication:
    create publication pub1 for table tab_root ;
    
    Dump adds only here too:
    --
    -- Name: pub1 tab_root; Type: PUBLICATION TABLE; Schema: public; Owner: vignesh
    --
    
    ALTER PUBLICATION pub1 ADD TABLE ONLY public.tab_root;
    
    Rest of the comments from [2] have been addressed.
    
    [1] - https://www.postgresql.org/message-id/CAA4eK1%2BV0-HaxqiEJShTOh5%2BSGw1p4kurK327n8w3WwLV9DD4Q%40mail.gmail.com
    [2] - https://www.postgresql.org/message-id/CAJpy0uDRv%3DVuG439QTVaX6pgBSvpzAQd5Px9qH%2BE-XAnwpMuDQ%40mail.gmail.com
    
    Regards,
    Vignesh
    
  260. Re: Skipping schema changes in publication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-02-27T11:31:14Z

    On Fri, Feb 27, 2026 at 12:10 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Rest of the comments are addressed in the attached v51 version.
    
    Thanks for the patches, I am still testing the patches, but sharing a
    few initial review comments.
    
    1)  v51-0001: Bug in testcase - /t/037_rep_changes_except_table.pl
    The test_except_root_partition() is invoked twice with values true and
    false. However, from the logs it appears that both executions use
    publish_via_partition_root = 1.
    LOG output:
      138 2026-02-27 12:26:51.167 IST client backend[72317]
    037_rep_changes_except_table.pl LOG:  statement: CREATE PUBLICATION
    tap_pub_part FOR ALL TABLES EXCEPT TABLE (sch1.t1) WITH
    (publish_via_partition_root = 1);
      ...
      ...
      238 2026-02-27 12:26:51.600 IST client backend[72369]
    037_rep_changes_except_table.pl LOG:  statement: CREATE PUBLICATION
    tap_pub_part FOR ALL TABLES EXCEPT TABLE (sch1.t1) WITH
    (publish_via_partition_root = 1);
    
    It seems $pubviaroot is assigned using the argument count instead of
    the argument value, which causes both runs to behave the same.
    Fix is to replace the assignment as:
      27 -  my $pubviaroot = @_;
      27 +  my ($pubviaroot) = @_;
    ~~~
    
    2) v51-0001: File: src/backend/commands/tablecmds.c
    
    + ereport(ERROR,
    + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("cannot attach table \"%s\" as partition because it is
    referenced in publication \"%s\" EXCEPT clause",
    +    RelationGetRelationName(attachrel), pubnames.data),
    + errdetail("The publication EXCEPT clause cannot contain tables that
    are partitions.");
    + errhint("Remove the table from the publication EXCEPT clause before
    attaching it."));
    + }
    
    In the ereport() call, there appears to be a small typo. A comma
    should follow errdetail() instead of a semicolon.
    ~~~
    
    3) v51-0002: File: src/backend/commands/publicationcmds.c
    +
    + /* Check that user is allowed to manipulate the publication tables. */
    + if (excepttables && !pubform->puballtables)
    + ereport(ERROR,
    + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("publication \"%s\" is defined as NON FOR ALL TABLES",
    +    NameStr(pubform->pubname)),
    + errdetail("EXCEPT Tables cannot be added to or dropped from non FOR
    ALL TABLES publications."));
     }
    
    It seems the check is for both SET and DROP cases, but this patch is
    only for SET. Also, I find it a bit confusing, could be made clearer.
    Suggestion:
    - errmsg("publication \"%s\" is defined as NON FOR ALL TABLES",
    + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
         NameStr(pubform->pubname)),
    - errdetail("EXCEPT Tables cannot be added to or dropped from non FOR
    ALL TABLES publications."));
    + errdetail("EXCEPT TABLE cannot be used with publications that are
    not defined as FOR ALL TABLES."));
    ~~~
    
    4) v51-0002 and v51-003:
    File: doc/src/sgml/ref/alter_publication.sgml
    ...
    -   remove one or more tables/schemas from the publication.  Note that adding
    -   tables/schemas to a publication that is already subscribed to will
    require an
    +   except tables/tables/schemas in the publication with the specified list; the
    +   existing except tables/ tables/schemas that were present in the publication
    ...
    4a) After adding “except tables” as a third option, the slash based
    formatting may be slightly confusing. It may be clearer to write as:
    ... the existing except tables, tables, or schemas that were present in ....
    ... The <literal>DROP</literal> clauses will remove one or more except
    tables, tables, or schemas from the publication....
    
    If the existing slash format is retained, there is a typo in "except
    tables/ tables/schemas", extra space after tables/.
    
    4b) Should we add examples for SET/DROP EXCEPT TABLE as well?
    ~~~
    
    5) Inconsistency in EXCEPT TABLE syntax
    In CREATE PUBLICATION, the syntax requires parentheses, for example
    EXCEPT TABLE (t1, t2), whereas in SET and DROP it is accepted only
    without parentheses, like - EXCEPT TABLE t1, t2;
    
    Should we keep the syntax consistent across all commands?
    ~~~
    
    --
    Thanks,
    Nisha
    
    
    
    
  261. Re: Skipping schema changes in publication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-02-27T15:14:18Z

    On Fri, Feb 27, 2026 at 5:01 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > Thanks for the patches, I am still testing the patches, but sharing a
    > few initial review comments.
    >
    
    I did few tests on v51 all patches, please find further comments -
    
    1. Partition Related Behavior
    -----------------------------
    Setup:
    CREATE TABLE t_part (id int) PARTITION BY RANGE (id);
    CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
    CREATE TABLE t_part_p2 (id int);
    ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    
    t_part
      |_> t_part_p1
      |_> t_part_p2
    
    Publication:
      create publication pubp1 for ALL TABLES EXCEPT TABLE (t_part);
     -- No data from all three tables is replicated, which is expected.
    
    1a) DETACH t_part_p2 from parent
    ALTER TABLE t_part DETACH PARTITION t_part_p2;
    
    When t_part_p2 is detached, it becomes a standalone table and is no
    longer part of the EXCEPT hierarchy. But, replication does not start
    automatically. It requires REFRESH PUBLICATION on the subscriber.
    Should we add a warning/hint during DETACH command to inform users
    that REFRESH PUBLICATION is required?
    It may also help to update the related documentation.
    ~~
    1b): Attach the partition again
      ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    When the partition is attached back, replication stops immediately as
    it becomes part of the excluded parent again. No REFRESH PUBLICATION
    is required on the subscriber. In this case, users may not realize
    that replication has stopped. Should we consider adding a warning
    during ATTACH in such scenarios?
    ~~~~
    
    2. Inherited Tables Behavior
    Setup:
     CREATE TABLE t_inh (id int);
     CREATE TABLE t_inh_c1 (c1 int) INHERITS (t_inh);
     CREATE TABLE t_inh_c2 (c2 int) INHERITS (t_inh);
    t_inh
      |_> t_inh_c1
      |_> t_inh_c2
    
    Publication:
      create publication pubi1 for ALL TABLES EXCEPT TABLE (t_inh);
     --All three tables are not replicated.
    
    2a): Remove child from parent
      alter table t_inh_c1 NO INHERIT t_inh;
    
    \d+ t_inh shows:
    Except Publications:
        "pubi1"
    Child tables: t_inh_c2
    
    But the publication still shows:
    Except tables:
        "public.t_inh"
        "public.t_inh_c1"
        "public.t_inh_c2"
    
    t_inh_c1 is no longer part of the excluded hierarchy, yet it remains
    in the EXCEPT list and replication does not start for it.
    This appears inconsistent and may be a bug.
    
    2b): Add inheritance to excluded parent
      alter table t_inh_c1 inherit t_inh;
    
    Publication:
    Except tables:
        "public.t_inh"
        "public.t_inh_c2"
    
    \d+ t_inh shows:
    Except Publications:
        "pubi1"
    Child tables: t_inh_c1,
                  t_inh_c2
    
    Here, the table hierarchy is updated, but the publication EXCEPT list
    is not updated. Replication continues for t_inh_c1 even though its
    parent is excluded.
    >From a user perspective, this appears incorrect.
    
    I am not sure whether we can determine if the publication was created
    for ONLY parent or for parent including children once it is created.
    If this behavior is unavoidable, it should be documented.
    ~~~~
    
    3. SET EXCEPT TABLE Behavior
    Publication created with:
      create publication pubi5 for ALL TABLES EXCEPT TABLE (ONLY t_inh);
      -- Replicates t_inh_c1 and t_inh_c2, as expected.
    Now:
     alter publication pubi5 SET EXCEPT TABLE t_inh;
    
    Publication pubi5
    Except tables:
        "public.t_inh"
        "public.t_inh_c1"
        "public.t_inh_c2"
    
    But, replication continues on the subscriber for both t_inh_c1 and
    t_inh_c2 even after REFRESH PUBLICATION. This seems like a bug.
    Note: The opposite works correctly. If SET EXCEPT removes previous
    tables, replication starts/resumes immediately for these tables.
    ~~~~
    
    --
    Thanks,
    Nisha
    
    
    
    
  262. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-28T08:13:15Z

    On Thu, 26 Feb 2026 at 19:41, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, Feb 25, 2026 at 10:43 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > I have also split the patch 0002 to 0002 and 0003
    > > 0001 - Support CREATE PUBLICATION ... EXCEPT TABLE syntax
    > >
    >
    > Few comments:
    > =============
    > *
    >   * pub_all_obj_type is one of:
    >   *
    > - * TABLES
    > + * TABLES [EXCEPT [TABLE] ( table [, ...] )]
    > As per above syntax in gram.y, [TABLE] is optional but won't it be
    > confusing when we also support sequences? Then we need to identify
    > whether the object given in EXCEPT list is table or sequence. So, I
    > think it is better to keep TABLE as non-optional.
    >
    > * Can we have one test related to INHERITS for actual replication
    > unless it is already there?
    
    Thanks for the comments, these comments are addressed in the v52
    version patch attached.
    Also the comments from [1] are addressed in the same.
    [1] - https://www.postgresql.org/message-id/CAA4eK1JrqoPvrFxZEO2rB%3D-jXK1BQdJZz2_2oeZqCQR3GRWC2g%40mail.gmail.com
    
    Regards,
    Vignesh
    
  263. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-02-28T11:57:16Z

    On Fri, Feb 27, 2026 at 8:44 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Fri, Feb 27, 2026 at 5:01 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > Thanks for the patches, I am still testing the patches, but sharing a
    > > few initial review comments.
    > >
    >
    > I did few tests on v51 all patches, please find further comments -
    >
    > 1. Partition Related Behavior
    > -----------------------------
    > Setup:
    > CREATE TABLE t_part (id int) PARTITION BY RANGE (id);
    > CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
    > CREATE TABLE t_part_p2 (id int);
    > ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    >
    > t_part
    >   |_> t_part_p1
    >   |_> t_part_p2
    >
    > Publication:
    >   create publication pubp1 for ALL TABLES EXCEPT TABLE (t_part);
    >  -- No data from all three tables is replicated, which is expected.
    >
    > 1a) DETACH t_part_p2 from parent
    > ALTER TABLE t_part DETACH PARTITION t_part_p2;
    >
    > When t_part_p2 is detached, it becomes a standalone table and is no
    > longer part of the EXCEPT hierarchy. But, replication does not start
    > automatically. It requires REFRESH PUBLICATION on the subscriber.
    > Should we add a warning/hint during DETACH command to inform users
    > that REFRESH PUBLICATION is required?
    > It may also help to update the related documentation.
    >
    
    This is required even when a regular (non-partitioned table) is
    created to let the corresponding entry populated in
    pg_subscription_rel. See docs[1] (REFRESH PUBLICATION ..).
    
    > ~~
    > 1b): Attach the partition again
    >   ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > When the partition is attached back, replication stops immediately as
    > it becomes part of the excluded parent again. No REFRESH PUBLICATION
    > is required on the subscriber. In this case, users may not realize
    > that replication has stopped. Should we consider adding a warning
    > during ATTACH in such scenarios?
    > ~~~~
    >
    
    This is worth considering but my opinion is that we don't need it as
    users should be aware of such behavior. For example, today, if a
    publication has published a root table say tab_root and one of its
    partitions is detached, we should stop that partition's replication
    but do we give WARNING for such cases?
    
    > 2. Inherited Tables Behavior
    > Setup:
    >  CREATE TABLE t_inh (id int);
    >  CREATE TABLE t_inh_c1 (c1 int) INHERITS (t_inh);
    >  CREATE TABLE t_inh_c2 (c2 int) INHERITS (t_inh);
    > t_inh
    >   |_> t_inh_c1
    >   |_> t_inh_c2
    >
    > Publication:
    >   create publication pubi1 for ALL TABLES EXCEPT TABLE (t_inh);
    >  --All three tables are not replicated.
    >
    > 2a): Remove child from parent
    >   alter table t_inh_c1 NO INHERIT t_inh;
    >
    > \d+ t_inh shows:
    > Except Publications:
    >     "pubi1"
    > Child tables: t_inh_c2
    >
    > But the publication still shows:
    > Except tables:
    >     "public.t_inh"
    >     "public.t_inh_c1"
    >     "public.t_inh_c2"
    >
    > t_inh_c1 is no longer part of the excluded hierarchy, yet it remains
    > in the EXCEPT list and replication does not start for it.
    > This appears inconsistent and may be a bug.
    >
    
    I think after removal, it shouldn't have been shown here, so, we
    should see why it is happening. OTOH, won't replication requires user
    to perform REFRESH PUBLICATION as in the previous case?
    
    Few other comments:
    1.
    --- a/src/backend/commands/subscriptioncmds.c
    +++ b/src/backend/commands/subscriptioncmds.c
    @@ -37,6 +37,7 @@
     #include "miscadmin.h"
     #include "nodes/makefuncs.h"
     #include "pgstat.h"
    +#include "replication/logical.h"
     #include "replication/logicallauncher.h"
     #include "replication/logicalworker.h"
     #include "replication/origin.h"
    
    There is no other change in this file, so do we even need this header included?
    
    2.
    + foreach_oid(pubid, exceptpuboids)
    + {
    + char    *pubname = get_publication_name(pubid, false);
    +
    + if (!first)
    + appendStringInfoString(&pubnames, ", ");
    +
    + first = false;
    +
    + appendStringInfoString(&pubnames, quote_literal_cstr(pubname));
    + }
    +
    + ereport(ERROR,
    + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("cannot attach table \"%s\" as partition because it is
    referenced in publication \"%s\" EXCEPT clause",
    +    RelationGetRelationName(attachrel), pubnames.data),
    + errdetail("The publication EXCEPT clause cannot contain tables that
    are partitions."),
    + errhint("Remove the table from the publication EXCEPT clause before
    attaching it."));
    
    A. As there could be multiple publications, so errmsg_plural form
    should be used, refer to other places.
    B. I think errhint should be removed from 0001 and added back by 0002
    where you can say how user can remove it by using SET CLAUSE
    
    [1] - https://www.postgresql.org/docs/devel/sql-altersubscription.html
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  264. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-02-28T14:07:14Z

    On Sat, 28 Feb 2026 at 17:27, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Feb 27, 2026 at 8:44 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > On Fri, Feb 27, 2026 at 5:01 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > Thanks for the patches, I am still testing the patches, but sharing a
    > > > few initial review comments.
    > > >
    > >
    > > I did few tests on v51 all patches, please find further comments -
    > >
    > > 1. Partition Related Behavior
    > > -----------------------------
    > > Setup:
    > > CREATE TABLE t_part (id int) PARTITION BY RANGE (id);
    > > CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
    > > CREATE TABLE t_part_p2 (id int);
    > > ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > >
    > > t_part
    > >   |_> t_part_p1
    > >   |_> t_part_p2
    > >
    > > Publication:
    > >   create publication pubp1 for ALL TABLES EXCEPT TABLE (t_part);
    > >  -- No data from all three tables is replicated, which is expected.
    > >
    > > 1a) DETACH t_part_p2 from parent
    > > ALTER TABLE t_part DETACH PARTITION t_part_p2;
    > >
    > > When t_part_p2 is detached, it becomes a standalone table and is no
    > > longer part of the EXCEPT hierarchy. But, replication does not start
    > > automatically. It requires REFRESH PUBLICATION on the subscriber.
    > > Should we add a warning/hint during DETACH command to inform users
    > > that REFRESH PUBLICATION is required?
    > > It may also help to update the related documentation.
    > >
    >
    > This is required even when a regular (non-partitioned table) is
    > created to let the corresponding entry populated in
    > pg_subscription_rel. See docs[1] (REFRESH PUBLICATION ..).
    >
    > > ~~
    > > 1b): Attach the partition again
    > >   ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > > When the partition is attached back, replication stops immediately as
    > > it becomes part of the excluded parent again. No REFRESH PUBLICATION
    > > is required on the subscriber. In this case, users may not realize
    > > that replication has stopped. Should we consider adding a warning
    > > during ATTACH in such scenarios?
    > > ~~~~
    > >
    >
    > This is worth considering but my opinion is that we don't need it as
    > users should be aware of such behavior. For example, today, if a
    > publication has published a root table say tab_root and one of its
    > partitions is detached, we should stop that partition's replication
    > but do we give WARNING for such cases?
    >
    > > 2. Inherited Tables Behavior
    > > Setup:
    > >  CREATE TABLE t_inh (id int);
    > >  CREATE TABLE t_inh_c1 (c1 int) INHERITS (t_inh);
    > >  CREATE TABLE t_inh_c2 (c2 int) INHERITS (t_inh);
    > > t_inh
    > >   |_> t_inh_c1
    > >   |_> t_inh_c2
    > >
    > > Publication:
    > >   create publication pubi1 for ALL TABLES EXCEPT TABLE (t_inh);
    > >  --All three tables are not replicated.
    > >
    > > 2a): Remove child from parent
    > >   alter table t_inh_c1 NO INHERIT t_inh;
    > >
    > > \d+ t_inh shows:
    > > Except Publications:
    > >     "pubi1"
    > > Child tables: t_inh_c2
    > >
    > > But the publication still shows:
    > > Except tables:
    > >     "public.t_inh"
    > >     "public.t_inh_c1"
    > >     "public.t_inh_c2"
    > >
    > > t_inh_c1 is no longer part of the excluded hierarchy, yet it remains
    > > in the EXCEPT list and replication does not start for it.
    > > This appears inconsistent and may be a bug.
    > >
    >
    > I think after removal, it shouldn't have been shown here, so, we
    > should see why it is happening.
    
    I believe this is existing behavior, and the same applies to
    table-level publications as well.
    For example:
    -- Create table publication
    create publication pubi1 for table t_inh;
    \d+ t_inh
                                              Table "public.t_inh"
     Column |  Type   | Collation | Nullable | Default | Storage |
    Compression | Stats target | Description
    --------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
     id     | integer |           |          |         | plain   |
        |              |
    Publications:
        "pubi1"
    Child tables: t_inh_c1,
                  t_inh_c2
    
    postgres=# \dRp+
                                                   Publication pubi1
      Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root
    ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------
     vignesh | f          | f             | t       | t       | t       |
    t         | none              | f
    Tables:
        "public.t_inh"
        "public.t_inh_c1"
        "public.t_inh_c2"
    
    --- Remove the inherits for one of the child table
    postgres=#   alter table t_inh_c1 NO INHERIT t_inh;
    ALTER TABLE
    
    --- Publication will still have t_inh_c1 table in case of table publication too
    postgres=# \dRp+
                                                   Publication pubi1
      Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root
    ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------
     vignesh | f          | f             | t       | t       | t       |
    t         | none              | f
    Tables:
        "public.t_inh"
        "public.t_inh_c1"
        "public.t_inh_c2"
    
    This occurs because when ONLY is not specified, the table t_inh and
    all its descendant tables (t_inh_c1 and t_inh_c2) are added to
    pg_publication_rel at the time the publication is created. Replication
    then proceeds strictly based on the entries in pg_publication_rel.
    There is no explicit parent child mapping maintained for these
    relations in the publication catalog; each table is treated as an
    independent entry.
    When "ALTER TABLE t_inh_c1 NO INHERIT t_inh;" is executed later,
    t_inh_c1 is not automatically removed from the publication. Since it
    was added as a separate entry in pg_publication_rel, removing the
    inheritance relationship does not affect its publication membership.
    I'm not sure whether this is the intended design or an unintended side
    effect. If this behavior is not expected, we can address it separately
    and adjust the implementation accordingly.
    
    Regards,
    Vignesh
    
    
    
    
  265. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-01T03:11:30Z

    On Fri, 27 Feb 2026 at 17:01, Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Fri, Feb 27, 2026 at 12:10 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Rest of the comments are addressed in the attached v51 version.
    >
    > Thanks for the patches, I am still testing the patches, but sharing a
    > few initial review comments.
    >
    > 1)  v51-0001: Bug in testcase - /t/037_rep_changes_except_table.pl
    > The test_except_root_partition() is invoked twice with values true and
    > false. However, from the logs it appears that both executions use
    > publish_via_partition_root = 1.
    > LOG output:
    >   138 2026-02-27 12:26:51.167 IST client backend[72317]
    > 037_rep_changes_except_table.pl LOG:  statement: CREATE PUBLICATION
    > tap_pub_part FOR ALL TABLES EXCEPT TABLE (sch1.t1) WITH
    > (publish_via_partition_root = 1);
    >   ...
    >   ...
    >   238 2026-02-27 12:26:51.600 IST client backend[72369]
    > 037_rep_changes_except_table.pl LOG:  statement: CREATE PUBLICATION
    > tap_pub_part FOR ALL TABLES EXCEPT TABLE (sch1.t1) WITH
    > (publish_via_partition_root = 1);
    >
    > It seems $pubviaroot is assigned using the argument count instead of
    > the argument value, which causes both runs to behave the same.
    > Fix is to replace the assignment as:
    >   27 -  my $pubviaroot = @_;
    >   27 +  my ($pubviaroot) = @_;
    
    Fixed
    
    > 2) v51-0001: File: src/backend/commands/tablecmds.c
    >
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("cannot attach table \"%s\" as partition because it is
    > referenced in publication \"%s\" EXCEPT clause",
    > +    RelationGetRelationName(attachrel), pubnames.data),
    > + errdetail("The publication EXCEPT clause cannot contain tables that
    > are partitions.");
    > + errhint("Remove the table from the publication EXCEPT clause before
    > attaching it."));
    > + }
    >
    > In the ereport() call, there appears to be a small typo. A comma
    > should follow errdetail() instead of a semicolon.
    
    Fixed
    
    > 3) v51-0002: File: src/backend/commands/publicationcmds.c
    > +
    > + /* Check that user is allowed to manipulate the publication tables. */
    > + if (excepttables && !pubform->puballtables)
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("publication \"%s\" is defined as NON FOR ALL TABLES",
    > +    NameStr(pubform->pubname)),
    > + errdetail("EXCEPT Tables cannot be added to or dropped from non FOR
    > ALL TABLES publications."));
    >  }
    >
    > It seems the check is for both SET and DROP cases, but this patch is
    > only for SET. Also, I find it a bit confusing, could be made clearer.
    > Suggestion:
    > - errmsg("publication \"%s\" is defined as NON FOR ALL TABLES",
    > + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    >      NameStr(pubform->pubname)),
    > - errdetail("EXCEPT Tables cannot be added to or dropped from non FOR
    > ALL TABLES publications."));
    > + errdetail("EXCEPT TABLE cannot be used with publications that are
    > not defined as FOR ALL TABLES."));
    
    Modified with slight changes to keep it inline with other error messages there.
    
    > 4) v51-0002 and v51-003:
    > File: doc/src/sgml/ref/alter_publication.sgml
    > ...
    > -   remove one or more tables/schemas from the publication.  Note that adding
    > -   tables/schemas to a publication that is already subscribed to will
    > require an
    > +   except tables/tables/schemas in the publication with the specified list; the
    > +   existing except tables/ tables/schemas that were present in the publication
    > ...
    > 4a) After adding “except tables” as a third option, the slash based
    > formatting may be slightly confusing. It may be clearer to write as:
    > ... the existing except tables, tables, or schemas that were present in ....
    > ... The <literal>DROP</literal> clauses will remove one or more except
    > tables, tables, or schemas from the publication....
    >
    > If the existing slash format is retained, there is a typo in "except
    > tables/ tables/schemas", extra space after tables/.
    
    Existing format was used, the typo issue was fixed.
    
    > 4b) Should we add examples for SET/DROP EXCEPT TABLE as well?
    
    Added them
    
    > 5) Inconsistency in EXCEPT TABLE syntax
    > In CREATE PUBLICATION, the syntax requires parentheses, for example
    > EXCEPT TABLE (t1, t2), whereas in SET and DROP it is accepted only
    > without parentheses, like - EXCEPT TABLE t1, t2;
    >
    > Should we keep the syntax consistent across all commands?
    
    I’ll look into this separately and share my analysis soon.
    
    The attached v53 version patch has the changes for the same.
    
    Also the comments from [1] have been addressed.
    [1] - https://www.postgresql.org/message-id/CAA4eK1L2LDps5AdtRYySQhmqSbKeUG2GinOKJCn7bvLEdKOoPg%40mail.gmail.com
    
    Regards,
    Vignesh
    
  266. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-01T12:22:42Z

    On Sat, Feb 28, 2026 at 7:37 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Sat, 28 Feb 2026 at 17:27, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Fri, Feb 27, 2026 at 8:44 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > On Fri, Feb 27, 2026 at 5:01 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > >
    > > > > Thanks for the patches, I am still testing the patches, but sharing a
    > > > > few initial review comments.
    > > > >
    > > >
    > > > I did few tests on v51 all patches, please find further comments -
    > > >
    > > > 1. Partition Related Behavior
    > > > -----------------------------
    > > > Setup:
    > > > CREATE TABLE t_part (id int) PARTITION BY RANGE (id);
    > > > CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
    > > > CREATE TABLE t_part_p2 (id int);
    > > > ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > > >
    > > > t_part
    > > >   |_> t_part_p1
    > > >   |_> t_part_p2
    > > >
    > > > Publication:
    > > >   create publication pubp1 for ALL TABLES EXCEPT TABLE (t_part);
    > > >  -- No data from all three tables is replicated, which is expected.
    > > >
    > > > 1a) DETACH t_part_p2 from parent
    > > > ALTER TABLE t_part DETACH PARTITION t_part_p2;
    > > >
    > > > When t_part_p2 is detached, it becomes a standalone table and is no
    > > > longer part of the EXCEPT hierarchy. But, replication does not start
    > > > automatically. It requires REFRESH PUBLICATION on the subscriber.
    > > > Should we add a warning/hint during DETACH command to inform users
    > > > that REFRESH PUBLICATION is required?
    > > > It may also help to update the related documentation.
    > > >
    > >
    > > This is required even when a regular (non-partitioned table) is
    > > created to let the corresponding entry populated in
    > > pg_subscription_rel. See docs[1] (REFRESH PUBLICATION ..).
    > >
    > > > ~~
    > > > 1b): Attach the partition again
    > > >   ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > > > When the partition is attached back, replication stops immediately as
    > > > it becomes part of the excluded parent again. No REFRESH PUBLICATION
    > > > is required on the subscriber. In this case, users may not realize
    > > > that replication has stopped. Should we consider adding a warning
    > > > during ATTACH in such scenarios?
    > > > ~~~~
    > > >
    > >
    > > This is worth considering but my opinion is that we don't need it as
    > > users should be aware of such behavior. For example, today, if a
    > > publication has published a root table say tab_root and one of its
    > > partitions is detached, we should stop that partition's replication
    > > but do we give WARNING for such cases?
    > >
    > > > 2. Inherited Tables Behavior
    > > > Setup:
    > > >  CREATE TABLE t_inh (id int);
    > > >  CREATE TABLE t_inh_c1 (c1 int) INHERITS (t_inh);
    > > >  CREATE TABLE t_inh_c2 (c2 int) INHERITS (t_inh);
    > > > t_inh
    > > >   |_> t_inh_c1
    > > >   |_> t_inh_c2
    > > >
    > > > Publication:
    > > >   create publication pubi1 for ALL TABLES EXCEPT TABLE (t_inh);
    > > >  --All three tables are not replicated.
    > > >
    > > > 2a): Remove child from parent
    > > >   alter table t_inh_c1 NO INHERIT t_inh;
    > > >
    > > > \d+ t_inh shows:
    > > > Except Publications:
    > > >     "pubi1"
    > > > Child tables: t_inh_c2
    > > >
    > > > But the publication still shows:
    > > > Except tables:
    > > >     "public.t_inh"
    > > >     "public.t_inh_c1"
    > > >     "public.t_inh_c2"
    > > >
    > > > t_inh_c1 is no longer part of the excluded hierarchy, yet it remains
    > > > in the EXCEPT list and replication does not start for it.
    > > > This appears inconsistent and may be a bug.
    > > >
    > >
    > > I think after removal, it shouldn't have been shown here, so, we
    > > should see why it is happening.
    >
    > I believe this is existing behavior, and the same applies to
    > table-level publications as well.
    > For example:
    > -- Create table publication
    > create publication pubi1 for table t_inh;
    > \d+ t_inh
    >                                           Table "public.t_inh"
    >  Column |  Type   | Collation | Nullable | Default | Storage |
    > Compression | Stats target | Description
    > --------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
    >  id     | integer |           |          |         | plain   |
    >     |              |
    > Publications:
    >     "pubi1"
    > Child tables: t_inh_c1,
    >               t_inh_c2
    >
    > postgres=# \dRp+
    >                                                Publication pubi1
    >   Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root
    > ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------
    >  vignesh | f          | f             | t       | t       | t       |
    > t         | none              | f
    > Tables:
    >     "public.t_inh"
    >     "public.t_inh_c1"
    >     "public.t_inh_c2"
    >
    > --- Remove the inherits for one of the child table
    > postgres=#   alter table t_inh_c1 NO INHERIT t_inh;
    > ALTER TABLE
    >
    > --- Publication will still have t_inh_c1 table in case of table publication too
    > postgres=# \dRp+
    >                                                Publication pubi1
    >   Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > Truncates | Generated columns | Via root
    > ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------
    >  vignesh | f          | f             | t       | t       | t       |
    > t         | none              | f
    > Tables:
    >     "public.t_inh"
    >     "public.t_inh_c1"
    >     "public.t_inh_c2"
    >
    > This occurs because when ONLY is not specified, the table t_inh and
    > all its descendant tables (t_inh_c1 and t_inh_c2) are added to
    > pg_publication_rel at the time the publication is created. Replication
    > then proceeds strictly based on the entries in pg_publication_rel.
    > There is no explicit parent child mapping maintained for these
    > relations in the publication catalog; each table is treated as an
    > independent entry.
    > When "ALTER TABLE t_inh_c1 NO INHERIT t_inh;" is executed later,
    > t_inh_c1 is not automatically removed from the publication. Since it
    > was added as a separate entry in pg_publication_rel, removing the
    > inheritance relationship does not affect its publication membership.
    > I'm not sure whether this is the intended design or an unintended side
    > effect. If this behavior is not expected, we can address it separately
    > and adjust the implementation accordingly.
    >
    
    So, the similar case for partitioned table works because there we add
    only root partitioned table in pg_publication_rel. I understand that
    current behaviour for inherited tables (each inherited table is
    considered separately after being added to the publication) is because
    of the existing design and should be changed separately, if required.
    I haven't checked whether it is possible to change this behavior but
    it appears that we should try to investigate and see the possibility
    of fixing it separately.
    
    Nisha, what do you think based on this analysis?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  267. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-01T12:24:43Z

    On Sat, Feb 28, 2026 at 5:27 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Feb 27, 2026 at 8:44 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > On Fri, Feb 27, 2026 at 5:01 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > Thanks for the patches, I am still testing the patches, but sharing a
    > > > few initial review comments.
    > > >
    > >
    > > I did few tests on v51 all patches, please find further comments -
    > >
    > > 1. Partition Related Behavior
    > > -----------------------------
    > > Setup:
    > > CREATE TABLE t_part (id int) PARTITION BY RANGE (id);
    > > CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
    > > CREATE TABLE t_part_p2 (id int);
    > > ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > >
    > > t_part
    > >   |_> t_part_p1
    > >   |_> t_part_p2
    > >
    > > Publication:
    > >   create publication pubp1 for ALL TABLES EXCEPT TABLE (t_part);
    > >  -- No data from all three tables is replicated, which is expected.
    > >
    > > 1a) DETACH t_part_p2 from parent
    > > ALTER TABLE t_part DETACH PARTITION t_part_p2;
    > >
    > > When t_part_p2 is detached, it becomes a standalone table and is no
    > > longer part of the EXCEPT hierarchy. But, replication does not start
    > > automatically. It requires REFRESH PUBLICATION on the subscriber.
    > > Should we add a warning/hint during DETACH command to inform users
    > > that REFRESH PUBLICATION is required?
    > > It may also help to update the related documentation.
    > >
    >
    > This is required even when a regular (non-partitioned table) is
    > created to let the corresponding entry populated in
    > pg_subscription_rel. See docs[1] (REFRESH PUBLICATION ..).
    >
    > > ~~
    > > 1b): Attach the partition again
    > >   ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > > When the partition is attached back, replication stops immediately as
    > > it becomes part of the excluded parent again. No REFRESH PUBLICATION
    > > is required on the subscriber. In this case, users may not realize
    > > that replication has stopped. Should we consider adding a warning
    > > during ATTACH in such scenarios?
    > > ~~~~
    > >
    >
    > This is worth considering but my opinion is that we don't need it as
    > users should be aware of such behavior. For example, today, if a
    > publication has published a root table say tab_root and one of its
    > partitions is detached, we should stop that partition's replication
    > but do we give WARNING for such cases?
    >
    
    I tried, we do not give WARNING for such cases during ATTACH/DETACH on
    HEAD.  I also feel that a warning for this EXCEPT case (and the
    previous one) is not needed.
    
    > > 2. Inherited Tables Behavior
    > > Setup:
    > >  CREATE TABLE t_inh (id int);
    > >  CREATE TABLE t_inh_c1 (c1 int) INHERITS (t_inh);
    > >  CREATE TABLE t_inh_c2 (c2 int) INHERITS (t_inh);
    > > t_inh
    > >   |_> t_inh_c1
    > >   |_> t_inh_c2
    > >
    > > Publication:
    > >   create publication pubi1 for ALL TABLES EXCEPT TABLE (t_inh);
    > >  --All three tables are not replicated.
    > >
    > > 2a): Remove child from parent
    > >   alter table t_inh_c1 NO INHERIT t_inh;
    > >
    > > \d+ t_inh shows:
    > > Except Publications:
    > >     "pubi1"
    > > Child tables: t_inh_c2
    > >
    > > But the publication still shows:
    > > Except tables:
    > >     "public.t_inh"
    > >     "public.t_inh_c1"
    > >     "public.t_inh_c2"
    > >
    > > t_inh_c1 is no longer part of the excluded hierarchy, yet it remains
    > > in the EXCEPT list and replication does not start for it.
    > > This appears inconsistent and may be a bug.
    > >
    >
    > I think after removal, it shouldn't have been shown here, so, we
    > should see why it is happening. OTOH, won't replication requires user
    > to perform REFRESH PUBLICATION as in the previous case?
    >
    > Few other comments:
    > 1.
    > --- a/src/backend/commands/subscriptioncmds.c
    > +++ b/src/backend/commands/subscriptioncmds.c
    > @@ -37,6 +37,7 @@
    >  #include "miscadmin.h"
    >  #include "nodes/makefuncs.h"
    >  #include "pgstat.h"
    > +#include "replication/logical.h"
    >  #include "replication/logicallauncher.h"
    >  #include "replication/logicalworker.h"
    >  #include "replication/origin.h"
    >
    > There is no other change in this file, so do we even need this header included?
    >
    > 2.
    > + foreach_oid(pubid, exceptpuboids)
    > + {
    > + char    *pubname = get_publication_name(pubid, false);
    > +
    > + if (!first)
    > + appendStringInfoString(&pubnames, ", ");
    > +
    > + first = false;
    > +
    > + appendStringInfoString(&pubnames, quote_literal_cstr(pubname));
    > + }
    > +
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("cannot attach table \"%s\" as partition because it is
    > referenced in publication \"%s\" EXCEPT clause",
    > +    RelationGetRelationName(attachrel), pubnames.data),
    > + errdetail("The publication EXCEPT clause cannot contain tables that
    > are partitions."),
    > + errhint("Remove the table from the publication EXCEPT clause before
    > attaching it."));
    >
    > A. As there could be multiple publications, so errmsg_plural form
    > should be used, refer to other places.
    > B. I think errhint should be removed from 0001 and added back by 0002
    > where you can say how user can remove it by using SET CLAUSE
    >
    > [1] - https://www.postgresql.org/docs/devel/sql-altersubscription.html
    >
    > --
    > With Regards,
    > Amit Kapila.
    
    
    
    
  268. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-01T12:51:09Z

    On Sun, Mar 1, 2026 at 5:52 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Sat, Feb 28, 2026 at 7:37 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Sat, 28 Feb 2026 at 17:27, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > > On Fri, Feb 27, 2026 at 8:44 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > >
    > > > > On Fri, Feb 27, 2026 at 5:01 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > > > >
    > > > > > Thanks for the patches, I am still testing the patches, but sharing a
    > > > > > few initial review comments.
    > > > > >
    > > > >
    > > > > I did few tests on v51 all patches, please find further comments -
    > > > >
    > > > > 1. Partition Related Behavior
    > > > > -----------------------------
    > > > > Setup:
    > > > > CREATE TABLE t_part (id int) PARTITION BY RANGE (id);
    > > > > CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
    > > > > CREATE TABLE t_part_p2 (id int);
    > > > > ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > > > >
    > > > > t_part
    > > > >   |_> t_part_p1
    > > > >   |_> t_part_p2
    > > > >
    > > > > Publication:
    > > > >   create publication pubp1 for ALL TABLES EXCEPT TABLE (t_part);
    > > > >  -- No data from all three tables is replicated, which is expected.
    > > > >
    > > > > 1a) DETACH t_part_p2 from parent
    > > > > ALTER TABLE t_part DETACH PARTITION t_part_p2;
    > > > >
    > > > > When t_part_p2 is detached, it becomes a standalone table and is no
    > > > > longer part of the EXCEPT hierarchy. But, replication does not start
    > > > > automatically. It requires REFRESH PUBLICATION on the subscriber.
    > > > > Should we add a warning/hint during DETACH command to inform users
    > > > > that REFRESH PUBLICATION is required?
    > > > > It may also help to update the related documentation.
    > > > >
    > > >
    > > > This is required even when a regular (non-partitioned table) is
    > > > created to let the corresponding entry populated in
    > > > pg_subscription_rel. See docs[1] (REFRESH PUBLICATION ..).
    > > >
    > > > > ~~
    > > > > 1b): Attach the partition again
    > > > >   ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > > > > When the partition is attached back, replication stops immediately as
    > > > > it becomes part of the excluded parent again. No REFRESH PUBLICATION
    > > > > is required on the subscriber. In this case, users may not realize
    > > > > that replication has stopped. Should we consider adding a warning
    > > > > during ATTACH in such scenarios?
    > > > > ~~~~
    > > > >
    > > >
    > > > This is worth considering but my opinion is that we don't need it as
    > > > users should be aware of such behavior. For example, today, if a
    > > > publication has published a root table say tab_root and one of its
    > > > partitions is detached, we should stop that partition's replication
    > > > but do we give WARNING for such cases?
    > > >
    > > > > 2. Inherited Tables Behavior
    > > > > Setup:
    > > > >  CREATE TABLE t_inh (id int);
    > > > >  CREATE TABLE t_inh_c1 (c1 int) INHERITS (t_inh);
    > > > >  CREATE TABLE t_inh_c2 (c2 int) INHERITS (t_inh);
    > > > > t_inh
    > > > >   |_> t_inh_c1
    > > > >   |_> t_inh_c2
    > > > >
    > > > > Publication:
    > > > >   create publication pubi1 for ALL TABLES EXCEPT TABLE (t_inh);
    > > > >  --All three tables are not replicated.
    > > > >
    > > > > 2a): Remove child from parent
    > > > >   alter table t_inh_c1 NO INHERIT t_inh;
    > > > >
    > > > > \d+ t_inh shows:
    > > > > Except Publications:
    > > > >     "pubi1"
    > > > > Child tables: t_inh_c2
    > > > >
    > > > > But the publication still shows:
    > > > > Except tables:
    > > > >     "public.t_inh"
    > > > >     "public.t_inh_c1"
    > > > >     "public.t_inh_c2"
    > > > >
    > > > > t_inh_c1 is no longer part of the excluded hierarchy, yet it remains
    > > > > in the EXCEPT list and replication does not start for it.
    > > > > This appears inconsistent and may be a bug.
    > > > >
    > > >
    > > > I think after removal, it shouldn't have been shown here, so, we
    > > > should see why it is happening.
    > >
    > > I believe this is existing behavior, and the same applies to
    > > table-level publications as well.
    > > For example:
    > > -- Create table publication
    > > create publication pubi1 for table t_inh;
    > > \d+ t_inh
    > >                                           Table "public.t_inh"
    > >  Column |  Type   | Collation | Nullable | Default | Storage |
    > > Compression | Stats target | Description
    > > --------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
    > >  id     | integer |           |          |         | plain   |
    > >     |              |
    > > Publications:
    > >     "pubi1"
    > > Child tables: t_inh_c1,
    > >               t_inh_c2
    > >
    > > postgres=# \dRp+
    > >                                                Publication pubi1
    > >   Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > > Truncates | Generated columns | Via root
    > > ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------
    > >  vignesh | f          | f             | t       | t       | t       |
    > > t         | none              | f
    > > Tables:
    > >     "public.t_inh"
    > >     "public.t_inh_c1"
    > >     "public.t_inh_c2"
    > >
    > > --- Remove the inherits for one of the child table
    > > postgres=#   alter table t_inh_c1 NO INHERIT t_inh;
    > > ALTER TABLE
    > >
    > > --- Publication will still have t_inh_c1 table in case of table publication too
    > > postgres=# \dRp+
    > >                                                Publication pubi1
    > >   Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    > > Truncates | Generated columns | Via root
    > > ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------
    > >  vignesh | f          | f             | t       | t       | t       |
    > > t         | none              | f
    > > Tables:
    > >     "public.t_inh"
    > >     "public.t_inh_c1"
    > >     "public.t_inh_c2"
    > >
    > > This occurs because when ONLY is not specified, the table t_inh and
    > > all its descendant tables (t_inh_c1 and t_inh_c2) are added to
    > > pg_publication_rel at the time the publication is created. Replication
    > > then proceeds strictly based on the entries in pg_publication_rel.
    > > There is no explicit parent child mapping maintained for these
    > > relations in the publication catalog; each table is treated as an
    > > independent entry.
    > > When "ALTER TABLE t_inh_c1 NO INHERIT t_inh;" is executed later,
    > > t_inh_c1 is not automatically removed from the publication. Since it
    > > was added as a separate entry in pg_publication_rel, removing the
    > > inheritance relationship does not affect its publication membership.
    > > I'm not sure whether this is the intended design or an unintended side
    > > effect. If this behavior is not expected, we can address it separately
    > > and adjust the implementation accordingly.
    > >
    >
    > So, the similar case for partitioned table works because there we add
    > only root partitioned table in pg_publication_rel. I understand that
    > current behaviour for inherited tables (each inherited table is
    > considered separately after being added to the publication) is because
    > of the existing design and should be changed separately, if required.
    > I haven't checked whether it is possible to change this behavior but
    > it appears that we should try to investigate and see the possibility
    > of fixing it separately.
    >
    
    IMO, we do not need any change in behaviour here.
    
    For inherited tables, publication membership is materialized at the
    time the publication is created and is not dynamically derived from
    the current inheritance hierarchy. Once inherited tables are added to
    a publication, each table is treated as an independent publication
    member going forward, regardless of subsequent changes to the
    inheritance relationship.
    This differs from partitioned tables in PostgreSQL. In the case of
    partitioning, publication membership is derived dynamically from the
    partition hierarchy. Partitions are not independent logical tables;
    they are structural components of a single logical table. Thus
    attaching or detaching a partition automatically impacts publication
    behavior. IMO, this distinction between inherited and partitioned
    tables is fundamental to their design, and thus should be preserved
    as-is, including in the context of publications.  Or let me know if my
    understanding is not correct here.
    
    thanks
    Shveta
    
    
    
    
  269. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-02T04:46:25Z

    On Sun, Mar 1, 2026 at 8:41 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    >
    > The attached v53 version patch has the changes for the same.
    >
    
    Thanks. Please find a few comments on 001:
    
    1)
    pg_get_publication_sequences():
    - sequences = GetAllPublicationRelations(RELKIND_SEQUENCE, false);
    + sequences = GetAllPublicationRelations(publication, RELKIND_SEQUENCE);
    
    This could be problematic. Earlier we were passing 'false' always for
    sequences, now we are letting it extracted from publication which can
    be true as well and thus we may hit Assert inside
    GetAllPublicationRelations() :
    
    Assert(!(relkind == RELKIND_SEQUENCE && pubviaroot));
    
    As an example, this will assert:
    create sequence seq1;
    create publication pub1 for all sequences with
    (publish_via_partition_root=true);
    select * from pg_publication_sequences ;
    
    2)
    get_rel_sync_entry():
    
    + if (pub->pubviaroot)
    + {
    + pub_relid = llast_oid(ancestors);
    + ancestor_level = list_length(ancestors);
    + }
    + else
    + {
    + /*
    + * When publishing partitions individually, we must
    + * still check whether the top-most ancestor is
    + * excluded. If the root is excluded, none of its
    + * partitions should be published.
    + */
    + ancestorexceptpubids = GetRelationExcludedPublications(llast_oid(ancestors));
    + }
    +
    + exceptpubids = GetRelationExcludedPublications(pub_relid);
    
    Bot pub->pubviaroot=On/OFF will ultimately end up doing these 2 steps:
    
    ---First get llast_oid(ancestors)
    -- Then execute GetRelationExcludedPublications().  (outside if-block
    but inside else-block)
    
    Why do we need an if-else block at all, as both are doing the same steps?
    
    And why do we need 2 variables except pubids and ancestorexceptpubids?
    
    
    3)
    +bool
    +publication_has_any_except_table(Oid pubid)
    
    I do not see its usage anywhere now. Shall we remove it?
    
    4)
    +static List *
    +GetRelationPublications(Oid relid, bool except_flag)
    
    Since it is static function, we shall rename it to
    get_relation_publications() similar to get_publication_relations()
    
    5)
    +/* Gets list of publication oids for a relation */
    +List *
    +GetRelationIncludedPublications(Oid relid)
    
    +/* Gets list of publication oids which has relation in EXCEPT clause */
    +List *
    +GetRelationExcludedPublications(Oid relid)
    
    We can make comment format multi-line similar to other functions in
    this file. See GetAllTablesPublications, GetSchemaPublications etc.
    
    ~~
    Reviewing further.
    
    thanks
    Shveta
    
    
    
    
  270. Re: Skipping schema changes in publication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-03-02T07:37:44Z

    On Sat, Feb 28, 2026 at 5:27 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Feb 27, 2026 at 8:44 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > On Fri, Feb 27, 2026 at 5:01 PM Nisha Moond <nisha.moond412@gmail.com> wrote:
    > > >
    > > > Thanks for the patches, I am still testing the patches, but sharing a
    > > > few initial review comments.
    > > >
    > >
    > > I did few tests on v51 all patches, please find further comments -
    > >
    > > 1. Partition Related Behavior
    > > -----------------------------
    > > Setup:
    > > CREATE TABLE t_part (id int) PARTITION BY RANGE (id);
    > > CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
    > > CREATE TABLE t_part_p2 (id int);
    > > ALTER TABLE t_part ATTACH PARTITION t_part_p2 FOR VALUES FROM (100) TO (200);
    > >
    > > t_part
    > >   |_> t_part_p1
    > >   |_> t_part_p2
    > >
    > > Publication:
    > >   create publication pubp1 for ALL TABLES EXCEPT TABLE (t_part);
    > >  -- No data from all three tables is replicated, which is expected.
    > >
    > > 1a) DETACH t_part_p2 from parent
    > > ALTER TABLE t_part DETACH PARTITION t_part_p2;
    > >
    > > When t_part_p2 is detached, it becomes a standalone table and is no
    > > longer part of the EXCEPT hierarchy. But, replication does not start
    > > automatically. It requires REFRESH PUBLICATION on the subscriber.
    > > Should we add a warning/hint during DETACH command to inform users
    > > that REFRESH PUBLICATION is required?
    > > It may also help to update the related documentation.
    > >
    >
    > This is required even when a regular (non-partitioned table) is
    > created to let the corresponding entry populated in
    > pg_subscription_rel. See docs[1] (REFRESH PUBLICATION ..).
    >
    
    Thanks for the doc link. The expectation seems that when a user adds a
    table to a publication, replication of that table will begin after
    running REFRESH PUBLICATION. But, in this case, the table is added
    indirectly (e.g., via a partition detach), which may not make it
    obvious to userthat a refresh is required.
    
    That said, I agree that users can handle it if they are aware of this behavior.
    
    > > ~~
    > > 2. Inherited Tables Behavior
    > > Setup:
    > >  CREATE TABLE t_inh (id int);
    > >  CREATE TABLE t_inh_c1 (c1 int) INHERITS (t_inh);
    > >  CREATE TABLE t_inh_c2 (c2 int) INHERITS (t_inh);
    > > t_inh
    > >   |_> t_inh_c1
    > >   |_> t_inh_c2
    > >
    > > Publication:
    > >   create publication pubi1 for ALL TABLES EXCEPT TABLE (t_inh);
    > >  --All three tables are not replicated.
    > >
    > > 2a): Remove child from parent
    > >   alter table t_inh_c1 NO INHERIT t_inh;
    > >
    > > \d+ t_inh shows:
    > > Except Publications:
    > >     "pubi1"
    > > Child tables: t_inh_c2
    > >
    > > But the publication still shows:
    > > Except tables:
    > >     "public.t_inh"
    > >     "public.t_inh_c1"
    > >     "public.t_inh_c2"
    > >
    > > t_inh_c1 is no longer part of the excluded hierarchy, yet it remains
    > > in the EXCEPT list and replication does not start for it.
    > > This appears inconsistent and may be a bug.
    > >
    >
    > I think after removal, it shouldn't have been shown here, so, we
    > should see why it is happening. OTOH, won't replication requires user
    > to perform REFRESH PUBLICATION as in the previous case?
    >
    
    Yes, this requires the user to run REFRESH PUBLICATION, but
    replication still does not start in this case. The user must reset the
    EXCEPT TABLE list to start replication for t_inh_c1.
    
    --
    Thanks,
    Nisha
    
    
    
    
  271. Re: Skipping schema changes in publication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-03-02T07:39:07Z

    On Sun, Mar 1, 2026 at 6:21 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Sun, Mar 1, 2026 at 5:52 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Sat, Feb 28, 2026 at 7:37 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > This occurs because when ONLY is not specified, the table t_inh and
    > > > all its descendant tables (t_inh_c1 and t_inh_c2) are added to
    > > > pg_publication_rel at the time the publication is created. Replication
    > > > then proceeds strictly based on the entries in pg_publication_rel.
    > > > There is no explicit parent child mapping maintained for these
    > > > relations in the publication catalog; each table is treated as an
    > > > independent entry.
    > > > When "ALTER TABLE t_inh_c1 NO INHERIT t_inh;" is executed later,
    > > > t_inh_c1 is not automatically removed from the publication. Since it
    > > > was added as a separate entry in pg_publication_rel, removing the
    > > > inheritance relationship does not affect its publication membership.
    > > > I'm not sure whether this is the intended design or an unintended side
    > > > effect. If this behavior is not expected, we can address it separately
    > > > and adjust the implementation accordingly.
    > > >
    > >
    > > So, the similar case for partitioned table works because there we add
    > > only root partitioned table in pg_publication_rel. I understand that
    > > current behaviour for inherited tables (each inherited table is
    > > considered separately after being added to the publication) is because
    > > of the existing design and should be changed separately, if required.
    > > I haven't checked whether it is possible to change this behavior but
    > > it appears that we should try to investigate and see the possibility
    > > of fixing it separately.
    > >
    >
    > IMO, we do not need any change in behaviour here.
    >
    > For inherited tables, publication membership is materialized at the
    > time the publication is created and is not dynamically derived from
    > the current inheritance hierarchy. Once inherited tables are added to
    > a publication, each table is treated as an independent publication
    > member going forward, regardless of subsequent changes to the
    > inheritance relationship.
    > This differs from partitioned tables in PostgreSQL. In the case of
    > partitioning, publication membership is derived dynamically from the
    > partition hierarchy. Partitions are not independent logical tables;
    > they are structural components of a single logical table. Thus
    > attaching or detaching a partition automatically impacts publication
    > behavior. IMO, this distinction between inherited and partitioned
    > tables is fundamental to their design, and thus should be preserved
    > as-is, including in the context of publications.  Or let me know if my
    > understanding is not correct here.
    >
    
    Okay, noted!
    My main concern is that whenever a user attaches or detaches a child
    table, they must manually reset the publication EXCEPT list if they
    want the table to be replicated. The doc under “For inherited
    tables...” --
    
      “If ONLY is not specified, the table and all its descendant tables
    (if any) are excluded.”
    
    does not clearly state that this applies only at the time of
    CREATE/ALTER PUBLICATION, and that users must explicitly handle
    changes if the inheritance hierarchy is modified later.
    
    I am fine with updating the doc accordingly.
    
    --
    Thanks,
    Nisha
    
    
    
    
  272. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-02T10:18:14Z

    All,
    
    Currently, the syntax we plan to support in patch001 is:
    
    CREATE PUBLICATION pub for ALL TABLES EXCEPT TABLE (tab1, tab2, ...)
    
    That is:
    
    a) The TABLE keyword is mandatory after EXCEPT.
    b) Parentheses around the table list are mandatory.
    
    There has been some confusion regarding the EXCEPT syntax. Below are
    the points that help justify the above syntax.
    
    1) TABLE keyword:
    
    Consider the command:
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT (tab1, tab2);
    
    At first glance, this appears acceptable even without the TABLE
    keyword after EXCEPT. However, in the future, if we support excluding
    a particular schema, for example:
    
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT SCHEMA s1, s2;
    
    then it becomes necessary to explicitly specify whether we are
    excluding a TABLE, SCHEMA, or SEQUENCE after EXCEPT.
    
    Similarly, if we extend CREATE PUBLICATION to support something like:
    CREATE PUBLICATION pub
    FOR TABLES, SEQUENCES IN SCHEMA sch1
    EXCEPT TABLE (t1), EXCEPT SEQUENCE (s1);
    
    then also, the TABLE or SEQUENCE keyword after EXCEPT becomes
    mandatory for clarity.
    
    2) Parentheses:
    
    Currently, we do not use parentheses around table names in commands such as:
    CREATE PUBLICATION pub1 FOR TABLE tab1, tab2;
    
    So it may seem acceptable to allow:
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT tab1, tab2;
    
    However, if we later support syntax like:
    
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT TABLE t;
    
    the future syntax for pub with tables-in-schema with EXCEPT and a
    regular table-inclusion might look like:
    
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT TABLE (t1, t2), TABLE t3;
    
    This is much clearer than a version without parentheses:
    
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT TABLE t1, t2, TABLE t3;
    
    In the first version, it is clearly understood that t1 and t2 are
    excluded, while t3 is included. Without parentheses, the intent
    becomes ambiguous.
    
    Therefore, making both the TABLE keyword and parentheses mandatory
    after EXCEPT ensures clarity and extensibility for future
    enhancements.
    
    ~~
    
    So we will proceed with this syntax unless there are any objections.
    
    thanks
    Shveta
    
    
    
    
  273. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-02T10:48:13Z

    On Sun, Mar 1, 2026 at 8:41 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > The attached v53 version patch has the changes for the same.
    >
    
    Few comments on 0001:
    ===================
    1.
    + appendStringInfoString(&pubnames, quote_literal_cstr(pubname));
    + }
    +
    + ereport(ERROR,
    + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg_plural("cannot attach table \"%s\" as partition because it is
    referenced in publication \"%s\" EXCEPT clause",
    +   "cannot attach table \"%s\" as partition because it is referenced
    in publications \"%s\" EXCEPT clause",
    
    Because of the quoting before the error message, the publication names
    in the error message will be displayed in single quotes which are
    inside double quotes.
    See below examples:
    ERROR:  cannot attach table "testpub_root" as partition because it is
    referenced in publication "'testpub8'" EXCEPT clause
    ERROR:  cannot attach table "testpub_root" as partition because it is
    referenced in publications "'testpub8', 'testpub10'" EXCEPT clause
    
    Do you this type of quoting single/multiple object names in error
    messages at other places? What is the rationale behind this?
    
    The other place where I see such names is following but it also
    doesn't use the pattern followed above:
    A.
    postgres=# create subscription sub1 connection 'dbname=postgres'
    publication pub9, pub10, pub11;
    WARNING:  publications "pub9", "pub10", "pub11" do not exist on the publisher
    postgres=# create subscription sub2 connection 'dbname=postgres'
    publication pub12;
    WARNING:  publication "pub12" does not exist on the publisher
    
    B.
    logical replication target relation "public.t10" is missing replicated
    columns: "c2", "c3". For this See logicalrep_get_attrs_str and
    following error_message
    ereport(ERROR,
    errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    errmsg_plural("logical replication target relation \"%s.%s\" is
    missing replicated column: %s",
      "logical replication target relation \"%s.%s\" is missing replicated
    columns: %s",
    
    I think we should follow what is done in logicalrep_get_attrs_str for
    message construction and translation.
    
    2.
      * pub_all_obj_type is one of:
      *
    - * TABLES
    + * TABLES [EXCEPT [TABLE] ( table [, ...] )]
    
    Though the table is no longer optional, the above syntax reference
    still shows it as optional.
    
    3.
    + List    *ancestor_exceptpuboids = GetRelationExcludedPublications(ancestor);;
    
    One spurious semicolon at the end of above statement
    
    4.
    @@ -5838,16 +5841,22 @@ RelationBuildPublicationDesc(Relation
    relation, PublicationDesc *pubdesc)
      foreach(lc, ancestors)
      {
      Oid ancestor = lfirst_oid(lc);
    + List    *ancestor_puboids = GetRelationIncludedPublications(ancestor);
    + List    *ancestor_exceptpuboids = GetRelationExcludedPublications(ancestor);;
    
    - puboids = list_concat_unique_oid(puboids,
    - GetRelationPublications(ancestor));
    + puboids = list_concat_unique_oid(puboids, ancestor_puboids);
      schemaid = get_rel_namespace(ancestor);
      puboids = list_concat_unique_oid(puboids,
      GetSchemaPublications(schemaid));
    + exceptpuboids = list_concat_unique_oid(exceptpuboids,
    +    ancestor_exceptpuboids);
      }
    
    Why do we need to get the exceptpuboids for all ancestors instead of
    just the top-one as we are doing in get_rel_sync_entry()?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  274. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-03-02T13:21:21Z

    On Mon, 2 Mar 2026 at 16:18, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Sun, Mar 1, 2026 at 8:41 AM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > The attached v53 version patch has the changes for the same.
    > >
    >
    > Few comments on 0001:
    > ===================
    > 1.
    > + appendStringInfoString(&pubnames, quote_literal_cstr(pubname));
    > + }
    > +
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg_plural("cannot attach table \"%s\" as partition because it is
    > referenced in publication \"%s\" EXCEPT clause",
    > +   "cannot attach table \"%s\" as partition because it is referenced
    > in publications \"%s\" EXCEPT clause",
    >
    > Because of the quoting before the error message, the publication names
    > in the error message will be displayed in single quotes which are
    > inside double quotes.
    > See below examples:
    > ERROR:  cannot attach table "testpub_root" as partition because it is
    > referenced in publication "'testpub8'" EXCEPT clause
    > ERROR:  cannot attach table "testpub_root" as partition because it is
    > referenced in publications "'testpub8', 'testpub10'" EXCEPT clause
    >
    > Do you this type of quoting single/multiple object names in error
    > messages at other places? What is the rationale behind this?
    >
    > The other place where I see such names is following but it also
    > doesn't use the pattern followed above:
    > A.
    > postgres=# create subscription sub1 connection 'dbname=postgres'
    > publication pub9, pub10, pub11;
    > WARNING:  publications "pub9", "pub10", "pub11" do not exist on the publisher
    > postgres=# create subscription sub2 connection 'dbname=postgres'
    > publication pub12;
    > WARNING:  publication "pub12" does not exist on the publisher
    >
    > B.
    > logical replication target relation "public.t10" is missing replicated
    > columns: "c2", "c3". For this See logicalrep_get_attrs_str and
    > following error_message
    > ereport(ERROR,
    > errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > errmsg_plural("logical replication target relation \"%s.%s\" is
    > missing replicated column: %s",
    >   "logical replication target relation \"%s.%s\" is missing replicated
    > columns: %s",
    >
    > I think we should follow what is done in logicalrep_get_attrs_str for
    > message construction and translation.
    >
    I agree. Made the changes for the same.
    
    > 2.
    >   * pub_all_obj_type is one of:
    >   *
    > - * TABLES
    > + * TABLES [EXCEPT [TABLE] ( table [, ...] )]
    >
    > Though the table is no longer optional, the above syntax reference
    > still shows it as optional.
    >
    > 3.
    > + List    *ancestor_exceptpuboids = GetRelationExcludedPublications(ancestor);;
    >
    > One spurious semicolon at the end of above statement
    >
    > 4.
    > @@ -5838,16 +5841,22 @@ RelationBuildPublicationDesc(Relation
    > relation, PublicationDesc *pubdesc)
    >   foreach(lc, ancestors)
    >   {
    >   Oid ancestor = lfirst_oid(lc);
    > + List    *ancestor_puboids = GetRelationIncludedPublications(ancestor);
    > + List    *ancestor_exceptpuboids = GetRelationExcludedPublications(ancestor);;
    >
    > - puboids = list_concat_unique_oid(puboids,
    > - GetRelationPublications(ancestor));
    > + puboids = list_concat_unique_oid(puboids, ancestor_puboids);
    >   schemaid = get_rel_namespace(ancestor);
    >   puboids = list_concat_unique_oid(puboids,
    >   GetSchemaPublications(schemaid));
    > + exceptpuboids = list_concat_unique_oid(exceptpuboids,
    > +    ancestor_exceptpuboids);
    >   }
    >
    > Why do we need to get the exceptpuboids for all ancestors instead of
    > just the top-one as we are doing in get_rel_sync_entry()?
    >
    I agree we should only check the root partitioned table here. Modified the code
    
    I have also addressed the other comments.
    I have addressed the comments shared by Shveta in [1].
    Attached the updated v54 patch.
    
    [1]: https://www.postgresql.org/message-id/CAJpy0uCr15=dxg+bmGeJUoNfKOHU2xZd2Wa6hg=YNTnQzz2fcA@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  275. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-03T04:37:38Z

    On Mon, Mar 2, 2026 at 6:51 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > Attached the updated v54 patch.
    >
    
    The patch is getting in shape. I have a few minor comments:
    
    1.
    + if (!first)
    + appendStringInfoString(&pubnames, _(", "));
    +
    + first = false;
    +
    + appendStringInfo(&pubnames, _("\"%s\""), pubname);
    
    Can we have a comment here like "/* translator: This is a separator in
    a list of publication names. */" similar to logicalrep_get_attrs_str.
    
    2.
    + List    *ancestor_puboids = GetRelationIncludedPublications(ancestor);
    
    - puboids = list_concat_unique_oid(puboids,
    - GetRelationPublications(ancestor));
    + puboids = list_concat_unique_oid(puboids, ancestor_puboids);
    
    Why do you choose to take a variable name instead of changing just a
    function name?
    
    3.
    src/test/subscription/t/037_rep_changes_except_table.pl
    
    How about naming it as 037_except.pl? We would like to add future
    except syntax variations in the same test file, so naming just except
    would be better.
    
    4.
    + * EXCEPT TABLES is processed here and output directly by
    + * dumpPublication(). This differs from the approach used in
    + * dumpPublicationTable() and dumpPublicationNamespace(), since that
    + * approach would require EXCEPT TABLE support for ALTER PUBLICATION,
    + * which is not currently supported.
    
    Is this comment accurate as per our current implementation of Alter
    Publication in 0002 patch? If not, let's change these comments.
    
    5.
    + * Only the top-most ancestor may appear in the EXCEPT
    + * clause. Therefore, for a partition, exclusion must be
    + * evaluated at the top-most ancestor.
    
    /may/can
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  276. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-03T09:29:11Z

    On Tue, Mar 3, 2026 at 10:07 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Mon, Mar 2, 2026 at 6:51 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > Attached the updated v54 patch.
    > >
    >
    > The patch is getting in shape. I have a few minor comments:
    >
    > 1.
    > + if (!first)
    > + appendStringInfoString(&pubnames, _(", "));
    > +
    > + first = false;
    > +
    > + appendStringInfo(&pubnames, _("\"%s\""), pubname);
    >
    > Can we have a comment here like "/* translator: This is a separator in
    > a list of publication names. */" similar to logicalrep_get_attrs_str.
    >
    > 2.
    > + List    *ancestor_puboids = GetRelationIncludedPublications(ancestor);
    >
    > - puboids = list_concat_unique_oid(puboids,
    > - GetRelationPublications(ancestor));
    > + puboids = list_concat_unique_oid(puboids, ancestor_puboids);
    >
    > Why do you choose to take a variable name instead of changing just a
    > function name?
    >
    > 3.
    > src/test/subscription/t/037_rep_changes_except_table.pl
    >
    > How about naming it as 037_except.pl? We would like to add future
    > except syntax variations in the same test file, so naming just except
    > would be better.
    >
    
    +1
    
    > 4.
    > + * EXCEPT TABLES is processed here and output directly by
    > + * dumpPublication(). This differs from the approach used in
    > + * dumpPublicationTable() and dumpPublicationNamespace(), since that
    > + * approach would require EXCEPT TABLE support for ALTER PUBLICATION,
    > + * which is not currently supported.
    >
    > Is this comment accurate as per our current implementation of Alter
    > Publication in 0002 patch? If not, let's change these comments.
    >
    > 5.
    > + * Only the top-most ancestor may appear in the EXCEPT
    > + * clause. Therefore, for a partition, exclusion must be
    > + * evaluated at the top-most ancestor.
    >
    > /may/can
    >
    > --
    > With Regards,
    > Amit Kapila.
    
    The patch looks good.  I have a few comments already shared by Amit.
    Apart from that, one trivial comment
    
    + * relation with EXCEPT clause; otherwise, returns the list of publications
    with --> in
    
    thanks
    Shveta
    
    
    
    
  277. Re: Skipping schema changes in publication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-03-03T10:15:19Z

    On Mon, Mar 2, 2026 at 6:51 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > Attached the updated v54 patch.
    >
    
    Few comments for v54 patch -
    1) The partition description shows the publication name which it is
    excluded from.
    simple test case:
    
     CREATE TABLE t_part (a int) PARTITION BY RANGE(a);
     CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
     CREATE PUBLICATION pub_p1 FOR ALL TABLES EXCEPT TABLE (t_part);
    postgres=# \d t_part_p1
                 Table "public.t_part_p1"
     Column |  Type   | Collation | Nullable | Default
    --------+---------+-----------+----------+---------
     a      | integer |           |          |
    Partition of: t_part FOR VALUES FROM (0) TO (100)
    Publications:
        "pub_p1"
    ~~~
    
    2) File: 037_rep_changes_except_table.pl:147
    +# Verify that data inserted into a table listed in the EXCEPT clause is not
    +# published.
    +$result = $node_publisher->safe_psql('postgres',
    + "SELECT count(*) = 0 FROM
    pg_logical_slot_get_binary_changes('test_slot', NULL, NULL,
    'proto_version', '1', 'publication_names', 'tap_sub_schema')"
    +);
    
    There seems to be a mistake in the publication name in the above call
    as tap_sub_schema is a subscription name.
    ~~~
    Couple of minor comments:
    3) File: pgouput.c
    + /*
    + * For a parition, changes are published via top-most
    + * ancestor when pubviaroot is true, so populate pub_relid
    + * accordingly
    + */
    
    3a) typo parition -> partition
    3b) There should be a full stop (.) at the end i.e. after accordingly
    ~~~
    
    File: pg_publication.c
     + else
     + errormsg = gettext_noop("cannot add relation \"%s\" to publication");
     +
     +
     + /* If in EXCEPT clause, must be root partitioned table */
    
    >> there is an extra empty line
    ~~~
    
    --
    Thanks,
    Nisha
    
    
    
    
  278. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-03-03T15:46:44Z

    On Tue, 3 Mar 2026 at 15:45, Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Mon, Mar 2, 2026 at 6:51 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > Attached the updated v54 patch.
    > >
    >
    > Few comments for v54 patch -
    > 1) The partition description shows the publication name which it is
    > excluded from.
    > simple test case:
    >
    >  CREATE TABLE t_part (a int) PARTITION BY RANGE(a);
    >  CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
    >  CREATE PUBLICATION pub_p1 FOR ALL TABLES EXCEPT TABLE (t_part);
    > postgres=# \d t_part_p1
    >              Table "public.t_part_p1"
    >  Column |  Type   | Collation | Nullable | Default
    > --------+---------+-----------+----------+---------
    >  a      | integer |           |          |
    > Partition of: t_part FOR VALUES FROM (0) TO (100)
    > Publications:
    >     "pub_p1"
    > ~~~
    >
    Fixed. Added a test for it as well.
    
    > 2) File: 037_rep_changes_except_table.pl:147
    > +# Verify that data inserted into a table listed in the EXCEPT clause is not
    > +# published.
    > +$result = $node_publisher->safe_psql('postgres',
    > + "SELECT count(*) = 0 FROM
    > pg_logical_slot_get_binary_changes('test_slot', NULL, NULL,
    > 'proto_version', '1', 'publication_names', 'tap_sub_schema')"
    > +);
    >
    > There seems to be a mistake in the publication name in the above call
    > as tap_sub_schema is a subscription name.
    > ~~~
    > Couple of minor comments:
    > 3) File: pgouput.c
    > + /*
    > + * For a parition, changes are published via top-most
    > + * ancestor when pubviaroot is true, so populate pub_relid
    > + * accordingly
    > + */
    >
    > 3a) typo parition -> partition
    > 3b) There should be a full stop (.) at the end i.e. after accordingly
    > ~~~
    >
    > File: pg_publication.c
    >  + else
    >  + errormsg = gettext_noop("cannot add relation \"%s\" to publication");
    >  +
    >  +
    >  + /* If in EXCEPT clause, must be root partitioned table */
    >
    > >> there is an extra empty line
    > ~~~
    >
    I have addressed the remaining comments as well.
    I have also addressed the comments by Amit and Shveta in [1] and [2].
    
    Attached the latest v55 patch.
    
    [1]: https://www.postgresql.org/message-id/CAA4eK1+2mL0N8iUdNTr1baO9kJjDZgRGiNTX6cT=ZoBm-m_Lqg@mail.gmail.com
    [2]: https://www.postgresql.org/message-id/CAJpy0uA7SKHvi2uZ=W2VaoGvh_5F_BOOUwvGC22L8+t4NBE8jQ@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  279. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-04T08:24:29Z

    On Tue, 3 Mar 2026 at 21:17, Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > On Tue, 3 Mar 2026 at 15:45, Nisha Moond <nisha.moond412@gmail.com> wrote:
    > >
    > > On Mon, Mar 2, 2026 at 6:51 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > > >
    > > > Attached the updated v54 patch.
    > > >
    > >
    > > Few comments for v54 patch -
    > > 1) The partition description shows the publication name which it is
    > > excluded from.
    > > simple test case:
    > >
    > >  CREATE TABLE t_part (a int) PARTITION BY RANGE(a);
    > >  CREATE TABLE t_part_p1 PARTITION OF t_part FOR VALUES FROM (0) TO (100);
    > >  CREATE PUBLICATION pub_p1 FOR ALL TABLES EXCEPT TABLE (t_part);
    > > postgres=# \d t_part_p1
    > >              Table "public.t_part_p1"
    > >  Column |  Type   | Collation | Nullable | Default
    > > --------+---------+-----------+----------+---------
    > >  a      | integer |           |          |
    > > Partition of: t_part FOR VALUES FROM (0) TO (100)
    > > Publications:
    > >     "pub_p1"
    > > ~~~
    > >
    > Fixed. Added a test for it as well.
    >
    > > 2) File: 037_rep_changes_except_table.pl:147
    > > +# Verify that data inserted into a table listed in the EXCEPT clause is not
    > > +# published.
    > > +$result = $node_publisher->safe_psql('postgres',
    > > + "SELECT count(*) = 0 FROM
    > > pg_logical_slot_get_binary_changes('test_slot', NULL, NULL,
    > > 'proto_version', '1', 'publication_names', 'tap_sub_schema')"
    > > +);
    > >
    > > There seems to be a mistake in the publication name in the above call
    > > as tap_sub_schema is a subscription name.
    > > ~~~
    > > Couple of minor comments:
    > > 3) File: pgouput.c
    > > + /*
    > > + * For a parition, changes are published via top-most
    > > + * ancestor when pubviaroot is true, so populate pub_relid
    > > + * accordingly
    > > + */
    > >
    > > 3a) typo parition -> partition
    > > 3b) There should be a full stop (.) at the end i.e. after accordingly
    > > ~~~
    > >
    > > File: pg_publication.c
    > >  + else
    > >  + errormsg = gettext_noop("cannot add relation \"%s\" to publication");
    > >  +
    > >  +
    > >  + /* If in EXCEPT clause, must be root partitioned table */
    > >
    > > >> there is an extra empty line
    > > ~~~
    > >
    > I have addressed the remaining comments as well.
    > I have also addressed the comments by Amit and Shveta in [1] and [2].
    >
    > Attached the latest v55 patch.
    
    Here is an updated version with a couple of typos fixed and separated
    the describe table query based on versions which make it more easier
    to read.
    
    Regards,
    Vignesh
    
  280. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-04T09:19:07Z

    On Wed, Mar 4, 2026 at 1:54 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Here is an updated version with a couple of typos fixed and separated
    > the describe table query based on versions which make it more easier
    > to read.
    >
    
    v56 LGTM.
    
    thanks
    Shveta
    
    
    
    
  281. Re: Skipping schema changes in publication

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

    On Wed, Mar 4, 2026 at 1:54 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Here is an updated version with a couple of typos fixed and separated
    > the describe table query based on versions which make it more easier
    > to read.
    >
    
    Thanks for the patch. It LGTM, no further comments.
    
    --
    Thanks,
    Nisha
    
    
    
    
  282. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-04T11:00:48Z

    On Wed, Mar 4, 2026 at 2:49 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Mar 4, 2026 at 1:54 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Here is an updated version with a couple of typos fixed and separated
    > > the describe table query based on versions which make it more easier
    > > to read.
    > >
    >
    > v56 LGTM.
    >
    
    Pushed.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  283. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-05T09:35:42Z

    On Wed, 4 Mar 2026 at 16:31, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, Mar 4, 2026 at 2:49 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Wed, Mar 4, 2026 at 1:54 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > Here is an updated version with a couple of typos fixed and separated
    > > > the describe table query based on versions which make it more easier
    > > > to read.
    > > >
    > >
    > > v56 LGTM.
    > >
    >
    > Pushed.
    
    Here is the rebased patch for the remaining items in the patch series.
    
    Regards,
    Vignesh
    
  284. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-05T11:52:09Z

    On Thu, Mar 5, 2026 at 3:05 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    >
    > Here is the rebased patch for the remaining items in the patch series.
    >
    
    Thanks. Please find a few comments for 001:
    
    
    1)
    alter_publication.sgml:
    
    The SET clause will replace the list of except tables/tables/schemas
    in the publication with the specified list
    
    The order is difficult to read. Would it be better to say it in this
    order: tables/schemas/except-tables
    
    2)
    postgres=# ALTER TABLE tab_top_root ATTACH PARTITION tab_root FOR
    VALUES FROM (0) TO (2000);
    ERROR:  cannot attach table "tab_root" as partition because it is
    referenced in publications "pub1", "pub3" EXCEPT clause
    DETAIL:  The publication EXCEPT clause cannot contain tables that are
    partitions.
    HINT:  Modify the publication's EXCEPT clause using ALTER PUBLICATION
    ... SET EXCEPT TABLE or DROP EXCEPT TABLE before attaching the table.
    
    Shall we shorten the HINT slightly? Is it necessary to explicitly say
    "before attaching the table", or can we omit it? Thoughts?
    
    3)
    + if (has_except_table)
    + ereport(ERROR,
    + errcode(ERRCODE_SYNTAX_ERROR),
    + errmsg("EXCEPT TABLE clause allowed only for ALL TABLES PUBLICATION"));
    +
    
    In what scenario will we hit it? I could not find one.
    
    4)
    -- fail - can't add an EXCEPT TABLE to 'FOR TABLE' publication
    ALTER PUBLICATION testpub_fortable ADD EXCEPT TABLE (testpub_tbl1);
    ERROR:  EXCEPT TABLE clause allowed only for SET clause
    
    Even though the comment says that it is a test for not allowing EXCEPT
    TABLE for 'FOR TABLE' (perhaps trying to hit the error in comment 3),
    it ultimately hits another error that EXCEPT is not supported with
    'ADD'. I think the error in comment-3 is unreachable. Or let me know
    if otherwise.
    We need to correct the comment here.
    
    5)
    -- fail - can't drop an EXCEPT TABLE from 'FOR TABLE' publication
    ALTER PUBLICATION testpub_fortable DROP EXCEPT TABLE (testpub_tbl1);
    ERROR:  EXCEPT TABLE clause allowed only for SET clause
    
    Same is valid for this, comment (i.e. the intent) and error does not match.
    
    ~~
    
    Please check other test-cases too for intent and error. There are many
    like above.
    
    thanks
    Shveta
    
    
    
    
  285. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-06T03:23:30Z

    On Thu, Mar 5, 2026 at 5:22 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > 2)
    > postgres=# ALTER TABLE tab_top_root ATTACH PARTITION tab_root FOR
    > VALUES FROM (0) TO (2000);
    > ERROR:  cannot attach table "tab_root" as partition because it is
    > referenced in publications "pub1", "pub3" EXCEPT clause
    > DETAIL:  The publication EXCEPT clause cannot contain tables that are
    > partitions.
    > HINT:  Modify the publication's EXCEPT clause using ALTER PUBLICATION
    > ... SET EXCEPT TABLE or DROP EXCEPT TABLE before attaching the table.
    >
    > Shall we shorten the HINT slightly? Is it necessary to explicitly say
    > "before attaching the table", or can we omit it? Thoughts?
    >
    
    I think we can omit it.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  286. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-06T08:17:06Z

    On Thu, 5 Mar 2026 at 17:22, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Thu, Mar 5, 2026 at 3:05 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > >
    > > Here is the rebased patch for the remaining items in the patch series.
    > >
    >
    > Thanks. Please find a few comments for 001:
    >
    >
    > 1)
    > alter_publication.sgml:
    >
    > The SET clause will replace the list of except tables/tables/schemas
    > in the publication with the specified list
    >
    > The order is difficult to read. Would it be better to say it in this
    > order: tables/schemas/except-tables
    
    I have changed it to tables/except tables/schemas to keep tables together
    
    > 2)
    > postgres=# ALTER TABLE tab_top_root ATTACH PARTITION tab_root FOR
    > VALUES FROM (0) TO (2000);
    > ERROR:  cannot attach table "tab_root" as partition because it is
    > referenced in publications "pub1", "pub3" EXCEPT clause
    > DETAIL:  The publication EXCEPT clause cannot contain tables that are
    > partitions.
    > HINT:  Modify the publication's EXCEPT clause using ALTER PUBLICATION
    > ... SET EXCEPT TABLE or DROP EXCEPT TABLE before attaching the table.
    >
    > Shall we shorten the HINT slightly? Is it necessary to explicitly say
    > "before attaching the table", or can we omit it? Thoughts?
    
    Modified to omit it.
    
    > 3)
    > + if (has_except_table)
    > + ereport(ERROR,
    > + errcode(ERRCODE_SYNTAX_ERROR),
    > + errmsg("EXCEPT TABLE clause allowed only for ALL TABLES PUBLICATION"));
    > +
    >
    > In what scenario will we hit it? I could not find one.
    
    It will hit in this scenario, also added a test for thi:
    create publication pub2 for except table (t1);
    ERROR:  EXCEPT TABLE clause allowed only for ALL TABLES PUBLICATION
    
    > 4)
    > -- fail - can't add an EXCEPT TABLE to 'FOR TABLE' publication
    > ALTER PUBLICATION testpub_fortable ADD EXCEPT TABLE (testpub_tbl1);
    > ERROR:  EXCEPT TABLE clause allowed only for SET clause
    >
    > Even though the comment says that it is a test for not allowing EXCEPT
    > TABLE for 'FOR TABLE' (perhaps trying to hit the error in comment 3),
    > it ultimately hits another error that EXCEPT is not supported with
    > 'ADD'. I think the error in comment-3 is unreachable. Or let me know
    > if otherwise.
    > We need to correct the comment here.
    
    This test can be removed as the error comes from the parser itself and
    there is one test already with testpub_foralltables_excepttable. This
    will be returned from:
                            | ALTER PUBLICATION name ADD_P pub_obj_list
                                    {
    +                                       bool has_except_table = false;
                                            AlterPublicationStmt *n =
    makeNode(AlterPublicationStmt);
    
                                            n->pubname = $3;
                                            n->pubobjects = $5;
    -
    preprocess_pubobj_list(n->pubobjects, yyscanner);
    +                                       has_except_table =
    preprocess_pubobj_list(n->pubobjects,
    +
                                                       yyscanner);
    +                                       if (has_except_table)
    +                                               ereport(ERROR,
    +
    errcode(ERRCODE_SYNTAX_ERROR),
    +
    errmsg("EXCEPT TABLE clause allowed only for SET clause"));
    +
                                            n->action = AP_AddObjects;
                                            $$ = (Node *) n;
                                    }
    
    > 5)
    > -- fail - can't drop an EXCEPT TABLE from 'FOR TABLE' publication
    > ALTER PUBLICATION testpub_fortable DROP EXCEPT TABLE (testpub_tbl1);
    > ERROR:  EXCEPT TABLE clause allowed only for SET clause
    >
    > Same is valid for this, comment (i.e. the intent) and error does not match.
    
    This test can be removed as the error comes from the parser itself and
    there is one test already with testpub_foralltables_excepttable.
    
    Thanks for the comments, the attached v58 version patch has the
    changes for the same.
    
    Regards,
    Vignesh
    
  287. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-06T10:56:39Z

    On Fri, Mar 6, 2026 at 1:47 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    
    Instead of a syntax like "ALTER PUBLICATION pub1 DROP EXCEPT TABLE t1"
    to allow resetting the entire except list by incrementally dropping
    the except tables, I could think of following alternatives
    
    Option-1: ALTER PUBLICATION pub1 SET ALL TABLES; This suggests it is
    still an ALL TABLES publication, but providing a new definition. Since
    it didn't include an EXCEPT clause this time, the exception list is
    now empty.
    
    Option-2: ALTER PUBLICATION pub1 SET EXCEPT TABLE DEFAULT; Since the
    "default" state of an ALL TABLES publication is to have zero
    exceptions, the "default" will serve as an alias for an empty list.
    
    If we follow the first one, then we can choose "ALTER PUBLICATION pub1
    SET ALL TABLES EXCEPT TABLE (t1)" to set a new except list instead of
    "ALTER PUBLICATION pub1 SET EXCEPT TABLE (t1)"
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  288. Re: Skipping schema changes in publication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-03-06T11:20:53Z

    On Fri, Mar 6, 2026 at 1:47 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Thanks for the comments, the attached v58 version patch has the
    > changes for the same.
    >
    
    Thanks for the patches. I reviewed and tested v58-0001, and here are a
    few comments:
    
    1) alter_publication.sgml
    +<phrase>where <replaceable
    class="parameter">publication_set_object</replaceable> is one
    of:</phrase>
    +
    +    EXCEPT TABLE [ ONLY ] ( <replaceable
    class="parameter">table_name</replaceable> [, ... ] )
    
    Should ONLY be moved inside parentheses(), as it applies per table:-
    EXCEPT TABLE ( [ ONLY ] table_name [, ...] )
    
    2) publicationcmds.c
      postgres=# alter publication pub2 set EXCEPT TABLE (t3);
      ERROR:  publication "pub2" is not defined as FOR ALL TABLES
      DETAIL:  EXCEPT Tables cannot be added to publications that are not
    defined as FOR ALL TABLES.
    
    The DETAIL message “EXCEPT Tables cannot be added …” could be adjusted , e.g.,
    EXCEPT tables cannot be added ...
    or
    EXCEPT TABLE cannot be used for publications ...
    
    3) gram.y
    The comments above "AlterPublicationStmt:" should be updated for the
    newly supported command.
    
    --
    Thanks,
    Nisha
    
    
    
    
  289. Re: Skipping schema changes in publication

    Andrei Lepikhov <lepihov@gmail.com> — 2026-03-06T12:55:23Z

    On 6/3/26 11:56, Amit Kapila wrote:
    > On Fri, Mar 6, 2026 at 1:47 PM vignesh C <vignesh21@gmail.com> wrote:
    >>
    > 
    > Instead of a syntax like "ALTER PUBLICATION pub1 DROP EXCEPT TABLE t1"
    > to allow resetting the entire except list by incrementally dropping
    > the except tables, I could think of following alternatives
    
    IMO, using the 'DROP' syntax is an unusual choice because it does not 
    match how an alter publication process actually happens.
    
    > 
    > Option-1: ALTER PUBLICATION pub1 SET ALL TABLES; This suggests it is
    > still an ALL TABLES publication, but providing a new definition. Since
    > it didn't include an EXCEPT clause this time, the exception list is
    > now empty.
    
    I vote for a style that allows incremental add/remove table.
    
    > If we follow the first one, then we can choose "ALTER PUBLICATION pub1
    > SET ALL TABLES EXCEPT TABLE (t1)" to set a new except list instead of
    > "ALTER PUBLICATION pub1 SET EXCEPT TABLE (t1)"
    This approach works best for me. It also matches the internal 
    replication logic: take the new publication state, compare it with the 
    old one, and process the differences.
    
    -- 
    regards, Andrei Lepikhov,
    pgEdge
    
    
    
    
  290. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-03-06T13:19:50Z

    On Fri, Mar 6, 2026 at 4:26 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Mar 6, 2026 at 1:47 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    >
    > Instead of a syntax like "ALTER PUBLICATION pub1 DROP EXCEPT TABLE t1"
    > to allow resetting the entire except list by incrementally dropping
    > the except tables, I could think of following alternatives
    >
    > Option-1: ALTER PUBLICATION pub1 SET ALL TABLES; This suggests it is
    > still an ALL TABLES publication, but providing a new definition. Since
    > it didn't include an EXCEPT clause this time, the exception list is
    > now empty.
    >
    > Option-2: ALTER PUBLICATION pub1 SET EXCEPT TABLE DEFAULT; Since the
    > "default" state of an ALL TABLES publication is to have zero
    > exceptions, the "default" will serve as an alias for an empty list.
    >
    > If we follow the first one, then we can choose "ALTER PUBLICATION pub1
    > SET ALL TABLES EXCEPT TABLE (t1)" to set a new except list instead of
    > "ALTER PUBLICATION pub1 SET EXCEPT TABLE (t1)"
    
    I prefer Option-1  as it provides a consistent extension of the
    existing CREATE PUBLICATION syntax. Since we already use FOR ALL
    TABLES and FOR ALL TABLES EXCEPT... during CREATE PUBLICATION,
    applying the same logic to ALTER PUBLICATION is intuitive and
    maintains a uniform experience for the user.  So +1 for Option-1.
    
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  291. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-07T01:11:46Z

    On Fri, Mar 6, 2026 at 6:25 PM Andrei Lepikhov <lepihov@gmail.com> wrote:
    >
    > On 6/3/26 11:56, Amit Kapila wrote:
    > >
    > > Option-1: ALTER PUBLICATION pub1 SET ALL TABLES; This suggests it is
    > > still an ALL TABLES publication, but providing a new definition. Since
    > > it didn't include an EXCEPT clause this time, the exception list is
    > > now empty.
    >
    > I vote for a style that allows incremental add/remove table.
    >
    > > If we follow the first one, then we can choose "ALTER PUBLICATION pub1
    > > SET ALL TABLES EXCEPT TABLE (t1)" to set a new except list instead of
    > > "ALTER PUBLICATION pub1 SET EXCEPT TABLE (t1)"
    > This approach works best for me. It also matches the internal
    > replication logic: take the new publication state, compare it with the
    > old one, and process the differences.
    >
    
    So, to summarize based on your and Dilip's inputs, the following
    syntax will be preferable to set/reset EXCEPT list: (a) To set a
    except table list to a different list than what is defined originally,
    we will use: "ALTER PUBLICATION pub1 SET ALL TABLES EXCEPT TABLE (t1,
    ..)" (b) To reset the entire except list, we will use "ALTER
    PUBLICATION pub1 SET ALL TABLES". Do let me know if I misunderstood
    your response?
    
    Thanks for the input.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  292. Re: Skipping schema changes in publication

    Andrei Lepikhov <lepihov@gmail.com> — 2026-03-08T14:37:15Z

    On 7/3/26 02:11, Amit Kapila wrote:
    > On Fri, Mar 6, 2026 at 6:25 PM Andrei Lepikhov <lepihov@gmail.com> wrote:
    > So, to summarize based on your and Dilip's inputs, the following
    > syntax will be preferable to set/reset EXCEPT list: (a) To set a
    > except table list to a different list than what is defined originally,
    > we will use: "ALTER PUBLICATION pub1 SET ALL TABLES EXCEPT TABLE (t1,
    > ..)" (b) To reset the entire except list, we will use "ALTER
    > PUBLICATION pub1 SET ALL TABLES". Do let me know if I misunderstood
    > your response?
    Yes, that's correct. Variants a and b seem the clearest to me.
    
    -- 
    regards, Andrei Lepikhov,
    pgEdge
    
    
    
    
  293. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-09T02:52:31Z

    On Sat, 7 Mar 2026 at 06:41, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Mar 6, 2026 at 6:25 PM Andrei Lepikhov <lepihov@gmail.com> wrote:
    > >
    > > On 6/3/26 11:56, Amit Kapila wrote:
    > > >
    > > > Option-1: ALTER PUBLICATION pub1 SET ALL TABLES; This suggests it is
    > > > still an ALL TABLES publication, but providing a new definition. Since
    > > > it didn't include an EXCEPT clause this time, the exception list is
    > > > now empty.
    > >
    > > I vote for a style that allows incremental add/remove table.
    > >
    > > > If we follow the first one, then we can choose "ALTER PUBLICATION pub1
    > > > SET ALL TABLES EXCEPT TABLE (t1)" to set a new except list instead of
    > > > "ALTER PUBLICATION pub1 SET EXCEPT TABLE (t1)"
    > > This approach works best for me. It also matches the internal
    > > replication logic: take the new publication state, compare it with the
    > > old one, and process the differences.
    > >
    >
    > So, to summarize based on your and Dilip's inputs, the following
    > syntax will be preferable to set/reset EXCEPT list: (a) To set a
    > except table list to a different list than what is defined originally,
    > we will use: "ALTER PUBLICATION pub1 SET ALL TABLES EXCEPT TABLE (t1,
    > ..)" (b) To reset the entire except list, we will use "ALTER
    > PUBLICATION pub1 SET ALL TABLES".
    
    The attached v59 patch has the changes to update the syntax accordingly.
    
    Regards,
    Vignesh
    
  294. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-09T04:32:26Z

    On Fri, 6 Mar 2026 at 16:51, Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Fri, Mar 6, 2026 at 1:47 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Thanks for the comments, the attached v58 version patch has the
    > > changes for the same.
    > >
    >
    > Thanks for the patches. I reviewed and tested v58-0001, and here are a
    > few comments:
    >
    > 1) alter_publication.sgml
    > +<phrase>where <replaceable
    > class="parameter">publication_set_object</replaceable> is one
    > of:</phrase>
    > +
    > +    EXCEPT TABLE [ ONLY ] ( <replaceable
    > class="parameter">table_name</replaceable> [, ... ] )
    >
    > Should ONLY be moved inside parentheses(), as it applies per table:-
    > EXCEPT TABLE ( [ ONLY ] table_name [, ...] )
    
    Modified
    
    > 2) publicationcmds.c
    >   postgres=# alter publication pub2 set EXCEPT TABLE (t3);
    >   ERROR:  publication "pub2" is not defined as FOR ALL TABLES
    >   DETAIL:  EXCEPT Tables cannot be added to publications that are not
    > defined as FOR ALL TABLES.
    >
    > The DETAIL message “EXCEPT Tables cannot be added …” could be adjusted , e.g.,
    > EXCEPT tables cannot be added ...
    > or
    > EXCEPT TABLE cannot be used for publications ...
    
    I felt the current message is in line with the existing messages in
    that function. So not making any change.
    
    > 3) gram.y
    > The comments above "AlterPublicationStmt:" should be updated for the
    > newly supported command.
    
    Updated
    
    The v59 version patch attached at [1] has the changes for the same.
    [1] - https://www.postgresql.org/message-id/CALDaNm34tpJDVxEr70dmggY6sWyJxvaSuHN%2BpW2B6skTtjs46A%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  295. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-09T04:36:00Z

    On Mon, Mar 9, 2026 at 8:22 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    >
    > The attached v59 patch has the changes to update the syntax accordingly.
    >
    
    Thanks for the patch. I have attached my suggestion for doc change,
    please take it if you find it appropriate.
    
    Reviewing further.
    
    thanks
    Shveta
    
  296. Re: Skipping schema changes in publication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-03-09T07:00:42Z

    On Mon, Mar 9, 2026 at 8:22 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > The attached v59 patch has the changes to update the syntax accordingly.
    >
    Thanks for the patch, here are few comments:
    
    1) commit message:
    "... the user must first modify
    the publication using ALTER PUBLICATION ... SET EXCEPT TABLE
    before attaching the partition."
    
    The command is modified now and "SET EXCEPT TABLE" is no longer
    supported, so it should be -
    
    "... the user must first modify
    the publication using ALTER PUBLICATION ... SET ALL TABLES EXCEPT TABLE
    before attaching the partition."
    ~~~
    
    2) Unexpected behavior: "ALTER PUBLICATION pub1 SET ALL TABLES;"
    succeeds for a non-ALL TABLES publication and removes the existing
    tables from the publication:
    
    CREATE publication pub1 for table t1;
    
    postgres=# \dRp+
                                                          Publication pub1
      Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Description
    ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
     moond_n | f          | f             | t       | t       | t       |
    t         | none              | f        |
    Tables:
        "public.t1"
    
    postgres=# alter publication pub1 set all tables ;
    ALTER PUBLICATION
    
    postgres=# \dRp+
                                                          Publication pub1
      Owner  | All tables | All sequences | Inserts | Updates | Deletes |
    Truncates | Generated columns | Via root | Description
    ---------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+-------------
     moond_n | f          | f             | t       | t       | t       |
    t         | none              | f        |
    (1 row)
    
    
    3) Should we add a test for "ALTER PUBLICATION tab_pub SET ALL
    TABLES;" verification?
    
    --
    Thanks,
    Nisha
    
    
    
    
  297. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-09T07:34:45Z

    On Mon, Mar 9, 2026 at 8:22 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    > The attached v59 patch has the changes to update the syntax accordingly.
    >
    
    Creation of FOR ALL TABLES publications requires superuser privileges.
    See following code in CreatePublication()
    
    ---
    /* FOR ALL TABLES and FOR ALL SEQUENCES requires superuser */
    if (!superuser())
    {
    if (stmt->for_all_tables || stmt->for_all_sequences)
    ereport(ERROR,
    errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    errmsg("must be superuser to create a FOR ALL TABLES or ALL SEQUENCES
    publication"));
    }
    ---
    
    Similarly, won't altering them also require the superuser privilege? I
    don't see the corresponding code in the patch.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  298. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-09T10:53:37Z

    On Mon, 9 Mar 2026 at 13:04, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Mon, Mar 9, 2026 at 8:22 AM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > The attached v59 patch has the changes to update the syntax accordingly.
    > >
    >
    > Creation of FOR ALL TABLES publications requires superuser privileges.
    > See following code in CreatePublication()
    >
    > ---
    > /* FOR ALL TABLES and FOR ALL SEQUENCES requires superuser */
    > if (!superuser())
    > {
    > if (stmt->for_all_tables || stmt->for_all_sequences)
    > ereport(ERROR,
    > errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > errmsg("must be superuser to create a FOR ALL TABLES or ALL SEQUENCES
    > publication"));
    > }
    > ---
    >
    > Similarly, won't altering them also require the superuser privilege? I
    > don't see the corresponding code in the patch.
    
    This is addressed in the v60 version patch attached.
    Also Nisha's comments from [1] and Shveta's comments from [2] are
    handled in the v60 version.
    
    [1] - https://www.postgresql.org/message-id/CABdArM65WySkXh0KSTdpX2cxKEPBdGGTt693vhpgAdW-uH97JQ%40mail.gmail.com
    [2] - https://www.postgresql.org/message-id/CAJpy0uAWyaorY4NzreVM5hURQYY7hKpj%3Dc8dWziF3GBkFDvLJw%40mail.gmail.com
    
    Regards,
    Vignesh
    
  299. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-10T04:54:48Z

    On Mon, Mar 9, 2026 at 4:23 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > This is addressed in the v60 version patch attached.
    > Also Nisha's comments from [1] and Shveta's comments from [2] are
    > handled in the v60 version.
    >
    
    *
    + /* Check that user is allowed to manipulate the publication. */
    + if (excepttables && !pubform->puballtables)
    + ereport(ERROR,
    + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    +    NameStr(pubform->pubname)),
    + errdetail("Except tables cannot be added to or dropped from
    publications that are not defined as FOR ALL TABLES."));
    +
    + /*
    + * SET ALL TABLES is allowed only for publications defined as FOR ALL
    + * TABLES.
    + */
    + if (stmt->action == AP_SetObjects && !pubform->puballtables &&
    + !tables && !excepttables && !schemaidlist)
    + ereport(ERROR,
    + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    +    NameStr(pubform->pubname)));
    +
    + /* Only a superuser can add or remove EXCEPT tables */
    + if (stmt->action == AP_SetObjects &&
    + (excepttables || (!tables && !excepttables && !schemaidlist)) &&
    + !superuser())
    + ereport(ERROR,
    + errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    + errmsg("must be superuser to add or drop except tables"));
    
    All the above checks are trying to drive the answer to whether the new
    Alter statement is for_all_tables via indirect checks. Why not keep
    for_all_tables boolean variable in AlterPublicationStmt similar to
    CreatePublicationStmt? That should simplify the above checks.
    
    * Also, the superuser check should be earlier in the code, say just
    after following existing check:
    if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) &&
    schemaidlist && !superuser())
    ereport(ERROR,
    (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    errmsg("must be superuser to add or set schemas")));
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  300. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-10T05:46:01Z

    On Tue, Mar 10, 2026 at 10:25 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Mon, Mar 9, 2026 at 4:23 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > This is addressed in the v60 version patch attached.
    > > Also Nisha's comments from [1] and Shveta's comments from [2] are
    > > handled in the v60 version.
    > >
    >
    > *
    > + /* Check that user is allowed to manipulate the publication. */
    > + if (excepttables && !pubform->puballtables)
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    > +    NameStr(pubform->pubname)),
    > + errdetail("Except tables cannot be added to or dropped from
    > publications that are not defined as FOR ALL TABLES."));
    > +
    > + /*
    > + * SET ALL TABLES is allowed only for publications defined as FOR ALL
    > + * TABLES.
    > + */
    > + if (stmt->action == AP_SetObjects && !pubform->puballtables &&
    > + !tables && !excepttables && !schemaidlist)
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    > +    NameStr(pubform->pubname)));
    > +
    > + /* Only a superuser can add or remove EXCEPT tables */
    > + if (stmt->action == AP_SetObjects &&
    > + (excepttables || (!tables && !excepttables && !schemaidlist)) &&
    > + !superuser())
    > + ereport(ERROR,
    > + errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > + errmsg("must be superuser to add or drop except tables"));
    >
    > All the above checks are trying to drive the answer to whether the new
    > Alter statement is for_all_tables via indirect checks. Why not keep
    > for_all_tables boolean variable in AlterPublicationStmt similar to
    > CreatePublicationStmt? That should simplify the above checks.
    >
    
    +1
    
    > * Also, the superuser check should be earlier in the code, say just
    > after following existing check:
    > if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) &&
    > schemaidlist && !superuser())
    > ereport(ERROR,
    > (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > errmsg("must be superuser to add or set schemas")));
    >
    
    I too thought about it, but then it seems the current order of check
    gives better error (IMO). As an example consider where pub1 is a
    publication for explicit tables, then doing 'ALTER PUB SET ALL TABLES'
    on it should give the error that 'pub1 is not for ALL TABLES' rather
    than 'must be superuser'. Because even if ownership was correct, it
    was not going to work.
    
    Currently:
    
    postgres=> alter publication pub1 set all tables;
    ERROR:  publication "pub1" is not defined as FOR ALL TABLES
    
    And for pub2 which is for all tables, we get user error:
    
    postgres=> alter publication pub2 set all tables;
    ERROR:  must be superuser to add or drop except tables
    
    The errors in both cases seem correct to me. What do you think?
    
    Thanks
    Shveta
    
    
    
    
  301. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-10T06:17:32Z

    On Tue, Mar 10, 2026 at 11:16 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Mar 10, 2026 at 10:25 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    >
    > > * Also, the superuser check should be earlier in the code, say just
    > > after following existing check:
    > > if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) &&
    > > schemaidlist && !superuser())
    > > ereport(ERROR,
    > > (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > > errmsg("must be superuser to add or set schemas")));
    > >
    >
    > I too thought about it, but then it seems the current order of check
    > gives better error (IMO). As an example consider where pub1 is a
    > publication for explicit tables, then doing 'ALTER PUB SET ALL TABLES'
    > on it should give the error that 'pub1 is not for ALL TABLES' rather
    > than 'must be superuser'. Because even if ownership was correct, it
    > was not going to work.
    >
    > Currently:
    >
    > postgres=> alter publication pub1 set all tables;
    > ERROR:  publication "pub1" is not defined as FOR ALL TABLES
    >
    > And for pub2 which is for all tables, we get user error:
    >
    > postgres=> alter publication pub2 set all tables;
    > ERROR:  must be superuser to add or drop except tables
    >
    > The errors in both cases seem correct to me. What do you think?
    >
    
    I still feel superuser check should be first and the user should get
    that when it tries to use FOR ALL TABLES. Also, the better error
    message for the same would be: "must be superuser to define FOR ALL
    TABLES publication"
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  302. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-10T12:49:34Z

    On Tue, 10 Mar 2026 at 10:25, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Mon, Mar 9, 2026 at 4:23 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > This is addressed in the v60 version patch attached.
    > > Also Nisha's comments from [1] and Shveta's comments from [2] are
    > > handled in the v60 version.
    > >
    >
    > *
    > + /* Check that user is allowed to manipulate the publication. */
    > + if (excepttables && !pubform->puballtables)
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    > +    NameStr(pubform->pubname)),
    > + errdetail("Except tables cannot be added to or dropped from
    > publications that are not defined as FOR ALL TABLES."));
    > +
    > + /*
    > + * SET ALL TABLES is allowed only for publications defined as FOR ALL
    > + * TABLES.
    > + */
    > + if (stmt->action == AP_SetObjects && !pubform->puballtables &&
    > + !tables && !excepttables && !schemaidlist)
    > + ereport(ERROR,
    > + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    > + errmsg("publication \"%s\" is not defined as FOR ALL TABLES",
    > +    NameStr(pubform->pubname)));
    > +
    > + /* Only a superuser can add or remove EXCEPT tables */
    > + if (stmt->action == AP_SetObjects &&
    > + (excepttables || (!tables && !excepttables && !schemaidlist)) &&
    > + !superuser())
    > + ereport(ERROR,
    > + errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > + errmsg("must be superuser to add or drop except tables"));
    >
    > All the above checks are trying to drive the answer to whether the new
    > Alter statement is for_all_tables via indirect checks. Why not keep
    > for_all_tables boolean variable in AlterPublicationStmt similar to
    > CreatePublicationStmt? That should simplify the above checks.
    >
    > * Also, the superuser check should be earlier in the code, say just
    > after following existing check:
    > if ((stmt->action == AP_AddObjects || stmt->action == AP_SetObjects) &&
    > schemaidlist && !superuser())
    > ereport(ERROR,
    > (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > errmsg("must be superuser to add or set schemas")));
    
    Thanks for the comments, these are addressed in the v61 version patch attached.
    
    Regards,
    Vignesh
    
  303. Re: Skipping schema changes in publication

    Masahiko Sawada <sawada.mshk@gmail.com> — 2026-03-10T20:54:31Z

    On Wed, Mar 4, 2026 at 3:01 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, Mar 4, 2026 at 2:49 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Wed, Mar 4, 2026 at 1:54 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > Here is an updated version with a couple of typos fixed and separated
    > > > the describe table query based on versions which make it more easier
    > > > to read.
    > > >
    > >
    > > v56 LGTM.
    > >
    >
    > Pushed.
    
    Sorry for joining the discussion late.
    
    I've tried with the new EXCEPT clause and I have some questions about
    the newly introduced syntax. With this feature, we can now write:
    
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);
    
    The exclusion table list is written in the form of TABLE (a, b, c) but
    it's quite different from the inclusion table list we can specify
    (e.g., TABLE a, TABLE b, TABLE c). I'm concerned that it could confuse
    users. Have these points already been discussed? Also, isn't the TABLE
    after EXCEPT redundant?
    
    Regards,
    
    -- 
    Masahiko Sawada
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  304. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-11T03:24:46Z

    On Wed, Mar 11, 2026 at 2:25 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    >
    > On Wed, Mar 4, 2026 at 3:01 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Wed, Mar 4, 2026 at 2:49 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Wed, Mar 4, 2026 at 1:54 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > >
    > > > > Here is an updated version with a couple of typos fixed and separated
    > > > > the describe table query based on versions which make it more easier
    > > > > to read.
    > > > >
    > > >
    > > > v56 LGTM.
    > > >
    > >
    > > Pushed.
    >
    > Sorry for joining the discussion late.
    >
    > I've tried with the new EXCEPT clause and I have some questions about
    > the newly introduced syntax. With this feature, we can now write:
    >
    > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);
    >
    > The exclusion table list is written in the form of TABLE (a, b, c) but
    > it's quite different from the inclusion table list we can specify
    > (e.g., TABLE a, TABLE b, TABLE c). I'm concerned that it could confuse
    > users. Have these points already been discussed? Also, isn't the TABLE
    > after EXCEPT redundant?
    >
    
    Thanks for the question.
    
    Yes, these concerns were discussed earlier when the EXCEPT syntax was
    introduced. The current syntax:
    
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);
    
    was chosen intentionally, mainly for extensibility and to avoid
    ambiguity in future enhancements.
    
    1. TABLE keyword after EXCEPT
    
    At first glance, the TABLE keyword might appear redundant. However, it
    becomes important if we later extend the syntax to allow excluding
    other object types, such as schemas or sequences. For example:
    
    CREATE PUBLICATION pub FOR ALL TABLES
    EXCEPT SCHEMA (s1, s2), EXCEPT TABLE (t1, t2);
    
    or potentially more complex combinations such as:
    
    CREATE PUBLICATION pub
    FOR  TABLES, SEQUENCES IN SCHEMA sch1
    EXCEPT TABLE (t1), EXCEPT SEQUENCE (s1);
    
    In such cases, explicitly specifying TABLE (or SCHEMA, SEQUENCE, etc.)
    after EXCEPT avoids ambiguity and keeps the syntax consistent.
    
    2. Parentheses around the exclusion list
    
    Parentheses are required to clearly separate the excluded objects from
    other elements in the publication definition, especially when mixed
    with inclusion clauses. For example, consider a future case like:
    
    CREATE PUBLICATION pub
    FOR TABLES IN SCHEMA s1
    EXCEPT TABLE (t1, t2), TABLE t3;
    
    Here it is clear that t1 and t2 are excluded, while t3 is explicitly
    included. Without parentheses, a statement like:
    
    CREATE PUBLICATION pub
    FOR TABLES IN SCHEMA s1
    EXCEPT TABLE t1, t2, TABLE t3;
    
    would be harder to interpret and potentially ambiguous.
    
    So although the syntax differs slightly from the existing inclusion
    list (e.g., TABLE a, b), requiring both the TABLE keyword and
    parentheses after EXCEPT helps keep the grammar unambiguous and makes
    the syntax easier to extend in the future.
    
    Original discussion is at [1].
    
    [1]: https://www.postgresql.org/message-id/CAJpy0uB%3DJxTYXOB7VmrhVLR%2B1PG0%3DTtHuGekaqibOPpo2UBLiQ%40mail.gmail.com
    
    thanks
    Shveta
    
    
    
    
  305. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-11T04:59:59Z

    On Tue, Mar 10, 2026 at 6:19 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    >
    > Thanks for the comments, these are addressed in the v61 version patch attached.
    >
    
    I don't have any further comments. The patch LGTM.
    
    thanks
    Shveta.
    
    
    
    
  306. Re: Skipping schema changes in publication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-03-11T05:41:29Z

    On Tue, Mar 10, 2026 at 6:19 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Thanks for the comments, these are addressed in the v61 version patch attached.
    >
    Thanks for the patch, I have few comments:
    
    1) publicationcmd.c: CheckAlterPublication()
    After the recent change to use stmt->for_all_tables, the
    "excepttables" parameter is no longer used in this function and can be
    removed.
    ~~~
    
    Couple of minor suggestions:
    2)
    + if (stmt->for_all_tables && !superuser())
    +   ereport(ERROR,
    +       errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    +       errmsg("must be superuser to define FOR ALL TABLES publication"));
    +
    
    Since this check is in the context of ALTER PUBLICATION, should the
    error message instead be:
    "must be superuser to alter FOR ALL TABLES publication"
    
    3) test comment in publication.sql:1015 -
      "-- fail - ADD/DROP EXCEPT table requires superuser privileges"
    The test uses only SET, not ADD/DROP, so the comment could be updated to:
    
    -- fail - modifying EXCEPT table list requires superuser privileges
    (I’m also fine with any better alternative.)
    
    --
    Thanks,
    Nisha
    
    
    
    
  307. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-11T10:07:36Z

    On Wed, 11 Mar 2026 at 11:11, Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Tue, Mar 10, 2026 at 6:19 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Thanks for the comments, these are addressed in the v61 version patch attached.
    > >
    > Thanks for the patch, I have few comments:
    >
    > 1) publicationcmd.c: CheckAlterPublication()
    > After the recent change to use stmt->for_all_tables, the
    > "excepttables" parameter is no longer used in this function and can be
    > removed.
    > ~~~
    >
    > Couple of minor suggestions:
    > 2)
    > + if (stmt->for_all_tables && !superuser())
    > +   ereport(ERROR,
    > +       errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    > +       errmsg("must be superuser to define FOR ALL TABLES publication"));
    > +
    >
    > Since this check is in the context of ALTER PUBLICATION, should the
    > error message instead be:
    > "must be superuser to alter FOR ALL TABLES publication"
    >
    > 3) test comment in publication.sql:1015 -
    >   "-- fail - ADD/DROP EXCEPT table requires superuser privileges"
    > The test uses only SET, not ADD/DROP, so the comment could be updated to:
    >
    > -- fail - modifying EXCEPT table list requires superuser privileges
    > (I’m also fine with any better alternative.)
    
    These comments are addressed in the v62 version patch attached.
    
    Regards,
    Vignesh
    
  308. Re: Skipping schema changes in publication

    Masahiko Sawada <sawada.mshk@gmail.com> — 2026-03-11T17:53:22Z

    On Tue, Mar 10, 2026 at 8:24 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Mar 11, 2026 at 2:25 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    > >
    > > On Wed, Mar 4, 2026 at 3:01 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > > On Wed, Mar 4, 2026 at 2:49 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > > >
    > > > > On Wed, Mar 4, 2026 at 1:54 PM vignesh C <vignesh21@gmail.com> wrote:
    > > > > >
    > > > > > Here is an updated version with a couple of typos fixed and separated
    > > > > > the describe table query based on versions which make it more easier
    > > > > > to read.
    > > > > >
    > > > >
    > > > > v56 LGTM.
    > > > >
    > > >
    > > > Pushed.
    > >
    > > Sorry for joining the discussion late.
    > >
    > > I've tried with the new EXCEPT clause and I have some questions about
    > > the newly introduced syntax. With this feature, we can now write:
    > >
    > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);
    > >
    > > The exclusion table list is written in the form of TABLE (a, b, c) but
    > > it's quite different from the inclusion table list we can specify
    > > (e.g., TABLE a, TABLE b, TABLE c). I'm concerned that it could confuse
    > > users. Have these points already been discussed? Also, isn't the TABLE
    > > after EXCEPT redundant?
    > >
    >
    > Thanks for the question.
    >
    > Yes, these concerns were discussed earlier when the EXCEPT syntax was
    > introduced. The current syntax:
    >
    > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);
    >
    > was chosen intentionally, mainly for extensibility and to avoid
    > ambiguity in future enhancements.
    >
    > 1. TABLE keyword after EXCEPT
    >
    > At first glance, the TABLE keyword might appear redundant. However, it
    > becomes important if we later extend the syntax to allow excluding
    > other object types, such as schemas or sequences. For example:
    >
    > CREATE PUBLICATION pub FOR ALL TABLES
    > EXCEPT SCHEMA (s1, s2), EXCEPT TABLE (t1, t2);
    >
    > or potentially more complex combinations such as:
    >
    > CREATE PUBLICATION pub
    > FOR  TABLES, SEQUENCES IN SCHEMA sch1
    > EXCEPT TABLE (t1), EXCEPT SEQUENCE (s1);
    >
    > In such cases, explicitly specifying TABLE (or SCHEMA, SEQUENCE, etc.)
    > after EXCEPT avoids ambiguity and keeps the syntax consistent.
    
    I understand that it's for future extension but I'm still unsure that
    users would not be confused by the fact that the syntaxes of inclusion
    list and exclusion list are different; TABLES IN SCHEMA s1 vs. SCHEMA
    (s1).
    
    >
    > 2. Parentheses around the exclusion list
    >
    > Parentheses are required to clearly separate the excluded objects from
    > other elements in the publication definition, especially when mixed
    > with inclusion clauses. For example, consider a future case like:
    >
    > CREATE PUBLICATION pub
    > FOR TABLES IN SCHEMA s1
    > EXCEPT TABLE (t1, t2), TABLE t3;
    >
    > Here it is clear that t1 and t2 are excluded, while t3 is explicitly
    > included. Without parentheses, a statement like:
    >
    > CREATE PUBLICATION pub
    > FOR TABLES IN SCHEMA s1
    > EXCEPT TABLE t1, t2, TABLE t3;
    >
    > would be harder to interpret and potentially ambiguous.
    >
    > So although the syntax differs slightly from the existing inclusion
    > list (e.g., TABLE a, b), requiring both the TABLE keyword and
    > parentheses after EXCEPT helps keep the grammar unambiguous and makes
    > the syntax easier to extend in the future.
    
    I'm still unsure that the syntax like TABLE (t1, t2) for the exclusion
    list is syntactically correct. The syntax of TABLE (...) is already
    used in a quite different way as follows (borrowed an example from
    stats_import.sql):
    
    CREATE FUNCTION stats_import.pg_statistic_get_difference(a text, b text)
    RETURNS TABLE (relname text, stats stats_import.pg_statistic_flat_t)
    BEGIN ATOMIC
      WITH aset AS (SELECT * FROM stats_import.pg_statistic_flat(a)),
           bset AS (SELECT * FROM stats_import.pg_statistic_flat(b))
      SELECT a AS relname, a_minus_b::stats_import.pg_statistic_flat_t
      FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b
      UNION ALL
      SELECT b AS relname, b_minus_a::stats_import.pg_statistic_flat_t
      FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a;
    END;
    
    Wouldn't it be more appropriate to use a plural form or the IN
    keyword, such as EXCEPT TABLES (t1, t2) or EXCEPT TABLES IN (t1, t2)?
    
    Or if we might want to add multiple items in the EXCEPT clause in the
    future we can have parentheses around all exclusion items as follow:
    
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, TABLE t2,
    TABLES IN SCHEMA s1);
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, TABLE
    t2), TABLE t3;
    
    > Original discussion is at [1].
    
    Thanks. I'll read the discussion.
    
    Regards,
    
    -- 
    Masahiko Sawada
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  309. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-12T09:47:37Z

    On Mon, Mar 2, 2026 at 9:18 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    
    After a couple of weeks away, I have been trying to catch up on what
    became of this feature.
    
    I was surprised by the late syntax change to make the TABLE keyword
    now mandatory in EXCEPT TABLE. My initial impression is that it forces
    the user to write an unnecessarily verbose command like:
    
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (tab1,tab2);
    vs
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (tab1,tab2);
    
    And the examples to justify making this mandatory [1] are not quite
    correct, AFAICT.
    
    Let's look at those in more detail:
    
    >
    > Currently, the syntax we plan to support in patch001 is:
    >
    > CREATE PUBLICATION pub for ALL TABLES EXCEPT TABLE (tab1, tab2, ...)
    >
    > That is:
    >
    > a) The TABLE keyword is mandatory after EXCEPT.
    > b) Parentheses around the table list are mandatory.
    >
    > There has been some confusion regarding the EXCEPT syntax. Below are
    > the points that help justify the above syntax.
    >
    > 1) TABLE keyword:
    >
    > Consider the command:
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT (tab1, tab2);
    >
    > At first glance, this appears acceptable even without the TABLE
    > keyword after EXCEPT. However, in the future, if we support excluding
    > a particular schema, for example:
    >
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT SCHEMA s1, s2;
    >
    > then it becomes necessary to explicitly specify whether we are
    > excluding a TABLE, SCHEMA, or SEQUENCE after EXCEPT.
    
    SEQUENCE? -- Hm, this example is "FOR ALL TABLES" -- emphasis on
    *tables* -- sequences have nothing to do with "FOR ALL TABLES": they
    would be handled by a "FOR ALL SEQUENCES" command.
    
    TABLE v SCHEMA? -- I agree that if there will be some future way for
    excluding all tables from certain schemas, then you will need to be
    able to distinguish excluded schemas from excluded tables. However,
    the command in this example is "FOR ALL TABLES"  -- emphasis on
    *tables* -- so IMO omission of TABLE keyword can still be understood
    to mean the default behaviour because the type is very clear.
    
    e.g. all these are unambiguous to me
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (tab1,tab2);
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (tab1,tab2);
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT SCHEMA (sch1,sch2);
    
    and combinations too:
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (tab1,tab2) SCHEMA (sch1,sch2);
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (tab1,tab2) SCHEMA
    (sch1 sch2);
    
    >
    > Similarly, if we extend CREATE PUBLICATION to support something like:
    > CREATE PUBLICATION pub
    > FOR TABLES, SEQUENCES IN SCHEMA sch1
    > EXCEPT TABLE (t1), EXCEPT SEQUENCE (s1);
    >
    > then also, the TABLE or SEQUENCE keyword after EXCEPT becomes
    > mandatory for clarity.
    >
    
    That example seemed bogus to me. If in future we allow to publish
    sequences by schema, then IMO that future syntax would be like:
    CREATE PUBLICATION pub1 FOR SEQUENCES IN SCHEMA sch1;
    
    Then, exclusions would look like:
    CREATE PUBLICATION pub1 FOR SEQUENCES IN SCHEMA sch1 EXCEPT SEQUENCE
    (seq1,seq2);
    
    But here the SEQUENCE keyword is also optional because the type
    (sequences) is already clear from the command!
    
    So above is the same as:
    CREATE PUBLICATION pub1 FOR SEQUENCES IN SCHEMA sch1 EXCEPT (seq1,seq2);
    
    Therefore, the equivalent of your example would be:
    CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA sch1 EXCEPT TABLE (tab1),
    FOR SEQUENCES IN SCHEMA sch1 EXCEPT SEQUENCE (seq1);
    
    or (omitting optional keywords)
    CREATE PUBLICATION pub1 FOR TABLES IN SCHEMA sch1 EXCEPT (tab1), FOR
    SEQUENCES IN SCHEMA sch1 EXCEPT (seq1);
    
    ~~~
    
    Summary.
    
    * Must the TABLE (or other type) keyword be mandatory to clarify what
    the excluded type is? No, I don't think so; the default type the
    command refers to is unambiguous already.
    
    * Does making the TABLE (or other type) mandatory make it easier for
    the end-user? I'm not so sure. Saying just EXCEPT (tab1) instead of
    EXCEPT TABLE (tab1,tab2) seemed more natural to me.
    
    * Was it just easier to implement when mandatory? Probably true.
    
    * Isn't this analogous to "ALTER TABLE ADD/DROP/ALTER [COLUMN]"
    commands [2]?  PostgreSQL doesn't force the user to type the
    unnecessary COLUMN keyword.
    
    
    IMO, none of the examples/reasons given justified why the TABLE needed
    to be made mandatory. Certainly, the CREATE PUBLICATION command is
    already long enough without enforcing redundant keywords.
    
    ======
    [1] https://www.postgresql.org/message-id/CAJpy0uB%3DJxTYXOB7VmrhVLR%2B1PG0%3DTtHuGekaqibOPpo2UBLiQ%40mail.gmail.com
    [2] https://www.postgresql.org/docs/current/sql-altertable.html
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  310. RE: Skipping schema changes in publication

    Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com> — 2026-03-12T11:50:39Z

    Dear Sawada-san,
    
    > I'm still unsure that the syntax like TABLE (t1, t2) for the exclusion
    > list is syntactically correct. The syntax of TABLE (...) is already
    > used in a quite different way as follows (borrowed an example from
    > stats_import.sql):
    > 
    > CREATE FUNCTION stats_import.pg_statistic_get_difference(a text, b text)
    > RETURNS TABLE (relname text, stats stats_import.pg_statistic_flat_t)
    > BEGIN ATOMIC
    >   WITH aset AS (SELECT * FROM stats_import.pg_statistic_flat(a)),
    >        bset AS (SELECT * FROM stats_import.pg_statistic_flat(b))
    >   SELECT a AS relname, a_minus_b::stats_import.pg_statistic_flat_t
    >   FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b
    >   UNION ALL
    >   SELECT b AS relname, b_minus_a::stats_import.pg_statistic_flat_t
    >   FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a;
    > END;
    > 
    > Wouldn't it be more appropriate to use a plural form or the IN
    > keyword, such as EXCEPT TABLES (t1, t2) or EXCEPT TABLES IN (t1, t2)?
    
    But there is an existing syntax to list target tables, FOR TABLE t1, t2, t3...,
    which does not have the plural. Current rule is to use the singular for
    specifying the exact table, and we follow.
    
    > Or if we might want to add multiple items in the EXCEPT clause in the
    > future we can have parentheses around all exclusion items as follow:
    > 
    > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, TABLE t2,
    > TABLES IN SCHEMA s1);
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1,
    > TABLE
    > t2), TABLE t3;
    
    I agree those alternatives could work, but one downside is the redundant use of
    "TABLE" when many tables are excluded. If we want to stay consistent with the
    existing style, perhaps we could write it as:
    
    ```
    CREATE PUBLICATION pub FOR ALL TABLES EXCPET (TABLE t1, t2, t3);
    ```
    
    Because we have already been accepting the syntax like "FOR TABLE t1, t2, t3".
    
    Best regards,
    Hayato Kuroda
    FUJITSU LIMITED
    
    
  311. Re: Skipping schema changes in publication

    Masahiko Sawada <sawada.mshk@gmail.com> — 2026-03-12T16:46:09Z

    On Thu, Mar 12, 2026 at 4:50 AM Hayato Kuroda (Fujitsu)
    <kuroda.hayato@fujitsu.com> wrote:
    >
    > Dear Sawada-san,
    >
    > > I'm still unsure that the syntax like TABLE (t1, t2) for the exclusion
    > > list is syntactically correct. The syntax of TABLE (...) is already
    > > used in a quite different way as follows (borrowed an example from
    > > stats_import.sql):
    > >
    > > CREATE FUNCTION stats_import.pg_statistic_get_difference(a text, b text)
    > > RETURNS TABLE (relname text, stats stats_import.pg_statistic_flat_t)
    > > BEGIN ATOMIC
    > >   WITH aset AS (SELECT * FROM stats_import.pg_statistic_flat(a)),
    > >        bset AS (SELECT * FROM stats_import.pg_statistic_flat(b))
    > >   SELECT a AS relname, a_minus_b::stats_import.pg_statistic_flat_t
    > >   FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b
    > >   UNION ALL
    > >   SELECT b AS relname, b_minus_a::stats_import.pg_statistic_flat_t
    > >   FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a;
    > > END;
    > >
    > > Wouldn't it be more appropriate to use a plural form or the IN
    > > keyword, such as EXCEPT TABLES (t1, t2) or EXCEPT TABLES IN (t1, t2)?
    >
    > But there is an existing syntax to list target tables, FOR TABLE t1, t2, t3...,
    > which does not have the plural. Current rule is to use the singular for
    > specifying the exact table, and we follow.
    
    "TABLE t1, t2, t3" and "TABLE (t1, t2, t3)" are different to me since
    the latter reminds me of TABLE (...) syntax for defining a returning
    table.
    
    > > Or if we might want to add multiple items in the EXCEPT clause in the
    > > future we can have parentheses around all exclusion items as follow:
    > >
    > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, TABLE t2,
    > > TABLES IN SCHEMA s1);
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1,
    > > TABLE
    > > t2), TABLE t3;
    >
    > I agree those alternatives could work, but one downside is the redundant use of
    > "TABLE" when many tables are excluded. If we want to stay consistent with the
    > existing style, perhaps we could write it as:
    >
    > ```
    > CREATE PUBLICATION pub FOR ALL TABLES EXCPET (TABLE t1, t2, t3);
    > ```
    >
    > Because we have already been accepting the syntax like "FOR TABLE t1, t2, t3".
    >
    
    Yeah, we can omit the TABLE keyword for the second and subsequent
    tables. My whole point is that using different syntaxes for the
    inclusion list and the exclusion list would quite confuse users.
    
    Regards,
    
    -- 
    Masahiko Sawada
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  312. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-13T03:24:45Z

    On Fri, Mar 13, 2026 at 3:46 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    >
    > On Thu, Mar 12, 2026 at 4:50 AM Hayato Kuroda (Fujitsu)
    > <kuroda.hayato@fujitsu.com> wrote:
    > >
    > > Dear Sawada-san,
    > >
    > > > I'm still unsure that the syntax like TABLE (t1, t2) for the exclusion
    > > > list is syntactically correct. The syntax of TABLE (...) is already
    > > > used in a quite different way as follows (borrowed an example from
    > > > stats_import.sql):
    > > >
    > > > CREATE FUNCTION stats_import.pg_statistic_get_difference(a text, b text)
    > > > RETURNS TABLE (relname text, stats stats_import.pg_statistic_flat_t)
    > > > BEGIN ATOMIC
    > > >   WITH aset AS (SELECT * FROM stats_import.pg_statistic_flat(a)),
    > > >        bset AS (SELECT * FROM stats_import.pg_statistic_flat(b))
    > > >   SELECT a AS relname, a_minus_b::stats_import.pg_statistic_flat_t
    > > >   FROM (TABLE aset EXCEPT TABLE bset) AS a_minus_b
    > > >   UNION ALL
    > > >   SELECT b AS relname, b_minus_a::stats_import.pg_statistic_flat_t
    > > >   FROM (TABLE bset EXCEPT TABLE aset) AS b_minus_a;
    > > > END;
    > > >
    > > > Wouldn't it be more appropriate to use a plural form or the IN
    > > > keyword, such as EXCEPT TABLES (t1, t2) or EXCEPT TABLES IN (t1, t2)?
    > >
    > > But there is an existing syntax to list target tables, FOR TABLE t1, t2, t3...,
    > > which does not have the plural. Current rule is to use the singular for
    > > specifying the exact table, and we follow.
    >
    > "TABLE t1, t2, t3" and "TABLE (t1, t2, t3)" are different to me since
    > the latter reminds me of TABLE (...) syntax for defining a returning
    > table.
    >
    > > > Or if we might want to add multiple items in the EXCEPT clause in the
    > > > future we can have parentheses around all exclusion items as follow:
    > > >
    > > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, TABLE t2,
    > > > TABLES IN SCHEMA s1);
    > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1,
    > > > TABLE
    > > > t2), TABLE t3;
    > >
    > > I agree those alternatives could work, but one downside is the redundant use of
    > > "TABLE" when many tables are excluded. If we want to stay consistent with the
    > > existing style, perhaps we could write it as:
    > >
    > > ```
    > > CREATE PUBLICATION pub FOR ALL TABLES EXCPET (TABLE t1, t2, t3);
    > > ```
    > >
    > > Because we have already been accepting the syntax like "FOR TABLE t1, t2, t3".
    > >
    >
    > Yeah, we can omit the TABLE keyword for the second and subsequent
    > tables. My whole point is that using different syntaxes for the
    > inclusion list and the exclusion list would quite confuse users.
    >
    
    Hou-San (off-list) gave an interesting example of how it might look
    when the example is combined with the hypothetical future ability to
    exclude all schema tables from a "FOR ALL TABLES"
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    SCHEMA s1);
    
    The syntax starts to look more complicated when there are multiple
    schemas, but IMO it is still perfectly fine.
    e.g.  FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN SCHEMA s1, s2);
    
    AFAICT, it is doable. The synopsis syntax changes are something like this:
    ------
    and publication_all_object is one of:
        ALL TABLES [ EXCEPT ( except_table_list_object [, ... ] ) ]
        ALL SEQUENCES
    
    and except_table_list_object is:
        TABLE except_table_object [, ...]
        TABLES IN SCHEMA schema_name [, ...]
    
    and except_table_object is:
        [ ONLY ] table_name [ * ]
    ------
    
    This gives the user flexibility to name the type however they want to:
    e.g. EXCEPT (TABLE t1, TABLE t2, TABLE t3)
    e.g. EXCEPT (TABLE t1, t2, t3)
    e.g. EXCEPT (TABLE t1, t2, TABLE t3)
    e.g. EXCEPT (TABLE t1, TABLES IN SCHEMA s1, s2, TABLE t2, t3);
    
    You get all the flexibility (e.g. which ellipses are expanded) for
    free from the grammar, and it is already very typical lists in the
    existing CREATE PUBLICATION command, which allow things like:
    e.g. CREATE PUBLICATION pub FOR TABLE t1,t2, TABLE t3;
    
    This syntax LGTM because
    * User is not required to repeat TABLE keyword multiple times (but
    they can if they want to)
    * The exclusion lists inside the EXCEPT look and work the same as the
    inclusion lists of CREATE PUBLICATION
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  313. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-13T09:51:34Z

    On Fri, Mar 13, 2026 at 8:55 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hou-San (off-list) gave an interesting example of how it might look
    > when the example is combined with the hypothetical future ability to
    > exclude all schema tables from a "FOR ALL TABLES"
    > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > SCHEMA s1);
    >
    
    So, if we follow this, in future, the syntax would look like:
    Create publication pub1 FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    SCHEMA s1, s2), ALL SEQUENCES EXCEPT (SEQUENCE s1);
    
    I think if we use the above syntax then one can also argue that why
    not use FOR ALL (TABLES, SEQUENCES) instead of ALL TABLES, ALL
    SEQUENCES.
    
    Instead the current command would like:
    Create publication pub1 FOR ALL TABLES EXCEPT TABLE (t1, t2), EXCEPT
    TABLES IN SCHEMA (s1, s2), ALL SEQUENCES EXCEPT SEQUENCE (seq1, seq2);
    
    Though it is slightly lengthy it makes the syntax local such that the
    meaning of a clause is determined by what is inside it, not by
    something that appears ten words later.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  314. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-13T10:00:05Z

    On Wed, Mar 11, 2026 at 11:24 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    >
    > >
    > > 2. Parentheses around the exclusion list
    > >
    > > Parentheses are required to clearly separate the excluded objects from
    > > other elements in the publication definition, especially when mixed
    > > with inclusion clauses. For example, consider a future case like:
    > >
    > > CREATE PUBLICATION pub
    > > FOR TABLES IN SCHEMA s1
    > > EXCEPT TABLE (t1, t2), TABLE t3;
    > >
    > > Here it is clear that t1 and t2 are excluded, while t3 is explicitly
    > > included. Without parentheses, a statement like:
    > >
    > > CREATE PUBLICATION pub
    > > FOR TABLES IN SCHEMA s1
    > > EXCEPT TABLE t1, t2, TABLE t3;
    > >
    > > would be harder to interpret and potentially ambiguous.
    > >
    > > So although the syntax differs slightly from the existing inclusion
    > > list (e.g., TABLE a, b), requiring both the TABLE keyword and
    > > parentheses after EXCEPT helps keep the grammar unambiguous and makes
    > > the syntax easier to extend in the future.
    >
    > I'm still unsure that the syntax like TABLE (t1, t2) for the exclusion
    > list is syntactically correct. The syntax of TABLE (...) is already
    > used in a quite different way as follows (borrowed an example from
    > stats_import.sql):
    >
    > CREATE FUNCTION stats_import.pg_statistic_get_difference(a text, b text)
    > RETURNS TABLE (relname text, stats stats_import.pg_statistic_flat_t)
    >
    
    Both are different usage and not clear if they are comparable.
    
    >
    > Wouldn't it be more appropriate to use a plural form or the IN
    > keyword, such as EXCEPT TABLES (t1, t2) or EXCEPT TABLES IN (t1, t2)?
    >
    
    We can consider it. Let's try to compare it with following existing
    publication syntax:
    
    CREATE PUBLICATION production_publication FOR TABLE users,
    departments, TABLES IN SCHEMA production;
    CREATE PUBLICATION alltables FOR ALL TABLES;
    
    Here we used singular form when a user could specify one or more
    objects and plural when all objects of schema or database are
    considered.
    
    As per this, the except syntax falls in the singular category which is
    in favor of current syntax.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  315. Re: Skipping schema changes in publication

    Masahiko Sawada <sawada.mshk@gmail.com> — 2026-03-13T18:21:16Z

    On Fri, Mar 13, 2026 at 2:51 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Mar 13, 2026 at 8:55 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Hou-San (off-list) gave an interesting example of how it might look
    > > when the example is combined with the hypothetical future ability to
    > > exclude all schema tables from a "FOR ALL TABLES"
    > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > > SCHEMA s1);
    > >
    >
    > So, if we follow this, in future, the syntax would look like:
    > Create publication pub1 FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > SCHEMA s1, s2), ALL SEQUENCES EXCEPT (SEQUENCE s1);
    >
    > I think if we use the above syntax then one can also argue that why
    > not use FOR ALL (TABLES, SEQUENCES) instead of ALL TABLES, ALL
    > SEQUENCES.
    
    The same is true for the syntaxes of TABLE(t1, t2) and SCHEMA(s1, s2),
    no? If we allow grouping the tables and the schemas by TABLE and
    SCHEMA keywords respectively in exclusion lists, one could argue why
    we don't allow it for the inclusion list.
    
    >
    > Instead the current command would like:
    > Create publication pub1 FOR ALL TABLES EXCEPT TABLE (t1, t2), EXCEPT
    > TABLES IN SCHEMA (s1, s2), ALL SEQUENCES EXCEPT SEQUENCE (seq1, seq2);
    >
    > Though it is slightly lengthy it makes the syntax local such that the
    > meaning of a clause is determined by what is inside it, not by
    > something that appears ten words later.
    
    While it helps the syntax locality, I'm concerned that it could make
    the context of the EXCEPT clause ambiguous. For example, IIUC the
    above syntax rule would allow us to write:
    
    CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLES IN SCHEMA (s1),
    EXCEPT TABLE (t1);
    
    Regarding the second EXCEPT clause, while one might think it excludes
    t1 from ALL TABLES, one might think it excludes t1 from the first
    EXCLUDE clause (i.e., the publication actually includes t1 in the
    publication).
    
    Regards,
    
    -- 
    Masahiko Sawada
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  316. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-14T09:35:21Z

    On Fri, Mar 13, 2026 at 11:51 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    >
    > On Fri, Mar 13, 2026 at 2:51 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Fri, Mar 13, 2026 at 8:55 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > > > Hou-San (off-list) gave an interesting example of how it might look
    > > > when the example is combined with the hypothetical future ability to
    > > > exclude all schema tables from a "FOR ALL TABLES"
    > > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > > > SCHEMA s1);
    > > >
    > >
    > > So, if we follow this, in future, the syntax would look like:
    > > Create publication pub1 FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > > SCHEMA s1, s2), ALL SEQUENCES EXCEPT (SEQUENCE s1);
    > >
    > > I think if we use the above syntax then one can also argue that why
    > > not use FOR ALL (TABLES, SEQUENCES) instead of ALL TABLES, ALL
    > > SEQUENCES.
    >
    > The same is true for the syntaxes of TABLE(t1, t2) and SCHEMA(s1, s2),
    > no? If we allow grouping the tables and the schemas by TABLE and
    > SCHEMA keywords respectively in exclusion lists, one could argue why
    > we don't allow it for the inclusion list.
    >
    > >
    > > Instead the current command would like:
    > > Create publication pub1 FOR ALL TABLES EXCEPT TABLE (t1, t2), EXCEPT
    > > TABLES IN SCHEMA (s1, s2), ALL SEQUENCES EXCEPT SEQUENCE (seq1, seq2);
    > >
    > > Though it is slightly lengthy it makes the syntax local such that the
    > > meaning of a clause is determined by what is inside it, not by
    > > something that appears ten words later.
    >
    > While it helps the syntax locality, I'm concerned that it could make
    > the context of the EXCEPT clause ambiguous. For example, IIUC the
    > above syntax rule would allow us to write:
    >
    > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLES IN SCHEMA (s1),
    > EXCEPT TABLE (t1);
    >
    > Regarding the second EXCEPT clause, while one might think it excludes
    > t1 from ALL TABLES, one might think it excludes t1 from the first
    > EXCLUDE clause (i.e., the publication actually includes t1 in the
    > publication).
    >
    
    According to me, the comma between EXCEPT clauses breaks such a chain
    of thought but others may think differently. The docs can clarify such
    a confusion if any but if multiple people interpret it the way you
    think, I am fine to go with the direction you are proposing. I am
    slightly worried that both syntaxes have some pros and cons, so even
    if we change to what you are suggesting, one can argue to go back to
    previous syntax as that also has some merits. So, I feel we can
    complete the pending patch [1] and try to get the opinion of some
    other people unless changing the code to support another alternative
    is difficult.
    
    [1] - https://www.postgresql.org/message-id/CALDaNm1SGJtJq_W68khExG1EL4ZmyZ2U7JSc1Z3FsYr4nSbi_A%40mail.gmail.com
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  317. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-15T12:25:34Z

    On Sat, 14 Mar 2026 at 15:05, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Mar 13, 2026 at 11:51 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    > >
    > > On Fri, Mar 13, 2026 at 2:51 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > > On Fri, Mar 13, 2026 at 8:55 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > >
    > > > > Hou-San (off-list) gave an interesting example of how it might look
    > > > > when the example is combined with the hypothetical future ability to
    > > > > exclude all schema tables from a "FOR ALL TABLES"
    > > > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > > > > SCHEMA s1);
    > > > >
    > > >
    > > > So, if we follow this, in future, the syntax would look like:
    > > > Create publication pub1 FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > > > SCHEMA s1, s2), ALL SEQUENCES EXCEPT (SEQUENCE s1);
    > > >
    > > > I think if we use the above syntax then one can also argue that why
    > > > not use FOR ALL (TABLES, SEQUENCES) instead of ALL TABLES, ALL
    > > > SEQUENCES.
    > >
    > > The same is true for the syntaxes of TABLE(t1, t2) and SCHEMA(s1, s2),
    > > no? If we allow grouping the tables and the schemas by TABLE and
    > > SCHEMA keywords respectively in exclusion lists, one could argue why
    > > we don't allow it for the inclusion list.
    > >
    > > >
    > > > Instead the current command would like:
    > > > Create publication pub1 FOR ALL TABLES EXCEPT TABLE (t1, t2), EXCEPT
    > > > TABLES IN SCHEMA (s1, s2), ALL SEQUENCES EXCEPT SEQUENCE (seq1, seq2);
    > > >
    > > > Though it is slightly lengthy it makes the syntax local such that the
    > > > meaning of a clause is determined by what is inside it, not by
    > > > something that appears ten words later.
    > >
    > > While it helps the syntax locality, I'm concerned that it could make
    > > the context of the EXCEPT clause ambiguous. For example, IIUC the
    > > above syntax rule would allow us to write:
    > >
    > > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLES IN SCHEMA (s1),
    > > EXCEPT TABLE (t1);
    > >
    > > Regarding the second EXCEPT clause, while one might think it excludes
    > > t1 from ALL TABLES, one might think it excludes t1 from the first
    > > EXCLUDE clause (i.e., the publication actually includes t1 in the
    > > publication).
    > >
    >
    > According to me, the comma between EXCEPT clauses breaks such a chain
    > of thought but others may think differently. The docs can clarify such
    > a confusion if any but if multiple people interpret it the way you
    > think, I am fine to go with the direction you are proposing. I am
    > slightly worried that both syntaxes have some pros and cons, so even
    > if we change to what you are suggesting, one can argue to go back to
    > previous syntax as that also has some merits. So, I feel we can
    > complete the pending patch [1] and try to get the opinion of some
    > other people unless changing the code to support another alternative
    > is difficult.
    
    +1 to proceed with completing the pending work.
    I implemented the suggested new syntax, and it only requires a few
    lines of code changes. I evaluated the change to ensure there is no
    additional complexity, and it appears straightforward. We can switch
    to the new syntax once we gather a few more opinions and finalize it.
    The attached 0002 patch includes the corresponding changes.
    
    Regards,
    Vignesh
    
  318. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-16T04:37:02Z

    On Sat, Mar 14, 2026 at 3:05 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    >
    > So, I feel we can
    > complete the pending patch [1] and try to get the opinion of some
    > other people unless changing the code to support another alternative
    > is difficult.
    >
    
    +1. It definitely needs more opinion before we go and change the patch.
    
    thanks
    Shveta
    
    
    
    
  319. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-16T06:35:44Z

    I was away for a couple of weeks leading up to when the EXCEPT TABLE
    feature got pushed (commit fd36606), so I missed my chance to review
    it.
    
    Here are some late review comments of code already on master:
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    1.
    +     <para>
    +      There can be a case where a subscription includes multiple publications.
    +      In such a case, a table or partition that is included in one publication
    +      and listed in the <literal>EXCEPT TABLE</literal> clause of another is
    +      considered included for replication.
    +     </para>
    
    1a
    IIUC you cannot directly specify a partition in the EXCEPT TABLE list
    but a partition still might implicitly be excluded if its root
    partition table is excluded. So I felt the wording "and listed in" is
    not quite correct since you cannot "list" a partition -- it could be
    more like below.
    
    SUGGESTION:
    There can be a case where a subscription includes multiple
    publications. In such a case, a table or partition that is included in
    one publication but excluded (explicitly or implicitly) by the
    <literal>EXCEPT TABLE</literal> clause of another is considered
    included for replication.
    
    ~
    
    1b.
    Anyway, this note is talking about *subscriptions* to multiple
    publications, so I felt that it belongs in the CREATE SUBSCRIPTION
    "Notes" section. Or, maybe on both pages.
    
    ======
    src/backend/catalog/pg_publication.c
    
    check_publication_add_relation:
    
    2.
    + if (pri->except)
    + errormsg = gettext_noop("cannot use publication EXCEPT clause for
    relation \"%s\"");
    + else
    + errormsg = gettext_noop("cannot add relation \"%s\" to publication");
    
    I felt that the first message wording could be improved. e.g. "using
    EXCEPT clause for a relation" sounds backwards. In passing, make it
    more similar to the other (else) message.
    
    SUGGESTION
    cannot specify relation \"%s\" in the publication EXCEPT clause
    
    ======
    src/backend/commands/tablecmds.c
    
    ATExecAttachPartition:
    
    3.
    + bool first = true;
    + StringInfoData pubnames;
    +
    + initStringInfo(&pubnames);
    +
    + foreach_oid(pubid, exceptpuboids)
    + {
    + char    *pubname = get_publication_name(pubid, false);
    +
    + if (!first)
    + {
    + /*
    + * translator: This is a separator in a list of publication
    + * names.
    + */
    + appendStringInfoString(&pubnames, _(", "));
    + }
    +
    + first = false;
    +
    + appendStringInfo(&pubnames, _("\"%s\""), pubname);
    + }
    
    3a.
    Why is appendStringInfo(&pubnames, _("\"%s\""), pubname) using the
    "_(" macro?. AFAIK it does not make sense to call gettext() for a
    pubname.
    
    ~
    
    3b.
    Postgres already has a function to make a CSV list of pubnames. Can't
    we build a list of pubnames here, then just call the common
    'GetPublicationsStr' to get that as a CSV string. PSA a patch
    demonstrating what I mean.
    
    ======
    src/test/subscription/t/037_except.pl
    
    4.
    +# Exclude tab1 (non-inheritance case), and also exclude parent and ONLY parent1
    +# to verify exclusion behavior for inherited tables, including the effect of
    +# ONLY in the EXCEPT TABLE clause.
    +$node_publisher->safe_psql('postgres',
    + "CREATE PUBLICATION tab_pub FOR ALL TABLES EXCEPT TABLE (tab1,
    parent, only parent1)"
    +);
    +
    +# Create a logical replication slot to help with later tests.
    +$node_publisher->safe_psql('postgres',
    + "SELECT pg_create_logical_replication_slot('test_slot', 'pgoutput')");
    +
    +$node_subscriber->safe_psql('postgres',
    + "CREATE SUBSCRIPTION tab_sub CONNECTION '$publisher_connstr'
    PUBLICATION tab_pub"
    +);
    +
    
    Why are those pub/sub prefixed "tab_"?  They are not tables.
    
    Note that elsewhere in this same patch tap test there are other
    pub/subs called "tap_pub_part" and "tap_sub_part". So why are some
    "tap_" and some "tab_"?
    
    I guess those ones called "tab_pub" and "tab_sub" look like they've
    been accidentally changed by a global "tab" replacement, or were
    assumed to be typos when they were not.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
  320. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-16T08:38:21Z

    Hi Vignesh.
    
    Unfortunately, IMO there are some fundamental problems here due to
    there being no accounting for publications with FOR ALL SEQUENCES. So
    I am posting my review comments for just the docs part so you can see
    it from my PoV. Maybe this was already discussed earlier in the thread
    but I saw no mentions of it. AFAICT the discussion/posts were mostly
    focussed on the setting/resetting of EXCEPT TABLE but seem to be
    overlooking the bigger picture.
    
    Below review comments are for v63-0001 docs only.
    
    ======
    Commit Message
    
    1.
    The first form replaces the current EXCEPT TABLE list with the specified
    tables. The second form clears the existing except table list.  Like the
    creation syntax, only root partitioned tables can be specified in the
    exclusion list.
    
    ~
    
    IMO the second form is a long-time missing command from Postgres. For
    example, it is possible to create an "empty" publication. But without
    this "ALTER PUBLICATION name SET ALL TABLES" there was no way to
    convert that to be a "FOR ALL TABLES" publication. So really this
    feature was independently needed and should be done anyway
    irrespective of any side-effect it has for the EXCEPT TABLE list.
    
    Ideally the SET/ADD/DROP ALL TABLES can split out and done ahead of
    the EXCEPT TABLE stuff.
    
    Similarly, SET/ADD/DROP should be implemented also for FOR ALL
    SEQUENCES otherwise there is no way to manipulate those either.
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    2.
    Implementing the "SET ALL TABLES" is only a start. You also need to
    have the other case:
    
    * ADD ALL TABLES
    * DROP ALL TABLES
    
    Note you might start out with something like "CREATE PUBLICATION pub
    FOR ALL TABLES, FOR ALL SEQUENCES" so you need to be able to be able
    to modify/remove the published TABLES part without overwriting
    published SEQUENCES part!!
    
    i.e The "SET" means set the publication; it doesn't mean add to the
    publication. So SET ALL TABLES obliterates any FOR ALL SEQUENCES.
    
    ~~~
    
    3.
    +<phrase>where <replaceable
    class="parameter">publication_except_tables</replaceable> is:</phrase>
    +
    +    [ EXCEPT TABLE ( [ ONLY ] <replaceable
    class="parameter">table_name</replaceable> [, ... ] ) ]
    +
    
    Hmm. Ideally, this should be part of 'publication_object' and
    publication_drop_object' because the ADD/DROP are also needed for the
    reasons given in my above review commentS.
    
    ~~~
    
    4.
       <para>
    -   The first three variants change which tables/schemas are part of the
    -   publication.  The <literal>SET</literal> clause will replace the list of
    -   tables/schemas in the publication with the specified list; the existing
    -   tables/schemas that were present in the publication will be removed.  The
    +   The first four variants modify which tables/schemas are included in the
    +   publication, or which tables are excluded from it.  The
    +   <literal>SET ALL TABLES</literal> clause is used to update the
    +   <literal>EXCEPT TABLE</literal> list of a <literal>FOR ALL TABLES</literal>
    +   publication.  If <literal>EXCEPT TABLE</literal> is specified with a list of
    +   tables, the existing except table list is replaced with the
    specified tables.
    +   If <literal>EXCEPT TABLE</literal> is omitted, the existing except table
    +   list is cleared. The <literal>SET</literal> clause, when 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.  The
    
    I find this all a bit dubious because nothing seems to be accounting
    for the possibility of "FOR ALL SEQUENCES" also in the publication...
    e.g this entire ALTER command should also have
    
    SET ALL SEQUENCES
    ADD ALL SEQUENCES
    DROP ALL SEQUENCE
    
    and
    
    SET ALL TABLES
    ADD ALL TABLES
    DROP ALL TABLES
    
    IMO, a user will need to take care when using ALTER PUBLICATION ...
    SET ALL TABLES that it does not destroy the publication of sequences
    (and vice versa)
    
    -- Start with an "empty" publication and make it a "FOR ALL TABLES"
    publication...
    CREATE PUBLICATION pub;
    ALTER PUBLICATION pub SET ALL TABLES;
    -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES"
    
    
    -- give some table exceptions to it
    ALTER PUBLICATION pub SET ALL TABLES EXCEPT TABLE(t1,t2);
    -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES
    EXCEPT TABLE(t1,t2)"
    
    
    -- add sequences to this
    ALTER PUBLICATION pub ADD ALL SEQUENCES
    -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES
    EXCEPT TABLE(t1,t2), FOR ALL SEQUENCES"
    
    
    -- remove the table exception
    -- here you cannot simply use SET ALL TABLES because you will lose the
    ALL SEQUENCES part of the publication!!
    -- So, instead you need to do like below
    ALTER PUBLICATION pub DROP ALL TABLES;
    ALTER PUBLICATION pub ADD ALL TABLES;
    -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES, FOR
    ALL SEQUENCES"
    
    ~~~
    
    5.
    +</programlisting></para>
    +
    +  <para>
    +   Replace the publication's EXCEPT table list:
    
    /EXCEPT table list/EXCEPT TABLE list/
    
    ~~~
    
    6.
    +  <para>
    +   Change the publication to include all tables by removing any existing
    +   EXCEPT table list:
    
    The clearing of the EXCEPT TABLES is more like a side-effect, so I
    think this can be worded differently.
    
    SUGGESTION
    Reset the publication to be a FOR ALL TABLES publication with no
    excluded tables.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  321. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-16T08:49:47Z

    On Mon, Mar 16, 2026 at 12:06 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > ======
    > src/backend/commands/tablecmds.c
    >
    > ATExecAttachPartition:
    >
    > 3.
    > + bool first = true;
    > + StringInfoData pubnames;
    > +
    > + initStringInfo(&pubnames);
    > +
    > + foreach_oid(pubid, exceptpuboids)
    > + {
    > + char    *pubname = get_publication_name(pubid, false);
    > +
    > + if (!first)
    > + {
    > + /*
    > + * translator: This is a separator in a list of publication
    > + * names.
    > + */
    > + appendStringInfoString(&pubnames, _(", "));
    > + }
    > +
    > + first = false;
    > +
    > + appendStringInfo(&pubnames, _("\"%s\""), pubname);
    > + }
    >
    > 3a.
    > Why is appendStringInfo(&pubnames, _("\"%s\""), pubname) using the
    > "_(" macro?. AFAIK it does not make sense to call gettext() for a
    > pubname.
    >
    
    Why? I see a similar usage in logicalrep_get_attrs_str().
    
    > ~
    >
    > 3b.
    > Postgres already has a function to make a CSV list of pubnames. Can't
    > we build a list of pubnames here, then just call the common
    > 'GetPublicationsStr' to get that as a CSV string. PSA a patch
    > demonstrating what I mean.
    >
    
    Here you are first forming a list then adding commas to it by parsing
    it. I think such a duplicate effort is not required.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  322. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-16T09:39:12Z

    On Mon, Mar 16, 2026 at 2:08 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > IMO, a user will need to take care when using ALTER PUBLICATION ...
    > SET ALL TABLES that it does not destroy the publication of sequences
    > (and vice versa)
    >
    > -- Start with an "empty" publication and make it a "FOR ALL TABLES"
    > publication...
    > CREATE PUBLICATION pub;
    > ALTER PUBLICATION pub SET ALL TABLES;
    > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES"
    >
    >
    > -- give some table exceptions to it
    > ALTER PUBLICATION pub SET ALL TABLES EXCEPT TABLE(t1,t2);
    > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES
    > EXCEPT TABLE(t1,t2)"
    >
    >
    > -- add sequences to this
    > ALTER PUBLICATION pub ADD ALL SEQUENCES
    > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES
    > EXCEPT TABLE(t1,t2), FOR ALL SEQUENCES"
    >
    >
    > -- remove the table exception
    > -- here you cannot simply use SET ALL TABLES because you will lose the
    > ALL SEQUENCES part of the publication!!
    
    Though the patch is not doing that but the syntax intends that. So, we
    should have a variant like "Alter Publication pub1 Set All Tables, All
    Sequences" to retain both.
    
    > -- So, instead you need to do like below
    > ALTER PUBLICATION pub DROP ALL TABLES;
    > ALTER PUBLICATION pub ADD ALL TABLES;
    > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES, FOR
    > ALL SEQUENCES"
    >
    
    I think we can make a separate case for ADD/DROP rather than tie it
    along with this patch. For example, DROP ALL Tables could be used for
    ALL TABLES and non-ALL TABLES publication, for later it could mean to
    drop all the publicized tables and for earlier it just toggles the
    flag.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  323. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-16T10:56:00Z

    On Mon, 16 Mar 2026 at 12:06, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > I was away for a couple of weeks leading up to when the EXCEPT TABLE
    > feature got pushed (commit fd36606), so I missed my chance to review
    > it.
    >
    > Here are some late review comments of code already on master:
    >
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 1.
    > +     <para>
    > +      There can be a case where a subscription includes multiple publications.
    > +      In such a case, a table or partition that is included in one publication
    > +      and listed in the <literal>EXCEPT TABLE</literal> clause of another is
    > +      considered included for replication.
    > +     </para>
    >
    > 1a
    > IIUC you cannot directly specify a partition in the EXCEPT TABLE list
    > but a partition still might implicitly be excluded if its root
    > partition table is excluded. So I felt the wording "and listed in" is
    > not quite correct since you cannot "list" a partition -- it could be
    > more like below.
    >
    > SUGGESTION:
    > There can be a case where a subscription includes multiple
    > publications. In such a case, a table or partition that is included in
    > one publication but excluded (explicitly or implicitly) by the
    > <literal>EXCEPT TABLE</literal> clause of another is considered
    > included for replication.
    
    Modified
    
    > ~
    >
    > 1b.
    > Anyway, this note is talking about *subscriptions* to multiple
    > publications, so I felt that it belongs in the CREATE SUBSCRIPTION
    > "Notes" section. Or, maybe on both pages.
    
    I felt this talks about publication, so this place is ok. We can do it
    like the way you are telling if others also feel that way.
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > check_publication_add_relation:
    >
    > 2.
    > + if (pri->except)
    > + errormsg = gettext_noop("cannot use publication EXCEPT clause for
    > relation \"%s\"");
    > + else
    > + errormsg = gettext_noop("cannot add relation \"%s\" to publication");
    >
    > I felt that the first message wording could be improved. e.g. "using
    > EXCEPT clause for a relation" sounds backwards. In passing, make it
    > more similar to the other (else) message.
    >
    > SUGGESTION
    > cannot specify relation \"%s\" in the publication EXCEPT clause
    
    Modified
    
    > ======
    > src/backend/commands/tablecmds.c
    >
    > ATExecAttachPartition:
    >
    > 3.
    > + bool first = true;
    > + StringInfoData pubnames;
    > +
    > + initStringInfo(&pubnames);
    > +
    > + foreach_oid(pubid, exceptpuboids)
    > + {
    > + char    *pubname = get_publication_name(pubid, false);
    > +
    > + if (!first)
    > + {
    > + /*
    > + * translator: This is a separator in a list of publication
    > + * names.
    > + */
    > + appendStringInfoString(&pubnames, _(", "));
    > + }
    > +
    > + first = false;
    > +
    > + appendStringInfo(&pubnames, _("\"%s\""), pubname);
    > + }
    >
    > 3a.
    > Why is appendStringInfo(&pubnames, _("\"%s\""), pubname) using the
    > "_(" macro?. AFAIK it does not make sense to call gettext() for a
    > pubname.
    
    I had seen that it is implemented similarly in logicalrep_get_attrs_str too:
    appendStringInfo(&attsbuf, _("\"%s\""), remoterel->attnames[i]);
    
    Amit also has mentioned this at [1].
    
    > ~
    >
    > 3b.
    > Postgres already has a function to make a CSV list of pubnames. Can't
    > we build a list of pubnames here, then just call the common
    > 'GetPublicationsStr' to get that as a CSV string. PSA a patch
    > demonstrating what I mean.
    
     The current implementation loops the publication only once. In the
    approach you suggested first you will loop through pubids and get the
    pubname then pass it to GetPublicationsStr and loop again. I felt the
    existing is better.
    
    > ======
    > src/test/subscription/t/037_except.pl
    >
    > 4.
    > +# Exclude tab1 (non-inheritance case), and also exclude parent and ONLY parent1
    > +# to verify exclusion behavior for inherited tables, including the effect of
    > +# ONLY in the EXCEPT TABLE clause.
    > +$node_publisher->safe_psql('postgres',
    > + "CREATE PUBLICATION tab_pub FOR ALL TABLES EXCEPT TABLE (tab1,
    > parent, only parent1)"
    > +);
    > +
    > +# Create a logical replication slot to help with later tests.
    > +$node_publisher->safe_psql('postgres',
    > + "SELECT pg_create_logical_replication_slot('test_slot', 'pgoutput')");
    > +
    > +$node_subscriber->safe_psql('postgres',
    > + "CREATE SUBSCRIPTION tab_sub CONNECTION '$publisher_connstr'
    > PUBLICATION tab_pub"
    > +);
    > +
    >
    > Why are those pub/sub prefixed "tab_"?  They are not tables.
    >
    > Note that elsewhere in this same patch tap test there are other
    > pub/subs called "tap_pub_part" and "tap_sub_part". So why are some
    > "tap_" and some "tab_"?
    >
    > I guess those ones called "tab_pub" and "tab_sub" look like they've
    > been accidentally changed by a global "tab" replacement, or were
    > assumed to be typos when they were not.
    
    Modified, additionally updated an incorrect test comment to reflect
    that only tables are created, not schemas.
    The attached patch has the changes for the same.
    
    [1] - https://www.postgresql.org/message-id/CAA4eK1LLZayJBtrZEMt8PTXdbv_XB14u4PTS4pVBUgEUHPikKQ%40mail.gmail.com
    
    Regards,
    Vignesh
    
  324. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-16T23:58:33Z

    Hi Vignesh.
    
    Thanks for the fixes! Below are some more details about the un-fixed
    ones, in case it makes any difference.
    
    On Mon, Mar 16, 2026 at 9:56 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Mon, 16 Mar 2026 at 12:06, Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    ...
    > > 1b.
    > > Anyway, this note is talking about *subscriptions* to multiple
    > > publications, so I felt that it belongs in the CREATE SUBSCRIPTION
    > > "Notes" section. Or, maybe on both pages.
    >
    > I felt this talks about publication, so this place is ok. We can do it
    > like the way you are telling if others also feel that way.
    
    OK. Just so others are aware, the CREATE SUBSCRIPTION page "Notes"
    section [1] is already saying things like:
    
    i) "If the subscription has several publications in which the same
    table has been published with different WHERE clauses..."
    
    ii) "Subscriptions having several publications in which the same table
    has been published with different column lists..."
    
    So, all I am suggesting is that CREATE SUBSCRIPTION should also have
    exactly the same kind of worded note to describe behaviour for
    subscriptions having several publications with clashing
    included/excluded tables.
    
    > > ======
    > > src/backend/commands/tablecmds.c
    > >
    > > ATExecAttachPartition:
    > >
    > > 3.
    > > + bool first = true;
    > > + StringInfoData pubnames;
    > > +
    > > + initStringInfo(&pubnames);
    > > +
    > > + foreach_oid(pubid, exceptpuboids)
    > > + {
    > > + char    *pubname = get_publication_name(pubid, false);
    > > +
    > > + if (!first)
    > > + {
    > > + /*
    > > + * translator: This is a separator in a list of publication
    > > + * names.
    > > + */
    > > + appendStringInfoString(&pubnames, _(", "));
    > > + }
    > > +
    > > + first = false;
    > > +
    > > + appendStringInfo(&pubnames, _("\"%s\""), pubname);
    > > + }
    > >
    > > 3a.
    > > Why is appendStringInfo(&pubnames, _("\"%s\""), pubname) using the
    > > "_(" macro?. AFAIK it does not make sense to call gettext() for a
    > > pubname.
    >
    > I had seen that it is implemented similarly in logicalrep_get_attrs_str too:
    > appendStringInfo(&attsbuf, _("\"%s\""), remoterel->attnames[i]);
    >
    > Amit also has mentioned this at [1].
    
    OK, but AFAICT that logicalrep_get_attrs_str code is the original
    mistaken usage. Copying it just makes 2 mistakes.
    
    e.g. the purpose of using gettext (e.g. the "_(x)" macro) is for
    marking the string for i18n translation. But there's nothing even to
    translate here, it's just a format specifier in quotes.
    
    Also, the fact that logicalrep_get_attrs_str was the one-and-only
    usage of _(\"%s\") in the entire PG source indicates to me that it's
    dubious.
    
    > > 3b.
    > > Postgres already has a function to make a CSV list of pubnames. Can't
    > > we build a list of pubnames here, then just call the common
    > > 'GetPublicationsStr' to get that as a CSV string. PSA a patch
    > > demonstrating what I mean.
    >
    >  The current implementation loops the publication only once. In the
    > approach you suggested first you will loop through pubids and get the
    > pubname then pass it to GetPublicationsStr and loop again. I felt the
    > existing is better.
    
    Sure. It's your call.
    
    FWIW, I think the extra looping is insignificant because:
    i) loops will be tiny (how many publications will be publishing the same table?)
    ii) it is only used for an ERROR message; performance hardly matters.
    
    OTOH, using the common function implementation is <10 lines instead of
    >20 lines.
    
    ======
    [1] https://www.postgresql.org/docs/devel/sql-createsubscription.html
    
    Kind Regards
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  325. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-17T01:52:23Z

    On Mon, Mar 16, 2026 at 8:39 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Mon, Mar 16, 2026 at 2:08 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > IMO, a user will need to take care when using ALTER PUBLICATION ...
    > > SET ALL TABLES that it does not destroy the publication of sequences
    > > (and vice versa)
    > >
    > > -- Start with an "empty" publication and make it a "FOR ALL TABLES"
    > > publication...
    > > CREATE PUBLICATION pub;
    > > ALTER PUBLICATION pub SET ALL TABLES;
    > > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES"
    > >
    > >
    > > -- give some table exceptions to it
    > > ALTER PUBLICATION pub SET ALL TABLES EXCEPT TABLE(t1,t2);
    > > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES
    > > EXCEPT TABLE(t1,t2)"
    > >
    > >
    > > -- add sequences to this
    > > ALTER PUBLICATION pub ADD ALL SEQUENCES
    > > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES
    > > EXCEPT TABLE(t1,t2), FOR ALL SEQUENCES"
    > >
    > >
    > > -- remove the table exception
    > > -- here you cannot simply use SET ALL TABLES because you will lose the
    > > ALL SEQUENCES part of the publication!!
    >
    > Though the patch is not doing that but the syntax intends that. So, we
    > should have a variant like "Alter Publication pub1 Set All Tables, All
    > Sequences" to retain both.
    
    Right. Doing "SET ALL TABLES, ALL SEQUENCES" is another way to achieve
    the same outcome using one ALTER command instead of two.
    
    But this way requires adding the currently missing SET/ADD/DROP ALL
    SEQUENCES syntax.
    
    >
    > > -- So, instead you need to do like below
    > > ALTER PUBLICATION pub DROP ALL TABLES;
    > > ALTER PUBLICATION pub ADD ALL TABLES;
    > > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES, FOR
    > > ALL SEQUENCES"
    > >
    >
    > I think we can make a separate case for ADD/DROP rather than tie it
    > along with this patch. For example, DROP ALL Tables could be used for
    > ALL TABLES and non-ALL TABLES publication, for later it could mean to
    > drop all the publicized tables and for earlier it just toggles the
    > flag.
    >
    
    The basic expected SET/ADD/DROP behaviours are already very clear;
    It's just that they are not yet implemented for "ALL TABLES" and "ALL
    SEQUENCES". Ideally, those would be prerequisites for this EXCEPT
    patch.
    
    If they are done separately later, then it means in the interim, there
    will be some publication clause combinations that can lead to dead
    ends, where the user would be unable to modify the publication as they
    want without dropping and recreating it.
    
    Maybe that is OK. Perhaps having functionality gaps doesn't matter as
    much as I think it does:
    - e.g1. The FOR ALL SEQUENCES has existed for some months, and we
    cannot use ALTER to change it. Nobody complained?
    - e.g2. For years, it's been impossible to convert an "empty"
    publication into a FOR ALL TABLES publication. Nobody complained?
    
    OTOH, doing them in the right order is preferable to avoid churning
    the ALTER PUBLICATION docs page synopsis/description.
    
    ------
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  326. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-17T03:16:38Z

    On Tue, Mar 17, 2026 at 7:22 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Mon, Mar 16, 2026 at 8:39 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > > On Mon, Mar 16, 2026 at 2:08 PM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > I think we can make a separate case for ADD/DROP rather than tie it
    > > along with this patch. For example, DROP ALL Tables could be used for
    > > ALL TABLES and non-ALL TABLES publication, for later it could mean to
    > > drop all the publicized tables and for earlier it just toggles the
    > > flag.
    > >
    >
    > The basic expected SET/ADD/DROP behaviours are already very clear;
    > It's just that they are not yet implemented for "ALL TABLES" and "ALL
    > SEQUENCES". Ideally, those would be prerequisites for this EXCEPT
    > patch.
    >
    > If they are done separately later, then it means in the interim, there
    > will be some publication clause combinations that can lead to dead
    > ends, where the user would be unable to modify the publication as they
    > want without dropping and recreating it.
    >
    > Maybe that is OK. Perhaps having functionality gaps doesn't matter as
    > much as I think it does:
    
    Possible.
    
    > - e.g1. The FOR ALL SEQUENCES has existed for some months, and we
    > cannot use ALTER to change it. Nobody complained?
    >
    
    Isn't the same true FOR ALL TABLES which is there from the time
    logical replication is introduced? So, we tried to follow the same for
    ALL SEQUENCES case.
    
    We can do extensions of the proposed or existing syntaxes based on the
    user input in future. If we try to find all possible ways to set/reset
    all the options provided for publications/subscriptions, we may find a
    few more. At this stage, we want one way for users to change the
    EXCEPT list which the proposed patch is providing.
    
    > - e.g2. For years, it's been impossible to convert an "empty"
    > publication into a FOR ALL TABLES publication. Nobody complained?
    >
    
    Right, so that indicates, there is no immediate need to support all
    kinds of syntaxes.
    
    > OTOH, doing them in the right order is preferable to avoid churning
    > the ALTER PUBLICATION docs page synopsis/description.
    >
    
    That is a small price to pay as compared to building/maintaining more
    variants when we don't see field demand for the same.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  327. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-17T03:25:30Z

    On Tue, Mar 17, 2026 at 5:29 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Thanks for the fixes! Below are some more details about the un-fixed
    > ones, in case it makes any difference.
    >
    > On Mon, Mar 16, 2026 at 9:56 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > On Mon, 16 Mar 2026 at 12:06, Peter Smith <smithpb2250@gmail.com> wrote:
    > > >
    > ...
    > > > 1b.
    > > > Anyway, this note is talking about *subscriptions* to multiple
    > > > publications, so I felt that it belongs in the CREATE SUBSCRIPTION
    > > > "Notes" section. Or, maybe on both pages.
    > >
    > > I felt this talks about publication, so this place is ok. We can do it
    > > like the way you are telling if others also feel that way.
    >
    > OK. Just so others are aware, the CREATE SUBSCRIPTION page "Notes"
    > section [1] is already saying things like:
    >
    > i) "If the subscription has several publications in which the same
    > table has been published with different WHERE clauses..."
    >
    > ii) "Subscriptions having several publications in which the same table
    > has been published with different column lists..."
    >
    > So, all I am suggesting is that CREATE SUBSCRIPTION should also have
    > exactly the same kind of worded note to describe behaviour for
    > subscriptions having several publications with clashing
    > included/excluded tables.
    >
    
    I don't know if that is required or not. Please wait to get the base
    feature stabilized, we can add more docs later as well after some
    inputs.
    
    > > > ======
    > > > src/backend/commands/tablecmds.c
    > > >
    > > > ATExecAttachPartition:
    > > >
    > > > 3.
    > > > + bool first = true;
    > > > + StringInfoData pubnames;
    > > > +
    > > > + initStringInfo(&pubnames);
    > > > +
    > > > + foreach_oid(pubid, exceptpuboids)
    > > > + {
    > > > + char    *pubname = get_publication_name(pubid, false);
    > > > +
    > > > + if (!first)
    > > > + {
    > > > + /*
    > > > + * translator: This is a separator in a list of publication
    > > > + * names.
    > > > + */
    > > > + appendStringInfoString(&pubnames, _(", "));
    > > > + }
    > > > +
    > > > + first = false;
    > > > +
    > > > + appendStringInfo(&pubnames, _("\"%s\""), pubname);
    > > > + }
    > > >
    > > > 3a.
    > > > Why is appendStringInfo(&pubnames, _("\"%s\""), pubname) using the
    > > > "_(" macro?. AFAIK it does not make sense to call gettext() for a
    > > > pubname.
    > >
    > > I had seen that it is implemented similarly in logicalrep_get_attrs_str too:
    > > appendStringInfo(&attsbuf, _("\"%s\""), remoterel->attnames[i]);
    > >
    > > Amit also has mentioned this at [1].
    >
    > OK, but AFAICT that logicalrep_get_attrs_str code is the original
    > mistaken usage. Copying it just makes 2 mistakes.
    >
    > e.g. the purpose of using gettext (e.g. the "_(x)" macro) is for
    > marking the string for i18n translation. But there's nothing even to
    > translate here, it's just a format specifier in quotes.
    >
    > Also, the fact that logicalrep_get_attrs_str was the one-and-only
    > usage of _(\"%s\") in the entire PG source indicates to me that it's
    > dubious.
    >
    
    I am not a translation expert, so not sure but will consider this
    input after taking care of the pending main patch.
    
    > > > 3b.
    > > > Postgres already has a function to make a CSV list of pubnames. Can't
    > > > we build a list of pubnames here, then just call the common
    > > > 'GetPublicationsStr' to get that as a CSV string. PSA a patch
    > > > demonstrating what I mean.
    > >
    > >  The current implementation loops the publication only once. In the
    > > approach you suggested first you will loop through pubids and get the
    > > pubname then pass it to GetPublicationsStr and loop again. I felt the
    > > existing is better.
    >
    > Sure. It's your call.
    >
    > FWIW, I think the extra looping is insignificant because:
    > i) loops will be tiny (how many publications will be publishing the same table?)
    >
    
    I don't have a ready answer for this, the number could be large as well.
    
    > ii) it is only used for an ERROR message; performance hardly matters.
    >
    > OTOH, using the common function implementation is <10 lines instead of
    > >20 lines.
    >
    
    Hmm, this is another way to look at it but looping extra time over
    publications to call existing function sounds redundant to me. I don't
    want to argue what is the best way for this but if more people also
    prefer this pattern then we can change it.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  328. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-17T06:53:45Z

    On Mon, 16 Mar 2026 at 14:08, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Vignesh.
    >
    > Unfortunately, IMO there are some fundamental problems here due to
    > there being no accounting for publications with FOR ALL SEQUENCES. So
    > I am posting my review comments for just the docs part so you can see
    > it from my PoV. Maybe this was already discussed earlier in the thread
    > but I saw no mentions of it. AFAICT the discussion/posts were mostly
    > focussed on the setting/resetting of EXCEPT TABLE but seem to be
    > overlooking the bigger picture.
    >
    > Below review comments are for v63-0001 docs only.
    >
    > ======
    > Commit Message
    >
    > 1.
    > The first form replaces the current EXCEPT TABLE list with the specified
    > tables. The second form clears the existing except table list.  Like the
    > creation syntax, only root partitioned tables can be specified in the
    > exclusion list.
    >
    > ~
    >
    > IMO the second form is a long-time missing command from Postgres. For
    > example, it is possible to create an "empty" publication. But without
    > this "ALTER PUBLICATION name SET ALL TABLES" there was no way to
    > convert that to be a "FOR ALL TABLES" publication. So really this
    > feature was independently needed and should be done anyway
    > irrespective of any side-effect it has for the EXCEPT TABLE list.
    >
    > Ideally the SET/ADD/DROP ALL TABLES can split out and done ahead of
    > the EXCEPT TABLE stuff.
    >
    > Similarly, SET/ADD/DROP should be implemented also for FOR ALL
    > SEQUENCES otherwise there is no way to manipulate those either.
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 2.
    > Implementing the "SET ALL TABLES" is only a start. You also need to
    > have the other case:
    >
    > * ADD ALL TABLES
    > * DROP ALL TABLES
    >
    > Note you might start out with something like "CREATE PUBLICATION pub
    > FOR ALL TABLES, FOR ALL SEQUENCES" so you need to be able to be able
    > to modify/remove the published TABLES part without overwriting
    > published SEQUENCES part!!
    >
    > i.e The "SET" means set the publication; it doesn't mean add to the
    > publication. So SET ALL TABLES obliterates any FOR ALL SEQUENCES.
    >
    > ~~~
    >
    > 3.
    > +<phrase>where <replaceable
    > class="parameter">publication_except_tables</replaceable> is:</phrase>
    > +
    > +    [ EXCEPT TABLE ( [ ONLY ] <replaceable
    > class="parameter">table_name</replaceable> [, ... ] ) ]
    > +
    >
    > Hmm. Ideally, this should be part of 'publication_object' and
    > publication_drop_object' because the ADD/DROP are also needed for the
    > reasons given in my above review commentS.
    >
    > ~~~
    >
    > 4.
    >    <para>
    > -   The first three variants change which tables/schemas are part of the
    > -   publication.  The <literal>SET</literal> clause will replace the list of
    > -   tables/schemas in the publication with the specified list; the existing
    > -   tables/schemas that were present in the publication will be removed.  The
    > +   The first four variants modify which tables/schemas are included in the
    > +   publication, or which tables are excluded from it.  The
    > +   <literal>SET ALL TABLES</literal> clause is used to update the
    > +   <literal>EXCEPT TABLE</literal> list of a <literal>FOR ALL TABLES</literal>
    > +   publication.  If <literal>EXCEPT TABLE</literal> is specified with a list of
    > +   tables, the existing except table list is replaced with the
    > specified tables.
    > +   If <literal>EXCEPT TABLE</literal> is omitted, the existing except table
    > +   list is cleared. The <literal>SET</literal> clause, when 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.  The
    >
    > I find this all a bit dubious because nothing seems to be accounting
    > for the possibility of "FOR ALL SEQUENCES" also in the publication...
    > e.g this entire ALTER command should also have
    >
    > SET ALL SEQUENCES
    > ADD ALL SEQUENCES
    > DROP ALL SEQUENCE
    >
    > and
    >
    > SET ALL TABLES
    > ADD ALL TABLES
    > DROP ALL TABLES
    >
    > IMO, a user will need to take care when using ALTER PUBLICATION ...
    > SET ALL TABLES that it does not destroy the publication of sequences
    > (and vice versa)
    >
    > -- Start with an "empty" publication and make it a "FOR ALL TABLES"
    > publication...
    > CREATE PUBLICATION pub;
    > ALTER PUBLICATION pub SET ALL TABLES;
    > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES"
    >
    >
    > -- give some table exceptions to it
    > ALTER PUBLICATION pub SET ALL TABLES EXCEPT TABLE(t1,t2);
    > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES
    > EXCEPT TABLE(t1,t2)"
    >
    >
    > -- add sequences to this
    > ALTER PUBLICATION pub ADD ALL SEQUENCES
    > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES
    > EXCEPT TABLE(t1,t2), FOR ALL SEQUENCES"
    >
    >
    > -- remove the table exception
    > -- here you cannot simply use SET ALL TABLES because you will lose the
    > ALL SEQUENCES part of the publication!!
    > -- So, instead you need to do like below
    > ALTER PUBLICATION pub DROP ALL TABLES;
    > ALTER PUBLICATION pub ADD ALL TABLES;
    > -- result is equivalent to "CREATE PUBLICATION ... FOR ALL TABLES, FOR
    > ALL SEQUENCES"
    >
    > ~~~
    >
    > 5.
    > +</programlisting></para>
    > +
    > +  <para>
    > +   Replace the publication's EXCEPT table list:
    >
    > /EXCEPT table list/EXCEPT TABLE list/
    >
    > ~~~
    >
    > 6.
    > +  <para>
    > +   Change the publication to include all tables by removing any existing
    > +   EXCEPT table list:
    >
    > The clearing of the EXCEPT TABLES is more like a side-effect, so I
    > think this can be worded differently.
    >
    > SUGGESTION
    > Reset the publication to be a FOR ALL TABLES publication with no
    > excluded tables.
    
    Thanks for the comments, the agreed comments have been addressed in
    the v64 version patch attached.
    
    Regards,
    Vignesh
    
  329. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-17T11:01:43Z

    On Tue, Mar 17, 2026 at 12:23 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Thanks for the comments, the agreed comments have been addressed in
    > the v64 version patch attached.
    >
    
    Please find a few comments:
    
    
    1)
    + The
    +   <literal>SET ALL TABLES</literal> clause is used to update the
    +   <literal>EXCEPT TABLE</literal> list of a <literal>FOR ALL TABLES</literal>
    +   publication.  If <literal>EXCEPT TABLE</literal> is specified with a list of
    +   tables, the existing except table list is replaced with the
    specified tables.
    +   If <literal>EXCEPT TABLE</literal> is omitted, the existing except table
    +   list is cleared.
    
    How about changing it to (or anything better to reflect new changes):
    
    The SET ALL TABLES clause can transform an empty publication, or one
    defined for ALL SEQUENCES (or both ALL TABLES and ALL SEQUENCES), into
    a publication defined for ALL TABLES. Likewise, SET ALL SEQUENCES can
    convert an empty publication, or one defined for ALL TABLES (or both
    ALL TABLES and ALL SEQUENCES), into a publication defined for ALL
    SEQUENCES.
    In addition, SET ALL TABLES may be used to update the EXCEPT TABLE
    list of a FOR ALL TABLES publication. If EXCEPT TABLE is specified
    with a list of tables, the existing exclusion list is replaced with
    the specified tables. If EXCEPT TABLE is omitted, the existing
    exclusion list is cleared.
    
    2)
    +bool
    +is_include_relation_publication(Oid pubid)
    
    The name 'is_include_relation_publication' looks slightly odd to me.
    Few options are: is_explicit_table_publication,
    is_table_list_publication, is_table_publication. Or anything better if
    you can think of?
    
    3)
    is_include_relation_publication:
    
    + /* If we find even one included relation, we are done */
    + if (!pubrel->prexcept)
    + {
    + result = true;
    + break;
    + }
    
    we can break the loop irrespective of the 'prexcept' flag as we can
    never have a combination of mixed prexcept entries for the same pub.
    Whether all will be with prexcept=true or all will be false. Even a
    loop is not needed. We can fetch the first entry alone (similar to
    is_schema_publication) and if that is valid, we can check the flag and
    return accordingly. Something like:
    
    if (HeapTupleIsValid())
    {
       pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
       result = !pubrel->prexcept
    }
    
    
    4)
    publication_add_relation:
    + /*
    + * True if EXCEPT tables require explicit relcache invalidation. If
    + * 'puballtables' changes, global invalidation covers them.
    + */
    + inval_except_table = (stmt != NULL) &&
    + (stmt->for_all_tables == pub->alltables);
    
    
    It took me some time to figure out why we don't need invalidation for
    the case where we are converting ALL SEQ to ALL TABLEs EXCEPT(..).  I
    think it is worth adding more comments here. Suggestion:
    
    /*
     * Determine whether EXCEPT tables require explicit relcache invalidation.
     *
     * For CREATE PUBLICATION with EXCEPT tables, invalidation is not needed,
     * since it is handled when marking the publication as ALL TABLES.
     *
     * 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 it is handled
     * when marking the publication as ALL TABLES.
     */
    
    thanks
    Shveta
    
    
    
    
  330. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-17T12:05:32Z

    On Tue, Mar 17, 2026 at 4:31 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > 2)
    > +bool
    > +is_include_relation_publication(Oid pubid)
    >
    > The name 'is_include_relation_publication' looks slightly odd to me.
    > Few options are: is_explicit_table_publication,
    > is_table_list_publication, is_table_publication. Or anything better if
    > you can think of?
    >
    
    +1 for is_table_publication() as it matches the existing function
    is_schema_publication().
    
    Few other comments:
    =================
    *
    -   The <literal>ADD TABLES IN SCHEMA</literal> and
    -   <literal>SET TABLES IN SCHEMA</literal> to a publication requires the
    +   The <literal>ADD TABLES IN SCHEMA</literal>,
    +   <literal>SET TABLES IN SCHEMA</literal>, and
    +   <literal>SET ALL TABLES</literal> to a publication requires the
        invoking user to be a superuser.
    
    Do we need to mention SET ALL SEQUENCES variant as well?
    
    *
    AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext,
         schemaidlist != NIL);
      AlterPublicationSchemas(stmt, tup, schemaidlist);
    +
    + if (stmt->for_all_tables || stmt->for_all_sequences)
    + {
    + bool nulls[Natts_pg_publication];
    + bool replaces[Natts_pg_publication];
    + Datum values[Natts_pg_publication];
    + bool dirty = false;
    +
    + memset(values, 0, sizeof(values));
    + memset(nulls, false, sizeof(nulls));
    + memset(replaces, false, sizeof(replaces));
    +
    + if (stmt->for_all_tables != pubform->puballtables)
    + {
    + values[Anum_pg_publication_puballtables - 1] =
    + BoolGetDatum(stmt->for_all_tables);
    + replaces[Anum_pg_publication_puballtables - 1] = true;
    + dirty = true;
    + }
    +
    + if (stmt->for_all_sequences != pubform->puballsequences)
    + {
    + values[Anum_pg_publication_puballsequences - 1] =
    + BoolGetDatum(stmt->for_all_sequences);
    + replaces[Anum_pg_publication_puballsequences - 1] = true;
    + dirty = true;
    + }
    +
    + if (dirty)
    + {
    + tup = heap_modify_tuple(tup, RelationGetDescr(rel), values,
    + nulls, replaces);
    + CatalogTupleUpdate(rel, &tup->t_self, tup);
    + CommandCounterIncrement();
    +
    + /* For ALL TABLES, we must invalidate all relcache entries */
    + if (replaces[Anum_pg_publication_puballtables - 1])
    + CacheInvalidateRelcacheAll();
    + }
    + }
    
    Can we move this new code to a separate function, say
    AlterPublicationAllFlags()?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  331. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-17T15:51:15Z

    On Tue, 17 Mar 2026 at 16:31, shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Mar 17, 2026 at 12:23 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Thanks for the comments, the agreed comments have been addressed in
    > > the v64 version patch attached.
    > >
    >
    > Please find a few comments:
    >
    >
    > 1)
    > + The
    > +   <literal>SET ALL TABLES</literal> clause is used to update the
    > +   <literal>EXCEPT TABLE</literal> list of a <literal>FOR ALL TABLES</literal>
    > +   publication.  If <literal>EXCEPT TABLE</literal> is specified with a list of
    > +   tables, the existing except table list is replaced with the
    > specified tables.
    > +   If <literal>EXCEPT TABLE</literal> is omitted, the existing except table
    > +   list is cleared.
    >
    > How about changing it to (or anything better to reflect new changes):
    >
    > The SET ALL TABLES clause can transform an empty publication, or one
    > defined for ALL SEQUENCES (or both ALL TABLES and ALL SEQUENCES), into
    > a publication defined for ALL TABLES. Likewise, SET ALL SEQUENCES can
    > convert an empty publication, or one defined for ALL TABLES (or both
    > ALL TABLES and ALL SEQUENCES), into a publication defined for ALL
    > SEQUENCES.
    > In addition, SET ALL TABLES may be used to update the EXCEPT TABLE
    > list of a FOR ALL TABLES publication. If EXCEPT TABLE is specified
    > with a list of tables, the existing exclusion list is replaced with
    > the specified tables. If EXCEPT TABLE is omitted, the existing
    > exclusion list is cleared.
    >
    > 2)
    > +bool
    > +is_include_relation_publication(Oid pubid)
    >
    > The name 'is_include_relation_publication' looks slightly odd to me.
    > Few options are: is_explicit_table_publication,
    > is_table_list_publication, is_table_publication. Or anything better if
    > you can think of?
    >
    > 3)
    > is_include_relation_publication:
    >
    > + /* If we find even one included relation, we are done */
    > + if (!pubrel->prexcept)
    > + {
    > + result = true;
    > + break;
    > + }
    >
    > we can break the loop irrespective of the 'prexcept' flag as we can
    > never have a combination of mixed prexcept entries for the same pub.
    > Whether all will be with prexcept=true or all will be false. Even a
    > loop is not needed. We can fetch the first entry alone (similar to
    > is_schema_publication) and if that is valid, we can check the flag and
    > return accordingly. Something like:
    >
    > if (HeapTupleIsValid())
    > {
    >    pubrel = (Form_pg_publication_rel) GETSTRUCT(tup);
    >    result = !pubrel->prexcept
    > }
    >
    >
    > 4)
    > publication_add_relation:
    > + /*
    > + * True if EXCEPT tables require explicit relcache invalidation. If
    > + * 'puballtables' changes, global invalidation covers them.
    > + */
    > + inval_except_table = (stmt != NULL) &&
    > + (stmt->for_all_tables == pub->alltables);
    >
    >
    > It took me some time to figure out why we don't need invalidation for
    > the case where we are converting ALL SEQ to ALL TABLEs EXCEPT(..).  I
    > think it is worth adding more comments here. Suggestion:
    >
    > /*
    >  * Determine whether EXCEPT tables require explicit relcache invalidation.
    >  *
    >  * For CREATE PUBLICATION with EXCEPT tables, invalidation is not needed,
    >  * since it is handled when marking the publication as ALL TABLES.
    >  *
    >  * 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 it is handled
    >  * when marking the publication as ALL TABLES.
    >  */
    
    These comments are addressed in the v65 version patch attached. Also
    the comments from [1] have been addressed in this.
    [1] - https://www.postgresql.org/message-id/CAA4eK1%2BmSpCzj%2BB2PW_68DJpXHA0KMgT9Nrz9P83_c1vdKya8g%40mail.gmail.com
    
    Regards,
    Vignesh
    
  332. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-18T04:10:24Z

    On Tue, Mar 17, 2026 at 9:21 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    >
    > These comments are addressed in the v65 version patch attached. Also
    > the comments from [1] have been addressed in this.
    > [1] - https://www.postgresql.org/message-id/CAA4eK1%2BmSpCzj%2BB2PW_68DJpXHA0KMgT9Nrz9P83_c1vdKya8g%40mail.gmail.com
    >
    
    Thanks, few trivial things:
    
    1)
    In the doc we talk about the first two variants and then the third
    variant. IIUC, you need to change variants to match the new details,
    i.e.
    
    Change these:
    ALTER PUBLICATION name ADD publication_object [, ...]
    ALTER PUBLICATION name SET { publication_object [, ...] |
    publication_all_object [, ... ] }
    ALTER PUBLICATION name DROP publication_drop_object [, ...]
    
    to:
    ALTER PUBLICATION name ADD publication_object [, ...]
    ALTER PUBLICATION name DROP publication_drop_object [, ...]
    ALTER PUBLICATION name SET { publication_object [, ...] |
    publication_all_object [, ... ] }
    
    or change the details to match variants.
    
    2)
    
    + /*
    + * Publications contain either only EXCEPT entries or only included
    + * tables. So checking a single tuple is sufficient.
    + */
    + result = !pubrel->prexcept;
    
    The command looks slightly misleading, can we please change it to:
    
    For any publication, pg_publication_rel contains either only EXCEPT
    entries or only explicitly included tables. Therefore, examining the
    first tuple is sufficient to determine table inclusion.
    
    3)
    publication_add_relation() contains repetitive comments at both the
    beginning and end of the function regarding invalidation of EXCEPT
    tables. We can keep the first one (slightly rephrased, if needed, to
    incorporate the second) and remove the latter.
    
    thanks
    Shveta
    
    
    
    
  333. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-18T04:27:26Z

    On Wed, Mar 18, 2026 at 3:10 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Tue, Mar 17, 2026 at 9:21 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > >
    > > These comments are addressed in the v65 version patch attached. Also
    > > the comments from [1] have been addressed in this.
    > > [1] - https://www.postgresql.org/message-id/CAA4eK1%2BmSpCzj%2BB2PW_68DJpXHA0KMgT9Nrz9P83_c1vdKya8g%40mail.gmail.com
    > >
    >
    > Thanks, few trivial things:
    >
    > 1)
    > In the doc we talk about the first two variants and then the third
    > variant. IIUC, you need to change variants to match the new details,
    > i.e.
    >
    > Change these:
    > ALTER PUBLICATION name ADD publication_object [, ...]
    > ALTER PUBLICATION name SET { publication_object [, ...] |
    > publication_all_object [, ... ] }
    > ALTER PUBLICATION name DROP publication_drop_object [, ...]
    >
    > to:
    > ALTER PUBLICATION name ADD publication_object [, ...]
    > ALTER PUBLICATION name DROP publication_drop_object [, ...]
    > ALTER PUBLICATION name SET { publication_object [, ...] |
    > publication_all_object [, ... ] }
    >
    > or change the details to match variants.
    >
    
    +1
    
    (I was about to post the same comment)
    
    Talking about "variants" like this forces the reader to keep referring
    back to the synopsis to try to figure out what is actually being
    described.
    
    A simple improvement like below could help clarify things:
    e.g. say "The third variant (DROP)" instead of just saying "The third variant"
    e.g. say "The fourth variant (SET parameters)" instead of just saying
    "The fourth variant"
    etc.
    
    Aside from that, AFAICT, the patch is a bit muddled about some variant numbers.
    
    e.g.1  "The first two variants modify which tables/schemas are part of
    the publication. The ADD and DROP clauses will add and remove one or
    more tables/schemas from the publication."
    
    No, because DROP is the 3rd variant, not in the "first two",
    
    e.g.2.  "The third variant either modifies the included tables/schemas
    or marks the publication..."
    
    No, the 3rd variant now is DROP. But this whole paragraph is referring
    to SET objects.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  334. Re: Skipping schema changes in publication

    Masahiko Sawada <sawada.mshk@gmail.com> — 2026-03-18T06:20:34Z

    On Sat, Mar 14, 2026 at 2:35 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Mar 13, 2026 at 11:51 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    > >
    > > On Fri, Mar 13, 2026 at 2:51 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > > On Fri, Mar 13, 2026 at 8:55 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > > > >
    > > > > Hou-San (off-list) gave an interesting example of how it might look
    > > > > when the example is combined with the hypothetical future ability to
    > > > > exclude all schema tables from a "FOR ALL TABLES"
    > > > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > > > > SCHEMA s1);
    > > > >
    > > >
    > > > So, if we follow this, in future, the syntax would look like:
    > > > Create publication pub1 FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > > > SCHEMA s1, s2), ALL SEQUENCES EXCEPT (SEQUENCE s1);
    > > >
    > > > I think if we use the above syntax then one can also argue that why
    > > > not use FOR ALL (TABLES, SEQUENCES) instead of ALL TABLES, ALL
    > > > SEQUENCES.
    > >
    > > The same is true for the syntaxes of TABLE(t1, t2) and SCHEMA(s1, s2),
    > > no? If we allow grouping the tables and the schemas by TABLE and
    > > SCHEMA keywords respectively in exclusion lists, one could argue why
    > > we don't allow it for the inclusion list.
    > >
    > > >
    > > > Instead the current command would like:
    > > > Create publication pub1 FOR ALL TABLES EXCEPT TABLE (t1, t2), EXCEPT
    > > > TABLES IN SCHEMA (s1, s2), ALL SEQUENCES EXCEPT SEQUENCE (seq1, seq2);
    > > >
    > > > Though it is slightly lengthy it makes the syntax local such that the
    > > > meaning of a clause is determined by what is inside it, not by
    > > > something that appears ten words later.
    > >
    > > While it helps the syntax locality, I'm concerned that it could make
    > > the context of the EXCEPT clause ambiguous. For example, IIUC the
    > > above syntax rule would allow us to write:
    > >
    > > CREATE PUBLICATION pub1 FOR ALL TABLES EXCEPT TABLES IN SCHEMA (s1),
    > > EXCEPT TABLE (t1);
    > >
    > > Regarding the second EXCEPT clause, while one might think it excludes
    > > t1 from ALL TABLES, one might think it excludes t1 from the first
    > > EXCLUDE clause (i.e., the publication actually includes t1 in the
    > > publication).
    > >
    >
    > According to me, the comma between EXCEPT clauses breaks such a chain
    > of thought but others may think differently. The docs can clarify such
    > a confusion if any but if multiple people interpret it the way you
    > think, I am fine to go with the direction you are proposing. I am
    > slightly worried that both syntaxes have some pros and cons, so even
    > if we change to what you are suggesting, one can argue to go back to
    > previous syntax as that also has some merits. So, I feel we can
    > complete the pending patch [1] and try to get the opinion of some
    > other people unless changing the code to support another alternative
    > is difficult.
    
    I agree to complete the pending ALTER PUBLICATION part first and then
    have more discussion on it. As Vignesh showed in another email, the
    changes are straightforward.
    
    Regards,
    
    -- 
    Masahiko Sawada
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  335. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-18T06:40:48Z

    Hi Vignesh.
    
    Some review comments for patch v65-0001.
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    1.
    It seems that SET ALL TABLES is not supported if the publication
    already has FOR TABLE.
    
    e.g
    alter publication pub1 set all tables;
    ERROR:  publication "pub1" does not support ALL TABLES operations
    DETAIL:  This operation requires the publication to be defined as FOR
    ALL TABLES/SEQUENCES or to be empty.
    
    e.g.
    alter publication pub2 set table t1;
    ERROR:  publication "pub2" is defined as FOR ALL TABLES
    DETAIL:  Tables or sequences cannot be added to or dropped from FOR
    ALL TABLES publications.
    
    I am not going to debate what rules are right or wrong. (Some rules do
    seem a bit ad-hoc to me, but maybe they are just erring on the safety
    side). But my point is that the documentation does not seem to say
    anything much about what the rules are
    
    e.g. it says "Adding/Setting any schema when the publication also
    publishes a table with a column list, and vice versa is not
    supported.", but OTOH it says nothing about what is
    supported/unsupported for SET ALL TABLES.
    
    ======
    src/backend/catalog/pg_publication.c
    
    2.
    +/*
    + * Returns true if the publication has explicitly included relation (i.e.,
    + * not marked as EXCEPT).
    + */
    +bool
    +is_table_publication(Oid pubid)
    
    To me, an "explicitly included relation" is like when you say "FOR
    TABLE t1", where the "t1" is explicitly named.
    
    So it's not very clear whether you consider  "FOR ALL TABLES" or a
    "FOR TABLES IN SCHEMA" publication explicitly includes tables or not?
    
    The function comment needs to be clearer.
    
    ~~~
    
    publication_add_relation:
    
    3.
    + /*
    + * Determine whether EXCEPT tables require explicit relcache invalidation.
    + *
    + * For CREATE PUBLICATION with EXCEPT tables, invalidation is not needed,
    + * since it is handled when marking the publication as ALL TABLES.
    + *
    + * 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 it is handled
    + * when marking the publication as ALL TABLES.
    + */
    + inval_except_table = (stmt != NULL) &&
    + (stmt->for_all_tables == pub->alltables);
    
    3a.
    Why is this code done at the top of the function? Can you move it to
    be adjacent to where it is getting used?
    
    ~
    
    3b.
    I think 'alter_stmt' or 'alter_pub_stmt' might be a more informative
    name here, instead of the generic 'stmt'
    
    ======
    src/backend/commands/publicationcmds.c
    
    4.
    - TransformPubWhereClauses(rels, queryString, pubform->pubviaroot);
    + if (isexcept)
    + oldrelids = GetExcludedPublicationTables(pubid,
    + PUBLICATION_PART_ROOT);
    + else
    + {
    + oldrelids = GetIncludedPublicationRelations(pubid,
    + PUBLICATION_PART_ROOT);
    
    I felt there were some subtle things that the logic is using here:
    
    e.g.
    It seems that because this function is called...
    And because it is SET ....
    And because it is SET ALL TABLES ...
    Then, the tables can only be those in the EXCEPT TABLE list
    
    Maybe more comments about 'isexcept', and maybe an Assert(oldrelids !=
    NIL); could help here (??)
    
    ~~~
    
    AlterPublicationAllFlags:
    
    5.
    + bool nulls[Natts_pg_publication];
    + bool replaces[Natts_pg_publication];
    + Datum values[Natts_pg_publication];
    + bool dirty = false;
    +
    + if (!stmt->for_all_tables && !stmt->for_all_sequences)
    + return;
    +
    + pubform = (Form_pg_publication) GETSTRUCT(tup);
    +
    + memset(values, 0, sizeof(values));
    + memset(nulls, false, sizeof(nulls));
    + memset(replaces, false, sizeof(replaces));
    
    AFAIK, these memsets are not needed if you just say "= {0}" where
    those vars are declared.
    
    ~~~
    
    AlterPublication:
    
    6.
    + relations = list_concat(relations, exceptrelations);
      AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext,
         schemaidlist != NIL);
    
    I did not quite understand this list_concat.
    
    Is this somehow asserting that `relations` must be empty when there
    were `exceptrelations` and vice versa, because other combinations are
    not supported by ALTER -- e.g. is this just a trick to so you can pass
    the same parameter to AlterPublicationTables? This seems related to
    the 'isexecpt' AlterPublicationTables function, which was also quite
    subtle.
    
    Bottom line is, I'm unsure what the logic is here, but it appears
    overly tricky to me. Would more comments help?
    
    
    ======
    src/backend/parser/gram.y
    
    7.
    + * ALL TABLES [ EXCEPT TABLE ( table_name [, ...] ) ]
    
    The CREATE/ALTER docs synopsis says "[ ONLY ] table_name [ * ]" which
    is different from this comment. So are ONLY and * handled properly or
    not?
    
    ======
    src/include/nodes/parsenodes.h
    
    8.
    + bool for_all_tables; /* True if SET ALL TABLES is specified */
    + bool for_all_sequences; /* True if SET ALL SEQUENCES is specified */
    
    Maybe these comments do not need to mention the keyword "SET".
    
    That way, in future if/when ADD/DROP get implemented, then this code
    won't need to churn again.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  336. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-18T08:05:12Z

    Hi Vignesh
    
    Here are some review comments for v65-0001 (test code part)
    
    ======
    src/test/regress/expected/publication.out
    
    1.
    +-- fail - SET ALL TABLES on a publication requires superuser privileges
    +ALTER PUBLICATION testpub5 SET ALL TABLES EXCEPT TABLE (testpub_tbl1); -- fail
    +ERROR:  must be superuser to alter FOR ALL TABLES publication
    +ALTER PUBLICATION testpub5 SET ALL TABLES; -- fail
    +ERROR:  must be superuser to alter FOR ALL TABLES publication
    +SET ROLE regress_publication_user;
    +DROP PUBLICATION testpub4, testpub5;
    
    The error messages don't quite seem correct. e.g. You are not trying
    "to alter FOR ALL TABLES publication". You are trying to alter a
    publication to convert it to a  "FOR ALL TABLES" publication.
    
    ======
    src/test/regress/sql/publication.sql
    
    2.
    +-- fail - SET ALL TABLES is not allowed for a 'FOR TABLE' publication
    
    I think these failure tests should be under the "-- SET ALL
    TABLES/SEQUENCES" test comment.
    
    ~~~
    
    3.
    +-- fail - SET ALL TABLES is not allowed for a schema publication
    
    I think these failure tests should be under the "-- SET ALL
    TABLES/SEQUENCES" test comment.
    
    ~~~
    
    4.
    +CREATE PUBLICATION testpub_forall_tbls_seqs;
    
    Add a comment to say this is creating an "empty" publication in
    preparation for subsequent tests
    
    ~~~
    
    5.
    +-- Remove all the EXCEPT tables.
    +ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES;
    +\dRp+ testpub_foralltables_excepttable
    +
    +-- Replace the publication EXCEPT table list with a specific EXCEPT table.
    +ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES
    EXCEPT TABLE (testpub_tbl1);
    +\dRp+ testpub_foralltables_excepttable
    
    5a.
    Are these test OK?
    * The 1st test says "Remove EXCEPT" but was there any EXCEPT list to start with?
    * The 2nd test say "Replace EXCEPT" but there was nothing replace
    because previous test removed it.
    * e.g. there seems not test that replaces one EXCEPT with a different EXCEPT
    
    ~
    
    5b.
    Shouldn't all these tests also all be under the "-- SET ALL
    TABLES/SEQUENCES" test comment?
    
    ~~~
    
    6.
    +-- fail - SET ALL TABLES on a publication requires superuser privileges
    +ALTER PUBLICATION testpub5 SET ALL TABLES EXCEPT TABLE (testpub_tbl1); -- fail
    +ALTER PUBLICATION testpub5 SET ALL TABLES; -- fail
    
    Shouldn't all these tests also all be under the "-- SET ALL
    TABLES/SEQUENCES" test comment?
    
    ======
    src/test/subscription/t/037_except.pl
    
    7.
    +# Verify that table synchronization occurs once tab1 is removed from the
    +# EXCEPT TABLE clause via SET ALL TABLES EXCEPT TABLE.
    +$result =
    +  $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM tab1");
    +is($result, qq(20),
    + 'check that the data is copied as the tab1 is removed from EXCEPT
    TABLE clause'
    +);
    
    The comment is strangely worded. Talking about removing from EXCEPT
    lists is confusing. How about?
    
    -- Verify that table synchronization now happens for tab1.
    -- tab1 is included now since the EXCEPT TABLE list is only (tab2).
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  337. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-18T08:14:21Z

    On Wed, Mar 18, 2026 at 1:35 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > ======
    > src/test/regress/expected/publication.out
    >
    > 1.
    > +-- fail - SET ALL TABLES on a publication requires superuser privileges
    > +ALTER PUBLICATION testpub5 SET ALL TABLES EXCEPT TABLE (testpub_tbl1); -- fail
    > +ERROR:  must be superuser to alter FOR ALL TABLES publication
    > +ALTER PUBLICATION testpub5 SET ALL TABLES; -- fail
    > +ERROR:  must be superuser to alter FOR ALL TABLES publication
    > +SET ROLE regress_publication_user;
    > +DROP PUBLICATION testpub4, testpub5;
    >
    > The error messages don't quite seem correct. e.g. You are not trying
    > "to alter FOR ALL TABLES publication". You are trying to alter a
    > publication to convert it to a  "FOR ALL TABLES" publication.
    >
    
    I also find this error message not conveying the proper information. I
    suggest changing it to: "must be superuser to set ALL TABLES" and
    "must be superuser to set ALL SEQUENCES" for all tables, sequences
    cases respectively.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  338. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-18T08:58:49Z

    On Wed, Mar 18, 2026 at 12:11 PM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 1.
    > It seems that SET ALL TABLES is not supported if the publication
    > already has FOR TABLE.
    >
    > e.g
    > alter publication pub1 set all tables;
    > ERROR:  publication "pub1" does not support ALL TABLES operations
    > DETAIL:  This operation requires the publication to be defined as FOR
    > ALL TABLES/SEQUENCES or to be empty.
    >
    > e.g.
    > alter publication pub2 set table t1;
    > ERROR:  publication "pub2" is defined as FOR ALL TABLES
    > DETAIL:  Tables or sequences cannot be added to or dropped from FOR
    > ALL TABLES publications.
    >
    > I am not going to debate what rules are right or wrong. (Some rules do
    > seem a bit ad-hoc to me, but maybe they are just erring on the safety
    > side). But my point is that the documentation does not seem to say
    > anything much about what the rules are
    >
    > e.g. it says "Adding/Setting any schema when the publication also
    > publishes a table with a column list, and vice versa is not
    > supported.", but OTOH it says nothing about what is
    > supported/unsupported for SET ALL TABLES.
    >
    
    Most of these rules pre-exist this patch, we can add more docs for
    these but not sure if it is worth it.
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > 2.
    > +/*
    > + * Returns true if the publication has explicitly included relation (i.e.,
    > + * not marked as EXCEPT).
    > + */
    > +bool
    > +is_table_publication(Oid pubid)
    >
    > To me, an "explicitly included relation" is like when you say "FOR
    > TABLE t1", where the "t1" is explicitly named.
    >
    > So it's not very clear whether you consider  "FOR ALL TABLES" or a
    > "FOR TABLES IN SCHEMA" publication explicitly includes tables or not?
    >
    > The function comment needs to be clearer.
    >
    
    It is clear for me that it means ("FOR TABLE t1") and follows what we
    wrote atop is_schema_publication.
    
    >
    > AlterPublication:
    >
    > 6.
    > + relations = list_concat(relations, exceptrelations);
    >   AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext,
    >      schemaidlist != NIL);
    >
    > I did not quite understand this list_concat.
    >
    > Is this somehow asserting that `relations` must be empty when there
    > were `exceptrelations` and vice versa, because other combinations are
    > not supported by ALTER -- e.g. is this just a trick to so you can pass
    > the same parameter to AlterPublicationTables? This seems related to
    > the 'isexecpt' AlterPublicationTables function, which was also quite
    > subtle.
    >
    > Bottom line is, I'm unsure what the logic is here, but it appears
    > overly tricky to me. Would more comments help?
    >
    
    The logic is quite simple here, we need to treat both types of
    included/excluded relations the same w.r.t recording them in catalog.
    The only difference is for excluded relations, except flag will be
    true. I feel adding comments for such simple stuff is not required,
    otherwise, the code will be filled with comments explaining simple
    things which one can easily make out if she understands the logic
    behind the code.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  339. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-18T13:21:02Z

    On Wed, 18 Mar 2026 at 12:11, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Vignesh.
    >
    > Some review comments for patch v65-0001.
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 1.
    > It seems that SET ALL TABLES is not supported if the publication
    > already has FOR TABLE.
    >
    > e.g
    > alter publication pub1 set all tables;
    > ERROR:  publication "pub1" does not support ALL TABLES operations
    > DETAIL:  This operation requires the publication to be defined as FOR
    > ALL TABLES/SEQUENCES or to be empty.
    >
    > e.g.
    > alter publication pub2 set table t1;
    > ERROR:  publication "pub2" is defined as FOR ALL TABLES
    > DETAIL:  Tables or sequences cannot be added to or dropped from FOR
    > ALL TABLES publications.
    >
    > I am not going to debate what rules are right or wrong. (Some rules do
    > seem a bit ad-hoc to me, but maybe they are just erring on the safety
    > side). But my point is that the documentation does not seem to say
    > anything much about what the rules are
    >
    > e.g. it says "Adding/Setting any schema when the publication also
    > publishes a table with a column list, and vice versa is not
    > supported.", but OTOH it says nothing about what is
    > supported/unsupported for SET ALL TABLES.
    
    Amit has already replied to this at [1].
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > 2.
    > +/*
    > + * Returns true if the publication has explicitly included relation (i.e.,
    > + * not marked as EXCEPT).
    > + */
    > +bool
    > +is_table_publication(Oid pubid)
    >
    > To me, an "explicitly included relation" is like when you say "FOR
    > TABLE t1", where the "t1" is explicitly named.
    >
    > So it's not very clear whether you consider  "FOR ALL TABLES" or a
    > "FOR TABLES IN SCHEMA" publication explicitly includes tables or not?
    >
    > The function comment needs to be clearer.
    
    Amit has already replied to this at [1].
    
    > ~~~
    >
    > publication_add_relation:
    >
    > 3.
    > + /*
    > + * Determine whether EXCEPT tables require explicit relcache invalidation.
    > + *
    > + * For CREATE PUBLICATION with EXCEPT tables, invalidation is not needed,
    > + * since it is handled when marking the publication as ALL TABLES.
    > + *
    > + * 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 it is handled
    > + * when marking the publication as ALL TABLES.
    > + */
    > + inval_except_table = (stmt != NULL) &&
    > + (stmt->for_all_tables == pub->alltables);
    >
    > 3a.
    > Why is this code done at the top of the function? Can you move it to
    > be adjacent to where it is getting used?
    
    Modified
    
    > ~
    >
    > 3b.
    > I think 'alter_stmt' or 'alter_pub_stmt' might be a more informative
    > name here, instead of the generic 'stmt'
    
    Modified
    
    > ======
    > src/backend/commands/publicationcmds.c
    >
    > 4.
    > - TransformPubWhereClauses(rels, queryString, pubform->pubviaroot);
    > + if (isexcept)
    > + oldrelids = GetExcludedPublicationTables(pubid,
    > + PUBLICATION_PART_ROOT);
    > + else
    > + {
    > + oldrelids = GetIncludedPublicationRelations(pubid,
    > + PUBLICATION_PART_ROOT);
    >
    > I felt there were some subtle things that the logic is using here:
    >
    > e.g.
    > It seems that because this function is called...
    > And because it is SET ....
    > And because it is SET ALL TABLES ...
    > Then, the tables can only be those in the EXCEPT TABLE list
    >
    > Maybe more comments about 'isexcept', and maybe an Assert(oldrelids !=
    > NIL); could help here (??)
    
    Updated comments
    
    > ~~~
    >
    > AlterPublicationAllFlags:
    >
    > 5.
    > + bool nulls[Natts_pg_publication];
    > + bool replaces[Natts_pg_publication];
    > + Datum values[Natts_pg_publication];
    > + bool dirty = false;
    > +
    > + if (!stmt->for_all_tables && !stmt->for_all_sequences)
    > + return;
    > +
    > + pubform = (Form_pg_publication) GETSTRUCT(tup);
    > +
    > + memset(values, 0, sizeof(values));
    > + memset(nulls, false, sizeof(nulls));
    > + memset(replaces, false, sizeof(replaces));
    >
    > AFAIK, these memsets are not needed if you just say "= {0}" where
    > those vars are declared.
    
    modified
    
    > AlterPublication:
    >
    > 6.
    > + relations = list_concat(relations, exceptrelations);
    >   AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext,
    >      schemaidlist != NIL);
    >
    > I did not quite understand this list_concat.
    >
    > Is this somehow asserting that `relations` must be empty when there
    > were `exceptrelations` and vice versa, because other combinations are
    > not supported by ALTER -- e.g. is this just a trick to so you can pass
    > the same parameter to AlterPublicationTables? This seems related to
    > the 'isexecpt' AlterPublicationTables function, which was also quite
    > subtle.
    >
    > Bottom line is, I'm unsure what the logic is here, but it appears
    > overly tricky to me. Would more comments help?
    
    Amit has already replied to this at [1].
    
    > ======
    > src/backend/parser/gram.y
    >
    > 7.
    > + * ALL TABLES [ EXCEPT TABLE ( table_name [, ...] ) ]
    >
    > The CREATE/ALTER docs synopsis says "[ ONLY ] table_name [ * ]" which
    > is different from this comment. So are ONLY and * handled properly or
    > not?
    
    It is handled, Is there any issue with the code? It is documented that
    way to keep it consistent with CreatePublicationStmt and ALTER
    PUBLICATION ADD/DROP/SET pub_obj.
    
    > ======
    > src/include/nodes/parsenodes.h
    >
    > 8.
    > + bool for_all_tables; /* True if SET ALL TABLES is specified */
    > + bool for_all_sequences; /* True if SET ALL SEQUENCES is specified */
    >
    > Maybe these comments do not need to mention the keyword "SET".
    >
    > That way, in future if/when ADD/DROP get implemented, then this code
    > won't need to churn again.
    
    Modified
    
    The attached v66 version patch has the changes for the same.
    
    The comments from [2] have also been addressed in the attached patch.
    [1] - https://www.postgresql.org/message-id/CAA4eK1LCh7iCy2raNRrfVaS0LJaHxYokCUaNVue-qLfR%3DiG1PA%40mail.gmail.com
    [2] - https://www.postgresql.org/message-id/CAJpy0uCMnF25JNgx1vnQMV3FQoiiqdOJYwnCqYcOZ-g6B8%2BfWA%40mail.gmail.com
    
    Regards,
    Vignesh
    
  340. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-18T13:23:53Z

    On Wed, 18 Mar 2026 at 09:57, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Wed, Mar 18, 2026 at 3:10 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Tue, Mar 17, 2026 at 9:21 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > >
    > > > These comments are addressed in the v65 version patch attached. Also
    > > > the comments from [1] have been addressed in this.
    > > > [1] - https://www.postgresql.org/message-id/CAA4eK1%2BmSpCzj%2BB2PW_68DJpXHA0KMgT9Nrz9P83_c1vdKya8g%40mail.gmail.com
    > > >
    > >
    > > Thanks, few trivial things:
    > >
    > > 1)
    > > In the doc we talk about the first two variants and then the third
    > > variant. IIUC, you need to change variants to match the new details,
    > > i.e.
    > >
    > > Change these:
    > > ALTER PUBLICATION name ADD publication_object [, ...]
    > > ALTER PUBLICATION name SET { publication_object [, ...] |
    > > publication_all_object [, ... ] }
    > > ALTER PUBLICATION name DROP publication_drop_object [, ...]
    > >
    > > to:
    > > ALTER PUBLICATION name ADD publication_object [, ...]
    > > ALTER PUBLICATION name DROP publication_drop_object [, ...]
    > > ALTER PUBLICATION name SET { publication_object [, ...] |
    > > publication_all_object [, ... ] }
    > >
    > > or change the details to match variants.
    > >
    >
    > +1
    >
    > (I was about to post the same comment)
    >
    > Talking about "variants" like this forces the reader to keep referring
    > back to the synopsis to try to figure out what is actually being
    > described.
    >
    > A simple improvement like below could help clarify things:
    > e.g. say "The third variant (DROP)" instead of just saying "The third variant"
    > e.g. say "The fourth variant (SET parameters)" instead of just saying
    > "The fourth variant"
    > etc.
    >
    > Aside from that, AFAICT, the patch is a bit muddled about some variant numbers.
    >
    > e.g.1  "The first two variants modify which tables/schemas are part of
    > the publication. The ADD and DROP clauses will add and remove one or
    > more tables/schemas from the publication."
    >
    > No, because DROP is the 3rd variant, not in the "first two",
    >
    > e.g.2.  "The third variant either modifies the included tables/schemas
    > or marks the publication..."
    >
    > No, the 3rd variant now is DROP. But this whole paragraph is referring
    > to SET objects.
    
    The order issue has been fixed in the v66 version patch attached at
    [1]. I felt the variant related detailing improvement can be handled
    separately if required after the patch is completed.
    [1] - https://www.postgresql.org/message-id/CALDaNm1tKuU479T%3DwinBqoMb3MzO3Mta2juk8W3t2R5ps0_zyg%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  341. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-18T13:26:32Z

    On Wed, 18 Mar 2026 at 13:35, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Vignesh
    >
    > Here are some review comments for v65-0001 (test code part)
    >
    > ======
    > src/test/regress/expected/publication.out
    >
    > 1.
    > +-- fail - SET ALL TABLES on a publication requires superuser privileges
    > +ALTER PUBLICATION testpub5 SET ALL TABLES EXCEPT TABLE (testpub_tbl1); -- fail
    > +ERROR:  must be superuser to alter FOR ALL TABLES publication
    > +ALTER PUBLICATION testpub5 SET ALL TABLES; -- fail
    > +ERROR:  must be superuser to alter FOR ALL TABLES publication
    > +SET ROLE regress_publication_user;
    > +DROP PUBLICATION testpub4, testpub5;
    >
    > The error messages don't quite seem correct. e.g. You are not trying
    > "to alter FOR ALL TABLES publication". You are trying to alter a
    > publication to convert it to a  "FOR ALL TABLES" publication.
    
    Modified the error message
    
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 2.
    > +-- fail - SET ALL TABLES is not allowed for a 'FOR TABLE' publication
    >
    > I think these failure tests should be under the "-- SET ALL
    > TABLES/SEQUENCES" test comment.
    
    Modified
    
    > 3.
    > +-- fail - SET ALL TABLES is not allowed for a schema publication
    >
    > I think these failure tests should be under the "-- SET ALL
    > TABLES/SEQUENCES" test comment.
    
    Modified
    
    > 4.
    > +CREATE PUBLICATION testpub_forall_tbls_seqs;
    >
    > Add a comment to say this is creating an "empty" publication in
    > preparation for subsequent tests
    
    Modified
    
    > 5.
    > +-- Remove all the EXCEPT tables.
    > +ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES;
    > +\dRp+ testpub_foralltables_excepttable
    > +
    > +-- Replace the publication EXCEPT table list with a specific EXCEPT table.
    > +ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES
    > EXCEPT TABLE (testpub_tbl1);
    > +\dRp+ testpub_foralltables_excepttable
    >
    > 5a.
    > Are these test OK?
    > * The 1st test says "Remove EXCEPT" but was there any EXCEPT list to start with?
    > * The 2nd test say "Replace EXCEPT" but there was nothing replace
    > because previous test removed it.
    > * e.g. there seems not test that replaces one EXCEPT with a different EXCEPT
    
    There is an except table testpub_tbl1 already present
    
    > 5b.
    > Shouldn't all these tests also all be under the "-- SET ALL
    > TABLES/SEQUENCES" test comment?
    
    Modified
    
    > 6.
    > +-- fail - SET ALL TABLES on a publication requires superuser privileges
    > +ALTER PUBLICATION testpub5 SET ALL TABLES EXCEPT TABLE (testpub_tbl1); -- fail
    > +ALTER PUBLICATION testpub5 SET ALL TABLES; -- fail
    >
    > Shouldn't all these tests also all be under the "-- SET ALL
    > TABLES/SEQUENCES" test comment?
    
    I felt this comes under permission category, exisiting is ok.
    
    > ======
    > src/test/subscription/t/037_except.pl
    >
    > 7.
    > +# Verify that table synchronization occurs once tab1 is removed from the
    > +# EXCEPT TABLE clause via SET ALL TABLES EXCEPT TABLE.
    > +$result =
    > +  $node_subscriber->safe_psql('postgres', "SELECT count(*) FROM tab1");
    > +is($result, qq(20),
    > + 'check that the data is copied as the tab1 is removed from EXCEPT
    > TABLE clause'
    > +);
    >
    > The comment is strangely worded. Talking about removing from EXCEPT
    > lists is confusing. How about?
    >
    > -- Verify that table synchronization now happens for tab1.
    > -- tab1 is included now since the EXCEPT TABLE list is only (tab2).
    
    Modified
    
    These comments have been fixed in the v66 version at [1].
    [1] - https://www.postgresql.org/message-id/CALDaNm1tKuU479T%3DwinBqoMb3MzO3Mta2juk8W3t2R5ps0_zyg%40mail.gmail.com
    
    Regards,
    Vignesh
    
    
    
    
  342. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-20T02:21:34Z

    On Wed, 18 Mar 2026 at 18:51, vignesh C <vignesh21@gmail.com> wrote:
    >
    > On Wed, 18 Mar 2026 at 12:11, Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > Hi Vignesh.
    > >
    > > Some review comments for patch v65-0001.
    > >
    > > ======
    > > doc/src/sgml/ref/alter_publication.sgml
    > >
    > > 1.
    > > It seems that SET ALL TABLES is not supported if the publication
    > > already has FOR TABLE.
    > >
    > > e.g
    > > alter publication pub1 set all tables;
    > > ERROR:  publication "pub1" does not support ALL TABLES operations
    > > DETAIL:  This operation requires the publication to be defined as FOR
    > > ALL TABLES/SEQUENCES or to be empty.
    > >
    > > e.g.
    > > alter publication pub2 set table t1;
    > > ERROR:  publication "pub2" is defined as FOR ALL TABLES
    > > DETAIL:  Tables or sequences cannot be added to or dropped from FOR
    > > ALL TABLES publications.
    > >
    > > I am not going to debate what rules are right or wrong. (Some rules do
    > > seem a bit ad-hoc to me, but maybe they are just erring on the safety
    > > side). But my point is that the documentation does not seem to say
    > > anything much about what the rules are
    > >
    > > e.g. it says "Adding/Setting any schema when the publication also
    > > publishes a table with a column list, and vice versa is not
    > > supported.", but OTOH it says nothing about what is
    > > supported/unsupported for SET ALL TABLES.
    >
    > Amit has already replied to this at [1].
    >
    > > ======
    > > src/backend/catalog/pg_publication.c
    > >
    > > 2.
    > > +/*
    > > + * Returns true if the publication has explicitly included relation (i.e.,
    > > + * not marked as EXCEPT).
    > > + */
    > > +bool
    > > +is_table_publication(Oid pubid)
    > >
    > > To me, an "explicitly included relation" is like when you say "FOR
    > > TABLE t1", where the "t1" is explicitly named.
    > >
    > > So it's not very clear whether you consider  "FOR ALL TABLES" or a
    > > "FOR TABLES IN SCHEMA" publication explicitly includes tables or not?
    > >
    > > The function comment needs to be clearer.
    >
    > Amit has already replied to this at [1].
    >
    > > ~~~
    > >
    > > publication_add_relation:
    > >
    > > 3.
    > > + /*
    > > + * Determine whether EXCEPT tables require explicit relcache invalidation.
    > > + *
    > > + * For CREATE PUBLICATION with EXCEPT tables, invalidation is not needed,
    > > + * since it is handled when marking the publication as ALL TABLES.
    > > + *
    > > + * 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 it is handled
    > > + * when marking the publication as ALL TABLES.
    > > + */
    > > + inval_except_table = (stmt != NULL) &&
    > > + (stmt->for_all_tables == pub->alltables);
    > >
    > > 3a.
    > > Why is this code done at the top of the function? Can you move it to
    > > be adjacent to where it is getting used?
    >
    > Modified
    >
    > > ~
    > >
    > > 3b.
    > > I think 'alter_stmt' or 'alter_pub_stmt' might be a more informative
    > > name here, instead of the generic 'stmt'
    >
    > Modified
    >
    > > ======
    > > src/backend/commands/publicationcmds.c
    > >
    > > 4.
    > > - TransformPubWhereClauses(rels, queryString, pubform->pubviaroot);
    > > + if (isexcept)
    > > + oldrelids = GetExcludedPublicationTables(pubid,
    > > + PUBLICATION_PART_ROOT);
    > > + else
    > > + {
    > > + oldrelids = GetIncludedPublicationRelations(pubid,
    > > + PUBLICATION_PART_ROOT);
    > >
    > > I felt there were some subtle things that the logic is using here:
    > >
    > > e.g.
    > > It seems that because this function is called...
    > > And because it is SET ....
    > > And because it is SET ALL TABLES ...
    > > Then, the tables can only be those in the EXCEPT TABLE list
    > >
    > > Maybe more comments about 'isexcept', and maybe an Assert(oldrelids !=
    > > NIL); could help here (??)
    >
    > Updated comments
    >
    > > ~~~
    > >
    > > AlterPublicationAllFlags:
    > >
    > > 5.
    > > + bool nulls[Natts_pg_publication];
    > > + bool replaces[Natts_pg_publication];
    > > + Datum values[Natts_pg_publication];
    > > + bool dirty = false;
    > > +
    > > + if (!stmt->for_all_tables && !stmt->for_all_sequences)
    > > + return;
    > > +
    > > + pubform = (Form_pg_publication) GETSTRUCT(tup);
    > > +
    > > + memset(values, 0, sizeof(values));
    > > + memset(nulls, false, sizeof(nulls));
    > > + memset(replaces, false, sizeof(replaces));
    > >
    > > AFAIK, these memsets are not needed if you just say "= {0}" where
    > > those vars are declared.
    >
    > modified
    >
    > > AlterPublication:
    > >
    > > 6.
    > > + relations = list_concat(relations, exceptrelations);
    > >   AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext,
    > >      schemaidlist != NIL);
    > >
    > > I did not quite understand this list_concat.
    > >
    > > Is this somehow asserting that `relations` must be empty when there
    > > were `exceptrelations` and vice versa, because other combinations are
    > > not supported by ALTER -- e.g. is this just a trick to so you can pass
    > > the same parameter to AlterPublicationTables? This seems related to
    > > the 'isexecpt' AlterPublicationTables function, which was also quite
    > > subtle.
    > >
    > > Bottom line is, I'm unsure what the logic is here, but it appears
    > > overly tricky to me. Would more comments help?
    >
    > Amit has already replied to this at [1].
    >
    > > ======
    > > src/backend/parser/gram.y
    > >
    > > 7.
    > > + * ALL TABLES [ EXCEPT TABLE ( table_name [, ...] ) ]
    > >
    > > The CREATE/ALTER docs synopsis says "[ ONLY ] table_name [ * ]" which
    > > is different from this comment. So are ONLY and * handled properly or
    > > not?
    >
    > It is handled, Is there any issue with the code? It is documented that
    > way to keep it consistent with CreatePublicationStmt and ALTER
    > PUBLICATION ADD/DROP/SET pub_obj.
    >
    > > ======
    > > src/include/nodes/parsenodes.h
    > >
    > > 8.
    > > + bool for_all_tables; /* True if SET ALL TABLES is specified */
    > > + bool for_all_sequences; /* True if SET ALL SEQUENCES is specified */
    > >
    > > Maybe these comments do not need to mention the keyword "SET".
    > >
    > > That way, in future if/when ADD/DROP get implemented, then this code
    > > won't need to churn again.
    >
    > Modified
    >
    > The attached v66 version patch has the changes for the same.
    
    While reviewing the patch I noticed couple of small improvements that
    could be done:
    1) changed "may be used " to "can be used" as that is the convention followed:
    In addition,
    +   <literal>SET ALL TABLES</literal> may be used to update the
    +   <literal>EXCEPT TABLE</literal> list of a <literal>FOR ALL TABLES</literal>
    +   publication.
    
    2) This condition was slightly difficult to understand while reading,
    simplified it:
    Changed:
    - if (!pri->except)
    + inval_except_table = (alter_stmt != NULL) &&
    + (alter_stmt->for_all_tables == pub->alltables);
    to:
    - if (!pri->except)
    + inval_except_table = (alter_stmt != NULL) && pub->alltables &&
    + (alter_stmt->for_all_tables && pri->except);
    
    The attached v67 patch has the changes for the same.
    
    Regards,
    Vignesh
    
  343. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-20T05:55:56Z

    On Fri, Mar 20, 2026 at 7:51 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    >
    > The attached v67 patch has the changes for the same.
    >
    
    It looks good to me.
    
    thanks
    Shveta
    
    
    
    
  344. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-20T11:56:02Z

    On Fri, Mar 20, 2026 at 11:26 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Fri, Mar 20, 2026 at 7:51 AM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > >
    > > The attached v67 patch has the changes for the same.
    > >
    >
    > It looks good to me.
    >
    
    Pushed.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  345. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-23T07:05:02Z

    I would like to summarize the discussion/feedback for the EXCEPT
    syntax implemented in [1].
    
    1)
    The currently implemented syntax is ([1]):
    
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);
    
    There were concerns about why the TABLE keyword and the parentheses
    '()' are required. These have been answered in [2].
    
    Please review the discussion there.
    
    2)
    Another feedback on current syntax was to move the TABLE keyword
    inside the parentheses:
    
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, TABLE t2,
    TABLES IN SCHEMA s1);
    CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, TABLE
    t2), TABLE t3;
    
    While this approach is workable, a downside is the repeated use of the
    TABLE keyword inside the parentheses, which can become verbose. But it
    can then be optimized to have:
    
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, t3);
    
    This could be extended further in the future:
    
    CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    SCHEMA s1, s2);
    
    This approach gives users flexibility to mix styles, for example:
    
    EXCEPT (TABLE t1, TABLE t2, TABLE t3)
    EXCEPT (TABLE t1, t2, t3)
    EXCEPT (TABLE t1, t2, TABLE t3)
    EXCEPT (TABLE t1, TABLES IN SCHEMA s1, s2, TABLE t2, t3)
    
    While flexible, this can reduce clarity due to mixed styles, making
    the statement harder to read. If extended further, the syntax could
    evolve into something like:
    
    CREATE PUBLICATION pub1 FOR
    ALL TABLES
       EXCEPT (TABLE t1, t2, TABLES IN SCHEMA s1, s2),
    ALL SEQUENCES
       EXCEPT (SEQUENCE s1);
    
    At this point, one might also question why not allow something like:
    FOR ALL (TABLES, SEQUENCES).
    
    Additionally, this shows a potential drift toward less structured
    syntax. Instead, with the syntax already implemented in [1], its
    future extension would look like:
    
    CREATE PUBLICATION pub1 FOR
    ALL TABLES
       EXCEPT TABLE (t1, t2),
       EXCEPT TABLES IN SCHEMA (s1, s2),
    ALL SEQUENCES
       EXCEPT SEQUENCE (seq1, seq2);
    
    Although slightly more verbose, this approach keeps each clause
    self-contained and explicit. The meaning of each part is determined
    locally, rather than depending on elements appearing far in the
    statement.
    
    The current syntax in [1] is simple and easy to follow. We have
    retained the current implementation for now, while remaining open to
    further discussion and suggestions.
    
    [1]:
    https://www.postgresql.org/message-id/CALDaNm2-Ob9qPR%2BvqUSVMkxYO8RW4LQ_S1XiB0Y7xa54U%3DDqbA%40mail.gmail.com
    https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=fd366065e06ae953c4f2d973d5c5f0474f3b87b6
    
    [2]: https://www.postgresql.org/message-id/CAJpy0uB20MhJJEaPJdm31t4fykJ%2BfChA_76jU2P9HX5knbJvAA%40mail.gmail.com
    
    thanks
    Shveta
    
    
    
    
  346. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-03-24T14:20:18Z

    On Mon, Mar 23, 2026 at 12:35 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > I would like to summarize the discussion/feedback for the EXCEPT
    > syntax implemented in [1].
    >
    > 1)
    > The currently implemented syntax is ([1]):
    >
    > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);
    >
    > There were concerns about why the TABLE keyword and the parentheses
    > '()' are required. These have been answered in [2].
    >
    > Please review the discussion there.
    >
    > 2)
    > Another feedback on current syntax was to move the TABLE keyword
    > inside the parentheses:
    >
    > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, TABLE t2,
    > TABLES IN SCHEMA s1);
    > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, TABLE
    > t2), TABLE t3;
    >
    > While this approach is workable, a downside is the repeated use of the
    > TABLE keyword inside the parentheses, which can become verbose. But it
    > can then be optimized to have:
    >
    > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, t3);
    >
    > This could be extended further in the future:
    >
    > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > SCHEMA s1, s2);
    >
    > This approach gives users flexibility to mix styles, for example:
    >
    > EXCEPT (TABLE t1, TABLE t2, TABLE t3)
    > EXCEPT (TABLE t1, t2, t3)
    > EXCEPT (TABLE t1, t2, TABLE t3)
    > EXCEPT (TABLE t1, TABLES IN SCHEMA s1, s2, TABLE t2, t3)
    >
    > While flexible, this can reduce clarity due to mixed styles, making
    > the statement harder to read. If extended further, the syntax could
    > evolve into something like:
    >
    > CREATE PUBLICATION pub1 FOR
    > ALL TABLES
    >    EXCEPT (TABLE t1, t2, TABLES IN SCHEMA s1, s2),
    > ALL SEQUENCES
    >    EXCEPT (SEQUENCE s1);
    >
    > At this point, one might also question why not allow something like:
    > FOR ALL (TABLES, SEQUENCES).
    >
    > Additionally, this shows a potential drift toward less structured
    > syntax. Instead, with the syntax already implemented in [1], its
    > future extension would look like:
    >
    > CREATE PUBLICATION pub1 FOR
    > ALL TABLES
    >    EXCEPT TABLE (t1, t2),
    >    EXCEPT TABLES IN SCHEMA (s1, s2),
    > ALL SEQUENCES
    >    EXCEPT SEQUENCE (seq1, seq2);
    >
    > Although slightly more verbose, this approach keeps each clause
    > self-contained and explicit. The meaning of each part is determined
    > locally, rather than depending on elements appearing far in the
    > statement.
    >
    > The current syntax in [1] is simple and easy to follow. We have
    > retained the current implementation for now, while remaining open to
    > further discussion and suggestions.
    >
    > [1]:
    > https://www.postgresql.org/message-id/CALDaNm2-Ob9qPR%2BvqUSVMkxYO8RW4LQ_S1XiB0Y7xa54U%3DDqbA%40mail.gmail.com
    > https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=fd366065e06ae953c4f2d973d5c5f0474f3b87b6
    >
    > [2]: https://www.postgresql.org/message-id/CAJpy0uB20MhJJEaPJdm31t4fykJ%2BfChA_76jU2P9HX5knbJvAA%40mail.gmail.com
    
    
    Thanks for the summary. While I find the current implementation
    simpler and more intuitive, and would prefer it if we were designing
    this from scratch, we must consider the existing patterns for table
    inclusion. Since the inclusion syntax already supports a mixed
    approach, users will likely expect the same flexibility for
    exclusions. For the sake of consistency across the features, I believe
    we should move toward the mixed approach, despite my preference for
    the current structured style.  Of course, this is just my opinion, and
    others' mileage may vary.
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  347. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-26T06:10:58Z

    On Tue, Mar 24, 2026 at 7:50 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
    >
    > On Mon, Mar 23, 2026 at 12:35 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > I would like to summarize the discussion/feedback for the EXCEPT
    > > syntax implemented in [1].
    > >
    > > 1)
    > > The currently implemented syntax is ([1]):
    > >
    > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);
    > >
    > > There were concerns about why the TABLE keyword and the parentheses
    > > '()' are required. These have been answered in [2].
    > >
    > > Please review the discussion there.
    > >
    > > 2)
    > > Another feedback on current syntax was to move the TABLE keyword
    > > inside the parentheses:
    > >
    > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, TABLE t2,
    > > TABLES IN SCHEMA s1);
    > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, TABLE
    > > t2), TABLE t3;
    > >
    > > While this approach is workable, a downside is the repeated use of the
    > > TABLE keyword inside the parentheses, which can become verbose. But it
    > > can then be optimized to have:
    > >
    > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, t3);
    > >
    > > This could be extended further in the future:
    > >
    > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > > SCHEMA s1, s2);
    > >
    > > This approach gives users flexibility to mix styles, for example:
    > >
    > > EXCEPT (TABLE t1, TABLE t2, TABLE t3)
    > > EXCEPT (TABLE t1, t2, t3)
    > > EXCEPT (TABLE t1, t2, TABLE t3)
    > > EXCEPT (TABLE t1, TABLES IN SCHEMA s1, s2, TABLE t2, t3)
    > >
    > > While flexible, this can reduce clarity due to mixed styles, making
    > > the statement harder to read. If extended further, the syntax could
    > > evolve into something like:
    > >
    > > CREATE PUBLICATION pub1 FOR
    > > ALL TABLES
    > >    EXCEPT (TABLE t1, t2, TABLES IN SCHEMA s1, s2),
    > > ALL SEQUENCES
    > >    EXCEPT (SEQUENCE s1);
    > >
    > > At this point, one might also question why not allow something like:
    > > FOR ALL (TABLES, SEQUENCES).
    > >
    > > Additionally, this shows a potential drift toward less structured
    > > syntax. Instead, with the syntax already implemented in [1], its
    > > future extension would look like:
    > >
    > > CREATE PUBLICATION pub1 FOR
    > > ALL TABLES
    > >    EXCEPT TABLE (t1, t2),
    > >    EXCEPT TABLES IN SCHEMA (s1, s2),
    > > ALL SEQUENCES
    > >    EXCEPT SEQUENCE (seq1, seq2);
    > >
    > > Although slightly more verbose, this approach keeps each clause
    > > self-contained and explicit. The meaning of each part is determined
    > > locally, rather than depending on elements appearing far in the
    > > statement.
    > >
    > > The current syntax in [1] is simple and easy to follow. We have
    > > retained the current implementation for now, while remaining open to
    > > further discussion and suggestions.
    > >
    > > [1]:
    > > https://www.postgresql.org/message-id/CALDaNm2-Ob9qPR%2BvqUSVMkxYO8RW4LQ_S1XiB0Y7xa54U%3DDqbA%40mail.gmail.com
    > > https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=fd366065e06ae953c4f2d973d5c5f0474f3b87b6
    > >
    > > [2]: https://www.postgresql.org/message-id/CAJpy0uB20MhJJEaPJdm31t4fykJ%2BfChA_76jU2P9HX5knbJvAA%40mail.gmail.com
    >
    >
    > Thanks for the summary. While I find the current implementation
    > simpler and more intuitive, and would prefer it if we were designing
    > this from scratch, we must consider the existing patterns for table
    > inclusion. Since the inclusion syntax already supports a mixed
    > approach, users will likely expect the same flexibility for
    > exclusions. For the sake of consistency across the features, I believe
    > we should move toward the mixed approach, despite my preference for
    > the current structured style.
    >
    
    IIUC, you think that we should change the current syntax "CREATE
    PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);" to "CREATE
    PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE a, b, c);".
    
    By now multiple people (Dilip Kumar, Peter Smith, Sawada Masahiko)
    have preferred the alternate syntax, to move TABLE inside () to make
    specifying inclusion and exclusion list in a similar way. Unless we
    have more feedback, I think we can change it now.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  348. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-26T15:24:37Z

    On Thu, 26 Mar 2026 at 11:41, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Tue, Mar 24, 2026 at 7:50 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
    > >
    > > On Mon, Mar 23, 2026 at 12:35 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > I would like to summarize the discussion/feedback for the EXCEPT
    > > > syntax implemented in [1].
    > > >
    > > > 1)
    > > > The currently implemented syntax is ([1]):
    > > >
    > > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);
    > > >
    > > > There were concerns about why the TABLE keyword and the parentheses
    > > > '()' are required. These have been answered in [2].
    > > >
    > > > Please review the discussion there.
    > > >
    > > > 2)
    > > > Another feedback on current syntax was to move the TABLE keyword
    > > > inside the parentheses:
    > > >
    > > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, TABLE t2,
    > > > TABLES IN SCHEMA s1);
    > > > CREATE PUBLICATION pub FOR TABLES IN SCHEMA s1 EXCEPT (TABLE t1, TABLE
    > > > t2), TABLE t3;
    > > >
    > > > While this approach is workable, a downside is the repeated use of the
    > > > TABLE keyword inside the parentheses, which can become verbose. But it
    > > > can then be optimized to have:
    > > >
    > > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, t3);
    > > >
    > > > This could be extended further in the future:
    > > >
    > > > CREATE PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE t1, t2, TABLES IN
    > > > SCHEMA s1, s2);
    > > >
    > > > This approach gives users flexibility to mix styles, for example:
    > > >
    > > > EXCEPT (TABLE t1, TABLE t2, TABLE t3)
    > > > EXCEPT (TABLE t1, t2, t3)
    > > > EXCEPT (TABLE t1, t2, TABLE t3)
    > > > EXCEPT (TABLE t1, TABLES IN SCHEMA s1, s2, TABLE t2, t3)
    > > >
    > > > While flexible, this can reduce clarity due to mixed styles, making
    > > > the statement harder to read. If extended further, the syntax could
    > > > evolve into something like:
    > > >
    > > > CREATE PUBLICATION pub1 FOR
    > > > ALL TABLES
    > > >    EXCEPT (TABLE t1, t2, TABLES IN SCHEMA s1, s2),
    > > > ALL SEQUENCES
    > > >    EXCEPT (SEQUENCE s1);
    > > >
    > > > At this point, one might also question why not allow something like:
    > > > FOR ALL (TABLES, SEQUENCES).
    > > >
    > > > Additionally, this shows a potential drift toward less structured
    > > > syntax. Instead, with the syntax already implemented in [1], its
    > > > future extension would look like:
    > > >
    > > > CREATE PUBLICATION pub1 FOR
    > > > ALL TABLES
    > > >    EXCEPT TABLE (t1, t2),
    > > >    EXCEPT TABLES IN SCHEMA (s1, s2),
    > > > ALL SEQUENCES
    > > >    EXCEPT SEQUENCE (seq1, seq2);
    > > >
    > > > Although slightly more verbose, this approach keeps each clause
    > > > self-contained and explicit. The meaning of each part is determined
    > > > locally, rather than depending on elements appearing far in the
    > > > statement.
    > > >
    > > > The current syntax in [1] is simple and easy to follow. We have
    > > > retained the current implementation for now, while remaining open to
    > > > further discussion and suggestions.
    > > >
    > > > [1]:
    > > > https://www.postgresql.org/message-id/CALDaNm2-Ob9qPR%2BvqUSVMkxYO8RW4LQ_S1XiB0Y7xa54U%3DDqbA%40mail.gmail.com
    > > > https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=fd366065e06ae953c4f2d973d5c5f0474f3b87b6
    > > >
    > > > [2]: https://www.postgresql.org/message-id/CAJpy0uB20MhJJEaPJdm31t4fykJ%2BfChA_76jU2P9HX5knbJvAA%40mail.gmail.com
    > >
    > >
    > > Thanks for the summary. While I find the current implementation
    > > simpler and more intuitive, and would prefer it if we were designing
    > > this from scratch, we must consider the existing patterns for table
    > > inclusion. Since the inclusion syntax already supports a mixed
    > > approach, users will likely expect the same flexibility for
    > > exclusions. For the sake of consistency across the features, I believe
    > > we should move toward the mixed approach, despite my preference for
    > > the current structured style.
    > >
    >
    > IIUC, you think that we should change the current syntax "CREATE
    > PUBLICATION pub FOR ALL TABLES EXCEPT TABLE (a, b, c);" to "CREATE
    > PUBLICATION pub FOR ALL TABLES EXCEPT (TABLE a, b, c);".
    >
    > By now multiple people (Dilip Kumar, Peter Smith, Sawada Masahiko)
    > have preferred the alternate syntax, to move TABLE inside () to make
    > specifying inclusion and exclusion list in a similar way. Unless we
    > have more feedback, I think we can change it now.
    
    Attached patch has the changes for the same.
    
    Regards,
    Vignesh
    
  349. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-27T01:36:14Z

    On Fri, Mar 27, 2026 at 2:24 AM vignesh C <vignesh21@gmail.com> wrote:
    >
    ...
    > > By now multiple people (Dilip Kumar, Peter Smith, Sawada Masahiko)
    > > have preferred the alternate syntax, to move TABLE inside () to make
    > > specifying inclusion and exclusion list in a similar way. Unless we
    > > have more feedback, I think we can change it now.
    >
    > Attached patch has the changes for the same.
    >
    
    Hi Vignesh.
    
    Here are some review comments for the new syntax patch 0001.
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    1.
    -    ALL TABLES [ EXCEPT TABLE ( <replaceable
    class="parameter">except_table_object</replaceable> [, ... ] ) ]
    +    ALL TABLES [ EXCEPT ( TABLE  <replaceable
    class="parameter">except_table_object</replaceable> [, ... ] ) ]
    
    I don't think this is correct. To have the same flexibility as the
    TABLE inclusion lists, the TABLE keyword needs to be inside the
    <except_table_object>, and there need to be more ellipses there too.
    
    e.g. Like this:
    
    and publication_all_object is one of:
        ALL TABLES [ EXCEPT ( except_table_object [, ... ] ) ]
        ALL SEQUENCES
    
    and except_table_object is:
        TABLE { [ ONLY ] table_name [ * ] } [, ...]
    
    ~~~
    
    2.
    The CREATE PUBLICATION page still refers to "EXCEPT TABLE" in multiple places.
    
    Perhaps now it should be called "EXCEPT (TABLE) clause" or just
    "EXCEPT clause" or something else.
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    3.
    -    ALL TABLES [ EXCEPT TABLE ( <replaceable
    class="parameter">except_table_object</replaceable> [, ... ] ) ]
    +    ALL TABLES [ EXCEPT ( TABLE  <replaceable
    class="parameter">except_table_object</replaceable> [, ... ] ) ]
    
    This ALTER PUBLICATION synopsis should have the same/similar changes
    as in the above review comment above for the CREATE PUBLICATION
    synopsis.
    
    e.g.
    
    and publication_all_object is one of:
        ALL TABLES [ EXCEPT ( except_table_object [, ... ] ) ]
        ALL SEQUENCES
    
    and except_table_object is:
        TABLE { [ ONLY ] table_name [ * ] } [, ...]
    
    ~~~
    
    4.
    Also, similar to the earlier review comment, this ALTER PUBLICATION
    page still refers to "EXCEPT TABLE" in multiple places.
    
    Perhaps now it should be called "EXCEPT (TABLE) clause" or just "EXCEPT clause".
    
    ======
    src/backend/parser/gram.y
    
    5.
    - EXCEPT TABLE '(' pub_except_obj_list ')' { $$ = $4; }
    + EXCEPT '(' TABLE pub_except_obj_list ')' { $$ = $4; }
    
    Same review comment as my above synopsis comments.
    
    This patch implementation only supports EXCEPT (TABLE t1,t2,t3);
    
    Specifically, it doesn't have the same flexibility you get from a
    table inclusion list.
    
    e.g. IMO cmd should also work for:
    EXCEPT (TABLE t1, TABLE t2,t3);
    etc.
    
    ======
    src/bin/pg_dump/pg_dump.c
    
    6.
    Code comment still refers to "EXCEPT TABLE clause", but it doesn't
    look like that anymore.
    
    ======
    src/test/regress/sql/publication.sql
    
    7.
    Comments still refer to "EXCEPT TABLE" and "EXCEPT TABLE clause", but
    it doesn't look like that anymore.
    
    ~~~
    
    8.
    There needs to be additional tests to confirm that the syntax has the
    same flexibility as TABLE inclusion lists do
    
    e.g. all these variations below are valid and equivalent:
    ... EXCEPT (TABLE t1, t2, t3, t4);
    ... EXCEPT (TABLE t1, t2, TABLE t3, t4);
    ... EXCEPT (TABLE t1, t2, TABLE t3, t4);
    ... EXCEPT (TABLE t1, TABLE t2, TABLE t3, TABLE t4);
    
    ======
    src/test/subscription/t/037_except.pl
    
    9.
    Comments still refer to "EXCEPT TABLE", but it doesn't look like that anymore.
    
    ======
    (more files)
    
    10.
    There are still more source files (not currently part of this patch)
    that are still calling this the "EXCEPT TABLE" clause, even though the
    syntax is different now.
    
    e.g. "src/backend/commands/publicationcmds.c" and others.
    
    Need to search the master source to find every affected comment.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  350. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-27T03:49:38Z

    On Fri, Mar 27, 2026 at 7:06 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > 8.
    > There needs to be additional tests to confirm that the syntax has the
    > same flexibility as TABLE inclusion lists do
    >
    > e.g. all these variations below are valid and equivalent:
    > ... EXCEPT (TABLE t1, t2, t3, t4);
    > ... EXCEPT (TABLE t1, t2, TABLE t3, t4);
    > ... EXCEPT (TABLE t1, t2, TABLE t3, t4);
    > ... EXCEPT (TABLE t1, TABLE t2, TABLE t3, TABLE t4);
    >
    
    From all of the above, test with any one variant is okay. Let's not
    add tests unless they test some different variant of syntax.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  351. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-27T05:49:36Z

    On Thu, Mar 26, 2026 at 8:54 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    >
    > Attached patch has the changes for the same.
    >
    
    I reviewed the EXCEPT patch and don’t have additional comments beyond
    what Peter has already mentioned. The feedback mainly falls into two
    categories:
    
    1) Update the doc and comments to reference the correct clause instead
    of “EXCEPT TABLE.”
    2) Add more flexibility in the syntax to allow optional TABLE keywords
    with each table name, enabling mixed styles in the future.
    
    thanks
    Shveta
    
    
    
    
  352. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-03-27T07:50:14Z

    On Fri, 27 Mar 2026 at 07:06, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Fri, Mar 27, 2026 at 2:24 AM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > ...
    > > > By now multiple people (Dilip Kumar, Peter Smith, Sawada Masahiko)
    > > > have preferred the alternate syntax, to move TABLE inside () to make
    > > > specifying inclusion and exclusion list in a similar way. Unless we
    > > > have more feedback, I think we can change it now.
    > >
    > > Attached patch has the changes for the same.
    > >
    >
    > Hi Vignesh.
    >
    > Here are some review comments for the new syntax patch 0001.
    >
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 1.
    > -    ALL TABLES [ EXCEPT TABLE ( <replaceable
    > class="parameter">except_table_object</replaceable> [, ... ] ) ]
    > +    ALL TABLES [ EXCEPT ( TABLE  <replaceable
    > class="parameter">except_table_object</replaceable> [, ... ] ) ]
    >
    > I don't think this is correct. To have the same flexibility as the
    > TABLE inclusion lists, the TABLE keyword needs to be inside the
    > <except_table_object>, and there need to be more ellipses there too.
    >
    > e.g. Like this:
    >
    > and publication_all_object is one of:
    >     ALL TABLES [ EXCEPT ( except_table_object [, ... ] ) ]
    >     ALL SEQUENCES
    >
    > and except_table_object is:
    >     TABLE { [ ONLY ] table_name [ * ] } [, ...]
    >
    > ~~~
    >
    > 2.
    > The CREATE PUBLICATION page still refers to "EXCEPT TABLE" in multiple places.
    >
    > Perhaps now it should be called "EXCEPT (TABLE) clause" or just
    > "EXCEPT clause" or something else.
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 3.
    > -    ALL TABLES [ EXCEPT TABLE ( <replaceable
    > class="parameter">except_table_object</replaceable> [, ... ] ) ]
    > +    ALL TABLES [ EXCEPT ( TABLE  <replaceable
    > class="parameter">except_table_object</replaceable> [, ... ] ) ]
    >
    > This ALTER PUBLICATION synopsis should have the same/similar changes
    > as in the above review comment above for the CREATE PUBLICATION
    > synopsis.
    >
    > e.g.
    >
    > and publication_all_object is one of:
    >     ALL TABLES [ EXCEPT ( except_table_object [, ... ] ) ]
    >     ALL SEQUENCES
    >
    > and except_table_object is:
    >     TABLE { [ ONLY ] table_name [ * ] } [, ...]
    >
    > ~~~
    >
    > 4.
    > Also, similar to the earlier review comment, this ALTER PUBLICATION
    > page still refers to "EXCEPT TABLE" in multiple places.
    >
    > Perhaps now it should be called "EXCEPT (TABLE) clause" or just "EXCEPT clause".
    >
    > ======
    > src/backend/parser/gram.y
    >
    > 5.
    > - EXCEPT TABLE '(' pub_except_obj_list ')' { $$ = $4; }
    > + EXCEPT '(' TABLE pub_except_obj_list ')' { $$ = $4; }
    >
    > Same review comment as my above synopsis comments.
    >
    > This patch implementation only supports EXCEPT (TABLE t1,t2,t3);
    >
    > Specifically, it doesn't have the same flexibility you get from a
    > table inclusion list.
    >
    > e.g. IMO cmd should also work for:
    > EXCEPT (TABLE t1, TABLE t2,t3);
    > etc.
    >
    > ======
    > src/bin/pg_dump/pg_dump.c
    >
    > 6.
    > Code comment still refers to "EXCEPT TABLE clause", but it doesn't
    > look like that anymore.
    >
    > ======
    > src/test/regress/sql/publication.sql
    >
    > 7.
    > Comments still refer to "EXCEPT TABLE" and "EXCEPT TABLE clause", but
    > it doesn't look like that anymore.
    >
    > ~~~
    >
    > 8.
    > There needs to be additional tests to confirm that the syntax has the
    > same flexibility as TABLE inclusion lists do
    >
    > e.g. all these variations below are valid and equivalent:
    > ... EXCEPT (TABLE t1, t2, t3, t4);
    > ... EXCEPT (TABLE t1, t2, TABLE t3, t4);
    > ... EXCEPT (TABLE t1, t2, TABLE t3, t4);
    > ... EXCEPT (TABLE t1, TABLE t2, TABLE t3, TABLE t4);
    >
    I have not tested all the scenarios as per Amit's suggestion in [1]. I
    have just modified an existing test.
    
    > ======
    > src/test/subscription/t/037_except.pl
    >
    > 9.
    > Comments still refer to "EXCEPT TABLE", but it doesn't look like that anymore.
    >
    > ======
    > (more files)
    >
    > 10.
    > There are still more source files (not currently part of this patch)
    > that are still calling this the "EXCEPT TABLE" clause, even though the
    > syntax is different now.
    >
    > e.g. "src/backend/commands/publicationcmds.c" and others.
    >
    > Need to search the master source to find every affected comment.
    >
    Hi Peter,
    
    I have addressed the comments. Attached the updated patch.
    
    [1]: https://www.postgresql.org/message-id/CAA4eK1Jjm+w3hEGgsDu_r1Pysez=8mmtGu6=XwPE4MuH+eYG8Q@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  353. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-27T09:23:39Z

    On Fri, Mar 27, 2026 at 1:20 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    >
    > I have addressed the comments. Attached the updated patch.
    >
    > [1]: https://www.postgresql.org/message-id/CAA4eK1Jjm+w3hEGgsDu_r1Pysez=8mmtGu6=XwPE4MuH+eYG8Q@mail.gmail.com
    >
    
    The patch looks fine  to me, just one trivial thing:
    
    /*
     * Similar with preprocess_pubobj_list(), but used for the EXCEPT clause.
     */
    
    with -->to
    
    thanks
    Shveta
    
    
    
    
  354. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-27T12:33:53Z

    On Fri, Mar 27, 2026 at 1:20 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >
    > I have addressed the comments. Attached the updated patch.
    >
    
    I got following crash:
    
    postgres=# create publication pub1 for all tables except (t1, t2);
    server closed the connection unexpectedly
            This probably means the server terminated abnormally
            before or while processing the request.
    The connection to the server was lost. Attempting reset: Failed.
    
    Server Log:
     ERROR:  syntax error at or near "table" at character 47
    [client backend] STATEMENT:  create publication pub1 for all tables
    except table (t1, t2);
    TRAP: failed Assert("pubobj->pubobjtype ==
    PUBLICATIONOBJ_EXCEPT_TABLE"), File: "../src/backend/parser/gram.y",
    Line: 20795, PID: 7512
    abort() has been called[postmaster] LOG:  client backend (PID 7512)
    was terminated by exception 0xC0000409
    [postmaster] DETAIL:  Failed process was running: create publication
    pub1 for all tables except (t1, t2);
    [postmaster] HINT:  See C include file "ntstatus.h" for a description
    of the hexadecimal value.
    
    BTW, why do we need preprocess_except_pubobj_list?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  355. Re: Skipping schema changes in publication

    SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com> — 2026-03-27T19:03:46Z

    Hi,
    
    On Fri, Mar 27, 2026 at 12:50 AM Shlok Kyal <shlok.kyal.oss@gmail.com>
    wrote:
    
    >
    > I have addressed the comments. Attached the updated patch.
    >
    > [1]:
    > https://www.postgresql.org/message-id/CAA4eK1Jjm+w3hEGgsDu_r1Pysez=8mmtGu6=XwPE4MuH+eYG8Q@mail.gmail.com
    
    
    A few minor comments:
    
    1. Add a negative test for missing table keyword for example EXCEPT (t1, t2)
    2. NIT comment, remove the extra space between parenthesis and TABLE below
    
      else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL",
    "TABLES"))
    - COMPLETE_WITH("EXCEPT TABLE (", "WITH (");
    + COMPLETE_WITH("EXCEPT ( TABLE", "WITH (");
    
    3. for pg_dump, to improve readability can you just add TABLE keyword for
    every tables in the except list?
    
    - /* Include EXCEPT TABLE clause if there are except_tables. */
    + /* Include EXCEPT (TABLE) clause if there are except_tables. */
      for (SimplePtrListCell *cell = pubinfo->except_tables.head; cell; cell =
    cell->next)
      {
      TableInfo  *tbinfo = (TableInfo *) cell->ptr;
    
      if (++n_except == 1)
    - appendPQExpBufferStr(query, " EXCEPT TABLE (");
    + appendPQExpBufferStr(query, " EXCEPT (TABLE ");
      else
      appendPQExpBufferStr(query, ", ");
    
    4. Is the Assert needed below, code already produces error message.
    
    + Assert(pubobj->pubobjtype == PUBLICATIONOBJ_EXCEPT_TABLE);
    +
    + if (pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION)
    + ereport(ERROR,
    + errcode(ERRCODE_SYNTAX_ERROR),
    + errmsg("invalid publication object list"),
    + errdetail("TABLE must be specified before a standalone table."),
    + parser_errposition(pubobj->location));
    
    Thanks,
    Satya
    
  356. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-03-27T20:49:34Z

    On Fri, 27 Mar 2026 at 18:04, Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Fri, Mar 27, 2026 at 1:20 PM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > I have addressed the comments. Attached the updated patch.
    > >
    >
    > I got following crash:
    >
    > postgres=# create publication pub1 for all tables except (t1, t2);
    > server closed the connection unexpectedly
    >         This probably means the server terminated abnormally
    >         before or while processing the request.
    > The connection to the server was lost. Attempting reset: Failed.
    >
    > Server Log:
    >  ERROR:  syntax error at or near "table" at character 47
    > [client backend] STATEMENT:  create publication pub1 for all tables
    > except table (t1, t2);
    > TRAP: failed Assert("pubobj->pubobjtype ==
    > PUBLICATIONOBJ_EXCEPT_TABLE"), File: "../src/backend/parser/gram.y",
    > Line: 20795, PID: 7512
    > abort() has been called[postmaster] LOG:  client backend (PID 7512)
    > was terminated by exception 0xC0000409
    > [postmaster] DETAIL:  Failed process was running: create publication
    > pub1 for all tables except (t1, t2);
    > [postmaster] HINT:  See C include file "ntstatus.h" for a description
    > of the hexadecimal value.
    This crash is happening because we hit the Assert(pubobj->pubobjtype
    == PUBLICATIONOBJ_EXCEPT_TABLE).
    In the above example the first table is specified without the TABLE
    keyword and hence its pubobjtype is PUBLICATIONOBJ_CONTINUATION.
    I have updated the Assert and also added a test case for the above scenario.
    
    >
    > BTW, why do we need preprocess_except_pubobj_list?
    >
    It is needed to check if the first table in the EXCEPT list is
    specified with TABLE Keyword.
    For example: create publication pub1 for all tables except (t1, t2);
    This should throw an error because t1 is specified without the TABLE keyword.
    
    We have a similar logic for FOR TABLE or FOR TABLES IN SCHEMA
    publications in 'preprocess_pubobj_list'
    
    I have attached the latest version here. I have also addressed the
    comment shared by Shveta in [1].
    [1]: https://www.postgresql.org/message-id/CAJpy0uCMhxUdEZMqDuSHXeAMES1gH56r-tkhR1J79A_f3wrLkg@mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
  357. Re: Skipping schema changes in publication

    Shlok Kyal <shlok.kyal.oss@gmail.com> — 2026-03-27T20:50:57Z

    On Sat, 28 Mar 2026 at 00:33, SATYANARAYANA NARLAPURAM
    <satyanarlapuram@gmail.com> wrote:
    >
    > Hi,
    >
    > On Fri, Mar 27, 2026 at 12:50 AM Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    >>
    >>
    >> I have addressed the comments. Attached the updated patch.
    >>
    >> [1]: https://www.postgresql.org/message-id/CAA4eK1Jjm+w3hEGgsDu_r1Pysez=8mmtGu6=XwPE4MuH+eYG8Q@mail.gmail.com
    >
    >
    > A few minor comments:
    >
    > 1. Add a negative test for missing table keyword for example EXCEPT (t1, t2)
    > 2. NIT comment, remove the extra space between parenthesis and TABLE below
    >
    >   else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES"))
    > - COMPLETE_WITH("EXCEPT TABLE (", "WITH (");
    > + COMPLETE_WITH("EXCEPT ( TABLE", "WITH (");
    >
    > 3. for pg_dump, to improve readability can you just add TABLE keyword for every tables in the except list?
    >
    > - /* Include EXCEPT TABLE clause if there are except_tables. */
    > + /* Include EXCEPT (TABLE) clause if there are except_tables. */
    >   for (SimplePtrListCell *cell = pubinfo->except_tables.head; cell; cell = cell->next)
    >   {
    >   TableInfo  *tbinfo = (TableInfo *) cell->ptr;
    >
    >   if (++n_except == 1)
    > - appendPQExpBufferStr(query, " EXCEPT TABLE (");
    > + appendPQExpBufferStr(query, " EXCEPT (TABLE ");
    >   else
    >   appendPQExpBufferStr(query, ", ");
    >
    > 4. Is the Assert needed below, code already produces error message.
    >
    > + Assert(pubobj->pubobjtype == PUBLICATIONOBJ_EXCEPT_TABLE);
    > +
    > + if (pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION)
    > + ereport(ERROR,
    > + errcode(ERRCODE_SYNTAX_ERROR),
    > + errmsg("invalid publication object list"),
    > + errdetail("TABLE must be specified before a standalone table."),
    > + parser_errposition(pubobj->location));
    I think the Assert should stay as a defensive check to ensure this
    function is only used for EXCEPT (TABLE) cases.
    
    Thanks for reviewing the patch. I have addressed the remaining
    comments and attached the patch in [1].
    [1]: https://www.postgresql.org/message-id/CANhcyEUQvEK%2BHOH6Y8Fy30fNvC631ZopWKhwgskXjKnuXiGV5Q%40mail.gmail.com
    
    Thanks,
    Shlok Kyal
    
    
    
    
  358. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-30T02:31:33Z

    Hi Shlok.
    
    Here are some review comments for the patch v3-0001.
    
    (not yet reviewed the test code)
    
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    synopsis:
    
    1.
    -    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    +    TABLE [ ONLY ] <replaceable
    class="parameter">table_name</replaceable> [ * ]
    
    This is still not correct because it is missing the ellipsis that is
    needed within 'except_table_object'. e.g, the currently documented
    synopsis would not permit FOR ALL TABLE EXCEPT (TABLE t1,t2,t3);
    
    You might describe that using one of these ways:
    
    i)
    TABLE { [ ONLY ] table_name [ * ] } [, ...]
    
    ii)
    TABLE [ ONLY ] table_name [ * ] [, ...]
    
    iii)
    TABLE table [,...]
    and table is:
    [ ONLY ] table_name [ * ]
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    synopsis:
    
    2.
    Same synopsis problem as described in the above review comment. It is
    missing the ellipsis that is needed within 'except_table_object'.
    
    ~~~
    
    EXCEPT:
    
    3.
        <varlistentry id="sql-createpublication-params-for-except-table">
    -    <term><literal>EXCEPT TABLE</literal></term>
    +    <term><literal>EXCEPT</literal></term>
         <listitem>
    
    The 'EXCEPT' clause is not really at the same level as all the other
    ones, because it can only be applied to the FOR ALL TABLES. So, I felt
    maybe 'EXCEPT' might make more sense as a sublist entry under the FOR
    ALL TABLES clause.
    
    Moving this would also eliminate some of the current repetition:
    e.g. "Tables listed in EXCEPT clause are excluded from the publication."
    e.g. "This clause specifies a list of tables to be excluded from the
    publication."
    
    ======
    src/backend/catalog/pg_publication.c
    
    GetAllPublicationRelations:
    
    4.
    - * in EXCEPT TABLE clause.
    + * in EXCEPT clause.
    
    /in EXCEPT clause/in the EXCEPT clause/
    
    ======
    src/backend/parser/gram.y
    
    5.
     static void preprocess_pubobj_list(List *pubobjspec_list,
         core_yyscan_t yyscanner);
    +static void preprocess_except_pubobj_list(List *pubexceptobjspec_list,
    + core_yyscan_t yyscanner);
    
    The new function and the parameter names do not match as they do in
    the existing function. e.g. Should this new function instead be called
    'preprocess_pubexceptobj_list'?
    
    ~~~
    
    preprocess_except_pubobj_list:
    
    6.
    + ListCell   *cell;
    + PublicationObjSpec *pubobj;
    
    Why is this function referring to PublicationObjSpec instead of
    PublicationExceptObjSpec. Is it correct?
    
    ~~~
    
    7.
    + if (pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION)
    + ereport(ERROR,
    + errcode(ERRCODE_SYNTAX_ERROR),
    + errmsg("invalid publication object list"),
    + errdetail("TABLE must be specified before a standalone table."),
    + parser_errposition(pubobj->location));
    
    If this was intended to mimic code from the existing function, then I
    felt it should say "standalone table name." instead of "standalone
    table.".
    
    ======
    src/bin/pg_dump/pg_dump.c
    
    8.
      if (++n_except == 1)
    - appendPQExpBufferStr(query, " EXCEPT TABLE (");
    + appendPQExpBufferStr(query, " EXCEPT (TABLE ");
      else
    - appendPQExpBufferStr(query, ", ");
    + appendPQExpBufferStr(query, ", TABLE ");
    
    You don't need the TABLE keyword in 2 places like that.
    
    SUGGESTION
    if (++n_except == 1)
      appendPQExpBufferStr(query, " EXCEPT (");
    else
      appendPQExpBufferStr(query, ", ");
    appendPQExpBuffer(query, "TABLE ONLY %s", fmtQualifiedDumpable(tbinfo));
    
    ======
    src/bin/pg_dump/t/002_pg_dump.pl
    
    9.
      'CREATE PUBLICATION pub10' => {
      create_order => 92,
      create_sql =>
    -   'CREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT TABLE
    (dump_test.test_inheritance_parent);',
    +   'CREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT (TABLE
    dump_test.test_inheritance_parent);',
      regexp => qr/^
    - \QCREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT TABLE (ONLY
    dump_test.test_inheritance_parent, ONLY
    dump_test.test_inheritance_child) WITH (publish = 'insert, update,
    delete, truncate');\E
    + \QCREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT (TABLE ONLY
    dump_test.test_inheritance_parent, TABLE ONLY
    dump_test.test_inheritance_child) WITH (publish = 'insert, update,
    delete, truncate');\E
      /xm,
      like => { %full_runs, section_post_data => 1, },
    
    (Maybe this question is unrelated to the current patch.)
    
    Why is the expected result explicitly including ONLY children like
    that instead of saying "EXCEPT (TABLE
    dump_test.test_inheritance_parent *)"?
    
    ======
    src/bin/psql/tab-complete.in.c
    
    10.
    - else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL",
    "TABLES", "EXCEPT", "TABLE", "(", MatchAnyN) && ends_with(prev_wd,
    ','))
    + else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL",
    "TABLES", "EXCEPT", "(", "TABLE", MatchAnyN) && ends_with(prev_wd,
    ','))
      COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
    - else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL",
    "TABLES", "EXCEPT", "TABLE", "(", MatchAnyN) && !ends_with(prev_wd,
    ','))
    + else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL",
    "TABLES", "EXCEPT", "(", "TABLE", MatchAnyN) && !ends_with(prev_wd,
    ','))
      COMPLETE_WITH(")");
    
    The CREATE PUBLICATION seems to have tab-completion logic for the ','
    separator and closing ')'. But I did not see any similar
    tab-completion logic for the EXCEPT clause in ALTER PUBLICATION. Is
    this maybe another issue independent of this patch?
    
    ======
    src/test/regress/expected/publication.out
    src/test/regress/sql/publication.sql
    src/test/subscription/t/037_except.pl
    
    (I will review these later)
    
    ======
    GENERAL
    
    11.
    The file src/backend/commands/publicationcmds.c still refers to
    "(EXCEPT TABLES)".
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  359. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-30T03:59:29Z

    On Mon, Mar 30, 2026 at 8:02 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Here are some review comments for the patch v3-0001.
    >
    > (not yet reviewed the test code)
    >
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > synopsis:
    >
    > 1.
    > -    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    > +    TABLE [ ONLY ] <replaceable
    > class="parameter">table_name</replaceable> [ * ]
    >
    > This is still not correct because it is missing the ellipsis that is
    > needed within 'except_table_object'. e.g, the currently documented
    > synopsis would not permit FOR ALL TABLE EXCEPT (TABLE t1,t2,t3);
    >
    > You might describe that using one of these ways:
    >
    > i)
    > TABLE { [ ONLY ] table_name [ * ] } [, ...]
    >
    
    But isn't '{' is used to indicate a group of options, usually combined
    with | to show choices. Examples:
    
    TABLES IN SCHEMA { schema_name | CURRENT_SCHEMA } [, ... ]
    
    ALTER PUBLICATION name OWNER TO { new_owner | CURRENT_ROLE |
    CURRENT_USER | SESSION_USER }
    
    In our case, I don't see such an option list. Let me know if I have
    misunderstood.
    
    thanks
    Shveta
    
    
    
    
  360. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-30T04:07:19Z

    On Mon, Mar 30, 2026 at 8:02 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > ~~~
    >
    > EXCEPT:
    >
    > 3.
    >     <varlistentry id="sql-createpublication-params-for-except-table">
    > -    <term><literal>EXCEPT TABLE</literal></term>
    > +    <term><literal>EXCEPT</literal></term>
    >      <listitem>
    >
    > The 'EXCEPT' clause is not really at the same level as all the other
    > ones, because it can only be applied to the FOR ALL TABLES. So, I felt
    > maybe 'EXCEPT' might make more sense as a sublist entry under the FOR
    > ALL TABLES clause.
    >
    
    Possible but in future it would be applicable for all the three
    variants listed above: FOR TABLES IN SCHEMA, FOR ALL TABLES, FOR ALL
    SEQUENCES. So, don't think making it as a sublist for "FOR ALL TABLE"
    is a good idea. I find keeping it at its current place okay as it is
    also an independent clause.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  361. Re: Skipping schema changes in publication

    Dilip Kumar <dilipbalaut@gmail.com> — 2026-03-30T04:11:33Z

    On Mon, Mar 30, 2026 at 9:37 AM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Mon, Mar 30, 2026 at 8:02 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    > > ~~~
    > >
    > > EXCEPT:
    > >
    > > 3.
    > >     <varlistentry id="sql-createpublication-params-for-except-table">
    > > -    <term><literal>EXCEPT TABLE</literal></term>
    > > +    <term><literal>EXCEPT</literal></term>
    > >      <listitem>
    > >
    > > The 'EXCEPT' clause is not really at the same level as all the other
    > > ones, because it can only be applied to the FOR ALL TABLES. So, I felt
    > > maybe 'EXCEPT' might make more sense as a sublist entry under the FOR
    > > ALL TABLES clause.
    > >
    >
    > Possible but in future it would be applicable for all the three
    > variants listed above: FOR TABLES IN SCHEMA, FOR ALL TABLES, FOR ALL
    > SEQUENCES. So, don't think making it as a sublist for "FOR ALL TABLE"
    > is a good idea. I find keeping it at its current place okay as it is
    > also an independent clause.
    
    Keeping it independent makes sense, as it will allow for easier future
    expansion.
    
    -- 
    Regards,
    Dilip Kumar
    Google
    
    
    
    
  362. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-03-30T04:36:58Z

    preprocess_except_pubobj_list:
    
    + Assert(pubobj->pubobjtype == PUBLICATIONOBJ_EXCEPT_TABLE ||
    +    pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION);
    +
    
    We need Sanity check on each object of the list rather than on first
    object alone i.e. each object should have type EXCEPT or its
    continuation. Thus it makes more sense to move Assert inside 'for
    loop' and add a comment too:
    <please feel free to change comment if needed>
    
    foreach(cell, pubexceptobjspec_list)
    {
       pubobj = (PublicationObjSpec *) lfirst(cell);
    
       /*
        * The object type must be either PUBLICATIONOBJ_EXCEPT_TABLE or
        * PUBLICATIONOBJ_CONTINUATION. It cannot be PUBLICATIONOBJ_TABLE,
        * PUBLICATIONOBJ_TABLES_IN_SCHEMA or
        * PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA here.
        */
       Assert(pubobj->pubobjtype == PUBLICATIONOBJ_EXCEPT_TABLE ||
          pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION);
    .....
    
    thanks
    Shveta
    
    
    
    
  363. RE: Skipping schema changes in publication

    Zhijie Hou (Fujitsu) <houzj.fnst@fujitsu.com> — 2026-03-30T05:51:23Z

    On Saturday, March 28, 2026 4:50 AM  Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > 
    > On Fri, 27 Mar 2026 at 18:04, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > >
    > >
    > > BTW, why do we need preprocess_except_pubobj_list?
    > >
    > It is needed to check if the first table in the EXCEPT list is specified with TABLE
    > Keyword.
    > For example: create publication pub1 for all tables except (t1, t2); This should
    > throw an error because t1 is specified without the TABLE keyword.
    > 
    > We have a similar logic for FOR TABLE or FOR TABLES IN SCHEMA
    > publications in 'preprocess_pubobj_list'
    
    The preprocess_pubobj_list function was originally introduced to handle mixed
    object types (SCHEMA and TABLE) in the main list, allowing type-specific
    processing per item. However, the EXCEPT clause currently only supports TABLE
    objects. So strictly speaking, we don't need a similar preprocessing function
    here, at least not until we support other object types in the EXCEPT clause. We
    could implement the same logic directly in bison code, as shown in the attached
    example.
    
    > 
    > I have attached the latest version here. I have also addressed the comment
    > shared by Shveta in [1].
    > [1]: https://www.postgresql.org/message-
    > id/CAJpy0uCMhxUdEZMqDuSHXeAMES1gH56r-
    > tkhR1J79A_f3wrLkg@mail.gmail.com
    
    Best Regards,
    Hou zj
    
    
  364. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-30T05:58:15Z

    Hi Shlok.
    
    Here are the remainder of my review comments for patch v3-0001.
    
    These are for the test code only.
    
    ======
    src/test/regress/sql/publication.sql
    
    Missing Test case:
    
    1.
    AFAICT, there are currently no test cases for ALTER PUBLICATION that
    are using variations of the syntax having multiple TABLE keywords.
    e.g. "EXCEPT (TABLE t1,t2,t3, TABLE t4)".
    
    IOW, the flexible syntax is being tested for CREATE PUBLICATION, but
    not for ALTER PUBLICATION.
    
    ~~~
    
    2.
    --- Replace the existing EXCEPT TABLE list (testpub_tbl1) with a new
    --- EXCEPT TABLE list containing only (testpub_tbl2).
    -ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES
    EXCEPT TABLE (testpub_tbl2);
    +-- Replace the existing table list in EXCEPT clause (testpub_tbl1) with a new
    +-- table list containing only (testpub_tbl2).
    +ALTER PUBLICATION testpub_foralltables_excepttable SET ALL TABLES
    EXCEPT (TABLE testpub_tbl2);
    
    The comment seems wrong because, IIUC, the existing exclude-list at
    this point would be "(TABLE testpub_tbl1, testpub_tbl2, TABLE
    testpub_tbl3)".
    
    Also /in EXCEPT clause/in the EXCEPT clause/
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  365. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-30T09:13:48Z

    On Mon, Mar 30, 2026 at 11:21 AM Zhijie Hou (Fujitsu)
    <houzj.fnst@fujitsu.com> wrote:
    >
    > On Saturday, March 28, 2026 4:50 AM  Shlok Kyal <shlok.kyal.oss@gmail.com> wrote:
    > >
    > > On Fri, 27 Mar 2026 at 18:04, Amit Kapila <amit.kapila16@gmail.com> wrote:
    > > >
    > > >
    > > > BTW, why do we need preprocess_except_pubobj_list?
    > > >
    > > It is needed to check if the first table in the EXCEPT list is specified with TABLE
    > > Keyword.
    > > For example: create publication pub1 for all tables except (t1, t2); This should
    > > throw an error because t1 is specified without the TABLE keyword.
    > >
    > > We have a similar logic for FOR TABLE or FOR TABLES IN SCHEMA
    > > publications in 'preprocess_pubobj_list'
    >
    > The preprocess_pubobj_list function was originally introduced to handle mixed
    > object types (SCHEMA and TABLE) in the main list, allowing type-specific
    > processing per item. However, the EXCEPT clause currently only supports TABLE
    > objects. So strictly speaking, we don't need a similar preprocessing function
    > here, at least not until we support other object types in the EXCEPT clause.
    >
    
    Fair enough. We could extend it later as and when required. I have one
    minor comment:
    
    *
    + | pub_except_obj_list ',' TABLE PublicationExceptObjSpec
    + { $$ = lappend($1, $4); }
      | pub_except_obj_list ',' PublicationExceptObjSpec
    
    Can we encapsulate 'TABLE PublicationExceptObjSpec' and
    'PublicationExceptObjSpec' into separate 'opt_tbl_except_obj_spec'?
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  366. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-30T14:44:30Z

    On Mon, 30 Mar 2026 at 08:02, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Shlok.
    >
    > Here are some review comments for the patch v3-0001.
    >
    > (not yet reviewed the test code)
    >
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > synopsis:
    >
    > 1.
    > -    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    > +    TABLE [ ONLY ] <replaceable
    > class="parameter">table_name</replaceable> [ * ]
    >
    > This is still not correct because it is missing the ellipsis that is
    > needed within 'except_table_object'. e.g, the currently documented
    > synopsis would not permit FOR ALL TABLE EXCEPT (TABLE t1,t2,t3);
    >
    > You might describe that using one of these ways:
    >
    > i)
    > TABLE { [ ONLY ] table_name [ * ] } [, ...]
    >
    > ii)
    > TABLE [ ONLY ] table_name [ * ] [, ...]
    >
    > iii)
    > TABLE table [,...]
    > and table is:
    > [ ONLY ] table_name [ * ]
    
    Modified
    
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > synopsis:
    >
    > 2.
    > Same synopsis problem as described in the above review comment. It is
    > missing the ellipsis that is needed within 'except_table_object'.
    
    The previous comment fixes this issue
    
    > ~~~
    >
    > EXCEPT:
    >
    > 3.
    >     <varlistentry id="sql-createpublication-params-for-except-table">
    > -    <term><literal>EXCEPT TABLE</literal></term>
    > +    <term><literal>EXCEPT</literal></term>
    >      <listitem>
    >
    > The 'EXCEPT' clause is not really at the same level as all the other
    > ones, because it can only be applied to the FOR ALL TABLES. So, I felt
    > maybe 'EXCEPT' might make more sense as a sublist entry under the FOR
    > ALL TABLES clause.
    >
    > Moving this would also eliminate some of the current repetition:
    > e.g. "Tables listed in EXCEPT clause are excluded from the publication."
    > e.g. "This clause specifies a list of tables to be excluded from the
    > publication."
    
    Amit has replied to this at [1].
    
    > ======
    > src/backend/catalog/pg_publication.c
    >
    > GetAllPublicationRelations:
    >
    > 4.
    > - * in EXCEPT TABLE clause.
    > + * in EXCEPT clause.
    >
    > /in EXCEPT clause/in the EXCEPT clause/
    
    modified
    
    > ======
    > src/backend/parser/gram.y
    >
    > 5.
    >  static void preprocess_pubobj_list(List *pubobjspec_list,
    >      core_yyscan_t yyscanner);
    > +static void preprocess_except_pubobj_list(List *pubexceptobjspec_list,
    > + core_yyscan_t yyscanner);
    >
    > The new function and the parameter names do not match as they do in
    > the existing function. e.g. Should this new function instead be called
    > 'preprocess_pubexceptobj_list'?
    
    The function has been removed now because of Hou-san's improvement
    suggestion at [2].
    
    > ~~~
    >
    > preprocess_except_pubobj_list:
    >
    > 6.
    > + ListCell   *cell;
    > + PublicationObjSpec *pubobj;
    >
    > Why is this function referring to PublicationObjSpec instead of
    > PublicationExceptObjSpec. Is it correct?
    
    The function has been removed now because of Hou-san's improvement
    suggestion at [2].
    
    > ~~~
    >
    > 7.
    > + if (pubobj->pubobjtype == PUBLICATIONOBJ_CONTINUATION)
    > + ereport(ERROR,
    > + errcode(ERRCODE_SYNTAX_ERROR),
    > + errmsg("invalid publication object list"),
    > + errdetail("TABLE must be specified before a standalone table."),
    > + parser_errposition(pubobj->location));
    >
    > If this was intended to mimic code from the existing function, then I
    > felt it should say "standalone table name." instead of "standalone
    > table.".
    
    The function has been removed now because of Hou-san's improvement
    suggestion at [2].
    
    > ======
    > src/bin/pg_dump/pg_dump.c
    >
    > 8.
    >   if (++n_except == 1)
    > - appendPQExpBufferStr(query, " EXCEPT TABLE (");
    > + appendPQExpBufferStr(query, " EXCEPT (TABLE ");
    >   else
    > - appendPQExpBufferStr(query, ", ");
    > + appendPQExpBufferStr(query, ", TABLE ");
    >
    > You don't need the TABLE keyword in 2 places like that.
    >
    > SUGGESTION
    > if (++n_except == 1)
    >   appendPQExpBufferStr(query, " EXCEPT (");
    > else
    >   appendPQExpBufferStr(query, ", ");
    > appendPQExpBuffer(query, "TABLE ONLY %s", fmtQualifiedDumpable(tbinfo));
    
    Modified
    
    > ======
    > src/bin/pg_dump/t/002_pg_dump.pl
    >
    > 9.
    >   'CREATE PUBLICATION pub10' => {
    >   create_order => 92,
    >   create_sql =>
    > -   'CREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT TABLE
    > (dump_test.test_inheritance_parent);',
    > +   'CREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT (TABLE
    > dump_test.test_inheritance_parent);',
    >   regexp => qr/^
    > - \QCREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT TABLE (ONLY
    > dump_test.test_inheritance_parent, ONLY
    > dump_test.test_inheritance_child) WITH (publish = 'insert, update,
    > delete, truncate');\E
    > + \QCREATE PUBLICATION pub10 FOR ALL TABLES EXCEPT (TABLE ONLY
    > dump_test.test_inheritance_parent, TABLE ONLY
    > dump_test.test_inheritance_child) WITH (publish = 'insert, update,
    > delete, truncate');\E
    >   /xm,
    >   like => { %full_runs, section_post_data => 1, },
    >
    > (Maybe this question is unrelated to the current patch.)
    >
    > Why is the expected result explicitly including ONLY children like
    > that instead of saying "EXCEPT (TABLE
    > dump_test.test_inheritance_parent *)"?
    
    This behavior is kept similar to how it is handled for table publication
    ex:
    CREATE TABLE test_inheritance_parent (col1 int NOT NULL, col2 int
    CHECK (col2 >= 42));
    CREATE TABLE test_inheritance_child (col1 int NOT NULL, CONSTRAINT
    test_inheritance_child CHECK (col2 >= 142857) ) INHERITS
    (test_inheritance_parent);
    CREATE PUBLICATION pub10 FOR TABLE test_inheritance_parent;
    
    Dump outputs the following:
    --
    -- Name: pub10 test_inheritance_child; Type: PUBLICATION TABLE;
    Schema: public; Owner: vignesh
    --
    ALTER PUBLICATION pub10 ADD TABLE ONLY public.test_inheritance_child;
    
    --
    -- Name: pub10 test_inheritance_parent; Type: PUBLICATION TABLE;
    Schema: public; Owner: vignesh
    --
    ALTER PUBLICATION pub10 ADD TABLE ONLY public.test_inheritance_parent;
    
    > ======
    > src/bin/psql/tab-complete.in.c
    >
    > 10.
    > - else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL",
    > "TABLES", "EXCEPT", "TABLE", "(", MatchAnyN) && ends_with(prev_wd,
    > ','))
    > + else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL",
    > "TABLES", "EXCEPT", "(", "TABLE", MatchAnyN) && ends_with(prev_wd,
    > ','))
    >   COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
    > - else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL",
    > "TABLES", "EXCEPT", "TABLE", "(", MatchAnyN) && !ends_with(prev_wd,
    > ','))
    > + else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL",
    > "TABLES", "EXCEPT", "(", "TABLE", MatchAnyN) && !ends_with(prev_wd,
    > ','))
    >   COMPLETE_WITH(")");
    >
    > The CREATE PUBLICATION seems to have tab-completion logic for the ','
    > separator and closing ')'. But I did not see any similar
    > tab-completion logic for the EXCEPT clause in ALTER PUBLICATION. Is
    > this maybe another issue independent of this patch?
    
    Modified
    
    > ======
    > src/test/regress/expected/publication.out
    > src/test/regress/sql/publication.sql
    > src/test/subscription/t/037_except.pl
    >
    > (I will review these later)
    >
    > ======
    > GENERAL
    >
    > 11.
    > The file src/backend/commands/publicationcmds.c still refers to
    > "(EXCEPT TABLES)".
    
    Modified
    
    The v4 version patch has the changes for the same.
    The v4 version also addresses the comments provided by Peter at [3]
    and Amit at [4].
    
    [1] - https://www.postgresql.org/message-id/CAA4eK1Kb5Mg_MPDAZGb4PofRryXLjEFRd2k3aV%3DKCW-kmmkwvw%40mail.gmail.com
    [2] - https://www.postgresql.org/message-id/TY4PR01MB169072D244423BED52C8D5B249452A%40TY4PR01MB16907.jpnprd01.prod.outlook.com
    [3] - https://www.postgresql.org/message-id/CAHut%2BPvV_Yvm3VYWrbP%3DTL5_yvKUbQJbv%3DP3yt09odEV%2B1igYg%40mail.gmail.com
    [4] - https://www.postgresql.org/message-id/CAA4eK1K0uHt-o%3DOAqh2qT7gDyef-BrHb%3DZ-94kCB8dH8r3SDeA%40mail.gmail.com
    
    Regards,
    Vignesh
    
  367. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-31T00:29:39Z

    Hi Vignesh.
    
    Some review comments for patch v4-0001.
    
    ======
    doc/src/sgml/ref/alter_publication.sgml
    
    1.
    -    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    +    TABLE <replaceable class="parameter">table_object</replaceable> [, ... ]
    +
    +<phrase>and <replaceable class="parameter">table_object</replaceable>
    is:</phrase>
    +
    +   [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    
    With the introduction of 'table_object' I expected that
    'table_and_columns' and 'publication_drop_object' would also make use
    of it. Why not?
    
    ======
    doc/src/sgml/ref/create_publication.sgml
    
    2.
    -    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    +    TABLE <replaceable class="parameter">table_object</replaceable> [, ... ]
    +
    +<phrase>and <replaceable class="parameter">table_object</replaceable>
    is:</phrase>
    +
    +   [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    
    With the introduction of 'table_object' I expected that
    'table_and_columns' would also make use of it. Why not?
    
    ======
    src/backend/parser/gram.y
    
    3.
    @@ -11399,7 +11399,7 @@ pub_obj_list: PublicationObjSpec
      ;
    
     opt_pub_except_clause:
    - EXCEPT TABLE '(' pub_except_obj_list ')' { $$ = $4; }
    + EXCEPT '(' TABLE pub_except_obj_list ')' { $$ = $4; }
      | /*EMPTY*/ { $$ = NIL; }
      ;
    
    @@ -11439,8 +11439,8 @@ PublicationExceptObjSpec:
    
     pub_except_obj_list: PublicationExceptObjSpec
      { $$ = list_make1($1); }
    - | pub_except_obj_list ',' PublicationExceptObjSpec
    - { $$ = lappend($1, $3); }
    + | pub_except_obj_list ',' opt_table PublicationExceptObjSpec
    + { $$ = lappend($1, $4); }
      ;
    
    IMO we should put 'table' in all those table-specific production names:
    e.g. opt_pub_except_clause ==> opt_pub_except_table_clause
    e.g. pub_except_obj_list ==> pub_except_tableobj_list
    e.g. PublicationExceptObjSpec ==> PublicationExceptTableObjSpec
    
    IIUC, in future when "FOR ALL SEQUENCES EXCEPT (SEQUENCES ...)" and/or
    "FOR ALL SCHEMAS EXCEPT (SCHEMA ...)" are implemented then these names
    won't be much good anymore, so I thought they should be made
    table-specific now to avoid churning them later.
    
    ======
    src/bin/psql/tab-complete.in.c
    
    4.
    - COMPLETE_WITH("EXCEPT TABLE (");
    + COMPLETE_WITH("EXCEPT ( TABLE");
    
    (this is in several places in  ALTER and CREATE)
    
    In v3 the space in "( TABLE" was changed to "(TABLE", but now in v4
    the space is back again. AFAICT the v3 change was in response to
    review [1] (comment #2). Was it reverted deliberately?
    
    ======
    [1] https://www.postgresql.org/message-id/CAHg%2BQDe%2B7J0e1JLjpqe-NxVJMWXH6UDtqugrm%2BDP7rHEJLrBqQ%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  368. RE: Skipping schema changes in publication

    Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com> — 2026-03-31T03:54:37Z

    Dear Peter,
    
    > ======
    > src/bin/psql/tab-complete.in.c
    > 
    > 4.
    > - COMPLETE_WITH("EXCEPT TABLE (");
    > + COMPLETE_WITH("EXCEPT ( TABLE");
    > 
    > (this is in several places in  ALTER and CREATE)
    > 
    > In v3 the space in "( TABLE" was changed to "(TABLE", but now in v4
    > the space is back again. AFAICT the v3 change was in response to
    > review [1] (comment #2). Was it reverted deliberately?
    
    I think it's proper to have the blank before/after the parentheses. See the
    existing example.
    
    ```
    	/* ALTER TABLE <name> SPLIT PARTITION <name> */
    	else if (Matches("ALTER", "TABLE", MatchAny, "SPLIT", "PARTITION", MatchAny))
    		COMPLETE_WITH("INTO ( PARTITION");
    ```
    
    Also, if we type till `CREATE PUBLICATION pub WITH` and type tab several times,
    the suggested string is something like below:
    
    ```
    CREATE PUBLICATION pub WITH ( PUBLISH
    ```
    
    It means normally the blank exists in between the term.
    
    Best regards,
    Hayato Kuroda
    FUJITSU LIMITED
    
    
    
    
    
  369. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-31T03:55:31Z

    On Tue, Mar 31, 2026 at 6:00 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Vignesh.
    >
    > Some review comments for patch v4-0001.
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 1.
    > -    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ *
    ]
    > +    TABLE <replaceable class="parameter">table_object</replaceable> [,
    ... ]
    > +
    > +<phrase>and <replaceable class="parameter">table_object</replaceable>
    > is:</phrase>
    > +
    > +   [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > With the introduction of 'table_object' I expected that
    > 'table_and_columns' and 'publication_drop_object' would also make use
    > of it. Why not?
    >
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 2.
    > -    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ *
    ]
    > +    TABLE <replaceable class="parameter">table_object</replaceable> [,
    ... ]
    > +
    > +<phrase>and <replaceable class="parameter">table_object</replaceable>
    > is:</phrase>
    > +
    > +   [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > With the introduction of 'table_object' I expected that
    > 'table_and_columns' would also make use of it. Why not?
    >
    
    These sound like worth considering but let's do them as a separate patch,
    otherwise, the main patch may take more time.
    
    >
    > ======
    > src/backend/parser/gram.y
    >
    > 3.
    > @@ -11399,7 +11399,7 @@ pub_obj_list: PublicationObjSpec
    >   ;
    >
    >  opt_pub_except_clause:
    > - EXCEPT TABLE '(' pub_except_obj_list ')' { $$ = $4; }
    > + EXCEPT '(' TABLE pub_except_obj_list ')' { $$ = $4; }
    >   | /*EMPTY*/ { $$ = NIL; }
    >   ;
    >
    > @@ -11439,8 +11439,8 @@ PublicationExceptObjSpec:
    >
    >  pub_except_obj_list: PublicationExceptObjSpec
    >   { $$ = list_make1($1); }
    > - | pub_except_obj_list ',' PublicationExceptObjSpec
    > - { $$ = lappend($1, $3); }
    > + | pub_except_obj_list ',' opt_table PublicationExceptObjSpec
    > + { $$ = lappend($1, $4); }
    >   ;
    >
    > IMO we should put 'table' in all those table-specific production names:
    > e.g. opt_pub_except_clause ==> opt_pub_except_table_clause
    > e.g. pub_except_obj_list ==> pub_except_tableobj_list
    > e.g. PublicationExceptObjSpec ==> PublicationExceptTableObjSpec
    >
    > IIUC, in future when "FOR ALL SEQUENCES EXCEPT (SEQUENCES ...)" and/or
    > "FOR ALL SCHEMAS EXCEPT (SCHEMA ...)" are implemented then these names
    > won't be much good anymore, so I thought they should be made
    > table-specific now to avoid churning them later.
    
    I think if/when we need to further extend this feature, we need to go back
    to the previous approach of using something akin to the
    preprocess_pub_all_objtype_list() kind of function. Consider when we need
    to support EXCEPT (TABLES IN SCHEMA s1, TABLE t1) kind of cases, the
    current code may or may not be the best way to realize it. So, I think for
    now we can leave discussion on this to future extensions of this feature.
    
    --
    With Regards,
    Amit Kapila.
    
  370. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-03-31T04:22:40Z

    On Tue, Mar 31, 2026 at 2:54 PM Hayato Kuroda (Fujitsu)
    <kuroda.hayato@fujitsu.com> wrote:
    >
    > Dear Peter,
    >
    > > ======
    > > src/bin/psql/tab-complete.in.c
    > >
    > > 4.
    > > - COMPLETE_WITH("EXCEPT TABLE (");
    > > + COMPLETE_WITH("EXCEPT ( TABLE");
    > >
    > > (this is in several places in  ALTER and CREATE)
    > >
    > > In v3 the space in "( TABLE" was changed to "(TABLE", but now in v4
    > > the space is back again. AFAICT the v3 change was in response to
    > > review [1] (comment #2). Was it reverted deliberately?
    >
    > I think it's proper to have the blank before/after the parentheses. See the
    > existing example.
    >
    > ```
    >         /* ALTER TABLE <name> SPLIT PARTITION <name> */
    >         else if (Matches("ALTER", "TABLE", MatchAny, "SPLIT", "PARTITION", MatchAny))
    >                 COMPLETE_WITH("INTO ( PARTITION");
    > ```
    >
    > Also, if we type till `CREATE PUBLICATION pub WITH` and type tab several times,
    > the suggested string is something like below:
    >
    > ```
    > CREATE PUBLICATION pub WITH ( PUBLISH
    > ```
    >
    > It means normally the blank exists in between the term.
    >
    
    OK. I didn't really have an opinion about it. I was only pointing out
    it was changed in v3 and then changed again in v4 so I was just
    checking if it was deliberate.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  371. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-03-31T08:45:11Z

    On Mon, Mar 30, 2026 at 8:14 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > The v4 version patch has the changes for the same.
    > The v4 version also addresses the comments provided by Peter at [3]
    > and Amit at [4].
    >
    
    Pushed the latest version.
    
    --
    With Regards,
    Amit Kapila.
    
    
    
    
  372. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-03-31T09:30:30Z

    On Tue, 31 Mar 2026 at 06:00, Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > Hi Vignesh.
    >
    > Some review comments for patch v4-0001.
    >
    > ======
    > doc/src/sgml/ref/alter_publication.sgml
    >
    > 1.
    > -    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    > +    TABLE <replaceable class="parameter">table_object</replaceable> [, ... ]
    > +
    > +<phrase>and <replaceable class="parameter">table_object</replaceable>
    > is:</phrase>
    > +
    > +   [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > With the introduction of 'table_object' I expected that
    > 'table_and_columns' and 'publication_drop_object' would also make use
    > of it. Why not?
    >
    > ======
    > doc/src/sgml/ref/create_publication.sgml
    >
    > 2.
    > -    [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    > +    TABLE <replaceable class="parameter">table_object</replaceable> [, ... ]
    > +
    > +<phrase>and <replaceable class="parameter">table_object</replaceable>
    > is:</phrase>
    > +
    > +   [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
    >
    > With the introduction of 'table_object' I expected that
    > 'table_and_columns' would also make use of it. Why not?
    >
    
    Thanks for the comments. This is addressed in the 002 patch attached.
    The 001 patch has the rebased version of the patch at [1] to fix
    Peter's earlier comments at [2].
    
    [1] - https://www.postgresql.org/message-id/CALDaNm0mYT55pjdyN-yY%3D5dSOfMVx307CtP3xYqzWVa0MPRtLQ%40mail.gmail.com
    [2] - https://www.postgresql.org/message-id/CAHut%2BPvw-XNBnW-ymGdWpLxaEime7_EdOihcUheGyZvw73kcgg%40mail.gmail.com
    
    Regards,
    Vignesh-
    
  373. Re: Skipping schema changes in publication

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

    On Tue, Mar 31, 2026 at 8:30 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    ...
    >
    > Thanks for the comments. This is addressed in the 002 patch attached.
    > The 001 patch has the rebased version of the patch at [1] to fix
    > Peter's earlier comments at [2].
    >
    > [1] - https://www.postgresql.org/message-id/CALDaNm0mYT55pjdyN-yY%3D5dSOfMVx307CtP3xYqzWVa0MPRtLQ%40mail.gmail.com
    > [2] - https://www.postgresql.org/message-id/CAHut%2BPvw-XNBnW-ymGdWpLxaEime7_EdOihcUheGyZvw73kcgg%40mail.gmail.com
    >
    
    Hi Vignesh.
    
    Thanks for following up on my outstanding review comments.
    
    //////
    Patch 0001 - Fix few issues in commit fd366065e0
    
    This did not address the review comment 3a about the redundant macro
    '_("\"%s\"")', but that one might be better handled in a separate
    thread anyway. I can post a separate thread for that one later.
    
    So, patch 0001 LGTM.
    
    //////
    Patch 0002 - Reuse table_object in publication command syntax
    
    LGTM.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  374. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-04-01T02:15:38Z

    On Wed, Apr 1, 2026 at 6:13 AM Peter Smith <smithpb2250@gmail.com> wrote:
    >
    > On Tue, Mar 31, 2026 at 8:30 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > ...
    > >
    > > Thanks for the comments. This is addressed in the 002 patch attached.
    > > The 001 patch has the rebased version of the patch at [1] to fix
    > > Peter's earlier comments at [2].
    > >
    > > [1] - https://www.postgresql.org/message-id/CALDaNm0mYT55pjdyN-yY%3D5dSOfMVx307CtP3xYqzWVa0MPRtLQ%40mail.gmail.com
    > > [2] - https://www.postgresql.org/message-id/CAHut%2BPvw-XNBnW-ymGdWpLxaEime7_EdOihcUheGyZvw73kcgg%40mail.gmail.com
    > >
    >
    > Hi Vignesh.
    >
    > Thanks for following up on my outstanding review comments.
    >
    > //////
    > Patch 0001 - Fix few issues in commit fd366065e0
    >
    > This did not address the review comment 3a about the redundant macro
    > '_("\"%s\"")', but that one might be better handled in a separate
    > thread anyway. I can post a separate thread for that one later.
    >
    
    Yes, as it uses an existing code as an example, it is better to start
    a separate thread. I was planning to do some research by myself after
    finishing the pending tasks here but feel free to start if you have
    formed your thoughts on it.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  375. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-04-01T03:55:53Z

    On Wed, Apr 1, 2026 at 1:15 PM Amit Kapila <amit.kapila16@gmail.com> wrote:
    >
    > On Wed, Apr 1, 2026 at 6:13 AM Peter Smith <smithpb2250@gmail.com> wrote:
    > >
    ...
    > > Hi Vignesh.
    > >
    > > Thanks for following up on my outstanding review comments.
    > >
    > > //////
    > > Patch 0001 - Fix few issues in commit fd366065e0
    > >
    > > This did not address the review comment 3a about the redundant macro
    > > '_("\"%s\"")', but that one might be better handled in a separate
    > > thread anyway. I can post a separate thread for that one later.
    > >
    >
    > Yes, as it uses an existing code as an example, it is better to start
    > a separate thread. I was planning to do some research by myself after
    > finishing the pending tasks here but feel free to start if you have
    > formed your thoughts on it.
    >
    
    New thread for this is here [1].
    
    ======
    [1] https://www.postgresql.org/message-id/CAHut%2BPvp7jYcaiZ3pXedXgLcWZWDBLXFUK05JtZpGv3Mj%3DUOjw%40mail.gmail.com
    
    Kind Regards,
    Peter Smith.
    Fujitsu Australia.
    
    
    
    
  376. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-04-20T12:13:15Z

    Hi,
    
    When changing a table to UNLOGGED, tables that appear in publications
    via EXCEPT clauses (prexcept = true) are currently allowed, but their
    entries remain in pg_publication_rel.
    
    For example:
    postgres=# create table t1(c1 int);
    CREATE TABLE
    postgres=# create publication pub1 for all tables except (table t1);
    CREATE PUBLICATION
    postgres=# alter table t1 set unlogged;
    ALTER TABLE
    postgres=# \d t1
                Unlogged table "public.t1"
     Column |  Type   | Collation | Nullable | Default
    --------+---------+-----------+----------+---------
     c1     | integer |           |          |
    Except publications:
        "pub1"
    
    Since UNLOGGED tables are not supported in publications, this leaves
    stale catalog entries. This patch removes such entries from
    pg_publication_rel when the table is changed to UNLOGGED, and emits a
    NOTICE to inform the user.
    
    Another option considered was to throw an error when setting such
    tables to UNLOGGED. However, allowing the operation was preferred,
    since UNLOGGED tables do not generate WAL and are not replicated
    anyway, so blocking the operation would be unnecessarily restrictive.
    
    Attached patch has the changes for the same.
    
    Thoughts?
    
    Regards,
    Vignesh
    
  377. Re: Skipping schema changes in publication

    Peter Smith <smithpb2250@gmail.com> — 2026-04-22T02:58:08Z

    On Mon, Apr 20, 2026 at 10:13 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Hi,
    >
    > When changing a table to UNLOGGED, tables that appear in publications
    > via EXCEPT clauses (prexcept = true) are currently allowed, but their
    > entries remain in pg_publication_rel.
    >
    > For example:
    > postgres=# create table t1(c1 int);
    > CREATE TABLE
    > postgres=# create publication pub1 for all tables except (table t1);
    > CREATE PUBLICATION
    > postgres=# alter table t1 set unlogged;
    > ALTER TABLE
    > postgres=# \d t1
    >             Unlogged table "public.t1"
    >  Column |  Type   | Collation | Nullable | Default
    > --------+---------+-----------+----------+---------
    >  c1     | integer |           |          |
    > Except publications:
    >     "pub1"
    >
    > Since UNLOGGED tables are not supported in publications, this leaves
    > stale catalog entries. This patch removes such entries from
    > pg_publication_rel when the table is changed to UNLOGGED, and emits a
    > NOTICE to inform the user.
    >
    > Another option considered was to throw an error when setting such
    > tables to UNLOGGED. However, allowing the operation was preferred,
    > since UNLOGGED tables do not generate WAL and are not replicated
    > anyway, so blocking the operation would be unnecessarily restrictive.
    >
    > Attached patch has the changes for the same.
    >
    > Thoughts?
    >
    
    Hi Vignesh -
    
    A couple of review comments for patch v1-0001.
    
    ======
    Background: I found when altering a published table from LOGGED to UNLOGGED...
    
    - If it was published by "FOR ALL TABLES" then it just becomes
    unpublished *silently*.
    - Ditto if it was published by "FOR TABLES IN SCHEMA"
    - It seems you only got an error on ALTER LOGGED->UNLOGGED when the
    published table was specifically published by "FOR TABLE".
    
    ======
    src/backend/commands/tablecmds.c
    
    1.
    + /* Find all publications associated with the relation. */
    + pubrellist = SearchSysCacheList1(PUBLICATIONRELMAP,
    + ObjectIdGetDatum(RelationGetRelid(rel)));
    
    This comment seems misleading. IIUC, it's only going to find relations
    specifically mentioned by name in the command. Any relations that are
    associated with the publication due to "FOR ALL TABLES" or "FOR TABLES
    IN SCHEMA" are not known.
    
    ~~~
    
    2.
    + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    + errmsg("cannot change table \"%s\" to unlogged because it is part of
    a publication",
    + RelationGetRelationName(rel)),
    + errdetail("Unlogged relations cannot be replicated.")))
    
    This does not seem like a very good error message in the first place
    (yes, I know this patch did not change it).
    
    e.g.
    
    a) It says it is "part of a publication", but it does not tell the
    user which ones. Maybe list them in the message or provide a HINT to
    say use \d to show them.
    
    b) It mixes terminology, first calling it a 'table', and then calling
    it a 'relation'
    
    ~~~
    
    3.
    + ereport(NOTICE,
    + errmsg("relation \"%s\" removed from publication \"%s\" due to being
    changed to UNLOGGED",
    +    RelationGetRelationName(rel), pubname));
    
    After some consideration, I think that this really should be an ERROR
    too, not just a NOTICE.
    
    Otherwise, if the user toggled the excluded table from
    LOGGED->UNLOGGED->LOGGED, then the same table would go from being
    excluded to being included! The consequences of publishing something
    that was originally *specifically* excluded by the user might be very
    bad, so IMO it's better to prevent that from happening -- i.e. sharing
    the same ERROR code that was there before.
    
    ======
    Kind Regards,
    Peter Smith.
    Fujitsu Australia
    
    
    
    
  378. Re: Skipping schema changes in publication

    shveta malik <shveta.malik@gmail.com> — 2026-04-22T09:54:28Z

    On Mon, Apr 20, 2026 at 5:43 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > Hi,
    >
    > When changing a table to UNLOGGED, tables that appear in publications
    > via EXCEPT clauses (prexcept = true) are currently allowed, but their
    > entries remain in pg_publication_rel.
    >
    > For example:
    > postgres=# create table t1(c1 int);
    > CREATE TABLE
    > postgres=# create publication pub1 for all tables except (table t1);
    > CREATE PUBLICATION
    > postgres=# alter table t1 set unlogged;
    > ALTER TABLE
    > postgres=# \d t1
    >             Unlogged table "public.t1"
    >  Column |  Type   | Collation | Nullable | Default
    > --------+---------+-----------+----------+---------
    >  c1     | integer |           |          |
    > Except publications:
    >     "pub1"
    >
    > Since UNLOGGED tables are not supported in publications, this leaves
    > stale catalog entries. This patch removes such entries from
    > pg_publication_rel when the table is changed to UNLOGGED, and emits a
    > NOTICE to inform the user.
    >
    > Another option considered was to throw an error when setting such
    > tables to UNLOGGED. However, allowing the operation was preferred,
    > since UNLOGGED tables do not generate WAL and are not replicated
    > anyway, so blocking the operation would be unnecessarily restrictive.
    >
    > Attached patch has the changes for the same.
    >
    
    The main concern of removal table form EXCEPT-list is that once the
    table is changed back to LOGGED, there is no internal way to add it to
    the EXCEPT list again.
    
    OTOH, raising an ERROR does not seem like a good solution either. When
    a user converts a table to UNLOGGED, they are effectively excluding it
    from publications. Therefore, throwing an error for the purpose that
    "table is part of EXCEPT, cannot convert it to UNLOGGED" does not
    appear appropriate, since both actions ultimately exclude the table
    from publication. I think we can keep the current behavior unchanged
    as it causes no harm.
    
    Alternatively, if we want to remove the table from the EXCEPT list:
    1) may be a WARNING is better?
    2) the error message can be improved; the current one ([1]) seems uninformative.
    3) And we can also add DETAIL saying add it to EXCEPT list manually if
    needed later(something on this line).
    
    Thoughts?
    
    [1]:
    + ereport(NOTICE,
    + errmsg("relation \"%s\" removed from publication \"%s\" due to being
    changed to UNLOGGED",
    +    RelationGetRelationName(rel), pubname));
    
    thanks
    Shveta
    
    
    
    
  379. RE: Skipping schema changes in publication

    Zhijie Hou (Fujitsu) <houzj.fnst@fujitsu.com> — 2026-04-22T10:02:56Z

    On Wednesday, April 22, 2026 5:54 PM shveta malik <shveta.malik@gmail.com> wrote:
    > On Mon, Apr 20, 2026 at 5:43 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > When changing a table to UNLOGGED, tables that appear in publications
    > > via EXCEPT clauses (prexcept = true) are currently allowed, but their
    > > entries remain in pg_publication_rel.
    > >
    > > For example:
    > > postgres=# create table t1(c1 int);
    > > CREATE TABLE
    > > postgres=# create publication pub1 for all tables except (table t1);
    > > CREATE PUBLICATION postgres=# alter table t1 set unlogged; ALTER TABLE
    > > postgres=# \d t1
    > >             Unlogged table "public.t1"
    > >  Column |  Type   | Collation | Nullable | Default
    > > --------+---------+-----------+----------+---------
    > >  c1     | integer |           |          |
    > > Except publications:
    > >     "pub1"
    > >
    > > Since UNLOGGED tables are not supported in publications, this leaves
    > > stale catalog entries. This patch removes such entries from
    > > pg_publication_rel when the table is changed to UNLOGGED, and emits a
    > > NOTICE to inform the user.
    > >
    > > Another option considered was to throw an error when setting such
    > > tables to UNLOGGED. However, allowing the operation was preferred,
    > > since UNLOGGED tables do not generate WAL and are not replicated
    > > anyway, so blocking the operation would be unnecessarily restrictive.
    > >
    > > Attached patch has the changes for the same.
    > >
    > 
    > The main concern of removal table form EXCEPT-list is that once the table is
    > changed back to LOGGED, there is no internal way to add it to the EXCEPT list
    > again.
    > 
    > OTOH, raising an ERROR does not seem like a good solution either. When a
    > user converts a table to UNLOGGED, they are effectively excluding it from
    > publications. Therefore, throwing an error for the purpose that "table is part
    > of EXCEPT, cannot convert it to UNLOGGED" does not appear appropriate,
    > since both actions ultimately exclude the table from publication. I think we
    > can keep the current behavior unchanged as it causes no harm.
    
    I also personally prefer keeping the current behavior, as there is no harm in
    doing so, and I'm not sure whether reporting one more ERROR would make users
    happy.
    
    Best Regards,
    Hou zj
    
    
  380. Re: Skipping schema changes in publication

    Amit Kapila <amit.kapila16@gmail.com> — 2026-04-22T11:32:01Z

    On Wed, Apr 22, 2026 at 3:33 PM Zhijie Hou (Fujitsu)
    <houzj.fnst@fujitsu.com> wrote:
    >
    > On Wednesday, April 22, 2026 5:54 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > On Mon, Apr 20, 2026 at 5:43 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > When changing a table to UNLOGGED, tables that appear in publications
    > > > via EXCEPT clauses (prexcept = true) are currently allowed, but their
    > > > entries remain in pg_publication_rel.
    > > >
    > > > For example:
    > > > postgres=# create table t1(c1 int);
    > > > CREATE TABLE
    > > > postgres=# create publication pub1 for all tables except (table t1);
    > > > CREATE PUBLICATION postgres=# alter table t1 set unlogged; ALTER TABLE
    > > > postgres=# \d t1
    > > >             Unlogged table "public.t1"
    > > >  Column |  Type   | Collation | Nullable | Default
    > > > --------+---------+-----------+----------+---------
    > > >  c1     | integer |           |          |
    > > > Except publications:
    > > >     "pub1"
    > > >
    > > > Since UNLOGGED tables are not supported in publications, this leaves
    > > > stale catalog entries. This patch removes such entries from
    > > > pg_publication_rel when the table is changed to UNLOGGED, and emits a
    > > > NOTICE to inform the user.
    > > >
    > > > Another option considered was to throw an error when setting such
    > > > tables to UNLOGGED. However, allowing the operation was preferred,
    > > > since UNLOGGED tables do not generate WAL and are not replicated
    > > > anyway, so blocking the operation would be unnecessarily restrictive.
    > > >
    > > > Attached patch has the changes for the same.
    > > >
    > >
    > > The main concern of removal table form EXCEPT-list is that once the table is
    > > changed back to LOGGED, there is no internal way to add it to the EXCEPT list
    > > again.
    > >
    > > OTOH, raising an ERROR does not seem like a good solution either. When a
    > > user converts a table to UNLOGGED, they are effectively excluding it from
    > > publications. Therefore, throwing an error for the purpose that "table is part
    > > of EXCEPT, cannot convert it to UNLOGGED" does not appear appropriate,
    > > since both actions ultimately exclude the table from publication. I think we
    > > can keep the current behavior unchanged as it causes no harm.
    >
    > I also personally prefer keeping the current behavior, as there is no harm in
    > doing so, and I'm not sure whether reporting one more ERROR would make users
    > happy.
    >
    
    +1.
    
    -- 
    With Regards,
    Amit Kapila.
    
    
    
    
  381. Re: Skipping schema changes in publication

    vignesh C <vignesh21@gmail.com> — 2026-04-23T04:50:08Z

    On Wed, 22 Apr 2026 at 15:33, Zhijie Hou (Fujitsu)
    <houzj.fnst@fujitsu.com> wrote:
    >
    > On Wednesday, April 22, 2026 5:54 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > On Mon, Apr 20, 2026 at 5:43 PM vignesh C <vignesh21@gmail.com> wrote:
    > > >
    > > > Hi,
    > > >
    > > > When changing a table to UNLOGGED, tables that appear in publications
    > > > via EXCEPT clauses (prexcept = true) are currently allowed, but their
    > > > entries remain in pg_publication_rel.
    > > >
    > > > For example:
    > > > postgres=# create table t1(c1 int);
    > > > CREATE TABLE
    > > > postgres=# create publication pub1 for all tables except (table t1);
    > > > CREATE PUBLICATION postgres=# alter table t1 set unlogged; ALTER TABLE
    > > > postgres=# \d t1
    > > >             Unlogged table "public.t1"
    > > >  Column |  Type   | Collation | Nullable | Default
    > > > --------+---------+-----------+----------+---------
    > > >  c1     | integer |           |          |
    > > > Except publications:
    > > >     "pub1"
    > > >
    > > > Since UNLOGGED tables are not supported in publications, this leaves
    > > > stale catalog entries. This patch removes such entries from
    > > > pg_publication_rel when the table is changed to UNLOGGED, and emits a
    > > > NOTICE to inform the user.
    > > >
    > > > Another option considered was to throw an error when setting such
    > > > tables to UNLOGGED. However, allowing the operation was preferred,
    > > > since UNLOGGED tables do not generate WAL and are not replicated
    > > > anyway, so blocking the operation would be unnecessarily restrictive.
    > > >
    > > > Attached patch has the changes for the same.
    > > >
    > >
    > > The main concern of removal table form EXCEPT-list is that once the table is
    > > changed back to LOGGED, there is no internal way to add it to the EXCEPT list
    > > again.
    > >
    > > OTOH, raising an ERROR does not seem like a good solution either. When a
    > > user converts a table to UNLOGGED, they are effectively excluding it from
    > > publications. Therefore, throwing an error for the purpose that "table is part
    > > of EXCEPT, cannot convert it to UNLOGGED" does not appear appropriate,
    > > since both actions ultimately exclude the table from publication. I think we
    > > can keep the current behavior unchanged as it causes no harm.
    >
    > I also personally prefer keeping the current behavior, as there is no harm in
    > doing so, and I'm not sure whether reporting one more ERROR would make users
    > happy.
    
    Thanks Hou-san, Amit, and Shveta for your inputs. Based on the
    feedback, let's keep the current behavior unchanged
    
    Regards,
    Vignesh