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