Here (finally ;-)) is a doc patch covering the Table Function C API. It

reflects the changes in the tablefunc-fix patch that I sent in the other
day. It also refers to "see contrib/tablefunc for more examples", which
is next on my list of things to finish and submit.

Joe Conway
This commit is contained in:
Bruce Momjian 2002-07-18 04:47:17 +00:00
parent 8c26bc5364
commit 5a5e46ea7e
1 changed files with 341 additions and 5 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.52 2002/06/20 16:57:00 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.53 2002/07/18 04:47:17 momjian Exp $
-->
<chapter id="xfunc">
@ -1461,12 +1461,348 @@ AS '<replaceable>PGROOT</replaceable>/tutorial/funcs'
LANGUAGE C;
</programlisting>
</para>
</sect2>
<sect2>
<title>Table Function API</title>
<para>
While there are ways to construct new rows or modify
existing rows from within a C function, these
are far too complex to discuss in this manual.
Consult the backend source code for examples.
The Table Function API assists in the creation of a user defined
C Language table functions (<xref linkend="xfunc-tablefunctions">).
Table functions are functions that produce a set of rows, made up of
either base (scalar) data types, or composite (multi-column) data types.
The API is split into two main components: support for returning
composite data types, and support for returning multiple rows
(set returning functions or SRFs).
</para>
<para>
The Table Function API relies on macros and functions to suppress most
of the complexity of building composite data types and return multiple
results. In addition to the version-1 conventions discussed elsewhere,
a table function always requires the following:
<programlisting>
#include "funcapi.h"
</programlisting>
</para>
<para>
The Table Function API support for returning composite data types
(or tuples) starts with the AttInMetadata struct. This struct holds
arrays of individual attribute information needed to create a tuple from
raw C strings. It also requires a copy of the TupleDesc. The information
carried here is derived from the TupleDesc, but it is stored here to
avoid redundant cpu cycles on each call to a Table Function.
<programlisting>
typedef struct
{
/* full TupleDesc */
TupleDesc tupdesc;
/* pointer to array of attribute "type"in finfo */
FmgrInfo *attinfuncs;
/* pointer to array of attribute type typelem */
Oid *attelems;
/* pointer to array of attribute type typtypmod */
int4 *atttypmods;
} AttInMetadata;
</programlisting>
To assist you in populating this struct, several functions and a macro
are available. Use
<programlisting>
TupleDesc RelationNameGetTupleDesc(char *relname)
</programlisting>
to get a TupleDesc based on the function's return type relation, or
<programlisting>
TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)
</programlisting>
to get a TupleDesc based on the function's type oid. This can be used to
get a TupleDesc for a base (scalar), or composite (relation) type. Then
<programlisting>
AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
</programlisting>
will return a pointer to an AttInMetadata struct, initialized based on
the function's TupleDesc. AttInMetadata is be used in conjunction with
C strings to produce a properly formed tuple. The metadata is stored here
for use across calls to avoid redundant work.
</para>
<para>
In order to return a tuple you must create a tuple slot based on the
TupleDesc. You can use
<programlisting>
TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc)
</programlisting>
to initialize this tuple slot, or obtain one through other (user provided)
means. The tuple slot is needed to create a Datum for return by the
function.
</para>
<para>
If desired,
<programlisting>
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
</programlisting>
can be used to build a HeapTuple given user data in C string form.
"values" is an array of C strings, one for each attribute of the return
tuple. The C strings should be in the form expected by the "in" function
of the attribute data type. For more information on this requirement,
see the individual data type "in" functions in the source code
(e.g. textin() for data type TEXT). In order to return a NULL value for
one of the attributes, the corresponding pointer in the "values" array
should be set to NULL.
</para>
<para>
Finally, in order to return a tuple using the SRF portion of the API
(described below), the tuple must be converted into a Datum. Use
<programlisting>
TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple)
</programlisting>
to get a Datum given a tuple and a slot.
</para>
<para>
The Table Function API support for set returning functions starts with
the FuncCallContext struct. This struct holds function context for
SRFs using fcinfo->flinfo->fn_extra to hold a pointer to it across calls.
<programlisting>
typedef struct
{
/*
* Number of times we've been called before.
*
* call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
* incremented for you every time SRF_RETURN_NEXT() is called.
*/
uint32 call_cntr;
/*
* OPTIONAL maximum number of calls
*
* max_calls is here for convenience ONLY and setting it is OPTIONAL.
* If not set, you must provide alternative means to know when the
* function is done.
*/
uint32 max_calls;
/*
* OPTIONAL pointer to result slot
*
* slot is for use when returning tuples (i.e. composite data types)
* and is not needed when returning base (i.e. scalar) data types.
*/
TupleTableSlot *slot;
/*
* OPTIONAL pointer to misc user provided context info
*
* user_fctx is for use as a pointer to your own struct to retain
* arbitrary context information between calls for your function.
*/
void *user_fctx;
/*
* OPTIONAL pointer to struct containing arrays of attribute type input
* metainfo
*
* attinmeta is for use when returning tuples (i.e. composite data types)
* and is not needed when returning base (i.e. scalar) data types. It
* is ONLY needed if you intend to use BuildTupleFromCStrings() to create
* the return tuple.
*/
AttInMetadata *attinmeta;
/*
* memory context used to initialize structure
*
* fmctx is set by SRF_FIRSTCALL_INIT() for you, and used by
* SRF_RETURN_DONE() for cleanup. It is primarily for internal use
* by the API.
*/
MemoryContext fmctx;
} FuncCallContext;
</programlisting>
To assist you in populating this struct, several functions and macros
are available. Use
<programlisting>
SRF_IS_FIRSTCALL()
</programlisting>
to determine if your function has been called for the first or a
subsequent time. On the first call (only) use
<programlisting>
SRF_FIRSTCALL_INIT()
</programlisting>
to initialize the FuncCallContext struct. On every function call,
including the first, use
<programlisting>
SRF_PERCALL_SETUP()
</programlisting>
to properly set up for using the FuncCallContext struct and clearing
any previously returned data left over from the previous pass.
</para>
<para>
If your function has data to return, use
<programlisting>
SRF_RETURN_NEXT(funcctx, result)
</programlisting>
to send it and prepare for the next call. Finally, when your function
is finished returning data, use
<programlisting>
SRF_RETURN_DONE(funcctx)
</programlisting>
to clean up and end the SRF.
</para>
<para>
A complete pseudo-code example looks like the following:
<programlisting>
Datum
my_Set_Returning_Function(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
Datum result;
[user defined declarations]
if(SRF_IS_FIRSTCALL())
{
[user defined code]
funcctx = SRF_FIRSTCALL_INIT();
[if returning composite]
[obtain slot]
funcctx->slot = slot;
[endif returning composite]
[user defined code]
}
[user defined code]
funcctx = SRF_PERCALL_SETUP();
[user defined code]
if (funcctx->call_cntr < funcctx->max_calls)
{
[user defined code]
[obtain result Datum]
SRF_RETURN_NEXT(funcctx, result);
}
else
{
SRF_RETURN_DONE(funcctx);
}
}
</programlisting>
</para>
<para>
An example of a simple composite returning SRF looks like:
<programlisting>
PG_FUNCTION_INFO_V1(testpassbyval);
Datum
testpassbyval(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
int call_cntr;
int max_calls;
TupleDesc tupdesc;
TupleTableSlot *slot;
AttInMetadata *attinmeta;
/* stuff done only on the first call of the function */
if(SRF_IS_FIRSTCALL())
{
/* create a function context for cross-call persistence */
funcctx = SRF_FIRSTCALL_INIT();
/* total number of tuples to be returned */
funcctx->max_calls = PG_GETARG_UINT32(0);
/*
* 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->slot = slot;
/*
* Generate attribute metadata needed later to produce tuples from raw
* C strings
*/
attinmeta = TupleDescGetAttInMetadata(tupdesc);
funcctx->attinmeta = attinmeta;
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
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 */
{
char **values;
HeapTuple tuple;
Datum result;
/*
* Prepare a values array for storage in our slot.
* This should be an array of C strings which will
* be processed later by the appropriate "in" functions.
*/
values = (char **) palloc(3 * sizeof(char *));
values[0] = (char *) palloc(16 * sizeof(char));
values[1] = (char *) palloc(16 * sizeof(char));
values[2] = (char *) palloc(16 * sizeof(char));
snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));
/* build a tuple */
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
result = TupleGetDatum(slot, tuple);
/* Clean up */
pfree(values[0]);
pfree(values[1]);
pfree(values[2]);
pfree(values);
SRF_RETURN_NEXT(funcctx, result);
}
else /* do when there is no more left */
{
SRF_RETURN_DONE(funcctx);
}
}
</programlisting>
with supporting SQL code of
<programlisting>
CREATE VIEW __testpassbyval AS
SELECT
0::INT4 AS f1,
0::INT4 AS f2,
0::INT4 AS f3;
CREATE OR REPLACE FUNCTION testpassbyval(int4, int4) RETURNS setof __testpassbyval
AS 'MODULE_PATHNAME','testpassbyval' LANGUAGE 'c' IMMUTABLE STRICT;
</programlisting>
</para>
<para>
See contrib/tablefunc for more examples of Table Functions.
</para>
</sect2>