aboutsummaryrefslogtreecommitdiff
path: root/src/pl/plpython/plpy_exec.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-05-07 18:15:00 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2024-05-07 18:15:00 -0400
commitbe18a12b663181f304d49022a452e31e4df42ff2 (patch)
treedde4796b8533453cce35289270982ac3071cb91e /src/pl/plpython/plpy_exec.c
parent05ffe9398b758bbb8d30cc76e9bbc638dab2d477 (diff)
downloadpostgresql-be18a12b663181f304d49022a452e31e4df42ff2.tar.gz
postgresql-be18a12b663181f304d49022a452e31e4df42ff2.zip
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 the outer one's "TD" dictionary. This is more or less the same problem that 1d2fe56e4 fixed for ordinary functions in plpython, so fix it the same way, by saving and restoring "TD" during a recursive invocation. This fix makes an ABI-incompatible change in struct PLySavedArgs. I'm not too worried about that because it seems highly unlikely that any extension is messing with those structs. We could imagine doing something weird to preserve nominal ABI compatibility in the back branches, like keeping the saved TD object in an extra element of namedargs[]. However, that would only be very nominal compatibility: if anything *is* touching PLySavedArgs, it would likely do the wrong thing due to not knowing about the additional value. So I judge it not worth the ugliness to do something different there. (I also changed struct PLyProcedure, but its added field fits into 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
Diffstat (limited to 'src/pl/plpython/plpy_exec.c')
-rw-r--r--src/pl/plpython/plpy_exec.c29
1 files changed, 26 insertions, 3 deletions
diff --git a/src/pl/plpython/plpy_exec.c b/src/pl/plpython/plpy_exec.c
index 3145c696996..6f7b5e121d6 100644
--- a/src/pl/plpython/plpy_exec.c
+++ b/src/pl/plpython/plpy_exec.c
@@ -335,6 +335,13 @@ PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
PLy_output_setup_tuple(&proc->result, rel_descr, proc);
PLy_input_setup_tuple(&proc->result_in, rel_descr, proc);
+ /*
+ * If the trigger is called recursively, we must push outer-level
+ * arguments into the stack. This must be immediately before the PG_TRY
+ * to ensure that the corresponding pop happens.
+ */
+ PLy_global_args_push(proc);
+
PG_TRY();
{
int rc PG_USED_FOR_ASSERTS_ONLY;
@@ -397,6 +404,7 @@ PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
}
PG_FINALLY();
{
+ PLy_global_args_pop(proc);
Py_XDECREF(plargs);
Py_XDECREF(plrv);
}
@@ -501,6 +509,13 @@ PLy_function_save_args(PLyProcedure *proc)
result->args = PyDict_GetItemString(proc->globals, "args");
Py_XINCREF(result->args);
+ /* If it's a trigger, also save "TD" */
+ if (proc->is_trigger)
+ {
+ result->td = PyDict_GetItemString(proc->globals, "TD");
+ Py_XINCREF(result->td);
+ }
+
/* Fetch all the named arguments */
if (proc->argnames)
{
@@ -550,6 +565,13 @@ PLy_function_restore_args(PLyProcedure *proc, PLySavedArgs *savedargs)
Py_DECREF(savedargs->args);
}
+ /* Restore the "TD" object, too */
+ if (savedargs->td)
+ {
+ PyDict_SetItemString(proc->globals, "TD", savedargs->td);
+ Py_DECREF(savedargs->td);
+ }
+
/* And free the PLySavedArgs struct */
pfree(savedargs);
}
@@ -568,8 +590,9 @@ PLy_function_drop_args(PLySavedArgs *savedargs)
Py_XDECREF(savedargs->namedargs[i]);
}
- /* Drop ref to the "args" object, too */
+ /* Drop refs to the "args" and "TD" objects, too */
Py_XDECREF(savedargs->args);
+ Py_XDECREF(savedargs->td);
/* And free the PLySavedArgs struct */
pfree(savedargs);
@@ -578,9 +601,9 @@ PLy_function_drop_args(PLySavedArgs *savedargs)
/*
* Save away any existing arguments for the given procedure, so that we can
* install new values for a recursive call. This should be invoked before
- * doing PLy_function_build_args().
+ * doing PLy_function_build_args() or PLy_trigger_build_args().
*
- * NB: caller must ensure that PLy_global_args_pop gets invoked once, and
+ * NB: callers must ensure that PLy_global_args_pop gets invoked once, and
* only once, per successful completion of PLy_global_args_push. Otherwise
* we'll end up out-of-sync between the actual call stack and the contents
* of proc->argstack.