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:
@ -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
|
||||
*
|
||||
|
Reference in New Issue
Block a user