Hi

I am sending new version. Current version does:

1. the plpy utility functions can use all ErrorData fields,
2. there are no new functions,
3. via GUC plpythonu.legacy_custom_exception we can return previous behave,
4. only exception Error is raised with natural structure - no composite
value spidata.
5. fields: message, detail and hint are implicitly translated to string -
it decrease a necessity of legacy mode

Curent version doesn't do:
1. touch plpy exception classes.

A doc should be enhanced, but the code should be +/- final.

Regards

Pavel
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml
new file mode 100644
index 015bbad..2bd9bcb
*** a/doc/src/sgml/plpython.sgml
--- b/doc/src/sgml/plpython.sgml
*************** $$ LANGUAGE plpythonu;
*** 1341,1360 ****
    <title>Utility Functions</title>
    <para>
     The <literal>plpy</literal> module also provides the functions
!    <literal>plpy.debug(<replaceable>msg</>)</literal>,
!    <literal>plpy.log(<replaceable>msg</>)</literal>,
!    <literal>plpy.info(<replaceable>msg</>)</literal>,
!    <literal>plpy.notice(<replaceable>msg</>)</literal>,
!    <literal>plpy.warning(<replaceable>msg</>)</literal>,
!    <literal>plpy.error(<replaceable>msg</>)</literal>, and
!    <literal>plpy.fatal(<replaceable>msg</>)</literal>.<indexterm><primary>elog</><secondary>in PL/Python</></indexterm>
     <function>plpy.error</function> and
     <function>plpy.fatal</function> actually raise a Python exception
     which, if uncaught, propagates out to the calling query, causing
     the current transaction or subtransaction to be aborted.
     <literal>raise plpy.Error(<replaceable>msg</>)</literal> and
     <literal>raise plpy.Fatal(<replaceable>msg</>)</literal> are
!    equivalent to calling
     <function>plpy.error</function> and
     <function>plpy.fatal</function>, respectively.
     The other functions only generate messages of different
--- 1341,1360 ----
    <title>Utility Functions</title>
    <para>
     The <literal>plpy</literal> module also provides the functions
!    <literal>plpy.debug(<replaceable>exception_params</>)</literal>,
!    <literal>plpy.log(<replaceable>exception_params</>)</literal>,
!    <literal>plpy.info(<replaceable>exception_params</>)</literal>,
!    <literal>plpy.notice(<replaceable>exception_params</>)</literal>,
!    <literal>plpy.warning(<replaceable>exception_params</>)</literal>,
!    <literal>plpy.error(<replaceable>exception_params</>)</literal>, and
!    <literal>plpy.fatal(<replaceable>exception_params</>)</literal>.<indexterm><primary>elog</><secondary>in PL/Python</></indexterm>
     <function>plpy.error</function> and
     <function>plpy.fatal</function> actually raise a Python exception
     which, if uncaught, propagates out to the calling query, causing
     the current transaction or subtransaction to be aborted.
     <literal>raise plpy.Error(<replaceable>msg</>)</literal> and
     <literal>raise plpy.Fatal(<replaceable>msg</>)</literal> are
!    partial equivalent to calling
     <function>plpy.error</function> and
     <function>plpy.fatal</function>, respectively.
     The other functions only generate messages of different
*************** $$ LANGUAGE plpythonu;
*** 1367,1372 ****
--- 1367,1397 ----
    </para>
  
    <para>
+ 
+    The <replaceable>exception_params</> are
+    <literal>[ <replaceable>message</replaceable> [, <replaceable>detail</replaceable> [, <replaceable>hint</replaceable> [, <replaceable>sqlstate</replaceable>  [, <replaceable>schema</replaceable>  [, <replaceable>table</replaceable>  [, <replaceable>column</replaceable>  [, <replaceable>datatype</replaceable>  [, <replaceable>constraint</replaceable> ]]]]]]]]]</literal>.
+    These parameters kan be entered as keyword parameters.
+    The <replaceable>message</replaceable>, <replaceable>detail</replaceable>, <replaceable>hint</replaceable>
+    are automaticly casted to string, other should be string.
+ 
+ <programlisting>
+ CREATE FUNCTION raise_custom_exception() RETURNS void AS $$
+ plpy.error("custom exception message", "some info about exception", "hint for users")
+ $$ LANGUAGE plpythonu;
+ 
+ postgres=# select raise_custom_exception();
+ ERROR:  XX000: plpy.Error: custom exception message
+ DETAIL:  some info about exception
+ HINT:  hint for users
+ CONTEXT:  Traceback (most recent call last):
+   PL/Python function "raise_custom_exception", line 2, in &lt;module&gt;
+     plpy.error("custom exception message", "some info about exception", "hint for users")
+ PL/Python function "raise_custom_exception"
+ LOCATION:  PLy_elog, plpy_elog.c:132
+ </programlisting>
+   </para>
+ 
+   <para>
     Another set of utility functions are
     <literal>plpy.quote_literal(<replaceable>string</>)</literal>,
     <literal>plpy.quote_nullable(<replaceable>string</>)</literal>, and
diff --git a/src/pl/plpython/expected/plpython_spi.out b/src/pl/plpython/expected/plpython_spi.out
new file mode 100644
index e715ee5..3daf181
*** a/src/pl/plpython/expected/plpython_spi.out
--- b/src/pl/plpython/expected/plpython_spi.out
***************
*** 1,3 ****
--- 1,4 ----
+ --set plpythonu.legacy_custom_exception = true;
  --
  -- nested calls
  --
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
new file mode 100644
index 7b76faf..bfe5c6f
*** a/src/pl/plpython/expected/plpython_test.out
--- b/src/pl/plpython/expected/plpython_test.out
*************** select module_contents();
*** 50,55 ****
--- 50,96 ----
  
  CREATE FUNCTION elog_test() RETURNS void
  AS $$
+ plpy.debug('debug','some detail')
+ plpy.log('log','some detail')
+ plpy.info('info','some detail')
+ plpy.info()
+ plpy.info('the question', 42);
+ plpy.info('This is message text.',
+                     detail = 'This is detail text',
+                     hint = 'This is hint text.',
+                     sqlstate = 'XX000',
+                     schema = 'any info about schema',
+                     table = 'any info about table',
+                     column = 'any info about column',
+                     datatype = 'any info about datatype',
+                     constraint = 'any info about constraint')
+ plpy.notice('notice','some detail')
+ plpy.warning('warning','some detail')
+ plpy.error('stop on error', 'some detail','some hint')
+ $$ LANGUAGE plpythonu;
+ SELECT elog_test();
+ INFO:  info
+ DETAIL:  some detail
+ INFO:  missing error text
+ INFO:  the question
+ DETAIL:  42
+ INFO:  This is message text.
+ DETAIL:  This is detail text
+ HINT:  This is hint text.
+ NOTICE:  notice
+ DETAIL:  some detail
+ WARNING:  warning
+ DETAIL:  some detail
+ ERROR:  plpy.Error: stop on error
+ DETAIL:  some detail
+ HINT:  some hint
+ CONTEXT:  Traceback (most recent call last):
+   PL/Python function "elog_test", line 18, in <module>
+     plpy.error('stop on error', 'some detail','some hint')
+ PL/Python function "elog_test"
+ set plpythonu.legacy_custom_exception=true;
+ CREATE FUNCTION elog_test_legacy() RETURNS void
+ AS $$
  plpy.debug('debug')
  plpy.log('log')
  plpy.info('info')
*************** plpy.notice('notice')
*** 60,66 ****
  plpy.warning('warning')
  plpy.error('error')
  $$ LANGUAGE plpythonu;
! SELECT elog_test();
  INFO:  info
  INFO:  37
  INFO:  ()
--- 101,107 ----
  plpy.warning('warning')
  plpy.error('error')
  $$ LANGUAGE plpythonu;
! SELECT elog_test_legacy();
  INFO:  info
  INFO:  37
  INFO:  ()
*************** NOTICE:  notice
*** 69,74 ****
  WARNING:  warning
  ERROR:  plpy.Error: error
  CONTEXT:  Traceback (most recent call last):
!   PL/Python function "elog_test", line 10, in <module>
      plpy.error('error')
  PL/Python function "elog_test"
--- 110,127 ----
  WARNING:  warning
  ERROR:  plpy.Error: error
  CONTEXT:  Traceback (most recent call last):
!   PL/Python function "elog_test_legacy", line 10, in <module>
      plpy.error('error')
+ PL/Python function "elog_test_legacy"
+ SELECT elog_test();
+ INFO:  ('info', 'some detail')
+ INFO:  ()
+ INFO:  ('the question', 42)
+ INFO:  This is message text.
+ NOTICE:  ('notice', 'some detail')
+ WARNING:  ('warning', 'some detail')
+ ERROR:  plpy.Error: ('stop on error', 'some detail', 'some hint')
+ CONTEXT:  Traceback (most recent call last):
+   PL/Python function "elog_test", line 18, in <module>
+     plpy.error('stop on error', 'some detail','some hint')
  PL/Python function "elog_test"
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
new file mode 100644
index f0b6abd..e16bda0
*** a/src/pl/plpython/expected/plpython_types.out
--- b/src/pl/plpython/expected/plpython_types.out
***************
*** 1,3 ****
--- 1,4 ----
+ set plpythonu.legacy_custom_exception = true;
  --
  -- Test data type behavior
  --
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
new file mode 100644
index 15406d6..4ec48c0
*** a/src/pl/plpython/plpy_elog.c
--- b/src/pl/plpython/plpy_elog.c
*************** PyObject   *PLy_exc_spi_error = NULL;
*** 23,31 ****
  
  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);
  static char *get_source_line(const char *src, int lineno);
  
  
  /*
   * Emit a PG error or notice, together with any available info about
--- 23,41 ----
  
  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);
  
+ static void get_string_attr(PyObject *obj, char *attrname, char **str);
+ static void get_int_attr(PyObject *obj, char *attrname, int *iv);
+ static bool set_string_attr(PyObject *obj, char *attrname, char *str);
+ static bool set_int_attr(PyObject *obj, char *attrname, int iv);
  
  /*
   * Emit a PG error or notice, together with any available info about
*************** PLy_elog(int elevel, const char *fmt,...
*** 51,62 ****
  	char	   *hint = NULL;
  	char	   *query = NULL;
  	int			position = 0;
  
  	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);
  		else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
  			elevel = FATAL;
  	}
--- 61,83 ----
  	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,
! 						&schema_name, &table_name, &column_name,
! 						&datatype_name, &constraint_name);
! 		else 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);
  		else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
  			elevel = FATAL;
  	}
*************** PLy_elog(int elevel, const char *fmt,...
*** 103,109 ****
  				 (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
  				 (hint) ? errhint("%s", hint) : 0,
  				 (query) ? internalerrquery(query) : 0,
! 				 (position) ? internalerrposition(position) : 0));
  	}
  	PG_CATCH();
  	{
--- 124,135 ----
  				 (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
  				 (hint) ? errhint("%s", hint) : 0,
  				 (query) ? internalerrquery(query) : 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();
  	{
*************** PLy_get_spi_sqlerrcode(PyObject *exc, in
*** 360,371 ****
  	Py_DECREF(sqlstate);
  }
  
- 
  /*
   * 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)
  {
  	PyObject   *spidata = NULL;
  
--- 386,399 ----
  	Py_DECREF(sqlstate);
  }
  
  /*
   * 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,
! 			char **schema_name, char **table_name, char **column_name,
! 			char **datatype_name, char **constraint_name)
  {
  	PyObject   *spidata = NULL;
  
*************** PLy_get_spi_error_data(PyObject *exc, in
*** 373,379 ****
  
  	if (spidata != NULL)
  	{
! 		PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position);
  	}
  	else
  	{
--- 401,409 ----
  
  	if (spidata != NULL)
  	{
! 		PyArg_ParseTuple(spidata, "izzzizzzzz", sqlerrcode, detail, hint, query, position,
! 												schema_name, table_name, column_name,
! 												datatype_name, constraint_name);
  	}
  	else
  	{
*************** PLy_get_spi_error_data(PyObject *exc, in
*** 390,395 ****
--- 420,450 ----
  }
  
  /*
+  * 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)
+ {
+ 	PLy_get_spi_sqlerrcode(exc, sqlerrcode);
+ 
+ 	get_string_attr(exc, "detail", detail);
+ 	get_string_attr(exc, "hint", hint);
+ 	get_string_attr(exc, "query", query);
+ 	get_int_attr(exc, "position", position);
+ 	get_string_attr(exc, "schema_name", schema_name);
+ 	get_string_attr(exc, "table_name", table_name);
+ 	get_string_attr(exc, "column_name", column_name);
+ 	get_string_attr(exc, "datatype_name", datatype_name);
+ 	get_string_attr(exc, "constraint_name", constraint_name);
+ 
+ 	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 *
*************** PLy_exception_set_plural(PyObject *exc,
*** 464,466 ****
--- 519,652 ----
  
  	PyErr_SetString(exc, buf);
  }
+ 
+ /* set exceptions from an ErrorData */
+ void
+ PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata)
+ {
+ 	PyObject   *args = NULL;
+ 	PyObject   *error = 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;
+ 
+ 	if (!set_string_attr(error, "sqlstate",
+ 							unpack_sql_state(edata->sqlerrcode)))
+ 		goto failure;
+ 
+ 	if (!set_string_attr(error, "detail", edata->detail))
+ 		goto failure;
+ 
+ 	if (!set_string_attr(error, "hint", edata->hint))
+ 		goto failure;
+ 
+ 	if (!set_string_attr(error, "query", edata->internalquery))
+ 		goto failure;
+ 
+ 	if (!set_int_attr(error, "position", edata->internalpos))
+ 		goto failure;
+ 
+ 	if (!set_string_attr(error, "schema_name", edata->schema_name))
+ 		goto failure;
+ 
+ 	if (!set_string_attr(error, "table_name", edata->table_name))
+ 		goto failure;
+ 
+ 	if (!set_string_attr(error, "column_name", edata->column_name))
+ 		goto failure;
+ 
+ 	if (!set_string_attr(error, "datatype_name", edata->datatype_name))
+ 		goto failure;
+ 
+ 	if (!set_string_attr(error, "constraint_name", edata->constraint_name))
+ 		goto failure;
+ 
+ 	PyErr_SetObject(excclass, error);
+ 
+ 	Py_DECREF(args);
+ 	Py_DECREF(error);
+ 
+ 	return;
+ 
+ failure:
+ 	Py_XDECREF(args);
+ 	Py_XDECREF(error);
+ 
+ 	elog(ERROR, "could not convert error to Python exception");
+ }
+ 
+ /* set value of string pointer on object field */
+ static void
+ get_string_attr(PyObject *obj, char *attrname, char **str)
+ {
+ 	PyObject *val;
+ 
+ 	val = PyObject_GetAttrString(obj, attrname);
+ 	if (val != NULL)
+ 	{
+ 		*str = PyString_AsString(val);
+ 		Py_DECREF(val);
+ 	}
+ }
+ 
+ /* same as previous for long */
+ static void
+ get_int_attr(PyObject *obj, char *attrname, int *iv)
+ {
+ 	PyObject *val;
+ 
+ 	val = PyObject_GetAttrString(obj, attrname);
+ 	if (val != NULL)
+ 	{
+ 		*iv = (int) (PyInt_AsLong(val));
+ 		Py_DECREF(val);
+ 	}
+ }
+ 
+ /* returns true, when object's field was succesfully changed */
+ static bool
+ set_string_attr(PyObject *obj, char *attrname, char *str)
+ {
+ 	int result;
+ 	PyObject *val;
+ 
+ 	if (str != NULL)
+ 	{
+ 		val = PyString_FromString(str);
+ 		if (!val)
+ 			return false;
+ 	}
+ 	else
+ 	{
+ 		val = Py_None;
+ 		Py_INCREF(Py_None);
+ 	}
+ 
+ 	result = PyObject_SetAttrString(obj, attrname, val);
+ 	Py_DECREF(val);
+ 
+ 	return result != -1;
+ }
+ 
+ /* same as previous for int */
+ static bool
+ set_int_attr(PyObject *obj, char *attrname, int iv)
+ {
+ 	int result;
+ 	PyObject *val;
+ 
+ 	val = PyInt_FromLong((long) iv);
+ 	if (!val)
+ 		return false;
+ 
+ 	result = PyObject_SetAttrString(obj, attrname, val);
+ 	Py_DECREF(val);
+ 
+ 	return result != -1;
+ }
diff --git a/src/pl/plpython/plpy_elog.h b/src/pl/plpython/plpy_elog.h
new file mode 100644
index 94725c2..5dd4ef7
*** a/src/pl/plpython/plpy_elog.h
--- b/src/pl/plpython/plpy_elog.h
*************** extern void PLy_exception_set(PyObject *
*** 17,20 ****
--- 17,22 ----
  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_main.c b/src/pl/plpython/plpy_main.c
new file mode 100644
index f950394..f74a062
*** a/src/pl/plpython/plpy_main.c
--- b/src/pl/plpython/plpy_main.c
*************** static int	plpython_version_bitmask = 0;
*** 70,75 ****
--- 70,77 ----
  /* initialize global variables */
  PyObject   *PLy_interp_globals = NULL;
  
+ bool plpythonu_legacy_custom_exception = false;
+ 
  /* this doesn't need to be global; use PLy_current_execution_context() */
  static PLyExecutionContext *PLy_execution_contexts = NULL;
  
*************** PLy_initialize(void)
*** 147,152 ****
--- 149,162 ----
  
  	PLy_execution_contexts = NULL;
  
+ 	DefineCustomBoolVariable("plpythonu.legacy_custom_exception",
+ 				  gettext_noop("Returns back a behave of function debug, log, info, ..."),
+ 							 NULL,
+ 							 &plpythonu_legacy_custom_exception,
+ 							 false,
+ 							 PGC_USERSET, 0,
+ 							 NULL, NULL, NULL);
+ 
  	inited = true;
  }
  
diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c
new file mode 100644
index a44b7fb..620cbe6
*** a/src/pl/plpython/plpy_plpymodule.c
--- b/src/pl/plpython/plpy_plpymodule.c
*************** static void PLy_add_exceptions(PyObject
*** 28,40 ****
  static void PLy_generate_spi_exceptions(PyObject *mod, PyObject *base);
  
  /* module functions */
! static PyObject *PLy_debug(PyObject *self, PyObject *args);
! static PyObject *PLy_log(PyObject *self, PyObject *args);
! static PyObject *PLy_info(PyObject *self, PyObject *args);
! 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_quote_literal(PyObject *self, PyObject *args);
  static PyObject *PLy_quote_nullable(PyObject *self, PyObject *args);
  static PyObject *PLy_quote_ident(PyObject *self, PyObject *args);
--- 28,40 ----
  static void PLy_generate_spi_exceptions(PyObject *mod, PyObject *base);
  
  /* module functions */
! static PyObject *PLy_debug(PyObject *self, PyObject *args, PyObject *kw);
! static PyObject *PLy_log(PyObject *self, PyObject *args, PyObject *kw);
! static PyObject *PLy_info(PyObject *self, PyObject *args, PyObject *kw);
! static PyObject *PLy_notice(PyObject *self, PyObject *args, PyObject *kw);
! static PyObject *PLy_warning(PyObject *self, PyObject *args, PyObject *kw);
! static PyObject *PLy_error(PyObject *self, PyObject *args, PyObject *kw);
! static PyObject *PLy_fatal(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);
*************** static PyMethodDef PLy_methods[] = {
*** 57,69 ****
  	/*
  	 * logging methods
  	 */
! 	{"debug", PLy_debug, METH_VARARGS, NULL},
! 	{"log", PLy_log, METH_VARARGS, NULL},
! 	{"info", PLy_info, METH_VARARGS, NULL},
! 	{"notice", PLy_notice, METH_VARARGS, NULL},
! 	{"warning", PLy_warning, METH_VARARGS, NULL},
! 	{"error", PLy_error, METH_VARARGS, NULL},
! 	{"fatal", PLy_fatal, METH_VARARGS, NULL},
  
  	/*
  	 * create a stored plan
--- 57,69 ----
  	/*
  	 * logging methods
  	 */
! 	{"debug", (PyCFunction) PLy_debug, METH_VARARGS|METH_KEYWORDS, NULL},
! 	{"log", (PyCFunction) PLy_log, METH_VARARGS|METH_KEYWORDS, NULL},
! 	{"info", (PyCFunction) PLy_info, METH_VARARGS|METH_KEYWORDS, NULL},
! 	{"notice", (PyCFunction) PLy_notice, METH_VARARGS|METH_KEYWORDS, NULL},
! 	{"warning", (PyCFunction) PLy_warning, METH_VARARGS|METH_KEYWORDS, NULL},
! 	{"error", (PyCFunction) PLy_error, METH_VARARGS|METH_KEYWORDS, NULL},
! 	{"fatal", (PyCFunction) PLy_fatal, METH_VARARGS|METH_KEYWORDS, NULL},
  
  	/*
  	 * create a stored plan
*************** PLy_generate_spi_exceptions(PyObject *mo
*** 272,318 ****
   * don't confuse these with PLy_elog
   */
  static PyObject *PLy_output(volatile int, PyObject *, PyObject *);
  
  static PyObject *
! PLy_debug(PyObject *self, PyObject *args)
  {
! 	return PLy_output(DEBUG2, self, args);
  }
  
  static PyObject *
! PLy_log(PyObject *self, PyObject *args)
  {
! 	return PLy_output(LOG, self, args);
  }
  
  static PyObject *
! PLy_info(PyObject *self, PyObject *args)
  {
! 	return PLy_output(INFO, self, args);
  }
  
  static PyObject *
! PLy_notice(PyObject *self, PyObject *args)
  {
! 	return PLy_output(NOTICE, self, args);
  }
  
  static PyObject *
! PLy_warning(PyObject *self, PyObject *args)
  {
! 	return PLy_output(WARNING, self, args);
  }
  
  static PyObject *
! PLy_error(PyObject *self, PyObject *args)
  {
! 	return PLy_output(ERROR, self, args);
  }
  
  static PyObject *
! PLy_fatal(PyObject *self, PyObject *args)
  {
! 	return PLy_output(FATAL, self, args);
  }
  
  static PyObject *
--- 272,330 ----
   * don't confuse these with PLy_elog
   */
  static PyObject *PLy_output(volatile int, PyObject *, PyObject *);
+ static PyObject *PLy_output_kw(volatile int level, PyObject *self,
+ 										  PyObject *args, PyObject *kw);
+ 
+ /* allow to switch between current and legacy design of following functions */
+ static PyObject *PLy_output_switch(volatile int level, PyObject *self,
+ 										  PyObject *args, PyObject *kw)
+ {
+ 	if (plpythonu_legacy_custom_exception)
+ 		return PLy_output(level, self, args);
+ 	else
+ 		return PLy_output_kw(level, self, args, kw);
+ }
  
  static PyObject *
! PLy_debug(PyObject *self, PyObject *args, PyObject *kw)
  {
! 	return PLy_output_switch(DEBUG2, self, args, kw);
  }
  
  static PyObject *
! PLy_log(PyObject *self, PyObject *args, PyObject *kw)
  {
! 	return PLy_output_switch(LOG, self, args, kw);
  }
  
  static PyObject *
! PLy_info(PyObject *self, PyObject *args, PyObject *kw)
  {
! 	return PLy_output_switch(INFO, self, args, kw);
  }
  
  static PyObject *
! PLy_notice(PyObject *self, PyObject *args, PyObject *kw)
  {
! 	return PLy_output_switch(NOTICE, self, args, kw);
  }
  
  static PyObject *
! PLy_warning(PyObject *self, PyObject *args, PyObject *kw)
  {
! 	return PLy_output_switch(WARNING, self, args, kw);
  }
  
  static PyObject *
! PLy_error(PyObject *self, PyObject *args, PyObject *kw)
  {
! 	return PLy_output_switch(ERROR, self, args, kw);
  }
  
  static PyObject *
! PLy_fatal(PyObject *self, PyObject *args, PyObject *kw)
  {
! 	return PLy_output_switch(FATAL, self, args, kw);
  }
  
  static PyObject *
*************** PLy_output(volatile int level, PyObject
*** 429,431 ****
--- 441,561 ----
  	Py_INCREF(Py_None);
  	return Py_None;
  }
+ 
+ /* enforce cast of object to string */
+ static char *
+ object_to_string(PyObject *obj)
+ {
+ 	if (obj)
+ 	{
+ 		PyObject *str = PyObject_Str(obj);
+ 		return PyString_AsString(str);
+ 	}
+ 
+ 	return NULL;
+ }
+ 
+ static PyObject *
+ PLy_output_kw(volatile int level, PyObject *self, PyObject *args, PyObject *kw)
+ {
+ 	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;
+ 	PyObject *o_message = NULL;
+ 	PyObject *o_detail = NULL;
+ 	PyObject *o_hint = NULL;
+ 	MemoryContext oldcontext ;
+ 
+ 	static char *kwlist[] = { "message", "detail", "hint",
+ 				  "sqlstate",
+ 				  "schema","table", "column",
+ 				  "datatype", "constraint",
+ 				  NULL };
+ 
+ 	if (!PyArg_ParseTupleAndKeywords(args, kw, "|OOOssssss", kwlist,
+ 			 &o_message, &o_detail, &o_hint,
+ 			 &sqlstatestr,
+ 			 &schema, &table, &column,
+ 			 &datatype, &constraint))
+ 		return NULL;
+ 
+ 	/* enforce message, detail, hint to string */
+ 	message = object_to_string(o_message);
+ 	detail = object_to_string(o_detail);
+ 	hint = object_to_string(o_hint);
+ 
+ 	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_exception_set_with_details(PLy_exc_error, edata);
+ 		FreeErrorData(edata);
+ 		return NULL;
+ 	}
+ 	PG_END_TRY();
+ 
+ 	Py_INCREF(Py_None);
+ 	return Py_None;
+ }
diff --git a/src/pl/plpython/plpy_plpymodule.h b/src/pl/plpython/plpy_plpymodule.h
new file mode 100644
index ee089b7..fc76511
*** a/src/pl/plpython/plpy_plpymodule.h
--- b/src/pl/plpython/plpy_plpymodule.h
***************
*** 10,15 ****
--- 10,17 ----
  /* A hash table mapping sqlstates to exceptions, for speedy lookup */
  extern HTAB *PLy_spi_exceptions;
  
+ /* GUC */
+ extern bool plpythonu_legacy_custom_exception;
  
  #if PY_MAJOR_VERSION >= 3
  PyMODINIT_FUNC PyInit_plpy(void);
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
new file mode 100644
index 58e78ec..2b53b14
*** a/src/pl/plpython/plpy_spi.c
--- b/src/pl/plpython/plpy_spi.c
*************** PLy_spi_exception_set(PyObject *excclass
*** 564,571 ****
  	if (!spierror)
  		goto failure;
  
! 	spidata = Py_BuildValue("(izzzi)", edata->sqlerrcode, edata->detail, edata->hint,
! 							edata->internalquery, edata->internalpos);
  	if (!spidata)
  		goto failure;
  
--- 564,573 ----
  	if (!spierror)
  		goto failure;
  
! 	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/sql/plpython_spi.sql b/src/pl/plpython/sql/plpython_spi.sql
new file mode 100644
index a882738..196385e
*** a/src/pl/plpython/sql/plpython_spi.sql
--- b/src/pl/plpython/sql/plpython_spi.sql
***************
*** 1,3 ****
--- 1,5 ----
+ --set plpythonu.legacy_custom_exception = true;
+ 
  --
  -- nested calls
  --
diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql
new file mode 100644
index c8d5ef5..b724050
*** a/src/pl/plpython/sql/plpython_test.sql
--- b/src/pl/plpython/sql/plpython_test.sql
*************** $$ LANGUAGE plpythonu;
*** 36,44 ****
  
  select module_contents();
  
- 
  CREATE FUNCTION elog_test() RETURNS void
  AS $$
  plpy.debug('debug')
  plpy.log('log')
  plpy.info('info')
--- 36,68 ----
  
  select module_contents();
  
  CREATE FUNCTION elog_test() RETURNS void
  AS $$
+ plpy.debug('debug','some detail')
+ plpy.log('log','some detail')
+ plpy.info('info','some detail')
+ plpy.info()
+ plpy.info('the question', 42);
+ plpy.info('This is message text.',
+                     detail = 'This is detail text',
+                     hint = 'This is hint text.',
+                     sqlstate = 'XX000',
+                     schema = 'any info about schema',
+                     table = 'any info about table',
+                     column = 'any info about column',
+                     datatype = 'any info about datatype',
+                     constraint = 'any info about constraint')
+ plpy.notice('notice','some detail')
+ plpy.warning('warning','some detail')
+ plpy.error('stop on error', 'some detail','some hint')
+ $$ LANGUAGE plpythonu;
+ 
+ SELECT elog_test();
+ 
+ set plpythonu.legacy_custom_exception=true;
+ 
+ CREATE FUNCTION elog_test_legacy() RETURNS void
+ AS $$
  plpy.debug('debug')
  plpy.log('log')
  plpy.info('info')
*************** plpy.warning('warning')
*** 50,53 ****
--- 74,79 ----
  plpy.error('error')
  $$ LANGUAGE plpythonu;
  
+ SELECT elog_test_legacy();
+ 
  SELECT elog_test();
diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql
new file mode 100644
index 19d920d..af9b8d7
*** a/src/pl/plpython/sql/plpython_types.sql
--- b/src/pl/plpython/sql/plpython_types.sql
***************
*** 1,3 ****
--- 1,5 ----
+ set plpythonu.legacy_custom_exception = true;
+ 
  --
  -- Test data type behavior
  --
-- 
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