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
+  * <[EMAIL PROTECTED]>, 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
+  * <[EMAIL PROTECTED]>, 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
+  * <[EMAIL PROTECTED]>, 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;

---------------------------(end of broadcast)---------------------------
TIP 1: subscribe and unsubscribe commands go to [EMAIL PROTECTED]

Reply via email to