]> 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 010f1e1645ec541261e5be0dee406e175c26732c..345f3f65236619d6cd431fde70a9aaef4eabe709 100644 (file)
@@ -1,63 +1,77 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- * This software is copyright by Andrew Bosma
- * but is really shameless cribbed from pltcl.c by Jan Weick, and
- * plperl.c by Mark Hollomon.
- *
- * The author hereby grants permission to use, copy, modify,
- * distribute, and license this software and its documentation for any
- * purpose, provided that existing copyright notices are retained in
- * all copies and that this notice is included verbatim in any
- * distributions. No written agreement, license, or royalty fee is
- * required for any of the authorized uses.  Modifications to this
- * software may be copyrighted by their author and need not follow the
- * licensing terms described here, provided that the new terms are
- * clearly indicated on the first page of each file where they apply.
- *
- * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY
- * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
- * DERIVATIVES THEREOF, EVEN IF THE AUTHOR HAVE BEEN ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
- * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
- * AND THE AUTHOR AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
- * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * IDENTIFICATION
- *     $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.46 2004/04/01 21:28:46 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 <unistd.h>
 #include <fcntl.h>
-#include <setjmp.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/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
 
-#include <Python.h>
+/* define our text domain for translations */
+#undef TEXTDOMAIN
+#define TEXTDOMAIN PG_TEXTDOMAIN("plpython")
+
 #include <compile.h>
 #include <eval.h>
-#include "plpython.h"
+
+PG_MODULE_MAGIC;
 
 /* convert Postgresql Datum or tuple into a PyObject.
  * input to Python.  Tuples are converted to dictionary
@@ -69,8 +83,9 @@ typedef PyObject *(*PLyDatumToObFunc) (const char *);
 typedef struct PLyDatumToOb
 {
        PLyDatumToObFunc func;
-       FmgrInfo        typfunc;
-       Oid                     typelem;
+       FmgrInfo        typfunc;                /* The type's output function */
+       Oid                     typoid;                 /* The OID of the type */
+       Oid                     typioparam;
        bool            typbyval;
 }      PLyDatumToOb;
 
@@ -91,8 +106,9 @@ typedef union PLyTypeInput
  */
 typedef struct PLyObToDatum
 {
-       FmgrInfo        typfunc;
-       Oid                     typelem;
+       FmgrInfo        typfunc;                /* The type's input function */
+       Oid                     typoid;                 /* The OID of the type */
+       Oid                     typioparam;
        bool            typbyval;
 }      PLyObToDatum;
 
@@ -116,38 +132,38 @@ typedef struct PLyTypeInfo
        PLyTypeInput in;
        PLyTypeOutput out;
        int                     is_rowtype;
+
        /*
-        * is_rowtype can be:
-        *              -1      not known yet (initial state)
-        *               0      scalar datatype
-        *               1      rowtype
-        *               2      rowtype, but I/O functions not set up yet
+        * is_rowtype can be: -1  not known yet (initial state) 0  scalar datatype
+        * 1  rowtype 2  rowtype, but I/O functions not set up yet
         */
 }      PLyTypeInfo;
 
 
-/* cached procedure data
- */
+/* cached procedure data */
 typedef struct PLyProcedure
 {
        char       *proname;            /* SQL name of procedure */
        char       *pyname;                     /* Python name of procedure */
        TransactionId fn_xmin;
-       CommandId       fn_cmin;
-       PLyTypeInfo result;                     /* also used to store info for trigger
-                                                                * tuple type */
+       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 */
        PyObject   *statics;            /* data saved across calls, local scope */
-       PyObject   *globals;            /* data saved across calls, global score */
+       PyObject   *globals;            /* data saved across calls, global scope */
        PyObject   *me;                         /* PyCObject containing pointer to this
                                                                 * PLyProcedure */
 }      PLyProcedure;
 
 
-/* Python objects.
- */
+/* Python objects */
 typedef struct PLyPlanObject
 {
        PyObject_HEAD
@@ -162,62 +178,60 @@ 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;
 
 
-/* function declarations
- */
+/* 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);
 
-/* most of the remaining of the declarations, all static
- */
+/* most of the remaining of the declarations, all static */
 
 /* these should only be called once at the first call
  * 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);
 
-/* error handler.  collects the current Python exception, if any,
- * and appends it to the error and sends it to elog
- */
-static void PLy_elog(int, const char *,...);
-
-/* 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.
- */
+/* Get the innermost python procedure called from the backend */
 static char *PLy_procedure_name(PLyProcedure *);
 
-/* some utility functions
- */
+/* some utility functions */
+static void PLy_elog(int, const char *,...)
+__attribute__((format(printf, 2, 3)));
+static char *PLy_traceback(int *);
+
 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 *);
 
-/* sub handlers for functions and triggers
- */
+/* sub handlers for functions and triggers */
 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 *,
@@ -226,11 +240,10 @@ static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *,
 static PyObject *PLy_procedure_call(PLyProcedure *, char *, PyObject *);
 
 static PLyProcedure *PLy_procedure_get(FunctionCallInfo fcinfo,
-                                                                          Oid tgreloid);
+                                 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 *);
@@ -238,15 +251,14 @@ static void PLy_procedure_delete(PLyProcedure *);
 
 static void PLy_typeinfo_init(PLyTypeInfo *);
 static void PLy_typeinfo_dealloc(PLyTypeInfo *);
-static void PLy_output_datum_func(PLyTypeInfo *, Form_pg_type);
-static void PLy_output_datum_func2(PLyObToDatum *, Form_pg_type);
-static void PLy_input_datum_func(PLyTypeInfo *, Oid, Form_pg_type);
-static void PLy_input_datum_func2(PLyDatumToOb *, Oid, Form_pg_type);
+static void PLy_output_datum_func(PLyTypeInfo *, HeapTuple);
+static void PLy_output_datum_func2(PLyObToDatum *, HeapTuple);
+static void PLy_input_datum_func(PLyTypeInfo *, Oid, HeapTuple);
+static void PLy_input_datum_func2(PLyDatumToOb *, Oid, HeapTuple);
 static void PLy_output_tuple_funcs(PLyTypeInfo *, TupleDesc);
 static void PLy_input_tuple_funcs(PLyTypeInfo *, TupleDesc);
 
-/* conversion functions
- */
+/* conversion functions */
 static PyObject *PLyDict_FromTuple(PLyTypeInfo *, HeapTuple, TupleDesc);
 static PyObject *PLyBool_FromString(const char *);
 static PyObject *PLyFloat_FromString(const char *);
@@ -254,35 +266,38 @@ static PyObject *PLyInt_FromString(const char *);
 static PyObject *PLyLong_FromString(const char *);
 static PyObject *PLyString_FromString(const char *);
 
-
-/* global data
- */
-static int     PLy_first_call = 1;
-static volatile int PLy_call_level = 0;
+static HeapTuple PLyMapping_ToTuple(PLyTypeInfo *, PyObject *);
+static HeapTuple PLySequence_ToTuple(PLyTypeInfo *, PyObject *);
+static HeapTuple PLyObject_ToTuple(PLyTypeInfo *, PyObject *);
 
 /*
- * Last function called by postgres backend
+ * Currently active plpython function
  */
-static PLyProcedure *PLy_last_procedure = NULL;
+static PLyProcedure *PLy_curr_procedure = NULL;
 
-/* this gets modified in plpython_call_handler and PLy_elog.
- * test it any old where, but do NOT modify it anywhere except
- * those two functions
+/*
+ * When a callback from Python into PG incurs an error, we temporarily store
+ * the error information here, and return NULL to the Python interpreter.
+ * Any further callback attempts immediately fail, and when the Python
+ * interpreter returns to the calling function, we re-throw the error (even if
+ * Python thinks it trapped the error and doesn't return NULL).  Eventually
+ * this ought to be improved to let Python code really truly trap the error,
+ * but that's more of a change from the pre-8.0 semantics than I have time for
+ * now --- it will only be possible if the callback query is executed inside a
+ * subtransaction.
  */
-static volatile int PLy_restart_in_progress = 0;
+static ErrorData *PLy_error_in_progress = NULL;
 
 static PyObject *PLy_interp_globals = NULL;
 static PyObject *PLy_interp_safe_globals = NULL;
 static PyObject *PLy_procedure_cache = NULL;
 
-/* Python exceptions
- */
+/* Python exceptions */
 static PyObject *PLy_exc_error = NULL;
 static PyObject *PLy_exc_fatal = NULL;
 static PyObject *PLy_exc_spi_error = NULL;
 
-/* some globals for the python module
- */
+/* some globals for the python module */
 static char PLy_plan_doc[] = {
        "Store a PostgreSQL plan"
 };
@@ -292,13 +307,6 @@ static char PLy_result_doc[] = {
 };
 
 
-#if DEBUG_EXC
-volatile int exc_save_calls = 0;
-volatile int exc_restore_calls = 0;
-volatile int func_enter_calls = 0;
-volatile int func_leave_calls = 0;
-#endif
-
 /*
  * the function definitions
  */
@@ -323,65 +331,51 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
 Datum
 plpython_call_handler(PG_FUNCTION_ARGS)
 {
-       DECLARE_EXC();
        Datum           retval;
+       PLyProcedure *save_curr_proc;
        PLyProcedure *volatile proc = NULL;
 
-       enter();
-
-       PLy_init_all();
-
        if (SPI_connect() != SPI_OK_CONNECT)
-               elog(ERROR, "could not connect to SPI manager");
+               elog(ERROR, "SPI_connect failed");
 
-       CALL_LEVEL_INC();
+       save_curr_proc = PLy_curr_procedure;
 
-       SAVE_EXC();
-       if (TRAP_EXC())
+       PG_TRY();
        {
-               RESTORE_EXC();
-               CALL_LEVEL_DEC();
-               if (PLy_call_level == 0)
+               if (CALLED_AS_TRIGGER(fcinfo))
                {
-                       PLy_restart_in_progress = 0;
-                       PyErr_Clear();
+                       TriggerData *tdata = (TriggerData *) fcinfo->context;
+                       HeapTuple       trv;
+
+                       proc = PLy_procedure_get(fcinfo,
+                                                                        RelationGetRelid(tdata->tg_relation));
+                       PLy_curr_procedure = proc;
+                       trv = PLy_trigger_handler(fcinfo, proc);
+                       retval = PointerGetDatum(trv);
                }
                else
-                       PLy_restart_in_progress += 1;
+               {
+                       proc = PLy_procedure_get(fcinfo, InvalidOid);
+                       PLy_curr_procedure = proc;
+                       retval = PLy_function_handler(fcinfo, proc);
+               }
+       }
+       PG_CATCH();
+       {
+               PLy_curr_procedure = save_curr_proc;
                if (proc)
                {
                        /* note: Py_DECREF needs braces around it, as of 2003/08 */
                        Py_DECREF(proc->me);
                }
-               RERAISE_EXC();
+               PyErr_Clear();
+               PG_RE_THROW();
        }
+       PG_END_TRY();
 
-       /*
-        * elog(DEBUG3, "PLy_restart_in_progress is %d",
-        * PLy_restart_in_progress);
-        */
-
-       if (CALLED_AS_TRIGGER(fcinfo))
-       {
-               TriggerData *tdata = (TriggerData *) fcinfo->context;
-               HeapTuple       trv;
-
-               proc = PLy_procedure_get(fcinfo,
-                                                                RelationGetRelid(tdata->tg_relation));
-               trv = PLy_trigger_handler(fcinfo, proc);
-               retval = PointerGetDatum(trv);
-       }
-       else
-       {
-               proc = PLy_procedure_get(fcinfo, InvalidOid);
-               retval = PLy_function_handler(fcinfo, proc);
-       }
-
-       CALL_LEVEL_DEC();
-       RESTORE_EXC();
+       PLy_curr_procedure = save_curr_proc;
 
        Py_DECREF(proc->me);
-       refc(proc->me);
 
        return retval;
 }
@@ -396,89 +390,86 @@ plpython_call_handler(PG_FUNCTION_ARGS)
  * BEFORE the event and is ROW level.  postgres expects the function
  * to take no arguments and return an argument of type trigger.
  */
-HeapTuple
+static HeapTuple
 PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 {
-       DECLARE_EXC();
        HeapTuple       rv = NULL;
        PyObject   *volatile plargs = NULL;
        PyObject   *volatile plrv = NULL;
 
-       enter();
-
-       SAVE_EXC();
-       if (TRAP_EXC())
+       PG_TRY();
        {
-               RESTORE_EXC();
-
-               Py_XDECREF(plargs);
-               Py_XDECREF(plrv);
-
-               RERAISE_EXC();
-       }
-
-       plargs = PLy_trigger_build_args(fcinfo, proc, &rv);
-       plrv = PLy_procedure_call(proc, "TD", plargs);
-
-       /*
-        * Disconnect from SPI manager
-        */
-       if (SPI_finish() != SPI_OK_FINISH)
-               elog(ERROR, "SPI_finish failed");
-
-       if (plrv == NULL)
-               elog(FATAL, "PLy_procedure_call returned NULL");
+               plargs = PLy_trigger_build_args(fcinfo, proc, &rv);
+               plrv = PLy_procedure_call(proc, "TD", plargs);
 
-       if (PLy_restart_in_progress)
-               elog(FATAL, "restart in progress not expected");
+               Assert(plrv != NULL);
+               Assert(!PLy_error_in_progress);
 
-       /*
-        * return of None means we're happy with the tuple
-        */
-       if (plrv != Py_None)
-       {
-               char       *srv;
-
-               if (!PyString_Check(plrv))
-                       elog(ERROR, "expected trigger to return None or a String");
+               /*
+                * Disconnect from SPI manager
+                */
+               if (SPI_finish() != SPI_OK_FINISH)
+                       elog(ERROR, "SPI_finish failed");
 
-               srv = PyString_AsString(plrv);
-               if (strcasecmp(srv, "SKIP") == 0)
-                       rv = NULL;
-               else if (strcasecmp(srv, "MODIFY") == 0)
+               /*
+                * return of None means we're happy with the tuple
+                */
+               if (plrv != Py_None)
                {
-                       TriggerData *tdata = (TriggerData *) fcinfo->context;
+                       char       *srv;
+
+                       if (!PyString_Check(plrv))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATA_EXCEPTION),
+                                       errmsg("unexpected return value from trigger procedure"),
+                                                errdetail("Expected None or a string.")));
+
+                       srv = PyString_AsString(plrv);
+                       if (pg_strcasecmp(srv, "SKIP") == 0)
+                               rv = NULL;
+                       else if (pg_strcasecmp(srv, "MODIFY") == 0)
+                       {
+                               TriggerData *tdata = (TriggerData *) fcinfo->context;
 
-                       if ((TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) ||
-                               (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)))
-                               rv = PLy_modify_tuple(proc, plargs, tdata, rv);
-                       else
-                               elog(WARNING, "ignoring modified tuple in DELETE trigger");
-               }
-               else if (strcasecmp(srv, "OK"))
-               {
-                       /*
-                        * hmmm, perhaps they only read the pltcl page, not a
-                        * surprising thing since i've written no documentation, so
-                        * accept a belated OK
-                        */
-                       elog(ERROR, "expected return to be \"SKIP\" or \"MODIFY\"");
+                               if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event) ||
+                                       TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
+                                       rv = PLy_modify_tuple(proc, plargs, tdata, rv);
+                               else
+                                       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
+                                */
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATA_EXCEPTION),
+                                       errmsg("unexpected return value from trigger procedure"),
+                                                errdetail("Expected None, \"OK\", \"SKIP\", or \"MODIFY\".")));
+                       }
                }
        }
+       PG_CATCH();
+       {
+               Py_XDECREF(plargs);
+               Py_XDECREF(plrv);
+
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
 
        Py_DECREF(plargs);
        Py_DECREF(plrv);
 
-       RESTORE_EXC();
-
        return rv;
 }
 
-HeapTuple
+static HeapTuple
 PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
                                 HeapTuple otup)
 {
-       DECLARE_EXC();
        PyObject   *volatile plntup;
        PyObject   *volatile plkeys;
        PyObject   *volatile platt;
@@ -499,363 +490,471 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
        modvalues = NULL;
        modnulls = NULL;
 
-       enter();
-
-       SAVE_EXC();
-       if (TRAP_EXC())
+       PG_TRY();
        {
-               RESTORE_EXC();
-
-               Py_XDECREF(plntup);
-               Py_XDECREF(plkeys);
-               Py_XDECREF(platt);
-               Py_XDECREF(plval);
-               Py_XDECREF(plstr);
-
-               if (modnulls)
-                       pfree(modnulls);
-               if (modvalues)
-                       pfree(modvalues);
-               if (modattrs)
-                       pfree(modattrs);
-
-               RERAISE_EXC();
-       }
-
-       if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
-               elog(ERROR, "TD[\"new\"] deleted, unable to modify tuple");
-       if (!PyDict_Check(plntup))
-               elog(ERROR, "TD[\"new\"] is not a dictionary object");
-       Py_INCREF(plntup);
-
-       plkeys = PyDict_Keys(plntup);
-       natts = PyList_Size(plkeys);
+               if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
+                       ereport(ERROR, 
+                                       (errmsg("TD[\"new\"] deleted, cannot modify row")));
+               if (!PyDict_Check(plntup))
+                       ereport(ERROR,
+                                       (errmsg("TD[\"new\"] is not a dictionary")));
+               Py_INCREF(plntup);
 
-       /* +1 to avoid palloc(0) on empty tuple */
-       modattrs = palloc(natts * sizeof(int) + 1);
-       modvalues = palloc(natts * sizeof(Datum) + 1);
-       modnulls = palloc(natts + 1);
+               plkeys = PyDict_Keys(plntup);
+               natts = PyList_Size(plkeys);
 
-       tupdesc = tdata->tg_relation->rd_att;
+               modattrs = (int *) palloc(natts * sizeof(int));
+               modvalues = (Datum *) palloc(natts * sizeof(Datum));
+               modnulls = (char *) palloc(natts * sizeof(char));
 
-       for (i = 0; i < natts; i++)
-       {
-               char       *src;
+               tupdesc = tdata->tg_relation->rd_att;
 
-               platt = PyList_GetItem(plkeys, i);
-               if (!PyString_Check(platt))
-                       elog(ERROR, "attribute name is not a string");
-               attn = SPI_fnumber(tupdesc, PyString_AsString(platt));
-               if (attn == SPI_ERROR_NOATTRIBUTE)
-                       elog(ERROR, "invalid attribute \"%s\" in tuple",
-                                PyString_AsString(platt));
-               atti = attn - 1;
+               for (i = 0; i < natts; i++)
+               {
+                       char       *src;
 
-               plval = PyDict_GetItem(plntup, platt);
-               if (plval == NULL)
-                       elog(FATAL, "python interpreter is probably corrupted");
+                       platt = PyList_GetItem(plkeys, i);
+                       if (!PyString_Check(platt))
+                               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)
+                               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;
 
-               Py_INCREF(plval);
+                       plval = PyDict_GetItem(plntup, platt);
+                       if (plval == NULL)
+                               elog(FATAL, "Python interpreter is probably corrupted");
 
-               modattrs[i] = attn;
+                       Py_INCREF(plval);
 
-               if (plval != Py_None && !tupdesc->attrs[atti]->attisdropped)
-               {
-                       plstr = PyObject_Str(plval);
-                       src = PyString_AsString(plstr);
+                       modattrs[i] = attn;
 
-                       modvalues[i] = FunctionCall3(&proc->result.out.r.atts[atti].typfunc,
-                                                                                CStringGetDatum(src),
-                                ObjectIdGetDatum(proc->result.out.r.atts[atti].typelem),
-                                                Int32GetDatum(tupdesc->attrs[atti]->atttypmod));
-                       modnulls[i] = ' ';
+                       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, "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,
+                                                                         tupdesc->attrs[atti]->atttypmod);
+                               modnulls[i] = ' ';
+
+                               Py_DECREF(plstr);
+                               plstr = NULL;
+                       }
+                       else
+                       {
+                               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';
+                       }
 
-                       Py_DECREF(plstr);
-                       plstr = NULL;
-               }
-               else
-               {
-                       modvalues[i] = (Datum) 0;
-                       modnulls[i] = 'n';
+                       Py_DECREF(plval);
+                       plval = NULL;
                }
 
-               Py_DECREF(plval);
-               plval = NULL;
+               rtup = SPI_modifytuple(tdata->tg_relation, otup, natts,
+                                                          modattrs, modvalues, modnulls);
+               if (rtup == NULL)
+                       elog(ERROR, "SPI_modifytuple failed: error %d", SPI_result);
        }
+       PG_CATCH();
+       {
+               Py_XDECREF(plntup);
+               Py_XDECREF(plkeys);
+               Py_XDECREF(plval);
+               Py_XDECREF(plstr);
 
-       rtup = SPI_modifytuple(tdata->tg_relation, otup, natts,
-                                                  modattrs, modvalues, modnulls);
-
-       /*
-        * FIXME -- these leak if not explicitly pfree'd by other elog calls,
-        * no?  (No, I think, but might as well leave the pfrees here...)
-        */
-       pfree(modattrs);
-       pfree(modvalues);
-       pfree(modnulls);
+               if (modnulls)
+                       pfree(modnulls);
+               if (modvalues)
+                       pfree(modvalues);
+               if (modattrs)
+                       pfree(modattrs);
 
-       if (rtup == NULL)
-               elog(ERROR, "SPI_modifytuple failed -- error %d", SPI_result);
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
 
        Py_DECREF(plntup);
        Py_DECREF(plkeys);
 
-       RESTORE_EXC();
+       pfree(modattrs);
+       pfree(modvalues);
+       pfree(modnulls);
 
        return rtup;
 }
 
-PyObject *
+static PyObject *
 PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *rv)
 {
-       DECLARE_EXC();
-       TriggerData *tdata;
+       TriggerData *tdata = (TriggerData *) fcinfo->context;
        PyObject   *pltname,
                           *pltevent,
                           *pltwhen,
                           *pltlevel,
-                          *pltrelid;
+                          *pltrelid,
+                          *plttablename,
+                          *plttableschema;
        PyObject   *pltargs,
                           *pytnew,
                           *pytold;
        PyObject   *volatile pltdata = NULL;
        char       *stroid;
 
-       enter();
-
-       SAVE_EXC();
-       if (TRAP_EXC())
-       {
-               RESTORE_EXC();
-
-               Py_XDECREF(pltdata);
-
-               RERAISE_EXC();
-       }
-
-       tdata = (TriggerData *) fcinfo->context;
-
-       pltdata = PyDict_New();
-       if (!pltdata)
-               PLy_elog(ERROR, "could not build arguments for trigger procedure");
-
-       pltname = PyString_FromString(tdata->tg_trigger->tgname);
-       PyDict_SetItemString(pltdata, "name", pltname);
-       Py_DECREF(pltname);
-
-       stroid = DatumGetCString(DirectFunctionCall1(oidout,
-                                                  ObjectIdGetDatum(tdata->tg_relation->rd_id)));
-       pltrelid = PyString_FromString(stroid);
-       PyDict_SetItemString(pltdata, "relid", pltrelid);
-       Py_DECREF(pltrelid);
-       pfree(stroid);
-
-       if (TRIGGER_FIRED_BEFORE(tdata->tg_event))
-               pltwhen = PyString_FromString("BEFORE");
-       else if (TRIGGER_FIRED_AFTER(tdata->tg_event))
-               pltwhen = PyString_FromString("AFTER");
-       else
-       {
-               elog(ERROR, "unrecognized WHEN tg_event: %u", tdata->tg_event);
-               pltwhen = NULL;                 /* keep compiler quiet */
-       }
-       PyDict_SetItemString(pltdata, "when", pltwhen);
-       Py_DECREF(pltwhen);
-
-       if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
+       PG_TRY();
        {
-               pltlevel = PyString_FromString("ROW");
-               PyDict_SetItemString(pltdata, "level", pltlevel);
-               Py_DECREF(pltlevel);
+               pltdata = PyDict_New();
+               if (!pltdata)
+                       PLy_elog(ERROR, "could not create new dictionary while building trigger arguments");
+
+               pltname = PyString_FromString(tdata->tg_trigger->tgname);
+               PyDict_SetItemString(pltdata, "name", pltname);
+               Py_DECREF(pltname);
+
+               stroid = DatumGetCString(DirectFunctionCall1(oidout,
+                                                          ObjectIdGetDatum(tdata->tg_relation->rd_id)));
+               pltrelid = PyString_FromString(stroid);
+               PyDict_SetItemString(pltdata, "relid", pltrelid);
+               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))
+                       pltwhen = PyString_FromString("AFTER");
+               else
+               {
+                       elog(ERROR, "unrecognized WHEN tg_event: %u", tdata->tg_event);
+                       pltwhen = NULL;         /* keep compiler quiet */
+               }
+               PyDict_SetItemString(pltdata, "when", pltwhen);
+               Py_DECREF(pltwhen);
 
-               if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
+               if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
                {
-                       pltevent = PyString_FromString("INSERT");
+                       pltlevel = PyString_FromString("ROW");
+                       PyDict_SetItemString(pltdata, "level", pltlevel);
+                       Py_DECREF(pltlevel);
 
-                       PyDict_SetItemString(pltdata, "old", Py_None);
-                       pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
-                                                                          tdata->tg_relation->rd_att);
-                       PyDict_SetItemString(pltdata, "new", pytnew);
-                       Py_DECREF(pytnew);
-                       *rv = tdata->tg_trigtuple;
+                       if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
+                       {
+                               pltevent = PyString_FromString("INSERT");
+
+                               PyDict_SetItemString(pltdata, "old", Py_None);
+                               pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
+                                                                                  tdata->tg_relation->rd_att);
+                               PyDict_SetItemString(pltdata, "new", pytnew);
+                               Py_DECREF(pytnew);
+                               *rv = tdata->tg_trigtuple;
+                       }
+                       else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
+                       {
+                               pltevent = PyString_FromString("DELETE");
+
+                               PyDict_SetItemString(pltdata, "new", Py_None);
+                               pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
+                                                                                  tdata->tg_relation->rd_att);
+                               PyDict_SetItemString(pltdata, "old", pytold);
+                               Py_DECREF(pytold);
+                               *rv = tdata->tg_trigtuple;
+                       }
+                       else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
+                       {
+                               pltevent = PyString_FromString("UPDATE");
+
+                               pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newtuple,
+                                                                                  tdata->tg_relation->rd_att);
+                               PyDict_SetItemString(pltdata, "new", pytnew);
+                               Py_DECREF(pytnew);
+                               pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
+                                                                                  tdata->tg_relation->rd_att);
+                               PyDict_SetItemString(pltdata, "old", pytold);
+                               Py_DECREF(pytold);
+                               *rv = tdata->tg_newtuple;
+                       }
+                       else
+                       {
+                               elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event);
+                               pltevent = NULL;        /* keep compiler quiet */
+                       }
+
+                       PyDict_SetItemString(pltdata, "event", pltevent);
+                       Py_DECREF(pltevent);
                }
-               else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
+               else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event))
                {
-                       pltevent = PyString_FromString("DELETE");
+                       pltlevel = PyString_FromString("STATEMENT");
+                       PyDict_SetItemString(pltdata, "level", pltlevel);
+                       Py_DECREF(pltlevel);
 
+                       PyDict_SetItemString(pltdata, "old", Py_None);
                        PyDict_SetItemString(pltdata, "new", Py_None);
-                       pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
-                                                                          tdata->tg_relation->rd_att);
-                       PyDict_SetItemString(pltdata, "old", pytold);
-                       Py_DECREF(pytold);
-                       *rv = tdata->tg_trigtuple;
-               }
-               else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
-               {
-                       pltevent = PyString_FromString("UPDATE");
-
-                       pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newtuple,
-                                                                          tdata->tg_relation->rd_att);
-                       PyDict_SetItemString(pltdata, "new", pytnew);
-                       Py_DECREF(pytnew);
-                       pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
-                                                                          tdata->tg_relation->rd_att);
-                       PyDict_SetItemString(pltdata, "old", pytold);
-                       Py_DECREF(pytold);
-                       *rv = tdata->tg_newtuple;
+                       *rv = NULL;
+
+                       if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
+                               pltevent = PyString_FromString("INSERT");
+                       else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
+                               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);
+                               pltevent = NULL;        /* keep compiler quiet */
+                       }
+
+                       PyDict_SetItemString(pltdata, "event", pltevent);
+                       Py_DECREF(pltevent);
                }
                else
-               {
-                       elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event);
-                       pltevent = NULL;        /* keep compiler quiet */
-               }
+                       elog(ERROR, "unrecognized LEVEL tg_event: %u", tdata->tg_event);
 
-               PyDict_SetItemString(pltdata, "event", pltevent);
-               Py_DECREF(pltevent);
-       }
-       else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event))
-       {
-               pltlevel = PyString_FromString("STATEMENT");
-               PyDict_SetItemString(pltdata, "level", pltlevel);
-               Py_DECREF(pltlevel);
-
-               PyDict_SetItemString(pltdata, "old", Py_None);
-               PyDict_SetItemString(pltdata, "new", Py_None);
-               *rv = NULL;
-
-               if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
-                       pltevent = PyString_FromString("INSERT");
-               else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
-                       pltevent = PyString_FromString("DELETE");
-               else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
-                       pltevent = PyString_FromString("UPDATE");
-               else
+               if (tdata->tg_trigger->tgnargs)
                {
-                       elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event);
-                       pltevent = NULL;        /* keep compiler quiet */
-               }
-
-               PyDict_SetItemString(pltdata, "event", pltevent);
-               Py_DECREF(pltevent);
-       }
-       else
-               elog(ERROR, "unrecognized LEVEL tg_event: %u", tdata->tg_event);
+                       /*
+                        * all strings...
+                        */
+                       int                     i;
+                       PyObject   *pltarg;
 
-       if (tdata->tg_trigger->tgnargs)
-       {
-               /*
-                * all strings...
-                */
-               int                     i;
-               PyObject   *pltarg;
+                       pltargs = PyList_New(tdata->tg_trigger->tgnargs);
+                       for (i = 0; i < tdata->tg_trigger->tgnargs; i++)
+                       {
+                               pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]);
 
-               pltargs = PyList_New(tdata->tg_trigger->tgnargs);
-               for (i = 0; i < tdata->tg_trigger->tgnargs; i++)
+                               /*
+                                * stolen, don't Py_DECREF
+                                */
+                               PyList_SetItem(pltargs, i, pltarg);
+                       }
+               }
+               else
                {
-                       pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]);
-
-                       /*
-                        * stolen, don't Py_DECREF
-                        */
-                       PyList_SetItem(pltargs, i, pltarg);
+                       Py_INCREF(Py_None);
+                       pltargs = Py_None;
                }
+               PyDict_SetItemString(pltdata, "args", pltargs);
+               Py_DECREF(pltargs);
        }
-       else
+       PG_CATCH();
        {
-               Py_INCREF(Py_None);
-               pltargs = Py_None;
+               Py_XDECREF(pltdata);
+               PG_RE_THROW();
        }
-       PyDict_SetItemString(pltdata, "args", pltargs);
-       Py_DECREF(pltargs);
-
-       RESTORE_EXC();
+       PG_END_TRY();
 
        return pltdata;
 }
 
 
 
-/* function handler and friends
- */
-Datum
+/* function handler and friends */
+static Datum
 PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
 {
-       DECLARE_EXC();
        Datum           rv;
        PyObject   *volatile plargs = NULL;
        PyObject   *volatile plrv = NULL;
        PyObject   *volatile plrv_so = NULL;
        char       *plrv_sc;
 
-       enter();
-
-       /*
-        * setup to catch elog in while building function arguments, and
-        * DECREF the plargs if the function call fails
-        */
-       SAVE_EXC();
-       if (TRAP_EXC())
+       PG_TRY();
        {
-               RESTORE_EXC();
+               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)
 
-               Py_XDECREF(plargs);
-               Py_XDECREF(plrv);
-               Py_XDECREF(plrv_so);
+                               /*
+                                * SETOF function parameters will be deleted when last row is
+                                * returned
+                                */
+                               PLy_function_delete_args(proc);
+                       Assert(plrv != NULL);
+                       Assert(!PLy_error_in_progress);
+               }
 
-               RERAISE_EXC();
-       }
+               /*
+                * Disconnect from SPI manager and then create the return values datum
+                * (if the input function does a palloc for it this must not be
+                * allocated in the SPI memory context because SPI_finish would free
+                * it).
+                */
+               if (SPI_finish() != SPI_OK_FINISH)
+                       elog(ERROR, "SPI_finish failed");
 
-       plargs = PLy_function_build_args(fcinfo, proc);
-       plrv = PLy_procedure_call(proc, "args", plargs);
+               if (proc->is_setof)
+               {
+                       bool            has_error = false;
+                       ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
 
-       /*
-        * Disconnect from SPI manager and then create the return values datum
-        * (if the input function does a palloc for it this must not be
-        * allocated in the SPI memory context because SPI_finish would free
-        * it).
-        */
-       if (SPI_finish() != SPI_OK_FINISH)
-               elog(ERROR, "SPI_finish failed");
+                       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.")));
+                       }
 
-       if (plrv == NULL)
-       {
-               elog(FATAL, "PLy_procedure_call returned NULL");
-#ifdef NOT_USED
-               if (!PLy_restart_in_progress)
-                       PLy_elog(ERROR, "function \"%s\" failed", proc->proname);
+                       /* 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;
+                       }
+               }
 
                /*
-                * FIXME is this dead code?  i'm pretty sure it is for unnested
-                * calls, but not for nested calls
+                * 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).
                 */
-               RAISE_EXC(1);
-#endif
-       }
+               if (proc->result.out.d.typoid == VOIDOID)
+               {
+                       if (plrv != Py_None)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                          errmsg("PL/Python function with return type \"void\" did not return None")));
 
-       /*
-        * convert the python PyObject to a postgresql Datum FIXME returning a
-        * NULL, ie PG_RETURN_NULL() blows the backend to small messy bits...
-        * it this a bug or expected?  so just call with the string value of
-        * None for now
-        */
+                       fcinfo->isnull = false;
+                       rv = (Datum) 0;
+               }
+               else if (plrv == Py_None)
+               {
+                       fcinfo->isnull = true;
+                       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 (plrv == Py_None)
-       {
-               fcinfo->isnull = true;
-               rv = (Datum) NULL;
+                       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, "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,
+                                                                  proc->result.out.d.typioparam,
+                                                                  -1);
+               }
        }
-       else
+       PG_CATCH();
        {
-               fcinfo->isnull = false;
-               plrv_so = PyObject_Str(plrv);
-               plrv_sc = PyString_AsString(plrv_so);
-               rv = FunctionCall3(&proc->result.out.d.typfunc,
-                                                  PointerGetDatum(plrv_sc),
-                                                  ObjectIdGetDatum(proc->result.out.d.typelem),
-                                                  Int32GetDatum(-1));
-       }
+               Py_XDECREF(plargs);
+               Py_XDECREF(plrv);
+               Py_XDECREF(plrv_so);
 
-       RESTORE_EXC();
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
 
        Py_XDECREF(plargs);
        Py_DECREF(plrv);
@@ -864,131 +963,141 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
        return rv;
 }
 
-PyObject *
+static PyObject *
 PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs)
 {
        PyObject   *rv;
-       PLyProcedure *current;
 
-       enter();
-
-       current = PLy_last_procedure;
-       PLy_last_procedure = proc;
        PyDict_SetItemString(proc->globals, kargs, vargs);
-       rv = PyEval_EvalCode((PyCodeObject *) proc->code, proc->globals, proc->globals);
-       PLy_last_procedure = current;
+       rv = PyEval_EvalCode((PyCodeObject *) proc->code,
+                                                proc->globals, proc->globals);
 
-       if ((rv == NULL) || (PyErr_Occurred()))
+       /*
+        * If there was an error in a PG callback, propagate that no matter what
+        * Python claims about its success.
+        */
+       if (PLy_error_in_progress)
+       {
+               ErrorData  *edata = PLy_error_in_progress;
+
+               PLy_error_in_progress = NULL;
+               ReThrowError(edata);
+       }
+
+       if (rv == NULL || PyErr_Occurred())
        {
                Py_XDECREF(rv);
-               if (!PLy_restart_in_progress)
-                       PLy_elog(ERROR, "function \"%s\" failed", proc->proname);
-               RAISE_EXC(1);
+               PLy_elog(ERROR, "PL/Python function \"%s\" failed", proc->proname);
        }
 
        return rv;
 }
 
-PyObject *
-PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
-{
-       DECLARE_EXC();
-       PyObject   *volatile arg = NULL;
-       PyObject   *volatile args = NULL;
-       int                     i;
-
-       enter();
-
-       /*
-        * FIXME -- if the setjmp setup is expensive, add the arg and args
-        * field to the procedure struct and cleanup at the start of the next
-        * call
-        */
-       SAVE_EXC();
-       if (TRAP_EXC())
-       {
-               RESTORE_EXC();
-               Py_XDECREF(arg);
-               Py_XDECREF(args);
-
-               RERAISE_EXC();
-       }
+static PyObject *
+PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
+{
+       PyObject   *volatile arg = NULL;
+       PyObject   *volatile args = NULL;
+       int                     i;
 
-       args = PyList_New(proc->nargs);
-       for (i = 0; i < proc->nargs; i++)
+       PG_TRY();
        {
-               if (proc->args[i].is_rowtype > 0)
+               args = PyList_New(proc->nargs);
+               for (i = 0; i < proc->nargs; i++)
                {
-                       if (fcinfo->argnull[i])
-                               arg = NULL;
-                       else
+                       if (proc->args[i].is_rowtype > 0)
                        {
-                               HeapTupleHeader td;
-                               Oid                     tupType;
-                               int32           tupTypmod;
-                               TupleDesc       tupdesc;
-                               HeapTupleData tmptup;
-
-                               td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
-                               /* Extract rowtype info and find a tupdesc */
-                               tupType = HeapTupleHeaderGetTypeId(td);
-                               tupTypmod = HeapTupleHeaderGetTypMod(td);
-                               tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
-
-                               /* Set up I/O funcs if not done yet */
-                               if (proc->args[i].is_rowtype != 1)
-                                       PLy_input_tuple_funcs(&(proc->args[i]), tupdesc);
-
-                               /* Build a temporary HeapTuple control structure */
-                               tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
-                               tmptup.t_data = td;
-
-                               arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc);
+                               if (fcinfo->argnull[i])
+                                       arg = NULL;
+                               else
+                               {
+                                       HeapTupleHeader td;
+                                       Oid                     tupType;
+                                       int32           tupTypmod;
+                                       TupleDesc       tupdesc;
+                                       HeapTupleData tmptup;
+
+                                       td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
+                                       /* Extract rowtype info and find a tupdesc */
+                                       tupType = HeapTupleHeaderGetTypeId(td);
+                                       tupTypmod = HeapTupleHeaderGetTypMod(td);
+                                       tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+                                       /* Set up I/O funcs if not done yet */
+                                       if (proc->args[i].is_rowtype != 1)
+                                               PLy_input_tuple_funcs(&(proc->args[i]), tupdesc);
+
+                                       /* Build a temporary HeapTuple control structure */
+                                       tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+                                       tmptup.t_data = td;
+
+                                       arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc);
+                                       ReleaseTupleDesc(tupdesc);
+                               }
                        }
-               }
-               else
-               {
-                       if (fcinfo->argnull[i])
-                               arg = NULL;
                        else
                        {
-                               char       *ct;
-                               Datum           dt;
-
-                               dt = FunctionCall3(&(proc->args[i].in.d.typfunc),
-                                                                  fcinfo->arg[i],
-                                                       ObjectIdGetDatum(proc->args[i].in.d.typelem),
-                                                                  Int32GetDatum(-1));
-                               ct = DatumGetCString(dt);
-                               arg = (proc->args[i].in.d.func) (ct);
-                               pfree(ct);
+                               if (fcinfo->argnull[i])
+                                       arg = NULL;
+                               else
+                               {
+                                       char       *ct;
+
+                                       ct = OutputFunctionCall(&(proc->args[i].in.d.typfunc),
+                                                                                       fcinfo->arg[i]);
+                                       arg = (proc->args[i].in.d.func) (ct);
+                                       pfree(ct);
+                               }
+                       }
+
+                       if (arg == NULL)
+                       {
+                               Py_INCREF(Py_None);
+                               arg = Py_None;
                        }
-               }
 
-               if (arg == NULL)
-               {
-                       Py_INCREF(Py_None);
-                       arg = Py_None;
-               }
+                       if (PyList_SetItem(args, i, arg) == -1)
+                               PLy_elog(ERROR, "PyList_SetItem() failed for PL/Python function \"%s\" while setting up arguments", proc->proname);
 
-               /*
-                * FIXME -- error check this
-                */
-               PyList_SetItem(args, i, arg);
+                       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;
+               }
        }
+       PG_CATCH();
+       {
+               Py_XDECREF(arg);
+               Py_XDECREF(args);
 
-       RESTORE_EXC();
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
 
        return args;
 }
 
 
+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
  */
 
 /* PLy_procedure_get: returns a cached PLyProcedure, or creates, stores and
- * returns a new PLyProcedure.  fcinfo is the call info, tgreloid is the
+ * returns a new PLyProcedure. fcinfo is the call info, tgreloid is the
  * relation OID when calling a trigger, or InvalidOid (zero) for ordinary
  * function calls.
  */
@@ -1002,8 +1111,6 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
        PLyProcedure *proc = NULL;
        int                     rv;
 
-       enter();
-
        fn_oid = fcinfo->flinfo->fn_oid;
        procTup = SearchSysCache(PROCOID,
                                                         ObjectIdGetDatum(fn_oid),
@@ -1012,7 +1119,7 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
                elog(ERROR, "cache lookup failed for function %u", fn_oid);
 
        rv = snprintf(key, sizeof(key), "%u_%u", fn_oid, tgreloid);
-       if ((rv >= sizeof(key)) || (rv < 0))
+       if (rv >= sizeof(key) || rv < 0)
                elog(ERROR, "key too long");
 
        plproc = PyDict_GetItemString(PLy_procedure_cache, key);
@@ -1023,14 +1130,12 @@ PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid)
                if (!PyCObject_Check(plproc))
                        elog(FATAL, "expected a PyCObject, didn't get one");
 
-               mark();
-
                proc = PyCObject_AsVoidPtr(plproc);
                if (proc->me != plproc)
                        elog(FATAL, "proc->me != plproc");
                /* did we find an up-to-date cache entry? */
                if (proc->fn_xmin != HeapTupleHeaderGetXmin(procTup->t_data) ||
-                       proc->fn_cmin != HeapTupleHeaderGetCmin(procTup->t_data))
+                       !ItemPointerEquals(&proc->fn_tid, &procTup->t_self))
                {
                        Py_DECREF(plproc);
                        proc = NULL;
@@ -1038,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);
 
@@ -1046,12 +1168,9 @@ 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];
-
-       DECLARE_EXC();
        Form_pg_proc procStruct;
        PLyProcedure *volatile proc;
        char       *volatile procSource = NULL;
@@ -1060,150 +1179,212 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
        int                     i,
                                rv;
 
-       enter();
-
        procStruct = (Form_pg_proc) GETSTRUCT(procTup);
 
        if (OidIsValid(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);
-       if ((rv >= sizeof(procName)) || (rv < 0))
+                                         HeapTupleGetOid(procTup));
+       if (rv >= sizeof(procName) || rv < 0)
                elog(ERROR, "procedure name would overrun buffer");
 
        proc = PLy_malloc(sizeof(PLyProcedure));
-       proc->proname = PLy_malloc(strlen(NameStr(procStruct->proname)) + 1);
-       strcpy(proc->proname, NameStr(procStruct->proname));
-       proc->pyname = PLy_malloc(strlen(procName) + 1);
-       strcpy(proc->pyname, procName);
+       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);
        PLy_typeinfo_init(&proc->result);
        for (i = 0; i < FUNC_MAX_ARGS; i++)
                PLy_typeinfo_init(&proc->args[i]);
        proc->nargs = 0;
        proc->code = proc->statics = NULL;
        proc->globals = proc->me = NULL;
+       proc->is_setof = procStruct->proretset;
+       proc->setof = NULL;
+       proc->argnames = NULL;
 
-       SAVE_EXC();
-       if (TRAP_EXC())
-       {
-               RESTORE_EXC();
-               PLy_procedure_delete(proc);
-               if (procSource)
-                       pfree(procSource);
-               RERAISE_EXC();
-       }
-
-       /*
-        * get information required for output conversion of the return value,
-        * but only if this isn't a trigger.
-        */
-       if (!CALLED_AS_TRIGGER(fcinfo))
+       PG_TRY();
        {
-               HeapTuple       rvTypeTup;
-               Form_pg_type rvTypeStruct;
+               /*
+                * get information required for output conversion of the return value,
+                * but only if this isn't a trigger.
+                */
+               if (!OidIsValid(tgreloid))
+               {
+                       HeapTuple       rvTypeTup;
+                       Form_pg_type rvTypeStruct;
+
+                       rvTypeTup = SearchSysCache(TYPEOID,
+                                                                       ObjectIdGetDatum(procStruct->prorettype),
+                                                                          0, 0, 0);
+                       if (!HeapTupleIsValid(rvTypeTup))
+                               elog(ERROR, "cache lookup failed for type %u",
+                                        procStruct->prorettype);
+                       rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
+
+                       /* Disallow pseudotype result, except for void */
+                       if (rvTypeStruct->typtype == TYPTYPE_PSEUDO &&
+                               procStruct->prorettype != VOIDOID)
+                       {
+                               if (procStruct->prorettype == TRIGGEROID)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                        errmsg("trigger functions can only be called as triggers")));
+                               else
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                  errmsg("PL/Python functions cannot return type %s",
+                                                                 format_type_be(procStruct->prorettype))));
+                       }
 
-               rvTypeTup = SearchSysCache(TYPEOID,
-                                                               ObjectIdGetDatum(procStruct->prorettype),
-                                                                  0, 0, 0);
-               if (!HeapTupleIsValid(rvTypeTup))
-                       elog(ERROR, "cache lookup failed for type %u",
-                                procStruct->prorettype);
+                       if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE)
+                       {
+                               /*
+                                * Tuple: set up later, during first call to
+                                * PLy_function_handler
+                                */
+                               proc->result.out.d.typoid = procStruct->prorettype;
+                               proc->result.is_rowtype = 2;
+                       }
+                       else
+                               PLy_output_datum_func(&proc->result, rvTypeTup);
 
-               rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
-               if (rvTypeStruct->typtype != 'c')
-                       PLy_output_datum_func(&proc->result, rvTypeStruct);
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("tuple return types are not supported yet")));
+                       ReleaseSysCache(rvTypeTup);
+               }
 
-               ReleaseSysCache(rvTypeTup);
-       }
-       else
-       {
                /*
-                * input/output conversion for trigger tuples.  use the result
-                * TypeInfo variable to store the tuple conversion info.
+                * 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.
                 */
-               TriggerData *tdata = (TriggerData *) fcinfo->context;
+               if (procStruct->pronargs)
+               {
+                       Oid             *types;
+                       char   **names,
+                                       *modes;
+                       int              i,
+                                        pos,
+                                        total;
+
+                       /* extract argument type info from the pg_proc tuple */
+                       total = get_func_arg_info(procTup, &types, &names, &modes);
+
+                       /* count number of in+inout args into proc->nargs */
+                       if (modes == NULL)
+                               proc->nargs = total;
+                       else
+                       {
+                               /* proc->nargs was initialized to 0 above */
+                               for (i = 0; i < total; i++)
+                               {
+                                       if (modes[i] != PROARGMODE_OUT &&
+                                               modes[i] != PROARGMODE_TABLE)
+                                               (proc->nargs)++;
+                               }
+                       }
 
-               PLy_input_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);
-               PLy_output_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);
-       }
+                       proc->argnames = (char **) PLy_malloc0(sizeof(char *) * proc->nargs);
+                       for (i = pos = 0; i < total; i++)
+                       {
+                               HeapTuple       argTypeTup;
+                               Form_pg_type argTypeStruct;
 
-       /*
-        * now get information required for input conversion of the procedures
-        * arguments.
-        */
-       proc->nargs = fcinfo->nargs;
-       for (i = 0; i < fcinfo->nargs; i++)
-       {
-               HeapTuple       argTypeTup;
-               Form_pg_type argTypeStruct;
+                               if (modes &&
+                                       (modes[i] == PROARGMODE_OUT ||
+                                        modes[i] == PROARGMODE_TABLE))
+                                       continue;       /* skip OUT arguments */
 
-               argTypeTup = SearchSysCache(TYPEOID,
-                                                       ObjectIdGetDatum(procStruct->proargtypes[i]),
-                                                                       0, 0, 0);
-               if (!HeapTupleIsValid(argTypeTup))
-                       elog(ERROR, "cache lookup failed for type %u",
-                                procStruct->proargtypes[i]);
-               argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
+                               Assert(types[i] == procStruct->proargtypes.values[pos]);
 
-               if (argTypeStruct->typtype != 'c')
-                       PLy_input_datum_func(&(proc->args[i]),
-                                                                procStruct->proargtypes[i],
-                                                                argTypeStruct);
-               else
-                       proc->args[i].is_rowtype = 2; /* still need to set I/O funcs */
+                               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;
+                               }
 
+                               /* get argument name */
+                               proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL;
 
-       /*
-        * get the text of the function.
-        */
-       prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
-                                                                 Anum_pg_proc_prosrc, &isnull);
-       if (isnull)
-               elog(ERROR, "null prosrc");
-       procSource = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                        prosrcdatum));
+                               ReleaseSysCache(argTypeTup);
+
+                               pos++;
+                       }
+               }
+
+               /*
+                * get the text of the function.
+                */
+               prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
+                                                                         Anum_pg_proc_prosrc, &isnull);
+               if (isnull)
+                       elog(ERROR, "null prosrc");
+               procSource = TextDatumGetCString(prosrcdatum);
 
-       PLy_procedure_compile(proc, procSource);
+               PLy_procedure_compile(proc, procSource);
 
-       pfree(procSource);
+               pfree(procSource);
 
-       proc->me = PyCObject_FromVoidPtr(proc, NULL);
-       PyDict_SetItemString(PLy_procedure_cache, key, proc->me);
+               proc->me = PyCObject_FromVoidPtr(proc, NULL);
+               PyDict_SetItemString(PLy_procedure_cache, key, proc->me);
+       }
+       PG_CATCH();
+       {
+               PLy_procedure_delete(proc);
+               if (procSource)
+                       pfree(procSource);
 
-       RESTORE_EXC();
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
 
        return proc;
 }
 
-void
+static void
 PLy_procedure_compile(PLyProcedure * proc, const char *src)
 {
        PyObject   *crv = NULL;
        char       *msrc;
 
-       enter();
-
        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);
@@ -1215,7 +1396,7 @@ PLy_procedure_compile(PLyProcedure * proc, const char *src)
        crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL);
        free(msrc);
 
-       if ((crv != NULL) && (!PyErr_Occurred()))
+       if (crv != NULL && (!PyErr_Occurred()))
        {
                int                     clen;
                char            call[NAMEDATALEN + 256];
@@ -1226,19 +1407,19 @@ PLy_procedure_compile(PLyProcedure * proc, const char *src)
                 * compile a call to the function
                 */
                clen = snprintf(call, sizeof(call), "%s()", proc->pyname);
-               if ((clen < 0) || (clen >= sizeof(call)))
+               if (clen < 0 || clen >= sizeof(call))
                        elog(ERROR, "string would overflow buffer");
                proc->code = Py_CompileString(call, "<string>", Py_eval_input);
-               if ((proc->code != NULL) && (!PyErr_Occurred()))
+               if (proc->code != NULL && (!PyErr_Occurred()))
                        return;
        }
        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);
 }
 
-char *
+static char *
 PLy_procedure_munge_source(const char *name, const char *src)
 {
        char       *mrc,
@@ -1247,8 +1428,6 @@ PLy_procedure_munge_source(const char *name, const char *src)
        size_t          mlen,
                                plen;
 
-       enter();
-
        /*
         * room for function source and the def statement
         */
@@ -1263,10 +1442,14 @@ PLy_procedure_munge_source(const char *name, const char *src)
 
        while (*sp != '\0')
        {
-               if (*sp == '\n')
+               if (*sp == '\r' && *(sp + 1) == '\n')
+                       sp++;
+
+               if (*sp == '\n' || *sp == '\r')
                {
-                       *mp++ = *sp++;
+                       *mp++ = '\n';
                        *mp++ = '\t';
+                       sp++;
                }
                else
                        *mp++ = *sp++;
@@ -1281,13 +1464,11 @@ PLy_procedure_munge_source(const char *name, const char *src)
        return mrc;
 }
 
-void
+static void
 PLy_procedure_delete(PLyProcedure * proc)
 {
        int                     i;
 
-       enter();
-
        Py_XDECREF(proc->code);
        Py_XDECREF(proc->statics);
        Py_XDECREF(proc->globals);
@@ -1297,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)
@@ -1304,125 +1486,137 @@ PLy_procedure_delete(PLyProcedure * proc)
                        if (proc->args[i].out.r.atts)
                                PLy_free(proc->args[i].out.r.atts);
                }
-
-       leave();
+               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.
  */
-void
+static void
 PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 {
        int                     i;
 
-       enter();
-
        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 = 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++)
        {
                HeapTuple       typeTup;
-               Form_pg_type typeStruct;
 
                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),
+                                                                ObjectIdGetDatum(desc->attrs[i]->atttypid),
                                                                 0, 0, 0);
                if (!HeapTupleIsValid(typeTup))
                        elog(ERROR, "cache lookup failed for type %u",
                                 desc->attrs[i]->atttypid);
-               typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
                PLy_input_datum_func2(&(arg->in.r.atts[i]),
                                                          desc->attrs[i]->atttypid,
-                                                         typeStruct);
+                                                         typeTup);
 
                ReleaseSysCache(typeTup);
        }
 }
 
-void
+static void
 PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
 {
        int                     i;
 
-       enter();
-
        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 = 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++)
        {
                HeapTuple       typeTup;
-               Form_pg_type typeStruct;
 
                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),
+                                                                ObjectIdGetDatum(desc->attrs[i]->atttypid),
                                                                 0, 0, 0);
                if (!HeapTupleIsValid(typeTup))
                        elog(ERROR, "cache lookup failed for type %u",
                                 desc->attrs[i]->atttypid);
-               typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
-               PLy_output_datum_func2(&(arg->out.r.atts[i]), typeStruct);
+               PLy_output_datum_func2(&(arg->out.r.atts[i]), typeTup);
 
                ReleaseSysCache(typeTup);
        }
 }
 
-void
-PLy_output_datum_func(PLyTypeInfo * arg, Form_pg_type typeStruct)
+static void
+PLy_output_datum_func(PLyTypeInfo * arg, HeapTuple typeTup)
 {
-       enter();
-
        if (arg->is_rowtype > 0)
                elog(ERROR, "PLyTypeInfo struct is initialized for a Tuple");
        arg->is_rowtype = 0;
-       PLy_output_datum_func2(&(arg->out.d), typeStruct);
+       PLy_output_datum_func2(&(arg->out.d), typeTup);
 }
 
-void
-PLy_output_datum_func2(PLyObToDatum * arg, Form_pg_type typeStruct)
+static void
+PLy_output_datum_func2(PLyObToDatum * arg, HeapTuple typeTup)
 {
-       enter();
+       Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
        perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
-       arg->typelem = typeStruct->typelem;
+       arg->typoid = HeapTupleGetOid(typeTup);
+       arg->typioparam = getTypeIOParam(typeTup);
        arg->typbyval = typeStruct->typbyval;
 }
 
-void
-PLy_input_datum_func(PLyTypeInfo * arg, Oid typeOid, Form_pg_type typeStruct)
+static void
+PLy_input_datum_func(PLyTypeInfo * arg, Oid typeOid, HeapTuple typeTup)
 {
-       enter();
-
        if (arg->is_rowtype > 0)
                elog(ERROR, "PLyTypeInfo struct is initialized for Tuple");
        arg->is_rowtype = 0;
-       PLy_input_datum_func2(&(arg->in.d), typeOid, typeStruct);
+       PLy_input_datum_func2(&(arg->in.d), typeOid, typeTup);
 }
 
-void
-PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, Form_pg_type typeStruct)
+static void
+PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, HeapTuple typeTup)
 {
+       Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+
        /* Get the type's conversion information */
        perm_fmgr_info(typeStruct->typoutput, &arg->typfunc);
-       arg->typelem = typeStruct->typelem;
+       arg->typoid = HeapTupleGetOid(typeTup);
+       arg->typioparam = getTypeIOParam(typeTup);
        arg->typbyval = typeStruct->typbyval;
 
        /* Determine which kind of Python object we will convert to */
@@ -1449,7 +1643,7 @@ PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, Form_pg_type typeStruct)
        }
 }
 
-void
+static void
 PLy_typeinfo_init(PLyTypeInfo * arg)
 {
        arg->is_rowtype = -1;
@@ -1458,7 +1652,7 @@ PLy_typeinfo_init(PLyTypeInfo * arg)
        arg->out.r.atts = NULL;
 }
 
-void
+static void
 PLy_typeinfo_dealloc(PLyTypeInfo * arg)
 {
        if (arg->is_rowtype == 1)
@@ -1470,130 +1664,359 @@ PLy_typeinfo_dealloc(PLyTypeInfo * arg)
        }
 }
 
-/* assumes that a bool is always returned as a 't' or 'f'
- */
-PyObject *
+/* assumes that a bool is always returned as a 't' or 'f' */
+static PyObject *
 PLyBool_FromString(const char *src)
 {
-       enter();
-
+       /*
+        * 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);
 }
 
-PyObject *
+static PyObject *
 PLyFloat_FromString(const char *src)
 {
        double          v;
        char       *eptr;
 
-       enter();
-
        errno = 0;
        v = strtod(src, &eptr);
-       if ((*eptr != '\0') || (errno))
+       if (*eptr != '\0' || errno)
                return NULL;
        return PyFloat_FromDouble(v);
 }
 
-PyObject *
+static PyObject *
 PLyInt_FromString(const char *src)
 {
        long            v;
        char       *eptr;
 
-       enter();
-
        errno = 0;
        v = strtol(src, &eptr, 0);
-       if ((*eptr != '\0') || (errno))
+       if (*eptr != '\0' || errno)
                return NULL;
        return PyInt_FromLong(v);
 }
 
-PyObject *
+static PyObject *
 PLyLong_FromString(const char *src)
 {
        return PyLong_FromString((char *) src, NULL, 0);
 }
 
-PyObject *
+static PyObject *
 PLyString_FromString(const char *src)
 {
        return PyString_FromString(src);
 }
 
-PyObject *
+static PyObject *
 PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
 {
-       DECLARE_EXC();
        PyObject   *volatile dict;
        int                     i;
 
-       enter();
-
        if (info->is_rowtype != 1)
                elog(ERROR, "PLyTypeInfo structure describes a datum");
 
        dict = PyDict_New();
        if (dict == NULL)
-               PLy_elog(ERROR, "could not create tuple dictionary");
+               PLy_elog(ERROR, "could not create new dictionary");
 
-       SAVE_EXC();
-       if (TRAP_EXC())
+       PG_TRY();
        {
-               RESTORE_EXC();
-               Py_DECREF(dict);
+               for (i = 0; i < info->in.r.natts; i++)
+               {
+                       char       *key,
+                                          *vsrc;
+                       Datum           vattr;
+                       bool            is_null;
+                       PyObject   *value;
 
-               RERAISE_EXC();
-       }
+                       if (desc->attrs[i]->attisdropped)
+                               continue;
+
+                       key = NameStr(desc->attrs[i]->attname);
+                       vattr = heap_getattr(tuple, (i + 1), desc, &is_null);
+
+                       if (is_null || info->in.r.atts[i].func == NULL)
+                               PyDict_SetItemString(dict, key, Py_None);
+                       else
+                       {
+                               vsrc = OutputFunctionCall(&info->in.r.atts[i].typfunc,
+                                                                                 vattr);
 
-       for (i = 0; i < info->in.r.natts; i++)
+                               /*
+                                * no exceptions allowed
+                                */
+                               value = info->in.r.atts[i].func(vsrc);
+                               pfree(vsrc);
+                               PyDict_SetItemString(dict, key, value);
+                               Py_DECREF(value);
+                       }
+               }
+       }
+       PG_CATCH();
        {
-               char       *key,
-                                  *vsrc;
-               Datum           vattr,
-                                       vdat;
-               bool            is_null;
-               PyObject   *value;
+               Py_DECREF(dict);
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
 
-               if (desc->attrs[i]->attisdropped)
-                       continue;
+       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);
-               vattr = heap_getattr(tuple, (i + 1), desc, &is_null);
+               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;
 
-               if ((is_null) || (info->in.r.atts[i].func == NULL))
-                       PyDict_SetItemString(dict, key, Py_None);
-               else
+                               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();
                {
-                       vdat = FunctionCall3(&info->in.r.atts[i].typfunc,
-                                                                vattr,
-                                                       ObjectIdGetDatum(info->in.r.atts[i].typelem),
-                                                          Int32GetDatum(desc->attrs[i]->atttypmod));
-                       vsrc = DatumGetCString(vdat);
+                       Py_XDECREF(so);
+                       Py_XDECREF(value);
+                       PG_RE_THROW();
+               }
+               PG_END_TRY();
+       }
 
-                       /*
-                        * no exceptions allowed
-                        */
-                       value = info->in.r.atts[i].func(vsrc);
-                       pfree(vsrc);
-                       PyDict_SetItemString(dict, key, value);
-                       Py_DECREF(value);
+       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();
        }
 
-       RESTORE_EXC();
+       tuple = heap_form_tuple(desc, values, nulls);
+       ReleaseTupleDesc(desc);
+       pfree(values);
+       pfree(nulls);
 
-       return dict;
+       return tuple;
 }
 
-/* initialization, some python variables function declared here
- */
 
-/* interface to postgresql elog
- */
+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 */
 static PyObject *PLy_debug(PyObject *, PyObject *);
 static PyObject *PLy_log(PyObject *, PyObject *);
 static PyObject *PLy_info(PyObject *, PyObject *);
@@ -1602,8 +2025,7 @@ static PyObject *PLy_warning(PyObject *, PyObject *);
 static PyObject *PLy_error(PyObject *, PyObject *);
 static PyObject *PLy_fatal(PyObject *, PyObject *);
 
-/* PLyPlanObject, PLyResultObject and SPI interface
- */
+/* PLyPlanObject, PLyResultObject and SPI interface */
 #define is_PLyPlanObject(x) ((x)->ob_type == &PLy_PlanType)
 static PyObject *PLy_plan_new(void);
 static void PLy_plan_dealloc(PyObject *);
@@ -1613,25 +2035,19 @@ static PyObject *PLy_plan_status(PyObject *, PyObject *);
 static PyObject *PLy_result_new(void);
 static void PLy_result_dealloc(PyObject *);
 static PyObject *PLy_result_getattr(PyObject *, char *);
-
-#ifdef NOT_USED
-/* Appear to be unused */
-static PyObject *PLy_result_fetch(PyObject *, PyObject *);
 static PyObject *PLy_result_nrows(PyObject *, PyObject *);
 static PyObject *PLy_result_status(PyObject *, PyObject *);
-#endif
-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 *);
 static PyObject *PLy_spi_execute(PyObject *, PyObject *);
-static const char *PLy_spi_error_string(int);
-static PyObject *PLy_spi_execute_query(char *query, int limit);
-static PyObject *PLy_spi_execute_plan(PyObject *, PyObject *, int);
+static PyObject *PLy_spi_execute_query(char *query, long limit);
+static PyObject *PLy_spi_execute_plan(PyObject *, PyObject *, long);
 static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *, int, int);
 
 
@@ -1645,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 */
@@ -1660,24 +2076,23 @@ 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 */
 };
 
 static PyMethodDef PLy_plan_methods[] = {
-       {"status", (PyCFunction) PLy_plan_status, METH_VARARGS, NULL},
+       {"status", PLy_plan_status, METH_VARARGS, NULL},
        {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 = {
@@ -1690,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 */
@@ -1705,19 +2120,15 @@ 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 */
 };
 
-#ifdef NOT_USED
-/* Appear to be unused */
 static PyMethodDef PLy_result_methods[] = {
-       {"fetch", (PyCFunction) PLy_result_fetch, METH_VARARGS, NULL,},
-       {"nrows", (PyCFunction) PLy_result_nrows, METH_VARARGS, NULL},
-       {"status", (PyCFunction) PLy_result_status, METH_VARARGS, NULL},
+       {"nrows", PLy_result_nrows, METH_VARARGS, NULL},
+       {"status", PLy_result_status, METH_VARARGS, NULL},
        {NULL, NULL, 0, NULL}
 };
-#endif
 
 static PyMethodDef PLy_methods[] = {
        /*
@@ -1745,15 +2156,12 @@ static PyMethodDef PLy_methods[] = {
 };
 
 
-/* plan object methods
- */
-PyObject *
+/* plan object methods */
+static PyObject *
 PLy_plan_new(void)
 {
        PLyPlanObject *ob;
 
-       enter();
-
        if ((ob = PyObject_NEW(PLyPlanObject, &PLy_PlanType)) == NULL)
                return NULL;
 
@@ -1766,13 +2174,11 @@ PLy_plan_new(void)
 }
 
 
-void
+static void
 PLy_plan_dealloc(PyObject * arg)
 {
        PLyPlanObject *ob = (PLyPlanObject *) arg;
 
-       enter();
-
        if (ob->plan)
                SPI_freeplan(ob->plan);
        if (ob->types)
@@ -1786,19 +2192,17 @@ PLy_plan_dealloc(PyObject * arg)
                PLy_free(ob->args);
        }
 
-       PyMem_DEL(arg);
-
-       leave();
+       arg->ob_type->tp_free(arg);
 }
 
 
-PyObject *
+static PyObject *
 PLy_plan_getattr(PyObject * self, char *name)
 {
        return Py_FindMethod(PLy_plan_methods, self, name);
 }
 
-PyObject *
+static PyObject *
 PLy_plan_status(PyObject * self, PyObject * args)
 {
        if (PyArg_ParseTuple(args, ""))
@@ -1807,22 +2211,19 @@ 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;
 }
 
 
 
-/* result object methods
- */
+/* result object methods */
 
-PyObject *
+static PyObject *
 PLy_result_new(void)
 {
        PLyResultObject *ob;
 
-       enter();
-
        if ((ob = PyObject_NEW(PLyResultObject, &PLy_ResultType)) == NULL)
                return NULL;
 
@@ -1836,35 +2237,25 @@ PLy_result_new(void)
        return (PyObject *) ob;
 }
 
-void
+static void
 PLy_result_dealloc(PyObject * arg)
 {
        PLyResultObject *ob = (PLyResultObject *) arg;
 
-       enter();
-
        Py_XDECREF(ob->nrows);
        Py_XDECREF(ob->rows);
        Py_XDECREF(ob->status);
 
-       PyMem_DEL(ob);
+       arg->ob_type->tp_free(arg);
 }
 
-PyObject *
-PLy_result_getattr(PyObject * self, char *attr)
+static PyObject *
+PLy_result_getattr(PyObject * self, char *name)
 {
-       return NULL;
-}
-
-#ifdef NOT_USED
-/* Appear to be unused */
-PyObject *
-PLy_result_fetch(PyObject * self, PyObject * args)
-{
-       return NULL;
+       return Py_FindMethod(PLy_result_methods, self, name);
 }
 
-PyObject *
+static PyObject *
 PLy_result_nrows(PyObject * self, PyObject * args)
 {
        PLyResultObject *ob = (PLyResultObject *) self;
@@ -1873,7 +2264,7 @@ PLy_result_nrows(PyObject * self, PyObject * args)
        return ob->nrows;
 }
 
-PyObject *
+static PyObject *
 PLy_result_status(PyObject * self, PyObject * args)
 {
        PLyResultObject *ob = (PLyResultObject *) self;
@@ -1881,8 +2272,8 @@ PLy_result_status(PyObject * self, PyObject * args)
        Py_INCREF(ob->status);
        return ob->status;
 }
-#endif
-int
+
+static Py_ssize_t
 PLy_result_length(PyObject * arg)
 {
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1890,8 +2281,8 @@ PLy_result_length(PyObject * arg)
        return PyList_Size(ob->rows);
 }
 
-PyObject *
-PLy_result_item(PyObject * arg, int idx)
+static PyObject *
+PLy_result_item(PyObject * arg, Py_ssize_t idx)
 {
        PyObject   *rv;
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1902,8 +2293,8 @@ PLy_result_item(PyObject * arg, int idx)
        return rv;
 }
 
-int
-PLy_result_ass_item(PyObject * arg, int idx, PyObject * item)
+static int
+PLy_result_ass_item(PyObject * arg, Py_ssize_t idx, PyObject * item)
 {
        int                     rv;
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1913,8 +2304,8 @@ PLy_result_ass_item(PyObject * arg, int idx, PyObject * item)
        return rv;
 }
 
-PyObject *
-PLy_result_slice(PyObject * arg, int lidx, int hidx)
+static PyObject *
+PLy_result_slice(PyObject * arg, Py_ssize_t lidx, Py_ssize_t hidx)
 {
        PyObject   *rv;
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1926,8 +2317,8 @@ PLy_result_slice(PyObject * arg, int lidx, int hidx)
        return rv;
 }
 
-int
-PLy_result_ass_slice(PyObject * arg, int lidx, int hidx, PyObject * slice)
+static int
+PLy_result_ass_slice(PyObject * arg, Py_ssize_t lidx, Py_ssize_t hidx, PyObject * slice)
 {
        int                     rv;
        PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1936,132 +2327,141 @@ PLy_result_ass_slice(PyObject * arg, int lidx, int hidx, PyObject * slice)
        return rv;
 }
 
-/* SPI interface
- */
-PyObject *
+/* SPI interface */
+static PyObject *
 PLy_spi_prepare(PyObject * self, PyObject * args)
 {
-       DECLARE_EXC();
        PLyPlanObject *plan;
        PyObject   *list = NULL;
        PyObject   *volatile optr = NULL;
        char       *query;
        void       *tmpplan;
+       MemoryContext oldcontext;
 
-       enter();
+       /* Can't execute more if we have an unhandled error */
+       if (PLy_error_in_progress)
+       {
+               PLy_exception_set(PLy_exc_error, "transaction aborted");
+               return NULL;
+       }
 
        if (!PyArg_ParseTuple(args, "s|O", &query, &list))
        {
-               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)))
+       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;
        }
 
-
        if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
                return NULL;
 
-       SAVE_EXC();
-       if (TRAP_EXC())
-       {
-               RESTORE_EXC();
-               Py_DECREF(plan);
-               Py_XDECREF(optr);
-               if (!PyErr_Occurred())
-                       PyErr_SetString(PLy_exc_spi_error,
-                                                       "Unknown error in PLy_spi_prepare");
-               /* XXX this oughta be replaced with errcontext mechanism */
-               PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
-               RERAISE_EXC();
-       }
-
-       if (list != NULL)
+       oldcontext = CurrentMemoryContext;
+       PG_TRY();
        {
-               int                     nargs,
-                                       i;
-
-               nargs = PySequence_Length(list);
-               if (nargs > 0)
+               if (list != NULL)
                {
-                       plan->nargs = nargs;
-                       plan->types = PLy_malloc(sizeof(Oid) * nargs);
-                       plan->values = PLy_malloc(sizeof(Datum) * nargs);
-                       plan->args = PLy_malloc(sizeof(PLyTypeInfo) * nargs);
+                       int                     nargs,
+                                               i;
 
-                       /*
-                        * the other loop might throw an exception, if PLyTypeInfo
-                        * member isn't properly initialized the Py_DECREF(plan) will
-                        * go boom
-                        */
-                       for (i = 0; i < nargs; i++)
-                       {
-                               PLy_typeinfo_init(&plan->args[i]);
-                               plan->values[i] = (Datum) NULL;
-                       }
-
-                       for (i = 0; i < nargs; i++)
+                       nargs = PySequence_Length(list);
+                       if (nargs > 0)
                        {
-                               char       *sptr;
-                               HeapTuple       typeTup;
-                               Form_pg_type typeStruct;
+                               plan->nargs = nargs;
+                               plan->types = PLy_malloc(sizeof(Oid) * nargs);
+                               plan->values = PLy_malloc(sizeof(Datum) * nargs);
+                               plan->args = PLy_malloc(sizeof(PLyTypeInfo) * nargs);
 
-                               optr = PySequence_GetItem(list, i);
-                               if (!PyString_Check(optr))
+                               /*
+                                * the other loop might throw an exception, if PLyTypeInfo
+                                * member isn't properly initialized the Py_DECREF(plan) will
+                                * go boom
+                                */
+                               for (i = 0; i < nargs; i++)
                                {
-                                       PyErr_SetString(PLy_exc_spi_error,
-                                                                       "Type names must be strings.");
-                                       RAISE_EXC(1);
+                                       PLy_typeinfo_init(&plan->args[i]);
+                                       plan->values[i] = PointerGetDatum(NULL);
                                }
-                               sptr = PyString_AsString(optr);
-                               /* XXX should extend this to allow qualified type names */
-                               typeTup = typenameType(makeTypeName(sptr));
-                               Py_DECREF(optr);
-                               optr = NULL;    /* this is important */
-
-                               plan->types[i] = HeapTupleGetOid(typeTup);
-                               typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
-                               if (typeStruct->typtype != 'c')
-                                       PLy_output_datum_func(&plan->args[i], typeStruct);
-                               else
+
+                               for (i = 0; i < nargs; i++)
                                {
-                                       PyErr_SetString(PLy_exc_spi_error,
-                                                        "tuples not handled in plpy.prepare, yet.");
-                                       RAISE_EXC(1);
+                                       char       *sptr;
+                                       HeapTuple       typeTup;
+                                       Oid                     typeId;
+                                       int32           typmod;
+                                       Form_pg_type typeStruct;
+
+                                       optr = PySequence_GetItem(list, i);
+                                       if (!PyString_Check(optr))
+                                               ereport(ERROR,
+                                                               (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
+                                       sptr = PyString_AsString(optr);
+
+                                       /********************************************************
+                                        * 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] = typeId;
+                                       typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+                                       if (typeStruct->typtype != TYPTYPE_COMPOSITE)
+                                               PLy_output_datum_func(&plan->args[i], typeTup);
+                                       else
+                                               ereport(ERROR,
+                                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                                errmsg("plpy.prepare does not support composite types")));
+                                       ReleaseSysCache(typeTup);
                                }
-                               ReleaseSysCache(typeTup);
                        }
                }
-       }
 
-       plan->plan = SPI_prepare(query, plan->nargs, plan->types);
-       if (plan->plan == NULL)
-       {
-               PLy_exception_set(PLy_exc_spi_error,
-                                        "Unable to prepare plan. SPI_prepare failed -- %s.",
-                                                 PLy_spi_error_string(SPI_result));
-               RAISE_EXC(1);
+               plan->plan = SPI_prepare(query, plan->nargs, plan->types);
+               if (plan->plan == NULL)
+                       elog(ERROR, "SPI_prepare failed: %s",
+                                SPI_result_code_string(SPI_result));
+
+               /* transfer plan from procCxt to topCxt */
+               tmpplan = plan->plan;
+               plan->plan = SPI_saveplan(tmpplan);
+               SPI_freeplan(tmpplan);
+               if (plan->plan == NULL)
+                       elog(ERROR, "SPI_saveplan failed: %s",
+                                SPI_result_code_string(SPI_result));
        }
-
-       /* transfer plan from procCxt to topCxt */
-       tmpplan = plan->plan;
-       plan->plan = SPI_saveplan(tmpplan);
-       SPI_freeplan(tmpplan);
-       if (plan->plan == NULL)
+       PG_CATCH();
        {
-               PLy_exception_set(PLy_exc_spi_error,
-                                          "Unable to save plan. SPI_saveplan failed -- %s.",
-                                                 PLy_spi_error_string(SPI_result));
-               RAISE_EXC(1);
+               MemoryContextSwitchTo(oldcontext);
+               PLy_error_in_progress = CopyErrorData();
+               FlushErrorState();
+               Py_DECREF(plan);
+               Py_XDECREF(optr);
+               if (!PyErr_Occurred())
+                       PLy_exception_set(PLy_exc_spi_error,
+                                                         "unrecognized error in PLy_spi_prepare");
+               /* XXX this oughta be replaced with errcontext mechanism */
+               PLy_elog(WARNING, "in PL/Python function \"%s\"",
+                                PLy_procedure_name(PLy_curr_procedure));
+               return NULL;
        }
-
-       RESTORE_EXC();
+       PG_END_TRY();
 
        return (PyObject *) plan;
 }
@@ -2069,62 +2469,48 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
 /* execute(query="select * from foo", limit=5)
  * execute(plan=plan, values=(foo, bar), limit=5)
  */
-PyObject *
+static PyObject *
 PLy_spi_execute(PyObject * self, PyObject * args)
 {
        char       *query;
        PyObject   *plan;
        PyObject   *list = NULL;
-       int                     limit = 0;
+       long            limit = 0;
 
-       enter();
-
-#ifdef NOT_USED
-
-       /*
-        * there should - hahaha - be an python exception set so just return
-        * NULL.  FIXME -- is this needed?
-        */
-       if (PLy_restart_in_progress)
+       /* Can't execute more if we have an unhandled error */
+       if (PLy_error_in_progress)
+       {
+               PLy_exception_set(PLy_exc_error, "transaction aborted");
                return NULL;
-#endif
+       }
 
-       if (PyArg_ParseTuple(args, "s|i", &query, &limit))
+       if (PyArg_ParseTuple(args, "s|l", &query, &limit))
                return PLy_spi_execute_query(query, limit);
 
        PyErr_Clear();
 
-       if ((PyArg_ParseTuple(args, "O|Oi", &plan, &list, &limit)) &&
-               (is_PLyPlanObject(plan)))
-       {
-               PyObject   *rv = PLy_spi_execute_plan(plan, list, limit);
-
-               return rv;
-       }
+       if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
+               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;
 }
 
-PyObject *
-PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
+static PyObject *
+PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit)
 {
-       DECLARE_EXC();
        volatile int nargs;
        int                     i,
                                rv;
        PLyPlanObject *plan;
-       char       *nulls;
-
-       enter();
+       MemoryContext oldcontext;
 
        if (list != NULL)
        {
-               if ((!PySequence_Check(list)) || (PyString_Check(list)))
+               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);
@@ -2137,260 +2523,251 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
        if (nargs != plan->nargs)
        {
                char       *sv;
-
                PyObject   *so = PyObject_Str(list);
 
+               if (!so)
+                       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;
        }
 
-       SAVE_EXC();
-       if (TRAP_EXC())
-       {
-               RESTORE_EXC();
-
-               /*
-                * cleanup plan->values array
-                */
-               for (i = 0; i < nargs; i++)
-               {
-                       if (!plan->args[i].out.d.typbyval &&
-                               (plan->values[i] != (Datum) NULL))
-                       {
-                               pfree(DatumGetPointer(plan->values[i]));
-                               plan->values[i] = (Datum) NULL;
-                       }
-               }
-
-               if (!PyErr_Occurred())
-                       PyErr_SetString(PLy_exc_error,
-                                                       "Unknown error in PLy_spi_execute_plan");
-               PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
-               RERAISE_EXC();
-       }
-
-       if (nargs)
+       oldcontext = CurrentMemoryContext;
+       PG_TRY();
        {
-               nulls = palloc((nargs + 1) * sizeof(char));
+               char       *nulls = palloc(nargs * sizeof(char));
+               volatile int j;
 
-               for (i = 0; i < nargs; i++)
+               for (j = 0; j < nargs; j++)
                {
                        PyObject   *elem,
                                           *so;
-                       char       *sv;
 
-                       elem = PySequence_GetItem(list, i);
+                       elem = PySequence_GetItem(list, j);
                        if (elem != Py_None)
                        {
                                so = PyObject_Str(elem);
-                               sv = PyString_AsString(so);
+                               if (!so)
+                                       PLy_elog(ERROR, "PL/Python function \"%s\" could not execute plan",
+                                                        PLy_procedure_name(PLy_curr_procedure));
+                               Py_DECREF(elem);
 
-                               /*
-                                * FIXME -- if this can elog, we have leak
-                                */
-                               plan->values[i] = FunctionCall3(&(plan->args[i].out.d.typfunc),
-                                                                                               CStringGetDatum(sv),
-                                                  ObjectIdGetDatum(plan->args[i].out.d.typelem),
-                                                                                               Int32GetDatum(-1));
+                               PG_TRY();
+                               {
+                                       char       *sv = PyString_AsString(so);
 
-                               Py_DECREF(so);
-                               Py_DECREF(elem);
+                                       plan->values[j] =
+                                               InputFunctionCall(&(plan->args[j].out.d.typfunc),
+                                                                                 sv,
+                                                                                 plan->args[j].out.d.typioparam,
+                                                                                 -1);
+                               }
+                               PG_CATCH();
+                               {
+                                       Py_DECREF(so);
+                                       PG_RE_THROW();
+                               }
+                               PG_END_TRY();
 
-                               nulls[i] = ' ';
+                               Py_DECREF(so);
+                               nulls[j] = ' ';
                        }
                        else
                        {
                                Py_DECREF(elem);
-                               plan->values[i] = (Datum) 0;
-                               nulls[i] = 'n';
+                               plan->values[j] =
+                                       InputFunctionCall(&(plan->args[j].out.d.typfunc),
+                                                                         NULL,
+                                                                         plan->args[j].out.d.typioparam,
+                                                                         -1);
+                               nulls[j] = 'n';
                        }
                }
-               nulls[i] = '\0';
+
+               rv = SPI_execute_plan(plan->plan, plan->values, nulls,
+                                                         PLy_curr_procedure->fn_readonly, limit);
+
+               pfree(nulls);
        }
-       else
-               nulls = NULL;
+       PG_CATCH();
+       {
+               int                     k;
+
+               MemoryContextSwitchTo(oldcontext);
+               PLy_error_in_progress = CopyErrorData();
+               FlushErrorState();
+
+               /*
+                * cleanup plan->values array
+                */
+               for (k = 0; k < nargs; k++)
+               {
+                       if (!plan->args[k].out.d.typbyval &&
+                               (plan->values[k] != PointerGetDatum(NULL)))
+                       {
+                               pfree(DatumGetPointer(plan->values[k]));
+                               plan->values[k] = PointerGetDatum(NULL);
+                       }
+               }
 
-       rv = SPI_execp(plan->plan, plan->values, nulls, limit);
-       RESTORE_EXC();
+               if (!PyErr_Occurred())
+                       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 PL/Python function \"%s\"",
+                                PLy_procedure_name(PLy_curr_procedure));
+               return NULL;
+       }
+       PG_END_TRY();
 
        for (i = 0; i < nargs; i++)
        {
                if (!plan->args[i].out.d.typbyval &&
-                       (plan->values[i] != (Datum) NULL))
+                       (plan->values[i] != PointerGetDatum(NULL)))
                {
                        pfree(DatumGetPointer(plan->values[i]));
-                       plan->values[i] = (Datum) NULL;
+                       plan->values[i] = PointerGetDatum(NULL);
                }
        }
 
        if (rv < 0)
        {
                PLy_exception_set(PLy_exc_spi_error,
-                                          "Unable to execute plan.  SPI_execp failed -- %s",
-                                                 PLy_spi_error_string(rv));
+                                                 "SPI_execute_plan failed: %s",
+                                                 SPI_result_code_string(rv));
                return NULL;
        }
 
        return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
 }
 
-PyObject *
-PLy_spi_execute_query(char *query, int limit)
+static PyObject *
+PLy_spi_execute_query(char *query, long limit)
 {
-       DECLARE_EXC();
        int                     rv;
+       MemoryContext oldcontext;
 
-       SAVE_EXC();
-       if (TRAP_EXC())
+       oldcontext = CurrentMemoryContext;
+       PG_TRY();
+       {
+               rv = SPI_execute(query, PLy_curr_procedure->fn_readonly, limit);
+       }
+       PG_CATCH();
        {
-               RESTORE_EXC();
-               if ((!PLy_restart_in_progress) && (!PyErr_Occurred()))
-                       PyErr_SetString(PLy_exc_spi_error,
-                                                       "Unknown error in PLy_spi_execute_query");
-               PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure));
-               RERAISE_EXC();
+               MemoryContextSwitchTo(oldcontext);
+               PLy_error_in_progress = CopyErrorData();
+               FlushErrorState();
+               if (!PyErr_Occurred())
+                       PLy_exception_set(PLy_exc_spi_error,
+                                                         "unrecognized error in PLy_spi_execute_query");
+               /* XXX this oughta be replaced with errcontext mechanism */
+               PLy_elog(WARNING, "in PL/Python function \"%s\"",
+                                PLy_procedure_name(PLy_curr_procedure));
+               return NULL;
        }
+       PG_END_TRY();
 
-       rv = SPI_exec(query, limit);
-       RESTORE_EXC();
        if (rv < 0)
        {
                PLy_exception_set(PLy_exc_spi_error,
-                                          "Unable to execute query.  SPI_exec failed -- %s",
-                                                 PLy_spi_error_string(rv));
+                                                 "SPI_execute failed: %s",
+                                                 SPI_result_code_string(rv));
                return NULL;
        }
 
        return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
 }
 
-PyObject *
+static PyObject *
 PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
 {
        PLyResultObject *result;
-
-       enter();
+       MemoryContext oldcontext;
 
        result = (PLyResultObject *) PLy_result_new();
        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)
        {
-               DECLARE_EXC();
                PLyTypeInfo args;
                int                     i;
 
-               PLy_typeinfo_init(&args);
                Py_DECREF(result->nrows);
                result->nrows = PyInt_FromLong(rows);
+               PLy_typeinfo_init(&args);
 
-               SAVE_EXC();
-               if (TRAP_EXC())
+               oldcontext = CurrentMemoryContext;
+               PG_TRY();
                {
-                       RESTORE_EXC();
-
-                       if (!PyErr_Occurred())
-                               PyErr_SetString(PLy_exc_error,
-                                               "Unknown error in PLy_spi_execute_fetch_result");
-                       Py_DECREF(result);
-                       PLy_typeinfo_dealloc(&args);
-                       RERAISE_EXC();
-               }
+                       if (rows)
+                       {
+                               Py_DECREF(result->rows);
+                               result->rows = PyList_New(rows);
 
-               if (rows)
-               {
-                       Py_DECREF(result->rows);
-                       result->rows = PyList_New(rows);
+                               PLy_input_tuple_funcs(&args, tuptable->tupdesc);
+                               for (i = 0; i < rows; i++)
+                               {
+                                       PyObject   *row = PLyDict_FromTuple(&args, tuptable->vals[i],
+                                                                                                               tuptable->tupdesc);
 
-                       PLy_input_tuple_funcs(&args, tuptable->tupdesc);
-                       for (i = 0; i < rows; i++)
-                       {
-                               PyObject   *row = PLyDict_FromTuple(&args, tuptable->vals[i],
-                                                                                                       tuptable->tupdesc);
+                                       PyList_SetItem(result->rows, i, row);
+                               }
+                               PLy_typeinfo_dealloc(&args);
 
-                               PyList_SetItem(result->rows, i, row);
+                               SPI_freetuptable(tuptable);
                        }
+               }
+               PG_CATCH();
+               {
+                       MemoryContextSwitchTo(oldcontext);
+                       PLy_error_in_progress = CopyErrorData();
+                       FlushErrorState();
+                       if (!PyErr_Occurred())
+                               PLy_exception_set(PLy_exc_error,
+                                                                 "unrecognized error in PLy_spi_execute_fetch_result");
+                       Py_DECREF(result);
                        PLy_typeinfo_dealloc(&args);
-
-                       SPI_freetuptable(tuptable);
+                       return NULL;
                }
-               RESTORE_EXC();
+               PG_END_TRY();
        }
 
        return (PyObject *) result;
 }
 
-const char *
-PLy_spi_error_string(int code)
-{
-       switch (code)
-       {
-               case SPI_ERROR_TYPUNKNOWN:
-                       return "SPI_ERROR_TYPUNKNOWN";
-               case SPI_ERROR_NOOUTFUNC:
-                       return "SPI_ERROR_NOOUTFUNC";
-               case SPI_ERROR_NOATTRIBUTE:
-                       return "SPI_ERROR_NOATTRIBUTE";
-               case SPI_ERROR_TRANSACTION:
-                       return "SPI_ERROR_TRANSACTION";
-               case SPI_ERROR_PARAM:
-                       return "SPI_ERROR_PARAM";
-               case SPI_ERROR_ARGUMENT:
-                       return "SPI_ERROR_ARGUMENT";
-               case SPI_ERROR_CURSOR:
-                       return "SPI_ERROR_CURSOR";
-               case SPI_ERROR_UNCONNECTED:
-                       return "SPI_ERROR_UNCONNECTED";
-               case SPI_ERROR_OPUNKNOWN:
-                       return "SPI_ERROR_OPUNKNOWN";
-               case SPI_ERROR_COPY:
-                       return "SPI_ERROR_COPY";
-               case SPI_ERROR_CONNECT:
-                       return "SPI_ERROR_CONNECT";
-       }
-       return "Unknown or Invalid code";
-}
 
-/* language handler and interpreter initialization
+/*
+ * language handler and interpreter initialization
  */
 
 /*
- * 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 int init_active = 0;
+       /* 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;
 
-       enter();
-
-       if (init_active)
-               elog(FATAL, "initialization of language module failed");
-       init_active = 1;
+       pg_bindtextdomain(TEXTDOMAIN);
 
        Py_Initialize();
        PLy_init_interp();
@@ -2401,45 +2778,27 @@ plpython_init(void)
        if (PLy_procedure_cache == NULL)
                PLy_elog(ERROR, "could not create procedure cache");
 
-       PLy_first_call = 0;
-
-       leave();
+       inited = true;
 }
 
 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
-        */
-
-}
-
-void
 PLy_init_interp(void)
 {
        PyObject   *mainmod;
 
-       enter();
-
        mainmod = PyImport_AddModule("__main__");
-       if ((mainmod == NULL) || (PyErr_Occurred()))
-               PLy_elog(ERROR, "could not import \"__main__\" module.");
+       if (mainmod == NULL || PyErr_Occurred())
+               PLy_elog(ERROR, "could not import \"__main__\" module");
        Py_INCREF(mainmod);
        PLy_interp_globals = PyModule_GetDict(mainmod);
        PLy_interp_safe_globals = PyDict_New();
        PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
        Py_DECREF(mainmod);
-       if ((PLy_interp_globals == NULL) || (PyErr_Occurred()))
+       if (PLy_interp_globals == NULL || PyErr_Occurred())
                PLy_elog(ERROR, "could not initialize globals");
 }
 
-void
+static void
 PLy_init_plpy(void)
 {
        PyObject   *main_mod,
@@ -2448,12 +2807,14 @@ PLy_init_plpy(void)
        PyObject   *plpy,
                           *plpy_dict;
 
-       enter();
-
        /*
         * 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);
 
@@ -2474,137 +2835,111 @@ 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 *);
 
-PyObject *
+static PyObject *
 PLy_debug(PyObject * self, PyObject * args)
 {
        return PLy_output(DEBUG2, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_log(PyObject * self, PyObject * args)
 {
        return PLy_output(LOG, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_info(PyObject * self, PyObject * args)
 {
        return PLy_output(INFO, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_notice(PyObject * self, PyObject * args)
 {
        return PLy_output(NOTICE, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_warning(PyObject * self, PyObject * args)
 {
        return PLy_output(WARNING, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_error(PyObject * self, PyObject * args)
 {
        return PLy_output(ERROR, self, args);
 }
 
-PyObject *
+static PyObject *
 PLy_fatal(PyObject * self, PyObject * args)
 {
        return PLy_output(FATAL, self, args);
 }
 
 
-PyObject *
+static PyObject *
 PLy_output(volatile int level, PyObject * self, PyObject * args)
 {
-       DECLARE_EXC();
        PyObject   *so;
        char       *volatile sv;
-
-       enter();
-
-       if (args == NULL)
-               elog(WARNING, "args is NULL");
+       MemoryContext oldcontext;
 
        so = PyObject_Str(args);
-       if ((so == NULL) || ((sv = PyString_AsString(so)) == NULL))
+       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");
        }
 
-       /*
-        * returning NULL here causes the python interpreter to bail. when
-        * control passes back into plpython_*_handler, we check for python
-        * exceptions and do the actual elog call.      actually PLy_elog.
-        */
-       if (level == ERROR)
-       {
-               PyErr_SetString(PLy_exc_error, sv);
-               return NULL;
-       }
-       else if (level >= FATAL)
+       oldcontext = CurrentMemoryContext;
+       PG_TRY();
        {
-               PyErr_SetString(PLy_exc_fatal, sv);
-               return NULL;
+               elog(level, "%s", sv);
        }
-
-       /*
-        * ok, this is a WARNING, or LOG message
-        *
-        * but just in case DON'T long jump out of the interpreter!
-        */
-       SAVE_EXC();
-       if (TRAP_EXC())
+       PG_CATCH();
        {
-               RESTORE_EXC();
-
+               MemoryContextSwitchTo(oldcontext);
+               PLy_error_in_progress = CopyErrorData();
+               FlushErrorState();
                Py_XDECREF(so);
 
                /*
-                * the real error message should already be written into the
-                * postgresql log, no?  whatever, this shouldn't happen so die
-                * hideously.
+                * returning NULL here causes the python interpreter to bail. when
+                * control passes back to PLy_procedure_call, we check for PG
+                * exceptions and re-throw the error.
                 */
-               elog(FATAL, "elog threw an unknown exception");
-               RERAISE_EXC();
+               PyErr_SetString(PLy_exc_error, sv);
+               return NULL;
        }
-
-       elog(level, "%s", sv);
-
-       RESTORE_EXC();
+       PG_END_TRY();
 
        Py_XDECREF(so);
-       Py_INCREF(Py_None);
 
        /*
-        * return a legal object so the interpreter will continue on its merry
-        * way
+        * return a legal object so the interpreter will continue on its merry way
         */
+       Py_INCREF(Py_None);
        return Py_None;
 }
 
 
 /*
- * Get the last procedure name called by the backend ( the innermost,
- * If a plpython procedure call calls the backend and the backend calls
- * another plpython procedure )
+ * Get the name of the last procedure called by the backend (the
+ * innermost, if a plpython procedure call calls the backend and the
+ * backend calls another plpython procedure).
  *
- * NB: this returns SQL name, not the internal Python procedure name
+ * NB: this returns the SQL name, not the internal Python procedure name
  */
-
-char *
+static char *
 PLy_procedure_name(PLyProcedure * proc)
 {
        if (proc == NULL)
@@ -2612,74 +2947,90 @@ 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 char *PLy_traceback(int *);
-static char *PLy_vprintf(const char *fmt, va_list ap);
-static char *PLy_printf(const char *fmt,...);
-
-void
+static void
 PLy_exception_set(PyObject * exc, const char *fmt,...)
 {
        char            buf[1024];
        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);
 }
 
-void
-PLy_elog(int elevel, const char *fmt,...)
+/*
+ * The same, pluralized.
+ */
+static void
+PLy_exception_set_plural(PyObject *exc,
+                                                const char *fmt_singular, const char *fmt_plural,
+                                                unsigned long n,...)
 {
-       DECLARE_EXC();
+       char            buf[1024];
        va_list         ap;
-       char       *xmsg,
-                          *emsg;
-       int                     xlevel;
 
-       enter();
+       va_start(ap, n);
+       vsnprintf(buf, sizeof(buf),
+                         dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
+                         ap);
+       va_end(ap);
 
-       xmsg = PLy_traceback(&xlevel);
+       PyErr_SetString(exc, buf);
+}
 
-       va_start(ap, fmt);
-       emsg = PLy_vprintf(fmt, ap);
-       va_end(ap);
+/* Emit a PG error or notice, together with any available info about the
+ * current Python error.  This should be used to propagate Python errors
+ * into PG.
+ */
+static void
+PLy_elog(int elevel, const char *fmt,...)
+{
+       char       *xmsg;
+       int                     xlevel;
+       StringInfoData emsg;
+
+       xmsg = PLy_traceback(&xlevel);
 
-       SAVE_EXC();
-       if (TRAP_EXC())
+       initStringInfo(&emsg);
+       for (;;)
        {
-               RESTORE_EXC();
-               mark();
+               va_list         ap;
+               bool            success;
 
-               /*
-                * elog called siglongjmp. cleanup, restore and reraise
-                */
-               PLy_restart_in_progress += 1;
-               PLy_free(emsg);
-               if (xmsg)
-                       PLy_free(xmsg);
-               RERAISE_EXC();
+               va_start(ap, fmt);
+               success = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
+               va_end(ap);
+               if (success)
+                       break;
+               enlargeStringInfo(&emsg, emsg.maxlen);
        }
 
-       ereport(elevel,
-                       (errmsg("plpython: %s", emsg),
-                        (xmsg) ? errdetail("%s", xmsg) : 0));
+       PG_TRY();
+       {
+               ereport(elevel,
+                               (errmsg("PL/Python: %s", emsg.data),
+                                (xmsg) ? errdetail("%s", xmsg) : 0));
+       }
+       PG_CATCH();
+       {
+               pfree(emsg.data);
+               if (xmsg)
+                       pfree(xmsg);
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
 
-       PLy_free(emsg);
+       pfree(emsg.data);
        if (xmsg)
-               PLy_free(xmsg);
-
-       leave();
-
-       RESTORE_EXC();
+               pfree(xmsg);
 }
 
-char *
+static char *
 PLy_traceback(int *xlevel)
 {
        PyObject   *e,
@@ -2688,10 +3039,8 @@ PLy_traceback(int *xlevel)
        PyObject   *eob,
                           *vob = NULL;
        char       *vstr,
-                          *estr,
-                          *xstr = NULL;
-
-       enter();
+                          *estr;
+       StringInfoData xstr;
 
        /*
         * get the current exception
@@ -2708,84 +3057,46 @@ PLy_traceback(int *xlevel)
        }
 
        PyErr_NormalizeException(&e, &v, &tb);
+       Py_XDECREF(tb);
 
        eob = PyObject_Str(e);
-       if ((v) && ((vob = PyObject_Str(v)) != NULL))
+       if (v && ((vob = PyObject_Str(v)) != NULL))
                vstr = PyString_AsString(vob);
        else
-               vstr = "Unknown";
+               vstr = "unknown";
 
-       estr = PyString_AsString(eob);
-       xstr = PLy_printf("%s: %s", estr, vstr);
+       /*
+        * I'm not sure what to do if eob is NULL here -- we can't call PLy_elog
+        * because that function calls us, so we could end up with infinite
+        * recursion.  I'm not even sure if eob could be NULL here -- would an
+        * Assert() be more appropriate?
+        */
+       estr = eob ? PyString_AsString(eob) : "unrecognized exception";
+       initStringInfo(&xstr);
+       appendStringInfo(&xstr, "%s: %s", estr, vstr);
 
        Py_DECREF(eob);
        Py_XDECREF(vob);
+       Py_XDECREF(v);
 
        /*
-        * intuit an appropriate error level for based on the exception type
+        * intuit an appropriate error level based on the exception type
         */
-       if ((PLy_exc_error) && (PyErr_GivenExceptionMatches(e, PLy_exc_error)))
+       if (PLy_exc_error && PyErr_GivenExceptionMatches(e, PLy_exc_error))
                *xlevel = ERROR;
-       else if ((PLy_exc_fatal) && (PyErr_GivenExceptionMatches(e, PLy_exc_fatal)))
+       else if (PLy_exc_fatal && PyErr_GivenExceptionMatches(e, PLy_exc_fatal))
                *xlevel = FATAL;
        else
                *xlevel = ERROR;
 
-       leave();
-
-       return xstr;
-}
-
-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;
-}
-
-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;
+       Py_DECREF(e);
+       return xstr.data;
 }
 
-/* python module code
- */
-
-
-/* some dumb utility functions
- */
+/* python module code */
 
-void *
+/* some dumb utility functions */
+static void *
 PLy_malloc(size_t bytes)
 {
        void       *ptr = malloc(bytes);
@@ -2797,21 +3108,30 @@ PLy_malloc(size_t bytes)
        return ptr;
 }
 
-void *
-PLy_realloc(void *optr, size_t bytes)
+static void *
+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;
 }
 
-/* define this away
- */
-void
+static char *
+PLy_strdup(const char *str)
+{
+       char       *result;
+       size_t          len;
+
+       len = strlen(str) + 1;
+       result = PLy_malloc(len);
+       memcpy(result, str, len);
+
+       return result;
+}
+
+/* define this away */
+static void
 PLy_free(void *ptr)
 {
        free(ptr);