diff options
Diffstat (limited to 'src/pl')
-rw-r--r-- | src/pl/plperl/plperl.c | 5 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_comp.c | 3 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_exec.c | 3 | ||||
-rw-r--r-- | src/pl/plpython/plpython.c | 852 | ||||
-rw-r--r-- | src/pl/plpython/plpython.h | 68 | ||||
-rw-r--r-- | src/pl/tcl/pltcl.c | 715 |
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; } |