diff options
Diffstat (limited to 'src/interfaces/libpq/fe-exec.c')
-rw-r--r-- | src/interfaces/libpq/fe-exec.c | 300 |
1 files changed, 171 insertions, 129 deletions
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 9e3bc3f5948..9f1f64c7265 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -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,6 +348,170 @@ makePGresult_badResponse_return: } +/* + * Assuming that we just sent a query to the backend, read the backend's + * response from stream <pfin> and respond accordingly. + * + * 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>. + */ + +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 */ + + /* loop because multiple messages, especially NOTICES, + can come back from the backend. NOTICES are output directly to stderr + */ + + emptiesSent = 0; /* No empty queries sent yet */ + pname[0] = '\0'; + + done = false; /* initial value */ + while (!done) { + /* read the result id */ + id = pqGetc(pfin, pfdebug); + if (id == EOF) { + /* hmm, no response from the backend-end, that's bad */ + (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 */ + *result_p = (PGresult*)NULL; + done = true; + } else { + switch (id) { + case 'A': + newNotify = (PGnotify*)malloc(sizeof(PGnotify)); + pqGetInt(&(newNotify->be_pid), 4, pfin, pfdebug); + pqGets(newNotify->relname, NAMEDATALEN, pfin, pfdebug); + DLAddTail(conn->notifyList, DLNewElem(newNotify)); + /* async messages are piggy'ed back on other messages, + so we stay in the while loop for other messages */ + break; + case 'C': /* portal query command, no rows returned */ + if (pqGets(cmdStatus, MAX_MESSAGE_LEN, pfin, pfdebug) == 1) { + sprintf(reason, + "PQexec() -- query command completed, " + "but return message from backend cannot be read."); + *result_p = (PGresult*)NULL; + done = true; + } else { + /* + // 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 */ + /* + * Increment a flag and process messages in the usual way because + * there may be async notifications pending. DZ - 31-8-1996 + */ + emptiesSent++; + } + break; + case 'E': /* error return */ + if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) { + (void) sprintf(reason, + "PQexec() -- error return detected from backend, " + "but attempt to read the error message failed."); + } + *result_p = (PGresult*)NULL; + done = true; + break; + 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 (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_p = makeEmptyPGresult(conn,PGRES_COMMAND_OK); + strncpy((*result_p)->cmdStatus, cmdStatus, CMDSTATUS_LEN-1); + done = true; + } + } + else { + *result_p = makeEmptyPGresult(conn, PGRES_EMPTY_QUERY); + done = true; + } + } + break; + case 'N': /* notices from the backend */ + if (pqGets(reason, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) { + sprintf(reason, + "PQexec() -- Notice detected from backend, " + "but attempt to read the notice failed."); + *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*/ + break; + case 'T': /* actual row results: */ + *result_p = makePGresult(conn, pname); + done = true; + break; + case 'D': /* copy command began successfully */ + *result_p = makeEmptyPGresult(conn, PGRES_COPY_IN); + done = true; + break; + case 'B': /* copy command began successfully */ + *result_p = makeEmptyPGresult(conn, PGRES_COPY_OUT); + done = true; + break; + default: + 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); + *result_p = (PGresult*)NULL; + done = true; + } /* switch on protocol character */ + } /* if character was received */ + } /* while not done */ +} + + /* * PQexec @@ -364,16 +528,7 @@ PGresult* PQexec(PGconn* conn, const char* query) { PGresult *result; - int id; char buffer[MAX_MESSAGE_LEN]; - 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) { @@ -381,10 +536,6 @@ PQexec(PGconn* conn, const char* query) return NULL; } - pfin = conn->Pfin; - pfout = conn->Pfout; - pfdebug = conn->Pfdebug; - /*clear the error string */ conn->errorMessage[0] = '\0'; @@ -406,129 +557,20 @@ PQexec(PGconn* conn, const char* query) sprintf(buffer,"Q%s",query); /* send the query to the backend; */ - if (pqPuts(buffer,pfout, pfdebug) == 1) { + 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)); + 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 - */ - - while (1) { + process_response_from_backend(conn->Pfin, conn->Pfout, conn->Pfdebug, conn, + &result, conn->errorMessage); + return(result); +} - /* read the result id */ - id = pqGetc(pfin,pfdebug); - if (id == EOF) { - /* hmm, no response from the backend-end, that's bad */ - (void) sprintf(conn->errorMessage, - "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; - } - switch (id) { - case 'A': - newNotify = (PGnotify*)malloc(sizeof(PGnotify)); - pqGetInt(&(newNotify->be_pid), 4, pfin, pfdebug); - pqGets(newNotify->relname, NAMEDATALEN, pfin, pfdebug); - DLAddTail(conn->notifyList, DLNewElem(newNotify)); - /* async messages are piggy'ed back on other messages, - so we stay in the while loop for other messages */ - break; - case 'C': /* portal query command, no rows returned */ - if (pqGets(cmdStatus, MAX_MESSAGE_LEN, pfin, pfdebug) == 1) { - sprintf(conn->errorMessage, - "PQexec() -- query command completed, " - "but return message from backend cannot be read."); - return (PGresult*)NULL; - } - else { - /* - // 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 */ - /* - * Increment a flag and process messages in the usual way because - * there may be async notifications pending. DZ - 31-8-1996 - */ - emptiesPending++; - emptySent = true; - } - break; - case 'E': /* error return */ - if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) { - (void) sprintf(conn->errorMessage, - "PQexec() -- error return detected from backend, " - "but attempt to read the error message failed."); - } - return (PGresult*)NULL; - break; - case 'I': /* empty query */ - /* read the 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 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; - } - } - else { - result = makeEmptyPGresult(conn, PGRES_EMPTY_QUERY); - return result; - } - } - break; - case 'N': /* notices from the backend */ - if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, pfin, pfdebug) == 1) { - sprintf(conn->errorMessage, - "PQexec() -- Notice detected from backend, " - "but attempt to read the notice failed."); - return (PGresult*)NULL; - } - else - fprintf(stderr,"%s", conn->errorMessage); - break; - case 'P': /* synchronous (normal) portal */ - pqGets(pname,MAX_MESSAGE_LEN,pfin, pfdebug); /* read in portal name*/ - break; - case 'T': /* actual row results: */ - return makePGresult(conn, pname); - break; - case 'D': /* copy command began successfully */ - return makeEmptyPGresult(conn,PGRES_COPY_IN); - break; - case 'B': /* copy command began successfully */ - return makeEmptyPGresult(conn,PGRES_COPY_OUT); - break; - default: - sprintf(conn->errorMessage, - "unknown protocol character %c read from backend\n", - id); - return (PGresult*)NULL; - } /* switch */ - } /* while (1)*/ -} /* * PQnotifies |