Commit 8fb0ac88 authored by Bryan Henderson's avatar Bryan Henderson

Make error messages more explicit, PQtrace() output more readable.

parent e2da92f1
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.25 1996/12/28 01:57:13 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.26 1996/12/31 07:29:15 bryanh Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -348,93 +348,64 @@ makePGresult_badResponse_return:
}
/*
* PQexec
* send a query to the backend and package up the result in a Pgresult
* Assuming that we just sent a query to the backend, read the backend's
* response from stream <pfin> and respond accordingly.
*
* if the query failed, return NULL, conn->errorMessage is set to
* a relevant message
* if query is successful, a new PGresult is returned
* the use is responsible for freeing that structure when done with it
* If <pfdebug> is non-null, write to that stream whatever we receive
* (it's a debugging trace).
*
* Return as <result> a pointer to a proper final PGresult structure,
* newly allocated, for the query based on the response we get. If the
* response we get indicates that the query didn't execute, return a
* null pointer and don't allocate any space, but also place a text
* string explaining the problem at <*reason>.
*/
PGresult*
PQexec(PGconn* conn, const char* query)
{
PGresult *result;
int id;
char buffer[MAX_MESSAGE_LEN];
static void
process_response_from_backend(FILE *pfin, FILE *pfout, FILE *pfdebug,
PGconn *conn,
PGresult **result_p, char * const reason) {
char id;
/* The protocol character received from the backend. The protocol
character is the first character in the backend's response to our
query. It defines the nature of the response.
*/
PGnotify *newNotify;
bool done;
/* We're all done with the query and ready to return the result. */
int emptiesSent;
/* Number of empty queries we have sent in order to flush out multiple
responses, less the number of corresponding responses we have
received.
*/
char cmdStatus[MAX_MESSAGE_LEN];
char pname[MAX_MESSAGE_LEN]; /* portal name */
PGnotify *newNotify;
FILE *pfin, *pfout, *pfdebug;
static int emptiesPending = 0;
bool emptySent = false;
pname[0]='\0';
if (!conn) return NULL;
if (!query) {
sprintf(conn->errorMessage, "PQexec() -- query pointer is null.");
return NULL;
}
pfin = conn->Pfin;
pfout = conn->Pfout;
pfdebug = conn->Pfdebug;
/*clear the error string */
conn->errorMessage[0] = '\0';
/* check to see if the query string is too long */
if (strlen(query) > MAX_MESSAGE_LEN) {
sprintf(conn->errorMessage, "PQexec() -- query is too long. "
"Maximum length is %d\n", MAX_MESSAGE_LEN -2 );
return NULL;
}
/* Don't try to send if we know there's no live connection. */
if (conn->status != CONNECTION_OK) {
sprintf(conn->errorMessage, "PQexec() -- There is no connection "
"to the backend.\n");
return NULL;
}
/* the frontend-backend protocol uses 'Q' to designate queries */
sprintf(buffer,"Q%s",query);
/* send the query to the backend; */
if (pqPuts(buffer,pfout, pfdebug) == 1) {
(void) sprintf(conn->errorMessage,
"PQexec() -- while sending query: %s\n"
"-- fprintf to Pfout failed: errno=%d\n%s\n",
query, errno,strerror(errno));
return NULL;
}
/* loop forever because multiple messages, especially NOTICES,
can come back from the backend
NOTICES are output directly to stderr
/* loop because multiple messages, especially NOTICES,
can come back from the backend. NOTICES are output directly to stderr
*/
while (1) {
emptiesSent = 0; /* No empty queries sent yet */
pname[0] = '\0';
done = false; /* initial value */
while (!done) {
/* read the result id */
id = pqGetc(pfin,pfdebug);
id = pqGetc(pfin, pfdebug);
if (id == EOF) {
/* hmm, no response from the backend-end, that's bad */
(void) sprintf(conn->errorMessage,
(void) sprintf(reason,
"PQexec() -- Request was sent to backend, but backend "
"closed the channel before "
"responding. This probably means the backend "
"terminated abnormally before or while processing "
"the request.\n");
conn->status = CONNECTION_BAD; /* No more connection to backend */
return (PGresult*)NULL;
}
*result_p = (PGresult*)NULL;
done = true;
} else {
switch (id) {
case 'A':
newNotify = (PGnotify*)malloc(sizeof(PGnotify));
......@@ -446,90 +417,161 @@ PQexec(PGconn* conn, const char* query)
break;
case 'C': /* portal query command, no rows returned */
if (pqGets(cmdStatus, MAX_MESSAGE_LEN, pfin, pfdebug) == 1) {
sprintf(conn->errorMessage,
sprintf(reason,
"PQexec() -- query command completed, "
"but return message from backend cannot be read.");
return (PGresult*)NULL;
}
else {
*result_p = (PGresult*)NULL;
done = true;
} else {
/*
// since backend may produce more than one result for some commands
// need to poll until clear
// since backend may produce more than one result for some
// commands need to poll until clear
// send an empty query down, and keep reading out of the pipe
// until an 'I' is received.
*/
pqPuts("Q",pfout,pfdebug); /* send an empty query */
pqPuts("Q ", pfout, pfdebug); /* send an empty query */
/*
* Increment a flag and process messages in the usual way because
* there may be async notifications pending. DZ - 31-8-1996
*/
emptiesPending++;
emptySent = true;
emptiesSent++;
}
break;
case 'E': /* error return */
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) {
(void) sprintf(conn->errorMessage,
(void) sprintf(reason,
"PQexec() -- error return detected from backend, "
"but attempt to read the error message failed.");
}
return (PGresult*)NULL;
*result_p = (PGresult*)NULL;
done = true;
break;
case 'I': /* empty query */
/* read the throw away the closing '\0' */
{
case 'I': { /* empty query */
/* read and throw away the closing '\0' */
int c;
if ((c = pqGetc(pfin,pfdebug)) != '\0') {
fprintf(stderr,"error!, unexpected character %c following 'I'\n", c);
}
if (emptiesPending) {
if (--emptiesPending == 0 && emptySent) { /* is this the last one? */
if (emptiesSent) {
if (--emptiesSent == 0) { /* is this the last one? */
/*
* If this is the result of a portal query command set the
* command status and message accordingly. DZ - 31-8-1996
*/
result = makeEmptyPGresult(conn,PGRES_COMMAND_OK);
strncpy(result->cmdStatus,cmdStatus, CMDSTATUS_LEN-1);
return result;
*result_p = makeEmptyPGresult(conn,PGRES_COMMAND_OK);
strncpy((*result_p)->cmdStatus, cmdStatus, CMDSTATUS_LEN-1);
done = true;
}
}
else {
result = makeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
return result;
*result_p = makeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
done = true;
}
}
break;
case 'N': /* notices from the backend */
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) {
sprintf(conn->errorMessage,
if (pqGets(reason, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) {
sprintf(reason,
"PQexec() -- Notice detected from backend, "
"but attempt to read the notice failed.");
return (PGresult*)NULL;
}
else
fprintf(stderr,"%s", conn->errorMessage);
*result_p = (PGresult*)NULL;
done = true;
} else
/* Should we really be doing this? These notices are not important
enough for us to presume to put them on stderr. Maybe the caller
should decide whether to put them on stderr or not. BJH 96.12.27
*/
fprintf(stderr,"%s", reason);
break;
case 'P': /* synchronous (normal) portal */
pqGets(pname,MAX_MESSAGE_LEN,pfin, pfdebug); /* read in portal name*/
pqGets(pname, MAX_MESSAGE_LEN, pfin, pfdebug); /* read in portal name*/
break;
case 'T': /* actual row results: */
return makePGresult(conn, pname);
*result_p = makePGresult(conn, pname);
done = true;
break;
case 'D': /* copy command began successfully */
return makeEmptyPGresult(conn,PGRES_COPY_IN);
*result_p = makeEmptyPGresult(conn, PGRES_COPY_IN);
done = true;
break;
case 'B': /* copy command began successfully */
return makeEmptyPGresult(conn,PGRES_COPY_OUT);
*result_p = makeEmptyPGresult(conn, PGRES_COPY_OUT);
done = true;
break;
default:
sprintf(conn->errorMessage,
"unknown protocol character %c read from backend\n",
sprintf(reason,
"unknown protocol character '%c' read from backend. "
"(The protocol character is the first character the "
"backend sends in response to a query it receives).\n",
id);
return (PGresult*)NULL;
} /* switch */
} /* while (1)*/
*result_p = (PGresult*)NULL;
done = true;
} /* switch on protocol character */
} /* if character was received */
} /* while not done */
}
/*
* PQexec
* send a query to the backend and package up the result in a Pgresult
*
* if the query failed, return NULL, conn->errorMessage is set to
* a relevant message
* if query is successful, a new PGresult is returned
* the use is responsible for freeing that structure when done with it
*
*/
PGresult*
PQexec(PGconn* conn, const char* query)
{
PGresult *result;
char buffer[MAX_MESSAGE_LEN];
if (!conn) return NULL;
if (!query) {
sprintf(conn->errorMessage, "PQexec() -- query pointer is null.");
return NULL;
}
/*clear the error string */
conn->errorMessage[0] = '\0';
/* check to see if the query string is too long */
if (strlen(query) > MAX_MESSAGE_LEN) {
sprintf(conn->errorMessage, "PQexec() -- query is too long. "
"Maximum length is %d\n", MAX_MESSAGE_LEN -2 );
return NULL;
}
/* Don't try to send if we know there's no live connection. */
if (conn->status != CONNECTION_OK) {
sprintf(conn->errorMessage, "PQexec() -- There is no connection "
"to the backend.\n");
return NULL;
}
/* the frontend-backend protocol uses 'Q' to designate queries */
sprintf(buffer,"Q%s",query);
/* send the query to the backend; */
if (pqPuts(buffer, conn->Pfout, conn->Pfdebug) == 1) {
(void) sprintf(conn->errorMessage,
"PQexec() -- while sending query: %s\n"
"-- fprintf to Pfout failed: errno=%d\n%s\n",
query, errno, strerror(errno));
return NULL;
}
process_response_from_backend(conn->Pfin, conn->Pfout, conn->Pfdebug, conn,
&result, conn->errorMessage);
return(result);
}
/*
* PQnotifies
* returns a PGnotify* structure of the latest async notification
......
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.3 1996/11/03 07:14:32 scrappy Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.4 1996/12/31 07:29:17 bryanh Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -35,7 +35,7 @@ pqGetc(FILE* fin, FILE* debug)
c = getc(fin);
if (debug && c != EOF)
putc(c,debug);
fprintf(debug, "From backend> %c\n", c);
return c;
}
......@@ -52,6 +52,7 @@ pqPutnchar(const char* s, int len, FILE *f, FILE *debug)
if (f == NULL)
return 1;
if (debug) fputs("To backend>", debug);
while (len--) {
status = fputc(*s,f);
if (debug)
......@@ -60,6 +61,7 @@ pqPutnchar(const char* s, int len, FILE *f, FILE *debug)
if (status == EOF)
return 1;
}
if (debug) fputc('\n', debug);
return 0;
}
......@@ -79,7 +81,7 @@ pqGetnchar(char* s, int len, FILE *f, FILE *debug)
*s = '\0';
if (debug) {
fputs(s,debug);
fprintf(debug, "From backend> %s\n", s);
}
return 0;
}
......@@ -100,7 +102,7 @@ pqGets(char* s, int len, FILE *f, FILE *debug)
*s = '\0';
if (debug) {
fputs(s,debug);
fprintf(debug, "From backend> %s\n", s);
}
return 0;
}
......@@ -114,22 +116,24 @@ pqGets(char* s, int len, FILE *f, FILE *debug)
returns 0 if successful, 1 otherwise
*/
int
pqPutInt(int i, int bytes, FILE* f, FILE *debug)
pqPutInt(const int integer, int bytes, FILE* f, FILE *debug)
{
int i;
int status;
i = integer;
if (bytes > 4)
bytes = 4;
while (bytes--) {
status = fputc(i & 0xff, f);
if (debug)
fputc(i & 0xff, debug);
i >>= 8;
if (status == EOF) {
return 1;
}
}
if (debug) fprintf(debug, "To backend (#)> %d\n", integer);
return 0;
}
......@@ -163,7 +167,7 @@ pqGetInt(int* result, int bytes, FILE* f, FILE *debug)
*result = n;
if (debug)
fprintf(debug,"%d",*result);
fprintf(debug,"From backend (#)> %d\n",*result);
return 0;
}
......@@ -181,7 +185,7 @@ pqPuts(const char* s, FILE *f, FILE *debug)
fflush(f);
if (debug) {
fputs(s,debug);
fprintf(debug, "To backend> %s\n", s);
}
return 0;
}
......@@ -195,3 +199,5 @@ pqFlush(FILE *f, FILE *debug)
if (debug)
fflush(debug);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment