Thread

  1. Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-04-22T19:31:18Z

    Hi,
    
    I came across a race condition in pg_get_publication_tables with
    concurrent DROP TABLE. pg_get_publication_tables collects table OIDs
    without locks on the first call, then opens each table on later calls.
    If a table is dropped in between, the function errors with "could not
    open relation with OID".
    
    This is common in environments where many tables are being created and
    dropped while pg_publication_tables is queried, such as with FOR ALL
    TABLES publications.
    
    Please find the attached patch that fixes this by skipping
    concurrently dropped tables instead of erroring out. Tables created
    after the list is built are simply not present in the result set,
    which is expected point-in-time behavior with no error. I've also
    added a TAP test for this issue.
    
    Thoughts?
    
    --
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
  2. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    shveta malik <shveta.malik@gmail.com> — 2026-04-23T11:15:11Z

    On Thu, Apr 23, 2026 at 1:01 AM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > Hi,
    >
    > I came across a race condition in pg_get_publication_tables with
    > concurrent DROP TABLE. pg_get_publication_tables collects table OIDs
    > without locks on the first call, then opens each table on later calls.
    > If a table is dropped in between, the function errors with "could not
    > open relation with OID".
    >
    
    I agree with the problem statement, this is a weird error:
    
    postgres=# select * from pg_publication_tables;
    ERROR:  could not open relation with OID 16390
    
    > This is common in environments where many tables are being created and
    > dropped while pg_publication_tables is queried, such as with FOR ALL
    > TABLES publications.
    > Please find the attached patch that fixes this by skipping
    > concurrently dropped tables instead of erroring out. Tables created
    > after the list is built are simply not present in the result set,
    > which is expected point-in-time behavior with no error.
    
    I too think that this should be fixed by skipping the dropped table.
    Will reveiw patch soon.
    
    thanks
    Shveta
    
    
    
    
  3. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

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

    On Thu, Apr 23, 2026 at 4:45 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Thu, Apr 23, 2026 at 1:01 AM Bharath Rupireddy
    > <bharath.rupireddyforpostgres@gmail.com> wrote:
    > >
    > > Hi,
    > >
    > > I came across a race condition in pg_get_publication_tables with
    > > concurrent DROP TABLE. pg_get_publication_tables collects table OIDs
    > > without locks on the first call, then opens each table on later calls.
    > > If a table is dropped in between, the function errors with "could not
    > > open relation with OID".
    > >
    >
    > I agree with the problem statement, this is a weird error:
    >
    > postgres=# select * from pg_publication_tables;
    > ERROR:  could not open relation with OID 16390
    >
    > > This is common in environments where many tables are being created and
    > > dropped while pg_publication_tables is queried, such as with FOR ALL
    > > TABLES publications.
    > > Please find the attached patch that fixes this by skipping
    > > concurrently dropped tables instead of erroring out. Tables created
    > > after the list is built are simply not present in the result set,
    > > which is expected point-in-time behavior with no error.
    >
    > I too think that this should be fixed by skipping the dropped table.
    > Will reveiw patch soon.
    >
    
    Bharath, I reviewed the patch. I personally think that manually
    incrementing the call counter of SRF (funcctx->call_cntr++) in
    pg_get_publication_tables() is not a good idea. I think these are
    read-only for us and any changes to SRF fields must use SRF macros.
    
    I tried to find if any other code-part does that, found one refernce
    in hstore_svals():
    
    /* ugly ugly ugly. why no macro for this? */
    (funcctx)->call_cntr++;
    
    Having said that, I could not find any other way to implement the fix
    also. Did you try exploring 'SRF_RETURN_NEXT_NULL' in this case? I am
    not very sure about this as well, as it will end up retruning NULL and
    may impact output and its usage in tablesync too.
    
    Let's see what others have to say on this fix.
    
    thanks
    Shveta
    
    
    
    
  4. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-04-24T06:21:25Z

    Hi,
    
    On Fri, Apr 24, 2026 at 11:19:40AM +0530, shveta malik wrote:
    > On Thu, Apr 23, 2026 at 4:45 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Thu, Apr 23, 2026 at 1:01 AM Bharath Rupireddy
    > > <bharath.rupireddyforpostgres@gmail.com> wrote:
    > > >
    > I tried to find if any other code-part does that, found one refernce
    > in hstore_svals():
    > 
    > /* ugly ugly ugly. why no macro for this? */
    > (funcctx)->call_cntr++;
    > 
    > Having said that, I could not find any other way to implement the fix
    > also.
    
    What about introducing a publication_tables_state struct stored in user_fctx
    that carries both the list and a private position index? (kind of what 
    pg_timezone_abbrevs_zone() is doing).
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  5. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    shveta malik <shveta.malik@gmail.com> — 2026-04-24T07:17:03Z

    On Fri, Apr 24, 2026 at 11:51 AM Bertrand Drouvot
    <bertranddrouvot.pg@gmail.com> wrote:
    >
    > Hi,
    >
    > On Fri, Apr 24, 2026 at 11:19:40AM +0530, shveta malik wrote:
    > > On Thu, Apr 23, 2026 at 4:45 PM shveta malik <shveta.malik@gmail.com> wrote:
    > > >
    > > > On Thu, Apr 23, 2026 at 1:01 AM Bharath Rupireddy
    > > > <bharath.rupireddyforpostgres@gmail.com> wrote:
    > > > >
    > > I tried to find if any other code-part does that, found one refernce
    > > in hstore_svals():
    > >
    > > /* ugly ugly ugly. why no macro for this? */
    > > (funcctx)->call_cntr++;
    > >
    > > Having said that, I could not find any other way to implement the fix
    > > also.
    >
    > What about introducing a publication_tables_state struct stored in user_fctx
    > that carries both the list and a private position index? (kind of what
    > pg_timezone_abbrevs_zone() is doing).
    >
    
    Yeah, that is a good idea. Seems doable.
    
    thanks
    Shveta
    
    
    
    
  6. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-04-24T20:20:56Z

    Hi,
    
    On Fri, Apr 24, 2026 at 12:17 AM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > > What about introducing a publication_tables_state struct stored in user_fctx
    > > that carries both the list and a private position index? (kind of what
    > > pg_timezone_abbrevs_zone() is doing).
    >
    > Yeah, that is a good idea. Seems doable.
    
    +1. Thanks for the pointer. Adding a new struct to carry both the
    table_infos and the current index into it seems simple with a smaller
    diff.
    
    If I were to think of another approach (I don't prefer this approach
    anyway), we could convert pg_get_publication_tables from the current
    value-per-call SRF function (SRF_IS_FIRSTCALL + SRF_RETURN_NEXT) to a
    materialized SRF function (InitMaterializedSRF +
    tuplestore_putvalues). With a materialized SRF function, there is no
    need to add a new structure or maintain per-call context - table_infos
    becomes a local variable, and we skip placing anything related to
    dropped tables into the tuplestore immediately in the second loop (the
    first loop remains the same, preparing the table_infos list). This
    approach seems more complex with a larger diff and requires use of
    InitMaterializedSRF for versions >= PG16, SetSingleFuncCall for PG15,
    and for PG14 requires inlining SetSingleFuncCall.
    
    I prefer adding the new struct to carry both table_infos and the
    current index into it with the current value-per-call SRF function,
    unless others have better ideas. If okay, I will send a new patch
    soon. Thank you!
    
    --
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  7. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

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

    On Sat, Apr 25, 2026 at 1:51 AM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > Hi,
    >
    > On Fri, Apr 24, 2026 at 12:17 AM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > > What about introducing a publication_tables_state struct stored in user_fctx
    > > > that carries both the list and a private position index? (kind of what
    > > > pg_timezone_abbrevs_zone() is doing).
    > >
    > > Yeah, that is a good idea. Seems doable.
    >
    > +1. Thanks for the pointer. Adding a new struct to carry both the
    > table_infos and the current index into it seems simple with a smaller
    > diff.
    >
    > If I were to think of another approach (I don't prefer this approach
    > anyway), we could convert pg_get_publication_tables from the current
    > value-per-call SRF function (SRF_IS_FIRSTCALL + SRF_RETURN_NEXT) to a
    > materialized SRF function (InitMaterializedSRF +
    > tuplestore_putvalues). With a materialized SRF function, there is no
    > need to add a new structure or maintain per-call context - table_infos
    > becomes a local variable, and we skip placing anything related to
    > dropped tables into the tuplestore immediately in the second loop (the
    > first loop remains the same, preparing the table_infos list). This
    > approach seems more complex with a larger diff and requires use of
    > InitMaterializedSRF for versions >= PG16, SetSingleFuncCall for PG15,
    > and for PG14 requires inlining SetSingleFuncCall.
    >
    > I prefer adding the new struct to carry both table_infos and the
    > current index into it with the current value-per-call SRF function,
    > unless others have better ideas.
    
    +1. It is simpler than the Materialization concept.
    
    > If okay, I will send a new patch
    > soon. Thank you!
    >
    
    Sure, Thanks!
    
    thanks
    Shveta
    
    
    
    
  8. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-04-27T08:36:00Z

    Hi,
    
    On Sun, Apr 26, 2026 at 8:45 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > > I prefer adding the new struct to carry both table_infos and the
    > > current index into it with the current value-per-call SRF function,
    > > unless others have better ideas.
    >
    > +1. It is simpler than the Materialization concept.
    >
    > > If okay, I will send a new patch
    > > soon. Thank you!
    >
    > Sure, Thanks!
    
    Attached v2 patch. I also refactored the test a bit. Please review. Thank you!
    
    I added this to the current CF: https://commitfest.postgresql.org/patch/6715/.
    
    -- 
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
  9. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-04-27T10:01:17Z

    Hi,
    
    On Mon, Apr 27, 2026 at 01:36:00AM -0700, Bharath Rupireddy wrote:
    > Hi,
    > 
    > On Sun, Apr 26, 2026 at 8:45 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > > I prefer adding the new struct to carry both table_infos and the
    > > > current index into it with the current value-per-call SRF function,
    > > > unless others have better ideas.
    > >
    > > +1. It is simpler than the Materialization concept.
    > >
    > > > If okay, I will send a new patch
    > > > soon. Thank you!
    > >
    > > Sure, Thanks!
    > 
    > Attached v2 patch. I also refactored the test a bit. Please review. Thank you!
    
    Thanks!
    
    I've 2 comments:
    
    1/ What about having just one curr_idx increment? (right after list_nth(),
    before the skip checks). I think that would be less error-prone if new skip
    conditions are added later.
    
    2/ I think that the test is racy and could also succeed even without the fixes.
    Indeed, I think that the drops can complete before any concurrent polling
    happens (I can see it by adding a pg_sleep(2) before the first poll in the DO
    block). What about using an injection point to ensure a relation is removed
    during the polling?
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  10. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-04-28T01:40:00Z

    Hi,
    
    On Mon, Apr 27, 2026 at 3:01 AM Bertrand Drouvot
    <bertranddrouvot.pg@gmail.com> wrote:
    >
    > I've 2 comments:
    
    Thank you for reviewing!
    
    > 1/ What about having just one curr_idx increment? (right after list_nth(),
    > before the skip checks). I think that would be less error-prone if new skip
    > conditions are added later.
    
    Done.
    
    > 2/ I think that the test is racy and could also succeed even without the fixes.
    > Indeed, I think that the drops can complete before any concurrent polling
    > happens (I can see it by adding a pg_sleep(2) before the first poll in the DO
    > block). What about using an injection point to ensure a relation is removed
    > during the polling?
    
    I initially considered an injection point but chose polling since the
    TAP test reproduced the bug consistently with hundreds of tables on my
    dev system. I've now added an injection point for predictability.
    
    I adjusted the commit message a bit. Please find the attached v3 patch
    for further review. Thank you!
    
    --
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
  11. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    shveta malik <shveta.malik@gmail.com> — 2026-04-28T03:59:39Z

    On Tue, Apr 28, 2026 at 7:11 AM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > Hi,
    >
    > On Mon, Apr 27, 2026 at 3:01 AM Bertrand Drouvot
    > <bertranddrouvot.pg@gmail.com> wrote:
    > >
    > > I've 2 comments:
    >
    > Thank you for reviewing!
    >
    > > 1/ What about having just one curr_idx increment? (right after list_nth(),
    > > before the skip checks). I think that would be less error-prone if new skip
    > > conditions are added later.
    >
    > Done.
    >
    > > 2/ I think that the test is racy and could also succeed even without the fixes.
    > > Indeed, I think that the drops can complete before any concurrent polling
    > > happens (I can see it by adding a pg_sleep(2) before the first poll in the DO
    > > block). What about using an injection point to ensure a relation is removed
    > > during the polling?
    >
    > I initially considered an injection point but chose polling since the
    > TAP test reproduced the bug consistently with hundreds of tables on my
    > dev system. I've now added an injection point for predictability.
    >
    > I adjusted the commit message a bit. Please find the attached v3 patch
    > for further review. Thank you!
    >
    
    Thanks Bharath. I have just one minor comment:
    
    + INJECTION_POINT("pg-get-publication-tables-build-list", NULL);
    
    Shall we name it as 'pg-get-publication-tables-list-built' to be more
    meaningful, as we are pausing after list is built.
    
    thanks
    Shveta
    
    
    
    
  12. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-04-28T04:21:54Z

    Hi,
    
    On Mon, Apr 27, 2026 at 8:59 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > > I initially considered an injection point but chose polling since the
    > > TAP test reproduced the bug consistently with hundreds of tables on my
    > > dev system. I've now added an injection point for predictability.
    > >
    > > I adjusted the commit message a bit. Please find the attached v3 patch
    > > for further review. Thank you!
    > >
    >
    > Thanks Bharath. I have just one minor comment:
    >
    > + INJECTION_POINT("pg-get-publication-tables-build-list", NULL);
    >
    > Shall we name it as 'pg-get-publication-tables-list-built' to be more
    > meaningful, as we are pausing after list is built.
    
    Sure. How about pg-get-publication-tables-after-list-built? It's 42
    bytes, under the INJ_NAME_MAXLEN of 64 bytes, but meaningful.
    
    --
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  13. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-04-28T05:54:47Z

    Hi,
    
    On Mon, Apr 27, 2026 at 09:21:54PM -0700, Bharath Rupireddy wrote:
    > Hi,
    > 
    > On Mon, Apr 27, 2026 at 8:59 PM shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > > I initially considered an injection point but chose polling since the
    > > > TAP test reproduced the bug consistently with hundreds of tables on my
    > > > dev system. I've now added an injection point for predictability.
    > > >
    > > > I adjusted the commit message a bit. Please find the attached v3 patch
    > > > for further review. Thank you!
    
    Thanks for the new version!
    
    > > Thanks Bharath. I have just one minor comment:
    > >
    > > + INJECTION_POINT("pg-get-publication-tables-build-list", NULL);
    > >
    > > Shall we name it as 'pg-get-publication-tables-list-built' to be more
    > > meaningful, as we are pausing after list is built.
    > 
    > Sure. How about pg-get-publication-tables-after-list-built? It's 42
    > bytes, under the INJ_NAME_MAXLEN of 64 bytes, but meaningful.
    
    That looks ok to me.
    
    Just still 2 minor comments:
    
    1/ 
    +       # Drop the table while the SRF is paused.
    
    s/the SRF/ pg_get_publication_tables()/ ?
    
    2/
    
    +# pg_get_publication_tables race with concurrent DROP TABLE.
    
    What about describing a bit more the bug? The other tests in 100_bugs.pl describe
    the bug they're testing.
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  14. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-04-28T14:17:00Z

    Hi,
    
    On Mon, Apr 27, 2026 at 10:54 PM Bertrand Drouvot
    <bertranddrouvot.pg@gmail.com> wrote:
    >
    > > Sure. How about pg-get-publication-tables-after-list-built? It's 42
    > > bytes, under the INJ_NAME_MAXLEN of 64 bytes, but meaningful.
    >
    > That looks ok to me.
    >
    > Just still 2 minor comments:
    >
    > 1/
    > +       # Drop the table while the SRF is paused.
    >
    > s/the SRF/ pg_get_publication_tables()/ ?
    >
    > 2/
    >
    > +# pg_get_publication_tables race with concurrent DROP TABLE.
    >
    > What about describing a bit more the bug? The other tests in 100_bugs.pl describe
    > the bug they're testing.
    
    Please find the attached v4 patch. Thank yoU!
    
    -- 
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
  15. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-04-28T15:28:06Z

    Hi,
    
    On Tue, Apr 28, 2026 at 07:17:00AM -0700, Bharath Rupireddy wrote:
    > Hi,
    > 
    > On Mon, Apr 27, 2026 at 10:54 PM Bertrand Drouvot
    > <bertranddrouvot.pg@gmail.com> wrote:
    > >
    > > > Sure. How about pg-get-publication-tables-after-list-built? It's 42
    > > > bytes, under the INJ_NAME_MAXLEN of 64 bytes, but meaningful.
    > >
    > > That looks ok to me.
    > >
    > > Just still 2 minor comments:
    > >
    > > 1/
    > > +       # Drop the table while the SRF is paused.
    > >
    > > s/the SRF/ pg_get_publication_tables()/ ?
    > >
    > > 2/
    > >
    > > +# pg_get_publication_tables race with concurrent DROP TABLE.
    > >
    > > What about describing a bit more the bug? The other tests in 100_bugs.pl describe
    > > the bug they're testing.
    > 
    > Please find the attached v4 patch. Thank yoU!
    
    Thanks, LGTM!
    
    Regards,
    
    -- 
    Bertrand Drouvot
    PostgreSQL Contributors Team
    RDS Open Source Databases
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  16. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Ajin Cherian <itsajin@gmail.com> — 2026-04-29T01:42:23Z

    On Wed, Apr 29, 2026 at 12:17 AM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    >
    >
    > Please find the attached v4 patch. Thank yoU!
    >
    
    One small comment. The includes need to be in alphabetical order.
    injection_point.h should come after fmgroids.h
    
     #include "utils/fmgroids.h"
     #include "utils/lsyscache.h"
     #include "utils/rel.h"
    +#include "utils/injection_point.h"
     #include "utils/syscache.h"
    
    regards,
    Ajin Cherian
    Fujitsu Australia
    
    
    
    
  17. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

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

    On Wed, Apr 29, 2026 at 7:12 AM Ajin Cherian <itsajin@gmail.com> wrote:
    >
    > On Wed, Apr 29, 2026 at 12:17 AM Bharath Rupireddy
    > <bharath.rupireddyforpostgres@gmail.com> wrote:
    > >
    > >
    > >
    > > Please find the attached v4 patch. Thank yoU!
    > >
    >
    > One small comment. The includes need to be in alphabetical order.
    > injection_point.h should come after fmgroids.h
    >
    >  #include "utils/fmgroids.h"
    >  #include "utils/lsyscache.h"
    >  #include "utils/rel.h"
    > +#include "utils/injection_point.h"
    >  #include "utils/syscache.h"
    >
    
    +1
    
    Also there is a trailing whitespace issue while applying the patch.
    Other than these, the patch looks good.
    
    thanks
    Shveta
    
    
    
    
  18. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-04-29T07:15:00Z

    Hi,
    
    On Tue, Apr 28, 2026 at 8:28 PM shveta malik <shveta.malik@gmail.com> wrote:
    >
    > On Wed, Apr 29, 2026 at 7:12 AM Ajin Cherian <itsajin@gmail.com> wrote:
    >
    > > One small comment. The includes need to be in alphabetical order.
    > > injection_point.h should come after fmgroids.h
    > >
    > > +#include "utils/injection_point.h"
    > >  #include "utils/syscache.h"
    >
    > Also there is a trailing whitespace issue while applying the patch.
    > Other than these, the patch looks good.
    
    Fixed. Please find the attached v5 patch.
    
    The fix is needed only for PG16 and later, not PG15 or PG14. The bug
    was introduced by b7ae03953690 [1] in PG16, which added a table_open()
    call in pg_get_publication_tables(). PG15 and earlier only use
    get_rel_namespace() and syscache lookups, both of which gracefully
    handle dropped relations (returning InvalidOid/false rather than
    erroring).
    
    I verified the bug and the fix on all affected branches. Please find
    the attached version-specific patches for backpatching. Thank you!
    
    [1] b7ae03953690 - Ignore dropped and generated columns from the column list
    
    --
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
  19. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Chao Li <li.evan.chao@gmail.com> — 2026-04-30T03:40:20Z

    
    > On Apr 29, 2026, at 15:15, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote:
    > 
    > Hi,
    > 
    > On Tue, Apr 28, 2026 at 8:28 PM shveta malik <shveta.malik@gmail.com> wrote:
    >> 
    >> On Wed, Apr 29, 2026 at 7:12 AM Ajin Cherian <itsajin@gmail.com> wrote:
    >> 
    >>> One small comment. The includes need to be in alphabetical order.
    >>> injection_point.h should come after fmgroids.h
    >>> 
    >>> +#include "utils/injection_point.h"
    >>> #include "utils/syscache.h"
    >> 
    >> Also there is a trailing whitespace issue while applying the patch.
    >> Other than these, the patch looks good.
    > 
    > Fixed. Please find the attached v5 patch.
    > 
    > The fix is needed only for PG16 and later, not PG15 or PG14. The bug
    > was introduced by b7ae03953690 [1] in PG16, which added a table_open()
    > call in pg_get_publication_tables(). PG15 and earlier only use
    > get_rel_namespace() and syscache lookups, both of which gracefully
    > handle dropped relations (returning InvalidOid/false rather than
    > erroring).
    > 
    > I verified the bug and the fix on all affected branches. Please find
    > the attached version-specific patches for backpatching. Thank you!
    > 
    > [1] b7ae03953690 - Ignore dropped and generated columns from the column list
    > 
    > --
    > Bharath Rupireddy
    > Amazon Web Services: https://aws.amazon.com
    > <v5-0001-PG16-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-Fix-pg_get_publication_tables-race-with-concurren.patch><v5-0001-PG18-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-PG17-Fix-pg_get_publication_tables-race-with-conc.txt>
    
    I am afraid this is only a partial fix.
    
    ```
    @@ -1599,12 +1621,18 @@ pg_get_publication_tables(FunctionCallInfo fcinfo, ArrayType *pubnames,
     		/* Show all columns when the column list is not specified. */
     		if (nulls[2])
     		{
    -			Relation	rel = table_open(relid, AccessShareLock);
    +			Relation	rel = try_table_open(relid, AccessShareLock);
     			int			nattnums = 0;
     			int16	   *attnums;
    -			TupleDesc	desc = RelationGetDescr(rel);
    +			TupleDesc	desc;
     			int			i;
     
    +			/* Skip if the relation has been concurrently dropped. */
    +			if (rel == NULL)
    +				continue;
    ```
    
    This change uses try_table_open() to detect whether a table has been dropped, but try_table_open() is only called when the column list is not specified. If a table is included in the publication with an explicit column list, then even if it is dropped concurrently, it may still be returned by pg_get_publication_tables().
    
    So this patch removes the “could not open relation with OID” error, but it does not fully ensure the accuracy of the returned table list. It also introduces inconsistent behavior between tables published with and without column lists.
    
    To resolve the race condition completely, I think we should try to open the table regardless of whether a column list is specified.
    
    Best regards,
    --
    Chao Li (Evan)
    HighGo Software Co., Ltd.
    https://www.highgo.com/
    
    
    
    
    
    
    
    
  20. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-05-01T01:30:00Z

    Hi,
    
    On Wed, Apr 29, 2026 at 8:41 PM Chao Li <li.evan.chao@gmail.com> wrote:
    >
    > > <v5-0001-PG16-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-Fix-pg_get_publication_tables-race-with-concurren.patch><v5-0001-PG18-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-PG17-Fix-pg_get_publication_tables-race-with-conc.txt>
    >
    > I am afraid this is only a partial fix.
    
    Thanks for reviewing it. Please find my responses below.
    
    > ```
    > @@ -1599,12 +1621,18 @@ pg_get_publication_tables(FunctionCallInfo fcinfo, ArrayType *pubnames,
    >                 /* Show all columns when the column list is not specified. */
    >                 if (nulls[2])
    >                 {
    > -                       Relation        rel = table_open(relid, AccessShareLock);
    > +                       Relation        rel = try_table_open(relid, AccessShareLock);
    >                         int                     nattnums = 0;
    >                         int16      *attnums;
    > -                       TupleDesc       desc = RelationGetDescr(rel);
    > +                       TupleDesc       desc;
    >                         int                     i;
    >
    > +                       /* Skip if the relation has been concurrently dropped. */
    > +                       if (rel == NULL)
    > +                               continue;
    > ```
    >
    > This change uses try_table_open() to detect whether a table has been dropped, but try_table_open() is only called when the column list is not specified. If a table is included in the publication with an explicit column list, then even if it is dropped concurrently, it may still be returned by pg_get_publication_tables().
    
    Right. The try_table_open() is only needed there because that's the
    only code path that actually opens the relation (to enumerate its
    columns). The column list path reads from the pg_publication_rel
    catalog - it never calls table_open(), so it cannot hit the ERROR.
    
    > So this patch removes the “could not open relation with OID” error, but it does not fully ensure the accuracy of the returned table list.
    
    IMO, no function returning table OIDs can guarantee they remain valid
    - a drop can happen right after we return the OID, and accuracy is in
    the caller's hands. All the callers of pg_get_publication_tables()
    already handle this by JOINing with pg_class.
    
    > It also introduces inconsistent behavior between tables published with and without column lists.
    
    These two paths do different things - one needs the relation open, the
    other doesn't. For callers, the outcome is the same: any stale OID
    gets filtered out by their pg_class JOIN.
    
    > To resolve the race condition completely, I think we should try to open the table regardless of whether a column list is specified.
    
    IMO, that would add unnecessary locking overhead in a path that
    doesn't need it. The bug is the ERROR, and this patch fixes it.
    
    In short, when no column list is specified,
    pg_get_publication_tables() needs to open the relation to enumerate
    all publishable columns and return them as an int2vector in the attrs
    output column. It currently uses table_open() for this, which errors
    out with concurrent table drops. This patch fixes it by using
    try_table_open() and skipping the relation if it's been dropped.
    
    Thoughts?
    
    --
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  21. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-05-04T02:34:00Z

    Hi,
    
    On Wed, Apr 29, 2026 at 12:15 AM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > Fixed. Please find the attached v5 patch.
    >
    > The fix is needed only for PG16 and later, not PG15 or PG14. The bug
    > was introduced by b7ae03953690 [1] in PG16, which added a table_open()
    > call in pg_get_publication_tables(). PG15 and earlier only use
    > get_rel_namespace() and syscache lookups, both of which gracefully
    > handle dropped relations (returning InvalidOid/false rather than
    > erroring).
    >
    > I verified the bug and the fix on all affected branches. Please find
    > the attached version-specific patches for backpatching. Thank you!
    >
    > [1] b7ae03953690 - Ignore dropped and generated columns from the column list
    
    Please find the attached v6 patch, which fixes a test failure on
    FreeBSD. This variant of the build forces parallel query via
    debug_parallel_query=regress, causing the pg_get_publication_tables
    injection point to fire in parallel workers as well. I disabled forced
    parallel query for this test case.
    
    Thank you!
    
    
    --
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
  22. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    shveta malik <shveta.malik@gmail.com> — 2026-05-04T05:08:10Z

    On Fri, May 1, 2026 at 7:00 AM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > Hi,
    >
    > On Wed, Apr 29, 2026 at 8:41 PM Chao Li <li.evan.chao@gmail.com> wrote:
    > >
    > > > <v5-0001-PG16-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-Fix-pg_get_publication_tables-race-with-concurren.patch><v5-0001-PG18-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-PG17-Fix-pg_get_publication_tables-race-with-conc.txt>
    > >
    > > I am afraid this is only a partial fix.
    >
    > Thanks for reviewing it. Please find my responses below.
    >
    > > ```
    > > @@ -1599,12 +1621,18 @@ pg_get_publication_tables(FunctionCallInfo fcinfo, ArrayType *pubnames,
    > >                 /* Show all columns when the column list is not specified. */
    > >                 if (nulls[2])
    > >                 {
    > > -                       Relation        rel = table_open(relid, AccessShareLock);
    > > +                       Relation        rel = try_table_open(relid, AccessShareLock);
    > >                         int                     nattnums = 0;
    > >                         int16      *attnums;
    > > -                       TupleDesc       desc = RelationGetDescr(rel);
    > > +                       TupleDesc       desc;
    > >                         int                     i;
    > >
    > > +                       /* Skip if the relation has been concurrently dropped. */
    > > +                       if (rel == NULL)
    > > +                               continue;
    > > ```
    > >
    > > This change uses try_table_open() to detect whether a table has been dropped, but try_table_open() is only called when the column list is not specified. If a table is included in the publication with an explicit column list, then even if it is dropped concurrently, it may still be returned by pg_get_publication_tables().
    >
    > Right. The try_table_open() is only needed there because that's the
    > only code path that actually opens the relation (to enumerate its
    > columns). The column list path reads from the pg_publication_rel
    > catalog - it never calls table_open(), so it cannot hit the ERROR.
    >
    > > So this patch removes the “could not open relation with OID” error, but it does not fully ensure the accuracy of the returned table list.
    >
    > IMO, no function returning table OIDs can guarantee they remain valid
    > - a drop can happen right after we return the OID, and accuracy is in
    > the caller's hands. All the callers of pg_get_publication_tables()
    > already handle this by JOINing with pg_class.
    >
    
    I agree with Bharath. Also I would like to add one more point. We do have this:
    
    + /* Skip if the relation has been concurrently dropped. */
    + if (!OidIsValid(schemaid))
    + continue;
    
    So if a table is dropped before we could access its explicitly
    mentioned column-list, above should handle it right even without
    'try_table_open' done? Moreover, pg_publication_rel will not return
    any row for dropped table, so we should be good. For 'table_infos',
    the case was different, as we saved the list in first call and are
    using that in subsequent call? But that is not the case with
    pg_publication_rel.
    
    > > It also introduces inconsistent behavior between tables published with and without column lists.
    >
    > These two paths do different things - one needs the relation open, the
    > other doesn't. For callers, the outcome is the same: any stale OID
    > gets filtered out by their pg_class JOIN.
    >
    > > To resolve the race condition completely, I think we should try to open the table regardless of whether a column list is specified.
    >
    > IMO, that would add unnecessary locking overhead in a path that
    > doesn't need it. The bug is the ERROR, and this patch fixes it.
    >
    > In short, when no column list is specified,
    > pg_get_publication_tables() needs to open the relation to enumerate
    > all publishable columns and return them as an int2vector in the attrs
    > output column. It currently uses table_open() for this, which errors
    > out with concurrent table drops. This patch fixes it by using
    > try_table_open() and skipping the relation if it's been dropped.
    >
    > Thoughts?
    >
    > --
    > Bharath Rupireddy
    > Amazon Web Services: https://aws.amazon.com
    
    
    
    
  23. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Chao Li <li.evan.chao@gmail.com> — 2026-05-05T03:08:29Z

    
    > On May 1, 2026, at 09:30, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote:
    > 
    > Hi,
    > 
    > On Wed, Apr 29, 2026 at 8:41 PM Chao Li <li.evan.chao@gmail.com> wrote:
    >> 
    >>> <v5-0001-PG16-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-Fix-pg_get_publication_tables-race-with-concurren.patch><v5-0001-PG18-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-PG17-Fix-pg_get_publication_tables-race-with-conc.txt>
    >> 
    >> I am afraid this is only a partial fix.
    > 
    > Thanks for reviewing it. Please find my responses below.
    > 
    >> ```
    >> @@ -1599,12 +1621,18 @@ pg_get_publication_tables(FunctionCallInfo fcinfo, ArrayType *pubnames,
    >>                /* Show all columns when the column list is not specified. */
    >>                if (nulls[2])
    >>                {
    >> -                       Relation        rel = table_open(relid, AccessShareLock);
    >> +                       Relation        rel = try_table_open(relid, AccessShareLock);
    >>                        int                     nattnums = 0;
    >>                        int16      *attnums;
    >> -                       TupleDesc       desc = RelationGetDescr(rel);
    >> +                       TupleDesc       desc;
    >>                        int                     i;
    >> 
    >> +                       /* Skip if the relation has been concurrently dropped. */
    >> +                       if (rel == NULL)
    >> +                               continue;
    >> ```
    >> 
    >> This change uses try_table_open() to detect whether a table has been dropped, but try_table_open() is only called when the column list is not specified. If a table is included in the publication with an explicit column list, then even if it is dropped concurrently, it may still be returned by pg_get_publication_tables().
    > 
    > Right. The try_table_open() is only needed there because that's the
    > only code path that actually opens the relation (to enumerate its
    > columns). The column list path reads from the pg_publication_rel
    > catalog - it never calls table_open(), so it cannot hit the ERROR.
    > 
    >> So this patch removes the “could not open relation with OID” error, but it does not fully ensure the accuracy of the returned table list.
    > 
    > IMO, no function returning table OIDs can guarantee they remain valid
    > - a drop can happen right after we return the OID, and accuracy is in
    > the caller's hands. All the callers of pg_get_publication_tables()
    > already handle this by JOINing with pg_class.
    > 
    
    Fair.
    
    Best regards,
    --
    Chao Li (Evan)
    HighGo Software Co., Ltd.
    https://www.highgo.com/
    
    
    
    
    
    
    
    
  24. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Chao Li <li.evan.chao@gmail.com> — 2026-05-05T03:10:10Z

    
    > On May 4, 2026, at 13:08, shveta malik <shveta.malik@gmail.com> wrote:
    > 
    > On Fri, May 1, 2026 at 7:00 AM Bharath Rupireddy
    > <bharath.rupireddyforpostgres@gmail.com> wrote:
    >> 
    >> Hi,
    >> 
    >> On Wed, Apr 29, 2026 at 8:41 PM Chao Li <li.evan.chao@gmail.com> wrote:
    >>> 
    >>>> <v5-0001-PG16-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-Fix-pg_get_publication_tables-race-with-concurren.patch><v5-0001-PG18-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-PG17-Fix-pg_get_publication_tables-race-with-conc.txt>
    >>> 
    >>> I am afraid this is only a partial fix.
    >> 
    >> Thanks for reviewing it. Please find my responses below.
    >> 
    >>> ```
    >>> @@ -1599,12 +1621,18 @@ pg_get_publication_tables(FunctionCallInfo fcinfo, ArrayType *pubnames,
    >>>                /* Show all columns when the column list is not specified. */
    >>>                if (nulls[2])
    >>>                {
    >>> -                       Relation        rel = table_open(relid, AccessShareLock);
    >>> +                       Relation        rel = try_table_open(relid, AccessShareLock);
    >>>                        int                     nattnums = 0;
    >>>                        int16      *attnums;
    >>> -                       TupleDesc       desc = RelationGetDescr(rel);
    >>> +                       TupleDesc       desc;
    >>>                        int                     i;
    >>> 
    >>> +                       /* Skip if the relation has been concurrently dropped. */
    >>> +                       if (rel == NULL)
    >>> +                               continue;
    >>> ```
    >>> 
    >>> This change uses try_table_open() to detect whether a table has been dropped, but try_table_open() is only called when the column list is not specified. If a table is included in the publication with an explicit column list, then even if it is dropped concurrently, it may still be returned by pg_get_publication_tables().
    >> 
    >> Right. The try_table_open() is only needed there because that's the
    >> only code path that actually opens the relation (to enumerate its
    >> columns). The column list path reads from the pg_publication_rel
    >> catalog - it never calls table_open(), so it cannot hit the ERROR.
    >> 
    >>> So this patch removes the “could not open relation with OID” error, but it does not fully ensure the accuracy of the returned table list.
    >> 
    >> IMO, no function returning table OIDs can guarantee they remain valid
    >> - a drop can happen right after we return the OID, and accuracy is in
    >> the caller's hands. All the callers of pg_get_publication_tables()
    >> already handle this by JOINing with pg_class.
    >> 
    > 
    > I agree with Bharath. Also I would like to add one more point. We do have this:
    > 
    > + /* Skip if the relation has been concurrently dropped. */
    > + if (!OidIsValid(schemaid))
    > + continue;
    > 
    
    Actually, this is the other comment I have. Why the comment says “if the relation has been dropped”, but the actual check is on schema id?
    
    Best regards,
    --
    Chao Li (Evan)
    HighGo Software Co., Ltd.
    https://www.highgo.com/
    
    
    
    
    
    
    
    
  25. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    shveta malik <shveta.malik@gmail.com> — 2026-05-05T04:02:00Z

    On Tue, May 5, 2026 at 8:40 AM Chao Li <li.evan.chao@gmail.com> wrote:
    >
    >
    >
    > > On May 4, 2026, at 13:08, shveta malik <shveta.malik@gmail.com> wrote:
    > >
    > > On Fri, May 1, 2026 at 7:00 AM Bharath Rupireddy
    > > <bharath.rupireddyforpostgres@gmail.com> wrote:
    > >>
    > >> Hi,
    > >>
    > >> On Wed, Apr 29, 2026 at 8:41 PM Chao Li <li.evan.chao@gmail.com> wrote:
    > >>>
    > >>>> <v5-0001-PG16-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-Fix-pg_get_publication_tables-race-with-concurren.patch><v5-0001-PG18-Fix-pg_get_publication_tables-race-with-conc.txt><v5-0001-PG17-Fix-pg_get_publication_tables-race-with-conc.txt>
    > >>>
    > >>> I am afraid this is only a partial fix.
    > >>
    > >> Thanks for reviewing it. Please find my responses below.
    > >>
    > >>> ```
    > >>> @@ -1599,12 +1621,18 @@ pg_get_publication_tables(FunctionCallInfo fcinfo, ArrayType *pubnames,
    > >>>                /* Show all columns when the column list is not specified. */
    > >>>                if (nulls[2])
    > >>>                {
    > >>> -                       Relation        rel = table_open(relid, AccessShareLock);
    > >>> +                       Relation        rel = try_table_open(relid, AccessShareLock);
    > >>>                        int                     nattnums = 0;
    > >>>                        int16      *attnums;
    > >>> -                       TupleDesc       desc = RelationGetDescr(rel);
    > >>> +                       TupleDesc       desc;
    > >>>                        int                     i;
    > >>>
    > >>> +                       /* Skip if the relation has been concurrently dropped. */
    > >>> +                       if (rel == NULL)
    > >>> +                               continue;
    > >>> ```
    > >>>
    > >>> This change uses try_table_open() to detect whether a table has been dropped, but try_table_open() is only called when the column list is not specified. If a table is included in the publication with an explicit column list, then even if it is dropped concurrently, it may still be returned by pg_get_publication_tables().
    > >>
    > >> Right. The try_table_open() is only needed there because that's the
    > >> only code path that actually opens the relation (to enumerate its
    > >> columns). The column list path reads from the pg_publication_rel
    > >> catalog - it never calls table_open(), so it cannot hit the ERROR.
    > >>
    > >>> So this patch removes the “could not open relation with OID” error, but it does not fully ensure the accuracy of the returned table list.
    > >>
    > >> IMO, no function returning table OIDs can guarantee they remain valid
    > >> - a drop can happen right after we return the OID, and accuracy is in
    > >> the caller's hands. All the callers of pg_get_publication_tables()
    > >> already handle this by JOINing with pg_class.
    > >>
    > >
    > > I agree with Bharath. Also I would like to add one more point. We do have this:
    > >
    > > + /* Skip if the relation has been concurrently dropped. */
    > > + if (!OidIsValid(schemaid))
    > > + continue;
    > >
    >
    > Actually, this is the other comment I have. Why the comment says “if the relation has been dropped”, but the actual check is on schema id?
    >
    
    Okay, I see your point. Shall we tweak it to the below to make it more
    understandable?
    
    
    Oid schemaid;
    
    /*
    * get_rel_namespace() returns InvalidOid if the relation no longer exists
    * (e.g., dropped concurrently). Skip such entries.
    */
    if (!OidIsValid(schemaid = get_rel_namespace(relid)))
    continue;
    
    thanks
    Shveta
    
    
    
    
  26. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Masahiko Sawada <sawada.mshk@gmail.com> — 2026-05-12T21:20:14Z

    Hi,
    
    On Sun, May 3, 2026 at 7:35 PM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > Hi,
    >
    > On Wed, Apr 29, 2026 at 12:15 AM Bharath Rupireddy
    > <bharath.rupireddyforpostgres@gmail.com> wrote:
    > >
    > > Fixed. Please find the attached v5 patch.
    > >
    > > The fix is needed only for PG16 and later, not PG15 or PG14. The bug
    > > was introduced by b7ae03953690 [1] in PG16, which added a table_open()
    > > call in pg_get_publication_tables(). PG15 and earlier only use
    > > get_rel_namespace() and syscache lookups, both of which gracefully
    > > handle dropped relations (returning InvalidOid/false rather than
    > > erroring).
    > >
    > > I verified the bug and the fix on all affected branches. Please find
    > > the attached version-specific patches for backpatching. Thank you!
    > >
    > > [1] b7ae03953690 - Ignore dropped and generated columns from the column list
    >
    > Please find the attached v6 patch, which fixes a test failure on
    > FreeBSD. This variant of the build forces parallel query via
    > debug_parallel_query=regress, causing the pg_get_publication_tables
    > injection point to fire in parallel workers as well. I disabled forced
    > parallel query for this test case.
    >
    
    I reviewed the patch and here are some comments:
    
    +/* State for pg_get_publication_tables SRF */
    +typedef struct
    +{
    + List    *table_infos; /* list of published_rel */
    + int curr_idx; /* current index into table_infos */
    +} publication_tables_state;
    
    I think we can define publication_table_state in
    pg_get_publication_tables() as it's used only in that function.
    
    ---
    + /* Skip if the relation has been concurrently dropped. */
    + if (!OidIsValid(schemaid))
    + continue;
    
    Although this check is done for all relations in table_infos, we also
    check the return value of try_table_open(), and these two checks have
    the same comment. I think we need more comments on why these two
    checks are required.
    
    In which case is schemaid InvalidOid and do we not call
    try_table_open() (i.e., nulls[2] is false)?
    
    It might make more sense to get-and-check the schemaid of each
    relation when adding the table information to table_infos.
    
    ---
    Looking at the regression tests in the patch, it tests the ALL TABLES
    publication cases and the concurrently-dropped table is handled when
    we call check the return value of try_table_open() but not
    get_rel_namespace(). I think the test case itself is fine but we can
    do the same test without adding a new injection point. For instance,
    
    backend-1: begin;
    backend-2: begin; lock table t_dropme in access exclusive mode;
    backend-1: select * from pg_publication_tables;
    backend-2: drop table t_dropme; commit;
    backend-1: get an error "could not open relation with XXX"
    
    ---
    If we use tuplestore instead of SRF, can we simplify the code as we
    would not need publication_tables_state and the above check? It would
    be only for the master, though.
    
    Regards,
    
    --
    Masahiko Sawada
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  27. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-06-15T23:21:31Z

    Hi,
    
    On Tue, May 12, 2026 at 2:20 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    >
    > I reviewed the patch and here are some comments:
    
    Thank you for reviewing!
    
    > +/* State for pg_get_publication_tables SRF */
    > +typedef struct
    > +{
    > + List    *table_infos; /* list of published_rel */
    > + int curr_idx; /* current index into table_infos */
    > +} publication_tables_state;
    >
    > I think we can define publication_table_state in
    > pg_get_publication_tables() as it's used only in that function.
    
    Done for pre-HEAD versions. For HEAD, I used the SRF approach.
    
    > ---
    > + /* Skip if the relation has been concurrently dropped. */
    > + if (!OidIsValid(schemaid))
    > + continue;
    >
    > Although this check is done for all relations in table_infos, we also
    > check the return value of try_table_open(), and these two checks have
    > the same comment. I think we need more comments on why these two
    > checks are required.
    
    When get_rel_namespace() returns InvalidOid for the concurrently
    dropped tables, it costs additional syscache lookups plus index scans
    (cache miss). So, there's no harm from the correctness perspective.
    
    However, I would like to optimize this part of the code by 1/ gating
    it with InvalidOid checkup, 2/ moving the schemaid fetch closer to
    where it's being used i.e. inside if (!pub->alltables). I would like
    to do this only for HEAD, so, I attached it as a separate 0002 patch.
    For pre-HEAD versions, I removed this check and retained the
    try_table_open() fix.
    
    > In which case is schemaid InvalidOid and do we not call
    > try_table_open() (i.e., nulls[2] is false)?
    
    This case is NOT possible, because the drop table ensures dependent
    catalog entries (pg_publication_namespace in this case) are deleted
    too.
    
    > It might make more sense to get-and-check the schemaid of each
    > relation when adding the table information to table_infos.
    
    That won't help fix the table_open() error with concurrent table drops.
    
    > ---
    > Looking at the regression tests in the patch, it tests the ALL TABLES
    > publication cases and the concurrently-dropped table is handled when
    > we call check the return value of try_table_open() but not
    > get_rel_namespace(). I think the test case itself is fine but we can
    > do the same test without adding a new injection point. For instance,
    >
    > backend-1: begin;
    > backend-2: begin; lock table t_dropme in access exclusive mode;
    > backend-1: select * from pg_publication_tables;
    > backend-2: drop table t_dropme; commit;
    > backend-1: get an error "could not open relation with XXX"
    
    Nice! It works. I used this for the test-case in the new patches.
    
    > ---
    > If we use tuplestore instead of SRF, can we simplify the code as we
    > would not need publication_tables_state and the above check? It would
    > be only for the master, though.
    
    I implemented this idea for HEAD and it simplifies the code a bit.
    
    Please find the attached v7 patches.
    
    --
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
  28. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Masahiko Sawada <sawada.mshk@gmail.com> — 2026-06-17T01:20:17Z

    On Mon, Jun 15, 2026 at 4:21 PM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > Hi,
    >
    > On Tue, May 12, 2026 at 2:20 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    > >
    > > I reviewed the patch and here are some comments:
    >
    > Thank you for reviewing!
    >
    > > +/* State for pg_get_publication_tables SRF */
    > > +typedef struct
    > > +{
    > > + List    *table_infos; /* list of published_rel */
    > > + int curr_idx; /* current index into table_infos */
    > > +} publication_tables_state;
    > >
    > > I think we can define publication_table_state in
    > > pg_get_publication_tables() as it's used only in that function.
    >
    > Done for pre-HEAD versions. For HEAD, I used the SRF approach.
    >
    > > ---
    > > + /* Skip if the relation has been concurrently dropped. */
    > > + if (!OidIsValid(schemaid))
    > > + continue;
    > >
    > > Although this check is done for all relations in table_infos, we also
    > > check the return value of try_table_open(), and these two checks have
    > > the same comment. I think we need more comments on why these two
    > > checks are required.
    >
    > When get_rel_namespace() returns InvalidOid for the concurrently
    > dropped tables, it costs additional syscache lookups plus index scans
    > (cache miss). So, there's no harm from the correctness perspective.
    >
    > However, I would like to optimize this part of the code by 1/ gating
    > it with InvalidOid checkup, 2/ moving the schemaid fetch closer to
    > where it's being used i.e. inside if (!pub->alltables). I would like
    > to do this only for HEAD, so, I attached it as a separate 0002 patch.
    > For pre-HEAD versions, I removed this check and retained the
    > try_table_open() fix.
    
    I'm not sure skipping get_rel_namespace() contributes to a visible
    performance gain. Rather I'm concerned that checking
    SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP) with
    schemaid=InvalidOid could lead to unexpected results.
    
    I'm concerned about the fact that the patch handles only the case
    where the table doesn't have the column list. The tables in the result
    not having the column list are guaranteed to be present since we do
    the existence check for them but others are not. I guess it's reliable
    if we could call try_table_open() for all tables in the table_infos,
    and check its namespace using RelationGetNamespace(). I think it
    doesn't lead to noticeable overheads in practice as we're already
    calling table_open() for all tables in ALL TABLES publications or ALL
    TABLE IN SCHEMA publications where users cannot specify the column
    list. What do you think?
    
    > > ---
    > > If we use tuplestore instead of SRF, can we simplify the code as we
    > > would not need publication_tables_state and the above check? It would
    > > be only for the master, though.
    >
    > I implemented this idea for HEAD and it simplifies the code a bit.
    
    Sorry, I should have mentioned that the changes to use tuplestore
    instead of SRF should be PG20. I think that we should not such a
    refactoring even PG19.
    
    Regards,
    
    -- 
    Masahiko Sawada
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  29. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-06-23T22:40:12Z

    Hi,
    
    On Tue, Jun 16, 2026 at 6:20 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    >
    > > However, I would like to optimize this part of the code by 1/ gating
    > > it with InvalidOid checkup, 2/ moving the schemaid fetch closer to
    > > where it's being used i.e. inside if (!pub->alltables). I would like
    > > to do this only for HEAD, so, I attached it as a separate 0002 patch.
    > > For pre-HEAD versions, I removed this check and retained the
    > > try_table_open() fix.
    >
    > I'm not sure skipping get_rel_namespace() contributes to a visible
    > performance gain. Rather I'm concerned that checking
    > SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP) with
    > schemaid=InvalidOid could lead to unexpected results.
    
    Thanks for reviewing!
    
    AFAICS it won't give unexpected results. The first time it looks up a
    publication with an invalid schema OID, it would cause a cache miss
    and leave a negative cache entry - but that's per publication, not per
    relation, so negligible.
    
    That said, with the approach of using try_table_open for all tables
    regardless and getting the schema from the open relation via
    RelationGetNamespace, schemaid is always valid by the time it reaches
    the syscache lookup (might be worth adding an assertion here). So the
    get_rel_namespace optimization (0002) can be dropped entirely.
    
    > I'm concerned about the fact that the patch handles only the case
    > where the table doesn't have the column list. The tables in the result
    > not having the column list are guaranteed to be present since we do
    > the existence check for them but others are not. I guess it's reliable
    > if we could call try_table_open() for all tables in the table_infos,
    > and check its namespace using RelationGetNamespace(). I think it
    > doesn't lead to noticeable overheads in practice as we're already
    > calling table_open() for all tables in ALL TABLES publications or ALL
    > TABLE IN SCHEMA publications where users cannot specify the column
    > list. What do you think?
    
    My earlier argument was that no function returning table OIDs can
    guarantee they remain valid - a drop can happen right after we return
    the OID, and accuracy is in the caller's hands. All the callers of
    pg_get_publication_tables already handle this by JOINing with
    pg_class.
    
    However, a closer look at other functions that either build a list of
    table OIDs (expand_partitioned_rtentry) or work on previously built
    table OIDs (vacuum_open_relation) proves me wrong - they all account
    for concurrent table drops with try_table_open. So, I'm convinced to
    add try_table_open in pg_get_publication_tables for all the tables
    regardless, unless I'm missing something here.
    
    > > > If we use tuplestore instead of SRF, can we simplify the code as we
    > > > would not need publication_tables_state and the above check? It would
    > > > be only for the master, though.
    > >
    > > I implemented this idea for HEAD and it simplifies the code a bit.
    >
    > Sorry, I should have mentioned that the changes to use tuplestore
    > instead of SRF should be PG20. I think that we should not such a
    > refactoring even PG19.
    
    I will drop the tuplestore changes for now and repost them as a
    refactoring patch after the PG20 dev branch is cut.
    
    Thoughts?
    
    --
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  30. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Masahiko Sawada <sawada.mshk@gmail.com> — 2026-06-26T20:30:23Z

    On Tue, Jun 23, 2026 at 3:40 PM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > Hi,
    >
    > On Tue, Jun 16, 2026 at 6:20 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    > >
    > > > However, I would like to optimize this part of the code by 1/ gating
    > > > it with InvalidOid checkup, 2/ moving the schemaid fetch closer to
    > > > where it's being used i.e. inside if (!pub->alltables). I would like
    > > > to do this only for HEAD, so, I attached it as a separate 0002 patch.
    > > > For pre-HEAD versions, I removed this check and retained the
    > > > try_table_open() fix.
    > >
    > > I'm not sure skipping get_rel_namespace() contributes to a visible
    > > performance gain. Rather I'm concerned that checking
    > > SearchSysCacheExists2(PUBLICATIONNAMESPACEMAP) with
    > > schemaid=InvalidOid could lead to unexpected results.
    >
    > Thanks for reviewing!
    >
    > AFAICS it won't give unexpected results. The first time it looks up a
    > publication with an invalid schema OID, it would cause a cache miss
    > and leave a negative cache entry - but that's per publication, not per
    > relation, so negligible.
    >
    > That said, with the approach of using try_table_open for all tables
    > regardless and getting the schema from the open relation via
    > RelationGetNamespace, schemaid is always valid by the time it reaches
    > the syscache lookup (might be worth adding an assertion here). So the
    > get_rel_namespace optimization (0002) can be dropped entirely.
    >
    > > I'm concerned about the fact that the patch handles only the case
    > > where the table doesn't have the column list. The tables in the result
    > > not having the column list are guaranteed to be present since we do
    > > the existence check for them but others are not. I guess it's reliable
    > > if we could call try_table_open() for all tables in the table_infos,
    > > and check its namespace using RelationGetNamespace(). I think it
    > > doesn't lead to noticeable overheads in practice as we're already
    > > calling table_open() for all tables in ALL TABLES publications or ALL
    > > TABLE IN SCHEMA publications where users cannot specify the column
    > > list. What do you think?
    >
    > My earlier argument was that no function returning table OIDs can
    > guarantee they remain valid - a drop can happen right after we return
    > the OID, and accuracy is in the caller's hands. All the callers of
    > pg_get_publication_tables already handle this by JOINing with
    > pg_class.
    >
    > However, a closer look at other functions that either build a list of
    > table OIDs (expand_partitioned_rtentry) or work on previously built
    > table OIDs (vacuum_open_relation) proves me wrong - they all account
    > for concurrent table drops with try_table_open. So, I'm convinced to
    > add try_table_open in pg_get_publication_tables for all the tables
    > regardless, unless I'm missing something here.
    
    +1
    
    >
    > > > > If we use tuplestore instead of SRF, can we simplify the code as we
    > > > > would not need publication_tables_state and the above check? It would
    > > > > be only for the master, though.
    > > >
    > > > I implemented this idea for HEAD and it simplifies the code a bit.
    > >
    > > Sorry, I should have mentioned that the changes to use tuplestore
    > > instead of SRF should be PG20. I think that we should not such a
    > > refactoring even PG19.
    >
    > I will drop the tuplestore changes for now and repost them as a
    > refactoring patch after the PG20 dev branch is cut.
    >
    > Thoughts?
    
    +1. We can revisit the tuplestore idea for PG20, possibly in a
    separate thread. For bug fixing, let's focus on making it simple and
    less invasive.
    
    Regards,
    
    -- 
    Masahiko Sawada
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  31. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-06-27T05:49:00Z

    Hi,
    
    On Fri, Jun 26, 2026 at 1:31 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    >
    > > My earlier argument was that no function returning table OIDs can
    > > guarantee they remain valid - a drop can happen right after we return
    > > the OID, and accuracy is in the caller's hands. All the callers of
    > > pg_get_publication_tables already handle this by JOINing with
    > > pg_class.
    > >
    > > However, a closer look at other functions that either build a list of
    > > table OIDs (expand_partitioned_rtentry) or work on previously built
    > > table OIDs (vacuum_open_relation) proves me wrong - they all account
    > > for concurrent table drops with try_table_open. So, I'm convinced to
    > > add try_table_open in pg_get_publication_tables for all the tables
    > > regardless, unless I'm missing something here.
    >
    > +1
    >
    > > I will drop the tuplestore changes for now and repost them as a
    > > refactoring patch after the PG20 dev branch is cut.
    > >
    > > Thoughts?
    >
    > +1. We can revisit the tuplestore idea for PG20, possibly in a
    > separate thread. For bug fixing, let's focus on making it simple and
    > less invasive.
    
    Please find the attached v8 patches. Thanks!
    
    -- 
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com
    
  32. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Masahiko Sawada <sawada.mshk@gmail.com> — 2026-06-30T17:37:57Z

    On Fri, Jun 26, 2026 at 10:49 PM Bharath Rupireddy
    <bharath.rupireddyforpostgres@gmail.com> wrote:
    >
    > Hi,
    >
    > On Fri, Jun 26, 2026 at 1:31 PM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    > >
    > > > My earlier argument was that no function returning table OIDs can
    > > > guarantee they remain valid - a drop can happen right after we return
    > > > the OID, and accuracy is in the caller's hands. All the callers of
    > > > pg_get_publication_tables already handle this by JOINing with
    > > > pg_class.
    > > >
    > > > However, a closer look at other functions that either build a list of
    > > > table OIDs (expand_partitioned_rtentry) or work on previously built
    > > > table OIDs (vacuum_open_relation) proves me wrong - they all account
    > > > for concurrent table drops with try_table_open. So, I'm convinced to
    > > > add try_table_open in pg_get_publication_tables for all the tables
    > > > regardless, unless I'm missing something here.
    > >
    > > +1
    > >
    > > > I will drop the tuplestore changes for now and repost them as a
    > > > refactoring patch after the PG20 dev branch is cut.
    > > >
    > > > Thoughts?
    > >
    > > +1. We can revisit the tuplestore idea for PG20, possibly in a
    > > separate thread. For bug fixing, let's focus on making it simple and
    > > less invasive.
    >
    > Please find the attached v8 patches. Thanks!
    
    Thank you for updating the patches!
    
    While changes to pg_get_publication_tables() look good to me, I think
    the regression tests should be implemented as an isolation test rather
    than a TAP test in 100_bugs.pl as it can be tested without subscribers
    and the isolation test works well for this kind of race condition
    tests.
    
    Regards,
    
    -- 
    Masahiko Sawada
    Amazon Web Services: https://aws.amazon.com
    
    
    
    
  33. Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE

    Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> — 2026-07-04T05:26:00Z

    Hi,
    
    On Tue, Jun 30, 2026 at 10:38 AM Masahiko Sawada <sawada.mshk@gmail.com> wrote:
    >
    > While changes to pg_get_publication_tables() look good to me,
    
    Thanks for reviewing!
    
    > I think
    > the regression tests should be implemented as an isolation test rather
    > than a TAP test in 100_bugs.pl as it can be tested without subscribers
    > and the isolation test works well for this kind of race condition
    > tests.
    
    Agreed. The isolation test avoids the extra publisher start/stop the
    TAP version needed, and it's about the same amount of code. Please
    find the attached v9 patches.
    
    -- 
    Bharath Rupireddy
    Amazon Web Services: https://aws.amazon.com