Beispiel #1
0
/*
 * pqSendSome: send data waiting in the output buffer.
 *
 * len is how much to try to send (typically equal to outCount, but may
 * be less).
 *
 * Return 0 on success, -1 on failure and 1 when not all data could be sent
 * because the socket would block and the connection is non-blocking.
 */
static int
pqSendSome(PGconn *conn, int len)
{
	char	   *ptr = conn->outBuffer;
	int			remaining = conn->outCount;
	int			result = 0;

	if (conn->sock == PGINVALID_SOCKET)
	{
		printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("connection not open\n"));
		/* Discard queued data; no chance it'll ever be sent */
		conn->outCount = 0;
		return -1;
	}

	/* while there's still data to send */
	while (len > 0)
	{
		int			sent;

#ifndef WIN32
		sent = pqsecure_write(conn, ptr, len);
#else

		/*
		 * Windows can fail on large sends, per KB article Q201213. The
		 * failure-point appears to be different in different versions of
		 * Windows, but 64k should always be safe.
		 */
		sent = pqsecure_write(conn, ptr, Min(len, 65536));
#endif

		if (sent < 0)
		{
			/* Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble */
			switch (SOCK_ERRNO)
			{
#ifdef EAGAIN
				case EAGAIN:
					break;
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
				case EWOULDBLOCK:
					break;
#endif
				case EINTR:
					continue;

				default:
					/* pqsecure_write set the error message for us */

					/*
					 * We used to close the socket here, but that's a bad idea
					 * since there might be unread data waiting (typically, a
					 * NOTICE message from the backend telling us it's
					 * committing hara-kiri...).  Leave the socket open until
					 * pqReadData finds no more data can be read.  But abandon
					 * attempt to send data.
					 */
					conn->outCount = 0;
					return -1;
			}
		}
		else
		{
			ptr += sent;
			len -= sent;
			remaining -= sent;
		}

		if (len > 0)
		{
			/*
			 * We didn't send it all, wait till we can send more.
			 *
			 * There are scenarios in which we can't send data because the
			 * communications channel is full, but we cannot expect the server
			 * to clear the channel eventually because it's blocked trying to
			 * send data to us.  (This can happen when we are sending a large
			 * amount of COPY data, and the server has generated lots of
			 * NOTICE responses.)  To avoid a deadlock situation, we must be
			 * prepared to accept and buffer incoming data before we try
			 * again.  Furthermore, it is possible that such incoming data
			 * might not arrive until after we've gone to sleep.  Therefore,
			 * we wait for either read ready or write ready.
			 *
			 * In non-blocking mode, we don't wait here directly, but return 1
			 * to indicate that data is still pending.  The caller should wait
			 * for both read and write ready conditions, and call
			 * PQconsumeInput() on read ready, but just in case it doesn't, we
			 * call pqReadData() ourselves before returning.  That's not
			 * enough if the data has not arrived yet, but it's the best we
			 * can do, and works pretty well in practice.  (The documentation
			 * used to say that you only need to wait for write-ready, so
			 * there are still plenty of applications like that out there.)
			 */
			if (pqReadData(conn) < 0)
			{
				result = -1;	/* error message already set up */
				break;
			}

			if (pqIsnonblocking(conn))
			{
				result = 1;
				break;
			}

			if (pqWait(TRUE, TRUE, conn))
			{
				result = -1;
				break;
			}
		}
	}

	/* shift the remaining contents of the buffer */
	if (remaining > 0)
		memmove(conn->outBuffer, ptr, remaining);
	conn->outCount = remaining;

	return result;
}
Beispiel #2
0
/*
 * 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
pqParseInput3(PGconn *conn)
{
	char		id;
	int			msgLength;
	int			avail;

	/*
	 * Loop to parse successive complete messages available in the buffer.
	 */
	for (;;)
	{
		/*
		 * Try to read a message.  First get the type code and length. Return
		 * if not enough data.
		 */
		conn->inCursor = conn->inStart;
		if (pqGetc(&id, conn))
			return;
		if (pqGetInt(&msgLength, 4, conn))
			return;

		/*
		 * 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);
			return;
		}
		if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id))
		{
			handleSyncLoss(conn, id, msgLength);
			return;
		}

		/*
		 * Can't process if message body isn't all here yet.
		 */
		msgLength -= 4;
		avail = conn->inEnd - conn->inCursor;
		if (avail < msgLength)
		{
			/*
			 * Before returning, enlarge the input buffer if needed to hold
			 * the whole message.  This is better than leaving it to
			 * pqReadData because we can avoid multiple cycles of realloc()
			 * when the message is large; also, we can implement a reasonable
			 * recovery strategy if we are unable to make the buffer big
			 * enough.
			 */
			if (pqCheckInBufferSpace(conn->inCursor + (size_t) 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);
			}
			return;
		}

		/*
		 * NOTIFY and NOTICE messages can happen in any state; 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.
		 *
		 * ParameterStatus ('S') messages are a special case: in IDLE state we
		 * must process 'em (this case could happen if a new value was adopted
		 * from config file due to SIGHUP), but otherwise we hold off until
		 * BUSY state.
		 */
		if (id == 'A')
		{
			if (getNotify(conn))
				return;
		}
		else if (id == 'N')
		{
			if (pqGetErrorNotice3(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;
			 * ParameterStatus is handled normally; 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 (pqGetErrorNotice3(conn, false /* treat as notice */ ))
					return;
			}
			else if (id == 'S')
			{
				if (getParameterStatus(conn))
					return;
			}
			else
			{
				pqInternalNotice(&conn->noticeHooks,
						"message type 0x%02x arrived from server while idle",
								 id);
				/* Discard the unexpected message */
				conn->inCursor += msgLength;
			}
		}
		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);
					conn->asyncStatus = PGASYNC_READY;
					break;
				case 'E':		/* error return */
					if (pqGetErrorNotice3(conn, true))
						return;
					conn->asyncStatus = PGASYNC_READY;
					break;
				case 'Z':		/* backend is ready for new query */
					if (getReadyForQuery(conn))
						return;
					conn->asyncStatus = PGASYNC_IDLE;
					break;
				case 'I':		/* empty query */
					if (conn->result == NULL)
					{
						conn->result = PQmakeEmptyPGresult(conn,
														   PGRES_EMPTY_QUERY);
						if (!conn->result)
							return;
					}
					conn->asyncStatus = PGASYNC_READY;
					break;
				case '1':		/* Parse Complete */
					/* If we're doing PQprepare, we're done; else ignore */
					if (conn->queryclass == PGQUERY_PREPARE)
					{
						if (conn->result == NULL)
						{
							conn->result = PQmakeEmptyPGresult(conn,
														   PGRES_COMMAND_OK);
							if (!conn->result)
								return;
						}
						conn->asyncStatus = PGASYNC_READY;
					}
					break;
				case '2':		/* Bind Complete */
				case '3':		/* Close Complete */
					/* Nothing to do for these message types */
					break;
				case 'S':		/* parameter status */
					if (getParameterStatus(conn))
						return;
					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 'T':		/* Row Description */
					if (conn->result == NULL ||
						conn->queryclass == PGQUERY_DESCRIBE)
					{
						/* First 'T' in a query sequence */
						if (getRowDescriptions(conn))
							return;

						/*
						 * If we're doing a Describe, we're ready to pass the
						 * result back to the client.
						 */
						if (conn->queryclass == PGQUERY_DESCRIBE)
							conn->asyncStatus = PGASYNC_READY;
					}
					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 'n':		/* No Data */

					/*
					 * NoData indicates that we will not be seeing a
					 * RowDescription message because the statement or portal
					 * inquired about doesn't return rows.
					 *
					 * If we're doing a Describe, we have to pass something
					 * back to the client, so set up a COMMAND_OK result,
					 * instead of TUPLES_OK.  Otherwise we can just ignore
					 * this message.
					 */
					if (conn->queryclass == PGQUERY_DESCRIBE)
					{
						if (conn->result == NULL)
						{
							conn->result = PQmakeEmptyPGresult(conn,
														   PGRES_COMMAND_OK);
							if (!conn->result)
								return;
						}
						conn->asyncStatus = PGASYNC_READY;
					}
					break;
				case 't':		/* Parameter Description */
					if (getParamDescriptions(conn))
						return;
					break;
				case 'D':		/* Data Row */
					if (conn->result != NULL &&
						conn->result->resultStatus == PGRES_TUPLES_OK)
					{
						/* Read another tuple of a normal query response */
						if (getAnotherTuple(conn, msgLength))
							return;
					}
					else if (conn->result != NULL &&
							 conn->result->resultStatus == PGRES_FATAL_ERROR)
					{
						/*
						 * We've already choked for some reason.  Just discard
						 * tuples till we get to the end of the query.
						 */
						conn->inCursor += msgLength;
					}
					else
					{
						/* Set up to report error at end of query */
						printfPQExpBuffer(&conn->errorMessage,
										  libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n"));
						pqSaveErrorResult(conn);
						/* Discard the unexpected message */
						conn->inCursor += msgLength;
					}
					break;
				case 'G':		/* Start Copy In */
					if (getCopyStart(conn, PGRES_COPY_IN))
						return;
					conn->asyncStatus = PGASYNC_COPY_IN;
					break;
				case 'H':		/* Start Copy Out */
					if (getCopyStart(conn, PGRES_COPY_OUT))
						return;
					conn->asyncStatus = PGASYNC_COPY_OUT;
					conn->copy_already_done = 0;
					break;
				case 'd':		/* Copy Data */

					/*
					 * If we see Copy Data, just silently drop it.	This would
					 * only occur if application exits COPY OUT mode too
					 * early.
					 */
					conn->inCursor += msgLength;
					break;
				case 'c':		/* Copy Done */

					/*
					 * If we see Copy Done, just silently drop it.	This is
					 * the normal case during PQendcopy.  We will keep
					 * swallowing data, expecting to see command-complete for
					 * the COPY command.
					 */
					break;
				default:
					printfPQExpBuffer(&conn->errorMessage,
									  libpq_gettext(
													"unexpected response from server; first received character was \"%c\"\n"),
									  id);
					/* build an error result holding the error message */
					pqSaveErrorResult(conn);
					/* 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 */
			conn->inStart = conn->inCursor;
		}
		else
		{
			/* Trouble --- report it */
			printfPQExpBuffer(&conn->errorMessage,
							  libpq_gettext("message contents do not agree with length in message type \"%c\"\n"),
							  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;
		}
	}
}
Beispiel #3
0
/*
 * Add an error-location display to the error message under construction.
 *
 * The cursor location is measured in logical characters; the query string
 * is presumed to be in the specified encoding.
 */
static void
reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding)
{
#define DISPLAY_SIZE	60		/* screen width limit, in screen cols */
#define MIN_RIGHT_CUT	10		/* try to keep this far away from EOL */

	char	   *wquery;
	int			slen,
				cno,
				i,
			   *qidx,
			   *scridx,
				qoffset,
				scroffset,
				ibeg,
				iend,
				loc_line;
	bool		mb_encoding,
				beg_trunc,
				end_trunc;

	/* Convert loc from 1-based to 0-based; no-op if out of range */
	loc--;
	if (loc < 0)
		return;

	/* Need a writable copy of the query */
	wquery = strdup(query);
	if (wquery == NULL)
		return;					/* fail silently if out of memory */

	/*
	 * Each character might occupy multiple physical bytes in the string, and
	 * in some Far Eastern character sets it might take more than one screen
	 * column as well.	We compute the starting byte offset and starting
	 * screen column of each logical character, and store these in qidx[] and
	 * scridx[] respectively.
	 */

	/* we need a safe allocation size... */
	slen = strlen(wquery) + 1;

	qidx = (int *) malloc(slen * sizeof(int));
	if (qidx == NULL)
	{
		free(wquery);
		return;
	}
	scridx = (int *) malloc(slen * sizeof(int));
	if (scridx == NULL)
	{
		free(qidx);
		free(wquery);
		return;
	}

	/* We can optimize a bit if it's a single-byte encoding */
	mb_encoding = (pg_encoding_max_length(encoding) != 1);

	/*
	 * Within the scanning loop, cno is the current character's logical
	 * number, qoffset is its offset in wquery, and scroffset is its starting
	 * logical screen column (all indexed from 0).	"loc" is the logical
	 * character number of the error location.	We scan to determine loc_line
	 * (the 1-based line number containing loc) and ibeg/iend (first character
	 * number and last+1 character number of the line containing loc). Note
	 * that qidx[] and scridx[] are filled only as far as iend.
	 */
	qoffset = 0;
	scroffset = 0;
	loc_line = 1;
	ibeg = 0;
	iend = -1;					/* -1 means not set yet */

	for (cno = 0; wquery[qoffset] != '\0'; cno++)
	{
		char		ch = wquery[qoffset];

		qidx[cno] = qoffset;
		scridx[cno] = scroffset;

		/*
		 * Replace tabs with spaces in the writable copy.  (Later we might
		 * want to think about coping with their variable screen width, but
		 * not today.)
		 */
		if (ch == '\t')
			wquery[qoffset] = ' ';

		/*
		 * If end-of-line, count lines and mark positions. Each \r or \n
		 * counts as a line except when \r \n appear together.
		 */
		else if (ch == '\r' || ch == '\n')
		{
			if (cno < loc)
			{
				if (ch == '\r' ||
					cno == 0 ||
					wquery[qidx[cno - 1]] != '\r')
					loc_line++;
				/* extract beginning = last line start before loc. */
				ibeg = cno + 1;
			}
			else
			{
				/* set extract end. */
				iend = cno;
				/* done scanning. */
				break;
			}
		}

		/* Advance */
		if (mb_encoding)
		{
			int			w;

			w = pg_encoding_dsplen(encoding, &wquery[qoffset]);
			/* treat any non-tab control chars as width 1 */
			if (w <= 0)
				w = 1;
			scroffset += w;
			qoffset += pg_encoding_mblen(encoding, &wquery[qoffset]);
		}
		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)
		{
			/*
			 * We first truncate right if it is enough.  This code might be
			 * off a space or so on enforcing MIN_RIGHT_CUT if there's a wide
			 * character right there, but that should be okay.
			 */
			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, "...");

		/*
		 * While we have the prefix in the msg buffer, compute its screen
		 * width.
		 */
		scroffset = 0;
		for (; i < msg->len; i += pg_encoding_mblen(encoding, &msg->data[i]))
		{
			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');
	}

	/* Clean up. */
	free(scridx);
	free(qidx);
	free(wquery);
}
Beispiel #4
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
pqGetCopyData3(PGconn *conn, char **buffer, int async)
{
	int			msgLength;

	for (;;)
	{
		/*
		 * Collect the next input message.	To make life simpler for async
		 * callers, we keep returning 0 until the next message is fully
		 * available, even if it is not Copy Data.
		 */
		msgLength = getCopyDataMessage(conn);
		if (msgLength < 0)
		{
			/*
			 * On end-of-copy, exit COPY_OUT mode and let caller read status
			 * with PQgetResult().	The normal case is that it's Copy Done,
			 * but we let parseInput read that.  If error, we expect the state
			 * was already changed.
			 */
			if (msgLength == -1)
				conn->asyncStatus = PGASYNC_BUSY;
			return msgLength;	/* end-of-copy or error */
		}
		if (msgLength == 0)
		{
			/* 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;
			continue;
		}

		/*
		 * Drop zero-length messages (shouldn't happen anyway).  Otherwise
		 * pass the data back to the caller.
		 */
		msgLength -= 4;
		if (msgLength > 0)
		{
			*buffer = (char *) malloc(msgLength + 1);
			if (*buffer == NULL)
			{
				printfPQExpBuffer(&conn->errorMessage,
								  libpq_gettext("out of memory\n"));
				return -2;
			}
			memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength);
			(*buffer)[msgLength] = '\0';		/* Add terminating null */

			/* Mark message consumed */
			conn->inStart = conn->inCursor + msgLength;

			return msgLength;
		}

		/* Empty, so drop it and loop around for another */
		conn->inStart = conn->inCursor;
	}
}
Beispiel #5
0
/*
 * 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 + (size_t) 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);
}
Beispiel #6
0
/*
 * Send initial SSPI authentication token.
 * If use_negotiate is 0, use kerberos authentication package which is
 * compatible with Unix. If use_negotiate is 1, use the negotiate package
 * which supports both kerberos and NTLM, but is not compatible with Unix.
 */
static int
pg_SSPI_startup(PGconn *conn, int use_negotiate)
{
	SECURITY_STATUS r;
	TimeStamp	expire;

	conn->sspictx = NULL;

	/*
	 * Retreive credentials handle
	 */
	conn->sspicred = malloc(sizeof(CredHandle));
	if (conn->sspicred == NULL)
	{
		printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
		return STATUS_ERROR;
	}

	r = AcquireCredentialsHandle(NULL,
								 use_negotiate ? "negotiate" : "kerberos",
								 SECPKG_CRED_OUTBOUND,
								 NULL,
								 NULL,
								 NULL,
								 NULL,
								 conn->sspicred,
								 &expire);
	if (r != SEC_E_OK)
	{
		pg_SSPI_error(conn, libpq_gettext("could not acquire SSPI credentials"), r);
		free(conn->sspicred);
		conn->sspicred = NULL;
		return STATUS_ERROR;
	}

	/*
	 * Compute target principal name. SSPI has a different format from GSSAPI,
	 * but not more complex. We can skip the @REALM part, because Windows will
	 * fill that in for us automatically.
	 */
	if (!(conn->pghost && conn->pghost[0] != '\0'))
	{
		printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("host name must be specified\n"));
		return STATUS_ERROR;
	}
	conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(conn->pghost) + 2);
	if (!conn->sspitarget)
	{
		printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
		return STATUS_ERROR;
	}
	sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost);

	/*
	 * Indicate that we're in SSPI authentication mode to make sure that
	 * pg_SSPI_continue is called next time in the negotiation.
	 */
	conn->usesspi = 1;

	return pg_SSPI_continue(conn);
}
Beispiel #7
0
/* ----------
 * pqReadData: read more data, if any is available
 * Possible return values:
 *	 1: successfully loaded at least one more byte
 *	 0: no data is presently available, but no error detected
 *	-1: error detected (including EOF = connection closure);
 *		conn->errorMessage set
 * NOTE: callers must not assume that pointers or indexes into conn->inBuffer
 * remain valid across this call!
 * ----------
 */
int
pqReadData(PGconn *conn)
{
    int			someread = 0;
    int			nread;
    char		sebuf[256];

    if (conn->sock < 0)
    {
        printfPQExpBuffer(&conn->errorMessage,
                          libpq_gettext("connection not open\n"));
        return -1;
    }

    /* Left-justify any data in the buffer to make room */
    if (conn->inStart < conn->inEnd)
    {
        if (conn->inStart > 0)
        {
            memmove(conn->inBuffer, conn->inBuffer + conn->inStart,
                    conn->inEnd - conn->inStart);
            conn->inEnd -= conn->inStart;
            conn->inCursor -= conn->inStart;
            conn->inStart = 0;
        }
    }
    else
    {
        /* buffer is logically empty, reset it */
        conn->inStart = conn->inCursor = conn->inEnd = 0;
    }

    /*
     * If the buffer is fairly full, enlarge it. We need to be able to enlarge
     * the buffer in case a single message exceeds the initial buffer size. We
     * enlarge before filling the buffer entirely so as to avoid asking the
     * kernel for a partial packet. The magic constant here should be large
     * enough for a TCP packet or Unix pipe bufferload.  8K is the usual pipe
     * buffer size, so...
     */
    if (conn->inBufSize - conn->inEnd < 8192)
    {
        if (pqCheckInBufferSpace(conn->inEnd + (size_t) 8192, conn))
        {
            /*
             * We don't insist that the enlarge worked, but we need some room
             */
            if (conn->inBufSize - conn->inEnd < 100)
                return -1;		/* errorMessage already set */
        }
    }

    /* OK, try to read some data */
retry3:
    nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd,
                          conn->inBufSize - conn->inEnd);
    if (nread < 0)
    {
        if (SOCK_ERRNO == EINTR)
            goto retry3;
        /* Some systems return EAGAIN/EWOULDBLOCK for no data */
#ifdef EAGAIN
        if (SOCK_ERRNO == EAGAIN)
            return someread;
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
        if (SOCK_ERRNO == EWOULDBLOCK)
            return someread;
#endif
        /* We might get ECONNRESET here if using TCP and backend died */
#ifdef ECONNRESET
        if (SOCK_ERRNO == ECONNRESET)
            goto definitelyFailed;
#endif
        printfPQExpBuffer(&conn->errorMessage,
                          libpq_gettext("could not receive data from server: %s\n"),
                          SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
        return -1;
    }
    if (nread > 0)
    {
        conn->inEnd += nread;

        /*
         * Hack to deal with the fact that some kernels will only give us back
         * 1 packet per recv() call, even if we asked for more and there is
         * more available.	If it looks like we are reading a long message,
         * loop back to recv() again immediately, until we run out of data or
         * buffer space.  Without this, the block-and-restart behavior of
         * libpq's higher levels leads to O(N^2) performance on long messages.
         *
         * Since we left-justified the data above, conn->inEnd gives the
         * amount of data already read in the current message.	We consider
         * the message "long" once we have acquired 32k ...
         */
        if (conn->inEnd > 32768 &&
                (conn->inBufSize - conn->inEnd) >= 8192)
        {
            someread = 1;
            goto retry3;
        }
        return 1;
    }

    if (someread)
        return 1;				/* got a zero read after successful tries */

    /*
     * A return value of 0 could mean just that no data is now available, or
     * it could mean EOF --- that is, the server has closed the connection.
     * Since we have the socket in nonblock mode, the only way to tell the
     * difference is to see if select() is saying that the file is ready.
     * Grumble.  Fortunately, we don't expect this path to be taken much,
     * since in normal practice we should not be trying to read data unless
     * the file selected for reading already.
     *
     * In SSL mode it's even worse: SSL_read() could say WANT_READ and then
     * data could arrive before we make the pqReadReady() test.  So we must
     * play dumb and assume there is more data, relying on the SSL layer to
     * detect true EOF.
     */

#ifdef USE_SSL
    if (conn->ssl)
        return 0;
#endif

    switch (pqReadReady(conn))
    {
    case 0:
        /* definitely no data available */
        return 0;
    case 1:
        /* ready for read */
        break;
    default:
        goto definitelyFailed;
    }

    /*
     * Still not sure that it's EOF, because some data could have just
     * arrived.
     */
retry4:
    nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd,
                          conn->inBufSize - conn->inEnd);
    if (nread < 0)
    {
        if (SOCK_ERRNO == EINTR)
            goto retry4;
        /* Some systems return EAGAIN/EWOULDBLOCK for no data */
#ifdef EAGAIN
        if (SOCK_ERRNO == EAGAIN)
            return 0;
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
        if (SOCK_ERRNO == EWOULDBLOCK)
            return 0;
#endif
        /* We might get ECONNRESET here if using TCP and backend died */
#ifdef ECONNRESET
        if (SOCK_ERRNO == ECONNRESET)
            goto definitelyFailed;
#endif
        printfPQExpBuffer(&conn->errorMessage,
                          libpq_gettext("could not receive data from server: %s\n"),
                          SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
        return -1;
    }
    if (nread > 0)
    {
        conn->inEnd += nread;
        return 1;
    }

    /*
     * OK, we are getting a zero read even though select() says ready. This
     * means the connection has been closed.  Cope.
     */
definitelyFailed:
    printfPQExpBuffer(&conn->errorMessage,
                      libpq_gettext(
                          "server closed the connection unexpectedly\n"
                          "\tThis probably means the server terminated abnormally\n"
                          "\tbefore or while processing the request.\n"));
    conn->status = CONNECTION_BAD;		/* No more connection to backend */
    pqsecure_close(conn);
    closesocket(conn->sock);
    conn->sock = -1;

    return -1;
}
Beispiel #8
0
/*
 * 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)
							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;
				case 'P':		/* synchronous (normal) portal */
					if (pqGets(&conn->workBuffer, conn))
						return;
					/* We pretty much ignore this message type... */
					break;
				case 'T':		/* row descriptions (start of query results) */
					if (conn->result == NULL)
					{
						/* First 'T' in a query sequence */
						if (getRowDescriptions(conn))
							return;
					}
					else
					{
						/*
						 * A new 'T' message is treated as the start of
						 * another PGresult.  (It is not clear that this is
						 * really possible with the current backend.) We stop
						 * parsing until the application accepts the current
						 * result.
						 */
						conn->asyncStatus = PGASYNC_READY;
						return;
					}
					break;
				case 'D':		/* ASCII data tuple */
					if (conn->result != NULL)
					{
						/* Read another tuple of a normal query response */
						if (getAnotherTuple(conn, FALSE))
							return;
					}
					else
					{
						pqInternalNotice(&conn->noticeHooks,
										 "server sent data (\"D\" message) without prior row description (\"T\" message)");
						/* Discard the unexpected message; good idea?? */
						conn->inStart = conn->inEnd;
						return;
					}
					break;
				case 'B':		/* Binary data tuple */
					if (conn->result != NULL)
					{
						/* Read another tuple of a normal query response */
						if (getAnotherTuple(conn, TRUE))
							return;
					}
					else
					{
						pqInternalNotice(&conn->noticeHooks,
										 "server sent binary data (\"B\" message) without prior row description (\"T\" message)");
						/* Discard the unexpected message; good idea?? */
						conn->inStart = conn->inEnd;
						return;
					}
					break;
				case 'G':		/* Start Copy In */
					conn->asyncStatus = PGASYNC_COPY_IN;
					break;
				case 'H':		/* Start Copy Out */
					conn->asyncStatus = PGASYNC_COPY_OUT;
					break;

					/*
					 * 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;
	}
}
Beispiel #9
0
/*
 *		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;
}
Beispiel #10
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;
}
Beispiel #11
0
/*
 * 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);
}
Beispiel #12
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;
	}
}
Beispiel #13
0
/*
 * pg_fe_sendauth
 *		client demux routine for outgoing authentication information
 */
int
pg_fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
			   const char *password, char *PQerrormsg)
{
#ifndef KRB5
	(void) hostname;			/* not used */
#endif

	switch (areq)
	{
		case AUTH_REQ_OK:
			break;

		case AUTH_REQ_KRB4:
			snprintf(PQerrormsg, PQERRORMSG_LENGTH,
				 libpq_gettext("Kerberos 4 authentication not supported\n"));
			return STATUS_ERROR;

		case AUTH_REQ_KRB5:
#ifdef KRB5
			pglock_thread();
			if (pg_krb5_sendauth(PQerrormsg, conn->sock,
								 hostname, conn->krbsrvname) != STATUS_OK)
			{
				/* PQerrormsg already filled in */
				pgunlock_thread();
				return STATUS_ERROR;
			}
			pgunlock_thread();
			break;
#else
			snprintf(PQerrormsg, PQERRORMSG_LENGTH,
				 libpq_gettext("Kerberos 5 authentication not supported\n"));
			return STATUS_ERROR;
#endif

		case AUTH_REQ_MD5:
		case AUTH_REQ_CRYPT:
		case AUTH_REQ_PASSWORD:
			if (password == NULL || *password == '\0')
			{
				(void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
								PQnoPasswordSupplied);
				return STATUS_ERROR;
			}
			if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
			{
				(void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
					 "fe_sendauth: error sending password authentication\n");
				return STATUS_ERROR;
			}
			break;

		case AUTH_REQ_SCM_CREDS:
			if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
				return STATUS_ERROR;
			break;

		default:
			snprintf(PQerrormsg, PQERRORMSG_LENGTH,
			libpq_gettext("authentication method %u not supported\n"), areq);
			return STATUS_ERROR;
	}

	return STATUS_OK;
}
Beispiel #14
0
static int
pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
{
	int			ret;
	char	   *crypt_pwd;

	/* Encrypt the password if needed. */

	switch (areq)
	{
		case AUTH_REQ_MD5:
			{
				char	   *crypt_pwd2;

				/* Allocate enough space for two MD5 hashes */
				crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
				if (!crypt_pwd)
				{
					fprintf(stderr, libpq_gettext("out of memory\n"));
					return STATUS_ERROR;
				}

				crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
				if (!pg_md5_encrypt(password, conn->pguser,
									strlen(conn->pguser), crypt_pwd2))
				{
					free(crypt_pwd);
					return STATUS_ERROR;
				}
				if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt,
									sizeof(conn->md5Salt), crypt_pwd))
				{
					free(crypt_pwd);
					return STATUS_ERROR;
				}
				break;
			}
		case AUTH_REQ_CRYPT:
			{
				char		salt[3];

				StrNCpy(salt, conn->cryptSalt, 3);
				crypt_pwd = crypt(password, salt);
				break;
			}
		case AUTH_REQ_PASSWORD:
			/* discard const so we can assign it */
			crypt_pwd = (char *) password;
			break;
		default:
			return STATUS_ERROR;
	}
	/* Packet has a message type as of protocol 3.0 */
	if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
		ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
	else
		ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
	if (areq == AUTH_REQ_MD5)
		free(crypt_pwd);
	return ret;
}
Beispiel #15
0
/*
 * Continue GSS authentication with next token as needed.
 */
static int
pg_GSS_continue(PGconn *conn)
{
	OM_uint32	maj_stat,
				min_stat,
				lmin_s;

	maj_stat = gss_init_sec_context(&min_stat,
									GSS_C_NO_CREDENTIAL,
									&conn->gctx,
									conn->gtarg_nam,
									GSS_C_NO_OID,
									GSS_C_MUTUAL_FLAG,
									0,
									GSS_C_NO_CHANNEL_BINDINGS,
		  (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
									NULL,
									&conn->goutbuf,
									NULL,
									NULL);

	if (conn->gctx != GSS_C_NO_CONTEXT)
	{
		free(conn->ginbuf.value);
		conn->ginbuf.value = NULL;
		conn->ginbuf.length = 0;
	}

	if (conn->goutbuf.length != 0)
	{
		/*
		 * GSS generated data to send to the server. We don't care if it's the
		 * first or subsequent packet, just send the same kind of password
		 * packet.
		 */
		if (pqPacketSend(conn, 'p',
						 conn->goutbuf.value, conn->goutbuf.length)
			!= STATUS_OK)
		{
			gss_release_buffer(&lmin_s, &conn->goutbuf);
			return STATUS_ERROR;
		}
	}
	gss_release_buffer(&lmin_s, &conn->goutbuf);

	if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
	{
		pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
					 conn,
					 maj_stat, min_stat);
		gss_release_name(&lmin_s, &conn->gtarg_nam);
		if (conn->gctx)
			gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
		return STATUS_ERROR;
	}

	if (maj_stat == GSS_S_COMPLETE)
		gss_release_name(&lmin_s, &conn->gtarg_nam);

	return STATUS_OK;
}
Beispiel #16
0
/*
 * parseInput subroutine to read a 'B' or 'D' (row data) message.
 * We add another tuple to the existing PGresult structure.
 * Returns: 0 if completed message, EOF if error or not enough data yet.
 *
 * Note that if we run out of data, we have to suspend and reprocess
 * the message after more data is received.  We keep a partially constructed
 * tuple in conn->curTuple, and avoid reallocating already-allocated storage.
 */
static int
getAnotherTuple(PGconn *conn, bool binary)
{
	PGresult   *result = conn->result;
	int			nfields = result->numAttributes;
	PGresAttValue *tup;

	/* the backend sends us a bitmap of which attributes are null */
	char		std_bitmap[64]; /* used unless it doesn't fit */
	char	   *bitmap = std_bitmap;
	int			i;
	size_t		nbytes;			/* the number of bytes in bitmap  */
	char		bmap;			/* One byte of the bitmap */
	int			bitmap_index;	/* Its index */
	int			bitcnt;			/* number of bits examined in current byte */
	int			vlen;			/* length of the current field value */

	result->binary = binary;

	/* Allocate tuple space if first time for this data message */
	if (conn->curTuple == NULL)
	{
		conn->curTuple = (PGresAttValue *)
			pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE);
		if (conn->curTuple == NULL)
			goto outOfMemory;
		MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue));

		/*
		 * If it's binary, fix the column format indicators.  We assume the
		 * backend will consistently send either B or D, not a mix.
		 */
		if (binary)
		{
			for (i = 0; i < nfields; i++)
				result->attDescs[i].format = 1;
		}
	}
	tup = conn->curTuple;

	/* Get the null-value bitmap */
	nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
	/* malloc() only for unusually large field counts... */
	if (nbytes > sizeof(std_bitmap))
	{
		bitmap = (char *) malloc(nbytes);
		if (!bitmap)
			goto outOfMemory;
	}

	if (pqGetnchar(bitmap, nbytes, conn))
		goto EOFexit;

	/* Scan the fields */
	bitmap_index = 0;
	bmap = bitmap[bitmap_index];
	bitcnt = 0;

	for (i = 0; i < nfields; i++)
	{
		if (!(bmap & 0200))
		{
			/* if the field value is absent, make it a null string */
			tup[i].value = result->null_field;
			tup[i].len = NULL_LEN;
		}
		else
		{
			/* get the value length (the first four bytes are for length) */
			if (pqGetInt(&vlen, 4, conn))
				goto EOFexit;
			if (!binary)
				vlen = vlen - 4;
			if (vlen < 0)
				vlen = 0;
			if (tup[i].value == NULL)
			{
				tup[i].value = (char *) pqResultAlloc(result, vlen + 1, binary);
				if (tup[i].value == NULL)
					goto outOfMemory;
			}
			tup[i].len = vlen;
			/* read in the value */
			if (vlen > 0)
				if (pqGetnchar((char *) (tup[i].value), vlen, conn))
					goto EOFexit;
			/* we have to terminate this ourselves */
			tup[i].value[vlen] = '\0';
		}
		/* advance the bitmap stuff */
		bitcnt++;
		if (bitcnt == BITS_PER_BYTE)
		{
			bitmap_index++;
			bmap = bitmap[bitmap_index];
			bitcnt = 0;
		}
		else
			bmap <<= 1;
	}

	/* Success!  Store the completed tuple in the result */
	if (!pqAddTuple(result, tup))
		goto outOfMemory;
	/* and reset for a new message */
	conn->curTuple = NULL;

	if (bitmap != std_bitmap)
		free(bitmap);
	return 0;

outOfMemory:
	/* Replace partially constructed result with an error result */

	/*
	 * we do NOT use pqSaveErrorResult() here, because of the likelihood that
	 * there's not enough memory to concatenate messages...
	 */
	pqClearAsyncResult(conn);
	printfPQExpBuffer(&conn->errorMessage,
					  libpq_gettext("out of memory for query result\n"));

	/*
	 * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
	 * do to recover...
	 */
	conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
	conn->asyncStatus = PGASYNC_READY;
	/* Discard the failed message --- good idea? */
	conn->inStart = conn->inEnd;

EOFexit:
	if (bitmap != NULL && bitmap != std_bitmap)
		free(bitmap);
	return EOF;
}
Beispiel #17
0
/*
 * Continue SSPI authentication with next token as needed.
 */
static int
pg_SSPI_continue(PGconn *conn)
{
	SECURITY_STATUS r;
	CtxtHandle	newContext;
	ULONG		contextAttr;
	SecBufferDesc inbuf;
	SecBufferDesc outbuf;
	SecBuffer	OutBuffers[1];
	SecBuffer	InBuffers[1];

	if (conn->sspictx != NULL)
	{
		/*
		 * On runs other than the first we have some data to send. Put this
		 * data in a SecBuffer type structure.
		 */
		inbuf.ulVersion = SECBUFFER_VERSION;
		inbuf.cBuffers = 1;
		inbuf.pBuffers = InBuffers;
		InBuffers[0].pvBuffer = conn->ginbuf.value;
		InBuffers[0].cbBuffer = conn->ginbuf.length;
		InBuffers[0].BufferType = SECBUFFER_TOKEN;
	}

	OutBuffers[0].pvBuffer = NULL;
	OutBuffers[0].BufferType = SECBUFFER_TOKEN;
	OutBuffers[0].cbBuffer = 0;
	outbuf.cBuffers = 1;
	outbuf.pBuffers = OutBuffers;
	outbuf.ulVersion = SECBUFFER_VERSION;

	r = InitializeSecurityContext(conn->sspicred,
								  conn->sspictx,
								  conn->sspitarget,
								  ISC_REQ_ALLOCATE_MEMORY,
								  0,
								  SECURITY_NETWORK_DREP,
								  (conn->sspictx == NULL) ? NULL : &inbuf,
								  0,
								  &newContext,
								  &outbuf,
								  &contextAttr,
								  NULL);

	if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED)
	{
		pg_SSPI_error(conn, libpq_gettext("SSPI continuation error"), r);

		return STATUS_ERROR;
	}

	if (conn->sspictx == NULL)
	{
		/* On first run, transfer retreived context handle */
		conn->sspictx = malloc(sizeof(CtxtHandle));
		if (conn->sspictx == NULL)
		{
			printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
			return STATUS_ERROR;
		}
		memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle));
	}
	else
	{
		/*
		 * On subsequent runs when we had data to send, free buffers that
		 * contained this data.
		 */
		free(conn->ginbuf.value);
		conn->ginbuf.value = NULL;
		conn->ginbuf.length = 0;
	}

	/*
	 * If SSPI returned any data to be sent to the server (as it normally
	 * would), send this data as a password packet.
	 */
	if (outbuf.cBuffers > 0)
	{
		if (outbuf.cBuffers != 1)
		{
			/*
			 * This should never happen, at least not for Kerberos
			 * authentication. Keep check in case it shows up with other
			 * authentication methods later.
			 */
			printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n");
			return STATUS_ERROR;
		}

		/*
		 * If the negotiation is complete, there may be zero bytes to send.
		 * The server is at this point not expecting any more data, so don't
		 * send it.
		 */
		if (outbuf.pBuffers[0].cbBuffer > 0)
		{
			if (pqPacketSend(conn, 'p',
				   outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer))
			{
				FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
				return STATUS_ERROR;
			}
		}
		FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
	}

	/* Cleanup is handled by the code in freePGconn() */
	return STATUS_OK;
}
Beispiel #18
0
static void
do_field(
	const PQprintOpt* po,
	const PGresult* res,
	const int i,
	const int j,
	const int fs_len,
	char **fields,
	const int nFields,
	char const **fieldNames,
	unsigned char *fieldNotNum,
	int *fieldMax,
	const int fieldMaxLen,
	FILE * fout)
{

	const char *pval, *p;
	int plen;
	bool skipit;

	plen = PQgetlength(res, i, j);
	pval = PQgetvalue(res, i, j);

	if (plen < 1 || !pval || !*pval) {
		if (po->align || po->expanded) {
			skipit = true;
		} else {
			skipit = false;
			goto efield;
		}
	} else {
		skipit = false;
	}

	if (skipit)
		return;

	if (po->align && !fieldNotNum[j]) {
		/* Detect whether field contains non-numeric data */
		char ch = '0';

		for (p = pval; *p; p += PQmblen(p, res->ClientEncoding)) {
			ch = *p;
			if (!((ch >= '0' && ch <= '9') || ch == '.'
				|| ch == 'E' || ch == 'e' || ch == ' ' || ch == '-')) {
				fieldNotNum[j] = 1;
				break;
			}
		}

		/*
		 * Above loop will believe E in first column is numeric; also, we
		 * insist on a digit in the last column for a numeric. This test
		 * is still not bulletproof but it handles most cases.
		 */
		if (*pval == 'E' || *pval == 'e' || !(ch >= '0' && ch <= '9'))
			fieldNotNum[j] = 1;
	}

	if (!po->expanded && (po->align || po->html3)) {
		if (plen > fieldMax[j])
			fieldMax[j] = plen;

		if (!(fields[i * nFields + j] = (char*) malloc(plen + 1))) {
			fprintf(stderr, libpq_gettext("out of memory\n"));
			exit(1);
		}

		strcpy(fields[i * nFields + j], pval);
	} else {
		if (po->expanded) {
			if (po->html3) {
				fprintf(fout,
					"<tr><td align=\"left\"><b>%s</b></td>"
					"<td align=\"%s\">%s</td></tr>\n",
					fieldNames[j],
					fieldNotNum[j] ? "left" : "right", pval);
			} else {
				if (po->align)
					fprintf(fout, "%-*s%s %s\n", fieldMaxLen - fs_len,
						fieldNames[j], po->fieldSep, pval);
				else
					fprintf(fout, "%s%s%s\n", fieldNames[j],
						po->fieldSep, pval);
			}
		} else {
			if (!po->html3) {
				fputs(pval, fout);
efield:
				if ((j + 1) < nFields)
					fputs(po->fieldSep, fout);
				else
					fputc('\n', fout);
			}
		}
	}
}
Beispiel #19
0
/*
 * pg_fe_sendauth
 *		client demux routine for outgoing authentication information
 */
int
pg_fe_sendauth(AuthRequest areq, PGconn *conn)
{
	switch (areq)
	{
		case AUTH_REQ_OK:
			break;

		case AUTH_REQ_KRB4:
			printfPQExpBuffer(&conn->errorMessage,
				 libpq_gettext("Kerberos 4 authentication not supported\n"));
			return STATUS_ERROR;

		case AUTH_REQ_KRB5:
#ifdef KRB5
			pglock_thread();
			if (pg_krb5_sendauth(conn) != STATUS_OK)
			{
				/* Error message already filled in */
				pgunlock_thread();
				return STATUS_ERROR;
			}
			pgunlock_thread();
			break;
#else
			printfPQExpBuffer(&conn->errorMessage,
				 libpq_gettext("Kerberos 5 authentication not supported\n"));
			return STATUS_ERROR;
#endif

#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
		case AUTH_REQ_GSS:
#if !defined(ENABLE_SSPI)
			/* no native SSPI, so use GSSAPI library for it */
		case AUTH_REQ_SSPI:
#endif
			{
				int			r;

				pglock_thread();

				/*
				 * If we have both GSS and SSPI support compiled in, use SSPI
				 * support by default. This is overridable by a connection
				 * string parameter. Note that when using SSPI we still leave
				 * the negotiate parameter off, since we want SSPI to use the
				 * GSSAPI kerberos protocol. For actual SSPI negotiate
				 * protocol, we use AUTH_REQ_SSPI.
				 */
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
				if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0))
					r = pg_GSS_startup(conn);
				else
					r = pg_SSPI_startup(conn, 0);
#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
				r = pg_GSS_startup(conn);
#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
				r = pg_SSPI_startup(conn, 0);
#endif
				if (r != STATUS_OK)
				{
					/* Error message already filled in. */
					pgunlock_thread();
					return STATUS_ERROR;
				}
				pgunlock_thread();
			}
			break;

		case AUTH_REQ_GSS_CONT:
			{
				int			r;

				pglock_thread();
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
				if (conn->usesspi)
					r = pg_SSPI_continue(conn);
				else
					r = pg_GSS_continue(conn);
#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
				r = pg_GSS_continue(conn);
#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
				r = pg_SSPI_continue(conn);
#endif
				if (r != STATUS_OK)
				{
					/* Error message already filled in. */
					pgunlock_thread();
					return STATUS_ERROR;
				}
				pgunlock_thread();
			}
			break;
#else							/* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */
			/* No GSSAPI *or* SSPI support */
		case AUTH_REQ_GSS:
		case AUTH_REQ_GSS_CONT:
			printfPQExpBuffer(&conn->errorMessage,
					 libpq_gettext("GSSAPI authentication not supported\n"));
			return STATUS_ERROR;
#endif   /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */

#ifdef ENABLE_SSPI
		case AUTH_REQ_SSPI:

			/*
			 * SSPI has it's own startup message so libpq can decide which
			 * method to use. Indicate to pg_SSPI_startup that we want SSPI
			 * negotiation instead of Kerberos.
			 */
			pglock_thread();
			if (pg_SSPI_startup(conn, 1) != STATUS_OK)
			{
				/* Error message already filled in. */
				pgunlock_thread();
				return STATUS_ERROR;
			}
			pgunlock_thread();
			break;
#else

			/*
			 * No SSPI support. However, if we have GSSAPI but not SSPI
			 * support, AUTH_REQ_SSPI will have been handled in the codepath
			 * for AUTH_REQ_GSSAPI above, so don't duplicate the case label in
			 * that case.
			 */
#if !defined(ENABLE_GSS)
		case AUTH_REQ_SSPI:
			printfPQExpBuffer(&conn->errorMessage,
					   libpq_gettext("SSPI authentication not supported\n"));
			return STATUS_ERROR;
#endif   /* !define(ENABLE_GSSAPI) */
#endif   /* ENABLE_SSPI */


		case AUTH_REQ_CRYPT:
			printfPQExpBuffer(&conn->errorMessage,
					  libpq_gettext("Crypt authentication not supported\n"));
			return STATUS_ERROR;

		case AUTH_REQ_MD5:
		case AUTH_REQ_PASSWORD:
			conn->password_needed = true;
			if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
			{
				printfPQExpBuffer(&conn->errorMessage,
								  PQnoPasswordSupplied);
				return STATUS_ERROR;
			}
			if (pg_password_sendauth(conn, conn->pgpass, areq) != STATUS_OK)
			{
				printfPQExpBuffer(&conn->errorMessage,
					 "fe_sendauth: error sending password authentication\n");
				return STATUS_ERROR;
			}
			break;

		case AUTH_REQ_SCM_CREDS:
			if (pg_local_sendauth(conn) != STATUS_OK)
				return STATUS_ERROR;
			break;

		default:
			printfPQExpBuffer(&conn->errorMessage,
			libpq_gettext("authentication method %u not supported\n"), areq);
			return STATUS_ERROR;
	}

	return STATUS_OK;
}
Beispiel #20
0
static char*
do_header(
	FILE* fout,
	const PQprintOpt* po,
	const int nFields,
	int* fieldMax,
	const char** fieldNames,
	unsigned char* fieldNotNum,
	const int fs_len,
	const PGresult* res)
{
	int j;			/* for loop index */
	char *border = NULL;

	if (po->html3) {
		fputs("<tr>", fout);
	} else {
		int tot = 0;
		int n = 0;
		char *p = NULL;

		for (; n < nFields; n++)
			tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);

		if (po->standard)
			tot += fs_len * 2 + 2;

		border = malloc(tot + 1);
		if (!border) {
			fprintf(stderr, libpq_gettext("out of memory\n"));
			exit(1);
		}

		p = border;
		if (po->standard) {
			char *fs;

			fs = po->fieldSep;
			while (*fs++)
				*p++ = '+';
		}

		for (j = 0; j < nFields; j++) {
			int len;

			for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-') ;
			if (po->standard || (j + 1) < nFields) {
				char *fs;

				fs = po->fieldSep;
				while (*fs++)
					*p++ = '+';
			}
		}

		*p = '\0';
		if (po->standard)
			fprintf(fout, "%s\n", border);
	}

	if (po->standard)
		fputs(po->fieldSep, fout);

	for (j = 0; j < nFields; j++) {
		const char *s;

		s = PQfname(res, j);
		if (po->html3) {
			fprintf(fout,
				"<th align=\"%s\">%s</th>",
				fieldNotNum[j] ? "left" : "right",
				fieldNames[j]);
		} else {
			int n = strlen(s);
 
			if (n > fieldMax[j])
				fieldMax[j] = n; 

			if (po->standard) {
				fprintf(fout, fieldNotNum[j] ? " %-*s " : " %*s ", fieldMax[j], s);
			} else {
				fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
			}

			if (po->standard || (j + 1) < nFields)
				fputs(po->fieldSep, fout);
		}
	}

	if (po->html3)
		fputs("</tr>\n", fout);
	else
		fprintf(fout, "\n%s\n", border);

	return border;
}
Beispiel #21
0
/*
 * pqSendSome: send data waiting in the output buffer.
 *
 * len is how much to try to send (typically equal to outCount, but may
 * be less).
 *
 * Return 0 on success, -1 on failure and 1 when not all data could be sent
 * because the socket would block and the connection is non-blocking.
 */
static int
pqSendSome(PGconn *conn, int len)
{
    char	   *ptr = conn->outBuffer;
    int			remaining = conn->outCount;
    int			result = 0;

    if (conn->sock < 0)
    {
        printfPQExpBuffer(&conn->errorMessage,
                          libpq_gettext("connection not open\n"));
        return -1;
    }

    /* while there's still data to send */
    while (len > 0)
    {
        int			sent;
        char		sebuf[256];

#ifndef WIN32
        sent = pqsecure_write(conn, ptr, len);
#else

        /*
         * Windows can fail on large sends, per KB article Q201213. The
         * failure-point appears to be different in different versions of
         * Windows, but 64k should always be safe.
         */
        sent = pqsecure_write(conn, ptr, Min(len, 65536));
#endif

        if (sent < 0)
        {
            /*
             * Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble. If it's
             * EPIPE or ECONNRESET, assume we've lost the backend connection
             * permanently.
             */
            switch (SOCK_ERRNO)
            {
#ifdef EAGAIN
            case EAGAIN:
                break;
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
            case EWOULDBLOCK:
                break;
#endif
            case EINTR:
                continue;

            case EPIPE:
#ifdef ECONNRESET
            case ECONNRESET:
#endif
                printfPQExpBuffer(&conn->errorMessage,
                                  libpq_gettext(
                                      "server closed the connection unexpectedly\n"
                                      "\tThis probably means the server terminated abnormally\n"
                                      "\tbefore or while processing the request.\n"));

                /*
                 * We used to close the socket here, but that's a bad idea
                 * since there might be unread data waiting (typically, a
                 * NOTICE message from the backend telling us it's
                 * committing hara-kiri...).  Leave the socket open until
                 * pqReadData finds no more data can be read.  But abandon
                 * attempt to send data.
                 */
                conn->outCount = 0;
                return -1;

            default:
                printfPQExpBuffer(&conn->errorMessage,
                                  libpq_gettext("could not send data to server: %s\n"),
                                  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                /* We don't assume it's a fatal error... */
                conn->outCount = 0;
                return -1;
            }
        }
        else
        {
            ptr += sent;
            len -= sent;
            remaining -= sent;
        }

        if (len > 0)
        {
            /*
             * We didn't send it all, wait till we can send more.
             *
             * If the connection is in non-blocking mode we don't wait, but
             * return 1 to indicate that data is still pending.
             */
            if (pqIsnonblocking(conn))
            {
                result = 1;
                break;
            }

            /*
             * There are scenarios in which we can't send data because the
             * communications channel is full, but we cannot expect the server
             * to clear the channel eventually because it's blocked trying to
             * send data to us.  (This can happen when we are sending a large
             * amount of COPY data, and the server has generated lots of
             * NOTICE responses.)  To avoid a deadlock situation, we must be
             * prepared to accept and buffer incoming data before we try
             * again.  Furthermore, it is possible that such incoming data
             * might not arrive until after we've gone to sleep.  Therefore,
             * we wait for either read ready or write ready.
             */
            if (pqReadData(conn) < 0)
            {
                result = -1;	/* error message already set up */
                break;
            }
            if (pqWait(TRUE, TRUE, conn))
            {
                result = -1;
                break;
            }
        }
    }

    /* shift the remaining contents of the buffer */
    if (remaining > 0)
        memmove(conn->outBuffer, ptr, remaining);
    conn->outCount = remaining;

    return result;
}
Beispiel #22
0
/*
 * Really old printing routines
 */
void
PQdisplayTuples(
	const PGresult* res,
	FILE* fp,		/* where to send the output */
	int fillAlign,		/* pad the fields with spaces */
	const char* fieldSep,	/* field separator */
	int printHeader,	/* display headers? */
	int quiet)
{
#define DEFAULT_FIELD_SEP " "

	int i, j;
	int nFields;
	int nTuples;
	int *fLength = NULL;

	if (fieldSep == NULL)
		fieldSep = DEFAULT_FIELD_SEP;

	/* Get some useful info about the results */
	nFields = PQnfields(res);
	nTuples = PQntuples(res);
	if (fp == NULL)
		fp = stdout;

	/* Figure the field lengths to align to */
	/* will be somewhat time consuming for very large results */
	if (fillAlign) {
		fLength = (int*) malloc(nFields * sizeof(int));
		if (!fLength) {
			fprintf(stderr, libpq_gettext("out of memory\n"));
			exit(1);
		}

		for (j = 0; j < nFields; j++) {
			fLength[j] = strlen(PQfname(res, j));
			for (i = 0; i < nTuples; i++) {
				int flen;

				flen = PQgetlength(res, i, j);
				if (flen > fLength[j])
					fLength[j] = flen;
			}
		}
	}

	if (printHeader) {
		/* first, print out the attribute names */
		for (i = 0; i < nFields; i++) {
			fputs(PQfname(res, i), fp);
			if (fillAlign)
				fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);

			fputs(fieldSep, fp);
		}

		fprintf(fp, "\n");

		/* Underline the attribute names */
		for (i = 0; i < nFields; i++) {
			if (fillAlign)
				fill(0, fLength[i], '-', fp);

			fputs(fieldSep, fp);
		}

		fprintf(fp, "\n");
	}

	/* next, print out the instances */
	for (i = 0; i < nTuples; i++) {
		for (j = 0; j < nFields; j++) {
			fprintf(fp, "%s", PQgetvalue(res, i, j));
			if (fillAlign)
				fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);

			fputs(fieldSep, fp);
		}

		fprintf(fp, "\n");
	}

	if (!quiet)
		fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
			(PQntuples(res) == 1) ? "" : "s");

	fflush(fp);
	if (fLength)
		free(fLength);
}
Beispiel #23
0
/*
 * PQendcopy
 *
 * See fe-exec.c for documentation.
 */
int
pqEndcopy3(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;
	}

	/* Send the CopyDone message if needed */
	if (conn->asyncStatus == PGASYNC_COPY_IN)
	{
		if (pqPutMsgStart('c', false, conn) < 0 ||
			pqPutMsgEnd(conn) < 0)
			return 1;

		/*
		 * If we sent the COPY command in extended-query mode, we must issue a
		 * Sync as well.
		 */
		if (conn->queryclass != PGQUERY_SIMPLE)
		{
			if (pqPutMsgStart('S', false, conn) < 0 ||
				pqPutMsgEnd(conn) < 0)
				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;

	/* Return to active duty */
	conn->asyncStatus = PGASYNC_BUSY;
	resetPQExpBuffer(&conn->errorMessage);

	/*
	 * Non blocking connections may have to abort at this point.  If everyone
	 * played the game there should be no problem, but in error scenarios the
	 * expected messages may not have arrived yet.	(We are assuming that the
	 * backend's packetizing will ensure that CommandComplete arrives along
	 * with the CopyDone; are there corner cases where that doesn't happen?)
	 */
	if (pqIsnonblocking(conn) && PQisBusy(conn))
		return 1;

	/* 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);

	return 1;
}
Beispiel #24
0
void
PQprintTuples(
	const PGresult* res,
	FILE* fout,		/* output stream */
	int PrintAttNames,	/* print attribute names or not */
	int TerseOutput,	/* delimiter bars or not? */
	int colWidth)		/* width of column, if 0, use variable width */
{
	int nFields;
	int nTups;
	int i, j;
	char formatString[80];
	char *tborder = NULL;

	nFields = PQnfields(res);
	nTups = PQntuples(res);

	if (colWidth > 0) {
		sprintf(formatString, "%%s %%-%ds", colWidth);
	} else {
		sprintf(formatString, "%%s %%s");
	}

	if (nFields > 0) {	/* only print rows with at least 1 field.  */
		if (!TerseOutput) {
			int width;

			width = nFields * 14;
			tborder = malloc(width + 1);
			if (!tborder) {
				fprintf(stderr, libpq_gettext("out of memory\n"));
				exit(1);
			}

			for (i = 0; i <= width; i++)
				tborder[i] = '-';

			tborder[i] = '\0';
			fprintf(fout, "%s\n", tborder);
		}

		for (i = 0; i < nFields; i++) {
			if (PrintAttNames)
				fprintf(fout, formatString, TerseOutput? "" : "|", PQfname(res, i));
		}

		if (PrintAttNames) {
			if (TerseOutput) {
				fprintf(fout, "\n");
			} else {
				fprintf(fout, "|\n%s\n", tborder);
			}
		}

		for (i = 0; i < nTups; i++) {
			for (j = 0; j < nFields; j++) {
				const char *pval;

				pval = PQgetvalue(res, i, j);
				fprintf(fout, formatString, TerseOutput? "" : "|", pval? pval : "");
			}

			if (TerseOutput)
				fprintf(fout, "\n");
			else
				fprintf(fout, "|\n%s\n", tborder);
		}
	}

	if (tborder)
		free(tborder);
}
Beispiel #25
0
/*
 * parseInput subroutine to read a 'D' (row data) message.
 * We add another tuple to the existing PGresult structure.
 * Returns: 0 if completed message, EOF if error or not enough data yet.
 *
 * Note that if we run out of data, we have to suspend and reprocess
 * the message after more data is received.  We keep a partially constructed
 * tuple in conn->curTuple, and avoid reallocating already-allocated storage.
 */
static int
getAnotherTuple(PGconn *conn, int msgLength)
{
	PGresult   *result = conn->result;
	int			nfields = result->numAttributes;
	PGresAttValue *tup;
	int			tupnfields;		/* # fields from tuple */
	int			vlen;			/* length of the current field value */
	int			i;

	/* Allocate tuple space if first time for this data message */
	if (conn->curTuple == NULL)
	{
		conn->curTuple = (PGresAttValue *)
			pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE);
		if (conn->curTuple == NULL)
			goto outOfMemory;
		MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue));
	}
	tup = conn->curTuple;

	/* Get the field count and make sure it's what we expect */
	if (pqGetInt(&tupnfields, 2, conn))
		return EOF;

	if (tupnfields != nfields)
	{
		/* Replace partially constructed result with an error result */
		printfPQExpBuffer(&conn->errorMessage,
				 libpq_gettext("unexpected field count in \"D\" message\n"));
		pqSaveErrorResult(conn);
		/* Discard the failed message by pretending we read it */
		conn->inCursor = conn->inStart + 5 + msgLength;
		return 0;
	}

	/* Scan the fields */
	for (i = 0; i < nfields; i++)
	{
		/* get the value length */
		if (pqGetInt(&vlen, 4, conn))
			return EOF;
		if (vlen == -1)
		{
			/* null field */
			tup[i].value = result->null_field;
			tup[i].len = NULL_LEN;
			continue;
		}
		if (vlen < 0)
			vlen = 0;
		if (tup[i].value == NULL)
		{
			bool		isbinary = (result->attDescs[i].format != 0);

			tup[i].value = (char *) pqResultAlloc(result, vlen + 1, isbinary);
			if (tup[i].value == NULL)
				goto outOfMemory;
		}
		tup[i].len = vlen;
		/* read in the value */
		if (vlen > 0)
			if (pqGetnchar((char *) (tup[i].value), vlen, conn))
				return EOF;
		/* we have to terminate this ourselves */
		tup[i].value[vlen] = '\0';
	}

	/* Success!  Store the completed tuple in the result */
	if (!pqAddTuple(result, tup))
		goto outOfMemory;
	/* and reset for a new message */
	conn->curTuple = NULL;

	return 0;

outOfMemory:

	/*
	 * Replace partially constructed result with an error result. First
	 * discard the old result to try to win back some memory.
	 */
	pqClearAsyncResult(conn);
	printfPQExpBuffer(&conn->errorMessage,
					  libpq_gettext("out of memory for query result\n"));
	pqSaveErrorResult(conn);

	/* Discard the failed message by pretending we read it */
	conn->inCursor = conn->inStart + 5 + msgLength;
	return 0;
}
Beispiel #26
0
/*
 * PQprint()
 *
 * Format results of a query for printing.
 *
 * PQprintOpt is a typedef (structure) that containes various flags and options. Consult sci.h for
 * details.
 *
 * This function should probably be removed sometime since psql doesn't use it anymore. It is
 * unclear to what extent this is used by external clients, however.
 */
void PQprint(FILE* fout, const PGresult* res, const PQprintOpt* po)
{
	int nFields;
	int i, j;
	int nTups;
	int *fieldMax = NULL;	/* in case we don't use them */
	unsigned char *fieldNotNum = NULL;
	char *border = NULL;
	char **fields = NULL;
	const char **fieldNames;
	int fieldMaxLen = 0;
	int numFieldName;
	int fs_len = strlen(po->fieldSep);
	int total_line_length = 0;
	int usePipe = 0;
	char *pagerenv;

#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
	sigset_t osigset;
	bool sigpipe_masked = false;
	bool sigpipe_pending;
#endif

#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
	pqsignal_f* oldsigpipehandler = NULL;
#endif

#ifdef TIOCGWINSZ
	struct winsize screen_size;
#else
	struct winsize {
		int ws_row;
		int ws_col;
	} screen_size;
#endif


	nFields = PQnfields(res);
	if (nFields <= 0)
		return; 	/* only print rows with at least 1 field.  */

	nTups = PQntuples(res);
	if (!(fieldNames = (const char **)calloc(nFields, sizeof(char *)))) {
		fprintf(stderr, libpq_gettext("out of memory\n"));
		exit(1);
	}

	if (!(fieldNotNum = (unsigned char *)calloc(nFields, 1))) {
		fprintf(stderr, libpq_gettext("out of memory\n"));
		exit(1);
	}

	if (!(fieldMax = (int *)calloc(nFields, sizeof(int)))) {
		fprintf(stderr, libpq_gettext("out of memory\n"));
		exit(1);
	}

	for (numFieldName = 0; po->fieldName && po->fieldName[numFieldName]; numFieldName++) ;

	for (j = 0; j < nFields; j++) {
		int len;
		const char *s;

		s = (j < numFieldName && po->fieldName[j][0]) ? po->fieldName[j] : PQfname(res, j);
		fieldNames[j] = s;
		len = s ? strlen(s) : 0;
		fieldMax[j] = len;
		len += fs_len;
		if (len > fieldMaxLen)
			fieldMaxLen = len;

		total_line_length += len;
	}

	total_line_length += nFields * strlen(po->fieldSep) + 1;
	if (fout == NULL)
		fout = stdout;

	if (po->pager
		&& fout == stdout
		&& isatty(fileno(stdin))
		&& isatty(fileno(stdout))) {
		/*
		 * If we think there'll be more than one screen of 
		 * output, try to pipe to the pager program.
		 */
#ifdef TIOCGWINSZ
		if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1
			|| screen_size.ws_col == 0
			|| screen_size.ws_row == 0) {
			screen_size.ws_row = 24;
			screen_size.ws_col = 80;
		}
#else
		screen_size.ws_row = 24;
		screen_size.ws_col = 80;
#endif
		pagerenv = getenv("PAGER");
		if (pagerenv != NULL 
			&& pagerenv[0] != '\0' 
			&& !po->html3
			&& ((po->expanded && nTups * (nFields + 1) >= screen_size.ws_row) 
				|| (!po->expanded 
					&& nTups * (total_line_length / screen_size.ws_col + 1) 
						* (1 + (po->standard != 0)) >= 
				screen_size.ws_row - (po->header != 0) * 
				(total_line_length / screen_size.ws_col + 1) * 2 -
				(po->header != 0) * 2	/* row count and newline */
			))) {
			fout = popen(pagerenv, "w");

			if (fout) {
				usePipe = 1;
#ifndef WIN32
#ifdef ENABLE_THREAD_SAFETY
				if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
					sigpipe_masked = true;
#else
				oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
#endif /* ENABLE_THREAD_SAFETY */
#endif /* WIN32 */
			} else {
				fout = stdout;
			}
		}
	}

	if (!po->expanded && (po->align || po->html3)) {
		if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char*)))) {
			fprintf(stderr, libpq_gettext("out of memory\n"));
			exit(1);
		}
	} else if (po->header && !po->html3) {
		if (po->expanded) {
			if (po->align)
				fprintf(fout, libpq_gettext("%-*s%s Value\n"), fieldMaxLen - fs_len,
					libpq_gettext("Field"), po->fieldSep);
			else
				fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"),
					po->fieldSep);
		} else {
			int len = 0;

			for (j = 0; j < nFields; j++) {
				const char *s;

				s = fieldNames[j];
				fputs(s, fout);
				len += strlen(s) + fs_len;
				if ((j + 1) < nFields)
					fputs(po->fieldSep, fout);
			}

			fputc('\n', fout);
			for (len -= fs_len; len--; fputc('-', fout)) ;
			fputc('\n', fout);
		}
	}

	if (po->expanded && po->html3) {
		if (po->caption)
			fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption);
		else
			fprintf(fout, "<center><h2>Query retrieved %d rows * %d fields</h2>""</center>\n",
				nTups, nFields);
	}

	for (i = 0; i < nTups; i++) {
		if (po->expanded) {
			if (po->html3)
				fprintf(fout, "<table %s><caption align=\"top\">%d</caption>\n",
					po->tableOpt ? po->tableOpt : "", i);
			else
				fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i);
		}

		for (j = 0; j < nFields; j++)
			do_field(po, res, i, j, fs_len, fields, nFields,
				fieldNames, fieldNotNum, fieldMax, fieldMaxLen, fout);

		if (po->html3 && po->expanded)
			fputs("</table>\n", fout);
	}

	if (!po->expanded && (po->align || po->html3)) {
		if (po->html3) {
			if (po->header) {
				if (po->caption)
					fprintf(fout, "<table %s><caption align=\"top\">%s</caption>\n",
						po->tableOpt ? po->tableOpt : "", po->caption);
				else
					fprintf(fout, "<table %s><caption align=\"top\">""Retrieved %d rows * %d fields"
						"</caption>\n", po->tableOpt ? po->tableOpt : "", nTups, nFields);
			} else {
				fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
			}
		}

		if (po->header)
			border = do_header(fout, po, nFields, fieldMax, fieldNames, fieldNotNum, fs_len, res);

		for (i = 0; i < nTups; i++)
			output_row(fout, po, nFields, fields, fieldNotNum, fieldMax, border, i);

		free(fields);
		if (border)
			free(border);
	}

	if (po->header && !po->html3)
		fprintf(fout, "(%d row%s)\n\n", PQntuples(res), (PQntuples(res) == 1) ? "" : "s");

	free(fieldMax);
	free(fieldNotNum);
	free((void *)fieldNames);
	if (usePipe) {
#ifdef WIN32
		_pclose(fout);
#else
		pclose(fout);

#ifdef ENABLE_THREAD_SAFETY
		/* we can't easily verify if EPIPE occurred, so say it did */
		if (sigpipe_masked)
			pq_reset_sigpipe(&osigset, sigpipe_pending, true);
#else
		pqsignal(SIGPIPE, oldsigpipehandler);
#endif /* ENABLE_THREAD_SAFETY */
#endif /* WIN32 */
	}
	if (po->html3 && !po->expanded)
		fputs("</table>\n", fout);
}
Beispiel #27
0
/*
 * 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 and length have already been consumed.
 * Exit: returns 0 if successfully consumed message.
 *		 returns EOF if not enough data.
 */
int
pqGetErrorNotice3(PGconn *conn, bool isError)
{
	PGresult   *res = NULL;
	PQExpBufferData workBuf;
	char		id;
	const char *val;
	const char *querytext = NULL;
	int			querypos = 0;

	/*
	 * Since the fields 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.	We shouldn't use
	 * conn->errorMessage either, since this might be only a notice.
	 */
	initPQExpBuffer(&workBuf);

	/*
	 * Make a PGresult to hold the accumulated fields.	We temporarily lie
	 * about the result status, so that PQmakeEmptyPGresult doesn't uselessly
	 * copy conn->errorMessage.
	 */
	res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
	if (!res)
		goto fail;
	res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;

	/*
	 * Read the fields and save into res.
	 */
	for (;;)
	{
		if (pqGetc(&id, conn))
			goto fail;
		if (id == '\0')
			break;				/* terminator found */
		if (pqGets(&workBuf, conn))
			goto fail;
		pqSaveMessageField(res, id, workBuf.data);
	}

	/*
	 * Now build the "overall" error message for PQresultErrorMessage.
	 */
	resetPQExpBuffer(&workBuf);
	val = PQresultErrorField(res, PG_DIAG_SEVERITY);
	if (val)
		appendPQExpBuffer(&workBuf, "%s:  ", val);
	if (conn->verbosity == PQERRORS_VERBOSE)
	{
		val = PQresultErrorField(res, PG_DIAG_SQLSTATE);
		if (val)
			appendPQExpBuffer(&workBuf, "%s: ", val);
	}
	val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
	if (val)
		appendPQExpBufferStr(&workBuf, val);
	val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
	if (val)
	{
		if (conn->verbosity != PQERRORS_TERSE && conn->last_query != NULL)
		{
			/* emit position as a syntax cursor display */
			querytext = conn->last_query;
			querypos = atoi(val);
		}
		else
		{
			/* emit position as text addition to primary message */
			/* translator: %s represents a digit string */
			appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
							  val);
		}
	}
	else
	{
		val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
		if (val)
		{
			querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
			if (conn->verbosity != PQERRORS_TERSE && querytext != NULL)
			{
				/* emit position as a syntax cursor display */
				querypos = atoi(val);
			}
			else
			{
				/* emit position as text addition to primary message */
				/* translator: %s represents a digit string */
				appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
								  val);
			}
		}
	}
	appendPQExpBufferChar(&workBuf, '\n');
	if (conn->verbosity != PQERRORS_TERSE)
	{
		if (querytext && querypos > 0)
			reportErrorPosition(&workBuf, querytext, querypos,
								conn->client_encoding);
		val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
		if (val)
			appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL:  %s\n"), val);
		val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
		if (val)
			appendPQExpBuffer(&workBuf, libpq_gettext("HINT:  %s\n"), val);
		val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
		if (val)
			appendPQExpBuffer(&workBuf, libpq_gettext("QUERY:  %s\n"), val);
		val = PQresultErrorField(res, PG_DIAG_CONTEXT);
		if (val)
			appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT:  %s\n"), val);
	}
	if (conn->verbosity == PQERRORS_VERBOSE)
	{
		const char *valf;
		const char *vall;

		valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE);
		vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE);
		val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION);
		if (val || valf || vall)
		{
			appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION:  "));
			if (val)
				appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val);
			if (valf && vall)	/* unlikely we'd have just one */
				appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"),
								  valf, vall);
			appendPQExpBufferChar(&workBuf, '\n');
		}
	}

	/*
	 * Either save error as current async result, or just emit the notice.
	 */
	if (isError)
	{
		res->errMsg = pqResultStrdup(res, workBuf.data);
		if (!res->errMsg)
			goto fail;
		pqClearAsyncResult(conn);
		conn->result = res;
		appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
	}
	else
	{
		/* We can cheat a little here and not copy the message. */
		res->errMsg = workBuf.data;
		if (res->noticeHooks.noticeRec != NULL)
			(*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
		PQclear(res);
	}

	termPQExpBuffer(&workBuf);
	return 0;

fail:
	PQclear(res);
	termPQExpBuffer(&workBuf);
	return EOF;
}
Beispiel #28
0
/*
 * pg_krb5_sendauth -- client routine to send authentication information to
 *					   the server
 */
static int
pg_krb5_sendauth(PGconn *conn)
{
	krb5_error_code retval;
	int			ret;
	krb5_principal server;
	krb5_auth_context auth_context = NULL;
	krb5_error *err_ret = NULL;
	struct krb5_info info;

	info.pg_krb5_initialised = 0;

	if (!(conn->pghost && conn->pghost[0] != '\0'))
	{
		printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("host name must be specified\n"));
		return STATUS_ERROR;
	}

	ret = pg_krb5_init(&conn->errorMessage, &info);
	if (ret != STATUS_OK)
		return ret;

	retval = krb5_sname_to_principal(info.pg_krb5_context, conn->pghost,
									 conn->krbsrvname,
									 KRB5_NT_SRV_HST, &server);
	if (retval)
	{
		printfPQExpBuffer(&conn->errorMessage,
						  "pg_krb5_sendauth: krb5_sname_to_principal: %s\n",
						  error_message(retval));
		pg_krb5_destroy(&info);
		return STATUS_ERROR;
	}

	/*
	 * libpq uses a non-blocking socket. But kerberos needs a blocking socket,
	 * and we have to block somehow to do mutual authentication anyway. So we
	 * temporarily make it blocking.
	 */
	if (!pg_set_block(conn->sock))
	{
		char		sebuf[256];

		printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf)));
		krb5_free_principal(info.pg_krb5_context, server);
		pg_krb5_destroy(&info);
		return STATUS_ERROR;
	}

	retval = krb5_sendauth(info.pg_krb5_context, &auth_context,
					  (krb5_pointer) & conn->sock, (char *) conn->krbsrvname,
						   info.pg_krb5_client, server,
						   AP_OPTS_MUTUAL_REQUIRED,
						   NULL, 0,		/* no creds, use ccache instead */
						   info.pg_krb5_ccache, &err_ret, NULL, NULL);
	if (retval)
	{
		if (retval == KRB5_SENDAUTH_REJECTED && err_ret)
		{
#if defined(HAVE_KRB5_ERROR_TEXT_DATA)
			printfPQExpBuffer(&conn->errorMessage,
				  libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
							  (int) err_ret->text.length, err_ret->text.data);
#elif defined(HAVE_KRB5_ERROR_E_DATA)
			printfPQExpBuffer(&conn->errorMessage,
				  libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
							  (int) err_ret->e_data->length,
							  (const char *) err_ret->e_data->data);
#else
#error "bogus configuration"
#endif
		}
		else
		{
			printfPQExpBuffer(&conn->errorMessage,
							  "krb5_sendauth: %s\n", error_message(retval));
		}

		if (err_ret)
			krb5_free_error(info.pg_krb5_context, err_ret);

		ret = STATUS_ERROR;
	}

	krb5_free_principal(info.pg_krb5_context, server);

	if (!pg_set_noblock(conn->sock))
	{
		char		sebuf[256];

		printfPQExpBuffer(&conn->errorMessage,
		 libpq_gettext("could not restore nonblocking mode on socket: %s\n"),
						  pqStrerror(errno, sebuf, sizeof(sebuf)));
		ret = STATUS_ERROR;
	}
	pg_krb5_destroy(&info);

	return ret;
}
Beispiel #29
0
/*
 *	Read data from a secure connection.
 */
ssize_t
pqsecure_read(PGconn *conn, void *ptr, size_t len)
{
	ssize_t		n;

#ifdef USE_SSL
	if (conn->ssl)
	{
		int			err;

		DECLARE_SIGPIPE_INFO(spinfo);

		/* SSL_read can write to the socket, so we need to disable SIGPIPE */
		DISABLE_SIGPIPE(conn, spinfo, return -1);

rloop:
		SOCK_ERRNO_SET(0);
		n = SSL_read(conn->ssl, ptr, len);
		err = SSL_get_error(conn->ssl, n);
		switch (err)
		{
			case SSL_ERROR_NONE:
				break;
			case SSL_ERROR_WANT_READ:
				n = 0;
				break;
			case SSL_ERROR_WANT_WRITE:

				/*
				 * Returning 0 here would cause caller to wait for read-ready,
				 * which is not correct since what SSL wants is wait for
				 * write-ready.  The former could get us stuck in an infinite
				 * wait, so don't risk it; busy-loop instead.
				 */
				goto rloop;
			case SSL_ERROR_SYSCALL:
				{
					char		sebuf[256];

					if (n == -1)
					{
						REMEMBER_EPIPE(spinfo, SOCK_ERRNO == EPIPE);
						printfPQExpBuffer(&conn->errorMessage,
									libpq_gettext("SSL SYSCALL error: %s\n"),
							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
					}
					else
					{
						printfPQExpBuffer(&conn->errorMessage,
						 libpq_gettext("SSL SYSCALL error: EOF detected\n"));

						SOCK_ERRNO_SET(ECONNRESET);
						n = -1;
					}
					break;
				}
			case SSL_ERROR_SSL:
				{
					char	   *err = SSLerrmessage();

					printfPQExpBuffer(&conn->errorMessage,
									  libpq_gettext("SSL error: %s\n"), err);
					SSLerrfree(err);
				}
				/* fall through */
			case SSL_ERROR_ZERO_RETURN:
				SOCK_ERRNO_SET(ECONNRESET);
				n = -1;
				break;
			default:
				printfPQExpBuffer(&conn->errorMessage,
						  libpq_gettext("unrecognized SSL error code: %d\n"),
								  err);
				n = -1;
				break;
		}

		RESTORE_SIGPIPE(conn, spinfo);
	}
Beispiel #30
0
/*
 * 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;
}