1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +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/executor/execUtils.c,v 1.59 2000/06/15 04:09:52 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.60 2000/06/17 21:48:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,8 @@
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
static void ExecGetIndexKeyInfo(Form_pg_index indexTuple, int *numAttsOutP,
AttrNumber **attsOutP, FuncIndexInfoPtr fInfoP);
@ -657,7 +659,7 @@ ExecGetIndexKeyInfo(Form_pg_index indexTuple,
* check parameters
* ----------------
*/
if (numAttsOutP == NULL && attsOutP == NULL)
if (numAttsOutP == NULL || attsOutP == NULL)
{
elog(DEBUG, "ExecGetIndexKeyInfo: %s",
"invalid parameters: numAttsOutP and attsOutP must be non-NULL");
@ -724,115 +726,112 @@ ExecGetIndexKeyInfo(Form_pg_index indexTuple,
/* ----------------------------------------------------------------
* ExecOpenIndices
*
* Here we scan the pg_index relation to find indices
* associated with a given heap relation oid. Since we
* don't know in advance how many indices we have, we
* form lists containing the information we need from
* pg_index and then process these lists.
* Find the indices associated with a result relation, open them,
* and save information about them in the result RelationInfo.
*
* Note: much of this code duplicates effort done by
* the IndexCatalogInformation function in plancat.c
* because IndexCatalogInformation is poorly written.
* At entry, caller has already opened and locked
* resultRelationInfo->ri_RelationDesc.
*
* It would be much better if the functionality provided
* by this function and IndexCatalogInformation was
* in the form of a small set of orthogonal routines..
* If you are trying to understand this, I suggest you
* look at the code to IndexCatalogInformation and
* FormIndexTuple.. -cim 9/27/89
* This used to be horribly ugly code, and slow too because it
* did a sequential scan of pg_index. Now we rely on the relcache
* to cache a list of the OIDs of the indices associated with any
* specific relation, and we use the pg_index syscache to get the
* entries we need from pg_index.
* ----------------------------------------------------------------
*/
void
ExecOpenIndices(Oid resultRelationOid,
RelationInfo *resultRelationInfo)
ExecOpenIndices(RelationInfo *resultRelationInfo)
{
Relation indexRd;
HeapScanDesc indexSd;
ScanKeyData key;
HeapTuple tuple;
Form_pg_index indexStruct;
Oid indexOid;
List *oidList;
List *nkeyList;
List *keyList;
List *fiList;
char *predString;
List *predList;
List *indexoid;
List *numkeys;
List *indexkeys;
List *indexfuncs;
List *indexpreds;
int len;
Relation resultRelation = resultRelationInfo->ri_RelationDesc;
List *indexoidlist,
*indexoidscan;
int len,
i;
RelationPtr relationDescs;
IndexInfo **indexInfoArray;
FuncIndexInfoPtr fInfoP;
int numKeyAtts;
AttrNumber *indexKeyAtts;
PredInfo *predicate;
int i;
resultRelationInfo->ri_NumIndices = 0;
if (!RelationGetForm(resultRelationInfo->ri_RelationDesc)->relhasindex)
/* checks for disabled indexes */
if (! RelationGetForm(resultRelation)->relhasindex)
return;
if (IsIgnoringSystemIndexes() &&
IsSystemRelationName(RelationGetRelationName(resultRelationInfo->ri_RelationDesc)))
IsSystemRelationName(RelationGetRelationName(resultRelation)))
return;
/* ----------------
* open pg_index
* ----------------
*/
indexRd = heap_openr(IndexRelationName, AccessShareLock);
/* ----------------
* form a scan key
* Get cached list of index OIDs
* ----------------
*/
ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid,
F_OIDEQ,
ObjectIdGetDatum(resultRelationOid));
indexoidlist = RelationGetIndexList(resultRelation);
len = length(indexoidlist);
if (len == 0)
return;
/* ----------------
* scan the index relation, looking for indices for our
* result relation..
* allocate space for result arrays
* ----------------
*/
indexSd = heap_beginscan(indexRd, /* scan desc */
false, /* scan backward flag */
SnapshotNow, /* NOW snapshot */
1, /* number scan keys */
&key); /* scan keys */
relationDescs = (RelationPtr) palloc(len * sizeof(Relation));
indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *));
oidList = NIL;
nkeyList = NIL;
keyList = NIL;
fiList = NIL;
predList = NIL;
resultRelationInfo->ri_NumIndices = len;
resultRelationInfo->ri_IndexRelationDescs = relationDescs;
resultRelationInfo->ri_IndexRelationInfo = indexInfoArray;
while (HeapTupleIsValid(tuple = heap_getnext(indexSd, 0)))
/* ----------------
* For each index, open the index relation and save pg_index info.
* ----------------
*/
i = 0;
foreach(indexoidscan, indexoidlist)
{
Oid indexOid = lfirsti(indexoidscan);
Relation indexDesc;
HeapTuple indexTuple;
Form_pg_index indexStruct;
int numKeyAtts;
AttrNumber *indexKeyAtts;
FuncIndexInfoPtr fInfoP;
PredInfo *predicate;
IndexInfo *ii;
/* ----------------
* For each index relation we find, extract the information
* we need and store it in a list..
* Open (and lock, if necessary) the index relation
*
* first get the oid of the index relation from the tuple
* Hack for not btree and hash indices: they use relation
* level exclusive locking on update (i.e. - they are not
* ready for MVCC) and so we have to exclusively lock
* indices here to prevent deadlocks if we will scan them
* - index_beginscan places AccessShareLock, indices
* update methods don't use locks at all. We release this
* lock in ExecCloseIndices. Note, that hashes use page
* level locking - i.e. are not deadlock-free, - let's
* them be on their way -:)) vadim 03-12-1998
* ----------------
*/
indexStruct = (Form_pg_index) GETSTRUCT(tuple);
indexOid = indexStruct->indexrelid;
indexDesc = index_open(indexOid);
if (indexDesc->rd_rel->relam != BTREE_AM_OID &&
indexDesc->rd_rel->relam != HASH_AM_OID)
LockRelation(indexDesc, AccessExclusiveLock);
/* ----------------
* allocate space for functional index information.
* Get the pg_index tuple for the index
* ----------------
*/
indexTuple = SearchSysCacheTupleCopy(INDEXRELID,
ObjectIdGetDatum(indexOid),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple))
elog(ERROR, "ExecOpenIndices: index %u not found", indexOid);
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
/* ----------------
* extract the index key information from the tuple
* ----------------
*/
fInfoP = (FuncIndexInfoPtr) palloc(sizeof(*fInfoP));
/* ----------------
* next get the index key information from the tuple
* ----------------
*/
ExecGetIndexKeyInfo(indexStruct,
&numKeyAtts,
&indexKeyAtts,
@ -844,6 +843,8 @@ ExecOpenIndices(Oid resultRelationOid,
*/
if (VARSIZE(&indexStruct->indpred) != 0)
{
char *predString;
predString = textout(&indexStruct->indpred);
predicate = (PredInfo *) stringToNode(predString);
pfree(predString);
@ -851,152 +852,21 @@ ExecOpenIndices(Oid resultRelationOid,
else
predicate = NULL;
/* ----------------
* save the index information into lists
* ----------------
*/
oidList = lconsi(indexOid, oidList);
nkeyList = lconsi(numKeyAtts, nkeyList);
keyList = lcons(indexKeyAtts, keyList);
fiList = lcons(fInfoP, fiList);
predList = lcons(predicate, predList);
/* Save the index info */
ii = makeNode(IndexInfo);
ii->ii_NumKeyAttributes = numKeyAtts;
ii->ii_KeyAttributeNumbers = indexKeyAtts;
ii->ii_FuncIndexInfo = fInfoP;
ii->ii_Predicate = (Node *) predicate;
heap_freetuple(indexTuple);
relationDescs[i] = indexDesc;
indexInfoArray[i] = ii;
i++;
}
/* ----------------
* we have the info we need so close the pg_index relation..
* ----------------
*/
heap_endscan(indexSd);
heap_close(indexRd, AccessShareLock);
/* ----------------
* Now that we've collected the index information into three
* lists, we open the index relations and store the descriptors
* and the key information into arrays.
* ----------------
*/
len = length(oidList);
if (len > 0)
{
/* ----------------
* allocate space for relation descs
* ----------------
*/
CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
relationDescs = (RelationPtr)
palloc(len * sizeof(Relation));
/* ----------------
* initialize index info array
* ----------------
*/
CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
indexInfoArray = (IndexInfo **)
palloc(len * sizeof(IndexInfo *));
for (i = 0; i < len; i++)
{
IndexInfo *ii = makeNode(IndexInfo);
ii->ii_NumKeyAttributes = 0;
ii->ii_KeyAttributeNumbers = (AttrNumber *) NULL;
ii->ii_FuncIndexInfo = (FuncIndexInfoPtr) NULL;
ii->ii_Predicate = NULL;
indexInfoArray[i] = ii;
}
/* ----------------
* attempt to open each of the indices. If we succeed,
* then store the index relation descriptor into the
* relation descriptor array.
* ----------------
*/
i = 0;
foreach(indexoid, oidList)
{
Relation indexDesc;
indexOid = lfirsti(indexoid);
indexDesc = index_open(indexOid);
if (indexDesc != NULL)
{
relationDescs[i++] = indexDesc;
/*
* Hack for not btree and hash indices: they use relation
* level exclusive locking on update (i.e. - they are not
* ready for MVCC) and so we have to exclusively lock
* indices here to prevent deadlocks if we will scan them
* - index_beginscan places AccessShareLock, indices
* update methods don't use locks at all. We release this
* lock in ExecCloseIndices. Note, that hashes use page
* level locking - i.e. are not deadlock-free, - let's
* them be on their way -:)) vadim 03-12-1998
*/
if (indexDesc->rd_rel->relam != BTREE_AM_OID &&
indexDesc->rd_rel->relam != HASH_AM_OID)
LockRelation(indexDesc, AccessExclusiveLock);
}
}
/* ----------------
* store the relation descriptor array and number of
* descs into the result relation info.
* ----------------
*/
resultRelationInfo->ri_NumIndices = i;
resultRelationInfo->ri_IndexRelationDescs = relationDescs;
/* ----------------
* store the index key information collected in our
* lists into the index info array
* ----------------
*/
i = 0;
foreach(numkeys, nkeyList)
{
numKeyAtts = lfirsti(numkeys);
indexInfoArray[i++]->ii_NumKeyAttributes = numKeyAtts;
}
i = 0;
foreach(indexkeys, keyList)
{
indexKeyAtts = (AttrNumber *) lfirst(indexkeys);
indexInfoArray[i++]->ii_KeyAttributeNumbers = indexKeyAtts;
}
i = 0;
foreach(indexfuncs, fiList)
{
FuncIndexInfoPtr fiP = (FuncIndexInfoPtr) lfirst(indexfuncs);
indexInfoArray[i++]->ii_FuncIndexInfo = fiP;
}
i = 0;
foreach(indexpreds, predList)
indexInfoArray[i++]->ii_Predicate = lfirst(indexpreds);
/* ----------------
* store the index info array into relation info
* ----------------
*/
resultRelationInfo->ri_IndexRelationInfo = indexInfoArray;
}
/* ----------------
* All done, resultRelationInfo now contains complete information
* on the indices associated with the result relation.
* ----------------
*/
/* should free oidList, nkeyList and keyList here */
/* OK - let's do it -jolly */
freeList(oidList);
freeList(nkeyList);
freeList(keyList);
freeList(fiList);
freeList(predList);
freeList(indexoidlist);
}
/* ----------------------------------------------------------------
@ -1035,91 +905,6 @@ ExecCloseIndices(RelationInfo *resultRelationInfo)
*/
}
/* ----------------------------------------------------------------
* ExecFormIndexTuple
*
* Most of this code is cannabilized from DefaultBuild().
* As said in the comments for ExecOpenIndices, most of
* this functionality should be rearranged into a proper
* set of routines..
* ----------------------------------------------------------------
*/
#ifdef NOT_USED
IndexTuple
ExecFormIndexTuple(HeapTuple heapTuple,
Relation heapRelation,
Relation indexRelation,
IndexInfo *indexInfo)
{
IndexTuple indexTuple;
TupleDesc heapDescriptor;
TupleDesc indexDescriptor;
Datum *datum;
char *nulls;
int numberOfAttributes;
AttrNumber *keyAttributeNumbers;
FuncIndexInfoPtr fInfoP;
/* ----------------
* get information from index info structure
* ----------------
*/
numberOfAttributes = indexInfo->ii_NumKeyAttributes;
keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers;
fInfoP = indexInfo->ii_FuncIndexInfo;
/* ----------------
* datum and null are arrays in which we collect the index attributes
* when forming a new index tuple.
* ----------------
*/
CXT1_printf("ExecFormIndexTuple: context is %d\n", CurrentMemoryContext);
datum = (Datum *) palloc(numberOfAttributes * sizeof *datum);
nulls = (char *) palloc(numberOfAttributes * sizeof *nulls);
/* ----------------
* get the tuple descriptors from the relations so we know
* how to form the index tuples..
* ----------------
*/
heapDescriptor = RelationGetDescr(heapRelation);
indexDescriptor = RelationGetDescr(indexRelation);
/* ----------------
* FormIndexDatum fills in its datum and null parameters
* with attribute information taken from the given heap tuple.
* ----------------
*/
FormIndexDatum(numberOfAttributes, /* num attributes */
keyAttributeNumbers, /* array of att nums to extract */
heapTuple, /* tuple from base relation */
heapDescriptor, /* heap tuple's descriptor */
datum, /* return: array of attributes */
nulls, /* return: array of char's */
fInfoP); /* functional index information */
indexTuple = index_formtuple(indexDescriptor,
datum,
nulls);
/* ----------------
* free temporary arrays
*
* XXX should store these in the IndexInfo instead of allocating
* and freeing on every insertion, but efficency here is not
* that important and FormIndexTuple is wasteful anyways..
* -cim 9/27/89
* ----------------
*/
pfree(nulls);
pfree(datum);
return indexTuple;
}
#endif
/* ----------------------------------------------------------------
* ExecInsertIndexTuples
*