]> granicus.if.org Git - postgresql/blobdiff - src/pl/plpython/plpython.c
Improve the recently-added support for properly pluralized error messages
[postgresql] / src / pl / plpython / plpython.c
index d20a5f72f1301954324ff53377294a1da6d67e5e..345f3f65236619d6cd431fde70a9aaef4eabe709 100644 (file)
@@ -1,12 +1,46 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.76 2006/03/14 22:48:24 tgl Exp $
+ *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.121 2009/06/04 18:33:08 tgl Exp $
  *
  *********************************************************************
  */
 
+#if defined(_MSC_VER) && defined(_DEBUG)
+/* Python uses #pragma to bring in a non-default libpython on VC++ if
+ * _DEBUG is defined */
+#undef _DEBUG
+/* Also hide away errcode, since we load Python.h before postgres.h */
+#define errcode __msvc_errcode
 #include <Python.h>
+#undef errcode
+#define _DEBUG
+#elif defined (_MSC_VER)
+#define errcode __msvc_errcode
+#include <Python.h>
+#undef errcode
+#else
+#include <Python.h>
+#endif
+
+/*
+ * Py_ssize_t compat for Python <= 2.4
+ */
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#endif
+
+/*
+ * PyBool_FromLong is supported from 2.3.
+ */
+#if PY_VERSION_HEX < 0x02030000
+#define PyBool_FromLong(x) PyInt_FromLong(x)
+#endif
+
+
 #include "postgres.h"
 
 /* system stuff */
 #include <fcntl.h>
 
 /* postgreSQL stuff */
-#include "access/heapam.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/trigger.h"
 #include "executor/spi.h"
+#include "funcapi.h"
 #include "fmgr.h"
+#include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "parser/parse_type.h"
 #include "tcop/tcopprot.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
 
+/* define our text domain for translations */
+#undef TEXTDOMAIN
+#define TEXTDOMAIN PG_TEXTDOMAIN("plpython")
+
 #include <compile.h>
 #include <eval.h>
 
+PG_MODULE_MAGIC;
+
 /* convert Postgresql Datum or tuple into a PyObject.
  * input to Python.  Tuples are converted to dictionary
  * objects.
@@ -42,7 +83,8 @@ typedef PyObject *(*PLyDatumToObFunc) (const char *);
 typedef struct PLyDatumToOb
 {
        PLyDatumToObFunc func;
-       FmgrInfo        typfunc;
+       FmgrInfo        typfunc;                /* The type's output function */
+       Oid                     typoid;                 /* The OID of the type */
        Oid                     typioparam;
        bool            typbyval;
 }      PLyDatumToOb;
@@ -104,10 +146,13 @@ typedef struct PLyProcedure
        char       *proname;            /* SQL name of procedure */
        char       *pyname;                     /* Python name of procedure */
        TransactionId fn_xmin;
-       CommandId       fn_cmin;
+       ItemPointerData fn_tid;
        bool            fn_readonly;
        PLyTypeInfo result;                     /* also used to store info for trigger tuple
                                                                 * type */
+       bool            is_setof;               /* true, if procedure returns result set */
+       PyObject   *setof;                      /* contents of result set. */
+       char      **argnames;           /* Argument names */
        PLyTypeInfo args[FUNC_MAX_ARGS];
        int                     nargs;
        PyObject   *code;                       /* compiled procedure code */
@@ -133,7 +178,7 @@ typedef struct PLyResultObject
 {
        PyObject_HEAD
        /* HeapTuple *tuples; */
-       PyObject   *nrows;                      /* number of rows returned by query */
+       PyObject * nrows;                       /* number of rows returned by query */
        PyObject   *rows;                       /* data rows, or None if no data returned */
        PyObject   *status;                     /* query status, SPI_OK_*, or SPI_ERR_* */
 }      PLyResultObject;
@@ -142,11 +187,11 @@ typedef struct PLyResultObject
 /* function declarations */
 
 /* Two exported functions: first is the magic telling Postgresql
- * what function call interface it implements. Second allows
- * preinitialization of the interpreter during postmaster startup.
+ * what function call interface it implements. Second is for
+ * initialization of the interpreter during library load.
  */
 Datum          plpython_call_handler(PG_FUNCTION_ARGS);
-void           plpython_init(void);
+void           _PG_init(void);
 
 PG_FUNCTION_INFO_V1(plpython_call_handler);
 
@@ -156,26 +201,28 @@ PG_FUNCTION_INFO_V1(plpython_call_handler);
  * of plpython_call_handler.  initialize the python interpreter
  * and global data.
  */
-static void PLy_init_all(void);
 static void PLy_init_interp(void);
 static void PLy_init_plpy(void);
 
-/* call PyErr_SetString with a vprint interface */
-static void
-PLy_exception_set(PyObject *, const char *,...)
+/* call PyErr_SetString with a vprint interface and translation support */
+static void PLy_exception_set(PyObject *, const char *,...)
 __attribute__((format(printf, 2, 3)));
+/* same, with pluralized message */
+static void PLy_exception_set_plural(PyObject *, const char *, const char *,
+                                                                        unsigned long n,...)
+__attribute__((format(printf, 2, 5)))
+__attribute__((format(printf, 3, 5)));
 
 /* Get the innermost python procedure called from the backend */
 static char *PLy_procedure_name(PLyProcedure *);
 
 /* some utility functions */
-static void PLy_elog(int, const char *,...);
+static void PLy_elog(int, const char *,...)
+__attribute__((format(printf, 2, 3)));
 static char *PLy_traceback(int *);
-static char *PLy_vprintf(const char *fmt, va_list ap);
-static char *PLy_printf(const char *fmt,...);
 
 static void *PLy_malloc(size_t);
-static void *PLy_realloc(void *, size_t);
+static void *PLy_malloc0(size_t);
 static char *PLy_strdup(const char *);
 static void PLy_free(void *);
 
@@ -184,6 +231,7 @@ static Datum PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *);
 static HeapTuple PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *);
 
 static PyObject *PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *);
+static void PLy_function_delete_args(PLyProcedure *);
 static PyObject *PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *,
                                           HeapTuple *);
 static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *,
@@ -194,9 +242,8 @@ static PyObject *PLy_procedure_call(PLyProcedure *, char *, PyObject *);
 static PLyProcedure *PLy_procedure_get(FunctionCallInfo fcinfo,
                                  Oid tgreloid);
 
-static PLyProcedure *PLy_procedure_create(FunctionCallInfo fcinfo,
-                                        Oid tgreloid,
-                                        HeapTuple procTup, char *key);
+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 *);
@@ -219,9 +266,9 @@ static PyObject *PLyInt_FromString(const char *);
 static PyObject *PLyLong_FromString(const char *);
 static PyObject *PLyString_FromString(const char *);
 
-
-/* global data */
-static bool    PLy_first_call = true;
+static HeapTuple PLyMapping_ToTuple(PLyTypeInfo *, PyObject *);
+static HeapTuple PLySequence_ToTuple(PLyTypeInfo *, PyObject *);
+static HeapTuple PLyObject_ToTuple(PLyTypeInfo *, PyObject *);
 
 /*
  * Currently active plpython function
@@ -288,10 +335,8 @@ plpython_call_handler(PG_FUNCTION_ARGS)
        PLyProcedure *save_curr_proc;
        PLyProcedure *volatile proc = NULL;
 
-       PLy_init_all();
-
        if (SPI_connect() != SPI_OK_CONNECT)
-               elog(ERROR, "could not connect to SPI manager");
+               elog(ERROR, "SPI_connect failed");
 
        save_curr_proc = PLy_curr_procedure;
 
@@ -376,8 +421,8 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
                        if (!PyString_Check(plrv))
                                ereport(ERROR,
                                                (errcode(ERRCODE_DATA_EXCEPTION),
-                                                errmsg("unexpected return value from trigger procedure"),
-                                                errdetail("Expected None or a String.")));
+                                       errmsg("unexpected return value from trigger procedure"),
+                                                errdetail("Expected None or a string.")));
 
                        srv = PyString_AsString(plrv);
                        if (pg_strcasecmp(srv, "SKIP") == 0)
@@ -390,17 +435,18 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
                                        TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
                                        rv = PLy_modify_tuple(proc, plargs, tdata, rv);
                                else
-                                       elog(WARNING, "ignoring modified tuple in DELETE trigger");
+                                       ereport(WARNING,
+                                                       (errmsg("PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored")));
                        }
                        else if (pg_strcasecmp(srv, "OK") != 0)
                        {
                                /*
-                                * accept "OK" as an alternative to None; otherwise,
-                                * raise an error
+                                * accept "OK" as an alternative to None; otherwise, raise an
+                                * error
                                 */
                                ereport(ERROR,
                                                (errcode(ERRCODE_DATA_EXCEPTION),
-                                                errmsg("unexpected return value from trigger procedure"),
+                                       errmsg("unexpected return value from trigger procedure"),
                                                 errdetail("Expected None, \"OK\", \"SKIP\", or \"MODIFY\".")));
                        }
                }
@@ -447,9 +493,11 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
        PG_TRY();
        {
                if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
-                       elog(ERROR, "TD[\"new\"] deleted, unable to modify tuple");
+                       ereport(ERROR, 
+                                       (errmsg("TD[\"new\"] deleted, cannot modify row")));
                if (!PyDict_Check(plntup))
-                       elog(ERROR, "TD[\"new\"] is not a dictionary object");
+                       ereport(ERROR,
+                                       (errmsg("TD[\"new\"] is not a dictionary")));
                Py_INCREF(plntup);
 
                plkeys = PyDict_Keys(plntup);
@@ -467,32 +515,41 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
 
                        platt = PyList_GetItem(plkeys, i);
                        if (!PyString_Check(platt))
-                               elog(ERROR, "attribute name is not a string");
+                               ereport(ERROR,
+                                               (errmsg("name of TD[\"new\"] attribute at ordinal position %d is not a string", i)));
                        attn = SPI_fnumber(tupdesc, PyString_AsString(platt));
                        if (attn == SPI_ERROR_NOATTRIBUTE)
-                               elog(ERROR, "invalid attribute \"%s\" in tuple",
-                                        PyString_AsString(platt));
+                               ereport(ERROR,
+                                               (errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row",
+                                                               PyString_AsString(platt))));
                        atti = attn - 1;
 
                        plval = PyDict_GetItem(plntup, platt);
                        if (plval == NULL)
-                               elog(FATAL, "python interpreter is probably corrupted");
+                               elog(FATAL, "Python interpreter is probably corrupted");
 
                        Py_INCREF(plval);
 
                        modattrs[i] = attn;
 
-                       if (plval != Py_None && !tupdesc->attrs[atti]->attisdropped)
+                       if (tupdesc->attrs[atti]->attisdropped)
+                       {
+                               modvalues[i] = (Datum) 0;
+                               modnulls[i] = 'n';
+                       }
+                       else if (plval != Py_None)
                        {
                                plstr = PyObject_Str(plval);
                                if (!plstr)
-                                       PLy_elog(ERROR, "function \"%s\" could not modify tuple", proc->proname);
+                                       PLy_elog(ERROR, "could not compute string representation of Python object in PL/Python function \"%s\" while modifying trigger row",
+                                                        proc->proname);
                                src = PyString_AsString(plstr);
 
-                               modvalues[i] = FunctionCall3(&proc->result.out.r.atts[atti].typfunc,
-                                                                                        CStringGetDatum(src),
-                                 ObjectIdGetDatum(proc->result.out.r.atts[atti].typioparam),
-                                                        Int32GetDatum(tupdesc->attrs[atti]->atttypmod));
+                               modvalues[i] =
+                                       InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
+                                                                         src,
+                                                                       proc->result.out.r.atts[atti].typioparam,
+                                                                         tupdesc->attrs[atti]->atttypmod);
                                modnulls[i] = ' ';
 
                                Py_DECREF(plstr);
@@ -500,7 +557,11 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
                        }
                        else
                        {
-                               modvalues[i] = PointerGetDatum(NULL);
+                               modvalues[i] =
+                                       InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
+                                                                         NULL,
+                                                                       proc->result.out.r.atts[atti].typioparam,
+                                                                         tupdesc->attrs[atti]->atttypmod);
                                modnulls[i] = 'n';
                        }
 
@@ -511,7 +572,7 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
                rtup = SPI_modifytuple(tdata->tg_relation, otup, natts,
                                                           modattrs, modvalues, modnulls);
                if (rtup == NULL)
-                       elog(ERROR, "SPI_modifytuple failed -- error %d", SPI_result);
+                       elog(ERROR, "SPI_modifytuple failed: error %d", SPI_result);
        }
        PG_CATCH();
        {
@@ -549,7 +610,9 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
                           *pltevent,
                           *pltwhen,
                           *pltlevel,
-                          *pltrelid;
+                          *pltrelid,
+                          *plttablename,
+                          *plttableschema;
        PyObject   *pltargs,
                           *pytnew,
                           *pytold;
@@ -560,7 +623,7 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
        {
                pltdata = PyDict_New();
                if (!pltdata)
-                       PLy_elog(ERROR, "could not build arguments for trigger procedure");
+                       PLy_elog(ERROR, "could not create new dictionary while building trigger arguments");
 
                pltname = PyString_FromString(tdata->tg_trigger->tgname);
                PyDict_SetItemString(pltdata, "name", pltname);
@@ -573,6 +636,19 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
                Py_DECREF(pltrelid);
                pfree(stroid);
 
+               stroid = SPI_getrelname(tdata->tg_relation);
+               plttablename = PyString_FromString(stroid);
+               PyDict_SetItemString(pltdata, "table_name", plttablename);
+               Py_DECREF(plttablename);
+               pfree(stroid);
+
+               stroid = SPI_getnspname(tdata->tg_relation);
+               plttableschema = PyString_FromString(stroid);
+               PyDict_SetItemString(pltdata, "table_schema", plttableschema);
+               Py_DECREF(plttableschema);
+               pfree(stroid);
+
+
                if (TRIGGER_FIRED_BEFORE(tdata->tg_event))
                        pltwhen = PyString_FromString("BEFORE");
                else if (TRIGGER_FIRED_AFTER(tdata->tg_event))
@@ -652,6 +728,8 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
                                pltevent = PyString_FromString("DELETE");
                        else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
                                pltevent = PyString_FromString("UPDATE");
+                       else if (TRIGGER_FIRED_BY_TRUNCATE(tdata->tg_event))
+                               pltevent = PyString_FromString("TRUNCATE");
                        else
                        {
                                elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event);
@@ -715,11 +793,21 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 
        PG_TRY();
        {
-               plargs = PLy_function_build_args(fcinfo, proc);
-               plrv = PLy_procedure_call(proc, "args", plargs);
+               if (!proc->is_setof || proc->setof == NULL)
+               {
+                       /* Simple type returning function or first time for SETOF function */
+                       plargs = PLy_function_build_args(fcinfo, proc);
+                       plrv = PLy_procedure_call(proc, "args", plargs);
+                       if (!proc->is_setof)
 
-               Assert(plrv != NULL);
-               Assert(!PLy_error_in_progress);
+                               /*
+                                * SETOF function parameters will be deleted when last row is
+                                * returned
+                                */
+                               PLy_function_delete_args(proc);
+                       Assert(plrv != NULL);
+                       Assert(!PLy_error_in_progress);
+               }
 
                /*
                 * Disconnect from SPI manager and then create the return values datum
@@ -730,20 +818,80 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
                if (SPI_finish() != SPI_OK_FINISH)
                        elog(ERROR, "SPI_finish failed");
 
+               if (proc->is_setof)
+               {
+                       bool            has_error = false;
+                       ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+
+                       if (proc->setof == NULL)
+                       {
+                               /* first time -- do checks and setup */
+                               if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+                                       (rsi->allowedModes & SFRM_ValuePerCall) == 0)
+                               {
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                        errmsg("unsupported set function return mode"),
+                                                        errdetail("PL/Python set-returning functions only support returning only value per call.")));
+                               }
+                               rsi->returnMode = SFRM_ValuePerCall;
+
+                               /* Make iterator out of returned object */
+                               proc->setof = PyObject_GetIter(plrv);
+                               Py_DECREF(plrv);
+                               plrv = NULL;
+
+                               if (proc->setof == NULL)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                        errmsg("returned object cannot be iterated"),
+                                       errdetail("PL/Python set-returning functions must return an iterable object.")));
+                       }
+
+                       /* Fetch next from iterator */
+                       plrv = PyIter_Next(proc->setof);
+                       if (plrv)
+                               rsi->isDone = ExprMultipleResult;
+                       else
+                       {
+                               rsi->isDone = ExprEndResult;
+                               has_error = PyErr_Occurred() != NULL;
+                       }
+
+                       if (rsi->isDone == ExprEndResult)
+                       {
+                               /* Iterator is exhausted or error happened */
+                               Py_DECREF(proc->setof);
+                               proc->setof = NULL;
+
+                               Py_XDECREF(plargs);
+                               Py_XDECREF(plrv);
+                               Py_XDECREF(plrv_so);
+
+                               PLy_function_delete_args(proc);
+
+                               if (has_error)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DATA_EXCEPTION),
+                                                 errmsg("error fetching next item from iterator")));
+
+                               fcinfo->isnull = true;
+                               return (Datum) NULL;
+                       }
+               }
+
                /*
-                * If the function is declared to return void, the Python
-                * return value must be None. For void-returning functions, we
-                * also treat a None return value as a special "void datum"
-                * rather than NULL (as is the case for non-void-returning
-                * functions).
+                * If the function is declared to return void, the Python return value
+                * must be None. For void-returning functions, we also treat a None
+                * return value as a special "void datum" rather than NULL (as is the
+                * case for non-void-returning functions).
                 */
                if (proc->result.out.d.typoid == VOIDOID)
                {
                        if (plrv != Py_None)
                                ereport(ERROR,
                                                (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                errmsg("invalid return value from plpython function"),
-                                                errdetail("Functions returning type \"void\" must return None.")));
+                                          errmsg("PL/Python function with return type \"void\" did not return None")));
 
                        fcinfo->isnull = false;
                        rv = (Datum) 0;
@@ -751,19 +899,51 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
                else if (plrv == Py_None)
                {
                        fcinfo->isnull = true;
-                       rv = PointerGetDatum(NULL);
+                       if (proc->result.is_rowtype < 1)
+                               rv = InputFunctionCall(&proc->result.out.d.typfunc,
+                                                                          NULL,
+                                                                          proc->result.out.d.typioparam,
+                                                                          -1);
+                       else
+                               /* Tuple as None */
+                               rv = (Datum) NULL;
+               }
+               else if (proc->result.is_rowtype >= 1)
+               {
+                       HeapTuple       tuple = NULL;
+
+                       if (PySequence_Check(plrv))
+                               /* composite type as sequence (tuple, list etc) */
+                               tuple = PLySequence_ToTuple(&proc->result, plrv);
+                       else if (PyMapping_Check(plrv))
+                               /* composite type as mapping (currently only dict) */
+                               tuple = PLyMapping_ToTuple(&proc->result, plrv);
+                       else
+                               /* returned as smth, must provide method __getattr__(name) */
+                               tuple = PLyObject_ToTuple(&proc->result, plrv);
+
+                       if (tuple != NULL)
+                       {
+                               fcinfo->isnull = false;
+                               rv = HeapTupleGetDatum(tuple);
+                       }
+                       else
+                       {
+                               fcinfo->isnull = true;
+                               rv = (Datum) NULL;
+                       }
                }
                else
                {
                        fcinfo->isnull = false;
                        plrv_so = PyObject_Str(plrv);
                        if (!plrv_so)
-                               PLy_elog(ERROR, "function \"%s\" could not create return value", proc->proname);
+                               PLy_elog(ERROR, "could not create string representation of Python object in PL/Python function \"%s\" while creating return value", proc->proname);
                        plrv_sc = PyString_AsString(plrv_so);
-                       rv = FunctionCall3(&proc->result.out.d.typfunc,
-                                                          PointerGetDatum(plrv_sc),
-                                                          ObjectIdGetDatum(proc->result.out.d.typioparam),
-                                                          Int32GetDatum(-1));
+                       rv = InputFunctionCall(&proc->result.out.d.typfunc,
+                                                                  plrv_sc,
+                                                                  proc->result.out.d.typioparam,
+                                                                  -1);
                }
        }
        PG_CATCH();
@@ -807,7 +987,7 @@ PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs)
        if (rv == NULL || PyErr_Occurred())
        {
                Py_XDECREF(rv);
-               PLy_elog(ERROR, "function \"%s\" failed", proc->proname);
+               PLy_elog(ERROR, "PL/Python function \"%s\" failed", proc->proname);
        }
 
        return rv;
@@ -852,6 +1032,7 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
                                        tmptup.t_data = td;
 
                                        arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc);
+                                       ReleaseTupleDesc(tupdesc);
                                }
                        }
                        else
@@ -861,13 +1042,9 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
                                else
                                {
                                        char       *ct;
-                                       Datum           dt;
 
-                                       dt = FunctionCall3(&(proc->args[i].in.d.typfunc),
-                                                                          fcinfo->arg[i],
-                                                        ObjectIdGetDatum(proc->args[i].in.d.typioparam),
-                                                                          Int32GetDatum(-1));
-                                       ct = DatumGetCString(dt);
+                                       ct = OutputFunctionCall(&(proc->args[i].in.d.typfunc),
+                                                                                       fcinfo->arg[i]);
                                        arg = (proc->args[i].in.d.func) (ct);
                                        pfree(ct);
                                }
@@ -879,10 +1056,12 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
                                arg = Py_None;
                        }
 
-                       /*
-                        * FIXME -- error check this
-                        */
-                       PyList_SetItem(args, i, arg);
+                       if (PyList_SetItem(args, i, arg) == -1)
+                               PLy_elog(ERROR, "PyList_SetItem() failed for PL/Python function \"%s\" while setting up arguments", proc->proname);
+
+                       if (proc->argnames && proc->argnames[i] &&
+                               PyDict_SetItemString(proc->globals, proc->argnames[i], arg) == -1)
+                               PLy_elog(ERROR, "PyDict_SetItemString() failed for PL/Python function \"%s\" while setting up arguments", proc->proname);
                        arg = NULL;
                }
        }
@@ -899,6 +1078,20 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
 }
 
 
+static void
+PLy_function_delete_args(PLyProcedure * proc)
+{
+       int                     i;
+
+       if (!proc->argnames)
+               return;
+
+       for (i = 0; i < proc->nargs; i++)
+               if (proc->argnames[i])
+                       PyDict_DelItemString(proc->globals, proc->argnames[i]);
+}
+
+
 /*
  * PLyProcedure functions
  */
@@ -942,7 +1135,7 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
                        elog(FATAL, "proc->me != plproc");
                /* did we find an up-to-date cache entry? */
                if (proc->fn_xmin != HeapTupleHeaderGetXmin(procTup->t_data) ||
-                       proc->fn_cmin != HeapTupleHeaderGetCmin(procTup->t_data))
+                       !ItemPointerEquals(&proc->fn_tid, &procTup->t_self))
                {
                        Py_DECREF(plproc);
                        proc = NULL;
@@ -950,7 +1143,24 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
        }
 
        if (proc == NULL)
-               proc = PLy_procedure_create(fcinfo, tgreloid, procTup, key);
+               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);
 
@@ -958,8 +1168,7 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
 }
 
 static PLyProcedure *
-PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
-                                        HeapTuple procTup, char *key)
+PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key)
 {
        char            procName[NAMEDATALEN + 256];
        Form_pg_proc procStruct;
@@ -976,13 +1185,13 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
                rv = snprintf(procName, sizeof(procName),
                                          "__plpython_procedure_%s_%u_trigger_%u",
                                          NameStr(procStruct->proname),
-                                         fcinfo->flinfo->fn_oid,
+                                         HeapTupleGetOid(procTup),
                                          tgreloid);
        else
                rv = snprintf(procName, sizeof(procName),
                                          "__plpython_procedure_%s_%u",
                                          NameStr(procStruct->proname),
-                                         fcinfo->flinfo->fn_oid);
+                                         HeapTupleGetOid(procTup));
        if (rv >= sizeof(procName) || rv < 0)
                elog(ERROR, "procedure name would overrun buffer");
 
@@ -990,7 +1199,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
        proc->proname = PLy_strdup(NameStr(procStruct->proname));
        proc->pyname = PLy_strdup(procName);
        proc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
-       proc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
+       proc->fn_tid = procTup->t_self;
        /* Remember if function is STABLE/IMMUTABLE */
        proc->fn_readonly =
                (procStruct->provolatile != PROVOLATILE_VOLATILE);
@@ -1000,6 +1209,9 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
        proc->nargs = 0;
        proc->code = proc->statics = NULL;
        proc->globals = proc->me = NULL;
+       proc->is_setof = procStruct->proretset;
+       proc->setof = NULL;
+       proc->argnames = NULL;
 
        PG_TRY();
        {
@@ -1007,7 +1219,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
                 * get information required for output conversion of the return value,
                 * but only if this isn't a trigger.
                 */
-               if (!CALLED_AS_TRIGGER(fcinfo))
+               if (!OidIsValid(tgreloid))
                {
                        HeapTuple       rvTypeTup;
                        Form_pg_type rvTypeStruct;
@@ -1021,76 +1233,117 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
                        rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
 
                        /* Disallow pseudotype result, except for void */
-                       if (rvTypeStruct->typtype == 'p' &&
+                       if (rvTypeStruct->typtype == TYPTYPE_PSEUDO &&
                                procStruct->prorettype != VOIDOID)
                        {
                                if (procStruct->prorettype == TRIGGEROID)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                        errmsg("trigger functions may only be called as triggers")));
+                                                        errmsg("trigger functions can only be called as triggers")));
                                else
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                  errmsg("plpython functions cannot return type %s",
+                                                  errmsg("PL/Python functions cannot return type %s",
                                                                  format_type_be(procStruct->prorettype))));
                        }
 
-                       if (rvTypeStruct->typtype == 'c')
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("plpython functions cannot return tuples yet")));
+                       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);
                }
-               else
-               {
-                       /*
-                        * input/output conversion for trigger tuples.  use the result
-                        * TypeInfo variable to store the tuple conversion info.
-                        */
-                       TriggerData *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);
-               }
 
                /*
-                * now get information required for input conversion of the procedure's
-                * arguments.
+                * Now get information required for input conversion of the
+                * procedure's arguments.  Note that we ignore output arguments
+                * here --- since we don't support returning record, and that was
+                * already checked above, there's no need to worry about multiple
+                * output arguments.
                 */
-               proc->nargs = fcinfo->nargs;
-               for (i = 0; i < fcinfo->nargs; i++)
+               if (procStruct->pronargs)
                {
-                       HeapTuple       argTypeTup;
-                       Form_pg_type argTypeStruct;
+                       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)++;
+                               }
+                       }
 
-                       argTypeTup = SearchSysCache(TYPEOID,
-                                                ObjectIdGetDatum(procStruct->proargtypes.values[i]),
-                                                                               0, 0, 0);
-                       if (!HeapTupleIsValid(argTypeTup))
-                               elog(ERROR, "cache lookup failed for type %u",
-                                        procStruct->proargtypes.values[i]);
-                       argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
+                       proc->argnames = (char **) PLy_malloc0(sizeof(char *) * proc->nargs);
+                       for (i = pos = 0; i < total; i++)
+                       {
+                               HeapTuple       argTypeTup;
+                               Form_pg_type argTypeStruct;
 
-                       /* Disallow pseudotype argument */
-                       if (argTypeStruct->typtype == 'p')
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                errmsg("plpython functions cannot take type %s",
-                                               format_type_be(procStruct->proargtypes.values[i]))));
-
-                       if (argTypeStruct->typtype != 'c')
-                               PLy_input_datum_func(&(proc->args[i]),
-                                                                        procStruct->proargtypes.values[i],
-                                                                        argTypeTup);
-                       else
-                               proc->args[i].is_rowtype = 2;   /* still need to set I/O funcs */
+                               if (modes &&
+                                       (modes[i] == PROARGMODE_OUT ||
+                                        modes[i] == PROARGMODE_TABLE))
+                                       continue;       /* skip OUT arguments */
 
-                       ReleaseSysCache(argTypeTup);
-               }
+                               Assert(types[i] == procStruct->proargtypes.values[pos]);
+
+                               argTypeTup = SearchSysCache(TYPEOID,
+                                                                                       ObjectIdGetDatum(types[i]),
+                                                                                       0, 0, 0);
+                               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.
@@ -1099,8 +1352,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
                                                                          Anum_pg_proc_prosrc, &isnull);
                if (isnull)
                        elog(ERROR, "null prosrc");
-               procSource = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                                prosrcdatum));
+               procSource = TextDatumGetCString(prosrcdatum);
 
                PLy_procedure_compile(proc, procSource);
 
@@ -1131,8 +1383,8 @@ PLy_procedure_compile(PLyProcedure * proc, const char *src)
        proc->globals = PyDict_Copy(PLy_interp_globals);
 
        /*
-        * SD is private preserved data between calls. GD is global data
-        * shared by all functions
+        * SD is private preserved data between calls. GD is global data shared by
+        * all functions
         */
        proc->statics = PyDict_New();
        PyDict_SetItemString(proc->globals, "SD", proc->statics);
@@ -1164,7 +1416,7 @@ PLy_procedure_compile(PLyProcedure * proc, const char *src)
        else
                Py_XDECREF(crv);
 
-       PLy_elog(ERROR, "could not compile function \"%s\"", proc->proname);
+       PLy_elog(ERROR, "could not compile PL/Python function \"%s\"", proc->proname);
 }
 
 static char *
@@ -1226,6 +1478,7 @@ PLy_procedure_delete(PLyProcedure * proc)
        if (proc->pyname)
                PLy_free(proc->pyname);
        for (i = 0; i < proc->nargs; i++)
+       {
                if (proc->args[i].is_rowtype == 1)
                {
                        if (proc->args[i].in.r.atts)
@@ -1233,10 +1486,16 @@ PLy_procedure_delete(PLyProcedure * proc)
                        if (proc->args[i].out.r.atts)
                                PLy_free(proc->args[i].out.r.atts);
                }
+               if (proc->argnames && proc->argnames[i])
+                       PLy_free(proc->argnames[i]);
+       }
+       if (proc->argnames)
+               PLy_free(proc->argnames);
 }
 
-/* conversion functions.  remember output from python is
- * input to postgresql, and vis versa.
+/*
+ * Conversion functions.  Remember output from Python is input to
+ * PostgreSQL, and vice versa.
  */
 static void
 PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
@@ -1245,10 +1504,15 @@ PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 
        if (arg->is_rowtype == 0)
                elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
-
        arg->is_rowtype = 1;
-       arg->in.r.natts = desc->natts;
-       arg->in.r.atts = PLy_malloc(desc->natts * sizeof(PLyDatumToOb));
+
+       if (arg->in.r.natts != desc->natts)
+       {
+               if (arg->in.r.atts)
+                       PLy_free(arg->in.r.atts);
+               arg->in.r.natts = desc->natts;
+               arg->in.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb));
+       }
 
        for (i = 0; i < desc->natts; i++)
        {
@@ -1257,6 +1521,9 @@ PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
                if (desc->attrs[i]->attisdropped)
                        continue;
 
+               if (arg->in.r.atts[i].typoid == desc->attrs[i]->atttypid)
+                       continue;                       /* already set up this entry */
+
                typeTup = SearchSysCache(TYPEOID,
                                                                 ObjectIdGetDatum(desc->attrs[i]->atttypid),
                                                                 0, 0, 0);
@@ -1279,10 +1546,15 @@ PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 
        if (arg->is_rowtype == 0)
                elog(ERROR, "PLyTypeInfo struct is initialized for a Datum");
-
        arg->is_rowtype = 1;
-       arg->out.r.natts = desc->natts;
-       arg->out.r.atts = PLy_malloc(desc->natts * sizeof(PLyDatumToOb));
+
+       if (arg->out.r.natts != desc->natts)
+       {
+               if (arg->out.r.atts)
+                       PLy_free(arg->out.r.atts);
+               arg->out.r.natts = desc->natts;
+               arg->out.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb));
+       }
 
        for (i = 0; i < desc->natts; i++)
        {
@@ -1291,6 +1563,9 @@ PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
                if (desc->attrs[i]->attisdropped)
                        continue;
 
+               if (arg->out.r.atts[i].typoid == desc->attrs[i]->atttypid)
+                       continue;                       /* already set up this entry */
+
                typeTup = SearchSysCache(TYPEOID,
                                                                 ObjectIdGetDatum(desc->attrs[i]->atttypid),
                                                                 0, 0, 0);
@@ -1340,6 +1615,7 @@ PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, HeapTuple typeTup)
 
        /* Get the type's conversion information */
        perm_fmgr_info(typeStruct->typoutput, &arg->typfunc);
+       arg->typoid = HeapTupleGetOid(typeTup);
        arg->typioparam = getTypeIOParam(typeTup);
        arg->typbyval = typeStruct->typbyval;
 
@@ -1392,9 +1668,15 @@ PLy_typeinfo_dealloc(PLyTypeInfo * arg)
 static PyObject *
 PLyBool_FromString(const char *src)
 {
+       /*
+        * We would like to use Py_RETURN_TRUE and Py_RETURN_FALSE here for
+        * generating SQL from trigger functions, but those are only supported in
+        * Python >= 2.3, and we support older versions.
+        * http://docs.python.org/api/boolObjects.html
+        */
        if (src[0] == 't')
-               return PyInt_FromLong(1);
-       return PyInt_FromLong(0);
+               return PyBool_FromLong(1);
+       return PyBool_FromLong(0);
 }
 
 static PyObject *
@@ -1446,7 +1728,7 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
 
        dict = PyDict_New();
        if (dict == NULL)
-               PLy_elog(ERROR, "could not create tuple dictionary");
+               PLy_elog(ERROR, "could not create new dictionary");
 
        PG_TRY();
        {
@@ -1454,8 +1736,7 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
                {
                        char       *key,
                                           *vsrc;
-                       Datum           vattr,
-                                               vdat;
+                       Datum           vattr;
                        bool            is_null;
                        PyObject   *value;
 
@@ -1469,11 +1750,8 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
                                PyDict_SetItemString(dict, key, Py_None);
                        else
                        {
-                               vdat = FunctionCall3(&info->in.r.atts[i].typfunc,
-                                                                        vattr,
-                                                        ObjectIdGetDatum(info->in.r.atts[i].typioparam),
-                                                                  Int32GetDatum(desc->attrs[i]->atttypmod));
-                               vsrc = DatumGetCString(vdat);
+                               vsrc = OutputFunctionCall(&info->in.r.atts[i].typfunc,
+                                                                                 vattr);
 
                                /*
                                 * no exceptions allowed
@@ -1495,6 +1773,247 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
        return dict;
 }
 
+
+static HeapTuple
+PLyMapping_ToTuple(PLyTypeInfo * info, PyObject * mapping)
+{
+       TupleDesc       desc;
+       HeapTuple       tuple;
+       Datum      *values;
+       bool       *nulls;
+       volatile int i;
+
+       Assert(PyMapping_Check(mapping));
+
+       desc = lookup_rowtype_tupdesc(info->out.d.typoid, -1);
+       if (info->is_rowtype == 2)
+               PLy_output_tuple_funcs(info, desc);
+       Assert(info->is_rowtype == 1);
+
+       /* Build tuple */
+       values = palloc(sizeof(Datum) * desc->natts);
+       nulls = palloc(sizeof(bool) * desc->natts);
+       for (i = 0; i < desc->natts; ++i)
+       {
+               char       *key;
+               PyObject   *volatile value,
+                                  *volatile so;
+
+               key = NameStr(desc->attrs[i]->attname);
+               value = so = NULL;
+               PG_TRY();
+               {
+                       value = PyMapping_GetItemString(mapping, key);
+                       if (value == Py_None)
+                       {
+                               values[i] = (Datum) NULL;
+                               nulls[i] = true;
+                       }
+                       else if (value)
+                       {
+                               char       *valuestr;
+
+                               so = PyObject_Str(value);
+                               if (so == NULL)
+                                       PLy_elog(ERROR, "could not compute string representation of Python object");
+                               valuestr = PyString_AsString(so);
+
+                               values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
+                                                                                         ,valuestr
+                                                                                         ,info->out.r.atts[i].typioparam
+                                                                                         ,-1);
+                               Py_DECREF(so);
+                               so = NULL;
+                               nulls[i] = false;
+                       }
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                                errmsg("key \"%s\" not found in mapping", key),
+                                                errhint("To return null in a column, "
+                                         "add the value None to the mapping with the key named after the column.")));
+
+                       Py_XDECREF(value);
+                       value = NULL;
+               }
+               PG_CATCH();
+               {
+                       Py_XDECREF(so);
+                       Py_XDECREF(value);
+                       PG_RE_THROW();
+               }
+               PG_END_TRY();
+       }
+
+       tuple = heap_form_tuple(desc, values, nulls);
+       ReleaseTupleDesc(desc);
+       pfree(values);
+       pfree(nulls);
+
+       return tuple;
+}
+
+
+static HeapTuple
+PLySequence_ToTuple(PLyTypeInfo * info, PyObject * sequence)
+{
+       TupleDesc       desc;
+       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")));
+
+       if (info->is_rowtype == 2)
+               PLy_output_tuple_funcs(info, desc);
+       Assert(info->is_rowtype == 1);
+
+       /* Build tuple */
+       values = palloc(sizeof(Datum) * desc->natts);
+       nulls = palloc(sizeof(bool) * desc->natts);
+       for (i = 0; i < desc->natts; ++i)
+       {
+               PyObject   *volatile value,
+                                  *volatile so;
+
+               value = so = NULL;
+               PG_TRY();
+               {
+                       value = PySequence_GetItem(sequence, i);
+                       Assert(value);
+                       if (value == Py_None)
+                       {
+                               values[i] = (Datum) NULL;
+                               nulls[i] = true;
+                       }
+                       else if (value)
+                       {
+                               char       *valuestr;
+
+                               so = PyObject_Str(value);
+                               if (so == NULL)
+                                       PLy_elog(ERROR, "could not compute string representation of Python object");
+                               valuestr = PyString_AsString(so);
+                               values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
+                                                                                         ,valuestr
+                                                                                         ,info->out.r.atts[i].typioparam
+                                                                                         ,-1);
+                               Py_DECREF(so);
+                               so = NULL;
+                               nulls[i] = false;
+                       }
+
+                       Py_XDECREF(value);
+                       value = NULL;
+               }
+               PG_CATCH();
+               {
+                       Py_XDECREF(so);
+                       Py_XDECREF(value);
+                       PG_RE_THROW();
+               }
+               PG_END_TRY();
+       }
+
+       tuple = heap_form_tuple(desc, values, nulls);
+       ReleaseTupleDesc(desc);
+       pfree(values);
+       pfree(nulls);
+
+       return tuple;
+}
+
+
+static HeapTuple
+PLyObject_ToTuple(PLyTypeInfo * info, PyObject * object)
+{
+       TupleDesc       desc;
+       HeapTuple       tuple;
+       Datum      *values;
+       bool       *nulls;
+       volatile int i;
+
+       desc = lookup_rowtype_tupdesc(info->out.d.typoid, -1);
+       if (info->is_rowtype == 2)
+               PLy_output_tuple_funcs(info, desc);
+       Assert(info->is_rowtype == 1);
+
+       /* Build tuple */
+       values = palloc(sizeof(Datum) * desc->natts);
+       nulls = palloc(sizeof(bool) * desc->natts);
+       for (i = 0; i < desc->natts; ++i)
+       {
+               char       *key;
+               PyObject   *volatile value,
+                                  *volatile so;
+
+               key = NameStr(desc->attrs[i]->attname);
+               value = so = NULL;
+               PG_TRY();
+               {
+                       value = PyObject_GetAttrString(object, key);
+                       if (value == Py_None)
+                       {
+                               values[i] = (Datum) NULL;
+                               nulls[i] = true;
+                       }
+                       else if (value)
+                       {
+                               char       *valuestr;
+
+                               so = PyObject_Str(value);
+                               if (so == NULL)
+                                       PLy_elog(ERROR, "could not compute string representation of Python object");
+                               valuestr = PyString_AsString(so);
+                               values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
+                                                                                         ,valuestr
+                                                                                         ,info->out.r.atts[i].typioparam
+                                                                                         ,-1);
+                               Py_DECREF(so);
+                               so = NULL;
+                               nulls[i] = false;
+                       }
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                                errmsg("attribute \"%s\" does not exist in Python object", key),
+                                                errhint("To return null in a column, "
+                                                                "let the returned object have an attribute named "
+                                                                "after column with value None.")));
+
+                       Py_XDECREF(value);
+                       value = NULL;
+               }
+               PG_CATCH();
+               {
+                       Py_XDECREF(so);
+                       Py_XDECREF(value);
+                       PG_RE_THROW();
+               }
+               PG_END_TRY();
+       }
+
+       tuple = heap_form_tuple(desc, values, nulls);
+       ReleaseTupleDesc(desc);
+       pfree(values);
+       pfree(nulls);
+
+       return tuple;
+}
+
+
 /* initialization, some python variables function declared here */
 
 /* interface to postgresql elog */
@@ -1518,11 +2037,11 @@ static void PLy_result_dealloc(PyObject *);
 static PyObject *PLy_result_getattr(PyObject *, char *);
 static PyObject *PLy_result_nrows(PyObject *, PyObject *);
 static PyObject *PLy_result_status(PyObject *, PyObject *);
-static int     PLy_result_length(PyObject *);
-static PyObject *PLy_result_item(PyObject *, int);
-static PyObject *PLy_result_slice(PyObject *, int, int);
-static int     PLy_result_ass_item(PyObject *, int, PyObject *);
-static int     PLy_result_ass_slice(PyObject *, int, int, PyObject *);
+static Py_ssize_t PLy_result_length(PyObject *);
+static PyObject *PLy_result_item(PyObject *, Py_ssize_t);
+static PyObject *PLy_result_slice(PyObject *, Py_ssize_t, Py_ssize_t);
+static int     PLy_result_ass_item(PyObject *, Py_ssize_t, PyObject *);
+static int     PLy_result_ass_slice(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
 
 
 static PyObject *PLy_spi_prepare(PyObject *, PyObject *);
@@ -1542,9 +2061,9 @@ static PyTypeObject PLy_PlanType = {
        /*
         * methods
         */
-       (destructor) PLy_plan_dealloc,          /* tp_dealloc */
+       PLy_plan_dealloc,                       /* tp_dealloc */
        0,                                                      /* tp_print */
-       (getattrfunc) PLy_plan_getattr,         /* tp_getattr */
+       PLy_plan_getattr,                       /* tp_getattr */
        0,                                                      /* tp_setattr */
        0,                                                      /* tp_compare */
        0,                                                      /* tp_repr */
@@ -1557,7 +2076,7 @@ static PyTypeObject PLy_PlanType = {
        0,                                                      /* tp_getattro */
        0,                                                      /* tp_setattro */
        0,                                                      /* tp_as_buffer */
-       0,                                                      /* tp_xxx4 */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,       /* tp_flags */
        PLy_plan_doc,                           /* tp_doc */
 };
 
@@ -1566,15 +2085,14 @@ static PyMethodDef PLy_plan_methods[] = {
        {NULL, NULL, 0, NULL}
 };
 
-
 static PySequenceMethods PLy_result_as_sequence = {
-       (inquiry) PLy_result_length,    /* sq_length */
-       (binaryfunc) 0,                         /* sq_concat */
-       (intargfunc) 0,                         /* sq_repeat */
-       (intargfunc) PLy_result_item,           /* sq_item */
-       (intintargfunc) PLy_result_slice,       /* sq_slice */
-       (intobjargproc) PLy_result_ass_item,            /* sq_ass_item */
-       (intintobjargproc) PLy_result_ass_slice,        /* sq_ass_slice */
+       PLy_result_length,                      /* sq_length */
+       NULL,                                           /* sq_concat */
+       NULL,                                           /* sq_repeat */
+       PLy_result_item,                        /* sq_item */
+       PLy_result_slice,                       /* sq_slice */
+       PLy_result_ass_item,            /* sq_ass_item */
+       PLy_result_ass_slice,           /* sq_ass_slice */
 };
 
 static PyTypeObject PLy_ResultType = {
@@ -1587,9 +2105,9 @@ static PyTypeObject PLy_ResultType = {
        /*
         * methods
         */
-       (destructor) PLy_result_dealloc,        /* tp_dealloc */
+       PLy_result_dealloc,                     /* tp_dealloc */
        0,                                                      /* tp_print */
-       (getattrfunc) PLy_result_getattr,       /* tp_getattr */
+       PLy_result_getattr,                     /* tp_getattr */
        0,                                                      /* tp_setattr */
        0,                                                      /* tp_compare */
        0,                                                      /* tp_repr */
@@ -1602,7 +2120,7 @@ static PyTypeObject PLy_ResultType = {
        0,                                                      /* tp_getattro */
        0,                                                      /* tp_setattro */
        0,                                                      /* tp_as_buffer */
-       0,                                                      /* tp_xxx4 */
+       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,       /* tp_flags */
        PLy_result_doc,                         /* tp_doc */
 };
 
@@ -1674,7 +2192,7 @@ PLy_plan_dealloc(PyObject * arg)
                PLy_free(ob->args);
        }
 
-       PyMem_DEL(arg);
+       arg->ob_type->tp_free(arg);
 }
 
 
@@ -1693,7 +2211,7 @@ PLy_plan_status(PyObject * self, PyObject * args)
                return Py_True;
                /* return PyInt_FromLong(self->status); */
        }
-       PyErr_SetString(PLy_exc_error, "plan.status() takes no arguments");
+       PLy_exception_set(PLy_exc_error, "plan.status takes no arguments");
        return NULL;
 }
 
@@ -1728,7 +2246,7 @@ PLy_result_dealloc(PyObject * arg)
        Py_XDECREF(ob->rows);
        Py_XDECREF(ob->status);
 
-       PyMem_DEL(ob);
+       arg->ob_type->tp_free(arg);
 }
 
 static PyObject *
@@ -1755,7 +2273,7 @@ PLy_result_status(PyObject * self, PyObject * args)
        return ob->status;
 }
 
-static int
+static Py_ssize_t
 PLy_result_length(PyObject * arg)
 {
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1764,7 +2282,7 @@ PLy_result_length(PyObject * arg)
 }
 
 static PyObject *
-PLy_result_item(PyObject * arg, int idx)
+PLy_result_item(PyObject * arg, Py_ssize_t idx)
 {
        PyObject   *rv;
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1776,7 +2294,7 @@ PLy_result_item(PyObject * arg, int idx)
 }
 
 static int
-PLy_result_ass_item(PyObject * arg, int idx, PyObject * item)
+PLy_result_ass_item(PyObject * arg, Py_ssize_t idx, PyObject * item)
 {
        int                     rv;
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1787,7 +2305,7 @@ PLy_result_ass_item(PyObject * arg, int idx, PyObject * item)
 }
 
 static PyObject *
-PLy_result_slice(PyObject * arg, int lidx, int hidx)
+PLy_result_slice(PyObject * arg, Py_ssize_t lidx, Py_ssize_t hidx)
 {
        PyObject   *rv;
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1800,7 +2318,7 @@ PLy_result_slice(PyObject * arg, int lidx, int hidx)
 }
 
 static int
-PLy_result_ass_slice(PyObject * arg, int lidx, int hidx, PyObject * slice)
+PLy_result_ass_slice(PyObject * arg, Py_ssize_t lidx, Py_ssize_t hidx, PyObject * slice)
 {
        int                     rv;
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1823,21 +2341,21 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
        /* Can't execute more if we have an unhandled error */
        if (PLy_error_in_progress)
        {
-               PyErr_SetString(PLy_exc_error, "Transaction aborted.");
+               PLy_exception_set(PLy_exc_error, "transaction aborted");
                return NULL;
        }
 
        if (!PyArg_ParseTuple(args, "s|O", &query, &list))
        {
-               PyErr_SetString(PLy_exc_spi_error,
-                                               "Invalid arguments for plpy.prepare()");
+               PLy_exception_set(PLy_exc_spi_error,
+                                                 "invalid arguments for plpy.prepare");
                return NULL;
        }
 
        if (list && (!PySequence_Check(list)))
        {
-               PyErr_SetString(PLy_exc_spi_error,
-                                        "Second argument in plpy.prepare() must be a sequence");
+               PLy_exception_set(PLy_exc_spi_error,
+                                                 "second argument of plpy.prepare must be a sequence");
                return NULL;
        }
 
@@ -1874,32 +2392,42 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
                                for (i = 0; i < nargs; i++)
                                {
                                        char       *sptr;
-                                       List       *names;
                                        HeapTuple       typeTup;
+                                       Oid                     typeId;
+                                       int32           typmod;
                                        Form_pg_type typeStruct;
 
                                        optr = PySequence_GetItem(list, i);
                                        if (!PyString_Check(optr))
-                                               elog(ERROR, "Type names must be strings.");
+                                               ereport(ERROR,
+                                                               (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
                                        sptr = PyString_AsString(optr);
 
-                                       /*
-                                        * Parse possibly-qualified type name and look it up in
-                                        * pg_type
-                                        */
-                                       names = stringToQualifiedNameList(sptr,
-                                                                                                         "PLy_spi_prepare");
-                                       typeTup = typenameType(NULL,
-                                                                                  makeTypeNameFromNameList(names));
+                                       /********************************************************
+                                        * 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 = SearchSysCache(TYPEOID,
+                                                                                        ObjectIdGetDatum(typeId),
+                                                                                        0, 0, 0);
+                                       if (!HeapTupleIsValid(typeTup))
+                                               elog(ERROR, "cache lookup failed for type %u", typeId);
+
                                        Py_DECREF(optr);
                                        optr = NULL;    /* this is important */
 
-                                       plan->types[i] = HeapTupleGetOid(typeTup);
+                                       plan->types[i] = typeId;
                                        typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
-                                       if (typeStruct->typtype != 'c')
+                                       if (typeStruct->typtype != TYPTYPE_COMPOSITE)
                                                PLy_output_datum_func(&plan->args[i], typeTup);
                                        else
-                                               elog(ERROR, "tuples not handled in plpy.prepare, yet.");
+                                               ereport(ERROR,
+                                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                                errmsg("plpy.prepare does not support composite types")));
                                        ReleaseSysCache(typeTup);
                                }
                        }
@@ -1926,10 +2454,10 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
                Py_DECREF(plan);
                Py_XDECREF(optr);
                if (!PyErr_Occurred())
-                       PyErr_SetString(PLy_exc_spi_error,
-                                                       "Unknown error in PLy_spi_prepare");
+                       PLy_exception_set(PLy_exc_spi_error,
+                                                         "unrecognized error in PLy_spi_prepare");
                /* XXX this oughta be replaced with errcontext mechanism */
-               PLy_elog(WARNING, "in function %s:",
+               PLy_elog(WARNING, "in PL/Python function \"%s\"",
                                 PLy_procedure_name(PLy_curr_procedure));
                return NULL;
        }
@@ -1952,7 +2480,7 @@ PLy_spi_execute(PyObject * self, PyObject * args)
        /* Can't execute more if we have an unhandled error */
        if (PLy_error_in_progress)
        {
-               PyErr_SetString(PLy_exc_error, "Transaction aborted.");
+               PLy_exception_set(PLy_exc_error, "transaction aborted");
                return NULL;
        }
 
@@ -1965,7 +2493,7 @@ PLy_spi_execute(PyObject * self, PyObject * args)
                is_PLyPlanObject(plan))
                return PLy_spi_execute_plan(plan, list, limit);
 
-       PyErr_SetString(PLy_exc_error, "Expected a query or plan.");
+       PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
        return NULL;
 }
 
@@ -1982,9 +2510,7 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
        {
                if (!PySequence_Check(list) || PyString_Check(list))
                {
-                       char       *msg = "plpy.execute() takes a sequence as its second argument";
-
-                       PyErr_SetString(PLy_exc_spi_error, msg);
+                       PLy_exception_set(PLy_exc_spi_error, "plpy.execute takes a sequence as its second argument");
                        return NULL;
                }
                nargs = PySequence_Length(list);
@@ -2000,12 +2526,14 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
                PyObject   *so = PyObject_Str(list);
 
                if (!so)
-                       PLy_elog(ERROR, "function \"%s\" could not execute plan",
+                       PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan",
                                         PLy_procedure_name(PLy_curr_procedure));
                sv = PyString_AsString(so);
-               PLy_exception_set(PLy_exc_spi_error,
-                                                 "Expected sequence of %d arguments, got %d. %s",
-                                                 plan->nargs, nargs, sv);
+               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);
 
                return NULL;
@@ -2015,30 +2543,31 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
        PG_TRY();
        {
                char       *nulls = palloc(nargs * sizeof(char));
+               volatile int j;
 
-               for (i = 0; i < nargs; i++)
+               for (j = 0; j < nargs; j++)
                {
                        PyObject   *elem,
                                           *so;
 
-                       elem = PySequence_GetItem(list, i);
+                       elem = PySequence_GetItem(list, j);
                        if (elem != Py_None)
                        {
                                so = PyObject_Str(elem);
                                if (!so)
-                                       PLy_elog(ERROR, "function \"%s\" could not execute plan",
+                                       PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan",
                                                         PLy_procedure_name(PLy_curr_procedure));
                                Py_DECREF(elem);
 
                                PG_TRY();
                                {
-                                       char *sv = PyString_AsString(so);
+                                       char       *sv = PyString_AsString(so);
 
-                                       plan->values[i] =
-                                               FunctionCall3(&(plan->args[i].out.d.typfunc),
-                                                                         CStringGetDatum(sv),
-                                                               ObjectIdGetDatum(plan->args[i].out.d.typioparam),
-                                                                         Int32GetDatum(-1));
+                                       plan->values[j] =
+                                               InputFunctionCall(&(plan->args[j].out.d.typfunc),
+                                                                                 sv,
+                                                                                 plan->args[j].out.d.typioparam,
+                                                                                 -1);
                                }
                                PG_CATCH();
                                {
@@ -2048,13 +2577,17 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
                                PG_END_TRY();
 
                                Py_DECREF(so);
-                               nulls[i] = ' ';
+                               nulls[j] = ' ';
                        }
                        else
                        {
                                Py_DECREF(elem);
-                               plan->values[i] = PointerGetDatum(NULL);
-                               nulls[i] = 'n';
+                               plan->values[j] =
+                                       InputFunctionCall(&(plan->args[j].out.d.typfunc),
+                                                                         NULL,
+                                                                         plan->args[j].out.d.typioparam,
+                                                                         -1);
+                               nulls[j] = 'n';
                        }
                }
 
@@ -2065,6 +2598,8 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
        }
        PG_CATCH();
        {
+               int                     k;
+
                MemoryContextSwitchTo(oldcontext);
                PLy_error_in_progress = CopyErrorData();
                FlushErrorState();
@@ -2072,21 +2607,21 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
                /*
                 * cleanup plan->values array
                 */
-               for (i = 0; i < nargs; i++)
+               for (k = 0; k < nargs; k++)
                {
-                       if (!plan->args[i].out.d.typbyval &&
-                               (plan->values[i] != PointerGetDatum(NULL)))
+                       if (!plan->args[k].out.d.typbyval &&
+                               (plan->values[k] != PointerGetDatum(NULL)))
                        {
-                               pfree(DatumGetPointer(plan->values[i]));
-                               plan->values[i] = PointerGetDatum(NULL);
+                               pfree(DatumGetPointer(plan->values[k]));
+                               plan->values[k] = PointerGetDatum(NULL);
                        }
                }
 
                if (!PyErr_Occurred())
-                       PyErr_SetString(PLy_exc_error,
-                                                       "Unknown error in PLy_spi_execute_plan");
+                       PLy_exception_set(PLy_exc_error,
+                                                         "unrecognized error in PLy_spi_execute_plan");
                /* XXX this oughta be replaced with errcontext mechanism */
-               PLy_elog(WARNING, "in function %s:",
+               PLy_elog(WARNING, "in PL/Python function \"%s\"",
                                 PLy_procedure_name(PLy_curr_procedure));
                return NULL;
        }
@@ -2130,10 +2665,10 @@ PLy_spi_execute_query(char *query, long limit)
                PLy_error_in_progress = CopyErrorData();
                FlushErrorState();
                if (!PyErr_Occurred())
-                       PyErr_SetString(PLy_exc_spi_error,
-                                                       "Unknown error in PLy_spi_execute_query");
+                       PLy_exception_set(PLy_exc_spi_error,
+                                                         "unrecognized error in PLy_spi_execute_query");
                /* XXX this oughta be replaced with errcontext mechanism */
-               PLy_elog(WARNING, "in function %s:",
+               PLy_elog(WARNING, "in PL/Python function \"%s\"",
                                 PLy_procedure_name(PLy_curr_procedure));
                return NULL;
        }
@@ -2160,24 +2695,19 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
        Py_DECREF(result->status);
        result->status = PyInt_FromLong(status);
 
-       if (status == SPI_OK_UTILITY)
-       {
-               Py_DECREF(result->nrows);
-               result->nrows = PyInt_FromLong(0);
-       }
-       else if (status != SPI_OK_SELECT)
+       if (status > 0 && tuptable == NULL)
        {
                Py_DECREF(result->nrows);
                result->nrows = PyInt_FromLong(rows);
        }
-       else
+       else if (status > 0 && tuptable != NULL)
        {
                PLyTypeInfo args;
                int                     i;
 
-               PLy_typeinfo_init(&args);
                Py_DECREF(result->nrows);
                result->nrows = PyInt_FromLong(rows);
+               PLy_typeinfo_init(&args);
 
                oldcontext = CurrentMemoryContext;
                PG_TRY();
@@ -2206,8 +2736,8 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
                        PLy_error_in_progress = CopyErrorData();
                        FlushErrorState();
                        if (!PyErr_Occurred())
-                               PyErr_SetString(PLy_exc_error,
-                                                       "Unknown error in PLy_spi_execute_fetch_result");
+                               PLy_exception_set(PLy_exc_error,
+                                                                 "unrecognized error in PLy_spi_execute_fetch_result");
                        Py_DECREF(result);
                        PLy_typeinfo_dealloc(&args);
                        return NULL;
@@ -2224,24 +2754,20 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
  */
 
 /*
- * plpython_init()                     - Initialize everything that can be
- *                                                       safely initialized during postmaster
- *                                                       startup.
+ * _PG_init()                  - library load-time initialization
  *
- * DO NOT make this static --- it has to be callable by preload
+ * DO NOT make this static nor change its name!
  */
 void
-plpython_init(void)
+_PG_init(void)
 {
-       static volatile bool init_active = false;
+       /* Be sure we do initialization only once (should be redundant now) */
+       static bool inited = false;
 
-       /* Do initialization only once */
-       if (!PLy_first_call)
+       if (inited)
                return;
 
-       if (init_active)
-               elog(FATAL, "initialization of language module failed");
-       init_active = true;
+       pg_bindtextdomain(TEXTDOMAIN);
 
        Py_Initialize();
        PLy_init_interp();
@@ -2252,20 +2778,7 @@ plpython_init(void)
        if (PLy_procedure_cache == NULL)
                PLy_elog(ERROR, "could not create procedure cache");
 
-       PLy_first_call = false;
-}
-
-static void
-PLy_init_all(void)
-{
-       /* Execute postmaster-startup safe initialization */
-       if (PLy_first_call)
-               plpython_init();
-
-       /*
-        * Any other initialization that must be done each time a new backend
-        * starts -- currently none
-        */
+       inited = true;
 }
 
 static void
@@ -2275,7 +2788,7 @@ PLy_init_interp(void)
 
        mainmod = PyImport_AddModule("__main__");
        if (mainmod == NULL || PyErr_Occurred())
-               PLy_elog(ERROR, "could not import \"__main__\" module.");
+               PLy_elog(ERROR, "could not import \"__main__\" module");
        Py_INCREF(mainmod);
        PLy_interp_globals = PyModule_GetDict(mainmod);
        PLy_interp_safe_globals = PyDict_New();
@@ -2297,7 +2810,11 @@ PLy_init_plpy(void)
        /*
         * initialize plpy module
         */
-       PLy_PlanType.ob_type = PLy_ResultType.ob_type = &PyType_Type;
+       if (PyType_Ready(&PLy_PlanType) < 0)
+               elog(ERROR, "could not initialize PLy_PlanType");
+       if (PyType_Ready(&PLy_ResultType) < 0)
+               elog(ERROR, "could not initialize PLy_ResultType");
+
        plpy = Py_InitModule("plpy", PLy_methods);
        plpy_dict = PyModule_GetDict(plpy);
 
@@ -2318,13 +2835,13 @@ PLy_init_plpy(void)
        plpy_mod = PyImport_AddModule("plpy");
        PyDict_SetItemString(main_dict, "plpy", plpy_mod);
        if (PyErr_Occurred())
-               elog(ERROR, "could not init plpy");
+               elog(ERROR, "could not initialize plpy");
 }
 
 /* the python interface to the elog function
  * don't confuse these with PLy_elog
  */
-static PyObject *PLy_output(int, PyObject *, PyObject *);
+static PyObject *PLy_output(volatile int, PyObject *, PyObject *);
 
 static PyObject *
 PLy_debug(PyObject * self, PyObject * args)
@@ -2380,7 +2897,7 @@ PLy_output(volatile int level, PyObject * self, PyObject * args)
        if (so == NULL || ((sv = PyString_AsString(so)) == NULL))
        {
                level = ERROR;
-               sv = "Unable to parse error message in `plpy.elog'";
+               sv = dgettext(TEXTDOMAIN, "could not parse error message in plpy.elog");
        }
 
        oldcontext = CurrentMemoryContext;
@@ -2430,8 +2947,8 @@ PLy_procedure_name(PLyProcedure * proc)
        return proc->proname;
 }
 
-/* output a python traceback/exception via the postgresql elog
- * function.  not pretty.
+/*
+ * Call PyErr_SetString with a vprint interface and translation support
  */
 static void
 PLy_exception_set(PyObject * exc, const char *fmt,...)
@@ -2440,7 +2957,27 @@ PLy_exception_set(PyObject * exc, const char *fmt,...)
        va_list         ap;
 
        va_start(ap, fmt);
-       vsnprintf(buf, sizeof(buf), fmt, ap);
+       vsnprintf(buf, sizeof(buf), dgettext(TEXTDOMAIN, fmt), ap);
+       va_end(ap);
+
+       PyErr_SetString(exc, buf);
+}
+
+/*
+ * The same, pluralized.
+ */
+static void
+PLy_exception_set_plural(PyObject *exc,
+                                                const char *fmt_singular, const char *fmt_plural,
+                                                unsigned long n,...)
+{
+       char            buf[1024];
+       va_list         ap;
+
+       va_start(ap, n);
+       vsnprintf(buf, sizeof(buf),
+                         dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
+                         ap);
        va_end(ap);
 
        PyErr_SetString(exc, buf);
@@ -2453,35 +2990,44 @@ PLy_exception_set(PyObject * exc, const char *fmt,...)
 static void
 PLy_elog(int elevel, const char *fmt,...)
 {
-       va_list         ap;
-       char       *xmsg,
-                          *emsg;
+       char       *xmsg;
        int                     xlevel;
+       StringInfoData emsg;
 
        xmsg = PLy_traceback(&xlevel);
 
-       va_start(ap, fmt);
-       emsg = PLy_vprintf(fmt, ap);
-       va_end(ap);
+       initStringInfo(&emsg);
+       for (;;)
+       {
+               va_list         ap;
+               bool            success;
+
+               va_start(ap, fmt);
+               success = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
+               va_end(ap);
+               if (success)
+                       break;
+               enlargeStringInfo(&emsg, emsg.maxlen);
+       }
 
        PG_TRY();
        {
                ereport(elevel,
-                               (errmsg("plpython: %s", emsg),
+                               (errmsg("PL/Python: %s", emsg.data),
                                 (xmsg) ? errdetail("%s", xmsg) : 0));
        }
        PG_CATCH();
        {
-               PLy_free(emsg);
+               pfree(emsg.data);
                if (xmsg)
-                       PLy_free(xmsg);
+                       pfree(xmsg);
                PG_RE_THROW();
        }
        PG_END_TRY();
 
-       PLy_free(emsg);
+       pfree(emsg.data);
        if (xmsg)
-               PLy_free(xmsg);
+               pfree(xmsg);
 }
 
 static char *
@@ -2493,8 +3039,8 @@ PLy_traceback(int *xlevel)
        PyObject   *eob,
                           *vob = NULL;
        char       *vstr,
-                          *estr,
-                          *xstr = NULL;
+                          *estr;
+       StringInfoData xstr;
 
        /*
         * get the current exception
@@ -2517,7 +3063,7 @@ PLy_traceback(int *xlevel)
        if (v && ((vob = PyObject_Str(v)) != NULL))
                vstr = PyString_AsString(vob);
        else
-               vstr = "Unknown";
+               vstr = "unknown";
 
        /*
         * I'm not sure what to do if eob is NULL here -- we can't call PLy_elog
@@ -2525,8 +3071,9 @@ PLy_traceback(int *xlevel)
         * recursion.  I'm not even sure if eob could be NULL here -- would an
         * Assert() be more appropriate?
         */
-       estr = eob ? PyString_AsString(eob) : "Unknown Exception";
-       xstr = PLy_printf("%s: %s", estr, vstr);
+       estr = eob ? PyString_AsString(eob) : "unrecognized exception";
+       initStringInfo(&xstr);
+       appendStringInfo(&xstr, "%s: %s", estr, vstr);
 
        Py_DECREF(eob);
        Py_XDECREF(vob);
@@ -2543,49 +3090,7 @@ PLy_traceback(int *xlevel)
                *xlevel = ERROR;
 
        Py_DECREF(e);
-       return xstr;
-}
-
-static char *
-PLy_printf(const char *fmt,...)
-{
-       va_list         ap;
-       char       *emsg;
-
-       va_start(ap, fmt);
-       emsg = PLy_vprintf(fmt, ap);
-       va_end(ap);
-       return emsg;
-}
-
-static char *
-PLy_vprintf(const char *fmt, va_list ap)
-{
-       size_t          blen;
-       int                     bchar,
-                               tries = 2;
-       char       *buf;
-
-       blen = strlen(fmt) * 2;
-       if (blen < 256)
-               blen = 256;
-       buf = PLy_malloc(blen * sizeof(char));
-
-       while (1)
-       {
-               bchar = vsnprintf(buf, blen, fmt, ap);
-               if (bchar > 0 && bchar < blen)
-                       return buf;
-               if (tries-- <= 0)
-                       break;
-               if (blen > 0)
-                       blen = bchar + 1;
-               else
-                       blen *= 2;
-               buf = PLy_realloc(buf, blen);
-       }
-       PLy_free(buf);
-       return NULL;
+       return xstr.data;
 }
 
 /* python module code */
@@ -2604,15 +3109,12 @@ PLy_malloc(size_t bytes)
 }
 
 static void *
-PLy_realloc(void *optr, size_t bytes)
+PLy_malloc0(size_t bytes)
 {
-       void       *nptr = realloc(optr, bytes);
+       void       *ptr = PLy_malloc(bytes);
 
-       if (nptr == NULL)
-               ereport(FATAL,
-                               (errcode(ERRCODE_OUT_OF_MEMORY),
-                                errmsg("out of memory")));
-       return nptr;
+       MemSet(ptr, 0, bytes);
+       return ptr;
 }
 
 static char *