]> 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 40d9de37abb9e509534ea6064ff7e3aeb17a102f..345f3f65236619d6cd431fde70a9aaef4eabe709 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.87 2006/09/02 12:30:01 momjian Exp $
+ *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.121 2009/06/04 18:33:08 tgl Exp $
  *
  *********************************************************************
  */
  * _DEBUG is defined */
 #undef _DEBUG
 /* Also hide away errcode, since we load Python.h before postgres.h */
-#define errcode __vc_errcode
+#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>
 
@@ -56,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;
@@ -118,13 +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 */
+       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 */
@@ -150,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;
@@ -176,22 +204,25 @@ PG_FUNCTION_INFO_V1(plpython_call_handler);
 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 *);
 
@@ -211,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 *);
@@ -306,7 +336,7 @@ plpython_call_handler(PG_FUNCTION_ARGS)
        PLyProcedure *volatile proc = NULL;
 
        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;
 
@@ -391,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)
@@ -405,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\".")));
                        }
                }
@@ -462,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);
@@ -482,16 +515,18 @@ 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);
 
@@ -506,14 +541,14 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
                        {
                                plstr = PyObject_Str(plval);
                                if (!plstr)
-                                       PLy_elog(ERROR, "function \"%s\" could not modify tuple",
+                                       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] =
                                        InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
                                                                          src,
-                                                                         proc->result.out.r.atts[atti].typioparam,
+                                                                       proc->result.out.r.atts[atti].typioparam,
                                                                          tupdesc->attrs[atti]->atttypmod);
                                modnulls[i] = ' ';
 
@@ -525,7 +560,7 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
                                modvalues[i] =
                                        InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
                                                                          NULL,
-                                                                         proc->result.out.r.atts[atti].typioparam,
+                                                                       proc->result.out.r.atts[atti].typioparam,
                                                                          tupdesc->attrs[atti]->atttypmod);
                                modnulls[i] = 'n';
                        }
@@ -537,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();
        {
@@ -575,9 +610,9 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
                           *pltevent,
                           *pltwhen,
                           *pltlevel,
-                      *pltrelid,
-                      *plttablename,
-                      *plttableschema;
+                          *pltrelid,
+                          *plttablename,
+                          *plttableschema;
        PyObject   *pltargs,
                           *pytnew,
                           *pytold;
@@ -588,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);
@@ -606,13 +641,13 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
                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");
@@ -693,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);
@@ -762,7 +799,11 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
                        plargs = PLy_function_build_args(fcinfo, proc);
                        plrv = PLy_procedure_call(proc, "args", plargs);
                        if (!proc->is_setof)
-                               /* SETOF function parameters will be deleted when last row is returned */
+
+                               /*
+                                * SETOF function parameters will be deleted when last row is
+                                * returned
+                                */
                                PLy_function_delete_args(proc);
                        Assert(plrv != NULL);
                        Assert(!PLy_error_in_progress);
@@ -779,18 +820,19 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 
                if (proc->is_setof)
                {
-                       bool has_error = false;
-                       ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo;
+                       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)
+                                       (rsi->allowedModes & SFRM_ValuePerCall) == 0)
                                {
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                        errmsg("only value per call is allowed")));
+                                                        errmsg("unsupported set function return mode"),
+                                                        errdetail("PL/Python set-returning functions only support returning only value per call.")));
                                }
                                rsi->returnMode = SFRM_ValuePerCall;
 
@@ -802,8 +844,8 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
                                if (proc->setof == NULL)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                        errmsg("returned object can not be iterated"),
-                                                        errdetail("SETOF must be returned as iterable object")));
+                                                        errmsg("returned object cannot be iterated"),
+                                       errdetail("PL/Python set-returning functions must return an iterable object.")));
                        }
 
                        /* Fetch next from iterator */
@@ -831,27 +873,25 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
                                if (has_error)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_DATA_EXCEPTION),
-                                                        errmsg("error fetching next item from iterator")));
+                                                 errmsg("error fetching next item from iterator")));
 
                                fcinfo->isnull = true;
-                               return (Datum)NULL;
+                               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;
@@ -861,16 +901,16 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
                        fcinfo->isnull = true;
                        if (proc->result.is_rowtype < 1)
                                rv = InputFunctionCall(&proc->result.out.d.typfunc,
-                                               NULL,
-                                               proc->result.out.d.typioparam,
-                                               -1);
+                                                                          NULL,
+                                                                          proc->result.out.d.typioparam,
+                                                                          -1);
                        else
                                /* Tuple as None */
                                rv = (Datum) NULL;
                }
                else if (proc->result.is_rowtype >= 1)
                {
-                       HeapTuple   tuple = NULL;
+                       HeapTuple       tuple = NULL;
 
                        if (PySequence_Check(plrv))
                                /* composite type as sequence (tuple, list etc) */
@@ -898,7 +938,7 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
                        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 = InputFunctionCall(&proc->result.out.d.typfunc,
                                                                   plrv_sc,
@@ -947,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;
@@ -1016,10 +1056,12 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
                                arg = Py_None;
                        }
 
-                       if (PyList_SetItem(args, i, arg) == -1 ||
-                                       (proc->argnames &&
-                                        PyDict_SetItemString(proc->globals, proc->argnames[i], arg) == -1))
-                               PLy_elog(ERROR, "problem setting up arguments for \"%s\"", proc->proname);
+                       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;
                }
        }
@@ -1037,15 +1079,16 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
 
 
 static void
-PLy_function_delete_args(PLyProcedure *proc)
+PLy_function_delete_args(PLyProcedure * proc)
 {
-       int     i;
+       int                     i;
 
        if (!proc->argnames)
                return;
 
-       for (i = 0;  i < proc->nargs;  i++)
-               PyDict_DelItemString(proc->globals, proc->argnames[i]);
+       for (i = 0; i < proc->nargs; i++)
+               if (proc->argnames[i])
+                       PyDict_DelItemString(proc->globals, proc->argnames[i]);
 }
 
 
@@ -1092,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;
@@ -1100,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);
 
@@ -1108,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;
@@ -1119,9 +1178,6 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
        bool            isnull;
        int                     i,
                                rv;
-       Datum           argnames;
-       Datum           *elems;
-       int             nelems;
 
        procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 
@@ -1129,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");
 
@@ -1143,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);
@@ -1163,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;
@@ -1177,23 +1233,26 @@ 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')
+                       if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE)
                        {
-                               /* Tuple: set up later, during first call to PLy_function_handler */
+                               /*
+                                * Tuple: set up later, during first call to
+                                * PLy_function_handler
+                                */
                                proc->result.out.d.typoid = procStruct->prorettype;
                                proc->result.is_rowtype = 2;
                        }
@@ -1202,69 +1261,88 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
 
                        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;
-               if (proc->nargs)
+               if (procStruct->pronargs)
                {
-                       argnames = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proargnames, &isnull);
-                       if (!isnull)
+                       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
                        {
-                               deconstruct_array(DatumGetArrayTypeP(argnames), TEXTOID, -1, false, 'i',
-                                               &elems, NULL, &nelems);
-                               if (nelems != proc->nargs)
-                                       elog(ERROR,
-                                                       "proargnames must have the same number of elements "
-                                                       "as the function has arguments");
-                               proc->argnames = (char **) PLy_malloc(sizeof(char *)*proc->nargs);
+                               /* proc->nargs was initialized to 0 above */
+                               for (i = 0; i < total; i++)
+                               {
+                                       if (modes[i] != PROARGMODE_OUT &&
+                                               modes[i] != PROARGMODE_TABLE)
+                                               (proc->nargs)++;
+                               }
                        }
-               }
-               for (i = 0; i < fcinfo->nargs; i++)
-               {
-                       HeapTuple       argTypeTup;
-                       Form_pg_type argTypeStruct;
 
-                       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 */
+
+                               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);
 
-                       ReleaseSysCache(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;
+                               }
 
-                       /* Fetch argument name */
-                       if (proc->argnames)
-                               proc->argnames[i] = PLy_strdup(DatumGetCString(DirectFunctionCall1(textout, elems[i])));
+                               /* get argument name */
+                               proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL;
+
+                               ReleaseSysCache(argTypeTup);
+
+                               pos++;
+                       }
                }
 
                /*
@@ -1274,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);
 
@@ -1306,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);
@@ -1339,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 *
@@ -1416,8 +1493,9 @@ PLy_procedure_delete(PLyProcedure * proc)
                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)
@@ -1426,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++)
        {
@@ -1438,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);
@@ -1460,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++)
        {
@@ -1472,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);
@@ -1521,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;
 
@@ -1573,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 *
@@ -1627,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();
        {
@@ -1674,13 +1775,13 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
 
 
 static HeapTuple
-PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
+PLyMapping_ToTuple(PLyTypeInfo * info, PyObject * mapping)
 {
        TupleDesc       desc;
        HeapTuple       tuple;
-       Datum           *values;
-       char            *nulls;
-       int             i;
+       Datum      *values;
+       bool       *nulls;
+       volatile int i;
 
        Assert(PyMapping_Check(mapping));
 
@@ -1690,13 +1791,13 @@ PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
        Assert(info->is_rowtype == 1);
 
        /* Build tuple */
-       values = palloc(sizeof(Datum)*desc->natts);
-       nulls = palloc(sizeof(char)*desc->natts);
-       for (i = 0;  i < desc->natts;  ++i)
+       values = palloc(sizeof(Datum) * desc->natts);
+       nulls = palloc(sizeof(bool) * desc->natts);
+       for (i = 0; i < desc->natts; ++i)
        {
-               char            *key;
-               PyObject        *value,
-                               *so;
+               char       *key;
+               PyObject   *volatile value,
+                                  *volatile so;
 
                key = NameStr(desc->attrs[i]->attname);
                value = so = NULL;
@@ -1706,31 +1807,31 @@ PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
                        if (value == Py_None)
                        {
                                values[i] = (Datum) NULL;
-                               nulls[i] = 'n';
+                               nulls[i] = true;
                        }
                        else if (value)
                        {
-                               char *valuestr;
+                               char       *valuestr;
 
                                so = PyObject_Str(value);
                                if (so == NULL)
-                                       PLy_elog(ERROR, "can't convert mapping type");
+                                       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);
+                                                                                         ,valuestr
+                                                                                         ,info->out.r.atts[i].typioparam
+                                                                                         ,-1);
                                Py_DECREF(so);
                                so = NULL;
-                               nulls[i] = ' ';
+                               nulls[i] = false;
                        }
                        else
                                ereport(ERROR,
                                                (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                                errmsg("no mapping found with key \"%s\"", key),
-                                                errhint("to return null in specific column, "
-                                                        "add value None to map with key named after 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;
@@ -1744,7 +1845,7 @@ PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
                PG_END_TRY();
        }
 
-       tuple = heap_formtuple(desc, values, nulls);
+       tuple = heap_form_tuple(desc, values, nulls);
        ReleaseTupleDesc(desc);
        pfree(values);
        pfree(nulls);
@@ -1754,38 +1855,38 @@ PLyMapping_ToTuple(PLyTypeInfo *info, PyObject *mapping)
 
 
 static HeapTuple
-PLySequence_ToTuple(PLyTypeInfo *info, PyObject *sequence)
+PLySequence_ToTuple(PLyTypeInfo * info, PyObject * sequence)
 {
        TupleDesc       desc;
        HeapTuple       tuple;
-       Datum           *values;
-       char            *nulls;
-       int             i;
+       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
+        * 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("returned sequence's length must be same as tuple's length")));
+               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(char)*desc->natts);
-       for (i = 0;  i < desc->natts;  ++i)
+       values = palloc(sizeof(Datum) * desc->natts);
+       nulls = palloc(sizeof(bool) * desc->natts);
+       for (i = 0; i < desc->natts; ++i)
        {
-               PyObject        *value,
-                               *so;
+               PyObject   *volatile value,
+                                  *volatile so;
 
                value = so = NULL;
                PG_TRY();
@@ -1795,23 +1896,23 @@ PLySequence_ToTuple(PLyTypeInfo *info, PyObject *sequence)
                        if (value == Py_None)
                        {
                                values[i] = (Datum) NULL;
-                               nulls[i] = 'n';
+                               nulls[i] = true;
                        }
                        else if (value)
                        {
-                               char *valuestr;
+                               char       *valuestr;
 
                                so = PyObject_Str(value);
                                if (so == NULL)
-                                       PLy_elog(ERROR, "can't convert sequence type");
+                                       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);
+                                                                                         ,valuestr
+                                                                                         ,info->out.r.atts[i].typioparam
+                                                                                         ,-1);
                                Py_DECREF(so);
                                so = NULL;
-                               nulls[i] = ' ';
+                               nulls[i] = false;
                        }
 
                        Py_XDECREF(value);
@@ -1826,7 +1927,7 @@ PLySequence_ToTuple(PLyTypeInfo *info, PyObject *sequence)
                PG_END_TRY();
        }
 
-       tuple = heap_formtuple(desc, values, nulls);
+       tuple = heap_form_tuple(desc, values, nulls);
        ReleaseTupleDesc(desc);
        pfree(values);
        pfree(nulls);
@@ -1836,13 +1937,13 @@ PLySequence_ToTuple(PLyTypeInfo *info, PyObject *sequence)
 
 
 static HeapTuple
-PLyObject_ToTuple(PLyTypeInfo *info, PyObject *object)
+PLyObject_ToTuple(PLyTypeInfo * info, PyObject * object)
 {
        TupleDesc       desc;
        HeapTuple       tuple;
-       Datum           *values;
-       char            *nulls;
-       int             i;
+       Datum      *values;
+       bool       *nulls;
+       volatile int i;
 
        desc = lookup_rowtype_tupdesc(info->out.d.typoid, -1);
        if (info->is_rowtype == 2)
@@ -1850,13 +1951,13 @@ PLyObject_ToTuple(PLyTypeInfo *info, PyObject *object)
        Assert(info->is_rowtype == 1);
 
        /* Build tuple */
-       values = palloc(sizeof(Datum)*desc->natts);
-       nulls = palloc(sizeof(char)*desc->natts);
-       for (i = 0;  i < desc->natts;  ++i)
+       values = palloc(sizeof(Datum) * desc->natts);
+       nulls = palloc(sizeof(bool) * desc->natts);
+       for (i = 0; i < desc->natts; ++i)
        {
-               char            *key;
-               PyObject        *value,
-                               *so;
+               char       *key;
+               PyObject   *volatile value,
+                                  *volatile so;
 
                key = NameStr(desc->attrs[i]->attname);
                value = so = NULL;
@@ -1866,31 +1967,31 @@ PLyObject_ToTuple(PLyTypeInfo *info, PyObject *object)
                        if (value == Py_None)
                        {
                                values[i] = (Datum) NULL;
-                               nulls[i] = 'n';
+                               nulls[i] = true;
                        }
                        else if (value)
                        {
-                               char *valuestr;
+                               char       *valuestr;
 
                                so = PyObject_Str(value);
                                if (so == NULL)
-                                       PLy_elog(ERROR, "can't convert object type");
+                                       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);
+                                                                                         ,valuestr
+                                                                                         ,info->out.r.atts[i].typioparam
+                                                                                         ,-1);
                                Py_DECREF(so);
                                so = NULL;
-                               nulls[i] = ' ';
+                               nulls[i] = false;
                        }
                        else
                                ereport(ERROR,
                                                (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                                errmsg("no attribute named \"%s\"", key),
-                                                errhint("to return null in specific column, "
-                                                        "let returned object to have attribute named "
-                                                        "after column with value None")));
+                                                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;
@@ -1904,7 +2005,7 @@ PLyObject_ToTuple(PLyTypeInfo *info, PyObject *object)
                PG_END_TRY();
        }
 
-       tuple = heap_formtuple(desc, values, nulls);
+       tuple = heap_form_tuple(desc, values, nulls);
        ReleaseTupleDesc(desc);
        pfree(values);
        pfree(nulls);
@@ -1936,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 *);
@@ -1960,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 */
@@ -1975,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 */
 };
 
@@ -1984,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 = {
@@ -2005,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 */
@@ -2020,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 */
 };
 
@@ -2092,7 +2192,7 @@ PLy_plan_dealloc(PyObject * arg)
                PLy_free(ob->args);
        }
 
-       PyMem_DEL(arg);
+       arg->ob_type->tp_free(arg);
 }
 
 
@@ -2111,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;
 }
 
@@ -2146,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 *
@@ -2173,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;
@@ -2182,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;
@@ -2194,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;
@@ -2205,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;
@@ -2218,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;
@@ -2241,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;
        }
 
@@ -2292,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);
                                }
                        }
@@ -2344,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;
        }
@@ -2370,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;
        }
 
@@ -2383,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;
 }
 
@@ -2400,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);
@@ -2418,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;
@@ -2433,29 +2543,30 @@ 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] =
-                                               InputFunctionCall(&(plan->args[i].out.d.typfunc),
+                                       plan->values[j] =
+                                               InputFunctionCall(&(plan->args[j].out.d.typfunc),
                                                                                  sv,
-                                                                                 plan->args[i].out.d.typioparam,
+                                                                                 plan->args[j].out.d.typioparam,
                                                                                  -1);
                                }
                                PG_CATCH();
@@ -2466,17 +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] =
-                                       InputFunctionCall(&(plan->args[i].out.d.typfunc),
+                               plan->values[j] =
+                                       InputFunctionCall(&(plan->args[j].out.d.typfunc),
                                                                          NULL,
-                                                                         plan->args[i].out.d.typioparam,
+                                                                         plan->args[j].out.d.typioparam,
                                                                          -1);
-                               nulls[i] = 'n';
+                               nulls[j] = 'n';
                        }
                }
 
@@ -2487,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();
@@ -2494,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;
        }
@@ -2552,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;
        }
@@ -2623,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;
@@ -2654,6 +2767,8 @@ _PG_init(void)
        if (inited)
                return;
 
+       pg_bindtextdomain(TEXTDOMAIN);
+
        Py_Initialize();
        PLy_init_interp();
        PLy_init_plpy();
@@ -2673,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();
@@ -2695,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);
 
@@ -2716,7 +2835,7 @@ 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
@@ -2778,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;
@@ -2828,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,...)
@@ -2838,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);
@@ -2851,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 *
@@ -2891,8 +3039,8 @@ PLy_traceback(int *xlevel)
        PyObject   *eob,
                           *vob = NULL;
        char       *vstr,
-                          *estr,
-                          *xstr = NULL;
+                          *estr;
+       StringInfoData xstr;
 
        /*
         * get the current exception
@@ -2915,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
@@ -2923,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);
@@ -2941,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 */
@@ -3002,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 *