diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out index 1f52af7fe0..4d615b41cc 100644 --- a/src/pl/plpython/expected/plpython_error.out +++ b/src/pl/plpython/expected/plpython_error.out @@ -422,3 +422,26 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN -- NOOP END $$ LANGUAGE plpgsql; +/* test the context stack trace for nested execution levels + */ +CREATE FUNCTION notice_innerfunc() RETURNS int AS $$ +plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$") +return 1 +$$ LANGUAGE plpythonu; +CREATE FUNCTION notice_outerfunc() RETURNS int AS $$ +plpy.execute("SELECT notice_innerfunc()") +return 1 +$$ LANGUAGE plpythonu; +\set SHOW_CONTEXT always +SELECT notice_outerfunc(); +NOTICE: inside DO +CONTEXT: PL/Python anonymous code block +SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$" +PL/Python function "notice_innerfunc" +SQL statement "SELECT notice_innerfunc()" +PL/Python function "notice_outerfunc" + notice_outerfunc +------------------ + 1 +(1 row) + diff --git a/src/pl/plpython/expected/plpython_error_0.out b/src/pl/plpython/expected/plpython_error_0.out index 5323906122..290902b182 100644 --- a/src/pl/plpython/expected/plpython_error_0.out +++ b/src/pl/plpython/expected/plpython_error_0.out @@ -422,3 +422,26 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN -- NOOP END $$ LANGUAGE plpgsql; +/* test the context stack trace for nested execution levels + */ +CREATE FUNCTION notice_innerfunc() RETURNS int AS $$ +plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$") +return 1 +$$ LANGUAGE plpythonu; +CREATE FUNCTION notice_outerfunc() RETURNS int AS $$ +plpy.execute("SELECT notice_innerfunc()") +return 1 +$$ LANGUAGE plpythonu; +\set SHOW_CONTEXT always +SELECT notice_outerfunc(); +NOTICE: inside DO +CONTEXT: PL/Python anonymous code block +SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$" +PL/Python function "notice_innerfunc" +SQL statement "SELECT notice_innerfunc()" +PL/Python function "notice_outerfunc" + notice_outerfunc +------------------ + 1 +(1 row) + diff --git a/src/pl/plpython/expected/plpython_error_5.out b/src/pl/plpython/expected/plpython_error_5.out index 5ff46ca50a..bc66ab5534 100644 --- a/src/pl/plpython/expected/plpython_error_5.out +++ b/src/pl/plpython/expected/plpython_error_5.out @@ -422,3 +422,26 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN -- NOOP END $$ LANGUAGE plpgsql; +/* test the context stack trace for nested execution levels + */ +CREATE FUNCTION notice_innerfunc() RETURNS int AS $$ +plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$") +return 1 +$$ LANGUAGE plpythonu; +CREATE FUNCTION notice_outerfunc() RETURNS int AS $$ +plpy.execute("SELECT notice_innerfunc()") +return 1 +$$ LANGUAGE plpythonu; +\set SHOW_CONTEXT always +SELECT notice_outerfunc(); +NOTICE: inside DO +CONTEXT: PL/Python anonymous code block +SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$" +PL/Python function "notice_innerfunc" +SQL statement "SELECT notice_innerfunc()" +PL/Python function "notice_outerfunc" + notice_outerfunc +------------------ + 1 +(1 row) + diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c index 5a197ce27a..6a66eba176 100644 --- a/src/pl/plpython/plpy_main.c +++ b/src/pl/plpython/plpy_main.c @@ -237,23 +237,26 @@ plpython_call_handler(PG_FUNCTION_ARGS) /* * Push execution context onto stack. It is important that this get * popped again, so avoid putting anything that could throw error between - * here and the PG_TRY. (plpython_error_callback expects the stack entry - * to be there, so we have to make the context first.) + * here and the PG_TRY. */ exec_ctx = PLy_push_execution_context(!nonatomic); - /* - * Setup error traceback support for ereport() - */ - plerrcontext.callback = plpython_error_callback; - plerrcontext.previous = error_context_stack; - error_context_stack = &plerrcontext; - PG_TRY(); { Oid funcoid = fcinfo->flinfo->fn_oid; PLyProcedure *proc; + /* + * Setup error traceback support for ereport(). Note that the PG_TRY + * structure pops this for us again at exit, so we needn't do that + * explicitly, nor do we risk the callback getting called after we've + * destroyed the exec_ctx. + */ + plerrcontext.callback = plpython_error_callback; + plerrcontext.arg = exec_ctx; + plerrcontext.previous = error_context_stack; + error_context_stack = &plerrcontext; + if (CALLED_AS_TRIGGER(fcinfo)) { Relation tgrel = ((TriggerData *) fcinfo->context)->tg_relation; @@ -279,9 +282,7 @@ plpython_call_handler(PG_FUNCTION_ARGS) } PG_END_TRY(); - /* Pop the error context stack */ - error_context_stack = plerrcontext.previous; - /* ... and then the execution context */ + /* Destroy the execution context */ PLy_pop_execution_context(); return retval; @@ -333,21 +334,22 @@ plpython_inline_handler(PG_FUNCTION_ARGS) /* * Push execution context onto stack. It is important that this get * popped again, so avoid putting anything that could throw error between - * here and the PG_TRY. (plpython_inline_error_callback doesn't currently - * need the stack entry, but for consistency with plpython_call_handler we - * do it in this order.) + * here and the PG_TRY. */ exec_ctx = PLy_push_execution_context(codeblock->atomic); - /* - * Setup error traceback support for ereport() - */ - plerrcontext.callback = plpython_inline_error_callback; - plerrcontext.previous = error_context_stack; - error_context_stack = &plerrcontext; - PG_TRY(); { + /* + * Setup error traceback support for ereport(). + * plpython_inline_error_callback doesn't currently need exec_ctx, but + * for consistency with plpython_call_handler we do it the same way. + */ + plerrcontext.callback = plpython_inline_error_callback; + plerrcontext.arg = exec_ctx; + plerrcontext.previous = error_context_stack; + error_context_stack = &plerrcontext; + PLy_procedure_compile(&proc, codeblock->source_text); exec_ctx->curr_proc = &proc; PLy_exec_function(&fake_fcinfo, &proc); @@ -361,9 +363,7 @@ plpython_inline_handler(PG_FUNCTION_ARGS) } PG_END_TRY(); - /* Pop the error context stack */ - error_context_stack = plerrcontext.previous; - /* ... and then the execution context */ + /* Destroy the execution context */ PLy_pop_execution_context(); /* Now clean up the transient procedure we made */ @@ -391,7 +391,7 @@ PLy_procedure_is_trigger(Form_pg_proc procStruct) static void plpython_error_callback(void *arg) { - PLyExecutionContext *exec_ctx = PLy_current_execution_context(); + PLyExecutionContext *exec_ctx = (PLyExecutionContext *) arg; if (exec_ctx->curr_proc) { diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c index 990a33cc6d..4e06413cd4 100644 --- a/src/pl/plpython/plpy_procedure.c +++ b/src/pl/plpython/plpy_procedure.c @@ -47,9 +47,7 @@ init_procedure_caches(void) } /* - * Get the name of the last procedure called by the backend (the - * innermost, if a plpython procedure call calls the backend and the - * backend calls another plpython procedure). + * PLy_procedure_name: get the name of the specified procedure. * * NB: this returns the SQL name, not the internal Python procedure name */ diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql index d0df7e607d..d712eb1078 100644 --- a/src/pl/plpython/sql/plpython_error.sql +++ b/src/pl/plpython/sql/plpython_error.sql @@ -328,3 +328,19 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN -- NOOP END $$ LANGUAGE plpgsql; + +/* test the context stack trace for nested execution levels + */ +CREATE FUNCTION notice_innerfunc() RETURNS int AS $$ +plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$") +return 1 +$$ LANGUAGE plpythonu; + +CREATE FUNCTION notice_outerfunc() RETURNS int AS $$ +plpy.execute("SELECT notice_innerfunc()") +return 1 +$$ LANGUAGE plpythonu; + +\set SHOW_CONTEXT always + +SELECT notice_outerfunc();