1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +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:
Tom Lane
2004-04-01 21:28:47 +00:00
parent 8590a62b75
commit 375369acd1
60 changed files with 1779 additions and 1733 deletions

View File

@ -1,6 +1,6 @@
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
$PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.84 2004/02/15 21:01:38 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.85 2004/04/01 21:28:43 tgl Exp $
-->
<chapter id="catalogs">
@ -809,24 +809,6 @@
</entry>
</row>
<row>
<entry><structfield>attisset</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>
If true, this attribute is a set. In that case, what is really
stored in the attribute is the OID of a row in the
<structname>pg_proc</structname> catalog. The
<structname>pg_proc</structname> row contains the query
string that defines this set, i.e., the query to run to get
the set. So the <structfield>atttypid</structfield> (see
above) refers to the type returned by this query, but the
actual length of this attribute is the length (size) of an
<type>oid</type>. --- At least this is the theory. All this
is probably quite broken these days.
</entry>
</row>
<row>
<entry><structfield>attalign</structfield></entry>
<entry><type>char</type></entry>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.33 2004/03/17 01:05:10 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.34 2004/04/01 21:28:43 tgl Exp $
-->
<chapter id="spi">
@ -1953,8 +1953,7 @@ char * SPI_getrelname(Relation <parameter>rel</parameter>)
allocations made by <function>palloc</function>,
<function>repalloc</function>, or SPI utility functions (except for
<function>SPI_copytuple</function>,
<function>SPI_copytupledesc</function>,
<function>SPI_copytupleintoslot</function>,
<function>SPI_returntuple</function>,
<function>SPI_modifytuple</function>, and
<function>SPI_palloc</function>) are made in this context. When a
procedure disconnects from the SPI manager (via
@ -2169,7 +2168,9 @@ HeapTuple SPI_copytuple(HeapTuple <parameter>row</parameter>)
<para>
<function>SPI_copytuple</function> makes a copy of a row in the
upper executor context.
upper executor context. This is normally used to return a modified
row from a trigger. In a function declared to return a composite
type, use <function>SPI_returntuple</function> instead.
</para>
</refsect1>
@ -2200,21 +2201,21 @@ HeapTuple SPI_copytuple(HeapTuple <parameter>row</parameter>)
<!-- *********************************************** -->
<refentry id="spi-spi-copytupledesc">
<refentry id="spi-spi-returntuple">
<refmeta>
<refentrytitle>SPI_copytupledesc</refentrytitle>
<refentrytitle>SPI_returntuple</refentrytitle>
</refmeta>
<refnamediv>
<refname>SPI_copytupledesc</refname>
<refpurpose>make a copy of a row descriptor in the upper executor context</refpurpose>
<refname>SPI_returntuple</refname>
<refpurpose>prepare to return a tuple as a Datum</refpurpose>
</refnamediv>
<indexterm><primary>SPI_copytupledesc</primary></indexterm>
<indexterm><primary>SPI_returntuple</primary></indexterm>
<refsynopsisdiv>
<synopsis>
TupleDesc SPI_copytupledesc(TupleDesc <parameter>tupdesc</parameter>)
HeapTupleHeader SPI_returntuple(HeapTuple <parameter>row</parameter>, TupleDesc <parameter>rowdesc</parameter>)
</synopsis>
</refsynopsisdiv>
@ -2222,63 +2223,16 @@ TupleDesc SPI_copytupledesc(TupleDesc <parameter>tupdesc</parameter>)
<title>Description</title>
<para>
<function>SPI_copytupledesc</function> makes a copy of a row
descriptor in the upper executor context.
<function>SPI_returntuple</function> makes a copy of a row in
the upper executor context, returning it in the form of a rowtype Datum.
The returned pointer need only be converted to Datum via PointerGetDatum
before returning.
</para>
</refsect1>
<refsect1>
<title>Arguments</title>
<variablelist>
<varlistentry>
<term><literal>TupleDesc <parameter>tupdesc</parameter></literal></term>
<listitem>
<para>
row descriptor to be copied
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>
the copied row descriptor; <symbol>NULL</symbol> only if
<parameter>tupdesc</parameter> is <symbol>NULL</symbol>
</para>
</refsect1>
</refentry>
<!-- *********************************************** -->
<refentry id="spi-spi-copytupleintoslot">
<refmeta>
<refentrytitle>SPI_copytupleintoslot</refentrytitle>
</refmeta>
<refnamediv>
<refname>SPI_copytupleintoslot</refname>
<refpurpose>make a copy of a row and descriptor in the upper executor context</refpurpose>
</refnamediv>
<indexterm><primary>SPI_copytupleintoslot</primary></indexterm>
<refsynopsisdiv>
<synopsis>
TupleTableSlot * SPI_copytupleintoslot(HeapTuple <parameter>row</parameter>, TupleDesc <parameter>rowdesc</parameter>)
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<function>SPI_copytupleintoslot</function> makes a copy of a row in
the upper executor context, returning it in the form of a filled-in
<type>TupleTableSlot</type> structure.
Note that this should be used for functions that are declared to return
composite types. It is not used for triggers; use
<function>SPI_copytuple</> for returning a modified row in a trigger.
</para>
</refsect1>
@ -2299,7 +2253,8 @@ TupleTableSlot * SPI_copytupleintoslot(HeapTuple <parameter>row</parameter>, Tup
<term><literal>TupleDesc <parameter>rowdesc</parameter></literal></term>
<listitem>
<para>
row descriptor to be copied
descriptor for row (pass the same descriptor each time for most
effective caching)
</para>
</listitem>
</varlistentry>
@ -2310,9 +2265,9 @@ TupleTableSlot * SPI_copytupleintoslot(HeapTuple <parameter>row</parameter>, Tup
<title>Return Value</title>
<para>
<type>TupleTableSlot</type> containing the copied row and
descriptor; <symbol>NULL</symbol> only if
<parameter>row</parameter> or <parameter>rowdesc</parameter> are
<type>HeapTupleHeader</type> pointing to copied row;
<symbol>NULL</symbol> only if
<parameter>row</parameter> or <parameter>rowdesc</parameter> is
<symbol>NULL</symbol>
</para>
</refsect1>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.80 2004/03/09 16:57:47 neilc Exp $
$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.81 2004/04/01 21:28:43 tgl Exp $
-->
<sect1 id="xfunc">
@ -1623,7 +1623,7 @@ SELECT name, c_overpaid(emp, 1500) AS overpaid
#include "executor/executor.h" /* for GetAttributeByName() */
bool
c_overpaid(TupleTableSlot *t, /* the current row of emp */
c_overpaid(HeapTupleHeader t, /* the current row of emp */
int32 limit)
{
bool isnull;
@ -1647,7 +1647,7 @@ PG_FUNCTION_INFO_V1(c_overpaid);
Datum
c_overpaid(PG_FUNCTION_ARGS)
{
TupleTableSlot *t = (TupleTableSlot *) PG_GETARG_POINTER(0);
HeapTupleHeader t = PG_GETARG_HEAPTUPLEHEADER(0);
int32 limit = PG_GETARG_INT32(1);
bool isnull;
int32 salary;
@ -1666,7 +1666,8 @@ c_overpaid(PG_FUNCTION_ARGS)
<function>GetAttributeByName</function> is the
<productname>PostgreSQL</productname> system function that
returns attributes out of the specified row. It has
three arguments: the argument of type <type>TupleTableSlot*</type> passed into
three arguments: the argument of type <type>HeapTupleHeader</type> passed
into
the function, the name of the desired attribute, and a
return parameter that tells whether the attribute
is null. <function>GetAttributeByName</function> returns a <type>Datum</type>
@ -1674,6 +1675,11 @@ c_overpaid(PG_FUNCTION_ARGS)
appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function> macro.
</para>
<para>
There is also <function>GetAttributeByNum</function>, which selects
the target attribute by column number instead of name.
</para>
<para>
The following command declares the function
<function>c_overpaid</function> in SQL:
@ -1681,8 +1687,11 @@ c_overpaid(PG_FUNCTION_ARGS)
<programlisting>
CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
AS '<replaceable>DIRECTORY</replaceable>/funcs', 'c_overpaid'
LANGUAGE C;
LANGUAGE C STRICT;
</programlisting>
Notice we have used <literal>STRICT</> so that we did not have to
check whether the input arguments were NULL.
</para>
</sect2>
@ -1700,38 +1709,25 @@ CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
</para>
<para>
The support for returning composite data types (or rows) starts
with the <structname>AttInMetadata</> structure. This structure
holds arrays of individual attribute information needed to create
a row from raw C strings. The information contained in the
structure is derived from a <structname>TupleDesc</> structure,
but it is stored to avoid redundant computations on each call to
a set-returning function (see next section). In the case of a
function returning a set, the <structname>AttInMetadata</>
structure should be computed once during the first call and saved
for reuse in later calls. <structname>AttInMetadata</> also
saves a pointer to the original <structname>TupleDesc</>.
<programlisting>
typedef struct AttInMetadata
{
/* full TupleDesc */
TupleDesc tupdesc;
/* array of attribute type input function finfo */
FmgrInfo *attinfuncs;
/* array of attribute type typelem */
Oid *attelems;
/* array of attribute typmod */
int32 *atttypmods;
} AttInMetadata;
</programlisting>
There are two ways you can build a composite data value (henceforth
a <quote>tuple</>): you can build it from an array of Datum values,
or from an array of C strings that can be passed to the input
conversion functions of the tuple's column datatypes. In either
case, you first need to obtain or construct a <structname>TupleDesc</>
descriptor for the tuple structure. When working with Datums, you
pass the <structname>TupleDesc</> to <function>BlessTupleDesc</>,
and then call <function>heap_formtuple</> for each row. When working
with C strings, you pass the <structname>TupleDesc</> to
<function>TupleDescGetAttInMetadata</>, and then call
<function>BuildTupleFromCStrings</> for each row. In the case of a
function returning a set of tuples, the setup steps can all be done
once during the first call of the function.
</para>
<para>
To assist you in populating this structure, several functions and a macro
are available. Use
Several helper functions are available for setting up the initial
<structname>TupleDesc</>. If you want to use a named composite type,
you can fetch the information from the system catalogs. Use
<programlisting>
TupleDesc RelationNameGetTupleDesc(const char *relname)
</programlisting>
@ -1741,36 +1737,43 @@ TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)
</programlisting>
to get a <structname>TupleDesc</> based on a type OID. This can
be used to get a <structname>TupleDesc</> for a base or
composite type. Then
composite type. When writing a function that returns
<structname>record</>, the expected <structname>TupleDesc</>
must be passed in by the caller.
</para>
<para>
Once you have a <structname>TupleDesc</>, call
<programlisting>
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
</programlisting>
if you plan to work with Datums, or
<programlisting>
AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
</programlisting>
will return a pointer to an <structname>AttInMetadata</>,
initialized based on the given
<structname>TupleDesc</>. <structname>AttInMetadata</> can be
used in conjunction with C strings to produce a properly formed
row value (internally called tuple).
if you plan to work with C strings. If you are writing a function
returning set, you can save the results of these functions in the
<structname>FuncCallContext</> structure --- use the
<structfield>tuple_desc</> or <structfield>attinmeta</> field
respectively.
</para>
<para>
To return a tuple you must create a tuple slot based on the
<structname>TupleDesc</>. You can use
When working with Datums, use
<programlisting>
TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc)
HeapTuple heap_formtuple(TupleDesc tupdesc, Datum *values, char *nulls)
</programlisting>
to initialize this tuple slot, or obtain one through other (user provided)
means. The tuple slot is needed to create a <type>Datum</> for return by the
function. The same slot can (and should) be reused on each call.
to build a <structname>HeapTuple</> given user data in Datum form.
</para>
<para>
After constructing an <structname>AttInMetadata</> structure,
When working with C strings, use
<programlisting>
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
</programlisting>
can be used to build a <structname>HeapTuple</> given user data
in C string form. <literal>values</literal> is an array of C strings, one for
each attribute of the return row. Each C string should be in
to build a <structname>HeapTuple</> given user data
in C string form. <literal>values</literal> is an array of C strings,
one for each attribute of the return row. Each C string should be in
the form expected by the input function of the attribute data
type. In order to return a null value for one of the attributes,
the corresponding pointer in the <parameter>values</> array
@ -1778,25 +1781,13 @@ HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
be called again for each row you return.
</para>
<para>
Building a tuple via <function>TupleDescGetAttInMetadata</> and
<function>BuildTupleFromCStrings</> is only convenient if your
function naturally computes the values to be returned as text
strings. If your code naturally computes the values as a set of
<type>Datum</> values, you should instead use the underlying
function <function>heap_formtuple</> to convert the
<type>Datum</type> values directly into a tuple. You will still need
the <structname>TupleDesc</> and a <structname>TupleTableSlot</>,
but not <structname>AttInMetadata</>.
</para>
<para>
Once you have built a tuple to return from your function, it
must be converted into a <type>Datum</>. Use
<programlisting>
TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple)
HeapTupleGetDatum(HeapTuple tuple)
</programlisting>
to get a <type>Datum</> given a tuple and a slot. This
to convert a <structname>HeapTuple</> into a valid Datum. This
<type>Datum</> can be returned directly if you intend to return
just a single row, or it can be used as the current return value
in a set-returning function.
@ -1851,8 +1842,8 @@ typedef struct
/*
* OPTIONAL pointer to result slot
*
* slot is for use when returning tuples (i.e., composite data types)
* and is not needed when returning base data types.
* This is obsolete and only present for backwards compatibility, viz,
* user-defined SRFs that use the deprecated TupleDescGetSlot().
*/
TupleTableSlot *slot;
@ -1868,9 +1859,9 @@ typedef struct
* OPTIONAL pointer to struct containing attribute type input metadata
*
* attinmeta is for use when returning tuples (i.e., composite data types)
* and is not needed when returning base data types. It
* is only needed if you intend to use BuildTupleFromCStrings() to create
* the return tuple.
* and is not used when returning base data types. It is only needed
* if you intend to use BuildTupleFromCStrings() to create the return
* tuple.
*/
AttInMetadata *attinmeta;
@ -1883,6 +1874,18 @@ typedef struct
* of the SRF.
*/
MemoryContext multi_call_memory_ctx;
/*
* OPTIONAL pointer to struct containing tuple description
*
* tuple_desc is for use when returning tuples (i.e. composite data types)
* and is only needed if you are going to build the tuples with
* heap_formtuple() rather than with BuildTupleFromCStrings(). Note that
* the TupleDesc pointer stored here should usually have been run through
* BlessTupleDesc() first.
*/
TupleDesc tuple_desc;
} FuncCallContext;
</programlisting>
</para>
@ -1956,8 +1959,6 @@ my_set_returning_function(PG_FUNCTION_ARGS)
<replaceable>user code</replaceable>
<replaceable>if returning composite</replaceable>
<replaceable>build TupleDesc, and perhaps AttInMetadata</replaceable>
<replaceable>obtain slot</replaceable>
funcctx-&gt;slot = slot;
<replaceable>endif returning composite</replaceable>
<replaceable>user code</replaceable>
MemoryContextSwitchTo(oldcontext);
@ -1998,7 +1999,6 @@ testpassbyval(PG_FUNCTION_ARGS)
int call_cntr;
int max_calls;
TupleDesc tupdesc;
TupleTableSlot *slot;
AttInMetadata *attinmeta;
/* stuff done only on the first call of the function */
@ -2018,12 +2018,6 @@ testpassbyval(PG_FUNCTION_ARGS)
/* Build a tuple description for a __testpassbyval tuple */
tupdesc = RelationNameGetTupleDesc("__testpassbyval");
/* allocate a slot for a tuple with this tupdesc */
slot = TupleDescGetSlot(tupdesc);
/* assign slot to function context */
funcctx-&gt;slot = slot;
/*
* generate attribute metadata needed later to produce tuples from raw
* C strings
@ -2039,7 +2033,6 @@ testpassbyval(PG_FUNCTION_ARGS)
call_cntr = funcctx-&gt;call_cntr;
max_calls = funcctx-&gt;max_calls;
slot = funcctx-&gt;slot;
attinmeta = funcctx-&gt;attinmeta;
if (call_cntr &lt; max_calls) /* do when there is more left to send */
@ -2049,7 +2042,7 @@ testpassbyval(PG_FUNCTION_ARGS)
Datum result;
/*
* Prepare a values array for storage in our slot.
* Prepare a values array for building the returned tuple.
* This should be an array of C strings which will
* be processed later by the type input functions.
*/
@ -2066,7 +2059,7 @@ testpassbyval(PG_FUNCTION_ARGS)
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
result = TupleGetDatum(slot, tuple);
result = HeapTupleGetDatum(tuple);
/* clean up (this is not really necessary) */
pfree(values[0]);