diff --git a/src/pl/plpython/expected/plpython_do.out b/src/pl/plpython/expected/plpython_do.out
index 9de261a..a21b088 100644
*** a/src/pl/plpython/expected/plpython_do.out
--- b/src/pl/plpython/expected/plpython_do.out
*************** DO $$ plpy.notice("This is plpythonu.")
*** 2,6 ****
  NOTICE:  This is plpythonu.
  CONTEXT:  PL/Python anonymous code block
  DO $$ nonsense $$ LANGUAGE plpythonu;
! ERROR:  PL/Python: NameError: global name 'nonsense' is not defined
  CONTEXT:  PL/Python anonymous code block
--- 2,6 ----
  NOTICE:  This is plpythonu.
  CONTEXT:  PL/Python anonymous code block
  DO $$ nonsense $$ LANGUAGE plpythonu;
! ERROR:  NameError: global name 'nonsense' is not defined
  CONTEXT:  PL/Python anonymous code block
diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index 1f24c13..70890a8 100644
*** a/src/pl/plpython/expected/plpython_error.out
--- b/src/pl/plpython/expected/plpython_error.out
*************** CREATE FUNCTION sql_syntax_error() RETUR
*** 8,19 ****
  'plpy.execute("syntax error")'
          LANGUAGE plpythonu;
  SELECT sql_syntax_error();
! WARNING:  PL/Python: plpy.SPIError: unrecognized error in PLy_spi_execute_query
  CONTEXT:  PL/Python function "sql_syntax_error"
! ERROR:  syntax error at or near "syntax"
! LINE 1: syntax error
!         ^
! QUERY:  syntax error
  CONTEXT:  PL/Python function "sql_syntax_error"
  /* check the handling of uncaught python exceptions
   */
--- 8,16 ----
  'plpy.execute("syntax error")'
          LANGUAGE plpythonu;
  SELECT sql_syntax_error();
! WARNING:  plpy.SPIError: unrecognized error in PLy_spi_execute_query
  CONTEXT:  PL/Python function "sql_syntax_error"
! ERROR:  plpy.SPIError: syntax error at or near "syntax"
  CONTEXT:  PL/Python function "sql_syntax_error"
  /* check the handling of uncaught python exceptions
   */
*************** CREATE FUNCTION exception_index_invalid(
*** 22,28 ****
  'return args[1]'
  	LANGUAGE plpythonu;
  SELECT exception_index_invalid('test');
! ERROR:  PL/Python: IndexError: list index out of range
  CONTEXT:  PL/Python function "exception_index_invalid"
  /* check handling of nested exceptions
   */
--- 19,25 ----
  'return args[1]'
  	LANGUAGE plpythonu;
  SELECT exception_index_invalid('test');
! ERROR:  IndexError: list index out of range
  CONTEXT:  PL/Python function "exception_index_invalid"
  /* check handling of nested exceptions
   */
*************** CREATE FUNCTION exception_index_invalid_
*** 32,44 ****
  return rv[0]'
  	LANGUAGE plpythonu;
  SELECT exception_index_invalid_nested();
! WARNING:  PL/Python: plpy.SPIError: unrecognized error in PLy_spi_execute_query
  CONTEXT:  PL/Python function "exception_index_invalid_nested"
! ERROR:  function test5(unknown) does not exist
! LINE 1: SELECT test5('foo')
!                ^
! HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
! QUERY:  SELECT test5('foo')
  CONTEXT:  PL/Python function "exception_index_invalid_nested"
  /* a typo
   */
--- 29,37 ----
  return rv[0]'
  	LANGUAGE plpythonu;
  SELECT exception_index_invalid_nested();
! WARNING:  plpy.SPIError: unrecognized error in PLy_spi_execute_query
  CONTEXT:  PL/Python function "exception_index_invalid_nested"
! ERROR:  plpy.SPIError: function test5(unknown) does not exist
  CONTEXT:  PL/Python function "exception_index_invalid_nested"
  /* a typo
   */
*************** return None
*** 54,62 ****
  '
  	LANGUAGE plpythonu;
  SELECT invalid_type_uncaught('rick');
! WARNING:  PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare
  CONTEXT:  PL/Python function "invalid_type_uncaught"
! ERROR:  type "test" does not exist
  CONTEXT:  PL/Python function "invalid_type_uncaught"
  /* for what it's worth catch the exception generated by
   * the typo, and return None
--- 47,55 ----
  '
  	LANGUAGE plpythonu;
  SELECT invalid_type_uncaught('rick');
! WARNING:  plpy.SPIError: unrecognized error in PLy_spi_prepare
  CONTEXT:  PL/Python function "invalid_type_uncaught"
! ERROR:  plpy.SPIError: type "test" does not exist
  CONTEXT:  PL/Python function "invalid_type_uncaught"
  /* for what it's worth catch the exception generated by
   * the typo, and return None
*************** return None
*** 77,86 ****
  '
  	LANGUAGE plpythonu;
  SELECT invalid_type_caught('rick');
! WARNING:  PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare
  CONTEXT:  PL/Python function "invalid_type_caught"
! ERROR:  type "test" does not exist
  CONTEXT:  PL/Python function "invalid_type_caught"
  /* for what it's worth catch the exception generated by
   * the typo, and reraise it as a plain error
   */
--- 70,84 ----
  '
  	LANGUAGE plpythonu;
  SELECT invalid_type_caught('rick');
! WARNING:  plpy.SPIError: unrecognized error in PLy_spi_prepare
  CONTEXT:  PL/Python function "invalid_type_caught"
! NOTICE:  type "test" does not exist
  CONTEXT:  PL/Python function "invalid_type_caught"
+  invalid_type_caught 
+ ---------------------
+  
+ (1 row)
+ 
  /* for what it's worth catch the exception generated by
   * the typo, and reraise it as a plain error
   */
*************** return None
*** 99,107 ****
  '
  	LANGUAGE plpythonu;
  SELECT invalid_type_reraised('rick');
! WARNING:  PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare
  CONTEXT:  PL/Python function "invalid_type_reraised"
! ERROR:  type "test" does not exist
  CONTEXT:  PL/Python function "invalid_type_reraised"
  /* no typo no messing about
   */
--- 97,105 ----
  '
  	LANGUAGE plpythonu;
  SELECT invalid_type_reraised('rick');
! WARNING:  plpy.SPIError: unrecognized error in PLy_spi_prepare
  CONTEXT:  PL/Python function "invalid_type_reraised"
! ERROR:  plpy.Error: type "test" does not exist
  CONTEXT:  PL/Python function "invalid_type_reraised"
  /* no typo no messing about
   */
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index a229b18..d92c987 100644
*** a/src/pl/plpython/expected/plpython_test.out
--- b/src/pl/plpython/expected/plpython_test.out
*************** select argument_test_one(users, fname, l
*** 35,40 ****
--- 35,53 ----
   willem doe => {fname: willem, lname: doe, userid: 3, username: w_doe}
  (3 rows)
  
+ -- check module contents
+ CREATE FUNCTION module_contents() RETURNS text AS
+ $$
+ contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
+ contents.sort()
+ return ", ".join(contents)
+ $$ LANGUAGE plpythonu;
+ select module_contents();
+                                       module_contents                                      
+ -------------------------------------------------------------------------------------------
+  Error, Fatal, SPIError, debug, error, execute, fatal, info, log, notice, prepare, warning
+ (1 row)
+ 
  CREATE FUNCTION elog_test() RETURNS void
  AS $$
  plpy.debug('debug')
*************** NOTICE:  notice
*** 60,64 ****
  CONTEXT:  PL/Python function "elog_test"
  WARNING:  warning
  CONTEXT:  PL/Python function "elog_test"
! ERROR:  error
  CONTEXT:  PL/Python function "elog_test"
--- 73,77 ----
  CONTEXT:  PL/Python function "elog_test"
  WARNING:  warning
  CONTEXT:  PL/Python function "elog_test"
! ERROR:  plpy.Error: error
  CONTEXT:  PL/Python function "elog_test"
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index a165936..982005b 100644
*** a/src/pl/plpython/expected/plpython_types.out
--- b/src/pl/plpython/expected/plpython_types.out
*************** CREATE FUNCTION test_type_conversion_arr
*** 596,602 ****
  return 5
  $$ LANGUAGE plpythonu;
  SELECT * FROM test_type_conversion_array_error();
! ERROR:  PL/Python: return value of function with array return type is not a Python sequence
  CONTEXT:  while creating return value
  PL/Python function "test_type_conversion_array_error"
  --
--- 596,602 ----
  return 5
  $$ LANGUAGE plpythonu;
  SELECT * FROM test_type_conversion_array_error();
! ERROR:  return value of function with array return type is not a Python sequence
  CONTEXT:  while creating return value
  PL/Python function "test_type_conversion_array_error"
  --
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
index 38ddf02..eeb23b7 100644
*** a/src/pl/plpython/expected/plpython_types_3.out
--- b/src/pl/plpython/expected/plpython_types_3.out
*************** CREATE FUNCTION test_type_conversion_arr
*** 596,602 ****
  return 5
  $$ LANGUAGE plpython3u;
  SELECT * FROM test_type_conversion_array_error();
! ERROR:  PL/Python: return value of function with array return type is not a Python sequence
  CONTEXT:  while creating return value
  PL/Python function "test_type_conversion_array_error"
  --
--- 596,602 ----
  return 5
  $$ LANGUAGE plpython3u;
  SELECT * FROM test_type_conversion_array_error();
! ERROR:  return value of function with array return type is not a Python sequence
  CONTEXT:  while creating return value
  PL/Python function "test_type_conversion_array_error"
  --
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 1ae1239..67eb0f3 100644
*** a/src/pl/plpython/plpython.c
--- b/src/pl/plpython/plpython.c
*************** typedef struct PLyProcedure
*** 214,223 ****
  	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;
  
  
  /* Python objects */
  typedef struct PLyPlanObject
--- 214,227 ----
  	PyObject   *code;			/* compiled procedure code */
  	PyObject   *statics;		/* data saved across calls, local scope */
  	PyObject   *globals;		/* data saved across calls, global scope */
  } PLyProcedure;
  
+ /* the procedure cache entry */
+ typedef struct PLyProcedureEntry
+ {
+ 	Oid				fn_oid;		/* hash key */
+ 	PLyProcedure	*proc;
+ } PLyProcedureEntry;
  
  /* Python objects */
  typedef struct PLyPlanObject
*************** static HeapTuple PLy_modify_tuple(PLyPro
*** 311,321 ****
  
  static PyObject *PLy_procedure_call(PLyProcedure *, char *, PyObject *);
  
! static PLyProcedure *PLy_procedure_get(FunctionCallInfo fcinfo,
! 				  Oid tgreloid);
  
! static PLyProcedure *PLy_procedure_create(HeapTuple procTup, Oid tgreloid,
! 					 char *key);
  
  static void PLy_procedure_compile(PLyProcedure *, const char *);
  static char *PLy_procedure_munge_source(const char *, const char *);
--- 315,324 ----
  
  static PyObject *PLy_procedure_call(PLyProcedure *, char *, PyObject *);
  
! static PLyProcedure *PLy_procedure_get(Oid fn_oid, bool is_trigger);
  
! 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 *);
*************** static HeapTuple PLyObject_ToTuple(PLyTy
*** 358,379 ****
   */
  static PLyProcedure *PLy_curr_procedure = NULL;
  
- /*
-  * When a callback from Python into PG incurs an error, we temporarily store
-  * the error information here, and return NULL to the Python interpreter.
-  * Any further callback attempts immediately fail, and when the Python
-  * interpreter returns to the calling function, we re-throw the error (even if
-  * Python thinks it trapped the error and doesn't return NULL).  Eventually
-  * this ought to be improved to let Python code really truly trap the error,
-  * but that's more of a change from the pre-8.0 semantics than I have time for
-  * now --- it will only be possible if the callback query is executed inside a
-  * subtransaction.
-  */
- 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;
  
  /* Python exceptions */
  static PyObject *PLy_exc_error = NULL;
--- 361,371 ----
   */
  static PLyProcedure *PLy_curr_procedure = NULL;
  
  
  static PyObject *PLy_interp_globals = NULL;
  static PyObject *PLy_interp_safe_globals = NULL;
! static HTAB *PLy_procedure_cache = NULL;
! static HTAB *PLy_trigger_cache = NULL;
  
  /* Python exceptions */
  static PyObject *PLy_exc_error = NULL;
*************** plpython_call_handler(PG_FUNCTION_ARGS)
*** 444,450 ****
  {
  	Datum		retval;
  	PLyProcedure *save_curr_proc;
! 	PLyProcedure *volatile proc = NULL;
  	ErrorContextCallback plerrcontext;
  
  	if (SPI_connect() != SPI_OK_CONNECT)
--- 436,442 ----
  {
  	Datum		retval;
  	PLyProcedure *save_curr_proc;
! 	PLyProcedure *proc = NULL;
  	ErrorContextCallback plerrcontext;
  
  	if (SPI_connect() != SPI_OK_CONNECT)
*************** plpython_call_handler(PG_FUNCTION_ARGS)
*** 463,480 ****
  	{
  		if (CALLED_AS_TRIGGER(fcinfo))
  		{
- 			TriggerData *tdata = (TriggerData *) fcinfo->context;
  			HeapTuple	trv;
  
! 			proc = PLy_procedure_get(fcinfo,
! 									 RelationGetRelid(tdata->tg_relation));
  			PLy_curr_procedure = proc;
  			trv = PLy_trigger_handler(fcinfo, proc);
  			retval = PointerGetDatum(trv);
  		}
  		else
  		{
! 			proc = PLy_procedure_get(fcinfo, InvalidOid);
  			PLy_curr_procedure = proc;
  			retval = PLy_function_handler(fcinfo, proc);
  		}
--- 455,470 ----
  	{
  		if (CALLED_AS_TRIGGER(fcinfo))
  		{
  			HeapTuple	trv;
  
! 			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->flinfo->fn_oid, false);
  			PLy_curr_procedure = proc;
  			retval = PLy_function_handler(fcinfo, proc);
  		}
*************** plpython_call_handler(PG_FUNCTION_ARGS)
*** 482,492 ****
  	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();
  	}
--- 472,477 ----
*************** plpython_call_handler(PG_FUNCTION_ARGS)
*** 497,504 ****
  
  	PLy_curr_procedure = save_curr_proc;
  
- 	Py_DECREF(proc->me);
- 
  	return retval;
  }
  
--- 482,487 ----
*************** static HeapTuple
*** 573,580 ****
  PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
  {
  	HeapTuple	rv = NULL;
! 	PyObject   *volatile plargs = NULL;
! 	PyObject   *volatile plrv = NULL;
  
  	PG_TRY();
  	{
--- 556,578 ----
  PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
  {
  	HeapTuple	rv = NULL;
! 	PyObject	*volatile plargs = NULL;
! 	PyObject   	*volatile plrv = NULL;
! 	TriggerData	*tdata;
! 
! 	AssertMacro(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();
  	{
*************** PLy_trigger_handler(FunctionCallInfo fci
*** 582,588 ****
  		plrv = PLy_procedure_call(proc, "TD", plargs);
  
  		Assert(plrv != NULL);
- 		Assert(!PLy_error_in_progress);
  
  		/*
  		 * Disconnect from SPI manager
--- 580,585 ----
*************** PLy_function_handler(FunctionCallInfo fc
*** 992,1005 ****
  			plargs = PLy_function_build_args(fcinfo, proc);
  			plrv = PLy_procedure_call(proc, "args", plargs);
  			if (!proc->is_setof)
! 
  				/*
  				 * SETOF function parameters will be deleted when last row is
  				 * returned
  				 */
  				PLy_function_delete_args(proc);
  			Assert(plrv != NULL);
- 			Assert(!PLy_error_in_progress);
  		}
  
  		/*
--- 989,1002 ----
  			plargs = PLy_function_build_args(fcinfo, proc);
  			plrv = PLy_procedure_call(proc, "args", plargs);
  			if (!proc->is_setof)
! 			{
  				/*
  				 * SETOF function parameters will be deleted when last row is
  				 * returned
  				 */
  				PLy_function_delete_args(proc);
+ 			}
  			Assert(plrv != NULL);
  		}
  
  		/*
*************** PLy_function_handler(FunctionCallInfo fc
*** 1149,1154 ****
--- 1146,1159 ----
  		Py_XDECREF(plargs);
  		Py_XDECREF(plrv);
  
+ 		/*
+ 		 * If there was an error the iterator might have not been exhausted
+ 		 * yet. Set it to NULL so the next invocation of the function will
+ 		 * start the iteration again.
+ 		 */
+ 		Py_XDECREF(proc->setof);
+ 		proc->setof = NULL;
+ 
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
*************** PLy_procedure_call(PLyProcedure *proc, c
*** 1174,1192 ****
  	 * If there was an error in a PG callback, propagate that no matter what
  	 * Python claims about its success.
  	 */
- 	if (PLy_error_in_progress)
- 	{
- 		ErrorData  *edata = PLy_error_in_progress;
- 
- 		PLy_error_in_progress = NULL;
- 		ReThrowError(edata);
- 	}
  
! 	if (rv == NULL || PyErr_Occurred())
! 	{
! 		Py_XDECREF(rv);
  		PLy_elog(ERROR, NULL);
- 	}
  
  	return rv;
  }
--- 1179,1188 ----
  	 * If there was an error in a PG callback, propagate that no matter what
  	 * Python claims about its success.
  	 */
  
! 	/* if the Python code returned an error, propagate it */
! 	if (rv == NULL)
  		PLy_elog(ERROR, NULL);
  
  	return rv;
  }
*************** PLy_function_delete_args(PLyProcedure *p
*** 1286,1291 ****
--- 1282,1301 ----
  }
  
  
+ /* Decide if 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 */
+ 	if (!(proc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
+ 		  ItemPointerEquals(&proc->fn_tid, &procTup->t_self)))
+ 		return false;
+ 
+ 	return true;
+ }
+ 
  /*
   * PLyProcedure functions
   */
*************** PLy_function_delete_args(PLyProcedure *p
*** 1296,1368 ****
   * function calls.
   */
  static PLyProcedure *
! PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
  {
! 	Oid			fn_oid;
! 	HeapTuple	procTup;
! 	char		key[128];
! 	PyObject   *plproc;
! 	PLyProcedure *proc = NULL;
! 	int			rv;
  
- 	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);
  
! 	if (plproc != NULL)
  	{
! 		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))
  		{
! 			Py_DECREF(plproc);
! 			proc = NULL;
  		}
  	}
  
! 	if (proc == NULL)
! 		proc = PLy_procedure_create(procTup, tgreloid, key);
  
! 	if (OidIsValid(tgreloid))
  	{
  		/*
! 		 * 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);
  	}
  
! 	ReleaseSysCache(procTup);
  
! 	return proc;
  }
  
  static PLyProcedure *
! PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
  {
  	char		procName[NAMEDATALEN + 256];
  	Form_pg_proc procStruct;
--- 1306,1492 ----
   * function calls.
   */
  static PLyProcedure *
! PLy_procedure_get(Oid fn_oid, bool is_trigger)
  {
! 	HeapTuple			procTup;
! 	PLyProcedureEntry	*entry;
! 	bool				found;
  
  	procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
  	if (!HeapTupleIsValid(procTup))
  		elog(ERROR, "cache lookup failed for function %u", fn_oid);
  
! 	/* 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);
  
! 	PG_TRY();
  	{
! 		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))
+ 		{
+ 			/* 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 */
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		/* 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 entry->proc;
! }
! 
! /* Set up output conversion functions for a procedure */
! static void
! PLy_procedure_output_conversion(PLyProcedure *proc, Form_pg_proc procStruct)
! {
! 	HeapTuple	rvTypeTup;
! 	Form_pg_type rvTypeStruct;
! 
! 	/* Get the return type */
! 	rvTypeTup = SearchSysCache1(TYPEOID,
! 								ObjectIdGetDatum(procStruct->prorettype));
! 	if (!HeapTupleIsValid(rvTypeTup))
! 		elog(ERROR, "cache lookup failed for type %u",
! 			 procStruct->prorettype);
! 	rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
! 
! 	/* Disallow pseudotype result, except for void */
! 	if (rvTypeStruct->typtype == TYPTYPE_PSEUDO &&
! 		procStruct->prorettype != VOIDOID)
! 	{
! 		if (procStruct->prorettype == TRIGGEROID)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg("trigger functions can only be called as triggers")));
! 		else
! 			ereport(ERROR,
! 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg("PL/Python functions cannot return type %s",
! 							format_type_be(procStruct->prorettype))));
! 	}
! 
! 	if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE)
  	{
  		/*
! 		 * Tuple: set up later, during first call to
! 		 * PLy_function_handler
  		 */
! 		proc->result.out.d.typoid = procStruct->prorettype;
! 		proc->result.is_rowtype = 2;
! 	}
! 	else
! 	{
! 		/* Do the real work */
! 		PLy_output_datum_func(&proc->result, rvTypeTup);
! 	}
  
! 	ReleaseSysCache(rvTypeTup);
! }
! 
! /* Set up output conversion functions for a procedure */
! static void
! PLy_procedure_input_conversion(PLyProcedure *proc, HeapTuple procTup,
! 							   Form_pg_proc procStruct)
! {
! 	Oid		*types;
! 	char	**names,
! 			*modes;
! 	int		i,
! 			pos,
! 			total;
! 
! 	/* Extract argument type info from the pg_proc tuple */
! 	total = get_func_arg_info(procTup, &types, &names, &modes);
! 
! 	/* Count number of in+inout args into proc->nargs */
! 	if (modes == NULL)
! 		proc->nargs = total;
! 	else
! 	{
! 		/* proc->nargs was initialized to 0 above */
! 		for (i = 0; i < total; i++)
! 		{
! 			if (modes[i] != PROARGMODE_OUT &&
! 				modes[i] != PROARGMODE_TABLE)
! 				(proc->nargs)++;
! 		}
  	}
  
! 	proc->argnames = (char **) PLy_malloc0(sizeof(char *) * proc->nargs);
! 	for (i = pos = 0; i < total; i++)
! 	{
! 		HeapTuple	argTypeTup;
! 		Form_pg_type argTypeStruct;
  
! 		if (modes &&
! 			(modes[i] == PROARGMODE_OUT ||
! 			 modes[i] == PROARGMODE_TABLE))
! 			continue;	/* skip OUT arguments */
! 
! 		Assert(types[i] == procStruct->proargtypes.values[pos]);
! 
! 		argTypeTup = SearchSysCache1(TYPEOID,
! 									 ObjectIdGetDatum(types[i]));
! 		if (!HeapTupleIsValid(argTypeTup))
! 			elog(ERROR, "cache lookup failed for type %u", types[i]);
! 		argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
! 
! 		/* Check argument type is OK, set up I/O function info */
! 		switch (argTypeStruct->typtype)
! 		{
! 			case TYPTYPE_PSEUDO:
! 				/* Disallow pseudotype argument */
! 				ereport(ERROR,
! 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 						 errmsg("PL/Python functions cannot accept type %s",
! 								format_type_be(types[i]))));
! 				break;
! 			case TYPTYPE_COMPOSITE:
! 				/* We'll set IO funcs at first call */
! 				proc->args[pos].is_rowtype = 2;
! 				break;
! 			default:
! 				PLy_input_datum_func(&(proc->args[pos]),
! 									 types[i],
! 									 argTypeTup);
! 				break;
! 		}
! 
! 		/* Get argument name */
! 		proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL;
! 
! 		ReleaseSysCache(argTypeTup);
! 
! 		pos++;
! 	}
  }
  
+ /* Create a new PLyProcedure structure */
  static PLyProcedure *
! PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
  {
  	char		procName[NAMEDATALEN + 256];
  	Form_pg_proc procStruct;
*************** PLy_procedure_create(HeapTuple procTup,
*** 1373,1395 ****
  	int			i,
  				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));
  	if (rv >= sizeof(procName) || rv < 0)
  		elog(ERROR, "procedure name would overrun buffer");
  
- 	proc = PLy_malloc(sizeof(PLyProcedure));
  	proc->proname = PLy_strdup(NameStr(procStruct->proname));
  	proc->pyname = PLy_strdup(procName);
  	proc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
--- 1497,1512 ----
  	int			i,
  				rv;
  
! 	proc = PLy_malloc(sizeof(PLyProcedure));
  
! 	procStruct = (Form_pg_proc) GETSTRUCT(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");
  
  	proc->proname = PLy_strdup(NameStr(procStruct->proname));
  	proc->pyname = PLy_strdup(procName);
  	proc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
*************** PLy_procedure_create(HeapTuple procTup,
*** 1402,1408 ****
  		PLy_typeinfo_init(&proc->args[i]);
  	proc->nargs = 0;
  	proc->code = proc->statics = NULL;
! 	proc->globals = proc->me = NULL;
  	proc->is_setof = procStruct->proretset;
  	proc->setof = NULL;
  	proc->argnames = NULL;
--- 1519,1525 ----
  		PLy_typeinfo_init(&proc->args[i]);
  	proc->nargs = 0;
  	proc->code = proc->statics = NULL;
! 	proc->globals = NULL;
  	proc->is_setof = procStruct->proretset;
  	proc->setof = NULL;
  	proc->argnames = NULL;
*************** PLy_procedure_create(HeapTuple procTup,
*** 1413,1459 ****
  		 * get information required for output conversion of the return value,
  		 * but only if this isn't a trigger.
  		 */
! 		if (!OidIsValid(tgreloid))
! 		{
! 			HeapTuple	rvTypeTup;
! 			Form_pg_type rvTypeStruct;
! 
! 			rvTypeTup = SearchSysCache1(TYPEOID,
! 								   ObjectIdGetDatum(procStruct->prorettype));
! 			if (!HeapTupleIsValid(rvTypeTup))
! 				elog(ERROR, "cache lookup failed for type %u",
! 					 procStruct->prorettype);
! 			rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
! 
! 			/* Disallow pseudotype result, except for void */
! 			if (rvTypeStruct->typtype == TYPTYPE_PSEUDO &&
! 				procStruct->prorettype != VOIDOID)
! 			{
! 				if (procStruct->prorettype == TRIGGEROID)
! 					ereport(ERROR,
! 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 							 errmsg("trigger functions can only be called as triggers")));
! 				else
! 					ereport(ERROR,
! 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 						  errmsg("PL/Python functions cannot return type %s",
! 								 format_type_be(procStruct->prorettype))));
! 			}
! 
! 			if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE)
! 			{
! 				/*
! 				 * Tuple: set up later, during first call to
! 				 * PLy_function_handler
! 				 */
! 				proc->result.out.d.typoid = procStruct->prorettype;
! 				proc->result.is_rowtype = 2;
! 			}
! 			else
! 				PLy_output_datum_func(&proc->result, rvTypeTup);
! 
! 			ReleaseSysCache(rvTypeTup);
! 		}
  
  		/*
  		 * Now get information required for input conversion of the
--- 1530,1537 ----
  		 * get information required for output conversion of the return value,
  		 * but only if this isn't a trigger.
  		 */
! 		if (!is_trigger)
! 			PLy_procedure_output_conversion(proc, procStruct);
  
  		/*
  		 * Now get information required for input conversion of the
*************** PLy_procedure_create(HeapTuple procTup,
*** 1463,1541 ****
  		 * arguments.
  		 */
  		if (procStruct->pronargs)
! 		{
! 			Oid		   *types;
! 			char	  **names,
! 					   *modes;
! 			int			i,
! 						pos,
! 						total;
! 
! 			/* extract argument type info from the pg_proc tuple */
! 			total = get_func_arg_info(procTup, &types, &names, &modes);
! 
! 			/* count number of in+inout args into proc->nargs */
! 			if (modes == NULL)
! 				proc->nargs = total;
! 			else
! 			{
! 				/* proc->nargs was initialized to 0 above */
! 				for (i = 0; i < total; i++)
! 				{
! 					if (modes[i] != PROARGMODE_OUT &&
! 						modes[i] != PROARGMODE_TABLE)
! 						(proc->nargs)++;
! 				}
! 			}
! 
! 			proc->argnames = (char **) PLy_malloc0(sizeof(char *) * proc->nargs);
! 			for (i = pos = 0; i < total; i++)
! 			{
! 				HeapTuple	argTypeTup;
! 				Form_pg_type argTypeStruct;
! 
! 				if (modes &&
! 					(modes[i] == PROARGMODE_OUT ||
! 					 modes[i] == PROARGMODE_TABLE))
! 					continue;	/* skip OUT arguments */
! 
! 				Assert(types[i] == procStruct->proargtypes.values[pos]);
! 
! 				argTypeTup = SearchSysCache1(TYPEOID,
! 											 ObjectIdGetDatum(types[i]));
! 				if (!HeapTupleIsValid(argTypeTup))
! 					elog(ERROR, "cache lookup failed for type %u", types[i]);
! 				argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
! 
! 				/* check argument type is OK, set up I/O function info */
! 				switch (argTypeStruct->typtype)
! 				{
! 					case TYPTYPE_PSEUDO:
! 						/* Disallow pseudotype argument */
! 						ereport(ERROR,
! 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 						  errmsg("PL/Python functions cannot accept type %s",
! 								 format_type_be(types[i]))));
! 						break;
! 					case TYPTYPE_COMPOSITE:
! 						/* we'll set IO funcs at first call */
! 						proc->args[pos].is_rowtype = 2;
! 						break;
! 					default:
! 						PLy_input_datum_func(&(proc->args[pos]),
! 											 types[i],
! 											 argTypeTup);
! 						break;
! 				}
! 
! 				/* get argument name */
! 				proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL;
! 
! 				ReleaseSysCache(argTypeTup);
! 
! 				pos++;
! 			}
! 		}
  
  		/*
  		 * get the text of the function.
--- 1541,1547 ----
  		 * arguments.
  		 */
  		if (procStruct->pronargs)
! 			PLy_procedure_input_conversion(proc, procTup, procStruct);
  
  		/*
  		 * get the text of the function.
*************** PLy_procedure_create(HeapTuple procTup,
*** 1550,1560 ****
  
  		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();
  	{
--- 1556,1561 ----
*************** PLy_procedure_create(HeapTuple procTup,
*** 1569,1574 ****
--- 1570,1576 ----
  	return proc;
  }
  
+ /* Insert the procedure into the Python interpreter */
  static void
  PLy_procedure_compile(PLyProcedure *proc, const char *src)
  {
*************** PLy_procedure_compile(PLyProcedure *proc
*** 1589,1597 ****
  	 */
  	msrc = PLy_procedure_munge_source(proc->pyname, src);
  	crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL);
! 	free(msrc);
  
! 	if (crv != NULL && (!PyErr_Occurred()))
  	{
  		int			clen;
  		char		call[NAMEDATALEN + 256];
--- 1591,1599 ----
  	 */
  	msrc = PLy_procedure_munge_source(proc->pyname, src);
  	crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL);
! 	pfree(msrc);
  
! 	if (crv != NULL)
  	{
  		int			clen;
  		char		call[NAMEDATALEN + 256];
*************** PLy_procedure_compile(PLyProcedure *proc
*** 1605,1617 ****
  		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()))
  			return;
  	}
- 	else
- 		Py_XDECREF(crv);
  
! 	PLy_elog(ERROR, "could not compile PL/Python function \"%s\"", proc->proname);
  }
  
  static char *
--- 1607,1621 ----
  		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)
  			return;
  	}
  
! 	if (proc->proname)
! 		PLy_elog(ERROR, "could not compile PL/Python function \"%s\"",
! 				 proc->proname);
! 	else
! 		PLy_elog(ERROR, "could not compile anonymous PL/Python code block");
  }
  
  static char *
*************** PLy_procedure_munge_source(const char *n
*** 1628,1634 ****
  	 */
  	mlen = (strlen(src) * 2) + strlen(name) + 16;
  
! 	mrc = PLy_malloc(mlen);
  	plen = snprintf(mrc, mlen, "def %s():\n\t", name);
  	Assert(plen >= 0 && plen < mlen);
  
--- 1632,1638 ----
  	 */
  	mlen = (strlen(src) * 2) + strlen(name) + 16;
  
! 	mrc = palloc(mlen);
  	plen = snprintf(mrc, mlen, "def %s():\n\t", name);
  	Assert(plen >= 0 && plen < mlen);
  
*************** PLy_procedure_delete(PLyProcedure *proc)
*** 1667,1673 ****
  	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)
--- 1671,1676 ----
*************** PLy_procedure_delete(PLyProcedure *proc)
*** 1686,1692 ****
  	}
  	if (proc->argnames)
  		PLy_free(proc->argnames);
- 	PLy_free(proc);
  }
  
  /*
--- 1689,1694 ----
*************** PLyMapping_ToTuple(PLyTypeInfo *info, Py
*** 2289,2294 ****
--- 2291,2299 ----
  		PyObject   *volatile value;
  		PLyObToDatum *att;
  
+ 		if (desc->attrs[i]->attisdropped)
+ 			continue;
+ 
  		key = NameStr(desc->attrs[i]->attname);
  		value = NULL;
  		att = &info->out.r.atts[i];
*************** PLySequence_ToTuple(PLyTypeInfo *info, P
*** 2339,2355 ****
  	HeapTuple	tuple;
  	Datum	   *values;
  	bool	   *nulls;
  	volatile int i;
  
  	Assert(PySequence_Check(sequence));
- 
  	/*
  	 * Check that sequence length is exactly same as PG tuple's. We actually
  	 * can ignore exceeding items or assume missing ones as null but to avoid
  	 * plpython developer's errors we are strict here
  	 */
  	desc = lookup_rowtype_tupdesc(info->out.d.typoid, -1);
! 	if (PySequence_Length(sequence) != desc->natts)
  		ereport(ERROR,
  				(errcode(ERRCODE_DATATYPE_MISMATCH),
  				 errmsg("length of returned sequence did not match number of columns in row")));
--- 2344,2366 ----
  	HeapTuple	tuple;
  	Datum	   *values;
  	bool	   *nulls;
+ 	int			idx;
  	volatile int i;
  
  	Assert(PySequence_Check(sequence));
  	/*
  	 * Check that sequence length is exactly same as PG tuple's. We actually
  	 * can ignore exceeding items or assume missing ones as null but to avoid
  	 * plpython developer's errors we are strict here
  	 */
  	desc = lookup_rowtype_tupdesc(info->out.d.typoid, -1);
! 	idx = 0;
! 	for (i = 0; i < desc->natts; ++i)
! 	{
! 		if (!desc->attrs[i]->attisdropped)
! 			idx++;
! 	}
! 	if (PySequence_Length(sequence) != idx)
  		ereport(ERROR,
  				(errcode(ERRCODE_DATATYPE_MISMATCH),
  				 errmsg("length of returned sequence did not match number of columns in row")));
*************** PLySequence_ToTuple(PLyTypeInfo *info, P
*** 2361,2376 ****
  	/* Build tuple */
  	values = palloc(sizeof(Datum) * desc->natts);
  	nulls = palloc(sizeof(bool) * desc->natts);
  	for (i = 0; i < desc->natts; ++i)
  	{
  		PyObject   *volatile value;
  		PLyObToDatum *att;
  
  		value = NULL;
  		att = &info->out.r.atts[i];
  		PG_TRY();
  		{
! 			value = PySequence_GetItem(sequence, i);
  			Assert(value);
  			if (value == Py_None)
  			{
--- 2372,2391 ----
  	/* Build tuple */
  	values = palloc(sizeof(Datum) * desc->natts);
  	nulls = palloc(sizeof(bool) * desc->natts);
+ 	idx = 0;
  	for (i = 0; i < desc->natts; ++i)
  	{
  		PyObject   *volatile value;
  		PLyObToDatum *att;
  
+ 		if (desc->attrs[i]->attisdropped)
+ 			continue;
+ 
  		value = NULL;
  		att = &info->out.r.atts[i];
  		PG_TRY();
  		{
! 			value = PySequence_GetItem(sequence, idx);
  			Assert(value);
  			if (value == Py_None)
  			{
*************** PLySequence_ToTuple(PLyTypeInfo *info, P
*** 2392,2397 ****
--- 2407,2414 ----
  			PG_RE_THROW();
  		}
  		PG_END_TRY();
+ 
+ 		idx++;
  	}
  
  	tuple = heap_form_tuple(desc, values, nulls);
*************** PLyObject_ToTuple(PLyTypeInfo *info, PyO
*** 2426,2431 ****
--- 2443,2451 ----
  		PyObject   *volatile value;
  		PLyObToDatum *att;
  
+ 		if (desc->attrs[i]->attisdropped)
+ 			continue;
+ 
  		key = NameStr(desc->attrs[i]->attname);
  		value = NULL;
  		att = &info->out.r.atts[i];
*************** static PyModuleDef PLy_module = {
*** 2628,2633 ****
--- 2648,2657 ----
  	NULL,						/* m_doc */
  	-1,							/* m_size */
  	PLy_methods,				/* m_methods */
+ 	NULL,                       /* m_reload */
+ 	NULL,                       /* m_traverse */
+ 	NULL,                       /* m_clear */
+ 	NULL                        /* m_free */
  };
  #endif
  
*************** PLy_plan_new(void)
*** 2637,2648 ****
  {
  	PLyPlanObject *ob;
  
! 	if ((ob = PyObject_NEW(PLyPlanObject, &PLy_PlanType)) == NULL)
  		return NULL;
  
  	ob->plan = NULL;
  	ob->nargs = 0;
  	ob->types = NULL;
  	ob->args = NULL;
  
  	return (PyObject *) ob;
--- 2661,2673 ----
  {
  	PLyPlanObject *ob;
  
! 	if ((ob = PyObject_New(PLyPlanObject, &PLy_PlanType)) == NULL)
  		return NULL;
  
  	ob->plan = NULL;
  	ob->nargs = 0;
  	ob->types = NULL;
+ 	ob->values = NULL;
  	ob->args = NULL;
  
  	return (PyObject *) ob;
*************** PLy_plan_dealloc(PyObject *arg)
*** 2658,2663 ****
--- 2683,2690 ----
  		SPI_freeplan(ob->plan);
  	if (ob->types)
  		PLy_free(ob->types);
+ 	if (ob->values)
+ 		PLy_free(ob->values);
  	if (ob->args)
  	{
  		int			i;
*************** PLy_result_new(void)
*** 2693,2699 ****
  {
  	PLyResultObject *ob;
  
! 	if ((ob = PyObject_NEW(PLyResultObject, &PLy_ResultType)) == NULL)
  		return NULL;
  
  	/* ob->tuples = NULL; */
--- 2720,2726 ----
  {
  	PLyResultObject *ob;
  
! 	if ((ob = PyObject_New(PLyResultObject, &PLy_ResultType)) == NULL)
  		return NULL;
  
  	/* ob->tuples = NULL; */
*************** PLy_spi_prepare(PyObject *self, PyObject
*** 2799,2823 ****
  	PyObject   *volatile optr = NULL;
  	char	   *query;
  	void	   *tmpplan;
! 	volatile MemoryContext oldcontext;
  
! 	/* Can't execute more if we have an unhandled error */
! 	if (PLy_error_in_progress)
! 	{
! 		PLy_exception_set(PLy_exc_error, "transaction aborted");
! 		return NULL;
! 	}
  
  	if (!PyArg_ParseTuple(args, "s|O", &query, &list))
  	{
- 		PLy_exception_set(PLy_exc_spi_error,
- 						  "invalid arguments for plpy.prepare");
  		return NULL;
  	}
  
  	if (list && (!PySequence_Check(list)))
  	{
! 		PLy_exception_set(PLy_exc_spi_error,
  					   "second argument of plpy.prepare must be a sequence");
  		return NULL;
  	}
--- 2826,2843 ----
  	PyObject   *volatile optr = NULL;
  	char	   *query;
  	void	   *tmpplan;
! 	int			nargs;
  
! 	MemoryContext volatile oldcontext = CurrentMemoryContext;
  
  	if (!PyArg_ParseTuple(args, "s|O", &query, &list))
  	{
  		return NULL;
  	}
  
  	if (list && (!PySequence_Check(list)))
  	{
! 		PLy_exception_set(PyExc_ValueError,
  					   "second argument of plpy.prepare must be a sequence");
  		return NULL;
  	}
*************** PLy_spi_prepare(PyObject *self, PyObject
*** 2825,2904 ****
  	if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
  		return NULL;
  
! 	oldcontext = CurrentMemoryContext;
  	PG_TRY();
  	{
! 		if (list != NULL)
! 		{
! 			int			nargs,
! 						i;
! 
! 			nargs = PySequence_Length(list);
! 			if (nargs > 0)
! 			{
! 				plan->nargs = nargs;
! 				plan->types = PLy_malloc(sizeof(Oid) * nargs);
! 				plan->values = PLy_malloc(sizeof(Datum) * nargs);
! 				plan->args = PLy_malloc(sizeof(PLyTypeInfo) * nargs);
  
! 				/*
! 				 * the other loop might throw an exception, if PLyTypeInfo
! 				 * member isn't properly initialized the Py_DECREF(plan) will
! 				 * go boom
! 				 */
! 				for (i = 0; i < nargs; i++)
! 				{
! 					PLy_typeinfo_init(&plan->args[i]);
! 					plan->values[i] = PointerGetDatum(NULL);
! 				}
  
! 				for (i = 0; i < nargs; i++)
! 				{
! 					char	   *sptr;
! 					HeapTuple	typeTup;
! 					Oid			typeId;
! 					int32		typmod;
! 					Form_pg_type typeStruct;
  
! 					optr = PySequence_GetItem(list, i);
! 					if (PyString_Check(optr))
! 						sptr = PyString_AsString(optr);
! 					else if (PyUnicode_Check(optr))
! 						sptr = PLyUnicode_AsString(optr);
! 					else
! 					{
! 						ereport(ERROR,
! 								(errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
! 						sptr = NULL;	/* keep compiler quiet */
! 					}
  
! 					/********************************************************
! 					 * Resolve argument type names and then look them up by
! 					 * oid in the system cache, and remember the required
! 					 *information for input conversion.
! 					 ********************************************************/
  
! 					parseTypeString(sptr, &typeId, &typmod);
  
! 					typeTup = SearchSysCache1(TYPEOID,
! 											  ObjectIdGetDatum(typeId));
! 					if (!HeapTupleIsValid(typeTup))
! 						elog(ERROR, "cache lookup failed for type %u", typeId);
  
! 					Py_DECREF(optr);
! 					optr = NULL;	/* this is important */
  
! 					plan->types[i] = typeId;
! 					typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
! 					if (typeStruct->typtype != TYPTYPE_COMPOSITE)
! 						PLy_output_datum_func(&plan->args[i], typeTup);
! 					else
! 						ereport(ERROR,
! 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 								 errmsg("plpy.prepare does not support composite types")));
! 					ReleaseSysCache(typeTup);
! 				}
! 			}
  		}
  
  		pg_verifymbstr(query, strlen(query), false);
--- 2845,2921 ----
  	if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
  		return NULL;
  
! 	nargs = (list == NULL ? 0 : PySequence_Length(list));
! 
! 	plan->nargs = nargs;
! 	plan->types = PLy_malloc(sizeof(Oid) * nargs);
! 	plan->values = PLy_malloc(sizeof(Datum) * nargs);
! 	plan->args = PLy_malloc(sizeof(PLyTypeInfo) * nargs);
! 
  	PG_TRY();
  	{
! 		int	i;
  
! 		/*
! 		 * the other loop might throw an exception, if PLyTypeInfo
! 		 * member isn't properly initialized the Py_DECREF(plan) will
! 		 * go boom
! 		 */
! 		for (i = 0; i < nargs; i++)
! 		{
! 			PLy_typeinfo_init(&plan->args[i]);
! 			plan->values[i] = PointerGetDatum(NULL);
! 		}
  
! 		for (i = 0; i < nargs; i++)
! 		{
! 			char	   *sptr;
! 			HeapTuple	typeTup;
! 			Oid			typeId;
! 			int32		typmod;
! 			Form_pg_type typeStruct;
  
! 			optr = PySequence_GetItem(list, i);
! 			if (PyString_Check(optr))
! 				sptr = PyString_AsString(optr);
! 			else if (PyUnicode_Check(optr))
! 				sptr = PLyUnicode_AsString(optr);
! 			else
! 			{
! 				ereport(ERROR,
! 						(errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
! 				sptr = NULL;	/* keep compiler quiet */
! 			}
  
! 			/********************************************************
! 			 * Resolve argument type names and then look them up by
! 			 * oid in the system cache, and remember the required
! 			 *information for input conversion.
! 			 ********************************************************/
  
! 			parseTypeString(sptr, &typeId, &typmod);
  
! 			typeTup = SearchSysCache1(TYPEOID,
! 									  ObjectIdGetDatum(typeId));
! 			if (!HeapTupleIsValid(typeTup))
! 				elog(ERROR, "cache lookup failed for type %u", typeId);
  
! 			Py_DECREF(optr);
! 			/*
! 			 * set optr to NULL, so we won't try to unref it again in
! 			 * case of an error
! 			 */
! 			optr = NULL;
  
! 			plan->types[i] = typeId;
! 			typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
! 			if (typeStruct->typtype != TYPTYPE_COMPOSITE)
! 				PLy_output_datum_func(&plan->args[i], typeTup);
! 			else
! 				ereport(ERROR,
! 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 						 errmsg("plpy.prepare does not support composite types")));
! 			ReleaseSysCache(typeTup);
  		}
  
  		pg_verifymbstr(query, strlen(query), false);
*************** PLy_spi_prepare(PyObject *self, PyObject
*** 2917,2935 ****
  	}
  	PG_CATCH();
  	{
  		MemoryContextSwitchTo(oldcontext);
! 		PLy_error_in_progress = CopyErrorData();
! 		FlushErrorState();
  		Py_DECREF(plan);
  		Py_XDECREF(optr);
  		if (!PyErr_Occurred())
  			PLy_exception_set(PLy_exc_spi_error,
  							  "unrecognized error in PLy_spi_prepare");
  		PLy_elog(WARNING, NULL);
  		return NULL;
  	}
  	PG_END_TRY();
  
  	return (PyObject *) plan;
  }
  
--- 2934,2955 ----
  	}
  	PG_CATCH();
  	{
+ 		ErrorData	*edata;
+ 
  		MemoryContextSwitchTo(oldcontext);
! 		edata = CopyErrorData();
  		Py_DECREF(plan);
  		Py_XDECREF(optr);
  		if (!PyErr_Occurred())
  			PLy_exception_set(PLy_exc_spi_error,
  							  "unrecognized error in PLy_spi_prepare");
  		PLy_elog(WARNING, NULL);
+ 		PLy_exception_set(PLy_exc_spi_error, edata->message);
  		return NULL;
  	}
  	PG_END_TRY();
  
+ 	Assert(plan->plan != NULL);
  	return (PyObject *) plan;
  }
  
*************** PLy_spi_execute(PyObject *self, PyObject
*** 2944,2956 ****
  	PyObject   *list = NULL;
  	long		limit = 0;
  
- 	/* Can't execute more if we have an unhandled error */
- 	if (PLy_error_in_progress)
- 	{
- 		PLy_exception_set(PLy_exc_error, "transaction aborted");
- 		return NULL;
- 	}
- 
  	if (PyArg_ParseTuple(args, "s|l", &query, &limit))
  		return PLy_spi_execute_query(query, limit);
  
--- 2964,2969 ----
*************** PLy_spi_execute(PyObject *self, PyObject
*** 2967,2983 ****
  static PyObject *
  PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
  {
! 	volatile int nargs;
! 	int			i,
! 				rv;
! 	PLyPlanObject *plan;
! 	volatile MemoryContext oldcontext;
  
  	if (list != NULL)
  	{
  		if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list))
  		{
! 			PLy_exception_set(PLy_exc_spi_error, "plpy.execute takes a sequence as its second argument");
  			return NULL;
  		}
  		nargs = PySequence_Length(list);
--- 2980,2996 ----
  static PyObject *
  PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
  {
! 	int 			volatile nargs;
! 	int				i, rv;
! 	PLyPlanObject	*plan;
! 	PyObject		*ret;
! 	MemoryContext volatile oldcontext = CurrentMemoryContext;
  
  	if (list != NULL)
  	{
  		if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list))
  		{
! 			PLy_exception_set(PyExc_ValueError, "plpy.execute takes a sequence as its second argument");
  			return NULL;
  		}
  		nargs = PySequence_Length(list);
*************** PLy_spi_execute_plan(PyObject *ob, PyObj
*** 2992,3003 ****
  		char	   *sv;
  		PyObject   *so = PyObject_Str(list);
  
! 		if (!so)
  			PLy_elog(ERROR, "could not execute plan");
  		sv = PyString_AsString(so);
! 		PLy_exception_set_plural(PLy_exc_spi_error,
! 							  "Expected sequence of %d argument, got %d: %s",
! 							 "Expected sequence of %d arguments, got %d: %s",
  								 plan->nargs,
  								 plan->nargs, nargs, sv);
  		Py_DECREF(so);
--- 3005,3016 ----
  		char	   *sv;
  		PyObject   *so = PyObject_Str(list);
  
! 		if (so == NULL)
  			PLy_elog(ERROR, "could not execute plan");
  		sv = PyString_AsString(so);
! 		PLy_exception_set_plural(PyExc_ValueError,
! 								 "Expected sequence of %d argument, got %d: %s",
! 								 "Expected sequence of %d arguments, got %d: %s",
  								 plan->nargs,
  								 plan->nargs, nargs, sv);
  		Py_DECREF(so);
*************** PLy_spi_execute_plan(PyObject *ob, PyObj
*** 3005,3015 ****
  		return NULL;
  	}
  
- 	oldcontext = CurrentMemoryContext;
  	PG_TRY();
  	{
! 		char	   *nulls = palloc(nargs * sizeof(char));
! 		volatile int j;
  
  		for (j = 0; j < nargs; j++)
  		{
--- 3018,3032 ----
  		return NULL;
  	}
  
  	PG_TRY();
  	{
! 		char	*nulls;
! 		int		volatile j;
! 
! 		if (nargs > 0)
! 			nulls = palloc(nargs * sizeof(char));
! 		else
! 			nulls = NULL;
  
  		for (j = 0; j < nargs; j++)
  		{
*************** PLy_spi_execute_plan(PyObject *ob, PyObj
*** 3049,3082 ****
  
  		rv = SPI_execute_plan(plan->plan, plan->values, nulls,
  							  PLy_curr_procedure->fn_readonly, limit);
  
- 		pfree(nulls);
  	}
  	PG_CATCH();
  	{
! 		int			k;
  
  		MemoryContextSwitchTo(oldcontext);
! 		PLy_error_in_progress = CopyErrorData();
  		FlushErrorState();
  
  		/*
  		 * cleanup plan->values array
  		 */
! 		for (k = 0; k < nargs; k++)
  		{
! 			if (!plan->args[k].out.d.typbyval &&
! 				(plan->values[k] != PointerGetDatum(NULL)))
  			{
! 				pfree(DatumGetPointer(plan->values[k]));
! 				plan->values[k] = PointerGetDatum(NULL);
  			}
  		}
  
  		if (!PyErr_Occurred())
! 			PLy_exception_set(PLy_exc_error,
  							  "unrecognized error in PLy_spi_execute_plan");
  		PLy_elog(WARNING, NULL);
  		return NULL;
  	}
  	PG_END_TRY();
--- 3066,3104 ----
  
  		rv = SPI_execute_plan(plan->plan, plan->values, nulls,
  							  PLy_curr_procedure->fn_readonly, limit);
+ 		ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
+ 
+ 		if (nargs > 0)
+ 			pfree(nulls);
+ 
  
  	}
  	PG_CATCH();
  	{
! 		ErrorData	*edata;
  
  		MemoryContextSwitchTo(oldcontext);
! 		edata = CopyErrorData();
  		FlushErrorState();
  
  		/*
  		 * cleanup plan->values array
  		 */
! 		for (i = 0; i < nargs; i++)
  		{
! 			if (!plan->args[i].out.d.typbyval &&
! 				(plan->values[i] != PointerGetDatum(NULL)))
  			{
! 				pfree(DatumGetPointer(plan->values[i]));
! 				plan->values[i] = PointerGetDatum(NULL);
  			}
  		}
  
  		if (!PyErr_Occurred())
! 			PLy_exception_set(PLy_exc_spi_error,
  							  "unrecognized error in PLy_spi_execute_plan");
  		PLy_elog(WARNING, NULL);
+ 		PLy_exception_set(PLy_exc_spi_error, edata->message);
  		return NULL;
  	}
  	PG_END_TRY();
*************** PLy_spi_execute_plan(PyObject *ob, PyObj
*** 3091,3128 ****
  		}
  	}
  
! 	if (rv < 0)
! 	{
! 		PLy_exception_set(PLy_exc_spi_error,
! 						  "SPI_execute_plan failed: %s",
! 						  SPI_result_code_string(rv));
! 		return NULL;
! 	}
! 
! 	return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
  }
  
  static PyObject *
  PLy_spi_execute_query(char *query, long limit)
  {
  	int			rv;
! 	volatile MemoryContext oldcontext;
  
- 	oldcontext = CurrentMemoryContext;
  	PG_TRY();
  	{
  		pg_verifymbstr(query, strlen(query), false);
  		rv = SPI_execute(query, PLy_curr_procedure->fn_readonly, limit);
  	}
  	PG_CATCH();
  	{
  		MemoryContextSwitchTo(oldcontext);
! 		PLy_error_in_progress = CopyErrorData();
  		FlushErrorState();
  		if (!PyErr_Occurred())
  			PLy_exception_set(PLy_exc_spi_error,
  							  "unrecognized error in PLy_spi_execute_query");
  		PLy_elog(WARNING, NULL);
  		return NULL;
  	}
  	PG_END_TRY();
--- 3113,3146 ----
  		}
  	}
  
! 	return ret;
  }
  
  static PyObject *
  PLy_spi_execute_query(char *query, long limit)
  {
  	int			rv;
! 	PyObject	*ret;
! 	MemoryContext volatile oldcontext = CurrentMemoryContext;
  
  	PG_TRY();
  	{
  		pg_verifymbstr(query, strlen(query), false);
  		rv = SPI_execute(query, PLy_curr_procedure->fn_readonly, limit);
+ 		ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
  	}
  	PG_CATCH();
  	{
+ 		ErrorData	*edata;
+ 
  		MemoryContextSwitchTo(oldcontext);
! 		edata = CopyErrorData();
  		FlushErrorState();
  		if (!PyErr_Occurred())
  			PLy_exception_set(PLy_exc_spi_error,
  							  "unrecognized error in PLy_spi_execute_query");
  		PLy_elog(WARNING, NULL);
+ 		PLy_exception_set(PLy_exc_spi_error, edata->message);
  		return NULL;
  	}
  	PG_END_TRY();
*************** PLy_spi_execute_query(char *query, long
*** 3135,3141 ****
  		return NULL;
  	}
  
! 	return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
  }
  
  static PyObject *
--- 3153,3159 ----
  		return NULL;
  	}
  
! 	return ret;
  }
  
  static PyObject *
*************** PLy_spi_execute_fetch_result(SPITupleTab
*** 3183,3190 ****
  		PG_CATCH();
  		{
  			MemoryContextSwitchTo(oldcontext);
- 			PLy_error_in_progress = CopyErrorData();
- 			FlushErrorState();
  			if (!PyErr_Occurred())
  				PLy_exception_set(PLy_exc_error,
  					   "unrecognized error in PLy_spi_execute_fetch_result");
--- 3201,3206 ----
*************** PLy_spi_execute_fetch_result(SPITupleTab
*** 3202,3207 ****
--- 3218,3263 ----
  	return (PyObject *) result;
  }
  
+ /*
+  * Exception support
+  */
+ 
+ /*
+  * Add an exception to the module. The first parameter is either the actual
+  * module (for Python 3) or the module dictionary
+  */
+ static void PLy_add_exception(PyObject *mod, const char *name, PyObject *exc)
+ {
+ #if PY_MAJOR_VERSION >= 3
+ 	Py_INCREF(exc);
+ 	PyModule_AddObject(mod, name, exc);
+ #else
+ 	PyDict_SetItemString(mod, name, exc);
+ #endif
+ }
+ 
+ /* Add exceptions to the plpy module */
+ static void
+ PLy_initialize_exceptions(PyObject *plpy)
+ {
+ 	PyObject	*mod;
+ 
+ #if PY_MAJOR_VERSION < 3
+ 	/* For Python <3 we add the exceptions to the module dictionary */
+ 	mod = PyModule_GetDict(plpy);
+ #else
+ 	/* In Python 3 you add them directly into the module */
+ 	mod = plpy;
+ #endif
+ 
+ 	PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL);
+ 	PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL);
+ 	PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, NULL);
+ 
+ 	PLy_add_exception(mod, "Error", PLy_exc_error);
+ 	PLy_add_exception(mod, "Fatal", PLy_exc_fatal);
+ 	PLy_add_exception(mod, "SPIError", PLy_exc_spi_error);
+ }
  
  /*
   * language handler and interpreter initialization
*************** PLy_spi_execute_fetch_result(SPITupleTab
*** 3211,3217 ****
  static PyMODINIT_FUNC
  PyInit_plpy(void)
  {
! 	return PyModule_Create(&PLy_module);
  }
  #endif
  
--- 3267,3281 ----
  static PyMODINIT_FUNC
  PyInit_plpy(void)
  {
! 	PyObject	*m;
! 
! 	m = PyModule_Create(&PLy_module);
! 	if (m == NULL)
! 		return NULL;
! 
! 	PLy_initialize_exceptions(m);
! 
! 	return m;
  }
  #endif
  
*************** _PG_init(void)
*** 3229,3234 ****
--- 3293,3300 ----
  	/* 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;
*************** _PG_init(void)
*** 3260,3268 ****
  	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");
  
  	inited = true;
  }
--- 3326,3346 ----
  	PLy_init_plpy();
  	if (PyErr_Occurred())
  		PLy_elog(FATAL, "untrapped error in initialization");
! 
! 	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;
  }
*************** PLy_init_plpy(void)
*** 3290,3297 ****
  	PyObject   *main_mod,
  			   *main_dict,
  			   *plpy_mod;
! 	PyObject   *plpy,
! 			   *plpy_dict;
  
  	/*
  	 * initialize plpy module
--- 3368,3374 ----
  	PyObject   *main_mod,
  			   *main_dict,
  			   *plpy_mod;
! 	PyObject   *plpy;
  
  	/*
  	 * initialize plpy module
*************** PLy_init_plpy(void)
*** 3305,3322 ****
  	plpy = PyModule_Create(&PLy_module);
  #else
  	plpy = Py_InitModule("plpy", PLy_methods);
  #endif
- 	plpy_dict = PyModule_GetDict(plpy);
  
  	/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
  
- 	PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL);
- 	PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL);
- 	PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, NULL);
- 	PyDict_SetItemString(plpy_dict, "Error", PLy_exc_error);
- 	PyDict_SetItemString(plpy_dict, "Fatal", PLy_exc_fatal);
- 	PyDict_SetItemString(plpy_dict, "SPIError", PLy_exc_spi_error);
- 
  	/*
  	 * initialize main module, and add plpy
  	 */
--- 3382,3393 ----
  	plpy = PyModule_Create(&PLy_module);
  #else
  	plpy = Py_InitModule("plpy", PLy_methods);
+ 	/* for Python 3 we initialized the exceptions in PyInit_plpy */
+ 	PLy_initialize_exceptions(plpy);
  #endif
  
  	/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
  
  	/*
  	 * initialize main module, and add plpy
  	 */
*************** PLy_init_plpy(void)
*** 3331,3337 ****
  /* the python interface to the elog function
   * don't confuse these with PLy_elog
   */
! static PyObject *PLy_output(volatile int, PyObject *, PyObject *);
  
  static PyObject *
  PLy_debug(PyObject *self, PyObject *args)
--- 3402,3408 ----
  /* the python interface to the elog function
   * don't confuse these with PLy_elog
   */
! static PyObject *PLy_output(int, PyObject *, PyObject *);
  
  static PyObject *
  PLy_debug(PyObject *self, PyObject *args)
*************** PLy_fatal(PyObject *self, PyObject *args
*** 3379,3387 ****
  static PyObject *
  PLy_output(volatile int level, PyObject *self, PyObject *args)
  {
! 	PyObject   *volatile so;
! 	char	   *volatile sv;
! 	volatile MemoryContext oldcontext;
  
  	if (PyTuple_Size(args) == 1)
  	{
--- 3450,3458 ----
  static PyObject *
  PLy_output(volatile int level, PyObject *self, PyObject *args)
  {
! 	PyObject			*so;
! 	char				*sv;
! 	MemoryContext		 volatile oldcontext = CurrentMemoryContext;
  
  	if (PyTuple_Size(args) == 1)
  	{
*************** PLy_output(volatile int level, PyObject
*** 3402,3432 ****
  		sv = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
  	}
  
! 	oldcontext = CurrentMemoryContext;
  	PG_TRY();
  	{
- 		pg_verifymbstr(sv, strlen(sv), false);
  		elog(level, "%s", sv);
  	}
  	PG_CATCH();
  	{
! 		MemoryContextSwitchTo(oldcontext);
! 		PLy_error_in_progress = CopyErrorData();
! 		FlushErrorState();
  
! 		PyErr_SetString(PLy_exc_error, sv);
  
  		/*
! 		 * Note: If sv came from PyString_AsString(), it points into storage
! 		 * owned by so.  So free so after using sv.
! 		 */
  		Py_XDECREF(so);
  
! 		/*
! 		 * returning NULL here causes the python interpreter to bail. when
! 		 * control passes back to PLy_procedure_call, we check for PG
! 		 * exceptions and re-throw the error.
! 		 */
  		return NULL;
  	}
  	PG_END_TRY();
--- 3473,3500 ----
  		sv = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
  	}
  
! 	pg_verifymbstr(sv, strlen(sv), false);
  	PG_TRY();
  	{
  		elog(level, "%s", sv);
  	}
  	PG_CATCH();
  	{
! 		ErrorData  *edata;
  
!         /* Must reset elog.c's state */
!         MemoryContextSwitchTo(oldcontext);
!         edata = CopyErrorData();
!         FlushErrorState();
  
  		/*
!         * Note: If sv came from PyString_AsString(), it points into storage
!         * owned by so.  So free so after using sv.
!         */
  		Py_XDECREF(so);
  
! 		/* Make Python raise the exception */
! 		PLy_exception_set(PLy_exc_error, edata->message);
  		return NULL;
  	}
  	PG_END_TRY();
*************** PLy_elog(int elevel, const char *fmt,...
*** 3528,3538 ****
  	{
  		if (fmt)
  			ereport(elevel,
! 					(errmsg("PL/Python: %s", emsg.data),
  					 (xmsg) ? errdetail("%s", xmsg) : 0));
  		else
  			ereport(elevel,
! 					(errmsg("PL/Python: %s", xmsg)));
  	}
  	PG_CATCH();
  	{
--- 3596,3606 ----
  	{
  		if (fmt)
  			ereport(elevel,
! 					(errmsg("%s", emsg.data),
  					 (xmsg) ? errdetail("%s", xmsg) : 0));
  		else
  			ereport(elevel,
! 					(errmsg("%s", xmsg)));
  	}
  	PG_CATCH();
  	{
*************** PLy_traceback(int *xlevel)
*** 3635,3653 ****
  
  /* some dumb utility functions */
  static void *
! PLy_malloc(size_t bytes)
  {
! 	void	   *ptr = malloc(bytes);
! 
! 	if (ptr == NULL)
! 		ereport(FATAL,
! 				(errcode(ERRCODE_OUT_OF_MEMORY),
! 				 errmsg("out of memory")));
! 	return ptr;
  }
  
  static void *
! PLy_malloc0(size_t bytes)
  {
  	void	   *ptr = PLy_malloc(bytes);
  
--- 3703,3716 ----
  
  /* some dumb utility functions */
  static void *
! PLy_malloc(Size bytes)
  {
! 	/* We need our allocations to be long-lived, so use TopMemoryContext */
! 	return MemoryContextAlloc(TopMemoryContext, bytes);
  }
  
  static void *
! PLy_malloc0(Size bytes)
  {
  	void	   *ptr = PLy_malloc(bytes);
  
*************** PLy_strdup(const char *str)
*** 3672,3678 ****
  static void
  PLy_free(void *ptr)
  {
! 	free(ptr);
  }
  
  /*
--- 3735,3741 ----
  static void
  PLy_free(void *ptr)
  {
! 	pfree(ptr);
  }
  
  /*
diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql
index 7cae124..298411a 100644
*** a/src/pl/plpython/sql/plpython_test.sql
--- b/src/pl/plpython/sql/plpython_test.sql
*************** return words'
*** 25,30 ****
--- 25,39 ----
  
  select argument_test_one(users, fname, lname) from users where lname = 'doe' order by 1;
  
+ -- check module contents
+ CREATE FUNCTION module_contents() RETURNS text AS
+ $$
+ contents = list(filter(lambda x: not x.startswith("__"), dir(plpy)))
+ contents.sort()
+ return ", ".join(contents)
+ $$ LANGUAGE plpythonu;
+ 
+ select module_contents();
  
  CREATE FUNCTION elog_test() RETURNS void
  AS $$
