From 594ba21bce05e8682eaf024f705fea8aeaba5ef3 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Wed, 15 Oct 2025 08:13:07 +0200 Subject: [PATCH] plpython: Remove support for major version conflict detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This essentially reverts commit 866566a690b, which installed safeguards against loading plpython2 and plpython3 into the same process. We don't support plpython2 anymore, so this is obsolete. The Python and PL/Python initialization now happens again in _PG_init() rather than the first time a PL/Python call handler is invoked. (Often, these will be very close together.) I kept the separate PLy_initialize() function introduced by 866566a690b to keep _PG_init() a bit modular. Reviewed-by: Mario González Troncoso Reviewed-by: Nathan Bossart Discussion: https://www.postgresql.org/message-id/flat/9eb9feb6-1df3-4f0c-a0dc-9bcf35273111%40eisentraut.org --- src/pl/plpython/plpy_main.c | 65 +++---------------------------------- 1 file changed, 4 insertions(+), 61 deletions(-) diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c index 70fc2c9257a..8ae02a239ae 100644 --- a/src/pl/plpython/plpy_main.c +++ b/src/pl/plpython/plpy_main.c @@ -39,6 +39,7 @@ PG_FUNCTION_INFO_V1(plpython3_call_handler); PG_FUNCTION_INFO_V1(plpython3_inline_handler); +static void PLy_initialize(void); static PLyTrigType PLy_procedure_is_trigger(Form_pg_proc procStruct); static void plpython_error_callback(void *arg); static void plpython_inline_error_callback(void *arg); @@ -47,10 +48,6 @@ static void PLy_init_interp(void); static PLyExecutionContext *PLy_push_execution_context(bool atomic_context); static void PLy_pop_execution_context(void); -/* static state for Python library conflict detection */ -static int *plpython_version_bitmask_ptr = NULL; -static int plpython_version_bitmask = 0; - /* initialize global variables */ PyObject *PLy_interp_globals = NULL; @@ -61,62 +58,17 @@ static PLyExecutionContext *PLy_execution_contexts = NULL; void _PG_init(void) { - int **bitmask_ptr; - - /* - * Set up a shared bitmask variable telling which Python version(s) are - * loaded into this process's address space. If there's more than one, we - * cannot call into libpython for fear of causing crashes. But postpone - * the actual failure for later, so that operations like pg_restore can - * load more than one plpython library so long as they don't try to do - * anything much with the language. - * - * While we only support Python 3 these days, somebody might create an - * out-of-tree version adding back support for Python 2. Conflicts with - * such an extension should be detected. - */ - bitmask_ptr = (int **) find_rendezvous_variable("plpython_version_bitmask"); - if (!(*bitmask_ptr)) /* am I the first? */ - *bitmask_ptr = &plpython_version_bitmask; - /* Retain pointer to the agreed-on shared variable ... */ - plpython_version_bitmask_ptr = *bitmask_ptr; - /* ... and announce my presence */ - *plpython_version_bitmask_ptr |= (1 << PY_MAJOR_VERSION); - - /* - * This should be safe even in the presence of conflicting plpythons, and - * it's necessary to do it before possibly throwing a conflict error, or - * the error message won't get localized. - */ pg_bindtextdomain(TEXTDOMAIN); + + PLy_initialize(); } /* - * Perform one-time setup of PL/Python, after checking for a conflict - * with other versions of Python. + * Perform one-time setup of PL/Python. */ static void PLy_initialize(void) { - static bool inited = false; - - /* - * Check for multiple Python libraries before actively doing anything with - * libpython. This must be repeated on each entry to PL/Python, in case a - * conflicting library got loaded since we last looked. - * - * It is attractive to weaken this error from FATAL to ERROR, but there - * would be corner cases, so it seems best to be conservative. - */ - if (*plpython_version_bitmask_ptr != (1 << PY_MAJOR_VERSION)) - ereport(FATAL, - (errmsg("multiple Python libraries are present in session"), - errdetail("Only one Python major version can be used in one session."))); - - /* The rest should only be done once per session */ - if (inited) - return; - PyImport_AppendInittab("plpy", PyInit_plpy); Py_Initialize(); PyImport_ImportModule("plpy"); @@ -130,8 +82,6 @@ PLy_initialize(void) explicit_subtransactions = NIL; PLy_execution_contexts = NULL; - - inited = true; } /* @@ -172,9 +122,6 @@ plpython3_validator(PG_FUNCTION_ARGS) if (!check_function_bodies) PG_RETURN_VOID(); - /* Do this only after making sure we need to do something */ - PLy_initialize(); - /* Get the new function's pg_proc entry */ tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid)); if (!HeapTupleIsValid(tuple)) @@ -199,8 +146,6 @@ plpython3_call_handler(PG_FUNCTION_ARGS) PLyExecutionContext *exec_ctx; ErrorContextCallback plerrcontext; - PLy_initialize(); - nonatomic = fcinfo->context && IsA(fcinfo->context, CallContext) && !castNode(CallContext, fcinfo->context)->atomic; @@ -279,8 +224,6 @@ plpython3_inline_handler(PG_FUNCTION_ARGS) PLyExecutionContext *exec_ctx; ErrorContextCallback plerrcontext; - PLy_initialize(); - /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */ SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);