commit 33951bc23365029ee94af5ec43e90893dcd737a8 Author: Pavel Stehule Date: Sat Apr 25 22:09:28 2015 +0200 initial implementation of (WITH|WITHOUT) CONTEXT clause to plpgsql RAISE statement. initial implementation of plpgsql GUC plpgsql.display_context_messages diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index d36acf6..8aebb87 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -3406,10 +3406,10 @@ END LOOP label ; raise errors. -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 level ( WITH | WITHOUT ) CONTEXT 'format' , expression , ... USING option = expression , ... ; +RAISE level ( WITH | WITHOUT ) CONTEXT condition_name USING option = expression , ... ; +RAISE level ( WITH | WITHOUT ) CONTEXT SQLSTATE 'sqlstate' USING option = expression , ... ; +RAISE level ( WITH | WITHOUT ) CONTEXT USING option = expression , ... ; RAISE ; @@ -3431,6 +3431,18 @@ RAISE ; + The options WITH CONTEXT or WITHOUT CONTEXT + can enforce or suppress context information related to error or notice. This possibility + can be forced by settings of configuration parameter plpgsql.display_context_min_messages. + This allows same values like level option plus + value none that is a default. When it is changed, then all errors and notices + with higher than specified severity are raised with context info. + +RAISE NOTICE WITH CONTEXT 'This message will have a context'; + + + + After level if any, you can write a format (which must be a simple string literal, not an expression). The diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index deefb1f..6d1e791 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -2921,6 +2921,7 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) char *err_table = NULL; char *err_schema = NULL; ListCell *lc; + bool hide_ctx = true; /* suppress context by default */ /* RAISE with no parameters: re-throw current exception */ if (stmt->condname == NULL && stmt->message == NULL && @@ -3080,10 +3081,16 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) err_message = pstrdup(unpack_sql_state(err_code)); } - /* - * Throw the error (may or may not come back) - */ - estate->err_text = raise_skip_msg; /* suppress traceback of raise */ + if (stmt->context_info == PLPGSQL_CONTEXT_DISPLAY) + hide_ctx = false; + else if (stmt->context_info == PLPGSQL_CONTEXT_DEFAULT) + { + if (plpgsql_display_context_min_messages != PLPGSQL_DISPLAY_CONTEXT_SUPPRESS) + hide_ctx = stmt->elog_level < plpgsql_display_context_min_messages; + } + + if (hide_ctx) + estate->err_text = raise_skip_msg; ereport(stmt->elog_level, (err_code ? errcode(err_code) : 0, @@ -3099,7 +3106,8 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) (err_table != NULL) ? err_generic_string(PG_DIAG_TABLE_NAME, err_table) : 0, (err_schema != NULL) ? - err_generic_string(PG_DIAG_SCHEMA_NAME, err_schema) : 0)); + err_generic_string(PG_DIAG_SCHEMA_NAME, err_schema) : 0, + errhidecontext(hide_ctx))); estate->err_text = NULL; /* un-suppress... */ diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y index 4026e41..48914a7 100644 --- a/src/pl/plpgsql/src/pl_gram.y +++ b/src/pl/plpgsql/src/pl_gram.y @@ -259,6 +259,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %token K_CONSTANT %token K_CONSTRAINT %token K_CONSTRAINT_NAME +%token K_CONTEXT %token K_CONTINUE %token K_CURRENT %token K_CURSOR @@ -341,6 +342,8 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %token K_WARNING %token K_WHEN %token K_WHILE +%token K_WITH +%token K_WITHOUT %% @@ -1716,6 +1719,7 @@ stmt_raise : K_RAISE new->cmd_type = PLPGSQL_STMT_RAISE; new->lineno = plpgsql_location_to_lineno(@1); new->elog_level = ERROR; /* default */ + new->context_info = PLPGSQL_CONTEXT_DEFAULT; new->condname = NULL; new->message = NULL; new->params = NIL; @@ -1773,6 +1777,21 @@ stmt_raise : K_RAISE if (tok == 0) yyerror("unexpected end of function definition"); + /* Optional choose about including context */ + if (tok == K_WITH || tok == K_WITHOUT) + { + if (tok == K_WITH) + new->context_info = PLPGSQL_CONTEXT_DISPLAY; + else + new->context_info = PLPGSQL_CONTEXT_SUPPRESS; + + /* keyword CONTEXT is required */ + if (yylex() != K_CONTEXT) + yyerror("expected CONTEXT"); + + tok = yylex(); + } + /* * Next we can have a condition name, or * equivalently SQLSTATE 'xxxxx', or a string @@ -2350,6 +2369,7 @@ unreserved_keyword : | K_CONSTANT | K_CONSTRAINT | K_CONSTRAINT_NAME + | K_CONTEXT | K_CONTINUE | K_CURRENT | K_CURSOR @@ -2412,6 +2432,8 @@ unreserved_keyword : | K_USE_VARIABLE | K_VARIABLE_CONFLICT | K_WARNING + | K_WITH + | K_WITHOUT ; %% diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index 266c314..e9f9029 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -40,6 +40,17 @@ static const struct config_enum_entry variable_conflict_options[] = { {NULL, 0, false} }; +static const struct config_enum_entry display_context_min_messages_options[] = { + {"none", PLPGSQL_DISPLAY_CONTEXT_SUPPRESS}, + {"exception", ERROR}, + {"warning", WARNING}, + {"notice", NOTICE}, + {"info", INFO}, + {"log", LOG}, + {"debug", DEBUG1}, + {NULL, 0, false} +}; + int plpgsql_variable_conflict = PLPGSQL_RESOLVE_ERROR; bool plpgsql_print_strict_params = false; @@ -51,6 +62,8 @@ char *plpgsql_extra_errors_string = NULL; int plpgsql_extra_warnings; int plpgsql_extra_errors; +int plpgsql_display_context_min_messages = PLPGSQL_DISPLAY_CONTEXT_SUPPRESS; + /* Hook for plugins */ PLpgSQL_plugin **plugin_ptr = NULL; @@ -154,6 +167,15 @@ _PG_init(void) PGC_SUSET, 0, NULL, NULL, NULL); + DefineCustomEnumVariable("plpgsql.display_context_min_messages", + gettext_noop("Sets minimal level of messages with context information."), + NULL, + &plpgsql_display_context_min_messages, + PLPGSQL_DISPLAY_CONTEXT_SUPPRESS, + display_context_min_messages_options, + PGC_SUSET, 0, + NULL, NULL, NULL); + DefineCustomBoolVariable("plpgsql.print_strict_params", gettext_noop("Print information about parameters in the DETAIL part of the error messages generated on INTO ... STRICT failures."), NULL, diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c index 683fdab..973cab8 100644 --- a/src/pl/plpgsql/src/pl_scanner.c +++ b/src/pl/plpgsql/src/pl_scanner.c @@ -107,6 +107,7 @@ static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD) PG_KEYWORD("constraint", K_CONSTRAINT, UNRESERVED_KEYWORD) PG_KEYWORD("constraint_name", K_CONSTRAINT_NAME, UNRESERVED_KEYWORD) + PG_KEYWORD("context", K_CONTEXT, UNRESERVED_KEYWORD) PG_KEYWORD("continue", K_CONTINUE, UNRESERVED_KEYWORD) PG_KEYWORD("current", K_CURRENT, UNRESERVED_KEYWORD) PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD) @@ -170,6 +171,8 @@ static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("use_variable", K_USE_VARIABLE, UNRESERVED_KEYWORD) PG_KEYWORD("variable_conflict", K_VARIABLE_CONFLICT, UNRESERVED_KEYWORD) PG_KEYWORD("warning", K_WARNING, UNRESERVED_KEYWORD) + PG_KEYWORD("with", K_WITH, UNRESERVED_KEYWORD) + PG_KEYWORD("without", K_WITHOUT, UNRESERVED_KEYWORD) }; static const int num_unreserved_keywords = lengthof(unreserved_keywords); diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index bec773a..73d3238 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -168,6 +168,18 @@ typedef enum } PLpgSQL_resolve_option; +/* -------- + * Manipulation with context of exception + * -------- + */ +enum +{ + PLPGSQL_CONTEXT_DISPLAY, + PLPGSQL_CONTEXT_SUPPRESS, + PLPGSQL_CONTEXT_DEFAULT +}; + + /********************************************************************** * Node and structure definitions **********************************************************************/ @@ -619,6 +631,7 @@ typedef struct int cmd_type; int lineno; int elog_level; + int context_info; char *condname; /* condition name, SQLSTATE, or NULL */ char *message; /* old-style message format literal, or NULL */ List *params; /* list of expressions for old-style message */ @@ -922,6 +935,10 @@ extern MemoryContext compile_tmp_cxt; extern PLpgSQL_plugin **plugin_ptr; +#define PLPGSQL_DISPLAY_CONTEXT_SUPPRESS 0 + +extern int plpgsql_display_context_min_messages; + /********************************************************************** * Function declarations **********************************************************************/ diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 78e5a85..4726df8 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -5426,3 +5426,38 @@ end; $$; ERROR: unhandled assertion CONTEXT: PL/pgSQL function inline_code_block line 3 at ASSERT +--possibility to enforce context message displaying +do $$ +begin + raise notice with context 'hello'; +end; +$$; +NOTICE: hello +CONTEXT: PL/pgSQL function inline_code_block line 3 at RAISE +do $$ +begin + raise exception with context 'hello'; +end; +$$; +ERROR: hello +CONTEXT: PL/pgSQL function inline_code_block line 3 at RAISE +set plpgsql.display_context_min_messages = 'notice'; +do $$ +begin + raise notice 'some notice'; + raise exception 'some exception'; +end; +$$; +NOTICE: some notice +CONTEXT: PL/pgSQL function inline_code_block line 3 at RAISE +ERROR: some exception +CONTEXT: PL/pgSQL function inline_code_block line 4 at RAISE +-- possibility to suppress default +do $$ +begin + raise notice without context 'some notice'; + raise exception without context 'some exception'; +end; +$$; +NOTICE: some notice +ERROR: some exception diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index e19e415..fdb8e49 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -4265,3 +4265,35 @@ exception when others then null; -- do nothing end; $$; + + +--possibility to enforce context message displaying +do $$ +begin + raise notice with context 'hello'; +end; +$$; + +do $$ +begin + raise exception with context 'hello'; +end; +$$; + +set plpgsql.display_context_min_messages = 'notice'; +do $$ +begin + raise notice 'some notice'; + raise exception 'some exception'; +end; +$$; + +-- possibility to suppress default +do $$ +begin + raise notice without context 'some notice'; + raise exception without context 'some exception'; +end; +$$; + +