Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc5bec54

Browse files
committed
Don't corrupt plpython's "TD" dictionary in a recursive trigger call.
If a plpython-language trigger caused another one to be invoked,the "TD" dictionary created for the inner one would overwrite theouter one's "TD" dictionary. This is more or less the same problemthat1d2fe56 fixed for ordinary functions in plpython, so fix itthe same way, by saving and restoring "TD" during a recursiveinvocation.This fix makes an ABI-incompatible change in struct PLySavedArgs.I'm not too worried about that because it seems highly unlikely thatany extension is messing with those structs. We could imagine doingsomething weird to preserve nominal ABI compatibility in the backbranches, like keeping the saved TD object in an extra element ofnamedargs[]. However, that would only be very nominal compatibility:if anything *is* touching PLySavedArgs, it would likely do the wrongthing due to not knowing about the additional value. So I judge itnot worth the ugliness to do something different there.(I also changed struct PLyProcedure, but its added field fitsinto formerly-padding space, so that should be safe.)Per bug #18456 from Jacques Combrink. This bug is very ancient,so back-patch to all supported branches.Discussion:https://postgr.es/m/3008982.1714853799@sss.pgh.pa.us
1 parent5091995 commitc5bec54

File tree

5 files changed

+80
-3
lines changed

5 files changed

+80
-3
lines changed

‎src/pl/plpython/expected/plpython_trigger.out

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,3 +618,30 @@ SELECT * FROM trigger_test_generated;
618618
---+---
619619
(0 rows)
620620

621+
-- recursive call of a trigger mustn't corrupt TD (bug #18456)
622+
CREATE TABLE recursive_trigger_test (a int, b int);
623+
CREATE FUNCTION recursive_trigger_func() RETURNS trigger
624+
LANGUAGE plpython3u
625+
AS $$
626+
if TD["event"] == "UPDATE":
627+
plpy.execute("INSERT INTO recursive_trigger_test VALUES (1, 2)")
628+
plpy.notice("TD[event] => " + str(TD["event"]) + ", expecting UPDATE");
629+
else:
630+
plpy.notice("TD[event] => " + str(TD["event"]) + ", expecting INSERT");
631+
return None
632+
$$;
633+
CREATE TRIGGER recursive_trigger_trig
634+
AFTER INSERT OR UPDATE ON recursive_trigger_test
635+
FOR EACH ROW EXECUTE PROCEDURE recursive_trigger_func();
636+
INSERT INTO recursive_trigger_test VALUES (0, 0);
637+
NOTICE: TD[event] => INSERT, expecting INSERT
638+
UPDATE recursive_trigger_test SET a = 11 WHERE b = 0;
639+
NOTICE: TD[event] => INSERT, expecting INSERT
640+
NOTICE: TD[event] => UPDATE, expecting UPDATE
641+
SELECT * FROM recursive_trigger_test;
642+
a | b
643+
----+---
644+
11 | 0
645+
1 | 2
646+
(2 rows)
647+

‎src/pl/plpython/plpy_exec.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,13 @@ PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
335335
PLy_output_setup_tuple(&proc->result,rel_descr,proc);
336336
PLy_input_setup_tuple(&proc->result_in,rel_descr,proc);
337337

338+
/*
339+
* If the trigger is called recursively, we must push outer-level
340+
* arguments into the stack. This must be immediately before the PG_TRY
341+
* to ensure that the corresponding pop happens.
342+
*/
343+
PLy_global_args_push(proc);
344+
338345
PG_TRY();
339346
{
340347
intrcPG_USED_FOR_ASSERTS_ONLY;
@@ -397,6 +404,7 @@ PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
397404
}
398405
PG_FINALLY();
399406
{
407+
PLy_global_args_pop(proc);
400408
Py_XDECREF(plargs);
401409
Py_XDECREF(plrv);
402410
}
@@ -501,6 +509,13 @@ PLy_function_save_args(PLyProcedure *proc)
501509
result->args=PyDict_GetItemString(proc->globals,"args");
502510
Py_XINCREF(result->args);
503511

512+
/* If it's a trigger, also save "TD" */
513+
if (proc->is_trigger)
514+
{
515+
result->td=PyDict_GetItemString(proc->globals,"TD");
516+
Py_XINCREF(result->td);
517+
}
518+
504519
/* Fetch all the named arguments */
505520
if (proc->argnames)
506521
{
@@ -550,6 +565,13 @@ PLy_function_restore_args(PLyProcedure *proc, PLySavedArgs *savedargs)
550565
Py_DECREF(savedargs->args);
551566
}
552567

568+
/* Restore the "TD" object, too */
569+
if (savedargs->td)
570+
{
571+
PyDict_SetItemString(proc->globals,"TD",savedargs->td);
572+
Py_DECREF(savedargs->td);
573+
}
574+
553575
/* And free the PLySavedArgs struct */
554576
pfree(savedargs);
555577
}
@@ -568,8 +590,9 @@ PLy_function_drop_args(PLySavedArgs *savedargs)
568590
Py_XDECREF(savedargs->namedargs[i]);
569591
}
570592

571-
/* Dropref to the "args"object, too */
593+
/* Droprefs to the "args"and "TD" objects, too */
572594
Py_XDECREF(savedargs->args);
595+
Py_XDECREF(savedargs->td);
573596

574597
/* And free the PLySavedArgs struct */
575598
pfree(savedargs);
@@ -578,9 +601,9 @@ PLy_function_drop_args(PLySavedArgs *savedargs)
578601
/*
579602
* Save away any existing arguments for the given procedure, so that we can
580603
* install new values for a recursive call. This should be invoked before
581-
* doing PLy_function_build_args().
604+
* doing PLy_function_build_args() or PLy_trigger_build_args().
582605
*
583-
* NB:caller must ensure that PLy_global_args_pop gets invoked once, and
606+
* NB:callers must ensure that PLy_global_args_pop gets invoked once, and
584607
* only once, per successful completion of PLy_global_args_push. Otherwise
585608
* we'll end up out-of-sync between the actual call stack and the contents
586609
* of proc->argstack.

‎src/pl/plpython/plpy_procedure.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
183183
proc->fn_readonly= (procStruct->provolatile!=PROVOLATILE_VOLATILE);
184184
proc->is_setof=procStruct->proretset;
185185
proc->is_procedure= (procStruct->prokind==PROKIND_PROCEDURE);
186+
proc->is_trigger=is_trigger;
186187
proc->src=NULL;
187188
proc->argnames=NULL;
188189
proc->args=NULL;

‎src/pl/plpython/plpy_procedure.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ typedef struct PLySavedArgs
1616
{
1717
structPLySavedArgs*next;/* linked-list pointer */
1818
PyObject*args;/* "args" element of globals dict */
19+
PyObject*td;/* "TD" element of globals dict, if trigger */
1920
intnargs;/* length of namedargs array */
2021
PyObject*namedargs[FLEXIBLE_ARRAY_MEMBER];/* named args */
2122
}PLySavedArgs;
@@ -32,6 +33,7 @@ typedef struct PLyProcedure
3233
boolfn_readonly;
3334
boolis_setof;/* true, if function returns result set */
3435
boolis_procedure;
36+
boolis_trigger;/* called as trigger? */
3537
PLyObToDatumresult;/* Function result output conversion info */
3638
PLyDatumToObresult_in;/* For converting input tuples in a trigger */
3739
char*src;/* textual procedure code, after mangling */

‎src/pl/plpython/sql/plpython_trigger.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,3 +467,27 @@ FOR EACH ROW EXECUTE PROCEDURE generated_test_func1();
467467
TRUNCATE trigger_test_generated;
468468
INSERT INTO trigger_test_generated (i)VALUES (1);
469469
SELECT*FROM trigger_test_generated;
470+
471+
472+
-- recursive call of a trigger mustn't corrupt TD (bug #18456)
473+
474+
CREATETABLErecursive_trigger_test (aint, bint);
475+
476+
CREATEFUNCTIONrecursive_trigger_func() RETURNS trigger
477+
LANGUAGE plpython3u
478+
AS $$
479+
if TD["event"]=="UPDATE":
480+
plpy.execute("INSERT INTO recursive_trigger_test VALUES (1, 2)")
481+
plpy.notice("TD[event] =>"+ str(TD["event"])+", expecting UPDATE");
482+
else:
483+
plpy.notice("TD[event] =>"+ str(TD["event"])+", expecting INSERT");
484+
return None
485+
$$;
486+
487+
CREATETRIGGERrecursive_trigger_trig
488+
AFTER INSERTORUPDATEON recursive_trigger_test
489+
FOR EACH ROW EXECUTE PROCEDURE recursive_trigger_func();
490+
491+
INSERT INTO recursive_trigger_testVALUES (0,0);
492+
UPDATE recursive_trigger_testSET a=11WHERE b=0;
493+
SELECT*FROM recursive_trigger_test;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp