diff options
Diffstat (limited to 'src/backend/utils/cache')
-rw-r--r-- | src/backend/utils/cache/Makefile | 4 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 53 | ||||
-rw-r--r-- | src/backend/utils/cache/typcache.c | 292 |
3 files changed, 327 insertions, 22 deletions
diff --git a/src/backend/utils/cache/Makefile b/src/backend/utils/cache/Makefile index b13ecc38ddc..216a0eaf1f6 100644 --- a/src/backend/utils/cache/Makefile +++ b/src/backend/utils/cache/Makefile @@ -4,7 +4,7 @@ # Makefile for utils/cache # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/utils/cache/Makefile,v 1.17 2002/12/13 19:45:56 tgl Exp $ +# $Header: /cvsroot/pgsql/src/backend/utils/cache/Makefile,v 1.17.2.1 2003/09/07 04:36:55 momjian Exp $ # #------------------------------------------------------------------------- @@ -12,7 +12,7 @@ subdir = src/backend/utils/cache top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = catcache.o inval.o relcache.o syscache.o lsyscache.o +OBJS = catcache.o inval.o relcache.o syscache.o lsyscache.o typcache.o all: SUBSYS.o diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 0faa097f349..ca09bc8c4d9 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.106 2003/08/11 23:04:49 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.106.2.1 2003/09/07 04:36:55 momjian Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -122,7 +122,6 @@ get_op_hash_function(Oid opno) { CatCList *catlist; int i; - HeapTuple tuple; Oid opclass = InvalidOid; /* @@ -137,10 +136,8 @@ get_op_hash_function(Oid opno) for (i = 0; i < catlist->n_members; i++) { - Form_pg_amop aform; - - tuple = &catlist->members[i]->tuple; - aform = (Form_pg_amop) GETSTRUCT(tuple); + HeapTuple tuple = &catlist->members[i]->tuple; + Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple); if (aform->amopstrategy == HTEqualStrategyNumber && opclass_is_hash(aform->amopclaid)) @@ -155,20 +152,7 @@ get_op_hash_function(Oid opno) if (OidIsValid(opclass)) { /* Found a suitable opclass, get its hash support function */ - tuple = SearchSysCache(AMPROCNUM, - ObjectIdGetDatum(opclass), - Int16GetDatum(HASHPROC), - 0, 0); - if (HeapTupleIsValid(tuple)) - { - Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple); - RegProcedure result; - - result = aform->amproc; - ReleaseSysCache(tuple); - Assert(RegProcedureIsValid(result)); - return result; - } + return get_opclass_proc(opclass, HASHPROC); } /* Didn't find a match... */ @@ -176,6 +160,35 @@ get_op_hash_function(Oid opno) } +/* ---------- AMPROC CACHES ---------- */ + +/* + * get_opclass_proc + * Get the OID of the specified support function + * for the specified opclass. + * + * Returns InvalidOid if there is no pg_amproc entry for the given keys. + */ +Oid +get_opclass_proc(Oid opclass, int16 procnum) +{ + HeapTuple tp; + Form_pg_amproc amproc_tup; + RegProcedure result; + + tp = SearchSysCache(AMPROCNUM, + ObjectIdGetDatum(opclass), + Int16GetDatum(procnum), + 0, 0); + if (!HeapTupleIsValid(tp)) + return InvalidOid; + amproc_tup = (Form_pg_amproc) GETSTRUCT(tp); + result = amproc_tup->amproc; + ReleaseSysCache(tp); + return result; +} + + /* ---------- ATTRIBUTE CACHES ---------- */ /* diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c new file mode 100644 index 00000000000..4507776f41f --- /dev/null +++ b/src/backend/utils/cache/typcache.c @@ -0,0 +1,292 @@ +/*------------------------------------------------------------------------- + * + * typcache.c + * POSTGRES type cache code + * + * The type cache exists to speed lookup of certain information about data + * types that is not directly available from a type's pg_type row. In + * particular, we use a type's default btree opclass, or the default hash + * opclass if no btree opclass exists, to determine which operators should + * be used for grouping and sorting the type (GROUP BY, ORDER BY ASC/DESC). + * + * Several seemingly-odd choices have been made to support use of the type + * cache by the generic array comparison routines array_eq() and array_cmp(). + * Because these routines are used as index support operations, they cannot + * leak memory. To allow them to execute efficiently, all information that + * either of them would like to re-use across calls is made available in the + * type cache. + * + * Once created, a type cache entry lives as long as the backend does, so + * there is no need for a call to release a cache entry. (For present uses, + * it would be okay to flush type cache entries at the ends of transactions, + * if we needed to reclaim space.) + * + * There is presently no provision for clearing out a cache entry if the + * stored data becomes obsolete. (The code will work if a type acquires + * opclasses it didn't have before while a backend runs --- but not if the + * definition of an existing opclass is altered.) However, the relcache + * doesn't cope with opclasses changing under it, either, so this seems + * a low-priority problem. + * + * + * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/typcache.c,v 1.1.2.1 2003/09/07 04:36:55 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/heapam.h" +#include "access/hash.h" +#include "access/nbtree.h" +#include "catalog/catname.h" +#include "catalog/indexing.h" +#include "catalog/pg_am.h" +#include "catalog/pg_opclass.h" +#include "parser/parse_coerce.h" +#include "utils/builtins.h" +#include "utils/catcache.h" +#include "utils/fmgroids.h" +#include "utils/hsearch.h" +#include "utils/lsyscache.h" +#include "utils/typcache.h" + + +static HTAB *TypeCacheHash = NULL; + + +static Oid lookup_default_opclass(Oid type_id, Oid am_id); + + +/* + * lookup_type_cache + * + * Fetch the type cache entry for the specified datatype, and make sure that + * all the fields requested by bits in 'flags' are valid. + * + * The result is never NULL --- we will elog() if the passed type OID is + * invalid. Note however that we may fail to find one or more of the + * requested opclass-dependent fields; the caller needs to check whether + * the fields are InvalidOid or not. + */ +TypeCacheEntry * +lookup_type_cache(Oid type_id, int flags) +{ + TypeCacheEntry *typentry; + bool found; + + if (TypeCacheHash == NULL) + { + /* First time through: initialize the hash table */ + HASHCTL ctl; + + if (!CacheMemoryContext) + CreateCacheMemoryContext(); + + MemSet(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(Oid); + ctl.entrysize = sizeof(TypeCacheEntry); + ctl.hash = tag_hash; + TypeCacheHash = hash_create("Type information cache", 64, + &ctl, HASH_ELEM | HASH_FUNCTION); + } + + /* Try to look up an existing entry */ + typentry = (TypeCacheEntry *) hash_search(TypeCacheHash, + (void *) &type_id, + HASH_FIND, NULL); + if (typentry == NULL) + { + /* + * If we didn't find one, we want to make one. But first get the + * required info from the pg_type row, just to make sure we don't + * make a cache entry for an invalid type OID. + */ + int16 typlen; + bool typbyval; + char typalign; + + get_typlenbyvalalign(type_id, &typlen, &typbyval, &typalign); + + typentry = (TypeCacheEntry *) hash_search(TypeCacheHash, + (void *) &type_id, + HASH_ENTER, &found); + if (typentry == NULL) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + Assert(!found); /* it wasn't there a moment ago */ + + MemSet(typentry, 0, sizeof(TypeCacheEntry)); + typentry->type_id = type_id; + typentry->typlen = typlen; + typentry->typbyval = typbyval; + typentry->typalign = typalign; + } + + /* If we haven't already found the opclass, try to do so */ + if (flags != 0 && typentry->btree_opc == InvalidOid) + { + typentry->btree_opc = lookup_default_opclass(type_id, + BTREE_AM_OID); + /* Only care about hash opclass if no btree opclass... */ + if (typentry->btree_opc == InvalidOid) + { + if (typentry->hash_opc == InvalidOid) + typentry->hash_opc = lookup_default_opclass(type_id, + HASH_AM_OID); + } + else + { + /* + * If we find a btree opclass where previously we only found + * a hash opclass, forget the hash equality operator so we + * can use the btree operator instead. + */ + typentry->eq_opr = InvalidOid; + typentry->eq_opr_finfo.fn_oid = InvalidOid; + } + } + + /* Look for requested operators and functions */ + if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) && + typentry->eq_opr == InvalidOid) + { + if (typentry->btree_opc != InvalidOid) + typentry->eq_opr = get_opclass_member(typentry->btree_opc, + BTEqualStrategyNumber); + if (typentry->eq_opr == InvalidOid && + typentry->hash_opc != InvalidOid) + typentry->eq_opr = get_opclass_member(typentry->hash_opc, + HTEqualStrategyNumber); + } + if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid) + { + if (typentry->btree_opc != InvalidOid) + typentry->lt_opr = get_opclass_member(typentry->btree_opc, + BTLessStrategyNumber); + } + if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid) + { + if (typentry->btree_opc != InvalidOid) + typentry->gt_opr = get_opclass_member(typentry->btree_opc, + BTGreaterStrategyNumber); + } + if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) && + typentry->cmp_proc == InvalidOid) + { + if (typentry->btree_opc != InvalidOid) + typentry->cmp_proc = get_opclass_proc(typentry->btree_opc, + BTORDER_PROC); + } + + /* + * Set up fmgr lookup info as requested + * + * Note: we tell fmgr the finfo structures live in CacheMemoryContext, + * which is not quite right (they're really in DynaHashContext) but this + * will do for our purposes. + */ + if ((flags & TYPECACHE_EQ_OPR_FINFO) && + typentry->eq_opr_finfo.fn_oid == InvalidOid && + typentry->eq_opr != InvalidOid) + { + Oid eq_opr_func; + + eq_opr_func = get_opcode(typentry->eq_opr); + if (eq_opr_func != InvalidOid) + fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo, + CacheMemoryContext); + } + if ((flags & TYPECACHE_CMP_PROC_FINFO) && + typentry->cmp_proc_finfo.fn_oid == InvalidOid && + typentry->cmp_proc != InvalidOid) + { + fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo, + CacheMemoryContext); + } + + return typentry; +} + +/* + * lookup_default_opclass + * + * Given the OIDs of a datatype and an access method, find the default + * operator class, if any. Returns InvalidOid if there is none. + */ +static Oid +lookup_default_opclass(Oid type_id, Oid am_id) +{ + int nexact = 0; + int ncompatible = 0; + Oid exactOid = InvalidOid; + Oid compatibleOid = InvalidOid; + Relation rel; + ScanKeyData skey[1]; + SysScanDesc scan; + HeapTuple tup; + + /* If it's a domain, look at the base type instead */ + type_id = getBaseType(type_id); + + /* + * We scan through all the opclasses available for the access method, + * looking for one that is marked default and matches the target type + * (either exactly or binary-compatibly, but prefer an exact match). + * + * We could find more than one binary-compatible match, in which case we + * require the user to specify which one he wants. If we find more + * than one exact match, then someone put bogus entries in pg_opclass. + * + * This is the same logic as GetDefaultOpClass() in indexcmds.c, except + * that we consider all opclasses, regardless of the current search path. + */ + rel = heap_openr(OperatorClassRelationName, AccessShareLock); + + ScanKeyEntryInitialize(&skey[0], 0x0, + Anum_pg_opclass_opcamid, F_OIDEQ, + ObjectIdGetDatum(am_id)); + + scan = systable_beginscan(rel, OpclassAmNameNspIndex, true, + SnapshotNow, 1, skey); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup); + + if (opclass->opcdefault) + { + if (opclass->opcintype == type_id) + { + nexact++; + exactOid = HeapTupleGetOid(tup); + } + else if (IsBinaryCoercible(type_id, opclass->opcintype)) + { + ncompatible++; + compatibleOid = HeapTupleGetOid(tup); + } + } + } + + systable_endscan(scan); + + heap_close(rel, AccessShareLock); + + if (nexact == 1) + return exactOid; + if (nexact != 0) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("there are multiple default operator classes for data type %s", + format_type_be(type_id)))); + if (ncompatible == 1) + return compatibleOid; + + return InvalidOid; +} |