Minor code refactoring in elog.c (no functional change).

Combine some duplicated code stanzas by creating small functions.
Most of these duplications arose at a time when I wouldn't have
trusted C compilers to auto-inline small functions intelligently,
but they're probably poor practice now.  Similarly split out some
bits that aren't actually duplicative as the code stands, but would
become so after an upcoming patch to add another error-handling
code path.

Take the opportunity to add some lengthier comments about what
we're doing here, too.  Re-order one function that seemed not
very well-placed.

Patch by me, per suggestions from Andres Freund.

Discussion: https://postgr.es/m/3bbbb0df-7382-bf87-9737-340ba096e034@postgrespro.ru
This commit is contained in:
Tom Lane 2022-12-07 14:39:25 -05:00
parent 3b31821953
commit 8305629afe

View File

@ -176,8 +176,15 @@ static char formatted_log_time[FORMATTED_TS_LEN];
static const char *err_gettext(const char *str) pg_attribute_format_arg(1);
static ErrorData *get_error_stack_entry(void);
static void set_stack_entry_domain(ErrorData *edata, const char *domain);
static void set_stack_entry_location(ErrorData *edata,
const char *filename, int lineno,
const char *funcname);
static bool matches_backtrace_functions(const char *funcname);
static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
static void FreeErrorDataContents(ErrorData *edata);
static void write_console(const char *line, int len);
static const char *process_log_prefix_padding(const char *p, int *ppadding);
static void log_line_prefix(StringInfo buf, ErrorData *edata);
@ -434,27 +441,13 @@ errstart(int elevel, const char *domain)
debug_query_string = NULL;
}
}
if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
{
/*
* Wups, stack not big enough. We treat this as a PANIC condition
* because it suggests an infinite loop of errors during error
* recovery.
*/
errordata_stack_depth = -1; /* make room on stack */
ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
}
/* Initialize data for this error frame */
edata = &errordata[errordata_stack_depth];
MemSet(edata, 0, sizeof(ErrorData));
edata = get_error_stack_entry();
edata->elevel = elevel;
edata->output_to_server = output_to_server;
edata->output_to_client = output_to_client;
/* the default text domain is the backend's */
edata->domain = domain ? domain : PG_TEXTDOMAIN("postgres");
/* initialize context_domain the same way (see set_errcontext_domain()) */
edata->context_domain = edata->domain;
set_stack_entry_domain(edata, domain);
/* Select default errcode based on elevel */
if (elevel >= ERROR)
edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;
@ -462,8 +455,6 @@ errstart(int elevel, const char *domain)
edata->sqlerrcode = ERRCODE_WARNING;
else
edata->sqlerrcode = ERRCODE_SUCCESSFUL_COMPLETION;
/* errno is saved here so that error parameter eval can't change it */
edata->saved_errno = errno;
/*
* Any allocations for this error state level should go into ErrorContext
@ -474,32 +465,6 @@ errstart(int elevel, const char *domain)
return true;
}
/*
* Checks whether the given funcname matches backtrace_functions; see
* check_backtrace_functions.
*/
static bool
matches_backtrace_functions(const char *funcname)
{
char *p;
if (!backtrace_symbol_list || funcname == NULL || funcname[0] == '\0')
return false;
p = backtrace_symbol_list;
for (;;)
{
if (*p == '\0') /* end of backtrace_symbol_list */
break;
if (strcmp(funcname, p) == 0)
return true;
p += strlen(p) + 1;
}
return false;
}
/*
* errfinish --- end an error-reporting cycle
*
@ -520,23 +485,7 @@ errfinish(const char *filename, int lineno, const char *funcname)
CHECK_STACK_DEPTH();
/* Save the last few bits of error state into the stack entry */
if (filename)
{
const char *slash;
/* keep only base name, useful especially for vpath builds */
slash = strrchr(filename, '/');
if (slash)
filename = slash + 1;
/* Some Windows compilers use backslashes in __FILE__ strings */
slash = strrchr(filename, '\\');
if (slash)
filename = slash + 1;
}
edata->filename = filename;
edata->lineno = lineno;
edata->funcname = funcname;
set_stack_entry_location(edata, filename, lineno, funcname);
elevel = edata->elevel;
@ -546,6 +495,7 @@ errfinish(const char *filename, int lineno, const char *funcname)
*/
oldcontext = MemoryContextSwitchTo(ErrorContext);
/* Collect backtrace, if enabled and we didn't already */
if (!edata->backtrace &&
edata->funcname &&
backtrace_functions &&
@ -596,31 +546,7 @@ errfinish(const char *filename, int lineno, const char *funcname)
EmitErrorReport();
/* Now free up subsidiary data attached to stack entry, and release it */
if (edata->message)
pfree(edata->message);
if (edata->detail)
pfree(edata->detail);
if (edata->detail_log)
pfree(edata->detail_log);
if (edata->hint)
pfree(edata->hint);
if (edata->context)
pfree(edata->context);
if (edata->backtrace)
pfree(edata->backtrace);
if (edata->schema_name)
pfree(edata->schema_name);
if (edata->table_name)
pfree(edata->table_name);
if (edata->column_name)
pfree(edata->column_name);
if (edata->datatype_name)
pfree(edata->datatype_name);
if (edata->constraint_name)
pfree(edata->constraint_name);
if (edata->internalquery)
pfree(edata->internalquery);
FreeErrorDataContents(edata);
errordata_stack_depth--;
/* Exit error-handling context */
@ -685,6 +611,120 @@ errfinish(const char *filename, int lineno, const char *funcname)
CHECK_FOR_INTERRUPTS();
}
/*
* get_error_stack_entry --- allocate and initialize a new stack entry
*
* The entry should be freed, when we're done with it, by calling
* FreeErrorDataContents() and then decrementing errordata_stack_depth.
*
* Returning the entry's address is just a notational convenience,
* since it had better be errordata[errordata_stack_depth].
*
* Although the error stack is not large, we don't expect to run out of space.
* Using more than one entry implies a new error report during error recovery,
* which is possible but already suggests we're in trouble. If we exhaust the
* stack, almost certainly we are in an infinite loop of errors during error
* recovery, so we give up and PANIC.
*
* (Note that this is distinct from the recursion_depth checks, which
* guard against recursion while handling a single stack entry.)
*/
static ErrorData *
get_error_stack_entry(void)
{
ErrorData *edata;
/* Allocate error frame */
errordata_stack_depth++;
if (unlikely(errordata_stack_depth >= ERRORDATA_STACK_SIZE))
{
/* Wups, stack not big enough */
errordata_stack_depth = -1; /* make room on stack */
ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
}
/* Initialize error frame to all zeroes/NULLs */
edata = &errordata[errordata_stack_depth];
memset(edata, 0, sizeof(ErrorData));
/* Save errno immediately to ensure error parameter eval can't change it */
edata->saved_errno = errno;
return edata;
}
/*
* set_stack_entry_domain --- fill in the internationalization domain
*/
static void
set_stack_entry_domain(ErrorData *edata, const char *domain)
{
/* the default text domain is the backend's */
edata->domain = domain ? domain : PG_TEXTDOMAIN("postgres");
/* initialize context_domain the same way (see set_errcontext_domain()) */
edata->context_domain = edata->domain;
}
/*
* set_stack_entry_location --- fill in code-location details
*
* Store the values of __FILE__, __LINE__, and __func__ from the call site.
* We make an effort to normalize __FILE__, since compilers are inconsistent
* about how much of the path they'll include, and we'd prefer that the
* behavior not depend on that (especially, that it not vary with build path).
*/
static void
set_stack_entry_location(ErrorData *edata,
const char *filename, int lineno,
const char *funcname)
{
if (filename)
{
const char *slash;
/* keep only base name, useful especially for vpath builds */
slash = strrchr(filename, '/');
if (slash)
filename = slash + 1;
/* Some Windows compilers use backslashes in __FILE__ strings */
slash = strrchr(filename, '\\');
if (slash)
filename = slash + 1;
}
edata->filename = filename;
edata->lineno = lineno;
edata->funcname = funcname;
}
/*
* matches_backtrace_functions --- checks whether the given funcname matches
* backtrace_functions
*
* See check_backtrace_functions.
*/
static bool
matches_backtrace_functions(const char *funcname)
{
const char *p;
if (!backtrace_symbol_list || funcname == NULL || funcname[0] == '\0')
return false;
p = backtrace_symbol_list;
for (;;)
{
if (*p == '\0') /* end of backtrace_symbol_list */
break;
if (strcmp(funcname, p) == 0)
return true;
p += strlen(p) + 1;
}
return false;
}
/*
* errcode --- add SQLSTATE error code to the current error
@ -1611,6 +1651,18 @@ CopyErrorData(void)
*/
void
FreeErrorData(ErrorData *edata)
{
FreeErrorDataContents(edata);
pfree(edata);
}
/*
* FreeErrorDataContents --- free the subsidiary data of an ErrorData.
*
* This can be used on either an error stack entry or a copied ErrorData.
*/
static void
FreeErrorDataContents(ErrorData *edata)
{
if (edata->message)
pfree(edata->message);
@ -1636,7 +1688,6 @@ FreeErrorData(ErrorData *edata)
pfree(edata->constraint_name);
if (edata->internalquery)
pfree(edata->internalquery);
pfree(edata);
}
/*
@ -1742,18 +1793,7 @@ ReThrowError(ErrorData *edata)
recursion_depth++;
MemoryContextSwitchTo(ErrorContext);
if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
{
/*
* Wups, stack not big enough. We treat this as a PANIC condition
* because it suggests an infinite loop of errors during error
* recovery.
*/
errordata_stack_depth = -1; /* make room on stack */
ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
}
newedata = &errordata[errordata_stack_depth];
newedata = get_error_stack_entry();
memcpy(newedata, edata, sizeof(ErrorData));
/* Make copies of separately-allocated fields */
@ -1854,26 +1894,11 @@ GetErrorContextStack(void)
ErrorContextCallback *econtext;
/*
* Okay, crank up a stack entry to store the info in.
* Crank up a stack entry to store the info in.
*/
recursion_depth++;
if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
{
/*
* Wups, stack not big enough. We treat this as a PANIC condition
* because it suggests an infinite loop of errors during error
* recovery.
*/
errordata_stack_depth = -1; /* make room on stack */
ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
}
/*
* Things look good so far, so initialize our error frame
*/
edata = &errordata[errordata_stack_depth];
MemSet(edata, 0, sizeof(ErrorData));
edata = get_error_stack_entry();
/*
* Set up assoc_context to be the caller's context, so any allocations