Improve plpgsql's RAISE command. It is now possible to attach DETAIL and
HINT fields to a user-thrown error message, and to specify the SQLSTATE error code to use. The syntax has also been tweaked so that the Oracle-compatible case "RAISE exception_name" works (though you won't get a very nice error message if you just write that much). Lastly, support the Oracle-compatible syntax "RAISE" with no parameters to re-throw the current error from within an EXCEPTION block. In passing, allow the syntax SQLSTATE 'nnnnn' within EXCEPTION lists, so that there is a way to trap errors with custom SQLSTATE codes. Pavel Stehule and Tom Lane
This commit is contained in:
parent
72e2db86b9
commit
4107478d37
@ -1,4 +1,4 @@
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.128 2008/05/03 00:11:36 tgl Exp $ -->
|
||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.129 2008/05/13 22:10:29 tgl Exp $ -->
|
||||
|
||||
<chapter id="plpgsql">
|
||||
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
|
||||
@ -2133,7 +2133,12 @@ END;
|
||||
condition name <literal>OTHERS</> matches every error type except
|
||||
<literal>QUERY_CANCELED</>. (It is possible, but often unwise,
|
||||
to trap <literal>QUERY_CANCELED</> by name.) Condition names are
|
||||
not case-sensitive.
|
||||
not case-sensitive. Also, an error condition can be specified
|
||||
by <literal>SQLSTATE</> code; for example these are equivalent:
|
||||
<programlisting>
|
||||
WHEN division_by_zero THEN ...
|
||||
WHEN SQLSTATE '22012' THEN ...
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -2750,13 +2755,19 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
|
||||
raise errors.
|
||||
|
||||
<synopsis>
|
||||
RAISE <replaceable class="parameter">level</replaceable> '<replaceable class="parameter">format</replaceable>' <optional>, <replaceable class="parameter">expression</replaceable> <optional>, ...</optional></optional>;
|
||||
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>
|
||||
|
||||
Possible levels are <literal>DEBUG</literal>,
|
||||
The <replaceable class="parameter">level</replaceable> option specifies
|
||||
the error severity. Allowed levels are <literal>DEBUG</literal>,
|
||||
<literal>LOG</literal>, <literal>INFO</literal>,
|
||||
<literal>NOTICE</literal>, <literal>WARNING</literal>,
|
||||
and <literal>EXCEPTION</literal>.
|
||||
and <literal>EXCEPTION</literal>, with <literal>EXCEPTION</literal>
|
||||
being the default.
|
||||
<literal>EXCEPTION</literal> raises an error (which normally aborts the
|
||||
current transaction); the other levels only generate messages of different
|
||||
priority levels.
|
||||
@ -2769,19 +2780,17 @@ RAISE <replaceable class="parameter">level</replaceable> '<replaceable class="pa
|
||||
</para>
|
||||
|
||||
<para>
|
||||
After <replaceable class="parameter">level</replaceable> if any,
|
||||
you can write a <replaceable class="parameter">format</replaceable>
|
||||
(which must be a simple string literal, not an expression). The
|
||||
format string specifies the error message text to be reported.
|
||||
The format string can be followed
|
||||
by optional argument expressions to be inserted into the message.
|
||||
Inside the format string, <literal>%</literal> is replaced by the
|
||||
next optional argument's string representation. Write
|
||||
string representation of the next optional argument's value. Write
|
||||
<literal>%%</literal> to emit a literal <literal>%</literal>.
|
||||
Arguments can be simple variables or expressions,
|
||||
but the format must be a simple string literal.
|
||||
</para>
|
||||
|
||||
<!--
|
||||
This example should work, but does not:
|
||||
RAISE NOTICE 'Id number ' || key || ' not found!';
|
||||
Put it back when we allow non-string-literal formats.
|
||||
-->
|
||||
|
||||
<para>
|
||||
In this example, the value of <literal>v_job_id</> will replace the
|
||||
<literal>%</literal> in the string:
|
||||
@ -2791,19 +2800,90 @@ RAISE NOTICE 'Calling cs_create_job(%)', v_job_id;
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This example will abort the transaction with the given error message:
|
||||
You can attach additional information to the error report by writing
|
||||
<literal>USING</> followed by <replaceable
|
||||
class="parameter">option</replaceable> = <replaceable
|
||||
class="parameter">expression</replaceable> items. The allowed
|
||||
<replaceable class="parameter">option</replaceable> keywords are
|
||||
<literal>MESSAGE</>, <literal>DETAIL</>, <literal>HINT</>, and
|
||||
<literal>ERRCODE</>, while each <replaceable
|
||||
class="parameter">expression</replaceable> can be any string-valued
|
||||
expression.
|
||||
<literal>MESSAGE</> sets the error message text (this option can't
|
||||
be used in the form of <command>RAISE</> that includes a format
|
||||
string before <literal>USING</>).
|
||||
<literal>DETAIL</> supplies an error detail message, while
|
||||
<literal>HINT</> supplies a hint message.
|
||||
<literal>ERRCODE</> specifies the error code (SQLSTATE) to report,
|
||||
either by condition name as shown in <xref linkend="errcodes-appendix">,
|
||||
or directly as a five-character SQLSTATE code.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This example will abort the transaction with the given error message
|
||||
and hint:
|
||||
<programlisting>
|
||||
RAISE EXCEPTION 'Nonexistent ID --> %', user_id;
|
||||
RAISE EXCEPTION 'Nonexistent ID --> %', user_id USING HINT = 'Please check your user id';
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
These two examples show equivalent ways of setting the SQLSTATE:
|
||||
<programlisting>
|
||||
RAISE 'Duplicate user ID: %', user_id USING ERRCODE = 'unique_violation';
|
||||
RAISE 'Duplicate user ID: %', user_id USING ERRCODE = '23505';
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There is a second <command>RAISE</> syntax in which the main argument
|
||||
is the condition name or SQLSTATE to be reported, for example:
|
||||
<programlisting>
|
||||
RAISE division_by_zero;
|
||||
RAISE SQLSTATE '22012';
|
||||
</programlisting>
|
||||
In this syntax, <literal>USING</> can be used to supply a custom
|
||||
error message, detail, or hint. Another way to do the earlier
|
||||
example is
|
||||
<programlisting>
|
||||
RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Still another variant is to write <literal>RAISE USING</> or <literal>RAISE
|
||||
<replaceable class="parameter">level</replaceable> USING</> and put
|
||||
everything else into the <literal>USING</> list.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The last variant of <command>RAISE</> has no parameters at all.
|
||||
This form can only be used inside a <literal>BEGIN</> block's
|
||||
<literal>EXCEPTION</> clause;
|
||||
it causes the error currently being handled to be re-thrown to the
|
||||
next enclosing block.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If no condition name nor SQLSTATE is specified in a
|
||||
<command>RAISE EXCEPTION</command> command, the default is to use
|
||||
<literal>RAISE_EXCEPTION</> (<literal>P0001</>). If no message
|
||||
text is specified, the default is to use the condition name or
|
||||
SQLSTATE as message text.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
<command>RAISE EXCEPTION</command> presently always generates
|
||||
the same <varname>SQLSTATE</varname> code, <literal>P0001</>, no matter what message
|
||||
it is invoked with. It is possible to trap this exception with
|
||||
<literal>EXCEPTION ... WHEN RAISE_EXCEPTION THEN ...</> but there
|
||||
is no way to tell one <command>RAISE</> from another.
|
||||
When specifying an error code by SQLSTATE code, you are not
|
||||
limited to the predefined error codes, but can select any
|
||||
error code consisting of five digits and/or upper-case ASCII
|
||||
letters, other than <literal>00000</>. It is recommended that
|
||||
you avoid throwing error codes that end in three zeroes, because
|
||||
these are category codes and can only be trapped by trapping
|
||||
the whole category.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="plpgsql-trigger">
|
||||
@ -4307,7 +4387,9 @@ $$ LANGUAGE plpgsql;
|
||||
<callout arearefs="co.plpgsql-porting-raise">
|
||||
<para>
|
||||
The syntax of <literal>RAISE</> is considerably different from
|
||||
Oracle's similar statement.
|
||||
Oracle's statement, although the basic case <literal>RAISE</>
|
||||
<replaceable class="parameter">exception_name</replaceable> works
|
||||
similarly.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="co.plpgsql-porting-exception">
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.111 2008/05/03 00:11:36 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.112 2008/05/13 22:10:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -52,6 +52,7 @@ static void check_labels(const char *start_label,
|
||||
const char *end_label);
|
||||
static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
|
||||
int until, const char *expected);
|
||||
static List *read_raise_options(void);
|
||||
|
||||
%}
|
||||
|
||||
@ -138,11 +139,7 @@ static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
|
||||
%type <list> proc_exceptions
|
||||
%type <exception_block> exception_sect
|
||||
%type <exception> proc_exception
|
||||
%type <condition> proc_conditions
|
||||
|
||||
|
||||
%type <ival> raise_level
|
||||
%type <str> raise_msg
|
||||
%type <condition> proc_conditions proc_condition
|
||||
|
||||
%type <list> getdiag_list
|
||||
%type <diagitem> getdiag_list_item
|
||||
@ -164,7 +161,6 @@ static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
|
||||
%token K_CONSTANT
|
||||
%token K_CONTINUE
|
||||
%token K_CURSOR
|
||||
%token K_DEBUG
|
||||
%token K_DECLARE
|
||||
%token K_DEFAULT
|
||||
%token K_DIAGNOSTICS
|
||||
@ -181,16 +177,13 @@ static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
|
||||
%token K_GET
|
||||
%token K_IF
|
||||
%token K_IN
|
||||
%token K_INFO
|
||||
%token K_INSERT
|
||||
%token K_INTO
|
||||
%token K_IS
|
||||
%token K_LOG
|
||||
%token K_LOOP
|
||||
%token K_MOVE
|
||||
%token K_NOSCROLL
|
||||
%token K_NOT
|
||||
%token K_NOTICE
|
||||
%token K_NULL
|
||||
%token K_OPEN
|
||||
%token K_OR
|
||||
@ -207,7 +200,6 @@ static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
|
||||
%token K_TO
|
||||
%token K_TYPE
|
||||
%token K_USING
|
||||
%token K_WARNING
|
||||
%token K_WHEN
|
||||
%token K_WHILE
|
||||
|
||||
@ -1246,7 +1238,7 @@ stmt_return : K_RETURN lno
|
||||
}
|
||||
;
|
||||
|
||||
stmt_raise : K_RAISE lno raise_level raise_msg
|
||||
stmt_raise : K_RAISE lno
|
||||
{
|
||||
PLpgSQL_stmt_raise *new;
|
||||
int tok;
|
||||
@ -1255,69 +1247,133 @@ stmt_raise : K_RAISE lno raise_level raise_msg
|
||||
|
||||
new->cmd_type = PLPGSQL_STMT_RAISE;
|
||||
new->lineno = $2;
|
||||
new->elog_level = $3;
|
||||
new->message = $4;
|
||||
new->elog_level = ERROR; /* default */
|
||||
new->condname = NULL;
|
||||
new->message = NULL;
|
||||
new->params = NIL;
|
||||
new->options = NIL;
|
||||
|
||||
tok = yylex();
|
||||
if (tok == 0)
|
||||
yyerror("unexpected end of function definition");
|
||||
|
||||
/*
|
||||
* We expect either a semi-colon, which
|
||||
* indicates no parameters, or a comma that
|
||||
* begins the list of parameter expressions
|
||||
* We could have just RAISE, meaning to re-throw
|
||||
* the current error.
|
||||
*/
|
||||
if (tok != ',' && tok != ';')
|
||||
yyerror("syntax error");
|
||||
|
||||
if (tok == ',')
|
||||
if (tok != ';')
|
||||
{
|
||||
do
|
||||
/*
|
||||
* First is an optional elog severity level.
|
||||
* Most of these are not plpgsql keywords,
|
||||
* so we rely on examining yytext.
|
||||
*/
|
||||
if (pg_strcasecmp(yytext, "exception") == 0)
|
||||
{
|
||||
PLpgSQL_expr *expr;
|
||||
new->elog_level = ERROR;
|
||||
tok = yylex();
|
||||
}
|
||||
else if (pg_strcasecmp(yytext, "warning") == 0)
|
||||
{
|
||||
new->elog_level = WARNING;
|
||||
tok = yylex();
|
||||
}
|
||||
else if (pg_strcasecmp(yytext, "notice") == 0)
|
||||
{
|
||||
new->elog_level = NOTICE;
|
||||
tok = yylex();
|
||||
}
|
||||
else if (pg_strcasecmp(yytext, "info") == 0)
|
||||
{
|
||||
new->elog_level = INFO;
|
||||
tok = yylex();
|
||||
}
|
||||
else if (pg_strcasecmp(yytext, "log") == 0)
|
||||
{
|
||||
new->elog_level = LOG;
|
||||
tok = yylex();
|
||||
}
|
||||
else if (pg_strcasecmp(yytext, "debug") == 0)
|
||||
{
|
||||
new->elog_level = DEBUG1;
|
||||
tok = yylex();
|
||||
}
|
||||
if (tok == 0)
|
||||
yyerror("unexpected end of function definition");
|
||||
|
||||
expr = read_sql_expression2(',', ';',
|
||||
", or ;",
|
||||
&tok);
|
||||
new->params = lappend(new->params, expr);
|
||||
} while (tok == ',');
|
||||
/*
|
||||
* Next we can have a condition name, or
|
||||
* equivalently SQLSTATE 'xxxxx', or a string
|
||||
* literal that is the old-style message format,
|
||||
* or USING to start the option list immediately.
|
||||
*/
|
||||
if (tok == T_STRING)
|
||||
{
|
||||
/* old style message and parameters */
|
||||
new->message = plpgsql_get_string_value();
|
||||
/*
|
||||
* We expect either a semi-colon, which
|
||||
* indicates no parameters, or a comma that
|
||||
* begins the list of parameter expressions,
|
||||
* 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, &tok);
|
||||
new->params = lappend(new->params, expr);
|
||||
}
|
||||
}
|
||||
else if (tok != K_USING)
|
||||
{
|
||||
/* must be condition name or SQLSTATE */
|
||||
if (pg_strcasecmp(yytext, "sqlstate") == 0)
|
||||
{
|
||||
/* next token should be a string literal */
|
||||
char *sqlstatestr;
|
||||
|
||||
if (yylex() != T_STRING)
|
||||
yyerror("syntax error");
|
||||
sqlstatestr = plpgsql_get_string_value();
|
||||
|
||||
if (strlen(sqlstatestr) != 5)
|
||||
yyerror("invalid SQLSTATE code");
|
||||
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
|
||||
yyerror("invalid SQLSTATE code");
|
||||
new->condname = sqlstatestr;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *cname;
|
||||
|
||||
if (tok != T_WORD)
|
||||
yyerror("syntax error");
|
||||
plpgsql_convert_ident(yytext, &cname, 1);
|
||||
plpgsql_recognize_err_condition(cname,
|
||||
false);
|
||||
new->condname = cname;
|
||||
}
|
||||
tok = yylex();
|
||||
if (tok != ';' && tok != K_USING)
|
||||
yyerror("syntax error");
|
||||
}
|
||||
|
||||
if (tok == K_USING)
|
||||
new->options = read_raise_options();
|
||||
}
|
||||
|
||||
$$ = (PLpgSQL_stmt *)new;
|
||||
}
|
||||
;
|
||||
|
||||
raise_msg : T_STRING
|
||||
{
|
||||
$$ = plpgsql_get_string_value();
|
||||
}
|
||||
;
|
||||
|
||||
raise_level : K_EXCEPTION
|
||||
{
|
||||
$$ = ERROR;
|
||||
}
|
||||
| K_WARNING
|
||||
{
|
||||
$$ = WARNING;
|
||||
}
|
||||
| K_NOTICE
|
||||
{
|
||||
$$ = NOTICE;
|
||||
}
|
||||
| K_INFO
|
||||
{
|
||||
$$ = INFO;
|
||||
}
|
||||
| K_LOG
|
||||
{
|
||||
$$ = LOG;
|
||||
}
|
||||
| K_DEBUG
|
||||
{
|
||||
$$ = DEBUG1;
|
||||
}
|
||||
;
|
||||
|
||||
loop_body : proc_sect K_END K_LOOP opt_label ';'
|
||||
{
|
||||
$$.stmts = $1;
|
||||
@ -1592,20 +1648,61 @@ proc_exception : K_WHEN lno proc_conditions K_THEN proc_sect
|
||||
}
|
||||
;
|
||||
|
||||
proc_conditions : proc_conditions K_OR opt_lblname
|
||||
proc_conditions : proc_conditions K_OR proc_condition
|
||||
{
|
||||
PLpgSQL_condition *old;
|
||||
|
||||
for (old = $1; old->next != NULL; old = old->next)
|
||||
/* skip */ ;
|
||||
old->next = plpgsql_parse_err_condition($3);
|
||||
|
||||
old->next = $3;
|
||||
$$ = $1;
|
||||
}
|
||||
| opt_lblname
|
||||
| proc_condition
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
proc_condition : opt_lblname
|
||||
{
|
||||
$$ = plpgsql_parse_err_condition($1);
|
||||
}
|
||||
| T_SCALAR
|
||||
{
|
||||
/*
|
||||
* Because we know the special sqlstate variable
|
||||
* is at the top of the namestack (see the
|
||||
* exception_sect production), the SQLSTATE
|
||||
* keyword will always lex as T_SCALAR. This
|
||||
* might not be true in other parsing contexts!
|
||||
*/
|
||||
PLpgSQL_condition *new;
|
||||
char *sqlstatestr;
|
||||
|
||||
if (pg_strcasecmp(yytext, "sqlstate") != 0)
|
||||
yyerror("syntax error");
|
||||
|
||||
/* next token should be a string literal */
|
||||
if (yylex() != T_STRING)
|
||||
yyerror("syntax error");
|
||||
sqlstatestr = plpgsql_get_string_value();
|
||||
|
||||
if (strlen(sqlstatestr) != 5)
|
||||
yyerror("invalid SQLSTATE code");
|
||||
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
|
||||
yyerror("invalid SQLSTATE code");
|
||||
|
||||
new = palloc(sizeof(PLpgSQL_condition));
|
||||
new->sqlerrstate = MAKE_SQLSTATE(sqlstatestr[0],
|
||||
sqlstatestr[1],
|
||||
sqlstatestr[2],
|
||||
sqlstatestr[3],
|
||||
sqlstatestr[4]);
|
||||
new->condname = sqlstatestr;
|
||||
new->next = NULL;
|
||||
|
||||
$$ = new;
|
||||
}
|
||||
;
|
||||
|
||||
expr_until_semi :
|
||||
@ -2658,6 +2755,55 @@ read_cursor_args(PLpgSQL_var *cursor, int until, const char *expected)
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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");
|
||||
|
||||
opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
|
||||
|
||||
if (pg_strcasecmp(yytext, "errcode") == 0)
|
||||
opt->opt_type = PLPGSQL_RAISEOPTION_ERRCODE;
|
||||
else if (pg_strcasecmp(yytext, "message") == 0)
|
||||
opt->opt_type = PLPGSQL_RAISEOPTION_MESSAGE;
|
||||
else if (pg_strcasecmp(yytext, "detail") == 0)
|
||||
opt->opt_type = PLPGSQL_RAISEOPTION_DETAIL;
|
||||
else if (pg_strcasecmp(yytext, "hint") == 0)
|
||||
opt->opt_type = PLPGSQL_RAISEOPTION_HINT;
|
||||
else
|
||||
{
|
||||
plpgsql_error_lineno = plpgsql_scanner_lineno();
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("unrecognized RAISE statement option \"%s\"",
|
||||
yytext)));
|
||||
}
|
||||
|
||||
if (yylex() != K_ASSIGN)
|
||||
yyerror("syntax error, expected \"=\"");
|
||||
|
||||
opt->expr = read_sql_expression2(',', ';', ", or ;", &tok);
|
||||
|
||||
result = lappend(result, opt);
|
||||
|
||||
if (tok == ';')
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Needed to avoid conflict between different prefix settings: */
|
||||
#undef yylex
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.125 2008/05/12 00:00:54 alvherre Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.126 2008/05/13 22:10:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1749,6 +1749,42 @@ build_datatype(HeapTuple typeTup, int32 typmod)
|
||||
return typ;
|
||||
}
|
||||
|
||||
/*
|
||||
* plpgsql_recognize_err_condition
|
||||
* Check condition name and translate it to SQLSTATE.
|
||||
*
|
||||
* Note: there are some cases where the same condition name has multiple
|
||||
* entries in the table. We arbitrarily return the first match.
|
||||
*/
|
||||
int
|
||||
plpgsql_recognize_err_condition(const char *condname, bool allow_sqlstate)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (allow_sqlstate)
|
||||
{
|
||||
if (strlen(condname) == 5 &&
|
||||
strspn(condname, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
|
||||
return MAKE_SQLSTATE(condname[0],
|
||||
condname[1],
|
||||
condname[2],
|
||||
condname[3],
|
||||
condname[4]);
|
||||
}
|
||||
|
||||
for (i = 0; exception_label_map[i].label != NULL; i++)
|
||||
{
|
||||
if (strcmp(condname, exception_label_map[i].label) == 0)
|
||||
return exception_label_map[i].sqlerrstate;
|
||||
}
|
||||
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("unrecognized exception condition \"%s\"",
|
||||
condname)));
|
||||
return 0; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
/*
|
||||
* plpgsql_parse_err_condition
|
||||
* Generate PLpgSQL_condition entry(s) for an exception condition name
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.213 2008/05/12 20:02:02 alvherre Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.214 2008/05/13 22:10:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -316,13 +316,17 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
|
||||
estate.err_text = NULL;
|
||||
|
||||
/*
|
||||
* Provide a more helpful message if a CONTINUE has been used outside
|
||||
* a loop.
|
||||
* Provide a more helpful message if a CONTINUE or RAISE has been used
|
||||
* outside the context it can work in.
|
||||
*/
|
||||
if (rc == PLPGSQL_RC_CONTINUE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("CONTINUE cannot be used outside a loop")));
|
||||
else if (rc == PLPGSQL_RC_RERAISE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("RAISE without parameters cannot be used outside an exception handler")));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
|
||||
@ -662,13 +666,17 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
|
||||
estate.err_text = NULL;
|
||||
|
||||
/*
|
||||
* Provide a more helpful message if a CONTINUE has been used outside
|
||||
* a loop.
|
||||
* Provide a more helpful message if a CONTINUE or RAISE has been used
|
||||
* outside the context it can work in.
|
||||
*/
|
||||
if (rc == PLPGSQL_RC_CONTINUE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("CONTINUE cannot be used outside a loop")));
|
||||
else if (rc == PLPGSQL_RC_RERAISE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("RAISE without parameters cannot be used outside an exception handler")));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
|
||||
@ -1109,6 +1117,11 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
|
||||
free_var(errm_var);
|
||||
errm_var->value = (Datum) 0;
|
||||
errm_var->isnull = true;
|
||||
|
||||
/* re-throw error if requested by handler */
|
||||
if (rc == PLPGSQL_RC_RERAISE)
|
||||
ReThrowError(edata);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1139,8 +1152,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
|
||||
switch (rc)
|
||||
{
|
||||
case PLPGSQL_RC_OK:
|
||||
case PLPGSQL_RC_CONTINUE:
|
||||
case PLPGSQL_RC_RETURN:
|
||||
case PLPGSQL_RC_CONTINUE:
|
||||
case PLPGSQL_RC_RERAISE:
|
||||
return rc;
|
||||
|
||||
case PLPGSQL_RC_EXIT:
|
||||
@ -1469,7 +1483,8 @@ exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt)
|
||||
break;
|
||||
|
||||
case PLPGSQL_RC_RETURN:
|
||||
return PLPGSQL_RC_RETURN;
|
||||
case PLPGSQL_RC_RERAISE:
|
||||
return rc;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unrecognized rc: %d", rc);
|
||||
@ -1532,7 +1547,8 @@ exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
|
||||
break;
|
||||
|
||||
case PLPGSQL_RC_RETURN:
|
||||
return PLPGSQL_RC_RETURN;
|
||||
case PLPGSQL_RC_RERAISE:
|
||||
return rc;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unrecognized rc: %d", rc);
|
||||
@ -1650,8 +1666,9 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
|
||||
*/
|
||||
rc = exec_stmts(estate, stmt->body);
|
||||
|
||||
if (rc == PLPGSQL_RC_RETURN)
|
||||
break; /* return from function */
|
||||
if (rc == PLPGSQL_RC_RETURN ||
|
||||
rc == PLPGSQL_RC_RERAISE)
|
||||
break; /* break out of the loop */
|
||||
else if (rc == PLPGSQL_RC_EXIT)
|
||||
{
|
||||
if (estate->exitlabel == NULL)
|
||||
@ -2267,64 +2284,163 @@ exec_init_tuple_store(PLpgSQL_execstate *estate)
|
||||
static int
|
||||
exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
|
||||
{
|
||||
char *cp;
|
||||
PLpgSQL_dstring ds;
|
||||
ListCell *current_param;
|
||||
int err_code = 0;
|
||||
char *condname = NULL;
|
||||
char *err_message = NULL;
|
||||
char *err_detail = NULL;
|
||||
char *err_hint = NULL;
|
||||
ListCell *lc;
|
||||
|
||||
plpgsql_dstring_init(&ds);
|
||||
current_param = list_head(stmt->params);
|
||||
/* RAISE with no parameters: re-throw current exception */
|
||||
if (stmt->condname == NULL && stmt->message == NULL &&
|
||||
stmt->options == NIL)
|
||||
return PLPGSQL_RC_RERAISE;
|
||||
|
||||
for (cp = stmt->message; *cp; cp++)
|
||||
if (stmt->condname)
|
||||
{
|
||||
/*
|
||||
* Occurrences of a single % are replaced by the next parameter's
|
||||
* external representation. Double %'s are converted to one %.
|
||||
*/
|
||||
if (cp[0] == '%')
|
||||
{
|
||||
Oid paramtypeid;
|
||||
Datum paramvalue;
|
||||
bool paramisnull;
|
||||
char *extval;
|
||||
|
||||
if (cp[1] == '%')
|
||||
{
|
||||
plpgsql_dstring_append_char(&ds, cp[1]);
|
||||
cp++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_param == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("too few parameters specified for RAISE")));
|
||||
|
||||
paramvalue = exec_eval_expr(estate,
|
||||
(PLpgSQL_expr *) lfirst(current_param),
|
||||
¶misnull,
|
||||
¶mtypeid);
|
||||
|
||||
if (paramisnull)
|
||||
extval = "<NULL>";
|
||||
else
|
||||
extval = convert_value_to_string(paramvalue, paramtypeid);
|
||||
plpgsql_dstring_append(&ds, extval);
|
||||
current_param = lnext(current_param);
|
||||
exec_eval_cleanup(estate);
|
||||
continue;
|
||||
}
|
||||
|
||||
plpgsql_dstring_append_char(&ds, cp[0]);
|
||||
err_code = plpgsql_recognize_err_condition(stmt->condname, true);
|
||||
condname = pstrdup(stmt->condname);
|
||||
}
|
||||
|
||||
/*
|
||||
* If more parameters were specified than were required to process the
|
||||
* format string, throw an error
|
||||
*/
|
||||
if (current_param != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("too many parameters specified for RAISE")));
|
||||
if (stmt->message)
|
||||
{
|
||||
PLpgSQL_dstring ds;
|
||||
ListCell *current_param;
|
||||
char *cp;
|
||||
|
||||
plpgsql_dstring_init(&ds);
|
||||
current_param = list_head(stmt->params);
|
||||
|
||||
for (cp = stmt->message; *cp; cp++)
|
||||
{
|
||||
/*
|
||||
* Occurrences of a single % are replaced by the next parameter's
|
||||
* external representation. Double %'s are converted to one %.
|
||||
*/
|
||||
if (cp[0] == '%')
|
||||
{
|
||||
Oid paramtypeid;
|
||||
Datum paramvalue;
|
||||
bool paramisnull;
|
||||
char *extval;
|
||||
|
||||
if (cp[1] == '%')
|
||||
{
|
||||
plpgsql_dstring_append_char(&ds, cp[1]);
|
||||
cp++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_param == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("too few parameters specified for RAISE")));
|
||||
|
||||
paramvalue = exec_eval_expr(estate,
|
||||
(PLpgSQL_expr *) lfirst(current_param),
|
||||
¶misnull,
|
||||
¶mtypeid);
|
||||
|
||||
if (paramisnull)
|
||||
extval = "<NULL>";
|
||||
else
|
||||
extval = convert_value_to_string(paramvalue, paramtypeid);
|
||||
plpgsql_dstring_append(&ds, extval);
|
||||
current_param = lnext(current_param);
|
||||
exec_eval_cleanup(estate);
|
||||
}
|
||||
else
|
||||
plpgsql_dstring_append_char(&ds, cp[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* If more parameters were specified than were required to process the
|
||||
* format string, throw an error
|
||||
*/
|
||||
if (current_param != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("too many parameters specified for RAISE")));
|
||||
|
||||
err_message = plpgsql_dstring_get(&ds);
|
||||
/* No dstring_free here, the pfree(err_message) does it */
|
||||
}
|
||||
|
||||
foreach(lc, stmt->options)
|
||||
{
|
||||
PLpgSQL_raise_option *opt = (PLpgSQL_raise_option *) lfirst(lc);
|
||||
Datum optionvalue;
|
||||
bool optionisnull;
|
||||
Oid optiontypeid;
|
||||
char *extval;
|
||||
|
||||
optionvalue = exec_eval_expr(estate, opt->expr,
|
||||
&optionisnull,
|
||||
&optiontypeid);
|
||||
if (optionisnull)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
||||
errmsg("RAISE statement option cannot be NULL")));
|
||||
|
||||
extval = convert_value_to_string(optionvalue, optiontypeid);
|
||||
|
||||
switch (opt->opt_type)
|
||||
{
|
||||
case PLPGSQL_RAISEOPTION_ERRCODE:
|
||||
if (err_code)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("RAISE option already specified: %s",
|
||||
"ERRCODE")));
|
||||
err_code = plpgsql_recognize_err_condition(extval, true);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized raise option: %d", opt->opt_type);
|
||||
}
|
||||
|
||||
exec_eval_cleanup(estate);
|
||||
}
|
||||
|
||||
/* Default code if nothing specified */
|
||||
if (err_code == 0 && stmt->elog_level >= ERROR)
|
||||
err_code = ERRCODE_RAISE_EXCEPTION;
|
||||
|
||||
/* Default error message if nothing specified */
|
||||
if (err_message == NULL)
|
||||
{
|
||||
if (condname)
|
||||
{
|
||||
err_message = condname;
|
||||
condname = NULL;
|
||||
}
|
||||
else
|
||||
err_message = pstrdup(unpack_sql_state(err_code));
|
||||
}
|
||||
|
||||
/*
|
||||
* Throw the error (may or may not come back)
|
||||
@ -2332,12 +2448,21 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
|
||||
estate->err_text = raise_skip_msg; /* suppress traceback of raise */
|
||||
|
||||
ereport(stmt->elog_level,
|
||||
((stmt->elog_level >= ERROR) ? errcode(ERRCODE_RAISE_EXCEPTION) : 0,
|
||||
errmsg_internal("%s", plpgsql_dstring_get(&ds))));
|
||||
(err_code ? errcode(err_code) : 0,
|
||||
errmsg_internal("%s", err_message),
|
||||
(err_detail != NULL) ? errdetail(err_detail) : 0,
|
||||
(err_hint != NULL) ? errhint(err_hint) : 0));
|
||||
|
||||
estate->err_text = NULL; /* un-suppress... */
|
||||
|
||||
plpgsql_dstring_free(&ds);
|
||||
if (condname != NULL)
|
||||
pfree(condname);
|
||||
if (err_message != NULL)
|
||||
pfree(err_message);
|
||||
if (err_detail != NULL)
|
||||
pfree(err_detail);
|
||||
if (err_hint != NULL)
|
||||
pfree(err_hint);
|
||||
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.70 2008/05/03 00:11:36 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.71 2008/05/13 22:10:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1003,7 +1003,12 @@ dump_raise(PLpgSQL_stmt_raise *stmt)
|
||||
int i = 0;
|
||||
|
||||
dump_ind();
|
||||
printf("RAISE '%s'\n", stmt->message);
|
||||
printf("RAISE level=%d", stmt->elog_level);
|
||||
if (stmt->condname)
|
||||
printf(" condname='%s'", stmt->condname);
|
||||
if (stmt->message)
|
||||
printf(" message='%s'", stmt->message);
|
||||
printf("\n");
|
||||
dump_indent += 2;
|
||||
foreach(lc, stmt->params)
|
||||
{
|
||||
@ -1012,6 +1017,36 @@ dump_raise(PLpgSQL_stmt_raise *stmt)
|
||||
dump_expr((PLpgSQL_expr *) lfirst(lc));
|
||||
printf("\n");
|
||||
}
|
||||
if (stmt->options)
|
||||
{
|
||||
dump_ind();
|
||||
printf(" USING\n");
|
||||
dump_indent += 2;
|
||||
foreach(lc, stmt->options)
|
||||
{
|
||||
PLpgSQL_raise_option *opt = (PLpgSQL_raise_option *) lfirst(lc);
|
||||
|
||||
dump_ind();
|
||||
switch (opt->opt_type)
|
||||
{
|
||||
case PLPGSQL_RAISEOPTION_ERRCODE:
|
||||
printf(" ERRCODE = ");
|
||||
break;
|
||||
case PLPGSQL_RAISEOPTION_MESSAGE:
|
||||
printf(" MESSAGE = ");
|
||||
break;
|
||||
case PLPGSQL_RAISEOPTION_DETAIL:
|
||||
printf(" DETAIL = ");
|
||||
break;
|
||||
case PLPGSQL_RAISEOPTION_HINT:
|
||||
printf(" HINT = ");
|
||||
break;
|
||||
}
|
||||
dump_expr(opt->expr);
|
||||
printf("\n");
|
||||
}
|
||||
dump_indent -= 2;
|
||||
}
|
||||
dump_indent -= 2;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.98 2008/05/03 00:11:36 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.99 2008/05/13 22:10:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -106,7 +106,8 @@ enum
|
||||
PLPGSQL_RC_OK,
|
||||
PLPGSQL_RC_EXIT,
|
||||
PLPGSQL_RC_RETURN,
|
||||
PLPGSQL_RC_CONTINUE
|
||||
PLPGSQL_RC_CONTINUE,
|
||||
PLPGSQL_RC_RERAISE
|
||||
};
|
||||
|
||||
/* ----------
|
||||
@ -119,6 +120,18 @@ enum
|
||||
PLPGSQL_GETDIAG_RESULT_OID
|
||||
};
|
||||
|
||||
/* --------
|
||||
* RAISE statement options
|
||||
* --------
|
||||
*/
|
||||
enum
|
||||
{
|
||||
PLPGSQL_RAISEOPTION_ERRCODE,
|
||||
PLPGSQL_RAISEOPTION_MESSAGE,
|
||||
PLPGSQL_RAISEOPTION_DETAIL,
|
||||
PLPGSQL_RAISEOPTION_HINT
|
||||
};
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Node and structure definitions
|
||||
@ -539,10 +552,18 @@ typedef struct
|
||||
int cmd_type;
|
||||
int lineno;
|
||||
int elog_level;
|
||||
char *message;
|
||||
List *params; /* list of expressions */
|
||||
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 */
|
||||
List *options; /* list of PLpgSQL_raise_option */
|
||||
} PLpgSQL_stmt_raise;
|
||||
|
||||
typedef struct
|
||||
{ /* RAISE statement option */
|
||||
int opt_type;
|
||||
PLpgSQL_expr *expr;
|
||||
} PLpgSQL_raise_option;
|
||||
|
||||
|
||||
typedef struct
|
||||
{ /* Generic SQL statement to execute */
|
||||
@ -772,6 +793,8 @@ extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
|
||||
bool add2namespace);
|
||||
extern PLpgSQL_rec *plpgsql_build_record(const char *refname, int lineno,
|
||||
bool add2namespace);
|
||||
extern int plpgsql_recognize_err_condition(const char *condname,
|
||||
bool allow_sqlstate);
|
||||
extern PLpgSQL_condition *plpgsql_parse_err_condition(char *condname);
|
||||
extern void plpgsql_adddatum(PLpgSQL_datum *new);
|
||||
extern int plpgsql_add_initdatums(int **varnos);
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.62 2008/05/09 15:36:31 petere Exp $
|
||||
* $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.63 2008/05/13 22:10:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -120,7 +120,6 @@ close { return K_CLOSE; }
|
||||
constant { return K_CONSTANT; }
|
||||
continue { return K_CONTINUE; }
|
||||
cursor { return K_CURSOR; }
|
||||
debug { return K_DEBUG; }
|
||||
declare { return K_DECLARE; }
|
||||
default { return K_DEFAULT; }
|
||||
diagnostics { return K_DIAGNOSTICS; }
|
||||
@ -137,16 +136,13 @@ from { return K_FROM; }
|
||||
get { return K_GET; }
|
||||
if { return K_IF; }
|
||||
in { return K_IN; }
|
||||
info { return K_INFO; }
|
||||
insert { return K_INSERT; }
|
||||
into { return K_INTO; }
|
||||
is { return K_IS; }
|
||||
log { return K_LOG; }
|
||||
loop { return K_LOOP; }
|
||||
move { return K_MOVE; }
|
||||
no{space}+scroll { return K_NOSCROLL; }
|
||||
not { return K_NOT; }
|
||||
notice { return K_NOTICE; }
|
||||
null { return K_NULL; }
|
||||
open { return K_OPEN; }
|
||||
or { return K_OR; }
|
||||
@ -163,7 +159,6 @@ then { return K_THEN; }
|
||||
to { return K_TO; }
|
||||
type { return K_TYPE; }
|
||||
using { return K_USING; }
|
||||
warning { return K_WARNING; }
|
||||
when { return K_WHEN; }
|
||||
while { return K_WHILE; }
|
||||
|
||||
|
@ -3267,7 +3267,7 @@ end;
|
||||
$$ language plpgsql;
|
||||
ERROR: cursor FOR loop must use a bound cursor variable
|
||||
CONTEXT: compile of PL/pgSQL function "forc_bad" near line 4
|
||||
-- return query execute
|
||||
-- test RETURN QUERY EXECUTE
|
||||
create or replace function return_dquery()
|
||||
returns setof int as $$
|
||||
begin
|
||||
@ -3285,3 +3285,132 @@ select * from return_dquery();
|
||||
(4 rows)
|
||||
|
||||
drop function return_dquery();
|
||||
-- Tests for 8.4's new RAISE features
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise notice '% % %', 1, 2, 3
|
||||
using errcode = '55001', detail = 'some detail info', hint = 'some hint';
|
||||
raise '% % %', 1, 2, 3
|
||||
using errcode = 'division_by_zero', detail = 'some detail info';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
NOTICE: 1 2 3
|
||||
DETAIL: some detail info
|
||||
HINT: some hint
|
||||
ERROR: 1 2 3
|
||||
DETAIL: some detail info
|
||||
-- Since we can't actually see the thrown SQLSTATE in default psql output,
|
||||
-- test it like this; this also tests re-RAISE
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise 'check me'
|
||||
using errcode = 'division_by_zero', detail = 'some detail info';
|
||||
exception
|
||||
when others then
|
||||
raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
NOTICE: SQLSTATE: 22012 SQLERRM: check me
|
||||
ERROR: check me
|
||||
DETAIL: some detail info
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise 'check me'
|
||||
using errcode = '1234F', detail = 'some detail info';
|
||||
exception
|
||||
when others then
|
||||
raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
NOTICE: SQLSTATE: 1234F SQLERRM: check me
|
||||
ERROR: check me
|
||||
DETAIL: some detail info
|
||||
-- SQLSTATE specification in WHEN
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise 'check me'
|
||||
using errcode = '1234F', detail = 'some detail info';
|
||||
exception
|
||||
when sqlstate '1234F' then
|
||||
raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
NOTICE: SQLSTATE: 1234F SQLERRM: check me
|
||||
ERROR: check me
|
||||
DETAIL: some detail info
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise division_by_zero using detail = 'some detail info';
|
||||
exception
|
||||
when others then
|
||||
raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
NOTICE: SQLSTATE: 22012 SQLERRM: division_by_zero
|
||||
ERROR: division_by_zero
|
||||
DETAIL: some detail info
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise division_by_zero;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
ERROR: division_by_zero
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise sqlstate '1234F';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
ERROR: 1234F
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise division_by_zero using message = 'custom' || ' message';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
ERROR: custom message
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise using message = 'custom' || ' message', errcode = '22012';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
ERROR: custom message
|
||||
-- conflict on message
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise notice 'some message' using message = 'custom' || ' message', errcode = '22012';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
ERROR: RAISE option already specified: MESSAGE
|
||||
CONTEXT: PL/pgSQL function "raise_test" line 2 at RAISE
|
||||
-- conflict on errcode
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise division_by_zero using message = 'custom' || ' message', errcode = '22012';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
ERROR: RAISE option already specified: ERRCODE
|
||||
CONTEXT: PL/pgSQL function "raise_test" line 2 at RAISE
|
||||
-- nothing to re-RAISE
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
select raise_test();
|
||||
ERROR: RAISE without parameters cannot be used outside an exception handler
|
||||
CONTEXT: PL/pgSQL function "raise_test"
|
||||
drop function raise_test();
|
||||
|
@ -2670,7 +2670,7 @@ begin
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
-- return query execute
|
||||
-- test RETURN QUERY EXECUTE
|
||||
|
||||
create or replace function return_dquery()
|
||||
returns setof int as $$
|
||||
@ -2683,3 +2683,132 @@ $$ language plpgsql;
|
||||
select * from return_dquery();
|
||||
|
||||
drop function return_dquery();
|
||||
|
||||
-- Tests for 8.4's new RAISE features
|
||||
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise notice '% % %', 1, 2, 3
|
||||
using errcode = '55001', detail = 'some detail info', hint = 'some hint';
|
||||
raise '% % %', 1, 2, 3
|
||||
using errcode = 'division_by_zero', detail = 'some detail info';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
-- Since we can't actually see the thrown SQLSTATE in default psql output,
|
||||
-- test it like this; this also tests re-RAISE
|
||||
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise 'check me'
|
||||
using errcode = 'division_by_zero', detail = 'some detail info';
|
||||
exception
|
||||
when others then
|
||||
raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise 'check me'
|
||||
using errcode = '1234F', detail = 'some detail info';
|
||||
exception
|
||||
when others then
|
||||
raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
-- SQLSTATE specification in WHEN
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise 'check me'
|
||||
using errcode = '1234F', detail = 'some detail info';
|
||||
exception
|
||||
when sqlstate '1234F' then
|
||||
raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise division_by_zero using detail = 'some detail info';
|
||||
exception
|
||||
when others then
|
||||
raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise division_by_zero;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise sqlstate '1234F';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise division_by_zero using message = 'custom' || ' message';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise using message = 'custom' || ' message', errcode = '22012';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
-- conflict on message
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise notice 'some message' using message = 'custom' || ' message', errcode = '22012';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
-- conflict on errcode
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise division_by_zero using message = 'custom' || ' message', errcode = '22012';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
-- nothing to re-RAISE
|
||||
create or replace function raise_test() returns void as $$
|
||||
begin
|
||||
raise;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
select raise_test();
|
||||
|
||||
drop function raise_test();
|
||||
|
Loading…
Reference in New Issue
Block a user