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

Commit2281575

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 parent78b7aaa commit2281575

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
@@ -63,6 +63,9 @@ static void PLy_init_interp(void);
6363
staticPLyExecutionContext*PLy_push_execution_context(void);
6464
staticvoidPLy_pop_execution_context(void);
6565

66+
/* static state for Python library conflict detection */
67+
staticint*plpython_version_bitmask_ptr=NULL;
68+
staticintplpython_version_bitmask=0;
6669
staticconstintplpython_python_version=PY_MAJOR_VERSION;
6770

6871
/* initialize global variables */
@@ -75,28 +78,81 @@ static PLyExecutionContext *PLy_execution_contexts = NULL;
7578
void
7679
_PG_init(void)
7780
{
78-
/* Be sure we do initialization only once (should be redundant now) */
79-
staticboolinited= false;
81+
int**bitmask_ptr;
8082
constint**version_ptr;
8183

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

85-
/* Be sure we don't run Python 2 and 3 in the same session (might crash) */
100+
/*
101+
* This should be safe even in the presence of conflicting plpythons, and
102+
* it's necessary to do it here for the next error to be localized.
103+
*/
104+
pg_bindtextdomain(TEXTDOMAIN);
105+
106+
/*
107+
* We used to have a scheme whereby PL/Python would fail immediately if
108+
* loaded into a session in which a conflicting libpython is already
109+
* present. We don't like to do that anymore, but it seems possible that
110+
* a plpython library adhering to the old convention is present in the
111+
* session, in which case we have to fail. We detect an old library if
112+
* plpython_python_version is already defined but the indicated version
113+
* isn't reflected in plpython_version_bitmask. Otherwise, set the
114+
* variable so that the right thing happens if an old library is loaded
115+
* later.
116+
*/
86117
version_ptr= (constint**)find_rendezvous_variable("plpython_python_version");
87118
if (!(*version_ptr))
88119
*version_ptr=&plpython_python_version;
89120
else
90121
{
91-
if (**version_ptr!=plpython_python_version)
122+
if ((*plpython_version_bitmask_ptr& (1 <<**version_ptr))==0)
92123
ereport(FATAL,
93124
(errmsg("Python major version mismatch in session"),
94125
errdetail("This session has previously used Python major version %d, and it is now attempting to use Python major version %d.",
95126
**version_ptr,plpython_python_version),
96127
errhint("Start a new session to use a different Python major version.")));
97128
}
129+
}
98130

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

101157
#ifPY_MAJOR_VERSION >=3
102158
PyImport_AppendInittab("plpy",PyInit_plpy);
@@ -120,7 +176,7 @@ _PG_init(void)
120176
}
121177

122178
/*
123-
* This shouldonlybe called once from_PG_init. Initialize the Python
179+
* This should be calledonlyonce, fromPLy_initialize. Initialize the Python
124180
* interpreter and global data.
125181
*/
126182
staticvoid
@@ -155,9 +211,10 @@ plpython_validator(PG_FUNCTION_ARGS)
155211
PG_RETURN_VOID();
156212

157213
if (!check_function_bodies)
158-
{
159214
PG_RETURN_VOID();
160-
}
215+
216+
/* Do this only after making sure we need to do something */
217+
PLy_initialize();
161218

162219
/* Get the new function's pg_proc entry */
163220
tuple=SearchSysCache1(PROCOID,ObjectIdGetDatum(funcoid));
@@ -191,6 +248,8 @@ plpython_call_handler(PG_FUNCTION_ARGS)
191248
PLyExecutionContext*exec_ctx;
192249
ErrorContextCallbackplerrcontext;
193250

251+
PLy_initialize();
252+
194253
/* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
195254
if (SPI_connect()!=SPI_OK_CONNECT)
196255
elog(ERROR,"SPI_connect failed");
@@ -266,6 +325,8 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
266325
PLyExecutionContext*exec_ctx;
267326
ErrorContextCallbackplerrcontext;
268327

328+
PLy_initialize();
329+
269330
/* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
270331
if (SPI_connect()!=SPI_OK_CONNECT)
271332
elog(ERROR,"SPI_connect failed");

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp