diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
index a623f3509bd56889a7017767c70882aa70b89513..384b7aecc485f437baaf719c0aae17269e4ff1d1 100644
--- a/doc/src/sgml/plpython.sgml
+++ b/doc/src/sgml/plpython.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpython.sgml,v 1.43 2009/12/19 22:23:21 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpython.sgml,v 1.44 2010/01/22 15:45:15 petere Exp $ -->
 
 <chapter id="plpython">
  <title>PL/Python - Python Procedural Language</title>
@@ -551,6 +551,24 @@ $$ LANGUAGE plpythonu;
   </para>
  </sect1>
 
+ <sect1 id="plpython-do">
+  <title>Anonymous Code Blocks</title>
+
+  <para>
+   PL/Python also supports anonymous code blocks called with the
+   <xref linkend="sql-do"> statement:
+
+<programlisting>
+DO $$
+    # PL/Python code
+$$ LANGUAGE plpythonu;
+</programlisting>
+
+   An anonymous code block receives no arguments, and whatever value it
+   might return is discarded.  Otherwise it behaves just like a function.
+  </para>
+ </sect1>
+
  <sect1 id="plpython-trigger">
   <title>Trigger Functions</title>
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index f37b9077a9be9d9d58a8d989c267d75812cf07cb..29e04bea01081cd306a9399b70e2322c84bb9317 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.576 2010/01/19 14:11:32 mha Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.577 2010/01/22 15:45:15 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201001191
+#define CATALOG_VERSION_NO	201001221
 
 #endif
diff --git a/src/include/catalog/pg_pltemplate.h b/src/include/catalog/pg_pltemplate.h
index f7b77081cbca81d3ba0e5a13b54da57866032534..59c890100b63ab0438651ec6d8734364765d15f2 100644
--- a/src/include/catalog/pg_pltemplate.h
+++ b/src/include/catalog/pg_pltemplate.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_pltemplate.h,v 1.12 2010/01/05 01:06:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_pltemplate.h,v 1.13 2010/01/22 15:45:15 petere Exp $
  *
  * NOTES
  *	  the genbki.pl script reads this file and generates .bki
@@ -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" _null_ _null_ "$libdir/plpython" _null_ ));
-DATA(insert ( "plpython2u"	f f "plpython_call_handler" _null_ _null_ "$libdir/plpython2" _null_ ));
-DATA(insert ( "plpython3u"	f f "plpython_call_handler" _null_ _null_ "$libdir/plpython3" _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 "plpython_call_handler" "plpython_inline_handler" _null_ "$libdir/plpython3" _null_ ));
 
 #endif   /* PG_PLTEMPLATE_H */
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index 5db880b08e1deca918640dcb0dc90ecba88f27d1..4b18076a0f4f1ba0621662615f7553719e39acf3 100644
--- a/src/pl/plpython/Makefile
+++ b/src/pl/plpython/Makefile
@@ -1,4 +1,4 @@
-# $PostgreSQL: pgsql/src/pl/plpython/Makefile,v 1.35 2009/12/15 22:59:54 petere Exp $
+# $PostgreSQL: pgsql/src/pl/plpython/Makefile,v 1.36 2010/01/22 15:45:15 petere Exp $
 
 subdir = src/pl/plpython
 top_builddir = ../../..
@@ -66,6 +66,7 @@ REGRESS = \
 	plpython_schema \
 	plpython_populate \
 	plpython_test \
+	plpython_do \
 	plpython_global \
 	plpython_import \
 	plpython_spi \
diff --git a/src/pl/plpython/expected/plpython_do.out b/src/pl/plpython/expected/plpython_do.out
new file mode 100644
index 0000000000000000000000000000000000000000..9de261ae4502b880d00b822050df70171c9d4726
--- /dev/null
+++ b/src/pl/plpython/expected/plpython_do.out
@@ -0,0 +1,6 @@
+DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
+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
diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index 36ffa8b5bd40bf5b2df167bf1d071dc139b392cd..1f24c13892d43f843c61e7b88797df9aa25a5d23 100644
--- a/src/pl/plpython/expected/plpython_error.out
+++ b/src/pl/plpython/expected/plpython_error.out
@@ -22,8 +22,7 @@ CREATE FUNCTION exception_index_invalid(text) RETURNS text
 'return args[1]'
 	LANGUAGE plpythonu;
 SELECT exception_index_invalid('test');
-ERROR:  PL/Python: PL/Python function "exception_index_invalid" failed
-DETAIL:  IndexError: list index out of range
+ERROR:  PL/Python: IndexError: list index out of range
 CONTEXT:  PL/Python function "exception_index_invalid"
 /* check handling of nested exceptions
  */
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 8ddb08c2029466e11f32d06ecea1951a1c4834ab..c6accdac9a86063a3837e30426f301348fea012f 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- *	$PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.135 2010/01/16 11:03:51 petere Exp $
+ *	$PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.136 2010/01/22 15:45:15 petere Exp $
  *
  *********************************************************************
  */
@@ -243,14 +243,13 @@ typedef struct PLyResultObject
 
 /* function declarations */
 
-/* Two exported functions: first is the magic telling Postgresql
- * what function call interface it implements. Second is for
- * initialization of the interpreter during library load.
- */
+/* exported functions */
 Datum		plpython_call_handler(PG_FUNCTION_ARGS);
+Datum		plpython_inline_handler(PG_FUNCTION_ARGS);
 void		_PG_init(void);
 
 PG_FUNCTION_INFO_V1(plpython_call_handler);
+PG_FUNCTION_INFO_V1(plpython_inline_handler);
 
 /* most of the remaining of the declarations, all static */
 
@@ -418,6 +417,12 @@ plpython_error_callback(void *arg)
 		errcontext("PL/Python function \"%s\"", PLy_procedure_name(PLy_curr_procedure));
 }
 
+static void
+plpython_inline_error_callback(void *arg)
+{
+	errcontext("PL/Python anonymous code block");
+}
+
 static void
 plpython_trigger_error_callback(void *arg)
 {
@@ -495,6 +500,60 @@ plpython_call_handler(PG_FUNCTION_ARGS)
 	return retval;
 }
 
+Datum
+plpython_inline_handler(PG_FUNCTION_ARGS)
+{
+	InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
+	FunctionCallInfoData fake_fcinfo;
+	FmgrInfo	flinfo;
+	PLyProcedure *save_curr_proc;
+	PLyProcedure *volatile proc = NULL;
+	ErrorContextCallback plerrcontext;
+
+	if (SPI_connect() != SPI_OK_CONNECT)
+		elog(ERROR, "SPI_connect failed");
+
+	save_curr_proc = PLy_curr_procedure;
+
+	/*
+	 * Setup error traceback support for ereport()
+	 */
+	plerrcontext.callback = plpython_inline_error_callback;
+	plerrcontext.previous = error_context_stack;
+	error_context_stack = &plerrcontext;
+
+	MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+	MemSet(&flinfo, 0, sizeof(flinfo));
+	fake_fcinfo.flinfo = &flinfo;
+	flinfo.fn_oid = InvalidOid;
+	flinfo.fn_mcxt = CurrentMemoryContext;
+
+	proc = PLy_malloc0(sizeof(PLyProcedure));
+	proc->pyname = PLy_strdup("__plpython_inline_block");
+	proc->result.out.d.typoid = VOIDOID;
+
+	PG_TRY();
+	{
+		PLy_procedure_compile(proc, codeblock->source_text);
+		PLy_curr_procedure = proc;
+		PLy_function_handler(&fake_fcinfo, proc);
+	}
+	PG_CATCH();
+	{
+		PLy_curr_procedure = save_curr_proc;
+		PyErr_Clear();
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+
+	/* Pop the error context stack */
+	error_context_stack = plerrcontext.previous;
+
+	PLy_curr_procedure = save_curr_proc;
+
+	PG_RETURN_VOID();
+}
+
 /* trigger and function sub handlers
  *
  * the python function is expected to return Py_None if the tuple is
@@ -1107,7 +1166,7 @@ PLy_procedure_call(PLyProcedure *proc, char *kargs, PyObject *vargs)
 	if (rv == NULL || PyErr_Occurred())
 	{
 		Py_XDECREF(rv);
-		PLy_elog(ERROR, "PL/Python function \"%s\" failed", proc->proname);
+		PLy_elog(ERROR, NULL);
 	}
 
 	return rv;
diff --git a/src/pl/plpython/sql/plpython_do.sql b/src/pl/plpython/sql/plpython_do.sql
new file mode 100644
index 0000000000000000000000000000000000000000..8596c39d37a98ce126ae9826ed0ad71eac095be3
--- /dev/null
+++ b/src/pl/plpython/sql/plpython_do.sql
@@ -0,0 +1,3 @@
+DO $$ plpy.notice("This is plpythonu.") $$ LANGUAGE plpythonu;
+
+DO $$ nonsense $$ LANGUAGE plpythonu;