diff -c -r --new-file src.00/gram.y src/gram.y
*** src.00/gram.y	2005-06-14 13:57:13.000000000 +0200
--- src/gram.y	2005-06-15 08:29:56.000000000 +0200
***************
*** 39,44 ****
--- 39,45 ----
  #include "plpgsql.h"
  
  #include "parser/parser.h"
+ #include "utils/elog.h"
  
  static PLpgSQL_expr		*read_sql_construct(int until,
  											int until2,
***************
*** 95,107 ****
  		PLpgSQL_exception_block	*exception_block;
  		PLpgSQL_nsitem			*nsitem;
  		PLpgSQL_diag_item		*diagitem;
  }
  
  %type <declhdr> decl_sect
  %type <varname> decl_varname
  %type <str>		decl_renname
! %type <ival>	decl_const decl_notnull
! %type <expr>	decl_defval decl_cursor_query
  %type <dtype>	decl_datatype
  %type <row>		decl_cursor_args
  %type <list>	decl_cursor_arglist
--- 96,109 ----
  		PLpgSQL_exception_block	*exception_block;
  		PLpgSQL_nsitem			*nsitem;
  		PLpgSQL_diag_item		*diagitem;
+ 		PLpgSQL_uexception		*uexception;
  }
  
  %type <declhdr> decl_sect
  %type <varname> decl_varname
  %type <str>		decl_renname
! %type <ival>	decl_const 
! %type <expr>	decl_cursor_query
  %type <dtype>	decl_datatype
  %type <row>		decl_cursor_args
  %type <list>	decl_cursor_arglist
***************
*** 121,126 ****
--- 123,129 ----
  %type <str>		opt_lblname opt_label
  %type <str>		opt_exitlabel
  %type <str>		execsql_start
+ %type <str>		exception_name
  
  %type <list>	proc_sect proc_stmts stmt_else loop_body
  %type <stmt>	proc_stmt pl_block
***************
*** 144,149 ****
--- 147,153 ----
  %type <ival>	getdiag_kind getdiag_target
  
  %type <ival>	lno
+ %type <uexception>	opt_uexception
  
  		/*
  		 * Keyword tokens
***************
*** 212,217 ****
--- 216,222 ----
  %token	T_LABEL
  %token	T_WORD
  %token	T_ERROR
+ %token  T_EXCEPTION
  
  %token	O_OPTION
  %token	O_DUMP
***************
*** 314,323 ****
  					{	$$ = NULL;	}
  				;
  
! decl_statement	: decl_varname decl_const decl_datatype decl_notnull decl_defval
  					{
  						PLpgSQL_variable	*var;
! 
  						var = plpgsql_build_variable($1.name, $1.lineno,
  													 $3, true);
  						if ($2)
--- 319,385 ----
  					{	$$ = NULL;	}
  				;
  
! decl_statement	: decl_varname decl_const decl_datatype
  					{
  						PLpgSQL_variable	*var;
! 						int notnull = 0;
! 						int tok;
! 						PLpgSQL_expr *default_val = NULL;
! 						char *s = NULL;
! 						
! 						if ($3->ttype == PLPGSQL_TTYPE_EXCEPTION) 
! 						{
! 						    if ((tok = yylex()) != ';')
! 						    {
! 							if (tok != K_ASSIGN && tok != K_DEFAULT)
! 							    yyerror("Syntax error.");
! 							else
! 							{
! 							    if (yylex() != T_STRING)
! 								    ereport(ERROR,
! 										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 										 errmsg("Value for exception type have to be string")));
! 							    else
! 							    {	
! 								s = plpgsql_get_string_value();
! 								if (strlen(s) != 5)
! 								    yyerror("Wrong format");
! 								if (yylex() != ';')
! 								    yyerror("Syntax error.");
! 							    }
! 							}
! 						    }
! 						}
! 						else
! 						{
! 						    if ((tok = yylex()) != ';')
! 						    {
! 						    	if (tok == K_NOT)
! 							{
! 							    if (yylex() == K_NULL)
! 							    {
! 								notnull = 1;
! 								tok = yylex();
! 							    }
! 							    else
! 								yyerror("Syntax error.");
! 							}
! 						    }
! 						    if (tok != ';')
! 						    {
! 							if (tok == K_ASSIGN || tok == K_DEFAULT)
! 							{						    
! 							    plpgsql_ns_setlocal(false);
! 							    default_val = plpgsql_read_expression(';', ";");
! 							    plpgsql_ns_setlocal(true);
! 							}
! 							else
! 							    yyerror("Syntax error.");
! 						    }
! 							
! 						}
! 						    
! 						
  						var = plpgsql_build_variable($1.name, $1.lineno,
  													 $3, true);
  						if ($2)
***************
*** 329,352 ****
  										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  										 errmsg("row or record variable cannot be CONSTANT")));
  						}
! 						if ($4)
  						{
  							if (var->dtype == PLPGSQL_DTYPE_VAR)
! 								((PLpgSQL_var *) var)->notnull = $4;
  							else
  								ereport(ERROR,
  										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  										 errmsg("row or record variable cannot be NOT NULL")));
  						}
! 						if ($5 != NULL)
  						{
  							if (var->dtype == PLPGSQL_DTYPE_VAR)
! 								((PLpgSQL_var *) var)->default_val = $5;
  							else
  								ereport(ERROR,
  										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  										 errmsg("default value for row or record variable is not supported")));
  						}
  					}
  				| decl_varname K_ALIAS K_FOR decl_aliasitem ';'
  					{
--- 391,424 ----
  										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  										 errmsg("row or record variable cannot be CONSTANT")));
  						}
! 						if (notnull)
  						{
  							if (var->dtype == PLPGSQL_DTYPE_VAR)
! 								((PLpgSQL_var *) var)->notnull = notnull;
  							else
  								ereport(ERROR,
  										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  										 errmsg("row or record variable cannot be NOT NULL")));
  						}
! 						if (default_val != NULL)
  						{
  							if (var->dtype == PLPGSQL_DTYPE_VAR)
! 								((PLpgSQL_var *) var)->default_val = default_val;
  							else
  								ereport(ERROR,
  										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  										 errmsg("default value for row or record variable is not supported")));
+ 
  						}
+ 						if (s != NULL)
+ 						    ((PLpgSQL_uexception *) var)->sqlerrstate = 
+ 							    MAKE_SQLSTATE(s[0],s[1],s[2],s[3],s[4]);
+ 						else
+ 						    ((PLpgSQL_uexception *) var)->sqlerrstate =
+ 							    plpgsql_newUsrExceptions();
+ 						
+ 						
+ 						
  					}
  				| decl_varname K_ALIAS K_FOR decl_aliasitem ';'
  					{
***************
*** 530,554 ****
  					}
  				;
  
- decl_notnull	:
- 					{ $$ = 0; }
- 				| K_NOT K_NULL
- 					{ $$ = 1; }
- 				;
- 
- decl_defval		: ';'
- 					{ $$ = NULL; }
- 				| decl_defkey
- 					{
- 						plpgsql_ns_setlocal(false);
- 						$$ = plpgsql_read_expression(';', ";");
- 						plpgsql_ns_setlocal(true);
- 					}
- 				;
- 
- decl_defkey		: K_ASSIGN
- 				| K_DEFAULT
- 				;
  
  proc_sect		:
  					{
--- 602,607 ----
***************
*** 1157,1163 ****
  					}
  				;
  
! stmt_raise		: K_RAISE lno raise_level raise_msg
  					{
  						PLpgSQL_stmt_raise		*new;
  						int	tok;
--- 1210,1216 ----
  					}
  				;
  
! stmt_raise		: K_RAISE lno raise_level opt_uexception raise_msg
  					{
  						PLpgSQL_stmt_raise		*new;
  						int	tok;
***************
*** 1167,1174 ****
  						new->cmd_type	= PLPGSQL_STMT_RAISE;
  						new->lineno		= $2;
  						new->elog_level = $3;
! 						new->message	= $4;
! 						new->params		= NIL;
  
  						tok = yylex();
  
--- 1220,1236 ----
  						new->cmd_type	= PLPGSQL_STMT_RAISE;
  						new->lineno		= $2;
  						new->elog_level = $3;
! 						new->message	= $5;
! 						new->params		= NULL;					
! 						
! 						if ($4 != NULL)
! 						{
! 						    new->sqlerrstate = $4->sqlerrstate;
! 						    new->refname = $4->refname;
! 						}
! 						else
! 						    new->sqlerrstate =  					
! 						    	(new->elog_level >= ERROR) ? ERRCODE_RAISE_EXCEPTION : 0;
  
  						tok = yylex();
  
***************
*** 1200,1205 ****
--- 1262,1279 ----
  					}
  				;
  
+ 
+ opt_uexception		: T_EXCEPTION 
+ 					{ 
+ 						$$ = yylval.uexception; 
+ 					}
+ 			| /* EMPTY */ 	
+ 					{	
+ 						$$ = NULL;
+ 					}
+ 				;
+ 			    
+ 
  raise_msg		: T_STRING
  					{
  						$$ = plpgsql_get_string_value();
***************
*** 1551,1557 ****
  					}
  				;
  
! proc_conditions	: proc_conditions K_OR opt_lblname
  						{
  							PLpgSQL_condition	*old;
  
--- 1625,1631 ----
  					}
  				;
  
! proc_conditions	: proc_conditions K_OR exception_name
  						{
  							PLpgSQL_condition	*old;
  
***************
*** 1561,1567 ****
  
  							$$ = $1;
  						}
! 				| opt_lblname
  						{
  							$$ = plpgsql_parse_err_condition($1);
  						}
--- 1635,1641 ----
  
  							$$ = $1;
  						}
! 				| exception_name
  						{
  							$$ = plpgsql_parse_err_condition($1);
  						}
***************
*** 1626,1631 ****
--- 1700,1719 ----
  					}
  				;
  
+ 
+ exception_name		: T_WORD
+ 					{
+ 						char	*name;
+ 
+ 						plpgsql_convert_ident(yytext, &name, 1);
+ 						$$ = name;
+ 					}
+ 			| T_EXCEPTION
+ 					{
+ 						$$ = yylval.uexception->refname; 
+ 					}
+ 				;
+ 
  lno				:
  					{
  						$$ = plpgsql_error_lineno = plpgsql_scanner_lineno();
***************
*** 1793,1798 ****
--- 1881,1893 ----
  	if (tok == YYEMPTY)
  		tok = yylex();
  
+ 	if (tok == K_EXCEPTION)
+ 	{
+ 	    result = (PLpgSQL_type *) palloc(sizeof(PLpgSQL_type));
+ 	    result->typname = "exception";
+ 	    result->ttype = PLPGSQL_TTYPE_EXCEPTION;
+ 	    return result;
+ 	}
  	if (tok == T_DTYPE)
  	{
  		/* lexer found word%TYPE and did its thing already */
diff -c -r --new-file src.00/pl_comp.c src/pl_comp.c
*** src.00/pl_comp.c	2005-06-14 13:57:13.000000000 +0200
--- src/pl_comp.c	2005-06-15 07:49:02.000000000 +0200
***************
*** 58,63 ****
--- 58,64 ----
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/syscache.h"
+ #include "utils/elog.h"
  
  
  /* ----------
***************
*** 80,85 ****
--- 81,88 ----
  bool		plpgsql_DumpExecTree = false;
  bool		plpgsql_check_syntax = false;
  
+ int 	plpgsql_user_excpt;
+ 
  PLpgSQL_function *plpgsql_curr_compile;
  
  /* A context appropriate for short-term allocs during compilation */
***************
*** 294,299 ****
--- 297,303 ----
  
  	plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
  	plpgsql_error_lineno = 0;
+ 	plpgsql_user_excpt = 0;
  
  	/*
  	 * Setup error traceback support for ereport()
***************
*** 904,909 ****
--- 908,917 ----
  			case PLPGSQL_NSTYPE_ROW:
  				plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[nse->itemno]);
  				return T_ROW;
+ 				
+ 			case PLPGSQL_NSTYPE_EXCEPTION:
+ 				plpgsql_yylval.uexception = (PLpgSQL_uexception *) (plpgsql_Datums[nse->itemno]);
+ 				return T_EXCEPTION;
  
  			default:
  				return T_ERROR;
***************
*** 1626,1631 ****
--- 1634,1657 ----
  				result = (PLpgSQL_variable *) rec;
  				break;
  			}
+ 		case PLPGSQL_TTYPE_EXCEPTION:
+ 			{
+ 			    /* Exception pseudo type */
+ 			    PLpgSQL_uexception *excpt;
+ 			    
+ 			    excpt = palloc0(sizeof(PLpgSQL_uexception));
+ 			    excpt->dtype = PLPGSQL_DTYPE_EXCEPTION;
+ 			    excpt->refname = pstrdup(refname);
+ 			    excpt->lineno = lineno;
+ 			    
+ 			    plpgsql_adddatum((PLpgSQL_datum *) excpt);
+ 			    if (add2namespace)
+ 				    plpgsql_ns_additem(PLPGSQL_NSTYPE_EXCEPTION,
+ 								    excpt->varno,
+ 								    refname);
+ 			    result = (PLpgSQL_variable *) excpt;
+ 			    break;
+ 			}
  		case PLPGSQL_TTYPE_PSEUDO:
  			ereport(ERROR,
  					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 1893,1898 ****
--- 1919,1932 ----
  	PLpgSQL_condition *new;
  	PLpgSQL_condition *prev;
  
+ 	PLpgSQL_nsitem *nse;
+ 	char	   *cp[1];
+ 
+ 
+ 	/* Do case conversion and word separation */
+ 	plpgsql_convert_ident(condname, cp, 1);
+ 
+ 
  	/*
  	 * XXX Eventually we will want to look for user-defined exception
  	 * names here.
***************
*** 1925,1930 ****
--- 1959,1989 ----
  	}
  
  	if (!prev)
+ 	{
+ 	    /* Do case conversion and word separation */
+ 	    plpgsql_convert_ident(condname, cp, 1);
+ 	    
+ 	    /*
+ 	    * Do a lookup on the compiler's namestack
+ 	    */
+ 	    nse = plpgsql_ns_lookup(cp[0], NULL);
+ 	    
+ 	    if (nse != NULL)
+ 	    {
+ 		PLpgSQL_uexception *excpt = (PLpgSQL_uexception *) (plpgsql_Datums[nse->itemno]);
+ 		if (nse->itemtype == PLPGSQL_NSTYPE_EXCEPTION)
+ 		{
+ 		    new = palloc(sizeof(PLpgSQL_condition));
+ 		    new->sqlerrstate = excpt->sqlerrstate;
+ 		    new->condname = condname;
+ 		    new->next = prev;
+ 		    prev = new;	    
+ 		}
+     	    }
+ 	    pfree(cp[0]);
+ 	}
+ 
+ 	if (!prev)
  		ereport(ERROR,
  				(errcode(ERRCODE_UNDEFINED_OBJECT),
  				 errmsg("unrecognized exception condition \"%s\"",
***************
*** 1934,1940 ****
  }
  
  /* ----------
!  * plpgsql_adddatum			Add a variable, record or row
   *					to the compiler's datum list.
   * ----------
   */
--- 1993,1999 ----
  }
  
  /* ----------
!  * plpgsql_adddatum			Add a variable, record or row, or exception
   *					to the compiler's datum list.
   * ----------
   */
***************
*** 2177,2179 ****
--- 2236,2253 ----
  	if (hentry == NULL)
  		elog(WARNING, "trying to delete function that does not exist");
  }
+ 
+ #define MAX_USER_EXCPT 999
+ 
+ int
+ plpgsql_newUsrExceptions(void)
+ {
+     char rs[4];    
+ 
+     if (plpgsql_user_excpt == MAX_USER_EXCPT)
+      	    ereport(ERROR,
+ 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 				 errmsg("Too much user's exception")));
+     sprintf(rs,"%03d", ++plpgsql_user_excpt);
+     return MAKE_SQLSTATE('U','0', rs[0],rs[1],rs[2]);
+ }
diff -c -r --new-file src.00/pl_exec.c src/pl_exec.c
*** src.00/pl_exec.c	2005-06-14 13:57:13.000000000 +0200
--- src/pl_exec.c	2005-06-15 08:44:34.000000000 +0200
***************
*** 267,273 ****
  				break;
  
  			default:
! 				elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype);
  		}
  	}
  
--- 267,273 ----
  				break;
  
  			default:
! 				elog(ERROR, ">>unrecognized dtype: %d", func->datums[i]->dtype);
  		}
  	}
  
***************
*** 698,705 ****
  			result = datum;
  			break;
  
  		default:
! 			elog(ERROR, "unrecognized dtype: %d", datum->dtype);
  			result = NULL;		/* keep compiler quiet */
  			break;
  	}
--- 698,708 ----
  			result = datum;
  			break;
  
+ 		case PLPGSQL_DTYPE_EXCEPTION:
+ 			result = NULL;
+ 			break;
  		default:
! 			elog(ERROR, ">>>>>unrecognized dtype: %d", datum->dtype);
  			result = NULL;		/* keep compiler quiet */
  			break;
  	}
***************
*** 800,805 ****
--- 803,809 ----
  
  			case PLPGSQL_DTYPE_RECFIELD:
  			case PLPGSQL_DTYPE_ARRAYELEM:
+ 			case PLPGSQL_DTYPE_EXCEPTION:
  				break;
  
  			default:
***************
*** 1960,1968 ****
  	 */
  	estate->err_text = raise_skip_msg;	/* suppress traceback of raise */
  
! 	ereport(stmt->elog_level,
! 	 ((stmt->elog_level >= ERROR) ? errcode(ERRCODE_RAISE_EXCEPTION) : 0,
! 	  errmsg_internal("%s", plpgsql_dstring_get(&ds))));
  
  	estate->err_text = NULL;	/* un-suppress... */
  
--- 1964,1981 ----
  	 */
  	estate->err_text = raise_skip_msg;	/* suppress traceback of raise */
  
!     	if (stmt->refname != NULL)
! 	    ereport(stmt->elog_level,		/* User's exception */
! 		(errcode(stmt->sqlerrstate), 
! 		 errdetail("User's exception sqlstate: %s, name: %s", unpack_sql_state(stmt->sqlerrstate), stmt->refname),
! 		 errhint("Unhandled user's exception, from RAISE stmt on line %d", stmt->lineno),
! 		    errmsg_internal("%s", plpgsql_dstring_get(&ds))));
! 	else
! 	    ereport(stmt->elog_level,
! 		(errcode(stmt->sqlerrstate), //(stmt->elog_level >= ERROR) ? errcode(ERRCODE_RAISE_EXCEPTION) : 0,
! 		    errmsg_internal("%s", plpgsql_dstring_get(&ds))));
! 	 
! 	  
  
  	estate->err_text = NULL;	/* un-suppress... */
  
diff -c -r --new-file src.00/plpgsql.h src/plpgsql.h
*** src.00/plpgsql.h	2005-06-14 13:57:13.000000000 +0200
--- src/plpgsql.h	2005-06-15 08:29:06.000000000 +0200
***************
*** 58,64 ****
  	PLPGSQL_NSTYPE_LABEL,
  	PLPGSQL_NSTYPE_VAR,
  	PLPGSQL_NSTYPE_ROW,
! 	PLPGSQL_NSTYPE_REC
  };
  
  /* ----------
--- 58,65 ----
  	PLPGSQL_NSTYPE_LABEL,
  	PLPGSQL_NSTYPE_VAR,
  	PLPGSQL_NSTYPE_ROW,
! 	PLPGSQL_NSTYPE_REC,
! 	PLPGSQL_NSTYPE_EXCEPTION
  };
  
  /* ----------
***************
*** 73,79 ****
  	PLPGSQL_DTYPE_RECFIELD,
  	PLPGSQL_DTYPE_ARRAYELEM,
  	PLPGSQL_DTYPE_EXPR,
! 	PLPGSQL_DTYPE_TRIGARG
  };
  
  /* ----------
--- 74,81 ----
  	PLPGSQL_DTYPE_RECFIELD,
  	PLPGSQL_DTYPE_ARRAYELEM,
  	PLPGSQL_DTYPE_EXPR,
! 	PLPGSQL_DTYPE_TRIGARG,
! 	PLPGSQL_DTYPE_EXCEPTION
  };
  
  /* ----------
***************
*** 85,91 ****
  	PLPGSQL_TTYPE_SCALAR,		/* scalar types and domains */
  	PLPGSQL_TTYPE_ROW,			/* composite types */
  	PLPGSQL_TTYPE_REC,			/* RECORD pseudotype */
! 	PLPGSQL_TTYPE_PSEUDO		/* other pseudotypes */
  };
  
  /* ----------
--- 87,94 ----
  	PLPGSQL_TTYPE_SCALAR,		/* scalar types and domains */
  	PLPGSQL_TTYPE_ROW,			/* composite types */
  	PLPGSQL_TTYPE_REC,			/* RECORD pseudotype */
! 	PLPGSQL_TTYPE_PSEUDO,		/* other pseudotypes */
! 	PLPGSQL_TTYPE_EXCEPTION
  };
  
  /* ----------
***************
*** 189,194 ****
--- 192,206 ----
  	int			lineno;
  } PLpgSQL_variable;
  
+ typedef struct
+ {
+ 	int 			dtype;
+ 	int			varno;
+ 	char	*refname;
+ 	int			lineno;
+ 	int			sqlerrstate;
+ } PLpgSQL_uexception;
+ 
  typedef struct PLpgSQL_expr
  {								/* SQL Query to plan and execute	*/
  	int			dtype;
***************
*** 514,520 ****
--- 526,534 ----
  	int			cmd_type;
  	int			lineno;
  	int			elog_level;
+ 	int 			sqlerrstate;			
  	char	   *message;
+ 	char       *refname;			/* name of user's exception */
  	List	   *params;			/* list of expressions */
  } PLpgSQL_stmt_raise;
  
***************
*** 686,691 ****
--- 700,707 ----
  extern void plpgsql_HashTableInit(void);
  extern void plpgsql_compile_error_callback(void *arg);
  
+ extern int plpgsql_newUsrExceptions(void);
+ 
  /* ----------
   * Functions in pl_handler.c
   * ----------
