12
12
#include "executor/spi.h"
13
13
#include "miscadmin.h"
14
14
#include "utils/guc.h"
15
+ #include "utils/memutils.h"
15
16
#include "utils/syscache.h"
16
17
17
18
#include "plpython.h"
@@ -66,11 +67,17 @@ static void plpython_error_callback(void *arg);
66
67
static void plpython_inline_error_callback (void * arg );
67
68
static void PLy_init_interp (void );
68
69
70
+ static PLyExecutionContext * PLy_push_execution_context (void );
71
+ static void PLy_pop_execution_context (void );
72
+
69
73
static const int plpython_python_version = PY_MAJOR_VERSION ;
70
74
71
75
/* initialize global variables */
72
76
PyObject * PLy_interp_globals = NULL ;
73
77
78
+ /* this doesn't need to be global; use PLy_current_execution_context() */
79
+ static PLyExecutionContext * PLy_execution_contexts = NULL ;
80
+
74
81
75
82
void
76
83
_PG_init (void )
@@ -114,6 +121,8 @@ _PG_init(void)
114
121
115
122
explicit_subtransactions = NIL ;
116
123
124
+ PLy_execution_contexts = NULL ;
125
+
117
126
inited = true;
118
127
}
119
128
@@ -179,13 +188,20 @@ Datum
179
188
plpython_call_handler (PG_FUNCTION_ARGS )
180
189
{
181
190
Datum retval ;
182
- PLyProcedure * save_curr_proc ;
191
+ PLyExecutionContext * exec_ctx ;
183
192
ErrorContextCallback plerrcontext ;
184
193
194
+ /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
185
195
if (SPI_connect () != SPI_OK_CONNECT )
186
196
elog (ERROR , "SPI_connect failed" );
187
197
188
- save_curr_proc = PLy_curr_procedure ;
198
+ /*
199
+ * Push execution context onto stack. It is important that this get
200
+ * popped again, so avoid putting anything that could throw error between
201
+ * here and the PG_TRY. (plpython_error_callback expects the stack entry
202
+ * to be there, so we have to make the context first.)
203
+ */
204
+ exec_ctx = PLy_push_execution_context ();
189
205
190
206
/*
191
207
* Setup error traceback support for ereport()
@@ -203,29 +219,29 @@ plpython_call_handler(PG_FUNCTION_ARGS)
203
219
HeapTuple trv ;
204
220
205
221
proc = PLy_procedure_get (fcinfo -> flinfo -> fn_oid , true);
206
- PLy_curr_procedure = proc ;
222
+ exec_ctx -> curr_proc = proc ;
207
223
trv = PLy_exec_trigger (fcinfo , proc );
208
224
retval = PointerGetDatum (trv );
209
225
}
210
226
else
211
227
{
212
228
proc = PLy_procedure_get (fcinfo -> flinfo -> fn_oid , false);
213
- PLy_curr_procedure = proc ;
229
+ exec_ctx -> curr_proc = proc ;
214
230
retval = PLy_exec_function (fcinfo , proc );
215
231
}
216
232
}
217
233
PG_CATCH ();
218
234
{
219
- PLy_curr_procedure = save_curr_proc ;
235
+ PLy_pop_execution_context () ;
220
236
PyErr_Clear ();
221
237
PG_RE_THROW ();
222
238
}
223
239
PG_END_TRY ();
224
240
225
241
/* Pop the error context stack */
226
242
error_context_stack = plerrcontext .previous ;
227
-
228
- PLy_curr_procedure = save_curr_proc ;
243
+ /* ... and then the execution context */
244
+ PLy_pop_execution_context () ;
229
245
230
246
return retval ;
231
247
}
@@ -244,22 +260,14 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
244
260
InlineCodeBlock * codeblock = (InlineCodeBlock * ) DatumGetPointer (PG_GETARG_DATUM (0 ));
245
261
FunctionCallInfoData fake_fcinfo ;
246
262
FmgrInfo flinfo ;
247
- PLyProcedure * save_curr_proc ;
248
263
PLyProcedure proc ;
264
+ PLyExecutionContext * exec_ctx ;
249
265
ErrorContextCallback plerrcontext ;
250
266
267
+ /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
251
268
if (SPI_connect () != SPI_OK_CONNECT )
252
269
elog (ERROR , "SPI_connect failed" );
253
270
254
- save_curr_proc = PLy_curr_procedure ;
255
-
256
- /*
257
- * Setup error traceback support for ereport()
258
- */
259
- plerrcontext .callback = plpython_inline_error_callback ;
260
- plerrcontext .previous = error_context_stack ;
261
- error_context_stack = & plerrcontext ;
262
-
263
271
MemSet (& fake_fcinfo , 0 , sizeof (fake_fcinfo ));
264
272
MemSet (& flinfo , 0 , sizeof (flinfo ));
265
273
fake_fcinfo .flinfo = & flinfo ;
@@ -270,27 +278,44 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
270
278
proc .pyname = PLy_strdup ("__plpython_inline_block" );
271
279
proc .result .out .d .typoid = VOIDOID ;
272
280
281
+ /*
282
+ * Push execution context onto stack. It is important that this get
283
+ * popped again, so avoid putting anything that could throw error between
284
+ * here and the PG_TRY. (plpython_inline_error_callback doesn't currently
285
+ * need the stack entry, but for consistency with plpython_call_handler
286
+ * we do it in this order.)
287
+ */
288
+ exec_ctx = PLy_push_execution_context ();
289
+
290
+ /*
291
+ * Setup error traceback support for ereport()
292
+ */
293
+ plerrcontext .callback = plpython_inline_error_callback ;
294
+ plerrcontext .previous = error_context_stack ;
295
+ error_context_stack = & plerrcontext ;
296
+
273
297
PG_TRY ();
274
298
{
275
299
PLy_procedure_compile (& proc , codeblock -> source_text );
276
- PLy_curr_procedure = & proc ;
300
+ exec_ctx -> curr_proc = & proc ;
277
301
PLy_exec_function (& fake_fcinfo , & proc );
278
302
}
279
303
PG_CATCH ();
280
304
{
305
+ PLy_pop_execution_context ();
281
306
PLy_procedure_delete (& proc );
282
- PLy_curr_procedure = save_curr_proc ;
283
307
PyErr_Clear ();
284
308
PG_RE_THROW ();
285
309
}
286
310
PG_END_TRY ();
287
311
288
- PLy_procedure_delete (& proc );
289
-
290
312
/* Pop the error context stack */
291
313
error_context_stack = plerrcontext .previous ;
314
+ /* ... and then the execution context */
315
+ PLy_pop_execution_context ();
292
316
293
- PLy_curr_procedure = save_curr_proc ;
317
+ /* Now clean up the transient procedure we made */
318
+ PLy_procedure_delete (& proc );
294
319
295
320
PG_RETURN_VOID ();
296
321
}
@@ -313,13 +338,54 @@ static bool PLy_procedure_is_trigger(Form_pg_proc procStruct)
313
338
static void
314
339
plpython_error_callback (void * arg )
315
340
{
316
- if (PLy_curr_procedure )
341
+ PLyExecutionContext * exec_ctx = PLy_current_execution_context ();
342
+
343
+ if (exec_ctx -> curr_proc )
317
344
errcontext ("PL/Python function \"%s\"" ,
318
- PLy_procedure_name (PLy_curr_procedure ));
345
+ PLy_procedure_name (exec_ctx -> curr_proc ));
319
346
}
320
347
321
348
static void
322
349
plpython_inline_error_callback (void * arg )
323
350
{
324
351
errcontext ("PL/Python anonymous code block" );
325
352
}
353
+
354
+ PLyExecutionContext *
355
+ PLy_current_execution_context (void )
356
+ {
357
+ if (PLy_execution_contexts == NULL )
358
+ elog (ERROR , "no Python function is currently executing" );
359
+
360
+ return PLy_execution_contexts ;
361
+ }
362
+
363
+ static PLyExecutionContext *
364
+ PLy_push_execution_context (void )
365
+ {
366
+ PLyExecutionContext * context = PLy_malloc (sizeof (PLyExecutionContext ));
367
+
368
+ context -> curr_proc = NULL ;
369
+ context -> scratch_ctx = AllocSetContextCreate (TopTransactionContext ,
370
+ "PL/Python scratch context" ,
371
+ ALLOCSET_DEFAULT_MINSIZE ,
372
+ ALLOCSET_DEFAULT_INITSIZE ,
373
+ ALLOCSET_DEFAULT_MAXSIZE );
374
+ context -> next = PLy_execution_contexts ;
375
+ PLy_execution_contexts = context ;
376
+ return context ;
377
+ }
378
+
379
+ static void
380
+ PLy_pop_execution_context (void )
381
+ {
382
+ PLyExecutionContext * context = PLy_execution_contexts ;
383
+
384
+ if (context == NULL )
385
+ elog (ERROR , "no Python function is currently executing" );
386
+
387
+ PLy_execution_contexts = context -> next ;
388
+
389
+ MemoryContextDelete (context -> scratch_ctx );
390
+ PLy_free (context );
391
+ }
0 commit comments