Hi
2014-10-14 22:57 GMT+02:00 Petr Jelinek <[email protected]>:
> On 09/09/14 17:37, Pavel Stehule wrote:
>
>> Ada is language with strong character, and PLpgSQL is little bit strange
>> fork - so it isn't easy to find some form, how to solve all requirements.
>>
>> My requests:
>>
>> * be consistent with current PLpgSQL syntax and logic
>> * allow some future extensibility
>> * allow a static analyses without hard expression processing
>>
>> But I am thinking so there are some points where can be some agreement -
>> although it is not ASSERT implementation.
>>
>> enhancing RAISE WHEN - please, see attached patch -
>>
>> I prefer RAISE WHEN again RAISE WHERE due consistency with EXIT and
>> CONTINUE [ WHEN ];
>>
>>
> Short review of the patch. Note that this has nothing to do with actual
> assertions, at the moment it's just enhancement of RAISE statement to make
> it optionally conditional. As I was one of the people who voted for it I do
> think we want this and I like the syntax.
>
> Code applies cleanly, seems formatted according to project standards -
> there is unnecessary whitespace added in variable declaration part of
> exec_stmt_raise which should be removed.
>
fixed
>
> Passes make check, I would prefer to have little more complex expression
> than just "true" in the new regression test added for this feature.
>
fixed
>
> Did some manual testing, seems to work as advertised.
>
> There are no docs at the moment which is the only show-stopper that I can
> see so far.
>
fixed
Regards
Pavel
>
> --
> Petr Jelinek http://www.2ndQuadrant.com/
>
> PostgreSQL Development, 24x7 Support, Training & Services
>
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 <optional> <replaceable>label</
*** 3369,3379 ****
raise errors.
<synopsis>
! RAISE <optional> <replaceable class="parameter">level</replaceable> </optional> '<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>;
! RAISE <optional> <replaceable class="parameter">level</replaceable> </optional> <replaceable class="parameter">condition_name</> <optional> USING <replaceable class="parameter">option</replaceable> = <replaceable class="parameter">expression</replaceable> <optional>, ... </optional> </optional>;
! RAISE <optional> <replaceable class="parameter">level</replaceable> </optional> SQLSTATE '<replaceable class="parameter">sqlstate</>' <optional> USING <replaceable class="parameter">option</replaceable> = <replaceable class="parameter">expression</replaceable> <optional>, ... </optional> </optional>;
! RAISE <optional> <replaceable class="parameter">level</replaceable> </optional> USING <replaceable class="parameter">option</replaceable> = <replaceable class="parameter">expression</replaceable> <optional>, ... </optional>;
! RAISE ;
</synopsis>
The <replaceable class="parameter">level</replaceable> option specifies
--- 3369,3379 ----
raise errors.
<synopsis>
! RAISE <optional> <replaceable class="parameter">level</replaceable> </optional> '<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> <optional> WHEN <replaceable class="parameter">boolean-expression</replaceable> </optional>;
! RAISE <optional> <replaceable class="parameter">level</replaceable> </optional> <replaceable class="parameter">condition_name</> <optional> USING <replaceable class="parameter">option</replaceable> = <replaceable class="parameter">expression</replaceable> <optional>, ... </optional> </optional> <optional> WHEN <replaceable class="parameter">boolean-expression</replaceable> </optional>;
! RAISE <optional> <replaceable class="parameter">level</replaceable> </optional> SQLSTATE '<replaceable class="parameter">sqlstate</>' <optional> USING <replaceable class="parameter">option</replaceable> = <replaceable class="parameter">expression</replaceable> <optional>, ... </optional> </optional> <optional> WHEN <replaceable class="parameter">boolean-expression</replaceable> </optional>;
! RAISE <optional> <replaceable class="parameter">level</replaceable> </optional> USING <replaceable class="parameter">option</replaceable> = <replaceable class="parameter">expression</replaceable> <optional>, ... </optional> <optional> WHEN <replaceable class="parameter">boolean-expression</replaceable> </optional>;
! RAISE <optional> WHEN <replaceable class="parameter">boolean-expression</replaceable> </optional>;
</synopsis>
The <replaceable class="parameter">level</replaceable> option specifies
*************** RAISE unique_violation USING MESSAGE = '
*** 3548,3553 ****
--- 3548,3561 ----
</para>
</note>
+ <para>
+ If <literal>WHEN</literal> is specified, the message or error is raised
+ only if <replaceable>boolean-expression</> is true.
+ <programlisting>
+ RAISE 'Unexpected NULL in varible: name' WHEN name IS NULL;
+ </programlisting>
+ </para>
+
</sect1>
<sect1 id="plpgsql-trigger">
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);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers