📄 fe-protocol2.c
字号:
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; } 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; } 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; } 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; 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; }}/* * 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 intgetRowDescriptions(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;}/* * parseInput subroutine to read a 'B' or 'D' (row data) message. * We add another tuple to the existing PGresult structure. * Returns: 0 if completed message, EOF if error or not enough data yet. * * Note that if we run out of data, we have to suspend and reprocess * the message after more data is received. We keep a partially constructed * tuple in conn->curTuple, and avoid reallocating already-allocated storage. */static intgetAnotherTuple(PGconn *conn, bool binary){ PGresult *result = conn->result; int nfields = result->numAttributes; PGresAttValue *tup; /* the backend sends us a bitmap of which attributes are null */ char std_bitmap[64]; /* used unless it doesn't fit */ char *bitmap = std_bitmap; int i; size_t nbytes; /* the number of bytes in bitmap */ char bmap; /* One byte of the bitmap */ int bitmap_index; /* Its index */ int bitcnt; /* number of bits examined in current byte */ int vlen; /* length of the current field value */ result->binary = binary; /* Allocate tuple space if first time for this data message */ if (conn->curTuple == NULL) { conn->curTuple = (PGresAttValue *) pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE); if (conn->curTuple == NULL) goto outOfMemory; MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue)); /* * If it's binary, fix the column format indicators. We assume the * backend will consistently send either B or D, not a mix. */ if (binary) { for (i = 0; i < nfields; i++) result->attDescs[i].format = 1; } } tup = conn->curTuple; /* Get the null-value bitmap */ nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE; /* malloc() only for unusually large field counts... */ if (nbytes > sizeof(std_bitmap)) { bitmap = (char *) malloc(nbytes); if (!bitmap) goto outOfMemory; } if (pqGetnchar(bitmap, nbytes, conn)) goto EOFexit; /* Scan the fields */ bitmap_index = 0; bmap = bitmap[bitmap_index]; bitcnt = 0; for (i = 0; i < nfields; i++) { if (!(bmap & 0200)) { /* if the field value is absent, make it a null string */ tup[i].value = result->null_field; tup[i].len = NULL_LEN; } else { /* get the value length (the first four bytes are for length) */ if (pqGetInt(&vlen, 4, conn)) goto EOFexit; if (!binary) vlen = vlen - 4; if (vlen < 0) vlen = 0; if (tup[i].value == NULL) { tup[i].value = (char *) pqResultAlloc(result, vlen + 1, binary); if (tup[i].value == NULL) goto outOfMemory; } tup[i].len = vlen; /* read in the value */ if (vlen > 0) if (pqGetnchar((char *) (tup[i].value), vlen, conn)) goto EOFexit; /* we have to terminate this ourselves */ tup[i].value[vlen] = '\0'; } /* advance the bitmap stuff */ bitcnt++; if (bitcnt == BITS_PER_BYTE) { bitmap_index++; bmap = bitmap[bitmap_index]; bitcnt = 0; } else bmap <<= 1; } /* Success! Store the completed tuple in the result */ if (!pqAddTuple(result, tup)) goto outOfMemory; /* and reset for a new message */ conn->curTuple = NULL; if (bitmap != std_bitmap) free(bitmap); return 0;outOfMemory: /* Replace partially constructed result with an error result */ /* * we do NOT use pqSaveErrorResult() here, because of the likelihood that * there's not enough memory to concatenate messages... */ pqClearAsyncResult(conn); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory for query result\n")); /* * 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; /* Discard the failed message --- good idea? */ conn->inStart = conn->inEnd;EOFexit: if (bitmap != NULL && bitmap != std_bitmap) free(bitmap); return EOF;}/* * 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 intpqGetErrorNotice2(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. */ res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); if (!res) goto failure; res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; res->errMsg = pqResultStrdup(res, workBuf.data); if (!res->errMsg) goto failure; /* * 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); appendPQExpBufferStr(&conn->errorMessage, res->errMsg); if (conn->xactStatus == PQTRANS_INTRANS) conn->xactStatus = PQTRANS_INERROR; } else { 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;}/* * checkXactStatus - attempt to track transaction-block status of server * * This is called each time we receive a command-complete message. By * watching for messages from BEGIN/COMMIT/ROLLBACK commands, we can do * a passable job of tracking the server's xact status. BUT: this does * not work at all on 7.3 servers with AUTOCOMMIT OFF. (Man, was that * feature ever a mistake.) Caveat user. * * The tags known here are all those used as far back as 7.0; is it worth * adding those from even-older servers? */static voidcheckXactStatus(PGconn *conn, const char *cmdTag){ if (strcmp(cmdTag, "BEGIN") == 0) conn->xactStatus = PQTRANS_INTRANS; else if (strcmp(cmdTag, "COMMIT") == 0) conn->xactStatus = PQTRANS_IDLE; else if (strcmp(cmdTag, "ROLLBACK") == 0) conn->xactStatus = PQTRANS_IDLE; else if (strcmp(cmdTag, "START TRANSACTION") == 0) /* 7.3 only */ conn->xactStatus = PQTRANS_INTRANS; /* * Normally we get into INERROR state by detecting an Error message. * However, if we see one of these tags then we know for sure the server * is in abort state ... */ else if (strcmp(cmdTag, "*ABORT STATE*") == 0) /* pre-7.3 only */ conn->xactStatus = PQTRANS_INERROR;}/* * 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 intgetNotify(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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -