Skip to content

Commit 457e9e8

Browse files
committed
Fix broken logic for reporting PL/Python function names in errcontext.
plpython_error_callback() reported the name of the function associated with the topmost PL/Python execution context. This was not merely wrong if there were nested PL/Python contexts, but it risked a core dump if the topmost one is an inline code block rather than a named function. That will have proname = NULL, and so we were passing a NULL pointer to snprintf("%s"). It seems that none of the PL/Python-testing machines in the buildfarm will dump core for that, but some platforms do, as reported by Marina Polyakova. Investigation finds that there actually is an existing regression test that used to prove that the behavior was wrong, though apparently no one had noticed that it was printing the wrong function name. It stopped showing the problem in 9.6 when we adjusted psql to not print CONTEXT by default for NOTICE messages. The problem is masked (if your platform avoids the core dump) in error cases, because PL/Python will throw away the originally generated error info in favor of a new traceback produced at the outer level. Repair by using ErrorContextCallback.arg to pass the correct context to the error callback. Add a regression test illustrating correct behavior. Back-patch to all supported branches, since they're all broken this way. Discussion: https://postgr.es/m/156b989dbc6fe7c4d3223cf51da61195@postgrespro.ru
1 parent 11f5d14 commit 457e9e8

File tree

7 files changed

+113
-30
lines changed

7 files changed

+113
-30
lines changed

src/pl/plpython/expected/plpython_error.out

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,26 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN
426426
-- NOOP
427427
END
428428
$$ LANGUAGE plpgsql;
429+
/* test the context stack trace for nested execution levels
430+
*/
431+
CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
432+
plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
433+
return 1
434+
$$ LANGUAGE plpythonu;
435+
CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
436+
plpy.execute("SELECT notice_innerfunc()")
437+
return 1
438+
$$ LANGUAGE plpythonu;
439+
\set SHOW_CONTEXT always
440+
SELECT notice_outerfunc();
441+
NOTICE: inside DO
442+
CONTEXT: PL/Python anonymous code block
443+
SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
444+
PL/Python function "notice_innerfunc"
445+
SQL statement "SELECT notice_innerfunc()"
446+
PL/Python function "notice_outerfunc"
447+
notice_outerfunc
448+
------------------
449+
1
450+
(1 row)
451+

src/pl/plpython/expected/plpython_error_0.out

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,26 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN
426426
-- NOOP
427427
END
428428
$$ LANGUAGE plpgsql;
429+
/* test the context stack trace for nested execution levels
430+
*/
431+
CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
432+
plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
433+
return 1
434+
$$ LANGUAGE plpythonu;
435+
CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
436+
plpy.execute("SELECT notice_innerfunc()")
437+
return 1
438+
$$ LANGUAGE plpythonu;
439+
\set SHOW_CONTEXT always
440+
SELECT notice_outerfunc();
441+
NOTICE: inside DO
442+
CONTEXT: PL/Python anonymous code block
443+
SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
444+
PL/Python function "notice_innerfunc"
445+
SQL statement "SELECT notice_innerfunc()"
446+
PL/Python function "notice_outerfunc"
447+
notice_outerfunc
448+
------------------
449+
1
450+
(1 row)
451+

src/pl/plpython/expected/plpython_error_5.out

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,26 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN
426426
-- NOOP
427427
END
428428
$$ LANGUAGE plpgsql;
429+
/* test the context stack trace for nested execution levels
430+
*/
431+
CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
432+
plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
433+
return 1
434+
$$ LANGUAGE plpythonu;
435+
CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
436+
plpy.execute("SELECT notice_innerfunc()")
437+
return 1
438+
$$ LANGUAGE plpythonu;
439+
\set SHOW_CONTEXT always
440+
SELECT notice_outerfunc();
441+
NOTICE: inside DO
442+
CONTEXT: PL/Python anonymous code block
443+
SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
444+
PL/Python function "notice_innerfunc"
445+
SQL statement "SELECT notice_innerfunc()"
446+
PL/Python function "notice_outerfunc"
447+
notice_outerfunc
448+
------------------
449+
1
450+
(1 row)
451+

src/pl/plpython/expected/plpython_subtransaction.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ SELECT subtransaction_deeply_nested_test();
182182
NOTICE: Swallowed SyntaxError('syntax error at or near "error"',)
183183
CONTEXT: PL/Python function "subtransaction_nested_test"
184184
SQL statement "SELECT subtransaction_nested_test('t')"
185-
PL/Python function "subtransaction_nested_test"
185+
PL/Python function "subtransaction_deeply_nested_test"
186186
subtransaction_deeply_nested_test
187187
-----------------------------------
188188
ok

src/pl/plpython/plpy_main.c

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -266,23 +266,26 @@ plpython_call_handler(PG_FUNCTION_ARGS)
266266
/*
267267
* Push execution context onto stack. It is important that this get
268268
* popped again, so avoid putting anything that could throw error between
269-
* here and the PG_TRY. (plpython_error_callback expects the stack entry
270-
* to be there, so we have to make the context first.)
269+
* here and the PG_TRY.
271270
*/
272271
exec_ctx = PLy_push_execution_context();
273272

274-
/*
275-
* Setup error traceback support for ereport()
276-
*/
277-
plerrcontext.callback = plpython_error_callback;
278-
plerrcontext.previous = error_context_stack;
279-
error_context_stack = &plerrcontext;
280-
281273
PG_TRY();
282274
{
283275
Oid funcoid = fcinfo->flinfo->fn_oid;
284276
PLyProcedure *proc;
285277

278+
/*
279+
* Setup error traceback support for ereport(). Note that the PG_TRY
280+
* structure pops this for us again at exit, so we needn't do that
281+
* explicitly, nor do we risk the callback getting called after we've
282+
* destroyed the exec_ctx.
283+
*/
284+
plerrcontext.callback = plpython_error_callback;
285+
plerrcontext.arg = exec_ctx;
286+
plerrcontext.previous = error_context_stack;
287+
error_context_stack = &plerrcontext;
288+
286289
if (CALLED_AS_TRIGGER(fcinfo))
287290
{
288291
Relation tgrel = ((TriggerData *) fcinfo->context)->tg_relation;
@@ -308,9 +311,7 @@ plpython_call_handler(PG_FUNCTION_ARGS)
308311
}
309312
PG_END_TRY();
310313

311-
/* Pop the error context stack */
312-
error_context_stack = plerrcontext.previous;
313-
/* ... and then the execution context */
314+
/* Destroy the execution context */
314315
PLy_pop_execution_context();
315316

316317
return retval;
@@ -353,21 +354,22 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
353354
/*
354355
* Push execution context onto stack. It is important that this get
355356
* popped again, so avoid putting anything that could throw error between
356-
* here and the PG_TRY. (plpython_inline_error_callback doesn't currently
357-
* need the stack entry, but for consistency with plpython_call_handler we
358-
* do it in this order.)
357+
* here and the PG_TRY.
359358
*/
360359
exec_ctx = PLy_push_execution_context();
361360

362-
/*
363-
* Setup error traceback support for ereport()
364-
*/
365-
plerrcontext.callback = plpython_inline_error_callback;
366-
plerrcontext.previous = error_context_stack;
367-
error_context_stack = &plerrcontext;
368-
369361
PG_TRY();
370362
{
363+
/*
364+
* Setup error traceback support for ereport().
365+
* plpython_inline_error_callback doesn't currently need exec_ctx, but
366+
* for consistency with plpython_call_handler we do it the same way.
367+
*/
368+
plerrcontext.callback = plpython_inline_error_callback;
369+
plerrcontext.arg = exec_ctx;
370+
plerrcontext.previous = error_context_stack;
371+
error_context_stack = &plerrcontext;
372+
371373
PLy_procedure_compile(&proc, codeblock->source_text);
372374
exec_ctx->curr_proc = &proc;
373375
PLy_exec_function(&fake_fcinfo, &proc);
@@ -381,9 +383,7 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
381383
}
382384
PG_END_TRY();
383385

384-
/* Pop the error context stack */
385-
error_context_stack = plerrcontext.previous;
386-
/* ... and then the execution context */
386+
/* Destroy the execution context */
387387
PLy_pop_execution_context();
388388

389389
/* Now clean up the transient procedure we made */
@@ -411,7 +411,7 @@ PLy_procedure_is_trigger(Form_pg_proc procStruct)
411411
static void
412412
plpython_error_callback(void *arg)
413413
{
414-
PLyExecutionContext *exec_ctx = PLy_current_execution_context();
414+
PLyExecutionContext *exec_ctx = (PLyExecutionContext *) arg;
415415

416416
if (exec_ctx->curr_proc)
417417
errcontext("PL/Python function \"%s\"",

src/pl/plpython/plpy_procedure.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ init_procedure_caches(void)
4545
}
4646

4747
/*
48-
* Get the name of the last procedure called by the backend (the
49-
* innermost, if a plpython procedure call calls the backend and the
50-
* backend calls another plpython procedure).
48+
* PLy_procedure_name: get the name of the specified procedure.
5149
*
5250
* NB: this returns the SQL name, not the internal Python procedure name
5351
*/

src/pl/plpython/sql/plpython_error.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,3 +328,19 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN
328328
-- NOOP
329329
END
330330
$$ LANGUAGE plpgsql;
331+
332+
/* test the context stack trace for nested execution levels
333+
*/
334+
CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
335+
plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
336+
return 1
337+
$$ LANGUAGE plpythonu;
338+
339+
CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
340+
plpy.execute("SELECT notice_innerfunc()")
341+
return 1
342+
$$ LANGUAGE plpythonu;
343+
344+
\set SHOW_CONTEXT always
345+
346+
SELECT notice_outerfunc();

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy