diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index c75b06afec77687f16a8d574f7abb8f15819db3a..35525b8b19e19a034fd60f972d0e1b5d00fc0770 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -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>
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b20f92ba7b26d65308de7c9649543c76bbab570d..4600a29c2c5e4f050bec3e65ba58b15b9603bd23 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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; }
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index eb5f0c7f0711ea8130bec775b06de9da5a3b3cd2..cb95347e592aa12f684832fbf3c11d0ce06f7cf4 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -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)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1cfc3e0346b5b1b16d2783cef89432eff8132148..1620a8607c28c8e351ece360c3a8de19064b9bed 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -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)
diff --git a/src/backend/utils/misc/help_config.c b/src/backend/utils/misc/help_config.c
index a3bcff212604295893fa61b9cea9010f53a68c53..926ab6014bc9b5fe49c2ec593985035ae6783505 100644
--- a/src/backend/utils/misc/help_config.c
+++ b/src/backend/utils/misc/help_config.c
@@ -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);
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ca1f87b1fe1011284e564bf3e93608210ae77b0d..707dca3391073c25901542299b4218571c1d9236 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -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);
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 1b99bf8159d26cfeac416129555422352ad4ac35..62d2d571b292d1d4423c87c3391b8a9013313ba7 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -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);