@@ -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)
597588 plrv = PLy_procedure_call (proc , "TD" , plargs );
598589
599590 Assert (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)
10151005 PLy_function_delete_args (proc );
10161006 }
10171007 Assert (plrv != NULL );
1018- Assert (!PLy_error_in_progress );
10191008 }
10201009
10211010 /*
@@ -1194,23 +1183,9 @@ PLy_procedure_call(PLyProcedure *proc, char *kargs, PyObject *vargs)
11941183 rv = PyEval_EvalCode ((PyCodeObject * ) proc -> code ,
11951184 proc -> 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 )
12121188 PLy_elog (ERROR , NULL );
1213- }
12141189
12151190 return rv ;
12161191}
@@ -2862,13 +2837,6 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
28622837 void * tmpplan ;
28632838 volatile 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-
28722840 if (!PyArg_ParseTuple (args , "s|O" , & query , & list ))
28732841 {
28742842 PLy_exception_set (PLy_exc_spi_error ,
@@ -2978,15 +2946,18 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
29782946 }
29792947 PG_CATCH ();
29802948 {
2949+ ErrorData * edata ;
2950+
29812951 MemoryContextSwitchTo (oldcontext );
2982- PLy_error_in_progress = CopyErrorData ();
2952+ edata = CopyErrorData ();
29832953 FlushErrorState ();
29842954 Py_DECREF (plan );
29852955 Py_XDECREF (optr );
29862956 if (!PyErr_Occurred ())
29872957 PLy_exception_set (PLy_exc_spi_error ,
29882958 "unrecognized error in PLy_spi_prepare" );
29892959 PLy_elog (WARNING , NULL );
2960+ PLy_spi_exception_set (edata );
29902961 return NULL ;
29912962 }
29922963 PG_END_TRY ();
@@ -3005,13 +2976,6 @@ PLy_spi_execute(PyObject *self, PyObject *args)
30052976 PyObject * list = NULL ;
30062977 long 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-
30152979 if (PyArg_ParseTuple (args , "s|l" , & query , & limit ))
30162980 return PLy_spi_execute_query (query , limit );
30172981
@@ -3116,9 +3080,10 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
31163080 PG_CATCH ();
31173081 {
31183082 int k ;
3083+ ErrorData * edata ;
31193084
31203085 MemoryContextSwitchTo (oldcontext );
3121- PLy_error_in_progress = CopyErrorData ();
3086+ edata = CopyErrorData ();
31223087 FlushErrorState ();
31233088
31243089 /*
@@ -3138,6 +3103,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
31383103 PLy_exception_set (PLy_exc_error ,
31393104 "unrecognized error in PLy_spi_execute_plan" );
31403105 PLy_elog (WARNING , NULL );
3106+ PLy_spi_exception_set (edata );
31413107 return NULL ;
31423108 }
31433109 PG_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-
31633121 return 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 }
31783136 PG_CATCH ();
31793137 {
3138+ ErrorData * edata ;
3139+
31803140 MemoryContextSwitchTo (oldcontext );
3181- PLy_error_in_progress = CopyErrorData ();
3141+ edata = CopyErrorData ();
31823142 FlushErrorState ();
31833143 if (!PyErr_Occurred ())
31843144 PLy_exception_set (PLy_exc_spi_error ,
31853145 "unrecognized error in PLy_spi_execute_query" );
31863146 PLy_elog (WARNING , NULL );
3147+ PLy_spi_exception_set (edata );
31873148 return NULL ;
31883149 }
31893150 PG_END_TRY ();
@@ -3244,8 +3205,6 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
32443205 PG_CATCH ();
32453206 {
32463207 MemoryContextSwitchTo (oldcontext );
3247- PLy_error_in_progress = CopyErrorData ();
3248- FlushErrorState ();
32493208 if (!PyErr_Occurred ())
32503209 PLy_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 }
35033462 PG_CATCH ();
35043463 {
3464+ ErrorData * edata ;
3465+
35053466 MemoryContextSwitchTo (oldcontext );
3506- PLy_error_in_progress = CopyErrorData ();
3467+ edata = CopyErrorData ();
35073468 FlushErrorState ();
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 */
35153474 Py_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 );
35223478 return NULL ;
35233479 }
35243480 PG_END_TRY ();
@@ -3584,6 +3540,48 @@ PLy_exception_set_plural(PyObject *exc,
35843540 PyErr_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+ goto failure ;
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+ goto failure ;
3562+
3563+ spidata = Py_BuildValue ("(zzi)" , edata -> hint ,
3564+ edata -> internalquery , edata -> internalpos );
3565+ if (!spidata )
3566+ goto failure ;
3567+
3568+ if (PyObject_SetAttrString (spierror , "spidata" , spidata ) == -1 )
3569+ goto failure ;
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,...)
35963594 char * xmsg ;
35973595 int xlevel ;
35983596 StringInfoData 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
36003607 xmsg = PLy_traceback (& xlevel );
36013608
@@ -3621,10 +3628,16 @@ PLy_elog(int elevel, const char *fmt,...)
36213628 if (fmt )
36223629 ereport (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 ));
36253635 else
36263636 ereport (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 }
36293642 PG_CATCH ();
36303643 {
@@ -3642,6 +3655,28 @@ PLy_elog(int elevel, const char *fmt,...)
36423655 pfree (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+ goto cleanup ;
3669+
3670+ if (!PyArg_ParseTuple (spidata , "zzi" , hint , query , position ))
3671+ goto cleanup ;
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{
0 commit comments