*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.77 1998/07/26 04:31:36 scrappy Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.78 1998/08/09 02:59:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include <string.h>
#include <errno.h>
-#include <signal.h>
#include <ctype.h> /* for isspace() */
#include "postgres.h"
static int conninfo_parse(const char *conninfo, char *errorMessage);
static char *conninfo_getval(char *keyword);
static void conninfo_free(void);
+static void defaultNoticeProcessor(void * arg, const char * message);
/* XXX Why is this not static? */
void PQsetenv(PGconn *conn);
*/
conn = makeEmptyPGconn();
if (conn == NULL)
- {
- fprintf(stderr,
- "FATAL: PQconnectdb() -- unable to allocate memory for a PGconn");
return (PGconn *) NULL;
- }
/* ----------
* Parse the conninfo string and save settings in conn structure
conn = makeEmptyPGconn();
if (conn == NULL)
- {
- fprintf(stderr,
- "FATAL: PQsetdbLogin() -- unable to allocate memory for a PGconn");
return (PGconn *) NULL;
- }
if ((pghost == NULL) || pghost[0] == '\0')
{
/* Zero all pointers */
MemSet((char *) conn, 0, sizeof(PGconn));
+ conn->noticeHook = defaultNoticeProcessor;
conn->status = CONNECTION_BAD;
conn->asyncStatus = PGASYNC_IDLE;
conn->notifyList = DLNewList();
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.
+ * Try to send "close connection" message to backend.
+ * BUT: backend might have already closed connection.
+ * To avoid being killed by SIGPIPE, we need to detect this before
+ * writing. Check for "read ready" condition which indicates EOF.
*/
-#ifndef WIN32
-#if defined(USE_POSIX_SIGNALS)
- struct sigaction ignore_action;
- 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);
-
- (void) pqPuts("X", conn);
- (void) pqFlush(conn);
-
- sigaction(SIGPIPE, &oldaction, NULL);
-#else
- void (*oldsignal)(int);
-
- oldsignal = signal(SIGPIPE, SIG_IGN);
-
- (void) pqPuts("X", conn);
- (void) pqFlush(conn);
-
- signal(SIGPIPE, oldsignal);
-#endif
-#endif /* Win32 uses no signals at all */
+ while (pqReadReady(conn)) {
+ if (pqReadData(conn) < 0)
+ break;
+ }
+ if (conn->sock >= 0) {
+ /* Should be safe now... */
+ (void) pqPuts("X", conn);
+ (void) pqFlush(conn);
+ }
}
/*
void
PQfinish(PGconn *conn)
{
- if (!conn)
- fprintf(stderr, "PQfinish() -- pointer to PGconn is null\n");
- else
+ if (conn)
{
closePGconn(conn);
freePGconn(conn);
void
PQreset(PGconn *conn)
{
- if (!conn)
- fprintf(stderr, "PQreset() -- pointer to PGconn is null\n");
- else
+ if (conn)
{
closePGconn(conn);
conn->status = connectDB(conn);
PQdb(PGconn *conn)
{
if (!conn)
- {
- fprintf(stderr, "PQdb() -- pointer to PGconn is null\n");
return (char *) NULL;
- }
return conn->dbName;
}
PQuser(PGconn *conn)
{
if (!conn)
- {
- fprintf(stderr, "PQuser() -- pointer to PGconn is null\n");
return (char *) NULL;
- }
return conn->pguser;
}
PQhost(PGconn *conn)
{
if (!conn)
- {
- fprintf(stderr, "PQhost() -- pointer to PGconn is null\n");
return (char *) NULL;
- }
-
return conn->pghost;
}
PQoptions(PGconn *conn)
{
if (!conn)
- {
- fprintf(stderr, "PQoptions() -- pointer to PGconn is null\n");
return (char *) NULL;
- }
return conn->pgoptions;
}
PQtty(PGconn *conn)
{
if (!conn)
- {
- fprintf(stderr, "PQtty() -- pointer to PGconn is null\n");
return (char *) NULL;
- }
return conn->pgtty;
}
PQport(PGconn *conn)
{
if (!conn)
- {
- fprintf(stderr, "PQport() -- pointer to PGconn is null\n");
return (char *) NULL;
- }
return conn->pgport;
}
PQstatus(PGconn *conn)
{
if (!conn)
- {
- fprintf(stderr, "PQstatus() -- pointer to PGconn is null\n");
return CONNECTION_BAD;
- }
return conn->status;
}
char *
PQerrorMessage(PGconn *conn)
{
+ static char noConn[] = "PQerrorMessage: conn pointer is NULL\n";
if (!conn)
- {
- fprintf(stderr, "PQerrorMessage() -- pointer to PGconn is null\n");
- return (char *) NULL;
- }
+ return noConn;
return conn->errorMessage;
}
PQsocket(PGconn *conn)
{
if (!conn)
- {
- fprintf(stderr, "PQsocket() -- pointer to PGconn is null\n");
return -1;
- }
return conn->sock;
}
conn->Pfdebug = NULL;
}
}
+
+void
+PQsetNoticeProcessor (PGconn *conn, PQnoticeProcessor proc, void *arg)
+{
+ if (conn == NULL)
+ return;
+ conn->noticeHook = proc;
+ conn->noticeArg = arg;
+}
+
+/*
+ * The default notice/error message processor just prints the
+ * message on stderr. Applications can override this if they
+ * want the messages to go elsewhere (a window, for example).
+ * Note that simply discarding notices is probably a bad idea.
+ */
+
+static void
+defaultNoticeProcessor(void * arg, const char * message)
+{
+ /* Note: we expect the supplied string to end with a newline already. */
+ fprintf(stderr, "%s", message);
+}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.60 1998/07/14 02:41:25 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.61 1998/08/09 02:59:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
};
+#define DONOTICE(conn,message) \
+ ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
+
+
static PGresult *makeEmptyPGresult(PGconn *conn, ExecStatusType status);
static void freeTuple(PGresAttValue *tuple, int numAttributes);
static void addTuple(PGresult *res, PGresAttValue *tup);
sprintf(conn->errorMessage, "PQsendQuery() -- query pointer is null.");
return 0;
}
+ /* check to see if the query string is too long */
+ if (strlen(query) > MAX_MESSAGE_LEN-2)
+ {
+ sprintf(conn->errorMessage, "PQsendQuery() -- query is too long. "
+ "Maximum length is %d\n", MAX_MESSAGE_LEN - 2);
+ return 0;
+ }
+
if (conn->asyncStatus != PGASYNC_IDLE)
{
sprintf(conn->errorMessage,
return 0;
}
- /* clear the error string */
- conn->errorMessage[0] = '\0';
-
- /* initialize async result-accumulation state */
- conn->result = NULL;
- conn->curTuple = NULL;
- conn->asyncErrorMessage[0] = '\0';
-
- /* check to see if the query string is too long */
- if (strlen(query) > MAX_MESSAGE_LEN-2)
- {
- sprintf(conn->errorMessage, "PQsendQuery() -- query is too long. "
- "Maximum length is %d\n", MAX_MESSAGE_LEN - 2);
- return 0;
+ /* Check for pending input (asynchronous Notice or Notify messages);
+ * also detect the case that the backend just closed the connection.
+ * Note: we have to loop if the first call to pqReadData successfully
+ * reads some data, since in that case pqReadData won't notice whether
+ * the connection is now closed.
+ */
+ while (pqReadReady(conn)) {
+ if (pqReadData(conn) < 0)
+ return 0; /* errorMessage already set */
+ parseInput(conn); /* deal with Notice or Notify, if any */
}
/* Don't try to send if we know there's no live connection. */
return 0;
}
+ /* clear the error string */
+ conn->errorMessage[0] = '\0';
+
+ /* initialize async result-accumulation state */
+ conn->result = NULL;
+ conn->curTuple = NULL;
+ conn->asyncErrorMessage[0] = '\0';
+
/* send the query to the backend; */
/* the frontend-backend protocol uses 'Q' to designate queries */
if (pqPutnchar("Q", 1, conn))
if (pqGetc(&id, conn))
return;
/*
- * NOTIFY messages can happen in any state besides COPY OUT;
+ * NOTIFY and NOTICE messages can happen in any state besides COPY OUT;
* always process them right away.
*/
if (id == 'A')
{
- /* Notify responses can happen at any time */
if (getNotify(conn))
return;
}
+ else if (id == 'N')
+ {
+ if (getNotice(conn))
+ return;
+ }
else
{
/*
{
if (conn->asyncStatus == PGASYNC_IDLE)
{
- fprintf(stderr,
+ sprintf(conn->errorMessage,
"Backend message type 0x%02x arrived while idle\n",
id);
+ DONOTICE(conn, conn->errorMessage);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
}
if (pqGetc(&id, conn))
return;
if (id != '\0')
- fprintf(stderr,
+ {
+ sprintf(conn->errorMessage,
"unexpected character %c following 'I'\n", id);
+ DONOTICE(conn, conn->errorMessage);
+ }
if (conn->result == NULL)
conn->result = makeEmptyPGresult(conn,
PGRES_EMPTY_QUERY);
if (pqGetInt(&(conn->be_key), 4, conn))
return;
break;
- case 'N': /* notices from the backend */
- if (getNotice(conn))
- return;
- break;
case 'P': /* synchronous (normal) portal */
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
return;
}
else
{
- fprintf(stderr,
+ sprintf(conn->errorMessage,
"Backend sent D message without prior T\n");
+ DONOTICE(conn, conn->errorMessage);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
return;
}
else
{
- fprintf(stderr,
+ sprintf(conn->errorMessage,
"Backend sent B message without prior T\n");
+ DONOTICE(conn, conn->errorMessage);
/* Discard the unexpected message; good idea?? */
conn->inStart = conn->inEnd;
return;
{
if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, conn))
return EOF;
- /*
- * Should we really be doing this? These notices
- * are not important enough for us to presume to
- * put them on stderr. Maybe the caller should
- * decide whether to put them on stderr or not.
- * BJH 96.12.27
- */
- fprintf(stderr, "%s", conn->errorMessage);
+ DONOTICE(conn, conn->errorMessage);
return 0;
}
* To recover, reset the connection (talk about using a sledgehammer...)
*/
PQclear(result);
- fprintf(stderr, "PQendcopy: resetting connection\n");
+
+ sprintf(conn->errorMessage, "PQendcopy: resetting connection\n");
+ DONOTICE(conn, conn->errorMessage);
+
PQreset(conn);
return 1;
PQresultStatus(PGresult *res)
{
if (!res)
- {
- fprintf(stderr, "PQresultStatus() -- pointer to PQresult is null\n");
return PGRES_NONFATAL_ERROR;
- }
-
return res->resultStatus;
}
PQntuples(PGresult *res)
{
if (!res)
- {
- fprintf(stderr, "PQntuples() -- pointer to PQresult is null\n");
return 0;
- }
return res->ntups;
}
PQnfields(PGresult *res)
{
if (!res)
- {
- fprintf(stderr, "PQnfields() -- pointer to PQresult is null\n");
return 0;
- }
return res->numAttributes;
}
/*
- returns NULL if the field_num is invalid
-*/
-char *
-PQfname(PGresult *res, int field_num)
+ * Helper routines to range-check field numbers and tuple numbers.
+ * Return TRUE if OK, FALSE if not
+ */
+
+static int
+check_field_number(const char *routineName, PGresult *res, int field_num)
{
if (!res)
+ return FALSE; /* no way to display error message... */
+ if (field_num < 0 || field_num >= res->numAttributes)
{
- fprintf(stderr, "PQfname() -- pointer to PQresult is null\n");
- return NULL;
+ sprintf(res->conn->errorMessage,
+ "%s: ERROR! field number %d is out of range 0..%d\n",
+ routineName, field_num, res->numAttributes - 1);
+ DONOTICE(res->conn, res->conn->errorMessage);
+ return FALSE;
}
+ return TRUE;
+}
+static int
+check_tuple_field_number(const char *routineName, PGresult *res,
+ int tup_num, int field_num)
+{
+ if (!res)
+ return FALSE; /* no way to display error message... */
+ if (tup_num < 0 || tup_num >= res->ntups)
+ {
+ sprintf(res->conn->errorMessage,
+ "%s: ERROR! tuple number %d is out of range 0..%d\n",
+ routineName, tup_num, res->ntups - 1);
+ DONOTICE(res->conn, res->conn->errorMessage);
+ return FALSE;
+ }
if (field_num < 0 || field_num >= res->numAttributes)
{
- fprintf(stderr,
- "PQfname: ERROR! field number %d is out of range 0..%d\n",
- field_num, res->numAttributes - 1);
- return NULL;
+ sprintf(res->conn->errorMessage,
+ "%s: ERROR! field number %d is out of range 0..%d\n",
+ routineName, field_num, res->numAttributes - 1);
+ DONOTICE(res->conn, res->conn->errorMessage);
+ return FALSE;
}
+ return TRUE;
+}
+
+/*
+ returns NULL if the field_num is invalid
+*/
+char *
+PQfname(PGresult *res, int field_num)
+{
+ if (! check_field_number("PQfname", res, field_num))
+ return NULL;
if (res->attDescs)
return res->attDescs[field_num].name;
else
char *field_case;
if (!res)
- {
- fprintf(stderr, "PQfnumber() -- pointer to PQresult is null\n");
return -1;
- }
if (field_name == NULL ||
field_name[0] == '\0' ||
Oid
PQftype(PGresult *res, int field_num)
{
- if (!res)
- {
- fprintf(stderr, "PQftype() -- pointer to PQresult is null\n");
+ if (! check_field_number("PQftype", res, field_num))
return InvalidOid;
- }
-
- if (field_num < 0 || field_num >= res->numAttributes)
- {
- fprintf(stderr,
- "PQftype: ERROR! field number %d is out of range 0..%d\n",
- field_num, res->numAttributes - 1);
- return InvalidOid;
- }
if (res->attDescs)
return res->attDescs[field_num].typid;
else
short
PQfsize(PGresult *res, int field_num)
{
- if (!res)
- {
- fprintf(stderr, "PQfsize() -- pointer to PQresult is null\n");
- return 0;
- }
-
- if (field_num < 0 || field_num >= res->numAttributes)
- {
- fprintf(stderr,
- "PQfsize: ERROR! field number %d is out of range 0..%d\n",
- field_num, res->numAttributes - 1);
+ if (! check_field_number("PQfsize", res, field_num))
return 0;
- }
if (res->attDescs)
return res->attDescs[field_num].typlen;
else
int
PQfmod(PGresult *res, int field_num)
{
- if (!res)
- {
- fprintf(stderr, "PQfmod() -- pointer to PQresult is null\n");
- return 0;
- }
-
- if (field_num < 0 || field_num >= res->numAttributes)
- {
- fprintf(stderr,
- "PQfmod: ERROR! field number %d is out of range 0..%d\n",
- field_num, res->numAttributes - 1);
+ if (! check_field_number("PQfmod", res, field_num))
return 0;
- }
if (res->attDescs)
return res->attDescs[field_num].atttypmod;
else
PQcmdStatus(PGresult *res)
{
if (!res)
- {
- fprintf(stderr, "PQcmdStatus() -- pointer to PQresult is null\n");
return NULL;
- }
return res->cmdStatus;
}
static char oidStatus[32] = {0};
if (!res)
- {
- fprintf(stderr, "PQoidStatus () -- pointer to PQresult is null\n");
- return NULL;
- }
+ return "";
oidStatus[0] = 0;
PQcmdTuples(PGresult *res)
{
if (!res)
- {
- fprintf(stderr, "PQcmdTuples () -- pointer to PQresult is null\n");
- return NULL;
- }
+ return "";
if (strncmp(res->cmdStatus, "INSERT", 6) == 0 ||
strncmp(res->cmdStatus, "DELETE", 6) == 0 ||
if (*p == 0)
{
- fprintf(stderr, "PQcmdTuples (%s) -- bad input from server\n",
+ sprintf(res->conn->errorMessage,
+ "PQcmdTuples (%s) -- bad input from server\n",
res->cmdStatus);
- return NULL;
+ DONOTICE(res->conn, res->conn->errorMessage);
+ return "";
}
p++;
if (*(res->cmdStatus) != 'I') /* UPDATE/DELETE */
p++; /* INSERT: skip oid */
if (*p == 0)
{
- fprintf(stderr, "PQcmdTuples (INSERT) -- there's no # of tuples\n");
- return NULL;
+ sprintf(res->conn->errorMessage,
+ "PQcmdTuples (INSERT) -- there's no # of tuples\n");
+ DONOTICE(res->conn, res->conn->errorMessage);
+ return "";
}
p++;
return (p);
char *
PQgetvalue(PGresult *res, int tup_num, int field_num)
{
- if (!res)
- {
- fprintf(stderr, "PQgetvalue: pointer to PQresult is null\n");
- return NULL;
- }
- if (tup_num < 0 || tup_num >= res->ntups)
- {
- fprintf(stderr,
- "PQgetvalue: There is no row %d in the query results. "
- "The highest numbered row is %d.\n",
- tup_num, res->ntups - 1);
+ if (! check_tuple_field_number("PQgetvalue", res, tup_num, field_num))
return NULL;
- }
- if (field_num < 0 || field_num >= res->numAttributes)
- {
- fprintf(stderr,
- "PQgetvalue: There is no field %d in the query results. "
- "The highest numbered field is %d.\n",
- field_num, res->numAttributes - 1);
- return NULL;
- }
-
return res->tuples[tup_num][field_num].value;
}
int
PQgetlength(PGresult *res, int tup_num, int field_num)
{
- if (!res)
- {
- fprintf(stderr, "PQgetlength() -- pointer to PQresult is null\n");
- return 0;
- }
-
- if (tup_num < 0 || tup_num >= res->ntups)
- {
- fprintf(stderr,
- "PQgetlength: There is no row %d in the query results. "
- "The highest numbered row is %d.\n",
- tup_num, res->ntups - 1);
- return 0;
- }
- if (field_num < 0 || field_num >= res->numAttributes)
- {
- fprintf(stderr,
- "PQgetlength: There is no field %d in the query results. "
- "The highest numbered field is %d.\n",
- field_num, res->numAttributes - 1);
+ if (! check_tuple_field_number("PQgetlength", res, tup_num, field_num))
return 0;
- }
-
if (res->tuples[tup_num][field_num].len != NULL_LEN)
return res->tuples[tup_num][field_num].len;
else
int
PQgetisnull(PGresult *res, int tup_num, int field_num)
{
- if (!res)
- {
- fprintf(stderr, "PQgetisnull() -- pointer to PQresult is null\n");
+ if (! check_tuple_field_number("PQgetisnull", res, tup_num, field_num))
return 1; /* pretend it is null */
- }
- if (tup_num < 0 || tup_num >= res->ntups)
- {
- fprintf(stderr,
- "PQgetisnull: There is no row %d in the query results. "
- "The highest numbered row is %d.\n",
- tup_num, res->ntups - 1);
- return 1; /* pretend it is null */
- }
- if (field_num < 0 || field_num >= res->numAttributes)
- {
- fprintf(stderr,
- "PQgetisnull: There is no field %d in the query results. "
- "The highest numbered field is %d.\n",
- field_num, res->numAttributes - 1);
- return 1; /* pretend it is null */
- }
-
if (res->tuples[tup_num][field_num].len == NULL_LEN)
return 1;
else