@@ -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+ typedef struct PLyProcedureEntry
223+ {
224+ Oid fn_oid ; /* hash key */
225+ PLyProcedure * proc ;
226+ } PLyProcedureEntry ;
227+
228+
222229/* Python objects */
223230typedef struct PLyPlanObject
224231{
@@ -311,11 +318,10 @@ static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *,
311318
312319static PyObject * PLy_procedure_call (PLyProcedure * , char * , PyObject * );
313320
314- static PLyProcedure * PLy_procedure_get (FunctionCallInfo fcinfo ,
315- Oid tgreloid );
321+ static PLyProcedure * PLy_procedure_get (Oid fn_oid , bool is_trigger );
316322
317- static PLyProcedure * PLy_procedure_create (HeapTuple procTup , Oid tgreloid ,
318- char * key );
323+ static PLyProcedure * PLy_procedure_create (HeapTuple procTup ,
324+ Oid fn_oid , bool is_trigger );
319325
320326static void PLy_procedure_compile (PLyProcedure * , const char * );
321327static char * PLy_procedure_munge_source (const char * , const char * );
@@ -373,7 +379,8 @@ static ErrorData *PLy_error_in_progress = NULL;
373379
374380static PyObject * PLy_interp_globals = NULL ;
375381static PyObject * PLy_interp_safe_globals = NULL ;
376- static PyObject * PLy_procedure_cache = NULL ;
382+ static HTAB * PLy_procedure_cache = NULL ;
383+ static HTAB * PLy_trigger_cache = NULL ;
377384
378385/* Python exceptions */
379386static PyObject * PLy_exc_error = NULL ;
@@ -444,7 +451,6 @@ plpython_call_handler(PG_FUNCTION_ARGS)
444451{
445452 Datum retval ;
446453 PLyProcedure * save_curr_proc ;
447- PLyProcedure * volatile proc = NULL ;
448454 ErrorContextCallback plerrcontext ;
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 HeapTuple trv ;
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 return retval ;
503502}
504503
@@ -575,6 +574,22 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
575574 HeapTuple rv = NULL ;
576575 PyObject * volatile plargs = NULL ;
577576 PyObject * volatile plrv = 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+ static bool
1307+ PLy_procedure_valid (PLyProcedure * proc , HeapTuple procTup )
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 */
12981326static PLyProcedure *
1299- PLy_procedure_get (FunctionCallInfo fcinfo , Oid tgreloid )
1327+ PLy_procedure_get (Oid fn_oid , bool is_trigger )
13001328{
1301- Oid fn_oid ;
13021329 HeapTuple procTup ;
1303- char key [128 ];
1304- PyObject * plproc ;
1305- PLyProcedure * proc = NULL ;
1306- int rv ;
1330+ PLyProcedureEntry * entry ;
1331+ bool found ;
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+ else if (!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- return proc ;
1376+ return entry -> proc ;
13621377}
13631378
1379+ /*
1380+ * Create a new PLyProcedure structure
1381+ */
13641382static PLyProcedure *
1365- PLy_procedure_create (HeapTuple procTup , Oid tgreloid , char * key )
1383+ PLy_procedure_create (HeapTuple procTup , Oid fn_oid , bool is_trigger )
13661384{
13671385 char procName [NAMEDATALEN + 256 ];
13681386 Form_pg_proc procStruct ;
@@ -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 HeapTuple rvTypeTup ;
14191429 Form_pg_type rvTypeStruct ;
@@ -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 return proc ;
15701575}
15711576
1577+ /*
1578+ * Insert the procedure into the Python interpreter
1579+ */
15721580static void
15731581PLy_procedure_compile (PLyProcedure * proc , const char * 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 int clen ;
15971605 char call [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 static bool inited = false;
32343238 const int * * version_ptr ;
3239+ HASHCTL hash_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