Re: Configuration parameters for plugin modules - Mailing list pgsql-patches

From Thomas Hallgren
Subject Re: Configuration parameters for plugin modules
Date
Msg-id c8gii5$893$1@news.hub.org
Whole thread Raw
In response to Configuration parameters for plugin modules  (Thomas Hallgren <thhal@mailblocks.com>)
Responses Re: Configuration parameters for plugin modules
List pgsql-patches
Same patch as before but I added a small change to the gram.y enabling
SET command to use qualified names.

- thomas
Index: doc/src/sgml/runtime.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/runtime.sgml,v
retrieving revision 1.263
diff -u -r1.263 runtime.sgml
--- doc/src/sgml/runtime.sgml    29 Apr 2004 04:37:09 -0000    1.263
+++ doc/src/sgml/runtime.sgml    19 May 2004 21:01:40 -0000
@@ -2924,6 +2924,60 @@
     </variablelist>
    </sect2>

+   <sect2 id="runtime-config-custom">
+    <title>Customized Options</title>
+
+    <para>
+     The following was designed to allow options not normally known to
+     <productname>PostgreSQL</productname> to be declared in the posgresql.conf
+     file and/or manipulated using the <command>SET</command> in a controlled
+     manner so that add-on modules to the postgres proper (such as lanugage
+     mappings for triggers and functions) can be configured in a unified way.
+    </para>
+
+    <variablelist>
+
+     <varlistentry id="guc-custom_variable_classes" xreflabel="custom_variable_classes">
+      <term><varname>custom_variable_classes</varname> (<type>string</type>)</term>
+      <indexterm><primary>custom_variable_classes</></>
+      <listitem>
+       <para>
+        This variable specifies one or several classes to be used for custom
+        variables. A custom variable is a variable not normally known to
+        the <productname>PostgreSQL</productname> proper but used by some add
+        on module.
+       </para>
+
+       <para>
+        Aribtrary variables can be defined for each class specified here. Those
+        variables will be treated as placeholders and have no meaning until the
+        module that defines them is loaded. When a module for a specific class is
+        loaded, it will add the proper variable definitions for the class
+        associated with it, convert any placeholder values according to those
+        definitions, and issue warnings for any placeholders that then remains.
+       </para>
+
+       <para>
+        Here is an example what custom variables might look like:
+
+<programlisting>
+custom_variable_class = 'plr,pljava'
+plr.foo = '/usr/lib/R'
+pljava.baz = 1
+plruby.var = true        <== this one would generate an error
+</programlisting>
+       </para>
+
+       <para>
+        This option can only be set at server start or in the
+        <filename>postgresql.conf</filename> configuration file.
+       </para>
+
+      </listitem>
+     </varlistentry>
+    </variablelist>
+    </sect2>
+
    <sect2 id="runtime-config-developer">
     <title>Developer Options</title>

Index: src/backend/parser/gram.y
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.454
diff -u -r2.454 gram.y
--- src/backend/parser/gram.y    10 May 2004 22:44:45 -0000    2.454
+++ src/backend/parser/gram.y    19 May 2004 21:01:43 -0000
@@ -309,7 +309,7 @@
 %type <str>        Sconst comment_text
 %type <str>        UserId opt_boolean ColId_or_Sconst
 %type <list>    var_list var_list_or_default
-%type <str>        ColId ColLabel type_name param_name
+%type <str>        ColId ColLabel var_name type_name param_name
 %type <node>    var_value zone_value

 %type <keyword> unreserved_keyword func_name_keyword
@@ -857,14 +857,14 @@
                 }
         ;

-set_rest:  ColId TO var_list_or_default
+set_rest:  var_name TO var_list_or_default
                 {
                     VariableSetStmt *n = makeNode(VariableSetStmt);
                     n->name = $1;
                     n->args = $3;
                     $$ = n;
                 }
-            | ColId '=' var_list_or_default
+            | var_name '=' var_list_or_default
                 {
                     VariableSetStmt *n = makeNode(VariableSetStmt);
                     n->name = $1;
@@ -914,6 +914,19 @@
                     n->name = "session_authorization";
                     n->args = NIL;
                     $$ = n;
+                }
+        ;
+
+var_name:
+            ColId                                { $$ = $1; }
+            | var_name '.' ColId
+                {
+                    int qLen = strlen($1);
+                    char* qualName = palloc(qLen + strlen($3) + 2);
+                    strcpy(qualName, $1);
+                    qualName[qLen] = '.';
+                    strcpy(qualName + qLen + 1, $3);
+                    $$ = qualName;
                 }
         ;

Index: src/backend/utils/misc/guc-file.l
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc-file.l,v
retrieving revision 1.21
diff -u -r1.21 guc-file.l
--- src/backend/utils/misc/guc-file.l    24 Feb 2004 22:06:32 -0000    1.21
+++ src/backend/utils/misc/guc-file.l    19 May 2004 21:01:43 -0000
@@ -34,6 +34,7 @@
     GUC_REAL = 4,
     GUC_EQUALS = 5,
     GUC_UNQUOTED_STRING = 6,
+    GUC_QUALIFIED_ID = 7,
     GUC_EOL = 99,
     GUC_ERROR = 100
 };
@@ -65,6 +66,7 @@
 LETTER_OR_DIGIT [A-Za-z_0-9\200-\377]

 ID              {LETTER}{LETTER_OR_DIGIT}*
+QUALIFIED_ID    {ID}"."{ID}

 UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])*
 STRING          \'([^'\n]|\\.)*\'
@@ -76,6 +78,7 @@
 #.*$            /* eat comment */

 {ID}            return GUC_ID;
+{QUALIFIED_ID}  return GUC_QUALIFIED_ID;
 {STRING}        return GUC_STRING;
 {UNQUOTED_STRING} return GUC_UNQUOTED_STRING;
 {INTEGER}       return GUC_INTEGER;
@@ -180,7 +183,7 @@
             case 0: /* no previous input */
                 if (token == GUC_EOL) /* empty line */
                     continue;
-                if (token != GUC_ID)
+                if (token != GUC_ID && token != GUC_QUALIFIED_ID)
                     goto parse_error;
                 opt_name = strdup(yytext);
                 if (opt_name == NULL)
@@ -216,6 +219,24 @@
             case 2: /* now we'd like an end of line */
                 if (token != GUC_EOL)
                     goto parse_error;
+
+                if (strcmp(opt_name, "custom_variable_classes") == 0)
+                {
+                    /* This variable must be added first as it controls the validity
+                     * of other variables
+                     */
+                    if (!set_config_option(opt_name, opt_value, context,
+                                           PGC_S_FILE, false, true))
+                    {
+                        FreeFile(fp);
+                        free(filename);
+                        goto cleanup_exit;
+                    }
+
+                    /* Don't include in list */
+                    parse_state = 0;
+                    break;
+                }

                 /* append to list */
                 item = malloc(sizeof *item);
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.205
diff -u -r1.205 guc.c
--- src/backend/utils/misc/guc.c    8 May 2004 02:11:46 -0000    1.205
+++ src/backend/utils/misc/guc.c    19 May 2004 21:01:44 -0000
@@ -20,6 +20,7 @@
 #include <float.h>
 #include <limits.h>
 #include <unistd.h>
+#include <ctype.h>

 #include "utils/guc.h"
 #include "utils/guc_tables.h"
@@ -103,6 +104,8 @@
 static const char *assign_log_stmtlvl(int *var, const char *newval,
                            bool doit, GucSource source);
 static bool assign_phony_autocommit(bool newval, bool doit, GucSource source);
+static const char *assign_custom_variable_classes(const char *newval, bool doit,
+                           GucSource source);
 static bool assign_stage_log_stats(bool newval, bool doit, GucSource source);
 static bool assign_log_stats(bool newval, bool doit, GucSource source);

@@ -167,6 +170,7 @@
 static char *session_authorization_string;
 static char *timezone_string;
 static char *XactIsoLevel_string;
+static char *custom_variable_classes;
 static int    max_function_args;
 static int    max_index_keys;
 static int    max_identifier_length;
@@ -1728,6 +1732,16 @@
         XLOG_sync_method_default, assign_xlog_sync_method, NULL
     },

+    {
+        {"custom_variable_classes", PGC_POSTMASTER, RESOURCES_KERNEL,
+            gettext_noop("Sets the list of known custom variable classes"),
+            NULL,
+            GUC_LIST_INPUT | GUC_LIST_QUOTE
+        },
+        &custom_variable_classes,
+        NULL, assign_custom_variable_classes, NULL
+    },
+
     /* End-of-list marker */
     {
         {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
@@ -1753,8 +1767,15 @@
 /*
  * Actual lookup of variables is done through this single, sorted array.
  */
-struct config_generic **guc_variables;
-int            num_guc_variables;
+static struct config_generic **guc_variables;
+
+/* Current number of variables contained in the vector
+ */
+static int num_guc_variables;
+
+/* Vector capacity
+ */
+static int size_guc_variables;

 static bool guc_dirty;            /* TRUE if need to do commit/abort work */

@@ -1768,6 +1789,10 @@
 static void ReportGUCOption(struct config_generic * record);
 static char *_ShowOption(struct config_generic * record);

+struct config_generic** get_guc_variables()
+{
+    return guc_variables;
+}

 /*
  * Build the sorted array.    This is split out so that it could be
@@ -1777,6 +1802,7 @@
 void
 build_guc_variables(void)
 {
+    int         size_vars;
     int            num_vars = 0;
     struct config_generic **guc_vars;
     int            i;
@@ -1814,8 +1840,12 @@
         num_vars++;
     }

+    /* Create table with 20% slack
+     */
+    size_vars = num_vars + num_vars / 4;
+
     guc_vars = (struct config_generic **)
-        malloc(num_vars * sizeof(struct config_generic *));
+        malloc(size_vars * sizeof(struct config_generic *));
     if (!guc_vars)
         ereport(FATAL,
                 (errcode(ERRCODE_OUT_OF_MEMORY),
@@ -1835,15 +1865,105 @@
     for (i = 0; ConfigureNamesString[i].gen.name; i++)
         guc_vars[num_vars++] = &ConfigureNamesString[i].gen;

-    qsort((void *) guc_vars, num_vars, sizeof(struct config_generic *),
-          guc_var_compare);
-
     if (guc_variables)
         free(guc_variables);
     guc_variables = guc_vars;
     num_guc_variables = num_vars;
+    size_guc_variables = size_vars;
+    qsort((void*) guc_variables, num_guc_variables,
+        sizeof(struct config_generic*), guc_var_compare);
+}
+
+static bool
+is_custom_class(const char *name, int dotPos)
+{
+    /* The assign_custom_variable_classes has made sure no empty
+     * identifiers or whitespace exists in the variable
+     */
+    bool result = false;
+    const char *ccs = GetConfigOption("custom_variable_classes");
+    if(ccs != NULL)
+    {
+        const char *start = ccs;
+        for(;; ++ccs)
+        {
+            int c = *ccs;
+            if(c == 0 || c == ',')
+            {
+                if(dotPos == ccs - start && strncmp(start, name, dotPos) == 0)
+                {
+                    result = true;
+                    break;
+                }
+                if(c == 0)
+                    break;
+                start = ccs + 1;
+            }
+        }
+    }
+    return result;
 }

+/*
+ * Add a new GUC variable to the list of known variables. The
+ * list is expanded if needed.
+ */
+static void
+add_guc_variable(struct config_generic *var)
+{
+    if(num_guc_variables + 1 >= size_guc_variables)
+    {
+        /* Increase the vector with 20%
+         */
+        int size_vars = size_guc_variables + size_guc_variables / 4;
+        if(size_vars == 0)
+            size_vars = 100;
+
+        struct config_generic** guc_vars = (struct config_generic**)
+            malloc(size_vars * sizeof(struct config_generic*));
+
+        if (guc_variables != NULL)
+        {
+            memcpy(guc_vars, guc_variables,
+                    num_guc_variables * sizeof(struct config_generic*));
+            free(guc_variables);
+        }
+
+        guc_variables = guc_vars;
+        size_guc_variables = size_vars;
+    }
+    guc_variables[num_guc_variables++] = var;
+    qsort((void*) guc_variables, num_guc_variables,
+        sizeof(struct config_generic*), guc_var_compare);
+}
+
+/*
+ * Create and add a placeholder variable. Its presumed to belong
+ * to a valid custom variable class at this point.
+ */
+static struct config_string*
+add_placeholder_variable(const char *name)
+{
+    size_t sz = sizeof(struct config_string) + sizeof(char*);
+    struct config_string*  var = (struct config_string*)malloc(sz);
+    struct config_generic* gen = &var->gen;
+
+    memset(var, 0, sz);
+
+    gen->name       = strdup(name);
+    gen->context    = PGC_USERSET;
+    gen->group      = CUSTOM_OPTIONS;
+    gen->short_desc = "GUC placeholder variable";
+    gen->flags      = GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_CUSTOM_PLACEHOLDER;
+    gen->vartype    = PGC_STRING;
+
+    /* The char* is allocated at the end of the struct since we have
+     * no 'static' place to point to.
+     */
+    var->variable = (char**)(var + 1);
+    add_guc_variable((struct config_generic*)var);
+    return var;
+}

 /*
  * Look up option NAME. If it exists, return a pointer to its record,
@@ -1852,6 +1972,7 @@
 static struct config_generic *
 find_option(const char *name)
 {
+    const char *dot;
     const char **key = &name;
     struct config_generic **res;
     int            i;
@@ -1881,6 +2002,16 @@
             return find_option(map_old_guc_names[i+1]);
     }

+    /* Check if the name is qualified, and if so, check if the qualifier
+     * maps to a custom variable class.
+     */
+    dot = strchr(name, GUC_QUALIFIER_SEPARATOR);
+    if(dot != NULL && is_custom_class(name, dot - name))
+        /*
+         * Add a placeholder variable for this name
+         */
+        return (struct config_generic*)add_placeholder_variable(name);
+
     /* Unknown name */
     return NULL;
 }
@@ -3459,6 +3590,196 @@
     PG_RETURN_TEXT_P(result_text);
 }

+static void
+define_custom_variable(struct config_generic* variable)
+{
+    const char* name = variable->name;
+    const char** nameAddr = &name;
+    const char* value;
+    struct config_string*   pHolder;
+    struct config_generic** res = (struct config_generic**)bsearch(
+                                            (void*)&nameAddr,
+                                            (void*)guc_variables,
+                                            num_guc_variables,
+                                            sizeof(struct config_generic*),
+                                            guc_var_compare);
+
+    if(res == NULL)
+    {
+        add_guc_variable(variable);
+        return;
+    }
+
+    /* This better be a placeholder
+     */
+    if(((*res)->flags & GUC_CUSTOM_PLACEHOLDER) == 0)
+    {
+        ereport(ERROR,
+                (errcode(ERRCODE_INTERNAL_ERROR),
+                 errmsg("attempt to redefine parameter \"%s\"", name)));
+    }
+    pHolder = (struct config_string*)*res;
+
+    /* We have the same name, no sorting is necessary.
+     */
+    *res = variable;
+
+    value = *pHolder->variable;
+
+    /* Assign the variable stored in the placeholder to the real
+     * variable.
+     */
+    set_config_option(name, value,
+                  pHolder->gen.context, pHolder->gen.source,
+                  false, true);
+
+    /* Free up stuff occupied by the placeholder variable
+     */
+    if(value != NULL)
+        free((void*)value);
+
+    if(pHolder->reset_val != NULL && pHolder->reset_val != value)
+        free(pHolder->reset_val);
+
+    if(pHolder->session_val != NULL
+    && pHolder->session_val != value
+    && pHolder->session_val != pHolder->reset_val)
+        free(pHolder->session_val);
+
+    if(pHolder->tentative_val != NULL
+    && pHolder->tentative_val != value
+    && pHolder->tentative_val != pHolder->reset_val
+    && pHolder->tentative_val != pHolder->session_val)
+        free(pHolder->tentative_val);
+
+    free(pHolder);
+}
+
+static void init_custom_variable(
+    struct config_generic* gen,
+    const char* name,
+    const char* short_desc,
+    const char* long_desc,
+    GucContext  context,
+    enum config_type type)
+{
+    gen->name       = strdup(name);
+    gen->context    = context;
+    gen->group      = CUSTOM_OPTIONS;
+    gen->short_desc = short_desc;
+    gen->long_desc  = long_desc;
+    gen->vartype    = type;
+}
+
+void DefineCustomBoolVariable(
+    const char* name,
+    const char* short_desc,
+    const char* long_desc,
+    bool*       valueAddr,
+    GucContext  context,
+    GucBoolAssignHook assign_hook,
+    GucShowHook show_hook)
+{
+    size_t sz = sizeof(struct config_bool);
+    struct config_bool*  var = (struct config_bool*)malloc(sz);
+
+    memset(var, 0, sz);
+    init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_BOOL);
+
+    var->variable    = valueAddr;
+    var->reset_val   = *valueAddr;
+    var->assign_hook = assign_hook;
+    var->show_hook   = show_hook;
+    define_custom_variable(&var->gen);
+}
+
+void DefineCustomIntVariable(
+    const char* name,
+    const char* short_desc,
+    const char* long_desc,
+    int*        valueAddr,
+    GucContext  context,
+    GucIntAssignHook assign_hook,
+    GucShowHook show_hook)
+{
+    size_t sz = sizeof(struct config_int);
+    struct config_int*  var = (struct config_int*)malloc(sz);
+
+    memset(var, 0, sz);
+    init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_INT);
+
+    var->variable    = valueAddr;
+    var->reset_val   = *valueAddr;
+    var->assign_hook = assign_hook;
+    var->show_hook   = show_hook;
+    define_custom_variable(&var->gen);
+}
+
+void DefineCustomRealVariable(
+    const char* name,
+    const char* short_desc,
+    const char* long_desc,
+    double*     valueAddr,
+    GucContext  context,
+    GucRealAssignHook assign_hook,
+    GucShowHook show_hook)
+{
+    size_t sz = sizeof(struct config_real);
+    struct config_real*  var = (struct config_real*)malloc(sz);
+
+    memset(var, 0, sz);
+    init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_REAL);
+
+    var->variable    = valueAddr;
+    var->reset_val   = *valueAddr;
+    var->assign_hook = assign_hook;
+    var->show_hook   = show_hook;
+    define_custom_variable(&var->gen);
+}
+
+void DefineCustomStringVariable(
+    const char* name,
+    const char* short_desc,
+    const char* long_desc,
+    char**      valueAddr,
+    GucContext  context,
+    GucStringAssignHook assign_hook,
+    GucShowHook show_hook)
+{
+    size_t sz = sizeof(struct config_string);
+    struct config_string*  var = (struct config_string*)malloc(sz);
+
+    memset(var, 0, sz);
+    init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_STRING);
+
+    var->variable    = valueAddr;
+    var->reset_val   = *valueAddr;
+    var->assign_hook = assign_hook;
+    var->show_hook   = show_hook;
+    define_custom_variable(&var->gen);
+}
+
+extern void EmittWarningsOnPlaceholders(const char* className)
+{
+    struct config_generic** vars = guc_variables;
+    struct config_generic** last = vars + num_guc_variables;
+
+    int nameLen = strlen(className);
+    while(vars < last)
+    {
+        struct config_generic* var = *vars++;
+        if((var->flags & GUC_CUSTOM_PLACEHOLDER) != 0 &&
+           strncmp(className, var->name, nameLen) == 0 &&
+           var->name[nameLen] == GUC_QUALIFIER_SEPARATOR)
+        {
+            ereport(INFO,
+                (errcode(ERRCODE_UNDEFINED_OBJECT),
+                 errmsg("unrecognized configuration parameter \"%s\"", var->name)));
+        }
+    }
+}
+
+
 /*
  * SHOW command
  */
@@ -4710,6 +5031,68 @@
     return true;
 }

+static const char *
+assign_custom_variable_classes(const char *newval, bool doit, GucSource source)
+{
+    /* Check syntax. newval must be a comma separated list of identifiers.
+     * Whitespace is allowed but skipped.
+     */
+    bool hasSpaceAfterToken = false;
+    const char *cp = newval;
+    int symLen = 0;
+    int c;
+    StringInfoData buf;
+
+    initStringInfo(&buf);
+    while((c = *cp++) != 0)
+    {
+        if(isspace(c))
+        {
+            if(symLen > 0)
+                hasSpaceAfterToken = true;
+            continue;
+        }
+
+        if(c == ',')
+        {
+            hasSpaceAfterToken = false;
+            if(symLen > 0)
+            {
+                symLen = 0;
+                appendStringInfoChar(&buf, ',');
+            }
+            continue;
+        }
+
+        if(hasSpaceAfterToken || !isalnum(c))
+        {
+            /* Syntax error due to token following space after
+             * token or non alpha numeric character
+             */
+            pfree(buf.data);
+            ereport(WARNING,
+                    (errcode(ERRCODE_SYNTAX_ERROR),
+                     errmsg("illegal syntax for custom_variable_classes \"%s\"", newval)));
+            return NULL;
+        }
+        symLen++;
+        appendStringInfoChar(&buf, (char)c);
+    }
+
+    if(symLen == 0 && buf.len > 0)
+        /*
+         * Remove stray ',' at end
+         */
+        buf.data[--buf.len] = 0;
+
+    if(buf.len == 0)
+        newval = NULL;
+    else if(doit)
+        newval = strdup(buf.data);
+
+    pfree(buf.data);
+    return newval;
+}

 static bool
 assign_stage_log_stats(bool newval, bool doit, GucSource source)
Index: src/backend/utils/misc/help_config.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/help_config.c,v
retrieving revision 1.9
diff -u -r1.9 help_config.c
--- src/backend/utils/misc/help_config.c    29 Nov 2003 19:52:04 -0000    1.9
+++ src/backend/utils/misc/help_config.c    19 May 2004 21:01:44 -0000
@@ -47,13 +47,15 @@
 GucInfoMain(void)
 {
     int            i;
+    struct config_generic **guc_vars = get_guc_variables();
+    int numOpts = GetNumConfigOptions();

     /* Initialize the guc_variables[] array */
     build_guc_variables();

-    for (i = 0; i < num_guc_variables; i++)
+    for (i = 0; i < numOpts; i++)
     {
-        mixedStruct *var = (mixedStruct *) guc_variables[i];
+        mixedStruct *var = (mixedStruct *) guc_vars[i];

         if (displayStruct(var))
             printMixedStruct(var);
Index: src/include/utils/guc.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/guc.h,v
retrieving revision 1.45
diff -u -r1.45 guc.h
--- src/include/utils/guc.h    7 Apr 2004 05:05:50 -0000    1.45
+++ src/include/utils/guc.h    19 May 2004 21:01:45 -0000
@@ -102,6 +102,15 @@
     PGC_S_SESSION                /* SET command */
 } GucSource;

+typedef const char* (*GucStringAssignHook)(const char *newval, bool doit, GucSource source);
+typedef bool (*GucBoolAssignHook)(bool newval, bool doit, GucSource source);
+typedef bool (*GucIntAssignHook)(int newval, bool doit, GucSource source);
+typedef bool (*GucRealAssignHook)(double newval, bool doit, GucSource source);
+
+typedef const char* (*GucShowHook)(void);
+
+#define GUC_QUALIFIER_SEPARATOR '.'
+
 /* GUC vars that are actually declared in guc.c, rather than elsewhere */
 extern bool log_duration;
 extern bool Debug_print_plan;
@@ -129,6 +138,45 @@

 extern void SetConfigOption(const char *name, const char *value,
                 GucContext context, GucSource source);
+
+extern void DefineCustomBoolVariable(
+    const char* name,
+    const char* short_desc,
+    const char* long_desc,
+    bool*       valueAddr,
+    GucContext  context,
+    GucBoolAssignHook assign_hook,
+    GucShowHook show_hook);
+
+extern void DefineCustomIntVariable(
+    const char* name,
+    const char* short_desc,
+    const char* long_desc,
+    int*        valueAddr,
+    GucContext  context,
+    GucIntAssignHook assign_hook,
+    GucShowHook show_hook);
+
+extern void DefineCustomRealVariable(
+    const char* name,
+    const char* short_desc,
+    const char* long_desc,
+    double*     valueAddr,
+    GucContext  context,
+    GucRealAssignHook assign_hook,
+    GucShowHook show_hook);
+
+extern void DefineCustomStringVariable(
+    const char* name,
+    const char* short_desc,
+    const char* long_desc,
+    char**      valueAddr,
+    GucContext  context,
+    GucStringAssignHook assign_hook,
+    GucShowHook show_hook);
+
+extern void EmittWarningsOnPlaceholders(const char* className);
+
 extern const char *GetConfigOption(const char *name);
 extern const char *GetConfigOptionResetString(const char *name);
 extern void ProcessConfigFile(GucContext context);
Index: src/include/utils/guc_tables.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/guc_tables.h,v
retrieving revision 1.10
diff -u -r1.10 guc_tables.h
--- src/include/utils/guc_tables.h    5 Apr 2004 03:02:11 -0000    1.10
+++ src/include/utils/guc_tables.h    19 May 2004 21:01:45 -0000
@@ -51,7 +51,8 @@
     COMPAT_OPTIONS_PREVIOUS,
     COMPAT_OPTIONS_CLIENT,
     DEVELOPER_OPTIONS,
-    COMPILE_OPTIONS
+    COMPILE_OPTIONS,
+    CUSTOM_OPTIONS
 };

 /*
@@ -98,6 +99,7 @@
 #define GUC_REPORT                0x0010    /* auto-report changes to client */
 #define GUC_NOT_IN_SAMPLE        0x0020    /* not in postgresql.conf.sample */
 #define GUC_DISALLOW_IN_FILE    0x0040    /* can't set in postgresql.conf */
+#define GUC_CUSTOM_PLACEHOLDER    0x0080    /* placeholder for a custom variable */

 /* bit values in status field */
 #define GUC_HAVE_TENTATIVE    0x0001        /* tentative value is defined */
@@ -113,8 +115,8 @@
     /* (all but reset_val are constants) */
     bool       *variable;
     bool        reset_val;
-    bool        (*assign_hook) (bool newval, bool doit, GucSource source);
-    const char *(*show_hook) (void);
+    GucBoolAssignHook assign_hook;
+    GucShowHook show_hook;
     /* variable fields, initialized at runtime: */
     bool        session_val;
     bool        tentative_val;
@@ -129,8 +131,8 @@
     int            reset_val;
     int            min;
     int            max;
-    bool        (*assign_hook) (int newval, bool doit, GucSource source);
-    const char *(*show_hook) (void);
+    GucIntAssignHook assign_hook;
+    GucShowHook show_hook;
     /* variable fields, initialized at runtime: */
     int            session_val;
     int            tentative_val;
@@ -145,8 +147,8 @@
     double        reset_val;
     double        min;
     double        max;
-    bool        (*assign_hook) (double newval, bool doit, GucSource source);
-    const char *(*show_hook) (void);
+    GucRealAssignHook assign_hook;
+    GucShowHook show_hook;
     /* variable fields, initialized at runtime: */
     double        session_val;
     double        tentative_val;
@@ -159,8 +161,8 @@
     /* (all are constants) */
     char      **variable;
     const char *boot_val;
-    const char *(*assign_hook) (const char *newval, bool doit, GucSource source);
-    const char *(*show_hook) (void);
+    GucStringAssignHook assign_hook;
+    GucShowHook show_hook;
     /* variable fields, initialized at runtime: */
     char       *reset_val;
     char       *session_val;
@@ -173,9 +175,8 @@
 extern const char *const GucContext_Names[];
 extern const char *const GucSource_Names[];

-/* the current set of variables */
-extern struct config_generic **guc_variables;
-extern int    num_guc_variables;
+/* get the current set of variables */
+extern struct config_generic **get_guc_variables(void);

 extern void build_guc_variables(void);


pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: [COMMITTERS] pgsql-server/src backend/main/main.c backend/p
Next
From: Bruce Momjian
Date:
Subject: Re: [HACKERS] pg_dump end comment