diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out index e1ec9c2c1322b5861f248f8cd0dca0a73faa63cd..be2ec9708ad1fb0e067308c780e1242e21b309a6 100644 --- a/src/pl/plpython/expected/plpython_error.out +++ b/src/pl/plpython/expected/plpython_error.out @@ -400,3 +400,29 @@ CONTEXT: Traceback (most recent call last): PL/Python function "manual_subxact_prepared", line 4, in <module> plpy.execute(save) PL/Python function "manual_subxact_prepared" +/* raising plpy.spiexception.* from python code should preserve sqlstate + */ +CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$ +raise plpy.spiexceptions.DivisionByZero() +$$ LANGUAGE plpythonu; +DO $$ +BEGIN + SELECT plpy_raise_spiexception(); +EXCEPTION WHEN division_by_zero THEN + -- NOOP +END +$$ LANGUAGE plpgsql; +/* setting a custom sqlstate should be handled + */ +CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$ +exc = plpy.spiexceptions.DivisionByZero() +exc.sqlstate = 'SILLY' +raise exc +$$ LANGUAGE plpythonu; +DO $$ +BEGIN + SELECT plpy_raise_spiexception_override(); +EXCEPTION WHEN SQLSTATE 'SILLY' THEN + -- NOOP +END +$$ LANGUAGE plpgsql; diff --git a/src/pl/plpython/expected/plpython_error_0.out b/src/pl/plpython/expected/plpython_error_0.out index 6f74a5038f08a67905f7622317691723e49d3f6f..39c63c547a41d1ade08bfd31ed6dbb39d04889dd 100644 --- a/src/pl/plpython/expected/plpython_error_0.out +++ b/src/pl/plpython/expected/plpython_error_0.out @@ -400,3 +400,29 @@ CONTEXT: Traceback (most recent call last): PL/Python function "manual_subxact_prepared", line 4, in <module> plpy.execute(save) PL/Python function "manual_subxact_prepared" +/* raising plpy.spiexception.* from python code should preserve sqlstate + */ +CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$ +raise plpy.spiexceptions.DivisionByZero() +$$ LANGUAGE plpythonu; +DO $$ +BEGIN + SELECT plpy_raise_spiexception(); +EXCEPTION WHEN division_by_zero THEN + -- NOOP +END +$$ LANGUAGE plpgsql; +/* setting a custom sqlstate should be handled + */ +CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$ +exc = plpy.spiexceptions.DivisionByZero() +exc.sqlstate = 'SILLY' +raise exc +$$ LANGUAGE plpythonu; +DO $$ +BEGIN + SELECT plpy_raise_spiexception_override(); +EXCEPTION WHEN SQLSTATE 'SILLY' THEN + -- NOOP +END +$$ LANGUAGE plpgsql; diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c index c375ac07fa8f12a2f7189a702e777fba956933e3..70450d7d9e5e5ce2b1101b97202a78858188110e 100644 --- a/src/pl/plpython/plpy_elog.c +++ b/src/pl/plpython/plpy_elog.c @@ -336,6 +336,31 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) Py_DECREF(e); } +/* + * Extract error code from SPIError's sqlstate attribute. + */ +static void +PLy_get_spi_sqlerrcode(PyObject *exc, int *sqlerrcode) +{ + PyObject *sqlstate; + char *buffer; + + sqlstate = PyObject_GetAttrString(exc, "sqlstate"); + if (sqlstate == NULL) + return; + + buffer = PyString_AsString(sqlstate); + if (strlen(buffer) == 5 && + strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5) + { + *sqlerrcode = MAKE_SQLSTATE(buffer[0], buffer[1], buffer[2], + buffer[3], buffer[4]); + } + + Py_DECREF(sqlstate); +} + + /* * Extract the error data from a SPIError */ @@ -345,13 +370,20 @@ PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hin PyObject *spidata = NULL; spidata = PyObject_GetAttrString(exc, "spidata"); - if (!spidata) - goto cleanup; - if (!PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position)) - goto cleanup; + if (spidata != NULL) + { + PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position); + } + else + { + /* + * If there's no spidata, at least set the sqlerrcode. This can happen + * if someone explicitly raises a SPI exception from Python code. + */ + PLy_get_spi_sqlerrcode(exc, sqlerrcode); + } -cleanup: PyErr_Clear(); /* no elog here, we simply won't report the errhint, errposition etc */ Py_XDECREF(spidata); diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql index 502bbec38f45858b7d89d1294175a64ce37fb786..d0df7e607d3e1ae32321d731ca88b56f9c74ec81 100644 --- a/src/pl/plpython/sql/plpython_error.sql +++ b/src/pl/plpython/sql/plpython_error.sql @@ -298,3 +298,33 @@ plpy.execute(rollback) $$ LANGUAGE plpythonu; SELECT manual_subxact_prepared(); + +/* raising plpy.spiexception.* from python code should preserve sqlstate + */ +CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$ +raise plpy.spiexceptions.DivisionByZero() +$$ LANGUAGE plpythonu; + +DO $$ +BEGIN + SELECT plpy_raise_spiexception(); +EXCEPTION WHEN division_by_zero THEN + -- NOOP +END +$$ LANGUAGE plpgsql; + +/* setting a custom sqlstate should be handled + */ +CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$ +exc = plpy.spiexceptions.DivisionByZero() +exc.sqlstate = 'SILLY' +raise exc +$$ LANGUAGE plpythonu; + +DO $$ +BEGIN + SELECT plpy_raise_spiexception_override(); +EXCEPTION WHEN SQLSTATE 'SILLY' THEN + -- NOOP +END +$$ LANGUAGE plpgsql;