While testing the configuration include directory patch, I decided to try the regular postgresql.conf "include" on a directory:
include 'base' Reloading the configuration then rather rudely kills the postmaster: FATAL: input in flex scanner failed Setting aside whether we should offer a better diagnostic when a user points "include" at a directory, the yy_fatal_error hack that made sense for scan.l doesn't cut it for guc-file.l. Like other guc-file.l errors, we need to choose the elevel based on which process is running the code. ERROR is never the right choice. We should instead stash the message, longjmp out of the flex parser, and proceed to handle the error mostly like a regular syntax error. See attached small patch. It seems plainly hackish to mutate the body of yy_fatal_error() by #define'ing fprintf(). Why didn't we instead #define YY_FATAL_ERROR and provide our own complete replacement? (Both strategies are undocumented.) I haven't changed that decision in this patch, though. On a related note, the out-of-memory handling during config file reload is inconsistent. guc-file.l uses palloc/pstrdup in various places. An OOM at those sites during a reload would kill the postmaster. guc.c has functions like guc_malloc that handle variable elevels. However, various check_* and assign_* functions, such as check_log_destination(), use pstrdup() and guc_malloc(ERROR). Failing there during a reload would also kill the postmaster. Is it worth doing better here? Incidentally, the wal writer process no longer exits promptly on postmaster death. I didn't look into that further. Thanks, nm
*** a/src/backend/utils/misc/guc-file.l --- b/src/backend/utils/misc/guc-file.l *************** *** 20,28 **** #include "utils/guc.h" ! /* 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, --- 20,41 ---- #include "utils/guc.h" ! /* ! * flex emits a yy_fatal_error() function that it calls in response to ! * critical errors like malloc failure, file I/O errors, and detection of ! * internal inconsistency. That function prints a message and calls exit(). ! * Mutate it to instead stash the message and jump out of the parser; see ! * GUC_safe_yylex() for the other half of the story. Assume all msg arguments ! * point to string constants; this holds for flex 2.5.31 (earliest we support) ! * and flex 2.5.35 (latest as of this writing). ! */ ! static const char *GUC_flex_fatal_errmsg; ! static sigjmp_buf GUC_flex_fatal_jmp; #undef fprintf ! #define fprintf(file, fmt, msg) \ ! 0; /* eat cast to void */ \ ! GUC_flex_fatal_errmsg = msg; \ ! siglongjmp(GUC_flex_fatal_jmp, 1) enum { GUC_ID = 1, *************** *** 32,39 **** enum { GUC_EQUALS = 5, GUC_UNQUOTED_STRING = 6, GUC_QUALIFIED_ID = 7, ! GUC_EOL = 99, ! GUC_ERROR = 100 }; static unsigned int ConfigFileLineno; --- 45,53 ---- GUC_EQUALS = 5, GUC_UNQUOTED_STRING = 6, GUC_QUALIFIED_ID = 7, ! GUC_EOL = 98, ! GUC_ERROR = 99, ! GUC_INTERNAL_ERROR = 100 }; static unsigned int ConfigFileLineno; *************** *** 437,442 **** ParseConfigFile(const char *config_file, const char *calling_file, bool strict, --- 451,469 ---- } /* + * Wrap the generated lexer so we can regain control after a fatal, internal + * flex error. On return of GUC_INTERNAL_ERROR, GUC_flex_fatal_errmsg will + * contain a pointer into static storage containing the error message. + */ + static int + GUC_safe_yylex(void) + { + if (sigsetjmp(GUC_flex_fatal_jmp, 1) != 0) + return GUC_INTERNAL_ERROR; + return GUC_yylex(); + } + + /* * Read and parse a single configuration file. This function recurses * to handle "include" directives. * *************** *** 478,484 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel, errorcount = 0; /* This loop iterates once per logical line */ ! while ((token = yylex())) { char *opt_name = NULL; char *opt_value = NULL; --- 505,511 ---- errorcount = 0; /* This loop iterates once per logical line */ ! while ((token = GUC_safe_yylex())) { char *opt_name = NULL; char *opt_value = NULL; *************** *** 493,501 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel, 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 && --- 520,528 ---- opt_name = pstrdup(yytext); /* next we have an optional equal sign; discard if present */ ! token = GUC_safe_yylex(); if (token == GUC_EQUALS) ! token = GUC_safe_yylex(); /* now we must have the option value */ if (token != GUC_ID && *************** *** 510,516 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel, opt_value = pstrdup(yytext); /* now we'd like an end of line, or possibly EOF */ ! token = yylex(); if (token != GUC_EOL) { if (token != 0) --- 537,543 ---- opt_value = pstrdup(yytext); /* now we'd like an end of line, or possibly EOF */ ! token = GUC_safe_yylex(); if (token != GUC_EOL) { if (token != 0) *************** *** 583,589 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel, pfree(opt_value); /* report the 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", --- 610,619 ---- pfree(opt_value); /* report the error */ ! if (token == GUC_INTERNAL_ERROR) ! elog(elevel, "%s at file \"%s\" line %u", ! GUC_flex_fatal_errmsg, config_file, ConfigFileLineno); ! else if (token == GUC_EOL || token == 0) ereport(elevel, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error in file \"%s\" line %u, near end of line", *************** *** 612,622 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel, break; } ! /* resync to next end-of-line or EOF */ ! while (token != GUC_EOL && token != 0) ! token = yylex(); ! /* break out of loop on EOF */ ! if (token == 0) break; } --- 642,655 ---- break; } ! /* ! * Resync to next end-of-line or EOF. Abandon the file after a ! * flex-internal error, which may corrupt parser state. Hopefully, ! * it's not so corrupt that yy_delete_buffer() will explode... ! */ ! while (token != GUC_EOL && token != 0 && token != GUC_INTERNAL_ERROR) ! token = GUC_safe_yylex(); ! if (token != GUC_EOL) break; }
-- Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-bugs