diff -cr cvs/pgsql/doc/src/sgml/config.sgml cvs.build/pgsql/doc/src/sgml/config.sgml *** cvs/pgsql/doc/src/sgml/config.sgml 2005-11-20 13:42:44.000000000 +0100 --- cvs.build/pgsql/doc/src/sgml/config.sgml 2005-12-02 00:05:38.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,247 ---- + + + 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. Later + values for configuration options will overwrite previous assignments. + This option can not be specified on the command line. + + + *************** *** 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. --- 278,289 ---- ! 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 2005-09-22 14:31:21.000000000 +0200 --- cvs.build/pgsql/src/backend/utils/misc/guc-file.l 2005-12-01 23:24:07.000000000 +0100 *************** *** 115,165 **** } ! /* ! * 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, parse_state; - 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 ! */ ! yyin = fp; ! parse_state = 0; ! head = tail = NULL; opt_name = opt_value = NULL; ConfigFileLineno = 1; --- 115,202 ---- } ! /* 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, ! 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, parse_state; ! 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'; ! 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); ! ! parse_state = 0; opt_name = opt_value = NULL; ConfigFileLineno = 1; *************** *** 209,220 **** { pfree(opt_name); pfree(opt_value); FreeFile(fp); ! goto cleanup_exit; } pfree(opt_name); pfree(opt_value); } else { /* append to list */ --- 246,277 ---- { 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; + yypush_buffer_state(lex_buffer); + if (ParseConfigFile(opt_value, + NewBasePath, + context, elevel, + head_p, tail_p) != 0) { + yy_delete_buffer(lex_buffer); + pfree(NewBasePath); + pfree(ConfigFileNameCopy); + FreeFile(fp); + return -1; + } + yypop_buffer_state(); + ConfigFileLineno = LocalConfigFileLineno; + } else { /* append to list */ *************** *** 222,232 **** item->name = opt_name; item->value = opt_value; item->next = NULL; ! if (!head) ! head = item; else ! tail->next = item; ! tail = item; } parse_state = 0; --- 279,289 ---- 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; } parse_state = 0; *************** *** 234,278 **** } } 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) 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))); } /* * scanstr --- 291,374 ---- } } + pfree(NewBasePath); + pfree(ConfigFileNameCopy); FreeFile(fp); ! return 0; parse_error: FreeFile(fp); ! free_name_value_list(*head_p); if (token == GUC_EOL) 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, 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); + } + } + } + /* * scanstr