Skip to content

Commit 8fb0ac8

Browse files
author
Bryan Henderson
committed
Make error messages more explicit, PQtrace() output more readable.
1 parent e2da92f commit 8fb0ac8

File tree

2 files changed

+186
-138
lines changed

2 files changed

+186
-138
lines changed

src/interfaces/libpq/fe-exec.c

Lines changed: 171 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.25 1996/12/28 01:57:13 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.26 1996/12/31 07:29:15 bryanh Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -348,6 +348,170 @@ makePGresult(PGconn* conn, char* pname)
348348
}
349349

350350

351+
/*
352+
* Assuming that we just sent a query to the backend, read the backend's
353+
* response from stream <pfin> and respond accordingly.
354+
*
355+
* If <pfdebug> is non-null, write to that stream whatever we receive
356+
* (it's a debugging trace).
357+
*
358+
* Return as <result> a pointer to a proper final PGresult structure,
359+
* newly allocated, for the query based on the response we get. If the
360+
* response we get indicates that the query didn't execute, return a
361+
* null pointer and don't allocate any space, but also place a text
362+
* string explaining the problem at <*reason>.
363+
*/
364+
365+
static void
366+
process_response_from_backend(FILE *pfin, FILE *pfout, FILE *pfdebug,
367+
PGconn *conn,
368+
PGresult **result_p, char * const reason) {
369+
370+
char id;
371+
/* The protocol character received from the backend. The protocol
372+
character is the first character in the backend's response to our
373+
query. It defines the nature of the response.
374+
*/
375+
PGnotify *newNotify;
376+
bool done;
377+
/* We're all done with the query and ready to return the result. */
378+
int emptiesSent;
379+
/* Number of empty queries we have sent in order to flush out multiple
380+
responses, less the number of corresponding responses we have
381+
received.
382+
*/
383+
char cmdStatus[MAX_MESSAGE_LEN];
384+
char pname[MAX_MESSAGE_LEN]; /* portal name */
385+
386+
/* loop because multiple messages, especially NOTICES,
387+
can come back from the backend. NOTICES are output directly to stderr
388+
*/
389+
390+
emptiesSent = 0; /* No empty queries sent yet */
391+
pname[0] = '\0';
392+
393+
done = false; /* initial value */
394+
while (!done) {
395+
/* read the result id */
396+
id = pqGetc(pfin, pfdebug);
397+
if (id == EOF) {
398+
/* hmm, no response from the backend-end, that's bad */
399+
(void) sprintf(reason,
400+
"PQexec() -- Request was sent to backend, but backend "
401+
"closed the channel before "
402+
"responding. This probably means the backend "
403+
"terminated abnormally before or while processing "
404+
"the request.\n");
405+
conn->status = CONNECTION_BAD; /* No more connection to backend */
406+
*result_p = (PGresult*)NULL;
407+
done = true;
408+
} else {
409+
switch (id) {
410+
case 'A':
411+
newNotify = (PGnotify*)malloc(sizeof(PGnotify));
412+
pqGetInt(&(newNotify->be_pid), 4, pfin, pfdebug);
413+
pqGets(newNotify->relname, NAMEDATALEN, pfin, pfdebug);
414+
DLAddTail(conn->notifyList, DLNewElem(newNotify));
415+
/* async messages are piggy'ed back on other messages,
416+
so we stay in the while loop for other messages */
417+
break;
418+
case 'C': /* portal query command, no rows returned */
419+
if (pqGets(cmdStatus, MAX_MESSAGE_LEN, pfin, pfdebug) == 1) {
420+
sprintf(reason,
421+
"PQexec() -- query command completed, "
422+
"but return message from backend cannot be read.");
423+
*result_p = (PGresult*)NULL;
424+
done = true;
425+
} else {
426+
/*
427+
// since backend may produce more than one result for some
428+
// commands need to poll until clear
429+
// send an empty query down, and keep reading out of the pipe
430+
// until an 'I' is received.
431+
*/
432+
pqPuts("Q ", pfout, pfdebug); /* send an empty query */
433+
/*
434+
* Increment a flag and process messages in the usual way because
435+
* there may be async notifications pending. DZ - 31-8-1996
436+
*/
437+
emptiesSent++;
438+
}
439+
break;
440+
case 'E': /* error return */
441+
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) {
442+
(void) sprintf(reason,
443+
"PQexec() -- error return detected from backend, "
444+
"but attempt to read the error message failed.");
445+
}
446+
*result_p = (PGresult*)NULL;
447+
done = true;
448+
break;
449+
case 'I': { /* empty query */
450+
/* read and throw away the closing '\0' */
451+
int c;
452+
if ((c = pqGetc(pfin,pfdebug)) != '\0') {
453+
fprintf(stderr,"error!, unexpected character %c following 'I'\n", c);
454+
}
455+
if (emptiesSent) {
456+
if (--emptiesSent == 0) { /* is this the last one? */
457+
/*
458+
* If this is the result of a portal query command set the
459+
* command status and message accordingly. DZ - 31-8-1996
460+
*/
461+
*result_p = makeEmptyPGresult(conn,PGRES_COMMAND_OK);
462+
strncpy((*result_p)->cmdStatus, cmdStatus, CMDSTATUS_LEN-1);
463+
done = true;
464+
}
465+
}
466+
else {
467+
*result_p = makeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
468+
done = true;
469+
}
470+
}
471+
break;
472+
case 'N': /* notices from the backend */
473+
if (pqGets(reason, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) {
474+
sprintf(reason,
475+
"PQexec() -- Notice detected from backend, "
476+
"but attempt to read the notice failed.");
477+
*result_p = (PGresult*)NULL;
478+
done = true;
479+
} else
480+
/* Should we really be doing this? These notices are not important
481+
enough for us to presume to put them on stderr. Maybe the caller
482+
should decide whether to put them on stderr or not. BJH 96.12.27
483+
*/
484+
fprintf(stderr,"%s", reason);
485+
break;
486+
case 'P': /* synchronous (normal) portal */
487+
pqGets(pname, MAX_MESSAGE_LEN, pfin, pfdebug); /* read in portal name*/
488+
break;
489+
case 'T': /* actual row results: */
490+
*result_p = makePGresult(conn, pname);
491+
done = true;
492+
break;
493+
case 'D': /* copy command began successfully */
494+
*result_p = makeEmptyPGresult(conn, PGRES_COPY_IN);
495+
done = true;
496+
break;
497+
case 'B': /* copy command began successfully */
498+
*result_p = makeEmptyPGresult(conn, PGRES_COPY_OUT);
499+
done = true;
500+
break;
501+
default:
502+
sprintf(reason,
503+
"unknown protocol character '%c' read from backend. "
504+
"(The protocol character is the first character the "
505+
"backend sends in response to a query it receives).\n",
506+
id);
507+
*result_p = (PGresult*)NULL;
508+
done = true;
509+
} /* switch on protocol character */
510+
} /* if character was received */
511+
} /* while not done */
512+
}
513+
514+
351515

352516
/*
353517
* PQexec
@@ -364,27 +528,14 @@ PGresult*
364528
PQexec(PGconn* conn, const char* query)
365529
{
366530
PGresult *result;
367-
int id;
368531
char buffer[MAX_MESSAGE_LEN];
369-
char cmdStatus[MAX_MESSAGE_LEN];
370-
char pname[MAX_MESSAGE_LEN]; /* portal name */
371-
PGnotify *newNotify;
372-
FILE *pfin, *pfout, *pfdebug;
373-
static int emptiesPending = 0;
374-
bool emptySent = false;
375-
376-
pname[0]='\0';
377532

378533
if (!conn) return NULL;
379534
if (!query) {
380535
sprintf(conn->errorMessage, "PQexec() -- query pointer is null.");
381536
return NULL;
382537
}
383538

384-
pfin = conn->Pfin;
385-
pfout = conn->Pfout;
386-
pfdebug = conn->Pfdebug;
387-
388539
/*clear the error string */
389540
conn->errorMessage[0] = '\0';
390541

@@ -406,129 +557,20 @@ PQexec(PGconn* conn, const char* query)
406557
sprintf(buffer,"Q%s",query);
407558

408559
/* send the query to the backend; */
409-
if (pqPuts(buffer,pfout, pfdebug) == 1) {
560+
if (pqPuts(buffer, conn->Pfout, conn->Pfdebug) == 1) {
410561
(void) sprintf(conn->errorMessage,
411562
"PQexec() -- while sending query: %s\n"
412563
"-- fprintf to Pfout failed: errno=%d\n%s\n",
413-
query, errno,strerror(errno));
564+
query, errno, strerror(errno));
414565
return NULL;
415566
}
416567

417-
/* loop forever because multiple messages, especially NOTICES,
418-
can come back from the backend
419-
NOTICES are output directly to stderr
420-
*/
421-
422-
while (1) {
568+
process_response_from_backend(conn->Pfin, conn->Pfout, conn->Pfdebug, conn,
569+
&result, conn->errorMessage);
570+
return(result);
571+
}
423572

424-
/* read the result id */
425-
id = pqGetc(pfin,pfdebug);
426-
if (id == EOF) {
427-
/* hmm, no response from the backend-end, that's bad */
428-
(void) sprintf(conn->errorMessage,
429-
"PQexec() -- Request was sent to backend, but backend "
430-
"closed the channel before "
431-
"responding. This probably means the backend "
432-
"terminated abnormally before or while processing "
433-
"the request.\n");
434-
conn->status = CONNECTION_BAD; /* No more connection to backend */
435-
return (PGresult*)NULL;
436-
}
437573

438-
switch (id) {
439-
case 'A':
440-
newNotify = (PGnotify*)malloc(sizeof(PGnotify));
441-
pqGetInt(&(newNotify->be_pid), 4, pfin, pfdebug);
442-
pqGets(newNotify->relname, NAMEDATALEN, pfin, pfdebug);
443-
DLAddTail(conn->notifyList, DLNewElem(newNotify));
444-
/* async messages are piggy'ed back on other messages,
445-
so we stay in the while loop for other messages */
446-
break;
447-
case 'C': /* portal query command, no rows returned */
448-
if (pqGets(cmdStatus, MAX_MESSAGE_LEN, pfin, pfdebug) == 1) {
449-
sprintf(conn->errorMessage,
450-
"PQexec() -- query command completed, "
451-
"but return message from backend cannot be read.");
452-
return (PGresult*)NULL;
453-
}
454-
else {
455-
/*
456-
// since backend may produce more than one result for some commands
457-
// need to poll until clear
458-
// send an empty query down, and keep reading out of the pipe
459-
// until an 'I' is received.
460-
*/
461-
pqPuts("Q",pfout,pfdebug); /* send an empty query */
462-
/*
463-
* Increment a flag and process messages in the usual way because
464-
* there may be async notifications pending. DZ - 31-8-1996
465-
*/
466-
emptiesPending++;
467-
emptySent = true;
468-
}
469-
break;
470-
case 'E': /* error return */
471-
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) {
472-
(void) sprintf(conn->errorMessage,
473-
"PQexec() -- error return detected from backend, "
474-
"but attempt to read the error message failed.");
475-
}
476-
return (PGresult*)NULL;
477-
break;
478-
case 'I': /* empty query */
479-
/* read the throw away the closing '\0' */
480-
{
481-
int c;
482-
if ((c = pqGetc(pfin,pfdebug)) != '\0') {
483-
fprintf(stderr,"error!, unexpected character %c following 'I'\n", c);
484-
}
485-
if (emptiesPending) {
486-
if (--emptiesPending == 0 && emptySent) { /* is this the last one? */
487-
/*
488-
* If this is the result of a portal query command set the
489-
* command status and message accordingly. DZ - 31-8-1996
490-
*/
491-
result = makeEmptyPGresult(conn,PGRES_COMMAND_OK);
492-
strncpy(result->cmdStatus,cmdStatus, CMDSTATUS_LEN-1);
493-
return result;
494-
}
495-
}
496-
else {
497-
result = makeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
498-
return result;
499-
}
500-
}
501-
break;
502-
case 'N': /* notices from the backend */
503-
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) {
504-
sprintf(conn->errorMessage,
505-
"PQexec() -- Notice detected from backend, "
506-
"but attempt to read the notice failed.");
507-
return (PGresult*)NULL;
508-
}
509-
else
510-
fprintf(stderr,"%s", conn->errorMessage);
511-
break;
512-
case 'P': /* synchronous (normal) portal */
513-
pqGets(pname,MAX_MESSAGE_LEN,pfin, pfdebug); /* read in portal name*/
514-
break;
515-
case 'T': /* actual row results: */
516-
return makePGresult(conn, pname);
517-
break;
518-
case 'D': /* copy command began successfully */
519-
return makeEmptyPGresult(conn,PGRES_COPY_IN);
520-
break;
521-
case 'B': /* copy command began successfully */
522-
return makeEmptyPGresult(conn,PGRES_COPY_OUT);
523-
break;
524-
default:
525-
sprintf(conn->errorMessage,
526-
"unknown protocol character %c read from backend\n",
527-
id);
528-
return (PGresult*)NULL;
529-
} /* switch */
530-
} /* while (1)*/
531-
}
532574

533575
/*
534576
* PQnotifies

0 commit comments

Comments
 (0)