libpq can now talk to either 3.0 or 2.0 protocol servers. It first tries

protocol 3, then falls back to 2 if postmaster rejects the startup packet
with an old-format error message.  A side benefit of the rewrite is that
SSL-encrypted connections can now be made without blocking.  (I think,
anyway, but do not have a good way to test.)
This commit is contained in:
Tom Lane 2003-06-08 17:43:00 +00:00
parent 152ce7a490
commit 6bdb7aa4db
13 changed files with 3334 additions and 1554 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/libpq/ip.c,v 1.7 2003/04/22 03:52:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/ip.c,v 1.8 2003/06/08 17:42:59 tgl Exp $
*
* This file and the IPV6 implementation were initially provided by
* Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
@ -72,27 +72,29 @@ getaddrinfo2(const char *hostname, const char *servname,
/*
* freeaddrinfo2 - free IPv6 addrinfo structures
* freeaddrinfo2 - free addrinfo structures for IPv4, IPv6, or Unix
*/
void
freeaddrinfo2(int hint_ai_family, struct addrinfo *ai)
freeaddrinfo2(struct addrinfo *ai)
{
#ifdef HAVE_UNIX_SOCKETS
if (hint_ai_family == AF_UNIX)
if (ai != NULL)
{
struct addrinfo *p;
while (ai != NULL)
#ifdef HAVE_UNIX_SOCKETS
if (ai->ai_family == AF_UNIX)
{
p = ai;
ai = ai->ai_next;
free(p->ai_addr);
free(p);
while (ai != NULL)
{
struct addrinfo *p = ai;
ai = ai->ai_next;
free(p->ai_addr);
free(p);
}
}
}
else
else
#endif /* HAVE_UNIX_SOCKETS */
freeaddrinfo(ai);
freeaddrinfo(ai);
}
}

View File

@ -30,7 +30,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.154 2003/05/29 19:15:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.155 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -242,8 +242,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
elog(LOG, "server socket failure: getaddrinfo2(): %s",
gai_strerror(ret));
if (addrs != NULL)
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
@ -251,7 +250,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
elog(LOG, "server socket failure: socket(): %s",
strerror(errno));
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
@ -262,7 +261,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
elog(LOG, "server socket failure: setsockopt(SO_REUSEADDR): %s",
strerror(errno));
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
}
@ -279,7 +278,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
sock_path);
else
elog(LOG, "\tIf not, wait a few seconds and retry.");
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
@ -288,7 +287,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
if (Setup_AF_UNIX() != STATUS_OK)
{
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
}
@ -308,12 +307,12 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
{
elog(LOG, "server socket failure: listen(): %s",
strerror(errno));
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_ERROR;
}
*fdP = fd;
freeaddrinfo2(hint.ai_family, addrs);
freeaddrinfo2(addrs);
return STATUS_OK;
}

View File

@ -5,7 +5,7 @@
*
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* $Id: ip.h,v 1.3 2003/04/02 00:49:28 tgl Exp $
* $Id: ip.h,v 1.4 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,7 +19,7 @@
extern int getaddrinfo2(const char *hostname, const char *servname,
const struct addrinfo *hintp,
struct addrinfo **result);
extern void freeaddrinfo2(int hint_ai_family, struct addrinfo *ai);
extern void freeaddrinfo2(struct addrinfo *ai);
extern char *SockAddr_ntop(const SockAddr *sa, char *dst, size_t cnt,
int v4conv);

View File

@ -4,7 +4,7 @@
#
# Copyright (c) 1994, Regents of the University of California
#
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.79 2003/05/10 02:05:50 momjian Exp $
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.80 2003/06/08 17:43:00 tgl Exp $
#
#-------------------------------------------------------------------------
@ -21,7 +21,7 @@ SO_MINOR_VERSION= 1
override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"'
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
pqexpbuffer.o pqsignal.o fe-secure.o \
fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
dllist.o md5.o ip.o wchar.o encnames.o \
$(filter crypt.o getaddrinfo.o inet_aton.o snprintf.o strerror.o path.o, $(LIBOBJS))

View File

@ -10,7 +10,7 @@
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.78 2003/05/16 04:58:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.79 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -559,7 +559,11 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
default:
return STATUS_ERROR;
}
ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
/* 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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.91 2003/04/25 01:24:00 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.92 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -58,8 +58,6 @@
#include "pqsignal.h"
#include "mb/pg_wchar.h"
#define DONOTICE(conn,message) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
static int pqSendSome(PGconn *conn, int len);
@ -227,7 +225,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("integer of size %lu not supported by pqGetInt\n"),
(unsigned long) bytes);
DONOTICE(conn, noticeBuf);
PGDONOTICE(conn, noticeBuf);
return EOF;
}
@ -265,7 +263,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
snprintf(noticeBuf, sizeof(noticeBuf),
libpq_gettext("integer of size %lu not supported by pqPutInt\n"),
(unsigned long) bytes);
DONOTICE(conn, noticeBuf);
PGDONOTICE(conn, noticeBuf);
return EOF;
}
@ -401,38 +399,57 @@ pqCheckInBufferSpace(int bytes_needed, PGconn *conn)
* msg_type is the message type byte, or 0 for a message without type byte
* (only startup messages have no type byte)
*
* force_len forces the message to have a length word; otherwise, we add
* a length word if protocol 3.
*
* Returns 0 on success, EOF on error
*
* The idea here is that we construct the message in conn->outBuffer,
* beginning just past any data already in outBuffer (ie, at
* outBuffer+outCount). We enlarge the buffer as needed to hold the message.
* When the message is complete, we fill in the length word and then advance
* outCount past the message, making it eligible to send. The state
* variable conn->outMsgStart points to the incomplete message's length word
* (it is either outCount or outCount+1 depending on whether there is a
* type byte). The state variable conn->outMsgEnd is the end of the data
* collected so far.
* When the message is complete, we fill in the length word (if needed) and
* then advance outCount past the message, making it eligible to send.
*
* The state variable conn->outMsgStart points to the incomplete message's
* length word: it is either outCount or outCount+1 depending on whether
* there is a type byte. If we are sending a message without length word
* (pre protocol 3.0 only), then outMsgStart is -1. The state variable
* conn->outMsgEnd is the end of the data collected so far.
*/
int
pqPutMsgStart(char msg_type, PGconn *conn)
pqPutMsgStart(char msg_type, bool force_len, PGconn *conn)
{
int lenPos;
int endPos;
/* where the message length word will go */
/* allow room for message type byte */
if (msg_type)
lenPos = conn->outCount + 1;
endPos = conn->outCount + 1;
else
lenPos = conn->outCount;
/* make sure there is room for it */
if (pqCheckOutBufferSpace(lenPos + 4, conn))
endPos = conn->outCount;
/* do we want a length word? */
if (force_len || PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
{
lenPos = endPos;
/* allow room for message length */
endPos += 4;
}
else
{
lenPos = -1;
}
/* make sure there is room for message header */
if (pqCheckOutBufferSpace(endPos, conn))
return EOF;
/* okay, save the message type byte if any */
if (msg_type)
conn->outBuffer[conn->outCount] = msg_type;
/* set up the message pointers */
conn->outMsgStart = lenPos;
conn->outMsgEnd = lenPos + 4;
/* length word will be filled in by pqPutMsgEnd */
conn->outMsgEnd = endPos;
/* length word, if needed, will be filled in by pqPutMsgEnd */
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> Msg %c\n",
@ -472,14 +489,20 @@ pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
int
pqPutMsgEnd(PGconn *conn)
{
uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
msgLen);
conn->outMsgEnd - conn->outCount);
msgLen = htonl(msgLen);
memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
/* Fill in length word if needed */
if (conn->outMsgStart >= 0)
{
uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
msgLen = htonl(msgLen);
memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
}
/* Make message eligible to send */
conn->outCount = conn->outMsgEnd;
if (conn->outCount >= 8192)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.22 2003/04/10 23:03:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.23 2003/06/08 17:43:00 tgl Exp $
*
* NOTES
* The client *requires* a valid server certificate. Since
@ -132,7 +132,7 @@ static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int client_cert_cb(SSL *, X509 **, EVP_PKEY **);
static int initialize_SSL(PGconn *);
static void destroy_SSL(void);
static int open_client_SSL(PGconn *);
static PostgresPollingStatusType open_client_SSL(PGconn *);
static void close_SSL(PGconn *);
static const char *SSLerrmessage(void);
#endif
@ -231,16 +231,30 @@ pqsecure_destroy(void)
/*
* Attempt to negotiate secure session.
*/
int
PostgresPollingStatusType
pqsecure_open_client(PGconn *conn)
{
int r = 0;
#ifdef USE_SSL
r = open_client_SSL(conn);
/* First time through? */
if (conn->ssl == NULL)
{
if (!(conn->ssl = SSL_new(SSL_context)) ||
!SSL_set_app_data(conn->ssl, conn) ||
!SSL_set_fd(conn->ssl, conn->sock))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not establish SSL connection: %s\n"),
SSLerrmessage());
close_SSL(conn);
return PGRES_POLLING_FAILED;
}
}
/* Begin or continue the actual handshake */
return open_client_SSL(conn);
#else
/* shouldn't get here */
return PGRES_POLLING_FAILED;
#endif
return r;
}
/*
@ -273,8 +287,15 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
n = 0;
break;
case SSL_ERROR_WANT_WRITE:
/* XXX to support nonblock I/O, we should return 0 here */
/*
* 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:
if (n == -1)
@ -322,16 +343,22 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
#ifdef USE_SSL
if (conn->ssl)
{
wloop:
n = SSL_write(conn->ssl, ptr, len);
switch (SSL_get_error(conn->ssl, n))
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
/*
* Returning 0 here causes caller to wait for write-ready,
* which is not really the right thing, but it's the best
* we can do.
*/
n = 0;
break;
case SSL_ERROR_WANT_WRITE:
/* XXX to support nonblock I/O, we should return 0 here */
goto wloop;
n = 0;
break;
case SSL_ERROR_SYSCALL:
if (n == -1)
printfPQExpBuffer(&conn->errorMessage,
@ -802,23 +829,45 @@ destroy_SSL(void)
/*
* Attempt to negotiate SSL connection.
*/
static int
static PostgresPollingStatusType
open_client_SSL(PGconn *conn)
{
#ifdef NOT_USED
int r;
#endif
if (!(conn->ssl = SSL_new(SSL_context)) ||
!SSL_set_app_data(conn->ssl, conn) ||
!SSL_set_fd(conn->ssl, conn->sock) ||
SSL_connect(conn->ssl) <= 0)
r = SSL_connect(conn->ssl);
if (r <= 0)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not establish SSL connection: %s\n"),
SSLerrmessage());
close_SSL(conn);
return -1;
switch (SSL_get_error(conn->ssl, r))
{
case SSL_ERROR_WANT_READ:
return PGRES_POLLING_READING;
case SSL_ERROR_WANT_WRITE:
return PGRES_POLLING_WRITING;
case SSL_ERROR_SYSCALL:
if (r == -1)
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL SYSCALL error: %s\n"),
SOCK_STRERROR(SOCK_ERRNO));
else
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL SYSCALL error: EOF detected\n"));
close_SSL(conn);
return PGRES_POLLING_FAILED;
case SSL_ERROR_SSL:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL error: %s\n"), SSLerrmessage());
close_SSL(conn);
return PGRES_POLLING_FAILED;
default:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("Unknown SSL error code\n"));
close_SSL(conn);
return PGRES_POLLING_FAILED;
}
}
/* check the certificate chain of the server */
@ -836,7 +885,7 @@ open_client_SSL(PGconn *conn)
libpq_gettext("certificate could not be validated: %s\n"),
X509_verify_cert_error_string(r));
close_SSL(conn);
return -1;
return PGRES_POLLING_FAILED;
}
#endif
@ -848,7 +897,7 @@ open_client_SSL(PGconn *conn)
libpq_gettext("certificate could not be obtained: %s\n"),
SSLerrmessage());
close_SSL(conn);
return -1;
return PGRES_POLLING_FAILED;
}
X509_NAME_oneline(X509_get_subject_name(conn->peer),
@ -871,11 +920,12 @@ open_client_SSL(PGconn *conn)
if (verify_peer(conn) == -1)
{
close_SSL(conn);
return -1;
return PGRES_POLLING_FAILED;
}
#endif
return 0;
/* SSL handshake is complete */
return PGRES_POLLING_OK;
}
/*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-fe.h,v 1.92 2003/04/19 00:02:30 tgl Exp $
* $Id: libpq-fe.h,v 1.93 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -38,9 +38,9 @@ extern "C"
typedef enum
{
/*
* Although you may decide to change this list in some way, values
* which become unused should never be removed, nor should constants
* be redefined - that would break compatibility with existing code.
* Although it is okay to add to this list, values which become unused
* should never be removed, nor should constants be redefined - that would
* break compatibility with existing code.
*/
CONNECTION_OK,
CONNECTION_BAD,
@ -56,7 +56,9 @@ typedef enum
* postmaster. */
CONNECTION_AUTH_OK, /* Received authentication; waiting for
* backend startup. */
CONNECTION_SETENV /* Negotiating environment. */
CONNECTION_SETENV, /* Negotiating environment. */
CONNECTION_SSL_STARTUP, /* Negotiating SSL. */
CONNECTION_NEEDED /* Internal state: connect() needed */
} ConnStatusType;
typedef enum
@ -71,7 +73,7 @@ typedef enum
typedef enum
{
PGRES_EMPTY_QUERY = 0,
PGRES_EMPTY_QUERY = 0, /* empty query string was executed */
PGRES_COMMAND_OK, /* a query command that doesn't return
* anything was executed properly by the
* backend */
@ -82,8 +84,8 @@ typedef enum
PGRES_COPY_IN, /* Copy In data transfer in progress */
PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from
* the backend */
PGRES_NONFATAL_ERROR,
PGRES_FATAL_ERROR
PGRES_NONFATAL_ERROR, /* notice or warning message */
PGRES_FATAL_ERROR /* query failed */
} ExecStatusType;
/* PGconn encapsulates a connection to the backend.

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-int.h,v 1.70 2003/05/08 18:33:39 tgl Exp $
* $Id: libpq-int.h,v 1.71 2003/06/08 17:43:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,6 +35,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast
#include "postgres_fe.h"
/* include stuff common to fe and be */
#include "getaddrinfo.h"
#include "libpq/pqcomm.h"
#include "lib/dllist.h"
/* include stuff found in fe only */
@ -45,23 +46,9 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast
#include <openssl/err.h>
#endif
/* libpq supports this version of the frontend/backend protocol.
*
* NB: we used to use PG_PROTOCOL_LATEST from the backend pqcomm.h file,
* but that's not really the right thing: just recompiling libpq
* against a more recent backend isn't going to magically update it
* for most sorts of protocol changes. So, when you change libpq
* to support a different protocol revision, you have to change this
* constant too. PG_PROTOCOL_EARLIEST and PG_PROTOCOL_LATEST in
* pqcomm.h describe what the backend knows, not what libpq knows.
*/
#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,0)
/*
* POSTGRES backend dependent Constants.
*/
#define PQERRORMSG_LENGTH 1024
#define CMDSTATUS_LEN 40
@ -96,20 +83,22 @@ typedef struct pgresAttDesc
int atttypmod; /* type-specific modifier info */
} PGresAttDesc;
/* Data for a single attribute of a single tuple */
/* We use char* for Attribute values.
The value pointer always points to a null-terminated area; we add a
null (zero) byte after whatever the backend sends us. This is only
particularly useful for text tuples ... with a binary value, the
value might have embedded nulls, so the application can't use C string
operators on it. But we add a null anyway for consistency.
Note that the value itself does not contain a length word.
A NULL attribute is a special case in two ways: its len field is NULL_LEN
and its value field points to null_field in the owning PGresult. All the
NULL attributes in a query result point to the same place (there's no need
to store a null string separately for each one).
/*
* Data for a single attribute of a single tuple
*
* We use char* for Attribute values.
*
* The value pointer always points to a null-terminated area; we add a
* null (zero) byte after whatever the backend sends us. This is only
* particularly useful for text tuples ... with a binary value, the
* value might have embedded nulls, so the application can't use C string
* operators on it. But we add a null anyway for consistency.
* Note that the value itself does not contain a length word.
*
* A NULL attribute is a special case in two ways: its len field is NULL_LEN
* and its value field points to null_field in the owning PGresult. All the
* NULL attributes in a query result point to the same place (there's no need
* to store a null string separately for each one).
*/
#define NULL_LEN (-1) /* pg_result len for NULL value */
@ -135,15 +124,6 @@ struct pg_result
int binary; /* binary tuple values if binary == 1,
* otherwise text */
/*
* The conn link in PGresult is no longer used by any libpq code. It
* should be removed entirely, because it could be a dangling link
* (the application could keep the PGresult around longer than it
* keeps the PGconn!) But there may be apps out there that depend on
* it, so we will leave it here at least for a release or so.
*/
PGconn *xconn; /* connection we did the query on, if any */
/*
* These fields are copied from the originating PGconn, so that
* operations on the PGresult don't have to reference the PGconn.
@ -194,6 +174,35 @@ typedef enum
PGASYNC_COPY_OUT /* Copy Out data transfer in progress */
} PGAsyncStatusType;
/* PGSetenvStatusType defines the state of the PQSetenv state machine */
/* (this is used only for 2.0-protocol connections) */
typedef enum
{
SETENV_STATE_OPTION_SEND, /* About to send an Environment Option */
SETENV_STATE_OPTION_WAIT, /* Waiting for above send to complete */
SETENV_STATE_QUERY1_SEND, /* About to send a status query */
SETENV_STATE_QUERY1_WAIT, /* Waiting for query to complete */
SETENV_STATE_QUERY2_SEND, /* About to send a status query */
SETENV_STATE_QUERY2_WAIT, /* Waiting for query to complete */
SETENV_STATE_IDLE
} PGSetenvStatusType;
/* Typedef for the EnvironmentOptions[] array */
typedef struct PQEnvironmentOption
{
const char *envName, /* name of an environment variable */
*pgName; /* name of corresponding SET variable */
} PQEnvironmentOption;
/* Typedef for parameter-status list entries */
typedef struct pgParameterStatus
{
struct pgParameterStatus *next; /* list link */
char *name; /* parameter name */
char *value; /* parameter value */
/* Note: name and value are stored in same malloc block as struct is */
} pgParameterStatus;
/* large-object-access data ... allocated only if large-object code is used. */
typedef struct pgLobjfuncs
{
@ -207,7 +216,8 @@ typedef struct pgLobjfuncs
Oid fn_lo_write; /* OID of backend function LOwrite */
} PGlobjfuncs;
/* PGconn stores all the state data associated with a single connection
/*
* PGconn stores all the state data associated with a single connection
* to a backend.
*/
struct pg_conn
@ -254,12 +264,21 @@ struct pg_conn
SockAddr laddr; /* Local address */
SockAddr raddr; /* Remote address */
int raddr_len; /* Length of remote address */
ProtocolVersion pversion; /* FE/BE protocol version in use */
char sversion[8]; /* The first few bytes of server version */
/* Transient state needed while establishing connection */
struct addrinfo *addrlist; /* list of possible backend addresses */
struct addrinfo *addr_cur; /* the one currently being tried */
PGSetenvStatusType setenv_state; /* for 2.0 protocol only */
const PQEnvironmentOption *next_eo;
/* Miscellaneous stuff */
int be_pid; /* PID of backend --- needed for cancels */
int be_key; /* key of backend --- needed for cancels */
char md5Salt[4]; /* password salt received from backend */
char cryptSalt[2]; /* password salt received from backend */
pgParameterStatus *pstatus; /* ParameterStatus data */
int client_encoding; /* encoding id */
PGlobjfuncs *lobjfuncs; /* private state for large-object access
* fns */
@ -279,7 +298,8 @@ struct pg_conn
int outCount; /* number of chars waiting in buffer */
/* State for constructing messages in outBuffer */
int outMsgStart; /* offset to msg start (length word) */
int outMsgStart; /* offset to msg start (length word);
* if -1, msg has no length word */
int outMsgEnd; /* offset to msg end (so far) */
/* Status for asynchronous result construction */
@ -324,10 +344,46 @@ extern int pqPacketSend(PGconn *conn, char pack_type,
/* === in fe-exec.c === */
extern void pqSetResultError(PGresult *res, const char *msg);
extern void pqCatenateResultError(PGresult *res, const char *msg);
extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
extern char *pqResultStrdup(PGresult *res, const char *str);
extern void pqClearAsyncResult(PGconn *conn);
extern int pqGetErrorNotice(PGconn *conn, bool isError);
extern void pqSaveErrorResult(PGconn *conn);
extern PGresult *pqPrepareAsyncResult(PGconn *conn);
extern int pqAddTuple(PGresult *res, PGresAttValue *tup);
extern void pqSaveParameterStatus(PGconn *conn, const char *name,
const char *value);
extern const char *pqGetParameterStatus(PGconn *conn, const char *name);
extern void pqHandleSendFailure(PGconn *conn);
/* === in fe-protocol2.c === */
extern PostgresPollingStatusType pqSetenvPoll(PGconn *conn);
extern char *pqBuildStartupPacket2(PGconn *conn, int *packetlen,
const PQEnvironmentOption *options);
extern void pqParseInput2(PGconn *conn);
extern int pqGetline2(PGconn *conn, char *s, int maxlen);
extern int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize);
extern int pqEndcopy2(PGconn *conn);
extern PGresult *pqFunctionCall2(PGconn *conn, Oid fnid,
int *result_buf, int *actual_result_len,
int result_is_int,
const PQArgBlock *args, int nargs);
/* === in fe-protocol3.c === */
extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen,
const PQEnvironmentOption *options);
extern void pqParseInput3(PGconn *conn);
extern int pqGetErrorNotice3(PGconn *conn, bool isError);
extern int pqGetline3(PGconn *conn, char *s, int maxlen);
extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
extern int pqEndcopy3(PGconn *conn);
extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid,
int *result_buf, int *actual_result_len,
int result_is_int,
const PQArgBlock *args, int nargs);
/* === in fe-misc.c === */
@ -345,7 +401,7 @@ extern int pqGetnchar(char *s, size_t len, PGconn *conn);
extern int pqPutnchar(const char *s, size_t len, PGconn *conn);
extern int pqGetInt(int *result, size_t bytes, PGconn *conn);
extern int pqPutInt(int value, size_t bytes, PGconn *conn);
extern int pqPutMsgStart(char msg_type, PGconn *conn);
extern int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn);
extern int pqPutMsgEnd(PGconn *conn);
extern int pqReadData(PGconn *conn);
extern int pqFlush(PGconn *conn);
@ -359,21 +415,14 @@ extern int pqWriteReady(PGconn *conn);
extern int pqsecure_initialize(PGconn *);
extern void pqsecure_destroy(void);
extern int pqsecure_open_client(PGconn *);
extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
extern void pqsecure_close(PGconn *);
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
/* bits in a byte */
#define BYTELEN 8
/* fall back options if they are not specified by arguments or defined
by environment variables */
#define DefaultHost "localhost"
#define DefaultTty ""
#define DefaultOption ""
#define DefaultAuthtype ""
#define DefaultPassword ""
/* Note: PGDONOTICE macro will work if applied to either PGconn or PGresult */
#define PGDONOTICE(conn,message) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
/*
* this is so that we can check is a connection is non-blocking internally