| /* -*-pgsql-c-*- */ |
| /* |
| * Scanner for the configuration file |
| * |
| * Copyright (c) 2000-2008, PostgreSQL Global Development Group |
| * |
| * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.45 2006/08/14 02:27:26 momjian Exp $ |
| */ |
| |
| %{ |
| |
| #include "postgres.h" |
| |
| #include <ctype.h> |
| #include <unistd.h> |
| |
| #include "miscadmin.h" |
| #include "storage/fd.h" |
| #include "utils/guc.h" |
| |
| #include "resourcemanager/resourcemanager.h" |
| #include "resourcemanager/utils/kvproperties.h" |
| |
| |
| |
| #define unify_version(a,b,c) ((a<<16)+(b<<8)+c) |
| #if unify_version(YY_FLEX_MAJOR_VERSION,YY_FLEX_MINOR_VERSION,YY_FLEX_SUBMINOR_VERSION) < unify_version(2,5,35) |
| int GUC_yylex_destroy (void); |
| int GUC_yyget_lineno (void); |
| FILE *GUC_yyget_in (void); |
| FILE *GUC_yyget_out (void); |
| int GUC_yyget_leng (void); |
| char *GUC_yyget_text (void); |
| void GUC_yyset_lineno (int line_number ); |
| void GUC_yyset_in (FILE * in_str ); |
| void GUC_yyset_out (FILE * out_str ); |
| int GUC_yyget_debug (void); |
| void GUC_yyset_debug (int bdebug ); |
| int GUC_yylex_destroy (void); |
| #endif |
| |
| |
| |
| |
| /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ |
| #undef fprintf |
| #define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) |
| |
| enum { |
| GUC_ID = 1, |
| GUC_STRING = 2, |
| GUC_INTEGER = 3, |
| GUC_REAL = 4, |
| GUC_EQUALS = 5, |
| GUC_UNQUOTED_STRING = 6, |
| GUC_QUALIFIED_ID = 7, |
| GUC_EOL = 99, |
| GUC_ERROR = 100 |
| }; |
| |
| struct name_value_pair |
| { |
| char *name; |
| char *value; |
| struct name_value_pair *next; |
| }; |
| |
| static unsigned int ConfigFileLineno; |
| |
| /* flex fails to supply a prototype for yylex, so provide one */ |
| int GUC_yylex(void); |
| |
| static bool ParseConfigFile(const char *config_file, const char *calling_file, |
| int depth, GucContext context, int elevel, |
| struct name_value_pair **head_p, |
| struct name_value_pair **tail_p); |
| static void free_name_value_list(struct name_value_pair * list); |
| static char *GUC_scanstr(const char *s); |
| |
| %} |
| |
| %option 8bit |
| %option never-interactive |
| %option nodefault |
| %option noinput |
| %option nounput |
| %option noyywrap |
| %option prefix="GUC_yy" |
| |
| |
| SIGN ("-"|"+") |
| DIGIT [0-9] |
| HEXDIGIT [0-9a-fA-F] |
| |
| UNIT_LETTER [a-zA-Z] |
| |
| INTEGER {SIGN}?({DIGIT}+|0x{HEXDIGIT}+){UNIT_LETTER}* |
| |
| EXPONENT [Ee]{SIGN}?{DIGIT}+ |
| REAL {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}? |
| |
| LETTER [A-Za-z_\200-\377] |
| LETTER_OR_DIGIT [A-Za-z_0-9\200-\377] |
| |
| ID {LETTER}{LETTER_OR_DIGIT}* |
| QUALIFIED_ID {ID}"."{ID} |
| |
| UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])* |
| STRING \'([^'\\\n]|\\.|\'\')*\' |
| |
| %% |
| |
| \n ConfigFileLineno++; return GUC_EOL; |
| [ \t\r]+ /* eat whitespace */ |
| #.* /* eat comment (.* matches anything until newline) */ |
| |
| {ID} return GUC_ID; |
| {QUALIFIED_ID} return GUC_QUALIFIED_ID; |
| {STRING} return GUC_STRING; |
| {UNQUOTED_STRING} return GUC_UNQUOTED_STRING; |
| {INTEGER} return GUC_INTEGER; |
| {REAL} return GUC_REAL; |
| = return GUC_EQUALS; |
| |
| . return GUC_ERROR; |
| |
| %% |
| |
| |
| |
| /* |
| * Exported 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; |
| |
| head = tail = NULL; |
| |
| if (ParseConfigFile(ConfigFileName, NULL, |
| 0, context, elevel, |
| &head, &tail)) { |
| //fprintf(stderr, "%s","Loaded config file."); |
| |
| /* try to apply options brute-force; depending on the context bad ones will be |
| * logged accordingly; |
| */ |
| for (item = head; item; item = item->next) |
| { |
| set_config_option(item->name, item->value, context, |
| PGC_S_FILE, false, true); |
| } |
| } |
| else { |
| //fprintf(stderr, "%s","Failed loading config file."); |
| } |
| free_name_value_list(head); |
| |
| /* process hawq-site.xml to overwrite config options. */ |
| List *conf = getHawqSiteConfigurationList(NULL, CurrentMemoryContext); |
| if ( conf != NULL ) |
| { |
| ListCell *cell = NULL; |
| foreach(cell, conf) |
| { |
| KVProperty prop = lfirst(cell); |
| |
| set_config_option(prop->Key.Str, |
| prop->Val.Str, |
| context, |
| PGC_S_FILE, |
| false, |
| true); |
| } |
| freeHawqSiteConfigurationList(CurrentMemoryContext, &conf); |
| Assert(conf == NULL); |
| } |
| } |
| |
| |
| /* |
| * Read and parse a single configuration file. This function recurses |
| * to handle "include" directives. |
| * |
| * Input parameters: |
| * config_file: absolute or relative path of file to read |
| * calling_file: absolute path of file containing the "include" directive, |
| * or NULL at outer level (config_file must be absolute at outer level) |
| * depth: recursion depth (used only to prevent infinite recursion) |
| * context: GucContext passed to ProcessConfigFile() |
| * elevel: error logging level determined by ProcessConfigFile() |
| * Output parameters: |
| * head_p, tail_p: head and tail of linked list of name/value pairs |
| * |
| * *head_p and *tail_p must be initialized to NULL before calling the outer |
| * recursion level. On exit, they contain a list of name-value pairs read |
| * from the input file(s). |
| * |
| * Returns TRUE if successful, FALSE if an error occurred. The error has |
| * already been ereport'd, it is only necessary for the caller to clean up |
| * its own state and release the name/value pairs list. |
| * |
| * Note: if elevel >= ERROR then an error will not return control to the |
| * caller, and internal state such as open files will not be cleaned up. |
| * This case occurs only during postmaster or standalone-backend startup, |
| * where an error will lead to immediate process exit anyway; so there is |
| * no point in contorting the code so it can clean up nicely. |
| */ |
| static bool |
| ParseConfigFile(const char *config_file, const char *calling_file, |
| int depth, GucContext context, int elevel, |
| struct name_value_pair **head_p, |
| struct name_value_pair **tail_p) |
| { |
| bool OK = true; |
| char abs_path[MAXPGPATH]; |
| FILE *fp; |
| YY_BUFFER_STATE lex_buffer; |
| int token; |
| |
| /* |
| * Reject too-deep include nesting depth. This is just a safety check |
| * to avoid dumping core due to stack overflow if an include file loops |
| * back to itself. The maximum nesting depth is pretty arbitrary. |
| */ |
| if (depth > 10) |
| { |
| ereport(elevel, |
| (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
| errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded", |
| config_file))); |
| |
| Assert(PGC_POSTMASTER != context || IsUnderPostmaster); |
| return false; |
| } |
| |
| /* |
| * If config_file is a relative path, convert to absolute. We consider |
| * it to be relative to the directory holding the calling file. |
| */ |
| if (!is_absolute_path(config_file)) |
| { |
| Assert(calling_file != NULL); |
| StrNCpy(abs_path, calling_file, MAXPGPATH); |
| get_parent_directory(abs_path); |
| join_path_components(abs_path, abs_path, config_file); |
| canonicalize_path(abs_path); |
| config_file = abs_path; |
| } |
| |
| fp = AllocateFile(config_file, "r"); |
| if (!fp) |
| { |
| ereport(elevel, |
| (errcode_for_file_access(), |
| errmsg("could not open configuration file \"%s\": %m", |
| config_file))); |
| |
| Assert(PGC_POSTMASTER != context || IsUnderPostmaster); |
| return false; |
| } |
| |
| /* |
| * Parse |
| */ |
| lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE); |
| yy_switch_to_buffer(lex_buffer); |
| |
| ConfigFileLineno = 1; |
| |
| /* This loop iterates once per logical line */ |
| while ((token = yylex())) |
| { |
| char *opt_name, *opt_value; |
| |
| if (token == GUC_EOL) /* empty or comment line */ |
| continue; |
| |
| /* first token on line is option name */ |
| if (token != GUC_ID && token != GUC_QUALIFIED_ID) |
| goto parse_error; |
| opt_name = pstrdup(yytext); |
| |
| /* next we have an optional equal sign; discard if present */ |
| token = yylex(); |
| if (token == GUC_EQUALS) |
| token = yylex(); |
| |
| /* now we must have the option value */ |
| if (token != GUC_ID && |
| token != GUC_STRING && |
| token != GUC_INTEGER && |
| token != GUC_REAL && |
| token != GUC_UNQUOTED_STRING) |
| goto parse_error; |
| if (token == GUC_STRING) /* strip quotes and escapes */ |
| opt_value = GUC_scanstr(yytext); |
| else |
| opt_value = pstrdup(yytext); |
| |
| /* now we'd like an end of line, or possibly EOF */ |
| token = yylex(); |
| if (token != GUC_EOL && token != 0) |
| goto parse_error; |
| |
| /* OK, process the option name and value */ |
| if (pg_strcasecmp(opt_name, "include") == 0) |
| { |
| /* |
| * An include directive isn't a variable and should be processed |
| * immediately. |
| */ |
| unsigned int save_ConfigFileLineno = ConfigFileLineno; |
| |
| if (!ParseConfigFile(opt_value, config_file, |
| depth + 1, context, elevel, |
| head_p, tail_p)) |
| { |
| Assert(PGC_POSTMASTER != context || IsUnderPostmaster); |
| pfree(opt_name); |
| pfree(opt_value); |
| OK = false; |
| goto cleanup_exit; |
| } |
| yy_switch_to_buffer(lex_buffer); |
| ConfigFileLineno = save_ConfigFileLineno; |
| pfree(opt_name); |
| pfree(opt_value); |
| } |
| else if (pg_strcasecmp(opt_name, "custom_variable_classes") == 0) |
| { |
| /* |
| * This variable must be processed first as it controls |
| * the validity of other variables; so apply immediately. |
| * This may fail; plow through, all failures get logged properly. |
| */ |
| set_config_option(opt_name, opt_value, context, |
| PGC_S_FILE, false, true); |
| pfree(opt_name); |
| pfree(opt_value); |
| } |
| else |
| { |
| /* append to list */ |
| struct name_value_pair *item; |
| |
| item = palloc(sizeof *item); |
| item->name = opt_name; |
| item->value = opt_value; |
| item->next = NULL; |
| if (*head_p == NULL) |
| *head_p = item; |
| else |
| (*tail_p)->next = item; |
| *tail_p = item; |
| } |
| |
| /* break out of loop if read EOF, else loop for next line */ |
| if (token == 0) |
| break; |
| } |
| |
| /* successful completion of parsing */ |
| goto cleanup_exit; |
| |
| parse_error: |
| if (token == GUC_EOL || token == 0) |
| ereport(elevel, |
| (errcode(ERRCODE_SYNTAX_ERROR), |
| errmsg("syntax error in file \"%s\" line %u, near end of line", |
| config_file, ConfigFileLineno - 1))); |
| else |
| ereport(elevel, |
| (errcode(ERRCODE_SYNTAX_ERROR), |
| errmsg("syntax error in file \"%s\" line %u, near token \"%s\"", |
| config_file, ConfigFileLineno, yytext))); |
| OK = false; |
| |
| cleanup_exit: |
| yy_delete_buffer(lex_buffer); |
| FreeFile(fp); |
| |
| Assert(OK || (context == PGC_POSTMASTER && IsUnderPostmaster) || context == PGC_SIGHUP); |
| |
| return OK; |
| } |
| |
| |
| /* |
| * Free a list of name/value pairs, including the names and the values |
| */ |
| static void |
| free_name_value_list(struct name_value_pair *list) |
| { |
| struct name_value_pair *item; |
| |
| item = list; |
| while (item) |
| { |
| struct name_value_pair *next = item->next; |
| |
| pfree(item->name); |
| pfree(item->value); |
| pfree(item); |
| item = next; |
| } |
| } |
| |
| |
| /* |
| * scanstr |
| * |
| * Strip the quotes surrounding the given string, and collapse any embedded |
| * '' sequences and backslash escapes. |
| * |
| * the string returned is palloc'd and should eventually be pfree'd by the |
| * caller. |
| */ |
| static char * |
| GUC_scanstr(const char *s) |
| { |
| char *newStr; |
| int len, |
| i, |
| j; |
| |
| Assert(s != NULL && s[0] == '\''); |
| len = strlen(s); |
| Assert(len >= 2); |
| Assert(s[len-1] == '\''); |
| |
| /* Skip the leading quote; we'll handle the trailing quote below */ |
| s++, len--; |
| |
| /* Since len still includes trailing quote, this is enough space */ |
| newStr = palloc(len); |
| |
| for (i = 0, j = 0; i < len; i++) |
| { |
| if (s[i] == '\\') |
| { |
| i++; |
| switch (s[i]) |
| { |
| case 'b': |
| newStr[j] = '\b'; |
| break; |
| case 'f': |
| newStr[j] = '\f'; |
| break; |
| case 'n': |
| newStr[j] = '\n'; |
| break; |
| case 'r': |
| newStr[j] = '\r'; |
| break; |
| case 't': |
| newStr[j] = '\t'; |
| break; |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| { |
| int k; |
| long octVal = 0; |
| |
| for (k = 0; |
| s[i + k] >= '0' && s[i + k] <= '7' && k < 3; |
| k++) |
| octVal = (octVal << 3) + (s[i + k] - '0'); |
| i += k - 1; |
| newStr[j] = ((char) octVal); |
| } |
| break; |
| default: |
| newStr[j] = s[i]; |
| break; |
| } /* switch */ |
| } |
| else if (s[i] == '\'' && s[i+1] == '\'') |
| { |
| /* doubled quote becomes just one quote */ |
| newStr[j] = s[++i]; |
| } |
| else |
| newStr[j] = s[i]; |
| j++; |
| } |
| |
| /* We copied the ending quote to newStr, so replace with \0 */ |
| Assert(j > 0 && j <= len); |
| newStr[--j] = '\0'; |
| |
| return newStr; |
| } |