mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +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:
		@@ -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 $$
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -343,7 +343,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);
 | 
				
			||||||
@@ -4404,7 +4404,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;
 | 
				
			||||||
@@ -4444,6 +4444,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;
 | 
				
			||||||
@@ -4453,7 +4454,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;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -4494,7 +4495,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,
 | 
				
			||||||
@@ -4525,7 +4527,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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4533,7 +4535,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 $$
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user