mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
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:
@ -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">
|
<chapter id="xfunc">
|
||||||
@ -1461,12 +1461,348 @@ AS '<replaceable>PGROOT</replaceable>/tutorial/funcs'
|
|||||||
LANGUAGE C;
|
LANGUAGE C;
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Table Function API</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
While there are ways to construct new rows or modify
|
The Table Function API assists in the creation of a user defined
|
||||||
existing rows from within a C function, these
|
C Language table functions (<xref linkend="xfunc-tablefunctions">).
|
||||||
are far too complex to discuss in this manual.
|
Table functions are functions that produce a set of rows, made up of
|
||||||
Consult the backend source code for examples.
|
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>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user