Dear patchers,
Please find enclosed a new patch submission which take into account
comments by Tom Lane about version 1.
(1) the function is renamed as "localisation" does not mean what I meant.
(2) three dots "..." appear after and before the line when truncated.
However, I still put the line number that I found useful.
The new version validates for me against current cvs head.
Same comment as with version one, I don't know how to test the multi-byte
part.
Have a nice day,
--
Fabien Coelho - [EMAIL PROTECTED]
*** ./src/bin/psql/common.c.orig Thu Mar 11 15:42:17 2004
--- ./src/bin/psql/common.c Thu Mar 11 18:05:22 2004
***************
*** 345,350 ****
--- 345,506 ----
}
+ #define DISPLAY_SIZE (60)
+ #define MIN_RIGHT_CUT (10)
+
+ /* on errors, print syntax error position if available.
+ * the query is expected to be in the client encoding.
+ */
+ static void
+ ReportSyntaxErrorPosition(const PGresult *result, const char *query)
+ {
+ int loc = 0;
+ char * sp = PQresultErrorField(result, PG_DIAG_STATEMENT_POSITION);
+
+ if (sp && sscanf(sp, "%d", &loc)!=1)
+ {
+ psql_error("INTERNAL ERROR: unexpected statement position '%s'\n", sp);
+ return;
+ }
+
+ /* do we have something to show? */
+ if (query!=NULL && loc>0)
+ {
+ int clen, slen, i, * qidx, ibeg, iend, last_nl, loc_line;
+ char *wquery, *cursor;
+ bool beg_trunc, end_trunc;
+
+ /* (1) let us first compute a character index for the query. */
+
+ /* we need a safe allocation size... */
+ slen = strlen(query);
+
+ /* the last one is needed to store last char mb length */
+ qidx = (int*) palloc(sizeof(int)*(slen+1));
+
+ qidx[0] = 0;
+ for (i=1; query[qidx[i-1]]!='\0' && i<slen+1; i++)
+ qidx[i] = qidx[i-1] + PQmblen(&query[qidx[i-1]],
pset.encoding);
+
+ clen = i-1;
+
+ /* we must be at the end! */
+ psql_assert(query[qidx[clen]] == '\0');
+
+ /* our localisation index start at 0! it must be in the query! */
+ loc--;
+ psql_assert(loc>=0 && loc<clen);
+
+ /* (2) now we build a working copy of the query. */
+ wquery = (char*) palloc(sizeof(char)*(strlen(query)+1));
+ strcpy(wquery, query);
+
+ /* the character number of the last newline. */
+ last_nl = -1;
+ /* input line number of our syntax error. */
+ loc_line = 1;
+ /* first included char of extract. */
+ ibeg = 0;
+ /* last not-included char of extract. */
+ iend = clen;
+
+ /* (3) we clean wquery string from tabs, carriage return and new lines.
+ * extract error line number and begin and end indexes.
+ */
+ for (i=0; i<clen; i++)
+ {
+ /* how to find a '\t', a '\r' or a '\n'?
+ * I assume here that all encodings must be ascii compatible...
+ */
+ if ((qidx[i+1]-qidx[i]) == 1)
+ {
+ if (wquery[qidx[i]] == '\t')
+ wquery[qidx[i]] = ' ';
+ if (wquery[qidx[i]] == '\r' || wquery[qidx[i]] == '\n')
+ {
+ /* count lines */
+ if (wquery[qidx[i]]=='\n' && i<loc)
+ loc_line++;
+
+ /* set extract end. */
+ if (iend==clen && i>loc)
+ iend = i;
+
+ wquery[qidx[i]] = ' ';
+ last_nl = i;
+ }
+ }
+ /* set extract beginning. */
+ if (i==loc && last_nl!=-1)
+ ibeg = last_nl+1;
+ }
+
+ /* (4) if the line extracted is too long, we truncate it. */
+ beg_trunc = false;
+ end_trunc = false;
+ if (iend-ibeg > DISPLAY_SIZE)
+ {
+ /* we first truncate right if it is enough. */
+ if (ibeg+DISPLAY_SIZE > loc+MIN_RIGHT_CUT)
+ {
+ iend = ibeg+DISPLAY_SIZE;
+ end_trunc = true;
+ }
+ else
+ {
+ /* we truncate right. */
+ if (loc+MIN_RIGHT_CUT < iend)
+ {
+ iend = loc+MIN_RIGHT_CUT;
+ end_trunc = true;
+ }
+
+ /* still too long?
+ * we know that loc is at the end
+ * (loc+MIN_RIGHT_CUT>=iend from above)
+ * now we truncate left.
+ */
+ if (iend-ibeg > DISPLAY_SIZE)
+ {
+ ibeg = iend-DISPLAY_SIZE;
+ beg_trunc = true;
+ }
+ }
+ }
+
+ /* (5) if the line is too short, we could fix it here. */
+
+ /* the extract MUST contain the localisation! */
+ psql_assert(ibeg<=loc && loc<iend);
+
+ /* (6) string to show cursor position under the extract. */
+ cursor = (char *) palloc(loc-ibeg+2);
+ for (i=0; i<loc-ibeg; i++)
+ cursor[i] = ' ';
+ cursor[i++] = '^';
+ cursor[i++] = '\0';
+
+ /* (7) now we show the resulting message. */
+
+ /* fix extract length for fprintf. */
+ wquery[qidx[iend]] = '\0';
+
+ /* translator: %s%s%s is a query extract as 'id INTEGER NOT NUL ...' */
+ psql_error("QUERY: %s%s%s\n",
+ beg_trunc? "...": "", &wquery[qidx[ibeg]],
+ end_trunc? "...": "");
+
+ /* translator: %s%s is a cursor such as ' ^' */
+ psql_error("QUERY: %s%s on line %d\n",
+ beg_trunc? " ": "",cursor, loc_line);
+
+ /* clean */
+ pfree(wquery);
+ pfree(qidx);
+ pfree(cursor);
+ }
+ }
+
/*
* AcceptResult
*
***************
*** 355,361 ****
* Returns true for valid result, false for error state.
*/
static bool
! AcceptResult(const PGresult *result)
{
bool OK = true;
--- 511,517 ----
* Returns true for valid result, false for error state.
*/
static bool
! AcceptResult(const PGresult *result, const char * query)
{
bool OK = true;
***************
*** 386,391 ****
--- 542,548 ----
if (!OK)
{
psql_error("%s", PQerrorMessage(pset.db));
+ ReportSyntaxErrorPosition(result, query);
CheckConnection();
}
***************
*** 449,455 ****
res = PQexec(pset.db, query);
! if (!AcceptResult(res) && res)
{
PQclear(res);
res = NULL;
--- 606,612 ----
res = PQexec(pset.db, query);
! if (!AcceptResult(res, query) && res)
{
PQclear(res);
res = NULL;
***************
*** 695,701 ****
results = PQexec(pset.db, query);
/* these operations are included in the timing result: */
! OK = (AcceptResult(results) && ProcessCopyResult(results));
if (pset.timing)
GETTIMEOFDAY(&after);
--- 852,858 ----
results = PQexec(pset.db, query);
/* these operations are included in the timing result: */
! OK = (AcceptResult(results, query) && ProcessCopyResult(results));
if (pset.timing)
GETTIMEOFDAY(&after);
*** ./src/test/regress/expected/constraints.out.orig Thu Mar 11 16:08:12 2004
--- ./src/test/regress/expected/constraints.out Thu Mar 11 16:08:20 2004
***************
*** 46,56 ****
--- 46,60 ----
-- test for extraneous comma
CREATE TABLE error_tbl (i int DEFAULT (100, ));
ERROR: syntax error at or near "," at character 43
+ QUERY: CREATE TABLE error_tbl (i int DEFAULT (100, ));
+ QUERY: ^ on line 1
-- this will fail because gram.y uses b_expr not a_expr for defaults,
-- to avoid a shift/reduce conflict that arises from NOT NULL being
-- part of the column definition syntax:
CREATE TABLE error_tbl (b1 bool DEFAULT 1 IN (1, 2));
ERROR: syntax error at or near "IN" at character 43
+ QUERY: CREATE TABLE error_tbl (b1 bool DEFAULT 1 IN (1, 2));
+ QUERY: ^ on line 1
-- this should work, however:
CREATE TABLE error_tbl (b1 bool DEFAULT (1 IN (1, 2)));
DROP TABLE error_tbl;
*** ./src/test/regress/expected/errors.out.orig Thu Mar 11 16:08:29 2004
--- ./src/test/regress/expected/errors.out Thu Mar 11 17:51:41 2004
***************
*** 23,34 ****
--- 23,38 ----
-- missing relation name
select;
ERROR: syntax error at or near ";" at character 7
+ QUERY: select;
+ QUERY: ^ on line 1
-- no such relation
select * from nonesuch;
ERROR: relation "nonesuch" does not exist
-- missing target list
select from pg_database;
ERROR: syntax error at or near "from" at character 8
+ QUERY: select from pg_database;
+ QUERY: ^ on line 1
-- bad name in target list
select nonesuch from pg_database;
ERROR: column "nonesuch" does not exist
***************
*** 41,46 ****
--- 45,52 ----
-- bad select distinct on syntax, distinct attribute missing
select distinct on (foobar) from pg_database;
ERROR: syntax error at or near "from" at character 29
+ QUERY: select distinct on (foobar) from pg_database;
+ QUERY: ^ on line 1
-- bad select distinct on syntax, distinct attribute not in target list
select distinct on (foobar) * from pg_database;
ERROR: column "foobar" does not exist
***************
*** 50,55 ****
--- 56,63 ----
-- missing relation name (this had better not wildcard!)
delete from;
ERROR: syntax error at or near ";" at character 12
+ QUERY: delete from;
+ QUERY: ^ on line 1
-- no such relation
delete from nonesuch;
ERROR: relation "nonesuch" does not exist
***************
*** 59,64 ****
--- 67,74 ----
-- missing relation name (this had better not wildcard!)
drop table;
ERROR: syntax error at or near ";" at character 11
+ QUERY: drop table;
+ QUERY: ^ on line 1
-- no such relation
drop table nonesuch;
ERROR: table "nonesuch" does not exist
***************
*** 69,74 ****
--- 79,86 ----
-- missing relation name
alter table rename;
ERROR: syntax error at or near ";" at character 19
+ QUERY: alter table rename;
+ QUERY: ^ on line 1
-- no such relation
alter table nonesuch rename to newnonesuch;
ERROR: relation "nonesuch" does not exist
***************
*** 123,131 ****
--- 135,147 ----
-- missing index name
drop index;
ERROR: syntax error at or near ";" at character 11
+ QUERY: drop index;
+ QUERY: ^ on line 1
-- bad index name
drop index 314159;
ERROR: syntax error at or near "314159" at character 12
+ QUERY: drop index 314159;
+ QUERY: ^ on line 1
-- no such index
drop index nonesuch;
ERROR: index "nonesuch" does not exist
***************
*** 135,146 ****
--- 151,168 ----
-- missing aggregate name
drop aggregate;
ERROR: syntax error at or near ";" at character 15
+ QUERY: drop aggregate;
+ QUERY: ^ on line 1
-- missing aggregate type
drop aggregate newcnt1;
ERROR: syntax error at or near ";" at character 23
+ QUERY: drop aggregate newcnt1;
+ QUERY: ^ on line 1
-- bad aggregate name
drop aggregate 314159 (int);
ERROR: syntax error at or near "314159" at character 16
+ QUERY: drop aggregate 314159 (int);
+ QUERY: ^ on line 1
-- bad aggregate type
drop aggregate newcnt (nonesuch);
ERROR: type "nonesuch" does not exist
***************
*** 156,164 ****
--- 178,190 ----
-- missing function name
drop function ();
ERROR: syntax error at or near "(" at character 15
+ QUERY: drop function ();
+ QUERY: ^ on line 1
-- bad function name
drop function 314159();
ERROR: syntax error at or near "314159" at character 15
+ QUERY: drop function 314159();
+ QUERY: ^ on line 1
-- no such function
drop function nonesuch();
ERROR: function nonesuch() does not exist
***************
*** 168,176 ****
--- 194,206 ----
-- missing type name
drop type;
ERROR: syntax error at or near ";" at character 10
+ QUERY: drop type;
+ QUERY: ^ on line 1
-- bad type name
drop type 314159;
ERROR: syntax error at or near "314159" at character 11
+ QUERY: drop type 314159;
+ QUERY: ^ on line 1
-- no such type
drop type nonesuch;
ERROR: type "nonesuch" does not exist
***************
*** 180,200 ****
--- 210,242 ----
-- missing everything
drop operator;
ERROR: syntax error at or near ";" at character 14
+ QUERY: drop operator;
+ QUERY: ^ on line 1
-- bad operator name
drop operator equals;
ERROR: syntax error at or near ";" at character 21
+ QUERY: drop operator equals;
+ QUERY: ^ on line 1
-- missing type list
drop operator ===;
ERROR: syntax error at or near ";" at character 18
+ QUERY: drop operator ===;
+ QUERY: ^ on line 1
-- missing parentheses
drop operator int4, int4;
ERROR: syntax error at or near "," at character 19
+ QUERY: drop operator int4, int4;
+ QUERY: ^ on line 1
-- missing operator name
drop operator (int4, int4);
ERROR: syntax error at or near "(" at character 15
+ QUERY: drop operator (int4, int4);
+ QUERY: ^ on line 1
-- missing type list contents
drop operator === ();
ERROR: syntax error at or near ")" at character 20
+ QUERY: drop operator === ();
+ QUERY: ^ on line 1
-- no such operator
drop operator === (int4);
ERROR: missing argument
***************
*** 209,214 ****
--- 251,258 ----
-- no such type1
drop operator = ( , int4);
ERROR: syntax error at or near "," at character 19
+ QUERY: drop operator = ( , int4);
+ QUERY: ^ on line 1
-- no such type1
drop operator = (nonesuch, int4);
ERROR: type nonesuch does not exist
***************
*** 218,244 ****
--- 262,300 ----
-- no such type2
drop operator = (int4, );
ERROR: syntax error at or near ")" at character 24
+ QUERY: drop operator = (int4, );
+ QUERY: ^ on line 1
--
-- DROP RULE
-- missing rule name
drop rule;
ERROR: syntax error at or near ";" at character 10
+ QUERY: drop rule;
+ QUERY: ^ on line 1
-- bad rule name
drop rule 314159;
ERROR: syntax error at or near "314159" at character 11
+ QUERY: drop rule 314159;
+ QUERY: ^ on line 1
-- no such rule
drop rule nonesuch on noplace;
ERROR: relation "noplace" does not exist
-- bad keyword
drop tuple rule nonesuch;
ERROR: syntax error at or near "tuple" at character 6
+ QUERY: drop tuple rule nonesuch;
+ QUERY: ^ on line 1
-- no such rule
drop instance rule nonesuch on noplace;
ERROR: syntax error at or near "instance" at character 6
+ QUERY: drop instance rule nonesuch on noplace;
+ QUERY: ^ on line 1
-- no such rule
drop rewrite rule nonesuch;
ERROR: syntax error at or near "rewrite" at character 6
+ QUERY: drop rewrite rule nonesuch;
+ QUERY: ^ on line 1
--
-- Check that division-by-zero is properly caught.
--
***************
*** 264,266 ****
--- 320,378 ----
ERROR: division by zero
select 1/0::float4;
ERROR: division by zero
+ --
+ -- Error Localisation
+ --
+ ;
+ xxx;
+ ERROR: syntax error at or near "xxx" at character 1
+ QUERY: xxx;
+ QUERY: ^ on line 1
+ CREATE foo;
+ ERROR: syntax error at or near "foo" at character 8
+ QUERY: CREATE foo;
+ QUERY: ^ on line 1
+ CREATE TABLE ;
+ ERROR: syntax error at or near ";" at character 14
+ QUERY: CREATE TABLE ;
+ QUERY: ^ on line 1
+ INSERT INTO foo VALUES(123) foo;
+ ERROR: syntax error at or near "foo" at character 29
+ QUERY: INSERT INTO foo VALUES(123) foo;
+ QUERY: ^ on line 1
+ INSERT INTO 123
+ VALUES(123);
+ ERROR: syntax error at or near "123" at character 13
+ QUERY: INSERT INTO 123
+ QUERY: ^ on line 1
+ INSERT INTO foo
+ VALUES(123) 123
+ ;
+ ERROR: syntax error at or near "123" at character 30
+ QUERY: VALUES(123) 123
+ QUERY: ^ on line 2
+ -- with a tab
+ CREATE TABLE foo
+ (id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY,
+ id3 INTEGER NOT NUL,
+ id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL);
+ ERROR: syntax error at or near "NUL" at character 94
+ QUERY: id3 INTEGER NOT NUL,
+ QUERY: ^ on line 3
+ -- long line to be truncated on the left
+ CREATE TABLE foo(id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER
NOT NUL,
+ id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL);
+ ERROR: syntax error at or near "NUL" at character 90
+ QUERY: ...T NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER NOT NUL,
+ QUERY: ^ on line 1
+ -- long line to be truncated on the right
+ CREATE TABLE foo(
+ id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL, id INT4
UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY);
+ ERROR: syntax error at or near "NUL" at character 35
+ QUERY: id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQ...
+ QUERY: ^ on line 2
+ -- long line to be truncated both ways
+ CREATE TABLE foo(id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER
NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL);
+ ERROR: syntax error at or near "NUL" at character 90
+ QUERY: ...L, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER NOT NUL, id4 I...
+ QUERY: ^ on line 1
*** ./src/test/regress/expected/strings.out.orig Thu Mar 11 16:01:29 2004
--- ./src/test/regress/expected/strings.out Thu Mar 11 16:01:43 2004
***************
*** 19,24 ****
--- 19,26 ----
' - third line'
AS "Illegal comment within continuation";
ERROR: syntax error at or near "' - third line'" at character 75
+ QUERY: ' - third line'
+ QUERY: ^ on line 3
--
-- test conversions between various string types
-- E021-10 implicit casting among the character data types
*** ./src/test/regress/output/create_function_1.source.orig Mon Jul 28 02:09:16
2003
--- ./src/test/regress/output/create_function_1.source Thu Mar 11 16:07:03 2004
***************
*** 55,60 ****
--- 55,62 ----
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
AS 'not even SQL';
ERROR: syntax error at or near "not" at character 1
+ QUERY: CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+ QUERY: ^ on line 1
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
AS 'SELECT 1, 2, 3;';
ERROR: return type mismatch in function declared to return integer
*** ./src/test/regress/sql/errors.sql.orig Thu Mar 11 15:45:47 2004
--- ./src/test/regress/sql/errors.sql Thu Mar 11 15:46:59 2004
***************
*** 276,278 ****
--- 276,316 ----
select 1::float4/0;
select 1/0::float4;
+
+
+ --
+ -- Error Localisation
+ --
+ ;
+
+ xxx;
+
+ CREATE foo;
+
+ CREATE TABLE ;
+
+ INSERT INTO foo VALUES(123) foo;
+
+ INSERT INTO 123
+ VALUES(123);
+
+ INSERT INTO foo
+ VALUES(123) 123
+ ;
+
+ -- with a tab
+ CREATE TABLE foo
+ (id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY,
+ id3 INTEGER NOT NUL,
+ id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL);
+
+ -- long line to be truncated on the left
+ CREATE TABLE foo(id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER
NOT NUL,
+ id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL);
+
+ -- long line to be truncated on the right
+ CREATE TABLE foo(
+ id3 INTEGER NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL, id INT4
UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY);
+
+ -- long line to be truncated both ways
+ CREATE TABLE foo(id INT4 UNIQUE NOT NULL, id2 TEXT NOT NULL PRIMARY KEY, id3 INTEGER
NOT NUL, id4 INT4 UNIQUE NOT NULL, id5 TEXT UNIQUE NOT NULL);
---------------------------(end of broadcast)---------------------------
TIP 6: Have you searched our list archives?
http://archives.postgresql.org