aboutsummaryrefslogtreecommitdiff
path: root/src/pl/plpython/plpy_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pl/plpython/plpy_main.c')
-rw-r--r--src/pl/plpython/plpy_main.c325
1 files changed, 325 insertions, 0 deletions
diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c
new file mode 100644
index 00000000000..03c03d1d7d9
--- /dev/null
+++ b/src/pl/plpython/plpy_main.c
@@ -0,0 +1,325 @@
+/*
+ * PL/Python main entry points
+ *
+ * src/pl/plpython/plpy_main.c
+ */
+
+#include "postgres.h"
+
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/trigger.h"
+#include "executor/spi.h"
+#include "miscadmin.h"
+#include "utils/guc.h"
+#include "utils/syscache.h"
+
+#include "plpython.h"
+
+#include "plpy_main.h"
+
+#include "plpy_elog.h"
+#include "plpy_exec.h"
+#include "plpy_plpymodule.h"
+#include "plpy_procedure.h"
+#include "plpy_subxactobject.h"
+
+
+/*
+ * exported functions
+ */
+
+#if PY_MAJOR_VERSION >= 3
+/* Use separate names to avoid clash in pg_pltemplate */
+#define plpython_validator plpython3_validator
+#define plpython_call_handler plpython3_call_handler
+#define plpython_inline_handler plpython3_inline_handler
+#endif
+
+extern void _PG_init(void);
+extern Datum plpython_validator(PG_FUNCTION_ARGS);
+extern Datum plpython_call_handler(PG_FUNCTION_ARGS);
+extern Datum plpython_inline_handler(PG_FUNCTION_ARGS);
+
+#if PY_MAJOR_VERSION < 3
+/* Define aliases plpython2_call_handler etc */
+extern Datum plpython2_validator(PG_FUNCTION_ARGS);
+extern Datum plpython2_call_handler(PG_FUNCTION_ARGS);
+extern Datum plpython2_inline_handler(PG_FUNCTION_ARGS);
+#endif
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(plpython_validator);
+PG_FUNCTION_INFO_V1(plpython_call_handler);
+PG_FUNCTION_INFO_V1(plpython_inline_handler);
+
+#if PY_MAJOR_VERSION < 3
+PG_FUNCTION_INFO_V1(plpython2_validator);
+PG_FUNCTION_INFO_V1(plpython2_call_handler);
+PG_FUNCTION_INFO_V1(plpython2_inline_handler);
+#endif
+
+
+static bool PLy_procedure_is_trigger(Form_pg_proc);
+static void plpython_error_callback(void *);
+static void plpython_inline_error_callback(void *);
+static void PLy_init_interp(void);
+
+static const int plpython_python_version = PY_MAJOR_VERSION;
+
+/* initialize global variables */
+PyObject *PLy_interp_globals = NULL;
+
+
+void
+_PG_init(void)
+{
+ /* Be sure we do initialization only once (should be redundant now) */
+ static bool inited = false;
+ const int **version_ptr;
+
+ if (inited)
+ return;
+
+ /* Be sure we don't run Python 2 and 3 in the same session (might crash) */
+ version_ptr = (const int **) find_rendezvous_variable("plpython_python_version");
+ if (!(*version_ptr))
+ *version_ptr = &plpython_python_version;
+ else
+ {
+ if (**version_ptr != plpython_python_version)
+ ereport(FATAL,
+ (errmsg("Python major version mismatch in session"),
+ errdetail("This session has previously used Python major version %d, and it is now attempting to use Python major version %d.",
+ **version_ptr, plpython_python_version),
+ errhint("Start a new session to use a different Python major version.")));
+ }
+
+ pg_bindtextdomain(TEXTDOMAIN);
+
+#if PY_MAJOR_VERSION >= 3
+ PyImport_AppendInittab("plpy", PyInit_plpy);
+#endif
+ Py_Initialize();
+#if PY_MAJOR_VERSION >= 3
+ PyImport_ImportModule("plpy");
+#endif
+ PLy_init_interp();
+ PLy_init_plpy();
+ if (PyErr_Occurred())
+ PLy_elog(FATAL, "untrapped error in initialization");
+
+ init_procedure_caches();
+
+ explicit_subtransactions = NIL;
+
+ inited = true;
+}
+
+/*
+ * This should only be called once from _PG_init. Initialize the Python
+ * interpreter and global data.
+ */
+void
+PLy_init_interp(void)
+{
+ static PyObject *PLy_interp_safe_globals = NULL;
+ PyObject *mainmod;
+
+ mainmod = PyImport_AddModule("__main__");
+ if (mainmod == NULL || PyErr_Occurred())
+ PLy_elog(ERROR, "could not import \"__main__\" module");
+ Py_INCREF(mainmod);
+ PLy_interp_globals = PyModule_GetDict(mainmod);
+ PLy_interp_safe_globals = PyDict_New();
+ PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
+ Py_DECREF(mainmod);
+ if (PLy_interp_globals == NULL || PyErr_Occurred())
+ PLy_elog(ERROR, "could not initialize globals");
+}
+
+Datum
+plpython_validator(PG_FUNCTION_ARGS)
+{
+ Oid funcoid = PG_GETARG_OID(0);
+ HeapTuple tuple;
+ Form_pg_proc procStruct;
+ bool is_trigger;
+
+ if (!check_function_bodies)
+ {
+ PG_RETURN_VOID();
+ }
+
+ /* Get the new function's pg_proc entry */
+ tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for function %u", funcoid);
+ procStruct = (Form_pg_proc) GETSTRUCT(tuple);
+
+ is_trigger = PLy_procedure_is_trigger(procStruct);
+
+ ReleaseSysCache(tuple);
+
+ PLy_procedure_get(funcoid, is_trigger);
+
+ PG_RETURN_VOID();
+}
+
+#if PY_MAJOR_VERSION < 3
+Datum
+plpython2_validator(PG_FUNCTION_ARGS)
+{
+ return plpython_validator(fcinfo);
+}
+#endif /* PY_MAJOR_VERSION < 3 */
+
+Datum
+plpython_call_handler(PG_FUNCTION_ARGS)
+{
+ Datum retval;
+ PLyProcedure *save_curr_proc;
+ ErrorContextCallback plerrcontext;
+
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed");
+
+ save_curr_proc = PLy_curr_procedure;
+
+ /*
+ * Setup error traceback support for ereport()
+ */
+ plerrcontext.callback = plpython_error_callback;
+ plerrcontext.previous = error_context_stack;
+ error_context_stack = &plerrcontext;
+
+ PG_TRY();
+ {
+ PLyProcedure *proc;
+
+ if (CALLED_AS_TRIGGER(fcinfo))
+ {
+ HeapTuple trv;
+
+ proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, true);
+ PLy_curr_procedure = proc;
+ trv = PLy_exec_trigger(fcinfo, proc);
+ retval = PointerGetDatum(trv);
+ }
+ else
+ {
+ proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, false);
+ PLy_curr_procedure = proc;
+ retval = PLy_exec_function(fcinfo, proc);
+ }
+ }
+ PG_CATCH();
+ {
+ PLy_curr_procedure = save_curr_proc;
+ PyErr_Clear();
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ /* Pop the error context stack */
+ error_context_stack = plerrcontext.previous;
+
+ PLy_curr_procedure = save_curr_proc;
+
+ return retval;
+}
+
+#if PY_MAJOR_VERSION < 3
+Datum
+plpython2_call_handler(PG_FUNCTION_ARGS)
+{
+ return plpython_call_handler(fcinfo);
+}
+#endif /* PY_MAJOR_VERSION < 3 */
+
+Datum
+plpython_inline_handler(PG_FUNCTION_ARGS)
+{
+ InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
+ FunctionCallInfoData fake_fcinfo;
+ FmgrInfo flinfo;
+ PLyProcedure *save_curr_proc;
+ PLyProcedure proc;
+ ErrorContextCallback plerrcontext;
+
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed");
+
+ save_curr_proc = PLy_curr_procedure;
+
+ /*
+ * Setup error traceback support for ereport()
+ */
+ plerrcontext.callback = plpython_inline_error_callback;
+ plerrcontext.previous = error_context_stack;
+ error_context_stack = &plerrcontext;
+
+ MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+ MemSet(&flinfo, 0, sizeof(flinfo));
+ fake_fcinfo.flinfo = &flinfo;
+ flinfo.fn_oid = InvalidOid;
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ MemSet(&proc, 0, sizeof(PLyProcedure));
+ proc.pyname = PLy_strdup("__plpython_inline_block");
+ proc.result.out.d.typoid = VOIDOID;
+
+ PG_TRY();
+ {
+ PLy_procedure_compile(&proc, codeblock->source_text);
+ PLy_curr_procedure = &proc;
+ PLy_exec_function(&fake_fcinfo, &proc);
+ }
+ PG_CATCH();
+ {
+ PLy_procedure_delete(&proc);
+ PLy_curr_procedure = save_curr_proc;
+ PyErr_Clear();
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ PLy_procedure_delete(&proc);
+
+ /* Pop the error context stack */
+ error_context_stack = plerrcontext.previous;
+
+ PLy_curr_procedure = save_curr_proc;
+
+ PG_RETURN_VOID();
+}
+
+#if PY_MAJOR_VERSION < 3
+Datum
+plpython2_inline_handler(PG_FUNCTION_ARGS)
+{
+ return plpython_inline_handler(fcinfo);
+}
+#endif /* PY_MAJOR_VERSION < 3 */
+
+static bool PLy_procedure_is_trigger(Form_pg_proc procStruct)
+{
+ return (procStruct->prorettype == TRIGGEROID ||
+ (procStruct->prorettype == OPAQUEOID &&
+ procStruct->pronargs == 0));
+}
+
+static void
+plpython_error_callback(void *arg)
+{
+ if (PLy_curr_procedure)
+ errcontext("PL/Python function \"%s\"",
+ PLy_procedure_name(PLy_curr_procedure));
+}
+
+static void
+plpython_inline_error_callback(void *arg)
+{
+ errcontext("PL/Python anonymous code block");
+}