]> granicus.if.org Git - postgresql/commitdiff
Repair some REINDEX problems per recent discussions. The relcache is
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 24 Sep 2003 18:54:02 +0000 (18:54 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 24 Sep 2003 18:54:02 +0000 (18:54 +0000)
now able to cope with assigning new relfilenode values to nailed-in-cache
indexes, so they can be reindexed using the fully crash-safe method.  This
leaves only shared system indexes as special cases.  Remove the 'index
deactivation' code, since it provides no useful protection in the shared-
index case.  Require reindexing of shared indexes to be done in standalone
mode, but remove other restrictions on REINDEX.  -P (IgnoreSystemIndexes)
now prevents using indexes for lookups, but does not disable index updates.
It is therefore safe to allow from PGOPTIONS.  Upshot: reindexing system catalogs
can be done without a standalone backend for all cases except
shared catalogs.

22 files changed:
doc/src/sgml/ref/postgres-ref.sgml
doc/src/sgml/ref/reindex.sgml
src/backend/access/index/genam.c
src/backend/access/transam/xact.c
src/backend/catalog/index.c
src/backend/catalog/pg_largeobject.c
src/backend/commands/functioncmds.c
src/backend/commands/indexcmds.c
src/backend/commands/vacuum.c
src/backend/executor/execUtils.c
src/backend/executor/nodeIndexscan.c
src/backend/storage/ipc/sinval.c
src/backend/tcop/postgres.c
src/backend/tcop/utility.c
src/backend/utils/cache/relcache.c
src/backend/utils/cache/syscache.c
src/backend/utils/init/miscinit.c
src/include/catalog/index.h
src/include/miscadmin.h
src/include/utils/errcodes.h
src/include/utils/rel.h
src/include/utils/relcache.h

index b80c9caafac0b55f49cec418849b4b34c4494a4a..279d8875e0ee58881a7c6887f07fd462a33f404c 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/postgres-ref.sgml,v 1.36 2003/09/18 20:30:15 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/postgres-ref.sgml,v 1.37 2003/09/24 18:54:01 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -177,9 +177,9 @@ PostgreSQL documentation
       <term><option>-P</option></term>
       <listitem>
        <para>
-       Ignore system indexes while scanning/updating system tables. The
-       <command>REINDEX</command> command for system tables/indexes
-       requires this option to be used.
+       Ignore system indexes when reading system tables (but still update
+       the indexes when modifying the tables).  This is useful when
+       recovering from damaged system indexes.
        </para>
       </listitem>
      </varlistentry>
index 29b96e462c3b5c7c69fd86f27da4e3d1e85c7a5a..d945112de79dcae79bf0776b930b9346492d4dd0 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/reindex.sgml,v 1.20 2003/09/11 21:42:20 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/reindex.sgml,v 1.21 2003/09/24 18:54:01 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -56,43 +56,6 @@ REINDEX { DATABASE | TABLE | INDEX } <replaceable class="PARAMETER">name</replac
     </listitem>
    </itemizedlist>
   </para>
-
-  <para>
-   If you suspect corruption of an index on a user table, you can
-   simply rebuild that index, or all indexes on the table, using
-   <command>REINDEX INDEX</command> or <command>REINDEX
-   TABLE</command>.  Another approach to dealing with a corrupted
-   user-table index is just to drop and recreate it.  This may in fact
-   be preferable if you would like to maintain some semblance of
-   normal operation on the table meanwhile.  <command>REINDEX</>
-   acquires exclusive lock on the table, while <command>CREATE
-   INDEX</> only locks out writes not reads of the table.
-  </para>
-
-  <para>
-   Things are more difficult if you need to recover from corruption of
-   an index on a system table.  In this case it's important for the
-   system to not have used any of the suspect indexes itself.
-   (Indeed, in this sort of scenario you may find that server
-   processes are crashing immediately at start-up, due to reliance on
-   the corrupted indexes.)  To recover safely, the server must be shut
-   down and a stand-alone <productname>PostgreSQL</productname> server
-   must be started instead with the command-line options
-   <option>-O</option> and <option>-P</option>.  (These options allow
-   system table modifications and prevent use of system indexes,
-   respectively.)  Then, <command>REINDEX DATABASE</>,
-   <command>REINDEX TABLE</>, or <command>REINDEX INDEX</> can be
-   issued, depending on how much you want to reconstruct.  If in
-   doubt, use <command>REINDEX DATABASE FORCE</> to force
-   reconstruction of all system indexes in the database.  Then quit
-   the standalone server session and restart the real server.
-  </para>
-
-  <para>
-   See the <xref linkend="app-postgres"> reference page for more
-   information about how to interact with the stand-alone server
-   interface.
-  </para>
  </refsect1>
   
  <refsect1>
@@ -104,8 +67,8 @@ REINDEX { DATABASE | TABLE | INDEX } <replaceable class="PARAMETER">name</replac
     <listitem>
      <para>
       Recreate all system indexes of a specified database. Indexes on
-      user tables are not included. This form of <command>REINDEX</>
-      can only be used in stand-alone mode (see above).
+      user tables are not processed.  Also, indexes on shared system
+      catalogs are skipped except in stand-alone mode (see below).
      </para>
     </listitem>
    </varlistentry>
@@ -114,7 +77,8 @@ REINDEX { DATABASE | TABLE | INDEX } <replaceable class="PARAMETER">name</replac
     <term><literal>TABLE</literal></term>
     <listitem>
      <para>
-      Recreate all indexes of a specified table.
+      Recreate all indexes of a specified table.  If the table has a
+      secondary <quote>TOAST</> table, that is reindexed as well.
      </para>
     </listitem>
    </varlistentry>
@@ -142,16 +106,93 @@ REINDEX { DATABASE | TABLE | INDEX } <replaceable class="PARAMETER">name</replac
     <term><literal>FORCE</literal></term>
     <listitem>
      <para>
-      Force rebuild of system indexes.  Without this key word,
-      <command>REINDEX</> skips system indexes that are not marked
-      invalid.  <literal>FORCE</> is irrelevant for <command>REINDEX
-      INDEX</> or when reindexing user indexes.
+      This is an obsolete option; it is ignored if specified.
      </para>
     </listitem>
    </varlistentry>
   </variablelist>
  </refsect1>
 
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   If you suspect corruption of an index on a user table, you can
+   simply rebuild that index, or all indexes on the table, using
+   <command>REINDEX INDEX</command> or <command>REINDEX
+   TABLE</command>.  Another approach to dealing with a corrupted
+   user-table index is just to drop and recreate it.  This may in fact
+   be preferable if you would like to maintain some semblance of
+   normal operation on the table meanwhile.  <command>REINDEX</>
+   acquires exclusive lock on the table, while <command>CREATE
+   INDEX</> only locks out writes not reads of the table.
+  </para>
+
+  <para>
+   Things are more difficult if you need to recover from corruption of
+   an index on a system table.  In this case it's important for the
+   system to not have used any of the suspect indexes itself.
+   (Indeed, in this sort of scenario you may find that server
+   processes are crashing immediately at start-up, due to reliance on
+   the corrupted indexes.)  To recover safely, the server must be started
+   with the <option>-P</option> option, which prevents it from using
+   indexes for system catalog lookups.
+  </para>
+
+  <para>
+   One way to do this is to shut down the postmaster and start a stand-alone
+   <productname>PostgreSQL</productname> server
+   with the <option>-P</option> option included on its command line.
+   Then, <command>REINDEX DATABASE</>,
+   <command>REINDEX TABLE</>, or <command>REINDEX INDEX</> can be
+   issued, depending on how much you want to reconstruct.  If in
+   doubt, use <command>REINDEX DATABASE</> to select
+   reconstruction of all system indexes in the database.  Then quit
+   the standalone server session and restart the regular server.
+   See the <xref linkend="app-postgres"> reference page for more
+   information about how to interact with the stand-alone server
+   interface.
+  </para>
+
+  <para>
+   Alternatively, a regular server session can be started with
+   <option>-P</option> included in its command line options.
+   The method for doing this varies across clients, but in all
+   <application>libpq</>-based clients, it is possible to set
+   the <envar>PGOPTIONS</envar> environment variable to <literal>-P</>
+   before starting the client.  Note that while this method does not
+   require locking out other clients, it may still be wise to prevent
+   other users from connecting to the damaged database until repairs
+   have been completed.
+  </para>
+
+  <para>
+   If corruption is suspected in the indexes of any of the shared
+   system catalogs (<structname>pg_database</structname>,
+   <structname>pg_group</structname>, or
+   <structname>pg_shadow</structname>), then a standalone server
+   must be used to repair it.  <command>REINDEX</> will not process
+   shared catalogs in multiuser mode.
+  </para>
+
+  <para>
+   For all indexes except the shared system catalogs, <command>REINDEX</>
+   is crash-safe and transaction-safe.  <command>REINDEX</> is not
+   crash-safe for shared indexes, which is why this case is disallowed
+   during normal operation.  If a failure occurs while reindexing one
+   of these catalogs in standalone mode, it is important that the failure
+   be rectified and the <command>REINDEX</> operation redone
+   before attempting to restart the regular server.
+  </para>
+
+  <para>
+   Prior to <productname>PostgreSQL</productname> 7.4, <command>REINDEX
+   TABLE</> did not automatically process TOAST tables, and so those had
+   to be reindexed by separate commands.  This is still possible, but
+   redundant.
+  </para>
+ </refsect1>
+
  <refsect1>
   <title>Examples</title>
 
@@ -172,11 +213,15 @@ REINDEX INDEX my_index;
   </para>
 
   <para>
-   Rebuild all system indexes (this will only work in a stand-alone
-   server session):
+   Rebuild all system indexes in a particular database, without trusting them
+   to be valid already:
 
 <programlisting>
-REINDEX DATABASE my_database FORCE;
+$ <userinput>export PGOPTIONS="-P"</userinput>
+$ <userinput>psql broken_db</userinput>
+...
+broken_db=> REINDEX DATABASE broken_db;
+broken_db=> \q
 </programlisting>
   </para>
  </refsect1>
index eaa0d172e546ca18b9a5b5593a830d54b2b3e42a..a362cd8cfaf6f55d27d5809b79b53722091099a5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.40 2003/08/04 02:39:57 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.41 2003/09/24 18:54:01 tgl Exp $
  *
  * NOTES
  *       many of the old access method routines have been turned into
@@ -184,21 +184,32 @@ systable_beginscan(Relation heapRelation,
                                   int nkeys, ScanKey key)
 {
        SysScanDesc sysscan;
+       Relation        irel;
+
+       if (indexOK && !IsIgnoringSystemIndexes())
+       {
+               /* We assume it's a system index, so index_openr is OK */
+               irel = index_openr(indexRelname);
+
+               if (ReindexIsProcessingIndex(RelationGetRelid(irel)))
+               {
+                       /* oops, can't use index that's being rebuilt */
+                       index_close(irel);
+                       irel = NULL;
+               }
+       }
+       else
+               irel = NULL;
 
        sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData));
 
        sysscan->heap_rel = heapRelation;
+       sysscan->irel = irel;
 
-       if (indexOK &&
-               heapRelation->rd_rel->relhasindex &&
-               !IsIgnoringSystemIndexes())
+       if (irel)
        {
-               Relation        irel;
                int                     i;
 
-               /* We assume it's a system index, so index_openr is OK */
-               sysscan->irel = irel = index_openr(indexRelname);
-
                /*
                 * Change attribute numbers to be index column numbers.
                 *
@@ -210,13 +221,13 @@ systable_beginscan(Relation heapRelation,
                        Assert(key[i].sk_attno == irel->rd_index->indkey[i]);
                        key[i].sk_attno = i + 1;
                }
+
                sysscan->iscan = index_beginscan(heapRelation, irel, snapshot,
                                                                                 nkeys, key);
                sysscan->scan = NULL;
        }
        else
        {
-               sysscan->irel = NULL;
                sysscan->scan = heap_beginscan(heapRelation, snapshot, nkeys, key);
                sysscan->iscan = NULL;
        }
index e632ae9e1f4f56195f5f3b8f20fd480eb3b52100..e17a3f3c37269b4051a739982cfb27cb2e633cf8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.152 2003/08/08 21:41:28 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.153 2003/09/24 18:54:01 tgl Exp $
  *
  * NOTES
  *             Transaction aborts can now occur two ways:
@@ -834,8 +834,6 @@ StartTransaction(void)
         */
        s->state = TRANS_START;
 
-       SetReindexProcessing(false);
-
        /*
         * generate a new transaction id
         */
@@ -1085,6 +1083,7 @@ AbortTransaction(void)
        AtEOXact_Namespace(false);
        AtEOXact_CatCache(false);
        AtEOXact_Files();
+       SetReindexProcessing(InvalidOid, InvalidOid);
        pgstat_count_xact_rollback();
 
        /*
index 02eba08af41d78c922f708584b097a478d72b40c..ed560fb8915b724b304fe703ceb2eaacaa0981d8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.216 2003/09/23 01:51:09 inoue Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.217 2003/09/24 18:54:01 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -76,27 +76,8 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
                                        Oid *classOids,
                                        bool primary);
 static Oid     IndexGetRelation(Oid indexId);
-static bool activate_index(Oid indexId, bool activate, bool inplace);
 
 
-static bool reindexing = false;
-
-
-bool
-SetReindexProcessing(bool reindexmode)
-{
-       bool            old = reindexing;
-
-       reindexing = reindexmode;
-       return old;
-}
-
-bool
-IsReindexProcessing(void)
-{
-       return reindexing;
-}
-
 /*
  *             ConstructTupleDescriptor
  *
@@ -498,8 +479,6 @@ index_create(Oid heapRelationId,
        Oid                     indexoid;
        int                     i;
 
-       SetReindexProcessing(false);
-
        /*
         * Only SELECT ... FOR UPDATE are allowed while doing this
         */
@@ -973,46 +952,6 @@ FormIndexDatum(IndexInfo *indexInfo,
 }
 
 
-/* ---------------------------------------------
- *             Indexes of the relation active ?
- *
- * Caller must hold an adequate lock on the relation to ensure the
- * answer won't be changing.
- * ---------------------------------------------
- */
-bool
-IndexesAreActive(Relation heaprel)
-{
-       bool            isactive;
-       Relation        indexRelation;
-       HeapScanDesc scan;
-       ScanKeyData entry;
-
-       if (heaprel->rd_rel->relkind != RELKIND_RELATION &&
-               heaprel->rd_rel->relkind != RELKIND_TOASTVALUE)
-               ereport(ERROR,
-                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                errmsg("relation \"%s\" isn't an indexable relation",
-                                               RelationGetRelationName(heaprel))));
-
-       /* If pg_class.relhasindex is set, indexes are active */
-       isactive = heaprel->rd_rel->relhasindex;
-       if (isactive)
-               return isactive;
-
-       /* Otherwise, look to see if there are any indexes */
-       indexRelation = heap_openr(IndexRelationName, AccessShareLock);
-       ScanKeyEntryInitialize(&entry, 0,
-                                                  Anum_pg_index_indrelid, F_OIDEQ,
-                                                  ObjectIdGetDatum(RelationGetRelid(heaprel)));
-       scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
-       if (heap_getnext(scan, ForwardScanDirection) == NULL)
-               isactive = true;                /* no indexes, so report "active" */
-       heap_endscan(scan);
-       heap_close(indexRelation, AccessShareLock);
-       return isactive;
-}
-
 /* ----------------
  *             set relhasindex of relation's pg_class entry
  *
@@ -1038,12 +977,13 @@ setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid)
        HeapScanDesc pg_class_scan = NULL;
 
        /*
-        * Find the tuple to update in pg_class.
+        * Find the tuple to update in pg_class.  In bootstrap mode we can't
+        * use heap_update, so cheat and overwrite the tuple in-place.  In
+        * normal processing, make a copy to scribble on.
         */
        pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
 
-       if (!IsIgnoringSystemIndexes() &&
-               (!IsReindexProcessing() || pg_class->rd_rel->relhasindex))
+       if (!IsBootstrapProcessingMode())
        {
                tuple = SearchSysCacheCopy(RELOID,
                                                                   ObjectIdGetDatum(relid),
@@ -1064,15 +1004,13 @@ setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid)
 
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "could not find tuple for relation %u", relid);
+       classtuple = (Form_pg_class) GETSTRUCT(tuple);
+
+       /* Apply required updates */
 
-       /*
-        * Update fields in the pg_class tuple.
-        */
        if (pg_class_scan)
                LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
 
-       classtuple = (Form_pg_class) GETSTRUCT(tuple);
-
        if (classtuple->relhasindex != hasindex)
        {
                classtuple->relhasindex = hasindex;
@@ -1141,80 +1079,48 @@ setNewRelfilenode(Relation relation)
        Relation        pg_class;
        HeapTuple       tuple;
        Form_pg_class rd_rel;
-       HeapScanDesc pg_class_scan = NULL;
-       bool            in_place_upd;
        RelationData workrel;
 
-       Assert(!IsSystemRelation(relation) || IsToastRelation(relation) ||
+       /* Can't change relfilenode for nailed tables (indexes ok though) */
+       Assert(!relation->rd_isnailed ||
                   relation->rd_rel->relkind == RELKIND_INDEX);
+       /* Can't change for shared tables or indexes */
+       Assert(!relation->rd_rel->relisshared);
 
        /* Allocate a new relfilenode */
        newrelfilenode = newoid();
 
        /*
-        * Find the RELATION relation tuple for the given relation.
+        * Find the pg_class tuple for the given relation.  This is not used
+        * during bootstrap, so okay to use heap_update always.
         */
        pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
 
-       in_place_upd = IsIgnoringSystemIndexes();
-
-       if (!in_place_upd)
-       {
-               tuple = SearchSysCacheCopy(RELOID,
-                                                       ObjectIdGetDatum(RelationGetRelid(relation)),
-                                                                  0, 0, 0);
-       }
-       else
-       {
-               ScanKeyData key[1];
-
-               ScanKeyEntryInitialize(&key[0], 0,
-                                                          ObjectIdAttributeNumber,
-                                                          F_OIDEQ,
-                                                  ObjectIdGetDatum(RelationGetRelid(relation)));
-
-               pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
-               tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
-       }
-
+       tuple = SearchSysCacheCopy(RELOID,
+                                                          ObjectIdGetDatum(RelationGetRelid(relation)),
+                                                          0, 0, 0);
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "could not find tuple for relation %u",
                         RelationGetRelid(relation));
        rd_rel = (Form_pg_class) GETSTRUCT(tuple);
 
-       /* schedule unlinking old relfilenode */
-       smgrunlink(DEFAULT_SMGR, relation);
-
        /* create another storage file. Is it a little ugly ? */
+       /* NOTE: any conflict in relfilenode value will be caught here */
        memcpy((char *) &workrel, relation, sizeof(RelationData));
        workrel.rd_fd = -1;
        workrel.rd_node.relNode = newrelfilenode;
        heap_storage_create(&workrel);
        smgrclose(DEFAULT_SMGR, &workrel);
 
+       /* schedule unlinking old relfilenode */
+       smgrunlink(DEFAULT_SMGR, relation);
+
        /* update the pg_class row */
-       if (in_place_upd)
-       {
-               LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
-               rd_rel->relfilenode = newrelfilenode;
-               LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
-               WriteNoReleaseBuffer(pg_class_scan->rs_cbuf);
-               BufferSync();
-               /* Send out shared cache inval if necessary */
-               if (!IsBootstrapProcessingMode())
-                       CacheInvalidateHeapTuple(pg_class, tuple);
-       }
-       else
-       {
-               rd_rel->relfilenode = newrelfilenode;
-               simple_heap_update(pg_class, &tuple->t_self, tuple);
-               CatalogUpdateIndexes(pg_class, tuple);
-       }
+       rd_rel->relfilenode = newrelfilenode;
+       simple_heap_update(pg_class, &tuple->t_self, tuple);
+       CatalogUpdateIndexes(pg_class, tuple);
 
-       if (!pg_class_scan)
-               heap_freetuple(tuple);
-       else
-               heap_endscan(pg_class_scan);
+       heap_freetuple(tuple);
 
        heap_close(pg_class, RowExclusiveLock);
 
@@ -1264,11 +1170,21 @@ UpdateStats(Oid relid, double reltuples)
        whichRel = relation_open(relid, ShareLock);
 
        /*
-        * Find the RELATION relation tuple for the given relation.
+        * Find the tuple to update in pg_class.  Normally we make a copy of
+        * the tuple using the syscache, modify it, and apply heap_update.
+        * But in bootstrap mode we can't use heap_update, so we cheat and
+        * overwrite the tuple in-place.
+        *
+        * We also must cheat if reindexing pg_class itself, because the
+        * target index may presently not be part of the set of indexes that
+        * CatalogUpdateIndexes would update (see reindex_relation).  In this
+        * case the stats updates will not be WAL-logged and so could be lost
+        * in a crash.  This seems OK considering VACUUM does the same thing.
         */
        pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
 
-       in_place_upd = (IsIgnoringSystemIndexes() || IsReindexProcessing());
+       in_place_upd = IsBootstrapProcessingMode() ||
+               ReindexIsProcessingHeap(RelationGetRelid(pg_class));
 
        if (!in_place_upd)
        {
@@ -1291,6 +1207,7 @@ UpdateStats(Oid relid, double reltuples)
 
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "could not find tuple for relation %u", relid);
+       rd_rel = (Form_pg_class) GETSTRUCT(tuple);
 
        /*
         * Figure values to insert.
@@ -1331,18 +1248,12 @@ UpdateStats(Oid relid, double reltuples)
         * also reduces the window wherein concurrent CREATE INDEX commands
         * may conflict.)
         */
-       rd_rel = (Form_pg_class) GETSTRUCT(tuple);
-
        if (rd_rel->relpages != (int32) relpages ||
                rd_rel->reltuples != (float4) reltuples)
        {
                if (in_place_upd)
                {
-                       /*
-                        * At bootstrap time, we don't need to worry about concurrency
-                        * or visibility of changes, so we cheat.  Also cheat if
-                        * REINDEX.
-                        */
+                       /* Bootstrap or reindex case: overwrite fields in place. */
                        LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE);
                        rd_rel->relpages = (int32) relpages;
                        rd_rel->reltuples = (float4) reltuples;
@@ -1562,10 +1473,13 @@ IndexBuildHeapScan(Relation heapRelation,
                                         * should not see any tuples inserted by open
                                         * transactions --- unless it's our own transaction.
                                         * (Consider INSERT followed by CREATE INDEX within a
-                                        * transaction.)
+                                        * transaction.)  An exception occurs when reindexing
+                                        * a system catalog, because we often release lock on
+                                        * system catalogs before committing.
                                         */
                                        if (!TransactionIdIsCurrentTransactionId(
-                                                         HeapTupleHeaderGetXmin(heapTuple->t_data)))
+                                                         HeapTupleHeaderGetXmin(heapTuple->t_data))
+                                               && !IsSystemRelation(heapRelation))
                                                elog(ERROR, "concurrent insert in progress");
                                        indexIt = true;
                                        tupleIsAlive = true;
@@ -1577,10 +1491,13 @@ IndexBuildHeapScan(Relation heapRelation,
                                         * should not see any tuples deleted by open
                                         * transactions --- unless it's our own transaction.
                                         * (Consider DELETE followed by CREATE INDEX within a
-                                        * transaction.)
+                                        * transaction.)  An exception occurs when reindexing
+                                        * a system catalog, because we often release lock on
+                                        * system catalogs before committing.
                                         */
                                        if (!TransactionIdIsCurrentTransactionId(
-                                                         HeapTupleHeaderGetXmax(heapTuple->t_data)))
+                                                         HeapTupleHeaderGetXmax(heapTuple->t_data))
+                                               && !IsSystemRelation(heapRelation))
                                                elog(ERROR, "concurrent delete in progress");
                                        indexIt = true;
                                        tupleIsAlive = false;
@@ -1690,81 +1607,57 @@ IndexGetRelation(Oid indexId)
        return result;
 }
 
-/* ---------------------------------
- * activate_index -- activate/deactivate the specified index.
- *             Note that currently PostgreSQL doesn't hold the
- *             status per index
- * ---------------------------------
- */
-static bool
-activate_index(Oid indexId, bool activate, bool inplace)
-{
-       if (!activate)                          /* Currently does nothing */
-               return true;
-       return reindex_index(indexId, false, inplace);
-}
-
-/* --------------------------------
- * reindex_index - This routine is used to recreate an index
- * --------------------------------
+/*
+ * reindex_index - This routine is used to recreate a single index
  */
-bool
-reindex_index(Oid indexId, bool force, bool inplace)
+void
+reindex_index(Oid indexId)
 {
        Relation        iRel,
                                heapRelation;
        IndexInfo  *indexInfo;
        Oid                     heapId;
-       bool            old;
+       bool            inplace;
 
        /*
         * Open our index relation and get an exclusive lock on it.
         *
-        * Note: doing this before opening the parent heap relation means there's
-        * a possibility for deadlock failure against another xact that is
-        * doing normal accesses to the heap and index.  However, it's not
-        * real clear why you'd be needing to do REINDEX on a table that's in
-        * active use, so I'd rather have the protection of making sure the
-        * index is locked down.
+        * Note: for REINDEX INDEX, doing this before opening the parent heap
+        * relation means there's a possibility for deadlock failure against
+        * another xact that is doing normal accesses to the heap and index.
+        * However, it's not real clear why you'd be wanting to do REINDEX INDEX
+        * on a table that's in active use, so I'd rather have the protection of
+        * making sure the index is locked down.  In the REINDEX TABLE and
+        * REINDEX DATABASE cases, there is no problem because caller already
+        * holds exclusive lock on the parent table.
         */
        iRel = index_open(indexId);
        LockRelation(iRel, AccessExclusiveLock);
 
-       old = SetReindexProcessing(true);
-
        /* Get OID of index's parent table */
        heapId = iRel->rd_index->indrelid;
 
-       /* Open the parent heap relation */
+       /* Open and lock the parent heap relation */
        heapRelation = heap_open(heapId, AccessExclusiveLock);
 
+       SetReindexProcessing(heapId, indexId);
+
        /*
         * If it's a shared index, we must do inplace processing (because we
-        * have no way to update relfilenode in other databases).  Also, if
-        * it's a nailed-in-cache index, we must do inplace processing because
-        * the relcache can't cope with changing its relfilenode.
+        * have no way to update relfilenode in other databases).  Otherwise
+        * we can do it the normal transaction-safe way.
         *
-        * In either of these cases, we are definitely processing a system index,
-        * so we'd better be ignoring system indexes.
+        * Since inplace processing isn't crash-safe, we only allow it in a
+        * standalone backend.  (In the REINDEX TABLE and REINDEX DATABASE cases,
+        * the caller should have detected this.)
         */
-       if (iRel->rd_rel->relisshared)
-       {
-               if (!IsIgnoringSystemIndexes())
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-                                  errmsg("the target relation %u is shared", indexId)));
-               inplace = true;
-       }
-#ifndef ENABLE_REINDEX_NAILED_RELATIONS
-       if (iRel->rd_isnailed)
-       {
-               if (!IsIgnoringSystemIndexes())
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-                                  errmsg("the target relation %u is nailed", indexId)));
-               inplace = true;
-       }
-#endif /* ENABLE_REINDEX_NAILED_RELATIONS */
+       inplace = iRel->rd_rel->relisshared;
+
+       if (inplace && IsUnderPostmaster)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("shared index \"%s\" can only be reindexed in standalone mode",
+                                               RelationGetRelationName(iRel))));
 
        /* Fetch info needed for index_build */
        indexInfo = BuildIndexInfo(iRel);
@@ -1797,160 +1690,94 @@ reindex_index(Oid indexId, bool force, bool inplace)
         * index_build will close both the heap and index relations (but not
         * give up the locks we hold on them).  So we're done.
         */
-
-       SetReindexProcessing(old);
-
-       return true;
+       SetReindexProcessing(InvalidOid, InvalidOid);
 }
 
 /*
- * ----------------------------
- * activate_indexes_of_a_table
- *     activate/deactivate indexes of the specified table.
+ * reindex_relation - This routine is used to recreate all indexes
+ * of a relation (and its toast relation too, if any).
  *
- * Caller must already hold exclusive lock on the table.
- * ----------------------------
+ * Returns true if any indexes were rebuilt.
  */
 bool
-activate_indexes_of_a_table(Relation heaprel, bool activate)
+reindex_relation(Oid relid)
 {
-       if (IndexesAreActive(heaprel))
-       {
-               if (!activate)
-                       setRelhasindex(RelationGetRelid(heaprel), false, false,
-                                                  InvalidOid);
-               else
-                       return false;
-       }
-       else
-       {
-               if (activate)
-                       reindex_relation(RelationGetRelid(heaprel), false);
-               else
-                       return false;
-       }
-       return true;
-}
-
-/* --------------------------------
- * reindex_relation - This routine is used to recreate indexes
- * of a relation.
- * --------------------------------
- */
-bool
-reindex_relation(Oid relid, bool force)
-{
-       Relation        indexRelation;
-       ScanKeyData entry;
-       HeapScanDesc scan;
-       HeapTuple       indexTuple;
-       bool            old,
-                               reindexed;
-       bool            deactivate_needed,
-                               overwrite;
        Relation        rel;
-
-       overwrite = deactivate_needed = false;
+       Oid                     toast_relid;
+       bool            is_pg_class;
+       bool            result;
+       List       *indexIds,
+                          *doneIndexes,
+                          *indexId;
 
        /*
         * Ensure to hold an exclusive lock throughout the transaction. The
-        * lock could be less intensive (in the non-overwrite path) but for
-        * now it's AccessExclusiveLock for simplicity.
+        * lock could perhaps be less intensive (in the non-overwrite case)
+        * but for now it's AccessExclusiveLock for simplicity.
         */
        rel = heap_open(relid, AccessExclusiveLock);
 
+       toast_relid = rel->rd_rel->reltoastrelid;
+
        /*
-        * ignore the indexes of the target system relation while processing
-        * reindex.
+        * Get the list of index OIDs for this relation.  (We trust to the
+        * relcache to get this with a sequential scan if ignoring system
+        * indexes.)
         */
-       if (!IsIgnoringSystemIndexes() &&
-               IsSystemRelation(rel) && !IsToastRelation(rel))
-               deactivate_needed = true;
+       indexIds = RelationGetIndexList(rel);
 
        /*
-        * Shared system indexes must be overwritten because it's impossible
-        * to update pg_class tuples of all databases.
+        * reindex_index will attempt to update the pg_class rows for the
+        * relation and index.  If we are processing pg_class itself, we
+        * want to make sure that the updates do not try to insert index
+        * entries into indexes we have not processed yet.  (When we are
+        * trying to recover from corrupted indexes, that could easily
+        * cause a crash.)  We can accomplish this because CatalogUpdateIndexes
+        * will use the relcache's index list to know which indexes to update.
+        * We just force the index list to be only the stuff we've processed.
+        *
+        * It is okay to not insert entries into the indexes we have not
+        * processed yet because all of this is transaction-safe.  If we fail
+        * partway through, the updated rows are dead and it doesn't matter
+        * whether they have index entries.  Also, a new pg_class index will
+        * be created with an entry for its own pg_class row because we do
+        * setNewRelfilenode() before we do index_build().
         */
-       if (rel->rd_rel->relisshared)
+       is_pg_class = (RelationGetRelid(rel) == RelOid_pg_class);
+       doneIndexes = NIL;
+
+       /* Reindex all the indexes. */
+       foreach(indexId, indexIds)
        {
-               if (IsIgnoringSystemIndexes())
-               {
-                       overwrite = true;
-                       deactivate_needed = true;
-               }
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-                                        errmsg("the target relation %u is shared", relid)));
-       }
+               Oid             indexOid = lfirsto(indexId);
 
-       old = SetReindexProcessing(true);
+               if (is_pg_class)
+                       RelationSetIndexList(rel, doneIndexes);
 
-       if (deactivate_needed)
-       {
-               if (IndexesAreActive(rel))
-               {
-                       if (!force)
-                       {
-                               SetReindexProcessing(old);
-                               heap_close(rel, NoLock);
-                               return false;
-                       }
-                       activate_indexes_of_a_table(rel, false);
-                       CommandCounterIncrement();
-               }
+               reindex_index(indexOid);
+
+               CommandCounterIncrement();
+
+               if (is_pg_class)
+                       doneIndexes = lappendo(doneIndexes, indexOid);
        }
 
+       if (is_pg_class)
+               RelationSetIndexList(rel, indexIds);
+
        /*
-        * Continue to hold the lock.
+        * Close rel, but continue to hold the lock.
         */
        heap_close(rel, NoLock);
 
-       indexRelation = heap_openr(IndexRelationName, AccessShareLock);
-       ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid,
-                                                  F_OIDEQ, ObjectIdGetDatum(relid));
-       scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry);
-       reindexed = false;
-       while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
-       {
-               Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
-
-               if (activate_index(index->indexrelid, true, overwrite))
-                       reindexed = true;
-               else
-               {
-                       reindexed = false;
-                       break;
-               }
-       }
-       heap_endscan(scan);
-       heap_close(indexRelation, AccessShareLock);
-       if (reindexed)
-       {
-               /*
-                * Ok,we could use the reindexed indexes of the target system
-                * relation now.
-                */
-               if (deactivate_needed)
-               {
-                       if (!overwrite && relid == RelOid_pg_class)
-                       {
-                               /*
-                                * For pg_class, relhasindex should be set to true here in
-                                * place.
-                                */
-                               setRelhasindex(relid, true, false, InvalidOid);
-                               CommandCounterIncrement();
+       result = (indexIds != NIL);
 
-                               /*
-                                * However the following setRelhasindex() is needed to
-                                * keep consistency with WAL.
-                                */
-                       }
-                       setRelhasindex(relid, true, false, InvalidOid);
-               }
-       }
-       SetReindexProcessing(old);
+       /*
+        * If the relation has a secondary toast rel, reindex that too while we
+        * still hold the lock on the master table.
+        */
+       if (toast_relid != InvalidOid)
+               result |= reindex_relation(toast_relid);
 
-       return reindexed;
+       return result;
 }
index 3bfe0ca23b97ad2ffa765d9524cce432f263c021..850256716995c5b8ff85b252b38756529d70f4f1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_largeobject.c,v 1.16 2003/08/04 02:39:58 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_largeobject.c,v 1.17 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,32 +77,29 @@ LargeObjectDrop(Oid loid)
 {
        bool            found = false;
        Relation        pg_largeobject;
-       Relation        pg_lo_idx;
        ScanKeyData skey[1];
-       IndexScanDesc sd;
+       SysScanDesc sd;
        HeapTuple       tuple;
 
-       ScanKeyEntryInitialize(&skey[0],
-                                                  (bits16) 0x0,
-                                                  (AttrNumber) 1,
+       ScanKeyEntryInitialize(&skey[0], 0x0,
+                                                  (AttrNumber) Anum_pg_largeobject_loid,
                                                   (RegProcedure) F_OIDEQ,
                                                   ObjectIdGetDatum(loid));
 
-       pg_largeobject = heap_openr(LargeObjectRelationName, RowShareLock);
-       pg_lo_idx = index_openr(LargeObjectLOidPNIndex);
+       pg_largeobject = heap_openr(LargeObjectRelationName, RowExclusiveLock);
 
-       sd = index_beginscan(pg_largeobject, pg_lo_idx, SnapshotNow, 1, skey);
+       sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndex, true,
+                                                       SnapshotNow, 1, skey);
 
-       while ((tuple = index_getnext(sd, ForwardScanDirection)) != NULL)
+       while ((tuple = systable_getnext(sd)) != NULL)
        {
                simple_heap_delete(pg_largeobject, &tuple->t_self);
                found = true;
        }
 
-       index_endscan(sd);
+       systable_endscan(sd);
 
-       index_close(pg_lo_idx);
-       heap_close(pg_largeobject, RowShareLock);
+       heap_close(pg_largeobject, RowExclusiveLock);
 
        if (!found)
                ereport(ERROR,
@@ -115,32 +112,29 @@ LargeObjectExists(Oid loid)
 {
        bool            retval = false;
        Relation        pg_largeobject;
-       Relation        pg_lo_idx;
        ScanKeyData skey[1];
-       IndexScanDesc sd;
+       SysScanDesc sd;
        HeapTuple       tuple;
 
        /*
         * See if we can find any tuples belonging to the specified LO
         */
-       ScanKeyEntryInitialize(&skey[0],
-                                                  (bits16) 0x0,
-                                                  (AttrNumber) 1,
+       ScanKeyEntryInitialize(&skey[0], 0x0,
+                                                  (AttrNumber) Anum_pg_largeobject_loid,
                                                   (RegProcedure) F_OIDEQ,
                                                   ObjectIdGetDatum(loid));
 
-       pg_largeobject = heap_openr(LargeObjectRelationName, RowShareLock);
-       pg_lo_idx = index_openr(LargeObjectLOidPNIndex);
+       pg_largeobject = heap_openr(LargeObjectRelationName, AccessShareLock);
 
-       sd = index_beginscan(pg_largeobject, pg_lo_idx, SnapshotNow, 1, skey);
+       sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndex, true,
+                                                       SnapshotNow, 1, skey);
 
-       if ((tuple = index_getnext(sd, ForwardScanDirection)) != NULL)
+       if ((tuple = systable_getnext(sd)) != NULL)
                retval = true;
 
-       index_endscan(sd);
+       systable_endscan(sd);
 
-       index_close(pg_lo_idx);
-       heap_close(pg_largeobject, RowShareLock);
+       heap_close(pg_largeobject, AccessShareLock);
 
        return retval;
 }
index ea5ba1031317e00c5c9d2bea1ce77c500543dee5..328643c171e7a1697a11657b35b37c014e115536 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.34 2003/09/10 19:59:23 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.35 2003/09/24 18:54:01 tgl Exp $
  *
  * DESCRIPTION
  *       These routines take the parse tree and pick out the
@@ -1095,24 +1095,25 @@ DropCast(DropCastStmt *stmt)
 void
 DropCastById(Oid castOid)
 {
-       Relation        relation,
-                               index;
+       Relation        relation;
        ScanKeyData scankey;
-       IndexScanDesc scan;
+       SysScanDesc scan;
        HeapTuple       tuple;
 
        relation = heap_openr(CastRelationName, RowExclusiveLock);
-       index = index_openr(CastOidIndex);
 
        ScanKeyEntryInitialize(&scankey, 0x0,
-                                                  1, F_OIDEQ, ObjectIdGetDatum(castOid));
-       scan = index_beginscan(relation, index, SnapshotNow, 1, &scankey);
-       tuple = index_getnext(scan, ForwardScanDirection);
+                                                  ObjectIdAttributeNumber,
+                                                  F_OIDEQ,
+                                                  ObjectIdGetDatum(castOid));
+       scan = systable_beginscan(relation, CastOidIndex, true,
+                                                         SnapshotNow, 1, &scankey);
+
+       tuple = systable_getnext(scan);
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "could not find tuple for cast %u", castOid);
        simple_heap_delete(relation, &tuple->t_self);
-       index_endscan(scan);
 
-       index_close(index);
+       systable_endscan(scan);
        heap_close(relation, RowExclusiveLock);
 }
index 4c2221263d7321a3488870c00daf83f0809c63c5..c8ccab8d4e103d8f23f729d6b700f96f553d34e7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.108 2003/09/23 01:51:09 inoue Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.109 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -112,14 +112,6 @@ DefineIndex(RangeVar *heapRelation,
        relationId = RelationGetRelid(rel);
        namespaceId = RelationGetNamespace(rel);
 
-       if (!IsBootstrapProcessingMode() &&
-               IsSystemRelation(rel) &&
-               !IndexesAreActive(rel))
-               ereport(ERROR,
-                               (errcode(ERRCODE_INDEXES_DEACTIVATED),
-                                errmsg("existing indexes are inactive"),
-                                errhint("REINDEX the table first.")));
-
        heap_close(rel, NoLock);
 
        /*
@@ -599,10 +591,6 @@ ReindexIndex(RangeVar *indexRelation, bool force /* currently unused */ )
 {
        Oid                     indOid;
        HeapTuple       tuple;
-       bool            overwrite;
-
-       /* Choose in-place-or-not mode */
-       overwrite = IsIgnoringSystemIndexes();
 
        indOid = RangeVarGetRelid(indexRelation, false);
        tuple = SearchSysCache(RELOID,
@@ -617,37 +605,14 @@ ReindexIndex(RangeVar *indexRelation, bool force /* currently unused */ )
                                 errmsg("relation \"%s\" is not an index",
                                                indexRelation->relname)));
 
-       if (IsSystemClass((Form_pg_class) GETSTRUCT(tuple)) &&
-               !IsToastClass((Form_pg_class) GETSTRUCT(tuple)))
-       {
-               if (!allowSystemTableMods)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                        errmsg("permission denied: \"%s\" is a system index",
-                                                       indexRelation->relname),
-                                        errhint("Do REINDEX in standalone postgres with -O -P options.")));
-               if (!IsIgnoringSystemIndexes())
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                        errmsg("permission denied: \"%s\" is a system index",
-                                                       indexRelation->relname),
-                                        errhint("Do REINDEX in standalone postgres with -P -O options.")));
-       }
+       /* Check permissions */
+       if (!pg_class_ownercheck(indOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+                                          indexRelation->relname);
 
        ReleaseSysCache(tuple);
 
-       /*
-        * In-place REINDEX within a transaction block is dangerous, because
-        * if the transaction is later rolled back we have no way to undo
-        * truncation of the index's physical file.  Disallow it.
-        */
-       if (overwrite)
-               PreventTransactionChain((void *) indexRelation, "REINDEX");
-
-       if (!reindex_index(indOid, force, overwrite))
-               ereport(WARNING,
-                               (errmsg("index \"%s\" wasn't reindexed",
-                                               indexRelation->relname)));
+       reindex_index(indOid);
 }
 
 /*
@@ -655,54 +620,62 @@ ReindexIndex(RangeVar *indexRelation, bool force /* currently unused */ )
  *             Recreate indexes of a table.
  */
 void
-ReindexTable(RangeVar *relation, bool force)
+ReindexTable(RangeVar *relation, bool force /* currently unused */ )
 {
        Oid                     heapOid;
-       char            relkind;
+       HeapTuple       tuple;
 
        heapOid = RangeVarGetRelid(relation, false);
-       relkind = get_rel_relkind(heapOid);
+       tuple = SearchSysCache(RELOID,
+                                                  ObjectIdGetDatum(heapOid),
+                                                  0, 0, 0);
+       if (!HeapTupleIsValid(tuple))           /* shouldn't happen */
+               elog(ERROR, "cache lookup failed for relation %u", heapOid);
 
-       if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE)
+       if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION &&
+               ((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_TOASTVALUE)
                ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 errmsg("relation \"%s\" is not a table",
                                                relation->relname)));
 
-       /*
-        * In-place REINDEX within a transaction block is dangerous, because
-        * if the transaction is later rolled back we have no way to undo
-        * truncation of the index's physical file.  Disallow it.
-        *
-        * XXX we assume that in-place reindex will only be done if
-        * IsIgnoringSystemIndexes() is true.
-        */
-       if (IsIgnoringSystemIndexes())
-               PreventTransactionChain((void *) relation, "REINDEX");
+       /* Check permissions */
+       if (!pg_class_ownercheck(heapOid, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+                                          relation->relname);
+
+       /* Can't reindex shared tables except in standalone mode */
+       if (((Form_pg_class) GETSTRUCT(tuple))->relisshared && IsUnderPostmaster)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("shared table \"%s\" can only be reindexed in standalone mode",
+                                               relation->relname)));
+
+       ReleaseSysCache(tuple);
 
-       if (!reindex_relation(heapOid, force))
+       if (!reindex_relation(heapOid))
                ereport(WARNING,
-                               (errmsg("table \"%s\" wasn't reindexed",
+                               (errmsg("table \"%s\" has no indexes",
                                                relation->relname)));
 }
 
 /*
  * ReindexDatabase
  *             Recreate indexes of a database.
+ *
+ * To reduce the probability of deadlocks, each table is reindexed in a
+ * separate transaction, so we can release the lock on it right away.
  */
 void
-ReindexDatabase(const char *dbname, bool force, bool all)
+ReindexDatabase(const char *dbname, bool force /* currently unused */,
+                               bool all)
 {
        Relation        relationRelation;
        HeapScanDesc scan;
        HeapTuple       tuple;
        MemoryContext private_context;
        MemoryContext old;
-       int                     relcnt,
-                               relalc,
-                               i,
-                               oncealc = 200;
-       Oid                *relids = (Oid *) NULL;
+       List       *relids = NIL;
 
        AssertArg(dbname);
 
@@ -715,21 +688,12 @@ ReindexDatabase(const char *dbname, bool force, bool all)
                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
                                           dbname);
 
-       if (!allowSystemTableMods)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                errmsg("REINDEX DATABASE must be done in standalone postgres with -O -P options")));
-       if (!IsIgnoringSystemIndexes())
-               ereport(ERROR,
-                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                errmsg("REINDEX DATABASE must be done in standalone postgres with -P -O options")));
-
        /*
         * We cannot run inside a user transaction block; if we were inside a
         * transaction, then our commit- and start-transaction-command calls
         * would not have the intended effect!
         */
-       PreventTransactionChain((void *) dbname, "REINDEX");
+       PreventTransactionChain((void *) dbname, "REINDEX DATABASE");
 
        /*
         * Create a memory context that will survive forced transaction
@@ -743,55 +707,68 @@ ReindexDatabase(const char *dbname, bool force, bool all)
                                                                                        ALLOCSET_DEFAULT_INITSIZE,
                                                                                        ALLOCSET_DEFAULT_MAXSIZE);
 
+       /*
+        * We always want to reindex pg_class first.  This ensures that if
+        * there is any corruption in pg_class' indexes, they will be fixed
+        * before we process any other tables.  This is critical because
+        * reindexing itself will try to update pg_class.
+        */
+       old = MemoryContextSwitchTo(private_context);
+       relids = lappendo(relids, RelOid_pg_class);
+       MemoryContextSwitchTo(old);
+
        /*
         * Scan pg_class to build a list of the relations we need to reindex.
+        *
+        * We only consider plain relations here (toast rels will be processed
+        * indirectly by reindex_relation).
         */
        relationRelation = heap_openr(RelationRelationName, AccessShareLock);
        scan = heap_beginscan(relationRelation, SnapshotNow, 0, NULL);
-       relcnt = relalc = 0;
        while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
        {
-               char            relkind;
+               Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
+
+               if (classtuple->relkind != RELKIND_RELATION)
+                       continue;
 
-               if (!all)
+               if (!all)                               /* only system tables? */
                {
-                       if (!(IsSystemClass((Form_pg_class) GETSTRUCT(tuple)) &&
-                                 !IsToastClass((Form_pg_class) GETSTRUCT(tuple))))
+                       if (!IsSystemClass(classtuple))
                                continue;
                }
-               relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind;
-               if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE)
+
+               if (IsUnderPostmaster)  /* silently ignore shared tables */
                {
-                       old = MemoryContextSwitchTo(private_context);
-                       if (relcnt == 0)
-                       {
-                               relalc = oncealc;
-                               relids = palloc(sizeof(Oid) * relalc);
-                       }
-                       else if (relcnt >= relalc)
-                       {
-                               relalc *= 2;
-                               relids = repalloc(relids, sizeof(Oid) * relalc);
-                       }
-                       MemoryContextSwitchTo(old);
-                       relids[relcnt] = HeapTupleGetOid(tuple);
-                       relcnt++;
+                       if (classtuple->relisshared)
+                               continue;
                }
+
+               if (HeapTupleGetOid(tuple) == RelOid_pg_class)
+                       continue;                       /* got it already */
+
+               old = MemoryContextSwitchTo(private_context);
+               relids = lappendo(relids, HeapTupleGetOid(tuple));
+               MemoryContextSwitchTo(old);
        }
        heap_endscan(scan);
        heap_close(relationRelation, AccessShareLock);
 
        /* Now reindex each rel in a separate transaction */
        CommitTransactionCommand();
-       for (i = 0; i < relcnt; i++)
+       while (relids)
        {
+               Oid             relid = lfirsto(relids);
+
                StartTransactionCommand();
                SetQuerySnapshot();             /* might be needed for functions in
                                                                 * indexes */
-               if (reindex_relation(relids[i], force))
+               if (reindex_relation(relid))
                        ereport(NOTICE,
-                                       (errmsg("relation %u was reindexed", relids[i])));
+                                       (errmsg("table \"%s\" was reindexed",
+                                                       get_rel_name(relid))));
                CommitTransactionCommand();
+               relids = lnext(relids);
        }
        StartTransactionCommand();
 
index f6331e8d214127742d9c07d86656747b135561cd..e626848f12b6bf237d19fdbeef06b4cf35696c2b 100644 (file)
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.259 2003/08/04 02:39:58 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.260 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -904,11 +904,6 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
        int                     nindexes,
                                i;
        VRelStats  *vacrelstats;
-       bool            reindex = false;
-
-       if (IsIgnoringSystemIndexes() &&
-               IsSystemRelation(onerel))
-               reindex = true;
 
        vacuum_set_xid_limits(vacstmt, onerel->rd_rel->relisshared,
                                                  &OldestXmin, &FreezeLimit);
@@ -927,27 +922,9 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
 
        /* Now open all indexes of the relation */
        vac_open_indexes(onerel, &nindexes, &Irel);
-       if (!Irel)
-               reindex = false;
-       else if (!RelationGetForm(onerel)->relhasindex)
-               reindex = true;
        if (nindexes > 0)
                vacrelstats->hasindex = true;
 
-#ifdef NOT_USED
-
-       /*
-        * reindex in VACUUM is dangerous under WAL. ifdef out until it
-        * becomes safe.
-        */
-       if (reindex)
-       {
-               vac_close_indexes(nindexes, Irel);
-               Irel = (Relation *) NULL;
-               activate_indexes_of_a_table(onerel, false);
-       }
-#endif   /* NOT_USED */
-
        /* Clean/scan index relation(s) */
        if (Irel != (Relation *) NULL)
        {
@@ -994,11 +971,6 @@ full_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
                }
        }
 
-#ifdef NOT_USED
-       if (reindex)
-               activate_indexes_of_a_table(onerel, true);
-#endif   /* NOT_USED */
-
        /* update shared free space map with final free space info */
        vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages);
 
index 9c815f84cda6d86fbc2a0651b32dbaaeeadaf1ce..4aa3170daa6a8995019f61df629018a99c205e7e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.103 2003/08/08 21:41:40 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.104 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -647,12 +647,9 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo)
 
        resultRelInfo->ri_NumIndices = 0;
 
-       /* checks for disabled indexes */
+       /* fast path if no indexes */
        if (!RelationGetForm(resultRelation)->relhasindex)
                return;
-       if (IsIgnoringSystemIndexes() &&
-               IsSystemRelation(resultRelation))
-               return;
 
        /*
         * Get cached list of index OIDs
index f93802269da7ee3c09cdb3034e735210fc5fe1fe..6ab2f0a47bd7f2eaf71b1dd6d7dc1ab1d91278a4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.83 2003/08/22 20:26:43 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.84 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -964,12 +964,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 
        currentRelation = heap_open(reloid, AccessShareLock);
 
-       if (!RelationGetForm(currentRelation)->relhasindex)
-               ereport(ERROR,
-                               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-                                errmsg("indexes of relation %u were deactivated",
-                                               reloid)));
-
        indexstate->ss.ss_currentRelation = currentRelation;
        indexstate->ss.ss_currentScanDesc = NULL;       /* no heap scan here */
 
index edd12ed5c84ce3ba156996fbcf40c94925ff2965..366a606684ae1e95e0098a08ff1fb5f732a52a32 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.59 2003/08/04 02:40:03 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.60 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,6 +73,12 @@ SendSharedInvalidMessage(SharedInvalidationMessage *msg)
 /*
  * ReceiveSharedInvalidMessages
  *             Process shared-cache-invalidation messages waiting for this backend
+ *
+ * NOTE: it is entirely possible for this routine to be invoked recursively
+ * as a consequence of processing inside the invalFunction or resetFunction.
+ * Hence, we must be holding no SI resources when we call them.  The only
+ * bad side-effect is that SIDelExpiredDataEntries might be called extra
+ * times on the way out of a nested call.
  */
 void
 ReceiveSharedInvalidMessages(
index c84d119ad78a9dd96d1e2fce60d2a21572a4a335..e61c866b374b8de8763bb7dcf016948e4319440f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.363 2003/09/14 00:03:32 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.364 2003/09/24 18:54:01 tgl Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
@@ -2252,9 +2252,12 @@ PostgresMain(int argc, char *argv[], const char *username)
 
                                /*
                                 * ignore system indexes
+                                *
+                                * As of PG 7.4 this is safe to allow from the client,
+                                * since it only disables reading the system indexes,
+                                * not writing them.  Worst case consequence is slowness.
                                 */
-                               if (secure)             /* XXX safe to allow from client??? */
-                                       IgnoreSystemIndexes(true);
+                               IgnoreSystemIndexes(true);
                                break;
 
                        case 'o':
@@ -2658,7 +2661,7 @@ PostgresMain(int argc, char *argv[], const char *username)
        if (!IsUnderPostmaster)
        {
                puts("\nPOSTGRES backend interactive interface ");
-               puts("$Revision: 1.363 $ $Date: 2003/09/14 00:03:32 $\n");
+               puts("$Revision: 1.364 $ $Date: 2003/09/24 18:54:01 $\n");
        }
 
        /*
index 629751e2f15bf2c76a8d4a2020e8c6d4faca2b9c..356948e12b86429f71f4e96d00730c14abc0f4eb 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.206 2003/09/09 23:22:21 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.207 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -992,11 +992,9 @@ ProcessUtility(Node *parsetree,
                                switch (stmt->kind)
                                {
                                        case OBJECT_INDEX:
-                                               CheckRelationOwnership(stmt->relation, false);
                                                ReindexIndex(stmt->relation, stmt->force);
                                                break;
                                        case OBJECT_TABLE:
-                                               CheckRelationOwnership(stmt->relation, false);
                                                ReindexTable(stmt->relation, stmt->force);
                                                break;
                                        case OBJECT_DATABASE:
index 592a99faa7d6baf0749f63d3d084866bb7588217..3c4cb46a74f2ab7af8429fddebe36f5496bb21fa 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.188 2003/08/04 02:40:06 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.189 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -279,9 +279,7 @@ static HTAB *OpClassCache = NULL;
 
 static void RelationClearRelation(Relation relation, bool rebuild);
 
-#ifdef ENABLE_REINDEX_NAILED_RELATIONS
 static void RelationReloadClassinfo(Relation relation);
-#endif   /* ENABLE_REINDEX_NAILED_RELATIONS */
 static void RelationFlushRelation(Relation relation);
 static Relation RelationSysNameCacheGetRelation(const char *relationName);
 static bool load_relcache_init_file(void);
@@ -290,7 +288,7 @@ static void write_relcache_init_file(void);
 static void formrdesc(const char *relationName, int natts,
                  FormData_pg_attribute *att);
 
-static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo);
+static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo, bool indexOK);
 static Relation AllocateRelationDesc(Relation relation, Form_pg_class relp);
 static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
                                           Relation relation);
@@ -322,7 +320,7 @@ static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
  *             and must eventually be freed with heap_freetuple.
  */
 static HeapTuple
-ScanPgRelation(RelationBuildDescInfo buildinfo)
+ScanPgRelation(RelationBuildDescInfo buildinfo, bool indexOK)
 {
        HeapTuple       pg_class_tuple;
        Relation        pg_class_desc;
@@ -367,11 +365,12 @@ ScanPgRelation(RelationBuildDescInfo buildinfo)
        /*
         * Open pg_class and fetch a tuple.  Force heap scan if we haven't yet
         * built the critical relcache entries (this includes initdb and
-        * startup without a pg_internal.init file).
+        * startup without a pg_internal.init file).  The caller can also
+        * force a heap scan by setting indexOK == false.
         */
        pg_class_desc = heap_openr(RelationRelationName, AccessShareLock);
        pg_class_scan = systable_beginscan(pg_class_desc, indexRelname,
-                                                                          criticalRelcachesBuilt,
+                                                                          indexOK && criticalRelcachesBuilt,
                                                                           SnapshotNow,
                                                                           nkeys, key);
 
@@ -834,7 +833,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
        /*
         * find the tuple in pg_class corresponding to the given relation id
         */
-       pg_class_tuple = ScanPgRelation(buildinfo);
+       pg_class_tuple = ScanPgRelation(buildinfo, true);
 
        /*
         * if no such tuple exists, return NULL
@@ -875,7 +874,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
         * it could be new too, but it's okay to forget that fact if forced to
         * flush the entry.)
         */
-       relation->rd_isnailed = false;
+       relation->rd_isnailed = 0;
        relation->rd_isnew = false;
        relation->rd_istemp = isTempNamespace(relation->rd_rel->relnamespace);
 
@@ -1386,7 +1385,7 @@ formrdesc(const char *relationName,
         * all entries built with this routine are nailed-in-cache; none are
         * for new or temp relations.
         */
-       relation->rd_isnailed = true;
+       relation->rd_isnailed = 1;
        relation->rd_isnew = false;
        relation->rd_istemp = false;
 
@@ -1500,7 +1499,7 @@ formrdesc(const char *relationName,
  *             Lookup an existing reldesc by OID.
  *
  *             Only try to get the reldesc by looking in the cache,
- *             do not go to the disk.
+ *             do not go to the disk if it's not present.
  *
  *             NB: relation ref count is incremented if successful.
  *             Caller should eventually decrement count.  (Usually,
@@ -1514,7 +1513,12 @@ RelationIdCacheGetRelation(Oid relationId)
        RelationIdCacheLookup(relationId, rd);
 
        if (RelationIsValid(rd))
+       {
                RelationIncrementReferenceCount(rd);
+               /* revalidate nailed index if necessary */
+               if (rd->rd_isnailed == 2)
+                       RelationReloadClassinfo(rd);
+       }
 
        return rd;
 }
@@ -1538,11 +1542,27 @@ RelationSysNameCacheGetRelation(const char *relationName)
        RelationSysNameCacheLookup(NameStr(name), rd);
 
        if (RelationIsValid(rd))
+       {
                RelationIncrementReferenceCount(rd);
+               /* revalidate nailed index if necessary */
+               if (rd->rd_isnailed == 2)
+                       RelationReloadClassinfo(rd);
+       }
 
        return rd;
 }
 
+/*
+ *             RelationNodeCacheGetRelation
+ *
+ *             As above, but lookup by relfilenode.
+ *
+ * NOTE: this must NOT try to revalidate invalidated nailed indexes, since
+ * that could cause us to return an entry with a different relfilenode than
+ * the caller asked for.  Currently this is used only by the buffer manager.
+ * Really the bufmgr's idea of relations should be separated out from the
+ * relcache ...
+ */
 Relation
 RelationNodeCacheGetRelation(RelFileNode rnode)
 {
@@ -1647,39 +1667,60 @@ RelationClose(Relation relation)
 #endif
 }
 
-#ifdef ENABLE_REINDEX_NAILED_RELATIONS
 /*
- * RelationReloadClassinfo
- *
- *     This function is especially for nailed relations.
- *     relhasindex/relfilenode could be changed even for
- *     nailed relations.
+ * RelationReloadClassinfo - reload the pg_class row (only)
+ *
+ *     This function is used only for nailed indexes.  Since a REINDEX can
+ *     change the relfilenode value for a nailed index, we have to reread
+ *     the pg_class row anytime we get an SI invalidation on a nailed index
+ *     (without throwing away the whole relcache entry, since we'd be unable
+ *     to rebuild it).
+ *
+ *     We can't necessarily reread the pg_class row 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_isnailed to 2.  This routine is called to fix the entry when it
+ *     is next needed.
  */
 static void
 RelationReloadClassinfo(Relation relation)
 {
        RelationBuildDescInfo buildinfo;
+       bool            indexOK;
        HeapTuple       pg_class_tuple;
        Form_pg_class relp;
 
-       if (!relation->rd_rel)
-               return;
+       /* Should be called only for invalidated nailed indexes */
+       Assert(relation->rd_isnailed == 2 &&
+                  relation->rd_rel->relkind == RELKIND_INDEX);
+       /* Read the pg_class row */
        buildinfo.infotype = INFO_RELID;
        buildinfo.i.info_id = relation->rd_id;
-       pg_class_tuple = ScanPgRelation(buildinfo);
+       /*
+        * Don't try to use an indexscan of pg_class_oid_index to reload the
+        * info for pg_class_oid_index ...
+        */
+       indexOK = strcmp(RelationGetRelationName(relation), ClassOidIndex) != 0;
+       pg_class_tuple = ScanPgRelation(buildinfo, indexOK);
        if (!HeapTupleIsValid(pg_class_tuple))
                elog(ERROR, "could not find tuple for system relation %u",
                         relation->rd_id);
-       RelationCacheDelete(relation);
        relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
-       memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
-       relation->rd_node.relNode = relp->relfilenode;
-       RelationCacheInsert(relation);
+       if (relation->rd_node.relNode != relp->relfilenode)
+       {
+               /* We have to re-insert the entry into the relcache indexes */
+               RelationCacheDelete(relation);
+               memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
+               relation->rd_node.relNode = relp->relfilenode;
+               RelationCacheInsert(relation);
+       }
        heap_freetuple(pg_class_tuple);
-
-       return;
+       /* Must adjust number of blocks after we know the new relfilenode */
+       relation->rd_targblock = InvalidBlockNumber;
+       RelationUpdateNumberOfBlocks(relation);
+       /* Okay, now it's valid again */
+       relation->rd_isnailed = 1;
 }
-#endif   /* ENABLE_REINDEX_NAILED_RELATIONS */
 
 /*
  * RelationClearRelation
@@ -1712,15 +1753,27 @@ RelationClearRelation(Relation relation, bool rebuild)
         * Never, never ever blow away a nailed-in system relation, because
         * we'd be unable to recover.  However, we must update rd_nblocks and
         * reset rd_targblock, in case we got called because of a relation
-        * cache flush that was triggered by VACUUM.
+        * cache flush that was triggered by VACUUM.  If it's a nailed index,
+        * then we need to re-read the pg_class row to see if its relfilenode
+        * changed.  We can't necessarily do that here, because we might be in
+        * a failed transaction.  We assume it's okay to do it if there are open
+        * references to the relcache entry (cf notes for AtEOXact_RelationCache).
+        * Otherwise just mark the entry as possibly invalid, and it'll be fixed
+        * when next opened.
         */
        if (relation->rd_isnailed)
        {
-               relation->rd_targblock = InvalidBlockNumber;
-               RelationUpdateNumberOfBlocks(relation);
-#ifdef ENABLE_REINDEX_NAILED_RELATIONS
-               RelationReloadClassinfo(relation);
-#endif   /* ENABLE_REINDEX_NAILED_RELATIONS */
+               if (relation->rd_rel->relkind == RELKIND_INDEX)
+               {
+                       relation->rd_isnailed = 2;      /* needs to be revalidated */
+                       if (relation->rd_refcnt > 1)
+                               RelationReloadClassinfo(relation);
+               }
+               else
+               {
+                       relation->rd_targblock = InvalidBlockNumber;
+                       RelationUpdateNumberOfBlocks(relation);
+               }
                return;
        }
 
@@ -1928,6 +1981,12 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
  *      because (a) during the first pass we won't process any more SI messages,
  *      so hash_seq_search will complete safely; (b) during the second pass we
  *      only hold onto pointers to nondeletable entries.
+ *
+ *      The two-phase approach also makes it easy to ensure that we process
+ *      nailed-in-cache indexes before other nondeletable items, and that we
+ *      process pg_class_oid_index first of all.  In scenarios where a nailed
+ *      index has been given a new relfilenode, we have to detect that update
+ *      before the nailed index is used in reloading any other relcache entry.
  */
 void
 RelationCacheInvalidate(void)
@@ -1935,6 +1994,7 @@ RelationCacheInvalidate(void)
        HASH_SEQ_STATUS status;
        RelIdCacheEnt *idhentry;
        Relation        relation;
+       List       *rebuildFirstList = NIL;
        List       *rebuildList = NIL;
        List       *l;
 
@@ -1954,15 +2014,33 @@ RelationCacheInvalidate(void)
                if (RelationHasReferenceCountZero(relation))
                {
                        /* Delete this entry immediately */
+                       Assert(!relation->rd_isnailed);
                        RelationClearRelation(relation, false);
                }
                else
                {
-                       /* Add entry to list of stuff to rebuild in second pass */
-                       rebuildList = lcons(relation, rebuildList);
+                       /*
+                        * Add this entry to list of stuff to rebuild in second pass.
+                        * pg_class_oid_index goes on the front of rebuildFirstList,
+                        * other nailed indexes on the back, and everything else into
+                        * rebuildList (in no particular order).
+                        */
+                       if (relation->rd_isnailed &&
+                               relation->rd_rel->relkind == RELKIND_INDEX)
+                       {
+                               if (strcmp(RelationGetRelationName(relation),
+                                                  ClassOidIndex) == 0)
+                                       rebuildFirstList = lcons(relation, rebuildFirstList);
+                               else
+                                       rebuildFirstList = lappend(rebuildFirstList, relation);
+                       }
+                       else
+                               rebuildList = lcons(relation, rebuildList);
                }
        }
 
+       rebuildList = nconc(rebuildFirstList, rebuildList);
+
        /* Phase 2: rebuild the items found to need rebuild in phase 1 */
        foreach(l, rebuildList)
        {
@@ -1976,6 +2054,11 @@ RelationCacheInvalidate(void)
  * AtEOXact_RelationCache
  *
  *     Clean up the relcache at transaction commit or abort.
+ *
+ * Note: this must be called *before* processing invalidation messages.
+ * In the case of abort, we don't want to try to rebuild any invalidated
+ * cache entries (since we can't safely do database accesses).  Therefore
+ * we must reset refcnts before handling pending invalidations.
  */
 void
 AtEOXact_RelationCache(bool commit)
@@ -2045,6 +2128,16 @@ AtEOXact_RelationCache(bool commit)
                        /* abort case, just reset it quietly */
                        RelationSetReferenceCount(relation, expected_refcnt);
                }
+
+               /*
+                * Flush any temporary index list.
+                */
+               if (relation->rd_indexvalid == 2)
+               {
+                       freeList(relation->rd_indexlist);
+                       relation->rd_indexlist = NIL;
+                       relation->rd_indexvalid = 0;
+               }
        }
 }
 
@@ -2101,7 +2194,7 @@ RelationBuildLocalRelation(const char *relname,
         * want it kicked out.  e.g. pg_attribute!!!
         */
        if (nailit)
-               rel->rd_isnailed = true;
+               rel->rd_isnailed = 1;
 
        /*
         * create a new tuple descriptor from the one passed in.  We do this
@@ -2288,7 +2381,7 @@ RelationCacheInitializePhase2(void)
                        buildinfo.infotype = INFO_RELNAME; \
                        buildinfo.i.info_name = (indname); \
                        ird = RelationBuildDesc(buildinfo, NULL); \
-                       ird->rd_isnailed = true; \
+                       ird->rd_isnailed = 1; \
                        RelationSetReferenceCount(ird, 1); \
                } while (0)
 
@@ -2575,7 +2668,7 @@ CheckConstraintFetch(Relation relation)
  * The index list is created only if someone requests it.  We scan pg_index
  * to find relevant indexes, and add the list to the relcache entry so that
  * we won't have to compute it again.  Note that shared cache inval of a
- * relcache entry will delete the old list and set rd_indexfound to false,
+ * relcache entry will delete the old list and set rd_indexvalid to 0,
  * so that we must recompute the index list on next request.  This handles
  * creation or deletion of an index.
  *
@@ -2602,7 +2695,7 @@ RelationGetIndexList(Relation relation)
        MemoryContext oldcxt;
 
        /* Quick exit if we already computed the list. */
-       if (relation->rd_indexfound)
+       if (relation->rd_indexvalid != 0)
                return listCopy(relation->rd_indexlist);
 
        /*
@@ -2638,7 +2731,7 @@ RelationGetIndexList(Relation relation)
        /* Now save a copy of the completed list in the relcache entry. */
        oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
        relation->rd_indexlist = listCopy(result);
-       relation->rd_indexfound = true;
+       relation->rd_indexvalid = 1;
        MemoryContextSwitchTo(oldcxt);
 
        return result;
@@ -2676,6 +2769,35 @@ insert_ordered_oid(List *list, Oid datum)
        return list;
 }
 
+/*
+ * RelationSetIndexList -- externally force the index list contents
+ *
+ * This is used to temporarily override what we think the set of valid
+ * indexes is.  The forcing will be valid only until transaction commit
+ * or abort.
+ *
+ * This should only be applied to nailed relations, because in a non-nailed
+ * relation the hacked index list could be lost at any time due to SI
+ * messages.  In practice it is only used on pg_class (see REINDEX).
+ *
+ * It is up to the caller to make sure the given list is correctly ordered.
+ */
+void
+RelationSetIndexList(Relation relation, List *indexIds)
+{
+       MemoryContext oldcxt;
+
+       Assert(relation->rd_isnailed == 1);
+       /* Copy the list into the cache context (could fail for lack of mem) */
+       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+       indexIds = listCopy(indexIds);
+       MemoryContextSwitchTo(oldcxt);
+       /* Okay to replace old list */
+       freeList(relation->rd_indexlist);
+       relation->rd_indexlist = indexIds;
+       relation->rd_indexvalid = 2;            /* mark list as forced */
+}
+
 /*
  * RelationGetIndexExpressions -- get the index expressions for an index
  *
@@ -3087,7 +3209,7 @@ load_relcache_init_file(void)
                        RelationSetReferenceCount(rel, 1);
                else
                        RelationSetReferenceCount(rel, 0);
-               rel->rd_indexfound = false;
+               rel->rd_indexvalid = 0;
                rel->rd_indexlist = NIL;
                MemSet(&rel->pgstat_info, 0, sizeof(rel->pgstat_info));
 
index aa89bb817879d12092bd603e61973b764729ccda..9b250eef62ec3c52a0d277a15bbf613b572a017e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.90 2003/08/04 02:40:06 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.91 2003/09/24 18:54:01 tgl Exp $
  *
  * NOTES
  *       These routines allow the parser/planner/executor to perform
@@ -436,19 +436,11 @@ static const struct cachedesc cacheinfo[] = {
        }}
 };
 
-static CatCache *SysCache[
-                                                 lengthof(cacheinfo)];
+static CatCache *SysCache[lengthof(cacheinfo)];
 static int     SysCacheSize = lengthof(cacheinfo);
 static bool CacheInitialized = false;
 
 
-bool
-IsCacheInitialized(void)
-{
-       return CacheInitialized;
-}
-
-
 /*
  * InitCatalogCache - initialize the caches
  *
index 9f19d1187ba8b652e0f320404435c5f5b7ced141..22baac3706f6e4f2ae21afdda46578235509386f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.113 2003/08/04 04:03:10 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.114 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,6 +51,11 @@ static char socketLockFile[MAXPGPATH];
 
 /* ----------------------------------------------------------------
  *             ignoring system indexes support stuff
+ *
+ * NOTE: "ignoring system indexes" means we do not use the system indexes
+ * for lookups (either in hardwired catalog accesses or in planner-generated
+ * plans).  We do, however, still update the indexes when a catalog
+ * modification is made.
  * ----------------------------------------------------------------
  */
 
@@ -61,15 +66,14 @@ static bool isIgnoringSystemIndexes = false;
  *             True if ignoring system indexes.
  */
 bool
-IsIgnoringSystemIndexes()
+IsIgnoringSystemIndexes(void)
 {
        return isIgnoringSystemIndexes;
 }
 
 /*
  * IgnoreSystemIndexes
- *     Set true or false whether PostgreSQL ignores system indexes.
- *
+ *             Set true or false whether PostgreSQL ignores system indexes.
  */
 void
 IgnoreSystemIndexes(bool mode)
@@ -77,6 +81,53 @@ IgnoreSystemIndexes(bool mode)
        isIgnoringSystemIndexes = mode;
 }
 
+/* ----------------------------------------------------------------
+ *             system index reindexing support
+ *
+ * When we are busy reindexing a system index, this code provides support
+ * for preventing catalog lookups from using that index.
+ * ----------------------------------------------------------------
+ */
+
+static Oid     currentlyReindexedHeap = InvalidOid;
+static Oid     currentlyReindexedIndex = InvalidOid;
+
+/*
+ * ReindexIsProcessingHeap
+ *             True if heap specified by OID is currently being reindexed.
+ */
+bool
+ReindexIsProcessingHeap(Oid heapOid)
+{
+       return heapOid == currentlyReindexedHeap;
+}
+
+/*
+ * ReindexIsProcessingIndex
+ *             True if index specified by OID is currently being reindexed.
+ */
+bool
+ReindexIsProcessingIndex(Oid indexOid)
+{
+       return indexOid == currentlyReindexedIndex;
+}
+
+/*
+ * SetReindexProcessing
+ *             Set flag that specified heap/index are being reindexed.
+ *             Pass InvalidOid to indicate that reindexing is not active.
+ */
+void
+SetReindexProcessing(Oid heapOid, Oid indexOid)
+{
+       /* Args should be both, or neither, InvalidOid */
+       Assert((heapOid == InvalidOid) == (indexOid == InvalidOid));
+       /* Reindexing is not re-entrant. */
+       Assert(indexOid == InvalidOid || currentlyReindexedIndex == InvalidOid);
+       currentlyReindexedHeap = heapOid;
+       currentlyReindexedIndex = indexOid;
+}
+
 /* ----------------------------------------------------------------
  *                             database path / name support stuff
  * ----------------------------------------------------------------
index e3ee98a7c38d5fad33417f477caeccf7879196c8..2a45c290b92ee8044b968c525c238da1ffe7d83d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: index.h,v 1.52 2003/08/04 02:40:10 momjian Exp $
+ * $Id: index.h,v 1.53 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,15 +51,12 @@ extern void FormIndexDatum(IndexInfo *indexInfo,
                           char *nullv);
 
 extern void UpdateStats(Oid relid, double reltuples);
-extern bool IndexesAreActive(Relation heaprel);
+
 extern void setRelhasindex(Oid relid, bool hasindex,
                           bool isprimary, Oid reltoastidxid);
 
 extern void setNewRelfilenode(Relation relation);
 
-extern bool SetReindexProcessing(bool processing);
-extern bool IsReindexProcessing(void);
-
 extern void index_build(Relation heapRelation, Relation indexRelation,
                        IndexInfo *indexInfo);
 
@@ -69,9 +66,7 @@ extern double IndexBuildHeapScan(Relation heapRelation,
                                   IndexBuildCallback callback,
                                   void *callback_state);
 
-extern bool activate_indexes_of_a_table(Relation heaprel, bool activate);
-
-extern bool reindex_index(Oid indexId, bool force, bool inplace);
-extern bool reindex_relation(Oid relid, bool force);
+extern void reindex_index(Oid indexId);
+extern bool reindex_relation(Oid relid);
 
 #endif   /* INDEX_H */
index d5d6d85c62a9c0168bd6fdf2c7e558544e25e73f..1c1c1d3451934ce10719a3387b98e09348adfe60 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: miscadmin.h,v 1.133 2003/08/26 15:38:25 tgl Exp $
+ * $Id: miscadmin.h,v 1.134 2003/09/24 18:54:01 tgl Exp $
  *
  * NOTES
  *       some of the information in this file should be moved to
@@ -296,18 +296,17 @@ extern void InitPostgres(const char *dbname, const char *username);
 extern void BaseInit(void);
 
 /* in utils/init/miscinit.c */
+extern void IgnoreSystemIndexes(bool mode);
+extern bool IsIgnoringSystemIndexes(void);
+extern void SetReindexProcessing(Oid heapOid, Oid indexOid);
+extern bool ReindexIsProcessingHeap(Oid heapOid);
+extern bool ReindexIsProcessingIndex(Oid indexOid);
 extern void CreateDataDirLockFile(const char *datadir, bool amPostmaster);
 extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster);
 extern void TouchSocketLockFile(void);
 extern void RecordSharedMemoryInLockFile(unsigned long id1,
                                                         unsigned long id2);
-
 extern void ValidatePgVersion(const char *path);
 extern void process_preload_libraries(char *preload_libraries_string);
 
-/* these externs do not belong here... */
-extern void IgnoreSystemIndexes(bool mode);
-extern bool IsIgnoringSystemIndexes(void);
-extern bool IsCacheInitialized(void);
-
 #endif   /* MISCADMIN_H */
index 13e42cf1b9d87ff59ab746baad5fb3680e734324..fa1cd254a4f56b8423947eeaa9f9e8028708057f 100644 (file)
@@ -11,7 +11,7 @@
  *
  * Copyright (c) 2003, PostgreSQL Global Development Group
  *
- * $Id: errcodes.h,v 1.5 2003/08/26 21:15:27 tgl Exp $
+ * $Id: errcodes.h,v 1.6 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 /* Class 55 - Object Not In Prerequisite State (class borrowed from DB2) */
 #define ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE       MAKE_SQLSTATE('5','5', '0','0','0')
 #define ERRCODE_OBJECT_IN_USE                          MAKE_SQLSTATE('5','5', '0','0','6')
-#define ERRCODE_INDEXES_DEACTIVATED                    MAKE_SQLSTATE('5','5', 'P','0','1')
 #define ERRCODE_CANT_CHANGE_RUNTIME_PARAM      MAKE_SQLSTATE('5','5', 'P','0','2')
 
 /* Class 57 - Operator Intervention (class borrowed from DB2) */
index 0971abf000ada59c28b3ed915eb358fb26d4f384..377822afc692636929f29d9c52a78f2eeda64dfa 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: rel.h,v 1.67 2003/08/04 02:40:15 momjian Exp $
+ * $Id: rel.h,v 1.68 2003/09/24 18:54:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -119,8 +119,10 @@ typedef struct RelationData
         * it is possible for new-ness to be "forgotten" (eg, after CLUSTER).
         */
        bool            rd_istemp;              /* rel uses the local buffer mgr */
-       bool            rd_isnailed;    /* rel is nailed in cache */
-       bool            rd_indexfound;  /* true if rd_indexlist is valid */
+       char            rd_isnailed;    /* rel is nailed in cache: 0 = no, 1 = yes,
+                                                                * 2 = yes but possibly invalid */
+       char            rd_indexvalid;  /* state of rd_indexlist: 0 = not valid,
+                                                                * 1 = valid, 2 = temporarily forced */
        Form_pg_class rd_rel;           /* RELATION tuple */
        TupleDesc       rd_att;                 /* tuple descriptor */
        Oid                     rd_id;                  /* relation's object id */
index 5d08e9b0256a191e556eabfb1bf6a0a12cd54d56..61e09c38461f9053e308f08696294aae5f720db0 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relcache.h,v 1.36 2003/08/04 02:40:15 momjian Exp $
+ * $Id: relcache.h,v 1.37 2003/09/24 18:54:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,8 @@ extern List *RelationGetIndexList(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
 extern List *RelationGetIndexPredicate(Relation relation);
 
+extern void RelationSetIndexList(Relation relation, List *indexIds);
+
 extern void RelationInitIndexAccessInfo(Relation relation);
 
 /*