Add PL/pgSQL SQLSTATE and SQLERRM support which sets these values on
authorBruce Momjian
Thu, 26 May 2005 00:16:31 +0000 (00:16 +0000)
committerBruce Momjian
Thu, 26 May 2005 00:16:31 +0000 (00:16 +0000)
error.

Pavel Stehule

doc/src/sgml/plpgsql.sgml
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/plpgsql.h
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index 0fe722cb8871621ff282f7aeee92c957f0d51e78..17e62744c5e7665298ab825caa78284e05fdf660 100644 (file)
@@ -1,5 +1,5 @@
 
 
  
@@ -2007,12 +2007,13 @@ END LOOP;
     
 
     
-     By default, any error occurring in a PL/pgSQL
-     function aborts execution of the function, and indeed of the
-     surrounding transaction as well.  You can trap errors and recover
-     from them by using a BEGIN block with an
-     EXCEPTION clause.  The syntax is an extension of the
-     normal syntax for a BEGIN block:
+     Any error occurring in PL/pgSQL sets variables
+     SQLSTATE and SQLERRM, and, by default,
+     aborts execution of the function, and indeed of the surrounding
+     transaction as well. You can trap errors and recover from them by
+     using a BEGIN block with an EXCEPTION
+     clause. The syntax is an extension of the normal syntax for a
+     BEGIN block:
 
 
  <<label>> 
index 8b5f6b5e28df593a3cd7bdb4218062149fe78e0f..d4a535d548be4de860822506f97023afdd61ed23 100644 (file)
@@ -4,7 +4,7 @@
  *                       procedural language
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.69 2005/04/07 14:53:04 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.70 2005/05/26 00:16:31 momjian Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -80,6 +80,11 @@ static   void             plpgsql_sql_error_callback(void *arg);
            int  n_initvars;
            int  *initvarnos;
        }                       declhdr;
+       struct 
+       {
+           int sqlstate_varno;
+           int sqlerrm_varno;
+       }                       fict_vars;
        List                    *list;
        PLpgSQL_type            *dtype;
        PLpgSQL_datum           *scalar;    /* a VAR, RECFIELD, or TRIGARG */
@@ -96,6 +101,7 @@ static   void             plpgsql_sql_error_callback(void *arg);
        PLpgSQL_diag_item       *diagitem;
 }
 
+%type  fict_vars_sect
 %type  decl_sect
 %type  decl_varname
 %type         decl_renname
@@ -244,19 +250,22 @@ opt_semi      :
                | ';'
                ;
 
-pl_block       : decl_sect K_BEGIN lno proc_sect exception_sect K_END
+pl_block       : decl_sect fict_vars_sect K_BEGIN lno proc_sect exception_sect K_END
                    {
                        PLpgSQL_stmt_block *new;
 
                        new = palloc0(sizeof(PLpgSQL_stmt_block));
 
                        new->cmd_type   = PLPGSQL_STMT_BLOCK;
-                       new->lineno     = $3;
+                       new->lineno     = $4;
                        new->label      = $1.label;
                        new->n_initvars = $1.n_initvars;
                        new->initvarnos = $1.initvarnos;
-                       new->body       = $4;
-                       new->exceptions = $5;
+                       new->body       = $5;
+                       new->exceptions = $6;
+
+                       new->sqlstate_varno = $2.sqlstate_varno;
+                       new->sqlerrm_varno = $2.sqlerrm_varno;
 
                        plpgsql_ns_pop();
 
@@ -264,6 +273,19 @@ pl_block       : decl_sect K_BEGIN lno proc_sect exception_sect K_END
                    }
                ;
 
+fict_vars_sect :
+                   {
+                       PLpgSQL_variable    *var;
+
+                       plpgsql_ns_setlocal(false);
+                       var = plpgsql_build_variable("sqlstate", 0,
+                                        plpgsql_build_datatype(TEXTOID, -1), true);  
+                       $$.sqlstate_varno = var->dno;
+                       var = plpgsql_build_variable("sqlerrm", 0,
+                                        plpgsql_build_datatype(TEXTOID, -1), true);  
+                       $$.sqlerrm_varno = var->dno;
+                       plpgsql_add_initdatums(NULL);
+                   };
 
 decl_sect      : opt_label
                    {
index 18bcd4fe2ee4db0538ebe15d302b1fb829b1949f..333ca21c0ba988e1d0769b808c5d1a0a095590fe 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.138 2005/05/06 17:24:55 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.139 2005/05/26 00:16:31 momjian Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -180,6 +180,7 @@ static Datum exec_simple_cast_value(Datum value, Oid valtype,
 static void exec_init_tuple_store(PLpgSQL_execstate *estate);
 static bool compatible_tupdesc(TupleDesc td1, TupleDesc td2);
 static void exec_set_found(PLpgSQL_execstate *estate, bool state);
+static char *unpack_sql_state(int ssval);
 
 
 /* ----------
@@ -747,6 +748,20 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
    int         i;
    int         n;
 
+
+   /* setup SQLSTATE and SQLERRM */
+   PLpgSQL_var *var;
+  
+   var = (PLpgSQL_var *) (estate->datums[block->sqlstate_varno]);
+   var->isnull = false;
+   var->freeval = true;
+   var->value = DirectFunctionCall1(textin, CStringGetDatum("00000"));
+  
+   var = (PLpgSQL_var *) (estate->datums[block->sqlerrm_varno]);
+       var->isnull = false;
+   var->freeval = true;
+   var->value = DirectFunctionCall1(textin, CStringGetDatum("Sucessful completion"));
+
    /*
     * First initialize all variables declared in this block
     */
@@ -855,6 +870,16 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
            RollbackAndReleaseCurrentSubTransaction();
            MemoryContextSwitchTo(oldcontext);
            CurrentResourceOwner = oldowner;
+           /* set SQLSTATE and SQLERRM variables */
+           
+           var = (PLpgSQL_var *) (estate->datums[block->sqlstate_varno]);
+           pfree((void *) (var->value));
+           var->value = DirectFunctionCall1(textin, CStringGetDatum(unpack_sql_state(edata->sqlerrcode)));
+  
+           var = (PLpgSQL_var *) (estate->datums[block->sqlerrm_varno]);
+           pfree((void *) (var->value));
+           var->value = DirectFunctionCall1(textin, CStringGetDatum(edata->message));
 
            /*
             * If AtEOSubXact_SPI() popped any SPI context of the subxact,
@@ -919,6 +944,26 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
    return PLPGSQL_RC_OK;
 }
 
+/* 
+ * unpack MAKE_SQLSTATE code 
+ * This code is copied from backend/utils/error/elog.c.
+ */
+static char *
+unpack_sql_state(int ssval)
+{
+   static  char        tbuf[12];
+   int         i;
+
+   for (i = 0; i < 5; i++)
+   {
+       tbuf[i] = PGUNSIXBIT(ssval);
+       ssval >>= 6;
+   }
+   tbuf[i] = '\0';
+   return tbuf;
+}
+
+
 
 /* ----------
  * exec_stmts          Iterate over a list of statements
index 80bcd9e8fa64a086302912aeebd02174f523f4c6..f3b14fdf643910d56f8edc243fc75f01a58e1bfa 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.58 2005/04/05 06:22:16 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.59 2005/05/26 00:16:31 momjian Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -336,9 +336,11 @@ typedef struct
    int         lineno;
    char       *label;
    List       *body;           /* List of statements */
-   List       *exceptions;     /* List of WHEN clauses */
-   int         n_initvars;
-   int        *initvarnos;
+   List       *exceptions;     /* List of WHEN clauses */
+   int         n_initvars;
+   int        *initvarnos;
+   int         sqlstate_varno;
+   int         sqlerrm_varno;
 } PLpgSQL_stmt_block;
 
 
index 08fbe46b3a240f4932ff382d3370ed20f44e580d..5ee5c338e46b6042eb0dc361cd866a607977820c 100644 (file)
@@ -2380,3 +2380,29 @@ ERROR:  control reached end of function without RETURN
 CONTEXT:  PL/pgSQL function "missing_return_expr"
 drop function void_return_expr();
 drop function missing_return_expr();
+-- test SQLSTATE and SQLERRM
+create or replace function trap_exceptions() returns void as $_$
+begin
+   begin
+     raise exception 'first exception';
+   exception when others then
+     raise notice '% %', SQLSTATE, SQLERRM;
+   end;
+   raise notice '% %', SQLSTATE, SQLERRM;
+   begin
+     raise exception 'last exception';
+   exception when others then
+     raise notice '% %', SQLSTATE, SQLERRM;
+   end;
+   return;
+end; $_$ language plpgsql;
+select trap_exceptions();
+NOTICE:  P0001 first exception
+NOTICE:  00000 Sucessful completion
+NOTICE:  P0001 last exception
+ trap_exceptions 
+-----------------
+(1 row)
+
+drop function trap_exceptions();
index 7ea7c8c6e0c558243f82b891059b0a77a848debf..e2ef2ec5fe339af034737620278aa1e070a3dbab 100644 (file)
@@ -2018,3 +2018,23 @@ select missing_return_expr();
 
 drop function void_return_expr();
 drop function missing_return_expr();
+-- test SQLSTATE and SQLERRM
+create or replace function trap_exceptions() returns void as $_$
+begin
+   begin
+     raise exception 'first exception';
+   exception when others then
+     raise notice '% %', SQLSTATE, SQLERRM;
+   end;
+   raise notice '% %', SQLSTATE, SQLERRM;
+   begin
+     raise exception 'last exception';
+   exception when others then
+     raise notice '% %', SQLSTATE, SQLERRM;
+   end;
+   return;
+end; $_$ language plpgsql;
+
+select trap_exceptions();
+
+drop function trap_exceptions();