mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +03:00
What I've done:
1. Rewritten libpq to allow asynchronous clients. 2. Implemented client side of cancel protocol in library, and patched psql.c to send a cancel request upon SIGINT. The backend doesn't notice it yet :-( 3. Implemented 'Z' protocol message addition and renaming of copy in/out start messages. These are implemented conditionally, ie, the client protocol version is checked; so the code should still work with 1.0 clients. 4. Revised protocol and libpq sgml documents (don't have an SGML compiler, though, so there may be some markup glitches here). What remains to be done: 1. Implement addition of atttypmod field to RowDescriptor messages. The client-side code is there but ifdef'd out. I have no idea what to change on the backend side. The field should be sent only if protocol >= 2.0, of course. 2. Implement backend response to cancel requests received as OOB messages. (This prolly need not be conditional on protocol version; just do it if you get SIGURG.) 3. Update libpq.3. (I'm hoping this can be generated mechanically from libpq.sgml... if not, will do it by hand.) Is there any other doco to fix? 4. Update non-libpq interfaces as necessary. I patched libpgtcl so that it would compile, but haven't tested it. Dunno what needs to be done with the other interfaces. Have at it! Tom Lane
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.8 1998/03/15 08:03:00 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.9 1998/05/06 23:51:00 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -48,7 +48,7 @@ int PgInputProc(DRIVER_INPUT_PROTO)
|
||||
{
|
||||
Pg_ConnectionId *connid;
|
||||
PGconn *conn;
|
||||
int c;
|
||||
char c;
|
||||
int avail;
|
||||
|
||||
connid = (Pg_ConnectionId *)cData;
|
||||
@@ -64,13 +64,24 @@ int PgInputProc(DRIVER_INPUT_PROTO)
|
||||
return PgEndCopy(connid, errorCodePtr);
|
||||
}
|
||||
|
||||
/* Try to load any newly arrived data */
|
||||
errno = 0;
|
||||
|
||||
if (pqReadData(conn) < 0) {
|
||||
*errorCodePtr = errno ? errno : EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Move data from libpq's buffer to tcl's */
|
||||
|
||||
conn->inCursor = conn->inStart;
|
||||
|
||||
avail = bufSize;
|
||||
while (avail > 0 &&
|
||||
(c = pqGetc(conn->Pfin, conn->Pfdebug)) != EOF) {
|
||||
/* fprintf(stderr, "%d: got char %c\n", bufSize-avail, c); */
|
||||
pqGetc(&c, conn) == 0) {
|
||||
*buf++ = c;
|
||||
--avail;
|
||||
if (c == '\n' && bufSize-avail > 3) {
|
||||
if (c == '\n' && bufSize-avail >= 3) {
|
||||
if ((bufSize-avail == 3 || buf[-4] == '\n') &&
|
||||
buf[-3] == '\\' && buf[-2] == '.') {
|
||||
avail += 3;
|
||||
@@ -79,6 +90,8 @@ int PgInputProc(DRIVER_INPUT_PROTO)
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Accept the data permanently */
|
||||
conn->inStart = conn->inCursor;
|
||||
/* fprintf(stderr, "returning %d chars\n", bufSize - avail); */
|
||||
return bufSize - avail;
|
||||
}
|
||||
@@ -100,16 +113,15 @@ int PgOutputProc(DRIVER_OUTPUT_PROTO)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
fprintf(stderr, "PgOutputProc called: bufSize=%d: atend:%d <", bufSize,
|
||||
strncmp(buf, "\\.\n", 3));
|
||||
fwrite(buf, 1, bufSize, stderr);
|
||||
fputs(">\n", stderr);
|
||||
*/
|
||||
fwrite(buf, 1, bufSize, conn->Pfout);
|
||||
if (bufSize > 2 && strncmp(&buf[bufSize-3], "\\.\n", 3) == 0) {
|
||||
/* fprintf(stderr,"checking closure\n"); */
|
||||
fflush(conn->Pfout);
|
||||
errno = 0;
|
||||
|
||||
if (pqPutnchar(buf, bufSize, conn)) {
|
||||
*errorCodePtr = errno ? errno : EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bufSize >= 3 && strncmp(&buf[bufSize-3], "\\.\n", 3) == 0) {
|
||||
(void) pqFlush(conn);
|
||||
if (PgEndCopy(connid, errorCodePtr) == -1)
|
||||
return -1;
|
||||
}
|
||||
@@ -156,10 +168,10 @@ PgSetConnectionId(Tcl_Interp *interp, PGconn *conn)
|
||||
for (i = 0; i < RES_START; i++) connid->results[i] = NULL;
|
||||
Tcl_InitHashTable(&connid->notify_hash, TCL_STRING_KEYS);
|
||||
|
||||
sprintf(connid->id, "pgsql%d", fileno(conn->Pfout));
|
||||
sprintf(connid->id, "pgsql%d", PQsocket(conn));
|
||||
|
||||
#if TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION == 5
|
||||
conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, conn->Pfin, conn->Pfout, (ClientData)connid);
|
||||
conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, NULL, NULL, (ClientData)connid);
|
||||
#else
|
||||
conn_chan = Tcl_CreateChannel(&Pg_ConnType, connid->id, (ClientData)connid,
|
||||
TCL_READABLE | TCL_WRITABLE);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Attic/Makefile.in,v 1.16 1998/04/27 14:55:46 scrappy Exp $
|
||||
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Attic/Makefile.in,v 1.17 1998/05/06 23:51:06 momjian Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -25,14 +25,13 @@ ifdef KRBVERS
|
||||
CFLAGS+= $(KRBFLAGS)
|
||||
endif
|
||||
|
||||
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-lobj.o \
|
||||
dllist.o pqsignal.o pqcomprim.o
|
||||
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
|
||||
dllist.o pqsignal.o
|
||||
|
||||
# Shared library stuff
|
||||
shlib :=
|
||||
install-shlib-dep :=
|
||||
ifeq ($(PORTNAME), linux)
|
||||
LINUX_ELF=@LINUX_ELF@
|
||||
ifdef LINUX_ELF
|
||||
install-shlib-dep := install-shlib
|
||||
shlib := libpq.so.$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION)
|
||||
@@ -84,9 +83,6 @@ fe-lobj.o: $(SRCDIR)/backend/fmgr.h
|
||||
dllist.c: $(SRCDIR)/backend/lib/dllist.c
|
||||
-ln -s $(SRCDIR)/backend/lib/dllist.c .
|
||||
|
||||
pqcomprim.c: $(SRCDIR)/backend/libpq/pqcomprim.c
|
||||
-ln -s $(SRCDIR)/backend/libpq/pqcomprim.c .
|
||||
|
||||
# The following rules cause dependencies in the backend directory to
|
||||
# get made if they don't exist, but don't cause them to get remade if they
|
||||
# are out of date.
|
||||
@@ -183,7 +179,7 @@ depend dep:
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f libpq.a $(shlib) $(OBJS) c.h dllist.c pqcomprim.c libpq.so
|
||||
rm -f libpq.a $(shlib) $(OBJS) c.h dllist.c libpq.so
|
||||
|
||||
ifeq (depend,$(wildcard depend))
|
||||
include depend
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.65 1998/04/21 04:00:06 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.66 1998/05/06 23:51:11 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -41,14 +41,14 @@
|
||||
#endif
|
||||
|
||||
|
||||
/* use a local version instead of the one found in pqpacket.c */
|
||||
static ConnStatusType connectDB(PGconn *conn);
|
||||
|
||||
static PGconn *makeEmptyPGconn(void);
|
||||
static void freePGconn(PGconn *conn);
|
||||
static void closePGconn(PGconn *conn);
|
||||
static int conninfo_parse(const char *conninfo, char *errorMessage);
|
||||
static char *conninfo_getval(char *keyword);
|
||||
static void conninfo_free(void);
|
||||
/* XXX Why is this not static? */
|
||||
void PQsetenv(PGconn *conn);
|
||||
|
||||
#define NOTIFYLIST_INITIAL_SIZE 10
|
||||
@@ -162,44 +162,30 @@ PGconn *
|
||||
PQconnectdb(const char *conninfo)
|
||||
{
|
||||
PGconn *conn;
|
||||
char errorMessage[ERROR_MSG_LENGTH];
|
||||
char *tmp;
|
||||
|
||||
/* ----------
|
||||
* Allocate memory for the conn structure
|
||||
* ----------
|
||||
*/
|
||||
conn = (PGconn *) malloc(sizeof(PGconn));
|
||||
conn = makeEmptyPGconn();
|
||||
if (conn == NULL)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"FATAL: PQsetdb() -- unable to allocate memory for a PGconn");
|
||||
"FATAL: PQconnectdb() -- unable to allocate memory for a PGconn");
|
||||
return (PGconn *) NULL;
|
||||
}
|
||||
MemSet((char *) conn, 0, sizeof(PGconn));
|
||||
|
||||
/* ----------
|
||||
* Parse the conninfo string and get the fallback resources
|
||||
* Parse the conninfo string and save settings in conn structure
|
||||
* ----------
|
||||
*/
|
||||
if (conninfo_parse(conninfo, errorMessage) < 0)
|
||||
if (conninfo_parse(conninfo, conn->errorMessage) < 0)
|
||||
{
|
||||
conn->status = CONNECTION_BAD;
|
||||
strcpy(conn->errorMessage, errorMessage);
|
||||
conninfo_free();
|
||||
return conn;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Setup the conn structure
|
||||
* ----------
|
||||
*/
|
||||
conn->lobjfuncs = (PGlobjfuncs *) NULL;
|
||||
conn->Pfout = NULL;
|
||||
conn->Pfin = NULL;
|
||||
conn->Pfdebug = NULL;
|
||||
conn->notifyList = DLNewList();
|
||||
|
||||
tmp = conninfo_getval("host");
|
||||
conn->pghost = tmp ? strdup(tmp) : NULL;
|
||||
tmp = conninfo_getval("port");
|
||||
@@ -208,12 +194,12 @@ PQconnectdb(const char *conninfo)
|
||||
conn->pgtty = tmp ? strdup(tmp) : NULL;
|
||||
tmp = conninfo_getval("options");
|
||||
conn->pgoptions = tmp ? strdup(tmp) : NULL;
|
||||
tmp = conninfo_getval("dbname");
|
||||
conn->dbName = tmp ? strdup(tmp) : NULL;
|
||||
tmp = conninfo_getval("user");
|
||||
conn->pguser = tmp ? strdup(tmp) : NULL;
|
||||
tmp = conninfo_getval("password");
|
||||
conn->pgpass = tmp ? strdup(tmp) : NULL;
|
||||
tmp = conninfo_getval("dbname");
|
||||
conn->dbName = tmp ? strdup(tmp) : NULL;
|
||||
|
||||
/* ----------
|
||||
* Free the connection info - all is in conn now
|
||||
@@ -226,24 +212,6 @@ PQconnectdb(const char *conninfo)
|
||||
* ----------
|
||||
*/
|
||||
conn->status = connectDB(conn);
|
||||
if (conn->status == CONNECTION_OK)
|
||||
{
|
||||
PGresult *res;
|
||||
|
||||
/*
|
||||
* Send a blank query to make sure everything works; in
|
||||
* particular, that the database exists.
|
||||
*/
|
||||
res = PQexec(conn, " ");
|
||||
if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY)
|
||||
{
|
||||
/* PQexec has put error message in conn->errorMessage */
|
||||
closePGconn(conn);
|
||||
}
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
PQsetenv(conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
@@ -311,150 +279,119 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
|
||||
{
|
||||
PGconn *conn;
|
||||
char *tmp;
|
||||
char errorMessage[ERROR_MSG_LENGTH];
|
||||
|
||||
/* An error message from some service we call. */
|
||||
bool error;
|
||||
|
||||
bool error = FALSE;
|
||||
/* We encountered an error that prevents successful completion */
|
||||
int i;
|
||||
|
||||
conn = (PGconn *) malloc(sizeof(PGconn));
|
||||
|
||||
conn = makeEmptyPGconn();
|
||||
if (conn == NULL)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"FATAL: PQsetdb() -- unable to allocate memory for a PGconn");
|
||||
"FATAL: PQsetdbLogin() -- unable to allocate memory for a PGconn");
|
||||
return (PGconn *) NULL;
|
||||
}
|
||||
|
||||
if ((pghost == NULL) || pghost[0] == '\0')
|
||||
{
|
||||
if ((tmp = getenv("PGHOST")) != NULL)
|
||||
conn->pghost = strdup(tmp);
|
||||
}
|
||||
else
|
||||
conn->pghost = strdup(pghost);
|
||||
|
||||
if ((pgport == NULL) || pgport[0] == '\0')
|
||||
{
|
||||
if ((tmp = getenv("PGPORT")) == NULL)
|
||||
tmp = DEF_PGPORT;
|
||||
conn->pgport = strdup(tmp);
|
||||
}
|
||||
else
|
||||
conn->pgport = strdup(pgport);
|
||||
|
||||
if ((pgtty == NULL) || pgtty[0] == '\0')
|
||||
{
|
||||
if ((tmp = getenv("PGTTY")) == NULL)
|
||||
tmp = DefaultTty;
|
||||
conn->pgtty = strdup(tmp);
|
||||
}
|
||||
else
|
||||
conn->pgtty = strdup(pgtty);
|
||||
|
||||
if ((pgoptions == NULL) || pgoptions[0] == '\0')
|
||||
{
|
||||
if ((tmp = getenv("PGOPTIONS")) == NULL)
|
||||
tmp = DefaultOption;
|
||||
conn->pgoptions = strdup(tmp);
|
||||
}
|
||||
else
|
||||
conn->pgoptions = strdup(pgoptions);
|
||||
|
||||
if (login)
|
||||
{
|
||||
conn->pguser = strdup(login);
|
||||
}
|
||||
else if ((tmp = getenv("PGUSER")) != NULL)
|
||||
{
|
||||
conn->pguser = strdup(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
conn->lobjfuncs = (PGlobjfuncs *) NULL;
|
||||
conn->Pfout = NULL;
|
||||
conn->Pfin = NULL;
|
||||
conn->Pfdebug = NULL;
|
||||
conn->notifyList = DLNewList();
|
||||
|
||||
if ((pghost == NULL) || pghost[0] == '\0')
|
||||
{
|
||||
conn->pghost = NULL;
|
||||
if ((tmp = getenv("PGHOST")) != NULL)
|
||||
conn->pghost = strdup(tmp);
|
||||
}
|
||||
else
|
||||
conn->pghost = strdup(pghost);
|
||||
|
||||
if ((pgport == NULL) || pgport[0] == '\0')
|
||||
{
|
||||
if ((tmp = getenv("PGPORT")) == NULL)
|
||||
tmp = DEF_PGPORT;
|
||||
conn->pgport = strdup(tmp);
|
||||
}
|
||||
else
|
||||
conn->pgport = strdup(pgport);
|
||||
|
||||
if ((pgtty == NULL) || pgtty[0] == '\0')
|
||||
{
|
||||
if ((tmp = getenv("PGTTY")) == NULL)
|
||||
tmp = DefaultTty;
|
||||
conn->pgtty = strdup(tmp);
|
||||
}
|
||||
else
|
||||
conn->pgtty = strdup(pgtty);
|
||||
|
||||
if ((pgoptions == NULL) || pgoptions[0] == '\0')
|
||||
{
|
||||
if ((tmp = getenv("PGOPTIONS")) == NULL)
|
||||
tmp = DefaultOption;
|
||||
conn->pgoptions = strdup(tmp);
|
||||
}
|
||||
else
|
||||
conn->pgoptions = strdup(pgoptions);
|
||||
|
||||
if (login)
|
||||
{
|
||||
error = FALSE;
|
||||
conn->pguser = strdup(login);
|
||||
}
|
||||
else if ((tmp = getenv("PGUSER")) != NULL)
|
||||
{
|
||||
error = FALSE;
|
||||
conn->pguser = strdup(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = fe_getauthname(errorMessage);
|
||||
if (tmp == 0)
|
||||
{
|
||||
error = TRUE;
|
||||
sprintf(conn->errorMessage,
|
||||
"FATAL: PQsetdb: Unable to determine a Postgres username!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
error = FALSE;
|
||||
conn->pguser = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (pwd)
|
||||
{
|
||||
conn->pgpass = strdup(pwd);
|
||||
}
|
||||
else if ((tmp = getenv("PGPASSWORD")) != NULL)
|
||||
{
|
||||
conn->pgpass = strdup(tmp);
|
||||
}
|
||||
else
|
||||
conn->pgpass = strdup(DefaultPassword);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
if ((((tmp = (char *) dbName) != NULL) && (dbName[0] != '\0'))
|
||||
|| ((tmp = getenv("PGDATABASE"))))
|
||||
conn->dbName = strdup(tmp);
|
||||
else
|
||||
conn->dbName = strdup(conn->pguser);
|
||||
|
||||
/*
|
||||
* if the database name is surrounded by double-quotes, then
|
||||
* don't convert case
|
||||
*/
|
||||
if (*conn->dbName == '"')
|
||||
{
|
||||
strcpy(conn->dbName, conn->dbName + 1);
|
||||
*(conn->dbName + strlen(conn->dbName) - 1) = '\0';
|
||||
}
|
||||
else
|
||||
for (i = 0; conn->dbName[i]; i++)
|
||||
if (isupper(conn->dbName[i]))
|
||||
conn->dbName[i] = tolower(conn->dbName[i]);
|
||||
}
|
||||
else
|
||||
conn->dbName = NULL;
|
||||
|
||||
if (error)
|
||||
conn->status = CONNECTION_BAD;
|
||||
else
|
||||
{
|
||||
conn->status = connectDB(conn);
|
||||
/* Puts message in conn->errorMessage */
|
||||
if (conn->status == CONNECTION_OK)
|
||||
{
|
||||
PGresult *res;
|
||||
|
||||
/*
|
||||
* Send a blank query to make sure everything works; in
|
||||
* particular, that the database exists.
|
||||
*/
|
||||
res = PQexec(conn, " ");
|
||||
if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY)
|
||||
{
|
||||
/* PQexec has put error message in conn->errorMessage */
|
||||
closePGconn(conn);
|
||||
}
|
||||
PQclear(res);
|
||||
}
|
||||
PQsetenv(conn);
|
||||
}
|
||||
conn->pguser = fe_getauthname(conn->errorMessage);
|
||||
}
|
||||
|
||||
if (conn->pguser == NULL)
|
||||
{
|
||||
error = TRUE;
|
||||
sprintf(conn->errorMessage,
|
||||
"FATAL: PQsetdbLogin(): Unable to determine a Postgres username!\n");
|
||||
}
|
||||
|
||||
if (pwd)
|
||||
{
|
||||
conn->pgpass = strdup(pwd);
|
||||
}
|
||||
else if ((tmp = getenv("PGPASSWORD")) != NULL)
|
||||
{
|
||||
conn->pgpass = strdup(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
conn->pgpass = strdup(DefaultPassword);
|
||||
}
|
||||
|
||||
if ((dbName == NULL) || dbName[0] == '\0')
|
||||
{
|
||||
if ((tmp = getenv("PGDATABASE")) != NULL)
|
||||
conn->dbName = strdup(tmp);
|
||||
else if (conn->pguser)
|
||||
conn->dbName = strdup(conn->pguser);
|
||||
}
|
||||
else
|
||||
conn->dbName = strdup(dbName);
|
||||
|
||||
if (conn->dbName)
|
||||
{
|
||||
/*
|
||||
* if the database name is surrounded by double-quotes, then
|
||||
* don't convert case
|
||||
*/
|
||||
if (*conn->dbName == '"')
|
||||
{
|
||||
strcpy(conn->dbName, conn->dbName + 1);
|
||||
conn->dbName[strlen(conn->dbName) - 1] = '\0';
|
||||
}
|
||||
else
|
||||
for (i = 0; conn->dbName[i]; i++)
|
||||
if (isupper(conn->dbName[i]))
|
||||
conn->dbName[i] = tolower(conn->dbName[i]);
|
||||
}
|
||||
|
||||
if (error)
|
||||
conn->status = CONNECTION_BAD;
|
||||
else
|
||||
conn->status = connectDB(conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
@@ -468,6 +405,7 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
|
||||
static ConnStatusType
|
||||
connectDB(PGconn *conn)
|
||||
{
|
||||
PGresult *res;
|
||||
struct hostent *hp;
|
||||
|
||||
StartupPacket sp;
|
||||
@@ -476,6 +414,7 @@ connectDB(PGconn *conn)
|
||||
int portno,
|
||||
family,
|
||||
len;
|
||||
char beresp;
|
||||
|
||||
/*
|
||||
* Initialize the startup packet.
|
||||
@@ -506,16 +445,17 @@ connectDB(PGconn *conn)
|
||||
conn->pghost);
|
||||
goto connect_errReturn;
|
||||
}
|
||||
family = AF_INET;
|
||||
}
|
||||
else
|
||||
else {
|
||||
hp = NULL;
|
||||
family = AF_UNIX;
|
||||
}
|
||||
|
||||
#if FALSE
|
||||
MemSet((char *) &port->raddr, 0, sizeof(port->raddr));
|
||||
#endif
|
||||
portno = atoi(conn->pgport);
|
||||
family = (conn->pghost != NULL) ? AF_INET : AF_UNIX;
|
||||
MemSet((char *) &conn->raddr, 0, sizeof(conn->raddr));
|
||||
conn->raddr.sa.sa_family = family;
|
||||
|
||||
portno = atoi(conn->pgport);
|
||||
if (family == AF_INET)
|
||||
{
|
||||
memmove((char *) &(conn->raddr.in.sin_addr),
|
||||
@@ -528,7 +468,8 @@ connectDB(PGconn *conn)
|
||||
{
|
||||
len = UNIXSOCK_PATH(conn->raddr.un, portno);
|
||||
}
|
||||
/* connect to the server */
|
||||
|
||||
/* Connect to the server */
|
||||
if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
@@ -545,6 +486,20 @@ connectDB(PGconn *conn)
|
||||
conn->pgport);
|
||||
goto connect_errReturn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the right options.
|
||||
* We need nonblocking I/O, and we don't want delay of outgoing data.
|
||||
*/
|
||||
|
||||
if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) < 0)
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
"connectDB() -- fcntl() failed: errno=%d\n%s\n",
|
||||
errno, strerror(errno));
|
||||
goto connect_errReturn;
|
||||
}
|
||||
|
||||
if (family == AF_INET)
|
||||
{
|
||||
struct protoent *pe;
|
||||
@@ -561,109 +516,155 @@ connectDB(PGconn *conn)
|
||||
&on, sizeof(on)) < 0)
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
"connectDB(): setsockopt failed\n");
|
||||
"connectDB() -- setsockopt failed: errno=%d\n%s\n",
|
||||
errno, strerror(errno));
|
||||
goto connect_errReturn;
|
||||
}
|
||||
}
|
||||
|
||||
/* fill in the client address */
|
||||
/* Fill in the client address */
|
||||
if (getsockname(conn->sock, &conn->laddr.sa, &laddrlen) < 0)
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
"connectDB() -- getsockname() failed: errno=%d\n%s\n",
|
||||
"connectDB() -- getsockname() failed: errno=%d\n%s\n",
|
||||
errno, strerror(errno));
|
||||
goto connect_errReturn;
|
||||
}
|
||||
|
||||
/* set up the socket file descriptors */
|
||||
conn->Pfout = fdopen(conn->sock, "w");
|
||||
conn->Pfin = fdopen(dup(conn->sock), "r");
|
||||
if ((conn->Pfout == NULL) || (conn->Pfin == NULL))
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
"connectDB() -- fdopen() failed: errno=%d\n%s\n",
|
||||
errno, strerror(errno));
|
||||
goto connect_errReturn;
|
||||
}
|
||||
/* Ensure our buffers are empty */
|
||||
conn->inStart = conn->inCursor = conn->inEnd = 0;
|
||||
conn->outCount = 0;
|
||||
|
||||
/* Send the startup packet. */
|
||||
|
||||
if (packetSend(conn, (char *) &sp, sizeof(StartupPacket)) != STATUS_OK)
|
||||
{
|
||||
sprintf(conn->errorMessage,
|
||||
"connectDB() -- couldn't send complete packet: errno=%d\n%s\n", errno, strerror(errno));
|
||||
"connectDB() -- couldn't send startup packet: errno=%d\n%s\n",
|
||||
errno, strerror(errno));
|
||||
goto connect_errReturn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the response from the backend, either an error message or an
|
||||
* authentication request.
|
||||
* Perform the authentication exchange:
|
||||
* wait for backend messages and respond as necessary.
|
||||
* We fall out of this loop when done talking to the postmaster.
|
||||
*/
|
||||
|
||||
do
|
||||
for (;;)
|
||||
{
|
||||
int beresp;
|
||||
|
||||
if ((beresp = pqGetc(conn->Pfin, conn->Pfdebug)) == EOF)
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
"connectDB() -- error getting authentication request\n");
|
||||
|
||||
/* Wait for some data to arrive (or for the channel to close) */
|
||||
if (pqWait(TRUE, FALSE, conn))
|
||||
goto connect_errReturn;
|
||||
}
|
||||
/* Load data, or detect EOF */
|
||||
if (pqReadData(conn) < 0)
|
||||
goto connect_errReturn;
|
||||
/* Scan the message.
|
||||
* If we run out of data, loop around to try again.
|
||||
*/
|
||||
conn->inCursor = conn->inStart;
|
||||
|
||||
if (pqGetc(&beresp, conn))
|
||||
continue; /* no data yet */
|
||||
|
||||
/* Handle errors. */
|
||||
|
||||
if (beresp == 'E')
|
||||
{
|
||||
pqGets(conn->errorMessage, sizeof(conn->errorMessage),
|
||||
conn->Pfin, conn->Pfdebug);
|
||||
|
||||
if (pqGets(conn->errorMessage, sizeof(conn->errorMessage), conn))
|
||||
continue;
|
||||
goto connect_errReturn;
|
||||
}
|
||||
|
||||
/* Check it was an authentication request. */
|
||||
|
||||
/* Otherwise it should be an authentication request. */
|
||||
if (beresp != 'R')
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
"connectDB() -- expected authentication request\n");
|
||||
|
||||
"connectDB() -- expected authentication request\n");
|
||||
goto connect_errReturn;
|
||||
}
|
||||
|
||||
/* Get the type of request. */
|
||||
|
||||
if (pqGetInt((int *) &areq, 4, conn->Pfin, conn->Pfdebug))
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
"connectDB() -- error getting authentication request type\n");
|
||||
|
||||
goto connect_errReturn;
|
||||
}
|
||||
if (pqGetInt((int *) &areq, 4, conn))
|
||||
continue;
|
||||
|
||||
/* Get the password salt if there is one. */
|
||||
|
||||
if (areq == AUTH_REQ_CRYPT &&
|
||||
pqGetnchar(conn->salt, sizeof(conn->salt),
|
||||
conn->Pfin, conn->Pfdebug))
|
||||
if (areq == AUTH_REQ_CRYPT)
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
"connectDB() -- error getting password salt\n");
|
||||
|
||||
goto connect_errReturn;
|
||||
if (pqGetnchar(conn->salt, sizeof(conn->salt), conn))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* OK, we successfully read the message; mark data consumed */
|
||||
conn->inStart = conn->inCursor;
|
||||
|
||||
/* Respond to the request. */
|
||||
|
||||
/* Respond to the request if necessary. */
|
||||
if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
|
||||
conn->errorMessage) != STATUS_OK)
|
||||
goto connect_errReturn;
|
||||
}
|
||||
while (areq != AUTH_REQ_OK);
|
||||
if (pqFlush(conn))
|
||||
goto connect_errReturn;
|
||||
|
||||
/* free the password so it's not hanging out in memory forever */
|
||||
/* Are we done? */
|
||||
if (areq == AUTH_REQ_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we expect to hear from the backend.
|
||||
* A ReadyForQuery message indicates that startup is successful,
|
||||
* but we might also get an Error message indicating failure.
|
||||
* (Notice messages indicating nonfatal warnings are also allowed
|
||||
* by the protocol.)
|
||||
* Easiest way to handle this is to let PQgetResult() read the messages.
|
||||
* We just have to fake it out about the state of the connection.
|
||||
*/
|
||||
|
||||
conn->status = CONNECTION_OK;
|
||||
conn->asyncStatus = PGASYNC_BUSY;
|
||||
res = PQgetResult(conn);
|
||||
/* NULL return indicating we have gone to IDLE state is expected */
|
||||
if (res) {
|
||||
if (res->resultStatus != PGRES_FATAL_ERROR)
|
||||
sprintf(conn->errorMessage,
|
||||
"connectDB() -- unexpected message during startup\n");
|
||||
PQclear(res);
|
||||
goto connect_errReturn;
|
||||
}
|
||||
|
||||
/* Given the new protocol that sends a ReadyForQuery message
|
||||
* after successful backend startup, it should no longer be
|
||||
* necessary to send an empty query to test for startup.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
|
||||
/*
|
||||
* Send a blank query to make sure everything works; in
|
||||
* particular, that the database exists.
|
||||
*/
|
||||
res = PQexec(conn, " ");
|
||||
if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY)
|
||||
{
|
||||
/* PQexec has put error message in conn->errorMessage */
|
||||
closePGconn(conn);
|
||||
PQclear(res);
|
||||
goto connect_errReturn;
|
||||
}
|
||||
PQclear(res);
|
||||
|
||||
#endif
|
||||
|
||||
/* Post-connection housekeeping.
|
||||
* Send environment variables to server
|
||||
*/
|
||||
|
||||
PQsetenv(conn);
|
||||
|
||||
/* Free the password so it's not hanging out in memory forever */
|
||||
/* XXX Is this *really* a good idea? The security gain is marginal
|
||||
* if not totally illusory, and it breaks PQreset() for databases
|
||||
* that use passwords.
|
||||
*/
|
||||
if (conn->pgpass != NULL)
|
||||
{
|
||||
free(conn->pgpass);
|
||||
@@ -673,6 +674,11 @@ connectDB(PGconn *conn)
|
||||
return CONNECTION_OK;
|
||||
|
||||
connect_errReturn:
|
||||
if (conn->sock >= 0)
|
||||
{
|
||||
close(conn->sock);
|
||||
conn->sock = -1;
|
||||
}
|
||||
return CONNECTION_BAD;
|
||||
|
||||
}
|
||||
@@ -704,6 +710,36 @@ PQsetenv(PGconn *conn)
|
||||
}
|
||||
} /* PQsetenv() */
|
||||
|
||||
/*
|
||||
* makeEmptyPGconn
|
||||
* - create a PGconn data structure with (as yet) no interesting data
|
||||
*/
|
||||
static PGconn *
|
||||
makeEmptyPGconn(void)
|
||||
{
|
||||
PGconn *conn = (PGconn *) malloc(sizeof(PGconn));
|
||||
if (conn == NULL)
|
||||
return conn;
|
||||
|
||||
/* Zero all pointers */
|
||||
MemSet((char *) conn, 0, sizeof(PGconn));
|
||||
|
||||
conn->status = CONNECTION_BAD;
|
||||
conn->asyncStatus = PGASYNC_IDLE;
|
||||
conn->notifyList = DLNewList();
|
||||
conn->sock = -1;
|
||||
conn->inBufSize = 8192;
|
||||
conn->inBuffer = (char *) malloc(conn->inBufSize);
|
||||
conn->outBufSize = 8192;
|
||||
conn->outBuffer = (char *) malloc(conn->outBufSize);
|
||||
if (conn->inBuffer == NULL || conn->outBuffer == NULL)
|
||||
{
|
||||
freePGconn(conn);
|
||||
conn = NULL;
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
/*
|
||||
* freePGconn
|
||||
* - free the PGconn data structure
|
||||
@@ -714,22 +750,32 @@ freePGconn(PGconn *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
PQclearAsyncResult(conn); /* deallocate result and curTuple */
|
||||
if (conn->sock >= 0)
|
||||
close(conn->sock); /* shouldn't happen, but... */
|
||||
if (conn->pghost)
|
||||
free(conn->pghost);
|
||||
if (conn->pgport)
|
||||
free(conn->pgport);
|
||||
if (conn->pgtty)
|
||||
free(conn->pgtty);
|
||||
if (conn->pgoptions)
|
||||
free(conn->pgoptions);
|
||||
if (conn->pgport)
|
||||
free(conn->pgport);
|
||||
if (conn->dbName)
|
||||
free(conn->dbName);
|
||||
if (conn->pguser)
|
||||
free(conn->pguser);
|
||||
if (conn->pgpass)
|
||||
free(conn->pgpass);
|
||||
/* Note that conn->Pfdebug is not ours to close or free */
|
||||
if (conn->notifyList)
|
||||
DLFreeList(conn->notifyList);
|
||||
if (conn->lobjfuncs)
|
||||
free(conn->lobjfuncs);
|
||||
if (conn->inBuffer)
|
||||
free(conn->inBuffer);
|
||||
if (conn->outBuffer)
|
||||
free(conn->outBuffer);
|
||||
free(conn);
|
||||
}
|
||||
|
||||
@@ -740,42 +786,54 @@ freePGconn(PGconn *conn)
|
||||
static void
|
||||
closePGconn(PGconn *conn)
|
||||
{
|
||||
/* GH: What to do for !USE_POSIX_SIGNALS ? */
|
||||
if (conn->sock >= 0)
|
||||
{
|
||||
/*
|
||||
* Try to send close message.
|
||||
* If connection is already gone, that's cool. No reason for kernel
|
||||
* to kill us when we try to write to it. So ignore SIGPIPE signals.
|
||||
*/
|
||||
#if defined(USE_POSIX_SIGNALS)
|
||||
struct sigaction ignore_action;
|
||||
struct sigaction ignore_action;
|
||||
struct sigaction oldaction;
|
||||
|
||||
/*
|
||||
* This is used as a constant, but not declared as such because the
|
||||
* sigaction structure is defined differently on different systems
|
||||
*/
|
||||
struct sigaction oldaction;
|
||||
ignore_action.sa_handler = SIG_IGN;
|
||||
sigemptyset(&ignore_action.sa_mask);
|
||||
ignore_action.sa_flags = 0;
|
||||
sigaction(SIGPIPE, (struct sigaction *) & ignore_action, &oldaction);
|
||||
|
||||
/*
|
||||
* If connection is already gone, that's cool. No reason for kernel
|
||||
* to kill us when we try to write to it. So ignore SIGPIPE signals.
|
||||
*/
|
||||
ignore_action.sa_handler = SIG_IGN;
|
||||
sigemptyset(&ignore_action.sa_mask);
|
||||
ignore_action.sa_flags = 0;
|
||||
sigaction(SIGPIPE, (struct sigaction *) & ignore_action, &oldaction);
|
||||
(void) pqPuts("X", conn);
|
||||
(void) pqFlush(conn);
|
||||
|
||||
fputs("X\0", conn->Pfout);
|
||||
fflush(conn->Pfout);
|
||||
sigaction(SIGPIPE, &oldaction, NULL);
|
||||
sigaction(SIGPIPE, &oldaction, NULL);
|
||||
#else
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
fputs("X\0", conn->Pfout);
|
||||
fflush(conn->Pfout);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
void (*oldsignal)(int);
|
||||
|
||||
oldsignal = signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
(void) pqPuts("X", conn);
|
||||
(void) pqFlush(conn);
|
||||
|
||||
signal(SIGPIPE, oldsignal);
|
||||
#endif
|
||||
if (conn->Pfout)
|
||||
fclose(conn->Pfout);
|
||||
if (conn->Pfin)
|
||||
fclose(conn->Pfin);
|
||||
if (conn->Pfdebug)
|
||||
fclose(conn->Pfdebug);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the connection, reset all transient state, flush I/O buffers.
|
||||
*/
|
||||
if (conn->sock >= 0)
|
||||
close(conn->sock);
|
||||
conn->sock = -1;
|
||||
conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just
|
||||
* absent */
|
||||
conn->asyncStatus = PGASYNC_IDLE;
|
||||
PQclearAsyncResult(conn); /* deallocate result and curTuple */
|
||||
if (conn->lobjfuncs)
|
||||
free(conn->lobjfuncs);
|
||||
conn->lobjfuncs = NULL;
|
||||
conn->inStart = conn->inCursor = conn->inEnd = 0;
|
||||
conn->outCount = 0;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -793,8 +851,7 @@ PQfinish(PGconn *conn)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conn->status == CONNECTION_OK)
|
||||
closePGconn(conn);
|
||||
closePGconn(conn);
|
||||
freePGconn(conn);
|
||||
}
|
||||
}
|
||||
@@ -818,12 +875,8 @@ PQreset(PGconn *conn)
|
||||
}
|
||||
|
||||
/*
|
||||
* PacketSend()
|
||||
*
|
||||
this is just like PacketSend(), defined in backend/libpq/pqpacket.c
|
||||
but we define it here to avoid linking in all of libpq.a
|
||||
|
||||
* packetSend -- send a single-packet message.
|
||||
* PacketSend() -- send a single-packet message.
|
||||
* this is like PacketSend(), defined in backend/libpq/pqpacket.c
|
||||
*
|
||||
* RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
|
||||
* SIDE_EFFECTS: may block.
|
||||
@@ -833,15 +886,16 @@ packetSend(PGconn *conn, const char *buf, size_t len)
|
||||
{
|
||||
/* Send the total packet size. */
|
||||
|
||||
if (pqPutInt(4 + len, 4, conn->Pfout, conn->Pfdebug))
|
||||
if (pqPutInt(4 + len, 4, conn))
|
||||
return STATUS_ERROR;
|
||||
|
||||
/* Send the packet itself. */
|
||||
|
||||
if (pqPutnchar(buf, len, conn->Pfout, conn->Pfdebug))
|
||||
if (pqPutnchar(buf, len, conn))
|
||||
return STATUS_ERROR;
|
||||
|
||||
pqFlush(conn->Pfout, conn->Pfdebug);
|
||||
if (pqFlush(conn))
|
||||
return STATUS_ERROR;
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
@@ -1203,6 +1257,17 @@ PQerrorMessage(PGconn *conn)
|
||||
return conn->errorMessage;
|
||||
}
|
||||
|
||||
int
|
||||
PQsocket(PGconn *conn)
|
||||
{
|
||||
if (!conn)
|
||||
{
|
||||
fprintf(stderr, "PQsocket() -- pointer to PGconn is null\n");
|
||||
return -1;
|
||||
}
|
||||
return conn->sock;
|
||||
}
|
||||
|
||||
void
|
||||
PQtrace(PGconn *conn, FILE *debug_port)
|
||||
{
|
||||
@@ -1218,8 +1283,8 @@ PQtrace(PGconn *conn, FILE *debug_port)
|
||||
void
|
||||
PQuntrace(PGconn *conn)
|
||||
{
|
||||
if (conn == NULL ||
|
||||
conn->status == CONNECTION_BAD)
|
||||
/* note: better allow untrace even when connection bad */
|
||||
if (conn == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,177 +5,496 @@
|
||||
*
|
||||
* DESCRIPTION
|
||||
* miscellaneous useful functions
|
||||
* these routines are analogous to the ones in libpq/pqcomm.c
|
||||
*
|
||||
* 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: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.10 1998/02/26 04:45:09 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.11 1998/05/06 23:51:14 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#if !defined(NO_UNISTD_H)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/types.h> /* for fd_set stuff */
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "libpq-fe.h"
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* pqGetc:
|
||||
get a character from stream f
|
||||
get a character from the connection
|
||||
|
||||
if debug is set, also echo the character fetched
|
||||
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(FILE *fin, FILE *debug)
|
||||
pqGetc(char *result, PGconn *conn)
|
||||
{
|
||||
int c;
|
||||
if (conn->inCursor >= conn->inEnd)
|
||||
return EOF;
|
||||
|
||||
c = getc(fin);
|
||||
*result = conn->inBuffer[conn->inCursor++];
|
||||
|
||||
if (debug && c != EOF)
|
||||
fprintf(debug, "From backend> %c\n", c);
|
||||
if (conn->Pfdebug)
|
||||
fprintf(conn->Pfdebug, "From backend> %c\n", *result);
|
||||
|
||||
return c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* pqPutnchar:
|
||||
send a string of exactly len length into stream f
|
||||
|
||||
returns 1 if there was an error, 0 otherwise.
|
||||
*/
|
||||
int
|
||||
pqPutnchar(const char *s, int len, FILE *f, FILE *debug)
|
||||
{
|
||||
if (debug)
|
||||
fprintf(debug, "To backend> %s\n", s);
|
||||
|
||||
return (pqPutNBytes(s, len, f) == EOF ? 1 : 0);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* pqGetnchar:
|
||||
get a string of exactly len bytes in buffer s (which must be 1 byte
|
||||
longer) from stream f and terminate it with a '\0'.
|
||||
*/
|
||||
int
|
||||
pqGetnchar(char *s, int len, FILE *f, FILE *debug)
|
||||
/* pqPutBytes: local routine to write N bytes to the connection,
|
||||
with buffering
|
||||
*/
|
||||
static int
|
||||
pqPutBytes(const char *s, int nbytes, PGconn *conn)
|
||||
{
|
||||
int status;
|
||||
int avail = conn->outBufSize - conn->outCount;
|
||||
|
||||
status = pqGetNBytes(s, len, f);
|
||||
|
||||
if (debug)
|
||||
fprintf(debug, "From backend (%d)> %s\n", len, s);
|
||||
|
||||
return (status == EOF ? 1 : 0);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* pqGets:
|
||||
get a string of up to length len from stream f
|
||||
*/
|
||||
int
|
||||
pqGets(char *s, int len, FILE *f, FILE *debug)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = pqGetString(s, len, f);
|
||||
|
||||
if (debug)
|
||||
fprintf(debug, "From backend> \"%s\"\n", s);
|
||||
|
||||
return (status == EOF ? 1 : 0);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* pgPutInt
|
||||
send an integer of 2 or 4 bytes to the file stream, compensate
|
||||
for host endianness.
|
||||
returns 0 if successful, 1 otherwise
|
||||
*/
|
||||
int
|
||||
pqPutInt(const int integer, int bytes, FILE *f, FILE *debug)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
switch (bytes)
|
||||
while (nbytes > avail)
|
||||
{
|
||||
case 2:
|
||||
retval = pqPutShort(integer, f);
|
||||
break;
|
||||
case 4:
|
||||
retval = pqPutLong(integer, f);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "** int size %d not supported\n", bytes);
|
||||
retval = 1;
|
||||
memcpy(conn->outBuffer + conn->outCount, s, avail);
|
||||
conn->outCount += avail;
|
||||
s += avail;
|
||||
nbytes -= avail;
|
||||
if (pqFlush(conn))
|
||||
return EOF;
|
||||
avail = conn->outBufSize;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
fprintf(debug, "To backend (%d#)> %d\n", bytes, integer);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* pgGetInt
|
||||
read a 2 or 4 byte integer from the stream and swab it around
|
||||
to compensate for different endianness
|
||||
returns 0 if successful
|
||||
*/
|
||||
int
|
||||
pqGetInt(int *result, int bytes, FILE *f, FILE *debug)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
switch (bytes)
|
||||
{
|
||||
case 2:
|
||||
retval = pqGetShort(result, f);
|
||||
break;
|
||||
case 4:
|
||||
retval = pqGetLong(result, f);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "** int size %d not supported\n", bytes);
|
||||
retval = 1;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
fprintf(debug, "From backend (#%d)> %d\n", bytes, *result);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
int
|
||||
pqPuts(const char *s, FILE *f, FILE *debug)
|
||||
{
|
||||
if (pqPutString(s, f) == EOF)
|
||||
return 1;
|
||||
|
||||
fflush(f);
|
||||
|
||||
if (debug)
|
||||
fprintf(debug, "To backend> %s\n", s);
|
||||
memcpy(conn->outBuffer + conn->outCount, s, nbytes);
|
||||
conn->outCount += nbytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
void
|
||||
pqFlush(FILE *f, FILE *debug)
|
||||
/* 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)
|
||||
{
|
||||
if (f)
|
||||
fflush(f);
|
||||
/* Copy conn data to locals for faster search loop */
|
||||
char *inBuffer = conn->inBuffer;
|
||||
int inCursor = conn->inCursor;
|
||||
int inEnd = conn->inEnd;
|
||||
int slen;
|
||||
|
||||
if (debug)
|
||||
fflush(debug);
|
||||
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] = '\0';
|
||||
}
|
||||
|
||||
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> %s\n", s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* pqGetnchar:
|
||||
get a string of exactly len bytes in buffer s (which must be 1 byte
|
||||
longer) and terminate it with a '\0'.
|
||||
*/
|
||||
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);
|
||||
s[len] = '\0';
|
||||
|
||||
conn->inCursor += len;
|
||||
|
||||
if (conn->Pfdebug)
|
||||
fprintf(conn->Pfdebug, "From backend (%d)> %s\n", len, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
/* pqPutnchar:
|
||||
send a string of exactly len bytes
|
||||
The buffer should have a terminating null, but it's not sent.
|
||||
*/
|
||||
int
|
||||
pqPutnchar(const char *s, int len, PGconn *conn)
|
||||
{
|
||||
if (pqPutBytes(s, len, conn))
|
||||
return EOF;
|
||||
|
||||
if (conn->Pfdebug)
|
||||
fprintf(conn->Pfdebug, "To backend> %s\n", 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:
|
||||
fprintf(stderr, "** int size %d not supported\n", bytes);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (conn->Pfdebug)
|
||||
fprintf(conn->Pfdebug, "From backend (#%d)> %d\n", 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:
|
||||
fprintf(stderr, "** int size %d not supported\n", bytes);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (conn->Pfdebug)
|
||||
fprintf(conn->Pfdebug, "To backend (%d#)> %d\n", 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=%d\n%s\n",
|
||||
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 open\n");
|
||||
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;
|
||||
sprintf(conn->errorMessage,
|
||||
"pqReadData() -- read() failed: errno=%d\n%s\n",
|
||||
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;
|
||||
sprintf(conn->errorMessage,
|
||||
"pqReadData() -- read() failed: errno=%d\n%s\n",
|
||||
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.
|
||||
*/
|
||||
sprintf(conn->errorMessage,
|
||||
"pqReadData() -- backend closed the channel unexpectedly.\n"
|
||||
"\tThis probably means the backend terminated abnormally"
|
||||
" before or while processing the request.\n");
|
||||
conn->status = CONNECTION_BAD; /* No more connection to
|
||||
* backend */
|
||||
close(conn->sock);
|
||||
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 open\n");
|
||||
return EOF;
|
||||
}
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
int sent = send(conn->sock, ptr, len, 0);
|
||||
if (sent < 0)
|
||||
{
|
||||
/* Anything except EAGAIN or EWOULDBLOCK is trouble */
|
||||
switch (errno)
|
||||
{
|
||||
#ifdef EAGAIN
|
||||
case EAGAIN:
|
||||
break;
|
||||
#endif
|
||||
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
|
||||
case EWOULDBLOCK:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
sprintf(conn->errorMessage,
|
||||
"pqFlush() -- couldn't send data: errno=%d\n%s\n",
|
||||
errno, strerror(errno));
|
||||
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 open\n");
|
||||
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=%d\n%s\n",
|
||||
errno, strerror(errno));
|
||||
return EOF;
|
||||
}
|
||||
/* On nonerror return, assume we're done */
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: libpq-fe.h,v 1.28 1998/03/20 04:02:57 momjian Exp $
|
||||
* $Id: libpq-fe.h,v 1.29 1998/05/06 23:51:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -28,6 +28,8 @@ extern "C"
|
||||
#include "libpq/pqcomm.h"
|
||||
#include "lib/dllist.h"
|
||||
|
||||
/* Application-visible enum types */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CONNECTION_OK,
|
||||
@@ -41,14 +43,13 @@ extern "C"
|
||||
/* anything was executed properly by the backend */
|
||||
PGRES_TUPLES_OK, /* a query command that returns tuples */
|
||||
/* was executed properly by the backend, PGresult */
|
||||
/* contains the resulttuples */
|
||||
PGRES_COPY_OUT,
|
||||
PGRES_COPY_IN,
|
||||
/* contains the result tuples */
|
||||
PGRES_COPY_OUT, /* Copy Out data transfer in progress */
|
||||
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
|
||||
|
||||
} ExecStatusType;
|
||||
|
||||
/* string descriptions of the ExecStatusTypes */
|
||||
@@ -63,29 +64,21 @@ extern "C"
|
||||
#define COMMAND_LENGTH 20
|
||||
#define REMARK_LENGTH 80
|
||||
#define PORTAL_NAME_LENGTH 16
|
||||
#define CMDSTATUS_LEN 40
|
||||
|
||||
/* ----------------
|
||||
* PQArgBlock --
|
||||
* Information (pointer to array of this structure) required
|
||||
* for the PQfn() call.
|
||||
* ----------------
|
||||
/* PGresult and the subsidiary types PGresAttDesc, PGresAttValue
|
||||
* represent the result of a query (or more precisely, of a single SQL
|
||||
* command --- a query string given to PQexec can contain multiple commands).
|
||||
* Note we assume that a single command can return at most one tuple group,
|
||||
* hence there is no need for multiple descriptor sets.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int len;
|
||||
int isint;
|
||||
union
|
||||
{
|
||||
int *ptr; /* can't use void (dec compiler barfs) */
|
||||
int integer;
|
||||
} u;
|
||||
} PQArgBlock;
|
||||
|
||||
typedef struct pgresAttDesc
|
||||
{
|
||||
char *name; /* type name */
|
||||
Oid adtid; /* type id */
|
||||
short adtsize; /* type size */
|
||||
short adtmod; /* type-specific modifier info */
|
||||
} PGresAttDesc;
|
||||
|
||||
/* use char* for Attribute values,
|
||||
@@ -102,59 +95,8 @@ extern "C"
|
||||
char *value; /* actual value */
|
||||
} PGresAttValue;
|
||||
|
||||
typedef struct pgNotify
|
||||
{
|
||||
char relname[NAMEDATALEN]; /* name of relation
|
||||
* containing data */
|
||||
int be_pid; /* process id of backend */
|
||||
} PGnotify;
|
||||
struct pg_conn; /* forward reference */
|
||||
|
||||
typedef struct pgLobjfuncs
|
||||
{
|
||||
Oid fn_lo_open; /* OID of backend function lo_open */
|
||||
Oid fn_lo_close;/* OID of backend function lo_close */
|
||||
Oid fn_lo_creat;/* OID of backend function lo_creat */
|
||||
Oid fn_lo_unlink; /* OID of backend function
|
||||
* lo_unlink */
|
||||
Oid fn_lo_lseek;/* OID of backend function lo_lseek */
|
||||
Oid fn_lo_tell; /* OID of backend function lo_tell */
|
||||
Oid fn_lo_read; /* OID of backend function LOread */
|
||||
Oid fn_lo_write;/* OID of backend function LOwrite */
|
||||
} PGlobjfuncs;
|
||||
|
||||
/* PGconn encapsulates a connection to the backend */
|
||||
typedef struct pg_conn
|
||||
{
|
||||
char *pghost; /* the machine on which the server is
|
||||
* running */
|
||||
char *pgtty; /* tty on which the backend messages is
|
||||
* displayed */
|
||||
char *pgport; /* the communication port with the backend */
|
||||
char *pgoptions; /* options to start the backend with */
|
||||
char *dbName; /* database name */
|
||||
ConnStatusType status;
|
||||
char errorMessage[ERROR_MSG_LENGTH];
|
||||
/* pipes for be/fe communication */
|
||||
FILE *Pfin;
|
||||
FILE *Pfout;
|
||||
FILE *Pfdebug;
|
||||
int sock; /* The socket */
|
||||
SockAddr laddr; /* Local address */
|
||||
SockAddr raddr; /* Remote address */
|
||||
char salt[2];
|
||||
int asyncNotifyWaiting;
|
||||
Dllist *notifyList;
|
||||
char *pguser; /* Postgres username of user who is
|
||||
* connected */
|
||||
char *pgpass;
|
||||
PGlobjfuncs *lobjfuncs; /* Backend function OID's for large object
|
||||
* access */
|
||||
} PGconn;
|
||||
|
||||
#define CMDSTATUS_LEN 40
|
||||
|
||||
/* PGresult encapsulates the result of a query */
|
||||
/* unlike the old libpq, we assume that queries only return in one group */
|
||||
typedef struct pg_result
|
||||
{
|
||||
int ntups;
|
||||
@@ -168,9 +110,99 @@ extern "C"
|
||||
* last insert query */
|
||||
int binary; /* binary tuple values if binary == 1,
|
||||
* otherwise ASCII */
|
||||
PGconn *conn;
|
||||
struct pg_conn *conn; /* connection we did the query on */
|
||||
} PGresult;
|
||||
|
||||
/* PGnotify represents the occurrence of a NOTIFY message */
|
||||
typedef struct pgNotify
|
||||
{
|
||||
char relname[NAMEDATALEN]; /* name of relation
|
||||
* containing data */
|
||||
int be_pid; /* process id of backend */
|
||||
} PGnotify;
|
||||
|
||||
/* PGAsyncStatusType is private to libpq, really shouldn't be seen by users */
|
||||
typedef enum
|
||||
{
|
||||
PGASYNC_IDLE, /* nothing's happening, dude */
|
||||
PGASYNC_BUSY, /* query in progress */
|
||||
PGASYNC_READY, /* result ready for PQgetResult */
|
||||
PGASYNC_COPY_IN, /* Copy In data transfer in progress */
|
||||
PGASYNC_COPY_OUT /* Copy Out data transfer in progress */
|
||||
} PGAsyncStatusType;
|
||||
|
||||
/* large-object-access data ... allocated only if large-object code is used.
|
||||
* Really shouldn't be visible to users */
|
||||
typedef struct pgLobjfuncs
|
||||
{
|
||||
Oid fn_lo_open; /* OID of backend function lo_open */
|
||||
Oid fn_lo_close;/* OID of backend function lo_close */
|
||||
Oid fn_lo_creat;/* OID of backend function lo_creat */
|
||||
Oid fn_lo_unlink; /* OID of backend function
|
||||
* lo_unlink */
|
||||
Oid fn_lo_lseek;/* OID of backend function lo_lseek */
|
||||
Oid fn_lo_tell; /* OID of backend function lo_tell */
|
||||
Oid fn_lo_read; /* OID of backend function LOread */
|
||||
Oid fn_lo_write;/* OID of backend function LOwrite */
|
||||
} PGlobjfuncs;
|
||||
|
||||
/* PGconn encapsulates a connection to the backend.
|
||||
* XXX contents of this struct really shouldn't be visible to applications
|
||||
*/
|
||||
typedef struct pg_conn
|
||||
{
|
||||
/* Saved values of connection options */
|
||||
char *pghost; /* the machine on which the server is
|
||||
* running */
|
||||
char *pgport; /* the server's communication port */
|
||||
char *pgtty; /* tty on which the backend messages is
|
||||
* displayed (NOT ACTUALLY USED???) */
|
||||
char *pgoptions; /* options to start the backend with */
|
||||
char *dbName; /* database name */
|
||||
char *pguser; /* Postgres username and password, if any */
|
||||
char *pgpass;
|
||||
|
||||
/* Optional file to write trace info to */
|
||||
FILE *Pfdebug;
|
||||
|
||||
/* Status indicators */
|
||||
ConnStatusType status;
|
||||
PGAsyncStatusType asyncStatus;
|
||||
Dllist *notifyList; /* Notify msgs not yet handed to application */
|
||||
|
||||
/* Connection data */
|
||||
int sock; /* Unix FD for socket, -1 if not connected */
|
||||
SockAddr laddr; /* Local address */
|
||||
SockAddr raddr; /* Remote address */
|
||||
|
||||
/* Miscellaneous stuff */
|
||||
char salt[2]; /* password salt received from backend */
|
||||
PGlobjfuncs *lobjfuncs; /* private state for large-object access fns */
|
||||
|
||||
/* Buffer for data received from backend and not yet processed */
|
||||
char *inBuffer; /* currently allocated buffer */
|
||||
int inBufSize; /* allocated size of buffer */
|
||||
int inStart; /* offset to first unconsumed data in buffer */
|
||||
int inCursor; /* next byte to tentatively consume */
|
||||
int inEnd; /* offset to first position after avail data */
|
||||
|
||||
/* Buffer for data not yet sent to backend */
|
||||
char *outBuffer; /* currently allocated buffer */
|
||||
int outBufSize; /* allocated size of buffer */
|
||||
int outCount; /* number of chars waiting in buffer */
|
||||
|
||||
/* Status for asynchronous result construction */
|
||||
PGresult *result; /* result being constructed */
|
||||
PGresAttValue *curTuple; /* tuple currently being read */
|
||||
|
||||
/* Message space. Placed last for code-size reasons.
|
||||
* errorMessage is the message last returned to the application.
|
||||
* When asyncStatus=READY, asyncErrorMessage is the pending message
|
||||
* that will be put in errorMessage by PQgetResult. */
|
||||
char errorMessage[ERROR_MSG_LENGTH];
|
||||
char asyncErrorMessage[ERROR_MSG_LENGTH];
|
||||
} PGconn;
|
||||
|
||||
typedef char pqbool;
|
||||
|
||||
/*
|
||||
@@ -179,7 +211,9 @@ extern "C"
|
||||
* defined. Pqbool, on the other hand, is unlikely to be used.
|
||||
*/
|
||||
|
||||
struct _PQprintOpt
|
||||
/* Print options for PQprint() */
|
||||
|
||||
typedef struct _PQprintOpt
|
||||
{
|
||||
pqbool header; /* print output field headings and row
|
||||
* count */
|
||||
@@ -193,15 +227,28 @@ extern "C"
|
||||
char *caption; /* HTML <caption> */
|
||||
char **fieldName; /* null terminated array of repalcement
|
||||
* field names */
|
||||
};
|
||||
} PQprintOpt;
|
||||
|
||||
typedef struct _PQprintOpt PQprintOpt;
|
||||
/* ----------------
|
||||
* PQArgBlock -- structure for PQfn() arguments
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int len;
|
||||
int isint;
|
||||
union
|
||||
{
|
||||
int *ptr; /* can't use void (dec compiler barfs) */
|
||||
int integer;
|
||||
} u;
|
||||
} PQArgBlock;
|
||||
|
||||
/* ----------------
|
||||
* Structure for the conninfo parameter definitions of PQconnectdb()
|
||||
* ----------------
|
||||
*/
|
||||
struct _PQconninfoOption
|
||||
typedef struct _PQconninfoOption
|
||||
{
|
||||
char *keyword; /* The keyword of the option */
|
||||
char *environ; /* Fallback environment variable name */
|
||||
@@ -215,9 +262,7 @@ extern "C"
|
||||
/* "D" Debug options - don't */
|
||||
/* create a field by default */
|
||||
int dispsize; /* Field size in characters for dialog */
|
||||
};
|
||||
|
||||
typedef struct _PQconninfoOption PQconninfoOption;
|
||||
} PQconninfoOption;
|
||||
|
||||
/* === in fe-connect.c === */
|
||||
/* make a new client connection to the backend */
|
||||
@@ -235,6 +280,7 @@ extern "C"
|
||||
*/
|
||||
extern void PQreset(PGconn *conn);
|
||||
|
||||
/* Accessor functions for PGconn objects */
|
||||
extern char *PQdb(PGconn *conn);
|
||||
extern char *PQuser(PGconn *conn);
|
||||
extern char *PQhost(PGconn *conn);
|
||||
@@ -243,51 +289,28 @@ extern "C"
|
||||
extern char *PQtty(PGconn *conn);
|
||||
extern ConnStatusType PQstatus(PGconn *conn);
|
||||
extern char *PQerrorMessage(PGconn *conn);
|
||||
extern int PQsocket(PGconn *conn);
|
||||
|
||||
/* Enable/disable tracing */
|
||||
extern void PQtrace(PGconn *conn, FILE *debug_port);
|
||||
extern void PQuntrace(PGconn *conn);
|
||||
|
||||
/* === in fe-exec.c === */
|
||||
/* Simple synchronous query */
|
||||
extern PGresult *PQexec(PGconn *conn, const char *query);
|
||||
extern int PQgetline(PGconn *conn, char *string, int length);
|
||||
extern int PQendcopy(PGconn *conn);
|
||||
extern void PQputline(PGconn *conn, const char *string);
|
||||
extern ExecStatusType PQresultStatus(PGresult *res);
|
||||
extern int PQntuples(PGresult *res);
|
||||
extern int PQnfields(PGresult *res);
|
||||
extern char *PQfname(PGresult *res, int field_num);
|
||||
extern int PQfnumber(PGresult *res, const char *field_name);
|
||||
extern Oid PQftype(PGresult *res, int field_num);
|
||||
extern short PQfsize(PGresult *res, int field_num);
|
||||
extern char *PQcmdStatus(PGresult *res);
|
||||
extern const char *PQoidStatus(PGresult *res);
|
||||
extern const char *PQcmdTuples(PGresult *res);
|
||||
extern char *PQgetvalue(PGresult *res, int tup_num, int field_num);
|
||||
extern int PQgetlength(PGresult *res, int tup_num, int field_num);
|
||||
extern int PQgetisnull(PGresult *res, int tup_num, int field_num);
|
||||
extern void PQclear(PGresult *res);
|
||||
/* PQdisplayTuples() is a better version of PQprintTuples() */
|
||||
extern void PQdisplayTuples(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);
|
||||
extern void PQprintTuples(PGresult *res,
|
||||
FILE *fout, /* output stream */
|
||||
int printAttName, /* print attribute names
|
||||
* or not */
|
||||
int terseOutput, /* delimiter bars or
|
||||
* not? */
|
||||
int width /* width of column, if
|
||||
* 0, use variable width */
|
||||
);
|
||||
extern void PQprint(FILE *fout, /* output stream */
|
||||
PGresult *res,
|
||||
PQprintOpt *ps /* option structure */
|
||||
);
|
||||
extern PGnotify *PQnotifies(PGconn *conn);
|
||||
/* Interface for multiple-result or asynchronous queries */
|
||||
extern int PQsendQuery(PGconn *conn, const char *query);
|
||||
extern PGresult *PQgetResult(PGconn *conn);
|
||||
/* Routines for managing an asychronous query */
|
||||
extern int PQisBusy(PGconn *conn);
|
||||
extern void PQconsumeInput(PGconn *conn);
|
||||
extern int PQrequestCancel(PGconn *conn);
|
||||
/* Routines for copy in/out */
|
||||
extern int PQgetline(PGconn *conn, char *string, int length);
|
||||
extern void PQputline(PGconn *conn, const char *string);
|
||||
extern int PQendcopy(PGconn *conn);
|
||||
/* Not really meant for application use: */
|
||||
extern PGresult *PQfn(PGconn *conn,
|
||||
int fnid,
|
||||
int *result_buf,
|
||||
@@ -295,47 +318,94 @@ extern "C"
|
||||
int result_is_int,
|
||||
PQArgBlock *args,
|
||||
int nargs);
|
||||
extern void PQclearAsyncResult(PGconn *conn);
|
||||
|
||||
/* Accessor functions for PGresult objects */
|
||||
extern ExecStatusType PQresultStatus(PGresult *res);
|
||||
extern int PQntuples(PGresult *res);
|
||||
extern int PQnfields(PGresult *res);
|
||||
extern char *PQfname(PGresult *res, int field_num);
|
||||
extern int PQfnumber(PGresult *res, const char *field_name);
|
||||
extern Oid PQftype(PGresult *res, int field_num);
|
||||
extern short PQfsize(PGresult *res, int field_num);
|
||||
extern short PQfmod(PGresult *res, int field_num);
|
||||
extern char *PQcmdStatus(PGresult *res);
|
||||
extern const char *PQoidStatus(PGresult *res);
|
||||
extern const char *PQcmdTuples(PGresult *res);
|
||||
extern char *PQgetvalue(PGresult *res, int tup_num, int field_num);
|
||||
extern int PQgetlength(PGresult *res, int tup_num, int field_num);
|
||||
extern int PQgetisnull(PGresult *res, int tup_num, int field_num);
|
||||
/* Delete a PGresult */
|
||||
extern void PQclear(PGresult *res);
|
||||
|
||||
/* === in fe-print.c === */
|
||||
extern void PQprint(FILE *fout, /* output stream */
|
||||
PGresult *res,
|
||||
PQprintOpt *ps /* option structure */
|
||||
);
|
||||
/* PQdisplayTuples() is a better version of PQprintTuples(),
|
||||
* but both are obsoleted by PQprint().
|
||||
*/
|
||||
extern void PQdisplayTuples(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);
|
||||
extern void PQprintTuples(PGresult *res,
|
||||
FILE *fout, /* output stream */
|
||||
int printAttName, /* print attribute names
|
||||
* or not */
|
||||
int terseOutput, /* delimiter bars or
|
||||
* not? */
|
||||
int width /* width of column, if
|
||||
* 0, use variable width */
|
||||
);
|
||||
|
||||
/* === in fe-auth.c === */
|
||||
extern MsgType fe_getauthsvc(char *PQerrormsg);
|
||||
extern void fe_setauthsvc(const char *name, char *PQerrormsg);
|
||||
extern char *fe_getauthname(char *PQerrormsg);
|
||||
|
||||
/* === in fe-misc.c === */
|
||||
/* pqGets and pqPuts gets and sends strings to the file stream
|
||||
returns 0 if successful
|
||||
if debug is non-null, debugging output is sent to that stream
|
||||
*/
|
||||
extern int pqGets(char *s, int maxlen, FILE *stream, FILE *debug);
|
||||
extern int pqGetnchar(char *s, int maxlen, FILE *stream, FILE *debug);
|
||||
extern int pqPutnchar(const char *s, int maxlen, FILE *stream, FILE *debug);
|
||||
extern int pqPuts(const char *s, FILE *stream, FILE *debug);
|
||||
extern int pqGetc(FILE *stream, FILE *debug);
|
||||
/* get a n-byte integer from the stream into result */
|
||||
/* returns 0 if successful */
|
||||
extern int pqGetInt(int *result, int bytes, FILE *stream, FILE *debug);
|
||||
/* put a n-byte integer into the stream */
|
||||
/* returns 0 if successful */
|
||||
extern int pqPutInt(const int n, int bytes, FILE *stream, FILE *debug);
|
||||
extern void pqFlush(FILE *stream, FILE *debug);
|
||||
/* "Get" and "Put" routines return 0 if successful, EOF if not.
|
||||
* Note that for Get, EOF merely means the buffer is exhausted,
|
||||
* not that there is necessarily any error.
|
||||
*/
|
||||
extern int pqGetc(char *result, PGconn *conn);
|
||||
extern int pqGets(char *s, int maxlen, PGconn *conn);
|
||||
extern int pqPuts(const char *s, PGconn *conn);
|
||||
extern int pqGetnchar(char *s, int len, PGconn *conn);
|
||||
extern int pqPutnchar(const char *s, int len, PGconn *conn);
|
||||
extern int pqGetInt(int *result, int bytes, PGconn *conn);
|
||||
extern int pqPutInt(int value, int bytes, PGconn *conn);
|
||||
extern int pqReadData(PGconn *conn);
|
||||
extern int pqFlush(PGconn *conn);
|
||||
extern int pqWait(int forRead, int forWrite, PGconn *conn);
|
||||
|
||||
/* === in fe-lobj.c === */
|
||||
int lo_open(PGconn *conn, Oid lobjId, int mode);
|
||||
int lo_close(PGconn *conn, int fd);
|
||||
int lo_read(PGconn *conn, int fd, char *buf, int len);
|
||||
int lo_write(PGconn *conn, int fd, char *buf, int len);
|
||||
int lo_lseek(PGconn *conn, int fd, int offset, int whence);
|
||||
Oid lo_creat(PGconn *conn, int mode);
|
||||
int lo_tell(PGconn *conn, int fd);
|
||||
int lo_unlink(PGconn *conn, Oid lobjId);
|
||||
Oid lo_import(PGconn *conn, char *filename);
|
||||
int lo_export(PGconn *conn, Oid lobjId, char *filename);
|
||||
extern int lo_open(PGconn *conn, Oid lobjId, int mode);
|
||||
extern int lo_close(PGconn *conn, int fd);
|
||||
extern int lo_read(PGconn *conn, int fd, char *buf, int len);
|
||||
extern int lo_write(PGconn *conn, int fd, char *buf, int len);
|
||||
extern int lo_lseek(PGconn *conn, int fd, int offset, int whence);
|
||||
extern Oid lo_creat(PGconn *conn, int mode);
|
||||
extern int lo_tell(PGconn *conn, int fd);
|
||||
extern int lo_unlink(PGconn *conn, Oid lobjId);
|
||||
extern Oid lo_import(PGconn *conn, char *filename);
|
||||
extern int lo_export(PGconn *conn, Oid lobjId, char *filename);
|
||||
|
||||
/* max length of message to send */
|
||||
#define MAX_MESSAGE_LEN 8193
|
||||
|
||||
/* maximum number of fields in a tuple */
|
||||
#define BYTELEN 8
|
||||
#define MAX_FIELDS 512
|
||||
|
||||
/* 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"
|
||||
|
||||
Reference in New Issue
Block a user