diff options
author | Noah Misch <noah@leadboat.com> | 2013-07-03 07:29:23 -0400 |
---|---|---|
committer | Noah Misch <noah@leadboat.com> | 2013-07-03 07:29:56 -0400 |
commit | 7cd9b1371d8b18d063dc38bc4fa7b30bd92c07a3 (patch) | |
tree | 68b4cb6ae2a84c92a6d2cbf2ffa45d1877558d0a /src | |
parent | 69e4fd4541979209d3bd238508d46d64c8ad46df (diff) | |
download | postgresql-7cd9b1371d8b18d063dc38bc4fa7b30bd92c07a3.tar.gz postgresql-7cd9b1371d8b18d063dc38bc4fa7b30bd92c07a3.zip |
Expose object name error fields in PL/pgSQL.
Specifically, permit attaching them to the error in RAISE and retrieving
them from a caught error in GET STACKED DIAGNOSTICS. RAISE enforces
nothing about the content of the fields; for its purposes, they are just
additional string fields. Consequently, clarify in the protocol and
libpq documentation that the usual relationships between error fields,
like a schema name appearing wherever a table name appears, are not
universal. This freedom has other applications; consider a FDW
propagating an error from an RDBMS having no schema support.
Back-patch to 9.3, where core support for the error fields was
introduced. This prevents the confusion of having a release where libpq
exposes the fields and PL/pgSQL does not.
Pavel Stehule, lexical revisions by Noah Misch.
Diffstat (limited to 'src')
-rw-r--r-- | src/pl/plpgsql/src/pl_exec.c | 98 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_funcs.c | 25 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_gram.y | 55 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_scanner.c | 10 | ||||
-rw-r--r-- | src/pl/plpgsql/src/plpgsql.h | 14 | ||||
-rw-r--r-- | src/test/regress/expected/plpgsql.out | 34 | ||||
-rw-r--r-- | src/test/regress/sql/plpgsql.sql | 32 |
7 files changed, 247 insertions, 21 deletions
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 70e67d9eb70..57789fc365b 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -1569,11 +1569,36 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt) unpack_sql_state(estate->cur_error->sqlerrcode)); break; + case PLPGSQL_GETDIAG_COLUMN_NAME: + exec_assign_c_string(estate, var, + estate->cur_error->column_name); + break; + + case PLPGSQL_GETDIAG_CONSTRAINT_NAME: + exec_assign_c_string(estate, var, + estate->cur_error->constraint_name); + break; + + case PLPGSQL_GETDIAG_DATATYPE_NAME: + exec_assign_c_string(estate, var, + estate->cur_error->datatype_name); + break; + case PLPGSQL_GETDIAG_MESSAGE_TEXT: exec_assign_c_string(estate, var, estate->cur_error->message); break; + case PLPGSQL_GETDIAG_TABLE_NAME: + exec_assign_c_string(estate, var, + estate->cur_error->table_name); + break; + + case PLPGSQL_GETDIAG_SCHEMA_NAME: + exec_assign_c_string(estate, var, + estate->cur_error->schema_name); + break; + default: elog(ERROR, "unrecognized diagnostic item kind: %d", diag_item->kind); @@ -2799,6 +2824,16 @@ exec_init_tuple_store(PLpgSQL_execstate *estate) estate->rettupdesc = rsi->expectedDesc; } +#define SET_RAISE_OPTION_TEXT(opt, name) \ +do { \ + if (opt) \ + ereport(ERROR, \ + (errcode(ERRCODE_SYNTAX_ERROR), \ + errmsg("RAISE option already specified: %s", \ + name))); \ + opt = pstrdup(extval); \ +} while (0) + /* ---------- * exec_stmt_raise Build a message and throw it with elog() * ---------- @@ -2811,6 +2846,11 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) char *err_message = NULL; char *err_detail = NULL; char *err_hint = NULL; + char *err_column = NULL; + char *err_constraint = NULL; + char *err_datatype = NULL; + char *err_table = NULL; + char *err_schema = NULL; ListCell *lc; /* RAISE with no parameters: re-throw current exception */ @@ -2927,28 +2967,28 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) condname = pstrdup(extval); break; case PLPGSQL_RAISEOPTION_MESSAGE: - if (err_message) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("RAISE option already specified: %s", - "MESSAGE"))); - err_message = pstrdup(extval); + SET_RAISE_OPTION_TEXT(err_message, "MESSAGE"); break; case PLPGSQL_RAISEOPTION_DETAIL: - if (err_detail) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("RAISE option already specified: %s", - "DETAIL"))); - err_detail = pstrdup(extval); + SET_RAISE_OPTION_TEXT(err_detail, "DETAIL"); break; case PLPGSQL_RAISEOPTION_HINT: - if (err_hint) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("RAISE option already specified: %s", - "HINT"))); - err_hint = pstrdup(extval); + SET_RAISE_OPTION_TEXT(err_hint, "HINT"); + break; + case PLPGSQL_RAISEOPTION_COLUMN: + SET_RAISE_OPTION_TEXT(err_column, "COLUMN"); + break; + case PLPGSQL_RAISEOPTION_CONSTRAINT: + SET_RAISE_OPTION_TEXT(err_constraint, "CONSTRAINT"); + break; + case PLPGSQL_RAISEOPTION_DATATYPE: + SET_RAISE_OPTION_TEXT(err_datatype, "DATATYPE"); + break; + case PLPGSQL_RAISEOPTION_TABLE: + SET_RAISE_OPTION_TEXT(err_table, "TABLE"); + break; + case PLPGSQL_RAISEOPTION_SCHEMA: + SET_RAISE_OPTION_TEXT(err_schema, "SCHEMA"); break; default: elog(ERROR, "unrecognized raise option: %d", opt->opt_type); @@ -2982,7 +3022,17 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) (err_code ? errcode(err_code) : 0, errmsg_internal("%s", err_message), (err_detail != NULL) ? errdetail_internal("%s", err_detail) : 0, - (err_hint != NULL) ? errhint("%s", err_hint) : 0)); + (err_hint != NULL) ? errhint("%s", err_hint) : 0, + (err_column != NULL) ? + err_generic_string(PG_DIAG_COLUMN_NAME, err_column) : 0, + (err_constraint != NULL) ? + err_generic_string(PG_DIAG_CONSTRAINT_NAME, err_constraint) : 0, + (err_datatype != NULL) ? + err_generic_string(PG_DIAG_DATATYPE_NAME, err_datatype) : 0, + (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)); estate->err_text = NULL; /* un-suppress... */ @@ -2994,6 +3044,16 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt) pfree(err_detail); if (err_hint != NULL) pfree(err_hint); + if (err_column != NULL) + pfree(err_column); + if (err_constraint != NULL) + pfree(err_constraint); + if (err_datatype != NULL) + pfree(err_datatype); + if (err_table != NULL) + pfree(err_table); + if (err_schema != NULL) + pfree(err_schema); return PLPGSQL_RC_OK; } diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index 9d561c2322e..87e528fe5bf 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -285,8 +285,18 @@ plpgsql_getdiag_kindname(int kind) return "PG_EXCEPTION_HINT"; case PLPGSQL_GETDIAG_RETURNED_SQLSTATE: return "RETURNED_SQLSTATE"; + case PLPGSQL_GETDIAG_COLUMN_NAME: + return "COLUMN_NAME"; + case PLPGSQL_GETDIAG_CONSTRAINT_NAME: + return "CONSTRAINT_NAME"; + case PLPGSQL_GETDIAG_DATATYPE_NAME: + return "PG_DATATYPE_NAME"; case PLPGSQL_GETDIAG_MESSAGE_TEXT: return "MESSAGE_TEXT"; + case PLPGSQL_GETDIAG_TABLE_NAME: + return "TABLE_NAME"; + case PLPGSQL_GETDIAG_SCHEMA_NAME: + return "SCHEMA_NAME"; } return "unknown"; @@ -1317,6 +1327,21 @@ dump_raise(PLpgSQL_stmt_raise *stmt) case PLPGSQL_RAISEOPTION_HINT: printf(" HINT = "); break; + case PLPGSQL_RAISEOPTION_COLUMN: + printf(" COLUMN = "); + break; + case PLPGSQL_RAISEOPTION_CONSTRAINT: + printf(" CONSTRAINT = "); + break; + case PLPGSQL_RAISEOPTION_DATATYPE: + printf(" DATATYPE = "); + break; + case PLPGSQL_RAISEOPTION_TABLE: + printf(" TABLE = "); + break; + case PLPGSQL_RAISEOPTION_SCHEMA: + printf(" SCHEMA = "); + break; } dump_expr(opt->expr); printf("\n"); diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y index a790ee30ea9..086987a58a4 100644 --- a/src/pl/plpgsql/src/pl_gram.y +++ b/src/pl/plpgsql/src/pl_gram.y @@ -251,10 +251,15 @@ static List *read_raise_options(void); %token <keyword> K_CASE %token <keyword> K_CLOSE %token <keyword> K_COLLATE +%token <keyword> K_COLUMN +%token <keyword> K_COLUMN_NAME %token <keyword> K_CONSTANT +%token <keyword> K_CONSTRAINT +%token <keyword> K_CONSTRAINT_NAME %token <keyword> K_CONTINUE %token <keyword> K_CURRENT %token <keyword> K_CURSOR +%token <keyword> K_DATATYPE %token <keyword> K_DEBUG %token <keyword> K_DECLARE %token <keyword> K_DEFAULT @@ -298,6 +303,7 @@ static List *read_raise_options(void); %token <keyword> K_OPTION %token <keyword> K_OR %token <keyword> K_PERFORM +%token <keyword> K_PG_DATATYPE_NAME %token <keyword> K_PG_EXCEPTION_CONTEXT %token <keyword> K_PG_EXCEPTION_DETAIL %token <keyword> K_PG_EXCEPTION_HINT @@ -311,11 +317,15 @@ static List *read_raise_options(void); %token <keyword> K_REVERSE %token <keyword> K_ROWTYPE %token <keyword> K_ROW_COUNT +%token <keyword> K_SCHEMA +%token <keyword> K_SCHEMA_NAME %token <keyword> K_SCROLL %token <keyword> K_SLICE %token <keyword> K_SQLSTATE %token <keyword> K_STACKED %token <keyword> K_STRICT +%token <keyword> K_TABLE +%token <keyword> K_TABLE_NAME %token <keyword> K_THEN %token <keyword> K_TO %token <keyword> K_TYPE @@ -896,7 +906,12 @@ stmt_getdiag : K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';' case PLPGSQL_GETDIAG_ERROR_DETAIL: case PLPGSQL_GETDIAG_ERROR_HINT: case PLPGSQL_GETDIAG_RETURNED_SQLSTATE: + case PLPGSQL_GETDIAG_COLUMN_NAME: + case PLPGSQL_GETDIAG_CONSTRAINT_NAME: + case PLPGSQL_GETDIAG_DATATYPE_NAME: case PLPGSQL_GETDIAG_MESSAGE_TEXT: + case PLPGSQL_GETDIAG_TABLE_NAME: + case PLPGSQL_GETDIAG_SCHEMA_NAME: if (!new->is_stacked) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -971,9 +986,24 @@ getdiag_item : K_PG_EXCEPTION_CONTEXT, "pg_exception_context")) $$ = PLPGSQL_GETDIAG_ERROR_CONTEXT; else if (tok_is_keyword(tok, &yylval, + K_COLUMN_NAME, "column_name")) + $$ = PLPGSQL_GETDIAG_COLUMN_NAME; + else if (tok_is_keyword(tok, &yylval, + K_CONSTRAINT_NAME, "constraint_name")) + $$ = PLPGSQL_GETDIAG_CONSTRAINT_NAME; + else if (tok_is_keyword(tok, &yylval, + K_PG_DATATYPE_NAME, "pg_datatype_name")) + $$ = PLPGSQL_GETDIAG_DATATYPE_NAME; + else if (tok_is_keyword(tok, &yylval, K_MESSAGE_TEXT, "message_text")) $$ = PLPGSQL_GETDIAG_MESSAGE_TEXT; else if (tok_is_keyword(tok, &yylval, + K_TABLE_NAME, "table_name")) + $$ = PLPGSQL_GETDIAG_TABLE_NAME; + else if (tok_is_keyword(tok, &yylval, + K_SCHEMA_NAME, "schema_name")) + $$ = PLPGSQL_GETDIAG_SCHEMA_NAME; + else if (tok_is_keyword(tok, &yylval, K_RETURNED_SQLSTATE, "returned_sqlstate")) $$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE; else @@ -2231,9 +2261,14 @@ unreserved_keyword : | K_ALIAS | K_ARRAY | K_BACKWARD + | K_COLUMN + | K_COLUMN_NAME | K_CONSTANT + | K_CONSTRAINT + | K_CONSTRAINT_NAME | K_CURRENT | K_CURSOR + | K_DATATYPE | K_DEBUG | K_DETAIL | K_DUMP @@ -2252,6 +2287,7 @@ unreserved_keyword : | K_NO | K_NOTICE | K_OPTION + | K_PG_DATATYPE_NAME | K_PG_EXCEPTION_CONTEXT | K_PG_EXCEPTION_DETAIL | K_PG_EXCEPTION_HINT @@ -2263,10 +2299,14 @@ unreserved_keyword : | K_REVERSE | K_ROW_COUNT | K_ROWTYPE + | K_SCHEMA + | K_SCHEMA_NAME | K_SCROLL | K_SLICE | K_SQLSTATE | K_STACKED + | K_TABLE + | K_TABLE_NAME | K_TYPE | K_USE_COLUMN | K_USE_VARIABLE @@ -3631,6 +3671,21 @@ read_raise_options(void) else if (tok_is_keyword(tok, &yylval, K_HINT, "hint")) opt->opt_type = PLPGSQL_RAISEOPTION_HINT; + else if (tok_is_keyword(tok, &yylval, + K_COLUMN, "column")) + opt->opt_type = PLPGSQL_RAISEOPTION_COLUMN; + else if (tok_is_keyword(tok, &yylval, + K_CONSTRAINT, "constraint")) + opt->opt_type = PLPGSQL_RAISEOPTION_CONSTRAINT; + else if (tok_is_keyword(tok, &yylval, + K_DATATYPE, "datatype")) + opt->opt_type = PLPGSQL_RAISEOPTION_DATATYPE; + else if (tok_is_keyword(tok, &yylval, + K_TABLE, "table")) + opt->opt_type = PLPGSQL_RAISEOPTION_TABLE; + else if (tok_is_keyword(tok, &yylval, + K_SCHEMA, "schema")) + opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA; else yyerror("unrecognized RAISE statement option"); diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c index 9b6f57e723f..84c51260d25 100644 --- a/src/pl/plpgsql/src/pl_scanner.c +++ b/src/pl/plpgsql/src/pl_scanner.c @@ -109,9 +109,14 @@ static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD) PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD) PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD) + PG_KEYWORD("column", K_COLUMN, UNRESERVED_KEYWORD) + PG_KEYWORD("column_name", K_COLUMN_NAME, UNRESERVED_KEYWORD) 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("current", K_CURRENT, UNRESERVED_KEYWORD) PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD) + PG_KEYWORD("datatype", K_DATATYPE, UNRESERVED_KEYWORD) PG_KEYWORD("debug", K_DEBUG, UNRESERVED_KEYWORD) PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD) PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD) @@ -130,6 +135,7 @@ static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("no", K_NO, UNRESERVED_KEYWORD) PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD) PG_KEYWORD("option", K_OPTION, UNRESERVED_KEYWORD) + PG_KEYWORD("pg_datatype_name", K_PG_DATATYPE_NAME, UNRESERVED_KEYWORD) PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT, UNRESERVED_KEYWORD) PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL, UNRESERVED_KEYWORD) PG_KEYWORD("pg_exception_hint", K_PG_EXCEPTION_HINT, UNRESERVED_KEYWORD) @@ -141,10 +147,14 @@ static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("reverse", K_REVERSE, UNRESERVED_KEYWORD) PG_KEYWORD("row_count", K_ROW_COUNT, UNRESERVED_KEYWORD) PG_KEYWORD("rowtype", K_ROWTYPE, UNRESERVED_KEYWORD) + PG_KEYWORD("schema", K_SCHEMA, UNRESERVED_KEYWORD) + PG_KEYWORD("schema_name", K_SCHEMA_NAME, UNRESERVED_KEYWORD) PG_KEYWORD("scroll", K_SCROLL, UNRESERVED_KEYWORD) PG_KEYWORD("slice", K_SLICE, UNRESERVED_KEYWORD) PG_KEYWORD("sqlstate", K_SQLSTATE, UNRESERVED_KEYWORD) PG_KEYWORD("stacked", K_STACKED, UNRESERVED_KEYWORD) + PG_KEYWORD("table", K_TABLE, UNRESERVED_KEYWORD) + PG_KEYWORD("table_name", K_TABLE_NAME, UNRESERVED_KEYWORD) PG_KEYWORD("type", K_TYPE, UNRESERVED_KEYWORD) PG_KEYWORD("use_column", K_USE_COLUMN, UNRESERVED_KEYWORD) PG_KEYWORD("use_variable", K_USE_VARIABLE, UNRESERVED_KEYWORD) diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 5cc44a0e1c6..cdf39929e0f 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -128,7 +128,12 @@ enum PLPGSQL_GETDIAG_ERROR_DETAIL, PLPGSQL_GETDIAG_ERROR_HINT, PLPGSQL_GETDIAG_RETURNED_SQLSTATE, - PLPGSQL_GETDIAG_MESSAGE_TEXT + PLPGSQL_GETDIAG_COLUMN_NAME, + PLPGSQL_GETDIAG_CONSTRAINT_NAME, + PLPGSQL_GETDIAG_DATATYPE_NAME, + PLPGSQL_GETDIAG_MESSAGE_TEXT, + PLPGSQL_GETDIAG_TABLE_NAME, + PLPGSQL_GETDIAG_SCHEMA_NAME }; /* -------- @@ -140,7 +145,12 @@ enum PLPGSQL_RAISEOPTION_ERRCODE, PLPGSQL_RAISEOPTION_MESSAGE, PLPGSQL_RAISEOPTION_DETAIL, - PLPGSQL_RAISEOPTION_HINT + PLPGSQL_RAISEOPTION_HINT, + PLPGSQL_RAISEOPTION_COLUMN, + PLPGSQL_RAISEOPTION_CONSTRAINT, + PLPGSQL_RAISEOPTION_DATATYPE, + PLPGSQL_RAISEOPTION_TABLE, + PLPGSQL_RAISEOPTION_SCHEMA }; /* -------- diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index fdd8c6b466e..b413267b1b3 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -3974,6 +3974,40 @@ select raise_test(); NOTICE: 22012 ERROR: substitute message drop function raise_test(); +-- test passing column_name, constraint_name, datatype_name, table_name +-- and schema_name error fields +create or replace function stacked_diagnostics_test() returns void as $$ +declare _column_name text; + _constraint_name text; + _datatype_name text; + _table_name text; + _schema_name text; +begin + raise exception using + column = '>>some column name<<', + constraint = '>>some constraint name<<', + datatype = '>>some datatype name<<', + table = '>>some table name<<', + schema = '>>some schema name<<'; +exception when others then + get stacked diagnostics + _column_name = column_name, + _constraint_name = constraint_name, + _datatype_name = pg_datatype_name, + _table_name = table_name, + _schema_name = schema_name; + raise notice 'column %, constraint %, type %, table %, schema %', + _column_name, _constraint_name, _datatype_name, _table_name, _schema_name; +end; +$$ language plpgsql; +select stacked_diagnostics_test(); +NOTICE: column >>some column name<<, constraint >>some constraint name<<, type >>some datatype name<<, table >>some table name<<, schema >>some schema name<< + stacked_diagnostics_test +-------------------------- + +(1 row) + +drop function stacked_diagnostics_test(); -- test CASE statement create or replace function case_test(bigint) returns text as $$ declare a int = 10; diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 017bd0b63f1..9ef9deab2e4 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -3262,6 +3262,38 @@ select raise_test(); drop function raise_test(); +-- test passing column_name, constraint_name, datatype_name, table_name +-- and schema_name error fields + +create or replace function stacked_diagnostics_test() returns void as $$ +declare _column_name text; + _constraint_name text; + _datatype_name text; + _table_name text; + _schema_name text; +begin + raise exception using + column = '>>some column name<<', + constraint = '>>some constraint name<<', + datatype = '>>some datatype name<<', + table = '>>some table name<<', + schema = '>>some schema name<<'; +exception when others then + get stacked diagnostics + _column_name = column_name, + _constraint_name = constraint_name, + _datatype_name = pg_datatype_name, + _table_name = table_name, + _schema_name = schema_name; + raise notice 'column %, constraint %, type %, table %, schema %', + _column_name, _constraint_name, _datatype_name, _table_name, _schema_name; +end; +$$ language plpgsql; + +select stacked_diagnostics_test(); + +drop function stacked_diagnostics_test(); + -- test CASE statement create or replace function case_test(bigint) returns text as $$ |