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); ! } ! } }