@@ -284,13 +284,17 @@ PLy_exception_set_plural(PyObject *, const char *, const char *,
284284__attribute__((format (printf ,2 ,5 )))
285285__attribute__((format (printf ,3 ,5 )));
286286
287+ /* like PLy_exception_set, but conserve more fields from ErrorData */
288+ static void PLy_spi_exception_set (ErrorData * edata );
289+
287290/* Get the innermost python procedure called from the backend */
288291static char * PLy_procedure_name (PLyProcedure * );
289292
290293/* some utility functions */
291294static void
292295PLy_elog (int ,const char * ,...)
293296__attribute__((format (printf ,2 ,3 )));
297+ static void PLy_get_spi_error_data (PyObject * exc ,char * * hint ,char * * query ,int * position );
294298static char * PLy_traceback (int * );
295299
296300static void * PLy_malloc (size_t );
@@ -364,19 +368,6 @@ static HeapTuple PLyObject_ToTuple(PLyTypeInfo *, PyObject *);
364368 */
365369static PLyProcedure * PLy_curr_procedure = NULL ;
366370
367- /*
368- * When a callback from Python into PG incurs an error, we temporarily store
369- * the error information here, and return NULL to the Python interpreter.
370- * Any further callback attempts immediately fail, and when the Python
371- * interpreter returns to the calling function, we re-throw the error (even if
372- * Python thinks it trapped the error and doesn't return NULL). Eventually
373- * this ought to be improved to let Python code really truly trap the error,
374- * but that's more of a change from the pre-8.0 semantics than I have time for
375- * now --- it will only be possible if the callback query is executed inside a
376- * subtransaction.
377- */
378- static ErrorData * PLy_error_in_progress = NULL ;
379-
380371static PyObject * PLy_interp_globals = NULL ;
381372static PyObject * PLy_interp_safe_globals = NULL ;
382373static HTAB * PLy_procedure_cache = NULL ;
@@ -597,7 +588,6 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
597588plrv = PLy_procedure_call (proc ,"TD" ,plargs );
598589
599590Assert (plrv != NULL );
600- Assert (!PLy_error_in_progress );
601591
602592/*
603593 * Disconnect from SPI manager
@@ -1015,7 +1005,6 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
10151005PLy_function_delete_args (proc );
10161006}
10171007Assert (plrv != NULL );
1018- Assert (!PLy_error_in_progress );
10191008}
10201009
10211010/*
@@ -1194,23 +1183,9 @@ PLy_procedure_call(PLyProcedure *proc, char *kargs, PyObject *vargs)
11941183rv = PyEval_EvalCode ((PyCodeObject * )proc -> code ,
11951184proc -> globals ,proc -> globals );
11961185
1197- /*
1198- * If there was an error in a PG callback, propagate that no matter what
1199- * Python claims about its success.
1200- */
1201- if (PLy_error_in_progress )
1202- {
1203- ErrorData * edata = PLy_error_in_progress ;
1204-
1205- PLy_error_in_progress = NULL ;
1206- ReThrowError (edata );
1207- }
1208-
1209- if (rv == NULL || PyErr_Occurred ())
1210- {
1211- Py_XDECREF (rv );
1186+ /* If the Python code returned an error, propagate it */
1187+ if (rv == NULL )
12121188PLy_elog (ERROR ,NULL );
1213- }
12141189
12151190return rv ;
12161191}
@@ -2862,13 +2837,6 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
28622837void * tmpplan ;
28632838volatile MemoryContext oldcontext ;
28642839
2865- /* Can't execute more if we have an unhandled error */
2866- if (PLy_error_in_progress )
2867- {
2868- PLy_exception_set (PLy_exc_error ,"transaction aborted" );
2869- return NULL ;
2870- }
2871-
28722840if (!PyArg_ParseTuple (args ,"s|O" ,& query ,& list ))
28732841{
28742842PLy_exception_set (PLy_exc_spi_error ,
@@ -2978,15 +2946,18 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
29782946}
29792947PG_CATCH ();
29802948{
2949+ ErrorData * edata ;
2950+
29812951MemoryContextSwitchTo (oldcontext );
2982- PLy_error_in_progress = CopyErrorData ();
2952+ edata = CopyErrorData ();
29832953FlushErrorState ();
29842954Py_DECREF (plan );
29852955Py_XDECREF (optr );
29862956if (!PyErr_Occurred ())
29872957PLy_exception_set (PLy_exc_spi_error ,
29882958"unrecognized error in PLy_spi_prepare" );
29892959PLy_elog (WARNING ,NULL );
2960+ PLy_spi_exception_set (edata );
29902961return NULL ;
29912962}
29922963PG_END_TRY ();
@@ -3005,13 +2976,6 @@ PLy_spi_execute(PyObject *self, PyObject *args)
30052976PyObject * list = NULL ;
30062977long limit = 0 ;
30072978
3008- /* Can't execute more if we have an unhandled error */
3009- if (PLy_error_in_progress )
3010- {
3011- PLy_exception_set (PLy_exc_error ,"transaction aborted" );
3012- return NULL ;
3013- }
3014-
30152979if (PyArg_ParseTuple (args ,"s|l" ,& query ,& limit ))
30162980return PLy_spi_execute_query (query ,limit );
30172981
@@ -3116,9 +3080,10 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
31163080PG_CATCH ();
31173081{
31183082int k ;
3083+ ErrorData * edata ;
31193084
31203085MemoryContextSwitchTo (oldcontext );
3121- PLy_error_in_progress = CopyErrorData ();
3086+ edata = CopyErrorData ();
31223087FlushErrorState ();
31233088
31243089/*
@@ -3138,6 +3103,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
31383103PLy_exception_set (PLy_exc_error ,
31393104"unrecognized error in PLy_spi_execute_plan" );
31403105PLy_elog (WARNING ,NULL );
3106+ PLy_spi_exception_set (edata );
31413107return NULL ;
31423108}
31433109PG_END_TRY ();
@@ -3152,14 +3118,6 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
31523118}
31533119}
31543120
3155- if (rv < 0 )
3156- {
3157- PLy_exception_set (PLy_exc_spi_error ,
3158- "SPI_execute_plan failed: %s" ,
3159- SPI_result_code_string (rv ));
3160- return NULL ;
3161- }
3162-
31633121return PLy_spi_execute_fetch_result (SPI_tuptable ,SPI_processed ,rv );
31643122}
31653123
@@ -3177,13 +3135,16 @@ PLy_spi_execute_query(char *query, long limit)
31773135}
31783136PG_CATCH ();
31793137{
3138+ ErrorData * edata ;
3139+
31803140MemoryContextSwitchTo (oldcontext );
3181- PLy_error_in_progress = CopyErrorData ();
3141+ edata = CopyErrorData ();
31823142FlushErrorState ();
31833143if (!PyErr_Occurred ())
31843144PLy_exception_set (PLy_exc_spi_error ,
31853145"unrecognized error in PLy_spi_execute_query" );
31863146PLy_elog (WARNING ,NULL );
3147+ PLy_spi_exception_set (edata );
31873148return NULL ;
31883149}
31893150PG_END_TRY ();
@@ -3244,8 +3205,6 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
32443205PG_CATCH ();
32453206{
32463207MemoryContextSwitchTo (oldcontext );
3247- PLy_error_in_progress = CopyErrorData ();
3248- FlushErrorState ();
32493208if (!PyErr_Occurred ())
32503209PLy_exception_set (PLy_exc_error ,
32513210"unrecognized error in PLy_spi_execute_fetch_result" );
@@ -3502,23 +3461,20 @@ PLy_output(volatile int level, PyObject *self, PyObject *args)
35023461}
35033462PG_CATCH ();
35043463{
3464+ ErrorData * edata ;
3465+
35053466MemoryContextSwitchTo (oldcontext );
3506- PLy_error_in_progress = CopyErrorData ();
3467+ edata = CopyErrorData ();
35073468FlushErrorState ();
35083469
3509- PyErr_SetString (PLy_exc_error ,sv );
3510-
35113470/*
35123471 * Note: If sv came from PyString_AsString(), it points into storage
35133472 * owned by so. So free so after using sv.
35143473 */
35153474Py_XDECREF (so );
35163475
3517- /*
3518- * returning NULL here causes the python interpreter to bail. when
3519- * control passes back to PLy_procedure_call, we check for PG
3520- * exceptions and re-throw the error.
3521- */
3476+ /* Make Python raise the exception */
3477+ PLy_exception_set (PLy_exc_error ,edata -> message );
35223478return NULL ;
35233479}
35243480PG_END_TRY ();
@@ -3584,6 +3540,48 @@ PLy_exception_set_plural(PyObject *exc,
35843540PyErr_SetString (exc ,buf );
35853541}
35863542
3543+ /*
3544+ * Raise a SPIError, passing in it more error details, like the
3545+ * internal query and error position.
3546+ */
3547+ static void
3548+ PLy_spi_exception_set (ErrorData * edata )
3549+ {
3550+ PyObject * args = NULL ;
3551+ PyObject * spierror = NULL ;
3552+ PyObject * spidata = NULL ;
3553+
3554+ args = Py_BuildValue ("(s)" ,edata -> message );
3555+ if (!args )
3556+ gotofailure ;
3557+
3558+ /* create a new SPIError with the error message as the parameter */
3559+ spierror = PyObject_CallObject (PLy_exc_spi_error ,args );
3560+ if (!spierror )
3561+ gotofailure ;
3562+
3563+ spidata = Py_BuildValue ("(zzi)" ,edata -> hint ,
3564+ edata -> internalquery ,edata -> internalpos );
3565+ if (!spidata )
3566+ gotofailure ;
3567+
3568+ if (PyObject_SetAttrString (spierror ,"spidata" ,spidata )== -1 )
3569+ gotofailure ;
3570+
3571+ PyErr_SetObject (PLy_exc_spi_error ,spierror );
3572+
3573+ Py_DECREF (args );
3574+ Py_DECREF (spierror );
3575+ Py_DECREF (spidata );
3576+ return ;
3577+
3578+ failure :
3579+ Py_XDECREF (args );
3580+ Py_XDECREF (spierror );
3581+ Py_XDECREF (spidata );
3582+ elog (ERROR ,"could not convert SPI error to Python exception" );
3583+ }
3584+
35873585/* Emit a PG error or notice, together with any available info about
35883586 * the current Python error, previously set by PLy_exception_set().
35893587 * This should be used to propagate Python errors into PG.If fmt is
@@ -3596,6 +3594,15 @@ PLy_elog(int elevel, const char *fmt,...)
35963594char * xmsg ;
35973595int xlevel ;
35983596StringInfoData emsg ;
3597+ PyObject * exc ,* val ,* tb ;
3598+ char * hint = NULL ;
3599+ char * query = NULL ;
3600+ int position = 0 ;
3601+
3602+ PyErr_Fetch (& exc ,& val ,& tb );
3603+ if (exc != NULL && PyErr_GivenExceptionMatches (val ,PLy_exc_spi_error ))
3604+ PLy_get_spi_error_data (val ,& hint ,& query ,& position );
3605+ PyErr_Restore (exc ,val ,tb );
35993606
36003607xmsg = PLy_traceback (& xlevel );
36013608
@@ -3621,10 +3628,16 @@ PLy_elog(int elevel, const char *fmt,...)
36213628if (fmt )
36223629ereport (elevel ,
36233630(errmsg ("PL/Python: %s" ,emsg .data ),
3624- (xmsg ) ?errdetail ("%s" ,xmsg ) :0 ));
3631+ (xmsg ) ?errdetail ("%s" ,xmsg ) :0 ,
3632+ (hint ) ?errhint (hint ) :0 ,
3633+ (query ) ?internalerrquery (query ) :0 ,
3634+ (position ) ?internalerrposition (position ) :0 ));
36253635else
36263636ereport (elevel ,
3627- (errmsg ("PL/Python: %s" ,xmsg )));
3637+ (errmsg ("PL/Python: %s" ,xmsg ),
3638+ (hint ) ?errhint (hint ) :0 ,
3639+ (query ) ?internalerrquery (query ) :0 ,
3640+ (position ) ?internalerrposition (position ) :0 ));
36283641}
36293642PG_CATCH ();
36303643{
@@ -3642,6 +3655,28 @@ PLy_elog(int elevel, const char *fmt,...)
36423655pfree (xmsg );
36433656}
36443657
3658+ /*
3659+ * Extract the error data from a SPIError
3660+ */
3661+ static void
3662+ PLy_get_spi_error_data (PyObject * exc ,char * * hint ,char * * query ,int * position )
3663+ {
3664+ PyObject * spidata = NULL ;
3665+
3666+ spidata = PyObject_GetAttrString (exc ,"spidata" );
3667+ if (!spidata )
3668+ gotocleanup ;
3669+
3670+ if (!PyArg_ParseTuple (spidata ,"zzi" ,hint ,query ,position ))
3671+ gotocleanup ;
3672+
3673+ cleanup :
3674+ PyErr_Clear ();
3675+ /* no elog here, we simply won't report the errhint, errposition etc */
3676+ Py_XDECREF (spidata );
3677+ }
3678+
3679+
36453680static char *
36463681PLy_traceback (int * xlevel )
36473682{