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 :
32 46 : PG_MODULE_MAGIC_EXT(
33 : .name = "plpython",
34 : .version = PG_VERSION
35 : );
36 :
37 52 : PG_FUNCTION_INFO_V1(plpython3_validator);
38 52 : PG_FUNCTION_INFO_V1(plpython3_call_handler);
39 16 : 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
59 46 : _PG_init(void)
60 : {
61 46 : pg_bindtextdomain(TEXTDOMAIN);
62 :
63 46 : PLy_initialize();
64 46 : }
65 :
66 : /*
67 : * Perform one-time setup of PL/Python.
68 : */
69 : static void
70 46 : PLy_initialize(void)
71 : {
72 46 : PyImport_AppendInittab("plpy", PyInit_plpy);
73 46 : Py_Initialize();
74 46 : PyImport_ImportModule("plpy");
75 46 : PLy_init_interp();
76 46 : PLy_init_plpy();
77 46 : if (PyErr_Occurred())
78 0 : PLy_elog(FATAL, "untrapped error in initialization");
79 :
80 46 : init_procedure_caches();
81 :
82 46 : explicit_subtransactions = NIL;
83 :
84 46 : PLy_execution_contexts = NULL;
85 46 : }
86 :
87 : /*
88 : * This should be called only once, from PLy_initialize. Initialize the Python
89 : * interpreter and global data.
90 : */
91 : static void
92 46 : PLy_init_interp(void)
93 : {
94 : static PyObject *PLy_interp_safe_globals = NULL;
95 : PyObject *mainmod;
96 :
97 46 : mainmod = PyImport_AddModule("__main__");
98 46 : if (mainmod == NULL || PyErr_Occurred())
99 0 : PLy_elog(ERROR, "could not import \"__main__\" module");
100 46 : Py_INCREF(mainmod);
101 46 : PLy_interp_globals = PyModule_GetDict(mainmod);
102 46 : PLy_interp_safe_globals = PyDict_New();
103 46 : if (PLy_interp_safe_globals == NULL)
104 0 : PLy_elog(ERROR, NULL);
105 46 : PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
106 46 : Py_DECREF(mainmod);
107 46 : if (PLy_interp_globals == NULL || PyErr_Occurred())
108 0 : PLy_elog(ERROR, "could not initialize globals");
109 46 : }
110 :
111 : Datum
112 502 : plpython3_validator(PG_FUNCTION_ARGS)
113 : {
114 502 : Oid funcoid = PG_GETARG_OID(0);
115 : HeapTuple tuple;
116 : Form_pg_proc procStruct;
117 : PLyTrigType is_trigger;
118 :
119 502 : if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
120 0 : PG_RETURN_VOID();
121 :
122 502 : if (!check_function_bodies)
123 2 : PG_RETURN_VOID();
124 :
125 : /* Get the new function's pg_proc entry */
126 500 : tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
127 500 : if (!HeapTupleIsValid(tuple))
128 0 : elog(ERROR, "cache lookup failed for function %u", funcoid);
129 500 : procStruct = (Form_pg_proc) GETSTRUCT(tuple);
130 :
131 500 : is_trigger = PLy_procedure_is_trigger(procStruct);
132 :
133 500 : ReleaseSysCache(tuple);
134 :
135 : /* We can't validate triggers against any particular table ... */
136 500 : (void) PLy_procedure_get(funcoid, InvalidOid, is_trigger);
137 :
138 498 : PG_RETURN_VOID();
139 : }
140 :
141 : Datum
142 1402 : plpython3_call_handler(PG_FUNCTION_ARGS)
143 : {
144 : bool nonatomic;
145 : Datum retval;
146 : PLyExecutionContext *exec_ctx;
147 : ErrorContextCallback plerrcontext;
148 :
149 2938 : nonatomic = fcinfo->context &&
150 1418 : IsA(fcinfo->context, CallContext) &&
151 16 : !castNode(CallContext, fcinfo->context)->atomic;
152 :
153 : /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
154 1402 : 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 : */
161 1402 : exec_ctx = PLy_push_execution_context(!nonatomic);
162 :
163 1402 : PG_TRY();
164 : {
165 1402 : 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 : */
174 1402 : plerrcontext.callback = plpython_error_callback;
175 1402 : plerrcontext.arg = exec_ctx;
176 1402 : plerrcontext.previous = error_context_stack;
177 1402 : error_context_stack = &plerrcontext;
178 :
179 1402 : if (CALLED_AS_TRIGGER(fcinfo))
180 80 : {
181 98 : Relation tgrel = ((TriggerData *) fcinfo->context)->tg_relation;
182 : HeapTuple trv;
183 :
184 98 : proc = PLy_procedure_get(funcoid, RelationGetRelid(tgrel), PLPY_TRIGGER);
185 98 : exec_ctx->curr_proc = proc;
186 98 : trv = PLy_exec_trigger(fcinfo, proc);
187 80 : retval = PointerGetDatum(trv);
188 : }
189 1304 : else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
190 : {
191 20 : proc = PLy_procedure_get(funcoid, InvalidOid, PLPY_EVENT_TRIGGER);
192 20 : exec_ctx->curr_proc = proc;
193 20 : PLy_exec_event_trigger(fcinfo, proc);
194 20 : retval = (Datum) 0;
195 : }
196 : else
197 : {
198 1284 : proc = PLy_procedure_get(funcoid, InvalidOid, PLPY_NOT_TRIGGER);
199 1278 : exec_ctx->curr_proc = proc;
200 1278 : retval = PLy_exec_function(fcinfo, proc);
201 : }
202 : }
203 186 : PG_CATCH();
204 : {
205 186 : PLy_pop_execution_context();
206 186 : PyErr_Clear();
207 186 : PG_RE_THROW();
208 : }
209 1216 : PG_END_TRY();
210 :
211 : /* Destroy the execution context */
212 1216 : PLy_pop_execution_context();
213 :
214 1216 : return retval;
215 : }
216 :
217 : Datum
218 42 : plpython3_inline_handler(PG_FUNCTION_ARGS)
219 : {
220 42 : LOCAL_FCINFO(fake_fcinfo, 0);
221 42 : 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 */
228 42 : SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
229 :
230 210 : MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
231 294 : MemSet(&flinfo, 0, sizeof(flinfo));
232 42 : fake_fcinfo->flinfo = &flinfo;
233 42 : flinfo.fn_oid = InvalidOid;
234 42 : flinfo.fn_mcxt = CurrentMemoryContext;
235 :
236 1764 : MemSet(&proc, 0, sizeof(PLyProcedure));
237 42 : proc.mcxt = AllocSetContextCreate(TopMemoryContext,
238 : "__plpython_inline_block",
239 : ALLOCSET_DEFAULT_SIZES);
240 42 : proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
241 42 : 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 : */
247 42 : 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 : */
254 42 : exec_ctx = PLy_push_execution_context(codeblock->atomic);
255 :
256 42 : 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 : */
263 42 : plerrcontext.callback = plpython_inline_error_callback;
264 42 : plerrcontext.arg = exec_ctx;
265 42 : plerrcontext.previous = error_context_stack;
266 42 : error_context_stack = &plerrcontext;
267 :
268 42 : PLy_procedure_compile(&proc, codeblock->source_text);
269 42 : exec_ctx->curr_proc = &proc;
270 42 : PLy_exec_function(fake_fcinfo, &proc);
271 : }
272 22 : PG_CATCH();
273 : {
274 22 : PLy_pop_execution_context();
275 22 : PLy_procedure_delete(&proc);
276 22 : PyErr_Clear();
277 22 : PG_RE_THROW();
278 : }
279 20 : PG_END_TRY();
280 :
281 : /* Destroy the execution context */
282 20 : PLy_pop_execution_context();
283 :
284 : /* Now clean up the transient procedure we made */
285 20 : PLy_procedure_delete(&proc);
286 :
287 20 : PG_RETURN_VOID();
288 : }
289 :
290 : static PLyTrigType
291 500 : PLy_procedure_is_trigger(Form_pg_proc procStruct)
292 : {
293 : PLyTrigType ret;
294 :
295 500 : switch (procStruct->prorettype)
296 : {
297 48 : case TRIGGEROID:
298 48 : ret = PLPY_TRIGGER;
299 48 : break;
300 2 : case EVENT_TRIGGEROID:
301 2 : ret = PLPY_EVENT_TRIGGER;
302 2 : break;
303 450 : default:
304 450 : ret = PLPY_NOT_TRIGGER;
305 450 : break;
306 : }
307 :
308 500 : return ret;
309 : }
310 :
311 : static void
312 932 : plpython_error_callback(void *arg)
313 : {
314 932 : PLyExecutionContext *exec_ctx = (PLyExecutionContext *) arg;
315 :
316 932 : if (exec_ctx->curr_proc)
317 : {
318 926 : if (exec_ctx->curr_proc->is_procedure)
319 8 : errcontext("PL/Python procedure \"%s\"",
320 : PLy_procedure_name(exec_ctx->curr_proc));
321 : else
322 918 : errcontext("PL/Python function \"%s\"",
323 : PLy_procedure_name(exec_ctx->curr_proc));
324 : }
325 932 : }
326 :
327 : static void
328 56 : plpython_inline_error_callback(void *arg)
329 : {
330 56 : errcontext("PL/Python anonymous code block");
331 56 : }
332 :
333 : PLyExecutionContext *
334 2856 : PLy_current_execution_context(void)
335 : {
336 2856 : if (PLy_execution_contexts == NULL)
337 0 : elog(ERROR, "no Python function is currently executing");
338 :
339 2856 : return PLy_execution_contexts;
340 : }
341 :
342 : MemoryContext
343 1586 : 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 1586 : if (context->scratch_ctx == NULL)
350 884 : context->scratch_ctx =
351 884 : AllocSetContextCreate(TopTransactionContext,
352 : "PL/Python scratch context",
353 : ALLOCSET_DEFAULT_SIZES);
354 1586 : return context->scratch_ctx;
355 : }
356 :
357 : static PLyExecutionContext *
358 1444 : 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 1444 : MemoryContextAlloc(atomic_context ? TopTransactionContext : PortalContext,
365 : sizeof(PLyExecutionContext));
366 1444 : context->curr_proc = NULL;
367 1444 : context->scratch_ctx = NULL;
368 1444 : context->next = PLy_execution_contexts;
369 1444 : PLy_execution_contexts = context;
370 1444 : return context;
371 : }
372 :
373 : static void
374 1444 : PLy_pop_execution_context(void)
375 : {
376 1444 : PLyExecutionContext *context = PLy_execution_contexts;
377 :
378 1444 : if (context == NULL)
379 0 : elog(ERROR, "no Python function is currently executing");
380 :
381 1444 : PLy_execution_contexts = context->next;
382 :
383 1444 : if (context->scratch_ctx)
384 850 : MemoryContextDelete(context->scratch_ctx);
385 1444 : pfree(context);
386 1444 : }
|