diff options
author | Bruce Momjian <bruce@momjian.us> | 1998-08-09 02:59:33 +0000 |
---|---|---|
committer | Bruce Momjian <bruce@momjian.us> | 1998-08-09 02:59:33 +0000 |
commit | e6311b4ad0f5c6eb52fd90a9ed51862b8f2d3869 (patch) | |
tree | c96017785851169c24a64e3eff2d4caba395ddbe /src/interfaces/libpq/fe-exec.c | |
parent | e46df2ff6e190e748d33e3c6c6ca60cc13b5959b (diff) |
The attached patch implements some changes that were discussed a
couple weeks ago on the hackers and interfaces lists:
1. When the backend sends a NOTICE message and closes the connection
(typically, because it was told to by the postmaster after
another backend coredumped), libpq will now print the notice
and close the connection cleanly. Formerly, the frontend app
would usually terminate ungracefully due to a SIGPIPE. (I am
not sure if 6.3.2 behaved that way, but the current cvs sources
do...)
2. libpq's various printouts to stderr are now fed through a single
"notice processor" routine, which can be overridden by the
application to direct notices someplace else. This should ease
porting libpq to Windows.
I also noticed and fixed a problem in PQprint: when sending output
to a pager subprocess, it would disable SIGPIPE in case the pager
terminates early (this is good) --- but afterwards it reset SIGPIPE
to SIG_DFL, rather than restoring the application's prior setting
(bad).
regards, tom lane
Diffstat (limited to 'src/interfaces/libpq/fe-exec.c')
-rw-r--r-- | src/interfaces/libpq/fe-exec.c | 285 |
1 files changed, 113 insertions, 172 deletions
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 63bc1a078be..89790183144 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -7,7 +7,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -44,6 +44,10 @@ const char *pgresStatus[] = { }; +#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); @@ -198,6 +202,14 @@ PQsendQuery(PGconn *conn, const char *query) 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, @@ -205,20 +217,16 @@ PQsendQuery(PGconn *conn, const char *query) 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. */ @@ -229,6 +237,14 @@ PQsendQuery(PGconn *conn, const char *query) 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)) @@ -297,15 +313,19 @@ parseInput(PGconn *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 { /* @@ -318,9 +338,10 @@ parseInput(PGconn *conn) { 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; } @@ -354,8 +375,11 @@ parseInput(PGconn *conn) 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); @@ -371,10 +395,6 @@ parseInput(PGconn *conn) 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; @@ -408,8 +428,9 @@ parseInput(PGconn *conn) } 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; @@ -424,8 +445,9 @@ parseInput(PGconn *conn) } 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; @@ -783,14 +805,7 @@ getNotice(PGconn *conn) { 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; } @@ -970,7 +985,10 @@ PQendcopy(PGconn *conn) * 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; @@ -1156,11 +1174,7 @@ ExecStatusType PQresultStatus(PGresult *res) { if (!res) - { - fprintf(stderr, "PQresultStatus() -- pointer to PQresult is null\n"); return PGRES_NONFATAL_ERROR; - } - return res->resultStatus; } @@ -1168,10 +1182,7 @@ int PQntuples(PGresult *res) { if (!res) - { - fprintf(stderr, "PQntuples() -- pointer to PQresult is null\n"); return 0; - } return res->ntups; } @@ -1179,32 +1190,64 @@ int 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 @@ -1221,10 +1264,7 @@ PQfnumber(PGresult *res, const char *field_name) char *field_case; if (!res) - { - fprintf(stderr, "PQfnumber() -- pointer to PQresult is null\n"); return -1; - } if (field_name == NULL || field_name[0] == '\0' || @@ -1258,19 +1298,8 @@ PQfnumber(PGresult *res, const char *field_name) 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 @@ -1280,19 +1309,8 @@ PQftype(PGresult *res, int field_num) 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 @@ -1302,19 +1320,8 @@ PQfsize(PGresult *res, int field_num) 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 @@ -1325,10 +1332,7 @@ char * PQcmdStatus(PGresult *res) { if (!res) - { - fprintf(stderr, "PQcmdStatus() -- pointer to PQresult is null\n"); return NULL; - } return res->cmdStatus; } @@ -1343,10 +1347,7 @@ PQoidStatus(PGresult *res) static char oidStatus[32] = {0}; if (!res) - { - fprintf(stderr, "PQoidStatus () -- pointer to PQresult is null\n"); - return NULL; - } + return ""; oidStatus[0] = 0; @@ -1371,10 +1372,7 @@ const char * 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 || @@ -1384,9 +1382,11 @@ PQcmdTuples(PGresult *res) 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 */ @@ -1395,8 +1395,10 @@ PQcmdTuples(PGresult *res) 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); @@ -1417,28 +1419,8 @@ PQcmdTuples(PGresult *res) 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; } @@ -1450,29 +1432,8 @@ PQgetvalue(PGresult *res, int tup_num, int field_num) 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 @@ -1485,28 +1446,8 @@ PQgetlength(PGresult *res, int tup_num, int field_num) 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 |