Fix things so that when CREATE INDEX CONCURRENTLY sets pg_index.indisvalid
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 2 May 2007 21:08:46 +0000 (21:08 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 2 May 2007 21:08:46 +0000 (21:08 +0000)
true at the very end of its processing, the update is broadcast via a
shared-cache-inval message for the index; without this, existing backends that
already have relcache entries for the index might never see it become valid.
Also, force a relcache inval on the index's parent table at the same time,
so that any cached plans for that table are re-planned; this ensures that
the newly valid index will be used if appropriate.  Aside from making
C.I.C. behave more reasonably, this is necessary infrastructure for some
aspects of the HOT patch.  Pavan Deolasee, with a little further stuff from
me.

src/backend/commands/indexcmds.c
src/backend/utils/cache/inval.c
src/backend/utils/cache/relcache.c

index 4c218eb925a1d51c2c20af592e63db0267ee3d11..49cd0f30b33be21adf1a19569a50d502658563a5 100644 (file)
@@ -41,6 +41,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/inval.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/relcache.h"
@@ -514,7 +515,9 @@ DefineIndex(RangeVar *heapRelation,
        for (ixcnt = 0; ixcnt < snapshot->xcnt; ixcnt++)
                XactLockTableWait(snapshot->xip[ixcnt]);
 
-       /* Index can now be marked valid -- update its pg_index entry */
+       /*
+        * Index can now be marked valid -- update its pg_index entry
+        */
        pg_index = heap_open(IndexRelationId, RowExclusiveLock);
 
        indexTuple = SearchSysCacheCopy(INDEXRELID,
@@ -534,6 +537,15 @@ DefineIndex(RangeVar *heapRelation,
 
        heap_close(pg_index, RowExclusiveLock);
 
+       /*
+        * The pg_index update will cause backends (including this one) to update
+        * relcache entries for the index itself, but we should also send a
+        * relcache inval on the parent table to force replanning of cached plans.
+        * Otherwise existing sessions might fail to use the new index where it
+        * would be useful.
+        */
+       CacheInvalidateRelcacheByRelid(heaprelid.relId);
+
        /*
         * Last thing to do is release the session-level lock on the parent table.
         */
index da721c1699dd6fa16f887c45bee8573fe5265302..574c18414cbf057251b47de29e473aa9c6def07f 100644 (file)
@@ -590,12 +590,27 @@ PrepareForTupleInvalidation(Relation relation, HeapTuple tuple)
                 * KLUGE ALERT: we always send the relcache event with MyDatabaseId,
                 * even if the rel in question is shared (which we can't easily tell).
                 * This essentially means that only backends in this same database
-                * will react to the relcache flush request. This is in fact
+                * will react to the relcache flush request.  This is in fact
                 * appropriate, since only those backends could see our pg_attribute
-                * change anyway.  It looks a bit ugly though.
+                * change anyway.  It looks a bit ugly though.  (In practice, shared
+                * relations can't have schema changes after bootstrap, so we should
+                * never come here for a shared rel anyway.)
                 */
                databaseId = MyDatabaseId;
        }
+       else if (tupleRelId == IndexRelationId)
+       {
+               Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple);
+
+               /*
+                * When a pg_index row is updated, we should send out a relcache inval
+                * for the index relation.  As above, we don't know the shared status
+                * of the index, but in practice it doesn't matter since indexes of
+                * shared catalogs can't have such updates.
+                */
+               relationId = indextup->indexrelid;
+               databaseId = MyDatabaseId;
+       }
        else
                return;
 
index 9cb4df0ee66bb673ecd7bbfe60f03cc20c4b7afd..f16c8c5fb572a6951c478b12516ef68ab6470ae5 100644 (file)
@@ -181,7 +181,7 @@ static HTAB *OpClassCache = NULL;
 
 static void RelationClearRelation(Relation relation, bool rebuild);
 
-static void RelationReloadClassinfo(Relation relation);
+static void RelationReloadIndexInfo(Relation relation);
 static void RelationFlushRelation(Relation relation);
 static bool load_relcache_init_file(void);
 static void write_relcache_init_file(void);
@@ -1504,7 +1504,7 @@ RelationIdGetRelation(Oid relationId)
                RelationIncrementReferenceCount(rd);
                /* revalidate nailed index if necessary */
                if (!rd->rd_isvalid)
-                       RelationReloadClassinfo(rd);
+                       RelationReloadIndexInfo(rd);
                return rd;
        }
 
@@ -1579,24 +1579,24 @@ RelationClose(Relation relation)
 }
 
 /*
- * RelationReloadClassinfo - reload the pg_class row (only)
+ * RelationReloadIndexInfo - reload minimal information for an open index
  *
- *     This function is used only for indexes.  We currently allow only the
- *     pg_class row of an existing index to change (to support changes of
- *     owner, tablespace, or relfilenode), not its pg_index row or other
- *     subsidiary index schema information.  Therefore it's sufficient to do
- *     this when we get an SI invalidation.  Furthermore, there are cases
- *     where it's necessary not to throw away the index information, especially
- *     for "nailed" indexes which we are unable to rebuild on-the-fly.
+ *     This function is used only for indexes.  A relcache inval on an index
+ *     can mean that its pg_class or pg_index row changed.  There are only
+ *     very limited changes that are allowed to an existing index's schema,
+ *     so we can update the relcache entry without a complete rebuild; which
+ *     is fortunate because we can't rebuild an index entry that is "nailed"
+ *     and/or in active use.  We support full replacement of the pg_class row,
+ *     as well as updates of a few simple fields of the pg_index row.
  *
- *     We can't necessarily reread the pg_class row right away; we might be
+ *     We can't necessarily reread the catalog rows right away; we might be
  *     in a failed transaction when we receive the SI notification.  If so,
  *     RelationClearRelation just marks the entry as invalid by setting
  *     rd_isvalid to false.  This routine is called to fix the entry when it
  *     is next needed.
  */
 static void
-RelationReloadClassinfo(Relation relation)
+RelationReloadIndexInfo(Relation relation)
 {
        bool            indexOK;
        HeapTuple       pg_class_tuple;
@@ -1635,6 +1635,33 @@ RelationReloadClassinfo(Relation relation)
        if (relation->rd_amcache)
                pfree(relation->rd_amcache);
        relation->rd_amcache = NULL;
+
+       /*
+        * For a non-system index, there are fields of the pg_index row that are
+        * allowed to change, so re-read that row and update the relcache entry.
+        * Most of the info derived from pg_index (such as support function lookup
+        * info) cannot change, and indeed the whole point of this routine is to
+        * update the relcache entry without clobbering that data; so wholesale
+        * replacement is not appropriate.
+        */
+       if (!IsSystemRelation(relation))
+       {
+               HeapTuple       tuple;
+               Form_pg_index index;
+
+               tuple = SearchSysCache(INDEXRELID,
+                                                          ObjectIdGetDatum(RelationGetRelid(relation)),
+                                                          0, 0, 0);
+               if (!HeapTupleIsValid(tuple))
+                               elog(ERROR, "cache lookup failed for index %u",
+                                        RelationGetRelid(relation));
+               index = (Form_pg_index) GETSTRUCT(tuple);
+
+               relation->rd_index->indisvalid = index->indisvalid;
+
+               ReleaseSysCache(tuple);
+       }
+
        /* Okay, now it's valid again */
        relation->rd_isvalid = true;
 }
@@ -1683,7 +1710,7 @@ RelationClearRelation(Relation relation, bool rebuild)
                {
                        relation->rd_isvalid = false;           /* needs to be revalidated */
                        if (relation->rd_refcnt > 1)
-                               RelationReloadClassinfo(relation);
+                               RelationReloadIndexInfo(relation);
                }
                return;
        }
@@ -1693,14 +1720,14 @@ RelationClearRelation(Relation relation, bool rebuild)
         * have valid index support information.  This avoids problems with active
         * use of the index support information.  As with nailed indexes, we
         * re-read the pg_class row to handle possible physical relocation of the
-        * index.
+        * index, and we check for pg_index updates too.
         */
        if (relation->rd_rel->relkind == RELKIND_INDEX &&
                relation->rd_refcnt > 0 &&
                relation->rd_indexcxt != NULL)
        {
                relation->rd_isvalid = false;   /* needs to be revalidated */
-               RelationReloadClassinfo(relation);
+               RelationReloadIndexInfo(relation);
                return;
        }