diff options
Diffstat (limited to 'src/backend/utils/cache/relcache.c')
-rw-r--r-- | src/backend/utils/cache/relcache.c | 209 |
1 files changed, 173 insertions, 36 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 6ab9427dfdb..14899e2968b 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.184 2003/02/09 06:56:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.185 2003/05/28 16:03:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -52,6 +52,8 @@ #include "catalog/pg_type.h" #include "commands/trigger.h" #include "miscadmin.h" +#include "optimizer/clauses.h" +#include "optimizer/planmain.h" #include "storage/smgr.h" #include "utils/builtins.h" #include "utils/catcache.h" @@ -939,10 +941,9 @@ void RelationInitIndexAccessInfo(Relation relation) { HeapTuple tuple; - Size iformsize; - Form_pg_index iform; Form_pg_am aform; MemoryContext indexcxt; + MemoryContext oldcontext; IndexStrategy strategy; Oid *operator; RegProcedure *support; @@ -952,8 +953,9 @@ RelationInitIndexAccessInfo(Relation relation) uint16 amsupport; /* - * Make a copy of the pg_index entry for the index. Note that this is - * a variable-length tuple. + * Make a copy of the pg_index entry for the index. Since pg_index + * contains variable-length and possibly-null fields, we have to do + * this honestly rather than just treating it as a Form_pg_index struct. */ tuple = SearchSysCache(INDEXRELID, ObjectIdGetDatum(RelationGetRelid(relation)), @@ -961,11 +963,11 @@ RelationInitIndexAccessInfo(Relation relation) 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); + oldcontext = MemoryContextSwitchTo(CacheMemoryContext); + relation->rd_indextuple = heap_copytuple(tuple); + relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple); + MemoryContextSwitchTo(oldcontext); ReleaseSysCache(tuple); - relation->rd_index = iform; /* * Make a copy of the pg_am entry for the index's access method @@ -982,6 +984,9 @@ RelationInitIndexAccessInfo(Relation relation) relation->rd_am = aform; natts = relation->rd_rel->relnatts; + if (natts != relation->rd_index->indnatts) + elog(ERROR, "RelationInitIndexAccessInfo: relnatts disagrees with indnatts for index %u", + RelationGetRelid(relation)); amstrategies = aform->amstrategies; amsupport = aform->amsupport; @@ -1047,9 +1052,15 @@ RelationInitIndexAccessInfo(Relation relation) * Fill the strategy map and the support RegProcedure arrays. * (supportinfo is left as zeroes, and is filled on-the-fly when used) */ - IndexSupportInitialize(iform, + IndexSupportInitialize(relation->rd_index, strategy, operator, support, amstrategies, amsupport, natts); + + /* + * expressions and predicate cache will be filled later + */ + relation->rd_indexprs = NIL; + relation->rd_indpred = NIL; } /* @@ -1087,8 +1098,7 @@ IndexSupportInitialize(Form_pg_index iform, { OpClassCacheEnt *opcentry; - if (iform->indkey[attIndex] == InvalidAttrNumber || - !OidIsValid(iform->indclass[attIndex])) + if (!OidIsValid(iform->indclass[attIndex])) elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple"); /* look up the info for this opclass, using a cache */ @@ -1729,8 +1739,8 @@ RelationClearRelation(Relation relation, bool rebuild) * with, we can only get rid of these fields: */ FreeTriggerDesc(relation->trigdesc); - if (relation->rd_index) - pfree(relation->rd_index); + if (relation->rd_indextuple) + pfree(relation->rd_indextuple); if (relation->rd_am) pfree(relation->rd_am); if (relation->rd_rel) @@ -2659,6 +2669,136 @@ insert_ordered_oid(List *list, Oid datum) return list; } +/* + * RelationGetIndexExpressions -- get the index expressions for an index + * + * We cache the result of transforming pg_index.indexprs into a node tree. + * If the rel is not an index or has no expressional columns, we return NIL. + * Otherwise, the returned tree is copied into the caller's memory context. + * (We don't want to return a pointer to the relcache copy, since it could + * disappear due to relcache invalidation.) + */ +List * +RelationGetIndexExpressions(Relation relation) +{ + List *result; + Datum exprsDatum; + bool isnull; + char *exprsString; + MemoryContext oldcxt; + + /* Quick exit if we already computed the result. */ + if (relation->rd_indexprs) + return (List *) copyObject(relation->rd_indexprs); + + /* Quick exit if there is nothing to do. */ + if (relation->rd_indextuple == NULL || + heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs)) + return NIL; + + /* + * We build the tree we intend to return in the caller's context. + * After successfully completing the work, we copy it into the relcache + * entry. This avoids problems if we get some sort of + * error partway through. + * + * We make use of the syscache's copy of pg_index's tupledesc + * to access the non-fixed fields of the tuple. We assume that + * the syscache will be initialized before any access of a + * partial index could occur. (This would probably fail if we + * were to allow partial indexes on system catalogs.) + */ + exprsDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple, + Anum_pg_index_indexprs, &isnull); + Assert(!isnull); + exprsString = DatumGetCString(DirectFunctionCall1(textout, exprsDatum)); + result = (List *) stringToNode(exprsString); + pfree(exprsString); + + /* + * Run the expressions through eval_const_expressions. This is not just + * an optimization, but is necessary, because the planner will be + * comparing them to const-folded qual clauses, and may fail to detect + * valid matches without this. + */ + result = (List *) eval_const_expressions((Node *) result); + + /* May as well fix opfuncids too */ + fix_opfuncids((Node *) result); + + /* Now save a copy of the completed tree in the relcache entry. */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + relation->rd_indexprs = (List *) copyObject(result); + MemoryContextSwitchTo(oldcxt); + + return result; +} + +/* + * RelationGetIndexPredicate -- get the index predicate for an index + * + * We cache the result of transforming pg_index.indpred into a node tree. + * If the rel is not an index or has no predicate, we return NIL. + * Otherwise, the returned tree is copied into the caller's memory context. + * (We don't want to return a pointer to the relcache copy, since it could + * disappear due to relcache invalidation.) + */ +List * +RelationGetIndexPredicate(Relation relation) +{ + List *result; + Datum predDatum; + bool isnull; + char *predString; + MemoryContext oldcxt; + + /* Quick exit if we already computed the result. */ + if (relation->rd_indpred) + return (List *) copyObject(relation->rd_indpred); + + /* Quick exit if there is nothing to do. */ + if (relation->rd_indextuple == NULL || + heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred)) + return NIL; + + /* + * We build the tree we intend to return in the caller's context. + * After successfully completing the work, we copy it into the relcache + * entry. This avoids problems if we get some sort of + * error partway through. + * + * We make use of the syscache's copy of pg_index's tupledesc + * to access the non-fixed fields of the tuple. We assume that + * the syscache will be initialized before any access of a + * partial index could occur. (This would probably fail if we + * were to allow partial indexes on system catalogs.) + */ + predDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple, + Anum_pg_index_indpred, &isnull); + Assert(!isnull); + predString = DatumGetCString(DirectFunctionCall1(textout, predDatum)); + result = (List *) stringToNode(predString); + pfree(predString); + + /* + * Run the expression through eval_const_expressions. This is not just + * an optimization, but is necessary, because the planner will be + * comparing it to const-folded qual clauses, and may fail to detect + * valid matches without this. + */ + result = (List *) eval_const_expressions((Node *) result); + + /* May as well fix opfuncids too */ + fix_opfuncids((Node *) result); + + /* Now save a copy of the completed tree in the relcache entry. */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + relation->rd_indpred = (List *) copyObject(result); + MemoryContextSwitchTo(oldcxt); + + return result; +} + /* * load_relcache_init_file, write_relcache_init_file @@ -2829,14 +2969,19 @@ load_relcache_init_file(void) if (rel->rd_isnailed) nailed_indexes++; - /* next, read the pg_index tuple form */ + /* next, read the pg_index tuple */ if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) goto read_failed; - rel->rd_index = (Form_pg_index) palloc(len); - if ((nread = fread(rel->rd_index, 1, len, fp)) != len) + rel->rd_indextuple = (HeapTuple) palloc(len); + if ((nread = fread(rel->rd_indextuple, 1, len, fp)) != len) goto read_failed; + /* Fix up internal pointers in the tuple -- see heap_copytuple */ + rel->rd_indextuple->t_datamcxt = CurrentMemoryContext; + rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE); + rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple); + /* next, read the access method tuple form */ if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) goto read_failed; @@ -2904,6 +3049,7 @@ load_relcache_init_file(void) nailed_rels++; Assert(rel->rd_index == NULL); + Assert(rel->rd_indextuple == NULL); Assert(rel->rd_am == NULL); Assert(rel->rd_indexcxt == NULL); Assert(rel->rd_istrat == NULL); @@ -2917,11 +3063,13 @@ load_relcache_init_file(void) * 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. + * have such. Ditto for index expressions and predicates. */ rel->rd_rules = NULL; rel->rd_rulescxt = NULL; rel->trigdesc = NULL; + rel->rd_indexprs = NIL; + rel->rd_indpred = NIL; /* * Reset transient-state fields in the relcache entry @@ -3076,26 +3224,15 @@ write_relcache_init_file(void) if (rel->rd_rel->relkind == RELKIND_INDEX) { Form_pg_am am = rel->rd_am; - HeapTuple tuple; - /* - * 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; + /* write the pg_index tuple */ + /* we assume this was created by heap_copytuple! */ + len = HEAPTUPLESIZE + rel->rd_indextuple->t_len; 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); + elog(FATAL, "cannot write init file -- index tuple length"); + + if (fwrite(rel->rd_indextuple, 1, len, fp) != len) + elog(FATAL, "cannot write init file -- index tuple"); /* next, write the access method tuple form */ len = sizeof(FormData_pg_am); |