1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-13 07:41:39 +03:00

Fix performance problems with pg_index lookups (see, for example,

discussion of 5/19/00).  pg_index is now searched for indexes of a
relation using an indexscan.  Moreover, this is done once and cached
in the relcache entry for the relation, in the form of a list of OIDs
for the indexes.  This list is used by the parser and executor to drive
lookups in the pg_index syscache when they want to know the properties
of the indexes.  Net result: index information will be fully cached
for repetitive operations such as inserts.
This commit is contained in:
Tom Lane
2000-06-17 21:49:04 +00:00
parent 9cf80f2f55
commit d03a933ec5
13 changed files with 389 additions and 525 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.100 2000/06/17 04:56:32 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.101 2000/06/17 21:48:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -46,6 +46,7 @@
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_index.h"
#include "catalog/pg_log.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_relcheck.h"
@ -1063,16 +1064,14 @@ formrdesc(char *relationName,
FormData_pg_attribute *att)
{
Relation relation;
Size len;
u_int i;
/* ----------------
* allocate new relation desc
* ----------------
*/
len = sizeof(RelationData);
relation = (Relation) palloc(len);
MemSet((char *) relation, 0, len);
relation = (Relation) palloc(sizeof(RelationData));
MemSet((char *) relation, 0, sizeof(RelationData));
/* ----------------
* don't open the unix file yet..
@ -1090,9 +1089,8 @@ formrdesc(char *relationName,
* initialize relation tuple form
* ----------------
*/
relation->rd_rel = (Form_pg_class)
palloc((Size) (sizeof(*relation->rd_rel)));
MemSet(relation->rd_rel, 0, sizeof(FormData_pg_class));
relation->rd_rel = (Form_pg_class) palloc(CLASS_TUPLE_SIZE);
MemSet(relation->rd_rel, 0, CLASS_TUPLE_SIZE);
strcpy(RelationGetPhysicalRelationName(relation), relationName);
/* ----------------
@ -1414,6 +1412,7 @@ RelationClearRelation(Relation relation, bool rebuildIt)
pfree(relation->rd_istrat);
if (relation->rd_support)
pfree(relation->rd_support);
freeList(relation->rd_indexlist);
/*
* If we're really done with the relcache entry, blow it away. But if
@ -2075,6 +2074,125 @@ RelCheckFetch(Relation relation)
heap_close(rcrel, AccessShareLock);
}
/*
* RelationGetIndexList -- get a list of OIDs of indexes on this relation
*
* The index list is created only if someone requests it. We scan pg_index
* to find relevant indexes, and add the list to the relcache entry so that
* we won't have to compute it again. Note that shared cache inval of a
* relcache entry will delete the old list and set rd_indexfound to false,
* so that we must recompute the index list on next request. This handles
* creation or deletion of an index.
*
* Since shared cache inval causes the relcache's copy of the list to go away,
* we return a copy of the list palloc'd in the caller's context. The caller
* may freeList() the returned list after scanning it. This is necessary
* since the caller will typically be doing syscache lookups on the relevant
* indexes, and syscache lookup could cause SI messages to be processed!
*/
List *
RelationGetIndexList(Relation relation)
{
Relation indrel;
Relation irel = (Relation) NULL;
ScanKeyData skey;
IndexScanDesc sd = (IndexScanDesc) NULL;
HeapScanDesc hscan = (HeapScanDesc) NULL;
bool hasindex;
List *result;
MemoryContext oldcxt;
/* Quick exit if we already computed the list. */
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 copy that
* list into the relcache entry. This avoids cache-context memory leakage
* if we get some sort of error partway through.
*/
result = NIL;
for (;;)
{
HeapTupleData tuple;
HeapTuple htup;
Buffer buffer;
Form_pg_index index;
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);
pfree(indexRes);
if (tuple.t_data == NULL)
continue;
htup = &tuple;
}
else
{
htup = heap_getnext(hscan, 0);
if (!HeapTupleIsValid(htup))
break;
}
index = (Form_pg_index) GETSTRUCT(htup);
result = lappendi(result, index->indexrelid);
if (hasindex)
ReleaseBuffer(buffer);
}
if (hasindex)
{
index_endscan(sd);
index_close(irel);
}
else
heap_endscan(hscan);
heap_close(indrel, AccessShareLock);
/* Now we can save the completed list in the relcache entry. */
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
relation->rd_indexlist = listCopy(result);
relation->rd_indexfound = true;
MemoryContextSwitchTo(oldcxt);
return result;
}
/*
* init_irels(), write_irels() -- handle special-case initialization of
* index relation descriptors.