]> granicus.if.org Git - postgresql/blob - src/backend/utils/misc/guc-file.l
Make autovacuum behavior more agressive, per discussion on hackers list
[postgresql] / src / backend / utils / misc / guc-file.l
1 /* -*-pgsql-c-*- */
2 /*
3  * Scanner for the configuration file
4  *
5  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
6  *
7  * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.45 2006/08/14 02:27:26 momjian Exp $
8  */
9
10 %{
11
12 #include "postgres.h"
13
14 #include <ctype.h>
15 #include <unistd.h>
16
17 #include "miscadmin.h"
18 #include "storage/fd.h"
19 #include "utils/guc.h"
20
21
22 /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
23 #undef fprintf
24 #define fprintf(file, fmt, msg)  ereport(ERROR, (errmsg_internal("%s", msg)))
25
26 enum {
27         GUC_ID = 1,
28         GUC_STRING = 2,
29         GUC_INTEGER = 3,
30         GUC_REAL = 4,
31         GUC_EQUALS = 5,
32         GUC_UNQUOTED_STRING = 6,
33         GUC_QUALIFIED_ID = 7,
34         GUC_EOL = 99,
35         GUC_ERROR = 100
36 };
37
38 struct name_value_pair
39 {
40         char       *name;
41         char       *value;
42         struct name_value_pair *next;
43 };
44
45 static unsigned int ConfigFileLineno;
46
47 /* flex fails to supply a prototype for yylex, so provide one */
48 int GUC_yylex(void);
49
50 static bool ParseConfigFile(const char *config_file, const char *calling_file,
51                                                         int depth, GucContext context, int elevel,
52                                                         struct name_value_pair **head_p,
53                                                         struct name_value_pair **tail_p);
54 static void free_name_value_list(struct name_value_pair * list);
55 static char *GUC_scanstr(const char *s);
56
57 %}
58
59 %option 8bit
60 %option never-interactive
61 %option nodefault
62 %option nounput
63 %option noyywrap
64 %option prefix="GUC_yy"
65
66
67 SIGN            ("-"|"+")
68 DIGIT           [0-9]
69 HEXDIGIT        [0-9a-fA-F]
70
71 UNIT_LETTER     [a-zA-Z]
72
73 INTEGER         {SIGN}?({DIGIT}+|0x{HEXDIGIT}+){UNIT_LETTER}*
74
75 EXPONENT        [Ee]{SIGN}?{DIGIT}+
76 REAL            {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}?
77
78 LETTER          [A-Za-z_\200-\377]
79 LETTER_OR_DIGIT [A-Za-z_0-9\200-\377]
80
81 ID              {LETTER}{LETTER_OR_DIGIT}*
82 QUALIFIED_ID    {ID}"."{ID}
83
84 UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])*
85 STRING          \'([^'\\\n]|\\.|\'\')*\'
86
87 %%
88
89 \n              ConfigFileLineno++; return GUC_EOL;
90 [ \t\r]+        /* eat whitespace */
91 #.*             /* eat comment (.* matches anything until newline) */
92
93 {ID}            return GUC_ID;
94 {QUALIFIED_ID}  return GUC_QUALIFIED_ID;
95 {STRING}        return GUC_STRING;
96 {UNQUOTED_STRING} return GUC_UNQUOTED_STRING;
97 {INTEGER}       return GUC_INTEGER;
98 {REAL}          return GUC_REAL;
99 =               return GUC_EQUALS;
100
101 .               return GUC_ERROR;
102
103 %%
104
105
106
107 /*
108  * Exported function to read and process the configuration file. The
109  * parameter indicates in what context the file is being read --- either
110  * postmaster startup (including standalone-backend startup) or SIGHUP.
111  * All options mentioned in the configuration file are set to new values.
112  * If an error occurs, no values will be changed.
113  */
114 void
115 ProcessConfigFile(GucContext context)
116 {
117         int                     elevel;
118         struct name_value_pair *item, *head, *tail;
119
120         Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP);
121
122         if (context == PGC_SIGHUP)
123         {
124                 /*
125                  * To avoid cluttering the log, only the postmaster bleats loudly
126                  * about problems with the config file.
127                  */
128                 elevel = IsUnderPostmaster ? DEBUG2 : LOG;
129         }
130         else
131                 elevel = ERROR;
132
133         head = tail = NULL;
134
135         if (!ParseConfigFile(ConfigFileName, NULL,
136                                                  0, context, elevel,
137                                                  &head, &tail))
138                 goto cleanup_list;
139
140         /* Check if all options are valid */
141         for (item = head; item; item = item->next)
142         {
143                 if (!set_config_option(item->name, item->value, context,
144                                                            PGC_S_FILE, false, false))
145                         goto cleanup_list;
146         }
147
148         /* If we got here all the options checked out okay, so apply them. */
149         for (item = head; item; item = item->next)
150         {
151                 set_config_option(item->name, item->value, context,
152                                                   PGC_S_FILE, false, true);
153         }
154
155  cleanup_list:
156         free_name_value_list(head);
157 }
158
159
160 /*
161  * Read and parse a single configuration file.  This function recurses
162  * to handle "include" directives.
163  *
164  * Input parameters:
165  *      config_file: absolute or relative path of file to read
166  *      calling_file: absolute path of file containing the "include" directive,
167  *              or NULL at outer level (config_file must be absolute at outer level)
168  *      depth: recursion depth (used only to prevent infinite recursion)
169  *      context: GucContext passed to ProcessConfigFile()
170  *      elevel: error logging level determined by ProcessConfigFile()
171  * Output parameters:
172  *      head_p, tail_p: head and tail of linked list of name/value pairs
173  *
174  * *head_p and *tail_p must be initialized to NULL before calling the outer
175  * recursion level.  On exit, they contain a list of name-value pairs read
176  * from the input file(s).
177  *
178  * Returns TRUE if successful, FALSE if an error occurred.  The error has
179  * already been ereport'd, it is only necessary for the caller to clean up
180  * its own state and release the name/value pairs list.
181  *
182  * Note: if elevel >= ERROR then an error will not return control to the
183  * caller, and internal state such as open files will not be cleaned up.
184  * This case occurs only during postmaster or standalone-backend startup,
185  * where an error will lead to immediate process exit anyway; so there is
186  * no point in contorting the code so it can clean up nicely.
187  */
188 static bool
189 ParseConfigFile(const char *config_file, const char *calling_file,
190                                 int depth, GucContext context, int elevel,
191                                 struct name_value_pair **head_p,
192                                 struct name_value_pair **tail_p)
193 {
194         bool            OK = true;
195         char            abs_path[MAXPGPATH];
196         FILE       *fp;
197         YY_BUFFER_STATE lex_buffer;
198         int                     token;
199
200         /*
201          * Reject too-deep include nesting depth.  This is just a safety check
202          * to avoid dumping core due to stack overflow if an include file loops
203          * back to itself.  The maximum nesting depth is pretty arbitrary.
204          */
205         if (depth > 10)
206         {
207                 ereport(elevel,
208                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
209                                  errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
210                                                 config_file)));
211                 return false;
212         }
213
214         /*
215          * If config_file is a relative path, convert to absolute.  We consider
216          * it to be relative to the directory holding the calling file.
217          */
218         if (!is_absolute_path(config_file))
219         {
220                 Assert(calling_file != NULL);
221                 StrNCpy(abs_path, calling_file, MAXPGPATH);
222                 get_parent_directory(abs_path);
223                 join_path_components(abs_path, abs_path, config_file);
224                 canonicalize_path(abs_path);
225                 config_file = abs_path;
226         }
227
228         fp = AllocateFile(config_file, "r");
229         if (!fp)
230         {
231                 ereport(elevel,
232                                 (errcode_for_file_access(),
233                                  errmsg("could not open configuration file \"%s\": %m",
234                                                 config_file)));
235                 return false;
236         }
237
238         /*
239          * Parse
240          */
241         lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE);
242         yy_switch_to_buffer(lex_buffer);
243
244         ConfigFileLineno = 1;
245
246         /* This loop iterates once per logical line */
247         while ((token = yylex()))
248         {
249                 char       *opt_name, *opt_value;
250
251                 if (token == GUC_EOL)   /* empty or comment line */
252                         continue;
253
254                 /* first token on line is option name */
255                 if (token != GUC_ID && token != GUC_QUALIFIED_ID)
256                         goto parse_error;
257                 opt_name = pstrdup(yytext);
258
259                 /* next we have an optional equal sign; discard if present */
260                 token = yylex();
261                 if (token == GUC_EQUALS)
262                         token = yylex();
263
264                 /* now we must have the option value */
265                 if (token != GUC_ID &&
266                         token != GUC_STRING && 
267                         token != GUC_INTEGER && 
268                         token != GUC_REAL && 
269                         token != GUC_UNQUOTED_STRING)
270                         goto parse_error;
271                 if (token == GUC_STRING)        /* strip quotes and escapes */
272                         opt_value = GUC_scanstr(yytext);
273                 else
274                         opt_value = pstrdup(yytext);
275
276                 /* now we'd like an end of line, or possibly EOF */
277                 token = yylex();
278                 if (token != GUC_EOL && token != 0)
279                         goto parse_error;
280
281                 /* OK, process the option name and value */
282                 if (pg_strcasecmp(opt_name, "include") == 0)
283                 {
284                         /*
285                          * An include directive isn't a variable and should be processed
286                          * immediately.
287                          */
288                         unsigned int save_ConfigFileLineno = ConfigFileLineno;
289
290                         if (!ParseConfigFile(opt_value, config_file,
291                                                                  depth + 1, context, elevel,
292                                                                  head_p, tail_p))
293                         {
294                                 pfree(opt_name);
295                                 pfree(opt_value);
296                                 OK = false;
297                                 goto cleanup_exit;
298                         }
299                         yy_switch_to_buffer(lex_buffer);
300                         ConfigFileLineno = save_ConfigFileLineno;
301                         pfree(opt_name);
302                         pfree(opt_value);
303                 }
304                 else if (pg_strcasecmp(opt_name, "custom_variable_classes") == 0)
305                 {
306                         /*
307                          * This variable must be processed first as it controls
308                          * the validity of other variables; so apply immediately.
309                          */
310                         if (!set_config_option(opt_name, opt_value, context,
311                                                                    PGC_S_FILE, false, true))
312                         {
313                                 pfree(opt_name);
314                                 pfree(opt_value);
315                                 /* we assume error message was logged already */
316                                 OK = false;
317                                 goto cleanup_exit;
318                         }
319                         pfree(opt_name);
320                         pfree(opt_value);
321                 }
322                 else
323                 {
324                         /* append to list */
325                         struct name_value_pair *item;
326
327                         item = palloc(sizeof *item);
328                         item->name = opt_name;
329                         item->value = opt_value;
330                         item->next = NULL;
331                         if (*head_p == NULL)
332                                 *head_p = item;
333                         else
334                                 (*tail_p)->next = item;
335                         *tail_p = item;
336                 }
337
338                 /* break out of loop if read EOF, else loop for next line */
339                 if (token == 0)
340                         break;
341         }
342
343         /* successful completion of parsing */
344         goto cleanup_exit;
345
346  parse_error:
347         if (token == GUC_EOL || token == 0)
348                 ereport(elevel,
349                                 (errcode(ERRCODE_SYNTAX_ERROR),
350                                  errmsg("syntax error in file \"%s\" line %u, near end of line",
351                                                 config_file, ConfigFileLineno - 1)));
352         else
353                 ereport(elevel,
354                                 (errcode(ERRCODE_SYNTAX_ERROR),
355                                  errmsg("syntax error in file \"%s\" line %u, near token \"%s\"", 
356                                                 config_file, ConfigFileLineno, yytext)));
357         OK = false;
358
359 cleanup_exit:
360         yy_delete_buffer(lex_buffer);
361         FreeFile(fp);
362         return OK;
363 }
364
365
366 /*
367  * Free a list of name/value pairs, including the names and the values
368  */
369 static void
370 free_name_value_list(struct name_value_pair *list)
371 {
372         struct name_value_pair *item;
373
374         item = list;
375         while (item)
376         {
377                 struct name_value_pair *next = item->next;
378
379                 pfree(item->name);
380                 pfree(item->value);
381                 pfree(item);
382                 item = next;
383         }
384 }
385
386
387 /*
388  *              scanstr
389  *
390  * Strip the quotes surrounding the given string, and collapse any embedded
391  * '' sequences and backslash escapes.
392  *
393  * the string returned is palloc'd and should eventually be pfree'd by the
394  * caller.
395  */
396 static char *
397 GUC_scanstr(const char *s)
398 {
399         char       *newStr;
400         int                     len,
401                                 i,
402                                 j;
403
404         Assert(s != NULL && s[0] == '\'');
405         len = strlen(s);
406         Assert(len >= 2);
407         Assert(s[len-1] == '\'');
408
409         /* Skip the leading quote; we'll handle the trailing quote below */
410         s++, len--;
411
412         /* Since len still includes trailing quote, this is enough space */
413         newStr = palloc(len);
414
415         for (i = 0, j = 0; i < len; i++)
416         {
417                 if (s[i] == '\\')
418                 {
419                         i++;
420                         switch (s[i])
421                         {
422                                 case 'b':
423                                         newStr[j] = '\b';
424                                         break;
425                                 case 'f':
426                                         newStr[j] = '\f';
427                                         break;
428                                 case 'n':
429                                         newStr[j] = '\n';
430                                         break;
431                                 case 'r':
432                                         newStr[j] = '\r';
433                                         break;
434                                 case 't':
435                                         newStr[j] = '\t';
436                                         break;
437                                 case '0':
438                                 case '1':
439                                 case '2':
440                                 case '3':
441                                 case '4':
442                                 case '5':
443                                 case '6':
444                                 case '7':
445                                         {
446                                                 int                     k;
447                                                 long            octVal = 0;
448
449                                                 for (k = 0;
450                                                          s[i + k] >= '0' && s[i + k] <= '7' && k < 3;
451                                                          k++)
452                                                         octVal = (octVal << 3) + (s[i + k] - '0');
453                                                 i += k - 1;
454                                                 newStr[j] = ((char) octVal);
455                                         }
456                                         break;
457                                 default:
458                                         newStr[j] = s[i];
459                                         break;
460                         }                                       /* switch */
461                 }
462                 else if (s[i] == '\'' && s[i+1] == '\'')
463                 {
464                         /* doubled quote becomes just one quote */
465                         newStr[j] = s[++i];
466                 }
467                 else
468                         newStr[j] = s[i];
469                 j++;
470         }
471
472         /* We copied the ending quote to newStr, so replace with \0 */
473         Assert(j > 0 && j <= len);
474         newStr[--j] = '\0';
475
476         return newStr;
477 }