diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index 5c3a6dc81ce890f514bcfba8f124a5a71668b9ca..e36a1450db38f08b61c8b2a5075c71e126da53ff 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.30 2001/10/28 06:25:41 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.31 2002/02/19 20:11:10 tgl Exp $ * * NOTES * many of the old access method routines have been turned into @@ -44,12 +44,14 @@ * next item pointer using the flags. * ---------------------------------------------------------------- */ - #include "postgres.h" -#include "access/genam.h" +#include "access/genam.h" +#include "access/heapam.h" +#include "miscadmin.h" #include "pgstat.h" + /* ---------------------------------------------------------------- * general access method routines * @@ -242,3 +244,156 @@ IndexScanRestorePosition(IndexScanDesc scan) } #endif + + +/* ---------------------------------------------------------------- + * heap-or-index-scan access to system catalogs + * + * These functions support system catalog accesses that normally use + * an index but need to be capable of being switched to heap scans + * if the system indexes are unavailable. The interface is + * as easy to use as a heap scan, and hides all the extra cruft of + * the present indexscan API. + * + * The specified scan keys must be compatible with the named index. + * Generally this means that they must constrain either all columns + * of the index, or the first K columns of an N-column index. + * + * These routines would work fine with non-system tables, actually, + * but they're only useful when there is a known index to use with + * the given scan keys, so in practice they're only good for + * predetermined types of scans of system catalogs. + * ---------------------------------------------------------------- + */ + +/* + * systable_beginscan --- set up for heap-or-index scan + * + * rel: catalog to scan, already opened and suitably locked + * indexRelname: name of index to conditionally use + * indexOK: if false, forces a heap scan (see notes below) + * snapshot: time qual to use (usually should be SnapshotNow) + * nkeys, key: scan keys + * + * The attribute numbers in the scan key should be set for the heap case. + * If we choose to index, we reset them to 1..n to reference the index + * columns. Note this means there must be one scankey qualification per + * index column! This is checked by the Asserts in the normal, index-using + * case, but won't be checked if the heapscan path is taken. + * + * The routine checks the normal cases for whether an indexscan is safe, + * but caller can make additional checks and pass indexOK=false if needed. + * In standard case indexOK can simply be constant TRUE. + */ +SysScanDesc +systable_beginscan(Relation rel, + const char *indexRelname, + bool indexOK, + Snapshot snapshot, + unsigned nkeys, ScanKey key) +{ + SysScanDesc sysscan; + + sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData)); + sysscan->heap_rel = rel; + sysscan->snapshot = snapshot; + sysscan->tuple.t_datamcxt = NULL; + sysscan->tuple.t_data = NULL; + sysscan->buffer = InvalidBuffer; + + if (indexOK && + rel->rd_rel->relhasindex && + !IsIgnoringSystemIndexes()) + { + Relation irel; + unsigned i; + + sysscan->irel = irel = index_openr(indexRelname); + /* + * Change attribute numbers to be index column numbers. + * + * This code could be generalized to search for the index key numbers + * to substitute, but for now there's no need. + */ + for (i = 0; i < nkeys; i++) + { + Assert(key[i].sk_attno == irel->rd_index->indkey[i]); + key[i].sk_attno = i+1; + } + sysscan->iscan = index_beginscan(irel, false, nkeys, key); + sysscan->scan = NULL; + } + else + { + sysscan->irel = (Relation) NULL; + sysscan->scan = heap_beginscan(rel, false, snapshot, nkeys, key); + sysscan->iscan = NULL; + } + + return sysscan; +} + +/* + * systable_getnext --- get next tuple in a heap-or-index scan + * + * Returns NULL if no more tuples available. + * + * Note that returned tuple is a reference to data in a disk buffer; + * it must not be modified, and should be presumed inaccessible after + * next getnext() or endscan() call. + */ +HeapTuple +systable_getnext(SysScanDesc sysscan) +{ + HeapTuple htup = (HeapTuple) NULL; + + if (sysscan->irel) + { + RetrieveIndexResult indexRes; + + if (BufferIsValid(sysscan->buffer)) + { + ReleaseBuffer(sysscan->buffer); + sysscan->buffer = InvalidBuffer; + } + + while ((indexRes = index_getnext(sysscan->iscan, ForwardScanDirection)) != NULL) + { + sysscan->tuple.t_self = indexRes->heap_iptr; + pfree(indexRes); + heap_fetch(sysscan->heap_rel, sysscan->snapshot, + &sysscan->tuple, &sysscan->buffer, + sysscan->iscan); + if (sysscan->tuple.t_data != NULL) + { + htup = &sysscan->tuple; + break; + } + } + } + else + htup = heap_getnext(sysscan->scan, 0); + + return htup; +} + +/* + * systable_endscan --- close scan, release resources + * + * Note that it's still up to the caller to close the heap relation. + */ +void +systable_endscan(SysScanDesc sysscan) +{ + if (sysscan->irel) + { + if (BufferIsValid(sysscan->buffer)) + ReleaseBuffer(sysscan->buffer); + index_endscan(sysscan->iscan); + index_close(sysscan->irel); + } + else + heap_endscan(sysscan->scan); + + pfree(sysscan); +} diff --git a/src/backend/access/index/istrat.c b/src/backend/access/index/istrat.c index 9e0fb8fe13371fb5ec5d823c8bb971022c6caf43..9366534bbac439bdefac895cde9828a1ef410e7b 100644 --- a/src/backend/access/index/istrat.c +++ b/src/backend/access/index/istrat.c @@ -9,29 +9,19 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.56 2001/11/05 17:46:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.57 2002/02/19 20:11:10 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/heapam.h" #include "access/istrat.h" -#include "catalog/catname.h" -#include "catalog/pg_amop.h" -#include "catalog/pg_amproc.h" -#include "catalog/pg_index.h" -#include "catalog/pg_operator.h" -#include "miscadmin.h" -#include "utils/fmgroids.h" -#include "utils/syscache.h" + #ifdef USE_ASSERT_CHECKING static bool StrategyEvaluationIsValid(StrategyEvaluation evaluation); static bool StrategyExpressionIsValid(StrategyExpression expression, StrategyNumber maxStrategy); -static ScanKey StrategyMapGetScanKeyEntry(StrategyMap map, - StrategyNumber strategyNumber); static bool StrategyOperatorIsValid(StrategyOperator operator, StrategyNumber maxStrategy); static bool StrategyTermIsValid(StrategyTerm term, @@ -63,7 +53,7 @@ static bool StrategyTermIsValid(StrategyTerm term, * Assumes that the index strategy number is valid. * Bounds checking should be done outside this routine. */ -static ScanKey +ScanKey StrategyMapGetScanKeyEntry(StrategyMap map, StrategyNumber strategyNumber) { @@ -453,161 +443,6 @@ RelationInvokeStrategy(Relation relation, } #endif -/* ---------------- - * FillScanKeyEntry - * - * Initialize a ScanKey entry for the given operator OID. - * ---------------- - */ -static void -FillScanKeyEntry(Oid operatorObjectId, ScanKey entry) -{ - HeapTuple tuple; - - tuple = SearchSysCache(OPEROID, - ObjectIdGetDatum(operatorObjectId), - 0, 0, 0); - - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "FillScanKeyEntry: unknown operator %u", - operatorObjectId); - - MemSet(entry, 0, sizeof(*entry)); - entry->sk_flags = 0; - entry->sk_procedure = ((Form_pg_operator) GETSTRUCT(tuple))->oprcode; - - ReleaseSysCache(tuple); - - if (!RegProcedureIsValid(entry->sk_procedure)) - elog(ERROR, "FillScanKeyEntry: no procedure for operator %u", - operatorObjectId); - - /* - * Mark entry->sk_func invalid, until and unless someone sets it up. - */ - entry->sk_func.fn_oid = InvalidOid; -} - - -/* - * IndexSupportInitialize - * Initializes an index strategy and associated support procedures. - * - * Data is returned into *indexStrategy, *indexSupport, and *isUnique, - * all of which are objects allocated by the caller. - * - * The primary input keys are indexObjectId and accessMethodObjectId. - * The caller also passes maxStrategyNumber, maxSupportNumber, and - * maxAttributeNumber, since these indicate the size of the indexStrategy - * and indexSupport arrays it has allocated --- but in practice these - * numbers must always match those obtainable from the system catalog - * entries for the index and access method. - */ -void -IndexSupportInitialize(IndexStrategy indexStrategy, - RegProcedure *indexSupport, - bool *isUnique, - Oid indexObjectId, - Oid accessMethodObjectId, - StrategyNumber maxStrategyNumber, - StrategyNumber maxSupportNumber, - AttrNumber maxAttributeNumber) -{ - HeapTuple tuple; - Form_pg_index iform; - int attIndex; - Oid operatorClassObjectId[INDEX_MAX_KEYS]; - - maxStrategyNumber = AMStrategies(maxStrategyNumber); - - tuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(indexObjectId), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "IndexSupportInitialize: no pg_index entry for index %u", - indexObjectId); - iform = (Form_pg_index) GETSTRUCT(tuple); - - *isUnique = iform->indisunique; - - /* - * XXX note that the following assumes the INDEX tuple is well formed - * and that the *key and *class are 0 terminated. - */ - for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++) - { - if (iform->indkey[attIndex] == InvalidAttrNumber || - !OidIsValid(iform->indclass[attIndex])) - elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple"); - operatorClassObjectId[attIndex] = iform->indclass[attIndex]; - } - - ReleaseSysCache(tuple); - - /* if support routines exist for this access method, load them */ - if (maxSupportNumber > 0) - { - for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++) - { - Oid opclass = operatorClassObjectId[attIndex]; - RegProcedure *loc; - StrategyNumber support; - - loc = &indexSupport[attIndex * maxSupportNumber]; - - for (support = 0; support < maxSupportNumber; ++support) - { - tuple = SearchSysCache(AMPROCNUM, - ObjectIdGetDatum(opclass), - Int16GetDatum(support + 1), - 0, 0); - if (HeapTupleIsValid(tuple)) - { - Form_pg_amproc amprocform; - - amprocform = (Form_pg_amproc) GETSTRUCT(tuple); - loc[support] = amprocform->amproc; - ReleaseSysCache(tuple); - } - else - loc[support] = InvalidOid; - } - } - } - - /* Now load the strategy information for the index operators */ - for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++) - { - Oid opclass = operatorClassObjectId[attIndex]; - StrategyMap map; - StrategyNumber strategy; - - map = IndexStrategyGetStrategyMap(indexStrategy, - maxStrategyNumber, - attIndex + 1); - - for (strategy = 1; strategy <= maxStrategyNumber; strategy++) - { - ScanKey mapentry = StrategyMapGetScanKeyEntry(map, strategy); - - tuple = SearchSysCache(AMOPSTRATEGY, - ObjectIdGetDatum(opclass), - Int16GetDatum(strategy), - 0, 0); - if (HeapTupleIsValid(tuple)) - { - Form_pg_amop amopform; - - amopform = (Form_pg_amop) GETSTRUCT(tuple); - FillScanKeyEntry(amopform->amopopr, mapentry); - ReleaseSysCache(tuple); - } - else - ScanKeyEntrySetIllegal(mapentry); - } - } -} - /* ---------------- * IndexStrategyDisplay * ---------------- diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 809702ddec900014d89c9e1fa7295dcf4964341b..ad192dca5082372ad8a74ac650ce74b088bedea7 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.181 2001/11/12 00:00:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.182 2002/02/19 20:11:11 tgl Exp $ * * * INTERFACE ROUTINES @@ -912,32 +912,21 @@ RelationRemoveInheritance(Relation relation) static void RelationRemoveIndexes(Relation relation) { - Relation indexRelation; - HeapTuple tuple; - HeapScanDesc scan; - ScanKeyData entry; - - indexRelation = heap_openr(IndexRelationName, RowExclusiveLock); + List *indexoidlist, + *indexoidscan; - ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_index_indrelid, - F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(relation))); - - scan = heap_beginscan(indexRelation, - false, - SnapshotNow, - 1, - &entry); + indexoidlist = RelationGetIndexList(relation); - while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) + foreach(indexoidscan, indexoidlist) { - index_drop(((Form_pg_index) GETSTRUCT(tuple))->indexrelid); + Oid indexoid = lfirsti(indexoidscan); + + index_drop(indexoid); /* advance cmd counter to make catalog changes visible */ CommandCounterIncrement(); } - heap_endscan(scan); - heap_close(indexRelation, RowExclusiveLock); + freeList(indexoidlist); } /* -------------------------------- @@ -984,17 +973,21 @@ RelationTruncateIndexes(Oid heapId) { Relation indexRelation; ScanKeyData entry; - HeapScanDesc scan; + SysScanDesc scan; HeapTuple indexTuple; /* Scan pg_index to find indexes on specified heap */ indexRelation = heap_openr(IndexRelationName, AccessShareLock); - ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid, F_OIDEQ, + ScanKeyEntryInitialize(&entry, 0, + Anum_pg_index_indrelid, + F_OIDEQ, ObjectIdGetDatum(heapId)); - scan = heap_beginscan(indexRelation, false, SnapshotNow, 1, &entry); + scan = systable_beginscan(indexRelation, IndexIndrelidIndex, true, + SnapshotNow, 1, &entry); - while (HeapTupleIsValid(indexTuple = heap_getnext(scan, 0))) + while (HeapTupleIsValid(indexTuple = systable_getnext(scan))) { + Form_pg_index indexform = (Form_pg_index) GETSTRUCT(indexTuple); Oid indexId; IndexInfo *indexInfo; Relation heapRelation, @@ -1003,8 +996,8 @@ RelationTruncateIndexes(Oid heapId) /* * For each index, fetch info needed for index_build */ - indexId = ((Form_pg_index) GETSTRUCT(indexTuple))->indexrelid; - indexInfo = BuildIndexInfo(indexTuple); + indexId = indexform->indexrelid; + indexInfo = BuildIndexInfo(indexform); /* * We have to re-open the heap rel each time through this loop @@ -1041,7 +1034,7 @@ RelationTruncateIndexes(Oid heapId) } /* Complete the scan and close pg_index */ - heap_endscan(scan); + systable_endscan(scan); heap_close(indexRelation, AccessShareLock); } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index ed57db68707e1c346840cceccdf47d75c4b8feea..bd6bd1de38aa56675c768a17bae1b2c761fc3af9 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.171 2002/01/06 00:37:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.172 2002/02/19 20:11:11 tgl Exp $ * * * INTERFACE ROUTINES @@ -333,67 +333,6 @@ ConstructTupleDescriptor(Relation heapRelation, return indexTupDesc; } -/* ---------------------------------------------------------------- - * AccessMethodObjectIdGetForm - * Returns an access method tuple given its object identifier, - * or NULL if no such AM tuple can be found. - * - * Scanning is done using CurrentMemoryContext as working storage, - * but the returned tuple will be allocated in resultCxt (which is - * typically CacheMemoryContext). - * - * There was a note here about adding indexing, but I don't see a need - * for it. There are so few tuples in pg_am that an indexscan would - * surely be slower. - * ---------------------------------------------------------------- - */ -Form_pg_am -AccessMethodObjectIdGetForm(Oid accessMethodObjectId, - MemoryContext resultCxt) -{ - Relation pg_am_desc; - HeapScanDesc pg_am_scan; - HeapTuple pg_am_tuple; - ScanKeyData key; - Form_pg_am aform; - - /* - * form a scan key for the pg_am relation - */ - ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber, - F_OIDEQ, - ObjectIdGetDatum(accessMethodObjectId)); - - /* - * fetch the desired access method tuple - */ - pg_am_desc = heap_openr(AccessMethodRelationName, AccessShareLock); - pg_am_scan = heap_beginscan(pg_am_desc, 0, SnapshotNow, 1, &key); - - pg_am_tuple = heap_getnext(pg_am_scan, 0); - - /* - * return NULL if not found - */ - if (!HeapTupleIsValid(pg_am_tuple)) - { - heap_endscan(pg_am_scan); - heap_close(pg_am_desc, AccessShareLock); - return NULL; - } - - /* - * if found AM tuple, then copy it into resultCxt and return the copy - */ - aform = (Form_pg_am) MemoryContextAlloc(resultCxt, sizeof *aform); - memcpy(aform, GETSTRUCT(pg_am_tuple), sizeof *aform); - - heap_endscan(pg_am_scan); - heap_close(pg_am_desc, AccessShareLock); - - return aform; -} - /* ---------------------------------------------------------------- * ConstructIndexReldesc * ---------------------------------------------------------------- @@ -401,12 +340,6 @@ AccessMethodObjectIdGetForm(Oid accessMethodObjectId, static void ConstructIndexReldesc(Relation indexRelation, Oid amoid) { - /* - * Fill in a copy of relevant pg_am entry - */ - indexRelation->rd_am = AccessMethodObjectIdGetForm(amoid, - CacheMemoryContext); - /* * Set up some additional fields of the index' pg_class entry. In * particular, initialize knowledge of whether the index is shared. @@ -953,9 +886,8 @@ index_drop(Oid indexId) * ---------------- */ IndexInfo * -BuildIndexInfo(HeapTuple indexTuple) +BuildIndexInfo(Form_pg_index indexStruct) { - Form_pg_index indexStruct = (Form_pg_index) GETSTRUCT(indexTuple); IndexInfo *ii = makeNode(IndexInfo); int i; int numKeys; @@ -1337,9 +1269,6 @@ setNewRelfilenode(Relation relation) } /* schedule unlinking old relfilenode */ smgrunlink(DEFAULT_SMGR, relation); - /* cleanup pg_internal.init if necessary */ - if (relation->rd_isnailed) - unlink(RELCACHE_INIT_FILENAME); /* create another storage file. Is it a little ugly ? */ memcpy((char *) &workrel, relation, sizeof(RelationData)); workrel.rd_node.relNode = newrelfilenode; @@ -1863,11 +1792,7 @@ bool reindex_index(Oid indexId, bool force, bool inplace) { Relation iRel, - indexRelation, heapRelation; - ScanKeyData entry; - HeapScanDesc scan; - HeapTuple indexTuple; IndexInfo *indexInfo; Oid heapId; bool old; @@ -1899,23 +1824,10 @@ reindex_index(Oid indexId, bool force, bool inplace) old = SetReindexProcessing(true); - /* Scan pg_index to find the index's pg_index entry */ - indexRelation = heap_openr(IndexRelationName, AccessShareLock); - ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indexrelid, F_OIDEQ, - ObjectIdGetDatum(indexId)); - scan = heap_beginscan(indexRelation, false, SnapshotNow, 1, &entry); - indexTuple = heap_getnext(scan, 0); - if (!HeapTupleIsValid(indexTuple)) - elog(ERROR, "reindex_index: index %u not found in pg_index", indexId); - /* Get OID of index's parent table */ - heapId = ((Form_pg_index) GETSTRUCT(indexTuple))->indrelid; + heapId = iRel->rd_index->indrelid; /* Fetch info needed for index_build */ - indexInfo = BuildIndexInfo(indexTuple); - - /* Complete the scan and close pg_index */ - heap_endscan(scan); - heap_close(indexRelation, AccessShareLock); + indexInfo = BuildIndexInfo(iRel->rd_index); /* Open the parent heap relation */ heapRelation = heap_open(heapId, ExclusiveLock); diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index 28b8516947354dcb2c02cad2b391265fdf9ad505..e600eab6a30e3e0ac640b20410c6bda26f019f95 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.82 2001/08/21 16:36:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.83 2002/02/19 20:11:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -80,12 +80,6 @@ char *Name_pg_description_indices[Num_pg_description_indices] = -static HeapTuple CatalogIndexFetchTuple(Relation heapRelation, - Relation idesc, - ScanKey skey, - int16 num_keys); - - /* * Changes (appends) to catalogs can and do happen at various places * throughout the code. We need a generic routine that will open all of @@ -151,18 +145,10 @@ CatalogIndexInsert(Relation *idescs, for (i = 0; i < nIndices; i++) { - HeapTuple index_tup; IndexInfo *indexInfo; InsertIndexResult indexRes; - index_tup = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(idescs[i]->rd_id), - 0, 0, 0); - if (!HeapTupleIsValid(index_tup)) - elog(ERROR, "CatalogIndexInsert: index %u not found", - idescs[i]->rd_id); - indexInfo = BuildIndexInfo(index_tup); - ReleaseSysCache(index_tup); + indexInfo = BuildIndexInfo(idescs[i]->rd_index); FormIndexDatum(indexInfo, heapTuple, @@ -178,127 +164,3 @@ CatalogIndexInsert(Relation *idescs, pfree(indexInfo); } } - - -/* - * CatalogIndexFetchTuple() -- Get a tuple that satisfies a scan key - * from a catalog relation. - * - * Since the index may contain pointers to dead tuples, we need to - * iterate until we find a tuple that's valid and satisfies the scan - * key. - */ -static HeapTuple -CatalogIndexFetchTuple(Relation heapRelation, - Relation idesc, - ScanKey skey, - int16 num_keys) -{ - IndexScanDesc sd; - RetrieveIndexResult indexRes; - HeapTupleData tuple; - HeapTuple result = NULL; - Buffer buffer; - - sd = index_beginscan(idesc, false, num_keys, skey); - tuple.t_datamcxt = CurrentMemoryContext; - tuple.t_data = NULL; - while ((indexRes = index_getnext(sd, ForwardScanDirection))) - { - tuple.t_self = indexRes->heap_iptr; - heap_fetch(heapRelation, SnapshotNow, &tuple, &buffer, sd); - pfree(indexRes); - if (tuple.t_data != NULL) - break; - } - - if (tuple.t_data != NULL) - { - result = heap_copytuple(&tuple); - ReleaseBuffer(buffer); - } - - index_endscan(sd); - - return result; -} - - -/*--------------------------------------------------------------------- - * Class-specific index lookups - *--------------------------------------------------------------------- - */ - -/* - * The remainder of the file is for individual index scan routines. - * These routines provide canned scanning code for certain widely-used - * indexes. Most indexes don't need one of these. - */ - - -HeapTuple -AttributeRelidNumIndexScan(Relation heapRelation, - Datum relid, - Datum attnum) -{ - Relation idesc; - ScanKeyData skey[2]; - HeapTuple tuple; - - ScanKeyEntryInitialize(&skey[0], - (bits16) 0x0, - (AttrNumber) 1, - (RegProcedure) F_OIDEQ, - relid); - - ScanKeyEntryInitialize(&skey[1], - (bits16) 0x0, - (AttrNumber) 2, - (RegProcedure) F_INT2EQ, - attnum); - - idesc = index_openr(AttributeRelidNumIndex); - tuple = CatalogIndexFetchTuple(heapRelation, idesc, skey, 2); - index_close(idesc); - return tuple; -} - - -HeapTuple -ClassNameIndexScan(Relation heapRelation, Datum relName) -{ - Relation idesc; - ScanKeyData skey[1]; - HeapTuple tuple; - - ScanKeyEntryInitialize(&skey[0], - (bits16) 0x0, - (AttrNumber) 1, - (RegProcedure) F_NAMEEQ, - relName); - - idesc = index_openr(ClassNameIndex); - tuple = CatalogIndexFetchTuple(heapRelation, idesc, skey, 1); - index_close(idesc); - return tuple; -} - - -HeapTuple -ClassOidIndexScan(Relation heapRelation, Datum relId) -{ - Relation idesc; - ScanKeyData skey[1]; - HeapTuple tuple; - - ScanKeyEntryInitialize(&skey[0], - (bits16) 0x0, - (AttrNumber) 1, - (RegProcedure) F_OIDEQ, - relId); - - idesc = index_openr(ClassOidIndex); - tuple = CatalogIndexFetchTuple(heapRelation, idesc, skey, 1); - index_close(idesc); - return tuple; -} diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 663a2b22008b4c8cf4fbc12c6feb43394a2c5082..f37394874264a6279565bf3a96cd3b01b08dea52 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.71 2002/01/06 00:37:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.72 2002/02/19 20:11:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -61,7 +61,6 @@ cluster(char *oldrelname, char *oldindexname) OIDNewHeap; Relation OldHeap, OldIndex; - HeapTuple tuple; bool istemp; char NewHeapName[NAMEDATALEN]; char NewIndexName[NAMEDATALEN]; @@ -90,16 +89,9 @@ cluster(char *oldrelname, char *oldindexname) /* * Check that index is in fact an index on the given relation */ - tuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(OIDOldIndex), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "CLUSTER: no pg_index entry for index %u", - OIDOldIndex); - if (((Form_pg_index) GETSTRUCT(tuple))->indrelid != OIDOldHeap) + if (OldIndex->rd_index->indrelid != OIDOldHeap) elog(ERROR, "CLUSTER: \"%s\" is not an index for table \"%s\"", saveoldindexname, saveoldrelname); - ReleaseSysCache(tuple); /* Drop relcache refcnts, but do NOT give up the locks */ heap_close(OldHeap, NoLock); @@ -188,10 +180,6 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName) { Relation OldIndex, NewHeap; - HeapTuple Old_pg_index_Tuple, - Old_pg_index_relation_Tuple; - Form_pg_index Old_pg_index_Form; - Form_pg_class Old_pg_index_relation_Form; IndexInfo *indexInfo; NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock); @@ -206,33 +194,18 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName) * its parent table is, so we don't need to do anything special for * the temp-table case here. */ - Old_pg_index_Tuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(OIDOldIndex), - 0, 0, 0); - Assert(Old_pg_index_Tuple); - Old_pg_index_Form = (Form_pg_index) GETSTRUCT(Old_pg_index_Tuple); - - indexInfo = BuildIndexInfo(Old_pg_index_Tuple); - - Old_pg_index_relation_Tuple = SearchSysCache(RELOID, - ObjectIdGetDatum(OIDOldIndex), - 0, 0, 0); - Assert(Old_pg_index_relation_Tuple); - Old_pg_index_relation_Form = (Form_pg_class) GETSTRUCT(Old_pg_index_relation_Tuple); + indexInfo = BuildIndexInfo(OldIndex->rd_index); index_create(RelationGetRelationName(NewHeap), NewIndexName, indexInfo, - Old_pg_index_relation_Form->relam, - Old_pg_index_Form->indclass, - Old_pg_index_Form->indisprimary, + OldIndex->rd_rel->relam, + OldIndex->rd_index->indclass, + OldIndex->rd_index->indisprimary, allowSystemTableMods); setRelhasindex(OIDNewHeap, true, - Old_pg_index_Form->indisprimary, InvalidOid); - - ReleaseSysCache(Old_pg_index_Tuple); - ReleaseSysCache(Old_pg_index_relation_Tuple); + OldIndex->rd_index->indisprimary, InvalidOid); index_close(OldIndex); heap_close(NewHeap, NoLock); diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 9b1a33673c737eb2b7012f92ee8018bed7ef136c..f01cf4498fdd88bcdb8f95d3e257543394436429 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.153 2002/02/14 15:24:06 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.154 2002/02/19 20:11:12 tgl Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -29,6 +29,7 @@ #include "catalog/pg_attrdef.h" #include "catalog/pg_index.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_relcheck.h" #include "catalog/pg_type.h" #include "commands/command.h" #include "commands/trigger.h" @@ -820,92 +821,8 @@ AlterTableAlterColumnStatistics(const char *relationName, #ifdef _DROP_COLUMN_HACK__ /* * ALTER TABLE DROP COLUMN trial implementation - * */ -/* - * system table scan(index scan/sequential scan) - */ -typedef struct SysScanDescData -{ - Relation heap_rel; - Relation irel; - HeapScanDesc scan; - IndexScanDesc iscan; - HeapTupleData tuple; - Buffer buffer; -} SysScanDescData, *SysScanDesc; - -static void * -systable_beginscan(Relation rel, const char *indexRelname, int nkeys, ScanKey entry) -{ - bool hasindex = (rel->rd_rel->relhasindex && !IsIgnoringSystemIndexes()); - SysScanDesc sysscan; - - sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData)); - sysscan->heap_rel = rel; - sysscan->irel = (Relation) NULL; - sysscan->tuple.t_datamcxt = NULL; - sysscan->tuple.t_data = NULL; - sysscan->buffer = InvalidBuffer; - if (hasindex) - { - sysscan->irel = index_openr((char *) indexRelname); - sysscan->iscan = index_beginscan(sysscan->irel, false, nkeys, entry); - } - else - sysscan->scan = heap_beginscan(rel, false, SnapshotNow, nkeys, entry); - return (void *) sysscan; -} - -static void -systable_endscan(void *scan) -{ - SysScanDesc sysscan = (SysScanDesc) scan; - - if (sysscan->irel) - { - if (BufferIsValid(sysscan->buffer)) - ReleaseBuffer(sysscan->buffer); - index_endscan(sysscan->iscan); - index_close(sysscan->irel); - } - else - heap_endscan(sysscan->scan); - pfree(scan); -} - -static HeapTuple -systable_getnext(void *scan) -{ - SysScanDesc sysscan = (SysScanDesc) scan; - HeapTuple htup = (HeapTuple) NULL; - RetrieveIndexResult indexRes; - - if (sysscan->irel) - { - if (BufferIsValid(sysscan->buffer)) - { - ReleaseBuffer(sysscan->buffer); - sysscan->buffer = InvalidBuffer; - } - while (indexRes = index_getnext(sysscan->iscan, ForwardScanDirection), indexRes != NULL) - { - sysscan->tuple.t_self = indexRes->heap_iptr; - heap_fetch(sysscan->heap_rel, SnapshotNow, &sysscan->tuple, &(sysscan->buffer)); - pfree(indexRes); - if (sysscan->tuple.t_data != NULL) - { - htup = &sysscan->tuple; - break; - } - } - } - else - htup = heap_getnext(sysscan->scan, 0); - return htup; -} - /* * find a specified attribute in a node entry */ @@ -957,10 +874,15 @@ RemoveColumnReferences(Oid reloid, int attnum, bool checkonly, HeapTuple reltup) /* * Remove/check constraints here */ - ScanKeyEntryInitialize(&entry, (bits16) 0x0, Anum_pg_relcheck_rcrelid, - (RegProcedure) F_OIDEQ, ObjectIdGetDatum(reloid)); + ScanKeyEntryInitialize(&entry, (bits16) 0x0, + Anum_pg_relcheck_rcrelid, + (RegProcedure) F_OIDEQ, + ObjectIdGetDatum(reloid)); + rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock); - sysscan = systable_beginscan(rcrel, RelCheckIndex, 1, &entry); + sysscan = systable_beginscan(rcrel, RelCheckIndex, true, + SnapshotNow, + 1, &entry); while (HeapTupleIsValid(htup = systable_getnext(sysscan))) { @@ -987,6 +909,7 @@ RemoveColumnReferences(Oid reloid, int attnum, bool checkonly, HeapTuple reltup) } } } + systable_endscan(sysscan); heap_close(rcrel, NoLock); diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 528e53ee47ddd7bf5f38ebb30be28bec22cfbca2..402ff51e4bc267ac8e643fda1afb8902a275c06a 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.101 2002/01/15 16:52:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.102 2002/02/19 20:11:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,7 +56,7 @@ CreateTrigger(CreateTrigStmt *stmt) char nulls[Natts_pg_trigger]; Relation rel; Relation tgrel; - HeapScanDesc tgscan; + SysScanDesc tgscan; ScanKeyData key; Relation pgrel; HeapTuple tuple; @@ -151,8 +151,9 @@ CreateTrigger(CreateTrigStmt *stmt) ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); - tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key); - while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0))) + tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true, + SnapshotNow, 1, &key); + while (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); @@ -161,7 +162,7 @@ CreateTrigger(CreateTrigStmt *stmt) stmt->trigname, stmt->relname); found++; } - heap_endscan(tgscan); + systable_endscan(tgscan); /* * Find and validate the trigger function. @@ -311,7 +312,7 @@ DropTrigger(DropTrigStmt *stmt) { Relation rel; Relation tgrel; - HeapScanDesc tgscan; + SysScanDesc tgscan; ScanKeyData key; Relation pgrel; HeapTuple tuple; @@ -343,8 +344,9 @@ DropTrigger(DropTrigStmt *stmt) ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); - tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key); - while (HeapTupleIsValid(tuple = heap_getnext(tgscan, 0))) + tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true, + SnapshotNow, 1, &key); + while (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); @@ -359,14 +361,15 @@ DropTrigger(DropTrigStmt *stmt) else found++; } + systable_endscan(tgscan); + heap_close(tgrel, RowExclusiveLock); + if (tgfound == 0) elog(ERROR, "DropTrigger: there is no trigger %s on relation %s", stmt->trigname, stmt->relname); if (tgfound > 1) elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s", tgfound, stmt->trigname, stmt->relname); - heap_endscan(tgscan); - heap_close(tgrel, RowExclusiveLock); /* * Update relation's pg_class entry. Crucial side-effect: other @@ -406,7 +409,7 @@ void RelationRemoveTriggers(Relation rel) { Relation tgrel; - HeapScanDesc tgscan; + SysScanDesc tgscan; ScanKeyData key; HeapTuple tup; bool found = false; @@ -415,10 +418,10 @@ RelationRemoveTriggers(Relation rel) ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); + tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true, + SnapshotNow, 1, &key); - tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key); - - while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0))) + while (HeapTupleIsValid(tup = systable_getnext(tgscan))) { /* Delete any comments associated with this trigger */ DeleteComments(tup->t_data->t_oid, RelationGetRelid(tgrel)); @@ -428,7 +431,7 @@ RelationRemoveTriggers(Relation rel) found = true; } - heap_endscan(tgscan); + systable_endscan(tgscan); /* * If we deleted any triggers, must update pg_class entry and advance @@ -468,9 +471,10 @@ RelationRemoveTriggers(Relation rel) ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgconstrrelid, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); + tgscan = systable_beginscan(tgrel, TriggerConstrRelidIndex, true, + SnapshotNow, 1, &key); - tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key); - while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0))) + while (HeapTupleIsValid(tup = systable_getnext(tgscan))) { Form_pg_trigger pg_trigger; Relation refrel; @@ -500,7 +504,7 @@ RelationRemoveTriggers(Relation rel) pfree(stmt.relname); pfree(stmt.trigname); } - heap_endscan(tgscan); + systable_endscan(tgscan); heap_close(tgrel, RowExclusiveLock); } @@ -518,69 +522,33 @@ RelationBuildTriggers(Relation relation) TriggerDesc *trigdesc; int ntrigs = relation->rd_rel->reltriggers; Trigger *triggers = NULL; - Trigger *build; + int found = 0; Relation tgrel; - Form_pg_trigger pg_trigger; - Relation irel = (Relation) NULL; ScanKeyData skey; - HeapTupleData tuple; - IndexScanDesc sd = (IndexScanDesc) NULL; - HeapScanDesc tgscan = (HeapScanDesc) NULL; + SysScanDesc tgscan; HeapTuple htup; - RetrieveIndexResult indexRes; - Buffer buffer; struct varlena *val; bool isnull; - int found; - bool hasindex; - - trigdesc = (TriggerDesc *) MemoryContextAlloc(CacheMemoryContext, - sizeof(TriggerDesc)); - MemSet(trigdesc, 0, sizeof(TriggerDesc)); ScanKeyEntryInitialize(&skey, (bits16) 0x0, - (AttrNumber) 1, + (AttrNumber) Anum_pg_trigger_tgrelid, (RegProcedure) F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); tgrel = heap_openr(TriggerRelationName, AccessShareLock); - hasindex = (tgrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes()); - if (hasindex) - { - irel = index_openr(TriggerRelidIndex); - sd = index_beginscan(irel, false, 1, &skey); - } - else - tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &skey); + tgscan = systable_beginscan(tgrel, TriggerRelidIndex, true, + SnapshotNow, 1, &skey); - for (found = 0;;) + while (HeapTupleIsValid(htup = systable_getnext(tgscan))) { - if (hasindex) - { - indexRes = index_getnext(sd, ForwardScanDirection); - if (!indexRes) - break; + Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); + Trigger *build; - tuple.t_self = indexRes->heap_iptr; - heap_fetch(tgrel, SnapshotNow, &tuple, &buffer, sd); - pfree(indexRes); - if (!tuple.t_data) - continue; - htup = &tuple; - } - else - { - htup = heap_getnext(tgscan, 0); - if (!HeapTupleIsValid(htup)) - break; - } if (found == ntrigs) elog(ERROR, "RelationBuildTriggers: unexpected record found for rel %s", RelationGetRelationName(relation)); - pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); - if (triggers == NULL) triggers = (Trigger *) MemoryContextAlloc(CacheMemoryContext, sizeof(Trigger)); @@ -628,25 +596,20 @@ RelationBuildTriggers(Relation relation) build->tgargs = NULL; found++; - if (hasindex) - ReleaseBuffer(buffer); } - if (found < ntrigs) + systable_endscan(tgscan); + heap_close(tgrel, AccessShareLock); + + if (found != ntrigs) elog(ERROR, "RelationBuildTriggers: %d record(s) not found for rel %s", ntrigs - found, RelationGetRelationName(relation)); - if (hasindex) - { - index_endscan(sd); - index_close(irel); - } - else - heap_endscan(tgscan); - heap_close(tgrel, AccessShareLock); - /* Build trigdesc */ + trigdesc = (TriggerDesc *) MemoryContextAlloc(CacheMemoryContext, + sizeof(TriggerDesc)); + MemSet(trigdesc, 0, sizeof(TriggerDesc)); trigdesc->triggers = triggers; trigdesc->numtriggers = ntrigs; for (found = 0; found < ntrigs; found++) @@ -1741,14 +1704,12 @@ void DeferredTriggerSetState(ConstraintsSetStmt *stmt) { Relation tgrel; - Relation irel = (Relation) NULL; List *l; List *ls; List *loid = NIL; MemoryContext oldcxt; bool found; DeferredTriggerStatus state; - bool hasindex; /* * Handle SET CONSTRAINTS ALL ... @@ -1817,21 +1778,12 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) * ---------- */ tgrel = heap_openr(TriggerRelationName, AccessShareLock); - hasindex = (tgrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes()); - if (hasindex) - irel = index_openr(TriggerConstrNameIndex); foreach(l, stmt->constraints) { ScanKeyData skey; - HeapTupleData tuple; - IndexScanDesc sd = (IndexScanDesc) NULL; - HeapScanDesc tgscan = (HeapScanDesc) NULL; + SysScanDesc tgscan; HeapTuple htup; - RetrieveIndexResult indexRes; - Buffer buffer; - Form_pg_trigger pg_trigger; - Oid constr_oid; /* * Check that only named constraints are set explicitly @@ -1844,47 +1796,28 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) */ ScanKeyEntryInitialize(&skey, (bits16) 0x0, - (AttrNumber) 1, + (AttrNumber) Anum_pg_trigger_tgconstrname, (RegProcedure) F_NAMEEQ, PointerGetDatum((char *) lfirst(l))); - if (hasindex) - sd = index_beginscan(irel, false, 1, &skey); - else - tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &skey); + tgscan = systable_beginscan(tgrel, TriggerConstrNameIndex, true, + SnapshotNow, 1, &skey); /* * ... and search for the constraint trigger row */ found = false; - for (;;) - { - if (hasindex) - { - indexRes = index_getnext(sd, ForwardScanDirection); - if (!indexRes) - break; - tuple.t_self = indexRes->heap_iptr; - heap_fetch(tgrel, SnapshotNow, &tuple, &buffer, sd); - pfree(indexRes); - if (!tuple.t_data) - continue; - htup = &tuple; - } - else - { - htup = heap_getnext(tgscan, 0); - if (!HeapTupleIsValid(htup)) - break; - } + while (HeapTupleIsValid(htup = systable_getnext(tgscan))) + { + Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); + Oid constr_oid; /* * If we found some, check that they fit the deferrability but * skip ON <event> RESTRICT ones, since they are silently * never deferrable. */ - pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); if (stmt->deferred && !pg_trigger->tgdeferrable && pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD && pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL) @@ -1894,24 +1827,16 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) constr_oid = htup->t_data->t_oid; loid = lappendi(loid, constr_oid); found = true; - - if (hasindex) - ReleaseBuffer(buffer); } + systable_endscan(tgscan); + /* * Not found ? */ if (!found) elog(ERROR, "Constraint '%s' does not exist", (char *) lfirst(l)); - - if (hasindex) - index_endscan(sd); - else - heap_endscan(tgscan); } - if (hasindex) - index_close(irel); heap_close(tgrel, AccessShareLock); if (!IsTransactionBlock()) diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 894a7e328559092b43b94eabfe35f2cc6874a260..a36ae25b3f840302bd1e40824acb45bcef3ce27e 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.213 2002/01/06 00:37:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.214 2002/02/19 20:11:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -315,20 +315,6 @@ vacuum_shutdown(VacuumStmt *vacstmt) vac_truncate_clog(initialOldestXmin, initialFreezeLimit); } - /* - * If we did a complete vacuum or analyze, then flush the init file - * that relcache.c uses to save startup time. The next backend startup - * will rebuild the init file with up-to-date information from - * pg_class. This lets the optimizer see the stats that we've - * collected for certain critical system indexes. See relcache.c for - * more details. - * - * Ignore any failure to unlink the file, since it might not be there if - * no backend has been started since the last vacuum. - */ - if (vacstmt->vacrel == NULL) - unlink(RELCACHE_INIT_FILENAME); - /* * Clean up working storage --- note we must do this after * StartTransactionCommand, else we might be trying to delete the @@ -535,8 +521,15 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples, if (!hasindex) pgcform->relhaspkey = false; - /* invalidate the tuple in the cache and write the buffer */ + /* + * Invalidate the tuple in the catcaches; this also arranges to flush + * the relation's relcache entry. (If we fail to commit for some reason, + * no flush will occur, but no great harm is done since there are no + * noncritical state updates here.) + */ RelationInvalidateHeapTuple(rd, &rtup); + + /* Write the buffer */ WriteBuffer(buffer); heap_close(rd, RowExclusiveLock); @@ -2816,10 +2809,6 @@ vac_close_indexes(int nindexes, Relation *Irel) bool vac_is_partial_index(Relation indrel) { - bool result; - HeapTuple cachetuple; - Form_pg_index indexStruct; - /* * If the index's AM doesn't support nulls, it's partial for our * purposes @@ -2828,18 +2817,7 @@ vac_is_partial_index(Relation indrel) return true; /* Otherwise, look to see if there's a partial-index predicate */ - cachetuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(RelationGetRelid(indrel)), - 0, 0, 0); - if (!HeapTupleIsValid(cachetuple)) - elog(ERROR, "vac_is_partial_index: index %u not found", - RelationGetRelid(indrel)); - indexStruct = (Form_pg_index) GETSTRUCT(cachetuple); - - result = (VARSIZE(&indexStruct->indpred) > VARHDRSZ); - - ReleaseSysCache(cachetuple); - return result; + return (VARSIZE(&indrel->rd_index->indpred) > VARHDRSZ); } diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index af0e190d396d42a65a773e62c9d5e9c38766183d..e7a335916bdfe99a922b65c5028ec761f9803b1d 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -6,25 +6,12 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execAmi.c,v 1.60 2001/10/25 05:49:27 momjian Exp $ + * $Id: execAmi.c,v 1.61 2002/02/19 20:11:13 tgl Exp $ * *------------------------------------------------------------------------- */ -/* - * INTERFACE ROUTINES - * - * ExecOpenScanR \ / amopen - * ExecBeginScan \ / ambeginscan - * ExecCloseR \ / amclose - * ExecInsert \ executor interface / aminsert - * ExecReScanR / to access methods \ amrescan - * ExecMarkPos / \ ammarkpos - * ExecRestrPos / \ amrestpos - */ - #include "postgres.h" - #include "access/genam.h" #include "access/heapam.h" #include "catalog/heap.h" @@ -50,202 +37,6 @@ #include "executor/nodeSubqueryscan.h" #include "executor/nodeUnique.h" -static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys, - bool isindex, ScanDirection dir, Snapshot snapshot); - -/* ---------------------------------------------------------------- - * ExecOpenScanR - * - * old comments: - * Parameters: - * relation -- relation to be opened and scanned. - * nkeys -- number of keys - * skeys -- keys to restrict scanning - * isindex -- if this is true, the relation is the relid of - * an index relation, else it is a heap relation. - * Returns the relation as(relDesc scanDesc) - * ---------------------------------------------------------------- - */ -void -ExecOpenScanR(Oid relOid, - int nkeys, - ScanKey skeys, - bool isindex, - ScanDirection dir, - Snapshot snapshot, - Relation *returnRelation, /* return */ - Pointer *returnScanDesc) /* return */ -{ - Relation relation; - Pointer scanDesc; - - /* - * note: scanDesc returned by ExecBeginScan can be either a - * HeapScanDesc or an IndexScanDesc so for now we make it a Pointer. - * There should be a better scan abstraction someday -cim 9/9/89 - */ - - /* - * Open the relation with the correct call depending on whether this - * is a heap relation or an index relation. - * - * For a table, acquire AccessShareLock for the duration of the query - * execution. For indexes, acquire no lock here; the index machinery - * does its own locks and unlocks. (We rely on having some kind of - * lock on the parent table to ensure the index won't go away!) - */ - if (isindex) - relation = index_open(relOid); - else - relation = heap_open(relOid, AccessShareLock); - - scanDesc = ExecBeginScan(relation, - nkeys, - skeys, - isindex, - dir, - snapshot); - - if (returnRelation != NULL) - *returnRelation = relation; - if (scanDesc != NULL) - *returnScanDesc = scanDesc; -} - -/* ---------------------------------------------------------------- - * ExecBeginScan - * - * beginscans a relation in current direction. - * - * XXX fix parameters to AMbeginscan (and btbeginscan) - * currently we need to pass a flag stating whether - * or not the scan should begin at an endpoint of - * the relation.. Right now we always pass false - * -cim 9/14/89 - * ---------------------------------------------------------------- - */ -static Pointer -ExecBeginScan(Relation relation, - int nkeys, - ScanKey skeys, - bool isindex, - ScanDirection dir, - Snapshot snapshot) -{ - Pointer scanDesc; - - /* - * open the appropriate type of scan. - * - * Note: ambeginscan()'s second arg is a boolean indicating that the scan - * should be done in reverse.. That is, if you pass it true, then the - * scan is backward. - */ - if (isindex) - { - scanDesc = (Pointer) index_beginscan(relation, - false, /* see above comment */ - nkeys, - skeys); - } - else - { - scanDesc = (Pointer) heap_beginscan(relation, - ScanDirectionIsBackward(dir), - snapshot, - nkeys, - skeys); - } - - if (scanDesc == NULL) - elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed."); - - return scanDesc; -} - -/* ---------------------------------------------------------------- - * ExecCloseR - * - * closes the relation and scan descriptor for a scan node. - * Also closes index relations and scans for index scans. - * ---------------------------------------------------------------- - */ -void -ExecCloseR(Plan *node) -{ - CommonScanState *state; - Relation relation; - HeapScanDesc scanDesc; - - /* - * get state for node and shut down the heap scan, if any - */ - switch (nodeTag(node)) - { - case T_SeqScan: - state = ((SeqScan *) node)->scanstate; - break; - - case T_IndexScan: - state = ((IndexScan *) node)->scan.scanstate; - break; - - case T_TidScan: - state = ((TidScan *) node)->scan.scanstate; - break; - - default: - elog(DEBUG, "ExecCloseR: not a scan node!"); - return; - } - - relation = state->css_currentRelation; - scanDesc = state->css_currentScanDesc; - - if (scanDesc != NULL) - heap_endscan(scanDesc); - - /* - * if this is an index scan then we have to take care of the index - * relations as well. - */ - if (IsA(node, IndexScan)) - { - IndexScan *iscan = (IndexScan *) node; - IndexScanState *indexstate = iscan->indxstate; - int numIndices; - RelationPtr indexRelationDescs; - IndexScanDescPtr indexScanDescs; - int i; - - numIndices = indexstate->iss_NumIndices; - indexRelationDescs = indexstate->iss_RelationDescs; - indexScanDescs = indexstate->iss_ScanDescs; - - for (i = 0; i < numIndices; i++) - { - /* - * shut down each of the index scans and close each of the - * index relations - */ - if (indexScanDescs[i] != NULL) - index_endscan(indexScanDescs[i]); - - if (indexRelationDescs[i] != NULL) - index_close(indexRelationDescs[i]); - } - } - - /* - * Finally, close the heap relation. - * - * Currently, we do not release the AccessShareLock acquired by - * ExecOpenScanR. This lock should be held till end of transaction. - * (There is a faction that considers this too much locking, however.) - */ - if (relation != NULL) - heap_close(relation, NoLock); -} /* ---------------------------------------------------------------- * ExecReScan @@ -374,27 +165,6 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent) } } -/* ---------------------------------------------------------------- - * ExecReScanR - * - * XXX this does not do the right thing with indices yet. - * ---------------------------------------------------------------- - */ -HeapScanDesc -ExecReScanR(Relation relDesc, /* LLL relDesc unused */ - HeapScanDesc scanDesc, - ScanDirection direction, - int nkeys, /* LLL nkeys unused */ - ScanKey skeys) -{ - if (scanDesc != NULL) - heap_rescan(scanDesc, /* scan desc */ - ScanDirectionIsBackward(direction), /* backward flag */ - skeys); /* scan keys */ - - return scanDesc; -} - /* ---------------------------------------------------------------- * ExecMarkPos * diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index d710c8270334cb0350702e055fae22537c630dee..5b0719487792960068bd816bf28cd616abd0c163 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.78 2001/10/25 05:49:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.79 2002/02/19 20:11:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -496,7 +496,6 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo) { Oid indexOid = lfirsti(indexoidscan); Relation indexDesc; - HeapTuple indexTuple; IndexInfo *ii; /* @@ -525,20 +524,9 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo) LockRelation(indexDesc, AccessExclusiveLock); /* - * Get the pg_index tuple for the index + * extract index key information from the index's pg_index tuple */ - indexTuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(indexOid), - 0, 0, 0); - if (!HeapTupleIsValid(indexTuple)) - elog(ERROR, "ExecOpenIndices: index %u not found", indexOid); - - /* - * extract the index key information from the tuple - */ - ii = BuildIndexInfo(indexTuple); - - ReleaseSysCache(indexTuple); + ii = BuildIndexInfo(indexDesc->rd_index); relationDescs[i] = indexDesc; indexInfoArray[i] = ii; diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index bde5bce334dbc9b1d2aa66379a1ef79bef3fd795..3ab73f62f9a9746d77e6eb0d4eea8dda256f3643 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.66 2002/02/11 20:10:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.67 2002/02/19 20:11:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -359,14 +359,20 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) int n_keys; ScanKey scan_keys; int *run_keys; + List *listscan; indxqual = lnext(indxqual); n_keys = numScanKeys[i]; scan_keys = scanKeys[i]; run_keys = runtimeKeyInfo[i]; + listscan = qual; for (j = 0; j < n_keys; j++) { + Expr *clause = lfirst(listscan); + + listscan = lnext(listscan); + /* * If we have a run-time key, then extract the run-time * expression and evaluate it with respect to the current @@ -382,7 +388,6 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) */ if (run_keys[j] != NO_OP) { - Expr *clause = nth(j, qual); Node *scanexpr; Datum scanvalue; bool isNull; @@ -448,6 +453,9 @@ ExecEndIndexScan(IndexScan *node) List *indxqual; int *numScanKeys; int numIndices; + Relation relation; + RelationPtr indexRelationDescs; + IndexScanDescPtr indexScanDescs; int i; scanstate = node->scan.scanstate; @@ -461,6 +469,9 @@ ExecEndIndexScan(IndexScan *node) numIndices = indexstate->iss_NumIndices; scanKeys = indexstate->iss_ScanKeys; numScanKeys = indexstate->iss_NumScanKeys; + indexRelationDescs = indexstate->iss_RelationDescs; + indexScanDescs = indexstate->iss_ScanDescs; + relation = scanstate->css_currentRelation; /* * Free the projection info and the scan attribute info @@ -475,9 +486,25 @@ ExecEndIndexScan(IndexScan *node) FreeExprContext(indexstate->iss_RuntimeContext); /* - * close the heap and index relations + * close the index relations + */ + for (i = 0; i < numIndices; i++) + { + if (indexScanDescs[i] != NULL) + index_endscan(indexScanDescs[i]); + + if (indexRelationDescs[i] != NULL) + index_close(indexRelationDescs[i]); + } + + /* + * close the heap relation. + * + * Currently, we do not release the AccessShareLock acquired by + * ExecInitIndexScan. This lock should be held till end of transaction. + * (There is a faction that considers this too much locking, however.) */ - ExecCloseR((Plan *) node); + heap_close(relation, NoLock); /* * free the scan keys used in scanning the indices @@ -589,6 +616,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) CommonScanState *scanstate; List *indxqual; List *indxid; + List *listscan; int i; int numIndices; int indexPtr; @@ -603,7 +631,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) Index relid; Oid reloid; Relation currentRelation; - HeapScanDesc currentScanDesc; ScanDirection direction; /* @@ -709,6 +736,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) * for each opclause in the given qual, convert each qual's * opclause into a single scan key */ + listscan = qual; for (j = 0; j < n_keys; j++) { Expr *clause; /* one clause of index qual */ @@ -725,7 +753,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) /* * extract clause information from the qualification */ - clause = nth(j, qual); + clause = lfirst(listscan); + listscan = lnext(listscan); op = (Oper *) clause->oper; if (!IsA(clause, Expr) ||!IsA(op, Oper)) @@ -989,25 +1018,19 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) direction = estate->es_direction; /* - * open the base relation + * open the base relation and acquire AccessShareLock on it. */ relid = node->scan.scanrelid; rtentry = rt_fetch(relid, rangeTable); reloid = rtentry->relid; - ExecOpenScanR(reloid, /* relation */ - 0, /* nkeys */ - (ScanKey) NULL, /* scan key */ - false, /* is index */ - direction, /* scan direction */ - estate->es_snapshot, /* */ - ¤tRelation, /* return: rel desc */ - (Pointer *) ¤tScanDesc); /* return: scan desc */ + currentRelation = heap_open(reloid, AccessShareLock); if (!RelationGetForm(currentRelation)->relhasindex) elog(ERROR, "indexes of the relation %u was inactivated", reloid); + scanstate->css_currentRelation = currentRelation; - scanstate->css_currentScanDesc = currentScanDesc; + scanstate->css_currentScanDesc = NULL; /* no heap scan here */ /* * get the scan type from the relation descriptor. @@ -1017,24 +1040,30 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) /* * open the index relations and initialize relation and scan - * descriptors. + * descriptors. Note we acquire no locks here; the index machinery + * does its own locks and unlocks. (We rely on having AccessShareLock + * on the parent table to ensure the index won't go away!) */ + listscan = indxid; for (i = 0; i < numIndices; i++) { - Oid indexOid = (Oid) nthi(i, indxid); + Oid indexOid = (Oid) lfirsti(listscan); if (indexOid != 0) { - ExecOpenScanR(indexOid, /* relation */ - numScanKeys[i], /* nkeys */ - scanKeys[i], /* scan key */ - true, /* is index */ - direction, /* scan direction */ - estate->es_snapshot, - &(relationDescs[i]), /* return: rel desc */ - (Pointer *) &(scanDescs[i])); - /* return: scan desc */ + relationDescs[i] = index_open(indexOid); + + /* + * Note: index_beginscan()'s second arg is a boolean indicating + * that the scan should be done in reverse. That is, if you pass + * it true, then the scan is backward. + */ + scanDescs[i] = index_beginscan(relationDescs[i], + false, /* see above comment */ + numScanKeys[i], + scanKeys[i]); } + listscan = lnext(listscan); } indexstate->iss_RelationDescs = relationDescs; diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index d7ebec4985f8d80783d9f4d3ff84f4fea0b77d31..957a1bb2b722813b2f888cc0969616ed7a4b7d46 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.33 2001/10/28 06:25:43 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.34 2002/02/19 20:11:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -154,7 +154,9 @@ InitScanRelation(SeqScan *node, EState *estate, /* * get the relation object id from the relid'th entry in the range - * table, open that relation and initialize the scan state... + * table, open that relation and initialize the scan state. + * + * We acquire AccessShareLock for the duration of the scan. */ relid = node->scanrelid; rangeTable = estate->es_range_table; @@ -162,14 +164,13 @@ InitScanRelation(SeqScan *node, EState *estate, reloid = rtentry->relid; direction = estate->es_direction; - ExecOpenScanR(reloid, /* relation */ - 0, /* nkeys */ - NULL, /* scan key */ - false, /* is index */ - direction, /* scan direction */ - estate->es_snapshot, - ¤tRelation, /* return: rel desc */ - (Pointer *) ¤tScanDesc); /* return: scan desc */ + currentRelation = heap_open(reloid, AccessShareLock); + + currentScanDesc = heap_beginscan(currentRelation, + ScanDirectionIsBackward(direction), + estate->es_snapshot, + 0, + NULL); scanstate->css_currentRelation = currentRelation; scanstate->css_currentScanDesc = currentScanDesc; @@ -189,7 +190,6 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent) { CommonScanState *scanstate; Oid reloid; - HeapScanDesc scandesc; /* * Once upon a time it was possible to have an outerPlan of a SeqScan, @@ -229,7 +229,6 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent) */ reloid = InitScanRelation(node, estate, scanstate); - scandesc = scanstate->css_currentScanDesc; scanstate->cstate.cs_TupFromTlist = false; /* @@ -259,11 +258,15 @@ void ExecEndSeqScan(SeqScan *node) { CommonScanState *scanstate; + Relation relation; + HeapScanDesc scanDesc; /* * get information from node */ scanstate = node->scanstate; + relation = scanstate->css_currentRelation; + scanDesc = scanstate->css_currentScanDesc; /* * Free the projection info and the scan attribute info @@ -276,9 +279,18 @@ ExecEndSeqScan(SeqScan *node) ExecFreeExprContext(&scanstate->cstate); /* - * close scan relation + * close heap scan + */ + heap_endscan(scanDesc); + + /* + * close the heap relation. + * + * Currently, we do not release the AccessShareLock acquired by + * InitScanRelation. This lock should be held till end of transaction. + * (There is a faction that considers this too much locking, however.) */ - ExecCloseR((Plan *) node); + heap_close(relation, NoLock); /* * clean out the tuple table @@ -303,7 +315,6 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent) { CommonScanState *scanstate; EState *estate; - Relation rel; HeapScanDesc scan; ScanDirection direction; @@ -317,11 +328,13 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent) estate->es_evTupleNull[node->scanrelid - 1] = false; return; } - rel = scanstate->css_currentRelation; + scan = scanstate->css_currentScanDesc; direction = estate->es_direction; - scan = ExecReScanR(rel, scan, direction, 0, NULL); - scanstate->css_currentScanDesc = scan; + + heap_rescan(scan, /* scan desc */ + ScanDirectionIsBackward(direction), /* backward flag */ + NULL); /* new scan keys */ } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 08685571cab4acc4863db757e637f285af50f945..5ee222744d3b64d35d2763c043759233a13f932b 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.22 2002/02/11 20:10:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.23 2002/02/19 20:11:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -300,15 +300,14 @@ ExecEndTidScan(TidScan *node) CommonScanState *scanstate; TidScanState *tidstate; + /* + * extract information from the node + */ scanstate = node->scan.scanstate; tidstate = node->tidstate; if (tidstate && tidstate->tss_TidList) pfree(tidstate->tss_TidList); - /* - * extract information from the node - */ - /* * Free the projection info and the scan attribute info * @@ -320,9 +319,13 @@ ExecEndTidScan(TidScan *node) ExecFreeExprContext(&scanstate->cstate); /* - * close the heap and tid relations + * close the heap relation. + * + * Currently, we do not release the AccessShareLock acquired by + * ExecInitTidScan. This lock should be held till end of transaction. + * (There is a faction that considers this too much locking, however.) */ - ExecCloseR((Plan *) node); + heap_close(scanstate->css_currentRelation, NoLock); /* * clear out tuple table slots @@ -460,14 +463,17 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent) /* * open the base relation + * + * We acquire AccessShareLock for the duration of the scan. */ relid = node->scan.scanrelid; rtentry = rt_fetch(relid, rangeTable); reloid = rtentry->relid; currentRelation = heap_open(reloid, AccessShareLock); + scanstate->css_currentRelation = currentRelation; - scanstate->css_currentScanDesc = 0; + scanstate->css_currentScanDesc = NULL; /* no heap scan here */ /* * get the scan type from the relation descriptor. diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 86ebb09ff677eb38749965668e9217a1b3fac0eb..447b3e61f2b8b9ccd871edb491bfc3c22c986023 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.69 2001/10/25 05:49:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.70 2002/02/19 20:11:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -98,20 +98,15 @@ find_secondary_indexes(Oid relationObjectId) foreach(indexoidscan, indexoidlist) { Oid indexoid = lfirsti(indexoidscan); - HeapTuple indexTuple; + Relation indexRelation; Form_pg_index index; IndexOptInfo *info; int i; - Relation indexRelation; int16 amorderstrategy; - indexTuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(indexoid), - 0, 0, 0); - if (!HeapTupleIsValid(indexTuple)) - elog(ERROR, "find_secondary_indexes: index %u not found", - indexoid); - index = (Form_pg_index) GETSTRUCT(indexTuple); + /* Extract info from the relation descriptor for the index */ + indexRelation = index_open(indexoid); + info = makeNode(IndexOptInfo); /* @@ -123,6 +118,7 @@ find_secondary_indexes(Oid relationObjectId) info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1)); /* Extract info from the pg_index tuple */ + index = indexRelation->rd_index; info->indexoid = index->indexrelid; info->indproc = index->indproc; /* functional index ?? */ if (VARSIZE(&index->indpred) > VARHDRSZ) /* partial index ?? */ @@ -156,14 +152,11 @@ find_secondary_indexes(Oid relationObjectId) info->indexkeys[i] = 0; info->nkeys = i; - /* Extract info from the relation descriptor for the index */ - indexRelation = index_open(index->indexrelid); info->relam = indexRelation->rd_rel->relam; info->pages = indexRelation->rd_rel->relpages; info->tuples = indexRelation->rd_rel->reltuples; info->amcostestimate = index_cost_estimator(indexRelation); amorderstrategy = indexRelation->rd_am->amorderstrategy; - index_close(indexRelation); /* * Fetch the ordering operators associated with the index, if any. @@ -171,26 +164,16 @@ find_secondary_indexes(Oid relationObjectId) MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1)); if (amorderstrategy != 0) { + int oprindex = amorderstrategy - 1; + for (i = 0; i < info->ncolumns; i++) { - HeapTuple amopTuple; - Form_pg_amop amop; - - amopTuple = - SearchSysCache(AMOPSTRATEGY, - ObjectIdGetDatum(index->indclass[i]), - Int16GetDatum(amorderstrategy), - 0, 0); - if (!HeapTupleIsValid(amopTuple)) - elog(ERROR, "find_secondary_indexes: no amop %u %d", - index->indclass[i], (int) amorderstrategy); - amop = (Form_pg_amop) GETSTRUCT(amopTuple); - info->ordering[i] = amop->amopopr; - ReleaseSysCache(amopTuple); + info->ordering[i] = indexRelation->rd_operator[oprindex]; + oprindex += indexRelation->rd_am->amstrategies; } } - ReleaseSysCache(indexTuple); + index_close(indexRelation); indexinfos = lcons(info, indexinfos); } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 7739185133874b4f3b948c1e289ade2370e7c715..766a5daad55b8fcaa5780896c20324535ec984f9 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.115 2001/12/12 03:28:49 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.116 2002/02/19 20:11:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -547,11 +547,9 @@ agg_get_candidates(char *aggname, Oid typeId, CandidateList *candidates) { - CandidateList current_candidate; Relation pg_aggregate_desc; - HeapScanDesc pg_aggregate_scan; + SysScanDesc pg_aggregate_scan; HeapTuple tup; - Form_pg_aggregate agg; int ncandidates = 0; ScanKeyData aggKey[1]; @@ -563,15 +561,15 @@ agg_get_candidates(char *aggname, NameGetDatum(aggname)); pg_aggregate_desc = heap_openr(AggregateRelationName, AccessShareLock); - pg_aggregate_scan = heap_beginscan(pg_aggregate_desc, - 0, - SnapshotSelf, /* ??? */ - 1, - aggKey); + pg_aggregate_scan = systable_beginscan(pg_aggregate_desc, + AggregateNameTypeIndex, true, + SnapshotNow, + 1, aggKey); - while (HeapTupleIsValid(tup = heap_getnext(pg_aggregate_scan, 0))) + while (HeapTupleIsValid(tup = systable_getnext(pg_aggregate_scan))) { - agg = (Form_pg_aggregate) GETSTRUCT(tup); + Form_pg_aggregate agg = (Form_pg_aggregate) GETSTRUCT(tup); + CandidateList current_candidate; current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); current_candidate->args = (Oid *) palloc(sizeof(Oid)); @@ -582,7 +580,7 @@ agg_get_candidates(char *aggname, ncandidates++; } - heap_endscan(pg_aggregate_scan); + systable_endscan(pg_aggregate_scan); heap_close(pg_aggregate_desc, AccessShareLock); return ncandidates; @@ -680,62 +678,46 @@ static CandidateList func_get_candidates(char *funcname, int nargs) { Relation heapRelation; - Relation idesc; - ScanKeyData skey; - HeapTupleData tuple; - IndexScanDesc sd; - RetrieveIndexResult indexRes; - Form_pg_proc pgProcP; + ScanKeyData skey[2]; + HeapTuple tuple; + SysScanDesc funcscan; CandidateList candidates = NULL; - CandidateList current_candidate; int i; heapRelation = heap_openr(ProcedureRelationName, AccessShareLock); - ScanKeyEntryInitialize(&skey, + + ScanKeyEntryInitialize(&skey[0], (bits16) 0x0, (AttrNumber) Anum_pg_proc_proname, (RegProcedure) F_NAMEEQ, PointerGetDatum(funcname)); + ScanKeyEntryInitialize(&skey[1], + (bits16) 0x0, + (AttrNumber) Anum_pg_proc_pronargs, + (RegProcedure) F_INT2EQ, + Int16GetDatum(nargs)); - idesc = index_openr(ProcedureNameIndex); - - sd = index_beginscan(idesc, false, 1, &skey); + funcscan = systable_beginscan(heapRelation, ProcedureNameIndex, true, + SnapshotNow, 2, skey); - do + while (HeapTupleIsValid(tuple = systable_getnext(funcscan))) { - indexRes = index_getnext(sd, ForwardScanDirection); - if (indexRes) - { - Buffer buffer; - - tuple.t_datamcxt = NULL; - tuple.t_data = NULL; - tuple.t_self = indexRes->heap_iptr; - heap_fetch(heapRelation, SnapshotNow, &tuple, &buffer, sd); - pfree(indexRes); - if (tuple.t_data != NULL) - { - pgProcP = (Form_pg_proc) GETSTRUCT(&tuple); - if (pgProcP->pronargs == nargs) - { - current_candidate = (CandidateList) - palloc(sizeof(struct _CandidateList)); - current_candidate->args = (Oid *) - palloc(FUNC_MAX_ARGS * sizeof(Oid)); - MemSet(current_candidate->args, 0, FUNC_MAX_ARGS * sizeof(Oid)); - for (i = 0; i < nargs; i++) - current_candidate->args[i] = pgProcP->proargtypes[i]; - - current_candidate->next = candidates; - candidates = current_candidate; - } - ReleaseBuffer(buffer); - } - } - } while (indexRes); + Form_pg_proc pgProcP = (Form_pg_proc) GETSTRUCT(tuple); + CandidateList current_candidate; + + current_candidate = (CandidateList) + palloc(sizeof(struct _CandidateList)); + current_candidate->args = (Oid *) + palloc(FUNC_MAX_ARGS * sizeof(Oid)); + MemSet(current_candidate->args, 0, FUNC_MAX_ARGS * sizeof(Oid)); + for (i = 0; i < nargs; i++) + current_candidate->args[i] = pgProcP->proargtypes[i]; + + current_candidate->next = candidates; + candidates = current_candidate; + } - index_endscan(sd); - index_close(idesc); + systable_endscan(funcscan); heap_close(heapRelation, AccessShareLock); return candidates; diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 15a39dc84bc4a99aa9a13fb4b32e844ede30c8c0..318f1b9eb7e26f6bc2a23b01205437a6c7de6d0d 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,15 +8,17 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.51 2001/10/25 05:49:40 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.52 2002/02/19 20:11:15 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "access/genam.h" #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/indexing.h" #include "catalog/pg_operator.h" #include "parser/parse_coerce.h" #include "parser/parse_func.h" @@ -80,13 +82,11 @@ static int binary_oper_get_candidates(char *opname, CandidateList *candidates) { - CandidateList current_candidate; Relation pg_operator_desc; - HeapScanDesc pg_operator_scan; + SysScanDesc pg_operator_scan; HeapTuple tup; - Form_pg_operator oper; int ncandidates = 0; - ScanKeyData opKey[2]; + ScanKeyData opKey[1]; *candidates = NULL; @@ -95,38 +95,94 @@ binary_oper_get_candidates(char *opname, F_NAMEEQ, NameGetDatum(opname)); - ScanKeyEntryInitialize(&opKey[1], 0, - Anum_pg_operator_oprkind, - F_CHAREQ, - CharGetDatum('b')); - pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock); - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SnapshotSelf, /* ??? */ - 2, - opKey); + pg_operator_scan = systable_beginscan(pg_operator_desc, + OperatorNameIndex, true, + SnapshotNow, + 1, opKey); - while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0))) + while (HeapTupleIsValid(tup = systable_getnext(pg_operator_scan))) { - oper = (Form_pg_operator) GETSTRUCT(tup); + Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tup); + + if (oper->oprkind == 'b') + { + CandidateList current_candidate; - current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); - current_candidate->args = (Oid *) palloc(2 * sizeof(Oid)); + current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); + current_candidate->args = (Oid *) palloc(2 * sizeof(Oid)); - current_candidate->args[0] = oper->oprleft; - current_candidate->args[1] = oper->oprright; - current_candidate->next = *candidates; - *candidates = current_candidate; - ncandidates++; + current_candidate->args[0] = oper->oprleft; + current_candidate->args[1] = oper->oprright; + current_candidate->next = *candidates; + *candidates = current_candidate; + ncandidates++; + } } - heap_endscan(pg_operator_scan); + systable_endscan(pg_operator_scan); heap_close(pg_operator_desc, AccessShareLock); return ncandidates; } /* binary_oper_get_candidates() */ +/* unary_oper_get_candidates() + * given opname, find all possible types for which + * a right/left unary operator named opname exists. + * Build a list of the candidate input types. + * Returns number of candidates found. + */ +static int +unary_oper_get_candidates(char *opname, + CandidateList *candidates, + char rightleft) +{ + Relation pg_operator_desc; + SysScanDesc pg_operator_scan; + HeapTuple tup; + int ncandidates = 0; + ScanKeyData opKey[1]; + + *candidates = NULL; + + ScanKeyEntryInitialize(&opKey[0], 0, + Anum_pg_operator_oprname, + F_NAMEEQ, + NameGetDatum(opname)); + + pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock); + pg_operator_scan = systable_beginscan(pg_operator_desc, + OperatorNameIndex, true, + SnapshotNow, + 1, opKey); + + while (HeapTupleIsValid(tup = systable_getnext(pg_operator_scan))) + { + Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tup); + + if (oper->oprkind == rightleft) + { + CandidateList current_candidate; + + current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); + current_candidate->args = (Oid *) palloc(sizeof(Oid)); + + if (rightleft == 'r') + current_candidate->args[0] = oper->oprleft; + else + current_candidate->args[0] = oper->oprright; + current_candidate->next = *candidates; + *candidates = current_candidate; + ncandidates++; + } + } + + systable_endscan(pg_operator_scan); + heap_close(pg_operator_desc, AccessShareLock); + + return ncandidates; +} /* unary_oper_get_candidates() */ + /* oper_select_candidate() * Given the input argtype array and one or more candidates @@ -739,66 +795,6 @@ compatible_oper_funcid(char *op, Oid arg1, Oid arg2, bool noError) return InvalidOid; } -/* unary_oper_get_candidates() - * given opname, find all possible types for which - * a right/left unary operator named opname exists. - * Build a list of the candidate input types. - * Returns number of candidates found. - */ -static int -unary_oper_get_candidates(char *opname, - CandidateList *candidates, - char rightleft) -{ - CandidateList current_candidate; - Relation pg_operator_desc; - HeapScanDesc pg_operator_scan; - HeapTuple tup; - Form_pg_operator oper; - int ncandidates = 0; - ScanKeyData opKey[2]; - - *candidates = NULL; - - ScanKeyEntryInitialize(&opKey[0], 0, - Anum_pg_operator_oprname, - F_NAMEEQ, - NameGetDatum(opname)); - - ScanKeyEntryInitialize(&opKey[1], 0, - Anum_pg_operator_oprkind, - F_CHAREQ, - CharGetDatum(rightleft)); - - pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock); - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - SnapshotSelf, /* ??? */ - 2, - opKey); - - while (HeapTupleIsValid(tup = heap_getnext(pg_operator_scan, 0))) - { - oper = (Form_pg_operator) GETSTRUCT(tup); - - current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); - current_candidate->args = (Oid *) palloc(sizeof(Oid)); - - if (rightleft == 'r') - current_candidate->args[0] = oper->oprleft; - else - current_candidate->args[0] = oper->oprright; - current_candidate->next = *candidates; - *candidates = current_candidate; - ncandidates++; - } - - heap_endscan(pg_operator_scan); - heap_close(pg_operator_desc, AccessShareLock); - - return ncandidates; -} /* unary_oper_get_candidates() */ - /* Given unary right operator (operator on right), return oper struct * diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 27abf6ace505ed9bc418a642dfdff1b3584819d2..95e217b9a4fae4de388ab63a6dbdf999ba0d4ae0 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.89 2001/11/26 21:15:14 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.90 2002/02/19 20:11:16 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -341,6 +341,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS) HeapTuple ht_idx; HeapTuple ht_idxrel; HeapTuple ht_indrel; + HeapTuple ht_am; Form_pg_index idxrec; Form_pg_class idxrelrec; Form_pg_class indrelrec; @@ -383,13 +384,13 @@ pg_get_indexdef(PG_FUNCTION_ARGS) /* * Fetch the pg_am tuple of the index' access method - * - * There is no syscache for this, so use index.c subroutine. */ - amrec = AccessMethodObjectIdGetForm(idxrelrec->relam, - CurrentMemoryContext); - if (!amrec) - elog(ERROR, "lookup for AM %u failed", idxrelrec->relam); + ht_am = SearchSysCache(AMOID, + ObjectIdGetDatum(idxrelrec->relam), + 0, 0, 0); + if (!HeapTupleIsValid(ht_am)) + elog(ERROR, "syscache lookup for AM %u failed", idxrelrec->relam); + amrec = (Form_pg_am) GETSTRUCT(ht_am); /* * Start the index definition @@ -504,10 +505,11 @@ pg_get_indexdef(PG_FUNCTION_ARGS) pfree(buf.data); pfree(keybuf.data); - pfree(amrec); + ReleaseSysCache(ht_idx); ReleaseSysCache(ht_idxrel); ReleaseSysCache(ht_indrel); + ReleaseSysCache(ht_am); PG_RETURN_TEXT_P(indexdef); } diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index cab6291acd08a6b3af97b560150826e611b736ac..47ab410cfc235c5be152d071cd8248b7a8fbb67b 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.86 2001/11/05 17:46:30 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.87 2002/02/19 20:11:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,9 +24,13 @@ #include "catalog/catname.h" #include "catalog/indexing.h" #include "miscadmin.h" +#ifdef CATCACHE_STATS +#include "storage/ipc.h" /* for on_proc_exit */ +#endif #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/catcache.h" +#include "utils/relcache.h" #include "utils/syscache.h" @@ -94,6 +98,9 @@ static Index CatalogCacheComputeTupleHashIndex(CatCache *cache, HeapTuple tuple); static void CatalogCacheInitializeCache(CatCache *cache); static Datum cc_hashname(PG_FUNCTION_ARGS); +#ifdef CATCACHE_STATS +static void CatCachePrintStats(void); +#endif /* @@ -147,6 +154,46 @@ cc_hashname(PG_FUNCTION_ARGS) } +#ifdef CATCACHE_STATS + +static void +CatCachePrintStats(void) +{ + CatCache *cache; + long cc_searches = 0; + long cc_hits = 0; + long cc_newloads = 0; + + elog(DEBUG, "Catcache stats dump: %d/%d tuples in catcaches", + CacheHdr->ch_ntup, CacheHdr->ch_maxtup); + + for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next) + { + if (cache->cc_ntup == 0 && cache->cc_searches == 0) + continue; /* don't print unused caches */ + elog(DEBUG, "Catcache %s/%s: %d tup, %ld srch, %ld hits, %ld loads, %ld not found", + cache->cc_relname, + cache->cc_indname, + cache->cc_ntup, + cache->cc_searches, + cache->cc_hits, + cache->cc_newloads, + cache->cc_searches - cache->cc_hits - cache->cc_newloads); + cc_searches += cache->cc_searches; + cc_hits += cache->cc_hits; + cc_newloads += cache->cc_newloads; + } + elog(DEBUG, "Catcache totals: %d tup, %ld srch, %ld hits, %ld loads, %ld not found", + CacheHdr->ch_ntup, + cc_searches, + cc_hits, + cc_newloads, + cc_searches - cc_hits - cc_newloads); +} + +#endif /* CATCACHE_STATS */ + + /* * Standard routine for creating cache context if it doesn't exist yet * @@ -289,6 +336,30 @@ CatalogCacheInitializeCache(CatCache *cache) cache->cc_tupdesc = tupdesc; } +/* + * InitCatCachePhase2 -- external interface for CatalogCacheInitializeCache + * + * The only reason to call this routine is to ensure that the relcache + * has created entries for all the catalogs and indexes referenced by + * catcaches. Therefore, open the index too. An exception is the indexes + * on pg_am, which we don't use (cf. IndexScanOK). + */ +void +InitCatCachePhase2(CatCache *cache) +{ + if (cache->cc_tupdesc == NULL) + CatalogCacheInitializeCache(cache); + + if (cache->id != AMOID && + cache->id != AMNAME) + { + Relation idesc; + + idesc = index_openr(cache->cc_indname); + index_close(idesc); + } +} + /* * CatalogCacheComputeHashIndex */ @@ -644,11 +715,11 @@ CatalogCacheFlushRelation(Oid relId) { bool isNull; - tupRelid = DatumGetObjectId( - fastgetattr(&ct->tuple, - cache->cc_reloidattr, - cache->cc_tupdesc, - &isNull)); + tupRelid = + DatumGetObjectId(fastgetattr(&ct->tuple, + cache->cc_reloidattr, + cache->cc_tupdesc, + &isNull)); Assert(!isNull); } @@ -717,20 +788,19 @@ InitCatCache(int id, CacheHdr->ch_ntup = 0; CacheHdr->ch_maxtup = MAXCCTUPLES; DLInitList(&CacheHdr->ch_lrulist); +#ifdef CATCACHE_STATS + on_proc_exit(CatCachePrintStats, 0); +#endif } /* * allocate a new cache structure + * + * Note: we assume zeroing initializes the bucket headers correctly */ cp = (CatCache *) palloc(sizeof(CatCache) + NCCBUCKETS * sizeof(Dllist)); MemSet((char *) cp, 0, sizeof(CatCache) + NCCBUCKETS * sizeof(Dllist)); - /* - * initialize the cache buckets (each bucket is a list header) - */ - for (i = 0; i < NCCBUCKETS; ++i) - DLInitList(&cp->cc_bucket[i]); - /* * initialize the cache's relation information for the relation * corresponding to this cache, and initialize some of the new cache's @@ -777,57 +847,44 @@ InitCatCache(int id, * certain system indexes that support critical syscaches. * We can't use an indexscan to fetch these, else we'll get into * infinite recursion. A plain heap scan will work, however. + * + * Once we have completed relcache initialization (signaled by + * criticalRelcachesBuilt), we don't have to worry anymore. */ static bool IndexScanOK(CatCache *cache, ScanKey cur_skey) { if (cache->id == INDEXRELID) { - static Oid indexSelfOid = InvalidOid; - - /* One-time lookup of the OID of pg_index_indexrelid_index */ - if (!OidIsValid(indexSelfOid)) - { - Relation rel; - ScanKeyData key; - HeapScanDesc sd; - HeapTuple ntp; - - rel = heap_openr(RelationRelationName, AccessShareLock); - ScanKeyEntryInitialize(&key, 0, Anum_pg_class_relname, - F_NAMEEQ, - PointerGetDatum(IndexRelidIndex)); - sd = heap_beginscan(rel, false, SnapshotNow, 1, &key); - ntp = heap_getnext(sd, 0); - if (!HeapTupleIsValid(ntp)) - elog(ERROR, "IndexScanOK: %s not found in %s", - IndexRelidIndex, RelationRelationName); - indexSelfOid = ntp->t_data->t_oid; - heap_endscan(sd); - heap_close(rel, AccessShareLock); - } - - /* Looking for pg_index_indexrelid_index? */ - if (DatumGetObjectId(cur_skey[0].sk_argument) == indexSelfOid) + /* + * Since the OIDs of indexes aren't hardwired, it's painful to + * figure out which is which. Just force all pg_index searches + * to be heap scans while building the relcaches. + */ + if (!criticalRelcachesBuilt) return false; } - else if (cache->id == AMOPSTRATEGY || - cache->id == AMPROCNUM) + else if (cache->id == AMOID || + cache->id == AMNAME) { - /* Looking for an OID or INT2 btree operator or function? */ - Oid lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument); - - if (lookup_oid == OID_BTREE_OPS_OID || - lookup_oid == INT2_BTREE_OPS_OID) - return false; + /* + * Always do heap scans in pg_am, because it's so small there's + * not much point in an indexscan anyway. We *must* do this when + * initially building critical relcache entries, but we might as + * well just always do it. + */ + return false; } else if (cache->id == OPEROID) { - /* Looking for an OID comparison function? */ - Oid lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument); + if (!criticalRelcachesBuilt) + { + /* Looking for an OID comparison function? */ + Oid lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument); - if (lookup_oid >= MIN_OIDCMP && lookup_oid <= MAX_OIDCMP) - return false; + if (lookup_oid >= MIN_OIDCMP && lookup_oid <= MAX_OIDCMP) + return false; + } } /* Normal case, allow index scan */ @@ -861,6 +918,10 @@ SearchCatCache(CatCache *cache, if (cache->cc_tupdesc == NULL) CatalogCacheInitializeCache(cache); +#ifdef CATCACHE_STATS + cache->cc_searches++; +#endif + /* * initialize the search key information */ @@ -919,6 +980,10 @@ SearchCatCache(CatCache *cache, cache->cc_relname, hash); #endif /* CACHEDEBUG */ +#ifdef CATCACHE_STATS + cache->cc_hits++; +#endif + return &ct->tuple; } @@ -1046,6 +1111,10 @@ SearchCatCache(CatCache *cache, DLAddHead(&CacheHdr->ch_lrulist, &ct->lrulist_elem); DLAddHead(&cache->cc_bucket[hash], &ct->cache_elem); +#ifdef CATCACHE_STATS + cache->cc_newloads++; +#endif + /* * If we've exceeded the desired size of the caches, try to throw away * the least recently used entry. diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 40d79942d5e0203e137d5bed7ff14e0207c9cb28..0e383b32c8417bfd9e1c123d7b8f33081cba92a8 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -48,6 +48,12 @@ * (XXX is it worth testing likewise for duplicate catcache flush entries? * Probably not.) * + * If a relcache flush is issued for a system relation that we preload + * from the relcache init file, we must also delete the init file so that + * it will be rebuilt during the next backend restart. The actual work of + * manipulating the init file is in relcache.c, but we keep track of the + * need for it here. + * * All the request lists are kept in TopTransactionContext memory, since * they need not live beyond the end of the current transaction. * @@ -56,7 +62,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.47 2001/11/16 23:30:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.48 2002/02/19 20:11:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -107,6 +113,8 @@ typedef struct InvalidationListHeader */ static InvalidationListHeader GlobalInvalidMsgs; +static bool RelcacheInitFileInval; /* init file must be invalidated? */ + /* * head of invalidation message list for the current command * eaten by AtCommit_LocalCache() in CommandCounterIncrement() @@ -339,6 +347,13 @@ RegisterRelcacheInvalidation(Oid dbId, Oid relId) dbId, relId); AddRelcacheInvalidationMessage(&LocalInvalidMsgs, dbId, relId); + + /* + * If the relation being invalidated is one of those cached in the + * relcache init file, mark that we need to zap that file at commit. + */ + if (RelationIdIsInInitFile(relId)) + RelcacheInitFileInval = true; } /* @@ -418,8 +433,8 @@ InvalidateSystemCaches(void) /* * PrepareForTupleInvalidation - * Invoke functions for the tuple which register invalidation - * of catalog/relation cache. + * Detect whether invalidation of this tuple implies invalidation + * of catalog/relation cache entries; if so, register inval events. */ static void PrepareForTupleInvalidation(Relation relation, HeapTuple tuple, @@ -525,8 +540,19 @@ AtEOXactInvalidationMessages(bool isCommit) { if (isCommit) { + /* + * Relcache init file invalidation requires processing both + * before and after we send the SI messages. However, we need + * not do anything unless we committed. + */ + if (RelcacheInitFileInval) + RelationCacheInitFileInvalidate(true); + ProcessInvalidationMessages(&GlobalInvalidMsgs, SendSharedInvalidMessage); + + if (RelcacheInitFileInval) + RelationCacheInitFileInvalidate(false); } else { @@ -534,6 +560,8 @@ AtEOXactInvalidationMessages(bool isCommit) LocalExecuteInvalidationMessage); } + RelcacheInitFileInval = false; + DiscardInvalidationMessages(&GlobalInvalidMsgs, false); DiscardInvalidationMessages(&LocalInvalidMsgs, false); DiscardInvalidationMessages(&RollbackMsgs, false); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 16a38df83d3cd60d894569e2be43d2521e0ed0f0..cd5162ac81c2e12579e2e76a9433075190a72fed 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.151 2002/01/16 17:34:42 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.152 2002/02/19 20:11:17 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,8 +41,12 @@ #include "catalog/catname.h" #include "catalog/index.h" #include "catalog/indexing.h" +#include "catalog/pg_amop.h" +#include "catalog/pg_amproc.h" #include "catalog/pg_attrdef.h" +#include "catalog/pg_attribute.h" #include "catalog/pg_index.h" +#include "catalog/pg_opclass.h" #include "catalog/pg_proc.h" #include "catalog/pg_relcheck.h" #include "catalog/pg_rewrite.h" @@ -53,12 +57,18 @@ #include "utils/builtins.h" #include "utils/catcache.h" #include "utils/fmgroids.h" -#include "utils/hsearch.h" -#include "utils/memutils.h" +#include "utils/inval.h" +#include "utils/lsyscache.h" #include "utils/relcache.h" +#include "utils/syscache.h" #include "utils/temprel.h" +/* + * name of relcache init file, used to speed up backend startup + */ +#define RELCACHE_INIT_FILENAME "pg_internal.init" + /* * hardcoded tuple descriptors. see lib/backend/catalog/pg_attribute.h */ @@ -88,14 +98,33 @@ static HTAB *RelationNodeCache; * relations created during this transaction. We need to keep track of * these. */ -static List *newlyCreatedRelns = NULL; +static List *newlyCreatedRelns = NIL; /* * This flag is false until we have prepared the critical relcache entries * that are needed to do indexscans on the tables read by relcache building. */ -static bool criticalRelcachesBuilt = false; +bool criticalRelcachesBuilt = false; + +/* + * This flag is set if we discover that we need to write a new relcache + * cache file at the end of startup. + */ +static bool needNewCacheFile = false; + +/* + * This counter counts relcache inval events received since backend startup + * (but only for rels that are actually in cache). Presently, we use it only + * to detect whether data about to be written by write_relcache_init_file() + * might already be obsolete. + */ +static long relcacheInvalsReceived = 0L; +/* + * This list remembers the OIDs of the relations cached in the relcache + * init file. + */ +static List *initFileRelationIds = NIL; /* * RelationBuildDescInfo exists so code can be shared @@ -220,6 +249,24 @@ do { \ elog(NOTICE, "trying to delete a reldesc that does not exist."); \ } while(0) + +/* + * Special cache for opclass-related information + */ +typedef struct opclasscacheent +{ + Oid opclassoid; /* lookup key: OID of opclass */ + bool valid; /* set TRUE after successful fill-in */ + StrategyNumber numStrats; /* max # of strategies (from pg_am) */ + StrategyNumber numSupport; /* max # of support procs (from pg_am) */ + Oid *operatorOids; /* strategy operators' OIDs */ + RegProcedure *operatorProcs; /* strategy operators' procs */ + RegProcedure *supportProcs; /* support procs */ +} OpClassCacheEnt; + +static HTAB *OpClassCache = NULL; + + /* non-export function prototypes */ static void RelationClearRelation(Relation relation, bool rebuildIt); @@ -229,28 +276,31 @@ static void RelationReloadClassinfo(Relation relation); #endif /* ENABLE_REINDEX_NAILED_RELATIONS */ static void RelationFlushRelation(Relation relation); static Relation RelationNameCacheGetRelation(const char *relationName); -static void init_irels(void); -static void write_irels(void); +static bool load_relcache_init_file(void); +static void write_relcache_init_file(void); static void formrdesc(char *relationName, int natts, FormData_pg_attribute *att); -static void fixrdesc(char *relationName); static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo); -static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo); -static HeapTuple scan_pg_rel_ind(RelationBuildDescInfo buildinfo); static Relation AllocateRelationDesc(Relation relation, Form_pg_class relp); static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo, Relation relation); -static void build_tupdesc_seq(RelationBuildDescInfo buildinfo, - Relation relation); -static void build_tupdesc_ind(RelationBuildDescInfo buildinfo, - Relation relation); static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo, Relation oldrelation); static void AttrDefaultFetch(Relation relation); static void RelCheckFetch(Relation relation); static List *insert_ordered_oid(List *list, Oid datum); +static void IndexSupportInitialize(Form_pg_index iform, + IndexStrategy indexStrategy, + Oid *indexOperator, + RegProcedure *indexSupport, + StrategyNumber maxStrategyNumber, + StrategyNumber maxSupportNumber, + AttrNumber maxAttributeNumber); +static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid, + StrategyNumber numStrats, + StrategyNumber numSupport); /* @@ -271,26 +321,11 @@ static List *insert_ordered_oid(List *list, Oid datum); */ static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo) -{ - /* - * If this is bootstrap time (initdb), then we can't use the system - * catalog indices, because they may not exist yet. Otherwise, we - * can, and do. - */ - - if (IsIgnoringSystemIndexes() || !criticalRelcachesBuilt) - return scan_pg_rel_seq(buildinfo); - else - return scan_pg_rel_ind(buildinfo); -} - -static HeapTuple -scan_pg_rel_seq(RelationBuildDescInfo buildinfo) { HeapTuple pg_class_tuple; - HeapTuple return_tuple; Relation pg_class_desc; - HeapScanDesc pg_class_scan; + const char *indexRelname; + SysScanDesc pg_class_scan; ScanKeyData key; /* @@ -303,6 +338,7 @@ scan_pg_rel_seq(RelationBuildDescInfo buildinfo) ObjectIdAttributeNumber, F_OIDEQ, ObjectIdGetDatum(buildinfo.i.info_id)); + indexRelname = ClassOidIndex; break; case INFO_RELNAME: @@ -310,83 +346,38 @@ scan_pg_rel_seq(RelationBuildDescInfo buildinfo) Anum_pg_class_relname, F_NAMEEQ, NameGetDatum(buildinfo.i.info_name)); + indexRelname = ClassNameIndex; break; default: elog(ERROR, "ScanPgRelation: bad buildinfo"); - return NULL; + return NULL; /* keep compiler quiet */ } /* - * open pg_class and fetch a tuple + * 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). */ pg_class_desc = heap_openr(RelationRelationName, AccessShareLock); - pg_class_scan = heap_beginscan(pg_class_desc, 0, SnapshotNow, 1, &key); - pg_class_tuple = heap_getnext(pg_class_scan, 0); - - /* - * get set to return tuple - */ - if (!HeapTupleIsValid(pg_class_tuple)) - return_tuple = pg_class_tuple; - else - { - /* - * a satanic bug used to live here: pg_class_tuple used to be - * returned here without having the corresponding buffer pinned. - * so when the buffer gets replaced, all hell breaks loose. this - * bug is discovered and killed by wei on 9/27/91. - */ - return_tuple = heap_copytuple(pg_class_tuple); - } - - /* all done */ - heap_endscan(pg_class_scan); - heap_close(pg_class_desc, AccessShareLock); - - return return_tuple; -} + pg_class_scan = systable_beginscan(pg_class_desc, indexRelname, + criticalRelcachesBuilt, + SnapshotNow, + 1, &key); -static HeapTuple -scan_pg_rel_ind(RelationBuildDescInfo buildinfo) -{ - Relation pg_class_desc; - HeapTuple return_tuple; - - pg_class_desc = heap_openr(RelationRelationName, AccessShareLock); + pg_class_tuple = systable_getnext(pg_class_scan); /* - * If the indexes of pg_class are deactivated we have to call - * scan_pg_rel_seq() instead. + * Must copy tuple before releasing buffer. */ - if (!pg_class_desc->rd_rel->relhasindex) - { - heap_close(pg_class_desc, AccessShareLock); - return scan_pg_rel_seq(buildinfo); - } - - switch (buildinfo.infotype) - { - case INFO_RELID: - return_tuple = ClassOidIndexScan(pg_class_desc, - ObjectIdGetDatum(buildinfo.i.info_id)); - break; - - case INFO_RELNAME: - return_tuple = ClassNameIndexScan(pg_class_desc, - PointerGetDatum(buildinfo.i.info_name)); - break; - - default: - elog(ERROR, "ScanPgRelation: bad buildinfo"); - return_tuple = NULL; /* keep compiler quiet */ - } + if (HeapTupleIsValid(pg_class_tuple)) + pg_class_tuple = heap_copytuple(pg_class_tuple); + /* all done */ + systable_endscan(pg_class_scan); heap_close(pg_class_desc, AccessShareLock); - /* The xxxIndexScan routines will have returned a palloc'd tuple. */ - - return return_tuple; + return pg_class_tuple; } /* @@ -452,75 +443,16 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp) * RelationBuildTupleDesc * * Form the relation's tuple descriptor from information in - * the pg_attribute, pg_attrdef & pg_relcheck system cataloges. + * the pg_attribute, pg_attrdef & pg_relcheck system catalogs. */ static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo, Relation relation) -{ - /* - * If this is bootstrap time (initdb), then we can't use the system - * catalog indices, because they may not exist yet. Otherwise, we - * can, and do. - */ - - if (IsIgnoringSystemIndexes() || !criticalRelcachesBuilt) - build_tupdesc_seq(buildinfo, relation); - else - build_tupdesc_ind(buildinfo, relation); -} - -static void -SetConstrOfRelation(Relation relation, - TupleConstr *constr, - int ndef, - AttrDefault *attrdef) -{ - if (constr->has_not_null || ndef > 0 || relation->rd_rel->relchecks) - { - relation->rd_att->constr = constr; - - if (ndef > 0) /* DEFAULTs */ - { - if (ndef < relation->rd_rel->relnatts) - constr->defval = (AttrDefault *) - repalloc(attrdef, ndef * sizeof(AttrDefault)); - else - constr->defval = attrdef; - constr->num_defval = ndef; - AttrDefaultFetch(relation); - } - else - constr->num_defval = 0; - - if (relation->rd_rel->relchecks > 0) /* CHECKs */ - { - constr->num_check = relation->rd_rel->relchecks; - constr->check = (ConstrCheck *) - MemoryContextAlloc(CacheMemoryContext, - constr->num_check * sizeof(ConstrCheck)); - MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck)); - RelCheckFetch(relation); - } - else - constr->num_check = 0; - } - else - { - pfree(constr); - relation->rd_att->constr = NULL; - } -} - -static void -build_tupdesc_seq(RelationBuildDescInfo buildinfo, - Relation relation) { HeapTuple pg_attribute_tuple; Relation pg_attribute_desc; - HeapScanDesc pg_attribute_scan; - Form_pg_attribute attp; - ScanKeyData key; + SysScanDesc pg_attribute_scan; + ScanKeyData skey[2]; int need; TupleConstr *constr; AttrDefault *attrdef = NULL; @@ -531,73 +463,89 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo, constr->has_not_null = false; /* - * form a scan key + * Form a scan key that selects only user attributes (attnum > 0). + * (Eliminating system attribute rows at the index level is lots + * faster than fetching them.) */ - ScanKeyEntryInitialize(&key, 0, + ScanKeyEntryInitialize(&skey[0], 0, Anum_pg_attribute_attrelid, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); + ScanKeyEntryInitialize(&skey[1], 0, + Anum_pg_attribute_attnum, + F_INT2GT, + Int16GetDatum(0)); /* - * open pg_attribute and begin a scan + * Open pg_attribute and begin a scan. Force heap scan if we haven't + * yet built the critical relcache entries (this includes initdb + * and startup without a pg_internal.init file). */ pg_attribute_desc = heap_openr(AttributeRelationName, AccessShareLock); - pg_attribute_scan = heap_beginscan(pg_attribute_desc, 0, SnapshotNow, 1, &key); + pg_attribute_scan = systable_beginscan(pg_attribute_desc, + AttributeRelidNumIndex, + criticalRelcachesBuilt, + SnapshotNow, + 2, skey); /* * add attribute data to relation->rd_att */ need = relation->rd_rel->relnatts; - pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0); - while (HeapTupleIsValid(pg_attribute_tuple) && need > 0) + while (HeapTupleIsValid(pg_attribute_tuple = systable_getnext(pg_attribute_scan))) { + Form_pg_attribute attp; + attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple); - if (attp->attnum > 0) - { - relation->rd_att->attrs[attp->attnum - 1] = - (Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext, + if (attp->attnum <= 0 || + attp->attnum > relation->rd_rel->relnatts) + elog(ERROR, "Bogus attribute number %d for %s", + attp->attnum, RelationGetRelationName(relation)); + + relation->rd_att->attrs[attp->attnum - 1] = + (Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext, ATTRIBUTE_TUPLE_SIZE); - memcpy((char *) (relation->rd_att->attrs[attp->attnum - 1]), - (char *) attp, - ATTRIBUTE_TUPLE_SIZE); - need--; + memcpy((char *) (relation->rd_att->attrs[attp->attnum - 1]), + (char *) attp, + ATTRIBUTE_TUPLE_SIZE); - /* Update if this attribute have a constraint */ - if (attp->attnotnull) - constr->has_not_null = true; + /* Update constraint/default info */ + if (attp->attnotnull) + constr->has_not_null = true; - if (attp->atthasdef) + if (attp->atthasdef) + { + if (attrdef == NULL) { - if (attrdef == NULL) - { - attrdef = (AttrDefault *) - MemoryContextAlloc(CacheMemoryContext, - relation->rd_rel->relnatts * - sizeof(AttrDefault)); - MemSet(attrdef, 0, + attrdef = (AttrDefault *) + MemoryContextAlloc(CacheMemoryContext, + relation->rd_rel->relnatts * + sizeof(AttrDefault)); + MemSet(attrdef, 0, relation->rd_rel->relnatts * sizeof(AttrDefault)); - } - attrdef[ndef].adnum = attp->attnum; - attrdef[ndef].adbin = NULL; - ndef++; } + attrdef[ndef].adnum = attp->attnum; + attrdef[ndef].adbin = NULL; + ndef++; } - pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0); + need--; + if (need == 0) + break; } - if (need > 0) - elog(ERROR, "catalog is missing %d attribute%s for relid %u", - need, (need == 1 ? "" : "s"), RelationGetRelid(relation)); - /* * end the scan and close the attribute relation */ - heap_endscan(pg_attribute_scan); + systable_endscan(pg_attribute_scan); heap_close(pg_attribute_desc, AccessShareLock); + if (need != 0) + elog(ERROR, "catalog is missing %d attribute(s) for relid %u", + need, RelationGetRelid(relation)); + /* * The attcacheoff values we read from pg_attribute should all be -1 * ("unknown"). Verify this if assert checking is on. They will be @@ -620,110 +568,43 @@ build_tupdesc_seq(RelationBuildDescInfo buildinfo, */ relation->rd_att->attrs[0]->attcacheoff = 0; - SetConstrOfRelation(relation, constr, ndef, attrdef); -} - -static void -build_tupdesc_ind(RelationBuildDescInfo buildinfo, - Relation relation) -{ - Relation attrel; - HeapTuple atttup; - Form_pg_attribute attp; - TupleConstr *constr; - AttrDefault *attrdef = NULL; - int ndef = 0; - int i; - - constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext, - sizeof(TupleConstr)); - constr->has_not_null = false; - - attrel = heap_openr(AttributeRelationName, AccessShareLock); - - for (i = 1; i <= relation->rd_rel->relnatts; i++) + /* + * Set up constraint/default info + */ + if (constr->has_not_null || ndef > 0 || relation->rd_rel->relchecks) { -#ifdef _DROP_COLUMN_HACK__ - bool columnDropped = false; -#endif /* _DROP_COLUMN_HACK__ */ - - atttup = AttributeRelidNumIndexScan(attrel, - ObjectIdGetDatum(RelationGetRelid(relation)), - Int32GetDatum(i)); + relation->rd_att->constr = constr; - if (!HeapTupleIsValid(atttup)) + if (ndef > 0) /* DEFAULTs */ { -#ifdef _DROP_COLUMN_HACK__ - atttup = AttributeRelidNumIndexScan(attrel, - ObjectIdGetDatum(RelationGetRelid(relation)), - Int32GetDatum(DROPPED_COLUMN_INDEX(i))); - if (!HeapTupleIsValid(atttup)) -#endif /* _DROP_COLUMN_HACK__ */ - elog(ERROR, "cannot find attribute %d of relation %s", i, - RelationGetRelationName(relation)); -#ifdef _DROP_COLUMN_HACK__ - columnDropped = true; -#endif /* _DROP_COLUMN_HACK__ */ + if (ndef < relation->rd_rel->relnatts) + constr->defval = (AttrDefault *) + repalloc(attrdef, ndef * sizeof(AttrDefault)); + else + constr->defval = attrdef; + constr->num_defval = ndef; + AttrDefaultFetch(relation); } + else + constr->num_defval = 0; - relation->rd_att->attrs[i - 1] = attp = - (Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext, - ATTRIBUTE_TUPLE_SIZE); - - memcpy((char *) attp, - (char *) (Form_pg_attribute) GETSTRUCT(atttup), - ATTRIBUTE_TUPLE_SIZE); - - /* don't forget to free the tuple returned from xxxIndexScan */ - heap_freetuple(atttup); - -#ifdef _DROP_COLUMN_HACK__ - if (columnDropped) - continue; -#endif /* _DROP_COLUMN_HACK__ */ - - /* Update if this attribute have a constraint */ - if (attp->attnotnull) - constr->has_not_null = true; - - if (attp->atthasdef) + if (relation->rd_rel->relchecks > 0) /* CHECKs */ { - if (attrdef == NULL) - { - attrdef = (AttrDefault *) - MemoryContextAlloc(CacheMemoryContext, - relation->rd_rel->relnatts * - sizeof(AttrDefault)); - MemSet(attrdef, 0, - relation->rd_rel->relnatts * sizeof(AttrDefault)); - } - attrdef[ndef].adnum = i; - attrdef[ndef].adbin = NULL; - ndef++; + constr->num_check = relation->rd_rel->relchecks; + constr->check = (ConstrCheck *) + MemoryContextAlloc(CacheMemoryContext, + constr->num_check * sizeof(ConstrCheck)); + MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck)); + RelCheckFetch(relation); } + else + constr->num_check = 0; + } + else + { + pfree(constr); + relation->rd_att->constr = NULL; } - - heap_close(attrel, AccessShareLock); - - /* - * The attcacheoff values we read from pg_attribute should all be -1 - * ("unknown"). Verify this if assert checking is on. They will be - * computed when and if needed during tuple access. - */ -#ifdef USE_ASSERT_CHECKING - for (i = 0; i < relation->rd_rel->relnatts; i++) - Assert(relation->rd_att->attrs[i]->attcacheoff == -1); -#endif - - /* - * However, we can easily set the attcacheoff value for the first - * attribute: it must be zero. This eliminates the need for special - * cases for attnum=1 that used to exist in fastgetattr() and - * index_getattr(). - */ - relation->rd_att->attrs[0]->attcacheoff = 0; - - SetConstrOfRelation(relation, constr, ndef, attrdef); } /* @@ -749,7 +630,7 @@ RelationBuildRuleLock(Relation relation) HeapTuple pg_rewrite_tuple; Relation pg_rewrite_desc; TupleDesc pg_rewrite_tupdesc; - HeapScanDesc pg_rewrite_scan; + SysScanDesc pg_rewrite_scan; ScanKeyData key; RuleLock *rulelock; int numlocks; @@ -786,12 +667,16 @@ RelationBuildRuleLock(Relation relation) /* * open pg_rewrite and begin a scan + * + * XXX: there is no suitable index for this scan. FIXME. */ pg_rewrite_desc = heap_openr(RewriteRelationName, AccessShareLock); - pg_rewrite_scan = heap_beginscan(pg_rewrite_desc, 0, SnapshotNow, 1, &key); pg_rewrite_tupdesc = RelationGetDescr(pg_rewrite_desc); + pg_rewrite_scan = systable_beginscan(pg_rewrite_desc, NULL, false, + SnapshotNow, + 1, &key); - while (HeapTupleIsValid(pg_rewrite_tuple = heap_getnext(pg_rewrite_scan, 0))) + while (HeapTupleIsValid(pg_rewrite_tuple = systable_getnext(pg_rewrite_scan))) { bool isnull; Datum ruleaction; @@ -854,7 +739,7 @@ RelationBuildRuleLock(Relation relation) /* * end the scan and close the attribute relation */ - heap_endscan(pg_rewrite_scan); + systable_endscan(pg_rewrite_scan); heap_close(pg_rewrite_desc, AccessShareLock); /* @@ -930,25 +815,6 @@ equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2) * recycling the given old relation object. The latter case * supports rebuilding a relcache entry without invalidating * pointers to it. - * - * To build a relation descriptor, we have to allocate space, - * open the underlying unix file and initialize the following - * fields: - * - * File rd_fd; open file descriptor - * BlockNumber rd_nblocks; number of blocks in rel - * it will be set in ambeginscan() - * int rd_refcnt; reference count - * Form_pg_am rd_am; AM tuple - * Form_pg_class rd_rel; RELATION tuple - * Oid rd_id; relation's object id - * LockInfoData rd_lockInfo; lock manager's info - * TupleDesc rd_att; tuple descriptor - * - * Note: rd_ismem (rel is in-memory only) is currently unused - * by any part of the system. someday this will indicate that - * the relation lives only in the main-memory buffer pool - * -cim 2/4/91 * -------------------------------- */ static Relation @@ -957,7 +823,6 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, { Relation relation; Oid relid; - Oid relam; HeapTuple pg_class_tuple; Form_pg_class relp; MemoryContext oldcxt; @@ -1005,14 +870,6 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, */ relation->rd_isnailed = false; - /* - * initialize the access method information (relation->rd_am) - */ - relam = relation->rd_rel->relam; - if (OidIsValid(relam)) - relation->rd_am = AccessMethodObjectIdGetForm(relam, - CacheMemoryContext); - /* * initialize the tuple descriptor (relation->rd_att). */ @@ -1035,9 +892,9 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, relation->trigdesc = NULL; /* - * initialize index strategy and support information for this relation + * if it's an index, initialize index-related information */ - if (OidIsValid(relam)) + if (OidIsValid(relation->rd_rel->relam)) RelationInitIndexAccessInfo(relation); /* @@ -1051,24 +908,8 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, relation->rd_node.tblNode = MyDatabaseId; relation->rd_node.relNode = relation->rd_rel->relfilenode; - /* - * Open the relation and assign the file descriptor returned by the - * storage manager code to rd_fd. - * - * We do not raise a hard error if we fail to open the relation at this - * point. If we did, it would be impossible to drop a relation whose - * underlying physical file had disappeared. - */ - if (relation->rd_rel->relkind != RELKIND_VIEW) - { - relation->rd_fd = smgropen(DEFAULT_SMGR, relation, true); - Assert(relation->rd_fd >= -1); - if (relation->rd_fd == -1) - elog(NOTICE, "RelationBuildDesc: can't open %s: %m", - RelationGetRelationName(relation)); - } - else - relation->rd_fd = -1; + /* make sure relation is marked as having no open file yet */ + relation->rd_fd = -1; /* * insert newly created relation into proper relcaches, restore memory @@ -1087,18 +928,55 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, void RelationInitIndexAccessInfo(Relation relation) { + HeapTuple tuple; + Size iformsize; + Form_pg_index iform; + Form_pg_am aform; MemoryContext indexcxt; IndexStrategy strategy; + Oid *operator; RegProcedure *support; FmgrInfo *supportinfo; int natts; uint16 amstrategies; uint16 amsupport; - Size stratSize; + + /* + * Make a copy of the pg_index entry for the index. Note that this + * is a variable-length tuple. + */ + tuple = SearchSysCache(INDEXRELID, + ObjectIdGetDatum(RelationGetRelid(relation)), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "RelationInitIndexAccessInfo: no pg_index entry for index %u", + RelationGetRelid(relation)); + iformsize = tuple->t_len - tuple->t_data->t_hoff; + iform = (Form_pg_index) MemoryContextAlloc(CacheMemoryContext, iformsize); + memcpy(iform, GETSTRUCT(tuple), iformsize); + ReleaseSysCache(tuple); + relation->rd_index = iform; + + /* this field is now kinda redundant... */ + relation->rd_uniqueindex = iform->indisunique; + + /* + * Make a copy of the pg_am entry for the index's access method + */ + tuple = SearchSysCache(AMOID, + ObjectIdGetDatum(relation->rd_rel->relam), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "RelationInitIndexAccessInfo: cache lookup failed for AM %u", + relation->rd_rel->relam); + aform = (Form_pg_am) MemoryContextAlloc(CacheMemoryContext, sizeof *aform); + memcpy(aform, GETSTRUCT(tuple), sizeof *aform); + ReleaseSysCache(tuple); + relation->rd_am = aform; natts = relation->rd_rel->relnatts; - amstrategies = relation->rd_am->amstrategies; - amsupport = relation->rd_am->amsupport; + amstrategies = aform->amstrategies; + amsupport = aform->amsupport; /* * Make the private context to hold index access info. The reason we @@ -1118,8 +996,23 @@ RelationInitIndexAccessInfo(Relation relation) /* * Allocate arrays to hold data */ - stratSize = AttributeNumberGetIndexStrategySize(natts, amstrategies); - strategy = (IndexStrategy) MemoryContextAlloc(indexcxt, stratSize); + if (amstrategies > 0) + { + int noperators = natts * amstrategies; + Size stratSize; + + stratSize = AttributeNumberGetIndexStrategySize(natts, amstrategies); + strategy = (IndexStrategy) MemoryContextAlloc(indexcxt, stratSize); + MemSet(strategy, 0, stratSize); + operator = (Oid *) + MemoryContextAlloc(indexcxt, noperators * sizeof(Oid)); + MemSet(operator, 0, noperators * sizeof(Oid)); + } + else + { + strategy = NULL; + operator = NULL; + } if (amsupport > 0) { @@ -1127,6 +1020,7 @@ RelationInitIndexAccessInfo(Relation relation) support = (RegProcedure *) MemoryContextAlloc(indexcxt, nsupport * sizeof(RegProcedure)); + MemSet(support, 0, nsupport * sizeof(RegProcedure)); supportinfo = (FmgrInfo *) MemoryContextAlloc(indexcxt, nsupport * sizeof(FmgrInfo)); MemSet(supportinfo, 0, nsupport * sizeof(FmgrInfo)); @@ -1137,31 +1031,301 @@ RelationInitIndexAccessInfo(Relation relation) supportinfo = NULL; } + relation->rd_istrat = strategy; + relation->rd_operator = operator; + relation->rd_support = support; + relation->rd_supportinfo = supportinfo; + /* * Fill the strategy map and the support RegProcedure arrays. * (supportinfo is left as zeroes, and is filled on-the-fly when used) */ - IndexSupportInitialize(strategy, support, - &relation->rd_uniqueindex, - RelationGetRelid(relation), - relation->rd_rel->relam, + IndexSupportInitialize(iform, + strategy, operator, support, amstrategies, amsupport, natts); - - relation->rd_istrat = strategy; - relation->rd_support = support; - relation->rd_supportinfo = supportinfo; } /* - * formrdesc + * IndexSupportInitialize + * Initializes an index strategy and associated support procedures, + * given the index's pg_index tuple. * - * This is a special cut-down version of RelationBuildDesc() - * used by RelationCacheInitialize() in initializing the relcache. - * The relation descriptor is built just from the supplied parameters, + * Data is returned into *indexStrategy, *indexOperator, and *indexSupport, + * all of which are objects allocated by the caller. + * + * The caller also passes maxStrategyNumber, maxSupportNumber, and + * maxAttributeNumber, since these indicate the size of the arrays + * it has allocated --- but in practice these numbers must always match + * those obtainable from the system catalog entries for the index and + * access method. + */ +static void +IndexSupportInitialize(Form_pg_index iform, + IndexStrategy indexStrategy, + Oid *indexOperator, + RegProcedure *indexSupport, + StrategyNumber maxStrategyNumber, + StrategyNumber maxSupportNumber, + AttrNumber maxAttributeNumber) +{ + int attIndex; + + maxStrategyNumber = AMStrategies(maxStrategyNumber); + + /* + * XXX note that the following assumes the INDEX tuple is well formed + * and that the *key and *class are 0 terminated. + */ + for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++) + { + OpClassCacheEnt *opcentry; + + if (iform->indkey[attIndex] == InvalidAttrNumber || + !OidIsValid(iform->indclass[attIndex])) + elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple"); + + /* look up the info for this opclass, using a cache */ + opcentry = LookupOpclassInfo(iform->indclass[attIndex], + maxStrategyNumber, + maxSupportNumber); + + /* load the strategy information for the index operators */ + if (maxStrategyNumber > 0) + { + StrategyMap map; + Oid *opers; + StrategyNumber strategy; + + map = IndexStrategyGetStrategyMap(indexStrategy, + maxStrategyNumber, + attIndex + 1); + opers = &indexOperator[attIndex * maxStrategyNumber]; + + for (strategy = 0; strategy < maxStrategyNumber; strategy++) + { + ScanKey mapentry; + + mapentry = StrategyMapGetScanKeyEntry(map, strategy + 1); + if (RegProcedureIsValid(opcentry->operatorProcs[strategy])) + { + MemSet(mapentry, 0, sizeof(*mapentry)); + mapentry->sk_flags = 0; + mapentry->sk_procedure = opcentry->operatorProcs[strategy]; + /* + * Mark mapentry->sk_func invalid, until and unless + * someone sets it up. + */ + mapentry->sk_func.fn_oid = InvalidOid; + } + else + ScanKeyEntrySetIllegal(mapentry); + opers[strategy] = opcentry->operatorOids[strategy]; + } + } + + /* if support routines exist for this access method, load them */ + if (maxSupportNumber > 0) + { + RegProcedure *procs; + StrategyNumber support; + + procs = &indexSupport[attIndex * maxSupportNumber]; + + for (support = 0; support < maxSupportNumber; ++support) + procs[support] = opcentry->supportProcs[support]; + } + } +} + +/* + * LookupOpclassInfo + * + * This routine maintains a per-opclass cache of the information needed + * by IndexSupportInitialize(). This is more efficient than relying on + * the catalog cache, because we can load all the info about a particular + * opclass in a single indexscan of pg_amproc or pg_amop. + * + * The information from pg_am about expected range of strategy and support + * numbers is passed in, rather than being looked up, mainly because the + * caller will have it already. + * + * XXX There isn't any provision for flushing the cache. However, there + * isn't any provision for flushing relcache entries when opclass info + * changes, either :-( + */ +static OpClassCacheEnt * +LookupOpclassInfo(Oid operatorClassOid, + StrategyNumber numStrats, + StrategyNumber numSupport) +{ + OpClassCacheEnt *opcentry; + bool found; + Relation pg_amop_desc; + Relation pg_amproc_desc; + SysScanDesc pg_amop_scan; + SysScanDesc pg_amproc_scan; + ScanKeyData key; + HeapTuple htup; + bool indexOK; + + if (OpClassCache == NULL) + { + /* First time through: initialize the opclass cache */ + HASHCTL ctl; + + if (!CacheMemoryContext) + CreateCacheMemoryContext(); + + MemSet(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(Oid); + ctl.entrysize = sizeof(OpClassCacheEnt); + ctl.hash = tag_hash; + OpClassCache = hash_create("Operator class cache", 64, + &ctl, HASH_ELEM | HASH_FUNCTION); + } + + opcentry = (OpClassCacheEnt *) hash_search(OpClassCache, + (void *) &operatorClassOid, + HASH_ENTER, &found); + if (opcentry == NULL) + elog(ERROR, "out of memory for operator class cache"); + + if (found && opcentry->valid) + { + /* Already made an entry for it */ + Assert(numStrats == opcentry->numStrats); + Assert(numSupport == opcentry->numSupport); + return opcentry; + } + + /* Need to fill in new entry */ + opcentry->valid = false; /* until known OK */ + opcentry->numStrats = numStrats; + opcentry->numSupport = numSupport; + + if (numStrats > 0) + { + opcentry->operatorOids = (Oid *) + MemoryContextAlloc(CacheMemoryContext, + numStrats * sizeof(Oid)); + MemSet(opcentry->operatorOids, 0, numStrats * sizeof(Oid)); + opcentry->operatorProcs = (RegProcedure *) + MemoryContextAlloc(CacheMemoryContext, + numStrats * sizeof(RegProcedure)); + MemSet(opcentry->operatorProcs, 0, numStrats * sizeof(RegProcedure)); + } + else + { + opcentry->operatorOids = NULL; + opcentry->operatorProcs = NULL; + } + + if (numSupport > 0) + { + opcentry->supportProcs = (RegProcedure *) + MemoryContextAlloc(CacheMemoryContext, + numSupport * sizeof(RegProcedure)); + MemSet(opcentry->supportProcs, 0, numSupport * sizeof(RegProcedure)); + } + else + opcentry->supportProcs = NULL; + + /* + * To avoid infinite recursion during startup, force a heap scan if + * we're looking up info for the opclasses used by the indexes we + * would like to reference here. + */ + indexOK = criticalRelcachesBuilt || + (operatorClassOid != OID_BTREE_OPS_OID && + operatorClassOid != INT2_BTREE_OPS_OID); + + /* + * Scan pg_amop to obtain operators for the opclass + */ + if (numStrats > 0) + { + ScanKeyEntryInitialize(&key, 0, + Anum_pg_amop_amopclaid, + F_OIDEQ, + ObjectIdGetDatum(operatorClassOid)); + pg_amop_desc = heap_openr(AccessMethodOperatorRelationName, + AccessShareLock); + pg_amop_scan = systable_beginscan(pg_amop_desc, + AccessMethodStrategyIndex, + indexOK, + SnapshotNow, + 1, &key); + + while (HeapTupleIsValid(htup = systable_getnext(pg_amop_scan))) + { + Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(htup); + + if (amopform->amopstrategy <= 0 || + (StrategyNumber) amopform->amopstrategy > numStrats) + elog(ERROR, "Bogus amopstrategy number %d for opclass %u", + amopform->amopstrategy, operatorClassOid); + opcentry->operatorOids[amopform->amopstrategy - 1] = + amopform->amopopr; + opcentry->operatorProcs[amopform->amopstrategy - 1] = + get_opcode(amopform->amopopr); + } + + systable_endscan(pg_amop_scan); + heap_close(pg_amop_desc, AccessShareLock); + } + + /* + * Scan pg_amproc to obtain support procs for the opclass + */ + if (numSupport > 0) + { + ScanKeyEntryInitialize(&key, 0, + Anum_pg_amproc_amopclaid, + F_OIDEQ, + ObjectIdGetDatum(operatorClassOid)); + pg_amproc_desc = heap_openr(AccessMethodProcedureRelationName, + AccessShareLock); + pg_amproc_scan = systable_beginscan(pg_amproc_desc, + AccessMethodProcedureIndex, + indexOK, + SnapshotNow, + 1, &key); + + while (HeapTupleIsValid(htup = systable_getnext(pg_amproc_scan))) + { + Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup); + + if (amprocform->amprocnum <= 0 || + (StrategyNumber) amprocform->amprocnum > numSupport) + elog(ERROR, "Bogus amproc number %d for opclass %u", + amprocform->amprocnum, operatorClassOid); + + opcentry->supportProcs[amprocform->amprocnum - 1] = + amprocform->amproc; + } + + systable_endscan(pg_amproc_scan); + heap_close(pg_amproc_desc, AccessShareLock); + } + + opcentry->valid = true; + return opcentry; +} + + +/* + * formrdesc + * + * This is a special cut-down version of RelationBuildDesc() + * used by RelationCacheInitialize() in initializing the relcache. + * The relation descriptor is built just from the supplied parameters, * without actually looking at any system table entries. We cheat * quite a lot since we only need to work for a few basic system * catalogs... * + * Note that these catalogs can't have constraints, default values, + * rules, or triggers, since we don't cope with any of that. + * * NOTE: we assume we are already switched into CacheMemoryContext. */ static void @@ -1223,6 +1387,10 @@ formrdesc(char *relationName, /* * initialize attribute tuple form + * + * Unlike the case with the relation tuple, this data had better be + * right because it will never be replaced. The input values must be + * correctly defined by macros in src/include/catalog/ headers. */ relation->rd_att = CreateTemplateTupleDesc(natts); @@ -1235,8 +1403,13 @@ formrdesc(char *relationName, memcpy((char *) relation->rd_att->attrs[i], (char *) &att[i], ATTRIBUTE_TUPLE_SIZE); + /* make sure attcacheoff is valid */ + relation->rd_att->attrs[i]->attcacheoff = -1; } + /* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */ + relation->rd_att->attrs[0]->attcacheoff = 0; + /* * initialize relation id from info in att array (my, this is ugly) */ @@ -1280,52 +1453,6 @@ formrdesc(char *relationName, } -/* - * fixrdesc - * - * Update the phony data inserted by formrdesc() with real info - * from pg_class. - */ -static void -fixrdesc(char *relationName) -{ - RelationBuildDescInfo buildinfo; - HeapTuple pg_class_tuple; - Form_pg_class relp; - Relation relation; - - /* - * find the tuple in pg_class corresponding to the given relation name - */ - buildinfo.infotype = INFO_RELNAME; - buildinfo.i.info_name = relationName; - - pg_class_tuple = ScanPgRelation(buildinfo); - - if (!HeapTupleIsValid(pg_class_tuple)) - elog(FATAL, "fixrdesc: no pg_class entry for %s", - relationName); - relp = (Form_pg_class) GETSTRUCT(pg_class_tuple); - - /* - * find the pre-made relcache entry (better be there!) - */ - relation = RelationNameCacheGetRelation(relationName); - if (!RelationIsValid(relation)) - elog(FATAL, "fixrdesc: no existing relcache entry for %s", - relationName); - - /* - * and copy pg_class_tuple to relation->rd_rel. (See notes in - * AllocateRelationDesc()) - */ - Assert(relation->rd_rel != NULL); - memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE); - - heap_freetuple(pg_class_tuple); -} - - /* ---------------------------------------------------------------- * Relation Descriptor Lookup Interface * ---------------------------------------------------------------- @@ -1351,13 +1478,7 @@ RelationIdCacheGetRelation(Oid relationId) RelationIdCacheLookup(relationId, rd); if (RelationIsValid(rd)) - { - /* re-open files if necessary */ - if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW) - rd->rd_fd = smgropen(DEFAULT_SMGR, rd, false); - RelationIncrementReferenceCount(rd); - } return rd; } @@ -1381,13 +1502,7 @@ RelationNameCacheGetRelation(const char *relationName) RelationNameCacheLookup(NameStr(name), rd); if (RelationIsValid(rd)) - { - /* re-open files if necessary */ - if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW) - rd->rd_fd = smgropen(DEFAULT_SMGR, rd, false); - RelationIncrementReferenceCount(rd); - } return rd; } @@ -1400,13 +1515,7 @@ RelationNodeCacheGetRelation(RelFileNode rnode) RelationNodeCacheLookup(rnode, rd); if (RelationIsValid(rd)) - { - /* re-open files if necessary */ - if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW) - rd->rd_fd = smgropen(DEFAULT_SMGR, rd, false); - RelationIncrementReferenceCount(rd); - } return rd; } @@ -1619,6 +1728,8 @@ RelationClearRelation(Relation relation, bool rebuildIt) * manager might have pointers into the rewrite rules. So to begin * with, we can only get rid of these fields: */ + if (relation->rd_index) + pfree(relation->rd_index); if (relation->rd_am) pfree(relation->rd_am); if (relation->rd_rel) @@ -1805,32 +1916,11 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId) RelationIdCacheLookup(relationId, relation); if (PointerIsValid(relation)) - RelationFlushRelation(relation); -} - -#if NOT_USED -/* only used by RelationIdInvalidateRelationCacheByAccessMethodId, - * which is dead code. - */ -static void -RelationFlushIndexes(Relation *r, - Oid accessMethodId) -{ - Relation relation = *r; - - if (!RelationIsValid(relation)) { - elog(NOTICE, "inval call to RFI"); - return; - } - - if (relation->rd_rel->relkind == RELKIND_INDEX && /* XXX style */ - (!OidIsValid(accessMethodId) || - relation->rd_rel->relam == accessMethodId)) + relcacheInvalsReceived++; RelationFlushRelation(relation); + } } -#endif - /* * RelationCacheInvalidate @@ -1873,6 +1963,8 @@ RelationCacheInvalidate(void) if (relation->rd_myxactonly) continue; + relcacheInvalsReceived++; + if (RelationHasReferenceCountZero(relation)) { /* Delete this entry immediately */ @@ -2071,7 +2163,12 @@ RelationPurgeLocalRelation(bool xactCommitted) /* * RelationCacheInitialize * - * This initializes the relation descriptor cache. + * This initializes the relation descriptor cache. At the time + * that this is invoked, we can't do database access yet (mainly + * because the transaction subsystem is not up), so we can't get + * "real" info. However it's okay to read the pg_internal.init + * cache file, if one is available. Otherwise we make phony + * entries for the minimum set of nailed-in-cache relations. */ #define INITRELCACHESIZE 400 @@ -2091,7 +2188,7 @@ RelationCacheInitialize(void) oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* - * create global caches + * create hashtables that index the relcache */ MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(NameData); @@ -2112,22 +2209,24 @@ RelationCacheInitialize(void) &ctl, HASH_ELEM | HASH_FUNCTION); /* - * initialize the cache with pre-made relation descriptors for some of - * the more important system relations. These relations should always - * be in the cache. - * - * NB: see also the list in RelationCacheInitializePhase2(). - */ - formrdesc(RelationRelationName, Natts_pg_class, Desc_pg_class); - formrdesc(AttributeRelationName, Natts_pg_attribute, Desc_pg_attribute); - formrdesc(ProcedureRelationName, Natts_pg_proc, Desc_pg_proc); - formrdesc(TypeRelationName, Natts_pg_type, Desc_pg_type); - - /* - * init_irels() used to be called here. It is changed to be called in - * RelationCacheInitializePhase2() now so that transactional control - * could guarantee the consistency. + * Try to load the relcache cache file. If successful, we're done + * for now. Otherwise, initialize the cache with pre-made descriptors + * for the critical "nailed-in" system catalogs. */ + if (IsBootstrapProcessingMode() || + ! load_relcache_init_file()) + { + formrdesc(RelationRelationName, + Natts_pg_class, Desc_pg_class); + formrdesc(AttributeRelationName, + Natts_pg_attribute, Desc_pg_attribute); + formrdesc(ProcedureRelationName, + Natts_pg_proc, Desc_pg_proc); + formrdesc(TypeRelationName, + Natts_pg_type, Desc_pg_type); + +#define NUM_CRITICAL_RELS 4 /* fix if you change list above */ + } MemoryContextSwitchTo(oldcxt); } @@ -2135,40 +2234,145 @@ RelationCacheInitialize(void) /* * RelationCacheInitializePhase2 * - * This completes initialization of the relcache after catcache - * is functional and we are able to actually load data from pg_class. + * This is called as soon as the catcache and transaction system + * are functional. At this point we can actually read data from + * the system catalogs. Update the relcache entries made during + * RelationCacheInitialize, and make sure we have entries for the + * critical system indexes. */ void RelationCacheInitializePhase2(void) { + HASH_SEQ_STATUS status; + RelNameCacheEnt *namehentry; + + if (IsBootstrapProcessingMode()) + return; + /* - * Get the real pg_class tuple for each nailed-in-cache relcache entry - * that was made by RelationCacheInitialize(), and replace the phony - * rd_rel entry made by formrdesc(). This is necessary so that we - * have, for example, the correct toast-table info for tables that - * have such. + * If we didn't get the critical system indexes loaded into relcache, + * do so now. These are critical because the catcache depends on them + * for catcache fetches that are done during relcache load. Thus, we + * have an infinite-recursion problem. We can break the recursion + * by doing heapscans instead of indexscans at certain key spots. + * To avoid hobbling performance, we only want to do that until we + * have the critical indexes loaded into relcache. Thus, the flag + * criticalRelcachesBuilt is used to decide whether to do heapscan + * or indexscan at the key spots, and we set it true after we've loaded + * the critical indexes. + * + * The critical indexes are marked as "nailed in cache", partly to make + * it easy for load_relcache_init_file to count them, but mainly + * because we cannot flush and rebuild them once we've set + * criticalRelcachesBuilt to true. (NOTE: perhaps it would be possible + * to reload them by temporarily setting criticalRelcachesBuilt to + * false again. For now, though, we just nail 'em in.) + */ + if (! criticalRelcachesBuilt) + { + RelationBuildDescInfo buildinfo; + Relation ird; + +#define LOAD_CRIT_INDEX(indname) \ + do { \ + buildinfo.infotype = INFO_RELNAME; \ + buildinfo.i.info_name = (indname); \ + ird = RelationBuildDesc(buildinfo, NULL); \ + ird->rd_isnailed = true; \ + RelationSetReferenceCount(ird, 1); \ + } while (0) + + LOAD_CRIT_INDEX(ClassNameIndex); + LOAD_CRIT_INDEX(ClassOidIndex); + LOAD_CRIT_INDEX(AttributeRelidNumIndex); + LOAD_CRIT_INDEX(IndexRelidIndex); + LOAD_CRIT_INDEX(AccessMethodStrategyIndex); + LOAD_CRIT_INDEX(AccessMethodProcedureIndex); + LOAD_CRIT_INDEX(OperatorOidIndex); + +#define NUM_CRITICAL_INDEXES 7 /* fix if you change list above */ + + criticalRelcachesBuilt = true; + } + + /* + * Now, scan all the relcache entries and update anything that might + * be wrong in the results from formrdesc or the relcache cache file. + * If we faked up relcache entries using formrdesc, then read + * the real pg_class rows and replace the fake entries with them. + * Also, if any of the relcache entries have rules or triggers, + * load that info the hard way since it isn't recorded in the cache file. */ - if (!IsBootstrapProcessingMode()) + hash_seq_init(&status, RelationNameCache); + + while ((namehentry = (RelNameCacheEnt *) hash_seq_search(&status)) != NULL) { + Relation relation = namehentry->reldesc; + /* - * Initialize critical system index relation descriptors, first. - * They are to make building relation descriptors fast. - * init_irels() used to be called in RelationCacheInitialize(). It - * is changed to be called here to be transaction safe. + * If it's a faked-up entry, read the real pg_class tuple. */ - MemoryContext oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + if (needNewCacheFile && relation->rd_isnailed) + { + HeapTuple htup; + Form_pg_class relp; - init_irels(); - MemoryContextSwitchTo(oldcxt); + htup = SearchSysCache(RELOID, + ObjectIdGetDatum(RelationGetRelid(relation)), + 0, 0, 0); + if (!HeapTupleIsValid(htup)) + elog(FATAL, "RelationCacheInitializePhase2: no pg_class entry for %s", + RelationGetRelationName(relation)); + relp = (Form_pg_class) GETSTRUCT(htup); + /* + * Copy tuple to relation->rd_rel. (See notes in + * AllocateRelationDesc()) + */ + Assert(relation->rd_rel != NULL); + memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE); - /* fix nailed-in-cache relations */ - fixrdesc(RelationRelationName); - fixrdesc(AttributeRelationName); - fixrdesc(ProcedureRelationName); - fixrdesc(TypeRelationName); + ReleaseSysCache(htup); + } + + /* + * Fix data that isn't saved in relcache cache file. + */ + if (relation->rd_rel->relhasrules && relation->rd_rules == NULL) + RelationBuildRuleLock(relation); + if (relation->rd_rel->reltriggers > 0 && relation->trigdesc == NULL) + RelationBuildTriggers(relation); } } +/* + * RelationCacheInitializePhase3 + * + * Final step of relcache initialization: write out a new relcache + * cache file if one is needed. + */ +void +RelationCacheInitializePhase3(void) +{ + if (IsBootstrapProcessingMode()) + return; + + if (needNewCacheFile) + { + /* + * Force all the catcaches to finish initializing and thereby + * open the catalogs and indexes they use. This will preload + * the relcache with entries for all the most important system + * catalogs and indexes, so that the init file will be most + * useful for future backends. + */ + InitCatalogCachePhase2(); + + /* now write the file */ + write_relcache_init_file(); + } +} + + /* used by XLogInitCache */ void CreateDummyCaches(void); void DestroyDummyCaches(void); @@ -2233,63 +2437,31 @@ AttrDefaultFetch(Relation relation) AttrDefault *attrdef = relation->rd_att->constr->defval; int ndef = relation->rd_att->constr->num_defval; Relation adrel; - Relation irel = (Relation) NULL; + SysScanDesc adscan; ScanKeyData skey; - HeapTupleData tuple; HeapTuple htup; - Form_pg_attrdef adform; - IndexScanDesc sd = (IndexScanDesc) NULL; - HeapScanDesc adscan = (HeapScanDesc) NULL; - RetrieveIndexResult indexRes; Datum val; bool isnull; int found; int i; - bool hasindex; ScanKeyEntryInitialize(&skey, (bits16) 0x0, - (AttrNumber) 1, + (AttrNumber) Anum_pg_attrdef_adrelid, (RegProcedure) F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); adrel = heap_openr(AttrDefaultRelationName, AccessShareLock); - hasindex = (adrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes()); - if (hasindex) - { - irel = index_openr(AttrDefaultIndex); - sd = index_beginscan(irel, false, 1, &skey); - } - else - adscan = heap_beginscan(adrel, false, SnapshotNow, 1, &skey); - tuple.t_datamcxt = NULL; - tuple.t_data = NULL; + adscan = systable_beginscan(adrel, AttrDefaultIndex, true, + SnapshotNow, + 1, &skey); + found = 0; - for (found = 0;;) + while (HeapTupleIsValid(htup = systable_getnext(adscan))) { - Buffer buffer; + Form_pg_attrdef adform = (Form_pg_attrdef) GETSTRUCT(htup); - if (hasindex) - { - indexRes = index_getnext(sd, ForwardScanDirection); - if (!indexRes) - break; - - tuple.t_self = indexRes->heap_iptr; - heap_fetch(adrel, SnapshotNow, &tuple, &buffer, sd); - pfree(indexRes); - if (tuple.t_data == NULL) - continue; - htup = &tuple; - } - else - { - htup = heap_getnext(adscan, 0); - if (!HeapTupleIsValid(htup)) - break; - } found++; - adform = (Form_pg_attrdef) GETSTRUCT(htup); for (i = 0; i < ndef; i++) { if (adform->adnum != attrdef[i].adnum) @@ -2312,8 +2484,6 @@ AttrDefaultFetch(Relation relation) val))); break; } - if (hasindex) - ReleaseBuffer(buffer); if (i >= ndef) elog(NOTICE, "AttrDefaultFetch: unexpected record found for attr %d in rel %s", @@ -2321,18 +2491,12 @@ AttrDefaultFetch(Relation relation) RelationGetRelationName(relation)); } - if (found < ndef) - elog(NOTICE, "AttrDefaultFetch: %d record not found for rel %s", - ndef - found, RelationGetRelationName(relation)); - - if (hasindex) - { - index_endscan(sd); - index_close(irel); - } - else - heap_endscan(adscan); + systable_endscan(adscan); heap_close(adrel, AccessShareLock); + + if (found != ndef) + elog(NOTICE, "AttrDefaultFetch: %d record(s) not found for rel %s", + ndef - found, RelationGetRelationName(relation)); } static void @@ -2341,60 +2505,28 @@ RelCheckFetch(Relation relation) ConstrCheck *check = relation->rd_att->constr->check; int ncheck = relation->rd_att->constr->num_check; Relation rcrel; - Relation irel = (Relation) NULL; + SysScanDesc rcscan; ScanKeyData skey; - HeapTupleData tuple; HeapTuple htup; - IndexScanDesc sd = (IndexScanDesc) NULL; - HeapScanDesc rcscan = (HeapScanDesc) NULL; - RetrieveIndexResult indexRes; Name rcname; Datum val; bool isnull; int found; - bool hasindex; ScanKeyEntryInitialize(&skey, (bits16) 0x0, - (AttrNumber) 1, + (AttrNumber) Anum_pg_relcheck_rcrelid, (RegProcedure) F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); rcrel = heap_openr(RelCheckRelationName, AccessShareLock); - hasindex = (rcrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes()); - if (hasindex) - { - irel = index_openr(RelCheckIndex); - sd = index_beginscan(irel, false, 1, &skey); - } - else - rcscan = heap_beginscan(rcrel, false, SnapshotNow, 1, &skey); - tuple.t_datamcxt = NULL; - tuple.t_data = NULL; + rcscan = systable_beginscan(rcrel, RelCheckIndex, true, + SnapshotNow, + 1, &skey); + found = 0; - for (found = 0;;) + while (HeapTupleIsValid(htup = systable_getnext(rcscan))) { - Buffer buffer; - - if (hasindex) - { - indexRes = index_getnext(sd, ForwardScanDirection); - if (!indexRes) - break; - - tuple.t_self = indexRes->heap_iptr; - heap_fetch(rcrel, SnapshotNow, &tuple, &buffer, sd); - pfree(indexRes); - if (tuple.t_data == NULL) - continue; - htup = &tuple; - } - else - { - htup = heap_getnext(rcscan, 0); - if (!HeapTupleIsValid(htup)) - break; - } if (found == ncheck) elog(ERROR, "RelCheckFetch: unexpected record found for rel %s", RelationGetRelationName(relation)); @@ -2417,22 +2549,14 @@ RelCheckFetch(Relation relation) DatumGetCString(DirectFunctionCall1(textout, val))); found++; - if (hasindex) - ReleaseBuffer(buffer); } - if (found < ncheck) - elog(ERROR, "RelCheckFetch: %d record not found for rel %s", - ncheck - found, RelationGetRelationName(relation)); - - if (hasindex) - { - index_endscan(sd); - index_close(irel); - } - else - heap_endscan(rcscan); + systable_endscan(rcscan); heap_close(rcrel, AccessShareLock); + + if (found != ncheck) + elog(ERROR, "RelCheckFetch: %d record(s) not found for rel %s", + ncheck - found, RelationGetRelationName(relation)); } /* @@ -2461,11 +2585,9 @@ List * RelationGetIndexList(Relation relation) { Relation indrel; - Relation irel = (Relation) NULL; + SysScanDesc indscan; ScanKeyData skey; - IndexScanDesc sd = (IndexScanDesc) NULL; - HeapScanDesc hscan = (HeapScanDesc) NULL; - bool hasindex; + HeapTuple htup; List *result; MemoryContext oldcxt; @@ -2473,29 +2595,6 @@ RelationGetIndexList(Relation relation) if (relation->rd_indexfound) return listCopy(relation->rd_indexlist); - /* Prepare to scan pg_index for entries having indrelid = this rel. */ - indrel = heap_openr(IndexRelationName, AccessShareLock); - hasindex = (indrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes()); - if (hasindex) - { - irel = index_openr(IndexIndrelidIndex); - ScanKeyEntryInitialize(&skey, - (bits16) 0x0, - (AttrNumber) 1, - (RegProcedure) F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(relation))); - sd = index_beginscan(irel, false, 1, &skey); - } - else - { - ScanKeyEntryInitialize(&skey, - (bits16) 0x0, - (AttrNumber) Anum_pg_index_indrelid, - (RegProcedure) F_OIDEQ, - ObjectIdGetDatum(RelationGetRelid(relation))); - hscan = heap_beginscan(indrel, false, SnapshotNow, 1, &skey); - } - /* * We build the list we intend to return (in the caller's context) * while doing the scan. After successfully completing the scan, we @@ -2504,51 +2603,26 @@ RelationGetIndexList(Relation relation) */ result = NIL; - for (;;) - { - HeapTupleData tuple; - HeapTuple htup; - Buffer buffer; - Form_pg_index index; + /* Prepare to scan pg_index for entries having indrelid = this rel. */ + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) Anum_pg_index_indrelid, + (RegProcedure) F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(relation))); - if (hasindex) - { - RetrieveIndexResult indexRes; - - indexRes = index_getnext(sd, ForwardScanDirection); - if (!indexRes) - break; - tuple.t_self = indexRes->heap_iptr; - tuple.t_datamcxt = NULL; - tuple.t_data = NULL; - heap_fetch(indrel, SnapshotNow, &tuple, &buffer, sd); - pfree(indexRes); - if (tuple.t_data == NULL) - continue; - htup = &tuple; - } - else - { - htup = heap_getnext(hscan, 0); - if (!HeapTupleIsValid(htup)) - break; - } + indrel = heap_openr(IndexRelationName, AccessShareLock); + indscan = systable_beginscan(indrel, IndexIndrelidIndex, true, + SnapshotNow, + 1, &skey); - index = (Form_pg_index) GETSTRUCT(htup); + while (HeapTupleIsValid(htup = systable_getnext(indscan))) + { + Form_pg_index index = (Form_pg_index) GETSTRUCT(htup); result = insert_ordered_oid(result, index->indexrelid); - - if (hasindex) - ReleaseBuffer(buffer); } - if (hasindex) - { - index_endscan(sd); - index_close(irel); - } - else - heap_endscan(hscan); + systable_endscan(indscan); heap_close(indrel, AccessShareLock); /* Now save a copy of the completed list in the relcache entry. */ @@ -2594,8 +2668,7 @@ insert_ordered_oid(List *list, Oid datum) /* - * init_irels(), write_irels() -- handle special-case initialization of - * index relation descriptors. + * load_relcache_init_file, write_relcache_init_file * * In late 1992, we started regularly having databases with more than * a thousand classes in them. With this number of classes, it became @@ -2609,7 +2682,7 @@ insert_ordered_oid(List *list, Oid datum) * In order to get around the problem, we do the following: * * + When the database system is initialized (at initdb time), we - * don't use indices on pg_attribute. We do sequential scans. + * don't use indexes. We do sequential scans. * * + When the backend is started up in normal mode, we load an image * of the appropriate relation descriptors, in internal format, @@ -2621,177 +2694,308 @@ insert_ordered_oid(List *list, Oid datum) * * We could dispense with the initialization file and just build the * critical reldescs the hard way on every backend startup, but that - * slows down backend startup noticeably if pg_class is large. + * slows down backend startup noticeably. + * + * We can in fact go further, and save more relcache entries than + * just the ones that are absolutely critical; this allows us to speed + * up backend startup by not having to build such entries the hard way. + * Presently, all the catalog and index entries that are referred to + * by catcaches are stored in the initialization file. * * As of v6.5, vacuum.c deletes the initialization file at completion * of a VACUUM, so that it will be rebuilt at the next backend startup. - * This ensures that vacuum-collected stats for the system indexes - * will eventually get used by the optimizer --- otherwise the relcache - * entries for these indexes will show zero sizes forever, since the - * relcache entries are pinned in memory and will never be reloaded - * from pg_class. + * This ensures that vacuum-collected stats for the system catalogs + * and indexes will be seen by backends started later. */ -/* pg_attnumind, pg_classnameind, pg_classoidind */ -#define Num_indices_bootstrap 3 - -static void -init_irels(void) +/* + * load_relcache_init_file -- attempt to load cache from the init file + * + * If successful, return TRUE and set criticalRelcachesBuilt to true. + * If not successful, return FALSE and set needNewCacheFile to true. + * + * NOTE: we assume we are already switched into CacheMemoryContext. + */ +static bool +load_relcache_init_file(void) { - Size len; - int nread; - File fd; - Relation irel[Num_indices_bootstrap]; - Relation ird; - Form_pg_am am; - Form_pg_class relform; - MemoryContext indexcxt; - IndexStrategy strat; - RegProcedure *support; - int nstrategies, - nsupport; + FILE *fp; + char initfilename[MAXPGPATH]; + Relation *rels; + int relno, + num_rels, + max_rels, + nailed_rels, + nailed_indexes; int i; - int relno; - if ((fd = FileNameOpenFile(RELCACHE_INIT_FILENAME, O_RDONLY | PG_BINARY, 0600)) < 0) + snprintf(initfilename, sizeof(initfilename), "%s/%s", + DatabasePath, RELCACHE_INIT_FILENAME); + + fp = AllocateFile(initfilename, PG_BINARY_R); + if (fp == NULL) { - write_irels(); - return; + needNewCacheFile = true; + return false; } - for (relno = 0; relno < Num_indices_bootstrap; relno++) + /* + * Read the index relcache entries from the file. Note we will not + * enter any of them into the cache if the read fails partway through; + * this helps to guard against broken init files. + */ + max_rels = 100; + rels = (Relation *) palloc(max_rels * sizeof(Relation)); + num_rels = 0; + nailed_rels = nailed_indexes = 0; + initFileRelationIds = NIL; + + for (relno = 0; ; relno++) { + Size len; + size_t nread; + Relation rel; + Form_pg_class relform; + /* first read the relation descriptor length */ - if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len)) + if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) + { + if (nread == 0) + break; /* end of file */ goto read_failed; + } /* safety check for incompatible relcache layout */ if (len != sizeof(RelationData)) goto read_failed; - ird = irel[relno] = (Relation) palloc(len); - MemSet(ird, 0, len); - - /* then, read the Relation structure */ - if ((nread = FileRead(fd, (char *) ird, len)) != len) - goto read_failed; - - /* reset transient fields */ - ird->rd_targblock = InvalidBlockNumber; - ird->rd_fd = -1; - ird->rd_refcnt = 0; + /* allocate another relcache header */ + if (num_rels >= max_rels) + { + max_rels *= 2; + rels = (Relation *) repalloc(rels, max_rels * sizeof(Relation)); + } - ird->rd_node.tblNode = MyDatabaseId; + rel = rels[num_rels++] = (Relation) palloc(len); - /* next, read the access method tuple form */ - if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len)) - goto read_failed; - - am = (Form_pg_am) palloc(len); - if ((nread = FileRead(fd, (char *) am, len)) != len) + /* then, read the Relation structure */ + if ((nread = fread(rel, 1, len, fp)) != len) goto read_failed; - ird->rd_am = am; - /* next read the relation tuple form */ - if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len)) + if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) goto read_failed; relform = (Form_pg_class) palloc(len); - if ((nread = FileRead(fd, (char *) relform, len)) != len) + if ((nread = fread(relform, 1, len, fp)) != len) goto read_failed; - ird->rd_rel = relform; + rel->rd_rel = relform; /* initialize attribute tuple forms */ - ird->rd_att = CreateTemplateTupleDesc(relform->relnatts); + rel->rd_att = CreateTemplateTupleDesc(relform->relnatts); /* next read all the attribute tuple form data entries */ - len = ATTRIBUTE_TUPLE_SIZE; for (i = 0; i < relform->relnatts; i++) { - if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len)) + if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) goto read_failed; - ird->rd_att->attrs[i] = (Form_pg_attribute) palloc(len); + rel->rd_att->attrs[i] = (Form_pg_attribute) palloc(len); - if ((nread = FileRead(fd, (char *) ird->rd_att->attrs[i], len)) != len) + if ((nread = fread(rel->rd_att->attrs[i], 1, len, fp)) != len) goto read_failed; } - /* - * prepare index info context --- parameters should match - * RelationInitIndexAccessInfo - */ - indexcxt = AllocSetContextCreate(CacheMemoryContext, - RelationGetRelationName(ird), - 0, /* minsize */ - 512, /* initsize */ - 1024); /* maxsize */ - ird->rd_indexcxt = indexcxt; - - /* next, read the index strategy map */ - if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len)) - goto read_failed; + /* If it's an index, there's more to do */ + if (rel->rd_rel->relkind == RELKIND_INDEX) + { + Form_pg_am am; + MemoryContext indexcxt; + IndexStrategy strat; + Oid *operator; + RegProcedure *support; + int nstrategies, + nsupport; + + /* Count nailed indexes to ensure we have 'em all */ + if (rel->rd_isnailed) + nailed_indexes++; + + /* next, read the pg_index tuple form */ + if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) + goto read_failed; - strat = (IndexStrategy) MemoryContextAlloc(indexcxt, len); - if ((nread = FileRead(fd, (char *) strat, len)) != len) - goto read_failed; + rel->rd_index = (Form_pg_index) palloc(len); + if ((nread = fread(rel->rd_index, 1, len, fp)) != len) + goto read_failed; - /* have to invalidate any FmgrInfo data in the strategy maps */ - nstrategies = am->amstrategies * relform->relnatts; - for (i = 0; i < nstrategies; i++) - strat->strategyMapData[i].entry[0].sk_func.fn_oid = InvalidOid; + /* next, read the access method tuple form */ + if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) + goto read_failed; - ird->rd_istrat = strat; + am = (Form_pg_am) palloc(len); + if ((nread = fread(am, 1, len, fp)) != len) + goto read_failed; + rel->rd_am = am; - /* finally, read the vector of support procedures */ - if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len)) - goto read_failed; - support = (RegProcedure *) MemoryContextAlloc(indexcxt, len); - if ((nread = FileRead(fd, (char *) support, len)) != len) - goto read_failed; + /* + * prepare index info context --- parameters should match + * RelationInitIndexAccessInfo + */ + indexcxt = AllocSetContextCreate(CacheMemoryContext, + RelationGetRelationName(rel), + 0, /* minsize */ + 512, /* initsize */ + 1024); /* maxsize */ + rel->rd_indexcxt = indexcxt; + + /* next, read the index strategy map */ + if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) + goto read_failed; - ird->rd_support = support; + strat = (IndexStrategy) MemoryContextAlloc(indexcxt, len); + if ((nread = fread(strat, 1, len, fp)) != len) + goto read_failed; - nsupport = relform->relnatts * am->amsupport; - ird->rd_supportinfo = (FmgrInfo *) - MemoryContextAlloc(indexcxt, nsupport * sizeof(FmgrInfo)); - MemSet(ird->rd_supportinfo, 0, nsupport * sizeof(FmgrInfo)); + /* have to invalidate any FmgrInfo data in the strategy maps */ + nstrategies = am->amstrategies * relform->relnatts; + for (i = 0; i < nstrategies; i++) + strat->strategyMapData[i].entry[0].sk_func.fn_oid = InvalidOid; + + rel->rd_istrat = strat; + + /* next, read the vector of operator OIDs */ + if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) + goto read_failed; + + operator = (Oid *) MemoryContextAlloc(indexcxt, len); + if ((nread = fread(operator, 1, len, fp)) != len) + goto read_failed; + + rel->rd_operator = operator; - RelationInitLockInfo(ird); + /* finally, read the vector of support procedures */ + if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) + goto read_failed; + support = (RegProcedure *) MemoryContextAlloc(indexcxt, len); + if ((nread = fread(support, 1, len, fp)) != len) + goto read_failed; + + rel->rd_support = support; + + /* add a zeroed support-fmgr-info vector */ + nsupport = relform->relnatts * am->amsupport; + rel->rd_supportinfo = (FmgrInfo *) + MemoryContextAlloc(indexcxt, nsupport * sizeof(FmgrInfo)); + MemSet(rel->rd_supportinfo, 0, nsupport * sizeof(FmgrInfo)); + } + else + { + /* Count nailed rels to ensure we have 'em all */ + if (rel->rd_isnailed) + nailed_rels++; + + Assert(rel->rd_index == NULL); + Assert(rel->rd_am == NULL); + Assert(rel->rd_indexcxt == NULL); + Assert(rel->rd_istrat == NULL); + Assert(rel->rd_operator == NULL); + Assert(rel->rd_support == NULL); + Assert(rel->rd_supportinfo == NULL); + } + + /* + * Rules and triggers are not saved (mainly because the internal + * format is complex and subject to change). They must be rebuilt + * if needed by RelationCacheInitializePhase2. This is not expected + * to be a big performance hit since few system catalogs have such. + */ + rel->rd_rules = NULL; + rel->rd_rulescxt = NULL; + rel->trigdesc = NULL; + + /* + * Reset transient-state fields in the relcache entry + */ + rel->rd_fd = -1; + rel->rd_targblock = InvalidBlockNumber; + if (rel->rd_isnailed) + RelationSetReferenceCount(rel, 1); + else + RelationSetReferenceCount(rel, 0); + rel->rd_indexfound = false; + rel->rd_indexlist = NIL; + MemSet(&rel->pgstat_info, 0, sizeof(rel->pgstat_info)); - RelationCacheInsert(ird); + /* + * Make sure database ID is correct. This is needed in case the + * pg_internal.init file was copied from some other database by + * CREATE DATABASE. + */ + if (rel->rd_rel->relisshared) + rel->rd_node.tblNode = InvalidOid; + else + rel->rd_node.tblNode = MyDatabaseId; + + RelationInitLockInfo(rel); + } + + /* + * We reached the end of the init file without apparent problem. + * Did we get the right number of nailed items? (This is a useful + * crosscheck in case the set of critical rels or indexes changes.) + */ + if (nailed_rels != NUM_CRITICAL_RELS || + nailed_indexes != NUM_CRITICAL_INDEXES) + goto read_failed; + + /* + * OK, all appears well. + * + * Now insert all the new relcache entries into the cache. + */ + for (relno = 0; relno < num_rels; relno++) + { + RelationCacheInsert(rels[relno]); + /* also make a list of their OIDs, for RelationIdIsInInitFile */ + initFileRelationIds = lconsi((int) RelationGetRelid(rels[relno]), + initFileRelationIds); } - /* successfully read the init file */ - FileClose(fd); + pfree(rels); + FreeFile(fp); + criticalRelcachesBuilt = true; - return; + return true; - /* init file is broken, so do it the hard way */ + /* + * init file is broken, so do it the hard way. We don't bother + * trying to free the clutter we just allocated; it's not in the + * relcache so it won't hurt. + */ read_failed: - FileClose(fd); - write_irels(); + pfree(rels); + FreeFile(fp); + + needNewCacheFile = true; + return false; } +/* + * Write out a new initialization file with the current contents + * of the relcache. + */ static void -write_irels(void) +write_relcache_init_file(void) { - Size len; - int nwritten; - File fd; - Relation irel[Num_indices_bootstrap]; - Relation ird; - Form_pg_am am; - Form_pg_class relform; - IndexStrategy strat; - RegProcedure *support; - int i; - int relno; - RelationBuildDescInfo bi; + FILE *fp; char tempfilename[MAXPGPATH]; char finalfilename[MAXPGPATH]; + HASH_SEQ_STATUS status; + RelNameCacheEnt *namehentry; + MemoryContext oldcxt; + int i; /* * We must write a temporary file and rename it into place. Otherwise, @@ -2803,8 +3007,10 @@ write_irels(void) snprintf(finalfilename, sizeof(finalfilename), "%s/%s", DatabasePath, RELCACHE_INIT_FILENAME); - fd = PathNameOpenFile(tempfilename, O_WRONLY | O_CREAT | O_TRUNC | PG_BINARY, 0600); - if (fd < 0) + unlink(tempfilename); /* in case it exists w/wrong permissions */ + + fp = AllocateFile(tempfilename, PG_BINARY_W); + if (fp == NULL) { /* * We used to consider this a fatal error, but we might as well @@ -2814,148 +3020,227 @@ write_irels(void) return; } - FileSeek(fd, 0L, SEEK_SET); - /* - * Build relation descriptors for the critical system indexes without - * resort to the descriptor cache. In order to do this, we set - * ProcessingMode to Bootstrap. The effect of this is to disable - * indexed relation searches -- a necessary step, since we're trying - * to instantiate the index relation descriptors here. Once we have - * the descriptors, nail them into cache so we never lose them. - */ - - /*--------- - * Removed the following ProcessingMode change -- inoue - * At this point - * 1) Catalog Cache isn't initialized - * 2) Relation Cache for the following critical indexes aren't built - * oldmode = GetProcessingMode(); - * SetProcessingMode(BootstrapProcessing); - *--------- + * Write all the reldescs (in no particular order). */ + hash_seq_init(&status, RelationNameCache); - bi.infotype = INFO_RELNAME; - bi.i.info_name = AttributeRelidNumIndex; - irel[0] = RelationBuildDesc(bi, NULL); - irel[0]->rd_isnailed = true; - - bi.i.info_name = ClassNameIndex; - irel[1] = RelationBuildDesc(bi, NULL); - irel[1]->rd_isnailed = true; - - bi.i.info_name = ClassOidIndex; - irel[2] = RelationBuildDesc(bi, NULL); - irel[2]->rd_isnailed = true; - - criticalRelcachesBuilt = true; + initFileRelationIds = NIL; - /* - * Removed the following ProcessingMode -- inoue - * SetProcessingMode(oldmode); - */ - - /* - * Write out the index reldescs to the special cache file. - */ - for (relno = 0; relno < Num_indices_bootstrap; relno++) + while ((namehentry = (RelNameCacheEnt *) hash_seq_search(&status)) != NULL) { - ird = irel[relno]; - - /* save the volatile fields in the relation descriptor */ - am = ird->rd_am; - ird->rd_am = (Form_pg_am) NULL; - relform = ird->rd_rel; - ird->rd_rel = (Form_pg_class) NULL; - strat = ird->rd_istrat; - support = ird->rd_support; + Relation rel = namehentry->reldesc; + Form_pg_class relform = rel->rd_rel; + Size len; /* - * first write the relation descriptor , excluding strategy and - * support + * first write the relcache entry proper */ len = sizeof(RelationData); /* first, write the relation descriptor length */ - if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len))) - != sizeof(len)) + if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) elog(FATAL, "cannot write init file -- descriptor length"); /* next, write out the Relation structure */ - if ((nwritten = FileWrite(fd, (char *) ird, len)) != len) + if (fwrite(rel, 1, len, fp) != len) elog(FATAL, "cannot write init file -- reldesc"); - /* next, write the access method tuple form */ - len = sizeof(FormData_pg_am); - if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len))) - != sizeof(len)) - elog(FATAL, "cannot write init file -- am tuple form length"); - - if ((nwritten = FileWrite(fd, (char *) am, len)) != len) - elog(FATAL, "cannot write init file -- am tuple form"); - /* next write the relation tuple form */ len = sizeof(FormData_pg_class); - if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len))) - != sizeof(len)) + if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) elog(FATAL, "cannot write init file -- relation tuple form length"); - if ((nwritten = FileWrite(fd, (char *) relform, len)) != len) + if (fwrite(relform, 1, len, fp) != len) elog(FATAL, "cannot write init file -- relation tuple form"); /* next, do all the attribute tuple form data entries */ - len = ATTRIBUTE_TUPLE_SIZE; for (i = 0; i < relform->relnatts; i++) { - if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len))) - != sizeof(len)) + len = ATTRIBUTE_TUPLE_SIZE; + if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) elog(FATAL, "cannot write init file -- length of attdesc %d", i); - if ((nwritten = FileWrite(fd, (char *) ird->rd_att->attrs[i], len)) - != len) + if (fwrite(rel->rd_att->attrs[i], 1, len, fp) != len) elog(FATAL, "cannot write init file -- attdesc %d", i); } - /* next, write the index strategy map */ - len = AttributeNumberGetIndexStrategySize(relform->relnatts, - am->amstrategies); - if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len))) - != sizeof(len)) - elog(FATAL, "cannot write init file -- strategy map length"); - - if ((nwritten = FileWrite(fd, (char *) strat, len)) != len) - elog(FATAL, "cannot write init file -- strategy map"); - - /* finally, write the vector of support procedures */ - len = relform->relnatts * (am->amsupport * sizeof(RegProcedure)); - if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len))) - != sizeof(len)) - elog(FATAL, "cannot write init file -- support vector length"); + /* If it's an index, there's more to do */ + if (rel->rd_rel->relkind == RELKIND_INDEX) + { + Form_pg_am am = rel->rd_am; + HeapTuple tuple; - if ((nwritten = FileWrite(fd, (char *) support, len)) != len) - elog(FATAL, "cannot write init file -- support vector"); + /* + * We need to write the index tuple form, but this is a bit + * tricky since it's a variable-length struct. Rather than + * hoping to intuit the length, fetch the pg_index tuple + * afresh using the syscache, and write that. + */ + tuple = SearchSysCache(INDEXRELID, + ObjectIdGetDatum(RelationGetRelid(rel)), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "write_relcache_init_file: no pg_index entry for index %u", + RelationGetRelid(rel)); + len = tuple->t_len - tuple->t_data->t_hoff; + if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) + elog(FATAL, "cannot write init file -- index tuple form length"); + if (fwrite(GETSTRUCT(tuple), 1, len, fp) != len) + elog(FATAL, "cannot write init file -- index tuple form"); + ReleaseSysCache(tuple); + + /* next, write the access method tuple form */ + len = sizeof(FormData_pg_am); + if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) + elog(FATAL, "cannot write init file -- am tuple form length"); + + if (fwrite(am, 1, len, fp) != len) + elog(FATAL, "cannot write init file -- am tuple form"); + + /* next, write the index strategy map */ + len = AttributeNumberGetIndexStrategySize(relform->relnatts, + am->amstrategies); + if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) + elog(FATAL, "cannot write init file -- strategy map length"); + + if (fwrite(rel->rd_istrat, 1, len, fp) != len) + elog(FATAL, "cannot write init file -- strategy map"); + + /* next, write the vector of operator OIDs */ + len = relform->relnatts * (am->amstrategies * sizeof(Oid)); + if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) + elog(FATAL, "cannot write init file -- operator vector length"); + + if (fwrite(rel->rd_operator, 1, len, fp) != len) + elog(FATAL, "cannot write init file -- operator vector"); + + /* finally, write the vector of support procedures */ + len = relform->relnatts * (am->amsupport * sizeof(RegProcedure)); + if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) + elog(FATAL, "cannot write init file -- support vector length"); + + if (fwrite(rel->rd_support, 1, len, fp) != len) + elog(FATAL, "cannot write init file -- support vector"); + } - /* restore volatile fields */ - ird->rd_am = am; - ird->rd_rel = relform; + /* also make a list of their OIDs, for RelationIdIsInInitFile */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + initFileRelationIds = lconsi((int) RelationGetRelid(rel), + initFileRelationIds); + MemoryContextSwitchTo(oldcxt); } - FileClose(fd); + FreeFile(fp); /* - * And rename the temp file to its final name, deleting any - * previously-existing init file. + * Now we have to check whether the data we've so painstakingly + * accumulated is already obsolete due to someone else's just-committed + * catalog changes. If so, we just delete the temp file and leave it + * to the next backend to try again. (Our own relcache entries will be + * updated by SI message processing, but we can't be sure whether what + * we wrote out was up-to-date.) * - * Note: a failure here is possible under Cygwin, if some other - * backend is holding open an unlinked-but-not-yet-gone init file. - * So treat this as a noncritical failure. + * This mustn't run concurrently with RelationCacheInitFileInvalidate, + * so grab a serialization lock for the duration. */ - if (rename(tempfilename, finalfilename) < 0) + LWLockAcquire(RelCacheInitLock, LW_EXCLUSIVE); + + /* Make sure we have seen all incoming SI messages */ + AcceptInvalidationMessages(); + + /* + * If we have received any SI relcache invals since backend start, + * assume we may have written out-of-date data. + */ + if (relcacheInvalsReceived == 0L) { - elog(NOTICE, "Cannot rename init file %s to %s: %m\n\tContinuing anyway, but there's something wrong.", tempfilename, finalfilename); /* - * If we fail, try to clean up the useless temp file; don't bother - * to complain if this fails too. + * OK, rename the temp file to its final name, deleting any + * previously-existing init file. + * + * Note: a failure here is possible under Cygwin, if some other + * backend is holding open an unlinked-but-not-yet-gone init file. + * So treat this as a noncritical failure. */ + if (rename(tempfilename, finalfilename) < 0) + { + elog(NOTICE, "Cannot rename init file %s to %s: %m\n\tContinuing anyway, but there's something wrong.", tempfilename, finalfilename); + /* + * If we fail, try to clean up the useless temp file; don't bother + * to complain if this fails too. + */ + unlink(tempfilename); + } + } + else + { + /* Delete the already-obsolete temp file */ unlink(tempfilename); } + + LWLockRelease(RelCacheInitLock); +} + +/* + * Detect whether a given relation (identified by OID) is one of the ones + * we store in the init file. + * + * Note that we effectively assume that all backends running in a database + * would choose to store the same set of relations in the init file; + * otherwise there are cases where we'd fail to detect the need for an init + * file invalidation. This does not seem likely to be a problem in practice. + */ +bool +RelationIdIsInInitFile(Oid relationId) +{ + return intMember((int) relationId, initFileRelationIds); +} + +/* + * Invalidate (remove) the init file during commit of a transaction that + * changed one or more of the relation cache entries that are kept in the + * init file. + * + * We actually need to remove the init file twice: once just before sending + * the SI messages that include relcache inval for such relations, and once + * just after sending them. The unlink before ensures that a backend that's + * currently starting cannot read the now-obsolete init file and then miss + * the SI messages that will force it to update its relcache entries. (This + * works because the backend startup sequence gets into the PROC array before + * trying to load the init file.) The unlink after is to synchronize with a + * backend that may currently be trying to write an init file based on data + * that we've just rendered invalid. Such a backend will see the SI messages, + * but we can't leave the init file sitting around to fool later backends. + * + * Ignore any failure to unlink the file, since it might not be there if + * no backend has been started since the last removal. + */ +void +RelationCacheInitFileInvalidate(bool beforeSend) +{ + char initfilename[MAXPGPATH]; + + snprintf(initfilename, sizeof(initfilename), "%s/%s", + DatabasePath, RELCACHE_INIT_FILENAME); + + if (beforeSend) + { + /* no interlock needed here */ + unlink(initfilename); + } + else + { + /* + * We need to interlock this against write_relcache_init_file, + * to guard against possibility that someone renames a new-but- + * already-obsolete init file into place just after we unlink. + * With the interlock, it's certain that write_relcache_init_file + * will notice our SI inval message before renaming into place, + * or else that we will execute second and successfully unlink + * the file. + */ + LWLockAcquire(RelCacheInitLock, LW_EXCLUSIVE); + unlink(initfilename); + LWLockRelease(RelCacheInitLock); + } } diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index e629bf48e468aa42a20caa53f5aad1e49588276d..dfbcccffbed250efda87862838f135fc11c352cb 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.66 2001/10/25 05:49:46 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.67 2002/02/19 20:11:18 tgl Exp $ * * NOTES * These routines allow the parser/planner/executor to perform @@ -113,6 +113,16 @@ static struct cachedesc cacheinfo[] = { 0, 0 }}, + {AccessMethodRelationName, /* AMOID */ + AmOidIndex, + 0, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }}, {AccessMethodOperatorRelationName, /* AMOPOPID */ AccessMethodOperatorIndex, 0, @@ -365,8 +375,7 @@ static struct cachedesc cacheinfo[] = { }} }; -static CatCache *SysCache[ - lengthof(cacheinfo)]; +static CatCache *SysCache[lengthof(cacheinfo)]; static int SysCacheSize = lengthof(cacheinfo); static bool CacheInitialized = false; @@ -383,7 +392,7 @@ IsCacheInitialized(void) * * Note that no database access is done here; we only allocate memory * and initialize the cache structure. Interrogation of the database - * to complete initialization of a cache happens only upon first use + * to complete initialization of a cache happens upon first use * of that cache. */ void @@ -411,6 +420,32 @@ InitCatalogCache(void) } +/* + * InitCatalogCachePhase2 - finish initializing the caches + * + * Finish initializing all the caches, including necessary database + * access. + * + * This is *not* essential; normally we allow syscaches to be initialized + * on first use. However, it is useful as a mechanism to preload the + * relcache with entries for the most-commonly-used system catalogs. + * Therefore, we invoke this routine when we need to write a new relcache + * init file. + */ +void +InitCatalogCachePhase2(void) +{ + int cacheId; + + Assert(CacheInitialized); + + for (cacheId = 0; cacheId < SysCacheSize; cacheId++) + { + InitCatCachePhase2(SysCache[cacheId]); + } +} + + /* * SearchSysCache * diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 3a48b133bfe8aeb3bf50de83fc858b740d51dd67..222ab6d54ab2aed462512795c63717403842055f 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.97 2001/11/02 16:30:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.98 2002/02/19 20:11:18 tgl Exp $ * * *------------------------------------------------------------------------- @@ -287,16 +287,15 @@ InitPostgres(const char *dbname, const char *username) AmiTransactionOverride(bootstrap); /* - * Initialize the relation descriptor cache. The pre-allocated - * reldescs are created here. + * Initialize the relation descriptor cache. This must create + * at least the minimum set of "nailed-in" cache entries. No + * catalog access happens here. */ RelationCacheInitialize(); /* - * Initialize all the system catalog caches. - * - * Does not touch files since all routines are builtins (?) - thomas - * 1997-11-01 + * Initialize all the system catalog caches. Note that no catalog + * access happens here; we only set up the cache structure. */ InitCatalogCache(); @@ -313,7 +312,11 @@ InitPostgres(const char *dbname, const char *username) if (!bootstrap) StartTransactionCommand(); - /* replace faked-up relcache entries with the real info */ + /* + * It's now possible to do real access to the system catalogs. + * + * Replace faked-up relcache entries with correct info. + */ RelationCacheInitializePhase2(); /* @@ -333,8 +336,10 @@ InitPostgres(const char *dbname, const char *username) } } else + { /* normal multiuser case */ InitializeSessionUserId(username); + } /* * Unless we are bootstrapping, double-check that InitMyDatabaseInfo() @@ -349,6 +354,13 @@ InitPostgres(const char *dbname, const char *username) set_default_client_encoding(); #endif + /* + * Final phase of relation cache startup: write a new cache file + * if necessary. This is done after ReverifyMyDatabase to avoid + * writing a cache file into a dead database. + */ + RelationCacheInitializePhase3(); + /* * Set up process-exit callbacks to remove temp relations and then do * pre-shutdown cleanup. This should be last because we want diff --git a/src/include/access/genam.h b/src/include/access/genam.h index db212156c8db9d9cae582ce76d0bf99ad350f528..c8cb4c105863495bfeb423f002a9a475b38c015d 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: genam.h,v 1.31 2001/11/05 17:46:31 momjian Exp $ + * $Id: genam.h,v 1.32 2002/02/19 20:11:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,9 +31,23 @@ typedef struct IndexBulkDeleteResult typedef bool (*IndexBulkDeleteCallback) (ItemPointer itemptr, void *state); -/* ---------------- - * generalized index_ interface routines (in indexam.c) - * ---------------- +/* Struct for heap-or-index scans of system tables */ +typedef struct SysScanDescData +{ + Relation heap_rel; /* catalog being scanned */ + Snapshot snapshot; /* time qual (normally SnapshotNow) */ + Relation irel; /* NULL if doing heap scan */ + HeapScanDesc scan; /* only valid in heap-scan case */ + IndexScanDesc iscan; /* only valid in index-scan case */ + HeapTupleData tuple; /* workspace for indexscan */ + Buffer buffer; /* working state for indexscan */ +} SysScanDescData; + +typedef SysScanDescData *SysScanDesc; + + +/* + * generalized index_ interface routines (in indexam.c) */ extern Relation index_open(Oid relationId); extern Relation index_openr(const char *relationName); @@ -59,9 +73,22 @@ extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum, extern struct FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum); -/* in genam.c */ +/* + * index access method support routines (in genam.c) + */ extern IndexScanDesc RelationGetIndexScan(Relation relation, bool scanFromEnd, uint16 numberOfKeys, ScanKey key); extern void IndexScanEnd(IndexScanDesc scan); +/* + * heap-or-index access to system catalogs (in genam.c) + */ +extern SysScanDesc systable_beginscan(Relation rel, + const char *indexRelname, + bool indexOK, + Snapshot snapshot, + unsigned nkeys, ScanKey key); +extern HeapTuple systable_getnext(SysScanDesc sysscan); +extern void systable_endscan(SysScanDesc sysscan); + #endif /* GENAM_H */ diff --git a/src/include/access/istrat.h b/src/include/access/istrat.h index c1000cb70b4a258f6926c62fd01a33bcab49cd68..1335863f8cf8df7715cebbbeb747c05f2276fed7 100644 --- a/src/include/access/istrat.h +++ b/src/include/access/istrat.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: istrat.h,v 1.23 2001/11/05 17:46:31 momjian Exp $ + * $Id: istrat.h,v 1.24 2002/02/19 20:11:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,6 +47,8 @@ */ #define IndexStrategyIsValid(s) PointerIsValid(s) +extern ScanKey StrategyMapGetScanKeyEntry(StrategyMap map, + StrategyNumber strategyNumber); extern StrategyMap IndexStrategyGetStrategyMap(IndexStrategy indexStrategy, StrategyNumber maxStrategyNum, AttrNumber attrNum); @@ -55,13 +57,5 @@ extern Size AttributeNumberGetIndexStrategySize(AttrNumber maxAttributeNumber, extern StrategyNumber RelationGetStrategy(Relation relation, AttrNumber attributeNumber, StrategyEvaluation evaluation, RegProcedure procedure); -extern void IndexSupportInitialize(IndexStrategy indexStrategy, - RegProcedure *indexSupport, - bool *isUnique, - Oid indexObjectId, - Oid accessMethodObjectId, - StrategyNumber maxStrategyNumber, - StrategyNumber maxSupportNumber, - AttrNumber maxAttributeNumber); #endif /* ISTRAT_H */ diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index fb6facec0a844c24e65087987b5793aa39e0a9b3..a2d47b836d25c09649549551fba73615636fb97b 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: index.h,v 1.43 2001/11/05 17:46:31 momjian Exp $ + * $Id: index.h,v 1.44 2002/02/19 20:11:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,6 +15,7 @@ #define INDEX_H #include "access/itup.h" +#include "catalog/pg_index.h" #include "nodes/execnodes.h" @@ -27,9 +28,6 @@ typedef void (*IndexBuildCallback) (Relation index, void *state); -extern Form_pg_am AccessMethodObjectIdGetForm(Oid accessMethodObjectId, - MemoryContext resultCxt); - extern Oid index_create(char *heapRelationName, char *indexRelationName, IndexInfo *indexInfo, @@ -40,7 +38,7 @@ extern Oid index_create(char *heapRelationName, extern void index_drop(Oid indexId); -extern IndexInfo *BuildIndexInfo(HeapTuple indexTuple); +extern IndexInfo *BuildIndexInfo(Form_pg_index indexStruct); extern void FormIndexDatum(IndexInfo *indexInfo, HeapTuple heapTuple, diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 2ca7bf8286e5953965b4618e70e7e11daf9d59b7..8d0ba9c4b9c88e33e8dc4a0af028a68a72a1c6cf 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: indexing.h,v 1.56 2001/11/05 17:46:31 momjian Exp $ + * $Id: indexing.h,v 1.57 2002/02/19 20:11:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -123,15 +123,6 @@ extern void CatalogCloseIndices(int nIndices, Relation *idescs); extern void CatalogIndexInsert(Relation *idescs, int nIndices, Relation heapRelation, HeapTuple heapTuple); -/* - * Canned functions for indexscans on certain system indexes. - * All index-value arguments should be passed as Datum for portability! - */ -extern HeapTuple AttributeRelidNumIndexScan(Relation heapRelation, - Datum relid, Datum attnum); -extern HeapTuple ClassNameIndexScan(Relation heapRelation, Datum relName); -extern HeapTuple ClassOidIndexScan(Relation heapRelation, Datum relId); - /* * These macros are just to keep the C compiler from spitting up on the diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 4e4e07a2c72eece838ef9dfb954b4e8df1dde1f2..c089a47731f0f7c0376666f0f6addb45081330c0 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: executor.h,v 1.61 2001/11/05 17:46:33 momjian Exp $ + * $Id: executor.h,v 1.62 2002/02/19 20:11:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,13 +30,7 @@ /* * prototypes from functions in execAmi.c */ -extern void ExecOpenScanR(Oid relOid, int nkeys, ScanKey skeys, bool isindex, - ScanDirection dir, Snapshot snapshot, - Relation *returnRelation, Pointer *returnScanDesc); -extern void ExecCloseR(Plan *node); extern void ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent); -extern HeapScanDesc ExecReScanR(Relation relDesc, HeapScanDesc scanDesc, - ScanDirection direction, int nkeys, ScanKey skeys); extern void ExecMarkPos(Plan *node); extern void ExecRestrPos(Plan *node); diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 4db2d7e2c0ff8ec6529b54a05018a9c1ad9c6327..91c2084fd9cd76df9d9df0af516fa37be48ebc8f 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lwlock.h,v 1.4 2001/11/05 17:46:35 momjian Exp $ + * $Id: lwlock.h,v 1.5 2002/02/19 20:11:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,6 +38,7 @@ typedef enum LWLockId ControlFileLock, CheckpointLock, CLogControlLock, + RelCacheInitLock, NumFixedLWLocks, /* must be last except for * MaxDynamicLWLock */ diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h index de5957441ac5c39522c3cba9f9c1f1cc3aed1c4c..6ad0d74851c9987a8c44177b358e281a94955698 100644 --- a/src/include/utils/catcache.h +++ b/src/include/utils/catcache.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catcache.h,v 1.37 2001/11/05 17:46:36 momjian Exp $ + * $Id: catcache.h,v 1.38 2002/02/19 20:11:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,6 +44,12 @@ typedef struct catcache int cc_key[4]; /* AttrNumber of each key */ PGFunction cc_hashfunc[4]; /* hash function to use for each key */ ScanKeyData cc_skey[4]; /* precomputed key info for heap scans */ +#ifdef CATCACHE_STATS + long cc_searches; /* total # searches against this cache */ + long cc_hits; /* # of matches against existing entry */ + long cc_newloads; /* # of successful loads of new entry */ + /* cc_searches - (cc_hits + cc_newloads) is # of failed searches */ +#endif Dllist cc_bucket[1]; /* hash buckets --- VARIABLE LENGTH ARRAY */ } CatCache; /* VARIABLE LENGTH STRUCT */ @@ -89,6 +95,7 @@ extern void AtEOXact_CatCache(bool isCommit); extern CatCache *InitCatCache(int id, char *relname, char *indname, int reloidattr, int nkeys, int *key); +extern void InitCatCachePhase2(CatCache *cache); extern HeapTuple SearchCatCache(CatCache *cache, Datum v1, Datum v2, diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 1bfcaa83de37c81e05a05abaa62040b2ce8116da..49814386ca6adc23a45a6915e829ae21956eddbf 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: rel.h,v 1.55 2001/11/05 17:46:36 momjian Exp $ + * $Id: rel.h,v 1.56 2002/02/19 20:11:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include "access/tupdesc.h" #include "catalog/pg_am.h" #include "catalog/pg_class.h" +#include "catalog/pg_index.h" #include "rewrite/prs2lock.h" #include "storage/block.h" #include "storage/fd.h" @@ -113,19 +114,23 @@ typedef struct RelationData bool rd_isnailed; /* rel is nailed in cache */ bool rd_indexfound; /* true if rd_indexlist is valid */ bool rd_uniqueindex; /* true if rel is a UNIQUE index */ - Form_pg_am rd_am; /* AM tuple (if an index) */ Form_pg_class rd_rel; /* RELATION tuple */ + TupleDesc rd_att; /* tuple descriptor */ Oid rd_id; /* relation's object id */ List *rd_indexlist; /* list of OIDs of indexes on relation */ LockInfoData rd_lockInfo; /* lock mgr's info for locking relation */ - TupleDesc rd_att; /* tuple descriptor */ RuleLock *rd_rules; /* rewrite rules */ MemoryContext rd_rulescxt; /* private memory cxt for rd_rules, if any */ TriggerDesc *trigdesc; /* Trigger info, or NULL if rel has none */ + /* These are non-NULL only for an index relation: */ + Form_pg_index rd_index; /* pg_index tuple describing this index */ + Form_pg_am rd_am; /* pg_am tuple for index's AM */ + /* index access support info (used only for an index relation) */ MemoryContext rd_indexcxt; /* private memory cxt for this stuff */ IndexStrategy rd_istrat; /* operator strategy map */ + Oid *rd_operator; /* OIDs of index operators */ RegProcedure *rd_support; /* OIDs of support procedures */ struct FmgrInfo *rd_supportinfo; /* lookup info for support * procedures */ diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index cd265636155d3caaa5f395e11c84a85490c1624e..e5af1bee61b8e88233e243312aa7e8c2f19ae270 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: relcache.h,v 1.29 2001/11/05 17:46:36 momjian Exp $ + * $Id: relcache.h,v 1.30 2002/02/19 20:11:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,7 @@ extern void RelationInitIndexAccessInfo(Relation relation); */ extern void RelationCacheInitialize(void); extern void RelationCacheInitializePhase2(void); +extern void RelationCacheInitializePhase3(void); /* * Routine to create a relcache entry for an about-to-be-created relation @@ -62,15 +63,18 @@ extern void RelationPurgeLocalRelation(bool xactComitted); extern void RelationCacheAbort(void); +/* + * Routines to help manage rebuilding of relcache init file + */ +extern bool RelationIdIsInInitFile(Oid relationId); +extern void RelationCacheInitFileInvalidate(bool beforeSend); /* XLOG support */ extern void CreateDummyCaches(void); extern void DestroyDummyCaches(void); -/* - * both vacuum.c and relcache.c need to know the name of the relcache init file - */ -#define RELCACHE_INIT_FILENAME "pg_internal.init" +/* should be used only by relcache.c and catcache.c */ +extern bool criticalRelcachesBuilt; #endif /* RELCACHE_H */ diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index 5e7b2d864ff78b1ad5574777492dc40363682114..6164c02b1525e6320b545250f39518e9a16f82e2 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: syscache.h,v 1.36 2001/11/05 17:46:36 momjian Exp $ + * $Id: syscache.h,v 1.37 2002/02/19 20:11:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,33 +30,35 @@ #define AGGNAME 0 #define AMNAME 1 -#define AMOPOPID 2 -#define AMOPSTRATEGY 3 -#define AMPROCNUM 4 -#define ATTNAME 5 -#define ATTNUM 6 -#define CLAAMNAME 7 -#define CLAOID 8 -#define GRONAME 9 -#define GROSYSID 10 -#define INDEXRELID 11 -#define INHRELID 12 -#define LANGNAME 13 -#define LANGOID 14 -#define OPERNAME 15 -#define OPEROID 16 -#define PROCNAME 17 -#define PROCOID 18 -#define RELNAME 19 -#define RELOID 20 -#define RULENAME 21 -#define SHADOWNAME 22 -#define SHADOWSYSID 23 -#define STATRELATT 24 -#define TYPENAME 25 -#define TYPEOID 26 +#define AMOID 2 +#define AMOPOPID 3 +#define AMOPSTRATEGY 4 +#define AMPROCNUM 5 +#define ATTNAME 6 +#define ATTNUM 7 +#define CLAAMNAME 8 +#define CLAOID 9 +#define GRONAME 10 +#define GROSYSID 11 +#define INDEXRELID 12 +#define INHRELID 13 +#define LANGNAME 14 +#define LANGOID 15 +#define OPERNAME 16 +#define OPEROID 17 +#define PROCNAME 18 +#define PROCOID 19 +#define RELNAME 20 +#define RELOID 21 +#define RULENAME 22 +#define SHADOWNAME 23 +#define SHADOWSYSID 24 +#define STATRELATT 25 +#define TYPENAME 26 +#define TYPEOID 27 extern void InitCatalogCache(void); +extern void InitCatalogCachePhase2(void); extern HeapTuple SearchSysCache(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4);