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