diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/Makefile | 6 | ||||
-rw-r--r-- | src/backend/utils/adt/lockfuncs.c | 23 | ||||
-rw-r--r-- | src/backend/utils/adt/pseudotypes.c | 55 | ||||
-rw-r--r-- | src/backend/utils/adt/rowtypes.c | 75 | ||||
-rw-r--r-- | src/backend/utils/adt/sets.c | 213 | ||||
-rw-r--r-- | src/backend/utils/cache/relcache.c | 30 | ||||
-rw-r--r-- | src/backend/utils/cache/typcache.c | 268 | ||||
-rw-r--r-- | src/backend/utils/fmgr/funcapi.c | 15 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 44 |
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); } |