From eb17c7f733621bec5758a09737795d243306f360 Mon Sep 17 00:00:00 2001
From: Catalin Iacob <iacobcatalin@gmail.com>
Date: Thu, 28 Jan 2016 17:14:11 +0100
Subject: [PATCH 2/2] use plpy.Error to hold the data not plpy.SPIError

---
 src/pl/plpython/plpy_elog.c       | 83 +++++++++++++++++++++++++++++++++++++++
 src/pl/plpython/plpy_elog.h       |  2 +
 src/pl/plpython/plpy_plpymodule.c |  2 +-
 src/pl/plpython/plpy_spi.c        |  1 +
 src/pl/plpython/plpy_spi.h        |  3 --
 5 files changed, 87 insertions(+), 4 deletions(-)

diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index aea79db..80301c0 100644
--- a/src/pl/plpython/plpy_elog.c
+++ b/src/pl/plpython/plpy_elog.c
@@ -25,6 +25,9 @@ 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 **schema_name, char **table_name, char **column_name,
 						  char **datatype_name, char **constraint_name);
+static void PLy_get_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);
 static char *get_source_line(const char *src, int lineno);
 
 
@@ -66,6 +69,11 @@ PLy_elog(int elevel, const char *fmt,...)
 						&schema_name, &table_name, &column_name,
 						&datatype_name, &constraint_name);
 
+		if (PyErr_GivenExceptionMatches(val, PLy_exc_error))
+			PLy_get_error_data(val, &sqlerrcode, &detail, &hint, &query, &position,
+						&schema_name, &table_name, &column_name,
+						&datatype_name, &constraint_name);
+
 		if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
 			elevel = FATAL;
 	}
@@ -409,6 +417,35 @@ PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hin
 }
 
 /*
+ * Extract the error data from an Error
+ */
+static void
+PLy_get_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   *err_detail = NULL;
+	PyObject   *err_hint = NULL;
+
+	err_detail = PyObject_GetAttrString(exc, "detail");
+	if (err_detail != NULL)
+	{
+		*detail = PyString_AsString(err_detail);
+		Py_DECREF(err_detail);
+	}
+
+	err_hint = PyObject_GetAttrString(exc, "hint");
+	if (err_hint != NULL)
+	{
+		*hint = PyString_AsString(err_hint);
+		Py_DECREF(err_hint);
+	}
+
+	PyErr_Clear();
+	/* no elog here, we simply won't report the errhint, errposition etc */
+}
+
+/*
  * Get the given source line as a palloc'd string
  */
 static char *
@@ -451,6 +488,52 @@ get_source_line(const char *src, int lineno)
 	return pnstrdup(s, next - s);
 }
 
+void
+PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata)
+{
+	PyObject   *args = NULL;
+	PyObject   *error = NULL;
+	PyObject   *err_detail = NULL;
+	PyObject   *err_hint = NULL;
+
+	args = Py_BuildValue("(s)", edata->message);
+	if (!args)
+		goto failure;
+
+	/* create a new exception with the error message as the parameter */
+	error = PyObject_CallObject(excclass, args);
+	if (!error)
+		goto failure;
+
+	err_detail = PyString_FromString(edata->detail ? edata->detail : "");
+	if (!err_detail)
+		goto failure;
+
+	if (PyObject_SetAttrString(error, "detail", err_detail) == -1)
+		goto failure;
+
+	err_hint = PyString_FromString(edata->hint ? edata->hint : "");
+	if (!err_hint)
+		goto failure;
+
+	if (PyObject_SetAttrString(error, "hint", err_hint) == -1)
+		goto failure;
+
+	PyErr_SetObject(excclass, error);
+
+	Py_DECREF(args);
+	Py_DECREF(error);
+	Py_DECREF(err_detail);
+	Py_DECREF(err_hint);
+	return;
+
+failure:
+	Py_XDECREF(args);
+	Py_XDECREF(error);
+	Py_XDECREF(err_detail);
+	Py_XDECREF(err_hint);
+	elog(ERROR, "could not convert error to Python exception");
+}
 
 /* call PyErr_SetString with a vprint interface and translation support */
 void
diff --git a/src/pl/plpython/plpy_elog.h b/src/pl/plpython/plpy_elog.h
index 94725c2..5dd4ef7 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_exception_set_with_details(PyObject *excclass, ErrorData *edata);
+
 #endif   /* PLPY_ELOG_H */
diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c
index f264746..3e7a20a 100644
--- a/src/pl/plpython/plpy_plpymodule.c
+++ b/src/pl/plpython/plpy_plpymodule.c
@@ -583,7 +583,7 @@ PLy_output_kw(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
 		edata = CopyErrorData();
 		FlushErrorState();
 
-		PLy_spi_exception_set(PLy_exc_spi_error, edata);
+		PLy_exception_set_with_details(PLy_exc_error, edata);
 		FreeErrorData(edata);
 		return NULL;
 	}
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index 36ea728..c1989c0 100644
--- a/src/pl/plpython/plpy_spi.c
+++ b/src/pl/plpython/plpy_spi.c
@@ -30,6 +30,7 @@
 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")
diff --git a/src/pl/plpython/plpy_spi.h b/src/pl/plpython/plpy_spi.h
index f9c54b8..b042794 100644
--- a/src/pl/plpython/plpy_spi.h
+++ b/src/pl/plpython/plpy_spi.h
@@ -22,7 +22,4 @@ 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);
 
-/* raise and fill SPIError */
-extern void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
-
 #endif   /* PLPY_SPI_H */
-- 
2.7.0

