]> granicus.if.org Git - postgresql/commitdiff
Add error stack traceback support for SQL-language functions.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 28 Jul 2003 18:33:18 +0000 (18:33 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 28 Jul 2003 18:33:18 +0000 (18:33 +0000)
src/backend/executor/functions.c
src/backend/optimizer/util/clauses.c

index bda57fa32147cc69c257957f6f576c4efe02abd0..c8df7ccb83ca96f076627038b4c10aba90953013 100644 (file)
@@ -1,14 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * functions.c
- *       Routines to handle functions called from the executor
+ *       Execution of SQL-language functions
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.68 2003/07/21 17:05:09 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.69 2003/07/28 18:33:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,7 +54,6 @@ typedef struct local_es
  * An SQLFunctionCache record is built during the first call,
  * and linked to from the fn_extra field of the FmgrInfo struct.
  */
-
 typedef struct
 {
        int                     typlen;                 /* length of the return type */
@@ -88,6 +87,7 @@ static void postquel_sub_params(SQLFunctionCachePtr fcache,
 static Datum postquel_execute(execution_state *es,
                                 FunctionCallInfo fcinfo,
                                 SQLFunctionCachePtr fcache);
+static void sql_exec_error_callback(void *arg);
 static void ShutdownSQLFunction(Datum arg);
 
 
@@ -323,15 +323,15 @@ postquel_getnext(execution_state *es)
 static void
 postquel_end(execution_state *es)
 {
+       /* mark status done to ensure we don't do ExecutorEnd twice */
+       es->status = F_EXEC_DONE;
+
        /* Utility commands don't need Executor. */
        if (es->qd->operation != CMD_UTILITY)
                ExecutorEnd(es->qd);
 
        FreeQueryDesc(es->qd);
-
        es->qd = NULL;
-
-       es->status = F_EXEC_DONE;
 }
 
 /* Build ParamListInfo array representing current arguments */
@@ -492,6 +492,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
 {
        MemoryContext oldcontext;
        SQLFunctionCachePtr fcache;
+       ErrorContextCallback sqlerrcontext;
        execution_state *es;
        Datum           result = 0;
 
@@ -502,6 +503,14 @@ fmgr_sql(PG_FUNCTION_ARGS)
         */
        oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
 
+       /*
+        * Setup error traceback support for ereport()
+        */
+       sqlerrcontext.callback = sql_exec_error_callback;
+       sqlerrcontext.arg = fcinfo->flinfo;
+       sqlerrcontext.previous = error_context_stack;
+       error_context_stack = &sqlerrcontext;
+
        /*
         * Initialize fcache (build plans) if first time through.
         */
@@ -580,6 +589,8 @@ fmgr_sql(PG_FUNCTION_ARGS)
                        }
                }
 
+               error_context_stack = sqlerrcontext.previous;
+
                MemoryContextSwitchTo(oldcontext);
 
                return result;
@@ -618,11 +629,74 @@ fmgr_sql(PG_FUNCTION_ARGS)
                }
        }
 
+       error_context_stack = sqlerrcontext.previous;
+
        MemoryContextSwitchTo(oldcontext);
 
        return result;
 }
 
+
+/*
+ * error context callback to let us supply a call-stack traceback
+ */
+static void
+sql_exec_error_callback(void *arg)
+{
+       FmgrInfo   *flinfo = (FmgrInfo *) arg;
+       SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) flinfo->fn_extra;
+       char       *fn_name;
+
+       fn_name = get_func_name(flinfo->fn_oid);
+       /* safety check, shouldn't happen */
+       if (fn_name == NULL)
+               return;
+
+       /*
+        * Try to determine where in the function we failed.  If there is a
+        * query with non-null QueryDesc, finger it.  (We check this rather
+        * than looking for F_EXEC_RUN state, so that errors during ExecutorStart
+        * or ExecutorEnd are blamed on the appropriate query; see postquel_start
+        * and postquel_end.)
+        */
+       if (fcache)
+       {
+               execution_state *es;
+               int             query_num;
+
+               es = fcache->func_state;
+               query_num = 1;
+               while (es)
+               {
+                       if (es->qd)
+                       {
+                               errcontext("SQL function \"%s\" query %d",
+                                                  fn_name, query_num);
+                               break;
+                       }
+                       es = es->next;
+                       query_num++;
+               }
+               if (es == NULL)
+               {
+                       /*
+                        * couldn't identify a running query; might be function entry,
+                        * function exit, or between queries.
+                        */
+                       errcontext("SQL function \"%s\"", fn_name);
+               }
+       }
+       else
+       {
+               /* must have failed during init_sql_fcache() */
+               errcontext("SQL function \"%s\" during startup", fn_name);
+       }
+
+       /* free result of get_func_name (in case this is only a notice) */
+       pfree(fn_name);
+}
+
+
 /*
  * callback function in case a function-returning-set needs to be shut down
  * before it has been run to completion
index 5df7e1c3cb30a8c37b5476ff46e4395612b22f59..6060f462e8f4c1219cc139a1947447f77817f482 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.147 2003/07/25 00:01:08 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.148 2003/07/28 18:33:18 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -70,6 +70,7 @@ static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
                                                                                  int *usecounts);
 static Node *substitute_actual_parameters_mutator(Node *node,
                                         substitute_actual_parameters_context *context);
+static void sql_inline_error_callback(void *arg);
 static Expr *evaluate_expr(Expr *expr, Oid result_type);
 
 
@@ -1730,6 +1731,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
        bool            isNull;
        MemoryContext oldcxt;
        MemoryContext mycxt;
+       ErrorContextCallback sqlerrcontext;
        List       *raw_parsetree_list;
        List       *querytree_list;
        Query      *querytree;
@@ -1780,6 +1782,15 @@ inline_function(Oid funcid, Oid result_type, List *args,
                }
        }
 
+       /*
+        * Setup error traceback support for ereport().  This is so that we can
+        * finger the function that bad information came from.
+        */
+       sqlerrcontext.callback = sql_inline_error_callback;
+       sqlerrcontext.arg = funcform;
+       sqlerrcontext.previous = error_context_stack;
+       error_context_stack = &sqlerrcontext;
+
        /*
         * Make a temporary memory context, so that we don't leak all the
         * stuff that parsing might create.
@@ -1926,12 +1937,15 @@ inline_function(Oid funcid, Oid result_type, List *args,
        newexpr = eval_const_expressions_mutator(newexpr,
                                                                                         lconso(funcid, active_fns));
 
+       error_context_stack = sqlerrcontext.previous;
+
        return (Expr *) newexpr;
 
        /* Here if func is not inlinable: release temp memory and return NULL */
 fail:
        MemoryContextSwitchTo(oldcxt);
        MemoryContextDelete(mycxt);
+       error_context_stack = sqlerrcontext.previous;
 
        return NULL;
 }
@@ -1978,6 +1992,18 @@ substitute_actual_parameters_mutator(Node *node,
                                                                   (void *) context);
 }
 
+/*
+ * error context callback to let us supply a call-stack traceback
+ */
+static void
+sql_inline_error_callback(void *arg)
+{
+       Form_pg_proc funcform = (Form_pg_proc) arg;
+
+       errcontext("SQL function \"%s\" during inlining",
+                          NameStr(funcform->proname));
+}
+
 /*
  * evaluate_expr: pre-evaluate a constant expression
  *