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

Commited75380

Browse files
committed
Create a stack of pl/python "execution contexts".
This replaces the former global variable PLy_curr_procedure, and providesa place to stash per-call-level information. In particular we create aper-call-level scratch memory context.For the moment, the scratch context is just used to avoid leaking memoryfrom datatype output function calls in PLyDict_FromTuple. There probablywill be more use-cases in future.Although this is a fix for a pre-existing memory leakage bug, it seemssufficiently invasive to not want to back-patch; it feels better as partof the major rearrangement of plpython code that we've already done aspart of 9.2.Jan Urbański
1 parent2e46bf6 commited75380

File tree

9 files changed

+141
-40
lines changed

9 files changed

+141
-40
lines changed

‎src/pl/plpython/plpy_cursorobject.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include"plpy_cursorobject.h"
1515

1616
#include"plpy_elog.h"
17+
#include"plpy_main.h"
1718
#include"plpy_planobject.h"
1819
#include"plpy_procedure.h"
1920
#include"plpy_resultobject.h"
@@ -119,6 +120,7 @@ PLy_cursor_query(const char *query)
119120

120121
PG_TRY();
121122
{
123+
PLyExecutionContext*exec_ctx=PLy_current_execution_context();
122124
SPIPlanPtrplan;
123125
Portalportal;
124126

@@ -130,7 +132,7 @@ PLy_cursor_query(const char *query)
130132
SPI_result_code_string(SPI_result));
131133

132134
portal=SPI_cursor_open(NULL,plan,NULL,NULL,
133-
PLy_curr_procedure->fn_readonly);
135+
exec_ctx->curr_proc->fn_readonly);
134136
SPI_freeplan(plan);
135137

136138
if (portal==NULL)
@@ -207,6 +209,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
207209

208210
PG_TRY();
209211
{
212+
PLyExecutionContext*exec_ctx=PLy_current_execution_context();
210213
Portalportal;
211214
char*volatilenulls;
212215
volatileintj;
@@ -253,7 +256,7 @@ PLy_cursor_plan(PyObject *ob, PyObject *args)
253256
}
254257

255258
portal=SPI_cursor_open(NULL,plan->plan,plan->values,nulls,
256-
PLy_curr_procedure->fn_readonly);
259+
exec_ctx->curr_proc->fn_readonly);
257260
if (portal==NULL)
258261
elog(ERROR,"SPI_cursor_open() failed: %s",
259262
SPI_result_code_string(SPI_result));

‎src/pl/plpython/plpy_elog.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include"plpy_elog.h"
1414

15+
#include"plpy_main.h"
1516
#include"plpy_procedure.h"
1617

1718

@@ -255,6 +256,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
255256
/* The first frame always points at <module>, skip it. */
256257
if (*tb_depth>0)
257258
{
259+
PLyExecutionContext*exec_ctx=PLy_current_execution_context();
258260
char*proname;
259261
char*fname;
260262
char*line;
@@ -270,7 +272,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
270272
else
271273
fname=PyString_AsString(name);
272274

273-
proname=PLy_procedure_name(PLy_curr_procedure);
275+
proname=PLy_procedure_name(exec_ctx->curr_proc);
274276
plain_filename=PyString_AsString(filename);
275277
plain_lineno=PyInt_AsLong(lineno);
276278

@@ -287,7 +289,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
287289
* function code object was compiled with "<string>" as the
288290
* filename
289291
*/
290-
if (PLy_curr_procedure&&plain_filename!=NULL&&
292+
if (exec_ctx->curr_proc&&plain_filename!=NULL&&
291293
strcmp(plain_filename,"<string>")==0)
292294
{
293295
/*
@@ -299,7 +301,7 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
299301
* for. But we do not go as far as traceback.py in reading
300302
* the source of imported modules.
301303
*/
302-
line=get_source_line(PLy_curr_procedure->src,plain_lineno);
304+
line=get_source_line(exec_ctx->curr_proc->src,plain_lineno);
303305
if (line)
304306
{
305307
appendStringInfo(&tbstr,"\n %s",line);

‎src/pl/plpython/plpy_exec.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,9 @@ PLy_function_delete_args(PLyProcedure *proc)
455455
staticvoid
456456
plpython_return_error_callback(void*arg)
457457
{
458-
if (PLy_curr_procedure)
458+
PLyExecutionContext*exec_ctx=PLy_current_execution_context();
459+
460+
if (exec_ctx->curr_proc)
459461
errcontext("while creating return value");
460462
}
461463

@@ -781,7 +783,9 @@ PLy_modify_tuple(PLyProcedure *proc, PyObject *pltd, TriggerData *tdata,
781783
staticvoid
782784
plpython_trigger_error_callback(void*arg)
783785
{
784-
if (PLy_curr_procedure)
786+
PLyExecutionContext*exec_ctx=PLy_current_execution_context();
787+
788+
if (exec_ctx->curr_proc)
785789
errcontext("while modifying trigger row");
786790
}
787791

‎src/pl/plpython/plpy_main.c

Lines changed: 90 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include"executor/spi.h"
1313
#include"miscadmin.h"
1414
#include"utils/guc.h"
15+
#include"utils/memutils.h"
1516
#include"utils/syscache.h"
1617

1718
#include"plpython.h"
@@ -66,11 +67,17 @@ static void plpython_error_callback(void *arg);
6667
staticvoidplpython_inline_error_callback(void*arg);
6768
staticvoidPLy_init_interp(void);
6869

70+
staticPLyExecutionContext*PLy_push_execution_context(void);
71+
staticvoidPLy_pop_execution_context(void);
72+
6973
staticconstintplpython_python_version=PY_MAJOR_VERSION;
7074

7175
/* initialize global variables */
7276
PyObject*PLy_interp_globals=NULL;
7377

78+
/* this doesn't need to be global; use PLy_current_execution_context() */
79+
staticPLyExecutionContext*PLy_execution_contexts=NULL;
80+
7481

7582
void
7683
_PG_init(void)
@@ -114,6 +121,8 @@ _PG_init(void)
114121

115122
explicit_subtransactions=NIL;
116123

124+
PLy_execution_contexts=NULL;
125+
117126
inited= true;
118127
}
119128

@@ -179,13 +188,20 @@ Datum
179188
plpython_call_handler(PG_FUNCTION_ARGS)
180189
{
181190
Datumretval;
182-
PLyProcedure*save_curr_proc;
191+
PLyExecutionContext*exec_ctx;
183192
ErrorContextCallbackplerrcontext;
184193

194+
/* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
185195
if (SPI_connect()!=SPI_OK_CONNECT)
186196
elog(ERROR,"SPI_connect failed");
187197

188-
save_curr_proc=PLy_curr_procedure;
198+
/*
199+
* Push execution context onto stack. It is important that this get
200+
* popped again, so avoid putting anything that could throw error between
201+
* here and the PG_TRY. (plpython_error_callback expects the stack entry
202+
* to be there, so we have to make the context first.)
203+
*/
204+
exec_ctx=PLy_push_execution_context();
189205

190206
/*
191207
* Setup error traceback support for ereport()
@@ -203,29 +219,29 @@ plpython_call_handler(PG_FUNCTION_ARGS)
203219
HeapTupletrv;
204220

205221
proc=PLy_procedure_get(fcinfo->flinfo->fn_oid, true);
206-
PLy_curr_procedure=proc;
222+
exec_ctx->curr_proc=proc;
207223
trv=PLy_exec_trigger(fcinfo,proc);
208224
retval=PointerGetDatum(trv);
209225
}
210226
else
211227
{
212228
proc=PLy_procedure_get(fcinfo->flinfo->fn_oid, false);
213-
PLy_curr_procedure=proc;
229+
exec_ctx->curr_proc=proc;
214230
retval=PLy_exec_function(fcinfo,proc);
215231
}
216232
}
217233
PG_CATCH();
218234
{
219-
PLy_curr_procedure=save_curr_proc;
235+
PLy_pop_execution_context();
220236
PyErr_Clear();
221237
PG_RE_THROW();
222238
}
223239
PG_END_TRY();
224240

225241
/* Pop the error context stack */
226242
error_context_stack=plerrcontext.previous;
227-
228-
PLy_curr_procedure=save_curr_proc;
243+
/* ... and then the execution context */
244+
PLy_pop_execution_context();
229245

230246
returnretval;
231247
}
@@ -244,22 +260,14 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
244260
InlineCodeBlock*codeblock= (InlineCodeBlock*)DatumGetPointer(PG_GETARG_DATUM(0));
245261
FunctionCallInfoDatafake_fcinfo;
246262
FmgrInfoflinfo;
247-
PLyProcedure*save_curr_proc;
248263
PLyProcedureproc;
264+
PLyExecutionContext*exec_ctx;
249265
ErrorContextCallbackplerrcontext;
250266

267+
/* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
251268
if (SPI_connect()!=SPI_OK_CONNECT)
252269
elog(ERROR,"SPI_connect failed");
253270

254-
save_curr_proc=PLy_curr_procedure;
255-
256-
/*
257-
* Setup error traceback support for ereport()
258-
*/
259-
plerrcontext.callback=plpython_inline_error_callback;
260-
plerrcontext.previous=error_context_stack;
261-
error_context_stack=&plerrcontext;
262-
263271
MemSet(&fake_fcinfo,0,sizeof(fake_fcinfo));
264272
MemSet(&flinfo,0,sizeof(flinfo));
265273
fake_fcinfo.flinfo=&flinfo;
@@ -270,27 +278,44 @@ plpython_inline_handler(PG_FUNCTION_ARGS)
270278
proc.pyname=PLy_strdup("__plpython_inline_block");
271279
proc.result.out.d.typoid=VOIDOID;
272280

281+
/*
282+
* Push execution context onto stack. It is important that this get
283+
* popped again, so avoid putting anything that could throw error between
284+
* here and the PG_TRY. (plpython_inline_error_callback doesn't currently
285+
* need the stack entry, but for consistency with plpython_call_handler
286+
* we do it in this order.)
287+
*/
288+
exec_ctx=PLy_push_execution_context();
289+
290+
/*
291+
* Setup error traceback support for ereport()
292+
*/
293+
plerrcontext.callback=plpython_inline_error_callback;
294+
plerrcontext.previous=error_context_stack;
295+
error_context_stack=&plerrcontext;
296+
273297
PG_TRY();
274298
{
275299
PLy_procedure_compile(&proc,codeblock->source_text);
276-
PLy_curr_procedure=&proc;
300+
exec_ctx->curr_proc=&proc;
277301
PLy_exec_function(&fake_fcinfo,&proc);
278302
}
279303
PG_CATCH();
280304
{
305+
PLy_pop_execution_context();
281306
PLy_procedure_delete(&proc);
282-
PLy_curr_procedure=save_curr_proc;
283307
PyErr_Clear();
284308
PG_RE_THROW();
285309
}
286310
PG_END_TRY();
287311

288-
PLy_procedure_delete(&proc);
289-
290312
/* Pop the error context stack */
291313
error_context_stack=plerrcontext.previous;
314+
/* ... and then the execution context */
315+
PLy_pop_execution_context();
292316

293-
PLy_curr_procedure=save_curr_proc;
317+
/* Now clean up the transient procedure we made */
318+
PLy_procedure_delete(&proc);
294319

295320
PG_RETURN_VOID();
296321
}
@@ -313,13 +338,54 @@ static bool PLy_procedure_is_trigger(Form_pg_proc procStruct)
313338
staticvoid
314339
plpython_error_callback(void*arg)
315340
{
316-
if (PLy_curr_procedure)
341+
PLyExecutionContext*exec_ctx=PLy_current_execution_context();
342+
343+
if (exec_ctx->curr_proc)
317344
errcontext("PL/Python function \"%s\"",
318-
PLy_procedure_name(PLy_curr_procedure));
345+
PLy_procedure_name(exec_ctx->curr_proc));
319346
}
320347

321348
staticvoid
322349
plpython_inline_error_callback(void*arg)
323350
{
324351
errcontext("PL/Python anonymous code block");
325352
}
353+
354+
PLyExecutionContext*
355+
PLy_current_execution_context(void)
356+
{
357+
if (PLy_execution_contexts==NULL)
358+
elog(ERROR,"no Python function is currently executing");
359+
360+
returnPLy_execution_contexts;
361+
}
362+
363+
staticPLyExecutionContext*
364+
PLy_push_execution_context(void)
365+
{
366+
PLyExecutionContext*context=PLy_malloc(sizeof(PLyExecutionContext));
367+
368+
context->curr_proc=NULL;
369+
context->scratch_ctx=AllocSetContextCreate(TopTransactionContext,
370+
"PL/Python scratch context",
371+
ALLOCSET_DEFAULT_MINSIZE,
372+
ALLOCSET_DEFAULT_INITSIZE,
373+
ALLOCSET_DEFAULT_MAXSIZE);
374+
context->next=PLy_execution_contexts;
375+
PLy_execution_contexts=context;
376+
returncontext;
377+
}
378+
379+
staticvoid
380+
PLy_pop_execution_context(void)
381+
{
382+
PLyExecutionContext*context=PLy_execution_contexts;
383+
384+
if (context==NULL)
385+
elog(ERROR,"no Python function is currently executing");
386+
387+
PLy_execution_contexts=context->next;
388+
389+
MemoryContextDelete(context->scratch_ctx);
390+
PLy_free(context);
391+
}

‎src/pl/plpython/plpy_main.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,19 @@
1010
/* the interpreter's globals dict */
1111
externPyObject*PLy_interp_globals;
1212

13+
/*
14+
* A stack of PL/Python execution contexts. Each time user-defined Python code
15+
* is called, an execution context is created and put on the stack. After the
16+
* Python code returns, the context is destroyed.
17+
*/
18+
typedefstructPLyExecutionContext
19+
{
20+
PLyProcedure*curr_proc;/* the currently executing procedure */
21+
MemoryContextscratch_ctx;/* a context for things like type I/O */
22+
structPLyExecutionContext*next;/* previous stack level */
23+
}PLyExecutionContext;
24+
25+
/* Get the current execution context */
26+
externPLyExecutionContext*PLy_current_execution_context(void);
27+
1328
#endif/* PLPY_MAIN_H */

‎src/pl/plpython/plpy_procedure.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@
2222
#include"plpy_main.h"
2323

2424

25-
PLyProcedure*PLy_curr_procedure=NULL;
26-
27-
2825
staticHTAB*PLy_procedure_cache=NULL;
2926
staticHTAB*PLy_trigger_cache=NULL;
3027

‎src/pl/plpython/plpy_procedure.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,4 @@ extern PLyProcedure *PLy_procedure_get(Oid fn_oid, bool is_trigger);
4545
externvoidPLy_procedure_compile(PLyProcedure*proc,constchar*src);
4646
externvoidPLy_procedure_delete(PLyProcedure*proc);
4747

48-
49-
/* currently active plpython function */
50-
externPLyProcedure*PLy_curr_procedure;
51-
5248
#endif/* PLPY_PROCEDURE_H */

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp