mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Replace TupleTableSlot convention for whole-row variables and function
results with tuples as ordinary varlena Datums. This commit does not in itself do much for us, except eliminate the horrid memory leak associated with evaluation of whole-row variables. However, it lays the groundwork for allowing composite types as table columns, and perhaps some other useful features as well. Per my proposal of a few days ago.
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -27,59 +27,6 @@
|
||||
#include "utils/builtins.h"
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
|
||||
75
src/backend/utils/adt/rowtypes.c
Normal file
75
src/backend/utils/adt/rowtypes.c
Normal file
@@ -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 */
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
30
src/backend/utils/cache/relcache.c
vendored
30
src/backend/utils/cache/relcache.c
vendored
@@ -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;
|
||||
|
||||
268
src/backend/utils/cache/typcache.c
vendored
268
src/backend/utils/cache/typcache.c
vendored
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user