Commit aad71b40 authored by Tom Lane's avatar Tom Lane

Add error stack traceback support for SQL-language functions.

parent 1c241545
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* functions.c * 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) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * 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 ...@@ -54,7 +54,6 @@ typedef struct local_es
* An SQLFunctionCache record is built during the first call, * An SQLFunctionCache record is built during the first call,
* and linked to from the fn_extra field of the FmgrInfo struct. * and linked to from the fn_extra field of the FmgrInfo struct.
*/ */
typedef struct typedef struct
{ {
int typlen; /* length of the return type */ int typlen; /* length of the return type */
...@@ -88,6 +87,7 @@ static void postquel_sub_params(SQLFunctionCachePtr fcache, ...@@ -88,6 +87,7 @@ static void postquel_sub_params(SQLFunctionCachePtr fcache,
static Datum postquel_execute(execution_state *es, static Datum postquel_execute(execution_state *es,
FunctionCallInfo fcinfo, FunctionCallInfo fcinfo,
SQLFunctionCachePtr fcache); SQLFunctionCachePtr fcache);
static void sql_exec_error_callback(void *arg);
static void ShutdownSQLFunction(Datum arg); static void ShutdownSQLFunction(Datum arg);
...@@ -323,15 +323,15 @@ postquel_getnext(execution_state *es) ...@@ -323,15 +323,15 @@ postquel_getnext(execution_state *es)
static void static void
postquel_end(execution_state *es) 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. */ /* Utility commands don't need Executor. */
if (es->qd->operation != CMD_UTILITY) if (es->qd->operation != CMD_UTILITY)
ExecutorEnd(es->qd); ExecutorEnd(es->qd);
FreeQueryDesc(es->qd); FreeQueryDesc(es->qd);
es->qd = NULL; es->qd = NULL;
es->status = F_EXEC_DONE;
} }
/* Build ParamListInfo array representing current arguments */ /* Build ParamListInfo array representing current arguments */
...@@ -492,6 +492,7 @@ fmgr_sql(PG_FUNCTION_ARGS) ...@@ -492,6 +492,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
{ {
MemoryContext oldcontext; MemoryContext oldcontext;
SQLFunctionCachePtr fcache; SQLFunctionCachePtr fcache;
ErrorContextCallback sqlerrcontext;
execution_state *es; execution_state *es;
Datum result = 0; Datum result = 0;
...@@ -502,6 +503,14 @@ fmgr_sql(PG_FUNCTION_ARGS) ...@@ -502,6 +503,14 @@ fmgr_sql(PG_FUNCTION_ARGS)
*/ */
oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); 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. * Initialize fcache (build plans) if first time through.
*/ */
...@@ -580,6 +589,8 @@ fmgr_sql(PG_FUNCTION_ARGS) ...@@ -580,6 +589,8 @@ fmgr_sql(PG_FUNCTION_ARGS)
} }
} }
error_context_stack = sqlerrcontext.previous;
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
return result; return result;
...@@ -618,11 +629,74 @@ fmgr_sql(PG_FUNCTION_ARGS) ...@@ -618,11 +629,74 @@ fmgr_sql(PG_FUNCTION_ARGS)
} }
} }
error_context_stack = sqlerrcontext.previous;
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
return result; 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 * callback function in case a function-returning-set needs to be shut down
* before it has been run to completion * before it has been run to completion
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -70,6 +70,7 @@ static Node *substitute_actual_parameters(Node *expr, int nargs, List *args, ...@@ -70,6 +70,7 @@ static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
int *usecounts); int *usecounts);
static Node *substitute_actual_parameters_mutator(Node *node, static Node *substitute_actual_parameters_mutator(Node *node,
substitute_actual_parameters_context *context); substitute_actual_parameters_context *context);
static void sql_inline_error_callback(void *arg);
static Expr *evaluate_expr(Expr *expr, Oid result_type); static Expr *evaluate_expr(Expr *expr, Oid result_type);
...@@ -1730,6 +1731,7 @@ inline_function(Oid funcid, Oid result_type, List *args, ...@@ -1730,6 +1731,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
bool isNull; bool isNull;
MemoryContext oldcxt; MemoryContext oldcxt;
MemoryContext mycxt; MemoryContext mycxt;
ErrorContextCallback sqlerrcontext;
List *raw_parsetree_list; List *raw_parsetree_list;
List *querytree_list; List *querytree_list;
Query *querytree; Query *querytree;
...@@ -1780,6 +1782,15 @@ inline_function(Oid funcid, Oid result_type, List *args, ...@@ -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 * Make a temporary memory context, so that we don't leak all the
* stuff that parsing might create. * stuff that parsing might create.
...@@ -1926,12 +1937,15 @@ inline_function(Oid funcid, Oid result_type, List *args, ...@@ -1926,12 +1937,15 @@ inline_function(Oid funcid, Oid result_type, List *args,
newexpr = eval_const_expressions_mutator(newexpr, newexpr = eval_const_expressions_mutator(newexpr,
lconso(funcid, active_fns)); lconso(funcid, active_fns));
error_context_stack = sqlerrcontext.previous;
return (Expr *) newexpr; return (Expr *) newexpr;
/* Here if func is not inlinable: release temp memory and return NULL */ /* Here if func is not inlinable: release temp memory and return NULL */
fail: fail:
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
MemoryContextDelete(mycxt); MemoryContextDelete(mycxt);
error_context_stack = sqlerrcontext.previous;
return NULL; return NULL;
} }
...@@ -1978,6 +1992,18 @@ substitute_actual_parameters_mutator(Node *node, ...@@ -1978,6 +1992,18 @@ substitute_actual_parameters_mutator(Node *node,
(void *) context); (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 * evaluate_expr: pre-evaluate a constant expression
* *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment