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);