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 : :
215 tgl@sss.pgh.pa.us 32 :CBC 23 : PG_MODULE_MAGIC_EXT(
33 : : .name = "plpython",
34 : : .version = PG_VERSION
35 : : );
36 : :
1330 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 void PLy_initialize(void);
43 : : static PLyTrigType PLy_procedure_is_trigger(Form_pg_proc procStruct);
44 : : static void plpython_error_callback(void *arg);
45 : : static void plpython_inline_error_callback(void *arg);
46 : : static void PLy_init_interp(void);
47 : :
48 : : static PLyExecutionContext *PLy_push_execution_context(bool atomic_context);
49 : : static void PLy_pop_execution_context(void);
50 : :
51 : : /* initialize global variables */
52 : : PyObject *PLy_interp_globals = NULL;
53 : :
54 : : /* this doesn't need to be global; use PLy_current_execution_context() */
55 : : static PLyExecutionContext *PLy_execution_contexts = NULL;
56 : :
57 : :
58 : : void
5062 peter_e@gmx.net 59 : 23 : _PG_init(void)
60 : : {
3577 tgl@sss.pgh.pa.us 61 : 23 : pg_bindtextdomain(TEXTDOMAIN);
62 : :
12 peter@eisentraut.org 63 :GNC 23 : PLy_initialize();
3577 tgl@sss.pgh.pa.us 64 :CBC 23 : }
65 : :
66 : : /*
67 : : * Perform one-time setup of PL/Python.
68 : : */
69 : : static void
70 : 23 : PLy_initialize(void)
71 : : {
5062 peter_e@gmx.net 72 : 23 : PyImport_AppendInittab("plpy", PyInit_plpy);
73 : 23 : Py_Initialize();
74 : 23 : PyImport_ImportModule("plpy");
75 : 23 : PLy_init_interp();
76 : 23 : PLy_init_plpy();
77 [ - + ]: 23 : if (PyErr_Occurred())
5062 peter_e@gmx.net 78 :UBC 0 : PLy_elog(FATAL, "untrapped error in initialization");
79 : :
5062 peter_e@gmx.net 80 :CBC 23 : init_procedure_caches();
81 : :
82 : 23 : explicit_subtransactions = NIL;
83 : :
4976 tgl@sss.pgh.pa.us 84 : 23 : PLy_execution_contexts = NULL;
5062 peter_e@gmx.net 85 :GIC 23 : }
86 : :
87 : : /*
88 : : * This should be called only once, from PLy_initialize. Initialize the Python
89 : : * interpreter and global data.
90 : : */
91 : : static void
5062 peter_e@gmx.net 92 :CBC 23 : PLy_init_interp(void)
93 : : {
94 : : static PyObject *PLy_interp_safe_globals = NULL;
95 : : PyObject *mainmod;
96 : :
97 : 23 : mainmod = PyImport_AddModule("__main__");
98 [ + - - + ]: 23 : if (mainmod == NULL || PyErr_Occurred())
5062 peter_e@gmx.net 99 :UBC 0 : PLy_elog(ERROR, "could not import \"__main__\" module");
100 : : Py_INCREF(mainmod);
5062 peter_e@gmx.net 101 :CBC 23 : PLy_interp_globals = PyModule_GetDict(mainmod);
102 : 23 : PLy_interp_safe_globals = PyDict_New();
4976 tgl@sss.pgh.pa.us 103 [ - + ]: 23 : if (PLy_interp_safe_globals == NULL)
2918 peter_e@gmx.net 104 :UBC 0 : PLy_elog(ERROR, NULL);
5062 peter_e@gmx.net 105 :CBC 23 : PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
106 : : Py_DECREF(mainmod);
107 [ + - - + ]: 23 : if (PLy_interp_globals == NULL || PyErr_Occurred())
5062 peter_e@gmx.net 108 :UBC 0 : PLy_elog(ERROR, "could not initialize globals");
5062 peter_e@gmx.net 109 :CBC 23 : }
110 : :
111 : : Datum
1330 andres@anarazel.de 112 : 251 : plpython3_validator(PG_FUNCTION_ARGS)
113 : : {
5062 peter_e@gmx.net 114 : 251 : Oid funcoid = PG_GETARG_OID(0);
115 : : HeapTuple tuple;
116 : : Form_pg_proc procStruct;
117 : : PLyTrigType is_trigger;
118 : :
4270 noah@leadboat.com 119 [ - + ]: 251 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
4270 noah@leadboat.com 120 :UBC 0 : PG_RETURN_VOID();
121 : :
5062 peter_e@gmx.net 122 [ + + ]:CBC 251 : if (!check_function_bodies)
123 : 1 : PG_RETURN_VOID();
124 : :
125 : : /* Get the new function's pg_proc entry */
126 : 250 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
127 [ - + ]: 250 : if (!HeapTupleIsValid(tuple))
5062 peter_e@gmx.net 128 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
5062 peter_e@gmx.net 129 :CBC 250 : procStruct = (Form_pg_proc) GETSTRUCT(tuple);
130 : :
131 : 250 : is_trigger = PLy_procedure_is_trigger(procStruct);
132 : :
133 : 250 : ReleaseSysCache(tuple);
134 : :
135 : : /* We can't validate triggers against any particular table ... */
4 tgl@sss.pgh.pa.us 136 :GNC 250 : (void) PLy_procedure_get(funcoid, InvalidOid, is_trigger);
137 : :
5062 peter_e@gmx.net 138 :CBC 249 : PG_RETURN_VOID();
139 : : }
140 : :
141 : : Datum
1330 andres@anarazel.de 142 : 701 : plpython3_call_handler(PG_FUNCTION_ARGS)
143 : : {
144 : : bool nonatomic;
145 : : Datum retval;
146 : : PLyExecutionContext *exec_ctx;
147 : : ErrorContextCallback plerrcontext;
148 : :
2835 peter_e@gmx.net 149 : 1469 : nonatomic = fcinfo->context &&
150 [ + + + + ]: 709 : IsA(fcinfo->context, CallContext) &&
151 [ + + ]: 8 : !castNode(CallContext, fcinfo->context)->atomic;
152 : :
153 : : /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
413 tgl@sss.pgh.pa.us 154 : 701 : SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
155 : :
156 : : /*
157 : : * Push execution context onto stack. It is important that this get
158 : : * popped again, so avoid putting anything that could throw error between
159 : : * here and the PG_TRY.
160 : : */
2835 peter_e@gmx.net 161 : 701 : exec_ctx = PLy_push_execution_context(!nonatomic);
162 : :
5062 163 [ + + ]: 701 : PG_TRY();
164 : : {
4658 tgl@sss.pgh.pa.us 165 : 701 : Oid funcoid = fcinfo->flinfo->fn_oid;
166 : : PLyProcedure *proc;
167 : :
168 : : /*
169 : : * Setup error traceback support for ereport(). Note that the PG_TRY
170 : : * structure pops this for us again at exit, so we needn't do that
171 : : * explicitly, nor do we risk the callback getting called after we've
172 : : * destroyed the exec_ctx.
173 : : */
2812 174 : 701 : plerrcontext.callback = plpython_error_callback;
175 : 701 : plerrcontext.arg = exec_ctx;
176 : 701 : plerrcontext.previous = error_context_stack;
177 : 701 : error_context_stack = &plerrcontext;
178 : :
5062 peter_e@gmx.net 179 [ + + + + ]: 701 : if (CALLED_AS_TRIGGER(fcinfo))
180 : 40 : {
4658 tgl@sss.pgh.pa.us 181 : 49 : Relation tgrel = ((TriggerData *) fcinfo->context)->tg_relation;
182 : : HeapTuple trv;
183 : :
67 peter@eisentraut.org 184 :GNC 49 : proc = PLy_procedure_get(funcoid, RelationGetRelid(tgrel), PLPY_TRIGGER);
4976 tgl@sss.pgh.pa.us 185 :CBC 49 : exec_ctx->curr_proc = proc;
5062 peter_e@gmx.net 186 : 49 : trv = PLy_exec_trigger(fcinfo, proc);
187 : 40 : retval = PointerGetDatum(trv);
188 : : }
67 peter@eisentraut.org 189 [ + + + + ]:GNC 652 : else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
190 : : {
191 : 10 : proc = PLy_procedure_get(funcoid, InvalidOid, PLPY_EVENT_TRIGGER);
192 : 10 : exec_ctx->curr_proc = proc;
193 : 10 : PLy_exec_event_trigger(fcinfo, proc);
194 : 10 : retval = (Datum) 0;
195 : : }
196 : : else
197 : : {
198 : 642 : proc = PLy_procedure_get(funcoid, InvalidOid, PLPY_NOT_TRIGGER);
4976 tgl@sss.pgh.pa.us 199 :CBC 639 : exec_ctx->curr_proc = proc;
5062 peter_e@gmx.net 200 : 639 : retval = PLy_exec_function(fcinfo, proc);
201 : : }
202 : : }
203 : 93 : PG_CATCH();
204 : : {
4976 tgl@sss.pgh.pa.us 205 : 93 : PLy_pop_execution_context();
5062 peter_e@gmx.net 206 : 93 : PyErr_Clear();
207 : 93 : PG_RE_THROW();
208 : : }
209 [ - + ]: 608 : PG_END_TRY();
210 : :
211 : : /* Destroy the execution context */
4976 tgl@sss.pgh.pa.us 212 : 608 : PLy_pop_execution_context();
213 : :
5062 peter_e@gmx.net 214 : 608 : return retval;
215 : : }
216 : :
217 : : Datum
1330 andres@anarazel.de 218 : 21 : plpython3_inline_handler(PG_FUNCTION_ARGS)
219 : : {
2466 220 : 21 : LOCAL_FCINFO(fake_fcinfo, 0);
5062 peter_e@gmx.net 221 : 21 : InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
222 : : FmgrInfo flinfo;
223 : : PLyProcedure proc;
224 : : PLyExecutionContext *exec_ctx;
225 : : ErrorContextCallback plerrcontext;
226 : :
227 : : /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
413 tgl@sss.pgh.pa.us 228 : 21 : SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
229 : :
2466 andres@anarazel.de 230 [ + - + - : 105 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
+ - + - +
+ ]
5062 peter_e@gmx.net 231 [ + - + - : 147 : MemSet(&flinfo, 0, sizeof(flinfo));
+ - + - +
+ ]
2466 andres@anarazel.de 232 : 21 : fake_fcinfo->flinfo = &flinfo;
5062 peter_e@gmx.net 233 : 21 : flinfo.fn_oid = InvalidOid;
234 : 21 : flinfo.fn_mcxt = CurrentMemoryContext;
235 : :
236 [ + - + - : 882 : MemSet(&proc, 0, sizeof(PLyProcedure));
+ - + - +
+ ]
3644 tgl@sss.pgh.pa.us 237 : 21 : proc.mcxt = AllocSetContextCreate(TopMemoryContext,
238 : : "__plpython_inline_block",
239 : : ALLOCSET_DEFAULT_SIZES);
240 : 21 : proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
3837 peter_e@gmx.net 241 : 21 : proc.langid = codeblock->langOid;
242 : :
243 : : /*
244 : : * This is currently sufficient to get PLy_exec_function to work, but
245 : : * someday we might need to be honest and use PLy_output_setup_func.
246 : : */
2902 tgl@sss.pgh.pa.us 247 : 21 : proc.result.typoid = VOIDOID;
248 : :
249 : : /*
250 : : * Push execution context onto stack. It is important that this get
251 : : * popped again, so avoid putting anything that could throw error between
252 : : * here and the PG_TRY.
253 : : */
2835 peter_e@gmx.net 254 : 21 : exec_ctx = PLy_push_execution_context(codeblock->atomic);
255 : :
5062 256 [ + + ]: 21 : PG_TRY();
257 : : {
258 : : /*
259 : : * Setup error traceback support for ereport().
260 : : * plpython_inline_error_callback doesn't currently need exec_ctx, but
261 : : * for consistency with plpython3_call_handler we do it the same way.
262 : : */
2812 tgl@sss.pgh.pa.us 263 : 21 : plerrcontext.callback = plpython_inline_error_callback;
264 : 21 : plerrcontext.arg = exec_ctx;
265 : 21 : plerrcontext.previous = error_context_stack;
266 : 21 : error_context_stack = &plerrcontext;
267 : :
5062 peter_e@gmx.net 268 : 21 : PLy_procedure_compile(&proc, codeblock->source_text);
4976 tgl@sss.pgh.pa.us 269 : 21 : exec_ctx->curr_proc = &proc;
2466 andres@anarazel.de 270 : 21 : PLy_exec_function(fake_fcinfo, &proc);
271 : : }
5062 peter_e@gmx.net 272 : 11 : PG_CATCH();
273 : : {
4976 tgl@sss.pgh.pa.us 274 : 11 : PLy_pop_execution_context();
5062 peter_e@gmx.net 275 : 11 : PLy_procedure_delete(&proc);
276 : 11 : PyErr_Clear();
277 : 11 : PG_RE_THROW();
278 : : }
279 [ - + ]: 10 : PG_END_TRY();
280 : :
281 : : /* Destroy the execution context */
4976 tgl@sss.pgh.pa.us 282 : 10 : PLy_pop_execution_context();
283 : :
284 : : /* Now clean up the transient procedure we made */
285 : 10 : PLy_procedure_delete(&proc);
286 : :
5062 peter_e@gmx.net 287 : 10 : PG_RETURN_VOID();
288 : : }
289 : :
290 : : static PLyTrigType
4887 bruce@momjian.us 291 : 250 : PLy_procedure_is_trigger(Form_pg_proc procStruct)
292 : : {
293 : : PLyTrigType ret;
294 : :
67 peter@eisentraut.org 295 [ + + + ]:GNC 250 : switch (procStruct->prorettype)
296 : : {
297 : 24 : case TRIGGEROID:
298 : 24 : ret = PLPY_TRIGGER;
299 : 24 : break;
300 : 1 : case EVENT_TRIGGEROID:
301 : 1 : ret = PLPY_EVENT_TRIGGER;
302 : 1 : break;
303 : 225 : default:
304 : 225 : ret = PLPY_NOT_TRIGGER;
305 : 225 : break;
306 : : }
307 : :
308 : 250 : return ret;
309 : : }
310 : :
311 : : static void
5062 peter_e@gmx.net 312 :CBC 466 : plpython_error_callback(void *arg)
313 : : {
2812 tgl@sss.pgh.pa.us 314 : 466 : PLyExecutionContext *exec_ctx = (PLyExecutionContext *) arg;
315 : :
4976 316 [ + + ]: 466 : if (exec_ctx->curr_proc)
317 : : {
2888 peter_e@gmx.net 318 [ + + ]: 463 : if (exec_ctx->curr_proc->is_procedure)
319 : 4 : errcontext("PL/Python procedure \"%s\"",
320 : : PLy_procedure_name(exec_ctx->curr_proc));
321 : : else
322 : 459 : errcontext("PL/Python function \"%s\"",
323 : : PLy_procedure_name(exec_ctx->curr_proc));
324 : : }
5062 325 : 466 : }
326 : :
327 : : static void
328 : 28 : plpython_inline_error_callback(void *arg)
329 : : {
330 : 28 : errcontext("PL/Python anonymous code block");
331 : 28 : }
332 : :
333 : : PLyExecutionContext *
4976 tgl@sss.pgh.pa.us 334 : 1428 : PLy_current_execution_context(void)
335 : : {
336 [ - + ]: 1428 : if (PLy_execution_contexts == NULL)
4976 tgl@sss.pgh.pa.us 337 [ # # ]:UBC 0 : elog(ERROR, "no Python function is currently executing");
338 : :
4976 tgl@sss.pgh.pa.us 339 :CBC 1428 : return PLy_execution_contexts;
340 : : }
341 : :
342 : : MemoryContext
3644 343 : 793 : PLy_get_scratch_context(PLyExecutionContext *context)
344 : : {
345 : : /*
346 : : * A scratch context might never be needed in a given plpython procedure,
347 : : * so allocate it on first request.
348 : : */
349 [ + + ]: 793 : if (context->scratch_ctx == NULL)
350 : 442 : context->scratch_ctx =
351 : 442 : AllocSetContextCreate(TopTransactionContext,
352 : : "PL/Python scratch context",
353 : : ALLOCSET_DEFAULT_SIZES);
354 : 793 : return context->scratch_ctx;
355 : : }
356 : :
357 : : static PLyExecutionContext *
2835 peter_e@gmx.net 358 : 722 : PLy_push_execution_context(bool atomic_context)
359 : : {
360 : : PLyExecutionContext *context;
361 : :
362 : : /* Pick a memory context similar to what SPI uses. */
363 : : context = (PLyExecutionContext *)
364 [ + + ]: 722 : MemoryContextAlloc(atomic_context ? TopTransactionContext : PortalContext,
365 : : sizeof(PLyExecutionContext));
4976 tgl@sss.pgh.pa.us 366 : 722 : context->curr_proc = NULL;
3644 367 : 722 : context->scratch_ctx = NULL;
4976 368 : 722 : context->next = PLy_execution_contexts;
369 : 722 : PLy_execution_contexts = context;
370 : 722 : return context;
371 : : }
372 : :
373 : : static void
374 : 722 : PLy_pop_execution_context(void)
375 : : {
4887 bruce@momjian.us 376 : 722 : PLyExecutionContext *context = PLy_execution_contexts;
377 : :
4976 tgl@sss.pgh.pa.us 378 [ - + ]: 722 : if (context == NULL)
4976 tgl@sss.pgh.pa.us 379 [ # # ]:UBC 0 : elog(ERROR, "no Python function is currently executing");
380 : :
4976 tgl@sss.pgh.pa.us 381 :CBC 722 : PLy_execution_contexts = context->next;
382 : :
3644 383 [ + + ]: 722 : if (context->scratch_ctx)
384 : 425 : MemoryContextDelete(context->scratch_ctx);
385 : 722 : pfree(context);
4976 386 : 722 : }
|