aboutsummaryrefslogtreecommitdiff
path: root/src/pl
diff options
context:
space:
mode:
Diffstat (limited to 'src/pl')
-rw-r--r--src/pl/plperl/plperl.c5
-rw-r--r--src/pl/plpgsql/src/pl_comp.c3
-rw-r--r--src/pl/plpgsql/src/pl_exec.c3
-rw-r--r--src/pl/plpython/plpython.c852
-rw-r--r--src/pl/plpython/plpython.h68
-rw-r--r--src/pl/tcl/pltcl.c715
6 files changed, 664 insertions, 982 deletions
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index ad9de225544..2d368a68ef9 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -33,16 +33,15 @@
* ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.47 2004/07/21 20:45:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.48 2004/07/31 00:45:44 tgl Exp $
*
**********************************************************************/
#include "postgres.h"
/* system stuff */
-#include <unistd.h>
#include <fcntl.h>
-#include <setjmp.h>
+#include <unistd.h>
/* postgreSQL stuff */
#include "access/heapam.h"
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index a91d9c53e46..75d0a1f8a82 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.77 2004/06/06 00:41:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.78 2004/07/31 00:45:46 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -38,7 +38,6 @@
#include "plpgsql.h"
#include <ctype.h>
-#include <setjmp.h>
#include "pl.tab.h"
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 0c9d865d17d..d6f8c6835cc 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.107 2004/06/09 19:08:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.108 2004/07/31 00:45:46 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -39,7 +39,6 @@
#include "pl.tab.h"
#include <ctype.h>
-#include <setjmp.h>
#include "access/heapam.h"
#include "catalog/pg_proc.h"
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 2b35954efb8..db938c3395a 100644
--- a/src/pl/plpython/plpython.c
+++ b/src/pl/plpython/plpython.c
@@ -29,7 +29,7 @@
* MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.49 2004/06/06 00:41:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.50 2004/07/31 00:45:52 tgl Exp $
*
*********************************************************************
*/
@@ -39,7 +39,6 @@
/* system stuff */
#include <unistd.h>
#include <fcntl.h>
-#include <setjmp.h>
/* postgreSQL stuff */
#include "access/heapam.h"
@@ -58,7 +57,6 @@
#include <Python.h>
#include <compile.h>
#include <eval.h>
-#include "plpython.h"
/* convert Postgresql Datum or tuple into a PyObject.
* input to Python. Tuples are converted to dictionary
@@ -192,11 +190,6 @@ 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
@@ -209,6 +202,11 @@ static char *PLy_procedure_name(PLyProcedure *);
/* some utility functions
*/
+static void PLy_elog(int, const char *,...);
+static char *PLy_traceback(int *);
+static char *PLy_vprintf(const char *fmt, va_list ap);
+static char *PLy_printf(const char *fmt,...);
+
static void *PLy_malloc(size_t);
static void *PLy_realloc(void *, size_t);
static void PLy_free(void *);
@@ -259,18 +257,26 @@ static PyObject *PLyString_FromString(const char *);
/* global data
*/
static int PLy_first_call = 1;
-static volatile int PLy_call_level = 0;
/*
* Last function called by postgres backend
+ *
+ * XXX replace this with errcontext mechanism
*/
static PLyProcedure *PLy_last_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-7.5 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;
@@ -293,13 +299,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
*/
@@ -324,65 +323,45 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
Datum
plpython_call_handler(PG_FUNCTION_ARGS)
{
- DECLARE_EXC();
Datum retval;
PLyProcedure *volatile proc = NULL;
- enter();
-
PLy_init_all();
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "could not connect to SPI manager");
- CALL_LEVEL_INC();
-
- 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));
+ trv = PLy_trigger_handler(fcinfo, proc);
+ retval = PointerGetDatum(trv);
}
else
- PLy_restart_in_progress += 1;
+ {
+ proc = PLy_procedure_get(fcinfo, InvalidOid);
+ retval = PLy_function_handler(fcinfo, proc);
+ }
+ }
+ PG_CATCH();
+ {
if (proc)
{
/* note: Py_DECREF needs braces around it, as of 2003/08 */
Py_DECREF(proc->me);
}
- RERAISE_EXC();
+ PyErr_Clear();
+ PG_RE_THROW();
}
-
- /*
- * 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();
+ PG_END_TRY();
Py_DECREF(proc->me);
- refc(proc->me);
return retval;
}
@@ -397,42 +376,27 @@ 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);
+ Assert(plrv != NULL);
+ Assert(!PLy_error_in_progress);
+
/*
* 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");
-
- if (PLy_restart_in_progress)
- elog(FATAL, "restart in progress not expected");
-
/*
* return of None means we're happy with the tuple
*/
@@ -466,20 +430,26 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
elog(ERROR, "expected return to be \"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;
@@ -500,29 +470,8 @@ 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))
@@ -585,31 +534,42 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata,
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 (rtup == NULL)
elog(ERROR, "SPI_modifytuple failed -- error %d", SPI_result);
+ }
+ PG_CATCH();
+ {
+ 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);
+
+ 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,
@@ -621,20 +581,8 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
PyObject *volatile pltdata = NULL;
char *stroid;
- enter();
-
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- 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");
@@ -767,8 +715,13 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
}
PyDict_SetItemString(pltdata, "args", pltargs);
Py_DECREF(pltargs);
-
- RESTORE_EXC();
+ }
+ PG_CATCH();
+ {
+ Py_XDECREF(pltdata);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
return pltdata;
}
@@ -777,37 +730,23 @@ PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *
/* function handler and friends
*/
-Datum
+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();
-
- Py_XDECREF(plargs);
- Py_XDECREF(plrv);
- Py_XDECREF(plrv_so);
-
- RERAISE_EXC();
- }
-
plargs = PLy_function_build_args(fcinfo, proc);
plrv = PLy_procedure_call(proc, "args", plargs);
+ Assert(plrv != NULL);
+ Assert(!PLy_error_in_progress);
+
/*
* Disconnect from SPI manager and then create the return values datum
* (if the input function does a palloc for it this must not be
@@ -817,28 +756,9 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
- 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);
-
- /*
- * FIXME is this dead code? i'm pretty sure it is for unnested
- * calls, but not for nested calls
- */
- RAISE_EXC(1);
-#endif
- }
-
/*
- * 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
+ * convert the python PyObject to a postgresql Datum
*/
-
if (plrv == Py_None)
{
fcinfo->isnull = true;
@@ -855,7 +775,16 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
Int32GetDatum(-1));
}
- RESTORE_EXC();
+ }
+ PG_CATCH();
+ {
+ Py_XDECREF(plargs);
+ Py_XDECREF(plrv);
+ Py_XDECREF(plrv_so);
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
Py_XDECREF(plargs);
Py_DECREF(plrv);
@@ -864,56 +793,49 @@ 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);
+ rv = PyEval_EvalCode((PyCodeObject *) proc->code,
+ proc->globals, proc->globals);
PLy_last_procedure = current;
+ /*
+ * 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, "function \"%s\" failed", proc->proname);
}
return rv;
}
-PyObject *
+static 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())
+ PG_TRY();
{
- RESTORE_EXC();
- Py_XDECREF(arg);
- Py_XDECREF(args);
-
- RERAISE_EXC();
- }
-
args = PyList_New(proc->nargs);
for (i = 0; i < proc->nargs; i++)
{
@@ -976,8 +898,15 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
*/
PyList_SetItem(args, i, arg);
}
+ }
+ PG_CATCH();
+ {
+ Py_XDECREF(arg);
+ Py_XDECREF(args);
- RESTORE_EXC();
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
return args;
}
@@ -1002,8 +931,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),
@@ -1023,8 +950,6 @@ 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");
@@ -1051,7 +976,6 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
{
char procName[NAMEDATALEN + 256];
- DECLARE_EXC();
Form_pg_proc procStruct;
PLyProcedure *volatile proc;
char *volatile procSource = NULL;
@@ -1060,8 +984,6 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
int i,
rv;
- enter();
-
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
if (OidIsValid(tgreloid))
@@ -1092,16 +1014,8 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
proc->code = proc->statics = NULL;
proc->globals = proc->me = NULL;
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- 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.
@@ -1185,20 +1099,26 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
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);
/*
@@ -1238,7 +1158,7 @@ PLy_procedure_compile(PLyProcedure * proc, const char *src)
PLy_elog(ERROR, "could not compile function \"%s\"", proc->proname);
}
-char *
+static char *
PLy_procedure_munge_source(const char *name, const char *src)
{
char *mrc,
@@ -1247,8 +1167,6 @@ PLy_procedure_munge_source(const char *name, const char *src)
size_t mlen,
plen;
- enter();
-
/*
* room for function source and the def statement
*/
@@ -1281,13 +1199,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);
@@ -1304,20 +1220,16 @@ PLy_procedure_delete(PLyProcedure * proc)
if (proc->args[i].out.r.atts)
PLy_free(proc->args[i].out.r.atts);
}
-
- leave();
}
/* conversion functions. remember output from python is
* input to postgresql, and vis 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");
@@ -1347,13 +1259,11 @@ PLy_input_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
}
}
-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");
@@ -1381,41 +1291,35 @@ PLy_output_tuple_funcs(PLyTypeInfo * arg, TupleDesc desc)
}
}
-void
+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), typeTup);
}
-void
+static void
PLy_output_datum_func2(PLyObToDatum * arg, HeapTuple typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
- enter();
-
perm_fmgr_info(typeStruct->typinput, &arg->typfunc);
arg->typioparam = getTypeIOParam(typeTup);
arg->typbyval = typeStruct->typbyval;
}
-void
+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, typeTup);
}
-void
+static void
PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, HeapTuple typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
@@ -1449,7 +1353,7 @@ PLy_input_datum_func2(PLyDatumToOb * arg, Oid typeOid, HeapTuple typeTup)
}
}
-void
+static void
PLy_typeinfo_init(PLyTypeInfo * arg)
{
arg->is_rowtype = -1;
@@ -1458,7 +1362,7 @@ PLy_typeinfo_init(PLyTypeInfo * arg)
arg->out.r.atts = NULL;
}
-void
+static void
PLy_typeinfo_dealloc(PLyTypeInfo * arg)
{
if (arg->is_rowtype == 1)
@@ -1472,24 +1376,20 @@ PLy_typeinfo_dealloc(PLyTypeInfo * arg)
/* assumes that a bool is always returned as a 't' or 'f'
*/
-PyObject *
+static PyObject *
PLyBool_FromString(const char *src)
{
- enter();
-
if (src[0] == 't')
return PyInt_FromLong(1);
return PyInt_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))
@@ -1497,14 +1397,12 @@ PLyFloat_FromString(const char *src)
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))
@@ -1512,27 +1410,24 @@ PLyInt_FromString(const char *src)
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");
@@ -1540,15 +1435,8 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
if (dict == NULL)
PLy_elog(ERROR, "could not create tuple dictionary");
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
{
- RESTORE_EXC();
- Py_DECREF(dict);
-
- RERAISE_EXC();
- }
-
for (i = 0; i < info->in.r.natts; i++)
{
char *key,
@@ -1583,8 +1471,13 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
Py_DECREF(value);
}
}
-
- RESTORE_EXC();
+ }
+ PG_CATCH();
+ {
+ Py_DECREF(dict);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
return dict;
}
@@ -1747,13 +1640,11 @@ static PyMethodDef PLy_methods[] = {
/* plan object methods
*/
-PyObject *
+static PyObject *
PLy_plan_new(void)
{
PLyPlanObject *ob;
- enter();
-
if ((ob = PyObject_NEW(PLyPlanObject, &PLy_PlanType)) == NULL)
return NULL;
@@ -1766,13 +1657,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)
@@ -1787,18 +1676,16 @@ PLy_plan_dealloc(PyObject * arg)
}
PyMem_DEL(arg);
-
- leave();
}
-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, ""))
@@ -1816,13 +1703,11 @@ PLy_plan_status(PyObject * self, PyObject * args)
/* result object methods
*/
-PyObject *
+static PyObject *
PLy_result_new(void)
{
PLyResultObject *ob;
- enter();
-
if ((ob = PyObject_NEW(PLyResultObject, &PLy_ResultType)) == NULL)
return NULL;
@@ -1836,13 +1721,11 @@ 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);
@@ -1850,7 +1733,7 @@ PLy_result_dealloc(PyObject * arg)
PyMem_DEL(ob);
}
-PyObject *
+static PyObject *
PLy_result_getattr(PyObject * self, char *attr)
{
return NULL;
@@ -1858,13 +1741,13 @@ PLy_result_getattr(PyObject * self, char *attr)
#ifdef NOT_USED
/* Appear to be unused */
-PyObject *
+static PyObject *
PLy_result_fetch(PyObject * self, PyObject * args)
{
return NULL;
}
-PyObject *
+static PyObject *
PLy_result_nrows(PyObject * self, PyObject * args)
{
PLyResultObject *ob = (PLyResultObject *) self;
@@ -1873,7 +1756,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;
@@ -1882,7 +1765,8 @@ PLy_result_status(PyObject * self, PyObject * args)
return ob->status;
}
#endif
-int
+
+static int
PLy_result_length(PyObject * arg)
{
PLyResultObject *ob = (PLyResultObject *) arg;
@@ -1890,7 +1774,7 @@ PLy_result_length(PyObject * arg)
return PyList_Size(ob->rows);
}
-PyObject *
+static PyObject *
PLy_result_item(PyObject * arg, int idx)
{
PyObject *rv;
@@ -1902,7 +1786,7 @@ PLy_result_item(PyObject * arg, int idx)
return rv;
}
-int
+static int
PLy_result_ass_item(PyObject * arg, int idx, PyObject * item)
{
int rv;
@@ -1913,7 +1797,7 @@ PLy_result_ass_item(PyObject * arg, int idx, PyObject * item)
return rv;
}
-PyObject *
+static PyObject *
PLy_result_slice(PyObject * arg, int lidx, int hidx)
{
PyObject *rv;
@@ -1926,7 +1810,7 @@ PLy_result_slice(PyObject * arg, int lidx, int hidx)
return rv;
}
-int
+static int
PLy_result_ass_slice(PyObject * arg, int lidx, int hidx, PyObject * slice)
{
int rv;
@@ -1938,17 +1822,22 @@ PLy_result_ass_slice(PyObject * arg, int lidx, int hidx, PyObject * slice)
/* SPI interface
*/
-PyObject *
+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)
+ {
+ PyErr_SetString(PLy_exc_error, "Transaction aborted.");
+ return NULL;
+ }
if (!PyArg_ParseTuple(args, "s|O", &query, &list))
{
@@ -1964,24 +1853,12 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
return NULL;
}
-
if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
return NULL;
- SAVE_EXC();
- if (TRAP_EXC())
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- 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)
{
int nargs,
@@ -2014,11 +1891,7 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
optr = PySequence_GetItem(list, i);
if (!PyString_Check(optr))
- {
- PyErr_SetString(PLy_exc_spi_error,
- "Type names must be strings.");
- RAISE_EXC(1);
- }
+ elog(ERROR, "Type names must be strings.");
sptr = PyString_AsString(optr);
/* XXX should extend this to allow qualified type names */
typeTup = typenameType(makeTypeName(sptr));
@@ -2030,11 +1903,7 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
if (typeStruct->typtype != 'c')
PLy_output_datum_func(&plan->args[i], typeTup);
else
- {
- PyErr_SetString(PLy_exc_spi_error,
- "tuples not handled in plpy.prepare, yet.");
- RAISE_EXC(1);
- }
+ elog(ERROR, "tuples not handled in plpy.prepare, yet.");
ReleaseSysCache(typeTup);
}
}
@@ -2042,26 +1911,32 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
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);
- }
+ elog(ERROR, "Unable to prepare plan. SPI_prepare failed -- %s.",
+ PLy_spi_error_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, "Unable to save plan. SPI_saveplan failed -- %s.",
+ PLy_spi_error_string(SPI_result));
+ }
+ 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())
+ 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));
+ return NULL;
}
-
- RESTORE_EXC();
+ PG_END_TRY();
return (PyObject *) plan;
}
@@ -2069,7 +1944,7 @@ 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;
@@ -2077,17 +1952,12 @@ PLy_spi_execute(PyObject * self, PyObject * args)
PyObject *list = NULL;
int 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)
+ {
+ PyErr_SetString(PLy_exc_error, "Transaction aborted.");
return NULL;
-#endif
+ }
if (PyArg_ParseTuple(args, "s|i", &query, &limit))
return PLy_spi_execute_query(query, limit);
@@ -2096,27 +1966,21 @@ PLy_spi_execute(PyObject * self, PyObject * args)
if ((PyArg_ParseTuple(args, "O|Oi", &plan, &list, &limit)) &&
(is_PLyPlanObject(plan)))
- {
- PyObject *rv = PLy_spi_execute_plan(plan, list, limit);
-
- return rv;
- }
+ return PLy_spi_execute_plan(plan, list, limit);
PyErr_SetString(PLy_exc_error, "Expected a query or plan.");
return NULL;
}
-PyObject *
+static PyObject *
PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
{
- DECLARE_EXC();
volatile int nargs;
int i,
rv;
PLyPlanObject *plan;
char *nulls;
-
- enter();
+ MemoryContext oldcontext;
if (list != NULL)
{
@@ -2149,31 +2013,9 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
return NULL;
}
- SAVE_EXC();
- if (TRAP_EXC())
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- 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();
- }
-
nulls = palloc(nargs * sizeof(char));
for (i = 0; i < nargs; i++)
@@ -2189,7 +2031,7 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
sv = PyString_AsString(so);
/*
- * FIXME -- if this can elog, we have leak
+ * FIXME -- if this elogs, we have Python reference leak
*/
plan->values[i] =
FunctionCall3(&(plan->args[i].out.d.typfunc),
@@ -2213,8 +2055,32 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
rv = SPI_execp(plan->plan, plan->values, nulls, limit);
pfree(nulls);
+ }
+ PG_CATCH();
+ {
+ MemoryContextSwitchTo(oldcontext);
+ PLy_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ /*
+ * 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;
+ }
+ }
- RESTORE_EXC();
+ 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));
+ return NULL;
+ }
+ PG_END_TRY();
for (i = 0; i < nargs; i++)
{
@@ -2237,25 +2103,30 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit)
return PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
}
-PyObject *
+static PyObject *
PLy_spi_execute_query(char *query, int limit)
{
- DECLARE_EXC();
int rv;
+ MemoryContext oldcontext;
- SAVE_EXC();
- if (TRAP_EXC())
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
+ {
+ rv = SPI_exec(query, limit);
+ }
+ PG_CATCH();
{
- RESTORE_EXC();
- if ((!PLy_restart_in_progress) && (!PyErr_Occurred()))
+ MemoryContextSwitchTo(oldcontext);
+ PLy_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ if (!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();
+ return NULL;
}
+ PG_END_TRY();
- rv = SPI_exec(query, limit);
- RESTORE_EXC();
if (rv < 0)
{
PLy_exception_set(PLy_exc_spi_error,
@@ -2267,12 +2138,11 @@ PLy_spi_execute_query(char *query, int limit)
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);
@@ -2290,7 +2160,6 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
}
else
{
- DECLARE_EXC();
PLyTypeInfo args;
int i;
@@ -2298,43 +2167,46 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
Py_DECREF(result->nrows);
result->nrows = PyInt_FromLong(rows);
- 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())
+ PyErr_SetString(PLy_exc_error,
+ "Unknown 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 *
+static const char *
PLy_spi_error_string(int code)
{
switch (code)
@@ -2384,8 +2256,6 @@ plpython_init(void)
if (!PLy_first_call)
return;
- enter();
-
if (init_active)
elog(FATAL, "initialization of language module failed");
init_active = 1;
@@ -2400,8 +2270,6 @@ plpython_init(void)
PLy_elog(ERROR, "could not create procedure cache");
PLy_first_call = 0;
-
- leave();
}
static void
@@ -2418,13 +2286,11 @@ PLy_init_all(void)
}
-void
+static 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.");
@@ -2437,7 +2303,7 @@ PLy_init_interp(void)
PLy_elog(ERROR, "could not initialize globals");
}
-void
+static void
PLy_init_plpy(void)
{
PyObject *main_mod,
@@ -2446,8 +2312,6 @@ PLy_init_plpy(void)
PyObject *plpy,
*plpy_dict;
- enter();
-
/*
* initialize plpy module
*/
@@ -2480,60 +2344,55 @@ PLy_init_plpy(void)
*/
static PyObject *PLy_output(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))
@@ -2542,54 +2401,34 @@ PLy_output(volatile int level, PyObject * self, PyObject * args)
sv = "Unable to 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)
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- PyErr_SetString(PLy_exc_error, sv);
- return NULL;
+ elog(level, "%s", sv);
}
- else if (level >= FATAL)
+ PG_CATCH();
{
- PyErr_SetString(PLy_exc_fatal, sv);
- return NULL;
- }
-
- /*
- * 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())
- {
- 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
*/
+ Py_INCREF(Py_None);
return Py_None;
}
@@ -2602,7 +2441,7 @@ PLy_output(volatile int level, PyObject * self, PyObject * args)
* NB: this returns SQL name, not the internal Python procedure name
*/
-char *
+static char *
PLy_procedure_name(PLyProcedure * proc)
{
if (proc == NULL)
@@ -2613,12 +2452,7 @@ PLy_procedure_name(PLyProcedure * proc)
/* output a python traceback/exception via the postgresql elog
* function. not pretty.
*/
-
-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];
@@ -2631,53 +2465,45 @@ PLy_exception_set(PyObject * exc, const char *fmt,...)
PyErr_SetString(exc, buf);
}
-void
+/* 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,...)
{
- DECLARE_EXC();
va_list ap;
char *xmsg,
*emsg;
int xlevel;
- enter();
-
xmsg = PLy_traceback(&xlevel);
va_start(ap, fmt);
emsg = PLy_vprintf(fmt, ap);
va_end(ap);
- SAVE_EXC();
- if (TRAP_EXC())
+ PG_TRY();
+ {
+ ereport(elevel,
+ (errmsg("plpython: %s", emsg),
+ (xmsg) ? errdetail("%s", xmsg) : 0));
+ }
+ PG_CATCH();
{
- RESTORE_EXC();
- mark();
-
- /*
- * elog called siglongjmp. cleanup, restore and reraise
- */
- PLy_restart_in_progress += 1;
PLy_free(emsg);
if (xmsg)
PLy_free(xmsg);
- RERAISE_EXC();
+ PG_RE_THROW();
}
-
- ereport(elevel,
- (errmsg("plpython: %s", emsg),
- (xmsg) ? errdetail("%s", xmsg) : 0));
+ PG_END_TRY();
PLy_free(emsg);
if (xmsg)
PLy_free(xmsg);
-
- leave();
-
- RESTORE_EXC();
}
-char *
+static char *
PLy_traceback(int *xlevel)
{
PyObject *e,
@@ -2689,8 +2515,6 @@ PLy_traceback(int *xlevel)
*estr,
*xstr = NULL;
- enter();
-
/*
* get the current exception
*/
@@ -2729,12 +2553,10 @@ PLy_traceback(int *xlevel)
else
*xlevel = ERROR;
- leave();
-
return xstr;
}
-char *
+static char *
PLy_printf(const char *fmt,...)
{
va_list ap;
@@ -2746,7 +2568,7 @@ PLy_printf(const char *fmt,...)
return emsg;
}
-char *
+static char *
PLy_vprintf(const char *fmt, va_list ap)
{
size_t blen;
@@ -2783,7 +2605,7 @@ PLy_vprintf(const char *fmt, va_list ap)
/* some dumb utility functions
*/
-void *
+static void *
PLy_malloc(size_t bytes)
{
void *ptr = malloc(bytes);
@@ -2795,7 +2617,7 @@ PLy_malloc(size_t bytes)
return ptr;
}
-void *
+static void *
PLy_realloc(void *optr, size_t bytes)
{
void *nptr = realloc(optr, bytes);
@@ -2809,7 +2631,7 @@ PLy_realloc(void *optr, size_t bytes)
/* define this away
*/
-void
+static void
PLy_free(void *ptr)
{
free(ptr);
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
deleted file mode 100644
index edd517675a5..00000000000
--- a/src/pl/plpython/plpython.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* $PostgreSQL: pgsql/src/pl/plpython/plpython.h,v 1.9 2003/11/29 19:52:13 pgsql Exp $ */
-
-#ifndef PLPYTHON_H
-#define PLPYTHON_H
-
-#define DEBUG_EXC 0
-#define DEBUG_LEVEL 0
-
-#define DECLARE_N_EXC(N) int rv_##N; sigjmp_buf buf_##N
-#define TRAP_N_EXC(N) ((rv_##N = sigsetjmp(Warn_restart, 1)) != 0)
-
-#if !DEBUG_EXC
-#define RESTORE_N_EXC(N) memcpy(&Warn_restart, &(buf_##N), sizeof(sigjmp_buf))
-#define SAVE_N_EXC(N) memcpy(&(buf_##N), &Warn_restart, sizeof(sigjmp_buf))
-#define RERAISE_N_EXC(N) siglongjmp(Warn_restart, rv_##N)
-#define RAISE_EXC(V) siglongjmp(Warn_restart, (V))
-#else
-#define RESTORE_N_EXC(N) do { \
- elog(WARNING, "exception (%d,%d) restore at %s:%d",\
- PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__));\
- exc_save_calls -= 1; \
- memcpy(&Warn_restart, &(buf_##N), sizeof(sigjmp_buf)); } while (0)
-#define SAVE_N_EXC(N) do { \
- exc_save_calls += 1; \
- elog(WARNING, "exception (%d,%d) save at %s:%d", \
- PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__)); \
- memcpy(&(buf_##N), &Warn_restart, sizeof(sigjmp_buf)); } while (0)
-#define RERAISE_N_EXC(N) do { \
- elog(WARNING, "exception (%d,%d) reraise at %s:%d", \
- PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__)); \
- siglongjmp(Warn_restart, rv_##N); } while (0)
-#define RAISE_EXC(V) do { \
- elog(WARNING, "exception (%d,%d) raise at %s:%d", \
- PLy_call_level, exc_save_calls, __FUNCTION__, (__LINE__)); \
- siglongjmp(Warn_restart, (V)); } while (0)
-#endif
-
-#define DECLARE_EXC() DECLARE_N_EXC(save_restart)
-#define SAVE_EXC() SAVE_N_EXC(save_restart)
-#define RERAISE_EXC() RERAISE_N_EXC(save_restart)
-#define RESTORE_EXC() RESTORE_N_EXC(save_restart)
-#define TRAP_EXC() TRAP_N_EXC(save_restart)
-
-#if DEBUG_LEVEL
-#define CALL_LEVEL_INC() do { PLy_call_level += 1; \
- elog(DEBUG4, "level: %d", PLy_call_level); } while (0)
-#define CALL_LEVEL_DEC() do { elog(DEBUG4, "level: %d", PLy_call_level); \
- PLy_call_level -= 1; } while (0)
-#else
-#define CALL_LEVEL_INC() do { PLy_call_level += 1; } while (0)
-#define CALL_LEVEL_DEC() do { PLy_call_level -= 1; } while (0)
-#endif
-
-/* temporary debugging macros
- */
-#if DEBUG_LEVEL
-#define enter() elog(DEBUG4, "Enter(%d): %s", func_enter_calls++,__FUNCTION__)
-#define leave() elog(DEBUG4, "Leave(%d): %s", func_leave_calls++,__FUNCTION__)
-#define mark() elog(DEBUG4, "Mark: %s:%d", __FUNCTION__, __LINE__);
-#define refc(O) elog(DEBUG4, "Ref<%p>:<%d>:%s:%d", (O), (((O) == NULL) ? -1 : (O)->ob_refcnt), __FUNCTION__, __LINE__)
-#else
-#define enter()
-#define leave()
-#define mark()
-#define refc(O)
-#endif
-
-#endif /* PLPYTHON_H */
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index e51e56e6d74..05c906f30f2 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -31,7 +31,7 @@
* ENHANCEMENTS, OR MODIFICATIONS.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.86 2004/06/06 00:41:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.87 2004/07/31 00:45:57 tgl Exp $
*
**********************************************************************/
@@ -41,7 +41,6 @@
#include <unistd.h>
#include <fcntl.h>
-#include <setjmp.h>
/* Hack to deal with Tcl 8.4 const-ification without losing compatibility */
#ifndef CONST84
@@ -132,8 +131,6 @@ typedef struct pltcl_query_desc
**********************************************************************/
static bool pltcl_pm_init_done = false;
static bool pltcl_be_init_done = false;
-static int pltcl_call_level = 0;
-static int pltcl_restart_in_progress = 0;
static Tcl_Interp *pltcl_hold_interp = NULL;
static Tcl_Interp *pltcl_norm_interp = NULL;
static Tcl_Interp *pltcl_safe_interp = NULL;
@@ -142,6 +139,19 @@ static Tcl_HashTable *pltcl_norm_query_hash = NULL;
static Tcl_HashTable *pltcl_safe_query_hash = NULL;
static FunctionCallInfo pltcl_current_fcinfo = NULL;
+/*
+ * When a callback from Tcl into PG incurs an error, we temporarily store
+ * the error information here, and return TCL_ERROR to the Tcl interpreter.
+ * Any further callback attempts immediately fail, and when the Tcl interpreter
+ * returns to the calling function, we re-throw the error (even if Tcl
+ * thinks it trapped the error and doesn't return TCL_ERROR). Eventually
+ * this ought to be improved to let Tcl code really truly trap the error,
+ * but that's more of a change from the pre-7.5 semantics than I have time
+ * for now --- it will only be possible if the callback query is executed
+ * inside a subtransaction.
+ */
+static ErrorData *pltcl_error_in_progress = NULL;
+
/**********************************************************************
* Forward declarations
**********************************************************************/
@@ -397,21 +407,11 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
FunctionCallInfo save_fcinfo;
/************************************************************
- * Initialize interpreters
+ * Initialize interpreters if first time through
************************************************************/
pltcl_init_all();
/************************************************************
- * Connect to SPI manager
- ************************************************************/
- if (SPI_connect() != SPI_OK_CONNECT)
- elog(ERROR, "could not connect to SPI manager");
- /************************************************************
- * Keep track about the nesting of Tcl-SPI-Tcl-... calls
- ************************************************************/
- pltcl_call_level++;
-
- /************************************************************
* Determine if called as function or trigger and
* call appropriate subhandler
************************************************************/
@@ -430,8 +430,6 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
pltcl_current_fcinfo = save_fcinfo;
- pltcl_call_level--;
-
return retval;
}
@@ -461,7 +459,10 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
int i;
int tcl_rc;
Datum retval;
- sigjmp_buf save_restart;
+
+ /* Connect to SPI manager */
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "could not connect to SPI manager");
/* Find or compile the function */
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid);
@@ -480,23 +481,10 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
/************************************************************
- * Catch elog(ERROR) during build of the Tcl command
- ************************************************************/
- memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
- if (sigsetjmp(Warn_restart, 1) != 0)
- {
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- Tcl_DStringFree(&tcl_cmd);
- Tcl_DStringFree(&list_tmp);
- pltcl_restart_in_progress = 1;
- if (--pltcl_call_level == 0)
- pltcl_restart_in_progress = 0;
- siglongjmp(Warn_restart, 1);
- }
-
- /************************************************************
* Add all call arguments to the command
************************************************************/
+ PG_TRY();
+ {
for (i = 0; i < prodesc->nargs; i++)
{
if (prodesc->arg_is_rowtype[i])
@@ -552,60 +540,53 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
}
}
}
+ }
+ PG_CATCH();
+ {
+ Tcl_DStringFree(&tcl_cmd);
+ Tcl_DStringFree(&list_tmp);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
Tcl_DStringFree(&list_tmp);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
/************************************************************
* Call the Tcl function
+ *
+ * We assume no PG error can be thrown directly from this call.
************************************************************/
tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
Tcl_DStringFree(&tcl_cmd);
/************************************************************
- * Check the return code from Tcl and handle
- * our special restart mechanism to get rid
- * of all nested call levels on transaction
- * abort.
+ * If there was an error in a PG callback, propagate that
+ * no matter what Tcl claims about its success.
************************************************************/
- if (tcl_rc != TCL_OK || pltcl_restart_in_progress)
+ if (pltcl_error_in_progress)
{
- if (!pltcl_restart_in_progress)
- {
- pltcl_restart_in_progress = 1;
- if (--pltcl_call_level == 0)
- pltcl_restart_in_progress = 0;
- UTF_BEGIN;
- ereport(ERROR,
- (errmsg("pltcl: %s", interp->result),
- errdetail("%s",
- UTF_U2E(Tcl_GetVar(interp, "errorInfo",
- TCL_GLOBAL_ONLY)))));
- UTF_END;
- }
- if (--pltcl_call_level == 0)
- pltcl_restart_in_progress = 0;
- siglongjmp(Warn_restart, 1);
+ ErrorData *edata = pltcl_error_in_progress;
+
+ pltcl_error_in_progress = NULL;
+ ReThrowError(edata);
}
/************************************************************
- * Convert the result value from the Tcl interpreter
- * into its PostgreSQL data format and return it.
- * Again, the function call could fire an elog and we
- * have to count for the current interpreter level we are
- * on. The save_restart from above is still good.
+ * Check for errors reported by Tcl itself.
************************************************************/
- if (sigsetjmp(Warn_restart, 1) != 0)
+ if (tcl_rc != TCL_OK)
{
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- pltcl_restart_in_progress = 1;
- if (--pltcl_call_level == 0)
- pltcl_restart_in_progress = 0;
- siglongjmp(Warn_restart, 1);
+ UTF_BEGIN;
+ ereport(ERROR,
+ (errmsg("pltcl: %s", interp->result),
+ errdetail("%s",
+ UTF_U2E(Tcl_GetVar(interp, "errorInfo",
+ TCL_GLOBAL_ONLY)))));
+ UTF_END;
}
/************************************************************
* Disconnect from SPI manager and then create the return
- * values datum (if the input function does a palloc for it
+ * value 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). But don't try to call
* the result_in_func if we've been told to return a NULL;
@@ -627,11 +608,6 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
UTF_END;
}
- /************************************************************
- * Finally we may restore normal error handling.
- ************************************************************/
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
-
return retval;
}
@@ -653,15 +629,15 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
Tcl_DString tcl_newtup;
int tcl_rc;
int i;
-
int *modattrs;
Datum *modvalues;
char *modnulls;
-
int ret_numvals;
CONST84 char **ret_values;
- sigjmp_buf save_restart;
+ /* Connect to SPI manager */
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "could not connect to SPI manager");
/* Find or compile the function */
prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid,
@@ -681,23 +657,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
Tcl_DStringInit(&tcl_cmd);
Tcl_DStringInit(&tcl_trigtup);
Tcl_DStringInit(&tcl_newtup);
-
- /************************************************************
- * We call external functions below - care for elog(ERROR)
- ************************************************************/
- memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
- if (sigsetjmp(Warn_restart, 1) != 0)
+ PG_TRY();
{
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- Tcl_DStringFree(&tcl_cmd);
- Tcl_DStringFree(&tcl_trigtup);
- Tcl_DStringFree(&tcl_newtup);
- pltcl_restart_in_progress = 1;
- if (--pltcl_call_level == 0)
- pltcl_restart_in_progress = 0;
- siglongjmp(Warn_restart, 1);
- }
-
/* The procedure name */
Tcl_DStringAppendElement(&tcl_cmd, prodesc->proname);
@@ -799,55 +760,54 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
else
elog(ERROR, "unrecognized LEVEL tg_event: %u", trigdata->tg_event);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- Tcl_DStringFree(&tcl_trigtup);
- Tcl_DStringFree(&tcl_newtup);
-
- /************************************************************
- * Finally append the arguments from CREATE TRIGGER
- ************************************************************/
+ /* Finally append the arguments from CREATE TRIGGER */
for (i = 0; i < trigdata->tg_trigger->tgnargs; i++)
Tcl_DStringAppendElement(&tcl_cmd, trigdata->tg_trigger->tgargs[i]);
+ }
+ PG_CATCH();
+ {
+ Tcl_DStringFree(&tcl_cmd);
+ Tcl_DStringFree(&tcl_trigtup);
+ Tcl_DStringFree(&tcl_newtup);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ Tcl_DStringFree(&tcl_trigtup);
+ Tcl_DStringFree(&tcl_newtup);
+
/************************************************************
* Call the Tcl function
+ *
+ * We assume no PG error can be thrown directly from this call.
************************************************************/
tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&tcl_cmd));
Tcl_DStringFree(&tcl_cmd);
/************************************************************
- * Check the return code from Tcl and handle
- * our special restart mechanism to get rid
- * of all nested call levels on transaction
- * abort.
+ * If there was an error in a PG callback, propagate that
+ * no matter what Tcl claims about its success.
************************************************************/
- if (tcl_rc == TCL_ERROR || pltcl_restart_in_progress)
+ if (pltcl_error_in_progress)
{
- if (!pltcl_restart_in_progress)
- {
- pltcl_restart_in_progress = 1;
- if (--pltcl_call_level == 0)
- pltcl_restart_in_progress = 0;
- UTF_BEGIN;
- ereport(ERROR,
- (errmsg("pltcl: %s", interp->result),
- errdetail("%s",
- UTF_U2E(Tcl_GetVar(interp, "errorInfo",
- TCL_GLOBAL_ONLY)))));
- UTF_END;
- }
- if (--pltcl_call_level == 0)
- pltcl_restart_in_progress = 0;
- siglongjmp(Warn_restart, 1);
+ ErrorData *edata = pltcl_error_in_progress;
+
+ pltcl_error_in_progress = NULL;
+ ReThrowError(edata);
}
- switch (tcl_rc)
+ /************************************************************
+ * Check for errors reported by Tcl itself.
+ ************************************************************/
+ if (tcl_rc != TCL_OK)
{
- case TCL_OK:
- break;
-
- default:
- elog(ERROR, "unsupported TCL return code: %d", tcl_rc);
+ UTF_BEGIN;
+ ereport(ERROR,
+ (errmsg("pltcl: %s", interp->result),
+ errdetail("%s",
+ UTF_U2E(Tcl_GetVar(interp, "errorInfo",
+ TCL_GLOBAL_ONLY)))));
+ UTF_END;
}
/************************************************************
@@ -871,11 +831,12 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
elog(ERROR, "could not split return value from trigger: %s",
interp->result);
- if (ret_numvals % 2 != 0)
+ /* Use a TRY to ensure ret_values will get freed */
+ PG_TRY();
{
- ckfree((char *) ret_values);
+
+ if (ret_numvals % 2 != 0)
elog(ERROR, "invalid return list from trigger - must have even # of elements");
- }
modattrs = (int *) palloc(tupdesc->natts * sizeof(int));
modvalues = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
@@ -888,19 +849,6 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
modnulls = palloc(tupdesc->natts);
memset(modnulls, 'n', tupdesc->natts);
- /************************************************************
- * Care for possible elog(ERROR)'s below
- ************************************************************/
- if (sigsetjmp(Warn_restart, 1) != 0)
- {
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- ckfree((char *) ret_values);
- pltcl_restart_in_progress = 1;
- if (--pltcl_call_level == 0)
- pltcl_restart_in_progress = 0;
- siglongjmp(Warn_restart, 1);
- }
-
for (i = 0; i < ret_numvals; i += 2)
{
CONST84 char *ret_name = ret_values[i];
@@ -970,8 +918,14 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
if (rettup == NULL)
elog(ERROR, "SPI_modifytuple() failed - RC = %d", SPI_result);
+ }
+ PG_CATCH();
+ {
+ ckfree((char *) ret_values);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
ckfree((char *) ret_values);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
return rettup;
}
@@ -1317,13 +1271,16 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp,
int argc, CONST84 char *argv[])
{
volatile int level;
- sigjmp_buf save_restart;
+ MemoryContext oldcontext;
/************************************************************
- * Suppress messages during the restart process
+ * Suppress messages if an error is already declared
************************************************************/
- if (pltcl_restart_in_progress)
+ if (pltcl_error_in_progress)
+ {
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
return TCL_ERROR;
+ }
if (argc != 3)
{
@@ -1354,26 +1311,25 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp,
}
/************************************************************
- * Catch the longjmp from elog() and begin a controlled
- * return though all interpreter levels if it happens
+ * If elog() throws an error, catch and save it, then return
+ * error indication to Tcl interpreter.
************************************************************/
- memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
- if (sigsetjmp(Warn_restart, 1) != 0)
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
+ {
+ UTF_BEGIN;
+ elog(level, "%s", UTF_U2E(argv[2]));
+ UTF_END;
+ }
+ PG_CATCH();
{
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- pltcl_restart_in_progress = 1;
+ MemoryContextSwitchTo(oldcontext);
+ pltcl_error_in_progress = CopyErrorData();
+ FlushErrorState();
return TCL_ERROR;
}
+ PG_END_TRY();
- /************************************************************
- * Call elog(), restore the original restart address
- * and return to the caller (if no longjmp)
- ************************************************************/
- UTF_BEGIN;
- elog(level, "%s", UTF_U2E(argv[2]));
- UTF_END;
-
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
return TCL_OK;
}
@@ -1535,6 +1491,7 @@ static int
pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
int argc, CONST84 char *argv[])
{
+ volatile int my_rc;
int spi_rc;
char buf[64];
int count = 0;
@@ -1546,17 +1503,20 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
HeapTuple *volatile tuples;
volatile TupleDesc tupdesc = NULL;
SPITupleTable *tuptable;
- sigjmp_buf save_restart;
+ MemoryContext oldcontext;
char *usage = "syntax error - 'SPI_exec "
"?-count n? "
"?-array name? query ?loop body?";
/************************************************************
- * Don't do anything if we are already in restart mode
+ * Don't do anything if we are already in error mode
************************************************************/
- if (pltcl_restart_in_progress)
+ if (pltcl_error_in_progress)
+ {
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
return TCL_ERROR;
+ }
/************************************************************
* Check the call syntax and get the count option
@@ -1604,25 +1564,24 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
}
/************************************************************
- * Prepare to start a controlled return through all
- * interpreter levels on transaction abort
+ * Execute the query and handle return codes
************************************************************/
- memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
- if (sigsetjmp(Warn_restart, 1) != 0)
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- pltcl_restart_in_progress = 1;
- Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
+ UTF_BEGIN;
+ spi_rc = SPI_exec(UTF_U2E(argv[query_idx]), count);
+ UTF_END;
+ }
+ PG_CATCH();
+ {
+ MemoryContextSwitchTo(oldcontext);
+ pltcl_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
return TCL_ERROR;
}
-
- /************************************************************
- * Execute the query and handle return codes
- ************************************************************/
- UTF_BEGIN;
- spi_rc = SPI_exec(UTF_U2E(argv[query_idx]), count);
- UTF_END;
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+ PG_END_TRY();
switch (spi_rc)
{
@@ -1687,83 +1646,80 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp,
}
/************************************************************
- * Only SELECT queries fall through to here - remember the
- * tuples we got
+ * Only SELECT queries fall through to here - process the tuples we got
************************************************************/
-
ntuples = SPI_processed;
+ tuptable = SPI_tuptable;
if (ntuples > 0)
{
- tuples = SPI_tuptable->vals;
- tupdesc = SPI_tuptable->tupdesc;
- }
-
- /************************************************************
- * Again prepare for elog(ERROR)
- ************************************************************/
- if (sigsetjmp(Warn_restart, 1) != 0)
- {
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- pltcl_restart_in_progress = 1;
- Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
- return TCL_ERROR;
- }
-
- /************************************************************
- * If there is no loop body given, just set the variables
- * from the first tuple (if any) and return the number of
- * tuples selected
- ************************************************************/
- if (argc == query_idx + 1)
- {
- if (ntuples > 0)
- pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
- snprintf(buf, sizeof(buf), "%d", ntuples);
- Tcl_SetResult(interp, buf, TCL_VOLATILE);
- SPI_freetuptable(SPI_tuptable);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- return TCL_OK;
+ tuples = tuptable->vals;
+ tupdesc = tuptable->tupdesc;
}
- tuptable = SPI_tuptable;
-
- /************************************************************
- * There is a loop body - process all tuples and evaluate
- * the body on each
- ************************************************************/
- query_idx++;
- for (i = 0; i < ntuples; i++)
+ my_rc = TCL_OK;
+ PG_TRY();
{
- pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc);
+ if (argc == query_idx + 1)
+ {
+ /************************************************************
+ * If there is no loop body given, just set the variables
+ * from the first tuple (if any)
+ ************************************************************/
+ if (ntuples > 0)
+ pltcl_set_tuple_values(interp, arrayname, 0,
+ tuples[0], tupdesc);
+ }
+ else
+ {
+ /************************************************************
+ * There is a loop body - process all tuples and evaluate
+ * the body on each
+ ************************************************************/
+ query_idx++;
+ for (i = 0; i < ntuples; i++)
+ {
+ pltcl_set_tuple_values(interp, arrayname, i,
+ tuples[i], tupdesc);
- loop_rc = Tcl_Eval(interp, argv[query_idx]);
+ loop_rc = Tcl_Eval(interp, argv[query_idx]);
- if (loop_rc == TCL_OK)
- continue;
- if (loop_rc == TCL_CONTINUE)
- continue;
- if (loop_rc == TCL_RETURN)
- {
- SPI_freetuptable(tuptable);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- return TCL_RETURN;
+ if (loop_rc == TCL_OK)
+ continue;
+ if (loop_rc == TCL_CONTINUE)
+ continue;
+ if (loop_rc == TCL_RETURN)
+ {
+ my_rc = TCL_RETURN;
+ break;
+ }
+ if (loop_rc == TCL_BREAK)
+ break;
+ my_rc = TCL_ERROR;
+ break;
+ }
}
- if (loop_rc == TCL_BREAK)
- break;
+
SPI_freetuptable(tuptable);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+ }
+ PG_CATCH();
+ {
+ MemoryContextSwitchTo(oldcontext);
+ pltcl_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
return TCL_ERROR;
}
-
- SPI_freetuptable(tuptable);
+ PG_END_TRY();
/************************************************************
* Finally return the number of tuples
************************************************************/
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- snprintf(buf, sizeof(buf), "%d", ntuples);
- Tcl_SetResult(interp, buf, TCL_VOLATILE);
- return TCL_OK;
+ if (my_rc == TCL_OK)
+ {
+ snprintf(buf, sizeof(buf), "%d", ntuples);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ }
+ return my_rc;
}
@@ -1787,14 +1743,17 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
HeapTuple typeTup;
Tcl_HashEntry *hashent;
int hashnew;
- sigjmp_buf save_restart;
Tcl_HashTable *query_hash;
+ MemoryContext oldcontext;
/************************************************************
- * Don't do anything if we are already in restart mode
+ * Don't do anything if we are already in error mode
************************************************************/
- if (pltcl_restart_in_progress)
+ if (pltcl_error_in_progress)
+ {
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
return TCL_ERROR;
+ }
/************************************************************
* Check the call syntax
@@ -1822,23 +1781,9 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo));
qdesc->argtypioparams = (Oid *) malloc(nargs * sizeof(Oid));
- /************************************************************
- * Prepare to start a controlled return through all
- * interpreter levels on transaction abort
- ************************************************************/
- memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
- if (sigsetjmp(Warn_restart, 1) != 0)
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- pltcl_restart_in_progress = 1;
- free(qdesc->argtypes);
- free(qdesc->arginfuncs);
- free(qdesc->argtypioparams);
- free(qdesc);
- ckfree((char *) args);
- return TCL_ERROR;
- }
-
/************************************************************
* Lookup the argument types by name in the system cache
* and remember the required information for input conversion
@@ -1882,10 +1827,7 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
UTF_END;
if (plan == NULL)
- {
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
elog(ERROR, "SPI_prepare() failed");
- }
/************************************************************
* Save the plan into permanent memory (right now it's in the
@@ -1893,10 +1835,8 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
************************************************************/
qdesc->plan = SPI_saveplan(plan);
if (qdesc->plan == NULL)
- {
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
elog(ERROR, "SPI_saveplan() failed");
- }
+
/* Release the procCxt copy to avoid within-function memory leak */
SPI_freeplan(plan);
@@ -1909,7 +1849,21 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
else
query_hash = pltcl_safe_query_hash;
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+ }
+ PG_CATCH();
+ {
+ MemoryContextSwitchTo(oldcontext);
+ pltcl_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ free(qdesc->argtypes);
+ free(qdesc->arginfuncs);
+ free(qdesc->argtypioparams);
+ free(qdesc);
+ ckfree((char *) args);
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ PG_END_TRY();
hashent = Tcl_CreateHashEntry(query_hash, qdesc->qname, &hashnew);
Tcl_SetHashValue(hashent, (ClientData) qdesc);
@@ -1928,6 +1882,7 @@ static int
pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
int argc, CONST84 char *argv[])
{
+ volatile int my_rc;
int spi_rc;
char buf[64];
volatile int i;
@@ -1940,13 +1895,13 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
CONST84 char *volatile arrayname = NULL;
int count = 0;
int callnargs;
- static CONST84 char **callargs = NULL;
+ CONST84 char **callargs;
int loop_rc;
int ntuples;
HeapTuple *volatile tuples = NULL;
volatile TupleDesc tupdesc = NULL;
SPITupleTable *tuptable;
- sigjmp_buf save_restart;
+ volatile MemoryContext oldcontext;
Tcl_HashTable *query_hash;
char *usage = "syntax error - 'SPI_execp "
@@ -1954,19 +1909,13 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
"?-array name? query ?args? ?loop body?";
/************************************************************
- * Tidy up from an earlier abort
+ * Don't do anything if we are already in error mode
************************************************************/
- if (callargs != NULL)
+ if (pltcl_error_in_progress)
{
- ckfree((char *) callargs);
- callargs = NULL;
- }
-
- /************************************************************
- * Don't do anything if we are already in restart mode
- ************************************************************/
- if (pltcl_restart_in_progress)
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
return TCL_ERROR;
+ }
/************************************************************
* Get the options and check syntax
@@ -2074,27 +2023,7 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
Tcl_SetResult(interp,
"argument list length doesn't match # of arguments for query",
TCL_VOLATILE);
- if (callargs != NULL)
- {
- ckfree((char *) callargs);
- callargs = NULL;
- }
- return TCL_ERROR;
- }
-
- /************************************************************
- * Prepare to start a controlled return through all
- * interpreter levels on transaction abort during the
- * parse of the arguments
- ************************************************************/
- memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
- if (sigsetjmp(Warn_restart, 1) != 0)
- {
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
ckfree((char *) callargs);
- callargs = NULL;
- pltcl_restart_in_progress = 1;
- Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
return TCL_ERROR;
}
@@ -2102,33 +2031,42 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
* Setup the value array for the SPI_execp() using
* the type specific input functions
************************************************************/
- argvalues = (Datum *) palloc(callnargs * sizeof(Datum));
-
- for (j = 0; j < callnargs; j++)
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- if (nulls && nulls[j] == 'n')
- {
- /* don't try to convert the input for a null */
- argvalues[j] = (Datum) 0;
- }
- else
+ argvalues = (Datum *) palloc(callnargs * sizeof(Datum));
+
+ for (j = 0; j < callnargs; j++)
{
- UTF_BEGIN;
- argvalues[j] =
- FunctionCall3(&qdesc->arginfuncs[j],
- CStringGetDatum(UTF_U2E(callargs[j])),
- ObjectIdGetDatum(qdesc->argtypioparams[j]),
- Int32GetDatum(-1));
- UTF_END;
+ if (nulls && nulls[j] == 'n')
+ {
+ /* don't try to convert the input for a null */
+ argvalues[j] = (Datum) 0;
+ }
+ else
+ {
+ UTF_BEGIN;
+ argvalues[j] =
+ FunctionCall3(&qdesc->arginfuncs[j],
+ CStringGetDatum(UTF_U2E(callargs[j])),
+ ObjectIdGetDatum(qdesc->argtypioparams[j]),
+ Int32GetDatum(-1));
+ UTF_END;
+ }
}
}
+ PG_CATCH();
+ {
+ ckfree((char *) callargs);
+ MemoryContextSwitchTo(oldcontext);
+ pltcl_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ PG_END_TRY();
- /************************************************************
- * Free the splitted argument value list
- ************************************************************/
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
ckfree((char *) callargs);
- callargs = NULL;
}
else
callnargs = 0;
@@ -2140,23 +2078,22 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
loop_body = i;
/************************************************************
- * Prepare to start a controlled return through all
- * interpreter levels on transaction abort
+ * Execute the plan
************************************************************/
- memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
- if (sigsetjmp(Warn_restart, 1) != 0)
+ oldcontext = CurrentMemoryContext;
+ PG_TRY();
{
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- pltcl_restart_in_progress = 1;
- Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
+ spi_rc = SPI_execp(qdesc->plan, argvalues, nulls, count);
+ }
+ PG_CATCH();
+ {
+ MemoryContextSwitchTo(oldcontext);
+ pltcl_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
return TCL_ERROR;
}
-
- /************************************************************
- * Execute the plan
- ************************************************************/
- spi_rc = SPI_execp(qdesc->plan, argvalues, nulls, count);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+ PG_END_TRY();
/************************************************************
* Check the return code from SPI_execp()
@@ -2224,85 +2161,79 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp,
}
/************************************************************
- * Only SELECT queries fall through to here - remember the
- * tuples we got
+ * Only SELECT queries fall through to here - process the tuples we got
************************************************************/
-
ntuples = SPI_processed;
+ tuptable = SPI_tuptable;
if (ntuples > 0)
{
- tuples = SPI_tuptable->vals;
- tupdesc = SPI_tuptable->tupdesc;
+ tuples = tuptable->vals;
+ tupdesc = tuptable->tupdesc;
}
- /************************************************************
- * Prepare to start a controlled return through all
- * interpreter levels on transaction abort during
- * the ouput conversions of the results
- ************************************************************/
- memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
- if (sigsetjmp(Warn_restart, 1) != 0)
+ my_rc = TCL_OK;
+ PG_TRY();
{
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- pltcl_restart_in_progress = 1;
- Tcl_SetResult(interp, "Transaction abort", TCL_VOLATILE);
- return TCL_ERROR;
- }
-
- /************************************************************
- * If there is no loop body given, just set the variables
- * from the first tuple (if any) and return the number of
- * tuples selected
- ************************************************************/
- if (loop_body >= argc)
- {
- if (ntuples > 0)
- pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- snprintf(buf, sizeof(buf), "%d", ntuples);
- Tcl_SetResult(interp, buf, TCL_VOLATILE);
- SPI_freetuptable(SPI_tuptable);
- return TCL_OK;
- }
-
- tuptable = SPI_tuptable;
-
- /************************************************************
- * There is a loop body - process all tuples and evaluate
- * the body on each
- ************************************************************/
- for (i = 0; i < ntuples; i++)
- {
- pltcl_set_tuple_values(interp, arrayname, i, tuples[i], tupdesc);
+ if (loop_body >= argc)
+ {
+ /************************************************************
+ * If there is no loop body given, just set the variables
+ * from the first tuple (if any)
+ ************************************************************/
+ if (ntuples > 0)
+ pltcl_set_tuple_values(interp, arrayname, 0,
+ tuples[0], tupdesc);
+ }
+ else
+ {
+ /************************************************************
+ * There is a loop body - process all tuples and evaluate
+ * the body on each
+ ************************************************************/
+ for (i = 0; i < ntuples; i++)
+ {
+ pltcl_set_tuple_values(interp, arrayname, i,
+ tuples[i], tupdesc);
- loop_rc = Tcl_Eval(interp, argv[loop_body]);
+ loop_rc = Tcl_Eval(interp, argv[loop_body]);
- if (loop_rc == TCL_OK)
- continue;
- if (loop_rc == TCL_CONTINUE)
- continue;
- if (loop_rc == TCL_RETURN)
- {
- SPI_freetuptable(tuptable);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- return TCL_RETURN;
+ if (loop_rc == TCL_OK)
+ continue;
+ if (loop_rc == TCL_CONTINUE)
+ continue;
+ if (loop_rc == TCL_RETURN)
+ {
+ my_rc = TCL_RETURN;
+ break;
+ }
+ if (loop_rc == TCL_BREAK)
+ break;
+ my_rc = TCL_ERROR;
+ break;
+ }
}
- if (loop_rc == TCL_BREAK)
- break;
+
SPI_freetuptable(tuptable);
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+ }
+ PG_CATCH();
+ {
+ MemoryContextSwitchTo(oldcontext);
+ pltcl_error_in_progress = CopyErrorData();
+ FlushErrorState();
+ Tcl_SetResult(interp, "Transaction aborted", TCL_VOLATILE);
return TCL_ERROR;
}
-
- SPI_freetuptable(tuptable);
+ PG_END_TRY();
/************************************************************
* Finally return the number of tuples
************************************************************/
- memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
- snprintf(buf, sizeof(buf), "%d", ntuples);
- Tcl_SetResult(interp, buf, TCL_VOLATILE);
- return TCL_OK;
+ if (my_rc == TCL_OK)
+ {
+ snprintf(buf, sizeof(buf), "%d", ntuples);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ }
+ return my_rc;
}