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; @@ -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 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; @@ -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 *