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 "access/htup_details.h"
10 : : #include "catalog/pg_proc.h"
11 : : #include "catalog/pg_type.h"
12 : : #include "commands/event_trigger.h"
13 : : #include "commands/trigger.h"
14 : : #include "executor/spi.h"
15 : : #include "miscadmin.h"
16 : : #include "plpy_elog.h"
17 : : #include "plpy_exec.h"
18 : : #include "plpy_main.h"
19 : : #include "plpy_plpymodule.h"
20 : : #include "plpy_procedure.h"
21 : : #include "plpy_subxactobject.h"
22 : : #include "plpy_util.h"
23 : : #include "utils/guc.h"
24 : : #include "utils/memutils.h"
25 : : #include "utils/rel.h"
26 : : #include "utils/syscache.h"
27 : :
28 : : /*
29 : : * exported functions
30 : : */
31 : :
164 tgl@sss.pgh.pa.us 32 :CBC 23 : PG_MODULE_MAGIC_EXT(
33 : : .name = "plpython",
34 : : .version = PG_VERSION
35 : : );
36 : :
1279 andres@anarazel.de 37 : 26 : PG_FUNCTION_INFO_V1(plpython3_validator);
38 : 26 : PG_FUNCTION_INFO_V1(plpython3_call_handler);
39 : 8 : PG_FUNCTION_INFO_V1(plpython3_inline_handler);
40 : :
41 : :
42 : : static PLyTrigType PLy_procedure_is_trigger(Form_pg_proc procStruct);
43 : : static void plpython_error_callback(void *arg);
44 : : static void plpython_inline_error_callback(void *arg);
45 : : static void PLy_init_interp(void);
46 : :
47 : : static PLyExecutionContext *PLy_push_execution_context(bool atomic_context);
48 : : static void PLy_pop_execution_context(void);
49 : :
50 : : /* static state for Python library conflict detection */
51 : : static int *plpython_version_bitmask_ptr = NULL;
52 : : static int plpython_version_bitmask = 0;
53 : :
54 : : /* initialize global variables */
55 : : PyObject *PLy_interp_globals = NULL;
56 : :
57 : : /* this doesn't need to be global; use PLy_current_execution_context() */
58 : : static PLyExecutionContext *PLy_execution_contexts = NULL;
59 : :
60 : :
61 : : void
5011 peter_e@gmx.net 62 : 23 : _PG_init(void)
63 : : {
64 : : int **bitmask_ptr;
65 : :
66 : : /*
67 : : * Set up a shared bitmask variable telling which Python version(s) are
68 : : * loaded into this process's address space. If there's more than one, we
69 : : * cannot call into libpython for fear of causing crashes. But postpone
70 : : * the actual failure for later, so that operations like pg_restore can
71 : : * load more than one plpython library so long as they don't try to do
72 : : * anything much with the language.
73 : : *
74 : : * While we only support Python 3 these days, somebody might create an
75 : : * out-of-tree version adding back support for Python 2. Conflicts with
76 : : * such an extension should be detected.
77 : : */
3526 tgl@sss.pgh.pa.us 78 : 23 : bitmask_ptr = (int **) find_rendezvous_variable("plpython_version_bitmask");
79 [ + - ]: 23 : if (!(*bitmask_ptr)) /* am I the first? */
80 : 23 : *bitmask_ptr = &plpython_version_bitmask;
81 : : /* Retain pointer to the agreed-on shared variable ... */
82 : 23 : plpython_version_bitmask_ptr = *bitmask_ptr;
83 : : /* ... and announce my presence */
84 : 23 : *plpython_version_bitmask_ptr |= (1 << PY_MAJOR_VERSION);
85 : :
86 : : /*
87 : : * This should be safe even in the presence of conflicting plpythons, and
88 : : * it's necessary to do it before possibly throwing a conflict error, or
89 : : * the error message won't get localized.
90 : : */
91 : 23 : pg_bindtextdomain(TEXTDOMAIN);
92 : 23 : }
93 : :
94 : : /*
95 : : * Perform one-time setup of PL/Python, after checking for a conflict
96 : : * with other versions of Python.
97 : : */
98 : : static void
99 : 972 : PLy_initialize(void)
100 : : {
101 : : static bool inited = false;
102 : :
103 : : /*
104 : : * Check for multiple Python libraries before actively doing anything with
105 : : * libpython. This must be repeated on each entry to PL/Python, in case a
106 : : * conflicting library got loaded since we last looked.
107 : : *
108 : : * It is attractive to weaken this error from FATAL to ERROR, but there
109 : : * would be corner cases, so it seems best to be conservative.
110 : : */
111 [ - + ]: 972 : if (*plpython_version_bitmask_ptr != (1 << PY_MAJOR_VERSION))
3526 tgl@sss.pgh.pa.us 112 [ # # ]:UBC 0 : ereport(FATAL,
113 : : (errmsg("multiple Python libraries are present in session"),
114 : : errdetail("Only one Python major version can be used in one session.")));
115 : :
116 : : /* The rest should only be done once per session */
3526 tgl@sss.pgh.pa.us 117 [ + + ]:CBC 972 : if (inited)
118 : 949 : return;
119 : :
5011 peter_e@gmx.net 120 : 23 : PyImport_AppendInittab("plpy", PyInit_plpy);
121 : 23 : Py_Initialize();
122 : 23 : PyImport_ImportModule("plpy");
123 : 23 : PLy_init_interp();
124 : 23 : PLy_init_plpy();
125 [ - + ]: 23 : if (PyErr_Occurred())
5011 peter_e@gmx.net 126 :UBC 0 : PLy_elog(FATAL, "untrapped error in initialization");
127 : :
5011 peter_e@gmx.net 128 :CBC 23 : init_procedure_caches();
129 : :
130 : 23 : explicit_subtransactions = NIL;
131 : :
4925 tgl@sss.pgh.pa.us 132 : 23 : PLy_execution_contexts = NULL;
133 : :
5011 peter_e@gmx.net 134 : 23 : inited = true;
135 : : }
136 : :
137 : : /*
138 : : * This should be called only once, from PLy_initialize. Initialize the Python
139 : : * interpreter and global data.
140 : : */
141 : : static void
142 : 23 : PLy_init_interp(void)
143 : : {
144 : : static PyObject *PLy_interp_safe_globals = NULL;
145 : : PyObject *mainmod;
146 : :
147 : 23 : mainmod = PyImport_AddModule("__main__");
148 [ + - - + ]: 23 : if (mainmod == NULL || PyErr_Occurred())
5011 peter_e@gmx.net 149 :UBC 0 : PLy_elog(ERROR, "could not import \"__main__\" module");
150 : : Py_INCREF(mainmod);
5011 peter_e@gmx.net 151 :CBC 23 : PLy_interp_globals = PyModule_GetDict(mainmod);
152 : 23 : PLy_interp_safe_globals = PyDict_New();
4925 tgl@sss.pgh.pa.us 153 [ - + ]: 23 : if (PLy_interp_safe_globals == NULL)
2867 peter_e@gmx.net 154 :UBC 0 : PLy_elog(ERROR, NULL);
5011 peter_e@gmx.net 155 :CBC 23 : PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
156 : : Py_DECREF(mainmod);
157 [ + - - + ]: 23 : if (PLy_interp_globals == NULL || PyErr_Occurred())
5011 peter_e@gmx.net 158 :UBC 0 : PLy_elog(ERROR, "could not initialize globals");
5011 peter_e@gmx.net 159 :CBC 23 : }
160 : :
161 : : Datum
1279 andres@anarazel.de 162 : 251 : plpython3_validator(PG_FUNCTION_ARGS)
163 : : {
5011 peter_e@gmx.net 164 : 251 : Oid funcoid = PG_GETARG_OID(0);
165 : : HeapTuple tuple;
166 : : Form_pg_proc procStruct;
167 : : PLyTrigType is_trigger;
168 : :
4219 noah@leadboat.com 169 [ - + ]: 251 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
4219 noah@leadboat.com 170 :UBC 0 : PG_RETURN_VOID();
171 : :
5011 peter_e@gmx.net 172 [ + + ]:CBC 251 : if (!check_function_bodies)
173 : 1 : PG_RETURN_VOID();
174 : :
175 : : /* Do this only after making sure we need to do something */
3526 tgl@sss.pgh.pa.us 176 : 250 : PLy_initialize();
177 : :
178 : : /* Get the new function's pg_proc entry */
5011 peter_e@gmx.net 179 : 250 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
180 [ - + ]: 250 : if (!HeapTupleIsValid(tuple))
5011 peter_e@gmx.net 181 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
5011 peter_e@gmx.net 182 :CBC 250 : procStruct = (Form_pg_proc) GETSTRUCT(tuple);
183 : :
184 : 250 : is_trigger = PLy_procedure_is_trigger(procStruct);
185 : :
186 : 250 : ReleaseSysCache(tuple);
187 : :
188 : : /* We can't validate triggers against any particular table ... */
4607 tgl@sss.pgh.pa.us 189 : 250 : PLy_procedure_get(funcoid, InvalidOid, is_trigger);
190 : :
5011 peter_e@gmx.net 191 : 249 : PG_RETURN_VOID();
192 : : }
193 : :
194 : : Datum
1279 andres@anarazel.de 195 : 701 : plpython3_call_handler(PG_FUNCTION_ARGS)
196 : : {
197 : : bool nonatomic;
198 : : Datum retval;
199 : : PLyExecutionContext *exec_ctx;
200 : : ErrorContextCallback plerrcontext;
201 : :
3526 tgl@sss.pgh.pa.us 202 : 701 : PLy_initialize();
203 : :
2784 peter_e@gmx.net 204 : 1469 : nonatomic = fcinfo->context &&
205 [ + + + + ]: 709 : IsA(fcinfo->context, CallContext) &&
206 [ + + ]: 8 : !castNode(CallContext, fcinfo->context)->atomic;
207 : :
208 : : /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
362 tgl@sss.pgh.pa.us 209 : 701 : SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
210 : :
211 : : /*
212 : : * Push execution context onto stack. It is important that this get
213 : : * popped again, so avoid putting anything that could throw error between
214 : : * here and the PG_TRY.
215 : : */
2784 peter_e@gmx.net 216 : 701 : exec_ctx = PLy_push_execution_context(!nonatomic);
217 : :
5011 218 [ + + ]: 701 : PG_TRY();
219 : : {
4607 tgl@sss.pgh.pa.us 220 : 701 : Oid funcoid = fcinfo->flinfo->fn_oid;
221 : : PLyProcedure *proc;
222 : :
223 : : /*
224 : : * Setup error traceback support for ereport(). Note that the PG_TRY
225 : : * structure pops this for us again at exit, so we needn't do that
226 : : * explicitly, nor do we risk the callback getting called after we've
227 : : * destroyed the exec_ctx.
228 : : */
2761 229 : 701 : plerrcontext.callback = plpython_error_callback;
230 : 701 : plerrcontext.arg = exec_ctx;
231 : 701 : plerrcontext.previous = error_context_stack;
232 : 701 : error_context_stack = &plerrcontext;
233 : :
5011 peter_e@gmx.net 234 [ + + + + ]: 701 : if (CALLED_AS_TRIGGER(fcinfo))
235 : 40 : {
4607 tgl@sss.pgh.pa.us 236 : 49 : Relation tgrel = ((TriggerData *) fcinfo->context)->tg_relation;
237 : : HeapTuple trv;
238 : :
16 peter@eisentraut.org 239 :GNC 49 : proc = PLy_procedure_get(funcoid, RelationGetRelid(tgrel), PLPY_TRIGGER);
4925 tgl@sss.pgh.pa.us 240 :CBC 49 : exec_ctx->curr_proc = proc;
5011 peter_e@gmx.net 241 : 49 : trv = PLy_exec_trigger(fcinfo, proc);
242 : 40 : retval = PointerGetDatum(trv);
243 : : }
16 peter@eisentraut.org 244 [ + + + + ]:GNC 652 : else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
245 : : {
246 : 10 : proc = PLy_procedure_get(funcoid, InvalidOid, PLPY_EVENT_TRIGGER);
247 : 10 : exec_ctx->curr_proc = proc;
248 : 10 : PLy_exec_event_trigger(fcinfo, proc);
249 : 10 : retval = (Datum) 0;
250 : : }
251 : : else
252 : : {
253 : 642 : proc = PLy_procedure_get(funcoid, InvalidOid, PLPY_NOT_TRIGGER);
4925 tgl@sss.pgh.pa.us 254 :CBC 639 : exec_ctx->curr_proc = proc;
5011 peter_e@gmx.net 255 : 639 : retval = PLy_exec_function(fcinfo, proc);
256 : : }
257 : : }
258 : 93 : PG_CATCH();
259 : : {
4925 tgl@sss.pgh.pa.us 260 : 93 : PLy_pop_execution_context();
5011 peter_e@gmx.net 261 : 93 : PyErr_Clear();
262 : 93 : PG_RE_THROW();
263 : : }
264 [ - + ]: 608 : PG_END_TRY();
265 : :
266 : : /* Destroy the execution context */
4925 tgl@sss.pgh.pa.us 267 : 608 : PLy_pop_execution_context();
268 : :
5011 peter_e@gmx.net 269 : 608 : return retval;
270 : : }
271 : :
272 : : Datum
1279 andres@anarazel.de 273 : 21 : plpython3_inline_handler(PG_FUNCTION_ARGS)
274 : : {
2415 275 : 21 : LOCAL_FCINFO(fake_fcinfo, 0);
5011 peter_e@gmx.net 276 : 21 : InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
277 : : FmgrInfo flinfo;
278 : : PLyProcedure proc;
279 : : PLyExecutionContext *exec_ctx;
280 : : ErrorContextCallback plerrcontext;
281 : :
3526 tgl@sss.pgh.pa.us 282 : 21 : PLy_initialize();
283 : :
284 : : /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
362 285 : 21 : SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
286 : :
2415 andres@anarazel.de 287 [ + - + - : 105 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
+ - + - +
+ ]
5011 peter_e@gmx.net 288 [ + - + - : 147 : MemSet(&flinfo, 0, sizeof(flinfo));
+ - + - +
+ ]
2415 andres@anarazel.de 289 : 21 : fake_fcinfo->flinfo = &flinfo;
5011 peter_e@gmx.net 290 : 21 : flinfo.fn_oid = InvalidOid;
291 : 21 : flinfo.fn_mcxt = CurrentMemoryContext;
292 : :
293 [ + - + - : 882 : MemSet(&proc, 0, sizeof(PLyProcedure));
+ - + - +
+ ]
3593 tgl@sss.pgh.pa.us 294 : 21 : proc.mcxt = AllocSetContextCreate(TopMemoryContext,
295 : : "__plpython_inline_block",
296 : : ALLOCSET_DEFAULT_SIZES);
297 : 21 : proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
3786 peter_e@gmx.net 298 : 21 : proc.langid = codeblock->langOid;
299 : :
300 : : /*
301 : : * This is currently sufficient to get PLy_exec_function to work, but
302 : : * someday we might need to be honest and use PLy_output_setup_func.
303 : : */
2851 tgl@sss.pgh.pa.us 304 : 21 : proc.result.typoid = VOIDOID;
305 : :
306 : : /*
307 : : * Push execution context onto stack. It is important that this get
308 : : * popped again, so avoid putting anything that could throw error between
309 : : * here and the PG_TRY.
310 : : */
2784 peter_e@gmx.net 311 : 21 : exec_ctx = PLy_push_execution_context(codeblock->atomic);
312 : :
5011 313 [ + + ]: 21 : PG_TRY();
314 : : {
315 : : /*
316 : : * Setup error traceback support for ereport().
317 : : * plpython_inline_error_callback doesn't currently need exec_ctx, but
318 : : * for consistency with plpython3_call_handler we do it the same way.
319 : : */
2761 tgl@sss.pgh.pa.us 320 : 21 : plerrcontext.callback = plpython_inline_error_callback;
321 : 21 : plerrcontext.arg = exec_ctx;
322 : 21 : plerrcontext.previous = error_context_stack;
323 : 21 : error_context_stack = &plerrcontext;
324 : :
5011 peter_e@gmx.net 325 : 21 : PLy_procedure_compile(&proc, codeblock->source_text);
4925 tgl@sss.pgh.pa.us 326 : 21 : exec_ctx->curr_proc = &proc;
2415 andres@anarazel.de 327 : 21 : PLy_exec_function(fake_fcinfo, &proc);
328 : : }
5011 peter_e@gmx.net 329 : 11 : PG_CATCH();
330 : : {
4925 tgl@sss.pgh.pa.us 331 : 11 : PLy_pop_execution_context();
5011 peter_e@gmx.net 332 : 11 : PLy_procedure_delete(&proc);
333 : 11 : PyErr_Clear();
334 : 11 : PG_RE_THROW();
335 : : }
336 [ - + ]: 10 : PG_END_TRY();
337 : :
338 : : /* Destroy the execution context */
4925 tgl@sss.pgh.pa.us 339 : 10 : PLy_pop_execution_context();
340 : :
341 : : /* Now clean up the transient procedure we made */
342 : 10 : PLy_procedure_delete(&proc);
343 : :
5011 peter_e@gmx.net 344 : 10 : PG_RETURN_VOID();
345 : : }
346 : :
347 : : static PLyTrigType
4836 bruce@momjian.us 348 : 250 : PLy_procedure_is_trigger(Form_pg_proc procStruct)
349 : : {
350 : : PLyTrigType ret;
351 : :
16 peter@eisentraut.org 352 [ + + + ]:GNC 250 : switch (procStruct->prorettype)
353 : : {
354 : 24 : case TRIGGEROID:
355 : 24 : ret = PLPY_TRIGGER;
356 : 24 : break;
357 : 1 : case EVENT_TRIGGEROID:
358 : 1 : ret = PLPY_EVENT_TRIGGER;
359 : 1 : break;
360 : 225 : default:
361 : 225 : ret = PLPY_NOT_TRIGGER;
362 : 225 : break;
363 : : }
364 : :
365 : 250 : return ret;
366 : : }
367 : :
368 : : static void
5011 peter_e@gmx.net 369 :CBC 466 : plpython_error_callback(void *arg)
370 : : {
2761 tgl@sss.pgh.pa.us 371 : 466 : PLyExecutionContext *exec_ctx = (PLyExecutionContext *) arg;
372 : :
4925 373 [ + + ]: 466 : if (exec_ctx->curr_proc)
374 : : {
2837 peter_e@gmx.net 375 [ + + ]: 463 : if (exec_ctx->curr_proc->is_procedure)
376 : 4 : errcontext("PL/Python procedure \"%s\"",
377 : : PLy_procedure_name(exec_ctx->curr_proc));
378 : : else
379 : 459 : errcontext("PL/Python function \"%s\"",
380 : : PLy_procedure_name(exec_ctx->curr_proc));
381 : : }
5011 382 : 466 : }
383 : :
384 : : static void
385 : 28 : plpython_inline_error_callback(void *arg)
386 : : {
387 : 28 : errcontext("PL/Python anonymous code block");
388 : 28 : }
389 : :
390 : : PLyExecutionContext *
4925 tgl@sss.pgh.pa.us 391 : 1428 : PLy_current_execution_context(void)
392 : : {
393 [ - + ]: 1428 : if (PLy_execution_contexts == NULL)
4925 tgl@sss.pgh.pa.us 394 [ # # ]:UBC 0 : elog(ERROR, "no Python function is currently executing");
395 : :
4925 tgl@sss.pgh.pa.us 396 :CBC 1428 : return PLy_execution_contexts;
397 : : }
398 : :
399 : : MemoryContext
3593 400 : 793 : PLy_get_scratch_context(PLyExecutionContext *context)
401 : : {
402 : : /*
403 : : * A scratch context might never be needed in a given plpython procedure,
404 : : * so allocate it on first request.
405 : : */
406 [ + + ]: 793 : if (context->scratch_ctx == NULL)
407 : 442 : context->scratch_ctx =
408 : 442 : AllocSetContextCreate(TopTransactionContext,
409 : : "PL/Python scratch context",
410 : : ALLOCSET_DEFAULT_SIZES);
411 : 793 : return context->scratch_ctx;
412 : : }
413 : :
414 : : static PLyExecutionContext *
2784 peter_e@gmx.net 415 : 722 : PLy_push_execution_context(bool atomic_context)
416 : : {
417 : : PLyExecutionContext *context;
418 : :
419 : : /* Pick a memory context similar to what SPI uses. */
420 : : context = (PLyExecutionContext *)
421 [ + + ]: 722 : MemoryContextAlloc(atomic_context ? TopTransactionContext : PortalContext,
422 : : sizeof(PLyExecutionContext));
4925 tgl@sss.pgh.pa.us 423 : 722 : context->curr_proc = NULL;
3593 424 : 722 : context->scratch_ctx = NULL;
4925 425 : 722 : context->next = PLy_execution_contexts;
426 : 722 : PLy_execution_contexts = context;
427 : 722 : return context;
428 : : }
429 : :
430 : : static void
431 : 722 : PLy_pop_execution_context(void)
432 : : {
4836 bruce@momjian.us 433 : 722 : PLyExecutionContext *context = PLy_execution_contexts;
434 : :
4925 tgl@sss.pgh.pa.us 435 [ - + ]: 722 : if (context == NULL)
4925 tgl@sss.pgh.pa.us 436 [ # # ]:UBC 0 : elog(ERROR, "no Python function is currently executing");
437 : :
4925 tgl@sss.pgh.pa.us 438 :CBC 722 : PLy_execution_contexts = context->next;
439 : :
3593 440 [ + + ]: 722 : if (context->scratch_ctx)
441 : 425 : MemoryContextDelete(context->scratch_ctx);
442 : 722 : pfree(context);
4925 443 : 722 : }
|