Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * PL/Python main entry points
3 : : *
4 : : * src/pl/plpython/plpy_main.c
5 : : */
6 : :
7 : : #include "postgres.h"
8 : :
9 : : #include "catalog/pg_proc.h"
10 : : #include "commands/event_trigger.h"
11 : : #include "commands/trigger.h"
12 : : #include "executor/spi.h"
13 : : #include "funcapi.h"
14 : : #include "miscadmin.h"
15 : : #include "plpy_elog.h"
16 : : #include "plpy_exec.h"
17 : : #include "plpy_main.h"
18 : : #include "plpy_plpymodule.h"
19 : : #include "plpy_subxactobject.h"
20 : : #include "plpy_util.h"
21 : : #include "utils/builtins.h"
22 : : #include "utils/guc.h"
23 : : #include "utils/lsyscache.h"
24 : : #include "utils/memutils.h"
25 : : #include "utils/syscache.h"
26 : :
27 : : /*
28 : : * exported functions
29 : : */
30 : :
589 tgl@sss.pgh.pa.us 31 :CBC 23 : PG_MODULE_MAGIC_EXT(
32 : : .name = "plpython",
33 : : .version = PG_VERSION
34 : : );
35 : :
1704 andres@anarazel.de 36 : 26 : PG_FUNCTION_INFO_V1(plpython3_validator);
37 : 26 : PG_FUNCTION_INFO_V1(plpython3_call_handler);
38 : 8 : PG_FUNCTION_INFO_V1(plpython3_inline_handler);
39 : :
40 : :
41 : : static PLyTrigType PLy_procedure_is_trigger(Form_pg_proc procStruct);
42 : : static void plpython_error_callback(void *arg);
43 : : static void plpython_inline_error_callback(void *arg);
44 : :
45 : : static PLyExecutionContext *PLy_push_execution_context(bool atomic_context);
46 : : static void PLy_pop_execution_context(void);
47 : :
48 : : /* initialize global variables */
49 : : PyObject *PLy_interp_globals = NULL;
50 : :
51 : : /* this doesn't need to be global; use PLy_current_execution_context() */
52 : : static PLyExecutionContext *PLy_execution_contexts = NULL;
53 : :
54 : :
55 : : void
5436 peter_e@gmx.net 56 : 23 : _PG_init(void)
57 : : {
58 : : PyObject *main_mod;
59 : : PyObject *main_dict;
60 : : PyObject *GD;
61 : : PyObject *plpy_mod;
62 : :
294 peter@eisentraut.org 63 : 23 : pg_bindtextdomain(TEXTDOMAIN);
64 : :
65 : : /* Add plpy to table of built-in modules. */
5436 peter_e@gmx.net 66 : 23 : PyImport_AppendInittab("plpy", PyInit_plpy);
67 : :
68 : : /* Initialize Python interpreter. */
69 : 23 : Py_Initialize();
70 : :
294 peter@eisentraut.org 71 : 23 : main_mod = PyImport_AddModule("__main__");
72 [ + - - + ]: 23 : if (main_mod == NULL || PyErr_Occurred())
294 peter@eisentraut.org 73 :UBC 0 : PLy_elog(ERROR, "could not import \"%s\" module", "__main__");
74 : : Py_INCREF(main_mod);
75 : :
294 peter@eisentraut.org 76 :CBC 23 : main_dict = PyModule_GetDict(main_mod);
77 [ - + ]: 23 : if (main_dict == NULL)
294 peter@eisentraut.org 78 :UBC 0 : PLy_elog(ERROR, NULL);
79 : :
80 : : /*
81 : : * Set up GD.
82 : : */
294 peter@eisentraut.org 83 :CBC 23 : GD = PyDict_New();
84 [ - + ]: 23 : if (GD == NULL)
294 peter@eisentraut.org 85 :UBC 0 : PLy_elog(ERROR, NULL);
294 peter@eisentraut.org 86 :CBC 23 : PyDict_SetItemString(main_dict, "GD", GD);
87 : :
88 : : /*
89 : : * Import plpy.
90 : : */
91 : 23 : plpy_mod = PyImport_ImportModule("plpy");
92 [ - + ]: 23 : if (plpy_mod == NULL)
294 peter@eisentraut.org 93 :UBC 0 : PLy_elog(ERROR, "could not import \"%s\" module", "plpy");
294 peter@eisentraut.org 94 [ - + ]:CBC 23 : if (PyDict_SetItemString(main_dict, "plpy", plpy_mod) == -1)
294 peter@eisentraut.org 95 :UBC 0 : PLy_elog(ERROR, NULL);
96 : :
5436 peter_e@gmx.net 97 [ - + ]:CBC 23 : if (PyErr_Occurred())
5436 peter_e@gmx.net 98 :UBC 0 : PLy_elog(FATAL, "untrapped error in initialization");
99 : :
100 : : Py_INCREF(main_dict);
294 peter@eisentraut.org 101 :CBC 23 : PLy_interp_globals = main_dict;
102 : :
103 : : Py_DECREF(main_mod);
104 : :
5436 peter_e@gmx.net 105 : 23 : explicit_subtransactions = NIL;
106 : :
5350 tgl@sss.pgh.pa.us 107 : 23 : PLy_execution_contexts = NULL;
5436 peter_e@gmx.net 108 : 23 : }
109 : :
110 : : Datum
1704 andres@anarazel.de 111 : 272 : plpython3_validator(PG_FUNCTION_ARGS)
112 : : {
137 tgl@sss.pgh.pa.us 113 : 272 : LOCAL_FCINFO(fake_fcinfo, 0);
5436 peter_e@gmx.net 114 : 272 : Oid funcoid = PG_GETARG_OID(0);
115 : : HeapTuple tuple;
116 : : Form_pg_proc procStruct;
117 : : Oid rettype;
118 : : Oid *argtypes;
119 : : char **argnames,
120 : : *argmodes;
121 : : int nargs;
122 : : PLyTrigType is_trigger;
123 : : TriggerData trigdata;
124 : : EventTriggerData etrigdata;
125 : : FmgrInfo flinfo;
126 : : PLyProcedureCache *pcache;
127 : :
4644 noah@leadboat.com 128 [ - + ]: 272 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
4644 noah@leadboat.com 129 :UBC 0 : PG_RETURN_VOID();
130 : :
131 : : /* Get the new function's pg_proc entry */
5436 peter_e@gmx.net 132 :CBC 272 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
133 [ - + ]: 272 : if (!HeapTupleIsValid(tuple))
5436 peter_e@gmx.net 134 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
5436 peter_e@gmx.net 135 :CBC 272 : procStruct = (Form_pg_proc) GETSTRUCT(tuple);
136 : :
137 : : /* Disallow pseudotype result, except for void or record */
4 tgl@sss.pgh.pa.us 138 :GNC 272 : rettype = procStruct->prorettype;
139 [ + + ]: 272 : if (get_typtype(rettype) == TYPTYPE_PSEUDO)
140 : : {
141 [ + + + + ]: 83 : if (rettype == VOIDOID || rettype == RECORDOID)
142 : : /* okay */ ;
143 [ + + + + ]: 26 : else if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
144 : : /* okay */ ;
145 : : else
146 [ + - ]: 1 : ereport(ERROR,
147 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
148 : : errmsg("PL/Python functions cannot return type %s",
149 : : format_type_be(rettype))));
150 : : }
151 : :
152 : : /* Disallow pseudotype arguments, except for output records */
153 : 271 : nargs = get_func_arg_info(tuple, &argtypes, &argnames, &argmodes);
154 [ + + ]: 462 : for (int i = 0; i < nargs; i++)
155 : : {
156 [ + + ]: 192 : if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
157 : : {
158 [ + + ]: 3 : if (argmodes &&
159 [ - + ]: 2 : (argmodes[i] == PROARGMODE_OUT ||
4 tgl@sss.pgh.pa.us 160 [ # # ]:UNC 0 : argmodes[i] == PROARGMODE_TABLE))
161 : : {
4 tgl@sss.pgh.pa.us 162 [ - + ]:GNC 2 : if (rettype == RECORDOID)
163 : : /* okay */ ;
164 : : else
4 tgl@sss.pgh.pa.us 165 [ # # ]:UNC 0 : ereport(ERROR,
166 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
167 : : errmsg("PL/Python functions cannot return type %s",
168 : : format_type_be(argtypes[i]))));
169 : : }
170 : : else
4 tgl@sss.pgh.pa.us 171 [ + - ]:GNC 1 : ereport(ERROR,
172 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
173 : : errmsg("PL/Python functions cannot accept type %s",
174 : : format_type_be(argtypes[i]))));
175 : : }
176 : : }
177 : :
5436 peter_e@gmx.net 178 :CBC 270 : is_trigger = PLy_procedure_is_trigger(procStruct);
179 : :
180 : 270 : ReleaseSysCache(tuple);
181 : :
182 : : /* The rest is not needed/wanted if check_function_bodies is off */
4 tgl@sss.pgh.pa.us 183 [ + + ]:GNC 270 : if (!check_function_bodies)
184 : 1 : PG_RETURN_VOID();
185 : :
186 : : /*
187 : : * Set up a fake flinfo/fcinfo with just enough info to satisfy
188 : : * PLy_procedure_get(). That function derives the call context (plain
189 : : * function, DML trigger, or event trigger) from the fcinfo, so we have to
190 : : * construct matching context here.
191 : : */
137 tgl@sss.pgh.pa.us 192 [ + - + - :CBC 1345 : MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
+ - + - +
+ ]
193 [ + - + - : 1883 : MemSet(&flinfo, 0, sizeof(flinfo));
+ - + - +
+ ]
194 : 269 : fake_fcinfo->flinfo = &flinfo;
195 : 269 : flinfo.fn_oid = funcoid;
196 : 269 : flinfo.fn_mcxt = CurrentMemoryContext;
197 : :
198 [ + + ]: 269 : if (is_trigger == PLPY_TRIGGER)
199 : : {
200 [ + - + - : 264 : MemSet(&trigdata, 0, sizeof(trigdata));
+ - + - +
+ ]
201 : 24 : trigdata.type = T_TriggerData;
202 : : /* We can't validate triggers against any particular table ... */
203 : 24 : fake_fcinfo->context = (Node *) &trigdata;
204 : : }
205 [ + + ]: 245 : else if (is_trigger == PLPY_EVENT_TRIGGER)
206 : : {
207 [ + - + - : 5 : MemSet(&etrigdata, 0, sizeof(etrigdata));
+ - + - +
+ ]
208 : 1 : etrigdata.type = T_EventTriggerData;
209 : 1 : fake_fcinfo->context = (Node *) &etrigdata;
210 : : }
211 : :
212 : 269 : pcache = PLy_procedure_get(fake_fcinfo, true);
213 : :
214 : : /*
215 : : * Release the reference count that PLy_procedure_get acquired; the
216 : : * PLyProcedure object remains valid for possible future use. (We could
217 : : * leave this to be done when the calling memory context is cleaned up,
218 : : * but it seems neater to do it right away. Note we mustn't release the
219 : : * pcache object, since the memory-context reset callback has a reference
220 : : * to it.)
221 : : */
222 [ - + ]: 268 : Assert(pcache->proc->cfunc.use_count > 0);
223 : 268 : pcache->proc->cfunc.use_count--;
224 : 268 : pcache->proc = NULL;
225 : :
5436 peter_e@gmx.net 226 : 268 : PG_RETURN_VOID();
227 : : }
228 : :
229 : : Datum
1704 andres@anarazel.de 230 : 731 : plpython3_call_handler(PG_FUNCTION_ARGS)
231 : : {
232 : : bool nonatomic;
233 : : Datum retval;
234 : : PLyExecutionContext *exec_ctx;
235 : : ErrorContextCallback plerrcontext;
236 : :
3209 peter_e@gmx.net 237 : 1529 : nonatomic = fcinfo->context &&
238 [ + + + + ]: 739 : IsA(fcinfo->context, CallContext) &&
239 [ + + ]: 8 : !castNode(CallContext, fcinfo->context)->atomic;
240 : :
241 : : /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
787 tgl@sss.pgh.pa.us 242 : 731 : SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
243 : :
244 : : /*
245 : : * Push execution context onto stack. It is important that this get
246 : : * popped again, so avoid putting anything that could throw error between
247 : : * here and the PG_TRY.
248 : : */
3209 peter_e@gmx.net 249 : 731 : exec_ctx = PLy_push_execution_context(!nonatomic);
250 : :
5436 251 [ + + ]: 731 : PG_TRY();
252 : : {
253 : : PLyProcedureCache *pcache;
254 : :
255 : : /*
256 : : * Setup error traceback support for ereport(). Note that the PG_TRY
257 : : * structure pops this for us again at exit, so we needn't do that
258 : : * explicitly, nor do we risk the callback getting called after we've
259 : : * destroyed the exec_ctx.
260 : : */
3186 tgl@sss.pgh.pa.us 261 : 731 : plerrcontext.callback = plpython_error_callback;
262 : 731 : plerrcontext.arg = exec_ctx;
263 : 731 : plerrcontext.previous = error_context_stack;
264 : 731 : error_context_stack = &plerrcontext;
265 : :
266 : : /*
267 : : * Look up (and if necessary compile) the procedure. This can throw
268 : : * an error, so it must happen inside the PG_TRY so that the execution
269 : : * context gets popped on the way out.
270 : : */
137 271 : 731 : pcache = PLy_procedure_get(fcinfo, false);
272 : 728 : exec_ctx->curr_proc = pcache->proc;
273 : :
5436 peter_e@gmx.net 274 [ + + + + ]: 728 : if (CALLED_AS_TRIGGER(fcinfo))
275 : 40 : {
276 : : HeapTuple trv;
277 : :
137 tgl@sss.pgh.pa.us 278 : 49 : trv = PLy_exec_trigger(fcinfo, pcache->proc);
5436 peter_e@gmx.net 279 : 40 : retval = PointerGetDatum(trv);
280 : : }
441 peter@eisentraut.org 281 [ + + + + ]: 679 : else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
282 : : {
137 tgl@sss.pgh.pa.us 283 : 10 : PLy_exec_event_trigger(fcinfo, pcache->proc);
441 peter@eisentraut.org 284 : 10 : retval = (Datum) 0;
285 : : }
286 : : else
137 tgl@sss.pgh.pa.us 287 : 669 : retval = PLy_exec_function(fcinfo, pcache);
288 : : }
5436 peter_e@gmx.net 289 : 108 : PG_CATCH();
290 : : {
291 : : /* Destroy the execution context */
5350 tgl@sss.pgh.pa.us 292 : 108 : PLy_pop_execution_context();
5436 peter_e@gmx.net 293 : 108 : PyErr_Clear();
294 : :
295 : 108 : PG_RE_THROW();
296 : : }
297 [ - + ]: 623 : PG_END_TRY();
298 : :
299 : : /* Destroy the execution context */
5350 tgl@sss.pgh.pa.us 300 : 623 : PLy_pop_execution_context();
301 : :
5436 peter_e@gmx.net 302 : 623 : return retval;
303 : : }
304 : :
305 : : Datum
1704 andres@anarazel.de 306 : 21 : plpython3_inline_handler(PG_FUNCTION_ARGS)
307 : : {
2840 308 : 21 : LOCAL_FCINFO(fake_fcinfo, 0);
5436 peter_e@gmx.net 309 : 21 : InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
310 : : FmgrInfo flinfo;
311 : : PLyProcedure proc;
312 : : PLyProcedureCache pcache;
313 : : PLyExecutionContext *exec_ctx;
314 : : ErrorContextCallback plerrcontext;
315 : :
316 : : /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
787 tgl@sss.pgh.pa.us 317 : 21 : SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
318 : :
2840 andres@anarazel.de 319 [ + - + - : 105 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
+ - + - +
+ ]
5436 peter_e@gmx.net 320 [ + - + - : 147 : MemSet(&flinfo, 0, sizeof(flinfo));
+ - + - +
+ ]
2840 andres@anarazel.de 321 : 21 : fake_fcinfo->flinfo = &flinfo;
5436 peter_e@gmx.net 322 : 21 : flinfo.fn_oid = InvalidOid;
323 : 21 : flinfo.fn_mcxt = CurrentMemoryContext;
324 : :
325 [ + - + - : 945 : MemSet(&proc, 0, sizeof(PLyProcedure));
+ - + - +
+ ]
4018 tgl@sss.pgh.pa.us 326 : 21 : proc.mcxt = AllocSetContextCreate(TopMemoryContext,
327 : : "__plpython_inline_block",
328 : : ALLOCSET_DEFAULT_SIZES);
329 : 21 : proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
4211 peter_e@gmx.net 330 : 21 : proc.langid = codeblock->langOid;
331 : :
332 : : /*
333 : : * This is currently sufficient to get PLy_exec_function to work, but
334 : : * someday we might need to be honest and use PLy_output_setup_func.
335 : : */
3276 tgl@sss.pgh.pa.us 336 : 21 : proc.result.typoid = VOIDOID;
337 : :
338 : : /* Set up a minimal PLyProcedureCache for the inline block */
137 339 [ + - + - : 168 : MemSet(&pcache, 0, sizeof(PLyProcedureCache));
+ - + - +
+ ]
340 : 21 : pcache.proc = &proc;
341 : 21 : pcache.fcontext = CurrentMemoryContext;
342 : :
343 : : /*
344 : : * Push execution context onto stack. It is important that this get
345 : : * popped again, so avoid putting anything that could throw error between
346 : : * here and the PG_TRY.
347 : : */
3209 peter_e@gmx.net 348 : 21 : exec_ctx = PLy_push_execution_context(codeblock->atomic);
349 : :
5436 350 [ + + ]: 21 : PG_TRY();
351 : : {
352 : : /*
353 : : * Setup error traceback support for ereport().
354 : : * plpython_inline_error_callback doesn't currently need exec_ctx, but
355 : : * for consistency with plpython3_call_handler we do it the same way.
356 : : */
3186 tgl@sss.pgh.pa.us 357 : 21 : plerrcontext.callback = plpython_inline_error_callback;
358 : 21 : plerrcontext.arg = exec_ctx;
359 : 21 : plerrcontext.previous = error_context_stack;
360 : 21 : error_context_stack = &plerrcontext;
361 : :
5436 peter_e@gmx.net 362 : 21 : PLy_procedure_compile(&proc, codeblock->source_text);
5350 tgl@sss.pgh.pa.us 363 : 21 : exec_ctx->curr_proc = &proc;
137 364 : 21 : PLy_exec_function(fake_fcinfo, &pcache);
365 : : }
5436 peter_e@gmx.net 366 : 11 : PG_CATCH();
367 : : {
5350 tgl@sss.pgh.pa.us 368 : 11 : PLy_pop_execution_context();
5436 peter_e@gmx.net 369 : 11 : PLy_procedure_delete(&proc);
370 : 11 : PyErr_Clear();
371 : 11 : PG_RE_THROW();
372 : : }
373 [ - + ]: 10 : PG_END_TRY();
374 : :
375 : : /* Destroy the execution context */
5350 tgl@sss.pgh.pa.us 376 : 10 : PLy_pop_execution_context();
377 : :
378 : : /* Now clean up the transient procedure we made */
379 : 10 : PLy_procedure_delete(&proc);
380 : :
5436 peter_e@gmx.net 381 : 10 : PG_RETURN_VOID();
382 : : }
383 : :
384 : : /*
385 : : * Determine whether a function is a (DML or event) trigger from its pg_proc
386 : : * result type. This is used by the validator, which has no call context to
387 : : * inspect; the call handler instead relies on the fcinfo's call context.
388 : : */
389 : : static PLyTrigType
5261 bruce@momjian.us 390 : 270 : PLy_procedure_is_trigger(Form_pg_proc procStruct)
391 : : {
392 : : PLyTrigType ret;
393 : :
441 peter@eisentraut.org 394 [ + + + ]: 270 : switch (procStruct->prorettype)
395 : : {
396 : 24 : case TRIGGEROID:
397 : 24 : ret = PLPY_TRIGGER;
398 : 24 : break;
399 : 1 : case EVENT_TRIGGEROID:
400 : 1 : ret = PLPY_EVENT_TRIGGER;
401 : 1 : break;
402 : 245 : default:
403 : 245 : ret = PLPY_NOT_TRIGGER;
404 : 245 : break;
405 : : }
406 : :
407 : 270 : return ret;
408 : : }
409 : :
410 : : static void
5436 peter_e@gmx.net 411 : 484 : plpython_error_callback(void *arg)
412 : : {
3186 tgl@sss.pgh.pa.us 413 : 484 : PLyExecutionContext *exec_ctx = (PLyExecutionContext *) arg;
414 : :
5350 415 [ + + ]: 484 : if (exec_ctx->curr_proc)
416 : : {
3262 peter_e@gmx.net 417 [ + + ]: 481 : if (exec_ctx->curr_proc->is_procedure)
418 : 4 : errcontext("PL/Python procedure \"%s\"",
419 : : PLy_procedure_name(exec_ctx->curr_proc));
420 : : else
421 : 477 : errcontext("PL/Python function \"%s\"",
422 : : PLy_procedure_name(exec_ctx->curr_proc));
423 : : }
5436 424 : 484 : }
425 : :
426 : : static void
427 : 28 : plpython_inline_error_callback(void *arg)
428 : : {
429 : 28 : errcontext("PL/Python anonymous code block");
430 : 28 : }
431 : :
432 : : PLyExecutionContext *
5350 tgl@sss.pgh.pa.us 433 : 1466 : PLy_current_execution_context(void)
434 : : {
435 [ - + ]: 1466 : if (PLy_execution_contexts == NULL)
5350 tgl@sss.pgh.pa.us 436 [ # # ]:UBC 0 : elog(ERROR, "no Python function is currently executing");
437 : :
5350 tgl@sss.pgh.pa.us 438 :CBC 1466 : return PLy_execution_contexts;
439 : : }
440 : :
441 : : MemoryContext
4018 442 : 804 : PLy_get_scratch_context(PLyExecutionContext *context)
443 : : {
444 : : /*
445 : : * A scratch context might never be needed in a given plpython procedure,
446 : : * so allocate it on first request.
447 : : */
448 [ + + ]: 804 : if (context->scratch_ctx == NULL)
449 : 447 : context->scratch_ctx =
450 : 447 : AllocSetContextCreate(TopTransactionContext,
451 : : "PL/Python scratch context",
452 : : ALLOCSET_DEFAULT_SIZES);
453 : 804 : return context->scratch_ctx;
454 : : }
455 : :
456 : : static PLyExecutionContext *
3209 peter_e@gmx.net 457 : 752 : PLy_push_execution_context(bool atomic_context)
458 : : {
459 : : PLyExecutionContext *context;
460 : :
461 : : /* Pick a memory context similar to what SPI uses. */
462 : : context = (PLyExecutionContext *)
463 [ + + ]: 752 : MemoryContextAlloc(atomic_context ? TopTransactionContext : PortalContext,
464 : : sizeof(PLyExecutionContext));
5350 tgl@sss.pgh.pa.us 465 : 752 : context->curr_proc = NULL;
4018 466 : 752 : context->scratch_ctx = NULL;
5350 467 : 752 : context->next = PLy_execution_contexts;
468 : 752 : PLy_execution_contexts = context;
469 : 752 : return context;
470 : : }
471 : :
472 : : static void
473 : 752 : PLy_pop_execution_context(void)
474 : : {
5261 bruce@momjian.us 475 : 752 : PLyExecutionContext *context = PLy_execution_contexts;
476 : :
5350 tgl@sss.pgh.pa.us 477 [ - + ]: 752 : if (context == NULL)
5350 tgl@sss.pgh.pa.us 478 [ # # ]:UBC 0 : elog(ERROR, "no Python function is currently executing");
479 : :
5350 tgl@sss.pgh.pa.us 480 :CBC 752 : PLy_execution_contexts = context->next;
481 : :
4018 482 [ + + ]: 752 : if (context->scratch_ctx)
483 : 430 : MemoryContextDelete(context->scratch_ctx);
484 : 752 : pfree(context);
5350 485 : 752 : }
|