summaryrefslogtreecommitdiff
path: root/src/backend/utils/cache
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/cache')
-rw-r--r--src/backend/utils/cache/Makefile4
-rw-r--r--src/backend/utils/cache/lsyscache.c53
-rw-r--r--src/backend/utils/cache/typcache.c292
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;
+}