📄 fe-protocol2.c
字号:
/*------------------------------------------------------------------------- * * fe-protocol2.c * functions that are specific to frontend/backend protocol version 2 * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.19.2.1 2005/11/22 18:23:30 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres_fe.h"#include <errno.h>#include <ctype.h>#include <fcntl.h>#include "libpq-fe.h"#include "libpq-int.h"#include "mb/pg_wchar.h"#ifdef WIN32#include "win32.h"#else#include <unistd.h>#include <netinet/in.h>#ifdef HAVE_NETINET_TCP_H#include <netinet/tcp.h>#endif#include <arpa/inet.h>#endifstatic int getRowDescriptions(PGconn *conn);static int getAnotherTuple(PGconn *conn, bool binary);static int pqGetErrorNotice2(PGconn *conn, bool isError);static void checkXactStatus(PGconn *conn, const char *cmdTag);static int getNotify(PGconn *conn);/* * pqSetenvPoll * * Polls the process of passing the values of a standard set of environment * variables to the backend. */PostgresPollingStatusTypepqSetenvPoll(PGconn *conn){ PGresult *res; if (conn == NULL || conn->status == CONNECTION_BAD) return PGRES_POLLING_FAILED; /* Check whether there are any data for us */ switch (conn->setenv_state) { /* These are reading states */ case SETENV_STATE_OPTION_WAIT: case SETENV_STATE_QUERY1_WAIT: case SETENV_STATE_QUERY2_WAIT: { /* Load waiting data */ int n = pqReadData(conn); if (n < 0) goto error_return; if (n == 0) return PGRES_POLLING_READING; break; } /* These are writing states, so we just proceed. */ case SETENV_STATE_OPTION_SEND: case SETENV_STATE_QUERY1_SEND: case SETENV_STATE_QUERY2_SEND: break; /* Should we raise an error if called when not active? */ case SETENV_STATE_IDLE: return PGRES_POLLING_OK; default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( "invalid setenv state %c, " "probably indicative of memory corruption\n" ), conn->setenv_state); goto error_return; } /* We will loop here until there is nothing left to do in this call. */ for (;;) { switch (conn->setenv_state) { case SETENV_STATE_OPTION_SEND: { /* * Send SET commands for stuff directed by Environment * Options. Note: we assume that SET commands won't start * transaction blocks, even in a 7.3 server with * autocommit off. */ char setQuery[100]; /* note length limit in * sprintf below */ if (conn->next_eo->envName) { const char *val; if ((val = getenv(conn->next_eo->envName))) { if (pg_strcasecmp(val, "default") == 0) sprintf(setQuery, "SET %s = DEFAULT", conn->next_eo->pgName); else sprintf(setQuery, "SET %s = '%.60s'", conn->next_eo->pgName, val);#ifdef CONNECTDEBUG fprintf(stderr, "Use environment variable %s to send %s\n", conn->next_eo->envName, setQuery);#endif if (!PQsendQuery(conn, setQuery)) goto error_return; conn->setenv_state = SETENV_STATE_OPTION_WAIT; } else conn->next_eo++; } else { /* No more options to send, so move on to querying */ conn->setenv_state = SETENV_STATE_QUERY1_SEND; } break; } case SETENV_STATE_OPTION_WAIT: { if (PQisBusy(conn)) return PGRES_POLLING_READING; res = PQgetResult(conn); if (res) { if (PQresultStatus(res) != PGRES_COMMAND_OK) { PQclear(res); goto error_return; } PQclear(res); /* Keep reading until PQgetResult returns NULL */ } else { /* Query finished, so send the next option */ conn->next_eo++; conn->setenv_state = SETENV_STATE_OPTION_SEND; } break; } case SETENV_STATE_QUERY1_SEND: { /* * Issue query to get information we need. Here we must * use begin/commit in case autocommit is off by default * in a 7.3 server. * * Note: version() exists in all protocol-2.0-supporting * backends. In 7.3 it would be safer to write * pg_catalog.version(), but we can't do that without * causing problems on older versions. */ if (!PQsendQuery(conn, "begin; select version(); end")) goto error_return; conn->setenv_state = SETENV_STATE_QUERY1_WAIT; return PGRES_POLLING_READING; } case SETENV_STATE_QUERY1_WAIT: { if (PQisBusy(conn)) return PGRES_POLLING_READING; res = PQgetResult(conn); if (res) { char *val; if (PQresultStatus(res) == PGRES_COMMAND_OK) { /* ignore begin/commit command results */ PQclear(res); continue; } if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) { PQclear(res); goto error_return; } /* * Extract server version and save as if * ParameterStatus */ val = PQgetvalue(res, 0, 0); if (val && strncmp(val, "PostgreSQL ", 11) == 0) { char *ptr; /* strip off PostgreSQL part */ val += 11; /* * strip off platform part (scribbles on result, * naughty naughty) */ ptr = strchr(val, ' '); if (ptr) *ptr = '\0'; pqSaveParameterStatus(conn, "server_version", val); } PQclear(res); /* Keep reading until PQgetResult returns NULL */ } else { /* Query finished, move to next */ conn->setenv_state = SETENV_STATE_QUERY2_SEND; } break; } case SETENV_STATE_QUERY2_SEND: { const char *query; /* * pg_client_encoding does not exist in pre-7.2 servers. * So we need to be prepared for an error here. Do *not* * start a transaction block, except in 7.3 servers where * we need to prevent autocommit-off from starting a * transaction anyway. */ if (conn->sversion >= 70300 && conn->sversion < 70400) query = "begin; select pg_catalog.pg_client_encoding(); end"; else query = "select pg_client_encoding()"; if (!PQsendQuery(conn, query)) goto error_return; conn->setenv_state = SETENV_STATE_QUERY2_WAIT; return PGRES_POLLING_READING; } case SETENV_STATE_QUERY2_WAIT: { if (PQisBusy(conn)) return PGRES_POLLING_READING; res = PQgetResult(conn); if (res) { const char *val; if (PQresultStatus(res) == PGRES_COMMAND_OK) { /* ignore begin/commit command results */ PQclear(res); continue; } if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) { /* Extract client encoding and save it */ val = PQgetvalue(res, 0, 0); if (val && *val) /* null should not happen, but */ pqSaveParameterStatus(conn, "client_encoding", val); } else { /* * Error: presumably function not available, so * use PGCLIENTENCODING or SQL_ASCII as the * fallback. */ val = getenv("PGCLIENTENCODING"); if (val && *val) pqSaveParameterStatus(conn, "client_encoding", val); else pqSaveParameterStatus(conn, "client_encoding", "SQL_ASCII"); } PQclear(res); /* Keep reading until PQgetResult returns NULL */ } else { /* Query finished, so we're done */ conn->setenv_state = SETENV_STATE_IDLE; return PGRES_POLLING_OK; } break; } default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid state %c, " "probably indicative of memory corruption\n"), conn->setenv_state); goto error_return; } } /* Unreachable */error_return: conn->setenv_state = SETENV_STATE_IDLE; return PGRES_POLLING_FAILED;}/* * 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. */voidpqParseInput2(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) return; } strncpy(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); 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -