diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml new file mode 100644 index f195495..eae72f6 *** a/doc/src/sgml/plpgsql.sgml --- b/doc/src/sgml/plpgsql.sgml *************** END LOOP label ! RAISE level 'format' , expression , ... USING option = expression , ... ; ! RAISE level condition_name USING option = expression , ... ; ! RAISE level SQLSTATE 'sqlstate' USING option = expression , ... ; ! RAISE level USING option = expression , ... ; ! RAISE ; The level option specifies --- 3369,3379 ---- raise errors. ! RAISE level 'format' , expression , ... USING option = expression , ... WHEN boolean-expression ; ! RAISE level condition_name USING option = expression , ... WHEN boolean-expression ; ! RAISE level SQLSTATE 'sqlstate' USING option = expression , ... WHEN boolean-expression ; ! RAISE level USING option = expression , ... WHEN boolean-expression ; ! RAISE WHEN boolean-expression ; The level option specifies *************** RAISE unique_violation USING MESSAGE = ' *** 3548,3553 **** --- 3548,3561 ---- + + If WHEN is specified, the message or error is raised + only if boolean-expression is true. + + RAISE 'Unexpected NULL in varible: name' WHEN name IS NULL; + + + diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c new file mode 100644 index 11cb47b..1c44f85 *** a/src/pl/plpgsql/src/pl_exec.c --- b/src/pl/plpgsql/src/pl_exec.c *************** exec_stmt_raise(PLpgSQL_execstate *estat *** 2892,2897 **** --- 2892,2908 ---- char *err_schema = NULL; ListCell *lc; + if (stmt->cond != NULL) + { + bool value; + bool isnull; + + value = exec_eval_boolean(estate, stmt->cond, &isnull); + exec_eval_cleanup(estate); + if (isnull || value == false) + return PLPGSQL_RC_OK; + } + /* RAISE with no parameters: re-throw current exception */ if (stmt->condname == NULL && stmt->message == NULL && stmt->options == NIL) diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y new file mode 100644 index 893f3a4..1f0b861 *** a/src/pl/plpgsql/src/pl_gram.y --- b/src/pl/plpgsql/src/pl_gram.y *************** static void current_token_is_not_varia *** 63,68 **** --- 63,69 ---- static PLpgSQL_expr *read_sql_construct(int until, int until2, int until3, + int until4, const char *expected, const char *sqlstart, bool isexpression, *************** static void check_labels(const char * *** 105,111 **** int end_location); static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor, int until, const char *expected); ! static List *read_raise_options(void); static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %} --- 106,112 ---- int end_location); static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor, int until, const char *expected); ! static List *read_raise_options(int *endtok); static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %} *************** for_control : for_variable K_IN *** 1422,1427 **** --- 1423,1429 ---- expr1 = read_sql_construct(DOT_DOT, K_LOOP, 0, + 0, "LOOP", "SELECT ", true, *************** stmt_raise : K_RAISE *** 1716,1721 **** --- 1718,1724 ---- { PLpgSQL_stmt_raise *new; int tok; + int endtok; new = palloc(sizeof(PLpgSQL_stmt_raise)); *************** stmt_raise : K_RAISE *** 1726,1731 **** --- 1729,1735 ---- new->message = NULL; new->params = NIL; new->options = NIL; + new->cond = NULL; tok = yylex(); if (tok == 0) *************** stmt_raise : K_RAISE *** 1796,1817 **** * or USING to begin the options list. */ tok = yylex(); ! if (tok != ',' && tok != ';' && tok != K_USING) yyerror("syntax error"); while (tok == ',') { PLpgSQL_expr *expr; ! expr = read_sql_construct(',', ';', K_USING, ! ", or ; or USING", "SELECT ", true, true, true, NULL, &tok); new->params = lappend(new->params, expr); } } ! else if (tok != K_USING) { /* must be condition name or SQLSTATE */ if (tok_is_keyword(tok, &yylval, --- 1800,1821 ---- * or USING to begin the options list. */ tok = yylex(); ! if (tok != ',' && tok != ';' && tok != K_USING && tok != K_WHEN) yyerror("syntax error"); while (tok == ',') { PLpgSQL_expr *expr; ! expr = read_sql_construct(',', ';', K_USING, K_WHEN, ! ", or ; or USING or WHEN", "SELECT ", true, true, true, NULL, &tok); new->params = lappend(new->params, expr); } } ! else if (tok != K_USING && tok != K_WHEN) { /* must be condition name or SQLSTATE */ if (tok_is_keyword(tok, &yylval, *************** stmt_raise : K_RAISE *** 1847,1853 **** } if (tok == K_USING) ! new->options = read_raise_options(); } check_raise_parameters(new); --- 1851,1863 ---- } if (tok == K_USING) ! { ! new->options = read_raise_options(&endtok); ! if (endtok == K_WHEN) ! new->cond = read_sql_expression(';', ";"); ! } ! if (tok == K_WHEN) ! new->cond = read_sql_expression(';', ";"); } check_raise_parameters(new); *************** stmt_dynexecute : K_EXECUTE *** 1906,1912 **** PLpgSQL_expr *expr; int endtoken; ! expr = read_sql_construct(K_INTO, K_USING, ';', "INTO or USING or ;", "SELECT ", true, true, true, --- 1916,1922 ---- PLpgSQL_expr *expr; int endtoken; ! expr = read_sql_construct(K_INTO, K_USING, ';', 0, "INTO or USING or ;", "SELECT ", true, true, true, *************** stmt_dynexecute : K_EXECUTE *** 1945,1951 **** yyerror("syntax error"); do { ! expr = read_sql_construct(',', ';', K_INTO, ", or ; or INTO", "SELECT ", true, true, true, --- 1955,1961 ---- yyerror("syntax error"); do { ! expr = read_sql_construct(',', ';', K_INTO, 0, ", or ; or INTO", "SELECT ", true, true, true, *************** current_token_is_not_variable(int tok) *** 2456,2462 **** static PLpgSQL_expr * read_sql_expression(int until, const char *expected) { ! return read_sql_construct(until, 0, 0, expected, "SELECT ", true, true, true, NULL, NULL); } --- 2466,2472 ---- static PLpgSQL_expr * read_sql_expression(int until, const char *expected) { ! return read_sql_construct(until, 0, 0, 0, expected, "SELECT ", true, true, true, NULL, NULL); } *************** static PLpgSQL_expr * *** 2465,2471 **** read_sql_expression2(int until, int until2, const char *expected, int *endtoken) { ! return read_sql_construct(until, until2, 0, expected, "SELECT ", true, true, true, NULL, endtoken); } --- 2475,2481 ---- read_sql_expression2(int until, int until2, const char *expected, int *endtoken) { ! return read_sql_construct(until, until2, 0, 0, expected, "SELECT ", true, true, true, NULL, endtoken); } *************** read_sql_expression2(int until, int unti *** 2473,2479 **** static PLpgSQL_expr * read_sql_stmt(const char *sqlstart) { ! return read_sql_construct(';', 0, 0, ";", sqlstart, false, true, true, NULL, NULL); } --- 2483,2489 ---- static PLpgSQL_expr * read_sql_stmt(const char *sqlstart) { ! return read_sql_construct(';', 0, 0, 0, ";", sqlstart, false, true, true, NULL, NULL); } *************** static PLpgSQL_expr * *** 2496,2501 **** --- 2506,2512 ---- read_sql_construct(int until, int until2, int until3, + int until4, const char *expected, const char *sqlstart, bool isexpression, *************** read_sql_construct(int until, *** 2529,2534 **** --- 2540,2547 ---- break; if (tok == until3 && parenlevel == 0) break; + if (tok == until4 && parenlevel == 0) + break; if (tok == '(' || tok == '[') parenlevel++; else if (tok == ')' || tok == ']') *************** read_cursor_args(PLpgSQL_var *cursor, in *** 3647,3653 **** * translated into a form where the second parameter is commented * out. */ ! item = read_sql_construct(',', ')', 0, ",\" or \")", sqlstart, true, true, --- 3660,3666 ---- * translated into a form where the second parameter is commented * out. */ ! item = read_sql_construct(',', ')', 0, 0, ",\" or \")", sqlstart, true, true, *************** read_cursor_args(PLpgSQL_var *cursor, in *** 3711,3724 **** * Parse RAISE ... USING options */ static List * ! read_raise_options(void) { List *result = NIL; for (;;) { PLpgSQL_raise_option *opt; - int tok; if ((tok = yylex()) == 0) yyerror("unexpected end of function definition"); --- 3724,3737 ---- * Parse RAISE ... USING options */ static List * ! read_raise_options(int *endtok) { List *result = NIL; + int tok; for (;;) { PLpgSQL_raise_option *opt; if ((tok = yylex()) == 0) yyerror("unexpected end of function definition"); *************** read_raise_options(void) *** 3759,3772 **** if (tok != '=' && tok != COLON_EQUALS) yyerror("syntax error, expected \"=\""); ! opt->expr = read_sql_expression2(',', ';', ", or ;", &tok); result = lappend(result, opt); ! if (tok == ';') break; } return result; } --- 3772,3788 ---- if (tok != '=' && tok != COLON_EQUALS) yyerror("syntax error, expected \"=\""); ! opt->expr = read_sql_construct(',', ';', K_WHEN, 0, ", or ; or WHEN", ! "SELECT ", true, true, true, NULL, &tok); result = lappend(result, opt); ! if (tok == ';' || tok == K_WHEN) break; } + *endtok = tok; + return result; } diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h new file mode 100644 index d6f31ff..3dee7f3 *** a/src/pl/plpgsql/src/plpgsql.h --- b/src/pl/plpgsql/src/plpgsql.h *************** typedef struct *** 621,626 **** --- 621,627 ---- char *message; /* old-style message format literal, or NULL */ List *params; /* list of expressions for old-style message */ List *options; /* list of PLpgSQL_raise_option */ + PLpgSQL_expr *cond; } PLpgSQL_stmt_raise; typedef struct diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out new file mode 100644 index 983f1b8..c149450 *** a/src/test/regress/expected/plpgsql.out --- b/src/test/regress/expected/plpgsql.out *************** NOTICE: outer_func() done *** 5351,5353 **** --- 5351,5388 ---- drop function outer_outer_func(int); drop function outer_func(int); drop function inner_func(int); + create or replace function test_func(text) + returns void as $$ + begin + raise notice 'Hello %', $1; + raise notice 'Hello %', $1 when false; + raise notice 'Hello % %', $1, $1 when true; + raise notice 'Hello % % %', $1, $2, $3 when null; + + raise notice using message = 'Nazdar'; + raise notice using message = 'Nazdar ' || $1 when true; + raise notice using message = 'Nazdar ' || $1, detail = 'Svete' when true; + + for i in 1..8 + loop + raise notice '% is even number', i when i % 2 = 0; + end loop; + end; + $$ language plpgsql; + select test_func('world'); + NOTICE: Hello world + NOTICE: Hello world world + NOTICE: Nazdar + NOTICE: Nazdar world + NOTICE: Nazdar world + DETAIL: Svete + NOTICE: 2 is even number + NOTICE: 4 is even number + NOTICE: 6 is even number + NOTICE: 8 is even number + test_func + ----------- + + (1 row) + + drop function test_func(text); diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql new file mode 100644 index 2abcbc8..2675da6 *** a/src/test/regress/sql/plpgsql.sql --- b/src/test/regress/sql/plpgsql.sql *************** select outer_outer_func(20); *** 4198,4200 **** --- 4198,4223 ---- drop function outer_outer_func(int); drop function outer_func(int); drop function inner_func(int); + + create or replace function test_func(text) + returns void as $$ + begin + raise notice 'Hello %', $1; + raise notice 'Hello %', $1 when false; + raise notice 'Hello % %', $1, $1 when true; + raise notice 'Hello % % %', $1, $2, $3 when null; + + raise notice using message = 'Nazdar'; + raise notice using message = 'Nazdar ' || $1 when true; + raise notice using message = 'Nazdar ' || $1, detail = 'Svete' when true; + + for i in 1..8 + loop + raise notice '% is even number', i when i % 2 = 0; + end loop; + end; + $$ language plpgsql; + + select test_func('world'); + + drop function test_func(text);