8b8805e978d9f3082c09351ee19bbad1ca09cac2
angie
  Tue May 14 10:38:44 2019 -0700
When saving a session, if a table is not in customTrash then check whether it has already been saved to a customData* db.  If not, just move on instead of aborting.  refs #22440

diff --git src/hg/hgSession/sessionData.c src/hg/hgSession/sessionData.c
index 46469e6..8e8acc0 100644
--- src/hg/hgSession/sessionData.c
+++ src/hg/hgSession/sessionData.c
@@ -188,41 +188,61 @@
         freeMem(encTrashPath);
         freeMem(newPath);
         }
     if (urlEncoded)
         freeMem(trashDirPrefix);
     }
 }
 
 static char *sessionDataDbTableName(char *tableName, char *sessionDataDbPrefix, char *dbSuffix)
 /* Alloc and return a new table name that includes a db derived from sessionDataDbPrefix. */
 {
 struct dyString *dy = dyStringCreate("%s%s.%s", sessionDataDbPrefix, dbSuffix, tableName);
 return dyStringCannibalize(&dy);
 }
 
+static char *findTableInSessionDataDbs(struct sqlConnection *conn, char *sessionDataDbPrefix,
+                                       char *tableName)
+/* Given a tableName (no db. prefix), if it exists in any of the sessionDataPrefix* dbs
+ * then return the full db.tableName, otherwise NULL. */
+{
+int day;
+for (day = 1;  day <= 31;  day++)
+    {
+    char dbDotTable[strlen(sessionDataDbPrefix) + 3 + strlen(tableName) + 1];
+    safef(dbDotTable, sizeof dbDotTable, "%s%02d.%s", sessionDataDbPrefix, day, tableName);
+    if (sqlTableExists(conn, dbDotTable))
+        return cloneString(dbDotTable);
+    }
+return NULL;
+}
+
 static char *saveTrashTable(char *tableName, char *sessionDataDbPrefix, char *dbSuffix)
 /* Move trash tableName out of customTrash to a sessionDataDbPrefix database, unless that
- * has been done already.  Return the new database.table name. */
+ * has been done already.  If table does not exist in either customTrash or customData*,
+ * then return NULL; otherwise return the new database.table name. */
 {
 char *newDbTableName = sessionDataDbTableName(tableName, sessionDataDbPrefix, dbSuffix);
 struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH);
 if (! sqlTableExists(conn, newDbTableName))
     {
     if (! sqlTableExists(conn, tableName))
-        errAbort("saveTrashTable: neither "CUSTOM_TRASH".%s nor %s exist",
-                 tableName, newDbTableName);
+        {
+        // It's possible that this table was already saved and moved out of customTrash as part
+        // of some other saved session.  We don't have a way of leaving a symlink in customTrash.
+        return findTableInSessionDataDbs(conn, sessionDataDbPrefix, tableName);
+        }
     struct dyString *dy = sqlDyStringCreate("rename table %s to %s", tableName, newDbTableName);
     sqlUpdate(conn, dy->string);
     dyStringFree(&dy);
     }
 else if (sqlTableExists(conn, tableName))
     errAbort("saveTrashTable: both %s and %s exist", tableName, newDbTableName);
 hFreeConn(&conn);
 return newDbTableName;
 }
 
 static void replaceColumnValue(struct sqlConnection *conn, char *tableName, char *columnName,
                                char *newVal)
 /* Replace all tableName.columnName values with newVal. */
 {
 struct dyString *dy = sqlDyStringCreate("update %s set %s = '%s'",
@@ -292,36 +312,39 @@
         char quote = *start;
         if (quote == '\'' || quote == '"')
             start++;
         else
             quote = '\0';
         char *end = start;
         while (*end && ((quote && *end != quote) || (!quote && !isspace(*end))))
             end++;
         if (stringIn(prefix, end))
             errAbort("saveDbTableName: encountered two instances of '%s', expected 0 or 1",
                      prefix);
         char *tableName = cloneStringZ(start, (end - start));
         if (!startsWith(sessionDataDbPrefix, tableName))
             {
             char *newDbTableName = saveTrashTable(tableName, sessionDataDbPrefix, dbSuffix);
+            if (newDbTableName)
+                {
                 updateSessionDataTablePaths(newDbTableName, sessionDir);
                 char *newString = replaceChars(*retString, tableName, newDbTableName);
                 freez(retString);
                 *retString = newString;
                 freeMem(newDbTableName);
                 }
+            }
         freeMem(tableName);
         }
     }
 }
 
 static char *newCtTrashFile()
 /* Alloc and return the name of a new trash file to hold custom track metadata. */
 {
 struct tempName tn;
 trashDirFile(&tn, "ct", CT_PREFIX, ".ctfile");
 return cloneString(tn.forCgi);
 }
 
 static char *saveTrackFile(struct cart *cart, char *varName, char *oldFile,
                            char *sessionDataDbPrefix, char *dbSuffix, char *sessionDir)