mirror of
https://github.com/postgres/postgres.git
synced 2025-06-18 00:02:37 -04:00
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
73 lines
2.4 KiB
C
73 lines
2.4 KiB
C
/*
|
|
* src/pl/plpython/plpy_procedure.h
|
|
*/
|
|
|
|
#ifndef PLPY_PROCEDURE_H
|
|
#define PLPY_PROCEDURE_H
|
|
|
|
#include "plpy_typeio.h"
|
|
|
|
|
|
extern void init_procedure_caches(void);
|
|
|
|
|
|
/* saved arguments for outer recursion level or set-returning function */
|
|
typedef struct PLySavedArgs
|
|
{
|
|
struct PLySavedArgs *next; /* linked-list pointer */
|
|
PyObject *args; /* "args" element of globals dict */
|
|
PyObject *td; /* "TD" element of globals dict, if trigger */
|
|
int nargs; /* length of namedargs array */
|
|
PyObject *namedargs[FLEXIBLE_ARRAY_MEMBER]; /* named args */
|
|
} PLySavedArgs;
|
|
|
|
/* cached procedure data */
|
|
typedef struct PLyProcedure
|
|
{
|
|
MemoryContext mcxt; /* context holding this PLyProcedure and its
|
|
* subsidiary data */
|
|
char *proname; /* SQL name of procedure */
|
|
char *pyname; /* Python name of procedure */
|
|
TransactionId fn_xmin;
|
|
ItemPointerData fn_tid;
|
|
bool fn_readonly;
|
|
bool is_setof; /* true, if function returns result set */
|
|
bool is_procedure;
|
|
bool is_trigger; /* called as trigger? */
|
|
PLyObToDatum result; /* Function result output conversion info */
|
|
PLyDatumToOb result_in; /* For converting input tuples in a trigger */
|
|
char *src; /* textual procedure code, after mangling */
|
|
char **argnames; /* Argument names */
|
|
PLyDatumToOb *args; /* Argument input conversion info */
|
|
int nargs; /* Number of elements in above arrays */
|
|
Oid langid; /* OID of plpython pg_language entry */
|
|
List *trftypes; /* OID list of transform types */
|
|
PyObject *code; /* compiled procedure code */
|
|
PyObject *statics; /* data saved across calls, local scope */
|
|
PyObject *globals; /* data saved across calls, global scope */
|
|
long calldepth; /* depth of recursive calls of function */
|
|
PLySavedArgs *argstack; /* stack of outer-level call arguments */
|
|
} PLyProcedure;
|
|
|
|
/* the procedure cache key */
|
|
typedef struct PLyProcedureKey
|
|
{
|
|
Oid fn_oid; /* function OID */
|
|
Oid fn_rel; /* triggered-on relation or InvalidOid */
|
|
} PLyProcedureKey;
|
|
|
|
/* the procedure cache entry */
|
|
typedef struct PLyProcedureEntry
|
|
{
|
|
PLyProcedureKey key; /* hash key */
|
|
PLyProcedure *proc;
|
|
} PLyProcedureEntry;
|
|
|
|
/* PLyProcedure manipulation */
|
|
extern char *PLy_procedure_name(PLyProcedure *proc);
|
|
extern PLyProcedure *PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger);
|
|
extern void PLy_procedure_compile(PLyProcedure *proc, const char *src);
|
|
extern void PLy_procedure_delete(PLyProcedure *proc);
|
|
|
|
#endif /* PLPY_PROCEDURE_H */
|