diff -c -r --new-file pgsql.old/doc/src/sgml/plpgsql.sgml pgsql/doc/src/sgml/plpgsql.sgml
*** pgsql.old/doc/src/sgml/plpgsql.sgml	2005-06-06 15:29:00.000000000 +0200
--- pgsql/doc/src/sgml/plpgsql.sgml	2005-06-06 20:46:00.000000000 +0200
***************
*** 2096,2101 ****
--- 2096,2116 ----
       contains <literal>Tom Jones</> not <literal>Joe Jones</>.
      </para>
  
+     <para>You can use variables <literal>SQLSTATE</literal> and <literal>SQLERRM</literal>
+     for detection type and message of exception (expecially in <literal>EXCEPTION WHEN OTHERS</literal> 
+     block. <literal>SQLSTATE</literal> contains five numbers exception's code,
+     <literal>SQLSTATE</literal> contains text of exception's message. Outside
+     block with <literal>EXCEPTION</literal> clause <literal>SQLSTATE</literal>
+     and <literal>SQLERRM</literal> are "00000" and "Successfull completation". 
+     </para>
+     
+     <note>
+      <para>Values of <literal>SQLSTATE</literal> are not compatible with ORACLE 
+      <literal>SQLSTATE</literal> values.
+      </para>
+     </note>
+ 
+ 
      <tip>
       <para>
        A block containing an <literal>EXCEPTION</> clause is significantly
diff -c -r --new-file pgsql.old/src/pl/plpgsql/src/gram.y pgsql/src/pl/plpgsql/src/gram.y
*** pgsql.old/src/pl/plpgsql/src/gram.y	2005-06-06 15:29:53.000000000 +0200
--- pgsql/src/pl/plpgsql/src/gram.y	2005-06-06 20:30:30.000000000 +0200
***************
*** 80,85 ****
--- 80,91 ----
  			int  n_initvars;
  			int  *initvarnos;
  		}						declhdr;
+  		struct
+  		{
+  			int sqlstate_varno;
+  			int sqlerrm_varno;
+  			List *proc_exceptions;
+  		}						exception_with_vars;
  		List					*list;
  		PLpgSQL_type			*dtype;
  		PLpgSQL_datum			*scalar;	/* a VAR, RECFIELD, or TRIGARG */
***************
*** 129,135 ****
  %type <stmt>	stmt_dynexecute stmt_getdiag
  %type <stmt>	stmt_open stmt_fetch stmt_close stmt_null
  
! %type <list>	exception_sect proc_exceptions
  %type <exception>	proc_exception
  %type <condition>	proc_conditions
  
--- 135,142 ----
  %type <stmt>	stmt_dynexecute stmt_getdiag
  %type <stmt>	stmt_open stmt_fetch stmt_close stmt_null
  
! %type <exception_with_vars> exception_sect fict_vars_sect
! %type <list>	proc_exceptions
  %type <exception>	proc_exception
  %type <condition>	proc_conditions
  
***************
*** 256,262 ****
  						new->n_initvars = $1.n_initvars;
  						new->initvarnos = $1.initvarnos;
  						new->body		= $4;
! 						new->exceptions	= $5;
  
  						plpgsql_ns_pop();
  
--- 263,272 ----
  						new->n_initvars = $1.n_initvars;
  						new->initvarnos = $1.initvarnos;
  						new->body		= $4;
! 
!  						new->exceptions	= $5.proc_exceptions;
!  						new->sqlstate_varno = $5.sqlstate_varno;
!  						new->sqlerrm_varno = $5.sqlerrm_varno;
  
  						plpgsql_ns_pop();
  
***************
*** 1453,1463 ****
  				;
  
  exception_sect	:
! 					{ $$ = NIL; }
! 				| K_EXCEPTION proc_exceptions
! 					{ $$ = $2; }
  				;
  
  proc_exceptions	: proc_exceptions proc_exception
  						{
  							$$ = lappend($1, $2);
--- 1463,1496 ----
  				;
  
  exception_sect	:
! 					{ $$.proc_exceptions = NIL; }
! 				| K_EXCEPTION fict_vars_sect proc_exceptions
! 					{ 
! 					    $$.proc_exceptions = $3;
! 					    $$.sqlstate_varno = $2.sqlstate_varno;
! 					    $$.sqlerrm_varno = $2.sqlerrm_varno; 
! 					}
  				;
  
+ fict_vars_sect :
+  					{
+  						PLpgSQL_variable	*var;
+  						plpgsql_ns_setlocal(false);
+  						
+  					 	var = plpgsql_build_variable("sqlstate", 0,
+  							 	plpgsql_build_datatype(TEXTOID, -1), true);  
+  						((PLpgSQL_var *) var)->isconst = true;
+  					 	$$.sqlstate_varno = var->dno;
+  					 	
+  						var = plpgsql_build_variable("sqlerrm", 0,
+  							 	plpgsql_build_datatype(TEXTOID, -1), true);  
+  						((PLpgSQL_var *) var)->isconst = true;
+  					 	$$.sqlerrm_varno = var->dno;
+  					} 
+                                 ;
+  
+ 
+ 
  proc_exceptions	: proc_exceptions proc_exception
  						{
  							$$ = lappend($1, $2);
diff -c -r --new-file pgsql.old/src/pl/plpgsql/src/pl_comp.c pgsql/src/pl/plpgsql/src/pl_comp.c
*** pgsql.old/src/pl/plpgsql/src/pl_comp.c	2005-06-06 15:29:53.000000000 +0200
--- pgsql/src/pl/plpgsql/src/pl_comp.c	2005-06-06 19:34:17.000000000 +0200
***************
*** 622,634 ****
  	function->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
  
  	/*
! 	 * Create the magic FOUND variable.
  	 */
  	var = plpgsql_build_variable("found", 0,
  								 plpgsql_build_datatype(BOOLOID, -1),
  								 true);
  	function->found_varno = var->dno;
  
  	/*
  	 * Forget about the above created variables
  	 */
--- 622,646 ----
  	function->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
  
  	/*
! 	 * Create the magic FOUND variable and SQLSTATE and SQLERRM vars
  	 */
  	var = plpgsql_build_variable("found", 0,
  								 plpgsql_build_datatype(BOOLOID, -1),
  								 true);
  	function->found_varno = var->dno;
  
+  	var = plpgsql_build_variable("sqlstate", 0,
+  								 plpgsql_build_datatype(TEXTOID, -1),
+  								 true);
+  	((PLpgSQL_var *) var)->isconst = true;
+  	function->sqlstate_varno = var->dno;
+  	var = plpgsql_build_variable("sqlerrm", 0,
+  								 plpgsql_build_datatype(TEXTOID, -1),
+  								 true);
+  	((PLpgSQL_var *) var)->isconst = true;
+  	function->sqlerrm_varno = var->dno;
+ 
+ 
  	/*
  	 * Forget about the above created variables
  	 */
diff -c -r --new-file pgsql.old/src/pl/plpgsql/src/pl_exec.c pgsql/src/pl/plpgsql/src/pl_exec.c
*** pgsql.old/src/pl/plpgsql/src/pl_exec.c	2005-06-06 15:29:53.000000000 +0200
--- pgsql/src/pl/plpgsql/src/pl_exec.c	2005-06-06 20:35:33.000000000 +0200
***************
*** 180,185 ****
--- 180,189 ----
  static void exec_init_tuple_store(PLpgSQL_execstate *estate);
  static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2);
  static void exec_set_found(PLpgSQL_execstate *estate, bool state);
+ static char *unpack_sql_state(int ssval);
+ static void exec_reset_sqlstate(PLpgSQL_execstate *estate, int sqlstate_varno, 
+ 		    int sqlerrm_varno);
+ 
  
  
  /* ----------
***************
*** 272,281 ****
  	}
  
  	/*
! 	 * Set the magic variable FOUND to false
  	 */
  	exec_set_found(&estate, false);
! 
  	/*
  	 * Now call the toplevel block of statements
  	 */
--- 276,285 ----
  	}
  
  	/*
! 	 * Set the magic variable FOUND to false SQLSTATE 0000 and SQLERRM Successfull ...
  	 */
  	exec_set_found(&estate, false);
! 	exec_reset_sqlstate(&estate,func->sqlstate_varno, func->sqlerrm_varno);
  	/*
  	 * Now call the toplevel block of statements
  	 */
***************
*** 536,544 ****
  	}
  
  	/*
! 	 * Set the magic variable FOUND to false
  	 */
  	exec_set_found(&estate, false);
  
  	/*
  	 * Now call the toplevel block of statements
--- 540,549 ----
  	}
  
  	/*
! 	 * Set the magic variable FOUND to false, SQLSTATE 0000 and SQLERRM Successfull ...
  	 */
  	exec_set_found(&estate, false);
+ 	exec_reset_sqlstate(&estate,func->sqlstate_varno, func->sqlerrm_varno);
  
  	/*
  	 * Now call the toplevel block of statements
***************
*** 747,752 ****
--- 752,775 ----
  	int			i;
  	int			n;
  
+  	/* setup SQLSTATE and SQLERRM really values visible only in exception blok */
+  	
+  	PLpgSQL_var *var;
+  	
+  	if (block->sqlstate_varno && block->sqlerrm_varno)
+  	{
+  	    var = (PLpgSQL_var *) (estate->datums[block->sqlstate_varno]);
+  	    var->isnull = false;
+  	    var->freeval = true;
+  	    var->value = DirectFunctionCall1(textin, CStringGetDatum("00000"));
+  	    
+  	    var = (PLpgSQL_var *) (estate->datums[block->sqlerrm_varno]);
+  	    var->isnull = false;
+  	    var->freeval = true;
+  	    var->value = DirectFunctionCall1(textin, CStringGetDatum("Successful completion"));
+  	}
+ 
+ 
  	/*
  	 * First initialize all variables declared in this block
  	 */
***************
*** 856,861 ****
--- 879,893 ----
  			MemoryContextSwitchTo(oldcontext);
  			CurrentResourceOwner = oldowner;
  
+  			
+  			var = (PLpgSQL_var *) (estate->datums[block->sqlstate_varno]);
+  			pfree((void *) (var->value));
+  			var->value = DirectFunctionCall1(textin, CStringGetDatum(unpack_sql_state(edata->sqlerrcode)));
+  			
+  			var = (PLpgSQL_var *) (estate->datums[block->sqlerrm_varno]);
+  			pfree((void *) (var->value));
+  			var->value = DirectFunctionCall1(textin, CStringGetDatum(edata->message));
+ 
  			/*
  			 * If AtEOSubXact_SPI() popped any SPI context of the subxact,
  			 * it will have left us in a disconnected state.  We need this
***************
*** 2005,2010 ****
--- 2037,2043 ----
  	estate->err_func = func;
  	estate->err_stmt = NULL;
  	estate->err_text = NULL;
+ 
  }
  
  /* ----------
***************
*** 4282,4287 ****
--- 4315,4361 ----
  	var->isnull = false;
  }
  
+ static void
+ exec_reset_sqlstate(PLpgSQL_execstate *estate, int sqlstate_varno, int sqlerrm_varno)
+ {
+     /* setup SQLSTATE and SQLERRM */
+     PLpgSQL_var *var;
+  
+     var = (PLpgSQL_var *) (estate->datums[sqlstate_varno]);
+     var->isnull = false;
+     var->freeval = true;
+     var->value = DirectFunctionCall1(textin, CStringGetDatum("00000"));
+ 
+     var = (PLpgSQL_var *) (estate->datums[sqlerrm_varno]);
+     var->isnull = false;
+     var->freeval = true;
+     var->value = DirectFunctionCall1(textin, CStringGetDatum("Successful completion"));
+    
+ }
+ 
+ /* 
+  * unpack MAKE_SQLSTATE code 
+  * This code is copied from backend/utils/error/elog.c.
+  */
+ 
+ static char *
+ unpack_sql_state(int ssval)
+ {
+     static char tbuf[12];
+     int i;
+    
+     for (i = 0; i < 5; i++)
+     {
+ 	tbuf[i] = PGUNSIXBIT(ssval);
+ 	ssval >>= 6;
+     }
+     tbuf[i] = '\0';
+     
+     return tbuf;
+ }
+ 
+ 
+ 
  /*
   * plpgsql_xact_cb --- post-transaction-commit-or-abort cleanup
   *
diff -c -r --new-file pgsql.old/src/pl/plpgsql/src/plpgsql.h pgsql/src/pl/plpgsql/src/plpgsql.h
*** pgsql.old/src/pl/plpgsql/src/plpgsql.h	2005-06-06 15:29:53.000000000 +0200
--- pgsql/src/pl/plpgsql/src/plpgsql.h	2005-06-06 20:20:43.000000000 +0200
***************
*** 339,344 ****
--- 339,346 ----
  	List	   *exceptions;		/* List of WHEN clauses */
  	int			n_initvars;
  	int		   *initvarnos;
+ 	int sqlstate_varno;
+ 	int sqlerrm_varno;
  } PLpgSQL_stmt_block;
  
  
***************
*** 583,588 ****
--- 585,593 ----
  	int			tg_nargs_varno;
  
  	int			ndatums;
+ 	int sqlstate_varno;
+ 	int sqlerrm_varno;
+ 
  	PLpgSQL_datum **datums;
  	PLpgSQL_stmt_block *action;
  } PLpgSQL_function;
diff -c -r --new-file pgsql.old/src/test/regress/expected/plpgsql.out pgsql/src/test/regress/expected/plpgsql.out
*** pgsql.old/src/test/regress/expected/plpgsql.out	2005-06-06 15:30:06.000000000 +0200
--- pgsql/src/test/regress/expected/plpgsql.out	2005-06-06 20:48:57.000000000 +0200
***************
*** 2380,2382 ****
--- 2380,2417 ----
  CONTEXT:  PL/pgSQL function "missing_return_expr"
  drop function void_return_expr();
  drop function missing_return_expr();
+ --
+ -- SQLSTATE and SQLERRM test
+ --
+ create function excpt_test() returns void as $$
+ begin
+     raise notice '% %', sqlstate, sqlerrm;
+     begin
+ 	raise exception 'usr exception';
+     exception when others then
+ 	raise notice 'caught usr exception % %', sqlstate, sqlerrm;
+ 	begin
+ 	    raise notice '% %', sqlstate, sqlerrm;
+ 	    perform 10/0;
+ 	exception when others then
+ 	    raise notice 'caught usr exception % %', sqlstate, sqlerrm;
+ 	end;
+ 	raise notice '% %', sqlstate, sqlerrm;
+     end;
+     raise notice '% %', sqlstate, sqlerrm;
+ end; $$ language plpgsql;
+ CREATE FUNCTION
+ select excpt_test();
+ NOTICE:  00000 Successful completion
+ NOTICE:  caught usr exception P0001 usr exception
+ NOTICE:  P0001 usr exception
+ NOTICE:  caught usr exception 22012 division by zero
+ NOTICE:  P0001 usr exception
+ NOTICE:  00000 Successful completion
+  excpt_test 
+ ------------
+  
+ (1 row)
+ 
+ drop function excpt_test();
+ DROP FUNCTION
diff -c -r --new-file pgsql.old/src/test/regress/sql/plpgsql.sql pgsql/src/test/regress/sql/plpgsql.sql
*** pgsql.old/src/test/regress/sql/plpgsql.sql	2005-06-06 15:30:03.000000000 +0200
--- pgsql/src/test/regress/sql/plpgsql.sql	2005-06-06 20:41:53.000000000 +0200
***************
*** 2018,2020 ****
--- 2018,2045 ----
  
  drop function void_return_expr();
  drop function missing_return_expr();
+ 
+ --
+ -- SQLSTATE and SQLERRM test
+ --
+ create function excpt_test() returns void as $$
+ begin
+     raise notice '% %', sqlstate, sqlerrm;
+     begin
+ 	raise exception 'usr exception';
+     exception when others then
+ 	raise notice 'caught usr exception % %', sqlstate, sqlerrm;
+ 	begin
+ 	    raise notice '% %', sqlstate, sqlerrm;
+ 	    perform 10/0;
+ 	exception when others then
+ 	    raise notice 'caught usr exception % %', sqlstate, sqlerrm;
+ 	end;
+ 	raise notice '% %', sqlstate, sqlerrm;
+     end;
+     raise notice '% %', sqlstate, sqlerrm;
+ end; $$ language plpgsql;
+ 
+ select excpt_test();
+ 
+ drop function excpt_test();
