pg_parser_continue_on_error_v4.patch
application/octet-stream
Filename: pg_parser_continue_on_error_v4.patch
Type: application/octet-stream
Part: 0
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 5297,5303 **** 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)
{
--- 5297,5303 ----
* 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,134 **** 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;
/*
--- 114,145 ----
char *cvc = NULL;
struct config_string *cvc_struct;
int i;
+ int errorcount;
+ int warningcount = 0;
! /*
! * 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);
! /* bail out early if errors were found during the parse stage */
! if (errorcount > 0)
goto cleanup_list;
/*
***************
*** 178,227 **** ProcessConfigFile(GucContext context)
gconf->status &= ~GUC_IS_IN_FILE;
}
! /*
! * Check if all options are valid. As a side-effect, the GUC_IS_IN_FILE
! * flag is set on each GUC variable mentioned in the list.
! */
for (item = head; item; item = item->next)
{
! char *sep = strchr(item->name, GUC_QUALIFIER_SEPARATOR);
!
if (sep)
{
! /*
! * We have to consider three cases for custom variables:
! *
! * 1. The class name is not valid according to the (new) setting
! * of custom_variable_classes. If so, reject. We don't care
! * which side is at fault.
! */
if (!is_custom_class(item->name, sep - item->name, cvc))
{
ereport(elevel,
(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
! * it would make a placeholder, which we don't want to do yet,
! * since we could still fail further down the list. Do nothing
! * (assuming that making the placeholder will succeed later).
! */
! if (find_option(item->name, false, elevel) == NULL)
continue;
! /*
! * 3. There is already a GUC entry (either real or placeholder) for
! * the variable. In this case we should let set_config_option
! * check it, since the assignment could well fail if it's a real
! * entry.
! */
}
! if (!set_config_option(item->name, item->value, context,
! PGC_S_FILE, GUC_ACTION_SET, false))
! goto cleanup_list;
}
/*
--- 189,252 ----
gconf->status &= ~GUC_IS_IN_FILE;
}
! /* Apply new values, skip lines with invalid variable names or values */
for (item = head; item; item = item->next)
{
! char *sep;
! char *pre_value = NULL;
!
! sep = strchr(item->name, GUC_QUALIFIER_SEPARATOR);
if (sep)
{
!
! /* Check that the relevant custom variable class exists. */
if (!is_custom_class(item->name, sep - item->name, cvc))
{
ereport(elevel,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized configuration parameter \"%s\"",
item->name)));
! warningcount++;
continue;
! }
}
! /* In SIGHUP cases in the postmaster, report changes */
! if (context == PGC_SIGHUP && !IsUnderPostmaster)
! {
! const char *preval = GetConfigOption(item->name, true, false);
!
! /* If option doesn't exist yet or is NULL, treat as empty string */
! if (!preval)
! preval = "";
! /* must dup, else might have dangling pointer below */
! pre_value = pstrdup(preval);
! }
!
! if (set_config_option(item->name, item->value, context,
! PGC_S_FILE, GUC_ACTION_SET, true))
! {
! set_config_sourcefile(item->name, item->filename,
! item->sourceline);
!
! if (pre_value)
! {
! const char *post_value = GetConfigOption(item->name, true, false);
!
! if (!post_value)
! post_value = "";
! if (strcmp(pre_value, post_value) != 0)
! ereport(elevel,
! (errmsg("parameter \"%s\" changed to \"%s\"",
! item->name, item->value)));
! }
! }
! else
! warningcount++;
!
! if (pre_value)
! pfree(pre_value);
!
}
/*
***************
*** 284,290 **** ProcessConfigFile(GucContext context)
* particular should be run only after initialization is complete.
*
* XXX this is an unmaintainable crock, because we have to know how
! * to set (or at least what to call to set) every variable that could
* potentially have PGC_S_DYNAMIC_DEFAULT or PGC_S_ENV_VAR source.
* However, there's no time to redesign it for 9.1.
*/
--- 309,315 ----
* particular should be run only after initialization is complete.
*
* XXX this is an unmaintainable crock, because we have to know how
! * to set (at least what to call to set) every variable that could
* potentially have PGC_S_DYNAMIC_DEFAULT or PGC_S_ENV_VAR source.
* However, there's no time to redesign it for 9.1.
*/
***************
*** 298,343 **** ProcessConfigFile(GucContext context)
PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
}
- /* If we got here all the options checked out okay, so apply them. */
- for (item = head; item; item = item->next)
- {
- char *pre_value = NULL;
-
- /* In SIGHUP cases in the postmaster, report changes */
- if (context == PGC_SIGHUP && !IsUnderPostmaster)
- {
- const char *preval = GetConfigOption(item->name, true, false);
-
- /* If option doesn't exist yet or is NULL, treat as empty string */
- if (!preval)
- preval = "";
- /* must dup, else might have dangling pointer below */
- pre_value = pstrdup(preval);
- }
-
- if (set_config_option(item->name, item->value, context,
- PGC_S_FILE, GUC_ACTION_SET, true))
- {
- set_config_sourcefile(item->name, item->filename,
- item->sourceline);
-
- if (pre_value)
- {
- const char *post_value = GetConfigOption(item->name, true, false);
-
- if (!post_value)
- post_value = "";
- if (strcmp(pre_value, post_value) != 0)
- ereport(elevel,
- (errmsg("parameter \"%s\" changed to \"%s\"",
- item->name, item->value)));
- }
- }
-
- if (pre_value)
- pfree(pre_value);
- }
-
/* Remember when we last successfully loaded the config file. */
PgReloadTime = GetCurrentTimestamp();
--- 323,328 ----
***************
*** 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];
--- 330,361 ----
FreeConfigVariables(head);
if (cvc)
free(cvc);
+
+
+ if (errorcount > 0 || warningcount > 0)
+ {
+ elevel = (errorcount > 0 && !IsUnderPostmaster) ? ERROR : WARNING;
+ /* If we got errors during postmaster start, complain and bail out */
+ ereport(elevel,
+ (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;
}
/*
--- 370,376 ----
(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;
}
/*
--- 405,418 ----
(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,
--- 424,430 ----
* 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
--- 432,461 ----
* 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,
--- 464,470 ----
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);
--- 516,523 ----
*/
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;
}
--- 576,618 ----
/* 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
***************
*** 5064,5070 **** 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
--- 5064,5070 ----
*
* 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
***************
*** 5083,5089 **** 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
--- 5083,5089 ----
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);