@@ -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
214215PyObject * code ;/* compiled procedure code */
215216PyObject * statics ;/* data saved across calls, local scope */
216217PyObject * 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{
445452Datum retval ;
446453PLyProcedure * save_curr_proc ;
447- PLyProcedure * volatile proc = NULL ;
448454ErrorContextCallback plerrcontext ;
449455
450456if (SPI_connect ()!= SPI_OK_CONNECT )
@@ -461,32 +467,27 @@ plpython_call_handler(PG_FUNCTION_ARGS)
461467
462468PG_TRY ();
463469{
470+ PLyProcedure * proc ;
471+
464472if (CALLED_AS_TRIGGER (fcinfo ))
465473{
466- TriggerData * tdata = (TriggerData * )fcinfo -> context ;
467474HeapTuple trv ;
468475
469- proc = PLy_procedure_get (fcinfo ,
470- RelationGetRelid (tdata -> tg_relation ));
476+ proc = PLy_procedure_get (fcinfo -> flinfo -> fn_oid , true);
471477PLy_curr_procedure = proc ;
472478trv = PLy_trigger_handler (fcinfo ,proc );
473479retval = PointerGetDatum (trv );
474480}
475481else
476482{
477- proc = PLy_procedure_get (fcinfo , InvalidOid );
483+ proc = PLy_procedure_get (fcinfo -> flinfo -> fn_oid , false );
478484PLy_curr_procedure = proc ;
479485retval = PLy_function_handler (fcinfo ,proc );
480486}
481487}
482488PG_CATCH ();
483489{
484490PLy_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- }
490491PyErr_Clear ();
491492PG_RE_THROW ();
492493}
@@ -497,8 +498,6 @@ plpython_call_handler(PG_FUNCTION_ARGS)
497498
498499PLy_curr_procedure = save_curr_proc ;
499500
500- Py_DECREF (proc -> me );
501-
502501return retval ;
503502}
504503
@@ -575,6 +574,22 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
575574HeapTuple rv = NULL ;
576575PyObject * volatile plargs = NULL ;
577576PyObject * 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
579594PG_TRY ();
580595{
@@ -1285,6 +1300,19 @@ PLy_function_delete_args(PLyProcedure *proc)
12851300PyDict_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 ;
13021329HeapTuple 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 ;
13091333procTup = SearchSysCache1 (PROCOID ,ObjectIdGetDatum (fn_oid ));
13101334if (!HeapTupleIsValid (procTup ))
13111335elog (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
13591374ReleaseSysCache (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{
13671385char procName [NAMEDATALEN + 256 ];
13681386Form_pg_proc procStruct ;
@@ -1374,18 +1392,10 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
13741392rv ;
13751393
13761394procStruct = (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 );
13891399if (rv >=sizeof (procName )|| rv < 0 )
13901400elog (ERROR ,"procedure name would overrun buffer" );
13911401
@@ -1402,7 +1412,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
14021412PLy_typeinfo_init (& proc -> args [i ]);
14031413proc -> nargs = 0 ;
14041414proc -> code = proc -> statics = NULL ;
1405- proc -> globals = proc -> me = NULL ;
1415+ proc -> globals = NULL ;
14061416proc -> is_setof = procStruct -> proretset ;
14071417proc -> setof = NULL ;
14081418proc -> 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{
14181428HeapTuple rvTypeTup ;
14191429Form_pg_type rvTypeStruct ;
@@ -1550,11 +1560,6 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
15501560
15511561pfree (procSource );
15521562procSource = 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}
15591564PG_CATCH ();
15601565{
@@ -1569,6 +1574,9 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
15691574return 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)
15911599crv = PyRun_String (msrc ,Py_file_input ,proc -> globals ,NULL );
15921600free (msrc );
15931601
1594- if (crv != NULL && (! PyErr_Occurred ()) )
1602+ if (crv != NULL )
15951603{
15961604int clen ;
15971605char call [NAMEDATALEN + 256 ];
@@ -1605,11 +1613,9 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
16051613if (clen < 0 || clen >=sizeof (call ))
16061614elog (ERROR ,"string would overflow buffer" );
16071615proc -> code = Py_CompileString (call ,"<string>" ,Py_eval_input );
1608- if (proc -> code != NULL && (! PyErr_Occurred ()) )
1616+ if (proc -> code != NULL )
16091617return ;
16101618}
1611- else
1612- Py_XDECREF (crv );
16131619
16141620PLy_elog (ERROR ,"could not compile PL/Python function \"%s\"" ,proc -> proname );
16151621}
@@ -1667,7 +1673,6 @@ PLy_procedure_delete(PLyProcedure *proc)
16671673Py_XDECREF (proc -> code );
16681674Py_XDECREF (proc -> statics );
16691675Py_XDECREF (proc -> globals );
1670- Py_XDECREF (proc -> me );
16711676if (proc -> proname )
16721677PLy_free (proc -> proname );
16731678if (proc -> pyname )
@@ -1686,7 +1691,6 @@ PLy_procedure_delete(PLyProcedure *proc)
16861691}
16871692if (proc -> argnames )
16881693PLy_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) */
32333237static bool inited = false;
32343238const int * * version_ptr ;
3239+ HASHCTL hash_ctl ;
32353240
32363241if (inited )
32373242return ;
@@ -3263,9 +3268,20 @@ _PG_init(void)
32633268PLy_init_plpy ();
32643269if (PyErr_Occurred ())
32653270PLy_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
32703286inited = true;
32713287}