summaryrefslogtreecommitdiff
path: root/src/interfaces
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces')
-rw-r--r--src/interfaces/libpq/fe-connect.c4
-rw-r--r--src/interfaces/libpq/fe-exec.c23
-rw-r--r--src/interfaces/libpq/fe-protocol3.c247
-rw-r--r--src/interfaces/libpq/libpq-int.h3
4 files changed, 267 insertions, 10 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index ef708658d7..98d834b595 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.327 2006/03/05 15:59:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.328 2006/03/14 22:48:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1918,6 +1918,8 @@ freePGconn(PGconn *conn)
free(conn->krbsrvname);
#endif
/* Note that conn->Pfdebug is not ours to close or free */
+ if (conn->last_query)
+ free(conn->last_query);
pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
notify = conn->notifyHead;
while (notify != NULL)
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 26d2b58fd9..7f09ff6dd2 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.181 2006/03/05 15:59:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.182 2006/03/14 22:48:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -667,6 +667,12 @@ PQsendQuery(PGconn *conn, const char *query)
/* remember we are using simple query protocol */
conn->queryclass = PGQUERY_SIMPLE;
+ /* and remember the query text too, if possible */
+ /* if insufficient memory, last_query just winds up NULL */
+ if (conn->last_query)
+ free(conn->last_query);
+ conn->last_query = strdup(query);
+
/*
* Give the data a push. In nonblock mode, don't complain if we're unable
* to send it all; PQgetResult() will do any additional flushing needed.
@@ -788,6 +794,12 @@ PQsendPrepare(PGconn *conn,
/* remember we are doing just a Parse */
conn->queryclass = PGQUERY_PREPARE;
+ /* and remember the query text too, if possible */
+ /* if insufficient memory, last_query just winds up NULL */
+ if (conn->last_query)
+ free(conn->last_query);
+ conn->last_query = strdup(query);
+
/*
* Give the data a push. In nonblock mode, don't complain if we're unable
* to send it all; PQgetResult() will do any additional flushing needed.
@@ -1017,6 +1029,15 @@ PQsendQueryGuts(PGconn *conn,
/* remember we are using extended query protocol */
conn->queryclass = PGQUERY_EXTENDED;
+ /* and remember the query text too, if possible */
+ /* if insufficient memory, last_query just winds up NULL */
+ if (conn->last_query)
+ free(conn->last_query);
+ if (command)
+ conn->last_query = strdup(command);
+ else
+ conn->last_query = NULL;
+
/*
* Give the data a push. In nonblock mode, don't complain if we're unable
* to send it all; PQgetResult() will do any additional flushing needed.
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 0fbb3738ad..35f015ecce 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -8,13 +8,12 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.25 2006/03/05 15:59:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.26 2006/03/14 22:48:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
-#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
@@ -51,6 +50,8 @@ static int getParameterStatus(PGconn *conn);
static int getNotify(PGconn *conn);
static int getCopyStart(PGconn *conn, ExecStatusType copytype);
static int getReadyForQuery(PGconn *conn);
+static void reportErrorPosition(PQExpBuffer msg, const char *query,
+ int loc, int encoding);
static int build_startup_packet(const PGconn *conn, char *packet,
const PQEnvironmentOption *options);
@@ -614,6 +615,8 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
PQExpBufferData workBuf;
char id;
const char *val;
+ const char *querytext = NULL;
+ int querypos = 0;
/*
* Since the fields might be pretty long, we create a temporary
@@ -666,22 +669,46 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
if (val)
{
- /* translator: %s represents a digit string */
- appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), val);
+ if (conn->verbosity != PQERRORS_TERSE && conn->last_query != NULL)
+ {
+ /* emit position as a syntax cursor display */
+ querytext = conn->last_query;
+ querypos = atoi(val);
+ }
+ else
+ {
+ /* emit position as text addition to primary message */
+ /* 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);
+ querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
+ if (conn->verbosity != PQERRORS_TERSE && querytext != NULL)
+ {
+ /* emit position as a syntax cursor display */
+ querypos = atoi(val);
+ }
+ else
+ {
+ /* emit position as text addition to primary message */
+ /* translator: %s represents a digit string */
+ appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
+ val);
+ }
}
}
appendPQExpBufferChar(&workBuf, '\n');
if (conn->verbosity != PQERRORS_TERSE)
{
+ if (querytext && querypos > 0)
+ reportErrorPosition(&workBuf, querytext, querypos,
+ conn->client_encoding);
val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
if (val)
appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"), val);
@@ -746,6 +773,212 @@ fail:
return EOF;
}
+/*
+ * Add an error-location display to the error message under construction.
+ *
+ * The cursor location is measured in logical characters; the query string
+ * is presumed to be in the specified encoding.
+ */
+static void
+reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding)
+{
+#define DISPLAY_SIZE 60 /* screen width limit, in screen cols */
+#define MIN_RIGHT_CUT 10 /* try to keep this far away from EOL */
+
+ char *wquery;
+ int clen,
+ slen,
+ i,
+ w,
+ *qidx,
+ *scridx,
+ qoffset,
+ scroffset,
+ ibeg,
+ iend,
+ loc_line;
+ bool beg_trunc,
+ end_trunc;
+
+ /* Need a writable copy of the query */
+ wquery = strdup(query);
+ if (wquery == NULL)
+ return; /* fail silently if out of memory */
+
+ /*
+ * Each character might occupy multiple physical bytes in the string, and
+ * in some Far Eastern character sets it might take more than one screen
+ * column as well. We compute the starting byte offset and starting
+ * screen column of each logical character, and store these in qidx[] and
+ * scridx[] respectively.
+ */
+
+ /* we need a safe allocation size... */
+ slen = strlen(query) + 1;
+
+ qidx = (int *) malloc(slen * sizeof(int));
+ if (qidx == NULL)
+ {
+ free(wquery);
+ return;
+ }
+ scridx = (int *) malloc(slen * sizeof(int));
+ if (scridx == NULL)
+ {
+ free(qidx);
+ free(wquery);
+ return;
+ }
+
+ qoffset = 0;
+ scroffset = 0;
+ for (i = 0; query[qoffset] != '\0'; i++)
+ {
+ qidx[i] = qoffset;
+ scridx[i] = scroffset;
+ w = pg_encoding_dsplen(encoding, &query[qoffset]);
+ /* treat control chars as width 1; see tab hack below */
+ if (w <= 0)
+ w = 1;
+ scroffset += w;
+ qoffset += pg_encoding_mblen(encoding, &query[qoffset]);
+ }
+ qidx[i] = qoffset;
+ scridx[i] = scroffset;
+ clen = i;
+
+ /* convert loc to zero-based offset in qidx/scridx arrays */
+ loc--;
+
+ /* do we have something to show? */
+ if (loc >= 0 && loc <= clen)
+ {
+ /* input line number of our syntax error. */
+ loc_line = 1;
+ /* first included char of extract. */
+ ibeg = 0;
+ /* last-plus-1 included char of extract. */
+ iend = clen;
+
+ /*
+ * Replace tabs with spaces in the writable copy. (Later we might
+ * want to think about coping with their variable screen width, but
+ * not today.)
+ *
+ * Extract line number and begin and end indexes of line containing
+ * error location. There will not be any newlines or carriage returns
+ * in the selected extract.
+ */
+ for (i = 0; i < clen; i++)
+ {
+ /* character length must be 1 or it's not ASCII */
+ if ((qidx[i + 1] - qidx[i]) == 1)
+ {
+ if (wquery[qidx[i]] == '\t')
+ wquery[qidx[i]] = ' ';
+ else if (wquery[qidx[i]] == '\r' || wquery[qidx[i]] == '\n')
+ {
+ if (i < loc)
+ {
+ /*
+ * count lines before loc. Each \r or \n counts
+ * as a line except when \r \n appear together.
+ */
+ if (wquery[qidx[i]] == '\r' ||
+ i == 0 ||
+ (qidx[i] - qidx[i - 1]) != 1 ||
+ wquery[qidx[i - 1]] != '\r')
+ loc_line++;
+ /* extract beginning = last line start before loc. */
+ ibeg = i + 1;
+ }
+ else
+ {
+ /* set extract end. */
+ iend = i;
+ /* done scanning. */
+ break;
+ }
+ }
+ }
+ }
+
+ /* If the line extracted is too long, we truncate it. */
+ beg_trunc = false;
+ end_trunc = false;
+ if (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
+ {
+ /*
+ * We first truncate right if it is enough. This code might be
+ * off a space or so on enforcing MIN_RIGHT_CUT if there's a wide
+ * character right there, but that should be okay.
+ */
+ if (scridx[ibeg] + DISPLAY_SIZE >= scridx[loc] + MIN_RIGHT_CUT)
+ {
+ while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
+ iend--;
+ end_trunc = true;
+ }
+ else
+ {
+ /* Truncate right if not too close to loc. */
+ while (scridx[loc] + MIN_RIGHT_CUT < scridx[iend])
+ {
+ iend--;
+ end_trunc = true;
+ }
+
+ /* Truncate left if still too long. */
+ while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
+ {
+ ibeg++;
+ beg_trunc = true;
+ }
+ }
+ }
+
+ /* truncate working copy at desired endpoint */
+ wquery[qidx[iend]] = '\0';
+
+ /* Begin building the finished message. */
+ i = msg->len;
+ appendPQExpBuffer(msg, libpq_gettext("LINE %d: "), loc_line);
+ if (beg_trunc)
+ appendPQExpBufferStr(msg, "...");
+
+ /*
+ * While we have the prefix in the msg buffer, compute its screen
+ * width.
+ */
+ scroffset = 0;
+ for (; i < msg->len; i += pg_encoding_mblen(encoding, &msg->data[i]))
+ {
+ w = pg_encoding_dsplen(encoding, &msg->data[i]);
+ if (w <= 0)
+ w = 1;
+ scroffset += w;
+ }
+
+ /* Finish up the LINE message line. */
+ appendPQExpBufferStr(msg, &wquery[qidx[ibeg]]);
+ if (end_trunc)
+ appendPQExpBufferStr(msg, "...");
+ appendPQExpBufferChar(msg, '\n');
+
+ /* Now emit the cursor marker line. */
+ scroffset += scridx[loc] - scridx[ibeg];
+ for (i = 0; i < scroffset; i++)
+ appendPQExpBufferChar(msg, ' ');
+ appendPQExpBufferChar(msg, '^');
+ appendPQExpBufferChar(msg, '\n');
+ }
+
+ /* Clean up. */
+ free(scridx);
+ free(qidx);
+ free(wquery);
+}
+
/*
* Attempt to read a ParameterStatus message.
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4ae59c9e9a..39533452ec 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.111 2006/03/05 15:59:10 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.112 2006/03/14 22:48:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -278,6 +278,7 @@ struct pg_conn
PGAsyncStatusType asyncStatus;
PGTransactionStatusType xactStatus; /* never changes to ACTIVE */
PGQueryClass queryclass;
+ char *last_query; /* last SQL command, or NULL if unknown */
bool options_valid; /* true if OK to attempt connection */
bool nonblocking; /* whether this connection is using nonblock
* sending semantics */