aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/adt/Makefile6
-rw-r--r--src/backend/utils/adt/lockfuncs.c23
-rw-r--r--src/backend/utils/adt/pseudotypes.c55
-rw-r--r--src/backend/utils/adt/rowtypes.c75
-rw-r--r--src/backend/utils/adt/sets.c213
-rw-r--r--src/backend/utils/cache/relcache.c30
-rw-r--r--src/backend/utils/cache/typcache.c268
-rw-r--r--src/backend/utils/fmgr/funcapi.c15
-rw-r--r--src/backend/utils/misc/guc.c44
9 files changed, 399 insertions, 330 deletions
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 5223e33816e..427d6334cd4 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for utils/adt
#
-# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.56 2003/11/29 19:51:57 pgsql Exp $
+# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.57 2004/04/01 21:28:45 tgl Exp $
#
subdir = src/backend/utils/adt
@@ -19,8 +19,8 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o float.o format_type.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o not_in.o numeric.o numutils.o \
- oid.o oracle_compat.o pseudotypes.o \
- regexp.o regproc.o ruleutils.o selfuncs.o sets.o \
+ oid.o oracle_compat.o pseudotypes.o rowtypes.o \
+ regexp.o regproc.o ruleutils.o selfuncs.o \
tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
network.o mac.o inet_net_ntop.o inet_net_pton.o \
ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index b009e167a3f..bc4901aa546 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -6,7 +6,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.12 2003/11/29 19:51:58 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.13 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,22 +52,22 @@ pg_lock_status(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* build tupdesc for result tuples */
- /* this had better match pg_locks view in initdb.sh */
+ /* this had better match pg_locks view in system_views.sql */
tupdesc = CreateTemplateTupleDesc(6, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
- OIDOID, -1, 0, false);
+ OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
- OIDOID, -1, 0, false);
+ OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "transaction",
- XIDOID, -1, 0, false);
+ XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pid",
- INT4OID, -1, 0, false);
+ INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "mode",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "granted",
- BOOLOID, -1, 0, false);
+ BOOLOID, -1, 0);
- funcctx->slot = TupleDescGetSlot(tupdesc);
+ funcctx->tuple_desc = BlessTupleDesc(tupdesc);
/*
* Collect all the locking information that we will format and
@@ -173,9 +173,8 @@ pg_lock_status(PG_FUNCTION_ARGS)
CStringGetDatum(GetLockmodeName(mode)));
values[5] = BoolGetDatum(granted);
- tuple = heap_formtuple(funcctx->slot->ttc_tupleDescriptor,
- values, nulls);
- result = TupleGetDatum(funcctx->slot, tuple);
+ tuple = heap_formtuple(funcctx->tuple_desc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 71b99553bc1..0526f52e677 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -16,7 +16,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.12 2003/11/29 19:51:59 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.13 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,59 +28,6 @@
/*
- * record_in - input routine for pseudo-type RECORD.
- */
-Datum
-record_in(PG_FUNCTION_ARGS)
-{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot accept a value of type record")));
-
- PG_RETURN_VOID(); /* keep compiler quiet */
-}
-
-/*
- * record_out - output routine for pseudo-type RECORD.
- */
-Datum
-record_out(PG_FUNCTION_ARGS)
-{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot display a value of type record")));
-
- PG_RETURN_VOID(); /* keep compiler quiet */
-}
-
-/*
- * record_recv - binary input routine for pseudo-type RECORD.
- */
-Datum
-record_recv(PG_FUNCTION_ARGS)
-{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot accept a value of type record")));
-
- PG_RETURN_VOID(); /* keep compiler quiet */
-}
-
-/*
- * record_send - binary output routine for pseudo-type RECORD.
- */
-Datum
-record_send(PG_FUNCTION_ARGS)
-{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot display a value of type record")));
-
- PG_RETURN_VOID(); /* keep compiler quiet */
-}
-
-
-/*
* cstring_in - input routine for pseudo-type CSTRING.
*
* We might as well allow this to support constructs like "foo_in('blah')".
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
new file mode 100644
index 00000000000..b487dfc9047
--- /dev/null
+++ b/src/backend/utils/adt/rowtypes.c
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * rowtypes.c
+ * I/O functions for generic composite types.
+ *
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.1 2004/04/01 21:28:45 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+
+
+/*
+ * record_in - input routine for any composite type.
+ */
+Datum
+record_in(PG_FUNCTION_ARGS)
+{
+ /* Need to decide on external format before we can write this */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("input of composite types not implemented yet")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * record_out - output routine for any composite type.
+ */
+Datum
+record_out(PG_FUNCTION_ARGS)
+{
+ /* Need to decide on external format before we can write this */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("output of composite types not implemented yet")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * record_recv - binary input routine for any composite type.
+ */
+Datum
+record_recv(PG_FUNCTION_ARGS)
+{
+ /* Need to decide on external format before we can write this */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("input of composite types not implemented yet")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * record_send - binary output routine for any composite type.
+ */
+Datum
+record_send(PG_FUNCTION_ARGS)
+{
+ /* Need to decide on external format before we can write this */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("output of composite types not implemented yet")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c
deleted file mode 100644
index 33f1ed1a4a8..00000000000
--- a/src/backend/utils/adt/sets.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * sets.c
- * Functions for sets, which are defined by queries.
- * Example: a set is defined as being the result of the query
- * retrieve (X.all)
- *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/sets.c,v 1.62 2004/01/06 23:55:18 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/catname.h"
-#include "catalog/indexing.h"
-#include "catalog/pg_language.h"
-#include "catalog/pg_namespace.h"
-#include "catalog/pg_proc.h"
-#include "executor/executor.h"
-#include "utils/fmgroids.h"
-#include "utils/sets.h"
-#include "utils/syscache.h"
-
-
-/*
- * SetDefine - converts query string defining set to an oid
- *
- * We create an SQL function having the given querystring as its body.
- * The name of the function is then changed to use the OID of its tuple
- * in pg_proc.
- */
-Oid
-SetDefine(char *querystr, Oid elemType)
-{
- Oid setoid;
- char *procname = GENERICSETNAME;
- char *fileName = "-";
- char realprocname[NAMEDATALEN];
- HeapTuple tup,
- newtup = NULL;
- Form_pg_proc proc;
- Relation procrel;
- int i;
- Datum replValue[Natts_pg_proc];
- char replNull[Natts_pg_proc];
- char repl[Natts_pg_proc];
-
- setoid = ProcedureCreate(procname, /* changed below, after oid known */
- PG_CATALOG_NAMESPACE, /* XXX wrong */
- false, /* don't replace */
- true, /* returnsSet */
- elemType, /* returnType */
- SQLlanguageId, /* language */
- F_FMGR_SQL_VALIDATOR,
- querystr, /* prosrc */
- fileName, /* probin */
- false, /* not aggregate */
- false, /* security invoker */
- false, /* isStrict (irrelevant, no args) */
- PROVOLATILE_VOLATILE, /* assume unsafe */
- 0, /* parameterCount */
- NULL, /* parameterTypes */
- NULL); /* parameterNames */
-
- /*
- * Since we're still inside this command of the transaction, we can't
- * see the results of the procedure definition unless we pretend we've
- * started the next command. (Postgres's solution to the Halloween
- * problem is to not allow you to see the results of your command
- * until you start the next command.)
- */
- CommandCounterIncrement();
-
- procrel = heap_openr(ProcedureRelationName, RowExclusiveLock);
-
- tup = SearchSysCache(PROCOID,
- ObjectIdGetDatum(setoid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for function %u", setoid);
-
- /*
- * We can tell whether the set was already defined by checking the
- * name. If it's GENERICSETNAME, the set is new. If it's "set<some
- * oid>" it's already defined.
- */
- proc = (Form_pg_proc) GETSTRUCT(tup);
- if (strcmp(procname, NameStr(proc->proname)) == 0)
- {
- /* make the real proc name */
- snprintf(realprocname, sizeof(realprocname), "set%u", setoid);
-
- /* set up the attributes to be modified or kept the same */
- repl[0] = 'r';
- for (i = 1; i < Natts_pg_proc; i++)
- repl[i] = ' ';
- replValue[0] = (Datum) realprocname;
- for (i = 1; i < Natts_pg_proc; i++)
- replValue[i] = (Datum) 0;
- for (i = 0; i < Natts_pg_proc; i++)
- replNull[i] = ' ';
-
- /* change the pg_proc tuple */
- newtup = heap_modifytuple(tup,
- procrel,
- replValue,
- replNull,
- repl);
-
- simple_heap_update(procrel, &newtup->t_self, newtup);
-
- setoid = HeapTupleGetOid(newtup);
-
- CatalogUpdateIndexes(procrel, newtup);
-
- heap_freetuple(newtup);
- }
-
- ReleaseSysCache(tup);
-
- heap_close(procrel, RowExclusiveLock);
-
- return setoid;
-}
-
-/*
- * This function executes set evaluation. The parser sets up a set reference
- * as a call to this function with the OID of the set to evaluate as argument.
- *
- * We build a new fcache for execution of the set's function and run the
- * function until it says "no mas". The fn_extra field of the call's
- * FmgrInfo record is a handy place to hold onto the fcache. (Since this
- * is a built-in function, there is no competing use of fn_extra.)
- */
-Datum
-seteval(PG_FUNCTION_ARGS)
-{
- Oid funcoid = PG_GETARG_OID(0);
- FuncExprState *fcache;
- Datum result;
- bool isNull;
- ExprDoneCond isDone;
-
- /*
- * If this is the first call, we need to set up the fcache for the
- * target set's function.
- */
- fcache = (FuncExprState *) fcinfo->flinfo->fn_extra;
- if (fcache == NULL)
- {
- MemoryContext oldcontext;
- FuncExpr *func;
-
- oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-
- func = makeNode(FuncExpr);
- func->funcid = funcoid;
- func->funcresulttype = InvalidOid; /* nothing will look at
- * this */
- func->funcretset = true;
- func->funcformat = COERCE_EXPLICIT_CALL;
- func->args = NIL; /* there are no arguments */
-
- fcache = (FuncExprState *) ExecInitExpr((Expr *) func, NULL);
-
- MemoryContextSwitchTo(oldcontext);
-
- init_fcache(funcoid, fcache, fcinfo->flinfo->fn_mcxt);
-
- fcinfo->flinfo->fn_extra = (void *) fcache;
- }
-
- /*
- * Evaluate the function. NOTE: we need no econtext because there are
- * no arguments to evaluate.
- */
-
- /* ExecMakeFunctionResult assumes these are initialized at call: */
- isNull = false;
- isDone = ExprSingleResult;
-
- result = ExecMakeFunctionResult(fcache,
- NULL, /* no econtext, see above */
- &isNull,
- &isDone);
-
- /*
- * Return isNull/isDone status.
- */
- fcinfo->isnull = isNull;
-
- if (isDone != ExprSingleResult)
- {
- ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- if (rsi && IsA(rsi, ReturnSetInfo))
- rsi->isDone = isDone;
- else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that "
- "cannot accept a set")));
- }
-
- PG_RETURN_DATUM(result);
-}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 5c302e18d39..85ad6ffe788 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.200 2004/03/16 05:05:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.201 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -63,6 +63,7 @@
#include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
+#include "utils/typcache.h"
/*
@@ -432,7 +433,10 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
AttrDefault *attrdef = NULL;
int ndef = 0;
- relation->rd_att->tdhasoid = RelationGetForm(relation)->relhasoids;
+ /* copy some fields from pg_class row to rd_att */
+ relation->rd_att->tdtypeid = relation->rd_rel->reltype;
+ relation->rd_att->tdtypmod = -1; /* unnecessary, but... */
+ relation->rd_att->tdhasoid = relation->rd_rel->relhasoids;
constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
sizeof(TupleConstr));
@@ -1312,9 +1316,12 @@ formrdesc(const char *relationName,
* Unlike the case with the relation tuple, this data had better be right
* because it will never be replaced. The input values must be
* correctly defined by macros in src/include/catalog/ headers.
+ *
+ * Note however that rd_att's tdtypeid, tdtypmod, tdhasoid fields are
+ * not right at this point. They will be fixed later when the real
+ * pg_class row is loaded.
*/
- relation->rd_att = CreateTemplateTupleDesc(natts,
- relation->rd_rel->relhasoids);
+ relation->rd_att = CreateTemplateTupleDesc(natts, false);
/*
* initialize tuple desc info
@@ -1595,6 +1602,7 @@ RelationReloadClassinfo(Relation relation)
static void
RelationClearRelation(Relation relation, bool rebuild)
{
+ Oid old_reltype = relation->rd_rel->reltype;
MemoryContext oldcxt;
/*
@@ -1679,6 +1687,7 @@ RelationClearRelation(Relation relation, bool rebuild)
if (!rebuild)
{
/* ok to zap remaining substructure */
+ flush_rowtype_cache(old_reltype);
FreeTupleDesc(relation->rd_att);
if (relation->rd_rulescxt)
MemoryContextDelete(relation->rd_rulescxt);
@@ -1704,6 +1713,7 @@ RelationClearRelation(Relation relation, bool rebuild)
if (RelationBuildDesc(buildinfo, relation) != relation)
{
/* Should only get here if relation was deleted */
+ flush_rowtype_cache(old_reltype);
FreeTupleDesc(old_att);
if (old_rulescxt)
MemoryContextDelete(old_rulescxt);
@@ -1715,11 +1725,15 @@ RelationClearRelation(Relation relation, bool rebuild)
relation->rd_isnew = old_isnew;
if (equalTupleDescs(old_att, relation->rd_att))
{
+ /* needn't flush typcache here */
FreeTupleDesc(relation->rd_att);
relation->rd_att = old_att;
}
else
+ {
+ flush_rowtype_cache(old_reltype);
FreeTupleDesc(old_att);
+ }
if (equalRuleLocks(old_rules, relation->rd_rules))
{
if (relation->rd_rulescxt)
@@ -2329,6 +2343,12 @@ RelationCacheInitializePhase2(void)
*/
Assert(relation->rd_rel != NULL);
memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
+
+ /*
+ * Also update the derived fields in rd_att.
+ */
+ relation->rd_att->tdtypeid = relp->reltype;
+ relation->rd_att->tdtypmod = -1; /* unnecessary, but... */
relation->rd_att->tdhasoid = relp->relhasoids;
ReleaseSysCache(htup);
@@ -2918,6 +2938,8 @@ load_relcache_init_file(void)
/* initialize attribute tuple forms */
rel->rd_att = CreateTemplateTupleDesc(relform->relnatts,
relform->relhasoids);
+ rel->rd_att->tdtypeid = relform->reltype;
+ rel->rd_att->tdtypmod = -1; /* unnecessary, but... */
/* next read all the attribute tuple form data entries */
has_not_null = false;
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index d6e560c34c5..7a8e67c83c8 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -28,12 +28,15 @@
* doesn't cope with opclasses changing under it, either, so this seems
* a low-priority problem.
*
+ * We do support clearing the tuple descriptor part of a rowtype's cache
+ * entry, since that may need to change as a consequence of ALTER TABLE.
+ *
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.4 2003/11/29 19:52:00 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.5 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,11 +56,42 @@
#include "utils/fmgroids.h"
#include "utils/hsearch.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/typcache.h"
+/* The main type cache hashtable searched by lookup_type_cache */
static HTAB *TypeCacheHash = NULL;
+/*
+ * We use a separate table for storing the definitions of non-anonymous
+ * record types. Once defined, a record type will be remembered for the
+ * life of the backend. Subsequent uses of the "same" record type (where
+ * sameness means equalTupleDescs) will refer to the existing table entry.
+ *
+ * Stored record types are remembered in a linear array of TupleDescs,
+ * which can be indexed quickly with the assigned typmod. There is also
+ * a hash table to speed searches for matching TupleDescs. The hash key
+ * uses just the first N columns' type OIDs, and so we may have multiple
+ * entries with the same hash key.
+ */
+#define REC_HASH_KEYS 16 /* use this many columns in hash key */
+
+typedef struct RecordCacheEntry
+{
+ /* the hash lookup key MUST BE FIRST */
+ Oid hashkey[REC_HASH_KEYS]; /* column type IDs, zero-filled */
+
+ /* list of TupleDescs for record types with this hashkey */
+ List *tupdescs;
+} RecordCacheEntry;
+
+static HTAB *RecordCacheHash = NULL;
+
+static TupleDesc *RecordCacheArray = NULL;
+static int32 RecordCacheArrayLen = 0; /* allocated length of array */
+static int32 NextRecordTypmod = 0; /* number of entries used */
+
static Oid lookup_default_opclass(Oid type_id, Oid am_id);
@@ -102,16 +136,26 @@ lookup_type_cache(Oid type_id, int flags)
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.
+ * If we didn't find one, we want to make one. But first look up
+ * 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;
+ HeapTuple tp;
+ Form_pg_type typtup;
- get_typlenbyvalalign(type_id, &typlen, &typbyval, &typalign);
+ tp = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(type_id),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for type %u", type_id);
+ typtup = (Form_pg_type) GETSTRUCT(tp);
+ if (!typtup->typisdefined)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s\" is only a shell",
+ NameStr(typtup->typname))));
+ /* Now make the typcache entry */
typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
(void *) &type_id,
HASH_ENTER, &found);
@@ -123,13 +167,20 @@ lookup_type_cache(Oid type_id, int flags)
MemSet(typentry, 0, sizeof(TypeCacheEntry));
typentry->type_id = type_id;
- typentry->typlen = typlen;
- typentry->typbyval = typbyval;
- typentry->typalign = typalign;
+ typentry->typlen = typtup->typlen;
+ typentry->typbyval = typtup->typbyval;
+ typentry->typalign = typtup->typalign;
+ typentry->typtype = typtup->typtype;
+ typentry->typrelid = typtup->typrelid;
+
+ ReleaseSysCache(tp);
}
/* If we haven't already found the opclass, try to do so */
- if (flags != 0 && typentry->btree_opc == InvalidOid)
+ if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
+ TYPECACHE_CMP_PROC |
+ TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO)) &&
+ typentry->btree_opc == InvalidOid)
{
typentry->btree_opc = lookup_default_opclass(type_id,
BTREE_AM_OID);
@@ -215,6 +266,30 @@ lookup_type_cache(Oid type_id, int flags)
CacheMemoryContext);
}
+ /*
+ * If it's a composite type (row type), get tupdesc if requested
+ */
+ if ((flags & TYPECACHE_TUPDESC) &&
+ typentry->tupDesc == NULL &&
+ typentry->typtype == 'c')
+ {
+ Relation rel;
+
+ if (!OidIsValid(typentry->typrelid)) /* should not happen */
+ elog(ERROR, "invalid typrelid for composite type %u",
+ typentry->type_id);
+ rel = relation_open(typentry->typrelid, AccessShareLock);
+ Assert(rel->rd_rel->reltype == typentry->type_id);
+ /*
+ * Notice that we simply store a link to the relcache's tupdesc.
+ * Since we are relying on relcache to detect cache flush events,
+ * there's not a lot of point to maintaining an independent copy.
+ */
+ typentry->tupDesc = RelationGetDescr(rel);
+
+ relation_close(rel, AccessShareLock);
+ }
+
return typentry;
}
@@ -296,3 +371,172 @@ lookup_default_opclass(Oid type_id, Oid am_id)
return InvalidOid;
}
+
+
+/*
+ * lookup_rowtype_tupdesc
+ *
+ * Given a typeid/typmod that should describe a known composite type,
+ * return the tuple descriptor for the type. Will ereport on failure.
+ *
+ * Note: returned TupleDesc points to cached copy; caller must copy it
+ * if intending to scribble on it or keep a reference for a long time.
+ */
+TupleDesc
+lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
+{
+ if (type_id != RECORDOID)
+ {
+ /*
+ * It's a named composite type, so use the regular typcache.
+ */
+ TypeCacheEntry *typentry;
+
+ typentry = lookup_type_cache(type_id, TYPECACHE_TUPDESC);
+ /* this should not happen unless caller messed up: */
+ if (typentry->tupDesc == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("type %u is not composite",
+ type_id)));
+ return typentry->tupDesc;
+ }
+ else
+ {
+ /*
+ * It's a transient record type, so look in our record-type table.
+ */
+ if (typmod < 0 || typmod >= NextRecordTypmod)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("record type has not been registered")));
+ }
+ return RecordCacheArray[typmod];
+ }
+}
+
+
+/*
+ * assign_record_type_typmod
+ *
+ * Given a tuple descriptor for a RECORD type, find or create a cache entry
+ * for the type, and set the tupdesc's tdtypmod field to a value that will
+ * identify this cache entry to lookup_rowtype_tupdesc.
+ */
+void
+assign_record_type_typmod(TupleDesc tupDesc)
+{
+ RecordCacheEntry *recentry;
+ TupleDesc entDesc;
+ Oid hashkey[REC_HASH_KEYS];
+ bool found;
+ int i;
+ List *l;
+ int32 newtypmod;
+ MemoryContext oldcxt;
+
+ Assert(tupDesc->tdtypeid == RECORDOID);
+
+ if (RecordCacheHash == NULL)
+ {
+ /* First time through: initialize the hash table */
+ HASHCTL ctl;
+
+ if (!CacheMemoryContext)
+ CreateCacheMemoryContext();
+
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
+ ctl.entrysize = sizeof(RecordCacheEntry);
+ ctl.hash = tag_hash;
+ RecordCacheHash = hash_create("Record information cache", 64,
+ &ctl, HASH_ELEM | HASH_FUNCTION);
+ }
+
+ /* Find or create a hashtable entry for this hash class */
+ MemSet(hashkey, 0, sizeof(hashkey));
+ for (i = 0; i < tupDesc->natts; i++)
+ {
+ if (i >= REC_HASH_KEYS)
+ break;
+ hashkey[i] = tupDesc->attrs[i]->atttypid;
+ }
+ recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
+ (void *) hashkey,
+ HASH_ENTER, &found);
+ if (recentry == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory")));
+ if (!found)
+ {
+ /* New entry ... hash_search initialized only the hash key */
+ recentry->tupdescs = NIL;
+ }
+
+ /* Look for existing record cache entry */
+ foreach(l, recentry->tupdescs)
+ {
+ entDesc = (TupleDesc) lfirst(l);
+ if (equalTupleDescs(tupDesc, entDesc))
+ {
+ tupDesc->tdtypmod = entDesc->tdtypmod;
+ return;
+ }
+ }
+
+ /* Not present, so need to manufacture an entry */
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ if (RecordCacheArray == NULL)
+ {
+ RecordCacheArray = (TupleDesc *) palloc(64 * sizeof(TupleDesc));
+ RecordCacheArrayLen = 64;
+ }
+ else if (NextRecordTypmod >= RecordCacheArrayLen)
+ {
+ int32 newlen = RecordCacheArrayLen * 2;
+
+ RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
+ newlen * sizeof(TupleDesc));
+ RecordCacheArrayLen = newlen;
+ }
+
+ /* if fail in subrs, no damage except possibly some wasted memory... */
+ entDesc = CreateTupleDescCopy(tupDesc);
+ recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
+ /* now it's safe to advance NextRecordTypmod */
+ newtypmod = NextRecordTypmod++;
+ entDesc->tdtypmod = newtypmod;
+ RecordCacheArray[newtypmod] = entDesc;
+
+ /* report to caller as well */
+ tupDesc->tdtypmod = newtypmod;
+
+ MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * flush_rowtype_cache
+ *
+ * If a typcache entry exists for a rowtype, delete the entry's cached
+ * tuple descriptor link. This is called from relcache.c when a cached
+ * relation tupdesc is about to be dropped.
+ */
+void
+flush_rowtype_cache(Oid type_id)
+{
+ TypeCacheEntry *typentry;
+
+ if (TypeCacheHash == NULL)
+ return; /* no table, so certainly no entry */
+
+ typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
+ (void *) &type_id,
+ HASH_FIND, NULL);
+ if (typentry == NULL)
+ return; /* no matching entry */
+
+ typentry->tupDesc = NULL;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index e6ef3fcec3c..dac4a8916e1 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -7,7 +7,7 @@
* Copyright (c) 2002-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.13 2003/12/19 00:02:11 joe Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.14 2004/04/01 21:28:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,9 +49,8 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
* Allocate suitably long-lived space and zero it
*/
retval = (FuncCallContext *)
- MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- sizeof(FuncCallContext));
- MemSet(retval, 0, sizeof(FuncCallContext));
+ MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
+ sizeof(FuncCallContext));
/*
* initialize the elements
@@ -61,6 +60,7 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
retval->slot = NULL;
retval->user_fctx = NULL;
retval->attinmeta = NULL;
+ retval->tuple_desc = NULL;
retval->multi_call_memory_ctx = fcinfo->flinfo->fn_mcxt;
/*
@@ -104,8 +104,11 @@ per_MultiFuncCall(PG_FUNCTION_ARGS)
* FuncCallContext is pointing to it), but in most usage patterns the
* tuples stored in it will be in the function's per-tuple context. So
* at the beginning of each call, the Slot will hold a dangling
- * pointer to an already-recycled tuple. We clear it out here. (See
- * also the definition of TupleGetDatum() in funcapi.h!)
+ * pointer to an already-recycled tuple. We clear it out here.
+ *
+ * Note: use of retval->slot is obsolete as of 7.5, and we expect that
+ * it will always be NULL. This is just here for backwards compatibility
+ * in case someone creates a slot anyway.
*/
if (retval->slot != NULL)
ExecClearTuple(retval->slot);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bc2499ed3af..26f4210ad0e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.194 2004/04/01 14:25:47 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.195 2004/04/01 21:28:45 tgl Exp $
*
*--------------------------------------------------------------------
*/
@@ -3453,9 +3453,9 @@ GetPGVariableResultDesc(const char *name)
/* need a tuple descriptor representing two TEXT columns */
tupdesc = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
}
else
{
@@ -3467,7 +3467,7 @@ GetPGVariableResultDesc(const char *name)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
}
return tupdesc;
}
@@ -3507,7 +3507,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest)
/* need a tuple descriptor representing a single TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
/* prepare for projection of tuples */
tstate = begin_tup_output_tupdesc(dest, tupdesc);
@@ -3532,9 +3532,9 @@ ShowAllGUCConfig(DestReceiver *dest)
/* need a tuple descriptor representing two TEXT columns */
tupdesc = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
/* prepare for projection of tuples */
tstate = begin_tup_output_tupdesc(dest, tupdesc);
@@ -3740,7 +3740,6 @@ show_all_settings(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
int call_cntr;
int max_calls;
- TupleTableSlot *slot;
AttInMetadata *attinmeta;
MemoryContext oldcontext;
@@ -3762,31 +3761,25 @@ show_all_settings(PG_FUNCTION_ARGS)
*/
tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "category",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "short_desc",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "extra_desc",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "context",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 7, "vartype",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "source",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "min_val",
- TEXTOID, -1, 0, false);
+ TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "max_val",
- TEXTOID, -1, 0, false);
-
- /* allocate a slot for a tuple with this tupdesc */
- slot = TupleDescGetSlot(tupdesc);
-
- /* assign slot to function context */
- funcctx->slot = slot;
+ TEXTOID, -1, 0);
/*
* Generate attribute metadata needed later to produce tuples from
@@ -3806,7 +3799,6 @@ show_all_settings(PG_FUNCTION_ARGS)
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
- slot = funcctx->slot;
attinmeta = funcctx->attinmeta;
if (call_cntr < max_calls) /* do when there is more left to send */
@@ -3837,7 +3829,7 @@ show_all_settings(PG_FUNCTION_ARGS)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
- result = TupleGetDatum(slot, tuple);
+ result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}