3
3
*
4
4
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
5
5
*
6
- * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.82 2004/01/25 03:07:22 neilc Exp $
6
+ * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.83 2004/03/14 04:25:17 tgl Exp $
7
7
*/
8
8
#include "postgres_fe.h"
9
9
#include "common.h"
@@ -345,6 +345,216 @@ ResetCancelConn(void)
345
345
}
346
346
347
347
348
+ /*
349
+ * on errors, print syntax error position if available.
350
+ *
351
+ * the query is expected to be in the client encoding.
352
+ */
353
+ static void
354
+ ReportSyntaxErrorPosition (const PGresult * result , const char * query )
355
+ {
356
+ #define DISPLAY_SIZE 60 /* screen width limit, in screen cols */
357
+ #define MIN_RIGHT_CUT 10 /* try to keep this far away from EOL */
358
+
359
+ int loc = 0 ;
360
+ const char * sp ;
361
+ int clen , slen , i , * qidx , * scridx , qoffset , scroffset , ibeg , iend ,
362
+ loc_line ;
363
+ char * wquery ;
364
+ bool beg_trunc , end_trunc ;
365
+ PQExpBufferData msg ;
366
+
367
+ if (query == NULL )
368
+ return ; /* nothing to do */
369
+ sp = PQresultErrorField (result , PG_DIAG_STATEMENT_POSITION );
370
+ if (sp == NULL )
371
+ return ; /* no syntax error location */
372
+ /*
373
+ * We punt if the report contains any CONTEXT. This typically means that
374
+ * the syntax error is from inside a function, and the cursor position
375
+ * is not relevant to the original query string.
376
+ */
377
+ if (PQresultErrorField (result , PG_DIAG_CONTEXT ) != NULL )
378
+ return ;
379
+
380
+ if (sscanf (sp , "%d" , & loc ) != 1 )
381
+ {
382
+ psql_error ("INTERNAL ERROR: unexpected statement position \"%s\"\n" ,
383
+ sp );
384
+ return ;
385
+ }
386
+
387
+ /* Make a writable copy of the query, and a buffer for messages. */
388
+ wquery = pg_strdup (query );
389
+
390
+ initPQExpBuffer (& msg );
391
+
392
+ /*
393
+ * The returned cursor position is measured in logical characters.
394
+ * Each character might occupy multiple physical bytes in the string,
395
+ * and in some Far Eastern character sets it might take more than one
396
+ * screen column as well. We compute the starting byte offset and
397
+ * starting screen column of each logical character, and store these
398
+ * in qidx[] and scridx[] respectively.
399
+ */
400
+
401
+ /* we need a safe allocation size... */
402
+ slen = strlen (query ) + 1 ;
403
+
404
+ qidx = (int * ) pg_malloc (slen * sizeof (int ));
405
+ scridx = (int * ) pg_malloc (slen * sizeof (int ));
406
+
407
+ qoffset = 0 ;
408
+ scroffset = 0 ;
409
+ for (i = 0 ; query [qoffset ] != '\0' ; i ++ )
410
+ {
411
+ qidx [i ] = qoffset ;
412
+ scridx [i ] = scroffset ;
413
+ scroffset += 1 ; /* XXX fix me when we have screen width info */
414
+ qoffset += PQmblen (& query [qoffset ], pset .encoding );
415
+ }
416
+ qidx [i ] = qoffset ;
417
+ scridx [i ] = scroffset ;
418
+ clen = i ;
419
+ psql_assert (clen < slen );
420
+
421
+ /* convert loc to zero-based offset in qidx/scridx arrays */
422
+ loc -- ;
423
+
424
+ /* do we have something to show? */
425
+ if (loc >= 0 && loc <= clen )
426
+ {
427
+ /* input line number of our syntax error. */
428
+ loc_line = 1 ;
429
+ /* first included char of extract. */
430
+ ibeg = 0 ;
431
+ /* last-plus-1 included char of extract. */
432
+ iend = clen ;
433
+
434
+ /*
435
+ * Replace tabs with spaces in the writable copy. (Later we might
436
+ * want to think about coping with their variable screen width,
437
+ * but not today.)
438
+ *
439
+ * Extract line number and begin and end indexes of line containing
440
+ * error location. There will not be any newlines or carriage
441
+ * returns in the selected extract.
442
+ */
443
+ for (i = 0 ; i < clen ; i ++ )
444
+ {
445
+ /* character length must be 1 or it's not ASCII */
446
+ if ((qidx [i + 1 ]- qidx [i ]) == 1 )
447
+ {
448
+ if (wquery [qidx [i ]] == '\t' )
449
+ wquery [qidx [i ]] = ' ' ;
450
+ else if (wquery [qidx [i ]] == '\r' || wquery [qidx [i ]] == '\n' )
451
+ {
452
+ if (i < loc )
453
+ {
454
+ /*
455
+ * count lines before loc. Each \r or \n counts
456
+ * as a line except when \r \n appear together.
457
+ */
458
+ if (wquery [qidx [i ]] == '\r' ||
459
+ i == 0 ||
460
+ (qidx [i ]- qidx [i - 1 ]) != 1 ||
461
+ wquery [qidx [i - 1 ]] != '\r' )
462
+ loc_line ++ ;
463
+ /* extract beginning = last line start before loc. */
464
+ ibeg = i + 1 ;
465
+ }
466
+ else
467
+ {
468
+ /* set extract end. */
469
+ iend = i ;
470
+ /* done scanning. */
471
+ break ;
472
+ }
473
+ }
474
+ }
475
+ }
476
+
477
+ /* If the line extracted is too long, we truncate it. */
478
+ beg_trunc = false;
479
+ end_trunc = false;
480
+ if (scridx [iend ]- scridx [ibeg ] > DISPLAY_SIZE )
481
+ {
482
+ /*
483
+ * We first truncate right if it is enough. This code might
484
+ * be off a space or so on enforcing MIN_RIGHT_CUT if there's
485
+ * a wide character right there, but that should be okay.
486
+ */
487
+ if (scridx [ibeg ]+ DISPLAY_SIZE >= scridx [loc ]+ MIN_RIGHT_CUT )
488
+ {
489
+ while (scridx [iend ]- scridx [ibeg ] > DISPLAY_SIZE )
490
+ iend -- ;
491
+ end_trunc = true;
492
+ }
493
+ else
494
+ {
495
+ /* Truncate right if not too close to loc. */
496
+ while (scridx [loc ]+ MIN_RIGHT_CUT < scridx [iend ])
497
+ {
498
+ iend -- ;
499
+ end_trunc = true;
500
+ }
501
+
502
+ /* Truncate left if still too long. */
503
+ while (scridx [iend ]- scridx [ibeg ] > DISPLAY_SIZE )
504
+ {
505
+ ibeg ++ ;
506
+ beg_trunc = true;
507
+ }
508
+ }
509
+ }
510
+
511
+ /* the extract MUST contain the target position! */
512
+ psql_assert (ibeg <=loc && loc <=iend );
513
+
514
+ /* truncate working copy at desired endpoint */
515
+ wquery [qidx [iend ]] = '\0' ;
516
+
517
+ /* Begin building the finished message. */
518
+ printfPQExpBuffer (& msg , gettext ("LINE %d: " ), loc_line );
519
+ if (beg_trunc )
520
+ appendPQExpBufferStr (& msg , "..." );
521
+
522
+ /*
523
+ * While we have the prefix in the msg buffer, compute its screen
524
+ * width.
525
+ */
526
+ scroffset = 0 ;
527
+ for (i = 0 ; i < msg .len ; i += PQmblen (& msg .data [i ], pset .encoding ))
528
+ {
529
+ scroffset += 1 ; /* XXX fix me when we have screen width info */
530
+ }
531
+
532
+ /* Finish and emit the message. */
533
+ appendPQExpBufferStr (& msg , & wquery [qidx [ibeg ]]);
534
+ if (end_trunc )
535
+ appendPQExpBufferStr (& msg , "..." );
536
+
537
+ psql_error ("%s\n" , msg .data );
538
+
539
+ /* Now emit the cursor marker line. */
540
+ scroffset += scridx [loc ] - scridx [ibeg ];
541
+ resetPQExpBuffer (& msg );
542
+ for (i = 0 ; i < scroffset ; i ++ )
543
+ appendPQExpBufferChar (& msg , ' ' );
544
+ appendPQExpBufferChar (& msg , '^' );
545
+
546
+ psql_error ("%s\n" , msg .data );
547
+ }
548
+
549
+ /* Clean up. */
550
+ termPQExpBuffer (& msg );
551
+
552
+ free (wquery );
553
+ free (qidx );
554
+ free (scridx );
555
+ }
556
+
557
+
348
558
/*
349
559
* AcceptResult
350
560
*
@@ -355,7 +565,7 @@ ResetCancelConn(void)
355
565
* Returns true for valid result, false for error state.
356
566
*/
357
567
static bool
358
- AcceptResult (const PGresult * result )
568
+ AcceptResult (const PGresult * result , const char * query )
359
569
{
360
570
bool OK = true;
361
571
@@ -386,6 +596,7 @@ AcceptResult(const PGresult *result)
386
596
if (!OK )
387
597
{
388
598
psql_error ("%s" , PQerrorMessage (pset .db ));
599
+ ReportSyntaxErrorPosition (result , query );
389
600
CheckConnection ();
390
601
}
391
602
@@ -449,7 +660,7 @@ PSQLexec(const char *query, bool start_xact)
449
660
450
661
res = PQexec (pset .db , query );
451
662
452
- if (!AcceptResult (res ) && res )
663
+ if (!AcceptResult (res , query ) && res )
453
664
{
454
665
PQclear (res );
455
666
res = NULL ;
@@ -695,7 +906,7 @@ SendQuery(const char *query)
695
906
results = PQexec (pset .db , query );
696
907
697
908
/* these operations are included in the timing result: */
698
- OK = (AcceptResult (results ) && ProcessCopyResult (results ));
909
+ OK = (AcceptResult (results , query ) && ProcessCopyResult (results ));
699
910
700
911
if (pset .timing )
701
912
GETTIMEOFDAY (& after );
0 commit comments