fe-misc.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:14k
- /*-------------------------------------------------------------------------
- *
- * FILE
- * fe-misc.c
- *
- * DESCRIPTION
- * miscellaneous useful functions
- *
- * The communication routines here are analogous to the ones in
- * backend/libpq/pqcomm.c and backend/libpq/pqcomprim.c, but operate
- * in the considerably different environment of the frontend libpq.
- * In particular, we work with a bare nonblock-mode socket, rather than
- * a stdio stream, so that we can avoid unwanted blocking of the application.
- *
- * XXX: MOVE DEBUG PRINTOUT TO HIGHER LEVEL. As is, block and restart
- * will cause repeat printouts.
- *
- * We must speak the same transmitted data representations as the backend
- * routines. Note that this module supports *only* network byte order
- * for transmitted ints, whereas the backend modules (as of this writing)
- * still handle either network or little-endian byte order.
- *
- * Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /usr/local/cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.25 1999/05/28 01:54:53 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
- #include "libpq-fe.h"
- #include "libpq-int.h"
- #include "postgres.h"
- #include "pqsignal.h"
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <signal.h>
- #include <time.h>
- #ifdef WIN32
- #include "win32.h"
- #else
- #include <sys/time.h>
- #if !defined(NO_UNISTD_H)
- #include <unistd.h>
- #endif
- #endif /* WIN32 */
- #ifdef HAVE_SYS_SELECT_H
- #include <sys/select.h>
- #endif
- #define DONOTICE(conn,message)
- ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
- /* --------------------------------------------------------------------- */
- /* pqGetc:
- get a character from the connection
- All these routines return 0 on success, EOF on error.
- Note that for the Get routines, EOF only means there is not enough
- data in the buffer, not that there is necessarily a hard error.
- */
- int
- pqGetc(char *result, PGconn *conn)
- {
- if (conn->inCursor >= conn->inEnd)
- return EOF;
- *result = conn->inBuffer[conn->inCursor++];
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend> %cn", *result);
- return 0;
- }
- /* --------------------------------------------------------------------- */
- /* pqPutBytes: local routine to write N bytes to the connection,
- with buffering
- */
- static int
- pqPutBytes(const char *s, int nbytes, PGconn *conn)
- {
- int avail = conn->outBufSize - conn->outCount;
- while (nbytes > avail)
- {
- memcpy(conn->outBuffer + conn->outCount, s, avail);
- conn->outCount += avail;
- s += avail;
- nbytes -= avail;
- if (pqFlush(conn))
- return EOF;
- avail = conn->outBufSize;
- }
- memcpy(conn->outBuffer + conn->outCount, s, nbytes);
- conn->outCount += nbytes;
- return 0;
- }
- /* --------------------------------------------------------------------- */
- /* pqGets:
- get a null-terminated string from the connection,
- and store it in a buffer of size maxlen bytes.
- If the incoming string is >= maxlen bytes, all of it is read,
- but the excess characters are silently discarded.
- */
- int
- pqGets(char *s, int maxlen, PGconn *conn)
- {
- /* Copy conn data to locals for faster search loop */
- char *inBuffer = conn->inBuffer;
- int inCursor = conn->inCursor;
- int inEnd = conn->inEnd;
- int slen;
- while (inCursor < inEnd && inBuffer[inCursor])
- inCursor++;
- if (inCursor >= inEnd)
- return EOF;
- slen = inCursor - conn->inCursor;
- if (slen < maxlen)
- strcpy(s, inBuffer + conn->inCursor);
- else
- {
- strncpy(s, inBuffer + conn->inCursor, maxlen - 1);
- s[maxlen - 1] = ' ';
- }
- conn->inCursor = ++inCursor;
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend> "%s"n", s);
- return 0;
- }
- /* --------------------------------------------------------------------- */
- int
- pqPuts(const char *s, PGconn *conn)
- {
- if (pqPutBytes(s, strlen(s) + 1, conn))
- return EOF;
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> %sn", s);
- return 0;
- }
- /* --------------------------------------------------------------------- */
- /* pqGetnchar:
- get a string of exactly len bytes in buffer s, no null termination
- */
- int
- pqGetnchar(char *s, int len, PGconn *conn)
- {
- if (len < 0 || len > conn->inEnd - conn->inCursor)
- return EOF;
- memcpy(s, conn->inBuffer + conn->inCursor, len);
- /* no terminating null */
- conn->inCursor += len;
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend (%d)> %.*sn", len, len, s);
- return 0;
- }
- /* --------------------------------------------------------------------- */
- /* pqPutnchar:
- send a string of exactly len bytes, no null termination needed
- */
- int
- pqPutnchar(const char *s, int len, PGconn *conn)
- {
- if (pqPutBytes(s, len, conn))
- return EOF;
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> %.*sn", len, s);
- return 0;
- }
- /* --------------------------------------------------------------------- */
- /* pgGetInt
- read a 2 or 4 byte integer and convert from network byte order
- to local byte order
- */
- int
- pqGetInt(int *result, int bytes, PGconn *conn)
- {
- uint16 tmp2;
- uint32 tmp4;
- switch (bytes)
- {
- case 2:
- if (conn->inCursor + 2 > conn->inEnd)
- return EOF;
- memcpy(&tmp2, conn->inBuffer + conn->inCursor, 2);
- conn->inCursor += 2;
- *result = (int) ntohs(tmp2);
- break;
- case 4:
- if (conn->inCursor + 4 > conn->inEnd)
- return EOF;
- memcpy(&tmp4, conn->inBuffer + conn->inCursor, 4);
- conn->inCursor += 4;
- *result = (int) ntohl(tmp4);
- break;
- default:
- sprintf(conn->errorMessage,
- "pqGetInt: int size %d not supportedn", bytes);
- DONOTICE(conn, conn->errorMessage);
- return EOF;
- }
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend (#%d)> %dn", bytes, *result);
- return 0;
- }
- /* --------------------------------------------------------------------- */
- /* pgPutInt
- send an integer of 2 or 4 bytes, converting from host byte order
- to network byte order.
- */
- int
- pqPutInt(int value, int bytes, PGconn *conn)
- {
- uint16 tmp2;
- uint32 tmp4;
- switch (bytes)
- {
- case 2:
- tmp2 = htons((uint16) value);
- if (pqPutBytes((const char *) &tmp2, 2, conn))
- return EOF;
- break;
- case 4:
- tmp4 = htonl((uint32) value);
- if (pqPutBytes((const char *) &tmp4, 4, conn))
- return EOF;
- break;
- default:
- sprintf(conn->errorMessage,
- "pqPutInt: int size %d not supportedn", bytes);
- DONOTICE(conn, conn->errorMessage);
- return EOF;
- }
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend (%d#)> %dn", bytes, value);
- return 0;
- }
- /* --------------------------------------------------------------------- */
- /* pqReadReady: is select() saying the file is ready to read?
- */
- static int
- pqReadReady(PGconn *conn)
- {
- fd_set input_mask;
- struct timeval timeout;
- if (conn->sock < 0)
- return 0;
- FD_ZERO(&input_mask);
- FD_SET(conn->sock, &input_mask);
- timeout.tv_sec = 0;
- timeout.tv_usec = 0;
- if (select(conn->sock + 1, &input_mask, (fd_set *) NULL, (fd_set *) NULL,
- &timeout) < 0)
- {
- sprintf(conn->errorMessage,
- "pqReadReady() -- select() failed: errno=%dn%sn",
- errno, strerror(errno));
- return 0;
- }
- return FD_ISSET(conn->sock, &input_mask);
- }
- /* --------------------------------------------------------------------- */
- /* 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 nread;
- if (conn->sock < 0)
- {
- strcpy(conn->errorMessage, "pqReadData() -- connection not openn");
- return -1;
- }
- /* Left-justify any data in the buffer to make room */
- if (conn->inStart < conn->inEnd)
- {
- memmove(conn->inBuffer, conn->inBuffer + conn->inStart,
- conn->inEnd - conn->inStart);
- conn->inEnd -= conn->inStart;
- conn->inCursor -= conn->inStart;
- conn->inStart = 0;
- }
- else
- 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 at least one TCP packet.
- */
- if (conn->inBufSize - conn->inEnd < 2000)
- {
- int newSize = conn->inBufSize * 2;
- char *newBuf = (char *) realloc(conn->inBuffer, newSize);
- if (newBuf)
- {
- conn->inBuffer = newBuf;
- conn->inBufSize = newSize;
- }
- }
- /* OK, try to read some data */
- tryAgain:
- nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd, 0);
- if (nread < 0)
- {
- if (errno == EINTR)
- goto tryAgain;
- /* Some systems return EAGAIN/EWOULDBLOCK for no data */
- #ifdef EAGAIN
- if (errno == EAGAIN)
- return 0;
- #endif
- #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- if (errno == EWOULDBLOCK)
- return 0;
- #endif
- /* We might get ECONNRESET here if using TCP and backend died */
- #ifdef ECONNRESET
- if (errno == ECONNRESET)
- goto definitelyFailed;
- #endif
- sprintf(conn->errorMessage,
- "pqReadData() -- read() failed: errno=%dn%sn",
- errno, strerror(errno));
- return -1;
- }
- if (nread > 0)
- {
- conn->inEnd += nread;
- return 1;
- }
- /*
- * 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.
- */
- if (!pqReadReady(conn))
- return 0; /* definitely no data available */
- /*
- * Still not sure that it's EOF, because some data could have just
- * arrived.
- */
- tryAgain2:
- nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd, 0);
- if (nread < 0)
- {
- if (errno == EINTR)
- goto tryAgain2;
- /* Some systems return EAGAIN/EWOULDBLOCK for no data */
- #ifdef EAGAIN
- if (errno == EAGAIN)
- return 0;
- #endif
- #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- if (errno == EWOULDBLOCK)
- return 0;
- #endif
- /* We might get ECONNRESET here if using TCP and backend died */
- #ifdef ECONNRESET
- if (errno == ECONNRESET)
- goto definitelyFailed;
- #endif
- sprintf(conn->errorMessage,
- "pqReadData() -- read() failed: errno=%dn%sn",
- errno, strerror(errno));
- 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:
- sprintf(conn->errorMessage,
- "pqReadData() -- backend closed the channel unexpectedly.n"
- "tThis probably means the backend terminated abnormallyn"
- "tbefore or while processing the request.n");
- conn->status = CONNECTION_BAD; /* No more connection to backend */
- #ifdef WIN32
- closesocket(conn->sock);
- #else
- close(conn->sock);
- #endif
- conn->sock = -1;
- return -1;
- }
- /* --------------------------------------------------------------------- */
- /* pqFlush: send any data waiting in the output buffer
- */
- int
- pqFlush(PGconn *conn)
- {
- char *ptr = conn->outBuffer;
- int len = conn->outCount;
- if (conn->sock < 0)
- {
- strcpy(conn->errorMessage, "pqFlush() -- connection not openn");
- return EOF;
- }
- while (len > 0)
- {
- /* Prevent being SIGPIPEd if backend has closed the connection. */
- #ifndef WIN32
- pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
- #endif
- int sent = send(conn->sock, ptr, len, 0);
- #ifndef WIN32
- pqsignal(SIGPIPE, oldsighandler);
- #endif
- if (sent < 0)
- {
- /*
- * Anything except EAGAIN or EWOULDBLOCK is trouble. If it's
- * EPIPE or ECONNRESET, assume we've lost the backend
- * connection permanently.
- */
- switch (errno)
- {
- #ifdef EAGAIN
- case EAGAIN:
- break;
- #endif
- #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- case EWOULDBLOCK:
- break;
- #endif
- case EPIPE:
- #ifdef ECONNRESET
- case ECONNRESET:
- #endif
- sprintf(conn->errorMessage,
- "pqFlush() -- backend closed the channel unexpectedly.n"
- "tThis probably means the backend terminated abnormally"
- " before 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.
- */
- return EOF;
- default:
- sprintf(conn->errorMessage,
- "pqFlush() -- couldn't send data: errno=%dn%sn",
- errno, strerror(errno));
- /* We don't assume it's a fatal error... */
- return EOF;
- }
- }
- else
- {
- ptr += sent;
- len -= sent;
- }
- if (len > 0)
- {
- /* We didn't send it all, wait till we can send more */
- if (pqWait(FALSE, TRUE, conn))
- return EOF;
- }
- }
- conn->outCount = 0;
- if (conn->Pfdebug)
- fflush(conn->Pfdebug);
- return 0;
- }
- /* --------------------------------------------------------------------- */
- /* pqWait: wait until we can read or write the connection socket
- */
- int
- pqWait(int forRead, int forWrite, PGconn *conn)
- {
- fd_set input_mask;
- fd_set output_mask;
- if (conn->sock < 0)
- {
- strcpy(conn->errorMessage, "pqWait() -- connection not openn");
- return EOF;
- }
- /* loop in case select returns EINTR */
- for (;;)
- {
- FD_ZERO(&input_mask);
- FD_ZERO(&output_mask);
- if (forRead)
- FD_SET(conn->sock, &input_mask);
- if (forWrite)
- FD_SET(conn->sock, &output_mask);
- if (select(conn->sock + 1, &input_mask, &output_mask, (fd_set *) NULL,
- (struct timeval *) NULL) < 0)
- {
- if (errno == EINTR)
- continue;
- sprintf(conn->errorMessage,
- "pqWait() -- select() failed: errno=%dn%sn",
- errno, strerror(errno));
- return EOF;
- }
- /* On nonerror return, assume we're done */
- break;
- }
- return 0;
- }