From f0ed4311b6f44dac079ae720b370413e948f30d5 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Sat, 17 Aug 2002 12:33:18 +0000 Subject: [PATCH] Add libpq connection timeout parameter. Denis A Ustimenko --- doc/src/sgml/libpq.sgml | 11 +++++- src/interfaces/libpq/fe-connect.c | 66 +++++++++++++++++++++++++++++-- src/interfaces/libpq/fe-misc.c | 24 +++++++++-- src/interfaces/libpq/libpq-int.h | 5 ++- 4 files changed, 97 insertions(+), 9 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index ca2d7e45e1..4d604b2ad4 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,5 +1,5 @@ @@ -171,6 +171,15 @@ PGconn *PQconnectdb(const char *conninfo) + + connect_timeout + + + Time space in seconds given to connect routine. Zero or not set means infinite. + + + + options diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 40474657c2..25030e250f 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.191 2002/08/15 02:56:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.192 2002/08/17 12:33:17 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -110,6 +110,9 @@ static const PQconninfoOption PQconninfoOptions[] = { {"password", "PGPASSWORD", DefaultPassword, NULL, "Database-Password", "*", 20}, + {"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL, + "Connect-timeout", "", 10}, /* strlen( INT32_MAX) == 10 */ + {"dbname", "PGDATABASE", NULL, NULL, "Database-Name", "", 20}, @@ -302,6 +305,8 @@ PQconnectStart(const char *conninfo) conn->pguser = tmp ? strdup(tmp) : NULL; tmp = conninfo_getval(connOptions, "password"); conn->pgpass = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "connect_timeout"); + conn->connect_timeout = tmp ? strdup(tmp) : NULL; #ifdef USE_SSL tmp = conninfo_getval(connOptions, "requiressl"); if (tmp && tmp[0] == '1') @@ -1052,12 +1057,39 @@ connectDBComplete(PGconn *conn) { PostgresPollingStatusType flag = PGRES_POLLING_WRITING; + struct timeval remains, *rp = NULL, finish_time, start_time; + if (conn == NULL || conn->status == CONNECTION_BAD) return 0; - for (;;) + /* + * Prepare to time calculations, if connect_timeout isn't zero. + */ + if (conn->connect_timeout != NULL) { + remains.tv_sec = atoi(conn->connect_timeout); + if (!remains.tv_sec) + { + conn->status = CONNECTION_BAD; + return 0; + } + remains.tv_usec = 0; + rp = &remains; + } + + + while (NULL == rp || remains.tv_sec > 0 || remains.tv_sec == 0 && remains.tv_usec > 0) + { /* + * If connecting timeout is set, get current time. + */ + if ( NULL != rp && -1 == gettimeofday(&start_time, NULL)) + { + conn->status = CONNECTION_BAD; + return 0; + } + + /* * Wait, if necessary. Note that the initial state (just after * PQconnectStart) is to wait for the socket to select for * writing. @@ -1071,7 +1103,7 @@ connectDBComplete(PGconn *conn) return 1; /* success! */ case PGRES_POLLING_READING: - if (pqWait(1, 0, conn)) + if (pqWaitTimed(1, 0, conn, rp)) { conn->status = CONNECTION_BAD; return 0; @@ -1079,7 +1111,7 @@ connectDBComplete(PGconn *conn) break; case PGRES_POLLING_WRITING: - if (pqWait(0, 1, conn)) + if (pqWaitTimed(0, 1, conn, rp)) { conn->status = CONNECTION_BAD; return 0; @@ -1096,7 +1128,31 @@ connectDBComplete(PGconn *conn) * Now try to advance the state machine. */ flag = PQconnectPoll(conn); + + /* + * If connecting timeout is set, calculate remain time. + */ + if (NULL != rp) { + if (-1 == gettimeofday(&finish_time, NULL)) + { + conn->status = CONNECTION_BAD; + return 0; + } + if (0 > (finish_time.tv_usec -= start_time.tv_usec)) + { + remains.tv_sec++; + finish_time.tv_usec += 1000000; + } + if (0 > (remains.tv_usec -= finish_time.tv_usec)) + { + remains.tv_sec--; + remains.tv_usec += 1000000; + } + remains.tv_sec -= finish_time.tv_sec - start_time.tv_sec; + } } + conn->status = CONNECTION_BAD; + return 0; } /* ---------------- @@ -1928,6 +1984,8 @@ freePGconn(PGconn *conn) free(conn->pguser); if (conn->pgpass) free(conn->pgpass); + if (conn->connect_timeout) + free(conn->connect_timeout); /* Note that conn->Pfdebug is not ours to close or free */ if (conn->notifyList) DLFreeList(conn->notifyList); diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index 1799b3c02d..be91d796c0 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -25,7 +25,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.76 2002/06/20 20:29:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.77 2002/08/17 12:33:18 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -775,11 +775,20 @@ pqFlush(PGconn *conn) */ int pqWait(int forRead, int forWrite, PGconn *conn) +{ + return pqWaitTimed( forRead, forWrite, conn, (const struct timeval *) NULL); +} + +int +pqWaitTimed(int forRead, int forWrite, PGconn *conn, const struct timeval *timeout) { fd_set input_mask; fd_set output_mask; fd_set except_mask; + struct timeval tmp_timeout; + struct timeval *ptmp_timeout = NULL; + if (conn->sock < 0) { printfPQExpBuffer(&conn->errorMessage, @@ -807,9 +816,18 @@ retry5: if (forWrite) FD_SET(conn->sock, &output_mask); FD_SET(conn->sock, &except_mask); - if (select(conn->sock + 1, &input_mask, &output_mask, &except_mask, - (struct timeval *) NULL) < 0) + + if (NULL != timeout) { + /* + * select may modify timeout argument on some platforms use copy + */ + tmp_timeout = *timeout; + ptmp_timeout = &tmp_timeout; + } + if (select(conn->sock + 1, &input_mask, &output_mask, + &except_mask, ptmp_timeout) < 0) + { if (SOCK_ERRNO == EINTR) goto retry5; printfPQExpBuffer(&conn->errorMessage, diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index aee2fce13c..2ee819ff1e 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.52 2002/07/20 05:43:31 momjian Exp $ + * $Id: libpq-int.h,v 1.53 2002/08/17 12:33:18 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -286,6 +286,8 @@ struct pg_conn PQExpBufferData workBuffer; /* expansible string */ int client_encoding; /* encoding id */ + + char *connect_timeout; }; /* String descriptions of the ExecStatusTypes. @@ -332,6 +334,7 @@ extern int pqReadData(PGconn *conn); extern int pqFlush(PGconn *conn); extern int pqSendSome(PGconn *conn); extern int pqWait(int forRead, int forWrite, PGconn *conn); +extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn, const struct timeval* timeout); extern int pqReadReady(PGconn *conn); extern int pqWriteReady(PGconn *conn);