On 27/01/11 23:24, Jan Urbański wrote:
> On 11/01/11 12:20, Jan Urbański wrote:
>> On 11/01/11 01:27, Tom Lane wrote:
>>> Hannu Krosing <ha...@2ndquadrant.com> writes:
>>>> On 10.1.2011 17:20, Jan Urbański wrote:
>>>>> I changed that patch to use Perl instead of sed to generate the
>>>>> exceptions, which should be a more portable.
> 
> Updated as an incremental patch on to of the recently sent version of
> explicit-subxacts.

Updated again.
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index 33dddc6..b3f0d0e 100644
*** a/src/pl/plpython/Makefile
--- b/src/pl/plpython/Makefile
*************** PSQLDIR = $(bindir)
*** 86,94 ****
--- 86,102 ----
  
  include $(top_srcdir)/src/Makefile.shlib
  
+ # Force this dependency to be known (see src/pl/plpgsql/src/Makefile)
+ plpython.o: spiexceptions.h
+ 
+ # Generate spiexceptions.h from utils/errcodes.h
+ spiexceptions.h: $(top_srcdir)/src/backend/utils/errcodes.txt generate-spiexceptions.pl
+ 	$(PERL) $(srcdir)/generate-spiexceptions.pl $< > $@
  
  all: all-lib
  
+ distprep: spiexceptions.h
+ 
  install: all installdirs install-lib
  ifeq ($(python_majorversion),2)
  	cd '$(DESTDIR)$(pkglibdir)' && rm -f plpython$(DLSUFFIX) && $(LN_S) $(shlib) plpython$(DLSUFFIX)
*************** endif
*** 134,143 ****
  submake:
  	$(MAKE) -C $(top_builddir)/src/test/regress pg_regress$(X)
  
! clean distclean maintainer-clean: clean-lib
  	rm -f $(OBJS)
  	rm -rf results
  	rm -f regression.diffs regression.out
  ifeq ($(PORTNAME), win32)
  	rm -f python${pytverstr}.def
  endif
--- 142,156 ----
  submake:
  	$(MAKE) -C $(top_builddir)/src/test/regress pg_regress$(X)
  
! clean distclean: clean-lib
  	rm -f $(OBJS)
  	rm -rf results
  	rm -f regression.diffs regression.out
+ 
+ # since we distribute spiexceptions.h, only remove it in maintainer-clean
+ maintainer-clean: clean distclean
+ 	rm -f spiexceptions.h
+ 
  ifeq ($(PORTNAME), win32)
  	rm -f python${pytverstr}.def
  endif
diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index 7597ca7..afbc6db 100644
*** a/src/pl/plpython/expected/plpython_error.out
--- b/src/pl/plpython/expected/plpython_error.out
*************** CREATE FUNCTION sql_syntax_error() RETUR
*** 32,38 ****
  'plpy.execute("syntax error")'
          LANGUAGE plpythonu;
  SELECT sql_syntax_error();
! ERROR:  plpy.SPIError: syntax error at or near "syntax"
  LINE 1: syntax error
          ^
  QUERY:  syntax error
--- 32,38 ----
  'plpy.execute("syntax error")'
          LANGUAGE plpythonu;
  SELECT sql_syntax_error();
! ERROR:  spiexceptions.SyntaxError: syntax error at or near "syntax"
  LINE 1: syntax error
          ^
  QUERY:  syntax error
*************** CREATE FUNCTION exception_index_invalid_
*** 54,60 ****
  return rv[0]'
  	LANGUAGE plpythonu;
  SELECT exception_index_invalid_nested();
! ERROR:  plpy.SPIError: function test5(unknown) does not exist
  LINE 1: SELECT test5('foo')
                 ^
  HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
--- 54,60 ----
  return rv[0]'
  	LANGUAGE plpythonu;
  SELECT exception_index_invalid_nested();
! ERROR:  spiexceptions.UndefinedFunction: function test5(unknown) does not exist
  LINE 1: SELECT test5('foo')
                 ^
  HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
*************** return None
*** 74,80 ****
  '
  	LANGUAGE plpythonu;
  SELECT invalid_type_uncaught('rick');
! ERROR:  plpy.SPIError: type "test" does not exist
  CONTEXT:  PL/Python function "invalid_type_uncaught"
  /* for what it's worth catch the exception generated by
   * the typo, and return None
--- 74,80 ----
  '
  	LANGUAGE plpythonu;
  SELECT invalid_type_uncaught('rick');
! ERROR:  spiexceptions.UndefinedObject: type "test" does not exist
  CONTEXT:  PL/Python function "invalid_type_uncaught"
  /* for what it's worth catch the exception generated by
   * the typo, and return None
*************** SELECT valid_type('rick');
*** 140,145 ****
--- 140,183 ----
   
  (1 row)
  
+ /* Check catching specific types of exceptions
+  */
+ CREATE TABLE specific (
+     i integer PRIMARY KEY
+ );
+ NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "specific_pkey" for table "specific"
+ CREATE FUNCTION specific_exception(i integer) RETURNS void AS
+ $$
+ from plpy import spiexceptions
+ try:
+     plpy.execute("insert into specific values (%s)" % (i or "NULL"));
+ except spiexceptions.NotNullViolation, e:
+     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
+ except spiexceptions.UniqueViolation, e:
+     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
+ $$ LANGUAGE plpythonu;
+ SELECT specific_exception(2);
+  specific_exception 
+ --------------------
+  
+ (1 row)
+ 
+ SELECT specific_exception(NULL);
+ NOTICE:  Violated the NOT NULL constraint, sqlstate 23502
+ CONTEXT:  PL/Python function "specific_exception"
+  specific_exception 
+ --------------------
+  
+ (1 row)
+ 
+ SELECT specific_exception(2);
+ NOTICE:  Violated the UNIQUE constraint, sqlstate 23505
+ CONTEXT:  PL/Python function "specific_exception"
+  specific_exception 
+ --------------------
+  
+ (1 row)
+ 
  /* manually starting subtransactions - a bad idea
   */
  CREATE FUNCTION manual_subxact() RETURNS void AS $$
diff --git a/src/pl/plpython/expected/plpython_subxact.out b/src/pl/plpython/expected/plpython_subxact.out
index 7508883..d7808f2 100644
*** a/src/pl/plpython/expected/plpython_subxact.out
--- b/src/pl/plpython/expected/plpython_subxact.out
*************** SELECT * FROM subxact_tbl;
*** 43,49 ****
  
  TRUNCATE subxact_tbl;
  SELECT subxact_test('SPI');
! ERROR:  plpy.SPIError: invalid input syntax for integer: "oops"
  LINE 1: insert into subxact_tbl values('oops')
                                         ^
  QUERY:  insert into subxact_tbl values('oops')
--- 43,49 ----
  
  TRUNCATE subxact_tbl;
  SELECT subxact_test('SPI');
! ERROR:  spiexceptions.InvalidTextRepresentation: invalid input syntax for integer: "oops"
  LINE 1: insert into subxact_tbl values('oops')
                                         ^
  QUERY:  insert into subxact_tbl values('oops')
*************** SELECT * FROM subxact_tbl;
*** 90,96 ****
  
  TRUNCATE subxact_tbl;
  SELECT subxact_ctx_test('SPI');
! ERROR:  plpy.SPIError: invalid input syntax for integer: "oops"
  LINE 1: insert into subxact_tbl values('oops')
                                         ^
  QUERY:  insert into subxact_tbl values('oops')
--- 90,96 ----
  
  TRUNCATE subxact_tbl;
  SELECT subxact_ctx_test('SPI');
! ERROR:  spiexceptions.InvalidTextRepresentation: invalid input syntax for integer: "oops"
  LINE 1: insert into subxact_tbl values('oops')
                                         ^
  QUERY:  insert into subxact_tbl values('oops')
*************** with plpy.subtransaction():
*** 128,134 ****
  return "ok"
  $$ LANGUAGE plpythonu;
  SELECT subxact_nested_test();
! ERROR:  plpy.SPIError: syntax error at or near "error"
  LINE 1: error
          ^
  QUERY:  error
--- 128,134 ----
  return "ok"
  $$ LANGUAGE plpythonu;
  SELECT subxact_nested_test();
! ERROR:  spiexceptions.SyntaxError: syntax error at or near "error"
  LINE 1: error
          ^
  QUERY:  error
*************** SELECT * FROM subxact_tbl;
*** 140,146 ****
  
  TRUNCATE subxact_tbl;
  SELECT subxact_nested_test('t');
! NOTICE:  Swallowed SPIError('syntax error at or near "error"',)
  CONTEXT:  PL/Python function "subxact_nested_test"
   subxact_nested_test 
  ---------------------
--- 140,146 ----
  
  TRUNCATE subxact_tbl;
  SELECT subxact_nested_test('t');
! NOTICE:  Swallowed SyntaxError('syntax error at or near "error"',)
  CONTEXT:  PL/Python function "subxact_nested_test"
   subxact_nested_test 
  ---------------------
*************** with plpy.subtransaction():
*** 166,172 ****
  return "ok"
  $$ LANGUAGE plpythonu;
  SELECT subxact_deeply_nested_test();
! NOTICE:  Swallowed SPIError('syntax error at or near "error"',)
  CONTEXT:  PL/Python function "subxact_nested_test"
  SQL statement "select subxact_nested_test('t')"
  PL/Python function "subxact_nested_test"
--- 166,172 ----
  return "ok"
  $$ LANGUAGE plpythonu;
  SELECT subxact_deeply_nested_test();
! NOTICE:  Swallowed SyntaxError('syntax error at or near "error"',)
  CONTEXT:  PL/Python function "subxact_nested_test"
  SQL statement "select subxact_nested_test('t')"
  PL/Python function "subxact_nested_test"
diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out
index c7d875e..2cb2ef6 100644
*** a/src/pl/plpython/expected/plpython_test.out
--- b/src/pl/plpython/expected/plpython_test.out
*************** contents.sort()
*** 43,51 ****
  return ", ".join(contents)
  $$ LANGUAGE plpythonu;
  select module_contents();
!                                               module_contents                                              
! -----------------------------------------------------------------------------------------------------------
!  Error, Fatal, SPIError, debug, error, execute, fatal, info, log, notice, prepare, subtransaction, warning
  (1 row)
  
  CREATE FUNCTION elog_test() RETURNS void
--- 43,51 ----
  return ", ".join(contents)
  $$ LANGUAGE plpythonu;
  select module_contents();
!                                                      module_contents                                                      
! --------------------------------------------------------------------------------------------------------------------------
!  Error, Fatal, SPIError, debug, error, execute, fatal, info, log, notice, prepare, spiexceptions, subtransaction, warning
  (1 row)
  
  CREATE FUNCTION elog_test() RETURNS void
diff --git a/src/pl/plpython/generate-spiexceptions.pl b/src/pl/plpython/generate-spiexceptions.pl
index ...cf050d1 .
*** a/src/pl/plpython/generate-spiexceptions.pl
--- b/src/pl/plpython/generate-spiexceptions.pl
***************
*** 0 ****
--- 1,44 ----
+ #!/usr/bin/perl
+ #
+ # Generate the spiexceptions.h header from errcodes.txt
+ # Copyright (c) 2000-2011, PostgreSQL Global Development Group
+ 
+ use warnings;
+ use strict;
+ 
+ print "/* autogenerated from src/backend/utils/errcodes.txt, do not edit */\n";
+ print "/* there is deliberately not an #ifndef SPIEXCEPTIONS_H here */\n";
+ 
+ open my $errcodes, $ARGV[0] or die;
+ 
+ while (<$errcodes>) {
+     chomp;
+ 
+     # Skip comments
+     next if /^#/;
+     next if /^\s*$/;
+ 
+     # Skip section headers
+     next if /^Section:/;
+ 
+     die unless /^([^\s]{5})\s+([EWS])\s+([^\s]+)(?:\s+)?([^\s]+)?/;
+ 
+     (my $sqlstate,
+      my $type,
+      my $errcode_macro,
+      my $condition_name) = ($1, $2, $3, $4);
+ 
+     # Skip non-errors
+     next unless $type eq 'E';
+ 
+     # Skip lines without PL/pgSQL condition names
+     next unless defined($condition_name);
+ 
+     # Change some_error_condition to SomeErrorCondition
+     $condition_name =~ s/([a-z])([^_]*)(?:_|$)/\u$1$2/g;
+ 
+     print "{ \"spiexceptions.$condition_name\", " .
+ 	"\"$condition_name\", $errcode_macro },\n";
+ }
+ 
+ close $errcodes;
diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c
index 7692c0b..7637116 100644
*** a/src/pl/plpython/plpython.c
--- b/src/pl/plpython/plpython.c
*************** typedef struct PLySubxactObject
*** 261,266 ****
--- 261,288 ----
  	bool	exited;
  } PLySubxactObject;
  
+ /* A list of all known exceptions, generated from backend/utils/errcodes.txt */
+ typedef struct ExceptionMap
+ {
+ 	char		*name;
+ 	char		*classname;
+ 	int			sqlstate;
+ } ExceptionMap;
+ 
+ static const ExceptionMap exception_map[] = {
+ #include "spiexceptions.h"
+ 	{NULL, NULL, 0}
+ };
+ 
+ /* A hashtable mapping sqlstates to exceptions, for speedy lookup */
+ static HTAB *PLy_spi_exceptions;
+ 
+ typedef struct PLyExceptionEntry
+ {
+ 	int			sqlstate;	/* hash key, must be first */
+ 	PyObject	*exc;		/* corresponding exception */
+ } PLyExceptionEntry;
+ 
  /* function declarations */
  
  #if PY_MAJOR_VERSION >= 3
*************** __attribute__((format(printf, 2, 5)))
*** 302,308 ****
  __attribute__((format(printf, 3, 5)));
  
  /* like PLy_exception_set, but conserve more fields from ErrorData */
! static void PLy_spi_exception_set(ErrorData *edata);
  
  /* Get the innermost python procedure called from the backend */
  static char *PLy_procedure_name(PLyProcedure *);
--- 324,330 ----
  __attribute__((format(printf, 3, 5)));
  
  /* like PLy_exception_set, but conserve more fields from ErrorData */
! static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
  
  /* Get the innermost python procedure called from the backend */
  static char *PLy_procedure_name(PLyProcedure *);
*************** static PyMethodDef PLy_methods[] = {
*** 2805,2810 ****
--- 2827,2836 ----
  	{NULL, NULL, 0, NULL}
  };
  
+ static PyMethodDef PLy_exc_methods[] = {
+ 	{NULL, NULL, 0, NULL}
+ };
+ 
  #if PY_MAJOR_VERSION >= 3
  static PyModuleDef PLy_module = {
  	PyModuleDef_HEAD_INIT,		/* m_base */
*************** static PyModuleDef PLy_module = {
*** 2813,2818 ****
--- 2839,2856 ----
  	-1,							/* m_size */
  	PLy_methods,				/* m_methods */
  };
+ 
+ static PyModuleDef PLy_exc_module = {
+ 	PyModuleDef_HEAD_INIT,		/* m_base */
+ 	"spiexceptions",			/* m_name */
+ 	NULL,						/* m_doc */
+ 	-1,							/* m_size */
+ 	PLy_exc_methods,			/* m_methods */
+ 	NULL,						/* m_reload */
+ 	NULL,						/* m_traverse */
+ 	NULL,						/* m_clear */
+ 	NULL						/* m_free */
+ };
  #endif
  
  /* plan object methods */
*************** PLy_spi_prepare(PyObject *self, PyObject
*** 3116,3122 ****
  	}
  	PG_CATCH();
  	{
! 		ErrorData	*edata;
  
  		/* Save error info */
  		MemoryContextSwitchTo(oldcontext);
--- 3154,3162 ----
  	}
  	PG_CATCH();
  	{
! 		ErrorData				*edata;
! 		PLyExceptionEntry		*entry;
! 		PyObject				*exc;
  
  		/* Save error info */
  		MemoryContextSwitchTo(oldcontext);
*************** PLy_spi_prepare(PyObject *self, PyObject
*** 3140,3147 ****
  			SPI_restore_connection();
  		}
  
  		/* Make Python raise the exception */
! 		PLy_spi_exception_set(edata);
  		return NULL;
  	}
  	PG_END_TRY();
--- 3180,3193 ----
  			SPI_restore_connection();
  		}
  
+ 		/* Look up the correct exception */
+ 		entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
+ 							HASH_FIND, NULL);
+ 		/* We really should find it, but just in case have a fallback */
+ 		Assert(entry != NULL);
+ 		exc = entry ? entry->exc : PLy_exc_spi_error;
  		/* Make Python raise the exception */
! 		PLy_spi_exception_set(exc, edata);
  		return NULL;
  	}
  	PG_END_TRY();
*************** PLy_spi_execute_plan(PyObject *ob, PyObj
*** 3296,3303 ****
  	}
  	PG_CATCH();
  	{
  		int			k;
- 		ErrorData	*edata;
  
  		/* Save error info */
  		MemoryContextSwitchTo(oldcontext);
--- 3342,3351 ----
  	}
  	PG_CATCH();
  	{
+ 		ErrorData				*edata;
+ 		PLyExceptionEntry		*entry;
+ 		PyObject				*exc;
  		int			k;
  
  		/* Save error info */
  		MemoryContextSwitchTo(oldcontext);
*************** PLy_spi_execute_plan(PyObject *ob, PyObj
*** 3332,3339 ****
  			SPI_restore_connection();
  		}
  
  		/* Make Python raise the exception */
! 		PLy_spi_exception_set(edata);
  		return NULL;
  	}
  	PG_END_TRY();
--- 3380,3393 ----
  			SPI_restore_connection();
  		}
  
+ 		/* Look up the correct exception */
+ 		entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
+ 							HASH_FIND, NULL);
+ 		/* We really should find it, but just in case have a fallback */
+ 		Assert(entry != NULL);
+ 		exc = entry ? entry->exc : PLy_exc_spi_error;
  		/* Make Python raise the exception */
! 		PLy_spi_exception_set(exc, edata);
  		return NULL;
  	}
  	PG_END_TRY();
*************** PLy_spi_execute_query(char *query, long
*** 3403,3409 ****
  	}
  	PG_CATCH();
  	{
! 		ErrorData	*edata;
  
  		/* Save error info */
  		MemoryContextSwitchTo(oldcontext);
--- 3457,3465 ----
  	}
  	PG_CATCH();
  	{
! 		ErrorData				*edata;
! 		PLyExceptionEntry		*entry;
! 		PyObject				*exc;
  
  		/* Save error info */
  		MemoryContextSwitchTo(oldcontext);
*************** PLy_spi_execute_query(char *query, long
*** 3425,3432 ****
  			SPI_restore_connection();
  		}
  
  		/* Make Python raise the exception */
! 		PLy_spi_exception_set(edata);
  		return NULL;
  	}
  	PG_END_TRY();
--- 3481,3494 ----
  			SPI_restore_connection();
  		}
  
+ 		/* Look up the correct exception */
+ 		entry = hash_search(PLy_spi_exceptions, &edata->sqlerrcode,
+ 							HASH_FIND, NULL);
+ 		/* We really should find it, but just in case have a fallback */
+ 		Assert(entry != NULL);
+ 		exc = entry ? entry->exc : PLy_exc_spi_error;
  		/* Make Python raise the exception */
! 		PLy_spi_exception_set(exc, edata);
  		return NULL;
  	}
  	PG_END_TRY();
*************** PLy_subxact_exit(PyObject *self, PyObjec
*** 3654,3662 ****
--- 3716,3761 ----
  /*
   * Add exceptions to the plpy module
   */
+ /* Add all the autogenerated exceptions as subclasses of SPIError */
+ static void
+ PLy_generate_spi_exceptions(PyObject *mod, PyObject *base)
+ {
+ 	int	i;
+ 
+ 	for (i = 0; exception_map[i].name != NULL; i++)
+ 	{
+ 		bool					 found;
+ 		PyObject				*exc;
+ 		PLyExceptionEntry		*entry;
+ 		PyObject				*sqlstate;
+ 		PyObject				*dict = PyDict_New();
+ 
+ 		sqlstate = PyString_FromString(unpack_sql_state(
+ 										   exception_map[i].sqlstate));
+ 		PyDict_SetItemString(dict, "sqlstate", sqlstate);
+ 		Py_DECREF(sqlstate);
+ 		exc = PyErr_NewException(exception_map[i].name, base, dict);
+ 		PyModule_AddObject(mod, exception_map[i].classname, exc);
+ 		entry = hash_search(PLy_spi_exceptions, &exception_map[i].sqlstate,
+ 							HASH_ENTER, &found);
+ 		entry->exc = exc;
+ 		Assert(!found);
+ 	}
+ }
+ 
  static void
  PLy_add_exceptions(PyObject *plpy)
  {
+ 	PyObject	*excmod;
+ 	HASHCTL		 hash_ctl;
+ #if PY_MAJOR_VERSION < 3
+ 	excmod = Py_InitModule("spiexceptions", PLy_exc_methods);
+ #else
+ 	excmod = PyModule_Create(&PLy_exc_module);
+ #endif
+ 	if (PyModule_AddObject(plpy, "spiexceptions", excmod) < 0)
+ 		PLy_elog(ERROR, "Failed to add the spiexceptions module");
+ 
  	PLy_exc_error = PyErr_NewException("plpy.Error", NULL, NULL);
  	PLy_exc_fatal = PyErr_NewException("plpy.Fatal", NULL, NULL);
  	PLy_exc_spi_error = PyErr_NewException("plpy.SPIError", NULL, NULL);
*************** PLy_add_exceptions(PyObject *plpy)
*** 3667,3672 ****
--- 3766,3780 ----
  	PyModule_AddObject(plpy, "Fatal", PLy_exc_fatal);
  	Py_INCREF(PLy_exc_spi_error);
  	PyModule_AddObject(plpy, "SPIError", PLy_exc_spi_error);
+ 
+ 	memset(&hash_ctl, 0, sizeof(hash_ctl));
+ 	hash_ctl.keysize = sizeof(int);
+ 	hash_ctl.entrysize = sizeof(PLyExceptionEntry);
+ 	hash_ctl.hash = tag_hash;
+ 	PLy_spi_exceptions = hash_create("SPI exceptions", 256,
+ 									 &hash_ctl, HASH_ELEM | HASH_FUNCTION);
+ 
+ 	PLy_generate_spi_exceptions(excmod, PLy_exc_spi_error);
  }
  
  #if PY_MAJOR_VERSION >= 3
*************** PLy_exception_set_plural(PyObject *exc,
*** 3973,3979 ****
   * internal query and error position.
   */
  static void
! PLy_spi_exception_set(ErrorData *edata)
  {
  	PyObject	*args = NULL;
  	PyObject	*spierror = NULL;
--- 4081,4087 ----
   * internal query and error position.
   */
  static void
! PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
  {
  	PyObject	*args = NULL;
  	PyObject	*spierror = NULL;
*************** PLy_spi_exception_set(ErrorData *edata)
*** 3983,3990 ****
  	if (!args)
  		goto failure;
  
! 	/* create a new SPIError with the error message as the parameter */
! 	spierror = PyObject_CallObject(PLy_exc_spi_error, args);
  	if (!spierror)
  		goto failure;
  
--- 4091,4098 ----
  	if (!args)
  		goto failure;
  
! 	/* create a new SPI exception with the error message as the parameter */
! 	spierror = PyObject_CallObject(excclass, args);
  	if (!spierror)
  		goto failure;
  
*************** PLy_spi_exception_set(ErrorData *edata)
*** 3996,4002 ****
  	if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
  		goto failure;
  
! 	PyErr_SetObject(PLy_exc_spi_error, spierror);
  
  	Py_DECREF(args);
  	Py_DECREF(spierror);
--- 4104,4110 ----
  	if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
  		goto failure;
  
! 	PyErr_SetObject(excclass, spierror);
  
  	Py_DECREF(args);
  	Py_DECREF(spierror);
diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql
index 7861cd6..6d6a163 100644
*** a/src/pl/plpython/sql/plpython_error.sql
--- b/src/pl/plpython/sql/plpython_error.sql
*************** return None
*** 131,136 ****
--- 131,157 ----
  
  SELECT valid_type('rick');
  
+ /* Check catching specific types of exceptions
+  */
+ CREATE TABLE specific (
+     i integer PRIMARY KEY
+ );
+ 
+ CREATE FUNCTION specific_exception(i integer) RETURNS void AS
+ $$
+ from plpy import spiexceptions
+ try:
+     plpy.execute("insert into specific values (%s)" % (i or "NULL"));
+ except spiexceptions.NotNullViolation, e:
+     plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
+ except spiexceptions.UniqueViolation, e:
+     plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
+ $$ LANGUAGE plpythonu;
+ 
+ SELECT specific_exception(2);
+ SELECT specific_exception(NULL);
+ SELECT specific_exception(2);
+ 
  /* manually starting subtransactions - a bad idea
   */
  CREATE FUNCTION manual_subxact() RETURNS void AS $$
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 49504d7..e1fe4e9 100644
*** a/src/tools/msvc/Solution.pm
--- b/src/tools/msvc/Solution.pm
*************** s{PG_VERSION_STR "[^"]+"}{__STRINGIFY(x)
*** 273,278 ****
--- 273,284 ----
          );
      }
  
+     if ($self->{options}->{python} && IsNewer('src\pl\plpython\spiexceptions.h','src\include\backend\errcodes.txt'))
+     {
+         print "Generating spiexceptions.h...\n";
+         system('perl src\pl\plpython\generate-spiexceptions.pl src\backend\utils\errcodes.txt > src\pl\plpython\spiexceptions.h');
+     }
+ 
      if (IsNewer('src\include\utils\errcodes.h','src\backend\utils\errcodes.txt'))
      {
          print "Generating errcodes.h...\n";
-- 
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