aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-07-28 18:33:18 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-07-28 18:33:18 +0000
commitaad71b40ca781c59d3b458a18a42bc70ced2eb7c (patch)
treed3260907272804526d5591491447a813669a9e2a
parent1c241545e3ce5c0fd0f0b874da6dbcae19556a99 (diff)
downloadpostgresql-aad71b40ca781c59d3b458a18a42bc70ced2eb7c.tar.gz
postgresql-aad71b40ca781c59d3b458a18a42bc70ced2eb7c.zip
Add error stack traceback support for SQL-language functions.
-rw-r--r--src/backend/executor/functions.c86
-rw-r--r--src/backend/optimizer/util/clauses.c28
2 files changed, 107 insertions, 7 deletions
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index bda57fa3214..c8df7ccb83c 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -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;
@@ -503,6 +504,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.
*/
fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
@@ -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
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 5df7e1c3cb3..6060f462e8f 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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;
@@ -1781,6 +1783,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;
}
@@ -1979,6 +1993,18 @@ substitute_actual_parameters_mutator(Node *node,
}
/*
+ * 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
*
* We use the executor's routine ExecEvalExpr() to avoid duplication of