mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +03:00
Allow a context to be passed in for error handling
As pointed out by Tom Lane, we can allow other users of the error handler callbacks to provide their own memory context by adding the context to use to ErrorData and using that instead of explicitly using ErrorContext. This then allows GetErrorContextStack() to be called from inside exception handlers, so modify plpgsql to take advantage of that and add an associated regression test for it.
This commit is contained in:
parent
a59516b631
commit
ddef1a39c6
@ -87,7 +87,7 @@ err_gettext(const char *str)
|
|||||||
/* This extension allows gcc to check the format string for consistency with
|
/* This extension allows gcc to check the format string for consistency with
|
||||||
the supplied arguments. */
|
the supplied arguments. */
|
||||||
__attribute__((format_arg(1)));
|
__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 */
|
/* Global variables */
|
||||||
ErrorContextCallback *error_context_stack = NULL;
|
ErrorContextCallback *error_context_stack = NULL;
|
||||||
@ -373,6 +373,11 @@ errstart(int elevel, const char *filename, int lineno,
|
|||||||
/* errno is saved here so that error parameter eval can't change it */
|
/* errno is saved here so that error parameter eval can't change it */
|
||||||
edata->saved_errno = errno;
|
edata->saved_errno = errno;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Any allocations for this error state level should go into ErrorContext
|
||||||
|
*/
|
||||||
|
edata->assoc_context = ErrorContext;
|
||||||
|
|
||||||
recursion_depth--;
|
recursion_depth--;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -786,7 +791,7 @@ errmsg(const char *fmt,...)
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE(edata->domain, message, false, true);
|
EVALUATE_MESSAGE(edata->domain, message, false, true);
|
||||||
|
|
||||||
@ -815,7 +820,7 @@ errmsg_internal(const char *fmt,...)
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE(edata->domain, message, false, false);
|
EVALUATE_MESSAGE(edata->domain, message, false, false);
|
||||||
|
|
||||||
@ -838,7 +843,7 @@ errmsg_plural(const char *fmt_singular, const char *fmt_plural,
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE_PLURAL(edata->domain, message, false);
|
EVALUATE_MESSAGE_PLURAL(edata->domain, message, false);
|
||||||
|
|
||||||
@ -859,7 +864,7 @@ errdetail(const char *fmt,...)
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE(edata->domain, detail, false, true);
|
EVALUATE_MESSAGE(edata->domain, detail, false, true);
|
||||||
|
|
||||||
@ -886,7 +891,7 @@ errdetail_internal(const char *fmt,...)
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE(edata->domain, detail, false, false);
|
EVALUATE_MESSAGE(edata->domain, detail, false, false);
|
||||||
|
|
||||||
@ -907,7 +912,7 @@ errdetail_log(const char *fmt,...)
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE(edata->domain, detail_log, false, true);
|
EVALUATE_MESSAGE(edata->domain, detail_log, false, true);
|
||||||
|
|
||||||
@ -930,7 +935,7 @@ errdetail_plural(const char *fmt_singular, const char *fmt_plural,
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE_PLURAL(edata->domain, detail, false);
|
EVALUATE_MESSAGE_PLURAL(edata->domain, detail, false);
|
||||||
|
|
||||||
@ -951,7 +956,7 @@ errhint(const char *fmt,...)
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE(edata->domain, hint, false, true);
|
EVALUATE_MESSAGE(edata->domain, hint, false, true);
|
||||||
|
|
||||||
@ -976,7 +981,7 @@ errcontext_msg(const char *fmt,...)
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE(edata->context_domain, context, true, true);
|
EVALUATE_MESSAGE(edata->context_domain, context, true, true);
|
||||||
|
|
||||||
@ -1102,7 +1107,7 @@ internalerrquery(const char *query)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (query)
|
if (query)
|
||||||
edata->internalquery = MemoryContextStrdup(ErrorContext, query);
|
edata->internalquery = MemoryContextStrdup(edata->assoc_context, query);
|
||||||
|
|
||||||
return 0; /* return value does not matter */
|
return 0; /* return value does not matter */
|
||||||
}
|
}
|
||||||
@ -1128,19 +1133,19 @@ err_generic_string(int field, const char *str)
|
|||||||
switch (field)
|
switch (field)
|
||||||
{
|
{
|
||||||
case PG_DIAG_SCHEMA_NAME:
|
case PG_DIAG_SCHEMA_NAME:
|
||||||
set_errdata_field(&edata->schema_name, str);
|
set_errdata_field(edata->assoc_context, &edata->schema_name, str);
|
||||||
break;
|
break;
|
||||||
case PG_DIAG_TABLE_NAME:
|
case PG_DIAG_TABLE_NAME:
|
||||||
set_errdata_field(&edata->table_name, str);
|
set_errdata_field(edata->assoc_context, &edata->table_name, str);
|
||||||
break;
|
break;
|
||||||
case PG_DIAG_COLUMN_NAME:
|
case PG_DIAG_COLUMN_NAME:
|
||||||
set_errdata_field(&edata->column_name, str);
|
set_errdata_field(edata->assoc_context, &edata->column_name, str);
|
||||||
break;
|
break;
|
||||||
case PG_DIAG_DATATYPE_NAME:
|
case PG_DIAG_DATATYPE_NAME:
|
||||||
set_errdata_field(&edata->datatype_name, str);
|
set_errdata_field(edata->assoc_context, &edata->datatype_name, str);
|
||||||
break;
|
break;
|
||||||
case PG_DIAG_CONSTRAINT_NAME:
|
case PG_DIAG_CONSTRAINT_NAME:
|
||||||
set_errdata_field(&edata->constraint_name, str);
|
set_errdata_field(edata->assoc_context, &edata->constraint_name, str);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unsupported ErrorData field id: %d", field);
|
elog(ERROR, "unsupported ErrorData field id: %d", field);
|
||||||
@ -1154,10 +1159,10 @@ err_generic_string(int field, const char *str)
|
|||||||
* set_errdata_field --- set an ErrorData string field
|
* set_errdata_field --- set an ErrorData string field
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
set_errdata_field(char **ptr, const char *str)
|
set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str)
|
||||||
{
|
{
|
||||||
Assert(*ptr == NULL);
|
Assert(*ptr == NULL);
|
||||||
*ptr = MemoryContextStrdup(ErrorContext, str);
|
*ptr = MemoryContextStrdup(cxt, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1257,6 +1262,9 @@ elog_start(const char *filename, int lineno, const char *funcname)
|
|||||||
edata->funcname = funcname;
|
edata->funcname = funcname;
|
||||||
/* errno is saved now so that error parameter eval can't change it */
|
/* errno is saved now so that error parameter eval can't change it */
|
||||||
edata->saved_errno = errno;
|
edata->saved_errno = errno;
|
||||||
|
|
||||||
|
/* Use ErrorContext for any allocations done at this level. */
|
||||||
|
edata->assoc_context = ErrorContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1282,7 +1290,7 @@ elog_finish(int elevel, const char *fmt,...)
|
|||||||
* Format error message just like errmsg_internal().
|
* Format error message just like errmsg_internal().
|
||||||
*/
|
*/
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
EVALUATE_MESSAGE(edata->domain, message, false, false);
|
EVALUATE_MESSAGE(edata->domain, message, false, false);
|
||||||
|
|
||||||
@ -1366,7 +1374,7 @@ EmitErrorReport(void)
|
|||||||
|
|
||||||
recursion_depth++;
|
recursion_depth++;
|
||||||
CHECK_STACK_DEPTH();
|
CHECK_STACK_DEPTH();
|
||||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
oldcontext = MemoryContextSwitchTo(edata->assoc_context);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call hook before sending message to log. The hook function is allowed
|
* Call hook before sending message to log. The hook function is allowed
|
||||||
@ -1446,6 +1454,9 @@ CopyErrorData(void)
|
|||||||
if (newedata->internalquery)
|
if (newedata->internalquery)
|
||||||
newedata->internalquery = pstrdup(newedata->internalquery);
|
newedata->internalquery = pstrdup(newedata->internalquery);
|
||||||
|
|
||||||
|
/* Use the calling context for string allocation */
|
||||||
|
newedata->assoc_context = CurrentMemoryContext;
|
||||||
|
|
||||||
return newedata;
|
return newedata;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1563,6 +1574,9 @@ ReThrowError(ErrorData *edata)
|
|||||||
if (newedata->internalquery)
|
if (newedata->internalquery)
|
||||||
newedata->internalquery = pstrdup(newedata->internalquery);
|
newedata->internalquery = pstrdup(newedata->internalquery);
|
||||||
|
|
||||||
|
/* Reset the assoc_context to be ErrorContext */
|
||||||
|
newedata->assoc_context = ErrorContext;
|
||||||
|
|
||||||
recursion_depth--;
|
recursion_depth--;
|
||||||
PG_RE_THROW();
|
PG_RE_THROW();
|
||||||
}
|
}
|
||||||
@ -1630,12 +1644,8 @@ pg_re_throw(void)
|
|||||||
* GetErrorContextStack - Return the context stack, for display/diags
|
* GetErrorContextStack - Return the context stack, for display/diags
|
||||||
*
|
*
|
||||||
* Returns a pstrdup'd string in the caller's context which includes the PG
|
* 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
|
* error call stack. It is the caller's responsibility to ensure this string
|
||||||
* pfree'd (or its context cleaned up) when done.
|
* 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).
|
|
||||||
*
|
*
|
||||||
* This information is collected by traversing the error contexts and calling
|
* This information is collected by traversing the error contexts and calling
|
||||||
* each context's callback function, each of which is expected to call
|
* each context's callback function, each of which is expected to call
|
||||||
@ -1644,78 +1654,64 @@ pg_re_throw(void)
|
|||||||
char *
|
char *
|
||||||
GetErrorContextStack(void)
|
GetErrorContextStack(void)
|
||||||
{
|
{
|
||||||
char *result = NULL;
|
|
||||||
ErrorData *edata;
|
ErrorData *edata;
|
||||||
ErrorContextCallback *econtext;
|
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
|
* Okay, crank up a stack entry to store the info in.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
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 */
|
errordata_stack_depth = -1; /* make room on stack */
|
||||||
ereport(PANIC,
|
ereport(PANIC, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));
|
||||||
(errmsg_internal("GetErrorContextStack called from exception handler")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize data for the top, and only at this point, error frame as the
|
* Things look good so far, so initialize our error frame
|
||||||
* callback functions we're about to call will turn around and call
|
|
||||||
* errcontext(), which expects to find a valid errordata stack.
|
|
||||||
*/
|
*/
|
||||||
errordata_stack_depth = 0;
|
|
||||||
edata = &errordata[errordata_stack_depth];
|
edata = &errordata[errordata_stack_depth];
|
||||||
MemSet(edata, 0, sizeof(ErrorData));
|
MemSet(edata, 0, sizeof(ErrorData));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use ErrorContext as a short lived context for calling the callbacks;
|
* Set up assoc_context to be the caller's context, so any allocations
|
||||||
* the callbacks will use it through errcontext() even if we don't call
|
* done (which will include edata->context) will use their context.
|
||||||
* them with it, so we have to clean it up below either way.
|
|
||||||
*/
|
*/
|
||||||
MemoryContextSwitchTo(ErrorContext);
|
edata->assoc_context = CurrentMemoryContext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call any context callback functions to collect the context information
|
* Call any context callback functions to collect the context information
|
||||||
* into edata->context.
|
* into edata->context.
|
||||||
*
|
*
|
||||||
* Errors occurring in callback functions should go through the regular
|
* Errors occurring in callback functions should go through the regular
|
||||||
* error handling code which should handle any recursive errors and must
|
* error handling code which should handle any recursive errors, though
|
||||||
* never call back to us.
|
* we double-check above, just in case.
|
||||||
*/
|
*/
|
||||||
for (econtext = error_context_stack;
|
for (econtext = error_context_stack;
|
||||||
econtext != NULL;
|
econtext != NULL;
|
||||||
econtext = econtext->previous)
|
econtext = econtext->previous)
|
||||||
(*econtext->callback) (econtext->arg);
|
(*econtext->callback) (econtext->arg);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
errordata_stack_depth--;
|
||||||
|
recursion_depth--;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy out the string into the caller's context, so we can free our
|
* Return a pointer to the string the caller asked for, which should have
|
||||||
* error context and reset the error stack. Caller is expected to
|
* been allocated in their context.
|
||||||
* pfree() the result or throw away the context.
|
|
||||||
*/
|
*/
|
||||||
if (edata->context)
|
return edata->context;
|
||||||
result = pstrdup(edata->context);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
FlushErrorState();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -397,6 +397,9 @@ typedef struct ErrorData
|
|||||||
int internalpos; /* cursor index into internalquery */
|
int internalpos; /* cursor index into internalquery */
|
||||||
char *internalquery; /* text of internally-generated query */
|
char *internalquery; /* text of internally-generated query */
|
||||||
int saved_errno; /* errno at entry */
|
int saved_errno; /* errno at entry */
|
||||||
|
|
||||||
|
/* context containing associated non-constant strings */
|
||||||
|
struct MemoryContextData *assoc_context;
|
||||||
} ErrorData;
|
} ErrorData;
|
||||||
|
|
||||||
extern void EmitErrorReport(void);
|
extern void EmitErrorReport(void);
|
||||||
|
@ -895,7 +895,6 @@ stmt_getdiag : K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
|
|||||||
/* these fields are disallowed in stacked case */
|
/* these fields are disallowed in stacked case */
|
||||||
case PLPGSQL_GETDIAG_ROW_COUNT:
|
case PLPGSQL_GETDIAG_ROW_COUNT:
|
||||||
case PLPGSQL_GETDIAG_RESULT_OID:
|
case PLPGSQL_GETDIAG_RESULT_OID:
|
||||||
case PLPGSQL_GETDIAG_CONTEXT:
|
|
||||||
if (new->is_stacked)
|
if (new->is_stacked)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
@ -921,6 +920,9 @@ stmt_getdiag : K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
|
|||||||
plpgsql_getdiag_kindname(ditem->kind)),
|
plpgsql_getdiag_kindname(ditem->kind)),
|
||||||
parser_errposition(@1)));
|
parser_errposition(@1)));
|
||||||
break;
|
break;
|
||||||
|
/* these fields are allowed in either case */
|
||||||
|
case PLPGSQL_GETDIAG_CONTEXT:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized diagnostic item kind: %d",
|
elog(ERROR, "unrecognized diagnostic item kind: %d",
|
||||||
ditem->kind);
|
ditem->kind);
|
||||||
|
@ -4987,3 +4987,101 @@ NOTICE: outer_func() done
|
|||||||
drop function outer_outer_func(int);
|
drop function outer_outer_func(int);
|
||||||
drop function outer_func(int);
|
drop function outer_func(int);
|
||||||
drop function inner_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);
|
||||||
|
@ -3927,3 +3927,59 @@ select outer_outer_func(20);
|
|||||||
drop function outer_outer_func(int);
|
drop function outer_outer_func(int);
|
||||||
drop function outer_func(int);
|
drop function outer_func(int);
|
||||||
drop function inner_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);
|
||||||
|
-- repeated call should to work
|
||||||
|
select outer_outer_func(20);
|
||||||
|
|
||||||
|
drop function outer_outer_func(int);
|
||||||
|
drop function outer_func(int);
|
||||||
|
drop function inner_func(int);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user