Hi,

the attached patch implements the TODO-item

        o %Add "include file" functionality in postgresql.conf

However I was not able to test it on Windows  :-(

I talked to Peter Eisentraut about how to handle relative file names and his
opinion was that a relative file name should be interpreted to be relative
to the path of the including file, i.e. to the path of the file that
contained the actual "include = other.conf" line. The patch follows this
idea.


Joachim
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 ----
  <programlisting>
  postmaster -c log_connections=yes -c log_destination='syslog'
  </programlisting>
+     (The special option <varname>include</varname> however can not be 
specified
+     on the command line).
      Command-line options override any conflicting settings in
      <filename>postgresql.conf</filename>.  Note that this means you won't
      be able to change the value on-the-fly by editing
***************
*** 224,229 ****
--- 226,247 ----
         </para>
        </listitem>
       </varlistentry>
+ 
+      <varlistentry id="guc-include" xreflabel="include">
+       <term><varname>include</varname> (<type>string</type>)</term>
+       <indexterm>
+        <primary><varname>include</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Specifies another configuration file that is read in by the starting
+         <application>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.
+        </para>
+       </listitem>
+      </varlistentry>
       </variablelist>
  
       <para>
***************
*** 260,268 ****
       </para>
  
       <para>
!       When setting any of these options, a relative path will be interpreted
        with respect to the directory in which the <command>postmaster</command>
!       is started.
       </para>
     </sect1>
  
--- 278,289 ----
       </para>
  
       <para>
!       When setting any of these options except for the
!       <varname>include</varname> option, a relative path will be interpreted
        with respect to the directory in which the <command>postmaster</command>
!       is started. For the <varname>include</varname> option, a relative path
!       will always be relative to the path of the file that contained the
!       <varname>include</varname> option.
       </para>
     </sect1>
  
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
---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?

               http://www.postgresql.org/docs/faq

Reply via email to