Hi 2014-11-26 16:46 GMT+01:00 Pavel Stehule <pavel.steh...@gmail.com>:
> > > 2014-11-26 13:31 GMT+01:00 Marko Tiikkaja <ma...@joh.to>: > >> On 11/26/14 8:55 AM, Pavel Stehule wrote: >> >>> * should be assertions globally enabled/disabled? - I have no personal >>> preference in this question. >>> >> >> I think so. The way I would use this function is to put expensive checks >> into strategic locations which would only run when developing locally (and >> additionally perhaps in one of the test environments.) And in that case >> I'd like to globally disable them for the live environment. >> > > ok > > >> >> * can be ASSERT exception handled ? - I prefer this be unhandled >>> exception >>> - like query_canceled because It should not be masked by plpgsql >>> EXCEPTION >>> WHEN OTHERS ... >>> >> >> I don't care much either way, as long as we get good information about >> what went wrong. A stack trace and hopefully something like >> print_strict_params for parameters to the "expr". >> > > There is more ways, I can live with both > here is proof concept what do you think about it? Regards Pavel > > Pavel > > > >> >> >> .marko >> > >
commit 8c24bdca4f8cc7a0b83e5b36aaba66ce13e4d933 Author: Pavel Stehule <pavel.steh...@gooddata.com> Date: Wed Nov 26 19:36:58 2014 +0100 initial diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt index 62ba092..366fcdd 100644 --- a/src/backend/utils/errcodes.txt +++ b/src/backend/utils/errcodes.txt @@ -454,6 +454,7 @@ P0000 E ERRCODE_PLPGSQL_ERROR plp P0001 E ERRCODE_RAISE_EXCEPTION raise_exception P0002 E ERRCODE_NO_DATA_FOUND no_data_found P0003 E ERRCODE_TOO_MANY_ROWS too_many_rows +P0004 E ERRCODE_ASSERT_EXCEPTION assert_exception Section: Class XX - Internal Error diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 11cb47b..3f50f54 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -136,6 +136,8 @@ static int exec_stmt_return_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_return_query *stmt); static int exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt); +static int exec_stmt_assert(PLpgSQL_execstate *estate, + PLpgSQL_stmt_assert *stmt); static int exec_stmt_execsql(PLpgSQL_execstate *estate, PLpgSQL_stmt_execsql *stmt); static int exec_stmt_dynexecute(PLpgSQL_execstate *estate, @@ -1465,6 +1467,10 @@ exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt); break; + case PLPGSQL_STMT_ASSERT: + rc = exec_stmt_assert(estate, (PLpgSQL_stmt_assert *) stmt); + break; + case PLPGSQL_STMT_EXECSQL: rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt); break; @@ -3091,6 +3097,57 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) return PLPGSQL_RC_OK; } +/* ---------- + * exec_stmt_assert Assert statement + * ---------- + */ +static int +exec_stmt_assert(PLpgSQL_execstate *estate, PLpgSQL_stmt_assert *stmt) +{ + bool value; + bool isnull; + + value = exec_eval_boolean(estate, stmt->cond, &isnull); + exec_eval_cleanup(estate); + + if (isnull || !value) + { + StringInfoData ds; + char *err_hint = NULL; + + initStringInfo(&ds); + + if (isnull) + appendStringInfo(&ds, "\"%s\" is null", stmt->cond->query + 7); + else + appendStringInfo(&ds, "\"%s\" is false", stmt->cond->query + 7); + + if (stmt->hint != NULL) + { + Oid expr_typeid; + bool expr_isnull; + Datum expr_val; + + expr_val = exec_eval_expr(estate, stmt->hint, + &expr_isnull, + &expr_typeid); + if (expr_isnull) + err_hint = pstrdup("Hint is null."); + else + err_hint = pstrdup(convert_value_to_string(estate, expr_val, expr_typeid)); + + exec_eval_cleanup(estate); + } + + ereport(ERROR, + (errcode(ERRCODE_ASSERT_EXCEPTION), + errmsg("assert_exception"), + errdetail(ds.data), + (err_hint != NULL) ? errhint("%s", err_hint) : 0)); + } + + return PLPGSQL_RC_OK; +} /* ---------- * Initialize a mostly empty execution state diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index d6825e4..1a8d00d 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -244,6 +244,8 @@ plpgsql_stmt_typename(PLpgSQL_stmt *stmt) return "RETURN QUERY"; case PLPGSQL_STMT_RAISE: return "RAISE"; + case PLPGSQL_STMT_ASSERT: + return "ASSERT"; case PLPGSQL_STMT_EXECSQL: return _("SQL statement"); case PLPGSQL_STMT_DYNEXECUTE: @@ -330,6 +332,7 @@ static void free_return(PLpgSQL_stmt_return *stmt); static void free_return_next(PLpgSQL_stmt_return_next *stmt); static void free_return_query(PLpgSQL_stmt_return_query *stmt); static void free_raise(PLpgSQL_stmt_raise *stmt); +static void free_assert(PLpgSQL_stmt_assert *stmt); static void free_execsql(PLpgSQL_stmt_execsql *stmt); static void free_dynexecute(PLpgSQL_stmt_dynexecute *stmt); static void free_dynfors(PLpgSQL_stmt_dynfors *stmt); @@ -391,6 +394,9 @@ free_stmt(PLpgSQL_stmt *stmt) case PLPGSQL_STMT_RAISE: free_raise((PLpgSQL_stmt_raise *) stmt); break; + case PLPGSQL_STMT_ASSERT: + free_assert((PLpgSQL_stmt_assert *) stmt); + break; case PLPGSQL_STMT_EXECSQL: free_execsql((PLpgSQL_stmt_execsql *) stmt); break; @@ -611,6 +617,13 @@ free_raise(PLpgSQL_stmt_raise *stmt) } static void +free_assert(PLpgSQL_stmt_assert *stmt) +{ + free_expr(stmt->cond); + free_expr(stmt->hint); +} + +static void free_execsql(PLpgSQL_stmt_execsql *stmt) { free_expr(stmt->sqlstmt); @@ -732,6 +745,7 @@ static void dump_return(PLpgSQL_stmt_return *stmt); static void dump_return_next(PLpgSQL_stmt_return_next *stmt); static void dump_return_query(PLpgSQL_stmt_return_query *stmt); static void dump_raise(PLpgSQL_stmt_raise *stmt); +static void dump_assert(PLpgSQL_stmt_assert *stmt); static void dump_execsql(PLpgSQL_stmt_execsql *stmt); static void dump_dynexecute(PLpgSQL_stmt_dynexecute *stmt); static void dump_dynfors(PLpgSQL_stmt_dynfors *stmt); @@ -804,6 +818,9 @@ dump_stmt(PLpgSQL_stmt *stmt) case PLPGSQL_STMT_RAISE: dump_raise((PLpgSQL_stmt_raise *) stmt); break; + case PLPGSQL_STMT_ASSERT: + dump_assert((PLpgSQL_stmt_assert *) stmt); + break; case PLPGSQL_STMT_EXECSQL: dump_execsql((PLpgSQL_stmt_execsql *) stmt); break; @@ -1354,6 +1371,24 @@ dump_raise(PLpgSQL_stmt_raise *stmt) } static void +dump_assert(PLpgSQL_stmt_assert *stmt) +{ + dump_ind(); + printf("ASSERT "); + dump_expr(stmt->cond); + printf("\n"); + + dump_indent += 2; + if (stmt->hint != NULL) + { + dump_ind(); + printf(" HINT = "); + dump_expr(stmt->hint); + } + dump_indent -= 2; +} + +static void dump_execsql(PLpgSQL_stmt_execsql *stmt) { dump_ind(); diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y index 4af28ea..77082f9 100644 --- a/src/pl/plpgsql/src/pl_gram.y +++ b/src/pl/plpgsql/src/pl_gram.y @@ -192,7 +192,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %type <loop_body> loop_body %type <stmt> proc_stmt pl_block %type <stmt> stmt_assign stmt_if stmt_loop stmt_while stmt_exit -%type <stmt> stmt_return stmt_raise stmt_execsql +%type <stmt> stmt_return stmt_raise stmt_assert stmt_execsql %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 stmt_foreach_a @@ -246,6 +246,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %token <keyword> K_ALIAS %token <keyword> K_ALL %token <keyword> K_ARRAY +%token <keyword> K_ASSERT %token <keyword> K_BACKWARD %token <keyword> K_BEGIN %token <keyword> K_BY @@ -870,6 +871,8 @@ proc_stmt : pl_block ';' { $$ = $1; } | stmt_raise { $$ = $1; } + | stmt_assert + { $$ = $1; } | stmt_execsql { $$ = $1; } | stmt_dynexecute @@ -1846,6 +1849,37 @@ stmt_raise : K_RAISE } ; +stmt_assert: K_ASSERT + { + PLpgSQL_stmt_assert *new; + int endtoken; + + new = palloc(sizeof(PLpgSQL_stmt_assert)); + + new->cmd_type = PLPGSQL_STMT_ASSERT; + new->lineno = plpgsql_location_to_lineno(@1); + + new->cond = read_sql_construct(',', ';', 0, + ", or ;", + "SELECT ", + true, true, true, + NULL, &endtoken); + + if (endtoken == ',') + { + new->hint = read_sql_construct(';', 0, 0, + ";", + "SELECT ", + true, true, true, + NULL, NULL); + } + else + new->hint = NULL; + + $$ = (PLpgSQL_stmt *) new; + } + ; + loop_body : proc_sect K_END K_LOOP opt_label ';' { $$.stmts = $1; diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c index db01ba5..c81ab4f 100644 --- a/src/pl/plpgsql/src/pl_scanner.c +++ b/src/pl/plpgsql/src/pl_scanner.c @@ -98,6 +98,7 @@ static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("absolute", K_ABSOLUTE, UNRESERVED_KEYWORD) PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD) PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD) + PG_KEYWORD("assert", K_ASSERT, UNRESERVED_KEYWORD) PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD) PG_KEYWORD("close", K_CLOSE, UNRESERVED_KEYWORD) PG_KEYWORD("collate", K_COLLATE, UNRESERVED_KEYWORD) diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index d6f31ff..28e3345 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -79,6 +79,7 @@ enum enum PLpgSQL_stmt_types { PLPGSQL_STMT_BLOCK, + PLPGSQL_STMT_ASSERT, PLPGSQL_STMT_ASSIGN, PLPGSQL_STMT_IF, PLPGSQL_STMT_CASE, @@ -631,6 +632,14 @@ typedef struct typedef struct +{ /* ASSERT statement */ + int cmd_type; + int lineno; + PLpgSQL_expr *cond; + PLpgSQL_expr *hint; +} PLpgSQL_stmt_assert; + +typedef struct { /* Generic SQL statement to execute */ int cmd_type; int lineno; diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index daf3447..3c32fbe 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -5365,3 +5365,26 @@ NOTICE: outer_func() done drop function outer_outer_func(int); drop function outer_func(int); drop function inner_func(int); +do $$ +begin + assert 1=1; +end; +$$; +do $$ +begin + assert 1=0; +end; +$$; +ERROR: assert_exception +DETAIL: "1=0" is false +CONTEXT: PL/pgSQL function inline_code_block line 3 at ASSERT +do $$ +declare var text := 'some value'; +begin + assert 1=0, format('content of var: "%s"', var); +end; +$$; +ERROR: assert_exception +DETAIL: "1=0" is false +HINT: content of var: "some value" +CONTEXT: PL/pgSQL function inline_code_block line 4 at ASSERT diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index a0840c9..5cd7f7d 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -4209,3 +4209,22 @@ select outer_outer_func(20); drop function outer_outer_func(int); drop function outer_func(int); drop function inner_func(int); + +do $$ +begin + assert 1=1; +end; +$$; + +do $$ +begin + assert 1=0; +end; +$$; + +do $$ +declare var text := 'some value'; +begin + assert 1=0, format('content of var: "%s"', var); +end; +$$;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers