Hello I am sending little bit smarter version - without redundant parsing. When test expression is defined, then expressions in WHEN part are modified like
$x in ( origin_expression ) $x is referenced to invisible *case* variable that carries result of test expression. Regards Pavel Stehule 2008/5/3 Pavel Stehule <[EMAIL PROTECTED]>: > Hello > > 2008/5/3 Tom Lane <[EMAIL PROTECTED]>: >> "Pavel Stehule" <[EMAIL PROTECTED]> writes: >>> 2008/5/2 Heikki Linnakangas <[EMAIL PROTECTED]>: >>>> How about taking a completely different strategy, and implement the >>>> CASE-WHEN construct fully natively in plpgsql, instead of trying to convert >>>> it to a single SQL CASE-WHEN expression? It's not a very good match anyway; >> >>> It was first variant. It's simpler for parsing and slower for >>> execution :(. It means more than once expression evaluation and for >>> simple case value casting and comparation. >> >> I agree with Heikki: this patch is seriously ugly, and "slower for >> execution" isn't a good enough reason for saddling us with having >> to maintain such a kluge in the parser. >> >> I don't really see why you should need to have multiple expression >> evaluations, anyhow. Can't you evaluate the test expression once >> and inject its value into the comparisons using CaseTestExpr, >> the same way the core CASE-expression code works? >> >> > > I have to look on this way. > > Regards > Pavel Stehule > > regards, tom lane >> >
*** ./doc/src/sgml/plpgsql.sgml.orig 2008-05-03 02:11:36.000000000 +0200 --- ./doc/src/sgml/plpgsql.sgml 2008-05-06 11:05:05.000000000 +0200 *************** *** 1601,1606 **** --- 1601,1622 ---- <para><literal>IF ... THEN ... ELSEIF ... THEN ... ELSE</></> </listitem> </itemizedlist> + + and four forms of <literal>CASE</>: + <itemizedlist> + <listitem> + <para><literal>CASE ... WHEN ... THEN ... END CASE</></> + </listitem> + <listitem> + <para><literal>CASE ... WHEN ... THEN ... ELSE ... END CASE</></> + </listitem> + <listitem> + <para><literal>CASE WHEN ... THEN ... END CASE</></> + </listitem> + <listitem> + <para><literal>CASE WHEN ... THEN ... ELSE ... END CASE</></> + </listitem> + </itemizedlist> </para> <sect3> *************** *** 1751,1756 **** --- 1767,1838 ---- <literal>ELSEIF</> is an alias for <literal>ELSIF</>. </para> </sect3> + + <sect3> + <title>Simple <literal>CASE</> statement</title> + <synopsis> + CASE <replaceable>expression</replaceable> + WHEN <replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> <optional> ... </optional></optional> THEN + <replaceable>statements</replaceable> + <optional> WHEN <replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> <optional> ... </optional></optional> THEN + <replaceable>statements</replaceable> + <optional> WHEN <replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> <optional> ... </optional></optional> THEN + <replaceable>statements</replaceable> + ... </optional></optional> + <optional> ELSE + <replaceable>statements</replaceable> </optional> + END CASE; + </synopsis> + <para> + Provide conditional execution based on equality of operands. If no case is matched, + then is ELSE clause executed. If statement doesn't contains ELSE clause, + then <literal>CASE_NOT_FOUND</literal> exception is raised. + </para> + <para>Here is example: + <programlisting> + CASE a + WHEN 1, 2 THEN + msg := 'one or two'; + ELSE + msg := 'other value than one or two'; + END CASE; + </programlisting> + </para> + </sect3> + + <sect3> + <title>Searched <literal>CASE</> statement</title> + <synopsis> + CASE + WHEN <replaceable>boolean-expression</replaceable> THEN + <replaceable>statements</replaceable> + <optional> WHEN <replaceable>boolean-expression</replaceable> THEN + <replaceable>statements</replaceable> + <optional> WHEN <replaceable>boolean-expression</replaceable> THEN + <replaceable>statements</replaceable> + ... </optional></optional> + <optional> ELSE + <replaceable>statements</replaceable> </optional> + END CASE; + </synopsis> + <para> + Provide conditional execution based on truth of + <replaceable>boolean-expression</replaceable>. If no case is matched, + then is ELSE clause executed. If statement doesn't contains ELSE clause, + then <literal>CASE_NOT_FOUND</literal> exception is raised. + </para> + <para> Here is example: + <programlisting> + CASE + WHEN a BETWEEN 0 AND 10 THEN + msg := 'value is between zero and ten'; + WHEN a BETWEEN 11 AND 20 THEN + msg := 'value is between eleven and twenty'; + END CASE; + </programlisting> + </para> + + </sect3> </sect2> <sect2 id="plpgsql-control-structures-loops"> *** ./src/backend/catalog/sql_feature_packages.txt.orig 2008-05-06 11:01:18.000000000 +0200 --- ./src/backend/catalog/sql_feature_packages.txt 2008-05-06 11:05:05.000000000 +0200 *************** *** 41,46 **** --- 41,48 ---- F671 Enhanced integrity management F701 Enhanced integrity management F812 Core + P004 PSM + P008 PSM S011 Core S023 Basic object support S024 Enhanced object support *** ./src/backend/catalog/sql_features.txt.orig 2008-05-06 11:01:27.000000000 +0200 --- ./src/backend/catalog/sql_features.txt 2008-05-06 11:05:05.000000000 +0200 *************** *** 297,302 **** --- 297,304 ---- F831 Full cursor update NO F831 Full cursor update 01 Updatable scrollable cursors NO F831 Full cursor update 02 Updatable ordered cursors NO + P004 Extended CASE statement YES + P008 Comma-separated predicates in simple CASE statement YES S011 Distinct data types NO S011 Distinct data types 01 USER_DEFINED_TYPES view NO S023 Basic structured types NO *** ./src/include/utils/errcodes.h.orig 2008-05-06 11:01:47.000000000 +0200 --- ./src/include/utils/errcodes.h 2008-05-06 11:05:05.000000000 +0200 *************** *** 107,112 **** --- 107,113 ---- /* Class 22 - Data Exception */ #define ERRCODE_DATA_EXCEPTION MAKE_SQLSTATE('2','2', '0','0','0') + #define ERRCODE_CASE_NOT_FOUND ERRCODE_DATA_EXCEPTION #define ERRCODE_ARRAY_ELEMENT_ERROR MAKE_SQLSTATE('2','2', '0','2','E') /* SQL99's actual definition of "array element error" is subscript error */ #define ERRCODE_ARRAY_SUBSCRIPT_ERROR ERRCODE_ARRAY_ELEMENT_ERROR *** ./src/pl/plpgsql/src/gram.y.orig 2008-05-03 02:11:36.000000000 +0200 --- ./src/pl/plpgsql/src/gram.y 2008-05-06 23:54:42.000000000 +0200 *************** *** 18,24 **** #include "parser/parser.h" - static PLpgSQL_expr *read_sql_construct(int until, int until2, int until3, --- 18,23 ---- *************** *** 37,42 **** --- 36,43 ---- static PLpgSQL_stmt *make_return_stmt(int lineno); static PLpgSQL_stmt *make_return_next_stmt(int lineno); static PLpgSQL_stmt *make_return_query_stmt(int lineno); + static PLpgSQL_stmt *make_case(int lineno, PLpgSQL_expr *case_expr, + List *case_when_list, List *else_stmts); static void check_assignable(PLpgSQL_datum *datum); static void read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict); *************** *** 101,106 **** --- 102,108 ---- PLpgSQL_nsitem *nsitem; PLpgSQL_diag_item *diagitem; PLpgSQL_stmt_fetch *fetch; + PLpgSQL_case_when *casewhen; } %type <declhdr> decl_sect *************** *** 115,121 **** %type <str> decl_stmts decl_stmt %type <expr> expr_until_semi expr_until_rightbracket ! %type <expr> expr_until_then expr_until_loop %type <expr> opt_exitcond %type <ival> assign_var --- 117,123 ---- %type <str> decl_stmts decl_stmt %type <expr> expr_until_semi expr_until_rightbracket ! %type <expr> expr_until_then expr_until_loop opt_expr_until_when %type <expr> opt_exitcond %type <ival> assign_var *************** *** 134,145 **** --- 136,150 ---- %type <stmt> stmt_return stmt_raise stmt_execsql stmt_execsql_insert %type <stmt> stmt_dynexecute stmt_for stmt_perform stmt_getdiag %type <stmt> stmt_open stmt_fetch stmt_move stmt_close stmt_null + %type <stmt> stmt_case %type <list> proc_exceptions %type <exception_block> exception_sect %type <exception> proc_exception %type <condition> proc_conditions + %type <casewhen> case_when + %type <list> case_when_list opt_case_default %type <ival> raise_level %type <str> raise_msg *************** *** 160,165 **** --- 165,171 ---- %token K_ASSIGN %token K_BEGIN %token K_BY + %token K_CASE %token K_CLOSE %token K_CONSTANT %token K_CONTINUE *************** *** 618,623 **** --- 624,631 ---- { $$ = $1; } | stmt_if { $$ = $1; } + | stmt_case + { $$ = $1; } | stmt_loop { $$ = $1; } | stmt_while *************** *** 816,821 **** --- 824,868 ---- } ; + stmt_case : K_CASE lno opt_expr_until_when case_when_list opt_case_default K_END K_CASE ';' + { + $$ = make_case($2, $3, $4, $5); + plpgsql_ns_pop(); + } + ; + + opt_case_default : + { + $$ = NIL; + } + | K_ELSE proc_stmts + { + $$ = $2; + } + ; + + case_when_list : case_when_list case_when + { + $$ = lappend($1, $2); + } + | case_when + { + $$ = list_make1($1); + } + ; + + case_when : K_WHEN lno expr_until_then proc_stmts + { + PLpgSQL_case_when *new = palloc(sizeof(PLpgSQL_case_when)); + + new->lineno = $2; + new->expr = $3; + new->stmts = $4; + + $$ = new; + } + ; + stmt_loop : opt_block_label K_LOOP lno loop_body { PLpgSQL_stmt_loop *new; *************** *** 1624,1629 **** --- 1671,1691 ---- { $$ = plpgsql_read_expression(K_LOOP, "LOOP"); } ; + opt_expr_until_when : + { + PLpgSQL_expr *expr = NULL; + int tok = yylex(); + + if (tok != K_WHEN) + { + plpgsql_push_back_token(tok); + expr = plpgsql_read_expression(K_WHEN, "WHEN"); + } + plpgsql_push_back_token(K_WHEN); + $$ = expr; + } + ; + opt_block_label : { plpgsql_ns_push(NULL); *************** *** 1636,1641 **** --- 1698,1704 ---- } ; + /* * need all the options because scanner will have tried to resolve as variable */ *************** *** 2659,2664 **** --- 2722,2810 ---- } + /* + * CASE statement + * When test expr is defined, then we inject WHEN expression as $(n+1) in (expression). + */ + static PLpgSQL_stmt * + make_case(int lineno, PLpgSQL_expr *t_expr, + List *case_when_list, List *else_stmts) + { + PLpgSQL_stmt_case *new; + + new = palloc(sizeof(PLpgSQL_stmt_case)); + new->cmd_type = PLPGSQL_STMT_CASE; + new->lineno = lineno; + new->t_expr = t_expr; + new->case_when_list = case_when_list; + new->else_stmts = else_stmts; + + /* + * Because I wouldn't generate plan of test_expr for + * getting its result's type, I expect integer as result. + * I can be corrected it later. + */ + if (t_expr) + { + ListCell *l; + int t_varno; + + new->t_var = (PLpgSQL_var *) plpgsql_build_variable("*case*", lineno, + plpgsql_build_datatype(INT4OID, -1), + false); + t_varno =new->t_var->varno; + + foreach(l, case_when_list) + { + PLpgSQL_dstring ds; + PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l); + PLpgSQL_expr *expr = cwt->expr; + PLpgSQL_expr *new_expr; + int nparams = expr->nparams; + char buff[32]; + + if (nparams >= MAX_EXPR_PARAMS) + { + plpgsql_error_lineno = plpgsql_scanner_lineno(); + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("too many variables specified in SQL statement"))); + } + + new_expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * (nparams + 1) - sizeof(int)); + new_expr->dtype = expr->dtype; + new_expr->nparams = nparams + 1; + new_expr->plan = NULL; + + memcpy(new_expr->params, expr->params, nparams * sizeof(int)); + new_expr->params[nparams] = t_varno; + + plpgsql_dstring_init(&ds); + + plpgsql_dstring_append(&ds, "SELECT $"); + snprintf(buff, sizeof(buff), "%d", nparams + 1); + plpgsql_dstring_append(&ds, buff); + plpgsql_dstring_append(&ds, " IN ("); + + /* copy expression query without SELECT keyword */ + Assert(strncmp(expr->query, "SELECT ", 7) == 0); + plpgsql_dstring_append(&ds, expr->query + 7); + plpgsql_dstring_append_char(&ds, ')'); + + new_expr->query = pstrdup(plpgsql_dstring_get(&ds)); + + plpgsql_dstring_free(&ds); + pfree(expr->query); + pfree(expr); + + cwt->expr = new_expr; + } + } + + return (PLpgSQL_stmt *) new; + } + + /* Needed to avoid conflict between different prefix settings: */ #undef yylex *** ./src/pl/plpgsql/src/plerrcodes.h.orig 2008-05-06 11:03:02.000000000 +0200 --- ./src/pl/plpgsql/src/plerrcodes.h 2008-05-06 11:05:05.000000000 +0200 *************** *** 750,752 **** --- 750,756 ---- { "index_corrupted", ERRCODE_INDEX_CORRUPTED }, + + { + "case_not_found", ERRCODE_CASE_NOT_FOUND + }, *** ./src/pl/plpgsql/src/pl_exec.c.orig 2008-05-03 02:11:36.000000000 +0200 --- ./src/pl/plpgsql/src/pl_exec.c 2008-05-06 23:09:09.000000000 +0200 *************** *** 95,100 **** --- 95,102 ---- PLpgSQL_stmt_getdiag *stmt); static int exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt); + static int exec_stmt_case(PLpgSQL_execstate *estate, + PLpgSQL_stmt_case *stmt); static int exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt); static int exec_stmt_while(PLpgSQL_execstate *estate, *************** *** 1238,1243 **** --- 1240,1249 ---- rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt); break; + case PLPGSQL_STMT_CASE: + rc = exec_stmt_case(estate, (PLpgSQL_stmt_case *) stmt); + break; + case PLPGSQL_STMT_LOOP: rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt); break; *************** *** 1429,1434 **** --- 1435,1530 ---- } + /*----------- + * case_stmt + * + * + *----------- + */ + static int + exec_stmt_case(PLpgSQL_execstate *estate, PLpgSQL_stmt_case *stmt) + { + bool isnull = true; + PLpgSQL_var *t_var = NULL; + + if (stmt->t_expr != NULL) + { + /* simple case */ + Datum t_val; + Oid t_oid; + + t_val = exec_eval_expr(estate, stmt->t_expr, + &isnull, + &t_oid); + t_var = (PLpgSQL_var *) estate->datums[stmt->t_var->varno]; + + /* when expected datatype is other than real, change it */ + if (stmt->t_var->datatype->typoid != t_oid) + { + MemoryContext oldcxt; + PLpgSQL_type *oldtype = stmt->t_var->datatype; + + pfree(oldtype->typname); + pfree(oldtype); + + oldcxt = MemoryContextSwitchTo(estate->err_func->fn_cxt); + stmt->t_var->datatype = plpgsql_build_datatype(t_oid, -1); + + MemoryContextSwitchTo(oldcxt); + } + + /* + * Store value only when is not null, null value means + * direct execution of else path. + */ + if (!isnull) + exec_assign_value(estate, + (PLpgSQL_datum *) t_var, + t_val, + t_oid, + &isnull); + + exec_eval_cleanup(estate); + } + + /* + * eval paths when test expr result is not null, or test_expr + * isn't defined /search case/ + */ + if (!isnull || stmt->t_expr == NULL) + { + ListCell *l; + bool value; + + foreach(l, stmt->case_when_list) + { + PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l); + + value = exec_eval_boolean(estate, cwt->expr, &isnull); + exec_eval_cleanup(estate); + if (!isnull && value) + { + if (t_var != NULL) + free_var(t_var); + + return exec_stmts(estate, cwt->stmts); + } + } + } + + if (stmt->else_stmts == NULL) + ereport(ERROR, + (errcode(ERRCODE_CASE_NOT_FOUND), + errmsg("case not found"), + errhint("CASE statement missing ELSE part."))); + + if (t_var != NULL) + free_var(t_var); + + return exec_stmts(estate, stmt->else_stmts); + } + + /* ---------- * exec_stmt_loop Loop over statements until * an exit occurs. *** ./src/pl/plpgsql/src/pl_funcs.c.orig 2008-05-03 02:11:36.000000000 +0200 --- ./src/pl/plpgsql/src/pl_funcs.c 2008-05-06 23:29:22.000000000 +0200 *************** *** 526,531 **** --- 526,532 ---- static void dump_block(PLpgSQL_stmt_block *block); static void dump_assign(PLpgSQL_stmt_assign *stmt); static void dump_if(PLpgSQL_stmt_if *stmt); + static void dump_case(PLpgSQL_stmt_case *stmt); static void dump_loop(PLpgSQL_stmt_loop *stmt); static void dump_while(PLpgSQL_stmt_while *stmt); static void dump_fori(PLpgSQL_stmt_fori *stmt); *************** *** 572,577 **** --- 573,581 ---- case PLPGSQL_STMT_IF: dump_if((PLpgSQL_stmt_if *) stmt); break; + case PLPGSQL_STMT_CASE: + dump_case((PLpgSQL_stmt_case *) stmt); + break; case PLPGSQL_STMT_LOOP: dump_loop((PLpgSQL_stmt_loop *) stmt); break; *************** *** 714,719 **** --- 718,762 ---- printf(" ENDIF\n"); } + static void + dump_case(PLpgSQL_stmt_case *stmt) + { + ListCell *l; + + dump_ind(); + printf("CASE "); + dump_indent += 6; + if (stmt->t_expr) + { + dump_expr(stmt->t_expr); + printf("\n"); + } + foreach(l, stmt->case_when_list) + { + PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l); + dump_ind(); + printf("WHEN "); + dump_expr(cwt->expr); + printf("\n"); + dump_ind(); + printf("THEN\n"); + dump_indent += 2; + dump_stmts(cwt->stmts); + dump_indent -= 2; + } + if (stmt->else_stmts) + { + dump_ind(); + printf("ELSE\n"); + dump_indent += 2; + dump_stmts(stmt->else_stmts); + dump_indent -= 2; + } + dump_indent -= 6; + dump_ind(); + printf(" ENDCASE\n"); + } + static void dump_loop(PLpgSQL_stmt_loop *stmt) { *** ./src/pl/plpgsql/src/plpgsql.h.orig 2008-05-03 02:11:36.000000000 +0200 --- ./src/pl/plpgsql/src/plpgsql.h 2008-05-06 23:55:53.000000000 +0200 *************** *** 76,81 **** --- 76,82 ---- PLPGSQL_STMT_BLOCK, PLPGSQL_STMT_ASSIGN, PLPGSQL_STMT_IF, + PLPGSQL_STMT_CASE, PLPGSQL_STMT_LOOP, PLPGSQL_STMT_WHILE, PLPGSQL_STMT_FORI, *************** *** 327,332 **** --- 328,340 ---- typedef struct + { + int lineno; + PLpgSQL_expr *expr; + List *stmts; + } PLpgSQL_case_when; + + typedef struct { /* Block of statements */ int cmd_type; int lineno; *************** *** 377,382 **** --- 385,401 ---- } PLpgSQL_stmt_if; + typedef struct /* CASE statement */ + { + int cmd_type; + int lineno; + PLpgSQL_expr *t_expr; + PLpgSQL_var *t_var; + List *case_when_list; + List *else_stmts; + } PLpgSQL_stmt_case; + + typedef struct { /* Unconditional LOOP statement */ int cmd_type; *** ./src/pl/plpgsql/src/scan.l.orig 2008-05-06 11:03:30.000000000 +0200 --- ./src/pl/plpgsql/src/scan.l 2008-05-06 11:05:05.000000000 +0200 *************** *** 115,120 **** --- 115,121 ---- alias { return K_ALIAS; } begin { return K_BEGIN; } by { return K_BY; } + case { return K_CASE; } close { return K_CLOSE; } constant { return K_CONSTANT; } continue { return K_CONTINUE; } *** ./src/test/regress/expected/plpgsql.out.orig 2008-05-03 02:11:36.000000000 +0200 --- ./src/test/regress/expected/plpgsql.out 2008-05-06 23:38:02.000000000 +0200 *************** *** 3285,3287 **** --- 3285,3387 ---- (4 rows) drop function return_dquery(); + --test CASE statement + create or replace FUNCTION case_test(int) + returns text as $$ + declare a int = 10; + b int = 1; + begin + case $1 + when 1 then + return 'one'; + when 2 then + return 'two'; + when 3,4,3+5 then + raise notice 'warning: ambiguous'; + return 'three, four or five'; + when a then + return 'ten'; + when a+b, a+1+b then + raise notice 'warning: ambiguous'; + return 'eleven, twelve'; + end case; + end; + $$ language plpgsql immutable; + select case_test(1); + case_test + ----------- + one + (1 row) + + select case_test(2); + case_test + ----------- + two + (1 row) + + select case_test(3); + NOTICE: warning: ambiguous + case_test + --------------------- + three, four or five + (1 row) + + select case_test(4); + NOTICE: warning: ambiguous + case_test + --------------------- + three, four or five + (1 row) + + select case_test(5); + ERROR: case not found + HINT: CASE statement missing ELSE part. + CONTEXT: PL/pgSQL function "case_test" line 4 at unknown + --raise exception: case not found + select case_test(6); + ERROR: case not found + HINT: CASE statement missing ELSE part. + CONTEXT: PL/pgSQL function "case_test" line 4 at unknown + select case_test(10); + case_test + ----------- + ten + (1 row) + + select case_test(11); + NOTICE: warning: ambiguous + case_test + ---------------- + eleven, twelve + (1 row) + + select case_test(12); + NOTICE: warning: ambiguous + case_test + ---------------- + eleven, twelve + (1 row) + + -- raise exception: case not found + select case_test(13); + ERROR: case not found + HINT: CASE statement missing ELSE part. + CONTEXT: PL/pgSQL function "case_test" line 4 at unknown + create or replace function catch() + returns void as $$ + begin + raise notice '%', case_test(6); + exception + when case_not_found then + raise notice 'catched case_not_found % %', SQLSTATE, SQLERRM; + end + $$ language plpgsql immutable; + select catch(); + NOTICE: catched case_not_found 22000 case not found + catch + ------- + + (1 row) + + drop function case_test(int); + drop function catch(); *** ./src/test/regress/sql/plpgsql.sql.orig 2008-05-03 02:11:36.000000000 +0200 --- ./src/test/regress/sql/plpgsql.sql 2008-05-06 23:36:47.000000000 +0200 *************** *** 2683,2685 **** --- 2683,2737 ---- select * from return_dquery(); drop function return_dquery(); + + --test CASE statement + create or replace FUNCTION case_test(int) + returns text as $$ + declare a int = 10; + b int = 1; + begin + case $1 + when 1 then + return 'one'; + when 2 then + return 'two'; + when 3,4,3+5 then + raise notice 'warning: ambiguous'; + return 'three, four or five'; + when a then + return 'ten'; + when a+b, a+1+b then + raise notice 'warning: ambiguous'; + return 'eleven, twelve'; + end case; + end; + $$ language plpgsql immutable; + + select case_test(1); + select case_test(2); + select case_test(3); + select case_test(4); + select case_test(5); + --raise exception: case not found + select case_test(6); + select case_test(10); + select case_test(11); + select case_test(12); + -- raise exception: case not found + select case_test(13); + + create or replace function catch() + returns void as $$ + begin + raise notice '%', case_test(6); + exception + when case_not_found then + raise notice 'catched case_not_found % %', SQLSTATE, SQLERRM; + end + $$ language plpgsql immutable; + + select catch(); + + drop function case_test(int); + drop function catch(); +
-- Sent via pgsql-patches mailing list (pgsql-patches@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-patches