diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index d3b48ae67528ef6f622649a96c77455578c71cee..c25db9344f6ba19699529e190aa5170564b1e82b 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -102,6 +102,7 @@ typedef int Py_ssize_t;
 #include "parser/parse_type.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
+#include "utils/hsearch.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/syscache.h"
@@ -214,11 +215,17 @@ typedef struct PLyProcedure
 	PyObject   *code;			/* compiled procedure code */
 	PyObject   *statics;		/* data saved across calls, local scope */
 	PyObject   *globals;		/* data saved across calls, global scope */
-	PyObject   *me;				/* PyCObject containing pointer to this
-								 * PLyProcedure */
 } PLyProcedure;
 
 
+/* the procedure cache entry */
+typedef struct PLyProcedureEntry
+{
+	Oid			fn_oid;			/* hash key */
+	PLyProcedure *proc;
+} PLyProcedureEntry;
+
+
 /* Python objects */
 typedef struct PLyPlanObject
 {
@@ -311,11 +318,10 @@ static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *,
 
 static PyObject *PLy_procedure_call(PLyProcedure *, char *, PyObject *);
 
-static PLyProcedure *PLy_procedure_get(FunctionCallInfo fcinfo,
-				  Oid tgreloid);
+static PLyProcedure *PLy_procedure_get(Oid fn_oid, bool is_trigger);
 
-static PLyProcedure *PLy_procedure_create(HeapTuple procTup, Oid tgreloid,
-					 char *key);
+static PLyProcedure *PLy_procedure_create(HeapTuple procTup,
+										  Oid fn_oid, bool is_trigger);
 
 static void PLy_procedure_compile(PLyProcedure *, const char *);
 static char *PLy_procedure_munge_source(const char *, const char *);
@@ -373,7 +379,8 @@ static ErrorData *PLy_error_in_progress = NULL;
 
 static PyObject *PLy_interp_globals = NULL;
 static PyObject *PLy_interp_safe_globals = NULL;
-static PyObject *PLy_procedure_cache = NULL;
+static HTAB *PLy_procedure_cache = NULL;
+static HTAB *PLy_trigger_cache = NULL;
 
 /* Python exceptions */
 static PyObject *PLy_exc_error = NULL;
@@ -444,7 +451,6 @@ plpython_call_handler(PG_FUNCTION_ARGS)
 {
 	Datum		retval;
 	PLyProcedure *save_curr_proc;
-	PLyProcedure *volatile proc = NULL;
 	ErrorContextCallback plerrcontext;
 
 	if (SPI_connect() != SPI_OK_CONNECT)
@@ -461,20 +467,20 @@ plpython_call_handler(PG_FUNCTION_ARGS)
 
 	PG_TRY();
 	{
+		PLyProcedure *proc;
+
 		if (CALLED_AS_TRIGGER(fcinfo))
 		{
-			TriggerData *tdata = (TriggerData *) fcinfo->context;
 			HeapTuple	trv;
 
-			proc = PLy_procedure_get(fcinfo,
-									 RelationGetRelid(tdata->tg_relation));
+			proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, true);
 			PLy_curr_procedure = proc;
 			trv = PLy_trigger_handler(fcinfo, proc);
 			retval = PointerGetDatum(trv);
 		}
 		else
 		{
-			proc = PLy_procedure_get(fcinfo, InvalidOid);
+			proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, false);
 			PLy_curr_procedure = proc;
 			retval = PLy_function_handler(fcinfo, proc);
 		}
@@ -482,11 +488,6 @@ plpython_call_handler(PG_FUNCTION_ARGS)
 	PG_CATCH();
 	{
 		PLy_curr_procedure = save_curr_proc;
-		if (proc)
-		{
-			/* note: Py_DECREF needs braces around it, as of 2003/08 */
-			Py_DECREF(proc->me);
-		}
 		PyErr_Clear();
 		PG_RE_THROW();
 	}
@@ -497,8 +498,6 @@ plpython_call_handler(PG_FUNCTION_ARGS)
 
 	PLy_curr_procedure = save_curr_proc;
 
-	Py_DECREF(proc->me);
-
 	return retval;
 }
 
@@ -575,6 +574,22 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
 	HeapTuple	rv = NULL;
 	PyObject   *volatile plargs = NULL;
 	PyObject   *volatile plrv = NULL;
+	TriggerData	*tdata;
+
+	Assert(CALLED_AS_TRIGGER(fcinfo));
+
+	/*
+	 * Input/output conversion for trigger tuples.  Use the result
+	 * TypeInfo variable to store the tuple conversion info.  We do
+	 * this over again on each call to cover the possibility that the
+	 * relation's tupdesc changed since the trigger was last called.
+	 * PLy_input_tuple_funcs and PLy_output_tuple_funcs are
+	 * responsible for not doing repetitive work.
+	 */
+	tdata = (TriggerData *) fcinfo->context;
+
+	PLy_input_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);
+	PLy_output_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);
 
 	PG_TRY();
 	{
@@ -1285,6 +1300,19 @@ PLy_function_delete_args(PLyProcedure *proc)
 			PyDict_DelItemString(proc->globals, proc->argnames[i]);
 }
 
+/*
+ * Decide whether a cached PLyProcedure struct is still valid
+ */
+static bool
+PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
+{
+	Assert(proc != NULL);
+
+	/* If the pg_proc tuple has changed, it's not valid */
+	return (proc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
+			ItemPointerEquals(&proc->fn_tid, &procTup->t_self));
+}
+
 
 /*
  * PLyProcedure functions
@@ -1296,73 +1324,63 @@ PLy_function_delete_args(PLyProcedure *proc)
  * function calls.
  */
 static PLyProcedure *
-PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
+PLy_procedure_get(Oid fn_oid, bool is_trigger)
 {
-	Oid			fn_oid;
 	HeapTuple	procTup;
-	char		key[128];
-	PyObject   *plproc;
-	PLyProcedure *proc = NULL;
-	int			rv;
+	PLyProcedureEntry *entry;
+	bool		found;
 
-	fn_oid = fcinfo->flinfo->fn_oid;
 	procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
 	if (!HeapTupleIsValid(procTup))
 		elog(ERROR, "cache lookup failed for function %u", fn_oid);
 
-	rv = snprintf(key, sizeof(key), "%u_%u", fn_oid, tgreloid);
-	if (rv >= sizeof(key) || rv < 0)
-		elog(ERROR, "key too long");
-
-	plproc = PyDict_GetItemString(PLy_procedure_cache, key);
+	/* Look for the function in the corresponding cache */
+	if (is_trigger)
+		entry = hash_search(PLy_trigger_cache,
+							&fn_oid, HASH_ENTER, &found);
+	else
+		entry = hash_search(PLy_procedure_cache,
+							&fn_oid, HASH_ENTER, &found);
 
-	if (plproc != NULL)
+	PG_TRY();
 	{
-		Py_INCREF(plproc);
-		if (!PyCObject_Check(plproc))
-			elog(FATAL, "expected a PyCObject, didn't get one");
-
-		proc = PyCObject_AsVoidPtr(plproc);
-		if (!proc)
-			PLy_elog(ERROR, "PyCObject_AsVoidPtr() failed");
-		if (proc->me != plproc)
-			elog(FATAL, "proc->me != plproc");
-		/* did we find an up-to-date cache entry? */
-		if (proc->fn_xmin != HeapTupleHeaderGetXmin(procTup->t_data) ||
-			!ItemPointerEquals(&proc->fn_tid, &procTup->t_self))
+		if (!found)
+		{
+			/* Haven't found it, create a new cache entry */
+			entry->proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
+		}
+		else if (!PLy_procedure_valid(entry->proc, procTup))
 		{
-			Py_DECREF(plproc);
-			proc = NULL;
+			/* Found it, but it's invalid, free and reuse the cache entry */
+			PLy_procedure_delete(entry->proc);
+			PLy_free(entry->proc);
+			entry->proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
 		}
+		/* Found it and it's valid, it's fine to use it */
 	}
-
-	if (proc == NULL)
-		proc = PLy_procedure_create(procTup, tgreloid, key);
-
-	if (OidIsValid(tgreloid))
+	PG_CATCH();
 	{
-		/*
-		 * Input/output conversion for trigger tuples.	Use the result
-		 * TypeInfo variable to store the tuple conversion info.  We do this
-		 * over again on each call to cover the possibility that the
-		 * relation's tupdesc changed since the trigger was last called.
-		 * PLy_input_tuple_funcs and PLy_output_tuple_funcs are responsible
-		 * for not doing repetitive work.
-		 */
-		TriggerData *tdata = (TriggerData *) fcinfo->context;
-
-		Assert(CALLED_AS_TRIGGER(fcinfo));
-		PLy_input_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);
-		PLy_output_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);
+		/* Do not leave an uninitialised entry in the cache */
+		if (is_trigger)
+			hash_search(PLy_trigger_cache,
+						&fn_oid, HASH_REMOVE, NULL);
+		else
+			hash_search(PLy_procedure_cache,
+						&fn_oid, HASH_REMOVE, NULL);
+		PG_RE_THROW();
 	}
+	PG_END_TRY();
 
 	ReleaseSysCache(procTup);
 
-	return proc;
+	return entry->proc;
 }
 
+/*
+ * Create a new PLyProcedure structure
+ */
 static PLyProcedure *
-PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
+PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
 {
 	char		procName[NAMEDATALEN + 256];
 	Form_pg_proc procStruct;
@@ -1374,18 +1392,10 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
 				rv;
 
 	procStruct = (Form_pg_proc) GETSTRUCT(procTup);
-
-	if (OidIsValid(tgreloid))
-		rv = snprintf(procName, sizeof(procName),
-					  "__plpython_procedure_%s_%u_trigger_%u",
-					  NameStr(procStruct->proname),
-					  HeapTupleGetOid(procTup),
-					  tgreloid);
-	else
-		rv = snprintf(procName, sizeof(procName),
-					  "__plpython_procedure_%s_%u",
-					  NameStr(procStruct->proname),
-					  HeapTupleGetOid(procTup));
+	rv = snprintf(procName, sizeof(procName),
+				  "__plpython_procedure_%s_%u",
+				  NameStr(procStruct->proname),
+				  fn_oid);
 	if (rv >= sizeof(procName) || rv < 0)
 		elog(ERROR, "procedure name would overrun buffer");
 
@@ -1402,7 +1412,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
 		PLy_typeinfo_init(&proc->args[i]);
 	proc->nargs = 0;
 	proc->code = proc->statics = NULL;
-	proc->globals = proc->me = NULL;
+	proc->globals = NULL;
 	proc->is_setof = procStruct->proretset;
 	proc->setof = NULL;
 	proc->argnames = NULL;
@@ -1413,7 +1423,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
 		 * get information required for output conversion of the return value,
 		 * but only if this isn't a trigger.
 		 */
-		if (!OidIsValid(tgreloid))
+		if (!is_trigger)
 		{
 			HeapTuple	rvTypeTup;
 			Form_pg_type rvTypeStruct;
@@ -1550,11 +1560,6 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
 
 		pfree(procSource);
 		procSource = NULL;
-
-		proc->me = PyCObject_FromVoidPtr(proc, NULL);
-		if (!proc->me)
-			PLy_elog(ERROR, "PyCObject_FromVoidPtr() failed");
-		PyDict_SetItemString(PLy_procedure_cache, key, proc->me);
 	}
 	PG_CATCH();
 	{
@@ -1569,6 +1574,9 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
 	return proc;
 }
 
+/*
+ * Insert the procedure into the Python interpreter
+ */
 static void
 PLy_procedure_compile(PLyProcedure *proc, const char *src)
 {
@@ -1591,7 +1599,7 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
 	crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL);
 	free(msrc);
 
-	if (crv != NULL && (!PyErr_Occurred()))
+	if (crv != NULL)
 	{
 		int			clen;
 		char		call[NAMEDATALEN + 256];
@@ -1605,11 +1613,9 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
 		if (clen < 0 || clen >= sizeof(call))
 			elog(ERROR, "string would overflow buffer");
 		proc->code = Py_CompileString(call, "<string>", Py_eval_input);
-		if (proc->code != NULL && (!PyErr_Occurred()))
+		if (proc->code != NULL)
 			return;
 	}
-	else
-		Py_XDECREF(crv);
 
 	PLy_elog(ERROR, "could not compile PL/Python function \"%s\"", proc->proname);
 }
@@ -1667,7 +1673,6 @@ PLy_procedure_delete(PLyProcedure *proc)
 	Py_XDECREF(proc->code);
 	Py_XDECREF(proc->statics);
 	Py_XDECREF(proc->globals);
-	Py_XDECREF(proc->me);
 	if (proc->proname)
 		PLy_free(proc->proname);
 	if (proc->pyname)
@@ -1686,7 +1691,6 @@ PLy_procedure_delete(PLyProcedure *proc)
 	}
 	if (proc->argnames)
 		PLy_free(proc->argnames);
-	PLy_free(proc);
 }
 
 /*
@@ -3232,6 +3236,7 @@ _PG_init(void)
 	/* Be sure we do initialization only once (should be redundant now) */
 	static bool inited = false;
 	const int **version_ptr;
+	HASHCTL		hash_ctl;
 
 	if (inited)
 		return;
@@ -3263,9 +3268,20 @@ _PG_init(void)
 	PLy_init_plpy();
 	if (PyErr_Occurred())
 		PLy_elog(FATAL, "untrapped error in initialization");
-	PLy_procedure_cache = PyDict_New();
-	if (PLy_procedure_cache == NULL)
-		PLy_elog(ERROR, "could not create procedure cache");
+
+	memset(&hash_ctl, 0, sizeof(hash_ctl));
+	hash_ctl.keysize = sizeof(Oid);
+	hash_ctl.entrysize = sizeof(PLyProcedureEntry);
+	hash_ctl.hash = oid_hash;
+	PLy_procedure_cache = hash_create("PL/Python procedures", 32, &hash_ctl,
+									  HASH_ELEM | HASH_FUNCTION);
+
+	memset(&hash_ctl, 0, sizeof(hash_ctl));
+	hash_ctl.keysize = sizeof(Oid);
+	hash_ctl.entrysize = sizeof(PLyProcedureEntry);
+	hash_ctl.hash = oid_hash;
+	PLy_trigger_cache = hash_create("PL/Python triggers", 32, &hash_ctl,
+									HASH_ELEM | HASH_FUNCTION);
 
 	inited = true;
 }