diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index b21f408e2cb..755a7512723 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.116 2000/06/17 04:56:36 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.117 2000/06/17 21:48:39 tgl Exp $ * * * INTERFACE ROUTINES @@ -1105,6 +1105,7 @@ index_create(char *heapRelationName, void index_drop(Oid indexId) { + Oid heapId; Relation userHeapRelation; Relation userIndexRelation; Relation indexRelation; @@ -1125,8 +1126,8 @@ index_drop(Oid indexId) * else other backends will still see this index in pg_index. * ---------------- */ - userHeapRelation = heap_open(IndexGetRelation(indexId), - AccessExclusiveLock); + heapId = IndexGetRelation(indexId); + userHeapRelation = heap_open(heapId, AccessExclusiveLock); userIndexRelation = index_open(indexId); LockRelation(userIndexRelation, AccessExclusiveLock); @@ -1158,6 +1159,7 @@ index_drop(Oid indexId) */ relationRelation = heap_openr(RelationRelationName, RowExclusiveLock); + /* Remove the pg_class tuple for the index itself */ tuple = SearchSysCacheTupleCopy(RELOID, ObjectIdGetDatum(indexId), 0, 0, 0); @@ -1166,6 +1168,23 @@ index_drop(Oid indexId) heap_delete(relationRelation, &tuple->t_self, NULL); heap_freetuple(tuple); + + /* + * Find the pg_class tuple for the owning relation. We do not attempt + * to clear relhasindex, since we are too lazy to test whether any other + * indexes remain (the next VACUUM will fix it if necessary). But we + * must send out a shared-cache-inval notice on the owning relation + * to ensure other backends update their relcache lists of indexes. + */ + tuple = SearchSysCacheTupleCopy(RELOID, + ObjectIdGetDatum(heapId), + 0, 0, 0); + + Assert(HeapTupleIsValid(tuple)); + + ImmediateInvalidateSharedHeapTuple(relationRelation, tuple); + heap_freetuple(tuple); + heap_close(relationRelation, RowExclusiveLock); /* ---------------- @@ -1447,9 +1466,6 @@ setRelhasindexInplace(Oid relid, bool hasindex, bool immediate) */ if (pg_class_scan) { - - if (!IsBootstrapProcessingMode()) - ImmediateInvalidateSharedHeapTuple(pg_class, tuple); rd_rel = (Form_pg_class) GETSTRUCT(tuple); rd_rel->relhasindex = hasindex; WriteNoReleaseBuffer(pg_class_scan->rs_cbuf); @@ -1461,12 +1477,18 @@ setRelhasindexInplace(Oid relid, bool hasindex, bool immediate) htup.t_self = tuple->t_self; heap_fetch(pg_class, SnapshotNow, &htup, &buffer); - ImmediateInvalidateSharedHeapTuple(pg_class, tuple); rd_rel = (Form_pg_class) GETSTRUCT(&htup); rd_rel->relhasindex = hasindex; WriteBuffer(buffer); } + /* + * Send out a shared-cache-inval message so other backends notice the + * update and fix their syscaches/relcaches. + */ + if (!IsBootstrapProcessingMode()) + ImmediateInvalidateSharedHeapTuple(pg_class, tuple); + if (!pg_class_scan) heap_freetuple(tuple); else diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index db20ca7372f..e29ed167963 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.114 2000/06/15 03:32:07 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.115 2000/06/17 21:48:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +32,7 @@ #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/relcache.h" #include "utils/syscache.h" #ifdef MULTIBYTE @@ -1081,78 +1082,43 @@ IsTypeByVal(Oid type) * Space for the array itself is palloc'ed. */ -typedef struct rel_list -{ - Oid index_rel_oid; - struct rel_list *next; -} RelationList; - static void GetIndexRelations(Oid main_relation_oid, int *n_indices, Relation **index_rels) { - RelationList *head, - *scan; - Relation pg_index_rel; - HeapScanDesc scandesc; - Oid index_relation_oid; - HeapTuple tuple; - TupleDesc tupDesc; + Relation relation; + List *indexoidlist, + *indexoidscan; int i; - bool isnull; - pg_index_rel = heap_openr(IndexRelationName, AccessShareLock); - scandesc = heap_beginscan(pg_index_rel, 0, SnapshotNow, 0, NULL); - tupDesc = RelationGetDescr(pg_index_rel); + relation = heap_open(main_relation_oid, AccessShareLock); + indexoidlist = RelationGetIndexList(relation); - *n_indices = 0; + *n_indices = length(indexoidlist); - head = (RelationList *) palloc(sizeof(RelationList)); - scan = head; - head->next = NULL; + if (*n_indices > 0) + *index_rels = (Relation *) palloc(*n_indices * sizeof(Relation)); + else + *index_rels = NULL; - while (HeapTupleIsValid(tuple = heap_getnext(scandesc, 0))) + i = 0; + foreach(indexoidscan, indexoidlist) { + Oid indexoid = lfirsti(indexoidscan); + Relation index = index_open(indexoid); - index_relation_oid = (Oid) DatumGetInt32(heap_getattr(tuple, 2, - tupDesc, &isnull)); - if (index_relation_oid == main_relation_oid) - { - scan->index_rel_oid = (Oid) DatumGetInt32(heap_getattr(tuple, - Anum_pg_index_indexrelid, - tupDesc, &isnull)); - (*n_indices)++; - scan->next = (RelationList *) palloc(sizeof(RelationList)); - scan = scan->next; - } - } - - heap_endscan(scandesc); - heap_close(pg_index_rel, AccessShareLock); - - /* We cannot trust to relhasindex of the main_relation now, so... */ - if (*n_indices == 0) - return; - - *index_rels = (Relation *) palloc(*n_indices * sizeof(Relation)); - - for (i = 0, scan = head; i < *n_indices; i++, scan = scan->next) - { - (*index_rels)[i] = index_open(scan->index_rel_oid); /* see comments in ExecOpenIndices() in execUtils.c */ - if ((*index_rels)[i] != NULL && - ((*index_rels)[i])->rd_rel->relam != BTREE_AM_OID && - ((*index_rels)[i])->rd_rel->relam != HASH_AM_OID) - LockRelation((*index_rels)[i], AccessExclusiveLock); + if (index != NULL && + index->rd_rel->relam != BTREE_AM_OID && + index->rd_rel->relam != HASH_AM_OID) + LockRelation(index, AccessExclusiveLock); + (*index_rels)[i] = index; + i++; } - for (i = 0, scan = head; i < *n_indices + 1; i++) - { - scan = head->next; - pfree(head); - head = scan; - } + freeList(indexoidlist); + heap_close(relation, AccessShareLock); } /* diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 31c96c7d6ab..f0d61aa1123 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.29 2000/06/15 03:32:07 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.30 2000/06/17 21:48:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -221,6 +221,13 @@ DefineIndex(char *heapRelationName, lossy, unique, primary); } + /* + * We update the relation's pg_class tuple even if it already has + * relhasindex = true. This is needed to cause a shared-cache-inval + * message to be sent for the pg_class tuple, which will cause other + * backends to flush their relcache entries and in particular their + * cached lists of the indexes for this relation. + */ setRelhasindexInplace(relationId, true, false); } diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 1ee4e28f210..5eed27387a7 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.159 2000/05/29 17:40:43 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.160 2000/06/17 21:48:43 tgl Exp $ * *------------------------------------------------------------------------- @@ -72,7 +72,7 @@ static void update_relstats(Oid relid, int num_pages, int num_tuples, bool hasin static VacPage tid_reaped(ItemPointer itemptr, VacPageList vacpagelist); static void reap_page(VacPageList vacpagelist, VacPage vacpage); static void vpage_insert(VacPageList vacpagelist, VacPage vpnew); -static void get_indices(Oid relid, int *nindices, Relation **Irel); +static void get_indices(Relation relation, int *nindices, Relation **Irel); static void close_indices(int nindices, Relation *Irel); static void get_index_desc(Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc); static void *vac_find_eq(void *bot, int nelem, int size, void *elm, @@ -416,7 +416,7 @@ vacuum_rel(Oid relid, bool analyze) /* Now open indices */ nindices = 0; Irel = (Relation *) NULL; - get_indices(vacrelstats->relid, &nindices, &Irel); + get_indices(onerel, &nindices, &Irel); if (!Irel) reindex = false; else if (!RelationGetForm(onerel)->relhasindex) @@ -2331,80 +2331,33 @@ CommonSpecialPortalIsOpen(void) return CommonSpecialPortalInUse; } + static void -get_indices(Oid relid, int *nindices, Relation **Irel) +get_indices(Relation relation, int *nindices, Relation **Irel) { - Relation pgindex; - Relation irel; - TupleDesc tupdesc; - HeapTuple tuple; - HeapScanDesc scan; - Datum d; - int i, - k; - bool n; - ScanKeyData key; - Oid *ioid; + List *indexoidlist, + *indexoidscan; + int i; - *nindices = i = 0; + indexoidlist = RelationGetIndexList(relation); - ioid = (Oid *) palloc(10 * sizeof(Oid)); + *nindices = length(indexoidlist); - /* prepare a heap scan on the pg_index relation */ - pgindex = heap_openr(IndexRelationName, AccessShareLock); - tupdesc = RelationGetDescr(pgindex); + if (*nindices > 0) + *Irel = (Relation *) palloc(*nindices * sizeof(Relation)); + else + *Irel = NULL; - ScanKeyEntryInitialize(&key, 0x0, Anum_pg_index_indrelid, - F_OIDEQ, - ObjectIdGetDatum(relid)); - - scan = heap_beginscan(pgindex, false, SnapshotNow, 1, &key); - - while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) + i = 0; + foreach(indexoidscan, indexoidlist) { - d = heap_getattr(tuple, Anum_pg_index_indexrelid, - tupdesc, &n); + Oid indexoid = lfirsti(indexoidscan); + + (*Irel)[i] = index_open(indexoid); i++; - if (i % 10 == 0) - ioid = (Oid *) repalloc(ioid, (i + 10) * sizeof(Oid)); - ioid[i - 1] = DatumGetObjectId(d); - } - - heap_endscan(scan); - heap_close(pgindex, AccessShareLock); - - if (i == 0) - { /* No one index found */ - pfree(ioid); - return; - } - - if (Irel != (Relation **) NULL) - *Irel = (Relation *) palloc(i * sizeof(Relation)); - - for (k = 0; i > 0;) - { - irel = index_open(ioid[--i]); - if (irel != (Relation) NULL) - { - if (Irel != (Relation **) NULL) - (*Irel)[k] = irel; - else - index_close(irel); - k++; - } - else - elog(NOTICE, "CAN'T OPEN INDEX %u - SKIP IT", ioid[i]); - } - *nindices = k; - pfree(ioid); - - if (Irel != (Relation **) NULL && *nindices == 0) - { - pfree(*Irel); - *Irel = (Relation *) NULL; } + freeList(indexoidlist); } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 8fbb4be6cb0..f80fe9abab7 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -27,7 +27,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.117 2000/06/15 04:09:50 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.118 2000/06/17 21:48:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -735,7 +735,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) */ if (resultRelationDesc->rd_rel->relhasindex && operation != CMD_DELETE) - ExecOpenIndices(resultRelationOid, resultRelationInfo); + ExecOpenIndices(resultRelationInfo); estate->es_result_relation_info = resultRelationInfo; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 852ff5872ce..c1b6e5d79a3 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.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 * diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 51e7d4027e6..5e34e806e32 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.33 2000/06/15 04:09:52 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.34 2000/06/17 21:48:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -285,7 +285,7 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent) * indices, but how to tell that here? */ if (rri->ri_RelationDesc->rd_rel->relhasindex) - ExecOpenIndices(reloid, rri); + ExecOpenIndices(rri); } /* diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 12b2f26bd84..64b47072f71 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -9,11 +9,7 @@ * * * IDENTIFICATION -<<<<<<< plancat.c - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.56 2000/06/15 03:32:16 momjian Exp $ -======= - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.56 2000/06/15 03:32:16 momjian Exp $ ->>>>>>> 1.53 + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.57 2000/06/17 21:48:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +28,7 @@ #include "parser/parsetree.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/relcache.h" #include "utils/syscache.h" #include "catalog/catalog.h" #include "miscadmin.h" @@ -54,7 +51,7 @@ relation_info(Query *root, Index relid, Form_pg_class relation; relationTuple = SearchSysCacheTuple(RELOID, - ObjectIdGetDatum(relationObjectId), + ObjectIdGetDatum(relationObjectId), 0, 0, 0); if (!HeapTupleIsValid(relationTuple)) elog(ERROR, "relation_info: Relation %u not found", @@ -81,33 +78,40 @@ relation_info(Query *root, Index relid, List * find_secondary_indexes(Query *root, Index relid) { - List *indexes = NIL; + List *indexinfos = NIL; + List *indexoidlist, + *indexoidscan; Oid indrelid = getrelid(relid, root->rtable); Relation relation; - HeapScanDesc scan; - ScanKeyData indexKey; - HeapTuple indexTuple; - /* Scan pg_index for tuples describing indexes of this rel */ - relation = heap_openr(IndexRelationName, AccessShareLock); + /* + * We used to scan pg_index directly, but now the relcache offers + * a cached list of OID indexes for each relation. So, get that list + * and then use the syscache to obtain pg_index entries. + */ + relation = heap_open(indrelid, AccessShareLock); + indexoidlist = RelationGetIndexList(relation); - ScanKeyEntryInitialize(&indexKey, 0, - Anum_pg_index_indrelid, - F_OIDEQ, - ObjectIdGetDatum(indrelid)); - - scan = heap_beginscan(relation, 0, SnapshotNow, - 1, &indexKey); - - while (HeapTupleIsValid(indexTuple = heap_getnext(scan, 0))) + foreach(indexoidscan, indexoidlist) { - Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple); - IndexOptInfo *info = makeNode(IndexOptInfo); + Oid indexoid = lfirsti(indexoidscan); + HeapTuple indexTuple; + Form_pg_index index; + IndexOptInfo *info; int i; Relation indexRelation; Oid relam; uint16 amorderstrategy; + indexTuple = SearchSysCacheTupleCopy(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); + info = makeNode(IndexOptInfo); + /* * Need to make these arrays large enough to be sure there is a * terminating 0 at the end of each one. @@ -172,13 +176,17 @@ find_secondary_indexes(Query *root, Index relid) } } - indexes = lcons(info, indexes); + heap_freetuple(indexTuple); + + indexinfos = lcons(info, indexinfos); } - heap_endscan(scan); + freeList(indexoidlist); + + /* XXX keep the lock here? */ heap_close(relation, AccessShareLock); - return indexes; + return indexinfos; } /* diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index dbd8faf319b..8e23170ac8a 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.147 2000/06/12 19:40:40 momjian Exp $ + * $Id: analyze.c,v 1.148 2000/06/17 21:48:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,8 @@ #include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/relcache.h" +#include "utils/syscache.h" void CheckSelectForUpdate(Query *qry); /* no points for style... */ @@ -2003,13 +2005,9 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint) { Relation pkrel; Form_pg_attribute *pkrel_attrs; - Relation indexRd; - HeapScanDesc indexSd; - ScanKeyData key; - HeapTuple indexTup; + List *indexoidlist, + *indexoidscan; Form_pg_index indexStruct = NULL; - Ident *pkattr; - int pkattno; int i; /* ---------- @@ -2023,37 +2021,37 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint) pkrel_attrs = pkrel->rd_att->attrs; /* ---------- - * Open pg_index and begin a scan for all indices defined on - * the referenced table + * Get the list of index OIDs for the table from the relcache, + * and look up each one in the pg_index syscache until we find one + * marked primary key (hopefully there isn't more than one such). * ---------- */ - indexRd = heap_openr(IndexRelationName, AccessShareLock); - ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid, - F_OIDEQ, - ObjectIdGetDatum(pkrel->rd_id)); - indexSd = heap_beginscan(indexRd, /* scan desc */ - false, /* scan backward flag */ - SnapshotNow, /* NOW snapshot */ - 1, /* number scan keys */ - &key); /* scan keys */ + indexoidlist = RelationGetIndexList(pkrel); - /* ---------- - * Fetch the index with indisprimary == true - * ---------- - */ - while (HeapTupleIsValid(indexTup = heap_getnext(indexSd, 0))) + foreach(indexoidscan, indexoidlist) { - indexStruct = (Form_pg_index) GETSTRUCT(indexTup); + Oid indexoid = lfirsti(indexoidscan); + HeapTuple indexTuple; + indexTuple = SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(indexoid), + 0, 0, 0); + if (!HeapTupleIsValid(indexTuple)) + elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found", + indexoid); + indexStruct = (Form_pg_index) GETSTRUCT(indexTuple); if (indexStruct->indisprimary) break; + indexStruct = NULL; } + freeList(indexoidlist); + /* ---------- * Check that we found it * ---------- */ - if (!HeapTupleIsValid(indexTup)) + if (indexStruct == NULL) elog(ERROR, "PRIMARY KEY for referenced table \"%s\" not found", fkconstraint->pktable_name); @@ -2064,8 +2062,9 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint) */ for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++) { - pkattno = indexStruct->indkey[i]; - pkattr = (Ident *) makeNode(Ident); + int pkattno = indexStruct->indkey[i]; + Ident *pkattr = makeNode(Ident); + pkattr->name = nameout(&(pkrel_attrs[pkattno - 1]->attname)); pkattr->indirection = NIL; pkattr->isRel = false; @@ -2073,12 +2072,6 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint) fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs, pkattr); } - /* ---------- - * End index scan and close relations - * ---------- - */ - heap_endscan(indexSd); - heap_close(indexRd, AccessShareLock); heap_close(pkrel, AccessShareLock); } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index f86f4eef778..4fe3e35b1e8 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.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. diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 3c85762dfaf..6d196eb5a2c 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: executor.h,v 1.43 2000/05/29 01:59:11 tgl Exp $ + * $Id: executor.h,v 1.44 2000/06/17 21:48:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -142,8 +142,7 @@ extern void ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate); extern Form_pg_attribute ExecGetTypeInfo(Relation relDesc); -extern void ExecOpenIndices(Oid resultRelationOid, - RelationInfo *resultRelationInfo); +extern void ExecOpenIndices(RelationInfo *resultRelationInfo); extern void ExecCloseIndices(RelationInfo *resultRelationInfo); extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, EState *estate, bool is_update); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 1bd2f10376e..2a585c0e7ea 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: rel.h,v 1.36 2000/04/12 17:16:55 momjian Exp $ + * $Id: rel.h,v 1.37 2000/06/17 21:49:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -92,14 +92,15 @@ typedef struct RelationData bool rd_isnailed; /* rel is nailed in cache */ bool rd_isnoname; /* rel has no name */ bool rd_unlinked; /* rel already unlinked or not created yet */ + bool rd_indexfound; /* true if rd_indexlist is valid */ 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 for locking - * relation */ + 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 */ - IndexStrategy rd_istrat; + IndexStrategy rd_istrat; /* info needed if rel is an index */ RegProcedure *rd_support; TriggerDesc *trigdesc; /* Trigger info, or NULL if rel has none */ } RelationData; @@ -138,13 +139,15 @@ typedef Relation *RelationPtr; * RelationSetReferenceCount * Sets relation reference count. */ -#define RelationSetReferenceCount(relation,count) ((relation)->rd_refcnt = (count)) +#define RelationSetReferenceCount(relation,count) \ + ((relation)->rd_refcnt = (count)) /* * RelationIncrementReferenceCount * Increments relation reference count. */ -#define RelationIncrementReferenceCount(relation) ((relation)->rd_refcnt += 1) +#define RelationIncrementReferenceCount(relation) \ + ((relation)->rd_refcnt += 1) /* * RelationDecrementReferenceCount @@ -199,7 +202,8 @@ typedef Relation *RelationPtr; * * Returns a Relation Name */ -#define RelationGetPhysicalRelationName(relation) (NameStr((relation)->rd_rel->relname)) +#define RelationGetPhysicalRelationName(relation) \ + (NameStr((relation)->rd_rel->relname)) /* * RelationGetNumberOfAttributes @@ -224,9 +228,11 @@ typedef Relation *RelationPtr; */ #define RelationGetIndexStrategy(relation) ((relation)->rd_istrat) - +/* + * Routines in utils/cache/rel.c + */ extern void RelationSetIndexSupport(Relation relation, - IndexStrategy strategy, - RegProcedure *support); + IndexStrategy strategy, + RegProcedure *support); #endif /* REL_H */ diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 073c846e4b3..aee58942a64 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: relcache.h,v 1.19 2000/01/31 04:35:57 tgl Exp $ + * $Id: relcache.h,v 1.20 2000/06/17 21:49:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,13 +19,20 @@ /* * relation lookup routines */ -extern Relation RelationIdCacheGetRelation(Oid relationId); extern Relation RelationIdGetRelation(Oid relationId); extern Relation RelationNameGetRelation(const char *relationName); +/* finds an existing cache entry, but won't make a new one */ +extern Relation RelationIdCacheGetRelation(Oid relationId); + extern void RelationClose(Relation relation); extern void RelationForgetRelation(Oid rid); +/* + * Routines to compute/retrieve additional cached information + */ +extern List *RelationGetIndexList(Relation relation); + /* * Routines for flushing/rebuilding relcache entries in various scenarios */