2015-10-08 12:11 GMT+02:00 Pavel Stehule <pavel.steh...@gmail.com>:

> Hi
>
> We cannot to raise PostgreSQL exception with setting all possible fields.
> I propose new function
>
> plpy.ereport(level, [ message [, detail [, hint [, sqlstate, ... ]]]])
>
> The implementation will be based on keyword parameters, so only required
> parameters should be used.
>
> Examples:
>
> plpy.ereport(plpy.NOTICE, 'some message', 'some detai')
> plpy.ereport(plpy.ERROR, 'some message', sqlstate = 'zx243');
>

patch attached

regards

Pavel


>
> Comments, notices, objections?
>
> Regards
>
> Pavel
>
commit 2593493f583f06a784b5d0b56f5efa04e62ad07a
Author: Pavel Stehule <pavel.steh...@gooddata.com>
Date:   Thu Oct 8 20:51:17 2015 +0200

    initial

diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index 1f52af7..76d8b2c 100644
--- a/src/pl/plpython/expected/plpython_error.out
+++ b/src/pl/plpython/expected/plpython_error.out
@@ -422,3 +422,59 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN
 	-- NOOP
 END
 $$ LANGUAGE plpgsql;
+CREATE FUNCTION nested_error_ereport() RETURNS text
+	AS
+'def fun1():
+	raise plpy.ereport(plpy.ERROR, "boom")
+
+def fun2():
+	fun1()
+
+def fun3():
+	fun2()
+
+fun3()
+return "not reached"
+'
+	LANGUAGE plpythonu;
+SELECT nested_error_ereport();
+ERROR:  plpy.SPIError: boom
+CONTEXT:  Traceback (most recent call last):
+  PL/Python function "nested_error_ereport", line 10, in <module>
+    fun3()
+  PL/Python function "nested_error_ereport", line 8, in fun3
+    fun2()
+  PL/Python function "nested_error_ereport", line 5, in fun2
+    fun1()
+  PL/Python function "nested_error_ereport", line 2, in fun1
+    raise plpy.ereport(plpy.ERROR, "boom")
+PL/Python function "nested_error_ereport"
+CREATE FUNCTION ereport_test()
+RETURNS void AS $$
+  plpy.ereport(plpy.NOTICE, "some notice")
+$$ LANGUAGE plpythonu;
+SELECT ereport_test();
+NOTICE:  some notice
+ ereport_test 
+--------------
+ 
+(1 row)
+
+\set VERBOSITY verbose
+CREATE OR REPLACE FUNCTION ereport_test()
+RETURNS void AS $$
+  plpy.ereport(plpy.NOTICE, "some notice", "some detail", "any hint", schema='schema_yy', table='table_xx', column='column_zz')
+$$ LANGUAGE plpythonu;
+SELECT ereport_test();
+NOTICE:  00000: some notice
+DETAIL:  some detail
+HINT:  any hint
+SCHEMA NAME:  schema_yy
+TABLE NAME:  table_xx
+COLUMN NAME:  column_zz
+LOCATION:  PLy_ereport, plpy_plpymodule.c:428
+ ereport_test 
+--------------
+ 
+(1 row)
+
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index 7b76faf..cceb5b8 100644
--- a/src/pl/plpython/expected/plpython_test.out
+++ b/src/pl/plpython/expected/plpython_test.out
@@ -43,9 +43,9 @@ contents.sort()
 return ", ".join(contents)
 $$ LANGUAGE plpythonu;
 select module_contents();
-                                                                               module_contents                                                                                
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- Error, Fatal, SPIError, cursor, debug, error, execute, fatal, info, log, notice, prepare, quote_ident, quote_literal, quote_nullable, spiexceptions, subtransaction, warning
+                                                                                                         module_contents                                                                                                         
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ DEBUG, ERROR, Error, Fatal, INFO, LOG, NOTICE, SPIError, WARNING, cursor, debug, ereport, error, execute, fatal, info, log, notice, prepare, quote_ident, quote_literal, quote_nullable, spiexceptions, subtransaction, warning
 (1 row)
 
 CREATE FUNCTION elog_test() RETURNS void
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index 15406d6..6825732 100644
--- a/src/pl/plpython/plpy_elog.c
+++ b/src/pl/plpython/plpy_elog.c
@@ -21,9 +21,10 @@ PyObject   *PLy_exc_fatal = NULL;
 PyObject   *PLy_exc_spi_error = NULL;
 
 
-static void PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth);
 static void PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
-					   char **hint, char **query, int *position);
+					   char **hint, char **query, int *position,
+					   char **schema_name, char **table_name, char **column_name,
+					   char **datatype_name, char **constraint_name);
 static char *get_source_line(const char *src, int lineno);
 
 
@@ -51,12 +52,19 @@ PLy_elog(int elevel, const char *fmt,...)
 	char	   *hint = NULL;
 	char	   *query = NULL;
 	int			position = 0;
+	char	   *schema_name = NULL;
+	char	   *table_name = NULL;
+	char	   *column_name = NULL;
+	char	   *datatype_name = NULL;
+	char	   *constraint_name = NULL;
 
 	PyErr_Fetch(&exc, &val, &tb);
 	if (exc != NULL)
 	{
 		if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
-			PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position);
+			PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position,
+						&schema_name, &table_name, &column_name,
+						&datatype_name, &constraint_name);
 		else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
 			elevel = FATAL;
 	}
@@ -103,7 +111,12 @@ PLy_elog(int elevel, const char *fmt,...)
 				 (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
 				 (hint) ? errhint("%s", hint) : 0,
 				 (query) ? internalerrquery(query) : 0,
-				 (position) ? internalerrposition(position) : 0));
+				 (position) ? internalerrposition(position) : 0,
+				 (schema_name) ? err_generic_string(PG_DIAG_SCHEMA_NAME, schema_name) : 0,
+				 (table_name) ? err_generic_string(PG_DIAG_TABLE_NAME, table_name) : 0,
+				 (column_name) ? err_generic_string(PG_DIAG_COLUMN_NAME, column_name) : 0,
+				 (datatype_name) ? err_generic_string(PG_DIAG_DATATYPE_NAME, datatype_name) : 0,
+				 (constraint_name) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME, constraint_name) : 0));
 	}
 	PG_CATCH();
 	{
@@ -132,7 +145,7 @@ PLy_elog(int elevel, const char *fmt,...)
  * tbmsg (both as palloc'd strings) and the traceback depth in
  * tb_depth.
  */
-static void
+void
 PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
 {
 	PyObject   *e,
@@ -365,7 +378,9 @@ PLy_get_spi_sqlerrcode(PyObject *exc, int *sqlerrcode)
  * Extract the error data from a SPIError
  */
 static void
-PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, 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,
+			char **schema_name, char **table_name, char **column_name,
+			char **datatype_name, char **constraint_name)
 {
 	PyObject   *spidata = NULL;
 
@@ -373,7 +388,9 @@ PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hin
 
 	if (spidata != NULL)
 	{
-		PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position);
+		PyArg_ParseTuple(spidata, "izzzizzzzz", sqlerrcode, detail, hint, query, position,
+						    schema_name, table_name, column_name,
+						    datatype_name, constraint_name);
 	}
 	else
 	{
diff --git a/src/pl/plpython/plpy_elog.h b/src/pl/plpython/plpy_elog.h
index 94725c2..29a0d9b 100644
--- a/src/pl/plpython/plpy_elog.h
+++ b/src/pl/plpython/plpy_elog.h
@@ -17,4 +17,6 @@ extern void PLy_exception_set(PyObject *exc, const char *fmt,...) pg_attribute_p
 extern void PLy_exception_set_plural(PyObject *exc, const char *fmt_singular, const char *fmt_plural,
 	unsigned long n,...) pg_attribute_printf(2, 5) pg_attribute_printf(3, 5);
 
+extern void PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth);
+
 #endif   /* PLPY_ELOG_H */
diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c
index a44b7fb..7bc775c 100644
--- a/src/pl/plpython/plpy_plpymodule.c
+++ b/src/pl/plpython/plpy_plpymodule.c
@@ -23,7 +23,7 @@
 
 HTAB	   *PLy_spi_exceptions = NULL;
 
-
+static void PLy_add_constants(PyObject *plpy);
 static void PLy_add_exceptions(PyObject *plpy);
 static void PLy_generate_spi_exceptions(PyObject *mod, PyObject *base);
 
@@ -35,6 +35,7 @@ static PyObject *PLy_notice(PyObject *self, PyObject *args);
 static PyObject *PLy_warning(PyObject *self, PyObject *args);
 static PyObject *PLy_error(PyObject *self, PyObject *args);
 static PyObject *PLy_fatal(PyObject *self, PyObject *args);
+static PyObject *PLy_ereport(PyObject *self, PyObject *args, PyObject *kw);
 static PyObject *PLy_quote_literal(PyObject *self, PyObject *args);
 static PyObject *PLy_quote_nullable(PyObject *self, PyObject *args);
 static PyObject *PLy_quote_ident(PyObject *self, PyObject *args);
@@ -64,6 +65,7 @@ static PyMethodDef PLy_methods[] = {
 	{"warning", PLy_warning, METH_VARARGS, NULL},
 	{"error", PLy_error, METH_VARARGS, NULL},
 	{"fatal", PLy_fatal, METH_VARARGS, NULL},
+	{"ereport", (PyCFunction) PLy_ereport, METH_VARARGS | METH_KEYWORDS, NULL},
 
 	/*
 	 * create a stored plan
@@ -133,6 +135,7 @@ PyInit_plpy(void)
 	if (m == NULL)
 		return NULL;
 
+	PLy_add_constants(m);
 	PLy_add_exceptions(m);
 
 	return m;
@@ -163,6 +166,7 @@ PLy_init_plpy(void)
 	/* for Python 3 we initialized the exceptions in PyInit_plpy */
 #else
 	plpy = Py_InitModule("plpy", PLy_methods);
+	PLy_add_constants(plpy);
 	PLy_add_exceptions(plpy);
 #endif
 
@@ -181,6 +185,21 @@ PLy_init_plpy(void)
 		PLy_elog(ERROR, "could not import \"plpy\" module");
 }
 
+#define PYMODULE_ADD_CONSTANT(m, name, value) \
+if (PyModule_AddIntConstant(m, name, value) != 0) \
+	PLy_elog(ERROR, "could not import \"plpy\" constant")
+
+static void
+PLy_add_constants(PyObject *plpy)
+{
+	PYMODULE_ADD_CONSTANT(plpy, "DEBUG", DEBUG2);
+	PYMODULE_ADD_CONSTANT(plpy, "LOG", LOG);
+	PYMODULE_ADD_CONSTANT(plpy, "INFO", INFO);
+	PYMODULE_ADD_CONSTANT(plpy, "NOTICE", NOTICE);
+	PYMODULE_ADD_CONSTANT(plpy, "WARNING", WARNING);
+	PYMODULE_ADD_CONSTANT(plpy, "ERROR", ERROR);
+}
+
 static void
 PLy_add_exceptions(PyObject *plpy)
 {
@@ -316,6 +335,118 @@ PLy_fatal(PyObject *self, PyObject *args)
 }
 
 static PyObject *
+PLy_ereport(PyObject *self, PyObject *args, PyObject *kw)
+{
+	int level;
+	int sqlstate = 0;
+	const char *sqlstatestr = NULL;
+	const char *message = NULL;
+	const char *detail = NULL;
+	const char *hint = NULL;
+	const char *column = NULL;
+	const char *constraint = NULL;
+	const char *datatype = NULL;
+	const char *table = NULL;
+	const char *schema = NULL;
+	MemoryContext oldcontext ;
+
+	static char *kwlist[] = { "level","message", "detail", "hint",
+				  "sqlstate",
+				  "schema","table", "column",
+				  "datatype", "constraint",
+				  NULL };
+
+	if (!PyArg_ParseTupleAndKeywords(args, kw, "i|ssssssssssss", kwlist,
+			 &level, &message, &detail, &hint,
+			 &sqlstatestr,
+			 &schema, &table, &column,
+			 &datatype, &constraint))
+		return NULL;
+
+	switch (level)
+	{
+		case DEBUG2:
+		case LOG:
+		case INFO:
+		case NOTICE:
+		case WARNING:
+		case ERROR:
+			break;
+		default:
+			PLy_elog(ERROR, "ereport level is not valid in plpy.ereport");
+	}
+
+	if (sqlstatestr != NULL)
+	{
+		if (strlen(sqlstatestr) != 5)
+			PLy_elog(ERROR, "invalid SQLSTATE code");
+
+		if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
+			PLy_elog(ERROR, "invalid SQLSTATE code");
+
+		sqlstate = MAKE_SQLSTATE(sqlstatestr[0],
+							  sqlstatestr[1],
+							  sqlstatestr[2],
+							  sqlstatestr[3],
+							  sqlstatestr[4]);
+	}
+
+	oldcontext = CurrentMemoryContext;
+	PG_TRY();
+	{
+		if (message != NULL)
+			pg_verifymbstr(message, strlen(message), false);
+		if (detail != NULL)
+			pg_verifymbstr(detail, strlen(detail), false);
+		if (hint != NULL)
+			pg_verifymbstr(hint, strlen(hint), false);
+		if (schema != NULL)
+			pg_verifymbstr(schema, strlen(schema), false);
+		if (table != NULL)
+			pg_verifymbstr(table, strlen(table), false);
+		if (column != NULL)
+			pg_verifymbstr(column, strlen(column), false);
+		if (datatype != NULL)
+			pg_verifymbstr(datatype, strlen(datatype), false);
+		if (constraint != NULL)
+			pg_verifymbstr(constraint, strlen(constraint), false);
+
+		ereport(level,
+				((sqlstate != 0) ? errcode(sqlstate) : 0,
+				 (message != NULL) ? errmsg_internal("%s", message) : 0,
+				 (detail != NULL) ? errdetail_internal("%s", detail) : 0,
+				 (hint != NULL) ? errhint("%s", hint) : 0,
+				 (column != NULL) ?
+				 err_generic_string(PG_DIAG_COLUMN_NAME, column) : 0,
+				 (constraint != NULL) ?
+				 err_generic_string(PG_DIAG_CONSTRAINT_NAME, constraint) : 0,
+				 (datatype != NULL) ?
+				 err_generic_string(PG_DIAG_DATATYPE_NAME, datatype) : 0,
+				 (table != NULL) ?
+				 err_generic_string(PG_DIAG_TABLE_NAME, table) : 0,
+				 (schema != NULL) ?
+				 err_generic_string(PG_DIAG_SCHEMA_NAME, schema) : 0));
+	}
+	PG_CATCH();
+	{
+		ErrorData	*edata;
+
+		MemoryContextSwitchTo(oldcontext);
+		edata = CopyErrorData();
+		FlushErrorState();
+
+		PLy_spi_exception_set(PLy_exc_spi_error, edata);
+		FreeErrorData(edata);
+		return NULL;
+	}
+	PG_END_TRY();
+
+	Py_INCREF(Py_None);
+	return Py_None;
+
+}
+
+static PyObject *
 PLy_quote_literal(PyObject *self, PyObject *args)
 {
 	const char *str;
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index d0e255f..4f143d6 100644
--- a/src/pl/plpython/plpy_spi.c
+++ b/src/pl/plpython/plpy_spi.c
@@ -30,7 +30,6 @@
 static PyObject *PLy_spi_execute_query(char *query, long limit);
 static PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
 static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status);
-static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
 
 
 /* prepare(query="select * from foo")
@@ -532,7 +531,7 @@ PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
  * Raise a SPIError, passing in it more error details, like the
  * internal query and error position.
  */
-static void
+void
 PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
 {
 	PyObject   *args = NULL;
@@ -548,8 +547,10 @@ PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
 	if (!spierror)
 		goto failure;
 
-	spidata = Py_BuildValue("(izzzi)", edata->sqlerrcode, edata->detail, edata->hint,
-							edata->internalquery, edata->internalpos);
+	spidata = Py_BuildValue("(izzzizzzzz)", edata->sqlerrcode, edata->detail, edata->hint,
+							edata->internalquery, edata->internalpos,
+							edata->schema_name, edata->table_name, edata->column_name,
+							edata->datatype_name, edata->constraint_name);
 	if (!spidata)
 		goto failure;
 
diff --git a/src/pl/plpython/plpy_spi.h b/src/pl/plpython/plpy_spi.h
index b042794..e9a5991 100644
--- a/src/pl/plpython/plpy_spi.h
+++ b/src/pl/plpython/plpy_spi.h
@@ -22,4 +22,7 @@ extern void PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner
 extern void PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner);
 extern void PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner);
 
+/* set spi exception */
+extern void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
+
 #endif   /* PLPY_SPI_H */
diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql
index d0df7e6..45c6b25 100644
--- a/src/pl/plpython/sql/plpython_error.sql
+++ b/src/pl/plpython/sql/plpython_error.sql
@@ -328,3 +328,38 @@ EXCEPTION WHEN SQLSTATE 'SILLY' THEN
 	-- NOOP
 END
 $$ LANGUAGE plpgsql;
+
+
+CREATE FUNCTION nested_error_ereport() RETURNS text
+	AS
+'def fun1():
+	raise plpy.ereport(plpy.ERROR, "boom")
+
+def fun2():
+	fun1()
+
+def fun3():
+	fun2()
+
+fun3()
+return "not reached"
+'
+	LANGUAGE plpythonu;
+
+SELECT nested_error_ereport();
+
+CREATE FUNCTION ereport_test()
+RETURNS void AS $$
+  plpy.ereport(plpy.NOTICE, "some notice")
+$$ LANGUAGE plpythonu;
+
+SELECT ereport_test();
+
+\set VERBOSITY verbose
+
+CREATE OR REPLACE FUNCTION ereport_test()
+RETURNS void AS $$
+  plpy.ereport(plpy.NOTICE, "some notice", "some detail", "any hint", schema='schema_yy', table='table_xx', column='column_zz')
+$$ LANGUAGE plpythonu;
+
+SELECT ereport_test();
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to