Hello,
the name of exception's variable I use as exception's name. Attached patch
work, but miss documentations, regress, ..
>BTW, is there any value in a separate "EXCEPTION" type? ISTM that an
>exception is just a SQLSTATE, which is in turn just a string. A separate
>exception type does simplify the parsing of RAISE, but I wonder if it
>would be useful to be able to also allow specifying the SQLSTATE code as
>a string literal.
Definition new attributes for exception isn't problem: level, errmsg.
Parsing raise stmt will be little bit more complicete. But why now?
CREATE OR REPLACE FUNCTION foo() RETURNS void AS $$
DECLARE my_own_exception EXCEPTION;
BEGIN
RAISE EXCEPTION my_own_exception 'some text';
END; $$ LANGUAGE plpgsql;
pokus=# select foo();
ERROR: some text
DETAIL: User's exception sqlstate: U0001, name: my_own_exception
HINT: Unhandled user's exception, from RAISE stmt on line 3
pokus=#
CREATE OR REPLACE FUNCTION foo() RETURNS void AS $$
DECLARE div_by_zero_test EXCEPTION = '22012';
BEGIN
RAISE EXCEPTION div_by_zero_test 'some text';
EXCEPTION WHEN division_by_zero THEN
RAISE NOTICE 'foo text';
END; $$ LANGUAGE plpgsql;
pokus=# select foo();
NOTICE: foo text
foo
-----
(1 row)
CREATE OR REPLACE FUNCTION foo() RETURNS void AS $$
DECLARE uexcpt01 EXCEPTION;
BEGIN
RAISE EXCEPTION uexcpt01 'aaaa';
EXCEPTION WHEN uexcpt01 THEN
RAISE NOTICE 'hello';
END; $$ LANGUAGE plpgsql;
pokus=# select foo();
NOTICE: hello
The patch isn't in production state (no from me:), but maybe is usefull
for test. The moust important is posibility handling own exception without
parsing SQLERRMS, I think. Setting SQLSTATE is usefull for
interoperatibility between procedures and throwing system errors.
Regards
Pavel Stehule
diff -c -r --new-file src.00/gram.y src/gram.y
*** src.00/gram.y 2005-06-14 13:57:13.000000000 +0200
--- src/gram.y 2005-06-15 08:29:56.000000000 +0200
***************
*** 39,44 ****
--- 39,45 ----
#include "plpgsql.h"
#include "parser/parser.h"
+ #include "utils/elog.h"
static PLpgSQL_expr *read_sql_construct(int until,
int until2,
***************
*** 95,107 ****
PLpgSQL_exception_block *exception_block;
PLpgSQL_nsitem *nsitem;
PLpgSQL_diag_item *diagitem;
}
%type <declhdr> decl_sect
%type <varname> decl_varname
%type <str> decl_renname
! %type <ival> decl_const decl_notnull
! %type <expr> decl_defval decl_cursor_query
%type <dtype> decl_datatype
%type <row> decl_cursor_args
%type <list> decl_cursor_arglist
--- 96,109 ----
PLpgSQL_exception_block *exception_block;
PLpgSQL_nsitem *nsitem;
PLpgSQL_diag_item *diagitem;
+ PLpgSQL_uexception *uexception;
}
%type <declhdr> decl_sect
%type <varname> decl_varname
%type <str> decl_renname
! %type <ival> decl_const
! %type <expr> decl_cursor_query
%type <dtype> decl_datatype
%type <row> decl_cursor_args
%type <list> decl_cursor_arglist
***************
*** 121,126 ****
--- 123,129 ----
%type <str> opt_lblname opt_label
%type <str> opt_exitlabel
%type <str> execsql_start
+ %type <str> exception_name
%type <list> proc_sect proc_stmts stmt_else loop_body
%type <stmt> proc_stmt pl_block
***************
*** 144,149 ****
--- 147,153 ----
%type <ival> getdiag_kind getdiag_target
%type <ival> lno
+ %type <uexception> opt_uexception
/*
* Keyword tokens
***************
*** 212,217 ****
--- 216,222 ----
%token T_LABEL
%token T_WORD
%token T_ERROR
+ %token T_EXCEPTION
%token O_OPTION
%token O_DUMP
***************
*** 314,323 ****
{ $$ = NULL; }
;
! decl_statement : decl_varname decl_const decl_datatype decl_notnull
decl_defval
{
PLpgSQL_variable *var;
!
var =
plpgsql_build_variable($1.name, $1.lineno,
$3, true);
if ($2)
--- 319,385 ----
{ $$ = NULL; }
;
! decl_statement : decl_varname decl_const decl_datatype
{
PLpgSQL_variable *var;
! int notnull = 0;
! int tok;
! PLpgSQL_expr *default_val =
NULL;
! char *s = NULL;
!
! if ($3->ttype ==
PLPGSQL_TTYPE_EXCEPTION)
! {
! if ((tok = yylex()) != ';')
! {
! if (tok != K_ASSIGN &&
tok != K_DEFAULT)
! yyerror("Syntax
error.");
! else
! {
! if (yylex() !=
T_STRING)
!
ereport(ERROR,
!
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
!
errmsg("Value for exception type have to be string")));
! else
! {
! s =
plpgsql_get_string_value();
! if (strlen(s)
!= 5)
!
yyerror("Wrong format");
! if (yylex() !=
';')
!
yyerror("Syntax error.");
! }
! }
! }
! }
! else
! {
! if ((tok = yylex()) != ';')
! {
! if (tok == K_NOT)
! {
! if (yylex() ==
K_NULL)
! {
! notnull = 1;
! tok = yylex();
! }
! else
! yyerror("Syntax
error.");
! }
! }
! if (tok != ';')
! {
! if (tok == K_ASSIGN ||
tok == K_DEFAULT)
! {
!
plpgsql_ns_setlocal(false);
! default_val =
plpgsql_read_expression(';', ";");
!
plpgsql_ns_setlocal(true);
! }
! else
! yyerror("Syntax
error.");
! }
!
! }
!
!
var =
plpgsql_build_variable($1.name, $1.lineno,
$3, true);
if ($2)
***************
*** 329,352 ****
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("row or record variable cannot be CONSTANT")));
}
! if ($4)
{
if (var->dtype ==
PLPGSQL_DTYPE_VAR)
! ((PLpgSQL_var
*) var)->notnull = $4;
else
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("row or record variable cannot be NOT NULL")));
}
! if ($5 != NULL)
{
if (var->dtype ==
PLPGSQL_DTYPE_VAR)
! ((PLpgSQL_var
*) var)->default_val = $5;
else
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("default value for row or record variable is not supported")));
}
}
| decl_varname K_ALIAS K_FOR decl_aliasitem ';'
{
--- 391,424 ----
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("row or record variable cannot be CONSTANT")));
}
! if (notnull)
{
if (var->dtype ==
PLPGSQL_DTYPE_VAR)
! ((PLpgSQL_var
*) var)->notnull = notnull;
else
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("row or record variable cannot be NOT NULL")));
}
! if (default_val != NULL)
{
if (var->dtype ==
PLPGSQL_DTYPE_VAR)
! ((PLpgSQL_var
*) var)->default_val = default_val;
else
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("default value for row or record variable is not supported")));
+
}
+ if (s != NULL)
+ ((PLpgSQL_uexception *)
var)->sqlerrstate =
+
MAKE_SQLSTATE(s[0],s[1],s[2],s[3],s[4]);
+ else
+ ((PLpgSQL_uexception *)
var)->sqlerrstate =
+
plpgsql_newUsrExceptions();
+
+
+
}
| decl_varname K_ALIAS K_FOR decl_aliasitem ';'
{
***************
*** 530,554 ****
}
;
- decl_notnull :
- { $$ = 0; }
- | K_NOT K_NULL
- { $$ = 1; }
- ;
-
- decl_defval : ';'
- { $$ = NULL; }
- | decl_defkey
- {
- plpgsql_ns_setlocal(false);
- $$ =
plpgsql_read_expression(';', ";");
- plpgsql_ns_setlocal(true);
- }
- ;
-
- decl_defkey : K_ASSIGN
- | K_DEFAULT
- ;
proc_sect :
{
--- 602,607 ----
***************
*** 1157,1163 ****
}
;
! stmt_raise : K_RAISE lno raise_level raise_msg
{
PLpgSQL_stmt_raise
*new;
int tok;
--- 1210,1216 ----
}
;
! stmt_raise : K_RAISE lno raise_level opt_uexception raise_msg
{
PLpgSQL_stmt_raise
*new;
int tok;
***************
*** 1167,1174 ****
new->cmd_type =
PLPGSQL_STMT_RAISE;
new->lineno = $2;
new->elog_level = $3;
! new->message = $4;
! new->params = NIL;
tok = yylex();
--- 1220,1236 ----
new->cmd_type =
PLPGSQL_STMT_RAISE;
new->lineno = $2;
new->elog_level = $3;
! new->message = $5;
! new->params = NULL;
!
! if ($4 != NULL)
! {
! new->sqlerrstate =
$4->sqlerrstate;
! new->refname = $4->refname;
! }
! else
! new->sqlerrstate =
! (new->elog_level >=
ERROR) ? ERRCODE_RAISE_EXCEPTION : 0;
tok = yylex();
***************
*** 1200,1205 ****
--- 1262,1279 ----
}
;
+
+ opt_uexception : T_EXCEPTION
+ {
+ $$ = yylval.uexception;
+ }
+ | /* EMPTY */
+ {
+ $$ = NULL;
+ }
+ ;
+
+
raise_msg : T_STRING
{
$$ = plpgsql_get_string_value();
***************
*** 1551,1557 ****
}
;
! proc_conditions : proc_conditions K_OR opt_lblname
{
PLpgSQL_condition
*old;
--- 1625,1631 ----
}
;
! proc_conditions : proc_conditions K_OR exception_name
{
PLpgSQL_condition
*old;
***************
*** 1561,1567 ****
$$ = $1;
}
! | opt_lblname
{
$$ =
plpgsql_parse_err_condition($1);
}
--- 1635,1641 ----
$$ = $1;
}
! | exception_name
{
$$ =
plpgsql_parse_err_condition($1);
}
***************
*** 1626,1631 ****
--- 1700,1719 ----
}
;
+
+ exception_name : T_WORD
+ {
+ char *name;
+
+ plpgsql_convert_ident(yytext,
&name, 1);
+ $$ = name;
+ }
+ | T_EXCEPTION
+ {
+ $$ =
yylval.uexception->refname;
+ }
+ ;
+
lno :
{
$$ = plpgsql_error_lineno =
plpgsql_scanner_lineno();
***************
*** 1793,1798 ****
--- 1881,1893 ----
if (tok == YYEMPTY)
tok = yylex();
+ if (tok == K_EXCEPTION)
+ {
+ result = (PLpgSQL_type *) palloc(sizeof(PLpgSQL_type));
+ result->typname = "exception";
+ result->ttype = PLPGSQL_TTYPE_EXCEPTION;
+ return result;
+ }
if (tok == T_DTYPE)
{
/* lexer found word%TYPE and did its thing already */
diff -c -r --new-file src.00/pl_comp.c src/pl_comp.c
*** src.00/pl_comp.c 2005-06-14 13:57:13.000000000 +0200
--- src/pl_comp.c 2005-06-15 07:49:02.000000000 +0200
***************
*** 58,63 ****
--- 58,64 ----
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
+ #include "utils/elog.h"
/* ----------
***************
*** 80,85 ****
--- 81,88 ----
bool plpgsql_DumpExecTree = false;
bool plpgsql_check_syntax = false;
+ int plpgsql_user_excpt;
+
PLpgSQL_function *plpgsql_curr_compile;
/* A context appropriate for short-term allocs during compilation */
***************
*** 294,299 ****
--- 297,303 ----
plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
plpgsql_error_lineno = 0;
+ plpgsql_user_excpt = 0;
/*
* Setup error traceback support for ereport()
***************
*** 904,909 ****
--- 908,917 ----
case PLPGSQL_NSTYPE_ROW:
plpgsql_yylval.row = (PLpgSQL_row *)
(plpgsql_Datums[nse->itemno]);
return T_ROW;
+
+ case PLPGSQL_NSTYPE_EXCEPTION:
+ plpgsql_yylval.uexception = (PLpgSQL_uexception
*) (plpgsql_Datums[nse->itemno]);
+ return T_EXCEPTION;
default:
return T_ERROR;
***************
*** 1626,1631 ****
--- 1634,1657 ----
result = (PLpgSQL_variable *) rec;
break;
}
+ case PLPGSQL_TTYPE_EXCEPTION:
+ {
+ /* Exception pseudo type */
+ PLpgSQL_uexception *excpt;
+
+ excpt = palloc0(sizeof(PLpgSQL_uexception));
+ excpt->dtype = PLPGSQL_DTYPE_EXCEPTION;
+ excpt->refname = pstrdup(refname);
+ excpt->lineno = lineno;
+
+ plpgsql_adddatum((PLpgSQL_datum *) excpt);
+ if (add2namespace)
+ plpgsql_ns_additem(PLPGSQL_NSTYPE_EXCEPTION,
+
excpt->varno,
+ refname);
+ result = (PLpgSQL_variable *) excpt;
+ break;
+ }
case PLPGSQL_TTYPE_PSEUDO:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 1893,1898 ****
--- 1919,1932 ----
PLpgSQL_condition *new;
PLpgSQL_condition *prev;
+ PLpgSQL_nsitem *nse;
+ char *cp[1];
+
+
+ /* Do case conversion and word separation */
+ plpgsql_convert_ident(condname, cp, 1);
+
+
/*
* XXX Eventually we will want to look for user-defined exception
* names here.
***************
*** 1925,1930 ****
--- 1959,1989 ----
}
if (!prev)
+ {
+ /* Do case conversion and word separation */
+ plpgsql_convert_ident(condname, cp, 1);
+
+ /*
+ * Do a lookup on the compiler's namestack
+ */
+ nse = plpgsql_ns_lookup(cp[0], NULL);
+
+ if (nse != NULL)
+ {
+ PLpgSQL_uexception *excpt = (PLpgSQL_uexception *)
(plpgsql_Datums[nse->itemno]);
+ if (nse->itemtype == PLPGSQL_NSTYPE_EXCEPTION)
+ {
+ new = palloc(sizeof(PLpgSQL_condition));
+ new->sqlerrstate = excpt->sqlerrstate;
+ new->condname = condname;
+ new->next = prev;
+ prev = new;
+ }
+ }
+ pfree(cp[0]);
+ }
+
+ if (!prev)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized exception condition
\"%s\"",
***************
*** 1934,1940 ****
}
/* ----------
! * plpgsql_adddatum Add a variable, record or row
* to the compiler's datum list.
* ----------
*/
--- 1993,1999 ----
}
/* ----------
! * plpgsql_adddatum Add a variable, record or row, or
exception
* to the compiler's datum list.
* ----------
*/
***************
*** 2177,2179 ****
--- 2236,2253 ----
if (hentry == NULL)
elog(WARNING, "trying to delete function that does not exist");
}
+
+ #define MAX_USER_EXCPT 999
+
+ int
+ plpgsql_newUsrExceptions(void)
+ {
+ char rs[4];
+
+ if (plpgsql_user_excpt == MAX_USER_EXCPT)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Too much user's exception")));
+ sprintf(rs,"%03d", ++plpgsql_user_excpt);
+ return MAKE_SQLSTATE('U','0', rs[0],rs[1],rs[2]);
+ }
diff -c -r --new-file src.00/pl_exec.c src/pl_exec.c
*** src.00/pl_exec.c 2005-06-14 13:57:13.000000000 +0200
--- src/pl_exec.c 2005-06-15 08:44:34.000000000 +0200
***************
*** 267,273 ****
break;
default:
! elog(ERROR, "unrecognized dtype: %d",
func->datums[i]->dtype);
}
}
--- 267,273 ----
break;
default:
! elog(ERROR, ">>unrecognized dtype: %d",
func->datums[i]->dtype);
}
}
***************
*** 698,705 ****
result = datum;
break;
default:
! elog(ERROR, "unrecognized dtype: %d", datum->dtype);
result = NULL; /* keep compiler quiet */
break;
}
--- 698,708 ----
result = datum;
break;
+ case PLPGSQL_DTYPE_EXCEPTION:
+ result = NULL;
+ break;
default:
! elog(ERROR, ">>>>>unrecognized dtype: %d",
datum->dtype);
result = NULL; /* keep compiler quiet */
break;
}
***************
*** 800,805 ****
--- 803,809 ----
case PLPGSQL_DTYPE_RECFIELD:
case PLPGSQL_DTYPE_ARRAYELEM:
+ case PLPGSQL_DTYPE_EXCEPTION:
break;
default:
***************
*** 1960,1968 ****
*/
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))));
estate->err_text = NULL; /* un-suppress... */
--- 1964,1981 ----
*/
estate->err_text = raise_skip_msg; /* suppress traceback of raise
*/
! if (stmt->refname != NULL)
! ereport(stmt->elog_level, /* User's exception */
! (errcode(stmt->sqlerrstate),
! errdetail("User's exception sqlstate: %s, name: %s",
unpack_sql_state(stmt->sqlerrstate), stmt->refname),
! errhint("Unhandled user's exception, from RAISE stmt on line
%d", stmt->lineno),
! errmsg_internal("%s", plpgsql_dstring_get(&ds))));
! else
! ereport(stmt->elog_level,
! (errcode(stmt->sqlerrstate), //(stmt->elog_level >= ERROR) ?
errcode(ERRCODE_RAISE_EXCEPTION) : 0,
! errmsg_internal("%s", plpgsql_dstring_get(&ds))));
!
!
estate->err_text = NULL; /* un-suppress... */
diff -c -r --new-file src.00/plpgsql.h src/plpgsql.h
*** src.00/plpgsql.h 2005-06-14 13:57:13.000000000 +0200
--- src/plpgsql.h 2005-06-15 08:29:06.000000000 +0200
***************
*** 58,64 ****
PLPGSQL_NSTYPE_LABEL,
PLPGSQL_NSTYPE_VAR,
PLPGSQL_NSTYPE_ROW,
! PLPGSQL_NSTYPE_REC
};
/* ----------
--- 58,65 ----
PLPGSQL_NSTYPE_LABEL,
PLPGSQL_NSTYPE_VAR,
PLPGSQL_NSTYPE_ROW,
! PLPGSQL_NSTYPE_REC,
! PLPGSQL_NSTYPE_EXCEPTION
};
/* ----------
***************
*** 73,79 ****
PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_ARRAYELEM,
PLPGSQL_DTYPE_EXPR,
! PLPGSQL_DTYPE_TRIGARG
};
/* ----------
--- 74,81 ----
PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_ARRAYELEM,
PLPGSQL_DTYPE_EXPR,
! PLPGSQL_DTYPE_TRIGARG,
! PLPGSQL_DTYPE_EXCEPTION
};
/* ----------
***************
*** 85,91 ****
PLPGSQL_TTYPE_SCALAR, /* scalar types and domains */
PLPGSQL_TTYPE_ROW, /* composite types */
PLPGSQL_TTYPE_REC, /* RECORD pseudotype */
! PLPGSQL_TTYPE_PSEUDO /* other pseudotypes */
};
/* ----------
--- 87,94 ----
PLPGSQL_TTYPE_SCALAR, /* scalar types and domains */
PLPGSQL_TTYPE_ROW, /* composite types */
PLPGSQL_TTYPE_REC, /* RECORD pseudotype */
! PLPGSQL_TTYPE_PSEUDO, /* other pseudotypes */
! PLPGSQL_TTYPE_EXCEPTION
};
/* ----------
***************
*** 189,194 ****
--- 192,206 ----
int lineno;
} PLpgSQL_variable;
+ typedef struct
+ {
+ int dtype;
+ int varno;
+ char *refname;
+ int lineno;
+ int sqlerrstate;
+ } PLpgSQL_uexception;
+
typedef struct PLpgSQL_expr
{ /* SQL Query to
plan and execute */
int dtype;
***************
*** 514,520 ****
--- 526,534 ----
int cmd_type;
int lineno;
int elog_level;
+ int sqlerrstate;
char *message;
+ char *refname; /* name of user's exception */
List *params; /* list of expressions */
} PLpgSQL_stmt_raise;
***************
*** 686,691 ****
--- 700,707 ----
extern void plpgsql_HashTableInit(void);
extern void plpgsql_compile_error_callback(void *arg);
+ extern int plpgsql_newUsrExceptions(void);
+
/* ----------
* Functions in pl_handler.c
* ----------
---------------------------(end of broadcast)---------------------------
TIP 7: don't forget to increase your free space map settings