@@ -284,13 +284,17 @@ PLy_exception_set_plural(PyObject *, const char *, const char *,
284
284
__attribute__((format (printf , 2 , 5 )))
285
285
__attribute__((format (printf , 3 , 5 )));
286
286
287
+ /* like PLy_exception_set, but conserve more fields from ErrorData */
288
+ static void PLy_spi_exception_set (ErrorData * edata );
289
+
287
290
/* Get the innermost python procedure called from the backend */
288
291
static char * PLy_procedure_name (PLyProcedure * );
289
292
290
293
/* some utility functions */
291
294
static void
292
295
PLy_elog (int , const char * ,...)
293
296
__attribute__((format (printf , 2 , 3 )));
297
+ static void PLy_get_spi_error_data (PyObject * exc , char * * hint , char * * query , int * position );
294
298
static char * PLy_traceback (int * );
295
299
296
300
static void * PLy_malloc (size_t );
@@ -364,19 +368,6 @@ static HeapTuple PLyObject_ToTuple(PLyTypeInfo *, PyObject *);
364
368
*/
365
369
static PLyProcedure * PLy_curr_procedure = NULL ;
366
370
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
-
380
371
static PyObject * PLy_interp_globals = NULL ;
381
372
static PyObject * PLy_interp_safe_globals = NULL ;
382
373
static HTAB * PLy_procedure_cache = NULL ;
@@ -597,7 +588,6 @@ PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
597
588
plrv = PLy_procedure_call (proc , "TD" , plargs );
598
589
599
590
Assert (plrv != NULL );
600
- Assert (!PLy_error_in_progress );
601
591
602
592
/*
603
593
* Disconnect from SPI manager
@@ -1015,7 +1005,6 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *proc)
1015
1005
PLy_function_delete_args (proc );
1016
1006
}
1017
1007
Assert (plrv != NULL );
1018
- Assert (!PLy_error_in_progress );
1019
1008
}
1020
1009
1021
1010
/*
@@ -1194,23 +1183,9 @@ PLy_procedure_call(PLyProcedure *proc, char *kargs, PyObject *vargs)
1194
1183
rv = PyEval_EvalCode ((PyCodeObject * ) proc -> code ,
1195
1184
proc -> globals , proc -> globals );
1196
1185
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 )
1212
1188
PLy_elog (ERROR , NULL );
1213
- }
1214
1189
1215
1190
return rv ;
1216
1191
}
@@ -2862,13 +2837,6 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
2862
2837
void * tmpplan ;
2863
2838
volatile MemoryContext oldcontext ;
2864
2839
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
-
2872
2840
if (!PyArg_ParseTuple (args , "s|O" , & query , & list ))
2873
2841
{
2874
2842
PLy_exception_set (PLy_exc_spi_error ,
@@ -2978,15 +2946,18 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
2978
2946
}
2979
2947
PG_CATCH ();
2980
2948
{
2949
+ ErrorData * edata ;
2950
+
2981
2951
MemoryContextSwitchTo (oldcontext );
2982
- PLy_error_in_progress = CopyErrorData ();
2952
+ edata = CopyErrorData ();
2983
2953
FlushErrorState ();
2984
2954
Py_DECREF (plan );
2985
2955
Py_XDECREF (optr );
2986
2956
if (!PyErr_Occurred ())
2987
2957
PLy_exception_set (PLy_exc_spi_error ,
2988
2958
"unrecognized error in PLy_spi_prepare" );
2989
2959
PLy_elog (WARNING , NULL );
2960
+ PLy_spi_exception_set (edata );
2990
2961
return NULL ;
2991
2962
}
2992
2963
PG_END_TRY ();
@@ -3005,13 +2976,6 @@ PLy_spi_execute(PyObject *self, PyObject *args)
3005
2976
PyObject * list = NULL ;
3006
2977
long limit = 0 ;
3007
2978
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
-
3015
2979
if (PyArg_ParseTuple (args , "s|l" , & query , & limit ))
3016
2980
return PLy_spi_execute_query (query , limit );
3017
2981
@@ -3116,9 +3080,10 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
3116
3080
PG_CATCH ();
3117
3081
{
3118
3082
int k ;
3083
+ ErrorData * edata ;
3119
3084
3120
3085
MemoryContextSwitchTo (oldcontext );
3121
- PLy_error_in_progress = CopyErrorData ();
3086
+ edata = CopyErrorData ();
3122
3087
FlushErrorState ();
3123
3088
3124
3089
/*
@@ -3138,6 +3103,7 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
3138
3103
PLy_exception_set (PLy_exc_error ,
3139
3104
"unrecognized error in PLy_spi_execute_plan" );
3140
3105
PLy_elog (WARNING , NULL );
3106
+ PLy_spi_exception_set (edata );
3141
3107
return NULL ;
3142
3108
}
3143
3109
PG_END_TRY ();
@@ -3152,14 +3118,6 @@ PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
3152
3118
}
3153
3119
}
3154
3120
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
-
3163
3121
return PLy_spi_execute_fetch_result (SPI_tuptable , SPI_processed , rv );
3164
3122
}
3165
3123
@@ -3177,13 +3135,16 @@ PLy_spi_execute_query(char *query, long limit)
3177
3135
}
3178
3136
PG_CATCH ();
3179
3137
{
3138
+ ErrorData * edata ;
3139
+
3180
3140
MemoryContextSwitchTo (oldcontext );
3181
- PLy_error_in_progress = CopyErrorData ();
3141
+ edata = CopyErrorData ();
3182
3142
FlushErrorState ();
3183
3143
if (!PyErr_Occurred ())
3184
3144
PLy_exception_set (PLy_exc_spi_error ,
3185
3145
"unrecognized error in PLy_spi_execute_query" );
3186
3146
PLy_elog (WARNING , NULL );
3147
+ PLy_spi_exception_set (edata );
3187
3148
return NULL ;
3188
3149
}
3189
3150
PG_END_TRY ();
@@ -3244,8 +3205,6 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
3244
3205
PG_CATCH ();
3245
3206
{
3246
3207
MemoryContextSwitchTo (oldcontext );
3247
- PLy_error_in_progress = CopyErrorData ();
3248
- FlushErrorState ();
3249
3208
if (!PyErr_Occurred ())
3250
3209
PLy_exception_set (PLy_exc_error ,
3251
3210
"unrecognized error in PLy_spi_execute_fetch_result" );
@@ -3502,23 +3461,20 @@ PLy_output(volatile int level, PyObject *self, PyObject *args)
3502
3461
}
3503
3462
PG_CATCH ();
3504
3463
{
3464
+ ErrorData * edata ;
3465
+
3505
3466
MemoryContextSwitchTo (oldcontext );
3506
- PLy_error_in_progress = CopyErrorData ();
3467
+ edata = CopyErrorData ();
3507
3468
FlushErrorState ();
3508
3469
3509
- PyErr_SetString (PLy_exc_error , sv );
3510
-
3511
3470
/*
3512
3471
* Note: If sv came from PyString_AsString(), it points into storage
3513
3472
* owned by so. So free so after using sv.
3514
3473
*/
3515
3474
Py_XDECREF (so );
3516
3475
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 );
3522
3478
return NULL ;
3523
3479
}
3524
3480
PG_END_TRY ();
@@ -3584,6 +3540,48 @@ PLy_exception_set_plural(PyObject *exc,
3584
3540
PyErr_SetString (exc , buf );
3585
3541
}
3586
3542
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
+
3587
3585
/* Emit a PG error or notice, together with any available info about
3588
3586
* the current Python error, previously set by PLy_exception_set().
3589
3587
* This should be used to propagate Python errors into PG. If fmt is
@@ -3596,6 +3594,15 @@ PLy_elog(int elevel, const char *fmt,...)
3596
3594
char * xmsg ;
3597
3595
int xlevel ;
3598
3596
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 );
3599
3606
3600
3607
xmsg = PLy_traceback (& xlevel );
3601
3608
@@ -3621,10 +3628,16 @@ PLy_elog(int elevel, const char *fmt,...)
3621
3628
if (fmt )
3622
3629
ereport (elevel ,
3623
3630
(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 ));
3625
3635
else
3626
3636
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 ));
3628
3641
}
3629
3642
PG_CATCH ();
3630
3643
{
@@ -3642,6 +3655,28 @@ PLy_elog(int elevel, const char *fmt,...)
3642
3655
pfree (xmsg );
3643
3656
}
3644
3657
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
+
3645
3680
static char *
3646
3681
PLy_traceback (int * xlevel )
3647
3682
{
0 commit comments