📄 guc-file.l
字号:
* 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 boolParseConfigFile(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))); 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); strlcpy(abs_path, calling_file, sizeof(abs_path)); 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))); 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; struct name_value_pair *item; 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 (guc_name_compare(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)) { 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 (guc_name_compare(opt_name, "custom_variable_classes") == 0) { /* * This variable must be processed first as it controls * the validity of other variables; so it goes at the head * of the result list. If we already found a value for it, * replace with this one. */ item = *head_p; if (item != NULL && guc_name_compare(item->name, "custom_variable_classes") == 0) { /* replace existing head item */ pfree(item->name); pfree(item->value); item->name = opt_name; item->value = opt_value; } else { /* prepend to list */ item = palloc(sizeof *item); item->name = opt_name; item->value = opt_value; item->next = *head_p; *head_p = item; if (*tail_p == NULL) *tail_p = item; } } else { /* ordinary variable, append to list */ 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); return OK;}/* * Free a list of name/value pairs, including the names and the values */static voidfree_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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -