diff -cr cvs/pgsql/doc/src/sgml/config.sgml cvs.build/pgsql/doc/src/sgml/config.sgml
*** cvs/pgsql/doc/src/sgml/config.sgml 2006-01-12 08:08:54.000000000 +0100
--- cvs.build/pgsql/doc/src/sgml/config.sgml 2006-01-20 14:23:39.000000000 +0100
***************
*** 71,76 ****
--- 71,78 ----
postmaster -c log_connections=yes -c log_destination='syslog'
+ (The special option include however can not be specified
+ on the command line).
Command-line options override any conflicting settings in
postgresql.conf. Note that this means you won't
be able to change the value on-the-fly by editing
***************
*** 224,229 ****
--- 226,248 ----
+
+
+ include (string)
+
+ include> configuration parameter
+
+
+
+ Specifies another configuration file that is read in by the starting
+ postgres> process. You can use this option multiple
+ times and you can also read in configuration files recursively.
+ This option can not be specified on the command line and can not be set
+ nor inspected with the
+ or commands.
+
+
+
***************
*** 260,268 ****
! When setting any of these options, a relative path will be interpreted
with respect to the directory in which the postmaster
! is started.
--- 279,290 ----
! When setting any of these options except for the
! include option, a relative path will be interpreted
with respect to the directory in which the postmaster
! is started. For the include option, a relative path
! will always be relative to the path of the file that contained the
! include option.
diff -cr cvs/pgsql/src/backend/utils/misc/guc-file.l cvs.build/pgsql/src/backend/utils/misc/guc-file.l
*** cvs/pgsql/src/backend/utils/misc/guc-file.l 2006-01-02 20:55:25.000000000 +0100
--- cvs.build/pgsql/src/backend/utils/misc/guc-file.l 2006-01-20 13:53:27.000000000 +0100
***************
*** 22,27 ****
--- 22,35 ----
#undef fprintf
#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg)))
+ /* include configuration files to a maximal recursion depth of 10 */
+ #define MAX_INCLUDE_FILE_DEPTH 10
+
+ /* we have to manually handle buffer stack management (newer versions of flex
+ * >= 2.5.31 can handle this via calls to push/pop buffer functions) */
+ static YY_BUFFER_STATE GUC_include_stack[MAX_INCLUDE_FILE_DEPTH];
+ static int GUC_include_stack_counter = 0;
+
static unsigned ConfigFileLineno;
enum {
***************
*** 115,164 ****
}
! /*
! * Official 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.
! * If an error occurs, no values will be changed.
*/
! 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);
! if (context == PGC_SIGHUP)
{
! /*
! * To avoid cluttering the log, only the postmaster bleats loudly
! * about problems with the config file.
! */
! elevel = IsUnderPostmaster ? DEBUG2 : LOG;
}
else
! elevel = ERROR;
! fp = AllocateFile(ConfigFileName, "r");
if (!fp)
{
ereport(elevel,
(errcode_for_file_access(),
errmsg("could not open configuration file \"%s\": %m",
! ConfigFileName)));
! return;
}
! /*
! * Parse
! */
! yyrestart(fp);
! head = tail = NULL;
opt_name = opt_value = NULL;
ConfigFileLineno = 1;
--- 123,220 ----
}
! /* Function that calls itself recursively for each configuration file that gets
! * included via the "include = 'file.conf'" directive.
! * The first time it gets called by ProcessConfigFile()
! * See below for notes on the parameters ConfigFileName and BasePath.
! *
! * The function returns
! * 0 if OK
! * -1 on error
*/
! static int
! ParseConfigFile(char* ConfigFileName,
! char* BasePath,
! int depth,
! GucContext context, int elevel,
! struct name_value_pair **head_p,
! struct name_value_pair **tail_p)
{
! struct name_value_pair *item;
! YY_BUFFER_STATE lex_buffer;
! int token;
! int LocalConfigFileLineno;
! char *opt_name;
! char *opt_value;
! char *ConfigFileNameCopy;
! char *NewBasePath;
! char *p;
! FILE *fp;
!
! /* when the function gets called for the first time by ProcessConfigFile,
! * we get a BasePath == NULL but the ConfigFileName is absolute in every
! * case (because the filename the user specified is transformed by
! * make_absolute_path()). If we encounter another include = '...' in the
! * configuration file however we can be sure that BasePath has been set to
! * the path of the including file. */
! if (!is_absolute_path(ConfigFileName))
{
! /* If the path is not absolute, make it absolute by prepending the
! * BasePath */
! int length;
! Assert(BasePath != NULL);
! length = strlen(BasePath)
! + 1
! + strlen(ConfigFileName)
! + 1;
! ConfigFileNameCopy = (char*) palloc(length);
! strcpy(ConfigFileNameCopy, BasePath);
! strcat(ConfigFileNameCopy, "/");
! strcat(ConfigFileNameCopy, ConfigFileName);
}
else
! ConfigFileNameCopy = pstrdup(ConfigFileName);
!
! /* Calculate the new base path, i.e. the path where the new config file is
! * in. We have an absolute path to the configuration file we're reading
! * already (it might be /usr/local/etc/../etc/include.conf however but it
! * doesn't matter).
! * What we do is basically just copying over the whole and then stripping
! * off everything behind the last slash. */
!
! NewBasePath = pstrdup(ConfigFileNameCopy);
! p = NewBasePath + strlen(NewBasePath);
! while (*p != '/' && p > NewBasePath)
! *p-- = '\0';
!
! /* strip off the slash as well */
! if (*p == '/')
! *p = '\0';
! if (depth > MAX_INCLUDE_FILE_DEPTH)
! {
! ereport(elevel,
! (errcode_for_file_access(),
! errmsg("could not open configuration file \"%s\": "
! "maximal inclusion depth reached",
! ConfigFileNameCopy)));
! return -1;
! }
!
! fp = AllocateFile(ConfigFileNameCopy, "r");
if (!fp)
{
ereport(elevel,
(errcode_for_file_access(),
errmsg("could not open configuration file \"%s\": %m",
! ConfigFileNameCopy)));
! return -1;
}
! lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE);
! yy_switch_to_buffer(lex_buffer);
!
opt_name = opt_value = NULL;
ConfigFileLineno = 1;
***************
*** 207,218 ****
{
pfree(opt_name);
pfree(opt_value);
FreeFile(fp);
! goto cleanup_exit;
}
pfree(opt_name);
pfree(opt_value);
}
else
{
/* append to list */
--- 263,299 ----
{
pfree(opt_name);
pfree(opt_value);
+ pfree(NewBasePath);
+ pfree(ConfigFileNameCopy);
FreeFile(fp);
! free_name_value_list(*head_p);
! return 0;
}
pfree(opt_name);
pfree(opt_value);
}
+ else if (strcmp(opt_name, "include") == 0)
+ {
+ LocalConfigFileLineno = ConfigFileLineno;
+ GUC_include_stack[GUC_include_stack_counter++] = lex_buffer;
+ if (ParseConfigFile(opt_value,
+ NewBasePath,
+ depth + 1,
+ context, elevel,
+ head_p, tail_p) != 0)
+ {
+ yy_delete_buffer(lex_buffer);
+ pfree(NewBasePath);
+ pfree(ConfigFileNameCopy);
+ FreeFile(fp);
+ return -1;
+ }
+ yy_delete_buffer(GUC_include_stack[GUC_include_stack_counter]);
+ GUC_include_stack_counter--;
+ lex_buffer = GUC_include_stack[GUC_include_stack_counter];
+ yy_switch_to_buffer(lex_buffer);
+ ConfigFileLineno = LocalConfigFileLineno;
+ }
else
{
/* append to list */
***************
*** 220,230 ****
item->name = opt_name;
item->value = opt_value;
item->next = NULL;
! if (!head)
! head = item;
else
! tail->next = item;
! tail = item;
}
/* break out of loop if read EOF, else loop for next line */
--- 301,311 ----
item->name = opt_name;
item->value = opt_value;
item->next = NULL;
! if (!(*head_p))
! *head_p = item;
else
! (*tail_p)->next = item;
! (*tail_p) = item;
}
/* break out of loop if read EOF, else loop for next line */
***************
*** 232,273 ****
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;
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)));
else
ereport(elevel,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
! ConfigFileName, ConfigFileLineno, yytext)));
}
--- 313,397 ----
break;
}
+ pfree(NewBasePath);
+ pfree(ConfigFileNameCopy);
FreeFile(fp);
! return 0;
parse_error:
FreeFile(fp);
! free_name_value_list(*head_p);
if (token == GUC_EOL || token == 0)
ereport(elevel,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error in file \"%s\" line %u, near end of line",
! ConfigFileNameCopy, ConfigFileLineno - 1)));
else
ereport(elevel,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
! ConfigFileNameCopy, ConfigFileLineno, yytext)));
!
! yy_delete_buffer(lex_buffer);
! pfree(NewBasePath);
! pfree(ConfigFileNameCopy);
! return -1;
! }
!
! /*
! * Official 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.
! * If an error occurs, no values will be changed.
! */
! void
! ProcessConfigFile(GucContext context)
! {
! int elevel;
! struct name_value_pair *item, *head, *tail;
!
! Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
!
! if (context == PGC_SIGHUP)
! {
! /*
! * To avoid cluttering the log, only the postmaster bleats loudly
! * about problems with the config file.
! */
! elevel = IsUnderPostmaster ? DEBUG2 : LOG;
! }
! else
! elevel = ERROR;
!
! /*
! * Parse
! */
! head = tail = NULL;
! if (ParseConfigFile(ConfigFileName, NULL, 0, context,
! elevel, &head, &tail) == 0)
! {
! /*
! * 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))
! {
! free_name_value_list(head);
! return;
! }
! }
!
! /* 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);
! }
! }
}