Patch for GUC custom variables - Mailing list pgsql-patches

From Thomas Hallgren
Subject Patch for GUC custom variables
Date
Msg-id c6m693$2orh$1@news.hub.org
Whole thread Raw
List pgsql-patches
Below is my patch addressing the TODO list item "Allow external interfaces
to
extend the GUC variable set ". It's outlined after the suggestions made by
Tom Lane and Joe Conway in this thread:

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

Description:

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.c) 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.

I didn't find a place where I could add documentation for the new GUC
functions.
Perhaps documenting GUC as a whole is a separate future track?

Kind regards,

Thomas Hallgren

Index: doc/src/sgml/runtime.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/runtime.sgml,v
retrieving revision 1.262
diff -u -r1.262 runtime.sgml
--- doc/src/sgml/runtime.sgml 22 Apr 2004 04:18:37 -0000 1.262
+++ doc/src/sgml/runtime.sgml 27 Apr 2004 17:17:54 -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/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 27 Apr 2004 17:17:58 -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.201
diff -u -r1.201 guc.c
--- src/backend/utils/misc/guc.c 19 Apr 2004 21:22:14 -0000 1.201
+++ src/backend/utils/misc/guc.c 27 Apr 2004 17:17:59 -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"
@@ -102,6 +103,8 @@
          GucSource source);
 static const char *assign_log_stmtlvl(int *var, const char *newval,
          bool doit, GucSource source);
+static const char *assign_custom_variable_classes(const char *newval, bool
doit,
+         GucSource source);
 static bool assign_phony_autocommit(bool newval, bool doit, GucSource
source);


@@ -165,6 +168,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;
@@ -1726,6 +1730,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
@@ -1751,8 +1765,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 */

@@ -1766,6 +1787,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
@@ -1775,6 +1800,7 @@
 void
 build_guc_variables(void)
 {
+ int         size_vars;
  int   num_vars = 0;
  struct config_generic **guc_vars;
  int   i;
@@ -1812,8 +1838,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),
@@ -1833,15 +1863,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,
@@ -1850,6 +1970,7 @@
 static struct config_generic *
 find_option(const char *name)
 {
+ const char *dot;
  const char **key = &name;
  struct config_generic **res;
  int   i;
@@ -1879,6 +2000,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;
 }
@@ -3457,6 +3588,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
  */
@@ -4468,7 +4789,7 @@
   else {
    if (source >= PGC_S_INTERACTIVE)
     ereport(ERROR,
-      (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+      ((ERRCODE_INVALID_PARAMETER_VALUE),
        errmsg("unrecognised \"log_destination\" key word: \"%s\"",
         tok)));
    pfree(rawstring);
@@ -4708,5 +5029,67 @@
  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;
+}

 #include "guc-file.c"
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 27 Apr 2004 17:18:00 -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 27 Apr 2004 17:18:02 -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 27 Apr 2004 17:18:02 -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: [HACKERS] compile warning in CVS HEAD
Next
From: Alvaro Herrera
Date:
Subject: Re: [BUGS] BUG #1134: ALTER USER ... RENAME breaks md5