From bac80cfdb8ac70b0463ae99e39cf32dc55ffc93e Mon Sep 17 00:00:00 2001
From: "Stephen R. van den Berg" <srb@cuci.nl>
Date: Mon, 15 Feb 2010 19:38:48 +0100
Subject: [PATCH] Fix pgsql autoreconnect behaviour.

Rev: CHANGES:1.205
Rev: lib/modules/Sql.pmod/pgsql.pike:1.74
---
 CHANGES                         |  2 ++
 lib/modules/Sql.pmod/pgsql.pike | 54 +++++++++++++++++++++------------
 2 files changed, 37 insertions(+), 19 deletions(-)

diff --git a/CHANGES b/CHANGES
index 87a7905cb0..ee3399f0cf 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,6 +21,8 @@ Bug fixes
 o Fixed dangling cache reference in prepared statements when using CREATE
   statements via Sql.pgsql.
 
+o Fixed broken autoreconnect logic in Sql.pgsql.
+
 o Improved widestring support for Parser.Tabular.
 
 o Fixed segfault in combine_path_nt on windows when the first char
diff --git a/lib/modules/Sql.pmod/pgsql.pike b/lib/modules/Sql.pmod/pgsql.pike
index deb5d0315d..2790dff7e9 100644
--- a/lib/modules/Sql.pmod/pgsql.pike
+++ b/lib/modules/Sql.pmod/pgsql.pike
@@ -99,6 +99,7 @@ private int warningsdropcount; // Number of uncollected warnings
 private int prepstmtused;    // Number of times prepared statements were used
 private int warningscollected;
 private int invalidatecache;
+private int connectionclosed;
 
 private string host, database, user, pass;
 private int port;
@@ -538,6 +539,17 @@ final private string pinpointerror(void|string query,void|string offset)
   return MARKSTART+(k>1?query[..k-2]:"")+MARKERROR+query[k-1..]+MARKEND;
 }
 
+private void phasedreconnect()
+{ connectionclosed=1;
+  if(!reconnect(1))
+  { sleep(RECONNECTDELAY);
+    if(!reconnect(1))
+    { sleep(RECONNECTBACKOFF);
+      reconnect(1);
+    }
+  }
+}
+
 final int _decodemsg(void|state waitforstate)
 {
 #ifdef DEBUG
@@ -889,6 +901,11 @@ final int _decodemsg(void|state waitforstate)
       case 'E':PD("ErrorResponse\n");
       { mapping(string:string) msgresponse;
 	msgresponse=getresponse();
+        void preplastmessage()
+        { lastmessage=({sprintf("%s %s:%s %s\n (%s:%s:%s)",
+	   msgresponse->S,msgresponse->C,msgresponse->P||"",msgresponse->M,
+	   msgresponse->F||"",msgresponse->R||"",msgresponse->L||"")});
+        };
 	warningsdropcount+=warningscollected;
 	warningscollected=0;
 	switch(msgresponse->C)
@@ -897,14 +914,16 @@ final int _decodemsg(void|state waitforstate)
 	    USERERROR(a2nls(lastmessage
 	     +({pinpointerror(_c.portal->_query,msgresponse->P)})
 	     +showbindings()));
+	  case "57P01":case "57P02":case "57P03":
+	    preplastmessage();phasedreconnect();
+            PD(a2nls(lastmessage));
+	    USERERROR(a2nls(lastmessage));
 	  case "08P01":case "42P05":
 	    errtype=protocolerror;
 	  case "XX000":case "42883":case "42P01":
 	    invalidatecache=1;
 	  default:
-	    lastmessage=({sprintf("%s %s:%s %s\n (%s:%s:%s)",
-	     msgresponse->S,msgresponse->C,msgresponse->P||"",msgresponse->M,
-	     msgresponse->F||"",msgresponse->R||"",msgresponse->L||"")});
+	    preplastmessage();
 	    if(msgresponse->D)
 	      lastmessage+=({msgresponse->D});
 	    if(msgresponse->H)
@@ -960,14 +979,7 @@ final int _decodemsg(void|state waitforstate)
 	}
 	else
 	{ array(string) msg=lastmessage;
-	  if(!reconnect(1))
-	  { sleep(RECONNECTDELAY);
-	    if(!reconnect(1))
-	    { sleep(RECONNECTBACKOFF);
-	      reconnect(1);
-	    }
-	  }
-	  msg+=lastmessage;
+          phasedreconnect();msg+=lastmessage;
 	  string s=sizeof(msg)?a2nls(msg):"";
 	  ERROR("%sConnection lost to database %s@%s:%d/%s %d\n",
 	   s,user,host,port,database,backendpid);
@@ -982,9 +994,7 @@ final int _decodemsg(void|state waitforstate)
 	break;
       case protocolerror:
 	array(string) msg=lastmessage;
-	lastmessage=({});
-	reconnect(1);
-	msg+=lastmessage;
+	lastmessage=({});phasedreconnect();msg+=lastmessage;
 	string s=sizeof(msg)?a2nls(msg):"";
 	ERROR("%sProtocol error with database %s\n",s,host_info());
 	break;
@@ -1706,6 +1716,9 @@ object big_query(string q,void|mapping(string|int:mixed) bindings,
   }					  // pgsql_result autoassigns to portal
   else
     tp=UNDEFINED;
+  connectionclosed=0;
+  for(;;)
+   {
   .pgsql_util.pgsql_result(this,q,_fetchlimit,portalbuffersize,_alltyped,from);
   if(unnamedportalinuse)
     portalname=PORTALPREFIX+(string)pportalcount++;
@@ -1716,7 +1729,7 @@ object big_query(string q,void|mapping(string|int:mixed) bindings,
   portalsinflight++; portalsopened++;
   clearmessage=1;
   mixed err;
-  if(err = catch
+  if(!(err = catch
     { if(!sizeof(preparedname) || !tp || !tp->preparedname)
       { PD("Parse statement %s\n",preparedname);
 	// Even though the protocol doesn't require the Parse command to be
@@ -1910,11 +1923,14 @@ object big_query(string q,void|mapping(string|int:mixed) bindings,
 	}
 	tprepared=tp;
       }
-    })
-  { PD("%O\n",err);
-    resync(1);
-    backendstatus=UNDEFINED;
+    }))
+    break;
+  PD("%O\n",err);
+  resync(1);
+  backendstatus=UNDEFINED;
+  if(!connectionclosed)
     throw(err);
+  tp=UNDEFINED;
   }
   { object tportal=_c.portal;		// Make copy, because it might dislodge
     tportal->fetch_row(1);		// upon initial fetch_row()
-- 
GitLab