Re: Table Function API doc patch - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: Table Function API doc patch |
Date | |
Msg-id | 200207160603.g6G631208215@candle.pha.pa.us Whole thread Raw |
In response to | Table Function API doc patch (Joe Conway <mail@joeconway.com>) |
List | pgsql-patches |
Your patch has been added to the PostgreSQL unapplied patches list at: http://candle.pha.pa.us/cgi-bin/pgpatches I will try to apply it within the next 48 hours. --------------------------------------------------------------------------- Joe Conway wrote: > 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. > > If there are no objections, please apply. > > Thanks, > > Joe [ text/html is unsupported, treating like TEXT/PLAIN ] > Index: doc/src/sgml/xfunc.sgml > =================================================================== > RCS file: /opt/src/cvs/pgsql/doc/src/sgml/xfunc.sgml,v > retrieving revision 1.52 > diff -c -r1.52 xfunc.sgml > *** doc/src/sgml/xfunc.sgml 20 Jun 2002 16:57:00 -0000 1.52 > --- doc/src/sgml/xfunc.sgml 10 Jul 2002 22:53:23 -0000 > *************** > *** 1461,1472 **** > LANGUAGE C; > </programlisting> > </para> > > <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. > </para> > </sect2> > > --- 1461,1808 ---- > LANGUAGE C; > </programlisting> > </para> > + </sect2> > + > + <sect2> > + <title>Table Function API</title> > + > + <para> > + 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> > > > ---------------------------(end of broadcast)--------------------------- > TIP 2: you can get off all lists at once with the unregister command > (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 853-3000 + If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania 19026
pgsql-patches by date: