]> granicus.if.org Git - postgresql/commitdiff
The patch adresses the TODO list item "Allow external interfaces to
authorBruce Momjian <bruce@momjian.us>
Wed, 26 May 2004 15:07:41 +0000 (15:07 +0000)
committerBruce Momjian <bruce@momjian.us>
Wed, 26 May 2004 15:07:41 +0000 (15:07 +0000)
extend the GUC variable set".

Plugin modules like the pl<lang> modules needs a way to declare
configuration parameters. The postmaster has no knowledge of such
modules when it reads the postgresql.conf file. Rather than allowing
totally unknown configuration parameters, the concept of a variable
"class" is introduced. Variables that belongs to a declared classes will
create a placeholder value of string type and will not generate an
error. When a module is loaded, it will declare variables for such a
class and make those variables "consume" any placeholders that has been
defined. Finally, the module will generate warnings for unrecognized
placeholders defined for its class.

More detail:
The design is outlined after the suggestions made by Tom Lane and Joe
Conway in this thread:

http://archives.postgresql.org/pgsql-hackers/2004-02/msg00229.php

A new string variable 'custom_variable_classes' is introduced. This
variable is a comma separated string of identifiers. Each identifier
denots a 'class' that will allow its members to be added without error.
This variable must be defined in postmaster.conf.

The lexer (guc_file.l) is changed so that it can accept a qualified name
in the form <ID>.<ID> as the name of a variable. I also changed so that
the 'custom_variable_classes', if found, is added first of all variables
in order to remove the order of declaration issue.

The guc_variables table is made more dynamic. It is originally created
with 20% slack and can grow dynamically. A capacity is introduced to
avoid resizing every time a new variable is added. guc_variables and
num_guc_variables becomes static (hidden).

The GucInfoMain now uses the new function get_guc_variables() and
GetNumConfigOptions  instead or using the guc_variables directly.

The find_option() function, when passed a missing name, will check if
the name is qualified. If the name is qualified and if the qualifier
denotes a class included in the 'custom_variable_classes', a placeholder
variable will be created. Such a placeholder will not participate in a
list operation but will otherwise function as a normal string variable.

Define<type>GucVariable() functions will be added, one for each variable
type. They are inteded to be used by add-on modules like the pl<lang>
mappings. Example:

extern void DefineCustomBoolVariable(
         const char* name,
         const char* short_desc,
         const char* long_desc,
         bool* valueAddr,
         GucContext context,
         GucBoolAssignHook assign_hook,
         GucShowHook show_hook);

(I created typedefs for the assign-hook and show-hook functions). A call
to these functions will define a new GUC-variable. If a placeholder
exists it will be replaced but it's value will be used in place of the
default value. The valueAddr is assumed ot point at a default value when
the define function is called. The only constraint that is imposed on a
Custom variable is that its name is qualified.

Finally, a function:

void EmittWarningsOnPlacholders(const char* className)

was added. This function should be called when a module has completed
its variable definitions. At that time, no placeholders should remain
for the class that the module uses. If they do, elog(INFO, ...) messages
will be issued to inform the user that unrecognized variables are
present.

Thomas Hallgren

doc/src/sgml/runtime.sgml
src/backend/parser/gram.y
src/backend/utils/misc/guc-file.l
src/backend/utils/misc/guc.c
src/backend/utils/misc/help_config.c
src/include/utils/guc.h
src/include/utils/guc_tables.h

index c75b06afec77687f16a8d574f7abb8f15819db3a..35525b8b19e19a034fd60f972d0e1b5d00fc0770 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.263 2004/04/29 04:37:09 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.264 2004/05/26 15:07:33 momjian Exp $
 -->
 
 <Chapter Id="runtime">
@@ -2924,6 +2924,60 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
     </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 b20f92ba7b26d65308de7c9649543c76bbab570d..4600a29c2c5e4f050bec3e65ba58b15b9603bd23 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.456 2004/05/26 13:56:51 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.457 2004/05/26 15:07:37 momjian Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -310,7 +310,7 @@ static void doNegateFloat(Value *v);
 %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
@@ -859,14 +859,14 @@ VariableSetStmt:
                                }
                ;
 
-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;
@@ -919,6 +919,19 @@ set_rest:  ColId TO var_list_or_default
                                }
                ;
 
+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;
+                               }
+               ;
+
 var_list_or_default:
                        var_list                                                                { $$ = $1; }
                        | DEFAULT                                                               { $$ = NIL; }
index eb5f0c7f0711ea8130bec775b06de9da5a3b3cd2..cb95347e592aa12f684832fbf3c11d0ce06f7cf4 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (c) 2000-2003, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.21 2004/02/24 22:06:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.22 2004/05/26 15:07:38 momjian Exp $
  */
 
 %{
@@ -34,6 +34,7 @@ enum {
        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          [A-Za-z_\200-\377]
 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 @@ STRING          \'([^'\n]|\\.)*\'
 #.*$            /* 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 @@ ProcessConfigFile(GucContext context)
             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)
@@ -217,6 +220,24 @@ ProcessConfigFile(GucContext context)
                                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);
                                if (item == NULL)
index 1cfc3e0346b5b1b16d2783cef89432eff8132148..1620a8607c28c8e351ece360c3a8de19064b9bed 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.207 2004/05/26 04:41:43 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.208 2004/05/26 15:07:39 momjian Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -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_statement(const char *newval, bool doit,
 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 *server_version_string;
 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 @@ static struct config_string ConfigureNamesString[] =
                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 @@ static const char * const map_old_guc_names[] = {
 /*
  * 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 int       guc_name_compare(const char *namea, const char *nameb);
 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 @@ static char *_ShowOption(struct config_generic * record);
 void
 build_guc_variables(void)
 {
+       int         size_vars;
        int                     num_vars = 0;
        struct config_generic **guc_vars;
        int                     i;
@@ -1814,8 +1840,12 @@ build_guc_variables(void)
                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,107 @@ build_guc_variables(void)
        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;
+               struct config_generic** guc_vars;
+
+               if(size_vars == 0)
+                       size_vars = 100;
+
+               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 +1974,7 @@ build_guc_variables(void)
 static struct config_generic *
 find_option(const char *name)
 {
+       const char *dot;
        const char **key = &name;
        struct config_generic **res;
        int                     i;
@@ -1881,6 +2004,16 @@ find_option(const char *name)
                        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;
 }
@@ -3455,6 +3588,196 @@ set_config_by_name(PG_FUNCTION_ARGS)
        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
  */
@@ -4706,6 +5029,68 @@ assign_phony_autocommit(bool newval, bool doit, GucSource source)
        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 a3bcff212604295893fa61b9cea9010f53a68c53..926ab6014bc9b5fe49c2ec593985035ae6783505 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/misc/help_config.c,v 1.9 2003/11/29 19:52:04 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/misc/help_config.c,v 1.10 2004/05/26 15:07:39 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,13 +47,15 @@ int
 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 ca1f87b1fe1011284e564bf3e93608210ae77b0d..707dca3391073c25901542299b4218571c1d9236 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (c) 2000-2003, PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
- * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.45 2004/04/07 05:05:50 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.46 2004/05/26 15:07:41 momjian Exp $
  *--------------------------------------------------------------------
  */
 #ifndef GUC_H
@@ -102,6 +102,15 @@ typedef enum
        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 int log_min_duration_statement;
 
 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 1b99bf8159d26cfeac416129555422352ad4ac35..62d2d571b292d1d4423c87c3391b8a9013313ba7 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  *
- *       $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.10 2004/04/05 03:02:11 momjian Exp $
+ *       $PostgreSQL: pgsql/src/include/utils/guc_tables.h,v 1.11 2004/05/26 15:07:41 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,7 +51,8 @@ enum config_group
        COMPAT_OPTIONS_PREVIOUS,
        COMPAT_OPTIONS_CLIENT,
        DEVELOPER_OPTIONS,
-       COMPILE_OPTIONS
+       COMPILE_OPTIONS,
+       CUSTOM_OPTIONS
 };
 
 /*
@@ -98,6 +99,7 @@ struct config_generic
 #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 @@ struct config_bool
        /* (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 @@ struct config_int
        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 @@ struct config_real
        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 @@ struct config_string
        /* (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 config_type_names[];
 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);