Hi,

On 9/30/22 2:11 PM, Drouvot, Bertrand wrote:
Hi,

On 7/6/22 3:30 PM, Drouvot, Bertrand wrote:
Hi,

On 10/28/21 11:07 PM, Andres Freund wrote:
Hi,

On 2021-10-28 16:24:22 -0400, Robert Haas wrote:
On Wed, Oct 27, 2021 at 2:56 AM Drouvot, Bertrand <bdrou...@amazon.com> wrote:
So you have in mind to check for XLogLogicalInfoActive() first, and if true, 
then open the relation and call
RelationIsAccessibleInLogicalDecoding()?
I think 0001 is utterly unacceptable. We cannot add calls to
table_open() in low-level functions like this. Suppose for example
that _bt_getbuf() calls _bt_log_reuse_page() which with 0001 applied
would call get_rel_logical_catalog(). _bt_getbuf() will have acquired
a buffer lock on the page. The idea that it's safe to call
table_open() while holding a buffer lock cannot be taken seriously.
Yes - that's pretty clearly a deadlock hazard. It shouldn't too hard to fix, I
think. Possibly a bit more verbose than nice, but...

Alternatively we could propagate the information whether a relcache entry is
for a catalog from the table to the index. Then we'd not need to change the
btree code to pass the table down.

Looking closer at RelationIsAccessibleInLogicalDecoding() It seems to me that the 
missing part to be able to tell whether or not an index is for a catalog is the 
rd_options->user_catalog_table value of its related heap relation.

Then, a way to achieve that could be to:

- Add to Relation a new "heap_rd_options" representing the rd_options of the 
related heap relation when appropriate

- Trigger the related indexes relcache invalidations when an 
ATExecSetRelOptions() is triggered on a heap relation

- Write an equivalent of RelationIsUsedAsCatalogTable() for indexes that would 
make use of the heap_rd_options instead

Does that sound like a valid option to you or do you have another idea in mind 
to propagate the information whether a relcache entry is for a catalog from the 
table to the index?


I ended up with the attached proposal to propagate the catalog information to 
the indexes.

The attached adds a new field "isusercatalog" in pg_index to indicate whether 
or not the index is linked to a table that has the storage parameter user_catalog_table 
set to true.

Then it defines new macros, including "IndexIsAccessibleInLogicalDecoding" 
making use of this new field.

This new macro replaces get_rel_logical_catalog() that was part of the previous 
patch version.

What do you think about this approach and the attached?

If that sounds reasonable, then I'll add tap tests for it and try to improve the way 
isusercatalog is propagated to the index(es) in case a reset is done on 
user_catalog_table on the table (currently in this POC patch, it's hardcoded to 
"false" which is the default value for user_catalog_table in boolRelOpts[]) (A 
better approach would be probably to retrieve the value from the table once the reset is 
done and then propagate it to the index(es).)

Please find attached a rebase to propagate the catalog information to the 
indexes.
It also takes care of the RESET on user_catalog_table (adding a new Macro 
"HEAP_DEFAULT_USER_CATALOG_TABLE") and adds a few tests in 
contrib/test_decoding/sql/ddl.sql.

Regards,

--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
From 2de2b9917d43d598da56c996361d934c45e52df2 Mon Sep 17 00:00:00 2001
From: bdrouvotAWS <bdrou...@amazon.com>
Date: Fri, 25 Nov 2022 09:42:40 +0000
Subject: [PATCH v27] Add info in WAL records in preparation for logical slot
 conflict handling.

When a WAL replay on standby indicates that a catalog table tuple is
to be deleted by an xid that is greater than a logical slot's
catalog_xmin, then that means the slot's catalog_xmin conflicts with
the xid, and we need to handle the conflict.  While subsequent commits
will do the actual conflict handling, this commit adds a new field
onCatalogTable in such WAL records, that is true for catalog tables,
so as to arrange for conflict handling.

Author: Andres Freund (in an older version), Amit Khandekar, Bertrand
Drouvot
Reviewed-By: Bertrand Drouvot, Andres Freund, Robert Haas, Fabrizio de
Royes Mello
---
 contrib/test_decoding/expected/ddl.out  | 29 +++++++++++++
 contrib/test_decoding/sql/ddl.sql       |  7 ++++
 doc/src/sgml/catalogs.sgml              | 11 +++++
 src/backend/access/common/reloptions.c  |  2 +-
 src/backend/access/gist/gistxlog.c      |  1 +
 src/backend/access/hash/hashinsert.c    |  1 +
 src/backend/access/heap/heapam.c        |  4 +-
 src/backend/access/heap/pruneheap.c     |  1 +
 src/backend/access/heap/visibilitymap.c |  3 +-
 src/backend/access/nbtree/nbtpage.c     |  2 +
 src/backend/access/spgist/spgvacuum.c   |  1 +
 src/backend/catalog/index.c             | 14 +++++--
 src/backend/commands/tablecmds.c        | 55 ++++++++++++++++++++++++-
 src/include/access/gistxlog.h           |  2 +
 src/include/access/hash_xlog.h          |  1 +
 src/include/access/heapam_xlog.h        |  5 ++-
 src/include/access/nbtxlog.h            |  2 +
 src/include/access/spgxlog.h            |  1 +
 src/include/catalog/pg_index.h          |  2 +
 src/include/utils/rel.h                 | 34 +++++++++++++++
 20 files changed, 169 insertions(+), 9 deletions(-)
  11.8% contrib/test_decoding/expected/
   7.0% contrib/test_decoding/sql/
   6.8% doc/src/sgml/
   7.8% src/backend/access/heap/
   5.8% src/backend/access/
   6.3% src/backend/catalog/
  23.9% src/backend/commands/
   6.1% src/include/access/
  22.6% src/include/utils/

diff --git a/contrib/test_decoding/expected/ddl.out 
b/contrib/test_decoding/expected/ddl.out
index 9a28b5ddc5..40cf2f4dc4 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -483,6 +483,7 @@ CREATE TABLE replication_metadata (
 )
 WITH (user_catalog_table = true)
 ;
+CREATE INDEX replication_metadata_idx1 on replication_metadata(relation);
 \d+ replication_metadata
                                                  Table 
"public.replication_metadata"
   Column  |  Type   | Collation | Nullable |                     Default       
               | Storage  | Stats target | Description 
@@ -492,8 +493,15 @@ WITH (user_catalog_table = true)
  options  | text[]  |           |          |                                   
               | extended |              | 
 Indexes:
     "replication_metadata_pkey" PRIMARY KEY, btree (id)
+    "replication_metadata_idx1" btree (relation)
 Options: user_catalog_table=true
 
+SELECT bool_and(indisusercatalog) from pg_index where indrelid = 
'replication_metadata'::regclass;
+ bool_and 
+----------
+ t
+(1 row)
+
 INSERT INTO replication_metadata(relation, options)
 VALUES ('foo', ARRAY['a', 'b']);
 ALTER TABLE replication_metadata RESET (user_catalog_table);
@@ -506,6 +514,13 @@ ALTER TABLE replication_metadata RESET 
(user_catalog_table);
  options  | text[]  |           |          |                                   
               | extended |              | 
 Indexes:
     "replication_metadata_pkey" PRIMARY KEY, btree (id)
+    "replication_metadata_idx1" btree (relation)
+
+SELECT bool_or(indisusercatalog) from pg_index where indrelid = 
'replication_metadata'::regclass;
+ bool_or 
+---------
+ f
+(1 row)
 
 INSERT INTO replication_metadata(relation, options)
 VALUES ('bar', ARRAY['a', 'b']);
@@ -519,8 +534,15 @@ ALTER TABLE replication_metadata SET (user_catalog_table = 
true);
  options  | text[]  |           |          |                                   
               | extended |              | 
 Indexes:
     "replication_metadata_pkey" PRIMARY KEY, btree (id)
+    "replication_metadata_idx1" btree (relation)
 Options: user_catalog_table=true
 
+SELECT bool_and(indisusercatalog) from pg_index where indrelid = 
'replication_metadata'::regclass;
+ bool_and 
+----------
+ t
+(1 row)
+
 INSERT INTO replication_metadata(relation, options)
 VALUES ('blub', NULL);
 -- make sure rewrites don't work
@@ -538,8 +560,15 @@ ALTER TABLE replication_metadata SET (user_catalog_table = 
false);
  rewritemeornot | integer |           |          |                             
                     | plain    |              | 
 Indexes:
     "replication_metadata_pkey" PRIMARY KEY, btree (id)
+    "replication_metadata_idx1" btree (relation)
 Options: user_catalog_table=false
 
+SELECT bool_or(indisusercatalog) from pg_index where indrelid = 
'replication_metadata'::regclass;
+ bool_or 
+---------
+ f
+(1 row)
+
 INSERT INTO replication_metadata(relation, options)
 VALUES ('zaphod', NULL);
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 
'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/contrib/test_decoding/sql/ddl.sql 
b/contrib/test_decoding/sql/ddl.sql
index 4f76bed72c..85ddd4be03 100644
--- a/contrib/test_decoding/sql/ddl.sql
+++ b/contrib/test_decoding/sql/ddl.sql
@@ -276,19 +276,25 @@ CREATE TABLE replication_metadata (
 )
 WITH (user_catalog_table = true)
 ;
+
+CREATE INDEX replication_metadata_idx1 on replication_metadata(relation);
+
 \d+ replication_metadata
+SELECT bool_and(indisusercatalog) from pg_index where indrelid = 
'replication_metadata'::regclass;
 
 INSERT INTO replication_metadata(relation, options)
 VALUES ('foo', ARRAY['a', 'b']);
 
 ALTER TABLE replication_metadata RESET (user_catalog_table);
 \d+ replication_metadata
+SELECT bool_or(indisusercatalog) from pg_index where indrelid = 
'replication_metadata'::regclass;
 
 INSERT INTO replication_metadata(relation, options)
 VALUES ('bar', ARRAY['a', 'b']);
 
 ALTER TABLE replication_metadata SET (user_catalog_table = true);
 \d+ replication_metadata
+SELECT bool_and(indisusercatalog) from pg_index where indrelid = 
'replication_metadata'::regclass;
 
 INSERT INTO replication_metadata(relation, options)
 VALUES ('blub', NULL);
@@ -299,6 +305,7 @@ ALTER TABLE replication_metadata ALTER COLUMN 
rewritemeornot TYPE text;
 
 ALTER TABLE replication_metadata SET (user_catalog_table = false);
 \d+ replication_metadata
+SELECT bool_or(indisusercatalog) from pg_index where indrelid = 
'replication_metadata'::regclass;
 
 INSERT INTO replication_metadata(relation, options)
 VALUES ('zaphod', NULL);
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9ed2b020b7..18d6b99cac 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -4437,6 +4437,17 @@ SCRAM-SHA-256$<replaceable>&lt;iteration 
count&gt;</replaceable>:<replaceable>&l
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>indisusercatalog</structfield> <type>bool</type>
+      </para>
+      <para>
+       If true, the index is linked to a table that is declared as an 
additional
+       catalog table for purposes of logical replication (means has <link 
linkend="sql-createtable"><literal>user_catalog_table</literal></link>)
+       set to true.
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>indisreplident</structfield> <type>bool</type>
diff --git a/src/backend/access/common/reloptions.c 
b/src/backend/access/common/reloptions.c
index 75b7344891..4b41f5e68d 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -120,7 +120,7 @@ static relopt_bool boolRelOpts[] =
                        RELOPT_KIND_HEAP,
                        AccessExclusiveLock
                },
-               false
+               HEAP_DEFAULT_USER_CATALOG_TABLE
        },
        {
                {
diff --git a/src/backend/access/gist/gistxlog.c 
b/src/backend/access/gist/gistxlog.c
index cb5affa3d2..65fc18554a 100644
--- a/src/backend/access/gist/gistxlog.c
+++ b/src/backend/access/gist/gistxlog.c
@@ -608,6 +608,7 @@ gistXLogPageReuse(Relation rel, BlockNumber blkno, 
FullTransactionId deleteXid)
         */
 
        /* XLOG stuff */
+       xlrec_reuse.onCatalogTable = IndexIsAccessibleInLogicalDecoding(rel);
        xlrec_reuse.locator = rel->rd_locator;
        xlrec_reuse.block = blkno;
        xlrec_reuse.snapshotConflictHorizon = deleteXid;
diff --git a/src/backend/access/hash/hashinsert.c 
b/src/backend/access/hash/hashinsert.c
index 9a921e341e..99d5d53d21 100644
--- a/src/backend/access/hash/hashinsert.c
+++ b/src/backend/access/hash/hashinsert.c
@@ -432,6 +432,7 @@ _hash_vacuum_one_page(Relation rel, Relation hrel, Buffer 
metabuf, Buffer buf)
                        xl_hash_vacuum_one_page xlrec;
                        XLogRecPtr      recptr;
 
+                       xlrec.onCatalogTable = 
RelationIsAccessibleInLogicalDecoding(hrel);
                        xlrec.snapshotConflictHorizon = snapshotConflictHorizon;
                        xlrec.ntuples = ndeletable;
 
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 747db50376..7d48b9214b 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -6831,6 +6831,7 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer,
                snapshotConflictHorizon = FreezeLimit;
                TransactionIdRetreat(snapshotConflictHorizon);
 
+               xlrec.onCatalogTable = 
RelationIsAccessibleInLogicalDecoding(rel);
                xlrec.snapshotConflictHorizon = snapshotConflictHorizon;
                xlrec.nplans = nplans;
 
@@ -8248,7 +8249,7 @@ bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate)
  * update the heap page's LSN.
  */
 XLogRecPtr
-log_heap_visible(RelFileLocator rlocator, Buffer heap_buffer, Buffer vm_buffer,
+log_heap_visible(Relation rel, Buffer heap_buffer, Buffer vm_buffer,
                                 TransactionId snapshotConflictHorizon, uint8 
vmflags)
 {
        xl_heap_visible xlrec;
@@ -8258,6 +8259,7 @@ log_heap_visible(RelFileLocator rlocator, Buffer 
heap_buffer, Buffer vm_buffer,
        Assert(BufferIsValid(heap_buffer));
        Assert(BufferIsValid(vm_buffer));
 
+       xlrec.onCatalogTable = RelationIsAccessibleInLogicalDecoding(rel);
        xlrec.snapshotConflictHorizon = snapshotConflictHorizon;
        xlrec.flags = vmflags;
        XLogBeginInsert();
diff --git a/src/backend/access/heap/pruneheap.c 
b/src/backend/access/heap/pruneheap.c
index 91c5f5e9ef..c2244ccaa3 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -418,6 +418,7 @@ heap_page_prune(Relation relation, Buffer buffer,
                        xl_heap_prune xlrec;
                        XLogRecPtr      recptr;
 
+                       xlrec.onCatalogTable = 
RelationIsAccessibleInLogicalDecoding(relation);
                        xlrec.snapshotConflictHorizon = 
prstate.snapshotConflictHorizon;
                        xlrec.nredirected = prstate.nredirected;
                        xlrec.ndead = prstate.ndead;
diff --git a/src/backend/access/heap/visibilitymap.c 
b/src/backend/access/heap/visibilitymap.c
index 4ed70275e2..0bd73f4d9f 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -283,8 +283,7 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer 
heapBuf,
                        if (XLogRecPtrIsInvalid(recptr))
                        {
                                Assert(!InRecovery);
-                               recptr = log_heap_visible(rel->rd_locator, 
heapBuf, vmBuf,
-                                                                               
  cutoff_xid, flags);
+                               recptr = log_heap_visible(rel, heapBuf, vmBuf, 
cutoff_xid, flags);
 
                                /*
                                 * If data checksums are enabled (or 
wal_log_hints=on), we
diff --git a/src/backend/access/nbtree/nbtpage.c 
b/src/backend/access/nbtree/nbtpage.c
index 65aa44893c..1e5bf7513e 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -836,6 +836,7 @@ _bt_log_reuse_page(Relation rel, BlockNumber blkno, 
FullTransactionId safexid)
         */
 
        /* XLOG stuff */
+       xlrec_reuse.onCatalogTable = IndexIsAccessibleInLogicalDecoding(rel);
        xlrec_reuse.locator = rel->rd_locator;
        xlrec_reuse.block = blkno;
        xlrec_reuse.snapshotConflictHorizon = safexid;
@@ -1358,6 +1359,7 @@ _bt_delitems_delete(Relation rel, Buffer buf,
                XLogRecPtr      recptr;
                xl_btree_delete xlrec_delete;
 
+               xlrec_delete.onCatalogTable = 
IndexIsAccessibleInLogicalDecoding(rel);
                xlrec_delete.snapshotConflictHorizon = snapshotConflictHorizon;
                xlrec_delete.ndeleted = ndeletable;
                xlrec_delete.nupdated = nupdatable;
diff --git a/src/backend/access/spgist/spgvacuum.c 
b/src/backend/access/spgist/spgvacuum.c
index ad90b213b9..000133aad6 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -503,6 +503,7 @@ vacuumRedirectAndPlaceholder(Relation index, Buffer buffer)
        spgxlogVacuumRedirect xlrec;
        GlobalVisState *vistest;
 
+       xlrec.onCatalogTable = IndexIsAccessibleInLogicalDecoding(index);
        xlrec.nToPlaceholder = 0;
        xlrec.snapshotConflictHorizon = InvalidTransactionId;
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 61f1d3926a..f6b2c9ac71 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -123,7 +123,8 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
                                                                bool 
isexclusion,
                                                                bool immediate,
                                                                bool isvalid,
-                                                               bool isready);
+                                                               bool isready,
+                                                               bool 
is_user_catalog);
 static void index_update_stats(Relation rel,
                                                           bool hasindex,
                                                           double reltuples);
@@ -545,7 +546,8 @@ UpdateIndexRelation(Oid indexoid,
                                        bool isexclusion,
                                        bool immediate,
                                        bool isvalid,
-                                       bool isready)
+                                       bool isready,
+                                       bool is_user_catalog)
 {
        int2vector *indkey;
        oidvector  *indcollation;
@@ -622,6 +624,7 @@ UpdateIndexRelation(Oid indexoid,
        values[Anum_pg_index_indcheckxmin - 1] = BoolGetDatum(false);
        values[Anum_pg_index_indisready - 1] = BoolGetDatum(isready);
        values[Anum_pg_index_indislive - 1] = BoolGetDatum(true);
+       values[Anum_pg_index_indisusercatalog - 1] = 
BoolGetDatum(is_user_catalog);
        values[Anum_pg_index_indisreplident - 1] = BoolGetDatum(false);
        values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
        values[Anum_pg_index_indcollation - 1] = PointerGetDatum(indcollation);
@@ -735,6 +738,7 @@ index_create(Relation heapRelation,
        TransactionId relfrozenxid;
        MultiXactId relminmxid;
        bool            create_storage = !RelFileNumberIsValid(relFileNumber);
+       bool            isusercatalog = false;
 
        /* constraint flags can only be set when a constraint is requested */
        Assert((constr_flags == 0) ||
@@ -1014,13 +1018,17 @@ index_create(Relation heapRelation,
         *        (Or, could define a rule to maintain the predicate) --Nels, 
Feb '92
         * ----------------
         */
+       if (heapRelation->rd_options)
+               isusercatalog = ((StdRdOptions *) 
(heapRelation)->rd_options)->user_catalog_table;
+
        UpdateIndexRelation(indexRelationId, heapRelationId, parentIndexRelid,
                                                indexInfo,
                                                collationObjectId, 
classObjectId, coloptions,
                                                isprimary, is_exclusion,
                                                (constr_flags & 
INDEX_CONSTR_CREATE_DEFERRABLE) == 0,
                                                !concurrent && !invalid,
-                                               !concurrent);
+                                               !concurrent,
+                                               isusercatalog);
 
        /*
         * Register relcache invalidation on the indexes' heap relation, to
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 845208d662..faa1fcc07d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -103,6 +103,7 @@
 #include "utils/syscache.h"
 #include "utils/timestamp.h"
 #include "utils/typcache.h"
+#include "utils/rel.h"
 
 /*
  * ON COMMIT action list
@@ -14183,6 +14184,10 @@ ATExecSetRelOptions(Relation rel, List *defList, 
AlterTableType operation,
        Datum           repl_val[Natts_pg_class];
        bool            repl_null[Natts_pg_class];
        bool            repl_repl[Natts_pg_class];
+       ListCell   *cell;
+       List       *rel_options;
+       bool            catalog_table_val = HEAP_DEFAULT_USER_CATALOG_TABLE;
+       bool            catalog_table = false;
        static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
 
        if (defList == NIL && operation != AT_ReplaceRelOptions)
@@ -14249,7 +14254,6 @@ ATExecSetRelOptions(Relation rel, List *defList, 
AlterTableType operation,
        {
                Query      *view_query = get_view_query(rel);
                List       *view_options = untransformRelOptions(newOptions);
-               ListCell   *cell;
                bool            check_option = false;
 
                foreach(cell, view_options)
@@ -14277,6 +14281,20 @@ ATExecSetRelOptions(Relation rel, List *defList, 
AlterTableType operation,
                }
        }
 
+       /* If user_catalog_table is part of the new options, record its new 
value */
+       rel_options = untransformRelOptions(newOptions);
+
+       foreach(cell, rel_options)
+       {
+               DefElem    *defel = (DefElem *) lfirst(cell);
+
+               if (strcmp(defel->defname, "user_catalog_table") == 0)
+               {
+                       catalog_table = true;
+                       catalog_table_val = defGetBoolean(defel);
+               }
+       }
+
        /*
         * All we need do here is update the pg_class row; the new options will 
be
         * propagated into relcaches during post-commit cache inval.
@@ -14303,6 +14321,41 @@ ATExecSetRelOptions(Relation rel, List *defList, 
AlterTableType operation,
 
        ReleaseSysCache(tuple);
 
+       /* Update the indexes if there is a need to */
+       if (catalog_table || operation == AT_ResetRelOptions)
+       {
+               Relation        pg_index;
+               HeapTuple       pg_index_tuple;
+               Form_pg_index pg_index_form;
+               ListCell   *index;
+
+               pg_index = table_open(IndexRelationId, RowExclusiveLock);
+
+               foreach(index, RelationGetIndexList(rel))
+               {
+                       Oid                     thisIndexOid = 
lfirst_oid(index);
+
+                       pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
+                                                                               
                 ObjectIdGetDatum(thisIndexOid));
+                       if (!HeapTupleIsValid(pg_index_tuple))
+                               elog(ERROR, "cache lookup failed for index %u", 
thisIndexOid);
+                       pg_index_form = (Form_pg_index) 
GETSTRUCT(pg_index_tuple);
+
+                       /* Modify the index only if user_catalog_table differ */
+                       if (catalog_table_val != 
pg_index_form->indisusercatalog)
+                       {
+                               pg_index_form->indisusercatalog = 
catalog_table_val;
+                               CatalogTupleUpdate(pg_index, 
&pg_index_tuple->t_self, pg_index_tuple);
+                               InvokeObjectPostAlterHookArg(IndexRelationId, 
thisIndexOid, 0,
+                                                                               
         InvalidOid, true);
+                       }
+
+                       heap_freetuple(pg_index_tuple);
+               }
+
+               table_close(pg_index, RowExclusiveLock);
+       }
+
        /* repeat the whole exercise for the toast table, if there's one */
        if (OidIsValid(rel->rd_rel->reltoastrelid))
        {
diff --git a/src/include/access/gistxlog.h b/src/include/access/gistxlog.h
index 33f1c7e31b..f924f9f7be 100644
--- a/src/include/access/gistxlog.h
+++ b/src/include/access/gistxlog.h
@@ -49,6 +49,7 @@ typedef struct gistxlogPageUpdate
  */
 typedef struct gistxlogDelete
 {
+       bool        onCatalogTable;
        TransactionId snapshotConflictHorizon;
        uint16          ntodelete;              /* number of deleted offsets */
 
@@ -97,6 +98,7 @@ typedef struct gistxlogPageDelete
  */
 typedef struct gistxlogPageReuse
 {
+       bool        onCatalogTable;
        RelFileLocator locator;
        BlockNumber block;
        FullTransactionId snapshotConflictHorizon;
diff --git a/src/include/access/hash_xlog.h b/src/include/access/hash_xlog.h
index 6dafb4a598..b16e7038e2 100644
--- a/src/include/access/hash_xlog.h
+++ b/src/include/access/hash_xlog.h
@@ -250,6 +250,7 @@ typedef struct xl_hash_init_bitmap_page
  */
 typedef struct xl_hash_vacuum_one_page
 {
+       bool        onCatalogTable;
        TransactionId snapshotConflictHorizon;
        int                     ntuples;
 
diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h
index 5c77290eec..dec62e8bed 100644
--- a/src/include/access/heapam_xlog.h
+++ b/src/include/access/heapam_xlog.h
@@ -242,6 +242,7 @@ typedef struct xl_heap_update
  */
 typedef struct xl_heap_prune
 {
+       bool        onCatalogTable;
        TransactionId snapshotConflictHorizon;
        uint16          nredirected;
        uint16          ndead;
@@ -342,6 +343,7 @@ typedef struct xl_heap_freeze_plan
  */
 typedef struct xl_heap_freeze_page
 {
+       bool        onCatalogTable;
        TransactionId snapshotConflictHorizon;
        uint16          nplans;
 
@@ -359,6 +361,7 @@ typedef struct xl_heap_freeze_page
  */
 typedef struct xl_heap_visible
 {
+       bool        onCatalogTable;
        TransactionId snapshotConflictHorizon;
        uint8           flags;
 } xl_heap_visible;
@@ -408,7 +411,7 @@ extern void heap2_desc(StringInfo buf, XLogReaderState 
*record);
 extern const char *heap2_identify(uint8 info);
 extern void heap_xlog_logical_rewrite(XLogReaderState *r);
 
-extern XLogRecPtr log_heap_visible(RelFileLocator rlocator, Buffer heap_buffer,
+extern XLogRecPtr log_heap_visible(Relation rel, Buffer heap_buffer,
                                                                   Buffer 
vm_buffer,
                                                                   
TransactionId snapshotConflictHorizon,
                                                                   uint8 
vmflags);
diff --git a/src/include/access/nbtxlog.h b/src/include/access/nbtxlog.h
index 3b2d959c69..f8ae3827d3 100644
--- a/src/include/access/nbtxlog.h
+++ b/src/include/access/nbtxlog.h
@@ -185,6 +185,7 @@ typedef struct xl_btree_dedup
  */
 typedef struct xl_btree_reuse_page
 {
+       bool        onCatalogTable;
        RelFileLocator locator;
        BlockNumber block;
        FullTransactionId snapshotConflictHorizon;
@@ -232,6 +233,7 @@ typedef struct xl_btree_vacuum
 
 typedef struct xl_btree_delete
 {
+       bool        onCatalogTable;
        TransactionId snapshotConflictHorizon;
        uint16          ndeleted;
        uint16          nupdated;
diff --git a/src/include/access/spgxlog.h b/src/include/access/spgxlog.h
index 82332cb694..8b4ab0e206 100644
--- a/src/include/access/spgxlog.h
+++ b/src/include/access/spgxlog.h
@@ -237,6 +237,7 @@ typedef struct spgxlogVacuumRoot
 
 typedef struct spgxlogVacuumRedirect
 {
+       bool        onCatalogTable;
        uint16          nToPlaceholder; /* number of redirects to make 
placeholders */
        OffsetNumber firstPlaceholder;  /* first placeholder tuple to remove */
        TransactionId snapshotConflictHorizon;  /* newest XID of removed 
redirects */
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index f853846ee1..dd16431378 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -43,6 +43,8 @@ CATALOG(pg_index,2610,IndexRelationId) BKI_SCHEMA_MACRO
        bool            indcheckxmin;   /* must we wait for xmin to be old? */
        bool            indisready;             /* is this index ready for 
inserts? */
        bool            indislive;              /* is this index alive at all? 
*/
+       bool            indisusercatalog;       /* is this index linked to a 
user catalog
+                                                                        * 
relation? */
        bool            indisreplident; /* is this index the identity for 
replication? */
 
        /* variable-length fields start here, but we allow direct access to 
indkey */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f383a2fca9..9b77e23c29 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -27,6 +27,7 @@
 #include "storage/smgr.h"
 #include "utils/relcache.h"
 #include "utils/reltrigger.h"
+#include "catalog/catalog.h"
 
 
 /*
@@ -343,6 +344,7 @@ typedef struct StdRdOptions
 
 #define HEAP_MIN_FILLFACTOR                    10
 #define HEAP_DEFAULT_FILLFACTOR                100
+#define HEAP_DEFAULT_USER_CATALOG_TABLE                false
 
 /*
  * RelationGetToastTupleTarget
@@ -378,6 +380,9 @@ typedef struct StdRdOptions
  * RelationIsUsedAsCatalogTable
  *             Returns whether the relation should be treated as a catalog 
table
  *             from the pov of logical decoding.  Note multiple eval of 
argument!
+ *             This definition should not invoke anything that performs catalog
+ *             access; otherwise it may cause infinite recursion. Check the 
comments
+ *             in RelationIsAccessibleInLogicalDecoding() for details.
  */
 #define RelationIsUsedAsCatalogTable(relation) \
        ((relation)->rd_options && \
@@ -678,12 +683,41 @@ RelationCloseSmgr(Relation relation)
  * RelationIsAccessibleInLogicalDecoding
  *             True if we need to log enough information to have access via
  *             decoding snapshot.
+ *             This definition should not invoke anything that performs catalog
+ *             access. Otherwise, e.g. logging a WAL entry for catalog 
relation may
+ *             invoke this function, which will in turn do catalog access, 
which may
+ *             in turn cause another similar WAL entry to be logged, leading to
+ *             infinite recursion.
  */
 #define RelationIsAccessibleInLogicalDecoding(relation) \
        (XLogLogicalInfoActive() && \
         RelationNeedsWAL(relation) && \
         (IsCatalogRelation(relation) || 
RelationIsUsedAsCatalogTable(relation)))
 
+/*
+ * IndexIsUserCatalog
+ *             True if index is linked to a user catalog relation.
+ */
+#define IndexIsUserCatalog(relation)                                           
                                        \
+       (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX),               
                \
+        (relation)->rd_index->indisusercatalog)
+
+/*
+ * IndexIsAccessibleInLogicalDecoding
+ *             True if we need to log enough information to have access via
+ *             decoding snapshot.
+ *             This definition should not invoke anything that performs catalog
+ *             access. Otherwise, e.g. logging a WAL entry for catalog 
relation may
+ *             invoke this function, which will in turn do catalog access, 
which may
+ *             in turn cause another similar WAL entry to be logged, leading to
+ *             infinite recursion.
+ */
+#define IndexIsAccessibleInLogicalDecoding(relation) \
+       (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX), \
+        XLogLogicalInfoActive() && \
+        RelationNeedsWAL(relation) && \
+        (IsCatalogRelation(relation) || IndexIsUserCatalog(relation)))
+
 /*
  * RelationIsLogicallyLogged
  *             True if we need to log enough information to extract the data 
from the
-- 
2.34.1

Reply via email to