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

Commit46211da

Browse files
committed
Use HTABs instead of Python dictionary objects to cache procedures
Two separate hash tables are used for regular procedures and fortrigger procedures, since the way trigger procedures work is quitedifferent from normal stored procedures. Change the signatures ofPLy_procedure_{get,create} to accept the function OID and a Booleanflag indicating whether it's a trigger. This should make implementinga PL/Python validator easier.Using HTABs instead of Python dictionaries makes error recoveryeasier, and allows for procedures to be cached based on their OIDs,not their names. It also allows getting rid of the PyCObject fieldthat used to hold a pointer to PLyProcedure, since PyCObjects aredeprecated in Python 2.7 and replaced by Capsules in Python 3.Jan Urbański
1 parentbdd8ed9 commit46211da

File tree

1 file changed

+110
-94
lines changed

1 file changed

+110
-94
lines changed

‎src/pl/plpython/plpython.c

Lines changed: 110 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ typedef int Py_ssize_t;
102102
#include"parser/parse_type.h"
103103
#include"tcop/tcopprot.h"
104104
#include"utils/builtins.h"
105+
#include"utils/hsearch.h"
105106
#include"utils/lsyscache.h"
106107
#include"utils/memutils.h"
107108
#include"utils/syscache.h"
@@ -214,11 +215,17 @@ typedef struct PLyProcedure
214215
PyObject*code;/* compiled procedure code */
215216
PyObject*statics;/* data saved across calls, local scope */
216217
PyObject*globals;/* data saved across calls, global scope */
217-
PyObject*me;/* PyCObject containing pointer to this
218-
* PLyProcedure */
219218
}PLyProcedure;
220219

221220

221+
/* the procedure cache entry */
222+
typedefstructPLyProcedureEntry
223+
{
224+
Oidfn_oid;/* hash key */
225+
PLyProcedure*proc;
226+
}PLyProcedureEntry;
227+
228+
222229
/* Python objects */
223230
typedefstructPLyPlanObject
224231
{
@@ -311,11 +318,10 @@ static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *,
311318

312319
staticPyObject*PLy_procedure_call(PLyProcedure*,char*,PyObject*);
313320

314-
staticPLyProcedure*PLy_procedure_get(FunctionCallInfofcinfo,
315-
Oidtgreloid);
321+
staticPLyProcedure*PLy_procedure_get(Oidfn_oid,boolis_trigger);
316322

317-
staticPLyProcedure*PLy_procedure_create(HeapTupleprocTup,Oidtgreloid,
318-
char*key);
323+
staticPLyProcedure*PLy_procedure_create(HeapTupleprocTup,
324+
Oidfn_oid,boolis_trigger);
319325

320326
staticvoidPLy_procedure_compile(PLyProcedure*,constchar*);
321327
staticchar*PLy_procedure_munge_source(constchar*,constchar*);
@@ -373,7 +379,8 @@ static ErrorData *PLy_error_in_progress = NULL;
373379

374380
staticPyObject*PLy_interp_globals=NULL;
375381
staticPyObject*PLy_interp_safe_globals=NULL;
376-
staticPyObject*PLy_procedure_cache=NULL;
382+
staticHTAB*PLy_procedure_cache=NULL;
383+
staticHTAB*PLy_trigger_cache=NULL;
377384

378385
/* Python exceptions */
379386
staticPyObject*PLy_exc_error=NULL;
@@ -444,7 +451,6 @@ plpython_call_handler(PG_FUNCTION_ARGS)
444451
{
445452
Datumretval;
446453
PLyProcedure*save_curr_proc;
447-
PLyProcedure*volatileproc=NULL;
448454
ErrorContextCallbackplerrcontext;
449455

450456
if (SPI_connect()!=SPI_OK_CONNECT)
@@ -461,32 +467,27 @@ plpython_call_handler(PG_FUNCTION_ARGS)
461467

462468
PG_TRY();
463469
{
470+
PLyProcedure*proc;
471+
464472
if (CALLED_AS_TRIGGER(fcinfo))
465473
{
466-
TriggerData*tdata= (TriggerData*)fcinfo->context;
467474
HeapTupletrv;
468475

469-
proc=PLy_procedure_get(fcinfo,
470-
RelationGetRelid(tdata->tg_relation));
476+
proc=PLy_procedure_get(fcinfo->flinfo->fn_oid, true);
471477
PLy_curr_procedure=proc;
472478
trv=PLy_trigger_handler(fcinfo,proc);
473479
retval=PointerGetDatum(trv);
474480
}
475481
else
476482
{
477-
proc=PLy_procedure_get(fcinfo,InvalidOid);
483+
proc=PLy_procedure_get(fcinfo->flinfo->fn_oid, false);
478484
PLy_curr_procedure=proc;
479485
retval=PLy_function_handler(fcinfo,proc);
480486
}
481487
}
482488
PG_CATCH();
483489
{
484490
PLy_curr_procedure=save_curr_proc;
485-
if (proc)
486-
{
487-
/* note: Py_DECREF needs braces around it, as of 2003/08 */
488-
Py_DECREF(proc->me);
489-
}
490491
PyErr_Clear();
491492
PG_RE_THROW();
492493
}
@@ -497,8 +498,6 @@ plpython_call_handler(PG_FUNCTION_ARGS)
497498

498499
PLy_curr_procedure=save_curr_proc;
499500

500-
Py_DECREF(proc->me);
501-
502501
returnretval;
503502
}
504503

@@ -575,6 +574,22 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
575574
HeapTuplerv=NULL;
576575
PyObject*volatileplargs=NULL;
577576
PyObject*volatileplrv=NULL;
577+
TriggerData*tdata;
578+
579+
Assert(CALLED_AS_TRIGGER(fcinfo));
580+
581+
/*
582+
* Input/output conversion for trigger tuples. Use the result
583+
* TypeInfo variable to store the tuple conversion info. We do
584+
* this over again on each call to cover the possibility that the
585+
* relation's tupdesc changed since the trigger was last called.
586+
* PLy_input_tuple_funcs and PLy_output_tuple_funcs are
587+
* responsible for not doing repetitive work.
588+
*/
589+
tdata= (TriggerData*)fcinfo->context;
590+
591+
PLy_input_tuple_funcs(&(proc->result),tdata->tg_relation->rd_att);
592+
PLy_output_tuple_funcs(&(proc->result),tdata->tg_relation->rd_att);
578593

579594
PG_TRY();
580595
{
@@ -1285,6 +1300,19 @@ PLy_function_delete_args(PLyProcedure *proc)
12851300
PyDict_DelItemString(proc->globals,proc->argnames[i]);
12861301
}
12871302

1303+
/*
1304+
* Decide whether a cached PLyProcedure struct is still valid
1305+
*/
1306+
staticbool
1307+
PLy_procedure_valid(PLyProcedure*proc,HeapTupleprocTup)
1308+
{
1309+
Assert(proc!=NULL);
1310+
1311+
/* If the pg_proc tuple has changed, it's not valid */
1312+
return (proc->fn_xmin==HeapTupleHeaderGetXmin(procTup->t_data)&&
1313+
ItemPointerEquals(&proc->fn_tid,&procTup->t_self));
1314+
}
1315+
12881316

12891317
/*
12901318
* PLyProcedure functions
@@ -1296,73 +1324,63 @@ PLy_function_delete_args(PLyProcedure *proc)
12961324
* function calls.
12971325
*/
12981326
staticPLyProcedure*
1299-
PLy_procedure_get(FunctionCallInfofcinfo,Oidtgreloid)
1327+
PLy_procedure_get(Oidfn_oid,boolis_trigger)
13001328
{
1301-
Oidfn_oid;
13021329
HeapTupleprocTup;
1303-
charkey[128];
1304-
PyObject*plproc;
1305-
PLyProcedure*proc=NULL;
1306-
intrv;
1330+
PLyProcedureEntry*entry;
1331+
boolfound;
13071332

1308-
fn_oid=fcinfo->flinfo->fn_oid;
13091333
procTup=SearchSysCache1(PROCOID,ObjectIdGetDatum(fn_oid));
13101334
if (!HeapTupleIsValid(procTup))
13111335
elog(ERROR,"cache lookup failed for function %u",fn_oid);
13121336

1313-
rv=snprintf(key,sizeof(key),"%u_%u",fn_oid,tgreloid);
1314-
if (rv >=sizeof(key)||rv<0)
1315-
elog(ERROR,"key too long");
1316-
1317-
plproc=PyDict_GetItemString(PLy_procedure_cache,key);
1337+
/* Look for the function in the corresponding cache */
1338+
if (is_trigger)
1339+
entry=hash_search(PLy_trigger_cache,
1340+
&fn_oid,HASH_ENTER,&found);
1341+
else
1342+
entry=hash_search(PLy_procedure_cache,
1343+
&fn_oid,HASH_ENTER,&found);
13181344

1319-
if (plproc!=NULL)
1345+
PG_TRY();
13201346
{
1321-
Py_INCREF(plproc);
1322-
if (!PyCObject_Check(plproc))
1323-
elog(FATAL,"expected a PyCObject, didn't get one");
1324-
1325-
proc=PyCObject_AsVoidPtr(plproc);
1326-
if (!proc)
1327-
PLy_elog(ERROR,"PyCObject_AsVoidPtr() failed");
1328-
if (proc->me!=plproc)
1329-
elog(FATAL,"proc->me != plproc");
1330-
/* did we find an up-to-date cache entry? */
1331-
if (proc->fn_xmin!=HeapTupleHeaderGetXmin(procTup->t_data)||
1332-
!ItemPointerEquals(&proc->fn_tid,&procTup->t_self))
1347+
if (!found)
1348+
{
1349+
/* Haven't found it, create a new cache entry */
1350+
entry->proc=PLy_procedure_create(procTup,fn_oid,is_trigger);
1351+
}
1352+
elseif (!PLy_procedure_valid(entry->proc,procTup))
13331353
{
1334-
Py_DECREF(plproc);
1335-
proc=NULL;
1354+
/* Found it, but it's invalid, free and reuse the cache entry */
1355+
PLy_procedure_delete(entry->proc);
1356+
PLy_free(entry->proc);
1357+
entry->proc=PLy_procedure_create(procTup,fn_oid,is_trigger);
13361358
}
1359+
/* Found it and it's valid, it's fine to use it */
13371360
}
1338-
1339-
if (proc==NULL)
1340-
proc=PLy_procedure_create(procTup,tgreloid,key);
1341-
1342-
if (OidIsValid(tgreloid))
1361+
PG_CATCH();
13431362
{
1344-
/*
1345-
* Input/output conversion for trigger tuples.Use the result
1346-
* TypeInfo variable to store the tuple conversion info. We do this
1347-
* over again on each call to cover the possibility that the
1348-
* relation's tupdesc changed since the trigger was last called.
1349-
* PLy_input_tuple_funcs and PLy_output_tuple_funcs are responsible
1350-
* for not doing repetitive work.
1351-
*/
1352-
TriggerData*tdata= (TriggerData*)fcinfo->context;
1353-
1354-
Assert(CALLED_AS_TRIGGER(fcinfo));
1355-
PLy_input_tuple_funcs(&(proc->result),tdata->tg_relation->rd_att);
1356-
PLy_output_tuple_funcs(&(proc->result),tdata->tg_relation->rd_att);
1363+
/* Do not leave an uninitialised entry in the cache */
1364+
if (is_trigger)
1365+
hash_search(PLy_trigger_cache,
1366+
&fn_oid,HASH_REMOVE,NULL);
1367+
else
1368+
hash_search(PLy_procedure_cache,
1369+
&fn_oid,HASH_REMOVE,NULL);
1370+
PG_RE_THROW();
13571371
}
1372+
PG_END_TRY();
13581373

13591374
ReleaseSysCache(procTup);
13601375

1361-
returnproc;
1376+
returnentry->proc;
13621377
}
13631378

1379+
/*
1380+
* Create a new PLyProcedure structure
1381+
*/
13641382
staticPLyProcedure*
1365-
PLy_procedure_create(HeapTupleprocTup,Oidtgreloid,char*key)
1383+
PLy_procedure_create(HeapTupleprocTup,Oidfn_oid,boolis_trigger)
13661384
{
13671385
charprocName[NAMEDATALEN+256];
13681386
Form_pg_procprocStruct;
@@ -1374,18 +1392,10 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
13741392
rv;
13751393

13761394
procStruct= (Form_pg_proc)GETSTRUCT(procTup);
1377-
1378-
if (OidIsValid(tgreloid))
1379-
rv=snprintf(procName,sizeof(procName),
1380-
"__plpython_procedure_%s_%u_trigger_%u",
1381-
NameStr(procStruct->proname),
1382-
HeapTupleGetOid(procTup),
1383-
tgreloid);
1384-
else
1385-
rv=snprintf(procName,sizeof(procName),
1386-
"__plpython_procedure_%s_%u",
1387-
NameStr(procStruct->proname),
1388-
HeapTupleGetOid(procTup));
1395+
rv=snprintf(procName,sizeof(procName),
1396+
"__plpython_procedure_%s_%u",
1397+
NameStr(procStruct->proname),
1398+
fn_oid);
13891399
if (rv >=sizeof(procName)||rv<0)
13901400
elog(ERROR,"procedure name would overrun buffer");
13911401

@@ -1402,7 +1412,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
14021412
PLy_typeinfo_init(&proc->args[i]);
14031413
proc->nargs=0;
14041414
proc->code=proc->statics=NULL;
1405-
proc->globals=proc->me=NULL;
1415+
proc->globals=NULL;
14061416
proc->is_setof=procStruct->proretset;
14071417
proc->setof=NULL;
14081418
proc->argnames=NULL;
@@ -1413,7 +1423,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
14131423
* get information required for output conversion of the return value,
14141424
* but only if this isn't a trigger.
14151425
*/
1416-
if (!OidIsValid(tgreloid))
1426+
if (!is_trigger)
14171427
{
14181428
HeapTuplervTypeTup;
14191429
Form_pg_typervTypeStruct;
@@ -1550,11 +1560,6 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
15501560

15511561
pfree(procSource);
15521562
procSource=NULL;
1553-
1554-
proc->me=PyCObject_FromVoidPtr(proc,NULL);
1555-
if (!proc->me)
1556-
PLy_elog(ERROR,"PyCObject_FromVoidPtr() failed");
1557-
PyDict_SetItemString(PLy_procedure_cache,key,proc->me);
15581563
}
15591564
PG_CATCH();
15601565
{
@@ -1569,6 +1574,9 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
15691574
returnproc;
15701575
}
15711576

1577+
/*
1578+
* Insert the procedure into the Python interpreter
1579+
*/
15721580
staticvoid
15731581
PLy_procedure_compile(PLyProcedure*proc,constchar*src)
15741582
{
@@ -1591,7 +1599,7 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
15911599
crv=PyRun_String(msrc,Py_file_input,proc->globals,NULL);
15921600
free(msrc);
15931601

1594-
if (crv!=NULL&& (!PyErr_Occurred()))
1602+
if (crv!=NULL)
15951603
{
15961604
intclen;
15971605
charcall[NAMEDATALEN+256];
@@ -1605,11 +1613,9 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
16051613
if (clen<0||clen >=sizeof(call))
16061614
elog(ERROR,"string would overflow buffer");
16071615
proc->code=Py_CompileString(call,"<string>",Py_eval_input);
1608-
if (proc->code!=NULL&& (!PyErr_Occurred()))
1616+
if (proc->code!=NULL)
16091617
return;
16101618
}
1611-
else
1612-
Py_XDECREF(crv);
16131619

16141620
PLy_elog(ERROR,"could not compile PL/Python function \"%s\"",proc->proname);
16151621
}
@@ -1667,7 +1673,6 @@ PLy_procedure_delete(PLyProcedure *proc)
16671673
Py_XDECREF(proc->code);
16681674
Py_XDECREF(proc->statics);
16691675
Py_XDECREF(proc->globals);
1670-
Py_XDECREF(proc->me);
16711676
if (proc->proname)
16721677
PLy_free(proc->proname);
16731678
if (proc->pyname)
@@ -1686,7 +1691,6 @@ PLy_procedure_delete(PLyProcedure *proc)
16861691
}
16871692
if (proc->argnames)
16881693
PLy_free(proc->argnames);
1689-
PLy_free(proc);
16901694
}
16911695

16921696
/*
@@ -3232,6 +3236,7 @@ _PG_init(void)
32323236
/* Be sure we do initialization only once (should be redundant now) */
32333237
staticboolinited= false;
32343238
constint**version_ptr;
3239+
HASHCTLhash_ctl;
32353240

32363241
if (inited)
32373242
return;
@@ -3263,9 +3268,20 @@ _PG_init(void)
32633268
PLy_init_plpy();
32643269
if (PyErr_Occurred())
32653270
PLy_elog(FATAL,"untrapped error in initialization");
3266-
PLy_procedure_cache=PyDict_New();
3267-
if (PLy_procedure_cache==NULL)
3268-
PLy_elog(ERROR,"could not create procedure cache");
3271+
3272+
memset(&hash_ctl,0,sizeof(hash_ctl));
3273+
hash_ctl.keysize=sizeof(Oid);
3274+
hash_ctl.entrysize=sizeof(PLyProcedureEntry);
3275+
hash_ctl.hash=oid_hash;
3276+
PLy_procedure_cache=hash_create("PL/Python procedures",32,&hash_ctl,
3277+
HASH_ELEM |HASH_FUNCTION);
3278+
3279+
memset(&hash_ctl,0,sizeof(hash_ctl));
3280+
hash_ctl.keysize=sizeof(Oid);
3281+
hash_ctl.entrysize=sizeof(PLyProcedureEntry);
3282+
hash_ctl.hash=oid_hash;
3283+
PLy_trigger_cache=hash_create("PL/Python triggers",32,&hash_ctl,
3284+
HASH_ELEM |HASH_FUNCTION);
32693285

32703286
inited= true;
32713287
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp