0cedad8cd900ffc7748341d00d99a589f9d19880
angie
  Thu May 19 12:19:44 2016 -0700
Separated out Galt's autoUpgrade code that robustly attempts to add a column to a table into a new lib module.  refs #17336 note-11

diff --git src/hg/lib/cartDb.c src/hg/lib/cartDb.c
index e6c0341..5c29528 100644
--- src/hg/lib/cartDb.c
+++ src/hg/lib/cartDb.c
@@ -11,44 +11,38 @@
  *
  * autoupgrade of the userDb and sessionDb tables
  *   alter table userDb    add column sessionKey varchar(255) NOT NULL default '';
  *   alter table sessionDb add column sessionKey varchar(255) NOT NULL default '';
  *
  */
 
 #include "common.h"
 #include "linefile.h"
 #include "dystring.h"
 #include "jksql.h"
 #include "hgConfig.h"
 #include "base64.h"
 #include "cartDb.h"
 #include "cart.h"
-
-#include "errCatch.h"
-#include "portable.h"
-#include "obscure.h"
-#include <sys/time.h>
+#include "autoUpgrade.h"
 
 extern DbConnector cartDefaultConnector;
 extern DbDisconnect cartDefaultDisconnector;
 
 static boolean userDbInitialized = FALSE;
 static boolean sessionDbInitialized = FALSE;
 
-struct dyString *dyUpgradeError = NULL;
-
 boolean cartDbHasSessionKey(struct sqlConnection *conn, char *table)
 /* Check to see if the table has the sessionKey field */
 {
 static boolean userDbHasSessionKey = FALSE;
 static boolean sessionDbHasSessionKey = FALSE;
 if (sameString(table, userDbTable()))
     {
     if (!userDbInitialized)
 	{
 	userDbInitialized = TRUE;
 	if (sqlFieldIndex(conn, table, "sessionKey") >= 0)
 	    {
 	    userDbHasSessionKey = TRUE;
 	    } 
 	}
@@ -59,184 +53,62 @@
     if (!sessionDbInitialized)
 	{
 	sessionDbInitialized = TRUE;
 	if (sqlFieldIndex(conn, table, "sessionKey") >= 0)
 	    {
 	    sessionDbHasSessionKey = TRUE;
 	    } 
 	}
     return sessionDbHasSessionKey;
     }
 else
     errAbort("Unknown table %s", table);
 return FALSE;
 }
 
-#define AUTOUPGRPATHSIZE 256
-static char *makeResultName(char *tableName, char *path)
-/* return path in trash for corresponding autoupgrade result file */
-{
-safef(path, AUTOUPGRPATHSIZE, "../trash/AUTO_UPGRADE_RESULT_%s", tableName);
-return cloneString(path);
-}
-
-static boolean checkAutoUpgradeTableResultTimeIsOld(char *tableName)
-/* Has enough time passed since the last upgrade check? 
- * The idea is to only check once every few minutes
- * rather than each time the CGI runs. */
-{
-char path[AUTOUPGRPATHSIZE];
-makeResultName(tableName, path);
-if (!fileExists(path))
-    return TRUE;  // If there is no result yet we should test and make one
-struct timeval rawtime;
-gettimeofday( &rawtime, NULL );
-time_t now = rawtime.tv_sec;
-time_t fMT = fileModTime(path);
-double diff = difftime(now, fMT);
-if (diff > (3 * 60))  // 3 minutes in seconds
-    return TRUE;  // The result is old we should test it again in case situation changed
-return FALSE;
-}
-
-static char * readAutoUpgradeTableResult(char *tableName)
-/* Read table upgrade result */
-{
-char path[AUTOUPGRPATHSIZE];
-char *result = NULL;
-makeResultName(tableName, path);
-if (!fileExists(path))
-    return NULL;  // There is no result yet
-readInGulp(path, &result, NULL);
-return result;
-}
-
-static void writeAutoUpgradeTableResult(char *tableName, char *result)
-/* Write table upgrade result */
-{
-char path[AUTOUPGRPATHSIZE];
-makeResultName(tableName, path);
-writeGulp(path, result, strlen(result));
-}
-
-
-void autoUpgradeTableAddSessionKey(struct sqlConnection *conn, char *tableName)
-/* Try to upgrade the table by adding sessionKey field
- * in a safe way handling success failures and retries
- * with multiple CGIs running. */
-{
-
-boolean testAgain = checkAutoUpgradeTableResultTimeIsOld(tableName);
-if (testAgain)
-    {
-    // Get the advisory lock for this table
-    // This prevents multiple CGI processes from trying to upgrade simultaneously
-    char lockName[256];
-    safef(lockName, sizeof lockName, "AUTO_UPGRADE_%s", tableName);
-    sqlGetLock(conn, lockName);
-
-    // Make sure that the table has not been already upgraded by some earlier process.
-    // We do not want to upgrade more than once.
-    if (sqlFieldIndex(conn, tableName, "sessionKey") == -1)
-	{
-
-	char result[4096];
-
-	// Put a catch around the table upgrade attempt,
-	// both to allow us to continue if it fails,
-	// and to catch the error message and save it in the results file
-	struct errCatch *errCatch = errCatchNew();
-	if (errCatchStart(errCatch))
-	    { 
-	    char query[256];
-	    sqlSafef(query, sizeof query, "alter table %s add column sessionKey varchar(255) NOT NULL default ''", tableName);
-	    sqlUpdate(conn, query);
-	    }
-	errCatchEnd(errCatch);
-	if (errCatch->gotError)
-	    {
-	    safef(result, sizeof result, "AUTOUPGRADE FAILED\n%s", errCatch->message->string);
-	    }
-	else
-	    {
-	    safef(result, sizeof result, "OK\n");
-	    }
-
-	errCatchFree(&errCatch);
-
-	writeAutoUpgradeTableResult(tableName, result);
-
-	}
-
-    // Release the advisory lock for this table
-    sqlReleaseLock(conn, lockName);
-
-    }
-else
-    {  // in the interests of speed, just use the old result
-    char *oldResult = readAutoUpgradeTableResult(tableName);
-    if (oldResult)
-	{
-	if (startsWith("AUTOUPGRADE FAILED", oldResult))
-	    {
-	    // cannot do this here since it is too early and the warn handler does not work right
-	    // it ends up writing html and javascript before the cookie and response header have even been finished.
-	    // warn("%s", oldResult);  
-	    // instead, save the error message for access later from select places like hgGateway
-	    if (!dyUpgradeError)
-		dyUpgradeError = dyStringNew(256);
-	    else
-		dyStringPrintf(dyUpgradeError,"\n");
-	    dyStringPrintf(dyUpgradeError,"%s", oldResult);
-	    }
-	}
-    }
-
-}
-
-
-
 boolean cartDbUseSessionKey()
 /* Check settings and and state to determine if sessionKey is in use */
 {
 static boolean initialized = FALSE;
 static boolean useSessionKey = FALSE;
 if (!initialized)
     {
     initialized = TRUE;
     char *sessionKey = cfgOption2("browser", "sessionKey");
     if (!sessionKey)
 	sessionKey = "on";  // DEFAULT but this might change to another value
     if (sameString(sessionKey, "on"))
 	{
 	useSessionKey = TRUE;
 	struct sqlConnection *conn = cartDefaultConnector();
 	boolean userDbHasSessionKey = cartDbHasSessionKey(conn, userDbTable());
 	boolean sessionDbHasSessionKey = cartDbHasSessionKey(conn, sessionDbTable());
 	if ( ! (userDbHasSessionKey && sessionDbHasSessionKey) )
 	    {
     	    //errAbort("brower.sessionKey=on but userDb and sessionDb are missing the sessionKey field.");
 	    // AUTO-UPGRADE tables to add missing sessionKey field here.
 	    if (!userDbHasSessionKey)
 		{
-		autoUpgradeTableAddSessionKey(conn, userDbTable());
+                autoUpgradeTableAddColumn(conn, userDbTable(), "sessionKey",
+                                          "varchar(255)", TRUE, "''");
 		userDbInitialized = FALSE;
 		userDbHasSessionKey = cartDbHasSessionKey(conn, userDbTable());
 		}
     	    if (!sessionDbHasSessionKey)
 		{
-		autoUpgradeTableAddSessionKey(conn, sessionDbTable());
+                autoUpgradeTableAddColumn(conn, sessionDbTable(), "sessionKey",
+                                          "varchar(255)", TRUE, "''");
 		sessionDbInitialized = FALSE;
 		sessionDbHasSessionKey = cartDbHasSessionKey(conn, sessionDbTable());
 		}
 	    if ( ! (userDbHasSessionKey && sessionDbHasSessionKey) )
 		useSessionKey = FALSE;
 	    }
 	cartDefaultDisconnector(&conn);
 	}
     else if (sameString(sessionKey, "off"))
 	{
 	useSessionKey = FALSE;
 	}
     else if (sameString(sessionKey, "autodetect"))
 	{
 	errAbort("brower.sessionKey=autodetect has not implemented yet."); // TODO