0b1dc051cd6bf574da1ff20aabb58911a26bfa2f
tdreszer
  Tue Apr 26 16:24:35 2011 -0700
Fixed multi-selects searches where values contained spaces.  Redmine 3763.  Also added sql locking for mdb updates.  This is mostly overkill, since updates are always run on a sandbox mdb.
diff --git src/hg/lib/mdb.c src/hg/lib/mdb.c
index 913875e..15cae04 100644
--- src/hg/lib/mdb.c
+++ src/hg/lib/mdb.c
@@ -637,32 +637,40 @@
 
         // Set up var struct from 1st half of pair
         AllocVar(rootVar);
         rootVar->var = cloneNextWordByDelimiter(&(words[thisWord]),'=');
         rootVar->notEqual = (rootVar->var[strlen(rootVar->var)-1] == '!'); // requested not equal
         if (rootVar->notEqual)
             rootVar->var[strlen(rootVar->var)-1] = 0;
         // Do not try to combine repeated vars because "foo=a foo=b" is 'AND' while "foo=a,b" is 'OR'.
 
         // Fill in the val(s) from second half of pair
         char *val = NULL;
         if (words[thisWord][0] != '\0' && words[thisWord][0] != '?') // "var=?" or "var=" will query by var name only
             val = cloneString(words[thisWord]);
         if (val != NULL)
             {
+            // Strip any single or double quotes first.
+            char *end = val + strlen(val) - 1;
+            if ((*val == '"'  && *end == '"')
+            ||  (*val == '\'' && *end == '\''))
+                {
+                *end = '\0';
+                val++;
+                }
             // handle comma separated list of vals (if unquoted)
-            if (val[0] != '\'' && val[0] != '"' && strchr(val,',') != NULL)
+            if (strchr(val,',') != NULL)
                 {
                 char * aVal = NULL;
                 while((aVal = cloneNextWordByDelimiter(&val,',')) != NULL)
                     {
                     AllocVar(limbVal);
                     limbVal->val = aVal;
                     slAddTail(&rootVar->vals,limbVal);
                     }
                 }
             else
                 {
                 AllocVar(limbVal);
                 limbVal->val = val;
                 rootVar->vals = limbVal;
                 }
@@ -998,30 +1006,35 @@
 
 // -------------- Updating the DB --------------
 int mdbObjsSetToDb(struct sqlConnection *conn,char *tableName,struct mdbObj *mdbObjs,boolean replace,boolean testOnly)
 // Adds or updates metadata obj/var pairs into the named table.  Returns total rows affected
 {
 char query[8192];
 struct mdbObj *mdbObj;
 struct mdbVar *mdbVar;
 int count = 0;
 
 if(tableName == NULL)
     tableName = mdbTableName(conn,TRUE); // defaults to sandbox, if it exists, else MDB_DEFAULT_NAME;
 else if(!sqlTableExists(conn,tableName))
     errAbort("mdbObjsSetToDb attempting to update non-existent table named '%s'.\n",tableName);
 
+// Table specific lock (over-cautious, since most work is done on sandbox tables)
+char lock[64];
+safef(lock,sizeof lock,"lock_%s",tableName);
+sqlGetLock(conn, lock);
+
 for(mdbObj = mdbObjs;mdbObj != NULL; mdbObj = mdbObj->next)
     {
     // Handle delete requests first
     if(mdbObj->deleteThis)
         {
         if(mdbObj->vars == NULL) // deletes all
             {
             safef(query, sizeof(query),"%s where obj = '%s'",tableName,mdbObj->obj);
             int delCnt = sqlRowCount(conn,query);
 
             if(delCnt>0)
                 {
                 safef(query, sizeof(query),
                     "delete from %s where obj = '%s'",tableName,mdbObj->obj);
                 verbose(2, "Requesting delete of %d rows:\n\t%s;\n",delCnt, query);
@@ -1093,30 +1106,31 @@
                 mdbObjsFree(&objExists);
                 continue;  // The object was found/updated so done with it
                 }
             }
         // Finally ready to insert new vars
         safef(query, sizeof(query),
             "insert into %s set obj='%s', var='%s', val='%s'",
                 tableName,mdbObj->obj,mdbVar->var,
                 sqlEscapeString(mdbVar->val)); // FIXME Strip quotes
         verbose(2, "Requesting insert of one row:\n\t%s;\n",query);
         if(!testOnly)
             sqlUpdate(conn, query);
         count++;
         }
     }
+sqlReleaseLock(conn, lock);
 return count;
 }
 
 int mdbObjsLoadToDb(struct sqlConnection *conn,char *tableName,struct mdbObj *mdbObjs,boolean testOnly)
 // Adds mdb Objs with minimal error checking
 {
 int count = 0;
 
 if (tableName == NULL)
     tableName = mdbTableName(conn,TRUE); // defaults to sandbox, if it exists, else MDB_DEFAULT_NAME;
 else if (!sqlTableExists(conn,tableName))
     errAbort("mdbObjsLoadToDb attempting to load non-existent table named '%s'.\n",tableName);
 
 assert(mdbObjs != NULL);  // If this is the case, then be vocal
 
@@ -3181,31 +3195,36 @@
 }
 
 struct mdbObj *mdbObjRepeatedSearch(struct sqlConnection *conn,struct slPair *varValPairs,boolean tables,boolean files)
 // Search the metaDb table for objs by var,val pairs.  Uses mdbCvSearchMethod() if available.
 // This method will use mdbObjsQueryByVars()
 {
 struct slPair *onePair;
 struct dyString *dyTerms = dyStringNew(256);
 // Build list of terms as "var1=val1 var2=val2a,val2b,val2c var3=%val3%"
 for(onePair = varValPairs; onePair != NULL; onePair = onePair->next)
     {
     if (isEmpty(((char *)(onePair->val)))) // NOTE: All the parens are needed to get the macro to do the right thing
         continue;
     enum cvSearchable searchBy = cvSearchMethod(onePair->name);
     if (searchBy == cvSearchBySingleSelect || searchBy == cvSearchByMultiSelect)  // multiSelect val will be filled with a comma delimited list
+        {
+        if (strchr((char *)onePair->val,' '))
+            dyStringPrintf(dyTerms,"%s=\"%s\" ",onePair->name,(char *)onePair->val);
+        else
         dyStringPrintf(dyTerms,"%s=%s ",onePair->name,(char *)onePair->val);
+        }
     else if (searchBy == cvSearchByFreeText)                                      // If select is by free text then like
         dyStringPrintf(dyTerms,"%s=%%%s%% ",onePair->name,(char *)onePair->val);
     else if (sameWord(onePair->name,MDB_VAR_COMPOSITE))  // special case.  Not directly searchable by UI but indirectly and will show up here.
         dyStringPrintf(dyTerms,"%s=%s ",onePair->name,(char *)onePair->val);
     else if (searchBy == cvSearchByDateRange || searchBy == cvSearchByIntegerRange)
         {
         // TO BE IMPLEMENTED
         }
     }
 // Be sure to include table or file in selections
 if (tables)
     dyStringPrintf(dyTerms,"%s=%s ",MDB_OBJ_TYPE,MDB_OBJ_TYPE_TABLE);
 if (files)
     dyStringPrintf(dyTerms,"%s=? ",MDB_VAR_FILENAME);