summaryrefslogtreecommitdiff
path: root/libpq/fe-protocol2.c
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-11-30 22:37:25 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-12-06 15:11:04 +0300
commitf1f39911e0d2d88c98eae96a3eb14a53c664206f (patch)
tree4cf4e3a84d895f59323d3b6ab4bfab38b3cab489 /libpq/fe-protocol2.c
parentfc9499b8a7b7a3e350bfabf2cd6ae0bc13f04bea (diff)
Upgrade to 12.1
Diffstat (limited to 'libpq/fe-protocol2.c')
-rw-r--r--libpq/fe-protocol2.c1623
1 files changed, 0 insertions, 1623 deletions
diff --git a/libpq/fe-protocol2.c b/libpq/fe-protocol2.c
deleted file mode 100644
index f1b90f3..0000000
--- a/libpq/fe-protocol2.c
+++ /dev/null
@@ -1,1623 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-protocol2.c
- * functions that are specific to frontend/backend protocol version 2
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-protocol2.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres_fe.h"
-
-#include <ctype.h>
-#include <fcntl.h>
-
-#include "libpq-fe.h"
-#include "libpq-int.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>
-#endif
-
-
-static 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.
- */
-PostgresPollingStatusType
-pqSetenvPoll(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_CLIENT_ENCODING_WAIT:
- 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_CLIENT_ENCODING_SEND:
- 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)
- {
- /*
- * The _CLIENT_ENCODING_SEND code is slightly different from
- * _OPTION_SEND below (e.g., no getenv() call), which is why a
- * different state is used.
- */
- case SETENV_STATE_CLIENT_ENCODING_SEND:
- {
- char setQuery[100]; /* note length limit in
- * sprintf below */
- const char *val = conn->client_encoding_initial;
-
- if (val)
- {
- if (pg_strcasecmp(val, "default") == 0)
- sprintf(setQuery, "SET client_encoding = DEFAULT");
- else
- sprintf(setQuery, "SET client_encoding = '%.60s'",
- val);
-#ifdef CONNECTDEBUG
- fprintf(stderr,
- "Sending client_encoding with %s\n",
- setQuery);
-#endif
- if (!PQsendQuery(conn, setQuery))
- goto error_return;
-
- conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_WAIT;
- }
- else
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
- break;
- }
-
- 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_CLIENT_ENCODING_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->setenv_state = SETENV_STATE_OPTION_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.
- */
-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;
- }
-}
-
-/*
- * 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 subroutine to read a 'B' or 'D' (row data) message.
- * We fill rowbuf with column pointers and then call the row processor.
- * 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
-getAnotherTuple(PGconn *conn, bool binary)
-{
- PGresult *result = conn->result;
- int nfields = result->numAttributes;
- const char *errmsg;
- PGdataValue *rowbuf;
-
- /* 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 */
-
- /* 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;
- }
-
- /* Save format specifier */
- result->binary = binary;
-
- /*
- * 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;
- }
-
- /* 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)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
- }
-
- 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++)
- {
- /* get the value length */
- if (!(bmap & 0200))
- vlen = NULL_LEN;
- else if (pqGetInt(&vlen, 4, conn))
- goto EOFexit;
- else
- {
- if (!binary)
- vlen = vlen - 4;
- if (vlen < 0)
- vlen = 0;
- }
- rowbuf[i].len = vlen;
-
- /*
- * rowbuf[i].value always points to the next address in the data
- * buffer even if the value is NULL. This allows row processors to
- * estimate data sizes more easily.
- */
- rowbuf[i].value = conn->inBuffer + conn->inCursor;
-
- /* Skip over the data value */
- if (vlen > 0)
- {
- if (pqSkipnchar(vlen, conn))
- goto EOFexit;
- }
-
- /* advance the bitmap stuff */
- bitcnt++;
- if (bitcnt == BITS_PER_BYTE)
- {
- bitmap_index++;
- bmap = bitmap[bitmap_index];
- bitcnt = 0;
- }
- else
- bmap <<= 1;
- }
-
- /* Release bitmap now if we allocated it */
- if (bitmap != std_bitmap)
- free(bitmap);
- bitmap = NULL;
-
- /* Advance inStart to show that the "D" message has been processed. */
- conn->inStart = conn->inCursor;
-
- /* Process the collected row */
- errmsg = NULL;
- if (pqRowProcessor(conn, &errmsg))
- return 0; /* normal, successful exit */
-
- goto set_error_result; /* pqRowProcessor failed, report it */
-
-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;
-
-set_error_result:
-
- /*
- * 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 (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 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;
-}
-
-/*
- * 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 void
-checkXactStatus(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 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;
-}
-
-
-/*
- * PQgetCopyData - read a row of data from the backend during COPY OUT
- *
- * If successful, sets *buffer to point to a malloc'd row of data, and
- * returns row length (always > 0) as result.
- * Returns 0 if no row available yet (only possible if async is true),
- * -1 if end of copy (consult PQgetResult), or -2 if error (consult
- * PQerrorMessage).
- */
-int
-pqGetCopyData2(PGconn *conn, char **buffer, int async)
-{
- bool found;
- int msgLength;
-
- for (;;)
- {
- /*
- * Do we have a complete line of data?
- */
- conn->inCursor = conn->inStart;
- found = false;
- while (conn->inCursor < conn->inEnd)
- {
- char c = conn->inBuffer[conn->inCursor++];
-
- if (c == '\n')
- {
- found = true;
- break;
- }
- }
- if (!found)
- goto nodata;
- msgLength = conn->inCursor - conn->inStart;
-
- /*
- * If it's the end-of-data marker, consume it, exit COPY_OUT mode, and
- * let caller read status with PQgetResult().
- */
- if (msgLength == 3 &&
- strncmp(&conn->inBuffer[conn->inStart], "\\.\n", 3) == 0)
- {
- conn->inStart = conn->inCursor;
- conn->asyncStatus = PGASYNC_BUSY;
- return -1;
- }
-
- /*
- * Pass the line back to the caller.
- */
- *buffer = (char *) malloc(msgLength + 1);
- if (*buffer == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return -2;
- }
- memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength);
- (*buffer)[msgLength] = '\0'; /* Add terminating null */
-
- /* Mark message consumed */
- conn->inStart = conn->inCursor;
-
- return msgLength;
-
-nodata:
- /* Don't block if async read requested */
- if (async)
- return 0;
- /* Need to load more data */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- return -2;
- }
-}
-
-
-/*
- * PQgetline - gets a newline-terminated string from the backend.
- *
- * See fe-exec.c for documentation.
- */
-int
-pqGetline2(PGconn *conn, char *s, int maxlen)
-{
- int result = 1; /* return value if buffer overflows */
-
- if (conn->sock == PGINVALID_SOCKET ||
- conn->asyncStatus != PGASYNC_COPY_OUT)
- {
- *s = '\0';
- return EOF;
- }
-
- /*
- * Since this is a purely synchronous routine, we don't bother to maintain
- * conn->inCursor; there is no need to back up.
- */
- while (maxlen > 1)
- {
- if (conn->inStart < conn->inEnd)
- {
- char c = conn->inBuffer[conn->inStart++];
-
- if (c == '\n')
- {
- result = 0; /* success exit */
- break;
- }
- *s++ = c;
- maxlen--;
- }
- else
- {
- /* need to load more data */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- {
- result = EOF;
- break;
- }
- }
- }
- *s = '\0';
-
- return result;
-}
-
-/*
- * PQgetlineAsync - gets a COPY data row without blocking.
- *
- * See fe-exec.c for documentation.
- */
-int
-pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize)
-{
- int avail;
-
- if (conn->asyncStatus != PGASYNC_COPY_OUT)
- return -1; /* we are not doing a copy... */
-
- /*
- * Move data from libpq's buffer to the caller's. We want to accept data
- * only in units of whole lines, not partial lines. This ensures that we
- * can recognize the terminator line "\\.\n". (Otherwise, if it happened
- * to cross a packet/buffer boundary, we might hand the first one or two
- * characters off to the caller, which we shouldn't.)
- */
-
- conn->inCursor = conn->inStart;
-
- avail = bufsize;
- while (avail > 0 && conn->inCursor < conn->inEnd)
- {
- char c = conn->inBuffer[conn->inCursor++];
-
- *buffer++ = c;
- --avail;
- if (c == '\n')
- {
- /* Got a complete line; mark the data removed from libpq */
- conn->inStart = conn->inCursor;
- /* Is it the endmarker line? */
- if (bufsize - avail == 3 && buffer[-3] == '\\' && buffer[-2] == '.')
- return -1;
- /* No, return the data line to the caller */
- return bufsize - avail;
- }
- }
-
- /*
- * We don't have a complete line. We'd prefer to leave it in libpq's
- * buffer until the rest arrives, but there is a special case: what if the
- * line is longer than the buffer the caller is offering us? In that case
- * we'd better hand over a partial line, else we'd get into an infinite
- * loop. Do this in a way that ensures we can't misrecognize a terminator
- * line later: leave last 3 characters in libpq buffer.
- */
- if (avail == 0 && bufsize > 3)
- {
- conn->inStart = conn->inCursor - 3;
- return bufsize - 3;
- }
- return 0;
-}
-
-/*
- * PQendcopy
- *
- * See fe-exec.c for documentation.
- */
-int
-pqEndcopy2(PGconn *conn)
-{
- PGresult *result;
-
- if (conn->asyncStatus != PGASYNC_COPY_IN &&
- conn->asyncStatus != PGASYNC_COPY_OUT)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("no COPY in progress\n"));
- return 1;
- }
-
- /*
- * make sure no data is waiting to be sent, abort if we are non-blocking
- * and the flush fails
- */
- if (pqFlush(conn) && pqIsnonblocking(conn))
- return 1;
-
- /* non blocking connections may have to abort at this point. */
- if (pqIsnonblocking(conn) && PQisBusy(conn))
- return 1;
-
- /* Return to active duty */
- conn->asyncStatus = PGASYNC_BUSY;
- resetPQExpBuffer(&conn->errorMessage);
-
- /* Wait for the completion response */
- result = PQgetResult(conn);
-
- /* Expecting a successful result */
- if (result && result->resultStatus == PGRES_COMMAND_OK)
- {
- PQclear(result);
- return 0;
- }
-
- /*
- * Trouble. For backwards-compatibility reasons, we issue the error
- * message as if it were a notice (would be nice to get rid of this
- * silliness, but too many apps probably don't handle errors from
- * PQendcopy reasonably). Note that the app can still obtain the error
- * status from the PGconn object.
- */
- if (conn->errorMessage.len > 0)
- {
- /* We have to strip the trailing newline ... pain in neck... */
- char svLast = conn->errorMessage.data[conn->errorMessage.len - 1];
-
- if (svLast == '\n')
- conn->errorMessage.data[conn->errorMessage.len - 1] = '\0';
- pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data);
- conn->errorMessage.data[conn->errorMessage.len - 1] = svLast;
- }
-
- PQclear(result);
-
- /*
- * The worst case is that we've lost sync with the backend entirely due to
- * application screwup of the copy in/out protocol. To recover, reset the
- * connection (talk about using a sledgehammer...)
- */
- pqInternalNotice(&conn->noticeHooks,
- "lost synchronization with server, resetting connection");
-
- /*
- * Users doing non-blocking connections need to handle the reset
- * themselves, they'll need to check the connection status if we return an
- * error.
- */
- if (pqIsnonblocking(conn))
- PQresetStart(conn);
- else
- PQreset(conn);
-
- return 1;
-}
-
-
-/*
- * 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);
-}
-
-
-/*
- * Construct startup packet
- *
- * Returns a malloc'd packet buffer, or NULL if out of memory
- */
-char *
-pqBuildStartupPacket2(PGconn *conn, int *packetlen,
- const PQEnvironmentOption *options)
-{
- StartupPacket *startpacket;
-
- *packetlen = sizeof(StartupPacket);
- startpacket = (StartupPacket *) malloc(sizeof(StartupPacket));
- if (!startpacket)
- return NULL;
-
- MemSet(startpacket, 0, sizeof(StartupPacket));
-
- startpacket->protoVersion = htonl(conn->pversion);
-
- /* strncpy is safe here: postmaster will handle full fields correctly */
- strncpy(startpacket->user, conn->pguser, SM_USER);
- strncpy(startpacket->database, conn->dbName, SM_DATABASE);
- strncpy(startpacket->tty, conn->pgtty, SM_TTY);
-
- if (conn->pgoptions)
- strncpy(startpacket->options, conn->pgoptions, SM_OPTIONS);
-
- return (char *) startpacket;
-}