From a56a016ceb612cdee1ddc5990682f36d541e5b07 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 24 Sep 2003 18:54:02 +0000 Subject: [PATCH] Repair some REINDEX problems per recent discussions. The relcache is 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. --- doc/src/sgml/ref/postgres-ref.sgml | 8 +- doc/src/sgml/ref/reindex.sgml | 141 ++++++--- src/backend/access/index/genam.c | 29 +- src/backend/access/transam/xact.c | 5 +- src/backend/catalog/index.c | 455 +++++++++------------------ src/backend/catalog/pg_largeobject.c | 44 ++- src/backend/commands/functioncmds.c | 21 +- src/backend/commands/indexcmds.c | 171 +++++----- src/backend/commands/vacuum.c | 30 +- src/backend/executor/execUtils.c | 7 +- src/backend/executor/nodeIndexscan.c | 8 +- src/backend/storage/ipc/sinval.c | 8 +- src/backend/tcop/postgres.c | 11 +- src/backend/tcop/utility.c | 4 +- src/backend/utils/cache/relcache.c | 202 +++++++++--- src/backend/utils/cache/syscache.c | 12 +- src/backend/utils/init/miscinit.c | 59 +++- src/include/catalog/index.h | 13 +- src/include/miscadmin.h | 13 +- src/include/utils/errcodes.h | 3 +- src/include/utils/rel.h | 8 +- src/include/utils/relcache.h | 4 +- 22 files changed, 621 insertions(+), 635 deletions(-) diff --git a/doc/src/sgml/ref/postgres-ref.sgml b/doc/src/sgml/ref/postgres-ref.sgml index b80c9caafa..279d8875e0 100644 --- a/doc/src/sgml/ref/postgres-ref.sgml +++ b/doc/src/sgml/ref/postgres-ref.sgml @@ -1,5 +1,5 @@ @@ -177,9 +177,9 @@ PostgreSQL documentation - Ignore system indexes while scanning/updating system tables. The - REINDEX 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. diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml index 29b96e462c..d945112de7 100644 --- a/doc/src/sgml/ref/reindex.sgml +++ b/doc/src/sgml/ref/reindex.sgml @@ -1,5 +1,5 @@ @@ -56,43 +56,6 @@ REINDEX { DATABASE | TABLE | INDEX } name - - - If you suspect corruption of an index on a user table, you can - simply rebuild that index, or all indexes on the table, using - REINDEX INDEX or REINDEX - TABLE. 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. REINDEX - acquires exclusive lock on the table, while CREATE - INDEX only locks out writes not reads of the table. - - - - 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 PostgreSQL server - must be started instead with the command-line options - and . (These options allow - system table modifications and prevent use of system indexes, - respectively.) Then, REINDEX DATABASE, - REINDEX TABLE, or REINDEX INDEX can be - issued, depending on how much you want to reconstruct. If in - doubt, use REINDEX DATABASE FORCE to force - reconstruction of all system indexes in the database. Then quit - the standalone server session and restart the real server. - - - - See the reference page for more - information about how to interact with the stand-alone server - interface. - @@ -104,8 +67,8 @@ REINDEX { DATABASE | TABLE | INDEX } name Recreate all system indexes of a specified database. Indexes on - user tables are not included. This form of 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). @@ -114,7 +77,8 @@ REINDEX { DATABASE | TABLE | INDEX } nameTABLE - Recreate all indexes of a specified table. + Recreate all indexes of a specified table. If the table has a + secondary TOAST table, that is reindexed as well. @@ -142,16 +106,93 @@ REINDEX { DATABASE | TABLE | INDEX } nameFORCE - Force rebuild of system indexes. Without this key word, - REINDEX skips system indexes that are not marked - invalid. FORCE is irrelevant for REINDEX - INDEX or when reindexing user indexes. + This is an obsolete option; it is ignored if specified. + + Notes + + + If you suspect corruption of an index on a user table, you can + simply rebuild that index, or all indexes on the table, using + REINDEX INDEX or REINDEX + TABLE. 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. REINDEX + acquires exclusive lock on the table, while CREATE + INDEX only locks out writes not reads of the table. + + + + 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, which prevents it from using + indexes for system catalog lookups. + + + + One way to do this is to shut down the postmaster and start a stand-alone + PostgreSQL server + with the option included on its command line. + Then, REINDEX DATABASE, + REINDEX TABLE, or REINDEX INDEX can be + issued, depending on how much you want to reconstruct. If in + doubt, use 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 reference page for more + information about how to interact with the stand-alone server + interface. + + + + Alternatively, a regular server session can be started with + included in its command line options. + The method for doing this varies across clients, but in all + libpq-based clients, it is possible to set + the PGOPTIONS environment variable to -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. + + + + If corruption is suspected in the indexes of any of the shared + system catalogs (pg_database, + pg_group, or + pg_shadow), then a standalone server + must be used to repair it. REINDEX will not process + shared catalogs in multiuser mode. + + + + For all indexes except the shared system catalogs, REINDEX + is crash-safe and transaction-safe. 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 REINDEX operation redone + before attempting to restart the regular server. + + + + Prior to PostgreSQL 7.4, REINDEX + TABLE did not automatically process TOAST tables, and so those had + to be reindexed by separate commands. This is still possible, but + redundant. + + + Examples @@ -172,11 +213,15 @@ REINDEX INDEX my_index; - 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: -REINDEX DATABASE my_database FORCE; +$ export PGOPTIONS="-P" +$ psql broken_db +... +broken_db=> REINDEX DATABASE broken_db; +broken_db=> \q diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index eaa0d172e5..a362cd8cfa 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -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; } diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index e632ae9e1f..e17a3f3c37 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -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(); /* diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 02eba08af4..ed560fb891 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -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); - /* 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); - } + /* schedule unlinking old relfilenode */ + smgrunlink(DEFAULT_SMGR, relation); - if (!pg_class_scan) - heap_freetuple(tuple); - else - heap_endscan(pg_class_scan); + /* update the pg_class row */ + rd_rel->relfilenode = newrelfilenode; + simple_heap_update(pg_class, &tuple->t_self, tuple); + CatalogUpdateIndexes(pg_class, tuple); + + 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 - * --------------------------------- +/* + * reindex_index - This routine is used to recreate a single 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 - * -------------------------------- - */ -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); - /* - * ignore the indexes of the target system relation while processing - * reindex. - */ - if (!IsIgnoringSystemIndexes() && - IsSystemRelation(rel) && !IsToastRelation(rel)) - deactivate_needed = true; + toast_relid = rel->rd_rel->reltoastrelid; /* - * Shared system indexes must be overwritten because it's impossible - * to update pg_class tuples of all databases. + * 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 (rel->rd_rel->relisshared) + indexIds = RelationGetIndexList(rel); + + /* + * 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(). + */ + 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); + + if (is_pg_class) + RelationSetIndexList(rel, doneIndexes); + + reindex_index(indexOid); + + CommandCounterIncrement(); + + if (is_pg_class) + doneIndexes = lappendo(doneIndexes, indexOid); } - old = SetReindexProcessing(true); - - if (deactivate_needed) - { - if (IndexesAreActive(rel)) - { - if (!force) - { - SetReindexProcessing(old); - heap_close(rel, NoLock); - return false; - } - activate_indexes_of_a_table(rel, false); - CommandCounterIncrement(); - } - } + 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); + result = (indexIds != NIL); - 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(); + /* + * 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); - /* - * However the following setRelhasindex() is needed to - * keep consistency with WAL. - */ - } - setRelhasindex(relid, true, false, InvalidOid); - } - } - SetReindexProcessing(old); - - return reindexed; + return result; } diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c index 3bfe0ca23b..8502567169 100644 --- a/src/backend/catalog/pg_largeobject.c +++ b/src/backend/catalog/pg_largeobject.c @@ -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; } diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index ea5ba10313..328643c171 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -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); } diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 4c2221263d..c8ccab8d4e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -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); - if (!reindex_relation(heapOid, force)) + /* 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)) 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 (!all) + if (classtuple->relkind != RELKIND_RELATION) + continue; + + 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(); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index f6331e8d21..e626848f12 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -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); diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9c815f84cd..4aa3170daa 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -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 diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index f93802269d..6ab2f0a47b 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -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 */ diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c index edd12ed5c8..366a606684 100644 --- a/src/backend/storage/ipc/sinval.c +++ b/src/backend/storage/ipc/sinval.c @@ -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( diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index c84d119ad7..e61c866b37 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -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"); } /* diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 629751e2f1..356948e12b 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -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: diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 592a99faa7..3c4cb46a74 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -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 + * RelationReloadClassinfo - reload the pg_class row (only) * - * This function is especially for nailed relations. - * relhasindex/relfilenode could be changed even for - * nailed relations. + * 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)); diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index aa89bb8178..9b250eef62 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -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 * diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 9f19d1187b..22baac3706 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -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 * ---------------------------------------------------------------- diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index e3ee98a7c3..2a45c290b9 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -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 */ diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index d5d6d85c62..1c1c1d3451 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -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 */ diff --git a/src/include/utils/errcodes.h b/src/include/utils/errcodes.h index 13e42cf1b9..fa1cd254a4 100644 --- a/src/include/utils/errcodes.h +++ b/src/include/utils/errcodes.h @@ -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 $ * *------------------------------------------------------------------------- */ @@ -281,7 +281,6 @@ /* 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) */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 0971abf000..377822afc6 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -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 */ diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 5d08e9b025..61e09c3846 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -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); /*