aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/pg_proc.c215
-rw-r--r--src/backend/commands/portalcmds.c6
-rw-r--r--src/backend/executor/functions.c41
-rw-r--r--src/backend/executor/spi.c167
-rw-r--r--src/backend/optimizer/util/clauses.c26
-rw-r--r--src/backend/parser/parse_type.c8
-rw-r--r--src/backend/tcop/postgres.c3
-rw-r--r--src/backend/tcop/pquery.c17
-rw-r--r--src/backend/utils/error/elog.c112
-rw-r--r--src/bin/psql/command.c12
-rw-r--r--src/bin/psql/common.c23
-rw-r--r--src/bin/psql/settings.h6
-rw-r--r--src/bin/psql/startup.c3
-rw-r--r--src/include/catalog/pg_proc.h4
-rw-r--r--src/include/executor/spi_priv.h9
-rw-r--r--src/include/postgres_ext.h4
-rw-r--r--src/include/tcop/pquery.h5
-rw-r--r--src/include/utils/elog.h8
-rw-r--r--src/interfaces/libpq/fe-protocol3.c15
-rw-r--r--src/pl/plpgsql/src/pl_comp.c22
-rw-r--r--src/pl/plpgsql/src/scan.l18
-rw-r--r--src/test/regress/expected/triggers.out2
-rw-r--r--src/test/regress/output/create_function_1.source5
23 files changed, 614 insertions, 117 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 3ea2af44d3e..6fe64eadd0d 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -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;
+
+ /*
+ * Nothing to do unless we are dealing with a syntax error that has
+ * a cursor position.
+ *
+ * Some PLs may prefer to report the error position as an internal
+ * error to begin with, so check that too.
+ */
+ 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)
+{
/*
- * 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...
+ * 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.
*
- * In the meantime, put in a CONTEXT entry that can cue clients
- * not to trust the syntax error position completely.
+ * We do the comparison a character at a time, not a byte at a time,
+ * so that we can do the correct cursorpos math.
*/
- errcontext("SQL function \"%s\"",
- NameStr(proc->proname));
+ 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;
}
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 34748fa77e1..855c9391c18 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -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;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 6ffc8875f19..8dec6131fb4 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -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);
}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index fb44d9d56fd..1fb60fff02d 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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;
- }
-
- 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;
+ _SPI_plan *spiplan = (_SPI_plan *) plan;
+ List *qtlist;
+
+ 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;
@@ -1100,6 +1107,14 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
_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.
*/
raw_parsetree_list = pg_parse_query(src);
@@ -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;
}
@@ -1356,6 +1403,32 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit,
}
/*
+ * _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()
*
* Do a FETCH or MOVE in a cursor
@@ -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;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 1487aec453d..c006cd49a1f 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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));
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 7695bc49e51..e6e8b00cda4 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -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);
}
/*
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 659eda80c85..91442d49e6e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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;
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 132afd3c6eb..c213182ad1f 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -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;
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index e826e6d99ee..8569b6ce5cf 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -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');
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 6b03e35c6f6..97b338efea7 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.113 2004/02/19 19:40:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.114 2004/03/21 22:29:11 tgl Exp $
*/
#include "postgres_fe.h"
#include "command.h"
@@ -1042,18 +1042,20 @@ SyncVerbosityVariable(void)
"default", "terse", "verbose", NULL))
{
case 1: /* default */
- PQsetErrorVerbosity(pset.db, PQERRORS_DEFAULT);
+ pset.verbosity = PQERRORS_DEFAULT;
break;
case 2: /* terse */
- PQsetErrorVerbosity(pset.db, PQERRORS_TERSE);
+ pset.verbosity = PQERRORS_TERSE;
break;
case 3: /* verbose */
- PQsetErrorVerbosity(pset.db, PQERRORS_VERBOSE);
+ pset.verbosity = PQERRORS_VERBOSE;
break;
default: /* not set or unrecognized value */
- PQsetErrorVerbosity(pset.db, PQERRORS_DEFAULT);
+ pset.verbosity = PQERRORS_DEFAULT;
break;
}
+
+ PQsetErrorVerbosity(pset.db, pset.verbosity);
}
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index 2a6be545caf..3b1a1228c1e 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.84 2004/03/15 10:41:26 ishii Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.85 2004/03/21 22:29:11 tgl Exp $
*/
#include "postgres_fe.h"
#include "common.h"
@@ -364,18 +364,19 @@ ReportSyntaxErrorPosition(const PGresult *result, const char *query)
bool beg_trunc, end_trunc;
PQExpBufferData msg;
- if (query == NULL)
- return; /* nothing to do */
+ if (pset.verbosity == PQERRORS_TERSE)
+ return;
+
sp = PQresultErrorField(result, PG_DIAG_STATEMENT_POSITION);
if (sp == NULL)
- return; /* no syntax error location */
- /*
- * We punt if the report contains any CONTEXT. This typically means that
- * the syntax error is from inside a function, and the cursor position
- * is not relevant to the original query string.
- */
- if (PQresultErrorField(result, PG_DIAG_CONTEXT) != NULL)
- return;
+ {
+ sp = PQresultErrorField(result, PG_DIAG_INTERNAL_POSITION);
+ if (sp == NULL)
+ return; /* no syntax error */
+ query = PQresultErrorField(result, PG_DIAG_INTERNAL_QUERY);
+ }
+ if (query == NULL)
+ return; /* nothing to reference location to */
if (sscanf(sp, "%d", &loc) != 1)
{
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 9c16402ac4c..e28383b16be 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/settings.h,v 1.16 2003/11/29 19:52:07 pgsql Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/settings.h,v 1.17 2004/03/21 22:29:11 tgl Exp $
*/
#ifndef SETTINGS_H
#define SETTINGS_H
@@ -36,8 +36,6 @@ typedef struct _psqlSettings
bool notty; /* stdin or stdout is not a tty (as
* determined on startup) */
- bool useReadline; /* use libreadline routines */
- bool useHistory;
bool getPassword; /* prompt the user for a username and
* password */
FILE *cur_cmd_source; /* describe the status of the current main
@@ -49,6 +47,8 @@ typedef struct _psqlSettings
unsigned lineno; /* also for error reporting */
bool timing; /* enable timing of all queries */
+
+ PGVerbosity verbosity; /* current error verbosity level */
} PsqlSettings;
extern PsqlSettings pset;
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 400f7d45786..03f4e97d918 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.85 2004/02/19 19:40:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.86 2004/03/21 22:29:11 tgl Exp $
*/
#include "postgres_fe.h"
@@ -143,6 +143,7 @@ main(int argc, char *argv[])
/* Default values for variables that are used in noninteractive cases */
SetVariableBool(pset.vars, "AUTOCOMMIT");
SetVariable(pset.vars, "VERBOSITY", "default");
+ pset.verbosity = PQERRORS_DEFAULT;
pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f3b70f31ec7..9112d9df9da 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.320 2004/02/14 20:16:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.321 2004/03/21 22:29:11 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@@ -3533,4 +3533,6 @@ extern Oid ProcedureCreate(const char *procedureName,
extern void check_sql_fn_retval(Oid rettype, char fn_typtype,
List *queryTreeList);
+extern bool function_parse_error_transpose(const char *prosrc);
+
#endif /* PG_PROC_H */
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
index 6c5c19994d5..dcafa1ccb9a 100644
--- a/src/include/executor/spi_priv.h
+++ b/src/include/executor/spi_priv.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.17 2003/11/29 22:41:01 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.18 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -27,11 +27,10 @@ typedef struct
typedef struct
{
- /*
- * context containing _SPI_plan itself as well as subsidiary
- * structures
- */
+ /* Context containing _SPI_plan itself as well as subsidiary data */
MemoryContext plancxt;
+ /* Original query string (used for error reporting) */
+ const char *query;
/* List of List of querytrees; one sublist per original parsetree */
List *qtlist;
/* List of plan trees --- length == # of querytrees, but flat list */
diff --git a/src/include/postgres_ext.h b/src/include/postgres_ext.h
index 235895c61f1..8f235f698d1 100644
--- a/src/include/postgres_ext.h
+++ b/src/include/postgres_ext.h
@@ -15,7 +15,7 @@
* use header files that are otherwise internal to Postgres to interface
* with the backend.
*
- * $PostgreSQL: pgsql/src/include/postgres_ext.h,v 1.14 2003/11/29 22:40:53 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/postgres_ext.h,v 1.15 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -59,6 +59,8 @@ typedef unsigned int Oid;
#define PG_DIAG_MESSAGE_DETAIL 'D'
#define PG_DIAG_MESSAGE_HINT 'H'
#define PG_DIAG_STATEMENT_POSITION 'P'
+#define PG_DIAG_INTERNAL_POSITION 'p'
+#define PG_DIAG_INTERNAL_QUERY 'q'
#define PG_DIAG_CONTEXT 'W'
#define PG_DIAG_SOURCE_FILE 'F'
#define PG_DIAG_SOURCE_LINE 'L'
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index baba06438ff..e2efd3d5e30 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.30 2003/11/29 22:41:14 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/pquery.h,v 1.31 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,6 +17,9 @@
#include "utils/portal.h"
+extern DLLIMPORT Portal ActivePortal;
+
+
extern void ProcessQuery(Query *parsetree,
Plan *plan,
ParamListInfo params,
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index f71881547eb..24db6696e66 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.66 2004/03/15 15:56:28 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.67 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -132,6 +132,12 @@ __attribute__((format(printf, 1, 2)));
extern int errfunction(const char *funcname);
extern int errposition(int cursorpos);
+extern int internalerrposition(int cursorpos);
+extern int internalerrquery(const char *query);
+
+extern int geterrposition(void);
+extern int getinternalerrposition(void);
+
/*----------
* Old-style error reporting API: to be used in this way:
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index cfdd97cf737..1f9621a9b17 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.11 2003/12/28 17:43:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.12 2004/03/21 22:29:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -640,6 +640,16 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
/* translator: %s represents a digit string */
appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), val);
}
+ else
+ {
+ val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
+ if (val)
+ {
+ /* translator: %s represents a digit string */
+ appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
+ val);
+ }
+ }
appendPQExpBufferChar(&workBuf, '\n');
if (conn->verbosity != PQERRORS_TERSE)
{
@@ -649,6 +659,9 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
if (val)
appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"), val);
+ val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
+ if (val)
+ appendPQExpBuffer(&workBuf, libpq_gettext("QUERY: %s\n"), val);
val = PQresultErrorField(res, PG_DIAG_CONTEXT);
if (val)
appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"), val);
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 46e9ca925b7..8c930de9c23 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.74 2004/03/19 18:58:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.75 2004/03/21 22:29:11 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -270,7 +270,6 @@ do_compile(FunctionCallInfo fcinfo,
elog(ERROR, "null prosrc");
proc_source = DatumGetCString(DirectFunctionCall1(textout, prosrcdatum));
plpgsql_scanner_init(proc_source, functype);
- pfree(proc_source);
plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
plpgsql_error_lineno = 0;
@@ -279,7 +278,7 @@ do_compile(FunctionCallInfo fcinfo,
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpgsql_compile_error_callback;
- plerrcontext.arg = NULL;
+ plerrcontext.arg = forValidator ? proc_source : (char *) NULL;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
@@ -714,6 +713,7 @@ do_compile(FunctionCallInfo fcinfo,
elog(ERROR, "plpgsql parser returned %d", parse_rc);
plpgsql_scanner_finish();
+ pfree(proc_source);
/*
* If that was successful, complete the functions info.
@@ -749,10 +749,26 @@ do_compile(FunctionCallInfo fcinfo,
/*
* error context callback to let us supply a call-stack traceback
+ *
+ * If we are validating, the function source is passed as argument.
*/
static void
plpgsql_compile_error_callback(void *arg)
{
+ if (arg)
+ {
+ /*
+ * Try to convert syntax error position to reference text of
+ * original CREATE FUNCTION command.
+ */
+ if (function_parse_error_transpose((const char *) arg))
+ return;
+ /*
+ * Done if a syntax error position was reported; otherwise we
+ * have to fall back to a "near line N" report.
+ */
+ }
+
if (plpgsql_error_funcname)
errcontext("compile of PL/pgSQL function \"%s\" near line %d",
plpgsql_error_funcname, plpgsql_error_lineno);
diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l
index 3e6ee8fd187..077efe6671e 100644
--- a/src/pl/plpgsql/src/scan.l
+++ b/src/pl/plpgsql/src/scan.l
@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.33 2004/03/19 18:58:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.34 2004/03/21 22:29:11 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -51,6 +51,8 @@
static YY_BUFFER_STATE scanbufhandle;
static char *scanbuf;
+static const char *scanstr; /* original input string */
+
static int scanner_functype;
static int scanner_typereported;
static int pushback_token;
@@ -431,7 +433,8 @@ plpgsql_yyerror(const char *message)
(errcode(ERRCODE_SYNTAX_ERROR),
/* translator: %s is typically "syntax error" */
errmsg("%s at end of input", message),
- errposition(cursorpos)));
+ internalerrposition(cursorpos),
+ internalerrquery(scanstr)));
}
else
{
@@ -439,7 +442,8 @@ plpgsql_yyerror(const char *message)
(errcode(ERRCODE_SYNTAX_ERROR),
/* translator: first %s is typically "syntax error" */
errmsg("%s at or near \"%s\"", message, loc),
- errposition(cursorpos)));
+ internalerrposition(cursorpos),
+ internalerrquery(scanstr)));
}
}
@@ -467,6 +471,10 @@ plpgsql_scanner_lineno(void)
/*
* Called before any actual parsing is done
+ *
+ * Note: the passed "str" must remain valid until plpgsql_scanner_finish().
+ * Although it is not fed directly to flex, we need the original string
+ * to cite in error messages.
*/
void
plpgsql_scanner_init(const char *str, int functype)
@@ -490,7 +498,9 @@ plpgsql_scanner_init(const char *str, int functype)
scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
/* Other setup */
- scanner_functype = functype;
+ scanstr = str;
+
+ scanner_functype = functype;
scanner_typereported = 0;
have_pushback_token = false;
diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out
index c44a81a75f8..870af40c069 100644
--- a/src/test/regress/expected/triggers.out
+++ b/src/test/regress/expected/triggers.out
@@ -85,12 +85,14 @@ DETAIL: Trigger "check_fkeys_pkey2_exist" found tuple referencing non-existent
delete from pkeys where pkey1 = 30 and pkey2 = '3';
NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys are deleted
ERROR: "check_fkeys2_fkey_restrict": tuple is referenced in "fkeys"
+CONTEXT: SQL query "delete from fkeys2 where fkey21 = $1 and fkey22 = $2 "
delete from pkeys where pkey1 = 40 and pkey2 = '4';
NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys are deleted
NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys2 are deleted
update pkeys set pkey1 = 7, pkey2 = '70' where pkey1 = 50 and pkey2 = '5';
NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys are deleted
ERROR: "check_fkeys2_fkey_restrict": tuple is referenced in "fkeys"
+CONTEXT: SQL query "delete from fkeys2 where fkey21 = $1 and fkey22 = $2 "
update pkeys set pkey1 = 7, pkey2 = '70' where pkey1 = 10 and pkey2 = '1';
NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys are deleted
NOTICE: check_pkeys_fkey_cascade: 1 tuple(s) of fkeys2 are deleted
diff --git a/src/test/regress/output/create_function_1.source b/src/test/regress/output/create_function_1.source
index e3886febaa9..62fcb01d093 100644
--- a/src/test/regress/output/create_function_1.source
+++ b/src/test/regress/output/create_function_1.source
@@ -55,8 +55,9 @@ DETAIL: Actual return type is "unknown".
CONTEXT: SQL function "test1"
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
AS 'not even SQL';
-ERROR: syntax error at or near "not" at character 1
-CONTEXT: SQL function "test1"
+ERROR: syntax error at or near "not" at character 62
+LINE 2: AS 'not even SQL';
+ ^
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
AS 'SELECT 1, 2, 3;';
ERROR: return type mismatch in function declared to return integer