Commit 316186f2 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Handle SPIErrors raised directly in PL/Python code.

If a PL/Python function raises an SPIError (or one if its subclasses)
directly with python's raise statement, treat it the same as an SPIError
generated internally. In particular, if the user sets the sqlstate
attribute, preserve that.

Oskari Saarenmaa and Jan Urbański, reviewed by Karl O. Pinc.
parent 96bb29dc
...@@ -400,3 +400,29 @@ CONTEXT: Traceback (most recent call last): ...@@ -400,3 +400,29 @@ CONTEXT: Traceback (most recent call last):
PL/Python function "manual_subxact_prepared", line 4, in <module> PL/Python function "manual_subxact_prepared", line 4, in <module>
plpy.execute(save) plpy.execute(save)
PL/Python function "manual_subxact_prepared" 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;
...@@ -400,3 +400,29 @@ CONTEXT: Traceback (most recent call last): ...@@ -400,3 +400,29 @@ CONTEXT: Traceback (most recent call last):
PL/Python function "manual_subxact_prepared", line 4, in <module> PL/Python function "manual_subxact_prepared", line 4, in <module>
plpy.execute(save) plpy.execute(save)
PL/Python function "manual_subxact_prepared" 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;
...@@ -336,6 +336,31 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth) ...@@ -336,6 +336,31 @@ PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
Py_DECREF(e); 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 * Extract the error data from a SPIError
*/ */
...@@ -345,13 +370,20 @@ PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hin ...@@ -345,13 +370,20 @@ PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hin
PyObject *spidata = NULL; PyObject *spidata = NULL;
spidata = PyObject_GetAttrString(exc, "spidata"); spidata = PyObject_GetAttrString(exc, "spidata");
if (!spidata)
goto cleanup;
if (!PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position)) if (spidata != NULL)
goto cleanup; {
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(); PyErr_Clear();
/* no elog here, we simply won't report the errhint, errposition etc */ /* no elog here, we simply won't report the errhint, errposition etc */
Py_XDECREF(spidata); Py_XDECREF(spidata);
......
...@@ -298,3 +298,33 @@ plpy.execute(rollback) ...@@ -298,3 +298,33 @@ plpy.execute(rollback)
$$ LANGUAGE plpythonu; $$ LANGUAGE plpythonu;
SELECT manual_subxact_prepared(); 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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment