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