Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0ddeaba

Browse files
committed
Avoid dump/reload problems when using both plpython2 and plpython3.
Commit8037160 installed a safeguard against loading plpython2and plpython3 at the same time, but asserted that both could still beused in the same database, just not in the same session. However, that'snot actually all that practical because dumping and reloading will fail(since both libraries necessarily get loaded into the restoring session).pg_upgrade is even worse, because it checks for missing libraries byloading every .so library mentioned in the entire installation into onesession, so that you can have only one across the whole cluster.We can improve matters by not throwing the error immediately in _PG_init,but only when and if we're asked to do something that requires callinginto libpython. This ameliorates both of the above situations, sincewhile execution of CREATE LANGUAGE, CREATE FUNCTION, etc will result inloading plpython, it isn't asked to do anything interesting (at leastnot if check_function_bodies is off, as it will be during a restore).It's possible that this opens some corner-case holes in which a crashcould be provoked with sufficient effort. However, since plpythononly exists as an untrusted language, any such crash would requiresuperuser privileges, making it "don't do that" not a security issue.To reduce the hazards in this area, the error is still FATAL when itdoes get thrown.Per a report from Paul Jones. Back-patch to 9.2, which is as far backas the patch applies without work. (It could be made to work in 9.1,but given the lack of previous complaints, I'm disinclined to expendeffort so far back. We've been pretty desultory about support forPython 3 in 9.1 anyway.)
1 parent8b5cc3e commit0ddeaba

File tree

1 file changed

+71
-10
lines changed

1 file changed

+71
-10
lines changed

‎src/pl/plpython/plpy_main.c

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ static void PLy_init_interp(void);
7272
staticPLyExecutionContext*PLy_push_execution_context(void);
7373
staticvoidPLy_pop_execution_context(void);
7474

75+
/* static state for Python library conflict detection */
76+
staticint*plpython_version_bitmask_ptr=NULL;
77+
staticintplpython_version_bitmask=0;
7578
staticconstintplpython_python_version=PY_MAJOR_VERSION;
7679

7780
/* initialize global variables */
@@ -84,28 +87,81 @@ static PLyExecutionContext *PLy_execution_contexts = NULL;
8487
void
8588
_PG_init(void)
8689
{
87-
/* Be sure we do initialization only once (should be redundant now) */
88-
staticboolinited= false;
90+
int**bitmask_ptr;
8991
constint**version_ptr;
9092

91-
if (inited)
92-
return;
93+
/*
94+
* Set up a shared bitmask variable telling which Python version(s) are
95+
* loaded into this process's address space. If there's more than one, we
96+
* cannot call into libpython for fear of causing crashes. But postpone
97+
* the actual failure for later, so that operations like pg_restore can
98+
* load more than one plpython library so long as they don't try to do
99+
* anything much with the language.
100+
*/
101+
bitmask_ptr= (int**)find_rendezvous_variable("plpython_version_bitmask");
102+
if (!(*bitmask_ptr))/* am I the first? */
103+
*bitmask_ptr=&plpython_version_bitmask;
104+
/* Retain pointer to the agreed-on shared variable ... */
105+
plpython_version_bitmask_ptr=*bitmask_ptr;
106+
/* ... and announce my presence */
107+
*plpython_version_bitmask_ptr |= (1 <<PY_MAJOR_VERSION);
93108

94-
/* Be sure we don't run Python 2 and 3 in the same session (might crash) */
109+
/*
110+
* This should be safe even in the presence of conflicting plpythons, and
111+
* it's necessary to do it here for the next error to be localized.
112+
*/
113+
pg_bindtextdomain(TEXTDOMAIN);
114+
115+
/*
116+
* We used to have a scheme whereby PL/Python would fail immediately if
117+
* loaded into a session in which a conflicting libpython is already
118+
* present. We don't like to do that anymore, but it seems possible that
119+
* a plpython library adhering to the old convention is present in the
120+
* session, in which case we have to fail. We detect an old library if
121+
* plpython_python_version is already defined but the indicated version
122+
* isn't reflected in plpython_version_bitmask. Otherwise, set the
123+
* variable so that the right thing happens if an old library is loaded
124+
* later.
125+
*/
95126
version_ptr= (constint**)find_rendezvous_variable("plpython_python_version");
96127
if (!(*version_ptr))
97128
*version_ptr=&plpython_python_version;
98129
else
99130
{
100-
if (**version_ptr!=plpython_python_version)
131+
if ((*plpython_version_bitmask_ptr& (1 <<**version_ptr))==0)
101132
ereport(FATAL,
102133
(errmsg("Python major version mismatch in session"),
103134
errdetail("This session has previously used Python major version %d, and it is now attempting to use Python major version %d.",
104135
**version_ptr,plpython_python_version),
105136
errhint("Start a new session to use a different Python major version.")));
106137
}
138+
}
107139

108-
pg_bindtextdomain(TEXTDOMAIN);
140+
/*
141+
* Perform one-time setup of PL/Python, after checking for a conflict
142+
* with other versions of Python.
143+
*/
144+
staticvoid
145+
PLy_initialize(void)
146+
{
147+
staticboolinited= false;
148+
149+
/*
150+
* Check for multiple Python libraries before actively doing anything with
151+
* libpython. This must be repeated on each entry to PL/Python, in case a
152+
* conflicting library got loaded since we last looked.
153+
*
154+
* It is attractive to weaken this error from FATAL to ERROR, but there
155+
* would be corner cases, so it seems best to be conservative.
156+
*/
157+
if (*plpython_version_bitmask_ptr!= (1 <<PY_MAJOR_VERSION))
158+
ereport(FATAL,
159+
(errmsg("multiple Python libraries are present in session"),
160+
errdetail("Only one Python major version can be used in one session.")));
161+
162+
/* The rest should only be done once per session */
163+
if (inited)
164+
return;
109165

110166
#ifPY_MAJOR_VERSION >=3
111167
PyImport_AppendInittab("plpy",PyInit_plpy);
@@ -129,7 +185,7 @@ _PG_init(void)
129185
}
130186

131187
/*
132-
* This shouldonlybe called once from_PG_init. Initialize the Python
188+
* This should be calledonlyonce, fromPLy_initialize. Initialize the Python
133189
* interpreter and global data.
134190
*/
135191
staticvoid
@@ -164,9 +220,10 @@ plpython_validator(PG_FUNCTION_ARGS)
164220
PG_RETURN_VOID();
165221

166222
if (!check_function_bodies)
167-
{
168223
PG_RETURN_VOID();
169-
}
224+
225+
/* Do this only after making sure we need to do something */
226+
PLy_initialize();
170227

171228
/* Get the new function's pg_proc entry */
172229
tuple=SearchSysCache1(PROCOID,ObjectIdGetDatum(funcoid));
@@ -200,6 +257,8 @@ plpython_call_handler(PG_FUNCTION_ARGS)
200257
PLyExecutionContext*exec_ctx;
201258
ErrorContextCallbackplerrcontext;
202259

260+
PLy_initialize();
261+
203262
/* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
204263
if (SPI_connect()!=SPI_OK_CONNECT)
205264
elog(ERROR,"SPI_connect failed");
@@ -275,6 +334,8 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
275334
PLyExecutionContext*exec_ctx;
276335
ErrorContextCallbackplerrcontext;
277336

337+
PLy_initialize();
338+
278339
/* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
279340
if (SPI_connect()!=SPI_OK_CONNECT)
280341
elog(ERROR,"SPI_connect failed");

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp