/* * PQfn - Send a function call to the POSTGRES backend. * * See fe-exec.c for documentation. */ PGresult * pqFunctionCall2(PGconn *conn, Oid fnid, int *result_buf, int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs) { bool needInput = false; ExecStatusType status = PGRES_FATAL_ERROR; char id; int i; /* PQfn already validated connection state */ if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ pqPuts(" ", conn) < 0 || /* dummy string */ pqPutInt(fnid, 4, conn) != 0 || /* function id */ pqPutInt(nargs, 4, conn) != 0) /* # of args */ { pqHandleSendFailure(conn); return NULL; } for (i = 0; i < nargs; ++i) { /* len.int4 + contents */ if (pqPutInt(args[i].len, 4, conn)) { pqHandleSendFailure(conn); return NULL; } if (args[i].isint) { if (pqPutInt(args[i].u.integer, 4, conn)) { pqHandleSendFailure(conn); return NULL; } } else { if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) { pqHandleSendFailure(conn); return NULL; } } } if (pqPutMsgEnd(conn) < 0 || pqFlush(conn)) { pqHandleSendFailure(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; } /* * Scan the message. If we run out of data, loop around to try again. */ conn->inCursor = conn->inStart; needInput = true; if (pqGetc(&id, conn)) continue; /* * We should see V or E response to the command, but might get N * and/or A notices first. We also need to swallow the final Z before * returning. */ switch (id) { case 'V': /* function result */ if (pqGetc(&id, conn)) continue; if (id == 'G') { /* function returned nonempty value */ if (pqGetInt(actual_result_len, 4, conn)) continue; if (result_is_int) { if (pqGetInt(result_buf, 4, conn)) continue; } else { if (pqGetnchar((char *) result_buf, *actual_result_len, conn)) continue; } if (pqGetc(&id, conn)) /* get the last '0' */ continue; } if (id == '0') { /* correctly finished function result message */ status = PGRES_COMMAND_OK; } else { /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); conn->inStart = conn->inCursor; return pqPrepareAsyncResult(conn); } break; case 'E': /* error return */ if (pqGetErrorNotice2(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 (pqGetErrorNotice2(conn, false)) continue; break; case 'Z': /* backend is ready for new query */ /* consume the message and exit */ conn->inStart = conn->inCursor; /* if we saved a result object (probably an error), use it */ if (conn->result) return pqPrepareAsyncResult(conn); return PQmakeEmptyPGresult(conn, status); default: /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); conn->inStart = conn->inCursor; return pqPrepareAsyncResult(conn); } /* Completed this message, keep going */ conn->inStart = conn->inCursor; needInput = false; } /* * We fall out of the loop only upon failing to read data. * conn->errorMessage has been set by pqWait or pqReadData. We want to * append it to any already-received error message. */ pqSaveErrorResult(conn); return pqPrepareAsyncResult(conn); }
/* * * * Control Message has msg_type='C'. * Control Message is consumed by Receiver thread on mirror side. * * Data Message has msg_type='M'. * Data Message is inserted in Shared memory and consumed by Consumer * thread on mirror side. */ bool FileRepConnClient_SendMessage( FileRepConsumerProcIndex_e messageType, bool messageSynchronous, char *message, uint32 messageLength) { char msgType = 0; int status = STATUS_OK; #ifdef USE_ASSERT_CHECKING int prevOutCount = filerep_conn->outCount; #endif /* // USE_ASSERT_CHECKING */ switch (messageType) { case FileRepMessageTypeXLog: msgType = '1'; break; case FileRepMessageTypeAO01: msgType = '2'; break; case FileRepMessageTypeWriter: msgType = '3'; break; case FileRepMessageTypeShutdown: msgType = 'S'; break; default: return false; } /** * Note that pqPutMsgStart and pqPutnchar both may grow the connection's internal buffer, and do not * flush data */ if (pqPutMsgStart(msgType, true, filerep_conn) < 0) { return false; } if (pqPutnchar(message, messageLength, filerep_conn) < 0) { return false; } /* * Server side needs complete messages for mode-transitions so disable * auto-flush since it flushes partial messages */ pqPutMsgEndNoAutoFlush(filerep_conn); /* assert that a flush did not occur */ Assert(prevOutCount + messageLength + 5 == filerep_conn->outCount); /* the +5 is the amount * added by * pgPutMsgStart */ /* * note also that we could do a flush beforehand to avoid having * pqPutMsgStart and pqPutnchar growing the buffer */ if (messageSynchronous || filerep_conn->outCount >= file_rep_min_data_before_flush) { int result = 0; /* wait and timeout will be handled by pqWaitTimeout */ while ((status = pqFlushNonBlocking(filerep_conn)) > 0) { /* retry on timeout */ while (!(result = pqWaitTimeout(FALSE, TRUE, filerep_conn, time(NULL) + file_rep_socket_timeout))) { if (FileRepSubProcess_IsStateTransitionRequested()) { elog(WARNING, "segment state transition requested while waiting to write data to socket"); status = -1; break; } } if (result < 0) { ereport(WARNING, (errcode_for_socket_access(), errmsg("could not write data to socket, failure detected : %m"))); status = -1; break; } if (status == -1) { break; } } if (status < 0) { return false; } Assert(status == 0); return true; } return true; }
/* * PQfn - Send a function call to the POSTGRES backend. * * See fe-exec.c for documentation. */ PGresult * pqFunctionCall3(PGconn *conn, Oid fnid, int *result_buf, 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; /* PQfn already validated connection state */ if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ 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 */ { pqHandleSendFailure(conn); return NULL; } for (i = 0; i < nargs; ++i) { /* len.int4 + contents */ if (pqPutInt(args[i].len, 4, conn)) { pqHandleSendFailure(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)) { pqHandleSendFailure(conn); return NULL; } } else { if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) { pqHandleSendFailure(conn); return NULL; } } } if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */ { pqHandleSendFailure(conn); return NULL; } if (pqPutMsgEnd(conn) < 0 || pqFlush(conn)) { pqHandleSendFailure(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; } /* * Scan the message. If we run out of data, loop around to try again. */ needInput = true; conn->inCursor = conn->inStart; if (pqGetc(&id, conn)) continue; if (pqGetInt(&msgLength, 4, conn)) continue; /* * 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); break; } if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) { handleSyncLoss(conn, id, msgLength); break; } /* * Can't process if message body isn't all here yet. */ msgLength -= 4; avail = conn->inEnd - conn->inCursor; if (avail < msgLength) { /* * Before looping, enlarge the input buffer if needed to hold the * whole message. See notes in parseInput. */ 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); break; } continue; } /* * We should see V or E response to the command, but might get N * and/or A notices first. We also need to swallow the final Z before * 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 { if (pqGetnchar((char *) 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; /* consume the message and exit */ conn->inStart += 5 + msgLength; /* if we saved a result object (probably an error), use it */ if (conn->result) return pqPrepareAsyncResult(conn); return PQmakeEmptyPGresult(conn, status); case 'S': /* parameter status */ if (getParameterStatus(conn)) continue; break; default: /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); /* trust the specified message length as what to skip */ conn->inStart += 5 + msgLength; return pqPrepareAsyncResult(conn); } /* Completed this message, keep going */ /* trust the specified message length as what to skip */ conn->inStart += 5 + msgLength; needInput = false; } /* * We fall out of the loop only upon failing to read data. * conn->errorMessage has been set by pqWait or pqReadData. We want to * append it to any already-received error message. */ pqSaveErrorResult(conn); return pqPrepareAsyncResult(conn); }
/* * 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; }