mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
Add error stack traceback support for SQL-language functions.
This commit is contained in:
parent
1c241545e3
commit
aad71b40ca
@ -1,14 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* 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
|
|||||||
* 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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_context_stack = sqlerrcontext.previous;
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
|||||||
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,
|
|||||||
(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
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user