/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
__attribute__((format_arg(1)));
-static void set_errdata_field(char **ptr, const char *str);
+static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
/* Global variables */
ErrorContextCallback *error_context_stack = NULL;
/* 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
+ */
+ edata->assoc_context = ErrorContext;
+
recursion_depth--;
return true;
}
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->domain, message, false, true);
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->domain, message, false, false);
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE_PLURAL(edata->domain, message, false);
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->domain, detail, false, true);
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->domain, detail, false, false);
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->domain, detail_log, false, true);
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE_PLURAL(edata->domain, detail, false);
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->domain, hint, false, true);
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->context_domain, context, true, true);
}
if (query)
- edata->internalquery = MemoryContextStrdup(ErrorContext, query);
+ edata->internalquery = MemoryContextStrdup(edata->assoc_context, query);
return 0; /* return value does not matter */
}
switch (field)
{
case PG_DIAG_SCHEMA_NAME:
- set_errdata_field(&edata->schema_name, str);
+ set_errdata_field(edata->assoc_context, &edata->schema_name, str);
break;
case PG_DIAG_TABLE_NAME:
- set_errdata_field(&edata->table_name, str);
+ set_errdata_field(edata->assoc_context, &edata->table_name, str);
break;
case PG_DIAG_COLUMN_NAME:
- set_errdata_field(&edata->column_name, str);
+ set_errdata_field(edata->assoc_context, &edata->column_name, str);
break;
case PG_DIAG_DATATYPE_NAME:
- set_errdata_field(&edata->datatype_name, str);
+ set_errdata_field(edata->assoc_context, &edata->datatype_name, str);
break;
case PG_DIAG_CONSTRAINT_NAME:
- set_errdata_field(&edata->constraint_name, str);
+ set_errdata_field(edata->assoc_context, &edata->constraint_name, str);
break;
default:
elog(ERROR, "unsupported ErrorData field id: %d", field);
* set_errdata_field --- set an ErrorData string field
*/
static void
-set_errdata_field(char **ptr, const char *str)
+set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str)
{
Assert(*ptr == NULL);
- *ptr = MemoryContextStrdup(ErrorContext, str);
+ *ptr = MemoryContextStrdup(cxt, str);
}
/*
edata->funcname = funcname;
/* errno is saved now so that error parameter eval can't change it */
edata->saved_errno = errno;
+
+ /* Use ErrorContext for any allocations done at this level. */
+ edata->assoc_context = ErrorContext;
}
/*
* Format error message just like errmsg_internal().
*/
recursion_depth++;
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
EVALUATE_MESSAGE(edata->domain, message, false, false);
recursion_depth++;
CHECK_STACK_DEPTH();
- oldcontext = MemoryContextSwitchTo(ErrorContext);
+ oldcontext = MemoryContextSwitchTo(edata->assoc_context);
/*
* Call hook before sending message to log. The hook function is allowed
if (newedata->internalquery)
newedata->internalquery = pstrdup(newedata->internalquery);
+ /* Use the calling context for string allocation */
+ newedata->assoc_context = CurrentMemoryContext;
+
return newedata;
}
if (newedata->internalquery)
newedata->internalquery = pstrdup(newedata->internalquery);
+ /* Reset the assoc_context to be ErrorContext */
+ newedata->assoc_context = ErrorContext;
+
recursion_depth--;
PG_RE_THROW();
}
* GetErrorContextStack - Return the context stack, for display/diags
*
* Returns a pstrdup'd string in the caller's context which includes the PG
- * call stack. It is the caller's responsibility to ensure this string is
- * pfree'd (or its context cleaned up) when done.
- *
- * Note that this function may *not* be called from any existing error case
- * and is not for error-reporting (use ereport() and friends instead, which
- * will also produce a stack trace).
+ * error call stack. It is the caller's responsibility to ensure this string
+ * is pfree'd (or its context cleaned up) when done.
*
* This information is collected by traversing the error contexts and calling
* each context's callback function, each of which is expected to call
char *
GetErrorContextStack(void)
{
- char *result = NULL;
ErrorData *edata;
ErrorContextCallback *econtext;
- MemoryContext oldcontext = CurrentMemoryContext;
-
- /* this function should not be called from an exception handler */
- Assert(recursion_depth == 0);
/*
- * This function should never be called from an exception handler and
- * therefore will only ever be the top item on the errordata stack
- * (which is set up so that the calls to the callback functions are
- * able to use it).
- *
- * Better safe than sorry, so double-check that we are not being called
- * from an exception handler.
+ * Okay, crank up a stack entry to store the info in.
*/
- if (errordata_stack_depth != -1)
+ 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("GetErrorContextStack called from exception handler")));
+ ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
}
/*
- * Initialize data for the top, and only at this point, error frame as the
- * callback functions we're about to call will turn around and call
- * errcontext(), which expects to find a valid errordata stack.
+ * Things look good so far, so initialize our error frame
*/
- errordata_stack_depth = 0;
edata = &errordata[errordata_stack_depth];
MemSet(edata, 0, sizeof(ErrorData));
/*
- * Use ErrorContext as a short lived context for calling the callbacks;
- * the callbacks will use it through errcontext() even if we don't call
- * them with it, so we have to clean it up below either way.
+ * Set up assoc_context to be the caller's context, so any allocations
+ * done (which will include edata->context) will use their context.
*/
- MemoryContextSwitchTo(ErrorContext);
+ edata->assoc_context = CurrentMemoryContext;
/*
* Call any context callback functions to collect the context information
* into edata->context.
*
* Errors occurring in callback functions should go through the regular
- * error handling code which should handle any recursive errors and must
- * never call back to us.
+ * error handling code which should handle any recursive errors, though
+ * we double-check above, just in case.
*/
for (econtext = error_context_stack;
econtext != NULL;
econtext = econtext->previous)
(*econtext->callback) (econtext->arg);
- MemoryContextSwitchTo(oldcontext);
-
/*
- * Copy out the string into the caller's context, so we can free our
- * error context and reset the error stack. Caller is expected to
- * pfree() the result or throw away the context.
+ * Clean ourselves off the stack, any allocations done should have been
+ * using edata->assoc_context, which we set up earlier to be the caller's
+ * context, so we're free to just remove our entry off the stack and
+ * decrement recursion depth and exit.
*/
- if (edata->context)
- result = pstrdup(edata->context);
+ errordata_stack_depth--;
+ recursion_depth--;
/*
- * Reset error stack- note that we should be the only item on the error
- * stack at this point and therefore it's safe to clean up the whole stack.
- * This function is not intended nor able to be called from exception
- * handlers.
+ * Return a pointer to the string the caller asked for, which should have
+ * been allocated in their context.
*/
- FlushErrorState();
-
- return result;
+ return edata->context;
}
drop function outer_outer_func(int);
drop function outer_func(int);
drop function inner_func(int);
+-- access to call stack from exception
+create function inner_func(int)
+returns int as $$
+declare
+ _context text;
+ sx int := 5;
+begin
+ begin
+ perform sx / 0;
+ exception
+ when division_by_zero then
+ get diagnostics _context = pg_context;
+ raise notice '***%***', _context;
+ end;
+
+ -- lets do it again, just for fun..
+ get diagnostics _context = pg_context;
+ raise notice '***%***', _context;
+ raise notice 'lets make sure we didnt break anything';
+ return 2 * $1;
+end;
+$$ language plpgsql;
+create or replace function outer_func(int)
+returns int as $$
+declare
+ myresult int;
+begin
+ raise notice 'calling down into inner_func()';
+ myresult := inner_func($1);
+ raise notice 'inner_func() done';
+ return myresult;
+end;
+$$ language plpgsql;
+create or replace function outer_outer_func(int)
+returns int as $$
+declare
+ myresult int;
+begin
+ raise notice 'calling down into outer_func()';
+ myresult := outer_func($1);
+ raise notice 'outer_func() done';
+ return myresult;
+end;
+$$ language plpgsql;
+select outer_outer_func(10);
+NOTICE: calling down into outer_func()
+NOTICE: calling down into inner_func()
+CONTEXT: PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: ***PL/pgSQL function inner_func(integer) line 10 at GET DIAGNOSTICS
+PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
+CONTEXT: PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: ***PL/pgSQL function inner_func(integer) line 15 at GET DIAGNOSTICS
+PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
+CONTEXT: PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: lets make sure we didnt break anything
+CONTEXT: PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: inner_func() done
+CONTEXT: PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: outer_func() done
+ outer_outer_func
+------------------
+ 20
+(1 row)
+
+-- repeated call should to work
+select outer_outer_func(20);
+NOTICE: calling down into outer_func()
+NOTICE: calling down into inner_func()
+CONTEXT: PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: ***PL/pgSQL function inner_func(integer) line 10 at GET DIAGNOSTICS
+PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
+CONTEXT: PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: ***PL/pgSQL function inner_func(integer) line 15 at GET DIAGNOSTICS
+PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
+CONTEXT: PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: lets make sure we didnt break anything
+CONTEXT: PL/pgSQL function outer_func(integer) line 6 at assignment
+PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: inner_func() done
+CONTEXT: PL/pgSQL function outer_outer_func(integer) line 6 at assignment
+NOTICE: outer_func() done
+ outer_outer_func
+------------------
+ 40
+(1 row)
+
+drop function outer_outer_func(int);
+drop function outer_func(int);
+drop function inner_func(int);