mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Preserve SQLSTATE when an SPI error is propagated through PL/python
exception handler. This was a regression in 9.1, when the capability to catch specific SPI errors was added, so backpatch to 9.1. Mika Eloranta, with some editing by Jan Urbański.
This commit is contained in:
parent
5df1403b0f
commit
f21fc7f9fc
@ -351,6 +351,28 @@ CONTEXT: PL/Python function "specific_exception"
|
|||||||
|
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
/* SPI errors in PL/Python functions should preserve the SQLSTATE value
|
||||||
|
*/
|
||||||
|
CREATE FUNCTION python_unique_violation() RETURNS void AS $$
|
||||||
|
plpy.execute("insert into specific values (1)")
|
||||||
|
plpy.execute("insert into specific values (1)")
|
||||||
|
$$ LANGUAGE plpythonu;
|
||||||
|
CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
perform python_unique_violation();
|
||||||
|
exception when unique_violation then
|
||||||
|
return 'ok';
|
||||||
|
end;
|
||||||
|
return 'not reached';
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
SELECT catch_python_unique_violation();
|
||||||
|
catch_python_unique_violation
|
||||||
|
-------------------------------
|
||||||
|
ok
|
||||||
|
(1 row)
|
||||||
|
|
||||||
/* manually starting subtransactions - a bad idea
|
/* manually starting subtransactions - a bad idea
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION manual_subxact() RETURNS void AS $$
|
CREATE FUNCTION manual_subxact() RETURNS void AS $$
|
||||||
|
@ -351,6 +351,28 @@ CONTEXT: PL/Python function "specific_exception"
|
|||||||
|
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
/* SPI errors in PL/Python functions should preserve the SQLSTATE value
|
||||||
|
*/
|
||||||
|
CREATE FUNCTION python_unique_violation() RETURNS void AS $$
|
||||||
|
plpy.execute("insert into specific values (1)")
|
||||||
|
plpy.execute("insert into specific values (1)")
|
||||||
|
$$ LANGUAGE plpythonu;
|
||||||
|
CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
perform python_unique_violation();
|
||||||
|
exception when unique_violation then
|
||||||
|
return 'ok';
|
||||||
|
end;
|
||||||
|
return 'not reached';
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
SELECT catch_python_unique_violation();
|
||||||
|
catch_python_unique_violation
|
||||||
|
-------------------------------
|
||||||
|
ok
|
||||||
|
(1 row)
|
||||||
|
|
||||||
/* manually starting subtransactions - a bad idea
|
/* manually starting subtransactions - a bad idea
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION manual_subxact() RETURNS void AS $$
|
CREATE FUNCTION manual_subxact() RETURNS void AS $$
|
||||||
|
@ -383,7 +383,7 @@ static char *PLy_procedure_name(PLyProcedure *);
|
|||||||
static void
|
static void
|
||||||
PLy_elog(int, const char *,...)
|
PLy_elog(int, const char *,...)
|
||||||
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
|
__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
|
||||||
static void PLy_get_spi_error_data(PyObject *exc, char **detail, char **hint, char **query, int *position);
|
static void PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hint, char **query, int *position);
|
||||||
static void PLy_traceback(char **, char **, int *);
|
static void PLy_traceback(char **, char **, int *);
|
||||||
|
|
||||||
static void *PLy_malloc(size_t);
|
static void *PLy_malloc(size_t);
|
||||||
@ -4441,7 +4441,7 @@ PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
|
|||||||
if (!spierror)
|
if (!spierror)
|
||||||
goto failure;
|
goto failure;
|
||||||
|
|
||||||
spidata = Py_BuildValue("(zzzi)", edata->detail, edata->hint,
|
spidata = Py_BuildValue("(izzzi)", edata->sqlerrcode, edata->detail, edata->hint,
|
||||||
edata->internalquery, edata->internalpos);
|
edata->internalquery, edata->internalpos);
|
||||||
if (!spidata)
|
if (!spidata)
|
||||||
goto failure;
|
goto failure;
|
||||||
@ -4481,6 +4481,7 @@ PLy_elog(int elevel, const char *fmt,...)
|
|||||||
*val,
|
*val,
|
||||||
*tb;
|
*tb;
|
||||||
const char *primary = NULL;
|
const char *primary = NULL;
|
||||||
|
int sqlerrcode = 0;
|
||||||
char *detail = NULL;
|
char *detail = NULL;
|
||||||
char *hint = NULL;
|
char *hint = NULL;
|
||||||
char *query = NULL;
|
char *query = NULL;
|
||||||
@ -4490,7 +4491,7 @@ PLy_elog(int elevel, const char *fmt,...)
|
|||||||
if (exc != NULL)
|
if (exc != NULL)
|
||||||
{
|
{
|
||||||
if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
|
if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
|
||||||
PLy_get_spi_error_data(val, &detail, &hint, &query, &position);
|
PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position);
|
||||||
else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
|
else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
|
||||||
elevel = FATAL;
|
elevel = FATAL;
|
||||||
}
|
}
|
||||||
@ -4531,7 +4532,8 @@ PLy_elog(int elevel, const char *fmt,...)
|
|||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
ereport(elevel,
|
ereport(elevel,
|
||||||
(errmsg_internal("%s", primary ? primary : "no exception data"),
|
(errcode(sqlerrcode ? sqlerrcode : ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg_internal("%s", primary ? primary : "no exception data"),
|
||||||
(detail) ? errdetail_internal("%s", detail) : 0,
|
(detail) ? errdetail_internal("%s", detail) : 0,
|
||||||
(tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
|
(tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
|
||||||
(hint) ? errhint("%s", hint) : 0,
|
(hint) ? errhint("%s", hint) : 0,
|
||||||
@ -4562,7 +4564,7 @@ PLy_elog(int elevel, const char *fmt,...)
|
|||||||
* Extract the error data from a SPIError
|
* Extract the error data from a SPIError
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
PLy_get_spi_error_data(PyObject *exc, char **detail, char **hint, char **query, int *position)
|
PLy_get_spi_error_data(PyObject *exc, int* sqlerrcode, char **detail, char **hint, char **query, int *position)
|
||||||
{
|
{
|
||||||
PyObject *spidata = NULL;
|
PyObject *spidata = NULL;
|
||||||
|
|
||||||
@ -4570,7 +4572,7 @@ PLy_get_spi_error_data(PyObject *exc, char **detail, char **hint, char **query,
|
|||||||
if (!spidata)
|
if (!spidata)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(spidata, "zzzi", detail, hint, query, position))
|
if (!PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
@ -257,6 +257,26 @@ SELECT specific_exception(2);
|
|||||||
SELECT specific_exception(NULL);
|
SELECT specific_exception(NULL);
|
||||||
SELECT specific_exception(2);
|
SELECT specific_exception(2);
|
||||||
|
|
||||||
|
/* SPI errors in PL/Python functions should preserve the SQLSTATE value
|
||||||
|
*/
|
||||||
|
CREATE FUNCTION python_unique_violation() RETURNS void AS $$
|
||||||
|
plpy.execute("insert into specific values (1)")
|
||||||
|
plpy.execute("insert into specific values (1)")
|
||||||
|
$$ LANGUAGE plpythonu;
|
||||||
|
|
||||||
|
CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
|
||||||
|
begin
|
||||||
|
begin
|
||||||
|
perform python_unique_violation();
|
||||||
|
exception when unique_violation then
|
||||||
|
return 'ok';
|
||||||
|
end;
|
||||||
|
return 'not reached';
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
|
||||||
|
SELECT catch_python_unique_violation();
|
||||||
|
|
||||||
/* manually starting subtransactions - a bad idea
|
/* manually starting subtransactions - a bad idea
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION manual_subxact() RETURNS void AS $$
|
CREATE FUNCTION manual_subxact() RETURNS void AS $$
|
||||||
|
Loading…
x
Reference in New Issue
Block a user