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