/* *NOTIFYandNOTICEmessagescanhappeninanystate;alwaysprocess *themrightaway. * *MostothermessagesshouldonlybeprocessedwhileinBUSYstate. *(Inparticular,inREADYstateweholdofffurtherparsinguntil *theapplicationcollectsthecurrentPGresult.) * *However,ifthestateisIDLEthenwegottrouble;weneedtodeal *withtheunexpectedmessagesomehow. * *ParameterStatus('S')messagesareaspecialcase:inIDLEstatewe *mustprocess'em(thiscasecouldhappenifanewvaluewasadopted *fromconfigfileduetoSIGHUP),butotherwiseweholdoffuntil *BUSYstate.
*/ if (id == PqMsg_NotificationResponse)
{ if (getNotify(conn)) return;
} elseif (id == PqMsg_NoticeResponse)
{ if (pqGetErrorNotice3(conn, false)) return;
} elseif (conn->asyncStatus != PGASYNC_BUSY)
{ /* If not IDLE state, just wait ... */ if (conn->asyncStatus != PGASYNC_IDLE) return;
/* *UnexpectedmessageinIDLEstate;needtorecoversomehow. *ERRORmessagesarehandledusingthenoticeprocessor; *ParameterStatusishandlednormally;anythingelseisjust *droppedonthefloorafterdisplayingasuitablewarning *notice.(AnERRORisverypossiblythebackendtellinguswhy *itisabouttoclosetheconnection,sowedon'twanttojust *discardit...)
*/ if (id == PqMsg_ErrorResponse)
{ if (pqGetErrorNotice3(conn, false/* treat as notice */ )) return;
} elseif (id == PqMsg_ParameterStatus)
{ if (getParameterStatus(conn)) return;
} else
{ /* Any other case is unexpected and we summarily skip it */
pqInternalNotice(&conn->noticeHooks, "message type 0x%02x arrived from server while idle",
id); /* Discard the unexpected message */
conn->inCursor += msgLength;
}
} else
{ /* *InBUSYstate,wecanprocesseverything.
*/ switch (id)
{ case PqMsg_CommandComplete: if (pqGets(&conn->workBuffer, conn)) return; if (!pgHavePendingResult(conn))
{
conn->result = PQmakeEmptyPGresult(conn,
PGRES_COMMAND_OK); if (!conn->result)
{
libpq_append_conn_error(conn, "out of memory");
pqSaveErrorResult(conn);
}
} if (conn->result)
strlcpy(conn->result->cmdStatus, conn->workBuffer.data,
CMDSTATUS_LEN);
conn->asyncStatus = PGASYNC_READY; break; case PqMsg_ErrorResponse: if (pqGetErrorNotice3(conn, true)) return;
conn->asyncStatus = PGASYNC_READY; break; case PqMsg_ReadyForQuery: if (getReadyForQuery(conn)) return; if (conn->pipelineStatus != PQ_PIPELINE_OFF)
{
conn->result = PQmakeEmptyPGresult(conn,
PGRES_PIPELINE_SYNC); if (!conn->result)
{
libpq_append_conn_error(conn, "out of memory");
pqSaveErrorResult(conn);
} else
{
conn->pipelineStatus = PQ_PIPELINE_ON;
conn->asyncStatus = PGASYNC_READY;
}
} else
{ /* Advance the command queue and set us idle */
pqCommandQueueAdvance(conn, true, false);
conn->asyncStatus = PGASYNC_IDLE;
} break; case PqMsg_EmptyQueryResponse: if (!pgHavePendingResult(conn))
{
conn->result = PQmakeEmptyPGresult(conn,
PGRES_EMPTY_QUERY); if (!conn->result)
{
libpq_append_conn_error(conn, "out of memory");
pqSaveErrorResult(conn);
}
}
conn->asyncStatus = PGASYNC_READY; break; case PqMsg_ParseComplete: /* If we're doing PQprepare, we're done; else ignore */ if (conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_PREPARE)
{ if (!pgHavePendingResult(conn))
{
conn->result = PQmakeEmptyPGresult(conn,
PGRES_COMMAND_OK); if (!conn->result)
{
libpq_append_conn_error(conn, "out of memory");
pqSaveErrorResult(conn);
}
}
conn->asyncStatus = PGASYNC_READY;
} break; case PqMsg_BindComplete: /* Nothing to do for this message type */ break; case PqMsg_CloseComplete: /* If we're doing PQsendClose, we're done; else ignore */ if (conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_CLOSE)
{ if (!pgHavePendingResult(conn))
{
conn->result = PQmakeEmptyPGresult(conn,
PGRES_COMMAND_OK); if (!conn->result)
{
libpq_append_conn_error(conn, "out of memory");
pqSaveErrorResult(conn);
}
}
conn->asyncStatus = PGASYNC_READY;
} break; case PqMsg_ParameterStatus: if (getParameterStatus(conn)) return; break; case PqMsg_BackendKeyData:
/* *Thisisexpectedonlyduringbackendstartup,butit's *justaseasytohandleitaspartofthemainloop. *Savethedataandcontinueprocessing.
*/ if (getBackendKeyData(conn, msgLength)) return; break; case PqMsg_RowDescription: if (conn->error_result ||
(conn->result != NULL &&
conn->result->resultStatus == PGRES_FATAL_ERROR))
{ /* *We'vealreadychokedforsomereason.Justdiscard *thedatatillwegettotheendofthequery.
*/
conn->inCursor += msgLength;
} elseif (conn->result == NULL ||
(conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_DESCRIBE))
{ /* First 'T' in a query sequence */ if (getRowDescriptions(conn, msgLength)) return;
} else
{ /* *Anew'T'messageistreatedasthestartof *anotherPGresult.(Itisnotclearthatthisis *reallypossiblewiththecurrentbackend.)Westop *parsinguntiltheapplicationacceptsthecurrent *result.
*/
conn->asyncStatus = PGASYNC_READY; return;
} break; case PqMsg_NoData:
/* *NoDataindicatesthatwewillnotbeseeinga *RowDescriptionmessagebecausethestatementorportal *inquiredaboutdoesn'treturnrows. * *Ifwe'redoingaDescribe,wehavetopasssomething *backtotheclient,sosetupaCOMMAND_OKresult, *insteadofPGRES_TUPLES_OK.Otherwisewecanjust *ignorethismessage.
*/ if (conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_DESCRIBE)
{ if (!pgHavePendingResult(conn))
{
conn->result = PQmakeEmptyPGresult(conn,
PGRES_COMMAND_OK); if (!conn->result)
{
libpq_append_conn_error(conn, "out of memory");
pqSaveErrorResult(conn);
}
}
conn->asyncStatus = PGASYNC_READY;
} break; case PqMsg_ParameterDescription: if (getParamDescriptions(conn, msgLength)) return; break; case PqMsg_DataRow: if (conn->result != NULL &&
(conn->result->resultStatus == PGRES_TUPLES_OK ||
conn->result->resultStatus == PGRES_TUPLES_CHUNK))
{ /* Read another tuple of a normal query response */ if (getAnotherTuple(conn, msgLength)) return;
} elseif (conn->error_result ||
(conn->result != NULL &&
conn->result->resultStatus == PGRES_FATAL_ERROR))
{ /* *We'vealreadychokedforsomereason.Justdiscard *tuplestillwegettotheendofthequery.
*/
conn->inCursor += msgLength;
} else
{ /* Set up to report error at end of query */
libpq_append_conn_error(conn, "server sent data (\"D\" message) without prior row description (\"T\" message)");
pqSaveErrorResult(conn); /* Discard the unexpected message */
conn->inCursor += msgLength;
} break; case PqMsg_CopyInResponse: if (getCopyStart(conn, PGRES_COPY_IN)) return;
conn->asyncStatus = PGASYNC_COPY_IN; break; case PqMsg_CopyOutResponse: if (getCopyStart(conn, PGRES_COPY_OUT)) return;
conn->asyncStatus = PGASYNC_COPY_OUT;
conn->copy_already_done = 0; break; case PqMsg_CopyBothResponse: if (getCopyStart(conn, PGRES_COPY_BOTH)) return;
conn->asyncStatus = PGASYNC_COPY_BOTH;
conn->copy_already_done = 0; break; case PqMsg_CopyData:
/* *IfweseeCopyDone,justsilentlydropit.Thisis *thenormalcaseduringPQendcopy.Wewillkeep *swallowingdata,expectingtoseecommand-completefor *theCOPYcommand.
*/ break; default:
libpq_append_conn_error(conn, "unexpected response from server; first received character was \"%c\"", 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 */
pqParseDone(conn, conn->inCursor);
} elseif (conn->error_result && conn->status == CONNECTION_BAD)
{ /* The connection was abandoned and we already reported it */ return;
} else
{ /* Trouble --- report it */
libpq_append_conn_error(conn, "message contents do not agree with length in message type \"%c\"", 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;
}
}
}
/* *handleFatalError:cleanupafteranonrecoverableerror * *Thisisforerrorswhereweneedtoabandontheconnection.Thecallerhas *alreadysavedtheerrormessageinconn->errorMessage.
*/ staticvoid
handleFatalError(PGconn *conn)
{ /* build an error result holding the error message */
pqSaveErrorResult(conn);
conn->asyncStatus = PGASYNC_READY; /* drop out of PQgetResult wait loop */ /* flush input data since we're giving up on processing it */
pqDropConnection(conn, true);
conn->status = CONNECTION_BAD; /* No more connection to backend */
}
/* *handleSyncLoss:cleanupafterlossofmessage-boundarysync * *Thereisn'treallyalotwecandohereexceptabandontheconnection.
*/ staticvoid
handleSyncLoss(PGconn *conn, char id, int msgLength)
{
libpq_append_conn_error(conn, "lost synchronization with server: got message type \"%c\", length %d",
id, msgLength);
handleFatalError(conn);
}
/* *parseInputsubroutinetoreada'T'(rowdescriptions)message. *We'llbuildanewPGresultstructure(unlesscalledforaDescribe *commandforapreparedstatement)containingtheattributedata. *Returns:0ifprocessedmessagesuccessfully,EOFtosuspendparsing *(thelattercaseisnotactuallyusedcurrently).
*/ staticint
getRowDescriptions(PGconn *conn, int msgLength)
{
PGresult *result; int nfields; constchar *errmsg; int i;
/* *WhendoingDescribeforapreparedstatement,there'llalreadybea *PGresultcreatedbygetParamDescriptions,andweshouldfilldatainto *that.Otherwise,createanew,emptyPGresult.
*/ if (!conn->cmd_queue_head ||
(conn->cmd_queue_head &&
conn->cmd_queue_head->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)
{
errmsg = NULL; /* means "out of memory", see below */ goto advance_and_error;
}
/* parseInput already read the 'T' label and message length. */ /* the next two bytes are the number of fields */ if (pqGetInt(&(result->numAttributes), 2, conn))
{ /* We should not run out of data here, so complain */
errmsg = libpq_gettext("insufficient data in \"T\" message"); goto advance_and_error;
}
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));
}
/* 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))
{ /* We should not run out of data here, so complain */
errmsg = libpq_gettext("insufficient data in \"T\" message"); goto advance_and_error;
}
/* *parseInputsubroutinetoreada't'(ParameterDescription)message. *We'llbuildanewPGresultstructurecontainingtheparameterdata. *Returns:0ifprocessedmessagesuccessfully,EOFtosuspendparsing *(thelattercaseisnotactuallyusedcurrently).
*/ staticint
getParamDescriptions(PGconn *conn, int msgLength)
{
PGresult *result; constchar *errmsg = NULL; /* means "out of memory", see below */ int nparams; int i;
result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); if (!result) goto advance_and_error;
/* parseInput already read the 't' label and message length. */ /* the next two bytes are the number of parameters */ if (pqGetInt(&(result->numParameters), 2, conn)) goto not_enough_data;
nparams = result->numParameters;
/* allocate space for the parameter descriptors */ if (nparams > 0)
{
result->paramDescs = (PGresParamDesc *)
pqResultAlloc(result, nparams * sizeof(PGresParamDesc), true); if (!result->paramDescs) goto advance_and_error;
MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc));
}
/* get parameter info */ for (i = 0; i < nparams; i++)
{ int typid;
if (pqGetInt(&typid, 4, conn)) goto not_enough_data;
result->paramDescs[i].typid = typid;
}
/* Success! */
conn->result = result;
return0;
not_enough_data:
errmsg = libpq_gettext("insufficient data in \"t\" message");
advance_and_error: /* Discard unsaved result, if any */ if (result && result != conn->result)
PQclear(result);
/* *parseInputsubroutinetoreada'D'(rowdata)message. *Wefillrowbufwithcolumnpointersandthencalltherowprocessor. *Returns:0ifprocessedmessagesuccessfully,EOFtosuspendparsing *(thelattercaseisnotactuallyusedcurrently).
*/ staticint
getAnotherTuple(PGconn *conn, int msgLength)
{
PGresult *result = conn->result; int nfields = result->numAttributes; constchar *errmsg;
PGdataValue *rowbuf; int tupnfields; /* # fields from tuple */ int vlen; /* length of the current field value */ int i;
/* Get the field count and make sure it's what we expect */ if (pqGetInt(&tupnfields, 2, conn))
{ /* We should not run out of data here, so complain */
errmsg = libpq_gettext("insufficient data in \"D\" message"); goto advance_and_error;
}
if (tupnfields != nfields)
{
errmsg = libpq_gettext("unexpected field count in \"D\" message"); goto advance_and_error;
}
/* Resize row buffer if needed */
rowbuf = conn->rowBuf; if (nfields > conn->rowBufLen)
{
rowbuf = (PGdataValue *) realloc(rowbuf,
nfields * sizeof(PGdataValue)); if (!rowbuf)
{
errmsg = NULL; /* means "out of memory", see below */ goto advance_and_error;
}
conn->rowBuf = rowbuf;
conn->rowBufLen = nfields;
}
/* Scan the fields */ for (i = 0; i < nfields; i++)
{ /* get the value length */ if (pqGetInt(&vlen, 4, conn))
{ /* We should not run out of data here, so complain */
errmsg = libpq_gettext("insufficient data in \"D\" message"); goto advance_and_error;
}
rowbuf[i].len = vlen;
/* Skip over the data value */ if (vlen > 0)
{ if (pqSkipnchar(vlen, conn))
{ /* We should not run out of data here, so complain */
errmsg = libpq_gettext("insufficient data in \"D\" message"); goto advance_and_error;
}
}
}
/* Process the collected row */
errmsg = NULL; if (pqRowProcessor(conn, &errmsg)) return0; /* normal, successful exit */
/* pqRowProcessor failed, fall through to report it */
/* If in pipeline mode, set error indicator for it */ if (isError && conn->pipelineStatus != PQ_PIPELINE_OFF)
conn->pipelineStatus = PQ_PIPELINE_ABORTED;
/* *Ifthisisanerrormessage,pre-emptivelyclearanyincompletequery *resultwemayhave.We'djustthrowitawaybelowanyway,and *releasingitbeforecollectingtheerrormightavoidout-of-memory.
*/ if (isError)
pqClearAsyncResult(conn);
/* *Eithersaveerrorascurrentasyncresult,orjustemitthenotice.
*/ if (isError)
{
pqClearAsyncResult(conn); /* redundant, but be safe */ if (res)
{
pqSetResultError(res, &workBuf, 0);
conn->result = res;
} else
{ /* Fall back to using the internal-error processing paths */
conn->error_result = true;
}
if (PQExpBufferDataBroken(workBuf))
libpq_append_conn_error(conn, "out of memory"); else
appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
} else
{ /* if we couldn't allocate the result set, just discard the NOTICE */ if (res)
{ /* *Wecancheatalittlehereandnotcopythemessage.Butifwe *wereunluckyenoughtorunoutofmemorywhilefillingworkBuf, *insert"outofmemory",asinpqSetResultError.
*/ if (PQExpBufferDataBroken(workBuf))
res->errMsg = libpq_gettext("out of memory\n"); else
res->errMsg = workBuf.data; if (res->noticeHooks.noticeRec != NULL)
res->noticeHooks.noticeRec(res->noticeHooks.noticeRecArg, res);
PQclear(res);
}
}
/* If we couldn't allocate a PGresult, just say "out of memory" */ if (res == NULL)
{
appendPQExpBufferStr(msg, libpq_gettext("out of memory\n")); return;
}
w = pg_encoding_dsplen(encoding, &wquery[qoffset]); /* treat any non-tab control chars as width 1 */ if (w <= 0)
w = 1;
scroffset += w;
qoffset += PQmblenBounded(&wquery[qoffset], encoding);
} else
{ /* We assume wide chars only exist in multibyte encodings */
scroffset++;
qoffset++;
}
} /* Fix up if we didn't find an end-of-line after loc */ if (iend < 0)
{
iend = cno; /* query length in chars, +1 */
qidx[iend] = qoffset;
scridx[iend] = scroffset;
}
/* Print only if loc is within computed query length */ if (loc <= cno)
{ /* If the line extracted is too long, we truncate it. */
beg_trunc = false;
end_trunc = false; if (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
{ /* *Wefirsttruncaterightifitisenough.Thiscodemightbe *offaspaceorsoonenforcingMIN_RIGHT_CUTifthere'sawide *characterrightthere,butthatshouldbeokay.
*/ if (scridx[ibeg] + DISPLAY_SIZE >= scridx[loc] + MIN_RIGHT_CUT)
{ while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
iend--;
end_trunc = true;
} else
{ /* Truncate right if not too close to loc. */ while (scridx[loc] + MIN_RIGHT_CUT < scridx[iend])
{
iend--;
end_trunc = true;
}
/* Truncate left if still too long. */ while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
{
ibeg++;
beg_trunc = true;
}
}
}
/* truncate working copy at desired endpoint */
wquery[qidx[iend]] = '\0';
/* Begin building the finished message. */
i = msg->len;
appendPQExpBuffer(msg, libpq_gettext("LINE %d: "), loc_line); if (beg_trunc)
appendPQExpBufferStr(msg, "...");
/* *Whilewehavetheprefixinthemsgbuffer,computeitsscreen *width.
*/
scroffset = 0; for (; i < msg->len; i += PQmblenBounded(&msg->data[i], encoding))
{ int w = pg_encoding_dsplen(encoding, &msg->data[i]);
if (w <= 0)
w = 1;
scroffset += w;
}
/* Finish up the LINE message line. */
appendPQExpBufferStr(msg, &wquery[qidx[ibeg]]); if (end_trunc)
appendPQExpBufferStr(msg, "...");
appendPQExpBufferChar(msg, '\n');
/* Now emit the cursor marker line. */
scroffset += scridx[loc] - scridx[ibeg]; for (i = 0; i < scroffset; i++)
appendPQExpBufferChar(msg, ' ');
appendPQExpBufferChar(msg, '^');
appendPQExpBufferChar(msg, '\n');
}
/* *AttempttoreadaNegotiateProtocolVersionmessage.Setsconn->pversion *totheversionthat'snegotiatedbytheserver. * *Entry:'v'messagetypeandlengthhavealreadybeenconsumed. *Exit:returns0ifsuccessfullyconsumedmessage. *returns1onfailure.Theerrormessageisfilledin.
*/ int
pqGetNegotiateProtocolVersion3(PGconn *conn)
{ int their_version; int num;
if (pqGetInt(&their_version, 4, conn) != 0) goto eof;
if (pqGetInt(&num, 4, conn) != 0) goto eof;
/* Check the protocol version */ if (their_version > conn->pversion)
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server requested downgrade to a higher-numbered version"); goto failure;
}
if (their_version < PG_PROTOCOL(3, 0))
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server requested downgrade to pre-3.0 protocol version"); goto failure;
}
/* 3.1 never existed, we went straight from 3.0 to 3.2 */ if (their_version == PG_PROTOCOL(3, 1))
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server requested downgrade to non-existent 3.1 protocol version"); goto failure;
}
if (num < 0)
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server reported negative number of unsupported parameters"); goto failure;
}
if (their_version == conn->pversion && num == 0)
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server negotiated but asks for no changes"); goto failure;
}
if (their_version < conn->min_pversion)
{
libpq_append_conn_error(conn, "server only supports protocol version %d.%d, but \"%s\" was set to %d.%d",
PG_PROTOCOL_MAJOR(their_version),
PG_PROTOCOL_MINOR(their_version), "min_protocol_version",
PG_PROTOCOL_MAJOR(conn->min_pversion),
PG_PROTOCOL_MINOR(conn->min_pversion));
goto failure;
}
/* the version is acceptable */
conn->pversion = their_version;
/* *Wedon'tcurrentlyrequestanyprotocolextensions,sowedon'texpect *theservertoreplywithanyeither.
*/ for (int i = 0; i < num; i++)
{ if (pqGets(&conn->workBuffer, conn))
{ goto eof;
} if (strncmp(conn->workBuffer.data, "_pq_.", 5) != 0)
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server reported unsupported parameter name without a \"%s\" prefix (\"%s\")", "_pq_.", conn->workBuffer.data); goto failure;
}
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server reported an unsupported parameter that was not requested (\"%s\")", conn->workBuffer.data); goto failure;
}
/* 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 */ if (!pqSaveParameterStatus(conn, conn->workBuffer.data, valueBuf.data))
{
libpq_append_conn_error(conn, "out of memory");
handleFatalError(conn);
}
termPQExpBuffer(&valueBuf); return0;
}
/* *parseInputsubroutinetoreadaBackendKeyDatamessage. *Entry:'v'messagetypeandlengthhavealreadybeenconsumed. *Exit:returns0ifsuccessfullyconsumedmessage. *returnsEOFifnotenoughdata.
*/ staticint
getBackendKeyData(PGconn *conn, int msgLength)
{ int cancel_key_len;
if (cancel_key_len != 4 && conn->pversion == PG_PROTOCOL(3, 0))
{
libpq_append_conn_error(conn, "received invalid BackendKeyData message: cancel key with length %d not allowed in protocol version 3.0 (must be 4 bytes)", cancel_key_len);
handleFatalError(conn); return0;
}
if (cancel_key_len < 4)
{
libpq_append_conn_error(conn, "received invalid BackendKeyData message: cancel key with length %d is too short (minimum 4 bytes)", cancel_key_len);
handleFatalError(conn); return0;
}
if (cancel_key_len > 256)
{
libpq_append_conn_error(conn, "received invalid BackendKeyData message: cancel key with length %d is too long (maximum 256 bytes)", cancel_key_len);
handleFatalError(conn); return0;
}
/* *AttempttoreadaNotifyresponsemessage. *Thisispossibleinseveralplaces,sowebreakitoutasasubroutine. * *Entry:'A'messagetypeandlengthhavealreadybeenconsumed. *Exit:returns0ifsuccessfullyconsumedNotifymessage. *returnsEOFifnotenoughdata.
*/ staticint
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)
{ /* *Notifymessagescanarriveatanystate,sowecannotassociatethe *errorwithanyparticularquery.There'snowaytoreturnbackan *"asyncerror",sothebestwecandoisdroptheconnection.That *seemsbetterthansilentlyignoringthenotification.
*/
libpq_append_conn_error(conn, "out of memory");
handleFatalError(conn); return0;
} if (pqGets(&conn->workBuffer, conn))
{
free(svname); return EOF;
}
/* *getCopyStart-processCopyInResponse,CopyOutResponseor *CopyBothResponsemessage * *parseInputalreadyreadthemessagetypeandlength.
*/ staticint
getCopyStart(PGconn *conn, ExecStatusType copytype)
{
PGresult *result; int nfields; int i;
result = PQmakeEmptyPGresult(conn, copytype); if (!result) goto failure;
if (pqGetc(&conn->copy_is_binary, conn)) goto failure;
result->binary = conn->copy_is_binary; /* 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));
}
/* *Ifit'salegitimateasyncmessagetype,processit.(NOTIFY *messagesarenotcurrentlypossiblehere,butwehandlethemfor *completeness.)Otherwise,ifit'sanythingexceptCopyData, *reportend-of-copy.
*/ switch (id)
{ case PqMsg_NotificationResponse: if (getNotify(conn)) return0; break; case PqMsg_NoticeResponse: if (pqGetErrorNotice3(conn, false)) return0; break; case PqMsg_ParameterStatus: if (getParameterStatus(conn)) return0; break; case PqMsg_CopyData: return msgLength; case PqMsg_CopyDone:
/* *IfthisisaCopyDonemessage,exitCOPY_OUTmodeandlet *callerreadstatuswithPQgetResult().Ifwe'rein *COPY_BOTHmode,returntoCOPY_INmode.
*/ if (conn->asyncStatus == PGASYNC_COPY_BOTH)
conn->asyncStatus = PGASYNC_COPY_IN; else
conn->asyncStatus = PGASYNC_BUSY; return -1; default: /* treat as end of copy */
/* Mark message consumed */
pqParseDone(conn, conn->inCursor + msgLength);
return msgLength;
}
/* Empty, so drop it and loop around for another */
pqParseDone(conn, conn->inCursor);
}
}
/* *PQgetline-getsanewline-terminatedstringfromthebackend. * *Seefe-exec.cfordocumentation.
*/ int
pqGetline3(PGconn *conn, char *s, int maxlen)
{ int status;
if (conn->sock == PGINVALID_SOCKET ||
(conn->asyncStatus != PGASYNC_COPY_OUT &&
conn->asyncStatus != PGASYNC_COPY_BOTH) ||
conn->copy_is_binary)
{
libpq_append_conn_error(conn, "PQgetline: not doing text COPY OUT");
*s = '\0'; return EOF;
}
while ((status = PQgetlineAsync(conn, s, maxlen - 1)) == 0)
{ /* need to load more data */ if (pqWait(true, false, conn) ||
pqReadData(conn) < 0)
{
*s = '\0'; return EOF;
}
}
if (status < 0)
{ /* End of copy detected; gin up old-style terminator */
strcpy(s, "\\."); return0;
}
/* *PQgetlineAsync-getsaCOPYdatarowwithoutblocking. * *Seefe-exec.cfordocumentation.
*/ int
pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize)
{ int msgLength; int avail;
if (conn->asyncStatus != PGASYNC_COPY_OUT
&& conn->asyncStatus != PGASYNC_COPY_BOTH) return -1; /* we are not doing a copy... */
/* *Recognizethenextinputmessage.Tomakelifesimplerforasync *callers,wekeepreturning0untilthenextmessageisfullyavailable *evenifitisnotCopyData.ThisshouldkeepPQendcopyfromblocking. *(Note:unlikepqGetCopyData3,wedonotchangeasyncStatushere.)
*/
msgLength = getCopyDataMessage(conn); if (msgLength < 0) return -1; /* end-of-copy or error */ if (msgLength == 0) return0; /* no data yet */
/* *Movedatafromlibpq'sbuffertothecaller's.Inthecasewherea *priorcallfoundthecaller'sbuffertoosmall,weuse *conn->copy_already_donetorememberhowmuchoftherowwasalready *returnedtothecaller.
*/
conn->inCursor += conn->copy_already_done;
avail = msgLength - 4 - conn->copy_already_done; if (avail <= bufsize)
{ /* Able to consume the whole message */
memcpy(buffer, &conn->inBuffer[conn->inCursor], avail); /* Mark message consumed */
conn->inStart = conn->inCursor + avail; /* Reset state for next time */
conn->copy_already_done = 0; return avail;
} else
{ /* We must return a partial message */
memcpy(buffer, &conn->inBuffer[conn->inCursor], bufsize); /* The message is NOT consumed from libpq's buffer */
conn->copy_already_done += bufsize; return bufsize;
}
}
/* *PQfn-SendafunctioncalltothePOSTGRESbackend. * *Seefe-exec.cfordocumentation.
*/
PGresult *
pqFunctionCall3(PGconn *conn, Oid fnid, int *result_buf, int buf_size, int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs)
{ bool needInput = false;
ExecStatusType status = PGRES_FATAL_ERROR; char id; int msgLength; int avail; int i;
/* already validated by PQfn */
Assert(conn->pipelineStatus == PQ_PIPELINE_OFF);
/* PQfn already validated connection state */
if (pqPutMsgStart(PqMsg_FunctionCall, conn) < 0 ||
pqPutInt(fnid, 4, conn) < 0 || /* function id */
pqPutInt(1, 2, conn) < 0 || /* # of format codes */
pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */
pqPutInt(nargs, 2, conn) < 0) /* # of args */
{ /* error message should be set up already */ return NULL;
}
for (i = 0; i < nargs; ++i)
{ /* len.int4 + contents */ if (pqPutInt(args[i].len, 4, conn)) return NULL; if (args[i].len == -1) continue; /* it's NULL */
if (args[i].isint)
{ if (pqPutInt(args[i].u.integer, args[i].len, conn)) return NULL;
} else
{ if (pqPutnchar(args[i].u.ptr, args[i].len, conn)) return NULL;
}
}
if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */ return NULL;
if (pqPutMsgEnd(conn) < 0 ||
pqFlush(conn)) return NULL;
for (;;)
{ if (needInput)
{ /* Wait for some data to arrive (or for the channel to close) */ if (pqWait(true, false, conn) ||
pqReadData(conn) < 0) break;
}
/* *WeshouldseeVorEresponsetothecommand,butmightgetN *and/orAnoticesfirst.WealsoneedtoswallowthefinalZbefore *returning.
*/ switch (id)
{ case'V': /* function result */ if (pqGetInt(actual_result_len, 4, conn)) continue; if (*actual_result_len != -1)
{ if (result_is_int)
{ if (pqGetInt(result_buf, *actual_result_len, conn)) continue;
} else
{ /* *Iftheserverreturnedtoomuchdataforthe *buffer,somethingfishyisgoingon.Abandonship.
*/ if (buf_size != -1 && *actual_result_len > buf_size)
{
libpq_append_conn_error(conn, "server returned too much data");
handleFatalError(conn); return pqPrepareAsyncResult(conn);
}
if (pqGetnchar(result_buf,
*actual_result_len,
conn)) continue;
}
} /* correctly finished function result message */
status = PGRES_COMMAND_OK; break; case'E': /* error return */ if (pqGetErrorNotice3(conn, true)) continue;
status = PGRES_FATAL_ERROR; break; case'A': /* notify message */ /* handle notify and go back to processing return values */ if (getNotify(conn)) continue; break; case'N': /* notice */ /* handle notice and go back to processing return values */ if (pqGetErrorNotice3(conn, false)) continue; break; case'Z': /* backend is ready for new query */ if (getReadyForQuery(conn)) continue;
#define ADD_STARTUP_OPTION(optname, optval) \ do { \ if (packet) \
strcpy(packet + packet_len, optname); \ if (pg_add_size_overflow(packet_len, strlen(optname) + 1, &packet_len)) \ return0; \ if (packet) \
strcpy(packet + packet_len, optval); \ if (pg_add_size_overflow(packet_len, strlen(optval) + 1, &packet_len)) \ return0; \
} while(0)
if (conn->pguser && conn->pguser[0])
ADD_STARTUP_OPTION("user", conn->pguser); if (conn->dbName && conn->dbName[0])
ADD_STARTUP_OPTION("database", conn->dbName); if (conn->replication && conn->replication[0])
ADD_STARTUP_OPTION("replication", conn->replication); if (conn->pgoptions && conn->pgoptions[0])
ADD_STARTUP_OPTION("options", conn->pgoptions); if (conn->send_appname)
{ /* Use appname if present, otherwise use fallback */
val = conn->appname ? conn->appname : conn->fbappname; if (val && val[0])
ADD_STARTUP_OPTION("application_name", val);
}
if (conn->client_encoding_initial && conn->client_encoding_initial[0])
ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial);
/* Add any environment-driven GUC settings needed */ for (next_eo = options; next_eo->envName; next_eo++)
{ if ((val = getenv(next_eo->envName)) != NULL)
{ if (pg_strcasecmp(val, "default") != 0)
ADD_STARTUP_OPTION(next_eo->pgName, val);
}
}
/* Add trailing terminator */ if (packet)
packet[packet_len] = '\0'; if (pg_add_size_overflow(packet_len, 1, &packet_len)) return0;
return packet_len;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.63 Sekunden
(vorverarbeitet am 2026-08-06)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.