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 decl_sect %type decl_varname %type decl_renname ! %type decl_const decl_notnull ! %type decl_defval decl_cursor_query %type decl_datatype %type decl_cursor_args %type decl_cursor_arglist --- 96,109 ---- PLpgSQL_exception_block *exception_block; PLpgSQL_nsitem *nsitem; PLpgSQL_diag_item *diagitem; + PLpgSQL_uexception *uexception; } %type decl_sect %type decl_varname %type decl_renname ! %type decl_const ! %type decl_cursor_query %type decl_datatype %type decl_cursor_args %type decl_cursor_arglist *************** *** 121,126 **** --- 123,129 ---- %type opt_lblname opt_label %type opt_exitlabel %type execsql_start + %type exception_name %type proc_sect proc_stmts stmt_else loop_body %type proc_stmt pl_block *************** *** 144,149 **** --- 147,153 ---- %type getdiag_kind getdiag_target %type lno + %type 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 * ----------