aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/misc/guc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/misc/guc.c')
-rw-r--r--src/backend/utils/misc/guc.c399
1 files changed, 392 insertions, 7 deletions
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1cfc3e0346b..1620a8607c2 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)