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

Reply via email to