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