Re: [Fwd: C&SRF API patch (was Re: [HACKERS] revised sample - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: [Fwd: C&SRF API patch (was Re: [HACKERS] revised sample
Date
Msg-id 200206172144.g5HLiVq15695@candle.pha.pa.us
Whole thread Raw
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:
> Tom Lane wrote:
>  > Joe Conway <mail@joeconway.com> writes:
>  >>Is the approach in my patch still too ugly to allow a builtin SRF (set
>  >>the function return type to 0 in pg_proc.h, create a view and fix the
>  >>pg_proc entry during initdb)?
>  >
>  > Too ugly for my taste anyway ...
>
> OK.
>
> Here is a patch for Composite and Set returning function support. I made
> two small changes to the API since last patch, which hopefully completes
> the decoupling of composite function support from SRF specific support.
> If there are no (further ;-)) objections, please apply. I'll send
> another post with a patch for contrib/showguc.
>
> Thanks,
>
> Joe
>

> Index: src/backend/access/common/tupdesc.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/backend/access/common/tupdesc.c,v
> retrieving revision 1.78
> diff -c -r1.78 tupdesc.c
> *** src/backend/access/common/tupdesc.c    29 Mar 2002 19:05:59 -0000    1.78
> --- src/backend/access/common/tupdesc.c    9 Jun 2002 21:00:31 -0000
> ***************
> *** 19,24 ****
> --- 19,27 ----
>
>   #include "postgres.h"
>
> + #include "funcapi.h"
> + #include "access/heapam.h"
> + #include "catalog/namespace.h"
>   #include "catalog/pg_type.h"
>   #include "nodes/parsenodes.h"
>   #include "parser/parse_type.h"
> ***************
> *** 548,551 ****
> --- 551,660 ----
>           desc->constr = NULL;
>       }
>       return desc;
> + }
> +
> +
> + /*
> +  * RelationNameGetTupleDesc
> +  *
> +  * Given a (possibly qualified) relation name, build a TupleDesc.
> +  */
> + TupleDesc
> + RelationNameGetTupleDesc(char *relname)
> + {
> +     RangeVar   *relvar;
> +     Relation    rel;
> +     TupleDesc    tupdesc;
> +     List       *relname_list;
> +
> +     /* Open relation and get the tuple description */
> +     relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
> +     relvar = makeRangeVarFromNameList(relname_list);
> +     rel = heap_openrv(relvar, AccessShareLock);
> +     tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
> +     relation_close(rel, AccessShareLock);
> +
> +     return tupdesc;
> + }
> +
> + /*
> +  * TypeGetTupleDesc
> +  *
> +  * Given a type Oid, build a TupleDesc.
> +  *
> +  * If the type is composite, *and* a colaliases List is provided, *and*
> +  * the List is of natts length, use the aliases instead of the relation
> +  * attnames.
> +  *
> +  * If the type is a base type, a single item alias List is required.
> +  */
> + TupleDesc
> + TypeGetTupleDesc(Oid typeoid, List *colaliases)
> + {
> +     Oid            relid = typeidTypeRelid(typeoid);
> +     TupleDesc    tupdesc;
> +
> +     /*
> +      * Build a suitable tupledesc representing the output rows
> +      */
> +     if (OidIsValid(relid))
> +     {
> +         /* Composite data type, i.e. a table's row type */
> +         Relation    rel;
> +         int            natts;
> +
> +         rel = relation_open(relid, AccessShareLock);
> +         tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
> +         natts = tupdesc->natts;
> +         relation_close(rel, AccessShareLock);
> +
> +         /* check to see if we've given column aliases */
> +         if(colaliases != NIL)
> +         {
> +             char       *label;
> +             int            varattno;
> +
> +             /* does the List length match the number of attributes */
> +             if (length(colaliases) != natts)
> +                 elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
> +
> +             /* OK, use the aliases instead */
> +             for (varattno = 0; varattno < natts; varattno++)
> +             {
> +                 label = strVal(nth(varattno, colaliases));
> +
> +                 if (label != NULL)
> +                     namestrcpy(&(tupdesc->attrs[varattno]->attname), label);
> +                 else
> +                     MemSet(NameStr(tupdesc->attrs[varattno]->attname), 0, NAMEDATALEN);
> +             }
> +         }
> +     }
> +     else
> +     {
> +         /* Must be a base data type, i.e. scalar */
> +         char       *attname;
> +
> +         /* the alias List is required for base types */
> +         if (colaliases == NIL)
> +             elog(ERROR, "TypeGetTupleDesc: no column alias was provided");
> +
> +         /* the alias List length must be 1 */
> +         if (length(colaliases) != 1)
> +             elog(ERROR, "TypeGetTupleDesc: number of aliases does not match number of attributes");
> +
> +         /* OK, get the column alias */
> +         attname = strVal(lfirst(colaliases));
> +
> +         tupdesc = CreateTemplateTupleDesc(1);
> +         TupleDescInitEntry(tupdesc,
> +                            (AttrNumber) 1,
> +                            attname,
> +                            typeoid,
> +                            -1,
> +                            0,
> +                            false);
> +     }
> +
> +     return tupdesc;
>   }
> Index: src/backend/executor/execTuples.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/backend/executor/execTuples.c,v
> retrieving revision 1.51
> diff -c -r1.51 execTuples.c
> *** src/backend/executor/execTuples.c    21 Mar 2002 06:21:04 -0000    1.51
> --- src/backend/executor/execTuples.c    9 Jun 2002 21:00:31 -0000
> ***************
> *** 107,117 ****
>    */
>   #include "postgres.h"
>
>   #include "access/heapam.h"
>   #include "catalog/pg_type.h"
>   #include "executor/executor.h"
>
> -
>   /* ----------------------------------------------------------------
>    *                  tuple table create/delete functions
>    * ----------------------------------------------------------------
> --- 107,117 ----
>    */
>   #include "postgres.h"
>
> + #include "funcapi.h"
>   #include "access/heapam.h"
>   #include "catalog/pg_type.h"
>   #include "executor/executor.h"
>
>   /* ----------------------------------------------------------------
>    *                  tuple table create/delete functions
>    * ----------------------------------------------------------------
> ***************
> *** 673,675 ****
> --- 673,795 ----
>
>       return typeInfo;
>   }
> +
> + /*
> +  * TupleDescGetSlot - Initialize a slot based on the supplied
> +  * tupledesc
> +  */
> + TupleTableSlot *
> + TupleDescGetSlot(TupleDesc tupdesc)
> + {
> +     TupleTableSlot       *slot;
> +
> +     /* Make a standalone slot */
> +     slot = MakeTupleTableSlot();
> +
> +     /* Bind the tuple description to the slot */
> +     ExecSetSlotDescriptor(slot, tupdesc, true);
> +
> +     /* Return the slot */
> +     return slot;
> + }
> +
> + /*
> +  * TupleDescGetAttInMetadata - Get a pointer to AttInMetadata based on the
> +  * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
> +  * to produce a properly formed tuple.
> +  */
> + AttInMetadata *
> + TupleDescGetAttInMetadata(TupleDesc tupdesc)
> + {
> +     int                natts;
> +     int                i;
> +     Oid                atttypeid;
> +     Oid                attinfuncid;
> +     Oid                attelem;
> +     FmgrInfo       *attinfuncinfo;
> +     Oid               *attelems;
> +     int4           *atttypmods;
> +     AttInMetadata  *attinmeta;
> +
> +     attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
> +     natts = tupdesc->natts;
> +
> +     /*
> +      * Gather info needed later to call the "in" function for each attribute
> +      */
> +     attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
> +     attelems = (Oid *) palloc(natts * sizeof(Oid));
> +     atttypmods = (int4 *) palloc(natts * sizeof(int4));
> +
> +     for (i = 0; i < natts; i++)
> +     {
> +         atttypeid = tupdesc->attrs[i]->atttypid;
> +         get_type_metadata(atttypeid, &attinfuncid, &attelem);
> +
> +         fmgr_info(attinfuncid, &attinfuncinfo[i]);
> +         attelems[i] = attelem;
> +         atttypmods[i] = tupdesc->attrs[i]->atttypmod;
> +     }
> +     attinmeta->tupdesc = tupdesc;
> +     attinmeta->attinfuncs = attinfuncinfo;
> +     attinmeta->attelems = attelems;
> +     attinmeta->atttypmods = atttypmods;
> +
> +     return attinmeta;
> + }
> +
> + /*
> +  * BuildTupleFromCStrings - 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.
> +  */
> + HeapTuple
> + BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
> + {
> +     TupleDesc            tupdesc;
> +     int                    natts;
> +     HeapTuple            tuple;
> +     char               *nulls;
> +     int                    i;
> +     Datum               *dvalues;
> +     FmgrInfo            attinfuncinfo;
> +     Oid                    attelem;
> +     int4                atttypmod;
> +
> +     tupdesc = attinmeta->tupdesc;
> +     natts = tupdesc->natts;
> +
> +     dvalues = (Datum *) palloc(natts * sizeof(Datum));
> +
> +     /* Call the "in" function for each attribute */
> +     for (i = 0; i < natts; i++)
> +     {
> +         if (values[i] != NULL)
> +         {
> +             attinfuncinfo = attinmeta->attinfuncs[i];
> +             attelem = attinmeta->attelems[i];
> +             atttypmod = attinmeta->atttypmods[i];
> +
> +             dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
> +                                         ObjectIdGetDatum(attelem),
> +                                         Int32GetDatum(atttypmod));
> +         }
> +         else
> +             dvalues[i] = PointerGetDatum(NULL);
> +     }
> +
> +     /*
> +      * Form a tuple
> +      */
> +     nulls = (char *) palloc(natts * sizeof(char));
> +     for (i = 0; i < natts; i++)
> +     {
> +         if (DatumGetPointer(dvalues[i]) != NULL)
> +             nulls[i] = ' ';
> +         else
> +             nulls[i] = 'n';
> +     }
> +     tuple = heap_formtuple(tupdesc, dvalues, nulls);
> +
> +     return tuple;
> + }
> +
> Index: src/backend/utils/adt/regproc.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/backend/utils/adt/regproc.c,v
> retrieving revision 1.68
> diff -c -r1.68 regproc.c
> *** src/backend/utils/adt/regproc.c    11 May 2002 00:24:16 -0000    1.68
> --- src/backend/utils/adt/regproc.c    9 Jun 2002 21:00:31 -0000
> ***************
> *** 37,44 ****
>   #include "utils/lsyscache.h"
>   #include "utils/syscache.h"
>
> -
> - static List *stringToQualifiedNameList(const char *string, const char *caller);
>   static void parseNameAndArgTypes(const char *string, const char *caller,
>                                    const char *type0_spelling,
>                                    List **names, int *nargs, Oid *argtypes);
> --- 37,42 ----
> ***************
> *** 960,973 ****
>   }
>
>
> - /*****************************************************************************
> -  *     SUPPORT ROUTINES                                                         *
> -  *****************************************************************************/
> -
>   /*
>    * Given a C string, parse it into a qualified-name list.
>    */
> ! static List *
>   stringToQualifiedNameList(const char *string, const char *caller)
>   {
>       char       *rawname;
> --- 958,967 ----
>   }
>
>
>   /*
>    * Given a C string, parse it into a qualified-name list.
>    */
> ! List *
>   stringToQualifiedNameList(const char *string, const char *caller)
>   {
>       char       *rawname;
> ***************
> *** 996,1001 ****
> --- 990,999 ----
>
>       return result;
>   }
> +
> + /*****************************************************************************
> +  *     SUPPORT ROUTINES                                                         *
> +  *****************************************************************************/
>
>   /*
>    * Given a C string, parse it into a qualified function or operator name
> Index: src/backend/utils/fmgr/Makefile
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/backend/utils/fmgr/Makefile,v
> retrieving revision 1.12
> diff -c -r1.12 Makefile
> *** src/backend/utils/fmgr/Makefile    16 Sep 2001 16:11:11 -0000    1.12
> --- src/backend/utils/fmgr/Makefile    9 Jun 2002 21:00:31 -0000
> ***************
> *** 12,18 ****
>   top_builddir = ../../../..
>   include $(top_builddir)/src/Makefile.global
>
> ! OBJS = dfmgr.o fmgr.o
>
>   override CPPFLAGS += -DPKGLIBDIR=\"$(pkglibdir)\" -DDLSUFFIX=\"$(DLSUFFIX)\"
>
> --- 12,18 ----
>   top_builddir = ../../../..
>   include $(top_builddir)/src/Makefile.global
>
> ! OBJS = dfmgr.o fmgr.o funcapi.o
>
>   override CPPFLAGS += -DPKGLIBDIR=\"$(pkglibdir)\" -DDLSUFFIX=\"$(DLSUFFIX)\"
>
> Index: src/backend/utils/fmgr/funcapi.c
> ===================================================================
> RCS file: src/backend/utils/fmgr/funcapi.c
> diff -N src/backend/utils/fmgr/funcapi.c
> *** /dev/null    1 Jan 1970 00:00:00 -0000
> --- src/backend/utils/fmgr/funcapi.c    9 Jun 2002 22:55:47 -0000
> ***************
> *** 0 ****
> --- 1,122 ----
> + /*-------------------------------------------------------------------------
> +  *
> +  * funcapi.c
> +  *      Utility and convenience functions for fmgr functions that return
> +  *      sets and/or composite types.
> +  *
> +  * Copyright (c) 2002, PostgreSQL Global Development Group
> +  *
> +  *-------------------------------------------------------------------------
> +  */
> +
> + #include "funcapi.h"
> + #include "catalog/pg_type.h"
> + #include "utils/syscache.h"
> +
> + /*
> +  * init_MultiFuncCall
> +  * Create an empty FuncCallContext data structure
> +  * and do some other basic Multi-function call setup
> +  * and error checking
> +  */
> + FuncCallContext *
> + init_MultiFuncCall(PG_FUNCTION_ARGS)
> + {
> +     FuncCallContext *retval;
> +
> +     /*
> +      * Bail if we're called in the wrong context
> +      */
> +     if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
> +         elog(ERROR, "function called in context that does not accept a set result");
> +
> +     if (fcinfo->flinfo->fn_extra == NULL)
> +     {
> +         /*
> +          * First call
> +          */
> +         MemoryContext oldcontext;
> +
> +         /* switch to the appropriate memory context */
> +         oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
> +
> +         /*
> +          * allocate space and zero it
> +          */
> +         retval = (FuncCallContext *) palloc(sizeof(FuncCallContext));
> +         MemSet(retval, 0, sizeof(FuncCallContext));
> +
> +         /*
> +          * initialize the elements
> +          */
> +         retval->call_cntr = 0;
> +         retval->max_calls = 0;
> +         retval->slot = NULL;
> +         retval->fctx = NULL;
> +         retval->attinmeta = NULL;
> +         retval->fmctx = fcinfo->flinfo->fn_mcxt;
> +
> +         /*
> +          * save the pointer for cross-call use
> +          */
> +         fcinfo->flinfo->fn_extra = retval;
> +
> +         /* back to the original memory context */
> +         MemoryContextSwitchTo(oldcontext);
> +     }
> +     else    /* second and subsequent calls */
> +     {
> +         elog(ERROR, "init_MultiFuncCall may not be called more than once");
> +
> +         /* never reached, but keep compiler happy */
> +         retval = NULL;
> +     }
> +
> +     return retval;
> + }
> +
> + /*
> +  * end_MultiFuncCall
> +  * Clean up after init_MultiFuncCall
> +  */
> + void
> + end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx)
> + {
> +     MemoryContext oldcontext;
> +
> +     /* unbind from fcinfo */
> +     fcinfo->flinfo->fn_extra = NULL;
> +
> +     /*
> +      * Caller is responsible to free up memory for individual
> +      * struct elements other than att_in_funcinfo and elements.
> +      */
> +     oldcontext = MemoryContextSwitchTo(funcctx->fmctx);
> +
> +     if (funcctx->attinmeta != NULL)
> +         pfree(funcctx->attinmeta);
> +
> +     pfree(funcctx);
> +
> +     MemoryContextSwitchTo(oldcontext);
> + }
> +
> + void
> + get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem)
> + {
> +     HeapTuple        typeTuple;
> +     Form_pg_type    typtup;
> +
> +     typeTuple = SearchSysCache(TYPEOID,
> +                                ObjectIdGetDatum(typeid),
> +                                0, 0, 0);
> +     if (!HeapTupleIsValid(typeTuple))
> +         elog(ERROR, "get_type_metadata: Cache lookup of type %u failed", typeid);
> +
> +     typtup = (Form_pg_type) GETSTRUCT(typeTuple);
> +
> +     *attinfuncid = typtup->typinput;
> +     *attelem = typtup->typelem;
> +
> +     ReleaseSysCache(typeTuple);
> + }
> Index: src/include/funcapi.h
> ===================================================================
> RCS file: src/include/funcapi.h
> diff -N src/include/funcapi.h
> *** /dev/null    1 Jan 1970 00:00:00 -0000
> --- src/include/funcapi.h    10 Jun 2002 00:22:42 -0000
> ***************
> *** 0 ****
> --- 1,197 ----
> + /*-------------------------------------------------------------------------
> +  *
> +  * funcapi.h
> +  *      Definitions for functions which return composite type and/or sets
> +  *
> +  * This file must be included by all Postgres modules that either define
> +  * or call FUNCAPI-callable functions or macros.
> +  *
> +  *
> +  * Copyright (c) 2002, PostgreSQL Global Development Group
> +  *
> +  *
> +  *-------------------------------------------------------------------------
> +  */
> + #ifndef FUNCAPI_H
> + #define FUNCAPI_H
> +
> + #include "postgres.h"
> +
> + #include "fmgr.h"
> + #include "access/htup.h"
> + #include "access/tupdesc.h"
> + #include "executor/executor.h"
> + #include "executor/tuptable.h"
> +
> + /*
> +  * All functions that can be called directly by fmgr must have this signature.
> +  * (Other functions can be called by using a handler that does have this
> +  * signature.)
> +  */
> +
> +
> + /*-------------------------------------------------------------------------
> +  *    Support to ease writing Functions returning composite types
> +  *-------------------------------------------------------------------------
> +  *
> +  * 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 an SRF.
> +  */
> + 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;
> +
> + /*-------------------------------------------------------------------------
> +  *        Support struct to ease writing Set Returning Functions (SRFs)
> +  *-------------------------------------------------------------------------
> +  *
> +  * This struct holds function context for Set Returning Functions.
> +  * Use fn_extra to hold a pointer to it across calls
> +  */
> + typedef struct
> + {
> +     /* Number of times we've been called before */
> +     uint            call_cntr;
> +
> +     /* Maximum number of calls */
> +     uint            max_calls;
> +
> +     /* pointer to result slot */
> +     TupleTableSlot *slot;
> +
> +     /* pointer to misc context info */
> +     void           *fctx;
> +
> +     /* pointer to struct containing arrays of attribute type input metainfo */
> +     AttInMetadata       *attinmeta;
> +
> +     /* memory context used to initialize structure */
> +     MemoryContext    fmctx;
> +
> + }    FuncCallContext;
> +
> + /*-------------------------------------------------------------------------
> +  *    Support to ease writing Functions returning composite types
> +  *
> +  * External declarations:
> +  * TupleDesc RelationNameGetTupleDesc(char *relname) - Use to get a TupleDesc
> +  *        based on the function's return type relation.
> +  * TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases) - Use 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.
> +  * TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc) - Initialize a slot
> +  *        given a TupleDesc.
> +  * AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc) - Get a pointer
> +  *        to AttInMetadata based on the function's TupleDesc. AttInMetadata can
> +  *        be used in conjunction with C strings to produce a properly formed
> +  *        tuple. Store the metadata here for use across calls to avoid redundant
> +  *        work.
> +  * HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) -
> +  *        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.
> +  *
> +  * Macro declarations:
> +  * TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple) - get a Datum
> +  *        given a tuple and a slot.
> +  */
> +
> + /* from tupdesc.c */
> + extern TupleDesc RelationNameGetTupleDesc(char *relname);
> + extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
> +
> + /* from execTuples.c */
> + extern TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc);
> + extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
> + extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
> +
> + /* from funcapi.c */
> + extern void get_type_metadata(Oid typeid, Oid *attinfuncid, Oid *attelem);
> +
> + #define TupleGetDatum(_slot, _tuple) \
> +     PointerGetDatum(ExecStoreTuple(_tuple, _slot, InvalidBuffer, true))
> +
> + /*-------------------------------------------------------------------------
> +  *        Support for Set Returning Functions (SRFs)
> +  *
> +  * The basic API for SRFs looks something like:
> +  *
> +  * Datum
> +  * my_Set_Returning_Function(PG_FUNCTION_ARGS)
> +  * {
> +  *     FuncCallContext       *funcctx;
> +  *     Datum                result;
> +  *     <user defined declarations>
> +  *
> +  *     if(SRF_IS_FIRSTPASS())
> +  *     {
> +  *         <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(funcctx);
> +  *     <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);
> +  *     }
> +  * }
> +  *
> +  */
> +
> + /* from funcapi.c */
> + extern FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
> + extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx);
> +
> + #define SRF_IS_FIRSTPASS() (fcinfo->flinfo->fn_extra == NULL)
> + #define SRF_FIRSTCALL_INIT() init_MultiFuncCall(fcinfo)
> + #define SRF_PERCALL_SETUP(_funcctx) \
> +     fcinfo->flinfo->fn_extra; \
> +     if(_funcctx->slot != NULL) \
> +         ExecClearTuple(_funcctx->slot)
> + #define SRF_RETURN_NEXT(_funcctx, _result) \
> +     do { \
> +         ReturnSetInfo       *rsi; \
> +         _funcctx->call_cntr++; \
> +         rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
> +         rsi->isDone = ExprMultipleResult; \
> +         PG_RETURN_DATUM(_result); \
> +     } while (0)
> +
> + #define  SRF_RETURN_DONE(_funcctx) \
> +     do { \
> +         ReturnSetInfo       *rsi; \
> +         end_MultiFuncCall(fcinfo, _funcctx); \
> +         rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
> +         rsi->isDone = ExprEndResult; \
> +         _funcctx->slot = NULL; \
> +         PG_RETURN_NULL(); \
> +     } while (0)
> +
> + #endif   /* FUNCAPI_H */
> Index: src/include/utils/builtins.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql/src/include/utils/builtins.h,v
> retrieving revision 1.182
> diff -c -r1.182 builtins.h
> *** src/include/utils/builtins.h    18 May 2002 21:38:41 -0000    1.182
> --- src/include/utils/builtins.h    9 Jun 2002 21:00:31 -0000
> ***************
> *** 341,346 ****
> --- 341,347 ----
>   extern Datum regclassout(PG_FUNCTION_ARGS);
>   extern Datum regtypein(PG_FUNCTION_ARGS);
>   extern Datum regtypeout(PG_FUNCTION_ARGS);
> + extern List *stringToQualifiedNameList(const char *string, const char *caller);
>
>   /* ruleutils.c */
>   extern Datum pg_get_ruledef(PG_FUNCTION_ARGS);
>

>
> ---------------------------(end of broadcast)---------------------------
> TIP 3: if posting/reading through Usenet, please send an appropriate
> subscribe-nomail command to majordomo@postgresql.org so that your
> message can get through to the mailing list cleanly
>

--
  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:

Previous
From: Peter Eisentraut
Date:
Subject: Re: 2nd revision of SSL patches
Next
From: Bruce Momjian
Date:
Subject: Re: [Fwd: contrib/showguc (was Re: [HACKERS] revised sample