/* * Attempt to read a Notify response message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'A' message type and length have already been consumed. * Exit: returns 0 if successfully consumed Notify message. * returns EOF if not enough data. */ static int getNotify(PGconn *conn) { int be_pid; char *svname; int nmlen; int extralen; PGnotify *newNotify; if (pqGetInt(&be_pid, 4, conn)) return EOF; if (pqGets(&conn->workBuffer, conn)) return EOF; /* must save name while getting extra string */ svname = strdup(conn->workBuffer.data); if (!svname) return EOF; if (pqGets(&conn->workBuffer, conn)) { free(svname); return EOF; } /* * Store the strings right after the PQnotify structure so it can all be * freed at once. We don't use NAMEDATALEN because we don't want to tie * this interface to a specific server name length. */ nmlen = strlen(svname); extralen = strlen(conn->workBuffer.data); newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2); if (newNotify) { newNotify->relname = (char *) newNotify + sizeof(PGnotify); strcpy(newNotify->relname, svname); newNotify->extra = newNotify->relname + nmlen + 1; strcpy(newNotify->extra, conn->workBuffer.data); newNotify->be_pid = be_pid; newNotify->next = NULL; if (conn->notifyTail) conn->notifyTail->next = newNotify; else conn->notifyHead = newNotify; conn->notifyTail = newNotify; } free(svname); return 0; }
/* * Attempt to read a ParameterStatus message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'S' message type and length have already been consumed. * Exit: returns 0 if successfully consumed message. * returns EOF if not enough data. */ static int getParameterStatus(PGconn *conn) { PQExpBufferData valueBuf; /* Get the parameter name */ if (pqGets(&conn->workBuffer, conn)) return EOF; /* Get the parameter value (could be large) */ initPQExpBuffer(&valueBuf); if (pqGets(&valueBuf, conn)) { termPQExpBuffer(&valueBuf); return EOF; } /* And save it */ pqSaveParameterStatus(conn, conn->workBuffer.data, valueBuf.data); termPQExpBuffer(&valueBuf); return 0; }
/* * Attempt to read a Notify response message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'A' message type and length have already been consumed. * Exit: returns 0 if successfully consumed Notify message. * returns EOF if not enough data. */ static int getNotify(PGconn *conn) { int be_pid; int nmlen; PGnotify *newNotify; if (pqGetInt(&be_pid, 4, conn)) return EOF; if (pqGets(&conn->workBuffer, conn)) return EOF; /* * Store the relation name right after the PQnotify structure so it can * all be freed at once. We don't use NAMEDATALEN because we don't want * to tie this interface to a specific server name length. */ nmlen = strlen(conn->workBuffer.data); newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + 1); if (newNotify) { newNotify->relname = (char *) newNotify + sizeof(PGnotify); strcpy(newNotify->relname, conn->workBuffer.data); /* fake up an empty-string extra field */ newNotify->extra = newNotify->relname + nmlen; newNotify->be_pid = be_pid; newNotify->next = NULL; if (conn->notifyTail) conn->notifyTail->next = newNotify; else conn->notifyHead = newNotify; conn->notifyTail = newNotify; } return 0; }
/* * Attempt to read an Error or Notice response message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'E' or 'N' message type has already been consumed. * Exit: returns 0 if successfully consumed message. * returns EOF if not enough data. */ static int pqGetErrorNotice2(PGconn *conn, bool isError) { PGresult *res = NULL; PQExpBufferData workBuf; char *startp; char *splitp; /* * Since the message might be pretty long, we create a temporary * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended * for stuff that is expected to be short. */ initPQExpBuffer(&workBuf); if (pqGets(&workBuf, conn)) goto failure; /* * Make a PGresult to hold the message. We temporarily lie about the * result status, so that PQmakeEmptyPGresult doesn't uselessly copy * conn->errorMessage. * * NB: This allocation can fail, if you run out of memory. The rest of the * function handles that gracefully, and we still try to set the error * message as the connection's error message. */ res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); if (res) { res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; res->errMsg = pqResultStrdup(res, workBuf.data); } /* * Break the message into fields. We can't do very much here, but we can * split the severity code off, and remove trailing newlines. Also, we use * the heuristic that the primary message extends only to the first * newline --- anything after that is detail message. (In some cases it'd * be better classed as hint, but we can hardly be expected to guess that * here.) */ while (workBuf.len > 0 && workBuf.data[workBuf.len - 1] == '\n') workBuf.data[--workBuf.len] = '\0'; splitp = strstr(workBuf.data, ": "); if (splitp) { /* what comes before the colon is severity */ *splitp = '\0'; pqSaveMessageField(res, PG_DIAG_SEVERITY, workBuf.data); startp = splitp + 3; } else { /* can't find a colon? oh well... */ startp = workBuf.data; } splitp = strchr(startp, '\n'); if (splitp) { /* what comes before the newline is primary message */ *splitp++ = '\0'; pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); /* the rest is detail; strip any leading whitespace */ while (*splitp && isspace((unsigned char) *splitp)) splitp++; pqSaveMessageField(res, PG_DIAG_MESSAGE_DETAIL, splitp); } else { /* single-line message, so all primary */ pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); } /* * Either save error as current async result, or just emit the notice. * Also, if it's an error and we were in a transaction block, assume the * server has now gone to error-in-transaction state. */ if (isError) { pqClearAsyncResult(conn); conn->result = res; resetPQExpBuffer(&conn->errorMessage); if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg) appendPQExpBufferStr(&conn->errorMessage, res->errMsg); else printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); if (conn->xactStatus == PQTRANS_INTRANS) conn->xactStatus = PQTRANS_INERROR; } else { if (res) { if (res->noticeHooks.noticeRec != NULL) (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); PQclear(res); } } termPQExpBuffer(&workBuf); return 0; failure: if (res) PQclear(res); termPQExpBuffer(&workBuf); return EOF; }
/* * parseInput subroutine to read a 'T' (row descriptions) message. * We build a PGresult structure containing the attribute data. * Returns: 0 if completed message, EOF if error or not enough data * received yet. * * Note that if we run out of data, we have to suspend and reprocess * the message after more data is received. Otherwise, conn->inStart * must get advanced past the processed data. */ static int getRowDescriptions(PGconn *conn) { PGresult *result; int nfields; const char *errmsg; int i; result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); if (!result) { errmsg = NULL; /* means "out of memory", see below */ goto advance_and_error; } /* parseInput already read the 'T' label. */ /* the next two bytes are the number of fields */ if (pqGetInt(&(result->numAttributes), 2, conn)) goto EOFexit; nfields = result->numAttributes; /* allocate space for the attribute descriptors */ if (nfields > 0) { result->attDescs = (PGresAttDesc *) pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); if (!result->attDescs) { errmsg = NULL; /* means "out of memory", see below */ goto advance_and_error; } MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); } /* get type info */ for (i = 0; i < nfields; i++) { int typid; int typlen; int atttypmod; if (pqGets(&conn->workBuffer, conn) || pqGetInt(&typid, 4, conn) || pqGetInt(&typlen, 2, conn) || pqGetInt(&atttypmod, 4, conn)) goto EOFexit; /* * Since pqGetInt treats 2-byte integers as unsigned, we need to * coerce the result to signed form. */ typlen = (int) ((int16) typlen); result->attDescs[i].name = pqResultStrdup(result, conn->workBuffer.data); if (!result->attDescs[i].name) { errmsg = NULL; /* means "out of memory", see below */ goto advance_and_error; } result->attDescs[i].tableid = 0; result->attDescs[i].columnid = 0; result->attDescs[i].format = 0; result->attDescs[i].typid = typid; result->attDescs[i].typlen = typlen; result->attDescs[i].atttypmod = atttypmod; } /* Success! */ conn->result = result; /* Advance inStart to show that the "T" message has been processed. */ conn->inStart = conn->inCursor; /* * We could perform additional setup for the new result set here, but for * now there's nothing else to do. */ /* And we're done. */ return 0; advance_and_error: /* * Discard the failed message. Unfortunately we don't know for sure where * the end is, so just throw away everything in the input buffer. This is * not very desirable but it's the best we can do in protocol v2. */ conn->inStart = conn->inEnd; /* * Replace partially constructed result with an error result. First * discard the old result to try to win back some memory. */ pqClearAsyncResult(conn); /* * If preceding code didn't provide an error message, assume "out of * memory" was meant. The advantage of having this special case is that * freeing the old result first greatly improves the odds that gettext() * will succeed in providing a translation. */ if (!errmsg) errmsg = libpq_gettext("out of memory for query result"); printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); /* * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can * do to recover... */ conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); conn->asyncStatus = PGASYNC_READY; EOFexit: if (result && result != conn->result) PQclear(result); return EOF; }
/* * parseInput: if appropriate, parse input data from backend * until input is exhausted or a stopping state is reached. * Note that this function will NOT attempt to read more data from the backend. */ void pqParseInput2(PGconn *conn) { char id; /* * Loop to parse successive complete messages available in the buffer. */ for (;;) { /* * Quit if in COPY_OUT state: we expect raw data from the server until * PQendcopy is called. Don't try to parse it according to the normal * protocol. (This is bogus. The data lines ought to be part of the * protocol and have identifying leading characters.) */ if (conn->asyncStatus == PGASYNC_COPY_OUT) return; /* * OK to try to read a message type code. */ conn->inCursor = conn->inStart; if (pqGetc(&id, conn)) return; /* * NOTIFY and NOTICE messages can happen in any state besides COPY * OUT; always process them right away. * * Most other messages should only be processed while in BUSY state. * (In particular, in READY state we hold off further parsing until * the application collects the current PGresult.) * * However, if the state is IDLE then we got trouble; we need to deal * with the unexpected message somehow. */ if (id == 'A') { if (getNotify(conn)) return; } else if (id == 'N') { if (pqGetErrorNotice2(conn, false)) return; } else if (conn->asyncStatus != PGASYNC_BUSY) { /* If not IDLE state, just wait ... */ if (conn->asyncStatus != PGASYNC_IDLE) return; /* * Unexpected message in IDLE state; need to recover somehow. * ERROR messages are displayed using the notice processor; * anything else is just dropped on the floor after displaying a * suitable warning notice. (An ERROR is very possibly the * backend telling us why it is about to close the connection, so * we don't want to just discard it...) */ if (id == 'E') { if (pqGetErrorNotice2(conn, false /* treat as notice */ )) return; } else { pqInternalNotice(&conn->noticeHooks, "message type 0x%02x arrived from server while idle", id); /* Discard the unexpected message; good idea?? */ conn->inStart = conn->inEnd; break; } } else { /* * In BUSY state, we can process everything. */ switch (id) { case 'C': /* command complete */ if (pqGets(&conn->workBuffer, conn)) return; if (conn->result == NULL) { conn->result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); if (!conn->result) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); pqSaveErrorResult(conn); } } if (conn->result) { strlcpy(conn->result->cmdStatus, conn->workBuffer.data, CMDSTATUS_LEN); } checkXactStatus(conn, conn->workBuffer.data); conn->asyncStatus = PGASYNC_READY; break; case 'E': /* error return */ if (pqGetErrorNotice2(conn, true)) return; conn->asyncStatus = PGASYNC_READY; break; case 'Z': /* backend is ready for new query */ conn->asyncStatus = PGASYNC_IDLE; break; case 'I': /* empty query */ /* read and throw away the closing '\0' */ if (pqGetc(&id, conn)) return; if (id != '\0') pqInternalNotice(&conn->noticeHooks, "unexpected character %c following empty query response (\"I\" message)", id); if (conn->result == NULL) { conn->result = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); if (!conn->result) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); pqSaveErrorResult(conn); } } conn->asyncStatus = PGASYNC_READY; break; case 'K': /* secret key data from the backend */ /* * This is expected only during backend startup, but it's * just as easy to handle it as part of the main loop. * Save the data and continue processing. */ if (pqGetInt(&(conn->be_pid), 4, conn)) return; if (pqGetInt(&(conn->be_key), 4, conn)) return; break; case 'P': /* synchronous (normal) portal */ if (pqGets(&conn->workBuffer, conn)) return; /* We pretty much ignore this message type... */ break; case 'T': /* row descriptions (start of query results) */ if (conn->result == NULL) { /* First 'T' in a query sequence */ if (getRowDescriptions(conn)) return; /* getRowDescriptions() moves inStart itself */ continue; } else { /* * A new 'T' message is treated as the start of * another PGresult. (It is not clear that this is * really possible with the current backend.) We stop * parsing until the application accepts the current * result. */ conn->asyncStatus = PGASYNC_READY; return; } break; case 'D': /* ASCII data tuple */ if (conn->result != NULL) { /* Read another tuple of a normal query response */ if (getAnotherTuple(conn, FALSE)) return; /* getAnotherTuple() moves inStart itself */ continue; } else { pqInternalNotice(&conn->noticeHooks, "server sent data (\"D\" message) without prior row description (\"T\" message)"); /* Discard the unexpected message; good idea?? */ conn->inStart = conn->inEnd; return; } break; case 'B': /* Binary data tuple */ if (conn->result != NULL) { /* Read another tuple of a normal query response */ if (getAnotherTuple(conn, TRUE)) return; /* getAnotherTuple() moves inStart itself */ continue; } else { pqInternalNotice(&conn->noticeHooks, "server sent binary data (\"B\" message) without prior row description (\"T\" message)"); /* Discard the unexpected message; good idea?? */ conn->inStart = conn->inEnd; return; } break; case 'G': /* Start Copy In */ conn->asyncStatus = PGASYNC_COPY_IN; break; case 'H': /* Start Copy Out */ conn->asyncStatus = PGASYNC_COPY_OUT; break; /* * Don't need to process CopyBothResponse here because it * never arrives from the server during protocol 2.0. */ default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( "unexpected response from server; first received character was \"%c\"\n"), id); /* build an error result holding the error message */ pqSaveErrorResult(conn); /* Discard the unexpected message; good idea?? */ conn->inStart = conn->inEnd; conn->asyncStatus = PGASYNC_READY; return; } /* switch on protocol character */ } /* Successfully consumed this message */ conn->inStart = conn->inCursor; } }
/* * Attempt to read an Error or Notice response message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'E' or 'N' message type and length have already been consumed. * Exit: returns 0 if successfully consumed message. * returns EOF if not enough data. */ int pqGetErrorNotice3(PGconn *conn, bool isError) { PGresult *res = NULL; 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 * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended * for stuff that is expected to be short. We shouldn't use * conn->errorMessage either, since this might be only a notice. */ initPQExpBuffer(&workBuf); /* * Make a PGresult to hold the accumulated fields. We temporarily lie * about the result status, so that PQmakeEmptyPGresult doesn't uselessly * copy conn->errorMessage. */ res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); if (!res) goto fail; res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; /* * Read the fields and save into res. */ for (;;) { if (pqGetc(&id, conn)) goto fail; if (id == '\0') break; /* terminator found */ if (pqGets(&workBuf, conn)) goto fail; pqSaveMessageField(res, id, workBuf.data); } /* * Now build the "overall" error message for PQresultErrorMessage. */ resetPQExpBuffer(&workBuf); val = PQresultErrorField(res, PG_DIAG_SEVERITY); if (val) appendPQExpBuffer(&workBuf, "%s: ", val); if (conn->verbosity == PQERRORS_VERBOSE) { val = PQresultErrorField(res, PG_DIAG_SQLSTATE); if (val) appendPQExpBuffer(&workBuf, "%s: ", val); } val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); if (val) appendPQExpBufferStr(&workBuf, val); val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); if (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) { 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); val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT); if (val) appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); if (val) appendPQExpBuffer(&workBuf, libpq_gettext("QUERY: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_CONTEXT); if (val) appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"), val); } if (conn->verbosity == PQERRORS_VERBOSE) { const char *valf; const char *vall; valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE); vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE); val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION); if (val || valf || vall) { appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION: ")); if (val) appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val); if (valf && vall) /* unlikely we'd have just one */ appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"), valf, vall); appendPQExpBufferChar(&workBuf, '\n'); } } /* * Either save error as current async result, or just emit the notice. */ if (isError) { res->errMsg = pqResultStrdup(res, workBuf.data); if (!res->errMsg) goto fail; pqClearAsyncResult(conn); conn->result = res; resetPQExpBuffer(&conn->errorMessage); appendPQExpBufferStr(&conn->errorMessage, workBuf.data); } else { /* We can cheat a little here and not copy the message. */ res->errMsg = workBuf.data; if (res->noticeHooks.noticeRec != NULL) (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); PQclear(res); } termPQExpBuffer(&workBuf); return 0; fail: PQclear(res); termPQExpBuffer(&workBuf); return EOF; }
/* * parseInput: if appropriate, parse input data from backend * until input is exhausted or a stopping state is reached. * Note that this function will NOT attempt to read more data from the backend. */ void pqParseInput3(PGconn *conn) { char id; int msgLength; int avail; /* * Loop to parse successive complete messages available in the buffer. */ for (;;) { /* * Try to read a message. First get the type code and length. Return * if not enough data. */ conn->inCursor = conn->inStart; if (pqGetc(&id, conn)) return; if (pqGetInt(&msgLength, 4, conn)) return; /* * Try to validate message type/length here. A length less than 4 is * definitely broken. Large lengths should only be believed for a few * message types. */ if (msgLength < 4) { handleSyncLoss(conn, id, msgLength); return; } if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) { handleSyncLoss(conn, id, msgLength); return; } /* * Can't process if message body isn't all here yet. */ msgLength -= 4; avail = conn->inEnd - conn->inCursor; if (avail < msgLength) { /* * Before returning, enlarge the input buffer if needed to hold * the whole message. This is better than leaving it to * pqReadData because we can avoid multiple cycles of realloc() * when the message is large; also, we can implement a reasonable * recovery strategy if we are unable to make the buffer big * enough. */ if (pqCheckInBufferSpace(conn->inCursor + msgLength, conn)) { /* * XXX add some better recovery code... plan is to skip over * the message using its length, then report an error. For the * moment, just treat this like loss of sync (which indeed it * might be!) */ handleSyncLoss(conn, id, msgLength); } return; } /* * NOTIFY and NOTICE messages can happen in any state; always process * them right away. * * Most other messages should only be processed while in BUSY state. * (In particular, in READY state we hold off further parsing until * the application collects the current PGresult.) * * However, if the state is IDLE then we got trouble; we need to deal * with the unexpected message somehow. * * ParameterStatus ('S') messages are a special case: in IDLE state we * must process 'em (this case could happen if a new value was adopted * from config file due to SIGHUP), but otherwise we hold off until * BUSY state. */ if (id == 'A') { if (getNotify(conn)) return; } else if (id == 'N') { if (pqGetErrorNotice3(conn, false)) return; } else if (conn->asyncStatus != PGASYNC_BUSY) { /* If not IDLE state, just wait ... */ if (conn->asyncStatus != PGASYNC_IDLE) return; /* * Unexpected message in IDLE state; need to recover somehow. * ERROR messages are displayed using the notice processor; * ParameterStatus is handled normally; anything else is just * dropped on the floor after displaying a suitable warning * notice. (An ERROR is very possibly the backend telling us why * it is about to close the connection, so we don't want to just * discard it...) */ if (id == 'E') { if (pqGetErrorNotice3(conn, false /* treat as notice */ )) return; } else if (id == 'S') { if (getParameterStatus(conn)) return; } else { pqInternalNotice(&conn->noticeHooks, "message type 0x%02x arrived from server while idle", id); /* Discard the unexpected message */ conn->inCursor += msgLength; } } else { /* * In BUSY state, we can process everything. */ switch (id) { case 'C': /* command complete */ if (pqGets(&conn->workBuffer, conn)) return; if (conn->result == NULL) { conn->result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); if (!conn->result) return; } strncpy(conn->result->cmdStatus, conn->workBuffer.data, CMDSTATUS_LEN); conn->asyncStatus = PGASYNC_READY; break; case 'E': /* error return */ if (pqGetErrorNotice3(conn, true)) return; conn->asyncStatus = PGASYNC_READY; break; case 'Z': /* backend is ready for new query */ if (getReadyForQuery(conn)) return; conn->asyncStatus = PGASYNC_IDLE; break; case 'I': /* empty query */ if (conn->result == NULL) { conn->result = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); if (!conn->result) return; } conn->asyncStatus = PGASYNC_READY; break; case '1': /* Parse Complete */ /* If we're doing PQprepare, we're done; else ignore */ if (conn->queryclass == PGQUERY_PREPARE) { if (conn->result == NULL) { conn->result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); if (!conn->result) return; } conn->asyncStatus = PGASYNC_READY; } break; case '2': /* Bind Complete */ case '3': /* Close Complete */ /* Nothing to do for these message types */ break; case 'S': /* parameter status */ if (getParameterStatus(conn)) return; break; case 'K': /* secret key data from the backend */ /* * This is expected only during backend startup, but it's * just as easy to handle it as part of the main loop. * Save the data and continue processing. */ if (pqGetInt(&(conn->be_pid), 4, conn)) return; if (pqGetInt(&(conn->be_key), 4, conn)) return; break; case 'T': /* Row Description */ if (conn->result == NULL || conn->queryclass == PGQUERY_DESCRIBE) { /* First 'T' in a query sequence */ if (getRowDescriptions(conn)) return; /* * If we're doing a Describe, we're ready to pass the * result back to the client. */ if (conn->queryclass == PGQUERY_DESCRIBE) conn->asyncStatus = PGASYNC_READY; } else { /* * A new 'T' message is treated as the start of * another PGresult. (It is not clear that this is * really possible with the current backend.) We stop * parsing until the application accepts the current * result. */ conn->asyncStatus = PGASYNC_READY; return; } break; case 'n': /* No Data */ /* * NoData indicates that we will not be seeing a * RowDescription message because the statement or portal * inquired about doesn't return rows. Set up a COMMAND_OK * result, instead of TUPLES_OK. */ if (conn->result == NULL) conn->result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); /* * If we're doing a Describe, we're ready to pass the * result back to the client. */ if (conn->queryclass == PGQUERY_DESCRIBE) conn->asyncStatus = PGASYNC_READY; break; case 't': /* Parameter Description */ if (getParamDescriptions(conn)) return; break; case 'D': /* Data Row */ if (conn->result != NULL && conn->result->resultStatus == PGRES_TUPLES_OK) { /* Read another tuple of a normal query response */ if (getAnotherTuple(conn, msgLength)) return; } else if (conn->result != NULL && conn->result->resultStatus == PGRES_FATAL_ERROR) { /* * We've already choked for some reason. Just discard * tuples till we get to the end of the query. */ conn->inCursor += msgLength; } else { /* Set up to report error at end of query */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n")); pqSaveErrorResult(conn); /* Discard the unexpected message */ conn->inCursor += msgLength; } break; case 'G': /* Start Copy In */ if (getCopyStart(conn, PGRES_COPY_IN)) return; conn->asyncStatus = PGASYNC_COPY_IN; break; case 'H': /* Start Copy Out */ if (getCopyStart(conn, PGRES_COPY_OUT)) return; conn->asyncStatus = PGASYNC_COPY_OUT; conn->copy_already_done = 0; break; case 'd': /* Copy Data */ /* * If we see Copy Data, just silently drop it. This would * only occur if application exits COPY OUT mode too * early. */ conn->inCursor += msgLength; break; case 'c': /* Copy Done */ /* * If we see Copy Done, just silently drop it. This is * the normal case during PQendcopy. We will keep * swallowing data, expecting to see command-complete for * the COPY command. */ break; default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( "unexpected response from server; first received character was \"%c\"\n"), id); /* build an error result holding the error message */ pqSaveErrorResult(conn); /* not sure if we will see more, so go to ready state */ conn->asyncStatus = PGASYNC_READY; /* Discard the unexpected message */ conn->inCursor += msgLength; break; } /* switch on protocol character */ } /* Successfully consumed this message */ if (conn->inCursor == conn->inStart + 5 + msgLength) { /* Normal case: parsing agrees with specified length */ conn->inStart = conn->inCursor; } else { /* Trouble --- report it */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("message contents do not agree with length in message type \"%c\"\n"), id); /* build an error result holding the error message */ pqSaveErrorResult(conn); conn->asyncStatus = PGASYNC_READY; /* trust the specified message length as what to skip */ conn->inStart += 5 + msgLength; } } }
/* * parseInput subroutine to read a 'T' (row descriptions) message. * We'll build a new PGresult structure (unless called for a Describe * command for a prepared statement) containing the attribute data. * Returns: 0 if completed message, EOF if not enough data yet. * * Note that if we run out of data, we have to release the partially * constructed PGresult, and rebuild it again next time. Fortunately, * that shouldn't happen often, since 'T' messages usually fit in a packet. */ static int getRowDescriptions(PGconn *conn) { PGresult *result; int nfields; int i; /* * When doing Describe for a prepared statement, there'll already be a * PGresult created by getParamDescriptions, and we should fill data into * that. Otherwise, create a new, empty PGresult. */ if (conn->queryclass == PGQUERY_DESCRIBE) { if (conn->result) result = conn->result; else result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); } else result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); if (!result) goto failure; /* parseInput already read the 'T' label and message length. */ /* the next two bytes are the number of fields */ if (pqGetInt(&(result->numAttributes), 2, conn)) goto failure; nfields = result->numAttributes; /* allocate space for the attribute descriptors */ if (nfields > 0) { result->attDescs = (PGresAttDesc *) pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); if (!result->attDescs) goto failure; MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); } /* result->binary is true only if ALL columns are binary */ result->binary = (nfields > 0) ? 1 : 0; /* get type info */ for (i = 0; i < nfields; i++) { int tableid; int columnid; int typid; int typlen; int atttypmod; int format; if (pqGets(&conn->workBuffer, conn) || pqGetInt(&tableid, 4, conn) || pqGetInt(&columnid, 2, conn) || pqGetInt(&typid, 4, conn) || pqGetInt(&typlen, 2, conn) || pqGetInt(&atttypmod, 4, conn) || pqGetInt(&format, 2, conn)) { goto failure; } /* * Since pqGetInt treats 2-byte integers as unsigned, we need to * coerce these results to signed form. */ columnid = (int) ((int16) columnid); typlen = (int) ((int16) typlen); format = (int) ((int16) format); result->attDescs[i].name = pqResultStrdup(result, conn->workBuffer.data); if (!result->attDescs[i].name) goto failure; result->attDescs[i].tableid = tableid; result->attDescs[i].columnid = columnid; result->attDescs[i].format = format; result->attDescs[i].typid = typid; result->attDescs[i].typlen = typlen; result->attDescs[i].atttypmod = atttypmod; if (format != 1) result->binary = 0; } /* Success! */ conn->result = result; return 0; failure: /* * Discard incomplete result, unless it's from getParamDescriptions. * * Note that if we hit a bufferload boundary while handling the * describe-statement case, we'll forget any PGresult space we just * allocated, and then reallocate it on next try. This will bloat the * PGresult a little bit but the space will be freed at PQclear, so it * doesn't seem worth trying to be smarter. */ if (result != conn->result) PQclear(result); return EOF; }
/* * parseInput subroutine to read a 'T' (row descriptions) message. * We build a PGresult structure containing the attribute data. * Returns: 0 if completed message, EOF if not enough data yet. * * Note that if we run out of data, we have to release the partially * constructed PGresult, and rebuild it again next time. Fortunately, * that shouldn't happen often, since 'T' messages usually fit in a packet. */ static int getRowDescriptions(PGconn *conn) { PGresult *result = NULL; int nfields; int i; result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); if (!result) goto failure; /* parseInput already read the 'T' label. */ /* the next two bytes are the number of fields */ if (pqGetInt(&(result->numAttributes), 2, conn)) goto failure; nfields = result->numAttributes; /* allocate space for the attribute descriptors */ if (nfields > 0) { result->attDescs = (PGresAttDesc *) pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); if (!result->attDescs) goto failure; MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); } /* get type info */ for (i = 0; i < nfields; i++) { int typid; int typlen; int atttypmod; if (pqGets(&conn->workBuffer, conn) || pqGetInt(&typid, 4, conn) || pqGetInt(&typlen, 2, conn) || pqGetInt(&atttypmod, 4, conn)) goto failure; /* * Since pqGetInt treats 2-byte integers as unsigned, we need to * coerce the result to signed form. */ typlen = (int) ((int16) typlen); result->attDescs[i].name = pqResultStrdup(result, conn->workBuffer.data); if (!result->attDescs[i].name) goto failure; result->attDescs[i].tableid = 0; result->attDescs[i].columnid = 0; result->attDescs[i].format = 0; result->attDescs[i].typid = typid; result->attDescs[i].typlen = typlen; result->attDescs[i].atttypmod = atttypmod; } /* Success! */ conn->result = result; return 0; failure: if (result) PQclear(result); return EOF; }
/* * Initialize SASL authentication exchange. */ static int pg_SASL_init(PGconn *conn, int payloadlen) { char *initialresponse = NULL; int initialresponselen; bool done; bool success; const char *selected_mechanism; PQExpBufferData mechanism_buf; initPQExpBuffer(&mechanism_buf); if (conn->sasl_state) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("duplicate SASL authentication request\n")); goto error; } /* * Parse the list of SASL authentication mechanisms in the * AuthenticationSASL message, and select the best mechanism that we * support. (Only SCRAM-SHA-256 is supported at the moment.) */ selected_mechanism = NULL; for (;;) { if (pqGets(&mechanism_buf, conn)) { printfPQExpBuffer(&conn->errorMessage, "fe_sendauth: invalid authentication request from server: invalid list of authentication mechanisms\n"); goto error; } if (PQExpBufferDataBroken(mechanism_buf)) goto oom_error; /* An empty string indicates end of list */ if (mechanism_buf.data[0] == '\0') break; /* * If we have already selected a mechanism, just skip through the rest * of the list. */ if (selected_mechanism) continue; /* * Do we support this mechanism? */ if (strcmp(mechanism_buf.data, SCRAM_SHA_256_NAME) == 0) { char *password; conn->password_needed = true; password = conn->connhost[conn->whichhost].password; if (password == NULL) password = conn->pgpass; if (password == NULL || password[0] == '\0') { printfPQExpBuffer(&conn->errorMessage, PQnoPasswordSupplied); goto error; } conn->sasl_state = pg_fe_scram_init(conn->pguser, password); if (!conn->sasl_state) goto oom_error; selected_mechanism = SCRAM_SHA_256_NAME; } } if (!selected_mechanism) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("none of the server's SASL authentication mechanisms are supported\n")); goto error; } /* Get the mechanism-specific Initial Client Response, if any */ pg_fe_scram_exchange(conn->sasl_state, NULL, -1, &initialresponse, &initialresponselen, &done, &success, &conn->errorMessage); if (done && !success) goto error; /* * Build a SASLInitialResponse message, and send it. */ if (pqPutMsgStart('p', true, conn)) goto error; if (pqPuts(selected_mechanism, conn)) goto error; if (initialresponse) { if (pqPutInt(initialresponselen, 4, conn)) goto error; if (pqPutnchar(initialresponse, initialresponselen, conn)) goto error; } if (pqPutMsgEnd(conn)) goto error; if (pqFlush(conn)) goto error; termPQExpBuffer(&mechanism_buf); if (initialresponse) free(initialresponse); return STATUS_OK; error: termPQExpBuffer(&mechanism_buf); if (initialresponse) free(initialresponse); return STATUS_ERROR; oom_error: termPQExpBuffer(&mechanism_buf); if (initialresponse) free(initialresponse); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; }