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

From Joe Conway
Subject Re: revised sample SRF C function; proposed SRF API
Date
Msg-id 3CF1995C.3020501@joeconway.com
Whole thread Raw
In response to revised sample SRF C function; proposed SRF API  (Joe Conway <mail@joeconway.com>)
List pgsql-hackers
Joe Conway wrote:
> 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:
>

Sorry -- I was a bit too quick with the last patch :-(. It generates a
"Cache reference leak" warning. This one is better.

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 02:03:19 -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(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(_values, _funcctx) \
+     do { \
+         _funcctx->slot = store_slot(_values, _funcctx); \
+     } while (0)
+
+ #define  FUNC_RETURN_NEXT(_funcctx) \
+     do { \
+         ReturnSetInfo       *rsi; \
+         _funcctx->call_cntr++; \
+         rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+         rsi->isDone = ExprMultipleResult; \
+         PG_RETURN_POINTER(_funcctx->slot); \
+     } while (0)
+
+
+ #define  FUNC_RETURN_DONE(_funcctx) \
+     do { \
+         ReturnSetInfo       *rsi; \
+         end_MultiFuncCall(fcinfo, _funcctx); \
+         rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+         rsi->isDone = ExprEndResult; \
+         _funcctx->slot = NULL; \
+         PG_RETURN_POINTER(_funcctx->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;
+     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;
+     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(values, funcctx);
+
+         /* Clean up */
+         pfree(varname);
+         pfree(values);
+
+         FUNC_RETURN_NEXT(funcctx);
+     }
+     else
+     {
+         /* All done! */
+         FUNC_RETURN_DONE(funcctx);
+     }
+ }
+
+ /* 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);
+         ReleaseSysCache(att_type);
+     }
+
+     funcctx->att_in_funcinfo = att_in_funcinfo;
+     funcctx->elements = elements;
+
+     /*
+      * Return the slot!
+      */
+     return slot;
+ }
+
+ static TupleTableSlot *
+ store_slot(char **values, FuncCallContext *funcctx)
+ {
+     TupleTableSlot       *slot = funcctx->slot;
+     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: Joe Conway
Date:
Subject: revised sample SRF C function; proposed SRF API
Next
From: Teodor Sigaev
Date:
Subject: Re: strange update problem with 7.2.1