diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 35349616bb9d99db9d7845f151a0a8caeeab823d..b630f0d6f8bc8cf054ce7709bd6753261639f6dc 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201101251
+#define CATALOG_VERSION_NO	201102011
 
 #endif
diff --git a/src/include/catalog/pg_pltemplate.h b/src/include/catalog/pg_pltemplate.h
index d987ddc40ee3cde3ee0b78ca7a7a17ab291c49c4..c0578f92c098d7115b1926b4d0088a6387b85ad5 100644
--- a/src/include/catalog/pg_pltemplate.h
+++ b/src/include/catalog/pg_pltemplate.h
@@ -72,8 +72,8 @@ DATA(insert ( "pltcl"		t t "pltcl_call_handler" _null_ _null_ "$libdir/pltcl" _n
 DATA(insert ( "pltclu"		f f "pltclu_call_handler" _null_ _null_ "$libdir/pltcl" _null_ ));
 DATA(insert ( "plperl"		t t "plperl_call_handler" "plperl_inline_handler" "plperl_validator" "$libdir/plperl" _null_ ));
 DATA(insert ( "plperlu"		f f "plperl_call_handler" "plperl_inline_handler" "plperl_validator" "$libdir/plperl" _null_ ));
-DATA(insert ( "plpythonu"	f f "plpython_call_handler" "plpython_inline_handler" _null_ "$libdir/plpython" _null_ ));
-DATA(insert ( "plpython2u"	f f "plpython_call_handler" "plpython_inline_handler" _null_ "$libdir/plpython2" _null_ ));
-DATA(insert ( "plpython3u"	f f "plpython3_call_handler" "plpython3_inline_handler" _null_ "$libdir/plpython3" _null_ ));
+DATA(insert ( "plpythonu"	f f "plpython_call_handler" "plpython_inline_handler" "plpython_validator" "$libdir/plpython" _null_ ));
+DATA(insert ( "plpython2u"	f f "plpython_call_handler" "plpython_inline_handler" "plpython_validator" "$libdir/plpython2" _null_ ));
+DATA(insert ( "plpython3u"	f f "plpython3_call_handler" "plpython3_inline_handler" "plpython3_validator" "$libdir/plpython3" _null_ ));
 
 #endif   /* PG_PLTEMPLATE_H */
diff --git a/src/pl/plpython/expected/README b/src/pl/plpython/expected/README
index f0115280909191d7f4ac998b140a1320fa128073..27c995db9a666894a6078abaa6a654b74eb1f154 100644
--- a/src/pl/plpython/expected/README
+++ b/src/pl/plpython/expected/README
@@ -1,5 +1,7 @@
 Guide to alternative expected files:
 
+plpython_error_0.out		Python 2.4 and older
+
 plpython_unicode.out		any version, when server encoding != SQL_ASCII and client encoding = UTF8; else ...
 plpython_unicode_0.out		any version, when server encoding != SQL_ASCII and client encoding != UTF8; else ...
 plpython_unicode_2.out		Python 2.2
diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index ea4a54c3d754a272f6ce2d1016716ba7c7d92194..2b6141c376ee9d58787f9015148195a599eadb37 100644
--- a/src/pl/plpython/expected/plpython_error.out
+++ b/src/pl/plpython/expected/plpython_error.out
@@ -1,6 +1,30 @@
 -- test error handling, i forgot to restore Warn_restart in
 -- the trigger handler once. the errors and subsequent core dump were
 -- interesting.
+/* Flat out Python syntax error
+ */
+CREATE FUNCTION python_syntax_error() RETURNS text
+        AS
+'.syntaxerror'
+        LANGUAGE plpythonu;
+ERROR:  could not compile PL/Python function "python_syntax_error"
+DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
+/* With check_function_bodies = false the function should get defined
+ * and the error reported when called
+ */
+SET check_function_bodies = false;
+CREATE FUNCTION python_syntax_error() RETURNS text
+        AS
+'.syntaxerror'
+        LANGUAGE plpythonu;
+SELECT python_syntax_error();
+ERROR:  could not compile PL/Python function "python_syntax_error"
+DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
+/* Run the function twice to check if the hashtable entry gets cleaned up */
+SELECT python_syntax_error();
+ERROR:  could not compile PL/Python function "python_syntax_error"
+DETAIL:  SyntaxError: invalid syntax (<string>, line 2)
+RESET check_function_bodies;
 /* Flat out syntax error
  */
 CREATE FUNCTION sql_syntax_error() RETURNS text
diff --git a/src/pl/plpython/expected/plpython_error_0.out b/src/pl/plpython/expected/plpython_error_0.out
new file mode 100644
index 0000000000000000000000000000000000000000..3842d8fb4a1acfeda27a24eb926425eeab03ca08
--- /dev/null
+++ b/src/pl/plpython/expected/plpython_error_0.out
@@ -0,0 +1,152 @@
+-- test error handling, i forgot to restore Warn_restart in
+-- the trigger handler once. the errors and subsequent core dump were
+-- interesting.
+/* Flat out Python syntax error
+ */
+CREATE FUNCTION python_syntax_error() RETURNS text
+        AS
+'.syntaxerror'
+        LANGUAGE plpythonu;
+ERROR:  could not compile PL/Python function "python_syntax_error"
+DETAIL:  SyntaxError: invalid syntax (line 2)
+/* With check_function_bodies = false the function should get defined
+ * and the error reported when called
+ */
+SET check_function_bodies = false;
+CREATE FUNCTION python_syntax_error() RETURNS text
+        AS
+'.syntaxerror'
+        LANGUAGE plpythonu;
+SELECT python_syntax_error();
+ERROR:  could not compile PL/Python function "python_syntax_error"
+DETAIL:  SyntaxError: invalid syntax (line 2)
+/* Run the function twice to check if the hashtable entry gets cleaned up */
+SELECT python_syntax_error();
+ERROR:  could not compile PL/Python function "python_syntax_error"
+DETAIL:  SyntaxError: invalid syntax (line 2)
+RESET check_function_bodies;
+/* Flat out syntax error
+ */
+CREATE FUNCTION sql_syntax_error() RETURNS text
+        AS
+'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"
+LINE 1: syntax error
+        ^
+QUERY:  syntax error
+CONTEXT:  PL/Python function "sql_syntax_error"
+/* check the handling of uncaught python exceptions
+ */
+CREATE FUNCTION exception_index_invalid(text) RETURNS text
+	AS
+'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_nested() RETURNS text
+	AS
+'rv = plpy.execute("SELECT test5(''foo'')")
+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
+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
+ */
+CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
+	AS
+'if "plan" not in SD:
+	q = "SELECT fname FROM users WHERE lname = $1"
+	SD["plan"] = plpy.prepare(q, [ "test" ])
+rv = plpy.execute(SD["plan"], [ a ])
+if len(rv):
+	return rv[0]["fname"]
+return None
+'
+	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
+ */
+CREATE FUNCTION invalid_type_caught(a text) RETURNS text
+	AS
+'if "plan" not in SD:
+	q = "SELECT fname FROM users WHERE lname = $1"
+	try:
+		SD["plan"] = plpy.prepare(q, [ "test" ])
+	except plpy.SPIError, ex:
+		plpy.notice(str(ex))
+		return None
+rv = plpy.execute(SD["plan"], [ a ])
+if len(rv):
+	return rv[0]["fname"]
+return None
+'
+	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
+ */
+CREATE FUNCTION invalid_type_reraised(a text) RETURNS text
+	AS
+'if "plan" not in SD:
+	q = "SELECT fname FROM users WHERE lname = $1"
+	try:
+		SD["plan"] = plpy.prepare(q, [ "test" ])
+	except plpy.SPIError, ex:
+		plpy.error(str(ex))
+rv = plpy.execute(SD["plan"], [ a ])
+if len(rv):
+	return rv[0]["fname"]
+return None
+'
+	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
+ */
+CREATE FUNCTION valid_type(a text) RETURNS text
+	AS
+'if "plan" not in SD:
+	SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ])
+rv = plpy.execute(SD["plan"], [ a ])
+if len(rv):
+	return rv[0]["fname"]
+return None
+'
+	LANGUAGE plpythonu;
+SELECT valid_type('rick');
+ valid_type 
+------------
+ 
+(1 row)
+
diff --git a/src/pl/plpython/expected/plpython_record.out b/src/pl/plpython/expected/plpython_record.out
index c8c4f9d5a3cbb02a8d549dff1dd6f7216b8c7d6a..770f764d6f47c6ca5e2bd0a5ba86bbeced2ed073 100644
--- a/src/pl/plpython/expected/plpython_record.out
+++ b/src/pl/plpython/expected/plpython_record.out
@@ -47,6 +47,7 @@ CREATE FUNCTION test_in_out_params_multi(first in text,
                                          second out text, third out text) AS $$
 return first + '_record_in_to_out';
 $$ LANGUAGE plpythonu;
+ERROR:  PL/Python functions cannot return type record
 CREATE FUNCTION test_inout_params(first inout text) AS $$
 return first + '_inout';
 $$ LANGUAGE plpythonu;
@@ -299,7 +300,10 @@ SELECT * FROM test_in_out_params('test_in');
 
 -- this doesn't work yet :-(
 SELECT * FROM test_in_out_params_multi('test_in');
-ERROR:  PL/Python functions cannot return type record
+ERROR:  function test_in_out_params_multi(unknown) does not exist
+LINE 1: SELECT * FROM test_in_out_params_multi('test_in');
+                      ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM test_inout_params('test_in');
      first     
 ---------------
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index 982005bea162e080f23a68bb8ba23105acfdf48d..e74a4009d462adb2a38133ffc511dcf2fb3746e9 100644
--- a/src/pl/plpython/expected/plpython_types.out
+++ b/src/pl/plpython/expected/plpython_types.out
@@ -571,9 +571,13 @@ PL/Python function "test_type_conversion_array_mixed2"
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [None]
 $$ LANGUAGE plpythonu;
-SELECT * FROM test_type_conversion_array_record();
 ERROR:  PL/Python functions cannot return type type_record[]
 DETAIL:  PL/Python does not support conversion to arrays of row types.
+SELECT * FROM test_type_conversion_array_record();
+ERROR:  function test_type_conversion_array_record() does not exist
+LINE 1: SELECT * FROM test_type_conversion_array_record();
+                      ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
 $$ LANGUAGE plpythonu;
diff --git a/src/pl/plpython/expected/plpython_types_3.out b/src/pl/plpython/expected/plpython_types_3.out
index eeb23b7b5e790944191468e3aed16734c4c707bc..577c1fff4e997f4729423ce8a9c810f642d1d6be 100644
--- a/src/pl/plpython/expected/plpython_types_3.out
+++ b/src/pl/plpython/expected/plpython_types_3.out
@@ -571,9 +571,13 @@ PL/Python function "test_type_conversion_array_mixed2"
 CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
 return [None]
 $$ LANGUAGE plpython3u;
-SELECT * FROM test_type_conversion_array_record();
 ERROR:  PL/Python functions cannot return type type_record[]
 DETAIL:  PL/Python does not support conversion to arrays of row types.
+SELECT * FROM test_type_conversion_array_record();
+ERROR:  function test_type_conversion_array_record() does not exist
+LINE 1: SELECT * FROM test_type_conversion_array_record();
+                      ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
 return 'abc'
 $$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index aafe556c04e2677ac8efe51ed930bd067a73fa47..fbfeb2c9f16029cc682f4c557e4ba2990fbc06c3 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -251,15 +251,18 @@ typedef struct PLyResultObject
 
 #if PY_MAJOR_VERSION >= 3
 /* Use separate names to avoid clash in pg_pltemplate */
+#define plpython_validator plpython3_validator
 #define plpython_call_handler plpython3_call_handler
 #define plpython_inline_handler plpython3_inline_handler
 #endif
 
 /* exported functions */
+Datum		plpython_validator(PG_FUNCTION_ARGS);
 Datum		plpython_call_handler(PG_FUNCTION_ARGS);
 Datum		plpython_inline_handler(PG_FUNCTION_ARGS);
 void		_PG_init(void);
 
+PG_FUNCTION_INFO_V1(plpython_validator);
 PG_FUNCTION_INFO_V1(plpython_call_handler);
 PG_FUNCTION_INFO_V1(plpython_inline_handler);
 
@@ -437,6 +440,42 @@ plpython_return_error_callback(void *arg)
 		errcontext("while creating return value");
 }
 
+static bool
+PLy_procedure_is_trigger(Form_pg_proc procStruct)
+{
+	return (procStruct->prorettype == TRIGGEROID ||
+			(procStruct->prorettype == OPAQUEOID &&
+			 procStruct->pronargs == 0));
+}
+
+Datum
+plpython_validator(PG_FUNCTION_ARGS)
+{
+	Oid			funcoid = PG_GETARG_OID(0);
+	HeapTuple	tuple;
+	Form_pg_proc procStruct;
+	bool		is_trigger;
+
+	if (!check_function_bodies)
+	{
+		PG_RETURN_VOID();
+	}
+
+	/* Get the new function's pg_proc entry */
+	tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for function %u", funcoid);
+	procStruct = (Form_pg_proc) GETSTRUCT(tuple);
+
+	is_trigger = PLy_procedure_is_trigger(procStruct);
+
+	ReleaseSysCache(tuple);
+
+	PLy_procedure_get(funcoid, is_trigger);
+
+	PG_RETURN_VOID();
+}
+
 Datum
 plpython_call_handler(PG_FUNCTION_ARGS)
 {
diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql
index 5ca68495be036f86df939d02c45115d312ce99e0..6509257b24deb355d6599f6ca651544b44694211 100644
--- a/src/pl/plpython/sql/plpython_error.sql
+++ b/src/pl/plpython/sql/plpython_error.sql
@@ -2,6 +2,29 @@
 -- the trigger handler once. the errors and subsequent core dump were
 -- interesting.
 
+/* Flat out Python syntax error
+ */
+CREATE FUNCTION python_syntax_error() RETURNS text
+        AS
+'.syntaxerror'
+        LANGUAGE plpythonu;
+
+/* With check_function_bodies = false the function should get defined
+ * and the error reported when called
+ */
+SET check_function_bodies = false;
+
+CREATE FUNCTION python_syntax_error() RETURNS text
+        AS
+'.syntaxerror'
+        LANGUAGE plpythonu;
+
+SELECT python_syntax_error();
+/* Run the function twice to check if the hashtable entry gets cleaned up */
+SELECT python_syntax_error();
+
+RESET check_function_bodies;
+
 /* Flat out syntax error
  */
 CREATE FUNCTION sql_syntax_error() RETURNS text