pg_parser_continue_on_error_v3.patch
application/octet-stream
Filename: pg_parser_continue_on_error_v3.patch
Type: application/octet-stream
Part: 0
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 5250,5256 **** readRecoveryCommandFile(void)
* Since we're asking ParseConfigFp() to error out at FATAL, there's no
* need to check the return value.
*/
! ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail);
for (item = head; item; item = item->next)
{
--- 5250,5256 ----
* Since we're asking ParseConfigFp() to error out at FATAL, there's no
* need to check the return value.
*/
! ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, 0, &head, &tail);
for (item = head; item; item = item->next)
{
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 477,483 **** parse_extension_control_file(ExtensionControlFile *control,
/*
* Parse the file content, using GUC's file parsing code
*/
! ParseConfigFp(file, filename, 0, ERROR, &head, &tail);
FreeFile(file);
--- 477,483 ----
/*
* Parse the file content, using GUC's file parsing code
*/
! ParseConfigFp(file, filename, 0, ERROR, 0, &head, &tail);
FreeFile(file);
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 43,48 **** int GUC_yylex(void);
--- 43,51 ----
static char *GUC_scanstr(const char *s);
+ /* Maximum number of errors to report */
+ #define MAX_ERROR_REPORTS 100
+
%}
%option 8bit
***************
*** 111,135 **** ProcessConfigFile(GucContext context)
char *cvc = NULL;
struct config_string *cvc_struct;
int i;
! 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 the file into a list of option names and values */
head = tail = NULL;
! if (!ParseConfigFile(ConfigFileName, NULL, 0, elevel, &head, &tail))
! goto cleanup_list;
/*
* We need the proposed new value of custom_variable_classes to check
--- 114,142 ----
char *cvc = NULL;
struct config_string *cvc_struct;
int i;
+ int errorcount;
! /*
! * Config files are only processes on startup (by the postmaster)
! * and on SIGHUP (by the postmaster and backends)
! */
! Assert((context == PGC_POSTMASTER && !IsUnderPostmaster) ||
! context == PGC_SIGHUP);
! /*
! * To avoid cluttering the log, only the postmaster bleats loudly
! * about problems with the config file.
! */
! elevel = IsUnderPostmaster ? DEBUG2 : LOG;
! /*
! * Parse the file into a list of option names and values. We continue even
! * if we hit a parse error, because we also want to report invalid values in
! * the parts we could parse.
! */
head = tail = NULL;
! errorcount = ParseConfigFile(ConfigFileName, NULL, 0, elevel, 0, &head, &tail);
/*
* We need the proposed new value of custom_variable_classes to check
***************
*** 201,207 **** ProcessConfigFile(GucContext context)
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized configuration parameter \"%s\"",
item->name)));
! goto cleanup_list;
}
/*
* 2. There is no GUC entry. If we called set_config_option then
--- 208,214 ----
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized configuration parameter \"%s\"",
item->name)));
! errorcount++;
}
/*
* 2. There is no GUC entry. If we called set_config_option then
***************
*** 221,228 **** ProcessConfigFile(GucContext context)
if (!set_config_option(item->name, item->value, context,
PGC_S_FILE, GUC_ACTION_SET, false))
! goto cleanup_list;
}
/*
* Check for variables having been removed from the config file, and
--- 228,247 ----
if (!set_config_option(item->name, item->value, context,
PGC_S_FILE, GUC_ACTION_SET, false))
! errorcount++;
!
! if (errorcount >= MAX_ERROR_REPORTS)
! {
! ereport(elevel,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("too many errors found, stopped processing file \"%s\"",
! ConfigFileName)));
! break;
! }
}
+ /* Don't change configuration options if errors were detected earlier */
+ if (errorcount > 0)
+ goto cleanup_list;
/*
* Check for variables having been removed from the config file, and
***************
*** 345,363 **** ProcessConfigFile(GucContext context)
FreeConfigVariables(head);
if (cvc)
free(cvc);
}
/*
! * See next function for details. This one will just work with a config_file
* name rather than an already opened File Descriptor
*/
! bool
ParseConfigFile(const char *config_file, const char *calling_file,
! int depth, int elevel,
ConfigVariable **head_p,
ConfigVariable **tail_p)
{
! bool OK = true;
FILE *fp;
char abs_path[MAXPGPATH];
--- 364,390 ----
FreeConfigVariables(head);
if (cvc)
free(cvc);
+
+ /* If we got errors during postmaster start, complain and bail out */
+ if (errorcount > 0 && !IsUnderPostmaster)
+ ereport(ERROR,
+ (errmsg("errors detected while parsing configuration files")));
}
/*
! * See ParseConfigFp for details. This one will just work with a config_file
* name rather than an already opened File Descriptor
+ *
+ * The return convention is the same as ParseConfigFp; additionally, file not
+ * found or file not accessible conditions are considered a single error.
*/
! int
ParseConfigFile(const char *config_file, const char *calling_file,
! int depth, int elevel, int nerrors,
ConfigVariable **head_p,
ConfigVariable **tail_p)
{
! int errorcount = 0;
FILE *fp;
char abs_path[MAXPGPATH];
***************
*** 372,378 **** ParseConfigFile(const char *config_file, const char *calling_file,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
config_file)));
! return false;
}
/*
--- 399,405 ----
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
config_file)));
! return 1;
}
/*
***************
*** 407,420 **** ParseConfigFile(const char *config_file, const char *calling_file,
(errcode_for_file_access(),
errmsg("could not open configuration file \"%s\": %m",
config_file)));
! return false;
}
! OK = ParseConfigFp(fp, config_file, depth, elevel, head_p, tail_p);
FreeFile(fp);
! return OK;
}
/*
--- 434,447 ----
(errcode_for_file_access(),
errmsg("could not open configuration file \"%s\": %m",
config_file)));
! return 1;
}
! errorcount = ParseConfigFp(fp, config_file, depth, elevel, nerrors, head_p, tail_p);
FreeFile(fp);
! return errorcount;
}
/*
***************
*** 426,431 **** ParseConfigFile(const char *config_file, const char *calling_file,
--- 453,459 ----
* config_file: absolute or relative path of file to read
* depth: recursion depth (used only to prevent infinite recursion)
* elevel: error logging level determined by ProcessConfigFile()
+ * nerrors: number of errors already seen while parsing previous config files
* Output parameters:
* head_p, tail_p: head and tail of linked list of name/value pairs
*
***************
*** 433,455 **** ParseConfigFile(const char *config_file, const char *calling_file,
* 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.
*/
! bool
ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
! ConfigVariable **head_p, ConfigVariable **tail_p)
{
- bool OK = true;
YY_BUFFER_STATE lex_buffer;
! int token;
/*
* Parse
--- 461,490 ----
* recursion level. On exit, they contain a list of name-value pairs read
* from the input file(s).
*
! * Returns 0 if successful, or the number of parsing errors found if any
! * occurred. The error(s) have already been ereport'd, it is only necessary
! * for the caller to clean up its own state and release the name/value pairs
! * list.
! *
! * If elevel < ERROR then we don't return immediately on the first error,
! * although we do return after coming across 100 of them. This is to prevent
! * trashing out logs when parsing a completely bogus file.
*
* 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.
+ *
+ * We use nerrors to break out early in case too many errors are found.
*/
! int
ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
! int nerrors, ConfigVariable **head_p, ConfigVariable **tail_p)
{
YY_BUFFER_STATE lex_buffer;
! int token,
! errorcount;
/*
* Parse
***************
*** 458,463 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
--- 493,499 ----
yy_switch_to_buffer(lex_buffer);
ConfigFileLineno = 1;
+ errorcount = 0;
/* This loop iterates once per logical line */
while ((token = yylex()))
***************
*** 509,523 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
*/
unsigned int save_ConfigFileLineno = ConfigFileLineno;
! if (!ParseConfigFile(opt_value, config_file,
! depth + 1, 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);
--- 545,552 ----
*/
unsigned int save_ConfigFileLineno = ConfigFileLineno;
! errorcount += ParseConfigFile(opt_value, config_file, depth + 1,
! elevel, errorcount, head_p, tail_p);
yy_switch_to_buffer(lex_buffer);
ConfigFileLineno = save_ConfigFileLineno;
pfree(opt_name);
***************
*** 576,602 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
/* 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);
! return OK;
}
--- 605,647 ----
/* break out of loop if read EOF, else loop for next line */
if (token == 0)
break;
+ /* skip over parse_error if we made it this far without errors */
+ continue;
+
+ 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)));
+ errorcount++;
+ /* fast forward till the next EOL/EOF */
+ while (token != 0 && token != GUC_EOL)
+ token = yylex();
+
+ /* break out of loop on EOF */
+ if (token == 0)
+ break;
+
+ /* avoid producing too much noise when parsing a bogus file */
+ if (errorcount + nerrors >= MAX_ERROR_REPORTS)
+ {
+ ereport(elevel,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("too many errors found, stopped processing file \"%s\"",
+ config_file)));
+ break;
+ }
}
/* successful completion of parsing */
yy_delete_buffer(lex_buffer);
! return errorcount;
}
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 5047,5053 **** config_enum_get_options(struct config_enum * record, const char *prefix,
*
* If there is an error (non-existing option, invalid value) then an
* ereport(ERROR) is thrown *unless* this is called in a context where we
! * don't want to ereport (currently, startup or SIGHUP config file reread).
* In that case we write a suitable error message via ereport(LOG) and
* return false. This is working around the deficiencies in the ereport
* mechanism, so don't blame me. In all other cases, the function
--- 5047,5053 ----
*
* If there is an error (non-existing option, invalid value) then an
* ereport(ERROR) is thrown *unless* this is called in a context where we
! * don't want to ereport (currently, settings defaults and config file read).
* In that case we write a suitable error message via ereport(LOG) and
* return false. This is working around the deficiencies in the ereport
* mechanism, so don't blame me. In all other cases, the function
***************
*** 5066,5072 **** set_config_option(const char *name, const char *value,
bool prohibitValueChange = false;
bool makeDefault;
! if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
{
/*
* To avoid cluttering the log, only the postmaster bleats loudly
--- 5066,5072 ----
bool prohibitValueChange = false;
bool makeDefault;
! if (source == PGC_S_FILE || source == PGC_S_DEFAULT)
{
/*
* To avoid cluttering the log, only the postmaster bleats loudly
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 110,120 **** typedef struct ConfigVariable
struct ConfigVariable *next;
} ConfigVariable;
! extern bool ParseConfigFile(const char *config_file, const char *calling_file,
! int depth, int elevel,
ConfigVariable **head_p, ConfigVariable **tail_p);
! extern bool ParseConfigFp(FILE *fp, const char *config_file,
! int depth, int elevel,
ConfigVariable **head_p, ConfigVariable **tail_p);
extern void FreeConfigVariables(ConfigVariable *list);
--- 110,120 ----
struct ConfigVariable *next;
} ConfigVariable;
! extern int ParseConfigFile(const char *config_file, const char *calling_file,
! int depth, int elevel, int nerrors,
ConfigVariable **head_p, ConfigVariable **tail_p);
! extern int ParseConfigFp(FILE *fp, const char *config_file,
! int depth, int elevel, int nerrors,
ConfigVariable **head_p, ConfigVariable **tail_p);
extern void FreeConfigVariables(ConfigVariable *list);