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);
|
||||
}
|
||||
Reference in New Issue
Block a user