revised sample SRF C function; proposed SRF API - Mailing list pgsql-hackers

From Joe Conway
Subject revised sample SRF C function; proposed SRF API
Date
Msg-id 3CF18C38.9050200@joeconway.com
Whole thread Raw
Responses Re: revised sample SRF C function; proposed SRF API
Re: revised sample SRF C function; proposed SRF API
List pgsql-hackers
Here is a revised patch for a sample C function returning setof
composite. (Same comments as last time -- It is a clone of SHOW ALL as
an SRF. For the moment, the function is implemented as contrib/showguc,
although a few minor changes to guc.c and guc.h were required to support
it.)

This version includes pieces that may be appropriate for fmgr.c and
fmgr.h, to hide some of the ugliness and facilitate writing C functions
which return setof composite. The API is something like this:

An SRF written in C must define a variable of type (FuncCallContext *).
The structure of FuncCallContext looks like:

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 array of attribute "type"in finfo */
    FmgrInfo *att_in_funcinfo;

    /* pointer to array of attribute type typelem */
    Oid *elements;

    /* memory context used to initialize structure */
    MemoryContext fmctx;
} FuncCallContext;

The first line after the function declarations should be:

    FUNC_MULTIPLE_RESULT(funcctx, relname, max_calls, misc_ctx);

where
    funcctx is the pointer to FuncCallContext. This is required.
    relname is the relation name for the composite type the function
       returns. This is required.
    max_calls is the maximum number of times the function is expected
       to return results. You don't have to provide or use this.
    misc_ctx is a pointer available for the user to store anything needed
       to retain context from call to call (i.e. this is what you
       previously might have assigned to fcinfo->flinfo->fn_extra).
       You don't have to provide or use this.

Next, use funcctx->call_cntr and funcctx->max_calls (or whatever method
you want) to determine whether or not the function is done returning
results. If not, prepare an array of C strings representing the
attribute values of your return tuple, and call:

    FUNC_BUILD_SLOT(values, funcctx);

This applies the attribute "in" functions to your values, and stores the
results in a TupleTableSlot.

Next, clean up as appropriate, and call:

    FUNC_RETURN_NEXT(funcctx);

This increments funcctx->call_cntr in preparation for the next call,
sets rsi->isDone = ExprMultipleResult to let the caller know we're not
done yet, and returns the slot.

Finally, when funcctx->call_cntr = funcctx->max_calls, call:

    FUNC_RETURN_DONE(funcctx);

This does some cleanup, sets rsi->isDone = ExprEndResult, and returns a
NULL slot.

I made some changes to pull as much into the first call initialization
as possible to save redundant work on subsequent calls. For the 96 rows
returned by this function, EXPLAIN ANALYZE time went from ~1.6 msec
using the first patch, to ~0.9 msec using this one.

Comments? Should this (maybe with a few tweaks) go into fmgr.c and
fmgr.h as the SRF API?

Thanks,

Joe


Index: contrib/showguc/Makefile
===================================================================
RCS file: contrib/showguc/Makefile
diff -N contrib/showguc/Makefile
*** /dev/null    1 Jan 1970 00:00:00 -0000
--- contrib/showguc/Makefile    27 May 2002 00:24:44 -0000
***************
*** 0 ****
--- 1,9 ----
+ subdir = contrib/showguc
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+
+ MODULES = showguc
+ DATA_built = showguc.sql
+ DOCS = README.showguc
+
+ include $(top_srcdir)/contrib/contrib-global.mk
Index: contrib/showguc/README.showguc
===================================================================
RCS file: contrib/showguc/README.showguc
diff -N contrib/showguc/README.showguc
*** /dev/null    1 Jan 1970 00:00:00 -0000
--- contrib/showguc/README.showguc    26 May 2002 05:46:57 -0000
***************
*** 0 ****
--- 1,77 ----
+ /*
+  * showguc
+  *
+  * Sample function to demonstrate a C function which returns setof composite.
+  *
+  * Copyright 2002 by PostgreSQL Global Development Group
+  * Cloned from src/backend/utils/misc/guc.c which was written by Peter Eisentraut
+  * <peter_e@gmx.net>, and modified to suit.
+  *
+  * Permission to use, copy, modify, and distribute this software and its
+  * documentation for any purpose, without fee, and without a written agreement
+  * is hereby granted, provided that the above copyright notice and this
+  * paragraph and the following two paragraphs appear in all copies.
+  *
+  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
+  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+  * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+  * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO
+  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+  *
+  */
+
+
+ Version 0.1 (25 May, 2002):
+   First release
+
+ Release Notes:
+
+   Version 0.1
+     - initial release
+
+ Installation:
+   Place these files in a directory called 'showguc' under 'contrib' in the PostgreSQL source tree. Then run:
+
+     make
+     make install
+
+   You can use showguc.sql to create the functions in your database of choice, e.g.
+
+     psql -U postgres template1 < showguc.sql
+
+   installs following functions into database template1:
+
+      showvars() - returns all GUC variables
+
+ Documentation
+ ==================================================================
+ Name
+
+ showvars() - returns all GUC variables
+
+ Synopsis
+
+ showvars()
+
+ Inputs
+
+   none
+
+ Outputs
+
+   Returns setof __gucvar, where __gucvar is varname TEXT, varval TEXT. All
+     GUC variables displayed by SHOW ALL are returned as a set.
+
+ Example usage
+
+   select showvars();
+
+ ==================================================================
+ -- Joe Conway
+
Index: contrib/showguc/showguc.c
===================================================================
RCS file: contrib/showguc/showguc.c
diff -N contrib/showguc/showguc.c
*** /dev/null    1 Jan 1970 00:00:00 -0000
--- contrib/showguc/showguc.c    27 May 2002 00:21:28 -0000
***************
*** 0 ****
--- 1,726 ----
+ /*--------------------------------------------------------------------
+  * showguc.c
+  *
+  * Sample function to demonstrate a C function which returns setof composite.
+  *
+  * Copyright 2002 by PostgreSQL Global Development Group
+  * Cloned from src/backend/utils/misc/guc.c which was written by Peter Eisentraut
+  * <peter_e@gmx.net>, and modified to suit.
+  *--------------------------------------------------------------------
+  */
+
+ #include "postgres.h"
+
+ #include <errno.h>
+ #include <float.h>
+ #include <limits.h>
+ #include <unistd.h>
+
+ #include "fmgr.h"
+ #include "showguc.h"
+
+ #include "utils/guc.h"
+ #include "access/xlog.h"
+ #include "access/heapam.h"
+ #include "catalog/namespace.h"
+ #include "catalog/pg_type.h"
+ #include "commands/async.h"
+ #include "commands/variable.h"
+ #include "executor/executor.h"
+ #include "libpq/auth.h"
+ #include "libpq/pqcomm.h"
+ #include "miscadmin.h"
+ #include "optimizer/cost.h"
+ #include "optimizer/geqo.h"
+ #include "optimizer/paths.h"
+ #include "optimizer/planmain.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_type.h"
+ #include "storage/fd.h"
+ #include "storage/freespace.h"
+ #include "storage/lock.h"
+ #include "storage/proc.h"
+ #include "tcop/tcopprot.h"
+ #include "utils/array.h"
+ #include "utils/builtins.h"
+ #include "utils/datetime.h"
+ #include "utils/elog.h"
+ #include "utils/pg_locale.h"
+ #include "utils/syscache.h"
+ #include "pgstat.h"
+
+
+ /* XXX these should be in other modules' header files */
+ extern bool Log_connections;
+ extern int    PreAuthDelay;
+ extern int    AuthenticationTimeout;
+ extern int    CheckPointTimeout;
+ extern int    CommitDelay;
+ extern int    CommitSiblings;
+ extern bool FixBTree;
+
+ #ifdef HAVE_SYSLOG
+ extern char *Syslog_facility;
+ extern char *Syslog_ident;
+
+ #endif
+
+ /*
+  * Debugging options
+  */
+ #ifdef USE_ASSERT_CHECKING
+ bool        assert_enabled = true;
+ #endif
+ bool        Debug_print_query = false;
+ bool        Debug_print_plan = false;
+ bool        Debug_print_parse = false;
+ bool        Debug_print_rewritten = false;
+ bool        Debug_pretty_print = false;
+
+ bool        Show_parser_stats = false;
+ bool        Show_planner_stats = false;
+ bool        Show_executor_stats = false;
+ bool        Show_query_stats = false;    /* this is sort of all three above
+                                          * together */
+ bool        Show_btree_build_stats = false;
+
+ bool        Explain_pretty_print = true;
+
+ bool        SQL_inheritance = true;
+
+ bool        Australian_timezones = false;
+
+ bool        Password_encryption = false;
+
+ #ifndef PG_KRB_SRVTAB
+ #define PG_KRB_SRVTAB ""
+ #endif
+
+ /*
+  * Declarations for GUC tables
+  *
+  * See src/backend/utils/misc/README for design notes.
+  */
+ enum config_type
+ {
+     PGC_BOOL,
+     PGC_INT,
+     PGC_REAL,
+     PGC_STRING
+ };
+
+ /* Generic fields applicable to all types of variables */
+ struct config_generic
+ {
+     /* constant fields, must be set correctly in initial value: */
+     const char *name;            /* name of variable - MUST BE FIRST */
+     GucContext    context;        /* context required to set the variable */
+     int            flags;            /* flag bits, see below */
+     /* variable fields, initialized at runtime: */
+     enum config_type vartype;    /* type of variable (set only at startup) */
+     int            status;            /* status bits, see below */
+     GucSource    reset_source;    /* source of the reset_value */
+     GucSource    session_source;    /* source of the session_value */
+     GucSource    tentative_source; /* source of the tentative_value */
+     GucSource    source;            /* source of the current actual value */
+ };
+
+ /* bit values in flags field */
+ #define GUC_LIST_INPUT        0x0001    /* input can be list format */
+ #define GUC_LIST_QUOTE        0x0002    /* double-quote list elements */
+ #define GUC_NO_SHOW_ALL        0x0004    /* exclude from SHOW ALL */
+ #define GUC_NO_RESET_ALL    0x0008    /* exclude from RESET ALL */
+
+ /* bit values in status field */
+ #define GUC_HAVE_TENTATIVE    0x0001    /* tentative value is defined */
+ #define GUC_HAVE_LOCAL        0x0002    /* a SET LOCAL has been executed */
+
+
+ /* GUC records for specific variable types */
+
+ struct config_bool
+ {
+     struct config_generic gen;
+     /* these fields must be set correctly in initial value: */
+     /* (all but reset_val are constants) */
+     bool       *variable;
+     bool        reset_val;
+     bool        (*assign_hook) (bool newval, bool doit, bool interactive);
+     const char *(*show_hook) (void);
+     /* variable fields, initialized at runtime: */
+     bool        session_val;
+     bool        tentative_val;
+ };
+
+ struct config_int
+ {
+     struct config_generic gen;
+     /* these fields must be set correctly in initial value: */
+     /* (all but reset_val are constants) */
+     int           *variable;
+     int            reset_val;
+     int            min;
+     int            max;
+     bool        (*assign_hook) (int newval, bool doit, bool interactive);
+     const char *(*show_hook) (void);
+     /* variable fields, initialized at runtime: */
+     int            session_val;
+     int            tentative_val;
+ };
+
+ struct config_real
+ {
+     struct config_generic gen;
+     /* these fields must be set correctly in initial value: */
+     /* (all but reset_val are constants) */
+     double       *variable;
+     double        reset_val;
+     double        min;
+     double        max;
+     bool        (*assign_hook) (double newval, bool doit, bool interactive);
+     const char *(*show_hook) (void);
+     /* variable fields, initialized at runtime: */
+     double        session_val;
+     double        tentative_val;
+ };
+
+ struct config_string
+ {
+     struct config_generic gen;
+     /* these fields must be set correctly in initial value: */
+     /* (all are constants) */
+     char      **variable;
+     const char *boot_val;
+     const char *(*assign_hook) (const char *newval, bool doit, bool interactive);
+     const char *(*show_hook) (void);
+     /* variable fields, initialized at runtime: */
+     char       *reset_val;
+     char       *session_val;
+     char       *tentative_val;
+ };
+
+ /* Macros for freeing malloc'd pointers only if appropriate to do so */
+ /* Some of these tests are probably redundant, but be safe ... */
+ #define SET_STRING_VARIABLE(rec, newval) \
+     do { \
+         if (*(rec)->variable && \
+             *(rec)->variable != (rec)->reset_val && \
+             *(rec)->variable != (rec)->session_val && \
+             *(rec)->variable != (rec)->tentative_val) \
+             free(*(rec)->variable); \
+         *(rec)->variable = (newval); \
+     } while (0)
+ #define SET_STRING_RESET_VAL(rec, newval) \
+     do { \
+         if ((rec)->reset_val && \
+             (rec)->reset_val != *(rec)->variable && \
+             (rec)->reset_val != (rec)->session_val && \
+             (rec)->reset_val != (rec)->tentative_val) \
+             free((rec)->reset_val); \
+         (rec)->reset_val = (newval); \
+     } while (0)
+ #define SET_STRING_SESSION_VAL(rec, newval) \
+     do { \
+         if ((rec)->session_val && \
+             (rec)->session_val != *(rec)->variable && \
+             (rec)->session_val != (rec)->reset_val && \
+             (rec)->session_val != (rec)->tentative_val) \
+             free((rec)->session_val); \
+         (rec)->session_val = (newval); \
+     } while (0)
+ #define SET_STRING_TENTATIVE_VAL(rec, newval) \
+     do { \
+         if ((rec)->tentative_val && \
+             (rec)->tentative_val != *(rec)->variable && \
+             (rec)->tentative_val != (rec)->reset_val && \
+             (rec)->tentative_val != (rec)->session_val) \
+             free((rec)->tentative_val); \
+         (rec)->tentative_val = (newval); \
+     } while (0)
+
+
+ /*
+  * This struct holds function context.
+  * 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 array of attribute "type"in finfo */
+     FmgrInfo       *att_in_funcinfo;
+
+     /* pointer to array of attribute type typelem */
+     Oid               *elements;
+
+     /* memory context used to initialize structure */
+     MemoryContext    fmctx;
+
+ }    FuncCallContext;
+
+ static char *GetNextGUCConfig(struct config_generic **guc_variables,
+                                 uint varnum, char **varname);
+ static char *_GetOption(struct config_generic *record);
+ static TupleTableSlot *first_pass_setup(char *relname, FuncCallContext *funcctx);
+ static TupleTableSlot *store_slot(TupleTableSlot *slot, char **values, FuncCallContext *funcctx);
+ static FuncCallContext *init_MultiFuncCall(PG_FUNCTION_ARGS);
+ static void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *fctx);
+ static Oid get_type_element(Oid type);
+ static Oid get_type_infunc(Type typ);
+
+ /*
+  * The following macros are candidates to go into fmgr.h
+  */
+ #define  FUNC_MULTIPLE_RESULT(_funcctx, _relname, _max_calls, _fctx) \
+     do { \
+         _funcctx = init_MultiFuncCall(fcinfo); \
+         if (_funcctx->call_cntr == 0) \
+         { \
+             _funcctx->max_calls = _max_calls; \
+             _funcctx->slot = first_pass_setup(_relname, _funcctx); \
+             _funcctx->fctx = _fctx; \
+         } \
+         else \
+             ExecClearTuple(_funcctx->slot); \
+     } while (0)
+
+ #define  FUNC_BUILD_SLOT(_slot, _values, _funcctx) \
+     do { \
+         _slot = store_slot(_slot, _values, _funcctx); \
+     } while (0)
+
+ #define  FUNC_RETURN_NEXT(_funcctx, _slot) \
+     do { \
+         ReturnSetInfo       *rsi; \
+         _funcctx->call_cntr++; \
+         rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+         rsi->isDone = ExprMultipleResult; \
+         PG_RETURN_POINTER(_slot); \
+     } while (0)
+
+
+ #define  FUNC_RETURN_DONE(_funcctx, _slot) \
+     do { \
+         ReturnSetInfo       *rsi; \
+         end_MultiFuncCall(fcinfo, _funcctx); \
+         rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+         rsi->isDone = ExprEndResult; \
+         _slot = NULL; \
+         PG_RETURN_POINTER(_slot); \
+     } while (0)
+
+ /*
+  * SHOW command
+  */
+ PG_FUNCTION_INFO_V1(showguc);
+
+ Datum
+ showguc(PG_FUNCTION_ARGS)
+ {
+     FuncCallContext       *funcctx;
+     char               *relname = "__gucvar";
+     char               *varname = NULL;
+     char               *varval;
+     char               **values;
+     TupleTableSlot       *slot;
+     uint                call_cntr;
+     uint                max_calls;
+     struct config_generic **fctx;
+
+     /*
+      * Setup the function context for multiple calls
+      */
+     FUNC_MULTIPLE_RESULT(funcctx, relname, get_num_guc_variables(), get_guc_variables());
+
+     /*
+      * All set up now, so use what we've got
+      */
+     call_cntr = funcctx->call_cntr;
+     max_calls = funcctx->max_calls;
+     slot = funcctx->slot;
+     fctx = (struct config_generic **) funcctx->fctx;
+
+     /*
+      * Are there any more results to send?
+      */
+     if (call_cntr < max_calls)
+     {
+         /*
+          * Get the next GUC variable name and value
+          */
+         varval = GetNextGUCConfig(fctx, call_cntr, &varname);
+
+         /*
+          * 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(2 * sizeof(char *));
+         values[0] = varname;
+         values[1] = varval;
+
+         /* Store the values */
+         FUNC_BUILD_SLOT(slot, values, funcctx);
+
+         /* Clean up */
+         pfree(varname);
+         pfree(values);
+
+         FUNC_RETURN_NEXT(funcctx, slot);
+     }
+     else
+     {
+         /* All done! */
+         FUNC_RETURN_DONE(funcctx, slot);
+     }
+ }
+
+ /* internal functions */
+
+ /*
+  * SHOW ALL command
+  */
+ static char *
+ GetNextGUCConfig(struct config_generic **guc_variables, uint varnum, char **varname)
+ {
+     struct config_generic *conf = guc_variables[varnum];
+
+     *varname = pstrdup(conf->name);
+
+     if ((conf->flags & GUC_NO_SHOW_ALL) == 0)
+         return _GetOption(conf);
+     else
+         return NULL;
+
+ }
+
+ static char *
+ _GetOption(struct config_generic *record)
+ {
+     char        buffer[256];
+     const char *val;
+     char       *retval;
+
+     switch (record->vartype)
+     {
+         case PGC_BOOL:
+             {
+                 struct config_bool *conf = (struct config_bool *) record;
+
+                 if (conf->show_hook)
+                     val = (*conf->show_hook) ();
+                 else
+                     val = *conf->variable ? "on" : "off";
+             }
+             break;
+
+         case PGC_INT:
+             {
+                 struct config_int *conf = (struct config_int *) record;
+
+                 if (conf->show_hook)
+                     val = (*conf->show_hook) ();
+                 else
+                 {
+                     snprintf(buffer, sizeof(buffer), "%d",
+                              *conf->variable);
+                     val = buffer;
+                 }
+             }
+             break;
+
+         case PGC_REAL:
+             {
+                 struct config_real *conf = (struct config_real *) record;
+
+                 if (conf->show_hook)
+                     val = (*conf->show_hook) ();
+                 else
+                 {
+                     snprintf(buffer, sizeof(buffer), "%g",
+                              *conf->variable);
+                     val = buffer;
+                 }
+             }
+             break;
+
+         case PGC_STRING:
+             {
+                 struct config_string *conf = (struct config_string *) record;
+
+                 if (conf->show_hook)
+                     val = (*conf->show_hook) ();
+                 else if (*conf->variable && **conf->variable)
+                     val = *conf->variable;
+                 else
+                     val = "unset";
+             }
+             break;
+
+         default:
+             /* just to keep compiler quiet */
+             val = "???";
+             break;
+     }
+     retval = pstrdup(val);
+
+     return retval;
+ }
+
+ /*
+  * The following functions are candidates to go into fmgr.c
+  */
+ static TupleTableSlot *
+ first_pass_setup(char *relname, FuncCallContext *funcctx)
+ {
+     TupleTableSlot       *slot;
+     Oid                    relid;
+     Relation            rel;
+     TupleDesc            tupdesc;
+     int                    natts;
+     int                    i;
+     Type                att_type;
+     Oid                    att_in_funcoid;
+     FmgrInfo           *att_in_funcinfo;
+     Oid                   *elements;
+
+     /*
+      * Make a standalone slot
+      */
+     slot = MakeTupleTableSlot();
+
+     /*
+      * Open relation and get the tuple description
+      */
+     relid = RelnameGetRelid(relname);
+     rel = relation_open(relid, AccessShareLock);
+     tupdesc = CreateTupleDescCopy(rel->rd_att);
+     relation_close(rel, AccessShareLock);
+     natts = tupdesc->natts;
+
+     /*
+      * Bind the tuple description to the slot
+      */
+     ExecSetSlotDescriptor(slot, tupdesc, true);
+
+     /*
+      * Gather info needed later to call the "in" function for each attribute
+      */
+     att_in_funcinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
+     elements = (Oid *) palloc(natts * sizeof(Oid));
+
+     for (i = 0; i < natts; i++)
+     {
+         att_type = typeidType(tupdesc->attrs[i]->atttypid);
+         att_in_funcoid = get_type_infunc(att_type);
+         fmgr_info(att_in_funcoid, &att_in_funcinfo[i]);
+         elements[i] = get_type_element(tupdesc->attrs[i]->atttypid);
+     }
+
+     funcctx->att_in_funcinfo = att_in_funcinfo;
+     funcctx->elements = elements;
+
+     /*
+      * Return the slot!
+      */
+     return slot;
+ }
+
+ static TupleTableSlot *
+ store_slot(TupleTableSlot *slot, char **values, FuncCallContext *funcctx)
+ {
+     TupleDesc            tupdesc;
+     int                    natts;
+     HeapTuple            tuple;
+     char               *nulls;
+     int                    i;
+     Datum               *dvalues;
+     FmgrInfo            att_in_funcinfo;
+     Oid                    element;
+
+     /*
+      * Get the tuple description
+      */
+     tupdesc = slot->ttc_tupleDescriptor;
+     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)
+         {
+             att_in_funcinfo = funcctx->att_in_funcinfo[i];
+             element = funcctx->elements[i];
+
+             dvalues[i] = FunctionCall3(&att_in_funcinfo, CStringGetDatum(values[i]),
+                                         ObjectIdGetDatum(element),
+                                         Int32GetDatum(tupdesc->attrs[i]->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);
+
+     /*
+      * Save the tuple in the tuple slot
+      */
+     slot = ExecStoreTuple(tuple,            /* tuple to store */
+                           slot,                /* slot to store in */
+                           InvalidBuffer,    /* buffer associated with
+                                              * this tuple */
+                           true);            /* pfree this pointer */
+
+     /*
+      * Clean up
+      */
+     pfree(nulls);
+     pfree(dvalues);
+
+     /*
+      * Return the slot!
+      */
+     return slot;
+ }
+
+
+ /*
+  * 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->att_in_funcinfo = NULL;
+         retval->elements = 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 */
+         retval = fcinfo->flinfo->fn_extra;
+
+     return retval;
+ }
+
+
+ /*
+  * end_MultiFuncCall
+  * Clean up
+  */
+ 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->att_in_funcinfo != NULL)
+         pfree(funcctx->att_in_funcinfo);
+
+     if (funcctx->elements != NULL)
+         pfree(funcctx->elements);
+
+     pfree(funcctx);
+
+     MemoryContextSwitchTo(oldcontext);
+ }
+
+
+ static Oid
+ get_type_element(Oid type)
+ {
+     HeapTuple    typeTuple;
+     Oid            result;
+
+     typeTuple = SearchSysCache(TYPEOID,
+                                ObjectIdGetDatum(type),
+                                0, 0, 0);
+     if (!HeapTupleIsValid(typeTuple))
+         elog(ERROR, "get_type_element: Cache lookup of type %u failed", type);
+     result = ((Form_pg_type) GETSTRUCT(typeTuple))->typelem;
+     ReleaseSysCache(typeTuple);
+     return result;
+ }
+
+
+ static Oid
+ get_type_infunc(Type typ)
+ {
+     Form_pg_type typtup;
+
+     typtup = (Form_pg_type) GETSTRUCT(typ);
+
+     return typtup->typinput;
+ }
+
Index: contrib/showguc/showguc.h
===================================================================
RCS file: contrib/showguc/showguc.h
diff -N contrib/showguc/showguc.h
*** /dev/null    1 Jan 1970 00:00:00 -0000
--- contrib/showguc/showguc.h    26 May 2002 02:06:52 -0000
***************
*** 0 ****
--- 1,15 ----
+ /*
+  * showguc.c
+  *
+  * Sample function to demonstrate a C function which returns setof composite.
+  *
+  * Copyright 2002 by PostgreSQL Global Development Group
+  * Cloned from src/backend/utils/misc/guc.c which was written by Peter Eisentraut
+  * <peter_e@gmx.net>, and modified to suit.
+  */
+ #ifndef SHOWGUC_H
+ #define SHOWGUC_H
+
+ extern Datum showguc(PG_FUNCTION_ARGS);
+
+ #endif   /* SHOWGUC_H */
Index: contrib/showguc/showguc.sql.in
===================================================================
RCS file: contrib/showguc/showguc.sql.in
diff -N contrib/showguc/showguc.sql.in
*** /dev/null    1 Jan 1970 00:00:00 -0000
--- contrib/showguc/showguc.sql.in    26 May 2002 05:36:18 -0000
***************
*** 0 ****
--- 1,7 ----
+ CREATE TABLE __gucvar(
+   varname TEXT,
+   varval TEXT
+ );
+
+ CREATE FUNCTION showvars() RETURNS setof __gucvar
+   AS 'MODULE_PATHNAME','showguc' LANGUAGE 'c' STABLE STRICT;
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /opt/src/cvs/pgsql/src/backend/utils/misc/guc.c,v
retrieving revision 1.69
diff -c -r1.69 guc.c
*** src/backend/utils/misc/guc.c    17 May 2002 20:32:29 -0000    1.69
--- src/backend/utils/misc/guc.c    26 May 2002 02:20:26 -0000
***************
*** 2539,2541 ****
--- 2539,2554 ----

      return newarray;
  }
+
+ struct config_generic **
+ get_guc_variables(void)
+ {
+     return guc_variables;
+ }
+
+ int
+ get_num_guc_variables(void)
+ {
+     return num_guc_variables;
+ }
+
Index: src/include/utils/guc.h
===================================================================
RCS file: /opt/src/cvs/pgsql/src/include/utils/guc.h,v
retrieving revision 1.17
diff -c -r1.17 guc.h
*** src/include/utils/guc.h    17 May 2002 01:19:19 -0000    1.17
--- src/include/utils/guc.h    26 May 2002 02:21:00 -0000
***************
*** 97,102 ****
--- 97,105 ----
  extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value);
  extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);

+ extern struct config_generic **get_guc_variables(void);
+ extern int get_num_guc_variables(void);
+
  extern bool Debug_print_query;
  extern bool Debug_print_plan;
  extern bool Debug_print_parse;

pgsql-hackers by date:

Previous
From: Tatsuo Ishii
Date:
Subject: Re: pgstatindex
Next
From: Joe Conway
Date:
Subject: Re: revised sample SRF C function; proposed SRF API