diff -c -r pgsql.old/src/pl/plpgsql/src/gram.y pgsql/src/pl/plpgsql/src/gram.y
*** pgsql.old/src/pl/plpgsql/src/gram.y	2005-04-07 16:53:04.000000000 +0200
--- pgsql/src/pl/plpgsql/src/gram.y	2005-04-19 14:50:19.000000000 +0200
***************
*** 80,85 ****
--- 80,90 ----
  			int  n_initvars;
  			int  *initvarnos;
  		}						declhdr;
+                 struct 
+   		{
+   	    		int sqlstate_varno;
+   			int sqlerrm_varno;
+   		}						fict_vars;
  		List					*list;
  		PLpgSQL_type			*dtype;
  		PLpgSQL_datum			*scalar;	/* a VAR, RECFIELD, or TRIGARG */
***************
*** 96,101 ****
--- 101,107 ----
  		PLpgSQL_diag_item		*diagitem;
  }
  
+ %type <fict_vars> fict_vars_sect
  %type <declhdr> decl_sect
  %type <varname> decl_varname
  %type <str>		decl_renname
***************
*** 244,262 ****
  				| ';'
  				;
  
! pl_block		: decl_sect K_BEGIN lno proc_sect exception_sect K_END
  					{
  						PLpgSQL_stmt_block *new;
  
  						new = palloc0(sizeof(PLpgSQL_stmt_block));
  
  						new->cmd_type	= PLPGSQL_STMT_BLOCK;
! 						new->lineno		= $3;
  						new->label		= $1.label;
  						new->n_initvars = $1.n_initvars;
  						new->initvarnos = $1.initvarnos;
! 						new->body		= $4;
! 						new->exceptions	= $5;
  
  						plpgsql_ns_pop();
  
--- 250,271 ----
  				| ';'
  				;
  
! pl_block		: decl_sect fict_vars_sect K_BEGIN lno proc_sect exception_sect K_END
  					{
  						PLpgSQL_stmt_block *new;
  
  						new = palloc0(sizeof(PLpgSQL_stmt_block));
  
  						new->cmd_type	= PLPGSQL_STMT_BLOCK;
! 						new->lineno		= $4;
  						new->label		= $1.label;
  						new->n_initvars = $1.n_initvars;
  						new->initvarnos = $1.initvarnos;
! 						new->body		= $5;
! 						new->exceptions	= $6;
! 
!  						new->sqlstate_varno = $2.sqlstate_varno;
!   						new->sqlerrm_varno = $2.sqlerrm_varno;
  
  						plpgsql_ns_pop();
  
***************
*** 264,269 ****
--- 273,290 ----
  					}
  				;
  
+ fict_vars_sect            :
+   					{
+  					        plpgsql_ns_setlocal(false);
+   						PLpgSQL_variable	*var;
+  						var = plpgsql_build_variable("sqlstate", 0,
+   									     plpgsql_build_datatype(TEXTOID, -1), true);  
+   						$$.sqlstate_varno = var->dno;
+  						var = plpgsql_build_variable("sqlerrm", 0,
+   									     plpgsql_build_datatype(TEXTOID, -1), true);  
+   					        $$.sqlerrm_varno = var->dno;
+   						plpgsql_add_initdatums(NULL);
+   					};
  
  decl_sect		: opt_label
  					{
diff -c -r 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-04-07 16:53:04.000000000 +0200
--- pgsql/src/pl/plpgsql/src/pl_exec.c	2005-04-19 14:58:04.000000000 +0200
***************
*** 179,184 ****
--- 179,185 ----
  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);
  
  
  /* ----------
***************
*** 746,751 ****
--- 747,766 ----
  	int			i;
  	int			n;
  
+ 
+   	/* setup SQLSTATE and SQLERRM */
+   	PLpgSQL_var *var;
+   
+   	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("Sucessful completion"));
+ 
  	/*
  	 * First initialize all variables declared in this block
  	 */
***************
*** 854,859 ****
--- 869,884 ----
  			RollbackAndReleaseCurrentSubTransaction();
  			MemoryContextSwitchTo(oldcontext);
  			CurrentResourceOwner = oldowner;
+  
+   			/* set SQLSTATE and SQLERRM variables */
+  			
+   			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,
***************
*** 918,923 ****
--- 943,970 ----
  	return PLPGSQL_RC_OK;
  }
  
+ /* 
+  * unpack MAKE_SQLSTATE code 
+  * This code is redundand backend/utils/error/elog.c. I din't 
+  * wont modify different part than plpgsql
+  */
+  
+ 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;
+ }
+ 
+ 
  
  /* ----------
   * exec_stmts			Iterate over a list of statements
diff -c -r pgsql.old/src/pl/plpgsql/src/plpgsql.h pgsql/src/pl/plpgsql/src/plpgsql.h
*** pgsql.old/src/pl/plpgsql/src/plpgsql.h	2005-04-05 08:22:16.000000000 +0200
--- pgsql/src/pl/plpgsql/src/plpgsql.h	2005-04-19 14:59:26.000000000 +0200
***************
*** 339,344 ****
--- 339,347 ----
  	List	   *exceptions;		/* List of WHEN clauses */
  	int			n_initvars;
  	int		   *initvarnos;
+   	int		sqlstate_varno;
+         int     	sqlerrm_varno;
+ 
  } PLpgSQL_stmt_block;
  
  
diff -c -r pgsql.old/src/test/regress/expected/plpgsql.out pgsql/src/test/regress/expected/plpgsql.out
*** pgsql.old/src/test/regress/expected/plpgsql.out	2005-04-07 16:53:04.000000000 +0200
--- pgsql/src/test/regress/expected/plpgsql.out	2005-04-19 16:53:38.000000000 +0200
***************
*** 2380,2382 ****
--- 2380,2407 ----
  CONTEXT:  PL/pgSQL function "missing_return_expr"
  drop function void_return_expr();
  drop function missing_return_expr();
+ -- test SQLSTATE and SQLERRM
+ create or replace function trap_exceptions() returns void as $_$
+ begin
+    begin
+      raise exception 'first exception';
+    exception when others then
+      raise notice '% %', SQLSTATE, SQLERRM;
+    end;
+    raise notice '% %', SQLSTATE, SQLERRM;
+    begin
+      raise exception 'last exception';
+    exception when others then
+      raise notice '% %', SQLSTATE, SQLERRM;
+    end;
+    return;
+ end; $_$ language plpgsql;
+ select trap_exceptions();
+ NOTICE:  P0001 first exception
+ NOTICE:  000000 Sucessful completion
+ NOTICE:  P0001 last exception
+  trap_exceptions 
+ -----------------
+  
+ (1 row)
+ drop function trap_exceptions();
diff -c -r pgsql.old/src/test/regress/sql/plpgsql.sql pgsql/src/test/regress/sql/plpgsql.sql
*** pgsql.old/src/test/regress/sql/plpgsql.sql	2005-04-07 16:53:04.000000000 +0200
--- pgsql/src/test/regress/sql/plpgsql.sql	2005-04-19 16:53:42.000000000 +0200
***************
*** 2018,2020 ****
--- 2018,2040 ----
  
  drop function void_return_expr();
  drop function missing_return_expr();
+ -- test SQLSTATE and SQLERRM
+ create or replace function trap_exceptions() returns void as $_$
+ begin
+    begin
+      raise exception 'first exception';
+    exception when others then
+      raise notice '% %', SQLSTATE, SQLERRM;
+    end;
+    raise notice '% %', SQLSTATE, SQLERRM;
+    begin
+      raise exception 'last exception';
+    exception when others then
+      raise notice '% %', SQLSTATE, SQLERRM;
+    end;
+    return;
+ end; $_$ language plpgsql;
+ 
+ select trap_exceptions();
+ 
+ drop function trap_exceptions();
