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