1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

Revise syntax-error reporting behavior to give pleasant results for

errors in internally-generated queries, such as those submitted by
plpgsql functions.  Per recent discussions with Fabien Coelho.
This commit is contained in:
Tom Lane
2004-03-21 22:29:11 +00:00
parent bee3b2a0a0
commit f938c2b91b
25 changed files with 671 additions and 124 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.112 2004/03/14 01:58:41 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.113 2004/03/21 22:29:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,9 +23,11 @@
#include "executor/executor.h"
#include "fmgr.h"
#include "miscadmin.h"
#include "mb/pg_wchar.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -45,6 +47,10 @@ Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
static Datum create_parameternames_array(int parameterCount,
const char *parameterNames[]);
static void sql_function_parse_error_callback(void *arg);
static int match_prosrc_to_query(const char *prosrc, const char *queryText,
int cursorpos);
static bool match_prosrc_to_literal(const char *prosrc, const char *literal,
int cursorpos, int *newcursorpos);
/* ----------------------------------------------------------------
@@ -763,12 +769,10 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
/*
* Setup error traceback support for ereport(). This is mostly
* so we can add context info that shows that a syntax-error
* location is inside the function body, not out in CREATE FUNCTION.
* Setup error traceback support for ereport().
*/
sqlerrcontext.callback = sql_function_parse_error_callback;
sqlerrcontext.arg = proc;
sqlerrcontext.arg = tuple;
sqlerrcontext.previous = error_context_stack;
error_context_stack = &sqlerrcontext;
@@ -800,22 +804,203 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
}
/*
* error context callback to let us supply a context marker
* Error context callback for handling errors in SQL function definitions
*/
static void
sql_function_parse_error_callback(void *arg)
{
Form_pg_proc proc = (Form_pg_proc) arg;
HeapTuple tuple = (HeapTuple) arg;
Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tuple);
bool isnull;
Datum tmp;
char *prosrc;
/* See if it's a syntax error; if so, transpose to CREATE FUNCTION */
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
if (isnull)
elog(ERROR, "null prosrc");
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
if (!function_parse_error_transpose(prosrc))
{
/* If it's not a syntax error, push info onto context stack */
errcontext("SQL function \"%s\"", NameStr(proc->proname));
}
pfree(prosrc);
}
/*
* Adjust a syntax error occurring inside the function body of a CREATE
* FUNCTION command. This can be used by any function validator, not only
* for SQL-language functions. It is assumed that the syntax error position
* is initially relative to the function body string (as passed in). If
* possible, we adjust the position to reference the original CREATE command;
* if we can't manage that, we set up an "internal query" syntax error instead.
*
* Returns true if a syntax error was processed, false if not.
*/
bool
function_parse_error_transpose(const char *prosrc)
{
int origerrposition;
int newerrposition;
const char *queryText;
/*
* XXX it'd be really nice to adjust the syntax error position to
* account for the offset from the start of the statement to the
* function body string, not to mention any quoting characters in
* the string, but I can't see any decent way to do that...
* Nothing to do unless we are dealing with a syntax error that has
* a cursor position.
*
* In the meantime, put in a CONTEXT entry that can cue clients
* not to trust the syntax error position completely.
* Some PLs may prefer to report the error position as an internal
* error to begin with, so check that too.
*/
errcontext("SQL function \"%s\"",
NameStr(proc->proname));
origerrposition = geterrposition();
if (origerrposition <= 0)
{
origerrposition = getinternalerrposition();
if (origerrposition <= 0)
return false;
}
/* We can get the original query text from the active portal (hack...) */
Assert(ActivePortal && ActivePortal->portalActive);
queryText = ActivePortal->sourceText;
/* Try to locate the prosrc in the original text */
newerrposition = match_prosrc_to_query(prosrc, queryText, origerrposition);
if (newerrposition > 0)
{
/* Successful, so fix error position to reference original query */
errposition(newerrposition);
/* Get rid of any report of the error as an "internal query" */
internalerrposition(0);
internalerrquery(NULL);
}
else
{
/*
* If unsuccessful, convert the position to an internal position
* marker and give the function text as the internal query.
*/
errposition(0);
internalerrposition(origerrposition);
internalerrquery(prosrc);
}
return true;
}
/*
* Try to locate the string literal containing the function body in the
* given text of the CREATE FUNCTION command. If successful, return the
* character (not byte) index within the command corresponding to the
* given character index within the literal. If not successful, return 0.
*/
static int
match_prosrc_to_query(const char *prosrc, const char *queryText,
int cursorpos)
{
/*
* Rather than fully parsing the CREATE FUNCTION command, we just scan
* the command looking for $prosrc$ or 'prosrc'. This could be fooled
* (though not in any very probable scenarios), so fail if we find
* more than one match.
*/
int prosrclen = strlen(prosrc);
int querylen = strlen(queryText);
int matchpos = 0;
int curpos;
int newcursorpos;
for (curpos = 0; curpos < querylen-prosrclen; curpos++)
{
if (queryText[curpos] == '$' &&
strncmp(prosrc, &queryText[curpos+1], prosrclen) == 0 &&
queryText[curpos+1+prosrclen] == '$')
{
/*
* Found a $foo$ match. Since there are no embedded quoting
* characters in a dollar-quoted literal, we don't have to do
* any fancy arithmetic; just offset by the starting position.
*/
if (matchpos)
return 0; /* multiple matches, fail */
matchpos = pg_mbstrlen_with_len(queryText, curpos+1)
+ cursorpos;
}
else if (queryText[curpos] == '\'' &&
match_prosrc_to_literal(prosrc, &queryText[curpos+1],
cursorpos, &newcursorpos))
{
/*
* Found a 'foo' match. match_prosrc_to_literal() has adjusted
* for any quotes or backslashes embedded in the literal.
*/
if (matchpos)
return 0; /* multiple matches, fail */
matchpos = pg_mbstrlen_with_len(queryText, curpos+1)
+ newcursorpos;
}
}
return matchpos;
}
/*
* Try to match the given source text to a single-quoted literal.
* If successful, adjust newcursorpos to correspond to the character
* (not byte) index corresponding to cursorpos in the source text.
*
* At entry, literal points just past a ' character. We must check for the
* trailing quote.
*/
static bool
match_prosrc_to_literal(const char *prosrc, const char *literal,
int cursorpos, int *newcursorpos)
{
int newcp = cursorpos;
int chlen;
/*
* This implementation handles backslashes and doubled quotes in the
* string literal. It does not handle the SQL syntax for literals
* continued across line boundaries.
*
* We do the comparison a character at a time, not a byte at a time,
* so that we can do the correct cursorpos math.
*/
while (*prosrc)
{
cursorpos--; /* characters left before cursor */
/*
* Check for backslashes and doubled quotes in the literal; adjust
* newcp when one is found before the cursor.
*/
if (*literal == '\\')
{
literal++;
if (cursorpos > 0)
newcp++;
}
else if (*literal == '\'')
{
if (literal[1] != '\'')
return false;
literal++;
if (cursorpos > 0)
newcp++;
}
chlen = pg_mblen(prosrc);
if (strncmp(prosrc, literal, chlen) != 0)
return false;
prosrc += chlen;
literal += chlen;
}
*newcursorpos = newcp;
if (*literal == '\'' && literal[1] != '\'')
return true;
return false;
}

View File

@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.25 2003/11/29 19:51:47 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.26 2004/03/21 22:29:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -270,6 +270,7 @@ void
PersistHoldablePortal(Portal portal)
{
QueryDesc *queryDesc = PortalGetQueryDesc(portal);
Portal saveActivePortal;
MemoryContext savePortalContext;
MemoryContext saveQueryContext;
MemoryContext oldcxt;
@@ -311,6 +312,8 @@ PersistHoldablePortal(Portal portal)
/*
* Set global portal context pointers.
*/
saveActivePortal = ActivePortal;
ActivePortal = portal;
savePortalContext = PortalContext;
PortalContext = PortalGetHeapMemory(portal);
saveQueryContext = QueryContext;
@@ -342,6 +345,7 @@ PersistHoldablePortal(Portal portal)
/* Mark portal not active */
portal->portalActive = false;
ActivePortal = saveActivePortal;
PortalContext = savePortalContext;
QueryContext = saveQueryContext;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.77 2004/01/07 18:56:26 neilc Exp $
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.78 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -645,12 +645,40 @@ sql_exec_error_callback(void *arg)
{
FmgrInfo *flinfo = (FmgrInfo *) arg;
SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) flinfo->fn_extra;
HeapTuple func_tuple;
Form_pg_proc functup;
char *fn_name;
int syntaxerrposition;
fn_name = get_func_name(flinfo->fn_oid);
/* safety check, shouldn't happen */
if (fn_name == NULL)
return;
/* Need access to function's pg_proc tuple */
func_tuple = SearchSysCache(PROCOID,
ObjectIdGetDatum(flinfo->fn_oid),
0, 0, 0);
if (!HeapTupleIsValid(func_tuple))
return; /* shouldn't happen */
functup = (Form_pg_proc) GETSTRUCT(func_tuple);
fn_name = NameStr(functup->proname);
/*
* If there is a syntax error position, convert to internal syntax error
*/
syntaxerrposition = geterrposition();
if (syntaxerrposition > 0)
{
bool isnull;
Datum tmp;
char *prosrc;
tmp = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_prosrc,
&isnull);
if (isnull)
elog(ERROR, "null prosrc");
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
errposition(0);
internalerrposition(syntaxerrposition);
internalerrquery(prosrc);
pfree(prosrc);
}
/*
* Try to determine where in the function we failed. If there is a
@@ -692,8 +720,7 @@ sql_exec_error_callback(void *arg)
errcontext("SQL function \"%s\" during startup", fn_name);
}
/* free result of get_func_name (in case this is only a notice) */
pfree(fn_name);
ReleaseSysCache(func_tuple);
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.111 2004/03/17 01:05:10 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.112 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -39,6 +39,8 @@ static int _SPI_execute_plan(_SPI_plan *plan,
Datum *Values, const char *Nulls,
bool useCurrentSnapshot, int tcount);
static void _SPI_error_callback(void *arg);
static void _SPI_cursor_operation(Portal portal, bool forward, int count,
DestReceiver *dest);
@@ -286,7 +288,8 @@ SPI_execp_current(void *plan, Datum *Values, const char *Nulls,
void *
SPI_prepare(const char *src, int nargs, Oid *argtypes)
{
_SPI_plan *plan;
_SPI_plan plan;
_SPI_plan *result;
if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
{
@@ -298,20 +301,21 @@ SPI_prepare(const char *src, int nargs, Oid *argtypes)
if (SPI_result < 0)
return NULL;
plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */
plan->argtypes = argtypes;
plan->nargs = nargs;
plan.plancxt = NULL; /* doesn't have own context */
plan.query = src;
plan.nargs = nargs;
plan.argtypes = argtypes;
SPI_result = _SPI_execute(src, 0, plan);
SPI_result = _SPI_execute(src, 0, &plan);
if (SPI_result >= 0) /* copy plan to procedure context */
plan = _SPI_copy_plan(plan, _SPI_CPLAN_PROCXT);
result = _SPI_copy_plan(&plan, _SPI_CPLAN_PROCXT);
else
plan = NULL;
result = NULL;
_SPI_end_call(true);
return (void *) plan;
return (void *) result;
}
void *
@@ -335,7 +339,6 @@ SPI_saveplan(void *plan)
SPI_result = 0;
return (void *) newplan;
}
int
@@ -927,12 +930,12 @@ SPI_cursor_close(Portal portal)
Oid
SPI_getargtypeid(void *plan, int argIndex)
{
if (plan == NULL || argIndex < 0 || argIndex >= ((_SPI_plan*)plan)->nargs)
{
SPI_result = SPI_ERROR_ARGUMENT;
return InvalidOid;
}
return ((_SPI_plan *) plan)->argtypes[argIndex];
if (plan == NULL || argIndex < 0 || argIndex >= ((_SPI_plan*)plan)->nargs)
{
SPI_result = SPI_ERROR_ARGUMENT;
return InvalidOid;
}
return ((_SPI_plan *) plan)->argtypes[argIndex];
}
/*
@@ -941,12 +944,12 @@ SPI_getargtypeid(void *plan, int argIndex)
int
SPI_getargcount(void *plan)
{
if (plan == NULL)
{
SPI_result = SPI_ERROR_ARGUMENT;
return -1;
}
return ((_SPI_plan *) plan)->nargs;
if (plan == NULL)
{
SPI_result = SPI_ERROR_ARGUMENT;
return -1;
}
return ((_SPI_plan *) plan)->nargs;
}
/*
@@ -961,22 +964,24 @@ SPI_getargcount(void *plan)
bool
SPI_is_cursor_plan(void *plan)
{
List *qtlist;
_SPI_plan *spiplan = (_SPI_plan *) plan;
if (spiplan == NULL)
{
SPI_result = SPI_ERROR_ARGUMENT;
return false;
}
_SPI_plan *spiplan = (_SPI_plan *) plan;
List *qtlist;
qtlist = spiplan->qtlist;
if(length(spiplan->ptlist) == 1 && length(qtlist) == 1)
{
Query *queryTree = (Query *) lfirst((List *) lfirst(qtlist));
if(queryTree->commandType == CMD_SELECT && queryTree->into == NULL)
return true;
}
return false;
if (spiplan == NULL)
{
SPI_result = SPI_ERROR_ARGUMENT;
return false;
}
qtlist = spiplan->qtlist;
if (length(spiplan->ptlist) == 1 && length(qtlist) == 1)
{
Query *queryTree = (Query *) lfirst((List *) lfirst(qtlist));
if (queryTree->commandType == CMD_SELECT && queryTree->into == NULL)
return true;
}
return false;
}
/* =================== private functions =================== */
@@ -1071,7 +1076,8 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
/*
* Plan and optionally execute a querystring.
*
* If plan != NULL, just prepare plan tree, else execute immediately.
* If plan != NULL, just prepare plan trees and save them in *plan;
* else execute immediately.
*/
static int
_SPI_execute(const char *src, int tcount, _SPI_plan *plan)
@@ -1080,6 +1086,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
List *query_list_list;
List *plan_list;
List *list_item;
ErrorContextCallback spierrcontext;
int nargs = 0;
Oid *argtypes = NULL;
int res = 0;
@@ -1099,6 +1106,14 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
SPI_tuptable = NULL;
_SPI_current->tuptable = NULL;
/*
* Setup error traceback support for ereport()
*/
spierrcontext.callback = _SPI_error_callback;
spierrcontext.arg = (void *) src;
spierrcontext.previous = error_context_stack;
error_context_stack = &spierrcontext;
/*
* Parse the request string into a list of raw parse trees.
*/
@@ -1149,14 +1164,23 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
if (stmt->filename == NULL)
return SPI_ERROR_COPY;
{
res = SPI_ERROR_COPY;
goto fail;
}
}
else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
IsA(queryTree->utilityStmt, ClosePortalStmt) ||
IsA(queryTree->utilityStmt, FetchStmt))
return SPI_ERROR_CURSOR;
{
res = SPI_ERROR_CURSOR;
goto fail;
}
else if (IsA(queryTree->utilityStmt, TransactionStmt))
return SPI_ERROR_TRANSACTION;
{
res = SPI_ERROR_TRANSACTION;
goto fail;
}
res = SPI_OK_UTILITY;
if (plan == NULL)
{
@@ -1171,7 +1195,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
res = _SPI_pquery(qdesc, true, false,
queryTree->canSetTag ? tcount : 0);
if (res < 0)
return res;
goto fail;
CommandCounterIncrement();
}
else
@@ -1180,7 +1204,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
NULL, false);
res = _SPI_pquery(qdesc, false, false, 0);
if (res < 0)
return res;
goto fail;
}
}
}
@@ -1191,6 +1215,13 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
plan->ptlist = plan_list;
}
fail:
/*
* Pop the error context stack
*/
error_context_stack = spierrcontext.previous;
return res;
}
@@ -1201,6 +1232,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
List *query_list_list = plan->qtlist;
List *plan_list = plan->ptlist;
List *query_list_list_item;
ErrorContextCallback spierrcontext;
int nargs = plan->nargs;
int res = 0;
ParamListInfo paramLI;
@@ -1234,6 +1266,14 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
SPI_tuptable = NULL;
_SPI_current->tuptable = NULL;
/*
* Setup error traceback support for ereport()
*/
spierrcontext.callback = _SPI_error_callback;
spierrcontext.arg = (void *) plan->query;
spierrcontext.previous = error_context_stack;
error_context_stack = &spierrcontext;
foreach(query_list_list_item, query_list_list)
{
List *query_list = lfirst(query_list_list_item);
@@ -1270,12 +1310,19 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
res = _SPI_pquery(qdesc, true, useCurrentSnapshot,
queryTree->canSetTag ? tcount : 0);
if (res < 0)
return res;
goto fail;
CommandCounterIncrement();
}
}
}
fail:
/*
* Pop the error context stack
*/
error_context_stack = spierrcontext.previous;
return res;
}
@@ -1355,6 +1402,32 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit,
return res;
}
/*
* _SPI_error_callback
*
* Add context information when a query invoked via SPI fails
*/
static void
_SPI_error_callback(void *arg)
{
const char *query = (const char *) arg;
int syntaxerrposition;
/*
* If there is a syntax error position, convert to internal syntax error;
* otherwise treat the query as an item of context stack
*/
syntaxerrposition = geterrposition();
if (syntaxerrposition > 0)
{
errposition(0);
internalerrposition(syntaxerrposition);
internalerrquery(query);
}
else
errcontext("SQL query \"%s\"", query);
}
/*
* _SPI_cursor_operation()
*
@@ -1490,8 +1563,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
parentcxt = _SPI_current->procCxt;
else if (location == _SPI_CPLAN_TOPCXT)
parentcxt = TopMemoryContext;
else
/* (this case not currently used) */
else /* (this case not currently used) */
parentcxt = CurrentMemoryContext;
/*
@@ -1508,6 +1580,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
/* Copy the SPI plan into its own context */
newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
newplan->plancxt = plancxt;
newplan->query = pstrdup(plan->query);
newplan->qtlist = (List *) copyObject(plan->qtlist);
newplan->ptlist = (List *) copyObject(plan->ptlist);
newplan->nargs = plan->nargs;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.165 2004/03/17 20:48:42 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.166 2004/03/21 22:29:11 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -1917,7 +1917,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
* can finger the function that bad information came from.
*/
sqlerrcontext.callback = sql_inline_error_callback;
sqlerrcontext.arg = funcform;
sqlerrcontext.arg = func_tuple;
sqlerrcontext.previous = error_context_stack;
error_context_stack = &sqlerrcontext;
@@ -2146,7 +2146,27 @@ substitute_actual_parameters_mutator(Node *node,
static void
sql_inline_error_callback(void *arg)
{
Form_pg_proc funcform = (Form_pg_proc) arg;
HeapTuple func_tuple = (HeapTuple) arg;
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int syntaxerrposition;
/* If it's a syntax error, convert to internal syntax error report */
syntaxerrposition = geterrposition();
if (syntaxerrposition > 0)
{
bool isnull;
Datum tmp;
char *prosrc;
tmp = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_prosrc,
&isnull);
if (isnull)
elog(ERROR, "null prosrc");
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
errposition(0);
internalerrposition(syntaxerrposition);
internalerrquery(prosrc);
}
errcontext("SQL function \"%s\" during inlining",
NameStr(funcform->proname));

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.64 2003/11/29 19:51:52 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.65 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -439,6 +439,12 @@ pts_error_callback(void *arg)
const char *str = (const char *) arg;
errcontext("invalid type name \"%s\"", str);
/*
* Currently we just suppress any syntax error position report,
* rather than transforming to an "internal query" error. It's
* unlikely that a type name is complex enough to need positioning.
*/
errposition(0);
}
/*

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.395 2004/03/15 15:56:22 momjian Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.396 2004/03/21 22:29:11 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -2710,6 +2710,7 @@ PostgresMain(int argc, char *argv[], const char *username)
*/
MemoryContextSwitchTo(TopMemoryContext);
MemoryContextResetAndDeleteChildren(ErrorContext);
ActivePortal = NULL;
PortalContext = NULL;
QueryContext = NULL;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.76 2004/03/18 23:26:17 momjian Exp $
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.77 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,6 +23,13 @@
#include "utils/memutils.h"
/*
* ActivePortal is the currently executing Portal (the most closely nested,
* if there are several).
*/
Portal ActivePortal = NULL;
static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
DestReceiver *dest);
static long PortalRunSelect(Portal portal, bool forward, long count,
@@ -395,6 +402,7 @@ PortalRun(Portal portal, long count,
char *completionTag)
{
bool result;
Portal saveActivePortal;
MemoryContext savePortalContext;
MemoryContext saveQueryContext;
MemoryContext oldContext;
@@ -430,6 +438,8 @@ PortalRun(Portal portal, long count,
/*
* Set global portal context pointers.
*/
saveActivePortal = ActivePortal;
ActivePortal = portal;
savePortalContext = PortalContext;
PortalContext = PortalGetHeapMemory(portal);
saveQueryContext = QueryContext;
@@ -505,6 +515,7 @@ PortalRun(Portal portal, long count,
/* Mark portal not active */
portal->portalActive = false;
ActivePortal = saveActivePortal;
PortalContext = savePortalContext;
QueryContext = saveQueryContext;
@@ -922,6 +933,7 @@ PortalRunFetch(Portal portal,
DestReceiver *dest)
{
long result;
Portal saveActivePortal;
MemoryContext savePortalContext;
MemoryContext saveQueryContext;
MemoryContext oldContext;
@@ -945,6 +957,8 @@ PortalRunFetch(Portal portal,
/*
* Set global portal context pointers.
*/
saveActivePortal = ActivePortal;
ActivePortal = portal;
savePortalContext = PortalContext;
PortalContext = PortalGetHeapMemory(portal);
saveQueryContext = QueryContext;
@@ -969,6 +983,7 @@ PortalRunFetch(Portal portal,
/* Mark portal not active */
portal->portalActive = false;
ActivePortal = saveActivePortal;
PortalContext = savePortalContext;
QueryContext = saveQueryContext;

View File

@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.129 2004/03/19 02:23:59 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.130 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -112,6 +112,8 @@ typedef struct ErrorData
char *hint; /* hint message */
char *context; /* context message */
int cursorpos; /* cursor index into query string */
int internalpos; /* cursor index into internalquery */
char *internalquery; /* text of internally-generated query */
int saved_errno; /* errno at entry */
} ErrorData;
@@ -364,6 +366,8 @@ errfinish(int dummy,...)
pfree(edata->hint);
if (edata->context)
pfree(edata->context);
if (edata->internalquery)
pfree(edata->internalquery);
MemoryContextSwitchTo(oldcontext);
@@ -809,6 +813,83 @@ errposition(int cursorpos)
return 0; /* return value does not matter */
}
/*
* internalerrposition --- add internal cursor position to the current error
*/
int
internalerrposition(int cursorpos)
{
ErrorData *edata = &errordata[errordata_stack_depth];
/* we don't bother incrementing recursion_depth */
CHECK_STACK_DEPTH();
edata->internalpos = cursorpos;
return 0; /* return value does not matter */
}
/*
* internalerrquery --- add internal query text to the current error
*
* Can also pass NULL to drop the internal query text entry. This case
* is intended for use in error callback subroutines that are editorializing
* on the layout of the error report.
*/
int
internalerrquery(const char *query)
{
ErrorData *edata = &errordata[errordata_stack_depth];
/* we don't bother incrementing recursion_depth */
CHECK_STACK_DEPTH();
if (edata->internalquery)
{
pfree(edata->internalquery);
edata->internalquery = NULL;
}
if (query)
edata->internalquery = MemoryContextStrdup(ErrorContext, query);
return 0; /* return value does not matter */
}
/*
* geterrposition --- return the currently set error position (0 if none)
*
* This is only intended for use in error callback subroutines, since there
* is no other place outside elog.c where the concept is meaningful.
*/
int
geterrposition(void)
{
ErrorData *edata = &errordata[errordata_stack_depth];
/* we don't bother incrementing recursion_depth */
CHECK_STACK_DEPTH();
return edata->cursorpos;
}
/*
* getinternalerrposition --- same for internal error position
*
* This is only intended for use in error callback subroutines, since there
* is no other place outside elog.c where the concept is meaningful.
*/
int
getinternalerrposition(void)
{
ErrorData *edata = &errordata[errordata_stack_depth];
/* we don't bother incrementing recursion_depth */
CHECK_STACK_DEPTH();
return edata->internalpos;
}
/*
* elog_finish --- finish up for old-style API
@@ -1192,7 +1273,11 @@ send_message_to_server_log(ErrorData *edata)
append_with_tabs(&buf, gettext("missing error text"));
if (edata->cursorpos > 0)
appendStringInfo(&buf, gettext(" at character %d"), edata->cursorpos);
appendStringInfo(&buf, gettext(" at character %d"),
edata->cursorpos);
else if (edata->internalpos > 0)
appendStringInfo(&buf, gettext(" at character %d"),
edata->internalpos);
appendStringInfoChar(&buf, '\n');
@@ -1212,6 +1297,13 @@ send_message_to_server_log(ErrorData *edata)
append_with_tabs(&buf, edata->hint);
appendStringInfoChar(&buf, '\n');
}
if (edata->internalquery)
{
log_line_prefix(&buf);
appendStringInfoString(&buf, gettext("QUERY: "));
append_with_tabs(&buf, edata->internalquery);
appendStringInfoChar(&buf, '\n');
}
if (edata->context)
{
log_line_prefix(&buf);
@@ -1365,6 +1457,19 @@ send_message_to_frontend(ErrorData *edata)
pq_sendstring(&msgbuf, tbuf);
}
if (edata->internalpos > 0)
{
snprintf(tbuf, sizeof(tbuf), "%d", edata->internalpos);
pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_POSITION);
pq_sendstring(&msgbuf, tbuf);
}
if (edata->internalquery)
{
pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_QUERY);
pq_sendstring(&msgbuf, edata->internalquery);
}
if (edata->filename)
{
pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FILE);
@@ -1406,6 +1511,9 @@ send_message_to_frontend(ErrorData *edata)
if (edata->cursorpos > 0)
appendStringInfo(&buf, gettext(" at character %d"),
edata->cursorpos);
else if (edata->internalpos > 0)
appendStringInfo(&buf, gettext(" at character %d"),
edata->internalpos);
appendStringInfoChar(&buf, '\n');