diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml index 015bbad..03f6bcc 100644 *** a/doc/src/sgml/plpython.sgml --- b/doc/src/sgml/plpython.sgml *************** *** 176,183 **** based on Python 3 in the same session, because the symbols in the dynamic modules would clash, which could result in crashes of the PostgreSQL server process. There is a check that prevents mixing ! Python major versions in a session, which will abort the session if ! a mismatch is detected. It is possible, however, to use both PL/Python variants in the same database, from separate sessions. --- 176,183 ---- based on Python 3 in the same session, because the symbols in the dynamic modules would clash, which could result in crashes of the PostgreSQL server process. There is a check that prevents mixing ! Python major versions within a session. ! It is possible, however, to use both PL/Python variants in the same database, from separate sessions. diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c index 3c2ebfa..6805148 100644 *** a/src/pl/plpython/plpy_main.c --- b/src/pl/plpython/plpy_main.c *************** static void PLy_init_interp(void); *** 63,68 **** --- 63,71 ---- static PLyExecutionContext *PLy_push_execution_context(void); 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; static const int plpython_python_version = PY_MAJOR_VERSION; /* initialize global variables */ *************** static PLyExecutionContext *PLy_executio *** 75,102 **** 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); --- 78,155 ---- void _PG_init(void) { ! int **bitmask_ptr; const int **version_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. ! */ ! 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 here for the next error to be localized. ! */ ! pg_bindtextdomain(TEXTDOMAIN); ! ! /* ! * We used to have a scheme whereby PL/Python would fail immediately if ! * loaded into a session in which a conflicting libpython is already ! * present. We don't like to do that anymore, but it seems possible that ! * a plpython library adhering to the old convention is present in the ! * session, in which case we have to fail. We detect an old library if ! * plpython_python_version is already defined but the indicated version ! * isn't reflected in plpython_version_bitmask. Otherwise, set the ! * variable so that the right thing happens if an old library is loaded ! * later. ! */ version_ptr = (const int **) find_rendezvous_variable("plpython_python_version"); if (!(*version_ptr)) *version_ptr = &plpython_python_version; else { ! if ((*plpython_version_bitmask_ptr & (1 << **version_ptr)) == 0) 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."))); } + } ! /* ! * Perform one-time setup of PL/Python, after checking for a conflict ! * with other versions of 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. ! */ ! if (*plpython_version_bitmask_ptr != (1 << PY_MAJOR_VERSION)) ! ereport(ERROR, ! (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; #if PY_MAJOR_VERSION >= 3 PyImport_AppendInittab("plpy", PyInit_plpy); *************** _PG_init(void) *** 120,126 **** } /* ! * This should only be called once from _PG_init. Initialize the Python * interpreter and global data. */ static void --- 173,179 ---- } /* ! * This should be called only once, from PLy_initialize. Initialize the Python * interpreter and global data. */ static void *************** plpython_validator(PG_FUNCTION_ARGS) *** 155,163 **** PG_RETURN_VOID(); if (!check_function_bodies) - { PG_RETURN_VOID(); ! } /* Get the new function's pg_proc entry */ tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid)); --- 208,217 ---- PG_RETURN_VOID(); 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)); *************** plpython_call_handler(PG_FUNCTION_ARGS) *** 191,196 **** --- 245,252 ---- PLyExecutionContext *exec_ctx; ErrorContextCallback plerrcontext; + PLy_initialize(); + /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed"); *************** plpython_inline_handler(PG_FUNCTION_ARGS *** 266,271 **** --- 322,329 ---- PLyExecutionContext *exec_ctx; ErrorContextCallback plerrcontext; + PLy_initialize(); + /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */ if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed");