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
+ * <[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 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
+ * <[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(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
+ * <[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 4: Don't 'kill -9' the postmaster