mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-30 19:00:29 +08:00
Support include directives in postgresql.conf.
Patch by Joachim Wieland, somewhat reworked for clarity and portability.
This commit is contained in:
parent
60d3c9fdf4
commit
5b8ac71042
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.48 2006/03/03 22:02:07 momjian Exp $
|
||||
$PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.49 2006/03/04 22:19:31 tgl Exp $
|
||||
-->
|
||||
<chapter Id="runtime-config">
|
||||
<title>Server Configuration</title>
|
||||
@ -47,7 +47,24 @@ search_path = '"$user", public'
|
||||
anywhere. Parameter values that are not simple identifiers or
|
||||
numbers must be single-quoted. To embed a single quote in a parameter
|
||||
value, write either two quotes (preferred) or backslash-quote.
|
||||
</para>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<indexterm>
|
||||
<primary><literal>include</></primary>
|
||||
<secondary>in configuration file</secondary>
|
||||
</indexterm>
|
||||
In addition to parameter settings, the <filename>postgresql.conf</>
|
||||
file can contain <firstterm>include directives</>, which specify
|
||||
another file to read and process as if it were inserted into the
|
||||
configuration file at this point. Include directives simply look like
|
||||
<programlisting>
|
||||
include 'filename'
|
||||
</programlisting>
|
||||
If the filename is not an absolute path, it is taken as relative to
|
||||
the directory containing the referencing configuration file.
|
||||
Inclusions can be nested.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<indexterm>
|
||||
|
@ -4,26 +4,25 @@
|
||||
*
|
||||
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.34 2006/01/02 19:55:25 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.35 2006/03/04 22:19:31 tgl Exp $
|
||||
*/
|
||||
|
||||
%{
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "miscadmin.h"
|
||||
#include "storage/fd.h"
|
||||
#include "utils/guc.h"
|
||||
|
||||
|
||||
/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
|
||||
#undef fprintf
|
||||
#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg)))
|
||||
|
||||
static unsigned ConfigFileLineno;
|
||||
|
||||
enum {
|
||||
GUC_ID = 1,
|
||||
GUC_STRING = 2,
|
||||
@ -36,9 +35,25 @@ enum {
|
||||
GUC_ERROR = 100
|
||||
};
|
||||
|
||||
/* prototype, so compiler is happy with our high warnings setting */
|
||||
struct name_value_pair
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
struct name_value_pair *next;
|
||||
};
|
||||
|
||||
static unsigned int ConfigFileLineno;
|
||||
|
||||
/* flex fails to supply a prototype for yylex, so provide one */
|
||||
int GUC_yylex(void);
|
||||
|
||||
static bool ParseConfigFile(const char *config_file, const char *calling_file,
|
||||
int depth, GucContext context, int elevel,
|
||||
struct name_value_pair **head_p,
|
||||
struct name_value_pair **tail_p);
|
||||
static void free_name_value_list(struct name_value_pair * list);
|
||||
static char *GUC_scanstr(const char *s);
|
||||
|
||||
%}
|
||||
|
||||
%option 8bit
|
||||
@ -85,38 +100,9 @@ STRING \'([^'\\\n]|\\.|\'\')*\'
|
||||
%%
|
||||
|
||||
|
||||
struct name_value_pair
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
struct name_value_pair *next;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Free a list of name/value pairs, including the names and the values
|
||||
*/
|
||||
static void
|
||||
free_name_value_list(struct name_value_pair * list)
|
||||
{
|
||||
struct name_value_pair *item;
|
||||
|
||||
item = list;
|
||||
while (item)
|
||||
{
|
||||
struct name_value_pair *save;
|
||||
|
||||
save = item->next;
|
||||
pfree(item->name);
|
||||
pfree(item->value);
|
||||
pfree(item);
|
||||
item = save;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Official function to read and process the configuration file. The
|
||||
* Exported function to read and process the configuration file. The
|
||||
* parameter indicates in what context the file is being read --- either
|
||||
* postmaster startup (including standalone-backend startup) or SIGHUP.
|
||||
* All options mentioned in the configuration file are set to new values.
|
||||
@ -126,10 +112,7 @@ void
|
||||
ProcessConfigFile(GucContext context)
|
||||
{
|
||||
int elevel;
|
||||
int token;
|
||||
char *opt_name, *opt_value;
|
||||
struct name_value_pair *item, *head, *tail;
|
||||
FILE *fp;
|
||||
|
||||
Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
|
||||
|
||||
@ -144,27 +127,124 @@ ProcessConfigFile(GucContext context)
|
||||
else
|
||||
elevel = ERROR;
|
||||
|
||||
fp = AllocateFile(ConfigFileName, "r");
|
||||
head = tail = NULL;
|
||||
|
||||
if (!ParseConfigFile(ConfigFileName, NULL,
|
||||
0, context, elevel,
|
||||
&head, &tail))
|
||||
goto cleanup_list;
|
||||
|
||||
/* Check if all options are valid */
|
||||
for (item = head; item; item = item->next)
|
||||
{
|
||||
if (!set_config_option(item->name, item->value, context,
|
||||
PGC_S_FILE, false, false))
|
||||
goto cleanup_list;
|
||||
}
|
||||
|
||||
/* If we got here all the options checked out okay, so apply them. */
|
||||
for (item = head; item; item = item->next)
|
||||
{
|
||||
set_config_option(item->name, item->value, context,
|
||||
PGC_S_FILE, false, true);
|
||||
}
|
||||
|
||||
cleanup_list:
|
||||
free_name_value_list(head);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read and parse a single configuration file. This function recurses
|
||||
* to handle "include" directives.
|
||||
*
|
||||
* Input parameters:
|
||||
* config_file: absolute or relative path of file to read
|
||||
* calling_file: absolute path of file containing the "include" directive,
|
||||
* or NULL at outer level (config_file must be absolute at outer level)
|
||||
* depth: recursion depth (used only to prevent infinite recursion)
|
||||
* context: GucContext passed to ProcessConfigFile()
|
||||
* elevel: error logging level determined by ProcessConfigFile()
|
||||
* Output parameters:
|
||||
* head_p, tail_p: head and tail of linked list of name/value pairs
|
||||
*
|
||||
* *head_p and *tail_p must be initialized to NULL before calling the outer
|
||||
* recursion level. On exit, they contain a list of name-value pairs read
|
||||
* from the input file(s).
|
||||
*
|
||||
* Returns TRUE if successful, FALSE if an error occurred. The error has
|
||||
* already been ereport'd, it is only necessary for the caller to clean up
|
||||
* its own state and release the name/value pairs list.
|
||||
*
|
||||
* Note: if elevel >= ERROR then an error will not return control to the
|
||||
* caller, and internal state such as open files will not be cleaned up.
|
||||
* This case occurs only during postmaster or standalone-backend startup,
|
||||
* where an error will lead to immediate process exit anyway; so there is
|
||||
* no point in contorting the code so it can clean up nicely.
|
||||
*/
|
||||
static bool
|
||||
ParseConfigFile(const char *config_file, const char *calling_file,
|
||||
int depth, GucContext context, int elevel,
|
||||
struct name_value_pair **head_p,
|
||||
struct name_value_pair **tail_p)
|
||||
{
|
||||
bool OK = true;
|
||||
char abs_path[MAXPGPATH];
|
||||
FILE *fp;
|
||||
YY_BUFFER_STATE lex_buffer;
|
||||
int token;
|
||||
|
||||
/*
|
||||
* Reject too-deep include nesting depth. This is just a safety check
|
||||
* to avoid dumping core due to stack overflow if an include file loops
|
||||
* back to itself. The maximum nesting depth is pretty arbitrary.
|
||||
*/
|
||||
if (depth > 10)
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||
errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
|
||||
config_file)));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If config_file is a relative path, convert to absolute. We consider
|
||||
* it to be relative to the directory holding the calling file.
|
||||
*/
|
||||
if (!is_absolute_path(config_file))
|
||||
{
|
||||
Assert(calling_file != NULL);
|
||||
StrNCpy(abs_path, calling_file, MAXPGPATH);
|
||||
get_parent_directory(abs_path);
|
||||
join_path_components(abs_path, abs_path, config_file);
|
||||
canonicalize_path(abs_path);
|
||||
config_file = abs_path;
|
||||
}
|
||||
|
||||
fp = AllocateFile(config_file, "r");
|
||||
if (!fp)
|
||||
{
|
||||
ereport(elevel,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not open configuration file \"%s\": %m",
|
||||
ConfigFileName)));
|
||||
return;
|
||||
config_file)));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse
|
||||
*/
|
||||
yyrestart(fp);
|
||||
head = tail = NULL;
|
||||
opt_name = opt_value = NULL;
|
||||
lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE);
|
||||
yy_switch_to_buffer(lex_buffer);
|
||||
|
||||
ConfigFileLineno = 1;
|
||||
|
||||
/* This loop iterates once per logical line */
|
||||
while ((token = yylex()))
|
||||
{
|
||||
char *opt_name, *opt_value;
|
||||
|
||||
if (token == GUC_EOL) /* empty or comment line */
|
||||
continue;
|
||||
|
||||
@ -195,8 +275,30 @@ ProcessConfigFile(GucContext context)
|
||||
if (token != GUC_EOL && token != 0)
|
||||
goto parse_error;
|
||||
|
||||
/* OK, save the option name and value */
|
||||
if (strcmp(opt_name, "custom_variable_classes") == 0)
|
||||
/* OK, process the option name and value */
|
||||
if (pg_strcasecmp(opt_name, "include") == 0)
|
||||
{
|
||||
/*
|
||||
* An include directive isn't a variable and should be processed
|
||||
* immediately.
|
||||
*/
|
||||
unsigned int save_ConfigFileLineno = ConfigFileLineno;
|
||||
|
||||
if (!ParseConfigFile(opt_value, config_file,
|
||||
depth + 1, context, elevel,
|
||||
head_p, tail_p))
|
||||
{
|
||||
pfree(opt_name);
|
||||
pfree(opt_value);
|
||||
OK = false;
|
||||
goto cleanup_exit;
|
||||
}
|
||||
yy_switch_to_buffer(lex_buffer);
|
||||
ConfigFileLineno = save_ConfigFileLineno;
|
||||
pfree(opt_name);
|
||||
pfree(opt_value);
|
||||
}
|
||||
else if (pg_strcasecmp(opt_name, "custom_variable_classes") == 0)
|
||||
{
|
||||
/*
|
||||
* This variable must be processed first as it controls
|
||||
@ -207,7 +309,8 @@ ProcessConfigFile(GucContext context)
|
||||
{
|
||||
pfree(opt_name);
|
||||
pfree(opt_value);
|
||||
FreeFile(fp);
|
||||
/* we assume error message was logged already */
|
||||
OK = false;
|
||||
goto cleanup_exit;
|
||||
}
|
||||
pfree(opt_name);
|
||||
@ -216,15 +319,17 @@ ProcessConfigFile(GucContext context)
|
||||
else
|
||||
{
|
||||
/* append to list */
|
||||
struct name_value_pair *item;
|
||||
|
||||
item = palloc(sizeof *item);
|
||||
item->name = opt_name;
|
||||
item->value = opt_value;
|
||||
item->next = NULL;
|
||||
if (!head)
|
||||
head = item;
|
||||
if (*head_p == NULL)
|
||||
*head_p = item;
|
||||
else
|
||||
tail->next = item;
|
||||
tail = item;
|
||||
(*tail_p)->next = item;
|
||||
*tail_p = item;
|
||||
}
|
||||
|
||||
/* break out of loop if read EOF, else loop for next line */
|
||||
@ -232,45 +337,49 @@ ProcessConfigFile(GucContext context)
|
||||
break;
|
||||
}
|
||||
|
||||
FreeFile(fp);
|
||||
|
||||
/*
|
||||
* Check if all options are valid
|
||||
*/
|
||||
for(item = head; item; item=item->next)
|
||||
{
|
||||
if (!set_config_option(item->name, item->value, context,
|
||||
PGC_S_FILE, false, false))
|
||||
goto cleanup_exit;
|
||||
}
|
||||
|
||||
/* If we got here all the options parsed okay, so apply them. */
|
||||
for(item = head; item; item=item->next)
|
||||
{
|
||||
set_config_option(item->name, item->value, context,
|
||||
PGC_S_FILE, false, true);
|
||||
}
|
||||
|
||||
cleanup_exit:
|
||||
free_name_value_list(head);
|
||||
return;
|
||||
/* successful completion of parsing */
|
||||
goto cleanup_exit;
|
||||
|
||||
parse_error:
|
||||
FreeFile(fp);
|
||||
free_name_value_list(head);
|
||||
if (token == GUC_EOL || token == 0)
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("syntax error in file \"%s\" line %u, near end of line",
|
||||
ConfigFileName, ConfigFileLineno - 1)));
|
||||
config_file, ConfigFileLineno - 1)));
|
||||
else
|
||||
ereport(elevel,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
|
||||
ConfigFileName, ConfigFileLineno, yytext)));
|
||||
config_file, ConfigFileLineno, yytext)));
|
||||
OK = false;
|
||||
|
||||
cleanup_exit:
|
||||
yy_delete_buffer(lex_buffer);
|
||||
FreeFile(fp);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Free a list of name/value pairs, including the names and the values
|
||||
*/
|
||||
static void
|
||||
free_name_value_list(struct name_value_pair *list)
|
||||
{
|
||||
struct name_value_pair *item;
|
||||
|
||||
item = list;
|
||||
while (item)
|
||||
{
|
||||
struct name_value_pair *next = item->next;
|
||||
|
||||
pfree(item->name);
|
||||
pfree(item->value);
|
||||
pfree(item);
|
||||
item = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* scanstr
|
||||
|
Loading…
Reference in New Issue
Block a user