Hello

I am sending enhanced version of original patch.


2008/5/5 Tom Lane <[EMAIL PROTECTED]>:
> "Pavel Stehule" <[EMAIL PROTECTED]> writes:
>> this patch adds possibility to set additional options (SQLSTATE,
>> DETAIL, DETAIL_LOG and HINT) for RAISE statement,
>
> I looked this over briefly.  A couple of comments:
>
> * Raising errors via hard-coded SQLSTATEs seems pretty unfriendly,
> at least for cases where we are reporting built-in errors.  Wouldn't
> it be better to be able to raise errors using the same SQLSTATE names
> that are recognized in EXCEPTION clauses?

There are new attribut CONDITION - all defined condition are possible
without duplicit names and category conditions.

example:
RAISE NOTICE 'custom unique violation' USING (CONDITION = 'unique_violation');

>
> * If we are going to let people throw random SQLSTATEs, there had better
> be a way to name those same SQLSTATEs in EXCEPTION.
>
we can trap EXCEPTION defined via SQLSTATE now:

EXCEPTION
   WHEN SQLSTATE 22001 THEN ...


> * I don't really like exposing DETAIL_LOG in this.  That was a spur of
> the moment addition and we might take it out again; I think it's way
> premature to set it in stone by exposing it as a plpgsql feature.

removed

>
> * Please avoid using errstart() directly.  This is unwarranted intimacy
> with elog.h's implementation and I also think it will have unpleasant
> behavior if an error occurs while evaluating the RAISE arguments.
> (In fact, I think a user could easily force a backend PANIC that way.)
> The approved way to deal with ereport options that might not be there
> is like this:
>
>        ereport(ERROR,
>                ( ...,
>                 have_sqlstate ? errcode(...) : 0,
>                  ...
>
> That is, you should evaluate all the options into local variables
> and then do one normal ereport call.
>

changed
> * // comments are against our coding conventions.
>
>                        regards, tom lane
>

removed


Regards
Pavel Stehule
*** ./doc/src/sgml/plpgsql.sgml.orig	2008-05-06 11:05:05.000000000 +0200
--- ./doc/src/sgml/plpgsql.sgml	2008-05-10 01:09:54.000000000 +0200
***************
*** 2184,2192 ****
--- 2184,2197 ----
      WHEN <replaceable>condition</replaceable> <optional> OR <replaceable>condition</replaceable> ... </optional> THEN
          <replaceable>handler_statements</replaceable>
      <optional> WHEN <replaceable>condition</replaceable> <optional> OR <replaceable>condition</replaceable> ... </optional> THEN
+           <replaceable>handler_statements</replaceable> </optional>
+     <optional> WHEN <replaceable>SQLSTATE xxxxx</replaceable> <optional> OR <replaceable>SQLSTATE xxxxx</replaceable> ... </optional> THEN
+           <replaceable>handler_statements</replaceable> </optional>
+     <optional> WHEN <replaceable>SQLSTATE xxxxx</replaceable> <optional> OR <replaceable>condition</replaceable> ... </optional> THEN
            <replaceable>handler_statements</replaceable>
        ... </optional>
  END;
+ 
  </synopsis>
      </para>
  
***************
*** 2215,2221 ****
       condition name <literal>OTHERS</> matches every error type except
       <literal>QUERY_CANCELED</>.  (It is possible, but often unwise,
       to trap <literal>QUERY_CANCELED</> by name.)  Condition names are
!      not case-sensitive.
      </para>
  
      <para>
--- 2220,2227 ----
       condition name <literal>OTHERS</> matches every error type except
       <literal>QUERY_CANCELED</>.  (It is possible, but often unwise,
       to trap <literal>QUERY_CANCELED</> by name.)  Condition names are
!      not case-sensitive. Any condition can be subtituted by SQLSTATE
!      value.
      </para>
  
      <para>
***************
*** 2243,2248 ****
--- 2249,2262 ----
              RAISE NOTICE 'caught division_by_zero';
              RETURN x;
      END;
+ 
+     ...
+     -- or same with SQLSTATE specification
+     EXCEPTION
+         WHEN SQLSTATE 22012 THEN
+             RAISE NOTICE 'caught division_by_zero';
+             RETURN x;
+     END;
  </programlisting>
  
       When control reaches the assignment to <literal>y</>, it will
***************
*** 2832,2838 ****
      raise errors.
  
  <synopsis>
! RAISE <replaceable class="parameter">level</replaceable> '<replaceable class="parameter">format</replaceable>' <optional>, <replaceable class="parameter">expression</replaceable> <optional>, ...</optional></optional>;
  </synopsis>
  
      Possible levels are <literal>DEBUG</literal>,
--- 2846,2852 ----
      raise errors.
  
  <synopsis>
! RAISE <replaceable class="parameter">level</replaceable> '<replaceable class="parameter">format</replaceable>' <optional>, <replaceable class="parameter">expression</replaceable> <optional>, ...</optional></optional> <optional> USING ( <replaceable class="parameter">option</replaceable> = <replaceable class="parameter">expression</replaceable> <optional>, ... </optional> ) </optional>;
  </synopsis>
  
      Possible levels are <literal>DEBUG</literal>,
***************
*** 2875,2891 ****
     <para>
      This example will abort the transaction with the given error message:
  <programlisting>
! RAISE EXCEPTION 'Nonexistent ID --> %', user_id;
  </programlisting>
     </para>
  
      <para>
!      <command>RAISE EXCEPTION</command> presently always generates
!      the same <varname>SQLSTATE</varname> code, <literal>P0001</>, no matter what message
       it is invoked with.  It is possible to trap this exception with
       <literal>EXCEPTION ... WHEN RAISE_EXCEPTION THEN ...</> but there
       is no way to tell one <command>RAISE</> from another.
      </para>
   </sect1>
  
   <sect1 id="plpgsql-trigger">
--- 2889,2919 ----
     <para>
      This example will abort the transaction with the given error message:
  <programlisting>
! RAISE EXCEPTION 'Nonexistent ID --> %', user_id USING (hint = 'Please, check your user id');
  </programlisting>
     </para>
  
      <para>
!      <command>RAISE EXCEPTION</command> presently generates
!      the same <varname>SQLSTATE</varname> code, <literal>P0001</> , no matter what message
       it is invoked with.  It is possible to trap this exception with
       <literal>EXCEPTION ... WHEN RAISE_EXCEPTION THEN ...</> but there
       is no way to tell one <command>RAISE</> from another.
      </para>
+     
+     <para>
+       With additional options is possible set some log informaition related to 
+       raised exception. Possible options are <literal>SQLSTATE</literal>,
+       <literal>CONDTION</literal>, <literal>DETAIL</literal> and <literal>HINT</literal>.
+       You cannot specify <literal>SQLSTATE</literal> and <literal>CONDITION</literal> 
+       together. Some conditions are not unique. These conditions cannot be used.
+       Conditional names are case sensitive. <literal>SQLSTATE</literal> categories 
+       (like xx000) cannot be used too.
+ <programlisting>
+ RAISE EXCEPTION 'custom unique violation' USING (condition = 'unique_violation');
+ </programlisting>
+     </para> 
+     
   </sect1>
  
   <sect1 id="plpgsql-trigger">
*** ./src/pl/plpgsql/src/gram.y.orig	2008-05-06 23:54:42.000000000 +0200
--- ./src/pl/plpgsql/src/gram.y	2008-05-09 20:32:29.000000000 +0200
***************
*** 53,58 ****
--- 53,59 ----
  									  const char *end_label);
  static PLpgSQL_expr 	*read_cursor_args(PLpgSQL_var *cursor,
  										  int until, const char *expected);
+ static List *read_exception_additional_options(void);
  
  %}
  
***************
*** 142,147 ****
--- 143,149 ----
  %type <exception_block> exception_sect
  %type <exception>	proc_exception
  %type <condition>	proc_conditions
+ %type <condition>	proc_condition
  
  %type <casewhen>	case_when	
  %type <list>	case_when_list opt_case_default
***************
*** 1305,1310 ****
--- 1307,1313 ----
  						new->elog_level = $3;
  						new->message	= $4;
  						new->params		= NIL;
+ 						new->options		= NIL;
  
  						tok = yylex();
  
***************
*** 1313,1319 ****
  						 * indicates no parameters, or a comma that
  						 * begins the list of parameter expressions
  						 */
! 						if (tok != ',' && tok != ';')
  							yyerror("syntax error");
  
  						if (tok == ',')
--- 1316,1322 ----
  						 * indicates no parameters, or a comma that
  						 * begins the list of parameter expressions
  						 */
! 						if (tok != ',' && tok != ';' && tok != K_USING)
  							yyerror("syntax error");
  
  						if (tok == ',')
***************
*** 1321,1333 ****
  							do
  							{
  								PLpgSQL_expr *expr;
! 
! 								expr = read_sql_expression2(',', ';',
! 															", or ;",
! 															&tok);
  								new->params = lappend(new->params, expr);
  							} while (tok == ',');
  						}
  
  						$$ = (PLpgSQL_stmt *)new;
  					}
--- 1324,1342 ----
  							do
  							{
  								PLpgSQL_expr *expr;
! 								
! 								expr = read_sql_construct(',', ';', K_USING, ", or ; or USING",
! 												    "SELECT ", true, true, &tok);
  								new->params = lappend(new->params, expr);
  							} while (tok == ',');
  						}
+ 						
+ 						if (tok == K_USING)
+ 						{
+ 							new->options = read_exception_additional_options();
+ 							if (yylex() != ';')
+ 								yyerror("syntax error");
+ 						}
  
  						$$ = (PLpgSQL_stmt *)new;
  					}
***************
*** 1639,1657 ****
  					}
  				;
  
! proc_conditions	: proc_conditions K_OR opt_lblname
  						{
  							PLpgSQL_condition	*old;
  
  							for (old = $1; old->next != NULL; old = old->next)
  								/* skip */ ;
! 							old->next = plpgsql_parse_err_condition($3);
! 
  							$$ = $1;
  						}
! 				| opt_lblname
  						{
! 							$$ = plpgsql_parse_err_condition($1);
  						}
  				;
  
--- 1648,1699 ----
  					}
  				;
  
! proc_conditions	: proc_conditions K_OR proc_condition
  						{
  							PLpgSQL_condition	*old;
  
  							for (old = $1; old->next != NULL; old = old->next)
  								/* skip */ ;
! 							old->next = $3;
  							$$ = $1;
  						}
! 				| proc_condition
  						{
! 							$$ = $1;
! 						}
! 				;
! 
! proc_condition			: opt_lblname
! 						{	
!     							$$ = plpgsql_parse_err_condition($1);
! 						}
! 				    | T_SCALAR
! 						{
! 							int	tok;
! 							PLpgSQL_condition	*new;
! 							char		buff[30];
! 				    
! 							if (pg_strcasecmp(((PLpgSQL_var *) yylval.scalar)->refname, "sqlstate") != 0)
! 								yyerror("syntax error");
! 
! 							/* next token is sqlstate */
! 							if ((tok = yylex()) == 0)
! 								yyerror("unexpected end of function definition");
! 
! 							if (strlen(yytext) != 5)
! 								ereport(ERROR,
! 										(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 										 errmsg("sqlstate has five characters")));
! 	
! 							snprintf(buff, sizeof(buff), "sqlstate %s", yytext);
! 
! 							new = palloc(sizeof(PLpgSQL_condition));
! 							new->sqlerrstate = MAKE_SQLSTATE(yytext[0], yytext[1], yytext[2],
! 											 yytext[3], yytext[4]);
! 							new->condname = pstrdup(buff);
! 							new->next = NULL;
! 
! 							$$ = new;
  						}
  				;
  
***************
*** 2805,2810 ****
--- 2847,2910 ----
  }
  
  
+ /*
+  * Procedure read RAISE statements additional options
+  */
+ static List *
+ read_exception_additional_options(void)
+ {
+ 	List *result = NIL;
+ 	
+ 	if (yylex() != '(')
+ 		yyerror("syntax error, expected \"(\"");
+ 	
+ 	for(;;)
+ 	{
+ 		int	tok;
+ 		PLpgSQL_raise_option *opt;
+ 
+ 		if ((tok = yylex()) == 0)
+ 			yyerror("unexpected end of function definition");
+ 
+ 		opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
+ 		if (pg_strcasecmp(yytext, "sqlstate") == 0)
+ 		{
+ 			opt->opt_type = PLPGSQL_RAISEOPTION_SQLSTATE;
+ 		}
+ 		else if (pg_strcasecmp(yytext, "detail") == 0)
+ 		{
+ 			opt->opt_type = PLPGSQL_RAISEOPTION_DETAIL;
+     		}
+ 		else if (pg_strcasecmp(yytext, "condition") == 0)
+ 		{
+ 			opt->opt_type = PLPGSQL_RAISEOPTION_CONDITION;
+ 		}
+ 		else if (pg_strcasecmp(yytext, "hint") == 0)
+ 		{
+ 			opt->opt_type = PLPGSQL_RAISEOPTION_HINT;
+ 		}
+ 		else
+ 		{
+ 			plpgsql_error_lineno = plpgsql_scanner_lineno();
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("unknown RAISE statement option \"%s\"",
+ 							yytext)));
+ 		}
+ 			
+ 		if (yylex() != K_ASSIGN)
+ 			yyerror("syntax error, expected \"=\"");			
+ 
+ 		opt->expr = read_sql_expression2(',', ')', ", or )", &tok);
+ 		result = lappend(result, opt);
+ 		
+ 		if (tok == ')')
+ 			break;	
+ 	}
+ 		
+ 	return result;	
+ }	
+ 
  /* Needed to avoid conflict between different prefix settings: */
  #undef yylex
  
*** ./src/pl/plpgsql/src/pl_comp.c.orig	2008-05-09 14:31:22.000000000 +0200
--- ./src/pl/plpgsql/src/pl_comp.c	2008-05-09 14:52:06.000000000 +0200
***************
*** 1751,1756 ****
--- 1751,1789 ----
  }
  
  /*
+  *  plpgsql_identify_err_condition
+  * 		Check condition and translate it to SQLSTATE.
+  *
+  * Because condition names are not unique, verifies uniquness of condition
+  * name. If name isn't unique raise exception.
+  */
+ int
+ plpgsql_recognize_err_condition(char *condname)
+ {
+ 	int			i;
+ 	int		sqlerrstate = 0;
+ 	bool	found = false;
+ 
+ 	for (i = 0; exception_label_map[i].label != NULL; i++)
+ 		if (strcmp(condname, exception_label_map[i].label) == 0)
+ 		{
+ 			if (found)
+ 				ereport(ERROR,
+ 					    (errmsg("condition name doesn't identify SQLSTATE exactly")));
+ 
+ 			found = true;
+ 			sqlerrstate = exception_label_map[i].sqlerrstate;
+ 		}
+ 
+ 	if (!found)
+ 		ereport(ERROR,
+ 		    	(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 			 errmsg("unrecognized exception condition \"%s\"",
+ 					    condname)));
+ 	return sqlerrstate;
+ }			
+ 
+ /*
   * plpgsql_parse_err_condition
   *		Generate PLpgSQL_condition entry(s) for an exception condition name
   *
*** ./src/pl/plpgsql/src/pl_exec.c.orig	2008-05-06 23:09:09.000000000 +0200
--- ./src/pl/plpgsql/src/pl_exec.c	2008-05-10 07:14:49.000000000 +0200
***************
*** 2367,2372 ****
--- 2367,2375 ----
  	char	   *cp;
  	PLpgSQL_dstring ds;
  	ListCell   *current_param;
+ 	int		err_code;
+ 	char			*err_detail = NULL;
+ 	char			*err_hint = NULL;
  
  	plpgsql_dstring_init(&ds);
  	current_param = list_head(stmt->params);
***************
*** 2423,2441 ****
  				(errcode(ERRCODE_SYNTAX_ERROR),
  				 errmsg("too many parameters specified for RAISE")));
  
  	/*
  	 * Throw the error (may or may not come back)
  	 */
  	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... */
  
  	plpgsql_dstring_free(&ds);
  
  	return PLPGSQL_RC_OK;
  }
  
--- 2426,2528 ----
  				(errcode(ERRCODE_SYNTAX_ERROR),
  				 errmsg("too many parameters specified for RAISE")));
  
+ 	err_code = (stmt->elog_level >= ERROR) ? ERRCODE_RAISE_EXCEPTION : 0;
+ 
+ 	if (stmt->options != NIL)
+ 	{
+ 		/* evaluate RAISE options */
+ 		ListCell	*l;
+ 		bool	sqlstate_changed = false;
+ 		
+ 		foreach(l, stmt->options)
+ 		{
+ 			Oid			optiontypeid;
+ 			Datum		optionvalue;
+ 			bool		optionisnull;
+ 			char	   *extval;
+ 
+ 			PLpgSQL_raise_option *opt = (PLpgSQL_raise_option *) lfirst(l);
+ 
+ 			optionvalue = exec_eval_expr(estate, opt->expr,
+ 										&optionisnull,
+ 										&optiontypeid);
+ 
+ 			if (optionisnull)
+ 				ereport(ERROR,
+ 					    (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 					     errmsg("RAISE statement option cannot be NULL")));
+ 			
+ 			extval = convert_value_to_string(optionvalue, optiontypeid);
+ 			
+ 			switch (opt->opt_type)
+ 			{
+ 				case PLPGSQL_RAISEOPTION_DETAIL:
+ 					err_detail = pstrdup(extval);
+ 					break;
+ 				case PLPGSQL_RAISEOPTION_HINT:
+ 					err_hint = pstrdup(extval);
+ 					break;
+ 				case PLPGSQL_RAISEOPTION_SQLSTATE:
+ 					if (strlen(extval) != 5)
+ 						ereport(ERROR,
+ 							    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 							     errmsg("sqlstate has five characters")));
+ 					err_code =  MAKE_SQLSTATE(extval[0], extval[1], extval[2],
+ 					                      extval[3], extval[4]);
+ 
+ 					if (sqlstate_changed)
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 								 errmsg("cannot use attributies SQLSTATE and CONDITION together")));
+ 
+ 					sqlstate_changed = true;
+ 					break;
+ 				case PLPGSQL_RAISEOPTION_CONDITION:
+ 					err_code = plpgsql_recognize_err_condition(extval);
+ 
+ 					if (sqlstate_changed)
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 								 errmsg("cannot use attributies SQLSTATE and CONDITION together")));
+ 
+ 					sqlstate_changed = true;
+ 					break;
+ 
+ 				default:
+ 					elog(ERROR, "unrecognized raise option: %d", opt->opt_type);
+ 			}
+ 
+ 			exec_eval_cleanup(estate);
+ 		}
+ 
+ 		if (err_code != 0 && ERRCODE_IS_CATEGORY(err_code))
+ 			ereport(ERROR,
+ 				    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				     errmsg("cannot raise category SQLSTATE value"),
+ 				     errhint("Cannot specify SQLSTATE value like 'xx0000'."))); 
+ 	}
+ 
  	/*
  	 * Throw the error (may or may not come back)
  	 */
  	estate->err_text = raise_skip_msg;	/* suppress traceback of raise */
  
  	ereport(stmt->elog_level,
! 		     ((errcode(err_code),
! 		       errmsg_internal("%s", plpgsql_dstring_get(&ds)),
! 		       (err_detail != NULL) ? errdetail(err_detail) : 0,
! 		       (err_hint != NULL) ? errhint(err_hint) : 0 )));
  
  	estate->err_text = NULL;	/* un-suppress... */
  
  	plpgsql_dstring_free(&ds);
  
+ 	if (err_detail != NULL)
+ 		pfree(err_detail);
+ 	
+ 	if (err_hint != NULL)
+ 		pfree(err_hint);
+ 
  	return PLPGSQL_RC_OK;
  }
  
*** ./src/pl/plpgsql/src/pl_funcs.c.orig	2008-05-07 14:58:21.000000000 +0200
--- ./src/pl/plpgsql/src/pl_funcs.c	2008-05-10 01:05:42.000000000 +0200
***************
*** 1055,1061 ****
  		dump_expr((PLpgSQL_expr *) lfirst(lc));
  		printf("\n");
  	}
! 	dump_indent -= 2;
  }
  
  static void
--- 1055,1090 ----
  		dump_expr((PLpgSQL_expr *) lfirst(lc));
  		printf("\n");
  	}
! 	if (stmt->options)
! 	{
! 		dump_ind();
! 		printf("    USING\n");
! 		dump_indent += 2;
! 		foreach(lc, stmt->options)
! 		{
! 			PLpgSQL_raise_option *opt = (PLpgSQL_raise_option *) lfirst(lc);
! 		
! 			dump_ind();
! 			switch (opt->opt_type)
! 			{
! 				case PLPGSQL_RAISEOPTION_DETAIL:
! 					printf("    DETAIL = ");
! 					break;
! 				case PLPGSQL_RAISEOPTION_HINT:
! 					printf("    HINT = ");
! 					break;
! 				case PLPGSQL_RAISEOPTION_SQLSTATE:
! 					printf("    SQLSTATE = ");
! 					break;
! 				case PLPGSQL_RAISEOPTION_CONDITION:
! 					printf("    CONDITION = ");
! 					break;	
! 			}
! 			dump_expr(opt->expr);
! 			printf("\n");
! 		}		
! 		dump_indent -= 2;
! 	}
  }
  
  static void
*** ./src/pl/plpgsql/src/plpgsql.h.orig	2008-05-06 23:55:53.000000000 +0200
--- ./src/pl/plpgsql/src/plpgsql.h	2008-05-09 14:49:13.000000000 +0200
***************
*** 121,126 ****
--- 121,138 ----
  };
  
  
+ /* --------
+  * RAISE statement options
+  * --------
+  */
+ enum
+ {
+ 	PLPGSQL_RAISEOPTION_SQLSTATE,
+ 	PLPGSQL_RAISEOPTION_DETAIL,
+ 	PLPGSQL_RAISEOPTION_HINT,
+ 	PLPGSQL_RAISEOPTION_CONDITION
+ }; 
+ 
  /**********************************************************************
   * Node and structure definitions
   **********************************************************************/
***************
*** 560,567 ****
--- 572,585 ----
  	int			elog_level;
  	char	   *message;
  	List	   *params;			/* list of expressions */
+ 	List	   *options;			/* elog options */
  } PLpgSQL_stmt_raise;
  
+ typedef struct
+ {								/* RAISE statement's option */
+ 	int 		opt_type;
+ 	PLpgSQL_expr *expr;
+ } PLpgSQL_raise_option;
  
  typedef struct
  {								/* Generic SQL statement to execute */
***************
*** 791,796 ****
--- 809,815 ----
  					   bool add2namespace);
  extern PLpgSQL_rec *plpgsql_build_record(const char *refname, int lineno,
  										 bool add2namespace);
+ extern int plpgsql_recognize_err_condition(char *condname);
  extern PLpgSQL_condition *plpgsql_parse_err_condition(char *condname);
  extern void plpgsql_adddatum(PLpgSQL_datum *new);
  extern int	plpgsql_add_initdatums(int **varnos);
*** ./src/test/regress/expected/plpgsql.out.orig	2008-05-06 23:38:02.000000000 +0200
--- ./src/test/regress/expected/plpgsql.out	2008-05-09 23:45:39.000000000 +0200
***************
*** 3385,3387 ****
--- 3385,3508 ----
  
  drop function case_test(int);
  drop function catch();
+ -- we can't set VERBOSITY verbose
+ -- because LOCATION field contains row number from pl_exec.c
+ create or replace function run_tests()
+ returns void as $$
+ begin
+   begin 
+     perform raise_options();
+   exception when others then
+     raise notice 'SQLSTATE: % SQLERRM %', sqlstate, sqlerrm;
+   end;
+   begin 
+     perform raise_options2();
+   exception when others then
+     raise notice 'SQLSTATE: % SQLERRM %', sqlstate, sqlerrm;
+   end;
+ end;
+ $$ language plpgsql;
+ create or replace function raise_options() returns void as $$
+ begin
+   raise exception '% % %', 1, 2, 3 
+      using (sqlstate = '55001', detail = 'some detail info', hint = 'some hint');
+ end;
+ $$ language plpgsql;
+ create or replace function raise_options2() returns void as $$
+ begin
+   raise exception 'custom unique violation' 
+      using (condition = 'unique_violation', detail = 'test of unique violation', hint = 'skip it');
+ end;
+ $$ language plpgsql;
+ -- test of exception's trap
+ create or replace function raise_options3() 
+ returns void as $$
+ begin 
+   raise exception 'aaaa' 
+      using(detail='bbb', hint=current_date, sqlstate='22201'); 
+ exception 
+   when sqlstate 22201 then 
+     raise notice 'custom 22201'; 
+   when unique_violation then 
+     raise notice 'custom unique_violation'; 
+ end; 
+ $$ language plpgsql;
+ create or replace function raise_options4() 
+ returns void as $$
+ begin 
+   raise exception 'aaaa' 
+      using(detail='bbb', hint=current_date, condition='unique_violation'); 
+ exception 
+   when sqlstate 22201 then 
+     raise notice 'custom 22201'; 
+   when unique_violation then 
+     raise notice 'custom unique_violation'; 
+ end; 
+ $$ language plpgsql;
+ -- trap category
+ create or replace function raise_options5()
+ returns void as $$
+ begin
+   raise exception 'custom exception'
+      using(sqlstate = '33001');
+ exception
+   when sqlstate 33000 then
+     raise notice 'category: 33000 real sqlstate: %', sqlstate;
+ end;
+ $$ language plpgsql;
+ -- cannot raise category
+ create or replace function raise_options6()
+ returns void as $$
+ begin
+   -- runtime error
+   raise exception 'custom exception'
+      using(sqlstate = '33000');
+ exception
+   when sqlstate 33000 then
+     raise notice 'category: 33000 real sqlstate: %', sqlstate;
+ end;
+ $$ language plpgsql;
+ select raise_options();
+ ERROR:  1 2 3
+ DETAIL:  some detail info
+ HINT:  some hint
+ select run_tests();
+ NOTICE:  SQLSTATE: 55001 SQLERRM 1 2 3
+ NOTICE:  SQLSTATE: 23505 SQLERRM custom unique violation
+  run_tests 
+ -----------
+  
+ (1 row)
+ 
+ select raise_options3();
+ NOTICE:  custom 22201
+  raise_options3 
+ ----------------
+  
+ (1 row)
+ 
+ select raise_options4();
+ NOTICE:  custom unique_violation
+  raise_options4 
+ ----------------
+  
+ (1 row)
+ 
+ select raise_options5();
+ NOTICE:  category: 33000 real sqlstate: 33001
+  raise_options5 
+ ----------------
+  
+ (1 row)
+ 
+ select raise_options6();
+ ERROR:  cannot raise category SQLSTATE value
+ HINT:  Cannot specify SQLSTATE value like 'xx0000'.
+ CONTEXT:  PL/pgSQL function "raise_options6" line 3 at RAISE
+ drop function raise_options();
+ drop function raise_options2();
+ drop function raise_options3();
+ drop function raise_options4();
+ drop function raise_options5();
+ drop function raise_options6();
+ drop function run_tests();
*** ./src/test/regress/sql/plpgsql.sql.orig	2008-05-06 23:36:47.000000000 +0200
--- ./src/test/regress/sql/plpgsql.sql	2008-05-09 23:26:26.000000000 +0200
***************
*** 2735,2737 ****
--- 2735,2836 ----
  drop function case_test(int);
  drop function catch();
  
+ -- we can't set VERBOSITY verbose
+ -- because LOCATION field contains row number from pl_exec.c
+ create or replace function run_tests()
+ returns void as $$
+ begin
+   begin 
+     perform raise_options();
+   exception when others then
+     raise notice 'SQLSTATE: % SQLERRM %', sqlstate, sqlerrm;
+   end;
+   begin 
+     perform raise_options2();
+   exception when others then
+     raise notice 'SQLSTATE: % SQLERRM %', sqlstate, sqlerrm;
+   end;
+ end;
+ $$ language plpgsql;
+ 
+ create or replace function raise_options() returns void as $$
+ begin
+   raise exception '% % %', 1, 2, 3 
+      using (sqlstate = '55001', detail = 'some detail info', hint = 'some hint');
+ end;
+ $$ language plpgsql;
+ 
+ create or replace function raise_options2() returns void as $$
+ begin
+   raise exception 'custom unique violation' 
+      using (condition = 'unique_violation', detail = 'test of unique violation', hint = 'skip it');
+ end;
+ $$ language plpgsql;
+ 
+ -- test of exception's trap
+ create or replace function raise_options3() 
+ returns void as $$
+ begin 
+   raise exception 'aaaa' 
+      using(detail='bbb', hint=current_date, sqlstate='22201'); 
+ exception 
+   when sqlstate 22201 then 
+     raise notice 'custom 22201'; 
+   when unique_violation then 
+     raise notice 'custom unique_violation'; 
+ end; 
+ $$ language plpgsql;
+ 
+ create or replace function raise_options4() 
+ returns void as $$
+ begin 
+   raise exception 'aaaa' 
+      using(detail='bbb', hint=current_date, condition='unique_violation'); 
+ exception 
+   when sqlstate 22201 then 
+     raise notice 'custom 22201'; 
+   when unique_violation then 
+     raise notice 'custom unique_violation'; 
+ end; 
+ $$ language plpgsql;
+ 
+ -- trap category
+ create or replace function raise_options5()
+ returns void as $$
+ begin
+   raise exception 'custom exception'
+      using(sqlstate = '33001');
+ exception
+   when sqlstate 33000 then
+     raise notice 'category: 33000 real sqlstate: %', sqlstate;
+ end;
+ $$ language plpgsql;
+ 
+ -- cannot raise category
+ create or replace function raise_options6()
+ returns void as $$
+ begin
+   -- runtime error
+   raise exception 'custom exception'
+      using(sqlstate = '33000');
+ exception
+   when sqlstate 33000 then
+     raise notice 'category: 33000 real sqlstate: %', sqlstate;
+ end;
+ $$ language plpgsql;
+ 
+ 
+ select raise_options();
+ select run_tests();
+ select raise_options3();
+ select raise_options4();
+ select raise_options5();
+ select raise_options6();
+ 
+ drop function raise_options();
+ drop function raise_options2();
+ drop function raise_options3();
+ drop function raise_options4();
+ drop function raise_options5();
+ drop function raise_options6();
+ drop function run_tests();
-- 
Sent via pgsql-patches mailing list (pgsql-patches@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-patches

Reply via email to