From: Bruce Momjian <bruce@momjian.us>
Date: Sat, 17 Aug 2002 12:33:18 +0000 (+0000)
Subject: Add libpq connection timeout parameter.
X-Git-Tag: REL7_3~879
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f0ed4311b6f44dac079ae720b370413e948f30d5;p=postgresql

Add libpq connection timeout parameter.

Denis A Ustimenko
---

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 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.92 2002/08/15 03:00:59 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.93 2002/08/17 12:33:17 momjian Exp $
 -->
 
  <chapter id="libpq">
@@ -171,6 +171,15 @@ PGconn *PQconnectdb(const char *conninfo)
      </listitem>
     </varlistentry>
 
+    <varlistentry>
+     <term><literal>connect_timeout</literal></term>
+     <listitem>
+     <para>
+      Time space in seconds given to connect routine. Zero or not set means infinite.
+     </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry>
      <term><literal>options</literal></term>
      <listitem>
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);