diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index d9c5ec1a00..746f2af216 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -21,7 +21,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.83 1998/09/01 04:33:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.84 1998/09/03 02:10:36 momjian Exp $ * * Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb * @@ -255,7 +255,7 @@ dumpClasses_nodumpData(FILE *fout, const char *classname, const bool oids) copydone = false; while (!copydone) { - ret = PQgetline(res->conn, copybuf, COPYBUFSIZ); + ret = PQgetline(g_conn, copybuf, COPYBUFSIZ); if (copybuf[0] == '\\' && copybuf[1] == '.' && @@ -281,7 +281,7 @@ dumpClasses_nodumpData(FILE *fout, const char *classname, const bool oids) } fprintf(fout, "\\.\n"); } - ret = PQendcopy(res->conn); + ret = PQendcopy(g_conn); if (ret != 0) { fprintf(stderr, "SQL query to dump the contents of Table '%s' " diff --git a/src/bin/psql/psql.c b/src/bin/psql/psql.c index ecb441fbc7..054faab480 100644 --- a/src/bin/psql/psql.c +++ b/src/bin/psql/psql.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.158 1998/09/01 04:33:51 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.159 1998/09/03 02:10:38 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -148,7 +148,7 @@ struct winsize /* declarations for functions in this file */ static void usage(char *progname); static void slashUsage(); -static bool handleCopyOut(PGresult *res, FILE *copystream); +static bool handleCopyOut(PGconn *conn, FILE *copystream); static bool handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream); static int tableList(PsqlSettings *pset, bool deep_tablelist, @@ -1125,20 +1125,20 @@ SendQuery(bool *success_p, PsqlSettings *pset, const char *query, break; case PGRES_COPY_OUT: if (copy_out) - *success_p = handleCopyOut(results, copystream); + *success_p = handleCopyOut(pset->db, copystream); else { if (!pset->quiet) printf("Copy command returns...\n"); - *success_p = handleCopyOut(results, stdout); + *success_p = handleCopyOut(pset->db, stdout); } break; case PGRES_COPY_IN: if (copy_in) - *success_p = handleCopyIn(results, false, copystream); + *success_p = handleCopyIn(pset->db, false, copystream); else - *success_p = handleCopyIn(results, + *success_p = handleCopyIn(pset->db, !pset->quiet && !pset->notty, stdin); break; @@ -1437,11 +1437,8 @@ do_connect(const char *new_dbname, else userparam = PQuser(olddb); - /* - * libpq doesn't provide an accessor function for the password, so - * we cheat here. - */ - pwparam = olddb->pgpass; + /* FIXME: if changing user, ought to prompt for a new password? */ + pwparam = PQpass(olddb); pset->db = PQsetdbLogin(PQhost(olddb), PQport(olddb), NULL, NULL, dbparam, userparam, pwparam); @@ -2915,7 +2912,7 @@ main(int argc, char **argv) #define COPYBUFSIZ 8192 static bool -handleCopyOut(PGresult *res, FILE *copystream) +handleCopyOut(PGconn *conn, FILE *copystream) { bool copydone; char copybuf[COPYBUFSIZ]; @@ -2925,7 +2922,7 @@ handleCopyOut(PGresult *res, FILE *copystream) while (!copydone) { - ret = PQgetline(res->conn, copybuf, COPYBUFSIZ); + ret = PQgetline(conn, copybuf, COPYBUFSIZ); if (copybuf[0] == '\\' && copybuf[1] == '.' && @@ -2950,13 +2947,13 @@ handleCopyOut(PGresult *res, FILE *copystream) } } fflush(copystream); - return !PQendcopy(res->conn); + return ! PQendcopy(conn); } static bool -handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) +handleCopyIn(PGconn *conn, const bool mustprompt, FILE *copystream) { bool copydone = false; bool firstload; @@ -2991,12 +2988,12 @@ handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) *s++ = c; if (c == EOF) { - PQputline(res->conn, "\\."); + PQputline(conn, "\\."); copydone = true; break; } *s = '\0'; - PQputline(res->conn, copybuf); + PQputline(conn, copybuf); if (firstload) { if (!strcmp(copybuf, "\\.")) @@ -3004,9 +3001,9 @@ handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) firstload = false; } } - PQputline(res->conn, "\n"); + PQputline(conn, "\n"); } - return !PQendcopy(res->conn); + return ! PQendcopy(conn); } diff --git a/src/interfaces/ecpg/lib/ecpglib.c b/src/interfaces/ecpg/lib/ecpglib.c index 3804544514..4b7ed87c1f 100644 --- a/src/interfaces/ecpg/lib/ecpglib.c +++ b/src/interfaces/ecpg/lib/ecpglib.c @@ -814,11 +814,11 @@ ECPGexecute(struct statement * stmt) break; case PGRES_COPY_OUT: ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", stmt->lineno); - PQendcopy(results->conn); + PQendcopy(actual_connection->connection); break; case PGRES_COPY_IN: ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", stmt->lineno); - PQendcopy(results->conn); + PQendcopy(actual_connection->connection); break; default: ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", @@ -995,7 +995,7 @@ ECPGlog(const char *format,...) if (!f) return; - sprintf(f, "[%d]: %s", getpid(), format); + sprintf(f, "[%d]: %s", (int) getpid(), format); va_start(ap, format); vfprintf(debugstream, f, ap); diff --git a/src/interfaces/libpgtcl/pgtclCmds.c b/src/interfaces/libpgtcl/pgtclCmds.c index bd6641554d..a8d828c016 100644 --- a/src/interfaces/libpgtcl/pgtclCmds.c +++ b/src/interfaces/libpgtcl/pgtclCmds.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.31 1998/09/01 04:39:56 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.32 1998/09/03 02:10:42 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -358,15 +358,15 @@ Pg_connect(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); } - if (conn->status == CONNECTION_OK) + if (PQstatus(conn) == CONNECTION_OK) { { PgSetConnectionId(interp, conn); return TCL_OK; } else { - Tcl_AppendResult(interp, "Connection to database failed\n", 0); - Tcl_AppendResult(interp, conn->errorMessage, 0); + Tcl_AppendResult(interp, "Connection to database failed\n", + PQerrorMessage(conn), 0); PQfinish(conn); return TCL_ERROR; } @@ -423,7 +423,6 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) Pg_ConnectionId *connid; PGconn *conn; PGresult *result; - int connStatus; if (argc != 3) { @@ -442,7 +441,6 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) return TCL_ERROR; } - connStatus = conn->status; result = PQexec(conn, argv[2]); /* Transfer any notify events from libpq to Tcl event queue. */ @@ -452,8 +450,8 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) { int rId = PgSetResultId(interp, argv[1], result); - if (result->resultStatus == PGRES_COPY_IN || - result->resultStatus == PGRES_COPY_OUT) + ExecStatusType rStat = PQresultStatus(result); + if (rStat == PGRES_COPY_IN || rStat == PGRES_COPY_OUT) { connid->res_copyStatus = RES_COPY_INPROGRESS; connid->res_copy = rId; @@ -463,7 +461,7 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) else { /* error occurred during the query */ - Tcl_SetResult(interp, conn->errorMessage, TCL_VOLATILE); + Tcl_SetResult(interp, PQerrorMessage(conn), TCL_VOLATILE); return TCL_ERROR; } } @@ -481,9 +479,12 @@ Pg_exec(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) -conn the connection that produced the result -assign arrayName - assign the results to an array - -assignbyidx arrayName - assign the results to an array using the first field as a key + assign the results to an array, using subscripts of the form + (tupno,attributeName) + -assignbyidx arrayName ?appendstr? + assign the results to an array using the first field's value as a key. + All but the first field of each tuple are stored, using subscripts of the form + (field0value,attributeNameappendstr) -numTuples the number of tuples in the query -attributes @@ -509,6 +510,7 @@ Pg_result(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) int tupno; char *arrVar; char nameBuffer[256]; + const char *appendstr; if (argc < 3 || argc > 5) { @@ -564,8 +566,9 @@ Pg_result(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) /* * this assignment assigns the table of result tuples into a giant - * array with the name given in the argument, the indices of the - * array or (tupno,attrName). Note we expect field names not to + * array with the name given in the argument. + * The indices of the array are of the form (tupno,attrName). + * Note we expect field names not to * exceed a few dozen characters, so truncating to prevent buffer * overflow shouldn't be a problem. */ @@ -589,28 +592,32 @@ Pg_result(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) } else if (strcmp(opt, "-assignbyidx") == 0) { - if (argc != 4) + if (argc != 4 && argc != 5) { - Tcl_AppendResult(interp, "-assignbyidx option must be followed by a variable name", 0); + Tcl_AppendResult(interp, "-assignbyidx option requires an array name and optionally an append string",0); return TCL_ERROR; } arrVar = argv[3]; + appendstr = (argc == 5) ? (const char *) argv[4] : ""; /* * this assignment assigns the table of result tuples into a giant - * array with the name given in the argument, the indices of the - * array or (tupno,attrName). Here, we still assume PQfname won't - * exceed 200 characters, but we dare not make the same assumption - * about the data in field 0. + * array with the name given in the argument. The indices of the array + * are of the form (field0Value,attrNameappendstr). + * Here, we still assume PQfname won't exceed 200 characters, + * but we dare not make the same assumption about the data in field 0 + * nor the append string. */ for (tupno = 0; tupno < PQntuples(result); tupno++) { const char *field0 = PQgetvalue(result, tupno, 0); - char *workspace = malloc(strlen(field0) + 210); + char * workspace = malloc(strlen(field0) + strlen(appendstr) + 210); for (i = 1; i < PQnfields(result); i++) { - sprintf(workspace, "%s,%.200s", field0, PQfname(result, i)); + sprintf(workspace, "%s,%.200s%s", field0, PQfname(result,i), + appendstr); + sprintf(workspace, "%s,%.200s", field0, PQfname(result,i)); if (Tcl_SetVar2(interp, arrVar, workspace, PQgetvalue(result, tupno, i), TCL_LEAVE_ERR_MSG) == NULL) @@ -701,7 +708,7 @@ Pg_result_errReturn: "\t-status\n", "\t-conn\n", "\t-assign arrayVarName\n", - "\t-assignbyidx arrayVarName\n", + "\t-assignbyidx arrayVarName ?appendstr?\n", "\t-numTuples\n", "\t-numAttrs\n" "\t-attributes\n" @@ -1238,7 +1245,7 @@ Pg_select(ClientData cData, Tcl_Interp * interp, int argc, char **argv) if ((result = PQexec(conn, argv[2])) == 0) { /* error occurred during the query */ - Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC); + Tcl_SetResult(interp, PQerrorMessage(conn), TCL_STATIC); return TCL_ERROR; } @@ -1406,11 +1413,10 @@ Pg_listen(ClientData cData, Tcl_Interp * interp, int argc, char *argv[]) ckfree(cmd); /* Transfer any notify events from libpq to Tcl event queue. */ PgNotifyTransferEvents(connid); - if (!result || (result->resultStatus != PGRES_COMMAND_OK)) + if (PQresultStatus(result) != PGRES_COMMAND_OK) { { /* Error occurred during the execution of command */ - if (result) - PQclear(result); + PQclear(result); ckfree(callback); ckfree(caserelname); Tcl_DeleteHashEntry(entry); diff --git a/src/interfaces/libpgtcl/pgtclId.c b/src/interfaces/libpgtcl/pgtclId.c index 9377e322e7..8853c1fdbe 100644 --- a/src/interfaces/libpgtcl/pgtclId.c +++ b/src/interfaces/libpgtcl/pgtclId.c @@ -12,7 +12,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.14 1998/09/01 04:39:58 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.15 1998/09/03 02:10:44 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -33,98 +33,62 @@ PgEndCopy(Pg_ConnectionId * connid, int *errorCodePtr) connid->res_copyStatus = RES_COPY_NONE; if (PQendcopy(connid->conn)) { - connid->results[connid->res_copy]->resultStatus = PGRES_BAD_RESPONSE; + PQclear(connid->results[connid->res_copy]); + connid->results[connid->res_copy] = + PQmakeEmptyPGresult(connid->conn, PGRES_BAD_RESPONSE); connid->res_copy = -1; *errorCodePtr = EIO; return -1; } else { - connid->results[connid->res_copy]->resultStatus = PGRES_COMMAND_OK; + PQclear(connid->results[connid->res_copy]); + connid->results[connid->res_copy] = + PQmakeEmptyPGresult(connid->conn, PGRES_COMMAND_OK); connid->res_copy = -1; return 0; } } /* - * Called when reading data (via gets) for a copy to stdout. - * - * NOTE: this routine knows way more than it ought to about libpq's - * internal buffering mechanisms. + * Called when reading data (via gets) for a copy to stdout. */ -int -PgInputProc(DRIVER_INPUT_PROTO) +int PgInputProc(DRIVER_INPUT_PROTO) { - Pg_ConnectionId *connid; - PGconn *conn; - char c; - int avail; + Pg_ConnectionId *connid; + PGconn *conn; + int avail; - connid = (Pg_ConnectionId *) cData; - conn = connid->conn; + connid = (Pg_ConnectionId *)cData; + conn = connid->conn; - if (connid->res_copy < 0 || - connid->results[connid->res_copy]->resultStatus != PGRES_COPY_OUT) + if (connid->res_copy < 0 || + PQresultStatus(connid->results[connid->res_copy]) != PGRES_COPY_OUT) { *errorCodePtr = EBUSY; return -1; - } + } - /* Try to load any newly arrived data */ - conn->errorMessage[0] = '\0'; - PQconsumeInput(conn); - if (conn->errorMessage[0]) + /* Read any newly arrived data into libpq's buffer, + * thereby clearing the socket's read-ready condition. + */ + if (! PQconsumeInput(conn)) { *errorCodePtr = EIO; return -1; - } + } - /* - * Move data from libpq's buffer to Tcl's. We want to accept data only - * in units of whole lines, not partial lines. This ensures that we - * can recognize the terminator line "\\.\n". (Otherwise, if it - * happened to cross a packet/buffer boundary, we might hand the first - * one or two characters off to Tcl, which we shouldn't.) - */ + /* Move data from libpq's buffer to Tcl's. */ - conn->inCursor = conn->inStart; + avail = PQgetlineAsync(conn, buf, bufSize); - avail = bufSize; - while (avail > 0 && conn->inCursor < conn->inEnd) + if (avail < 0) { - c = conn->inBuffer[conn->inCursor++]; - *buf++ = c; - --avail; - if (c == '\n') - { - /* Got a complete line; mark the data removed from libpq */ - conn->inStart = conn->inCursor; - /* Is it the endmarker line? */ - if (bufSize - avail == 3 && buf[-3] == '\\' && buf[-2] == '.') - { - /* Yes, change state and return 0 */ - return PgEndCopy(connid, errorCodePtr); - } - /* No, return the data to Tcl */ - /* fprintf(stderr, "returning %d chars\n", bufSize - avail); */ - return bufSize - avail; - } - } + /* Endmarker detected, change state and return 0 */ + return PgEndCopy(connid, errorCodePtr); + } - /* - * We don't have a complete line. We'd prefer to leave it in libpq's - * buffer until the rest arrives, but there is a special case: what if - * the line is longer than the buffer Tcl is offering us? In that - * case we'd better hand over a partial line, else we'd get into an - * infinite loop. Do this in a way that ensures we can't misrecognize - * a terminator line later: leave last 3 characters in libpq buffer. - */ - if (avail == 0 && bufSize > 3) - { - conn->inStart = conn->inCursor - 3; - return bufSize - 3; - } - return 0; + return avail; } /* @@ -140,17 +104,13 @@ PgOutputProc(DRIVER_OUTPUT_PROTO) conn = connid->conn; if (connid->res_copy < 0 || - connid->results[connid->res_copy]->resultStatus != PGRES_COPY_IN) + PQresultStatus(connid->results[connid->res_copy]) != PGRES_COPY_IN) { *errorCodePtr = EBUSY; return -1; } - conn->errorMessage[0] = '\0'; - - PQputnbytes(conn, buf, bufSize); - - if (conn->errorMessage[0]) + if (PQputnbytes(conn, buf, bufSize)) { *errorCodePtr = EIO; return -1; @@ -398,7 +358,7 @@ getresid(Tcl_Interp * interp, char *id, Pg_ConnectionId ** connid_p) connid = (Pg_ConnectionId *) Tcl_GetChannelInstanceData(conn_chan); - if (resid < 0 || resid > connid->res_max || connid->results[resid] == NULL) + if (resid < 0 || resid >= connid->res_max || connid->results[resid] == NULL) { Tcl_SetResult(interp, "Invalid result handle", TCL_STATIC); return -1; diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 85a95c9727..5d244fa838 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.80 1998/09/01 04:40:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.81 1998/09/03 02:10:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1384,6 +1384,14 @@ PQuser(PGconn *conn) return conn->pguser; } +char * +PQpass(PGconn *conn) +{ + if (!conn) + return (char *) NULL; + return conn->pgpass; +} + char * PQhost(PGconn *conn) { @@ -1393,11 +1401,11 @@ PQhost(PGconn *conn) } char * -PQoptions(PGconn *conn) +PQport(PGconn *conn) { if (!conn) return (char *) NULL; - return conn->pgoptions; + return conn->pgport; } char * @@ -1409,11 +1417,11 @@ PQtty(PGconn *conn) } char * -PQport(PGconn *conn) +PQoptions(PGconn *conn) { if (!conn) return (char *) NULL; - return conn->pgport; + return conn->pgoptions; } ConnStatusType @@ -1442,6 +1450,14 @@ PQsocket(PGconn *conn) return conn->sock; } +int +PQbackendPID(PGconn *conn) +{ + if (!conn || conn->status != CONNECTION_OK) + return 0; + return conn->be_pid; +} + void PQtrace(PGconn *conn, FILE *debug_port) { diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 9f2651365b..fe5e7e037d 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.65 1998/09/01 04:40:05 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.66 1998/09/03 02:10:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -49,7 +49,6 @@ const char *const pgresStatus[] = { ((*(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); static void parseInput(PGconn *conn); @@ -60,13 +59,16 @@ static int getNotice(PGconn *conn); /* - * PGresult - - * returns a newly allocated, initialized PGresult + * PQmakeEmptyPGresult + * returns a newly allocated, initialized PGresult with given status * + * Note this is exported --- you wouldn't think an application would need + * to build its own PGresults, but this has proven useful in both libpgtcl + * and the Perl5 interface, so maybe it's not so unreasonable. */ -static PGresult * -makeEmptyPGresult(PGconn *conn, ExecStatusType status) +PGresult * +PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) { PGresult *result; @@ -252,13 +254,15 @@ PQsendQuery(PGconn *conn, const char *query) /* * Consume any available input from the backend + * 0 return: some kind of trouble + * 1 return: no problem */ -void +int PQconsumeInput(PGconn *conn) { if (!conn) - return; + return 0; /* * Load more data, if available. We do this no matter what state we @@ -266,9 +270,12 @@ PQconsumeInput(PGconn *conn) * application wants to get rid of a read-select condition. Note that * we will NOT block waiting for more input. */ - if (pqReadData(conn) < 0) + if (pqReadData(conn) < 0) { strcpy(conn->asyncErrorMessage, conn->errorMessage); + return 0; + } /* Parsing of the data waits till later. */ + return 1; } @@ -346,8 +353,8 @@ parseInput(PGconn *conn) { case 'C': /* command complete */ if (conn->result == NULL) - conn->result = makeEmptyPGresult(conn, - PGRES_COMMAND_OK); + conn->result = PQmakeEmptyPGresult(conn, + PGRES_COMMAND_OK); if (pqGets(conn->result->cmdStatus, CMDSTATUS_LEN, conn)) return; conn->asyncStatus = PGASYNC_READY; @@ -379,8 +386,8 @@ parseInput(PGconn *conn) DONOTICE(conn, conn->errorMessage); } if (conn->result == NULL) - conn->result = makeEmptyPGresult(conn, - PGRES_EMPTY_QUERY); + conn->result = PQmakeEmptyPGresult(conn, + PGRES_EMPTY_QUERY); conn->asyncStatus = PGASYNC_READY; break; case 'K': /* secret key data from the backend */ @@ -499,7 +506,7 @@ getRowDescriptions(PGconn *conn) int nfields; int i; - result = makeEmptyPGresult(conn, PGRES_TUPLES_OK); + result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); /* parseInput already read the 'T' label. */ /* the next two bytes are the number of fields */ @@ -536,7 +543,7 @@ getRowDescriptions(PGconn *conn) } result->attDescs[i].name = strdup(typName); result->attDescs[i].typid = typid; - result->attDescs[i].typlen = (short) typlen; + result->attDescs[i].typlen = typlen; result->attDescs[i].atttypmod = atttypmod; } @@ -695,7 +702,7 @@ PQgetResult(PGconn *conn) pqClearAsyncResult(conn); conn->asyncStatus = PGASYNC_IDLE; /* conn->errorMessage has been set by pqWait or pqReadData. */ - return makeEmptyPGresult(conn, PGRES_FATAL_ERROR); + return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); } /* Parse it. */ parseInput(conn); @@ -719,22 +726,22 @@ PQgetResult(PGconn *conn) conn->result = NULL;/* handing over ownership to caller */ conn->curTuple = NULL; /* just in case */ if (!res) - res = makeEmptyPGresult(conn, PGRES_FATAL_ERROR); + res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); strcpy(conn->errorMessage, conn->asyncErrorMessage); /* Set the state back to BUSY, allowing parsing to proceed. */ conn->asyncStatus = PGASYNC_BUSY; break; case PGASYNC_COPY_IN: - res = makeEmptyPGresult(conn, PGRES_COPY_IN); + res = PQmakeEmptyPGresult(conn, PGRES_COPY_IN); break; case PGASYNC_COPY_OUT: - res = makeEmptyPGresult(conn, PGRES_COPY_OUT); + res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT); break; default: sprintf(conn->errorMessage, "PQgetResult: Unexpected asyncStatus %d\n", (int) conn->asyncStatus); - res = makeEmptyPGresult(conn, PGRES_FATAL_ERROR); + res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); break; } @@ -880,6 +887,9 @@ PQnotifies(PGconn *conn) * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips * the terminating \n (like gets(3)). * + * CAUTION: the caller is responsible for detecting the end-of-copy signal + * (a line containing just "\.") when using this routine. + * * RETURNS: * EOF if it is detected or invalid arguments are given * 0 if EOL is reached (i.e., \n has been read) @@ -936,26 +946,114 @@ PQgetline(PGconn *conn, char *s, int maxlen) return result; } +/* + * PQgetlineAsync - gets a newline-terminated string without blocking. + * + * This routine is for applications that want to do "COPY to stdout" + * asynchronously, that is without blocking. Having issued the COPY command + * and gotten a PGRES_COPY_OUT response, the app should call PQconsumeInput + * and this routine until the end-of-data signal is detected. Unlike + * PQgetline, this routine takes responsibility for detecting end-of-data. + * + * On each call, PQgetlineAsync will return data if a complete newline- + * terminated data line is available in libpq's input buffer, or if the + * incoming data line is too long to fit in the buffer offered by the caller. + * Otherwise, no data is returned until the rest of the line arrives. + * + * If -1 is returned, the end-of-data signal has been recognized (and removed + * from libpq's input buffer). The caller *must* next call PQendcopy and + * then return to normal processing. + * + * RETURNS: + * -1 if the end-of-copy-data marker has been recognized + * 0 if no data is available + * >0 the number of bytes returned. + * The data returned will not extend beyond a newline character. If possible + * a whole line will be returned at one time. But if the buffer offered by + * the caller is too small to hold a line sent by the backend, then a partial + * data line will be returned. This can be detected by testing whether the + * last returned byte is '\n' or not. + * The returned string is *not* null-terminated. + */ + +int +PQgetlineAsync(PGconn *conn, char *buffer, int bufsize) +{ + int avail; + + if (!conn || conn->asyncStatus != PGASYNC_COPY_OUT) + return -1; /* we are not doing a copy... */ + + /* + * Move data from libpq's buffer to the caller's. + * We want to accept data only in units of whole lines, + * not partial lines. This ensures that we can recognize + * the terminator line "\\.\n". (Otherwise, if it happened + * to cross a packet/buffer boundary, we might hand the first + * one or two characters off to the caller, which we shouldn't.) + */ + + conn->inCursor = conn->inStart; + + avail = bufsize; + while (avail > 0 && conn->inCursor < conn->inEnd) + { + char c = conn->inBuffer[conn->inCursor++]; + *buffer++ = c; + --avail; + if (c == '\n') + { + /* Got a complete line; mark the data removed from libpq */ + conn->inStart = conn->inCursor; + /* Is it the endmarker line? */ + if (bufsize-avail == 3 && buffer[-3] == '\\' && buffer[-2] == '.') + return -1; + /* No, return the data line to the caller */ + return bufsize - avail; + } + } + + /* + * We don't have a complete line. + * We'd prefer to leave it in libpq's buffer until the rest arrives, + * but there is a special case: what if the line is longer than the + * buffer the caller is offering us? In that case we'd better hand over + * a partial line, else we'd get into an infinite loop. + * Do this in a way that ensures we can't misrecognize a terminator + * line later: leave last 3 characters in libpq buffer. + */ + if (avail == 0 && bufsize > 3) + { + conn->inStart = conn->inCursor - 3; + return bufsize - 3; + } + return 0; +} + /* * PQputline -- sends a string to the backend. + * Returns 0 if OK, EOF if not. * * Chiefly here so that applications can use "COPY from stdin". */ -void +int PQputline(PGconn *conn, const char *s) { - if (conn && conn->sock >= 0) - (void) pqPutnchar(s, strlen(s), conn); + if (!conn || conn->sock < 0) + return EOF; + return pqPutnchar(s, strlen(s), conn); } /* * PQputnbytes -- like PQputline, but buffer need not be null-terminated. + * Returns 0 if OK, EOF if not. */ -void +int PQputnbytes(PGconn *conn, const char *buffer, int nbytes) { - if (conn && conn->sock >= 0) - (void) pqPutnchar(buffer, nbytes, conn); + if (!conn || conn->sock < 0) + return EOF; + return pqPutnchar(buffer, nbytes, conn); } /* @@ -1155,7 +1253,7 @@ PQfn(PGconn *conn, sprintf(conn->errorMessage, "FATAL: PQfn: protocol error: id=%x\n", id); conn->inStart = conn->inCursor; - return makeEmptyPGresult(conn, PGRES_FATAL_ERROR); + return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); } break; case 'E': /* error return */ @@ -1176,13 +1274,13 @@ PQfn(PGconn *conn, case 'Z': /* backend is ready for new query */ /* consume the message and exit */ conn->inStart = conn->inCursor; - return makeEmptyPGresult(conn, status); + return PQmakeEmptyPGresult(conn, status); default: /* The backend violates the protocol. */ sprintf(conn->errorMessage, "FATAL: PQfn: protocol error: id=%x\n", id); conn->inStart = conn->inCursor; - return makeEmptyPGresult(conn, PGRES_FATAL_ERROR); + return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); } /* Completed this message, keep going */ conn->inStart = conn->inCursor; @@ -1190,7 +1288,7 @@ PQfn(PGconn *conn, } /* we fall out of the loop only upon failing to read data */ - return makeEmptyPGresult(conn, PGRES_FATAL_ERROR); + return PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); } @@ -1220,6 +1318,14 @@ PQnfields(PGresult *res) return res->numAttributes; } +int +PQbinaryTuples(PGresult *res) +{ + if (!res) + return 0; + return res->binary; +} + /* * Helper routines to range-check field numbers and tuple numbers. * Return TRUE if OK, FALSE if not @@ -1332,7 +1438,7 @@ PQftype(PGresult *res, int field_num) return InvalidOid; } -short +int PQfsize(PGresult *res, int field_num) { if (!check_field_number("PQfsize", res, field_num)) diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index d091f6e6db..2a844167a7 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -24,7 +24,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.20 1998/09/01 04:40:08 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.21 1998/09/03 02:10:50 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -174,15 +174,14 @@ pqGetnchar(char *s, int len, PGconn *conn) conn->inCursor += len; if (conn->Pfdebug) - fprintf(conn->Pfdebug, "From backend (%d)> %s\n", len, s); + fprintf(conn->Pfdebug, "From backend (%d)> %.*s\n", len, len, s); return 0; } /* --------------------------------------------------------------------- */ /* pqPutnchar: - send a string of exactly len bytes - The buffer should have a terminating null, but it's not sent. + send a string of exactly len bytes, no null termination needed */ int pqPutnchar(const char *s, int len, PGconn *conn) @@ -191,7 +190,7 @@ pqPutnchar(const char *s, int len, PGconn *conn) return EOF; if (conn->Pfdebug) - fprintf(conn->Pfdebug, "To backend> %s\n", s); + fprintf(conn->Pfdebug, "To backend> %.*s\n", len, s); return 0; } diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 993536023b..1f07baba11 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-fe.h,v 1.40 1998/09/01 04:40:10 momjian Exp $ + * $Id: libpq-fe.h,v 1.41 1998/09/03 02:10:51 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,20 +20,10 @@ extern "C" #endif #include -/* these wouldn't need to be included if PGSockAddr weren't exported: */ -#ifdef WIN32 -#include -#else -#include -#include -#include -#endif -/* ---------------- - * include stuff common to fe and be - * ---------------- +/* postgres_ext.h defines the backend's externally visible types, + * such as Oid. */ #include "postgres_ext.h" -#include "lib/dllist.h" /* Application-visible enum types */ @@ -46,11 +36,11 @@ extern "C" typedef enum { PGRES_EMPTY_QUERY = 0, - PGRES_COMMAND_OK, /* a query command that doesn't return */ - /* 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 result tuples */ + PGRES_COMMAND_OK, /* a query command that doesn't return 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 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 @@ -59,193 +49,45 @@ extern "C" PGRES_FATAL_ERROR } ExecStatusType; -/* string descriptions of the ExecStatusTypes */ - extern const char *const pgresStatus[]; +/* String descriptions of the ExecStatusTypes */ + extern const char * const pgresStatus[]; -/* - * POSTGRES backend dependent Constants. +/* PGconn encapsulates a connection to the backend. + * The contents of this struct are not supposed to be known to applications. */ + typedef struct pg_conn PGconn; -/* ERROR_MSG_LENGTH should really be the same as ELOG_MAXLEN in utils/elog.h*/ -#define ERROR_MSG_LENGTH 4096 -#define CMDSTATUS_LEN 40 - -/* 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. +/* PGresult encapsulates the result of a query (or more precisely, of a single + * SQL command --- a query string given to PQsendQuery can contain multiple + * commands and thus return multiple PGresult objects). + * The contents of this struct are not supposed to be known to applications. */ + typedef struct pg_result PGresult; - typedef struct pgresAttDesc - { - char *name; /* type name */ - Oid typid; /* type id */ - short typlen; /* type size */ - int atttypmod; /* type-specific modifier info */ - } PGresAttDesc; - -/* use char* for Attribute values, - ASCII tuples are guaranteed to be null-terminated - For binary tuples, the first four bytes of the value is the size, - and the bytes afterwards are the value. The binary value is - not guaranteed to be null-terminated. In fact, it can have embedded nulls +/* PGnotify represents the occurrence of a NOTIFY message. + * Ideally this would be an opaque typedef, but it's so simple that it's + * unlikely to change. */ - -#define NULL_LEN (-1) /* pg_result len for NULL value */ - - typedef struct pgresAttValue - { - int len; /* length in bytes of the value */ - char *value; /* actual value */ - } PGresAttValue; - - struct pg_conn; /* forward reference */ - - typedef struct pg_result - { - int ntups; - int numAttributes; - PGresAttDesc *attDescs; - PGresAttValue **tuples; /* each PGresTuple is an array of - * PGresAttValue's */ - int tupArrSize; /* size of tuples array allocated */ - ExecStatusType resultStatus; - char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the - * last insert query */ - int binary; /* binary tuple values if binary == 1, - * otherwise ASCII */ - 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 */ + int be_pid; /* process id of backend */ } PGnotify; -/* PQnoticeProcessor is a typedef for a callback function type */ - typedef void (*PQnoticeProcessor) (void *arg, const char *message); - -/* 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; - -/* generic socket address type for PGconn connection information. - * Really shouldn't be visible to users */ - typedef union PGSockAddr - { - struct sockaddr sa; - struct sockaddr_in in; -#ifndef WIN32 - struct sockaddr_un un; -#endif - } PGSockAddr; - -/* 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, - * but we might break some existing applications if we tried to make it - * completely opaque. +/* PQnoticeProcessor is the function type for the notice-message callback. */ - 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; - - /* Callback procedure for notice/error message processing */ - PQnoticeProcessor noticeHook; - void *noticeArg; - - /* 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 */ - PGSockAddr laddr; /* Local address */ - PGSockAddr raddr; /* Remote address */ - int raddr_len; /* Length of remote address */ - - /* Miscellaneous stuff */ - int be_pid; /* PID of backend --- needed for cancels */ - int be_key; /* key of backend --- needed for cancels */ - 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; - - /* - * We can't use the conventional "bool", because we are designed to be - * included in a user's program, and user may already have that type - * defined. Pqbool, on the other hand, is unlikely to be used. - */ - - typedef char pqbool; +typedef void (*PQnoticeProcessor) (void * arg, const char * message); /* Print options for PQprint() */ + + /* + * We can't use the conventional "bool", because we are designed to be + * included in a user's program, and user may already have that type + * defined. Pqbool, on the other hand, is unlikely to be used. + */ + typedef char pqbool; typedef struct _PQprintOpt { @@ -263,6 +105,26 @@ extern "C" * field names */ } PQprintOpt; +/* ---------------- + * Structure for the conninfo parameter definitions returned by PQconndefaults + * ---------------- + */ + typedef struct _PQconninfoOption + { + char *keyword; /* The keyword of the option */ + char *environ; /* Fallback environment variable name */ + char *compiled; /* Fallback compiled in default value */ + char *val; /* Options value */ + char *label; /* Label for field in connect dialog */ + char *dispchar; /* Character to display for this field */ + /* in a connect dialog. Values are: */ + /* "" Display entered value as is */ + /* "*" Password field - hide value */ + /* "D" Debug options - don't */ + /* create a field by default */ + int dispsize; /* Field size in characters for dialog */ + } PQconninfoOption; + /* ---------------- * PQArgBlock -- structure for PQfn() arguments * ---------------- @@ -278,26 +140,6 @@ extern "C" } u; } PQArgBlock; -/* ---------------- - * Structure for the conninfo parameter definitions returned by PQconndefaults - * ---------------- - */ - typedef struct _PQconninfoOption - { - char *keyword; /* The keyword of the option */ - char *environ; /* Fallback environment variable name */ - char *compiled; /* Fallback compiled in default value */ - char *val; /* Options value */ - char *label; /* Label for field in connect dialog */ - char *dispchar; /* Character to display for this field */ - /* in a connect dialog. Values are: */ - /* "" Display entered value as is */ - /* "*" Password field - hide value */ - /* "D" Debug options - don't */ - /* create a field by default */ - int dispsize; /* Field size in characters for dialog */ - } PQconninfoOption; - /* ---------------- * Exported functions of libpq * ---------------- @@ -332,13 +174,16 @@ extern "C" /* Accessor functions for PGconn objects */ extern char *PQdb(PGconn *conn); extern char *PQuser(PGconn *conn); + extern char *PQpass(PGconn *conn); extern char *PQhost(PGconn *conn); - extern char *PQoptions(PGconn *conn); extern char *PQport(PGconn *conn); extern char *PQtty(PGconn *conn); + extern char *PQoptions(PGconn *conn); extern ConnStatusType PQstatus(PGconn *conn); extern char *PQerrorMessage(PGconn *conn); extern int PQsocket(PGconn *conn); + extern int PQsocket(PGconn *conn); + extern int PQbackendPID(PGconn *conn); /* Enable/disable tracing */ extern void PQtrace(PGconn *conn, FILE *debug_port); @@ -361,12 +206,13 @@ extern "C" /* Routines for managing an asychronous query */ extern int PQisBusy(PGconn *conn); - extern void PQconsumeInput(PGconn *conn); + extern int PQconsumeInput(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 void PQputnbytes(PGconn *conn, const char *buffer, int nbytes); + extern int PQputline(PGconn *conn, const char *string); + extern int PQgetlineAsync(PGconn *conn, char *buffer, int bufsize); + extern int PQputnbytes(PGconn *conn, const char *buffer, int nbytes); extern int PQendcopy(PGconn *conn); /* @@ -385,10 +231,11 @@ extern "C" extern ExecStatusType PQresultStatus(PGresult *res); extern int PQntuples(PGresult *res); extern int PQnfields(PGresult *res); + extern int PQbinaryTuples(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 int PQfsize(PGresult *res, int field_num); extern int PQfmod(PGresult *res, int field_num); extern char *PQcmdStatus(PGresult *res); extern const char *PQoidStatus(PGresult *res); @@ -400,6 +247,9 @@ extern "C" /* Delete a PGresult */ extern void PQclear(PGresult *res); + /* Make an empty PGresult with given status (some apps find this useful) */ + extern PGresult * PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); + /* === in fe-print.c === */ extern void PQprint(FILE *fout, /* output stream */ diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index c338fa9233..4c877de2d2 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -4,9 +4,14 @@ * This file contains internal definitions meant to be used only by * the frontend libpq library, not by applications that call it. * + * An application can include this file if it wants to bypass the + * official API defined by libpq-fe.h, but code that does so is much + * more likely to break across PostgreSQL releases than code that uses + * only the official API. + * * Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.2 1998/09/01 04:40:12 momjian Exp $ + * $Id: libpq-int.h,v 1.3 1998/09/03 02:10:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +26,7 @@ * ---------------- */ #include "libpq/pqcomm.h" +#include "lib/dllist.h" /* libpq supports this version of the frontend/backend protocol. * @@ -35,6 +41,147 @@ #define PG_PROTOCOL_LIBPQ PG_PROTOCOL(2,0) +/* + * POSTGRES backend dependent Constants. + */ + +/* ERROR_MSG_LENGTH should really be the same as ELOG_MAXLEN in utils/elog.h*/ +#define ERROR_MSG_LENGTH 4096 +#define CMDSTATUS_LEN 40 + +/* 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 pgresAttDesc + { + char *name; /* type name */ + Oid typid; /* type id */ + int typlen; /* type size */ + int atttypmod; /* type-specific modifier info */ + } PGresAttDesc; + +/* use char* for Attribute values, + ASCII tuples are guaranteed to be null-terminated + For binary tuples, the first four bytes of the value is the size, + and the bytes afterwards are the value. The binary value is + not guaranteed to be null-terminated. In fact, it can have embedded nulls + */ + +#define NULL_LEN (-1) /* pg_result len for NULL value */ + + typedef struct pgresAttValue + { + int len; /* length in bytes of the value */ + char *value; /* actual value */ + } PGresAttValue; + + struct pg_result + { + int ntups; + int numAttributes; + PGresAttDesc *attDescs; + PGresAttValue **tuples; /* each PGresTuple is an array of + * PGresAttValue's */ + int tupArrSize; /* size of tuples array allocated */ + ExecStatusType resultStatus; + char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the + * last insert query */ + int binary; /* binary tuple values if binary == 1, + * otherwise ASCII */ + PGconn *conn; /* connection we did the query on */ + }; + +/* PGAsyncStatusType defines the state of the query-execution state machine */ + 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. */ + 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 stores all the state data associated with a single connection + * to a backend. + */ + 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; + + /* Callback procedure for notice/error message processing */ + PQnoticeProcessor noticeHook; + void *noticeArg; + + /* 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 */ + int raddr_len; /* Length of remote address */ + + /* Miscellaneous stuff */ + int be_pid; /* PID of backend --- needed for cancels */ + int be_key; /* key of backend --- needed for cancels */ + 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]; + }; + /* ---------------- * Internal functions of libpq * Functions declared here need to be visible across files of libpq, diff --git a/src/interfaces/libpq/libpqdll.def b/src/interfaces/libpq/libpqdll.def index f1a0b324d1..fee6f217d6 100644 --- a/src/interfaces/libpq/libpqdll.def +++ b/src/interfaces/libpq/libpqdll.def @@ -9,52 +9,57 @@ EXPORTS PQrequestCancel @ 6 PQdb @ 7 PQuser @ 8 - PQhost @ 9 - PQoptions @ 10 + PQpass @ 9 + PQhost @ 10 PQport @ 11 PQtty @ 12 - PQstatus @ 13 - PQerrorMessage @ 14 - PQsocket @ 15 - PQtrace @ 16 - PQuntrace @ 17 - PQsetNoticeProcessor @ 18 - PQexec @ 19 - PQnotifies @ 20 - PQsendQuery @ 21 - PQgetResult @ 22 - PQisBusy @ 23 - PQconsumeInput @ 24 - PQgetline @ 25 - PQputline @ 26 - PQputnbytes @ 27 - PQendcopy @ 28 - PQfn @ 29 - PQresultStatus @ 30 - PQntuples @ 31 - PQnfields @ 32 - PQfname @ 33 - PQfnumber @ 34 - PQftype @ 35 - PQfsize @ 36 - PQfmod @ 37 - PQcmdStatus @ 38 - PQoidStatus @ 39 - PQcmdTuples @ 40 - PQgetvalue @ 41 - PQgetlength @ 42 - PQgetisnull @ 43 - PQclear @ 44 - PQprint @ 45 - PQdisplayTuples @ 46 - PQprintTuples @ 47 - lo_open @ 48 - lo_close @ 49 - lo_read @ 50 - lo_write @ 51 - lo_lseek @ 52 - lo_creat @ 53 - lo_tell @ 54 - lo_unlink @ 55 - lo_import @ 56 - lo_export @ 57 + PQoptions @ 13 + PQstatus @ 14 + PQerrorMessage @ 15 + PQsocket @ 16 + PQbackendPID @ 17 + PQtrace @ 18 + PQuntrace @ 19 + PQsetNoticeProcessor @ 20 + PQexec @ 21 + PQnotifies @ 22 + PQsendQuery @ 23 + PQgetResult @ 24 + PQisBusy @ 25 + PQconsumeInput @ 26 + PQgetline @ 27 + PQputline @ 28 + PQgetlineAsync @ 29 + PQputnbytes @ 30 + PQendcopy @ 31 + PQfn @ 32 + PQresultStatus @ 33 + PQntuples @ 34 + PQnfields @ 35 + PQbinaryTuples @ 36 + PQfname @ 37 + PQfnumber @ 38 + PQftype @ 39 + PQfsize @ 40 + PQfmod @ 41 + PQcmdStatus @ 42 + PQoidStatus @ 43 + PQcmdTuples @ 44 + PQgetvalue @ 45 + PQgetlength @ 46 + PQgetisnull @ 47 + PQclear @ 48 + PQmakeEmptyPGresult @ 49 + PQprint @ 50 + PQdisplayTuples @ 51 + PQprintTuples @ 52 + lo_open @ 53 + lo_close @ 54 + lo_read @ 55 + lo_write @ 56 + lo_lseek @ 57 + lo_creat @ 58 + lo_tell @ 59 + lo_unlink @ 60 + lo_import @ 61 + lo_export @ 62 diff --git a/src/interfaces/perl5/Pg.xs b/src/interfaces/perl5/Pg.xs index 2de5e01073..f66936cbc1 100644 --- a/src/interfaces/perl5/Pg.xs +++ b/src/interfaces/perl5/Pg.xs @@ -1,6 +1,6 @@ /*------------------------------------------------------- * - * $Id: Pg.xs,v 1.7 1998/06/01 16:41:19 mergl Exp $ + * $Id: Pg.xs,v 1.8 1998/09/03 02:10:56 momjian Exp $ * * Copyright (c) 1997, 1998 Edmund Mergl * @@ -318,7 +318,7 @@ PQexec(conn, query) char * query CODE: RETVAL = PQexec(conn, query); - if (! RETVAL) { RETVAL = (PGresult *)calloc(1, sizeof(PGresult)); } + if (! RETVAL) { RETVAL = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); } OUTPUT: RETVAL @@ -752,7 +752,7 @@ PQexec(conn, query) if (RETVAL) { RETVAL->result = PQexec((PGconn *)conn, query); if (!RETVAL->result) { - RETVAL->result = (PG_result)calloc(1, sizeof(PGresult)); + RETVAL->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); } } OUTPUT: