/* Put up pages for selecting and filtering on fields. */ #include "common.h" #include "hash.h" #include "linefile.h" #include "obscure.h" #include "dystring.h" #include "cheapcgi.h" #include "jksql.h" #include "htmshell.h" #include "cart.h" #include "jsHelper.h" #include "web.h" #include "trackDb.h" #include "asParse.h" #include "kxTok.h" #include "customTrack.h" #include "joiner.h" #include "hgTables.h" #include "bedCart.h" #include "wiggle.h" #include "wikiTrack.h" #include "makeItemsItem.h" #include "bedDetail.h" #include "pgSnp.h" #include "samAlignment.h" #include "trackHub.h" /* ------- Stuff shared by Select Fields and Filters Pages ----------*/ boolean varOn(char *var) /* Return TRUE if variable exists and is set. */ { return cartVarExists(cart, var) && cartBoolean(cart, var); } static char *dbTableVar(char *prefix, char *db, char *table) /* Get variable name of form prefixDb.table */ { static char buf[128]; safef(buf, sizeof(buf), "%s%s.%s", prefix, db, table); return buf; } static char *dbTableFieldVar(char *prefix, char *db, char *table, char *field) /* Get variable name of form prefixDb.table.field */ { static char buf[128]; safef(buf, sizeof(buf), "%s%s.%s.%s", prefix, db, table, field); return buf; } struct dbTable /* database/table pair. */ { struct dbTable *next; char *db; /* Database name. */ char *table; /* Table name. */ }; static struct dbTable *dbTableNew(char *db, char *table) /* Return new dbTable struct. */ { struct dbTable *dt; AllocVar(dt); dt->db = cloneString(db); dt->table = cloneString(table); return dt; } static int dbTableCmp(const void *va, const void *vb) /* Compare two dbTables. */ { const struct dbTable *a = *((struct dbTable **)va); const struct dbTable *b = *((struct dbTable **)vb); int diff; diff = strcmp(a->db, b->db); if (diff == 0) diff = strcmp(a->table, b->table); return diff; } #if defined(NOT_USED) static void dbTableFree(struct dbTable **pDt) /* Free up dbTable struct. */ { struct dbTable *dt = *pDt; if (dt != NULL) { freeMem(dt->db); freeMem(dt->table); freez(pDt); } } #endif static struct dbTable *extraTableList(char *prefix) /* Get list of tables (other than the primary table) * where we are displaying fields. */ { struct hashEl *varList = NULL, *var; int prefixSize = strlen(prefix); struct dbTable *dtList = NULL, *dt; /* Build up list of tables to show by looking at * variables with right prefix in cart. */ varList = cartFindPrefix(cart, prefix); for (var = varList; var != NULL; var = var->next) { if (cartBoolean(cart, var->name)) { /* From variable name parse out database and table. */ char *dbTab = cloneString(var->name + prefixSize); char *db = dbTab; char *table = strchr(db, '.'); if (table == NULL) internalErr(); *table++ = 0; dt = dbTableNew(db, table); slAddHead(&dtList, dt); freez(&dbTab); } } if (varList == NULL && curTrack != NULL) { char *defaultLinkedTables = trackDbSetting(curTrack, "defaultLinkedTables"); if (defaultLinkedTables != NULL) { struct slName *t, *tables = slNameListFromString(defaultLinkedTables, ','); for (t = tables; t != NULL; t = t->next) { char varName[1024]; safef(varName, sizeof(varName), "%s%s.%s", prefix, database, t->name); cartSetBoolean(cart, varName, TRUE); dt = dbTableNew(database, t->name); slAddHead(&dtList, dt); } } } slSort(&dtList, dbTableCmp); return dtList; } static void showLinkedTables(struct joiner *joiner, struct dbTable *inList, char *varPrefix, char *buttonName, char *buttonText) /* Print section with list of linked tables and check boxes to turn them * on. */ { struct dbTable *outList = NULL, *out, *in; char dtName[256]; struct hash *uniqHash = newHash(0); struct hash *inHash = newHash(8); /* Build up list of tables we link to in outList. */ for (in = inList; in != NULL; in = in->next) { struct sqlConnection *conn = NULL; if (!trackHubDatabase(database)) conn = hAllocConn(in->db); struct joinerPair *jpList, *jp; /* Keep track of tables in inList. */ safef(dtName, sizeof(dtName), "%s.%s", inList->db, inList->table); hashAdd(inHash, dtName, NULL); /* First table in input is not allowed in output. */ if (in == inList) hashAdd(uniqHash, dtName, NULL); /* Scan through joining information and add tables, * avoiding duplicate additions. */ jpList = joinerRelate(joiner, in->db, in->table); for (jp = jpList; jp != NULL; jp = jp->next) { safef(dtName, sizeof(dtName), "%s.%s", jp->b->database, jp->b->table); if (!hashLookup(uniqHash, dtName) && !accessControlDenied(jp->b->database, jp->b->table)) { hashAdd(uniqHash, dtName, NULL); out = dbTableNew(jp->b->database, jp->b->table); slAddHead(&outList, out); } } joinerPairFreeList(&jpList); hFreeConn(&conn); } slSort(&outList, dbTableCmp); /* Print html. */ if (outList != NULL) { webNewSection("Linked Tables"); hTableStart(); for (out = outList; out != NULL; out = out->next) { struct sqlConnection *conn = hAllocConn(out->db); struct asObject *asObj = asForTable(conn, out->table); char *var = dbTableVar(varPrefix, out->db, out->table); hPrintf(""); hPrintf(""); cgiMakeCheckBox(var, varOn(var)); hPrintf(""); hPrintf("%s", out->db); hPrintf("%s", out->table); hPrintf(""); if (asObj != NULL) hPrintf("%s", asObj->comment); else hPrintf(" "); hPrintf(""); hPrintf(""); hFreeConn(&conn); } hTableEnd(); hPrintf("
"); cgiMakeButton(buttonName, buttonText); } } /* ------- Select Fields Stuff ----------*/ static char *checkVarPrefix() /* Return prefix for checkBox */ { static char buf[128]; safef(buf, sizeof(buf), "%scheck.", hgtaFieldSelectPrefix); return buf; } static char *checkVarName(char *db, char *table, char *field) /* Get variable name for check box on given table/field. */ { return dbTableFieldVar(checkVarPrefix(), db, table, field); } static char *selFieldLinkedTablePrefix() /* Get prefix for openLinked check-boxes. */ { static char buf[128]; safef(buf, sizeof(buf), "%s%s.", hgtaFieldSelectPrefix, "linked"); return buf; } static char *setClearAllVar(char *setOrClearPrefix, char *db, char *table) /* Return concatenation of a and b. */ { static char buf[128]; safef(buf, sizeof(buf), "%s%s.%s", setOrClearPrefix, db, table); return buf; } static void showTableButtons(char *db, char *table, boolean withGetButton) /* Put up the last buttons in a showTable section. */ { hPrintf("
\n"); if (withGetButton) { if (doGalaxy()) /* need form fields here and Galaxy so add step to Galaxy */ cgiMakeButton(hgtaDoGalaxySelectedFields, "done with selections"); else cgiMakeButton(hgtaDoPrintSelectedFields, "get output"); hPrintf(" "); cgiMakeButton(hgtaDoMainPage, "cancel"); hPrintf(" "); } jsInit(); cgiMakeOnClickSubmitButton(jsSetVerticalPosition("mainForm"), setClearAllVar(hgtaDoSetAllFieldPrefix,db,table), "check all"); hPrintf(" "); cgiMakeOnClickSubmitButton(jsSetVerticalPosition("mainForm"), setClearAllVar(hgtaDoClearAllFieldPrefix,db,table), "clear all"); cgiDown(0.7); // Extra spacing below the buttons } static void showTableFieldsOnList(char *db, char *rootTable, struct asObject *asObj, struct slName *fieldList, boolean showItemRgb, boolean withGetButton) /* Put up html table with check box, name, description, etc for each field. */ { hTableStart(); struct slName *fieldName; for (fieldName = fieldList; fieldName != NULL; fieldName = fieldName->next) { char *field = fieldName->name; char *var = checkVarName(db, rootTable, field); struct asColumn *asCol; hPrintf(""); hPrintf(""); cgiMakeCheckBox(var, varOn(var)); hPrintf(""); hPrintf(""); if (showItemRgb && sameWord(field,"reserved")) hPrintf("itemRgb"); else hPrintf("%s", field); hPrintf(""); if (asObj != NULL) { asCol = asColumnFind(asObj, field); if (asCol != NULL) hPrintf("%s", asCol->comment); else hPrintf(" "); } hPrintf(""); } hTableEnd(); showTableButtons(db, rootTable, withGetButton); } static void showTableFieldsDb(char *db, char *rootTable, boolean withGetButton) /* Put up a little html table with a check box, name, and hopefully * a description for each field in SQL rootTable. */ { struct sqlConnection *conn = NULL; if (!trackHubDatabase(database)) conn = hAllocConn(db); char *table = chromTable(conn, rootTable); struct trackDb *tdb = findTdbForTable(db, curTrack, rootTable, ctLookupName); struct asObject *asObj = asForTable(conn, rootTable); boolean showItemRgb = FALSE; showItemRgb=bedItemRgb(tdb); /* should we expect itemRgb instead of "reserved" */ struct slName *fieldList; if (isBigBed(database, table, curTrack, ctLookupName)) fieldList = bigBedGetFields(table, conn); else if (isBamTable(table)) fieldList = bamGetFields(table); else if (isVcfTable(table)) fieldList = vcfGetFields(table); else fieldList = sqlListFields(conn, table); showTableFieldsOnList(db, rootTable, asObj, fieldList, showItemRgb, withGetButton); freez(&table); hFreeConn(&conn); } static void showBedTableFields(char *db, char *table, int fieldCount, boolean withGetButton) /* Put up html table with a check box for each field of custom * track. */ { struct slName *field, *fieldList = getBedFields(fieldCount); hTableStart(); for (field = fieldList; field != NULL; field = field->next) { char *var = checkVarName(db, table, field->name); hPrintf(""); cgiMakeCheckBox(var, varOn(var)); hPrintf(""); hPrintf(" %s
\n", field->name); hPrintf(""); } hTableEnd(); showTableButtons(db, table, withGetButton); } static void showTableFieldsCt(char *db, char *table, boolean withGetButton) /* Put up html table with a check box for each field of custom * track. */ { struct customTrack *ct = ctLookupName(table); char *type = ct->dbTrackType; if (type == NULL) type = ct->tdb->type; struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH); struct asObject *asObj = asForTdb(conn, ct->tdb); if (asObj) { struct slName *fieldList = NULL; if (ct->dbTableName != NULL) fieldList = sqlListFields(conn, ct->dbTableName); if (fieldList == NULL) fieldList = asColNames(asObj); showTableFieldsOnList(db, table, asObj, fieldList, FALSE, withGetButton); asObjectFree(&asObj); slNameFreeList(&fieldList); } else showBedTableFields(db, table, ct->fieldCount, withGetButton); hFreeConn(&conn); } static void showTableFields(char *db, char *rootTable, boolean withGetButton) /* Put up a little html table with a check box, name, and hopefully * a description for each field in SQL rootTable. */ { if (isCustomTrack(rootTable)) showTableFieldsCt(db, rootTable, withGetButton); else if (sameWord(rootTable, WIKI_TRACK_TABLE)) showTableFieldsDb(wikiDbName(), rootTable, withGetButton); else showTableFieldsDb(db, rootTable, withGetButton); } static void showLinkedFields(struct dbTable *dtList) /* Put up a section with fields for each linked table. */ { struct dbTable *dt; for (dt = dtList; dt != NULL; dt = dt->next) { /* Put it up in a new section. */ webNewSection("%s.%s fields", dt->db, dt->table); showTableFields(dt->db, dt->table, FALSE); } } static void doBigSelectPage(char *db, char *table) /* Put up big field selection page. Assumes html page open already*/ { struct joiner *joiner = allJoiner; struct dbTable *dtList, *dt; char dbTableBuf[256]; cartSetString(cart, hgtaFieldSelectTable, getDbTable(db, table)); if (strchr(table, '.')) htmlOpen("Select Fields from %s", table); else htmlOpen("Select Fields from %s.%s", db, table); hPrintf("
\n", cgiScriptName(), cartUsualString(cart, "formMethod", "POST")); cartSaveSession(cart); cgiMakeHiddenVar(hgtaDatabase, db); cgiMakeHiddenVar(hgtaTable, table); dbOverrideFromTable(dbTableBuf, &db, &table); showTableFields(db, table, TRUE); dtList = extraTableList(selFieldLinkedTablePrefix()); showLinkedFields(dtList); dt = dbTableNew(db, table); slAddHead(&dtList, dt); showLinkedTables(joiner, dtList, selFieldLinkedTablePrefix(), hgtaDoSelectFieldsMore, "allow selection from checked tables"); /* clean up. */ hPrintf("
"); cgiDown(0.9); htmlClose(); joinerFree(&joiner); } void doSelectFieldsMore() /* Do select fields page (generally as a continuation. */ { char *db = cartString(cart, hgtaDatabase); char *table = cartString(cart, hgtaTable); doBigSelectPage(db, table); } void doOutSelectedFields(char *table, struct sqlConnection *conn) /* Put up select fields (for tab-separated output) page. */ { if (anySubtrackMerge(database, curTable)) errAbort("Can't do selected fields output when subtrack merge is on. " "Please go back and select another output type, or clear the subtrack merge."); else if (anyIntersection()) errAbort("Can't do selected fields output when intersection is on. " "Please go back and select another output type, or clear the intersection."); else { char *fsTable = cartOptionalString(cart, hgtaFieldSelectTable); char *dbTable = NULL; table = connectingTableForTrack(table); dbTable = getDbTable(database, table); /* Remove cart state if table has been changed: */ if (fsTable && ! sameString(fsTable, dbTable)) { cartRemovePrefix(cart, hgtaFieldSelectPrefix); cartRemove(cart, hgtaFieldSelectTable); } doBigSelectPage(database, table); } } boolean primaryOrLinked(char *dbTableField) /* Return TRUE if this is the primary table for field selection, or if it * is linked with that table. */ { char dbTable[256]; char *ptr = NULL; /* Extract just the db.table part of db.table.field */ safef(dbTable, sizeof(dbTable), "%s", dbTableField); ptr = strchr(dbTable, '.'); if (ptr == NULL) errAbort("Expected 3 .-separated words in %s but can't find first .", dbTableField); ptr = strchr(ptr+1, '.'); if (ptr == NULL) errAbort("Expected 3 .-separated words in %s but can't find second .", dbTableField); *ptr = 0; if (sameString(dbTable, cartString(cart, hgtaFieldSelectTable))) return TRUE; else { char varName[256]; safef(varName, sizeof(varName), "%s%s", selFieldLinkedTablePrefix(), dbTable); return cartUsualBoolean(cart, varName, FALSE); } } void doPrintSelectedFields() /* Actually produce selected field output as text stream. */ { char *db = cartString(cart, hgtaDatabase); char *table = cartString(cart, hgtaTable); char *varPrefix = checkVarPrefix(); int varPrefixSize = strlen(varPrefix); struct hashEl *varList = NULL, *var; struct slName *fieldList = NULL, *field; textOpen(); /* Gather together field list for primary and linked tables from cart. */ varList = cartFindPrefix(cart, varPrefix); for (var = varList; var != NULL; var = var->next) { if (!sameString(var->val, "0")) { field = slNameNew(var->name + varPrefixSize); if (primaryOrLinked(field->name)) slAddHead(&fieldList, field); } } if (fieldList == NULL) errAbort("Please go back and select at least one field"); slReverse(&fieldList); /* Do output. */ tabOutSelectedFields(db, table, NULL, fieldList); /* Clean up. */ slFreeList(&fieldList); hashElFreeList(&varList); } static void setCheckVarsForTable(char *dbTable, char *val) /* Return list of check variables for this table. */ { char prefix[128]; struct hashEl *varList, *var; safef(prefix, sizeof(prefix), "%s%s.", checkVarPrefix(), dbTable); varList = cartFindPrefix(cart, prefix); for (var = varList; var != NULL; var = var->next) cartSetString(cart, var->name, val); hashElFreeList(&varList); } void doClearAllField(char *dbTable) /* Clear all checks by fields in db.table. */ { setCheckVarsForTable(dbTable, "0"); doSelectFieldsMore(); } void doSetAllField(char *dbTable) /* Set all checks by fields in db.table. */ { setCheckVarsForTable(dbTable, "1"); doSelectFieldsMore(); } /* ------- Filter Page Stuff ----------*/ char *filterFieldVarName(char *db, char *table, char *field, char *type) /* Return variable name for filter page. */ { static char buf[256]; safef(buf, sizeof(buf), "%s%s.%s.%s.%s", hgtaFilterVarPrefix, db, table, field, type); return buf; } static char *filterPatternVarName(char *db, char *table, char *field) /* Return variable name for a filter page text box. */ { return filterFieldVarName(db, table, field, filterPatternVar); } static void removeFilterVars() /* Remove filter variables from cart. */ { cartRemovePrefix(cart, hgtaFilterPrefix); cartRemove(cart, hgtaFilterTable); } boolean anyFilter() /* Return TRUE if any filter set. If there is filter state from a filter * defined on a different table, clear it. */ { char *filterTable = cartOptionalString(cart, hgtaFilterTable); if (filterTable == NULL) return FALSE; else { char *dbTable = getDbTable(database, curTable); boolean curTableHasFilter = sameString(filterTable, dbTable); freez(&dbTable); if (curTableHasFilter) return TRUE; else { removeFilterVars(); return FALSE; } } } /* Droplist menus for filtering on fields: */ char *ddOpMenu[] = { "does", "doesn't" }; int ddOpMenuSize = 2; char *logOpMenu[] = { "AND", "OR" }; int logOpMenuSize = 2; char *cmpOpMenu[] = { "ignored", "in range", "<", "<=", "=", "!=", ">=", ">" }; int cmpOpMenuSize = ArraySize(cmpOpMenu); char *eqOpMenu[] = { "ignored", "=", "!=", }; int eqOpMenuSize = ArraySize(eqOpMenu); char *maxOutMenu[] = { "100,000", "1,000,000", "10,000,000", }; int maxOutMenuSize = ArraySize(maxOutMenu); void stringFilterOption(char *db, char *table, char *field, char *logOp) /* Print out a table row with filter constraint options for a string/char. */ { char *name; hPrintf(" %s \n", field); name = filterFieldVarName(db, table, field, filterDdVar); cgiMakeDropListClassWithStyle(name, ddOpMenu, ddOpMenuSize, cartUsualString(cart, name, ddOpMenu[0]),"normalText","width: 76px"); hPrintf("match \n"); name = filterPatternVarName(db, table, field); cgiMakeTextVarWithExtraHtml(name, cartUsualString(cart, name, "*"),140,NULL); //cgiMakeTextVar(name, cartUsualString(cart, name, "*"), 20); if (logOp == NULL) logOp = ""; hPrintf(" %s \n", logOp); } static void makeEnumValMenu(char *type, char ***pMenu, int *pMenuSize) /* Given a SQL type description of an enum or set, parse out the list of * values and turn them into a char array for menu display, with "*" as * the first item (no constraint). * This assumes that the values do not contain the ' character. * This will leak a little mem unless you free *pMenu[1] and *pMenu * when done. */ { static char *noop = "*"; char *dup = NULL; char *words[256]; int wordCount = 0; int len = 0, i = 0; if (startsWith("enum(", type)) dup = cloneString(type + strlen("enum(")); else if (startsWith("set(", type)) dup = cloneString(type + strlen("set(")); else errAbort("makeEnumValMenu: expecting a SQL type description that begins " "with \"enum(\" or \"set(\", but got \"%s\".", type); stripChar(dup, '\''); wordCount = chopCommas(dup, words); len = strlen(words[wordCount-1]); if (words[wordCount-1][len-1] == ')') words[wordCount-1][len-1] = 0; else errAbort("makeEnumValMenu: expecting a ')' at the end of the last word " "of SQL type, but got \"%s\"", type); *pMenuSize = wordCount + 1; AllocArray(*pMenu, wordCount+1); *pMenu[0] = noop; for (i = 1; i < wordCount + 1; i++) { (*pMenu)[i] = words[i-1]; } } void enumFilterOption(char *db, char *table, char *field, char *type, char *logOp) /* Print out a table row with filter constraint options for an enum/set. */ { char *name = NULL; char **valMenu = NULL; int valMenuSize = 0; hPrintf(" %s " "\n", field); name = filterFieldVarName(db, table, field, filterDdVar); cgiMakeDropListClassWithStyle(name, ddOpMenu, ddOpMenuSize, cartUsualString(cart, name, ddOpMenu[0]),"normalText","width: 76px"); hPrintf("%s\n", isSqlSetType(type) ? "include" : "match"); name = filterPatternVarName(db, table, field); makeEnumValMenu(type, &valMenu, &valMenuSize); if (logOp == NULL) logOp = ""; if (valMenuSize-1 > 2) { struct slName *defaults = cartOptionalSlNameList(cart, name); if (defaults == NULL) defaults = slNameNew("*"); cgiMakeCheckboxGroup(name, valMenu, valMenuSize, defaults, 5); hPrintf("%s \n", logOp); } else { cgiMakeDropList(name, valMenu, valMenuSize,cartUsualString(cart, name, valMenu[0])); hPrintf(" %s \n", logOp); } } static void numericFilter(char *db, char *table, char *field, char *label,char *logOp) /* Print out a table row with filter constraint options for a number. */ { char *name; hPrintf(" %sis\n", label); name = filterFieldVarName(db, table, field, filterCmpVar); cgiMakeDropListClassWithStyle(name, cmpOpMenu, cmpOpMenuSize, cartUsualString(cart, name, cmpOpMenu[0]),"normalText","width: 76px"); puts("\n"); name = filterPatternVarName(db, table, field); cgiMakeTextVar(name, cartUsualString(cart, name, "0"), 20); if (logOp == NULL) logOp = ""; hPrintf(" %s\n", logOp); } static void numericFilterWithLimits(char *db, char *table, char *field, char *label, double min,double max,char *logOp) /* Print out a filter constraint for an integer within a range. */ { char *name; hPrintf(" %sis\n", label); name = filterFieldVarName(db, table, field, filterCmpVar); cgiMakeDropListClassWithStyle(name, cmpOpMenu, cmpOpMenuSize, cartUsualString(cart, name, cmpOpMenu[0]),"normalText","width: 76px"); puts("\n"); name = filterPatternVarName(db, table, field); cgiMakeTextVar(name, cartUsualString(cart, name, "0"), 20); if (logOp == NULL) logOp = ""; hPrintf(" %s\n", logOp); } void integerFilter(char *db, char *table, char *field, char *label,char *logOp) /* Print out a filter constraint for an integer within a range. */ { char *name; hPrintf(" %sis\n", label); name = filterFieldVarName(db, table, field, filterCmpVar); cgiMakeDropListClassWithStyle(name, cmpOpMenu, cmpOpMenuSize, cartUsualString(cart, name, cmpOpMenu[0]),"normalText","width: 76px"); puts("\n"); name = filterPatternVarName(db, table, field); cgiMakeTextVar(name, cartUsualString(cart, name, "0"), 20); if (logOp == NULL) logOp = ""; hPrintf(" %s\n", logOp); } void integerFilterWithLimits(char *db, char *table, char *field, char *label, int min,int max,char *logOp) /* Print out a filter constraint for an integer within a range. */ { char *name; hPrintf(" %s is\n", label); name = filterFieldVarName(db, table, field, filterCmpVar); cgiMakeDropListClassWithStyle(name, cmpOpMenu, cmpOpMenuSize, cartUsualString(cart, name, cmpOpMenu[0]),"normalText","width: 76px"); puts("\n"); name = filterPatternVarName(db, table, field); int val = cartUsualInt(cart, name, 0); cgiMakeIntVarWithLimits(name,val,label,140,min,max); if (logOp == NULL) logOp = ""; hPrintf(" %s\n", logOp); } void eqFilterOption(char *db, char *table, char *field, char *fieldLabel1, char *fieldLabel2, char *logOp) /* Print out a table row with filter constraint options for an equality * comparison. */ { char *name; hPrintf(" %s \n", fieldLabel1); puts(" is "); name = filterFieldVarName(db, table, field, filterCmpVar); cgiMakeDropList(name, eqOpMenu, eqOpMenuSize, cartUsualString(cart, name, eqOpMenu[0])); /* make a dummy pat_ CGI var for consistency with other filter options */ name = filterPatternVarName(db, table, field); cgiMakeHiddenVar(name, "0"); hPrintf("\n"); hPrintf("%s\n", fieldLabel2); if (logOp == NULL) logOp = ""; hPrintf("%s\n", logOp); } static void printSqlFieldListAsControlTable(struct sqlFieldType *ftList, char *db, char *rootTable, struct trackDb *tdb, boolean isBedGr) /* Print out table of controls for fields. */ { int fieldNum = 0; int bedGraphColumn = 5; /* default score column */ int noBinBedGraphColumn = bedGraphColumn; boolean gotFirst = FALSE; struct sqlFieldType *ft; hPrintf("\n"); for (ft = ftList; ft != NULL; ft = ft->next) { char *field = ft->name; char *type = ft->type; char *logic = ""; if ((0 == fieldNum) && (!sameWord(field,"bin"))) noBinBedGraphColumn -= 1; if (!sameWord(type, "longblob")) { if (!gotFirst) gotFirst = TRUE; else if (!isBedGr) logic = " AND "; } if (!isBedGr || (noBinBedGraphColumn == fieldNum)) { if (isSqlEnumType(type) || isSqlSetType(type)) { enumFilterOption(db, rootTable, field, type, logic); } else if(isSqlIntType(type)) { integerFilter(db, rootTable, field, field, logic); } else if(isSqlNumType(type)) { if(isBedGr) { double min, max; double tDbMin, tDbMax; wigFetchMinMaxLimits(tdb, &min, &max, &tDbMin, &tDbMax); if (tDbMin < min) min = tDbMin; if (tDbMax > max) max = tDbMax; numericFilterWithLimits(db, rootTable, field, field, min, max, logic); hPrintf("\n", field, min, max); } else { numericFilter(db, rootTable, field, field, logic); } } else //if (isSqlStringType(type)) { stringFilterOption(db, rootTable, field, logic); } } ++fieldNum; } hPrintf("
(%s range: [%g:%g]) " "
\n"); } static void filterControlsForTableDb(char *db, char *rootTable) /* Put up filter controls for a single database table. */ { struct sqlConnection *conn = NULL; if (!trackHubDatabase(db)) conn = hAllocConn(db); char *table = chromTable(conn, rootTable); struct trackDb *tdb = findTdbForTable(db, curTrack, rootTable, ctLookupName); boolean isSmallWig = isWiggle(db, table); boolean isWig = isSmallWig || isBigWigTable(table); boolean isBedGr = isBedGraph(rootTable); boolean isBam = isBamTable(rootTable); boolean isVcf = isVcfTable(rootTable); int bedGraphColumn = 5; /* default score column */ if (isBedGr) { int wordCount; char *words[8]; char *typeLine = cloneString(tdb->type); wordCount = chopLine(typeLine,words); if (wordCount > 1) bedGraphColumn = sqlUnsigned(words[1]); freez(&typeLine); } if (isWig) { hPrintf("\n"); if ((tdb != NULL) && (tdb->type != NULL)) { double min, max; wiggleMinMax(tdb,&min,&max); numericFilterWithLimits(db, rootTable, filterDataValueVar,filterDataValueVar,min,max,""); hPrintf("
(dataValue range: [%g:%g]) " "
\n", min, max); } else { numericFilter(db, rootTable, filterDataValueVar,filterDataValueVar, ""); hPrintf("\n"); } } else { struct sqlFieldType *ftList; if (isBigBed(database, table, curTrack, ctLookupName)) ftList = bigBedListFieldsAndTypes(table, conn); else if (isBamTable(table)) ftList = bamListFieldsAndTypes(); else if (isVcfTable(table)) ftList = vcfListFieldsAndTypes(); else ftList = sqlListFieldsAndTypes(conn, table); printSqlFieldListAsControlTable(ftList, db, rootTable, tdb, isBedGr); } /* Printf free-form query row. */ if (!(isWig||isBedGr||isBam||isVcf)) { char *name; hPrintf("
\n"); name = filterFieldVarName(db, rootTable, "", filterRawLogicVar); cgiMakeDropList(name, logOpMenu, logOpMenuSize, cartUsualString(cart, name, logOpMenu[0])); hPrintf(" Free-form query: "); name = filterFieldVarName(db, rootTable, "", filterRawQueryVar); char *val = cartUsualString(cart, name, ""); // escape double quotes to avoid HTML parse trouble in the text input. val = htmlEncodeText(val, FALSE); cgiMakeTextVar(name, val, 50); hPrintf("
\n"); } if (isWig||isBedGr||isBam||isVcf) { char *name; hPrintf("
Limit data output to: \n"); name = filterFieldVarName(db, rootTable, "_", filterMaxOutputVar); cgiMakeDropList(name, maxOutMenu, maxOutMenuSize, cartUsualString(cart, name, maxOutMenu[0])); hPrintf(" lines
\n"); } freez(&table); hFreeConn(&conn); hPrintf("
\n"); cgiMakeButton(hgtaDoFilterSubmit, "submit"); hPrintf(" "); cgiMakeButton(hgtaDoMainPage, "cancel"); } static void filterControlsForTableCt(char *db, char *table) /* Put up filter controls for a custom track. */ { struct customTrack *ct = ctLookupName(table); char *type = ct->dbTrackType; puts(""); if (type != NULL && startsWithWord("maf", type)) { stringFilterOption(db, table, "chrom", " AND "); integerFilter(db, table, "chromStart", "chromStart", " AND "); integerFilter(db, table, "chromEnd", "chromEnd", " AND "); } else if (type != NULL && (startsWithWord("makeItems", type) || sameWord("bedDetail", type) || sameWord("pgSnp", type))) { struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH); struct sqlFieldType *ftList = sqlListFieldsAndTypes(conn, ct->dbTableName); printSqlFieldListAsControlTable(ftList, db, table, ct->tdb, FALSE); hFreeConn(&conn); } else if (ct->wiggle || isBigWigTable(table)) { if ((ct->tdb != NULL) && (ct->tdb != NULL)) { double min, max; wiggleMinMax(ct->tdb,&min,&max); numericFilterWithLimits("ct", table, filterDataValueVar, filterDataValueVar,min,max,""); hPrintf("\n", min, max); } else { numericFilter("ct", table, filterDataValueVar, filterDataValueVar,""); } } else if (isBigBed(db, table, curTrack, ctLookupName)) { struct sqlFieldType *ftList = bigBedListFieldsAndTypes(table, NULL); printSqlFieldListAsControlTable(ftList, db, table, ct->tdb, FALSE); } else if (isBamTable(table)) { struct sqlFieldType *ftList = bamListFieldsAndTypes(); printSqlFieldListAsControlTable(ftList, db, table, ct->tdb, FALSE); } else if (isVcfTable(table)) { struct sqlFieldType *ftList = vcfListFieldsAndTypes(); printSqlFieldListAsControlTable(ftList, db, table, ct->tdb, FALSE); } else { if (ct->fieldCount >= 3) { stringFilterOption(db, table, "chrom", " AND "); integerFilter(db, table, "chromStart", "chromStart", " AND "); integerFilter(db, table, "chromEnd", "chromEnd", " AND "); } if (ct->fieldCount >= 4) { stringFilterOption(db, table, "name", " AND "); } if (ct->fieldCount >= 5) { numericFilter(db, table, "score", "score", " AND "); } if (ct->fieldCount >= 6) { stringFilterOption(db, table, "strand", " AND "); } if (ct->fieldCount >= 8) { integerFilter(db, table, "thickStart", "thickStart", " AND "); integerFilter(db, table, "thickEnd", "thickEnd", " AND "); } if (ct->fieldCount >= 12) { integerFilter(db, table, "blockCount", "blockCount", " AND "); } /* These are not bed fields, just extra constraints that we offer: */ if (ct->fieldCount >= 3) { integerFilter(db, table, "chromLength", "(chromEnd - chromStart)", (ct->fieldCount >= 8) ? " AND " : ""); } if (ct->fieldCount >= 8) { integerFilter( db, table, "thickLength", "(thickEnd - thickStart)", " AND "); eqFilterOption(db, table, "compareStarts","chromStart","thickStart", " AND "); eqFilterOption(db, table, "compareEnds", "chromEnd", "thickEnd", ""); } } puts("
(dataValue range: [%g,%g]) " "
"); if (ct->wiggle || isBigWigTable(table) || isBamTable(table) || isVcfTable(table)) { char *name; hPrintf("
Limit data output to: \n"); name = filterFieldVarName("ct", table, "_", filterMaxOutputVar); cgiMakeDropList(name, maxOutMenu, maxOutMenuSize, cartUsualString(cart, name, maxOutMenu[0])); hPrintf(" lines
\n"); } hPrintf("
\n"); cgiMakeButton(hgtaDoFilterSubmit, "submit"); hPrintf(" "); cgiMakeButton(hgtaDoMainPage, "cancel"); } static void filterControlsForTable(char *db, char *rootTable) /* Put up filter controls for a single table. */ { if (isCustomTrack(rootTable)) filterControlsForTableCt(db, rootTable); else filterControlsForTableDb(db, rootTable); cgiDown(0.7); // Extra spacing below the buttons } static void showLinkedFilters(struct dbTable *dtList) /* Put up a section with filters for each linked table. */ { struct dbTable *dt; for (dt = dtList; dt != NULL; dt = dt->next) { /* Put it up in a new section. */ webNewSection("%s.%s based filters", dt->db, dt->table); filterControlsForTable(dt->db, dt->table); } } #define filterLinkedTablePrefix hgtaFilterPrefix "linked." static void doBigFilterPage(struct sqlConnection *conn, char *db, char *table) /* Put up filter page on given db.table. */ { struct joiner *joiner = allJoiner; struct dbTable *dtList, *dt; char dbTableBuf[256]; if (strchr(table, '.')) htmlOpen("Filter on Fields from %s", table); else htmlOpen("Filter on Fields from %s.%s", db, table); jsIncludeFile("jquery.js", NULL); jsIncludeFile("utils.js", NULL); commonCssStyles(); hPrintf("
\n", cgiScriptName(), cartUsualString(cart, "formMethod", "POST")); cartSaveSession(cart); cgiMakeHiddenVar(hgtaDatabase, db); cgiMakeHiddenVar(hgtaTable, table); dbOverrideFromTable(dbTableBuf, &db, &table); filterControlsForTable(db, table); dtList = extraTableList(filterLinkedTablePrefix); showLinkedFilters(dtList); dt = dbTableNew(db, table); slAddHead(&dtList, dt); showLinkedTables(joiner, dtList, filterLinkedTablePrefix, hgtaDoFilterMore, "allow filtering using fields in checked tables"); hPrintf("
\n"); cgiDown(0.9); htmlClose(); } void doFilterMore(struct sqlConnection *conn) /* Continue with Filter Page. */ { char *db = cartString(cart, hgtaDatabase); char *table = cartString(cart, hgtaTable); doBigFilterPage(conn, db, table); } void doFilterPage(struct sqlConnection *conn) /* Respond to filter create/edit button */ { char *table = connectingTableForTrack(curTable); char *db = database; if (sameWord(table, WIKI_TRACK_TABLE)) db = wikiDbName(); doBigFilterPage(conn, db, table); } void doFilterSubmit(struct sqlConnection *conn) /* Respond to submit on filters page. */ { cartSetString(cart, hgtaFilterTable, getDbTable(database, curTable)); doMainPage(conn); } void doClearFilter(struct sqlConnection *conn) /* Respond to click on clear filter. */ { removeFilterVars(); doMainPage(conn); } void constrainFreeForm(char *rawQuery, struct dyString *clause) /* Let the user type in an expression that may contain * - field names * - parentheses * - comparison/arithmetic/logical operators * - numbers * - patterns with wildcards * Make sure they don't use any SQL reserved words, ;'s, etc. * Let SQL handle the actual parsing of nested expressions etc. - * this is just a token cop. */ { struct kxTok *tokList, *tokPtr; char *ptr; int numLeftParen, numRightParen; if ((rawQuery == NULL) || (rawQuery[0] == 0)) return; /* tokenize (do allow wildcards, and include quotes.) */ kxTokIncludeQuotes(TRUE); tokList = kxTokenizeFancy(rawQuery, TRUE, TRUE, TRUE); /* to be extra conservative, wrap the whole expression in parens. */ dyStringAppend(clause, "("); numLeftParen = numRightParen = 0; for (tokPtr = tokList; tokPtr != NULL; tokPtr = tokPtr->next) { if (tokPtr->spaceBefore) dyStringAppendC(clause, ' '); if ((tokPtr->type == kxtEquals) || (tokPtr->type == kxtGT) || (tokPtr->type == kxtGE) || (tokPtr->type == kxtLT) || (tokPtr->type == kxtLE) || (tokPtr->type == kxtAnd) || (tokPtr->type == kxtOr) || (tokPtr->type == kxtNot) || (tokPtr->type == kxtAdd) || (tokPtr->type == kxtSub) || (tokPtr->type == kxtDiv)) { dyStringAppend(clause, tokPtr->string); } else if (tokPtr->type == kxtOpenParen) { dyStringAppend(clause, tokPtr->string); numLeftParen++; } else if (tokPtr->type == kxtCloseParen) { dyStringAppend(clause, tokPtr->string); numRightParen++; } else if ((tokPtr->type == kxtWildString) || (tokPtr->type == kxtString)) { char *word = cloneString(tokPtr->string); toUpperN(word, strlen(word)); if (startsWith("SQL_", word) || startsWith("MYSQL_", word) || sameString("ALTER", word) || sameString("BENCHMARK", word) || sameString("CHANGE", word) || sameString("CREATE", word) || sameString("DELAY", word) || sameString("DELETE", word) || sameString("DROP", word) || sameString("FLUSH", word) || sameString("GET_LOCK", word) || sameString("GRANT", word) || sameString("INSERT", word) || sameString("KILL", word) || sameString("LOAD", word) || sameString("LOAD_FILE", word) || sameString("LOCK", word) || sameString("MODIFY", word) || sameString("PROCESS", word) || sameString("QUIT", word) || sameString("RELEASE_LOCK", word) || sameString("RELOAD", word) || sameString("REPLACE", word) || sameString("REVOKE", word) || sameString("SELECT", word) || sameString("SESSION_USER", word) || sameString("SHOW", word) || sameString("SYSTEM_USER", word) || sameString("UNLOCK", word) || sameString("UPDATE", word) || sameString("USE", word) || sameString("USER", word) || sameString("VERSION", word)) { errAbort("Illegal SQL word \"%s\" in free-form query string", tokPtr->string); } else if (sameString("*", tokPtr->string)) { // special case for multiplication in a wildcard world dyStringPrintf(clause, "%s", tokPtr->string); } else { /* Replace normal wildcard characters with SQL: */ while ((ptr = strchr(tokPtr->string, '?')) != NULL) *ptr = '_'; while ((ptr = strchr(tokPtr->string, '*')) != NULL) *ptr = '%'; dyStringPrintf(clause, "%s", tokPtr->string); } } else if (tokPtr->type == kxtPunct && sameString(",", tokPtr->string)) { /* Don't take just any old punct, but allow comma for in-lists. */ dyStringAppend(clause, tokPtr->string); } else if (tokPtr->type == kxtEnd) { break; } else { errAbort("Unrecognized token \"%s\" in free-form query string", tokPtr->string); } } dyStringAppend(clause, ")"); if (numLeftParen != numRightParen) errAbort("Unequal number of left parentheses (%d) and right parentheses (%d) in free-form query expression", numLeftParen, numRightParen); slFreeList(&tokList); } static boolean wildReal(char *pat) /* Return TRUE if pat is something we really might want to search on. */ { return pat != NULL && pat[0] != 0 && !sameString(pat, "*"); } static boolean cmpReal(char *pat, char *cmpOp) /* Return TRUE if we have a real cmpOp. */ { return pat != NULL && pat[0] != 0 && !sameString(cmpOp, cmpOpMenu[0]); } static boolean filteredOrLinked(char *db, char *table) /* Return TRUE if this table is the table to be filtered or if it is to be * linked with that table. */ { char *dbTable = getDbTable(db, table); char *filterTable = cartUsualString(cart, hgtaFilterTable, ""); boolean isFilterTable = sameString(dbTable, filterTable); freez(&dbTable); if (isFilterTable) return TRUE; else { char varName[256]; safef(varName, sizeof(varName), "%slinked.%s.%s", hgtaFilterPrefix, db, table); return cartUsualBoolean(cart, varName, FALSE); } } struct joinerDtf *filteringTables() /* Get list of tables we're filtering on as joinerDtf list (with * the field entry NULL). */ { if (!anyFilter()) return NULL; else { struct joinerDtf *dtfList = NULL, *dtf; struct hashEl *varList, *var; struct hash *uniqHash = hashNew(0); int prefixSize = strlen(hgtaFilterVarPrefix); varList = cartFindPrefix(cart, hgtaFilterVarPrefix); for (var = varList; var != NULL; var = var->next) { char *dupe = cloneString(var->name + prefixSize); char *parts[5]; int partCount; char dbTable[256]; char *db, *table, *field, *type; partCount = chopByChar(dupe, '.', parts, ArraySize(parts)); if (partCount != 4) { warn("Part count != expected 4 line %d of %s", __LINE__, __FILE__); continue; } db = parts[0]; table = parts[1]; field = parts[2]; type = parts[3]; safef(dbTable, sizeof(dbTable), "%s.%s", db, table); if (! filteredOrLinked(db, table)) continue; if (!hashLookup(uniqHash, dbTable)) { boolean gotFilter = FALSE; if (sameString(type, filterPatternVar)) { char *pat = trimSpaces(var->val); gotFilter = wildReal(pat); } else if (sameString(type, filterCmpVar)) { char *patVar = filterPatternVarName(db, table, field); char *pat = trimSpaces(cartOptionalString(cart, patVar)); gotFilter = cmpReal(pat, var->val); } else if (sameString(type, filterRawQueryVar)) { char *pat = trimSpaces(var->val); gotFilter = (pat != NULL && pat[0] != 0); } if (gotFilter) { hashAdd(uniqHash, dbTable, NULL); AllocVar(dtf); dtf->database = cloneString(db); dtf->table = cloneString(table); slAddHead(&dtfList, dtf); } } freeMem(dupe); } hashFree(&uniqHash); return dtfList; } } static char *getSqlType(struct sqlConnection *conn, char *table, char *field) /* Return the type of the given field. */ { struct sqlResult *sr = NULL; char **row = NULL; char query[512]; char *type = NULL; safef(query, sizeof(query), "describe %s %s", table, field); sr = sqlGetResult(conn, query); if ((row = sqlNextRow(sr)) != NULL) type = cloneString(row[1]); else errAbort("getSqlType: no results for query \"%s\".", query); sqlFreeResult(&sr); return type; } static void normalizePatList(struct slName **pPatList) /* patList might be a plain old list of terms, in which case we keep the * terms only if they are not no-ops. patList might also be one element * that is a space-separated list of terms, in which case we make a new * list item for each non-no-op term. (Trim spaces while we're at it.) */ { struct slName *pat, *nextPat, *patListOut = NULL; if (pPatList == NULL) return; for (pat = *pPatList; pat != NULL; pat = nextPat) { nextPat = pat->next; strcpy(pat->name, trimSpaces(pat->name)); if (hasWhiteSpace(pat->name)) { char *line = pat->name, *word; while ((word = nextQuotedWord(&line)) != NULL) if (wildReal(word)) { struct slName *newPat = slNameNew(word); slAddHead(&patListOut, newPat); } slNameFree(&pat); } else if (wildReal(pat->name)) slAddHead(&patListOut, pat); } *pPatList = patListOut; } char *filterClause(char *db, char *table, char *chrom, char *extraClause) /* Get filter clause (something to put after 'where') * for table */ { struct sqlConnection *conn = NULL; char varPrefix[128]; int varPrefixSize, fieldNameSize; struct hashEl *varList, *var; struct dyString *dy = NULL; boolean needAnd = FALSE; char oldDb[128]; char dbTableBuf[256]; char explicitDb[128]; char splitTable[256]; char explicitDbTable[512]; /* Return just extraClause (which may be NULL) if no filter on us. */ if (! (anyFilter() && filteredOrLinked(db, table))) return cloneString(extraClause); safef(oldDb, sizeof(oldDb), "%s", db); dbOverrideFromTable(dbTableBuf, &db, &table); if (!sameString(oldDb, db)) safef(explicitDb, sizeof(explicitDb), "%s.", db); else explicitDb[0] = 0; /* Cope with split table and/or custom tracks. */ if (isCustomTrack(table)) { conn = hAllocConn(CUSTOM_TRASH); struct customTrack *ct = ctLookupName(table); safef(explicitDbTable, sizeof(explicitDbTable), "%s", ct->dbTableName); } else { conn = hAllocConn(db); safef(splitTable, sizeof(splitTable), "%s_%s", chrom, table); if (!sqlTableExists(conn, splitTable)) safef(splitTable, sizeof(splitTable), "%s", table); safef(explicitDbTable, sizeof(explicitDbTable), "%s%s", explicitDb, splitTable); } /* Get list of filter variables for this table. */ safef(varPrefix, sizeof(varPrefix), "%s%s.%s.", hgtaFilterVarPrefix, db, table); varPrefixSize = strlen(varPrefix); varList = cartFindPrefix(cart, varPrefix); if (varList == NULL) { hFreeConn(&conn); return cloneString(extraClause); } /* Create filter clause string, stepping through vars. */ dy = dyStringNew(0); for (var = varList; var != NULL; var = var->next) { /* Parse variable name into field and type. */ char field[64], *s, *type; s = var->name + varPrefixSize; type = strchr(s, '.'); if (type == NULL) internalErr(); fieldNameSize = type - s; if (fieldNameSize >= sizeof(field)) internalErr(); memcpy(field, s, fieldNameSize); field[fieldNameSize] = 0; type += 1; /* rawLogic and rawQuery are handled below; * filterMaxOutputVar is not really a filter variable and is handled * in wiggle.c. */ if (startsWith("raw", type) || sameString(filterMaxOutputVar, type)) continue; /* Any other variables that are missing a name: * .. * are illegal */ if (fieldNameSize < 1) { warn("Missing name in cart variable: %s\n", var->name); continue; } if (sameString(type, filterDdVar)) { char *patVar = filterPatternVarName(db, table, field); struct slName *patList = cartOptionalSlNameList(cart, patVar); normalizePatList(&patList); if (slCount(patList) > 0) { char *ddVal = cartString(cart, var->name); boolean neg = sameString(ddVal, ddOpMenu[1]); char *fieldType = getSqlType(conn, explicitDbTable, field); boolean needOr = FALSE; if (needAnd) dyStringAppend(dy, " and "); needAnd = TRUE; if (neg) dyStringAppend(dy, "not "); boolean composite = (slCount(patList) > 1); if (composite || neg) dyStringAppendC(dy, '('); struct slName *pat; for (pat = patList; pat != NULL; pat = pat->next) { char *sqlPat = sqlLikeFromWild(pat->name); if (needOr) dyStringAppend(dy, " OR "); needOr = TRUE; if (isSqlSetType(fieldType)) { dyStringPrintf(dy, "FIND_IN_SET('%s', %s.%s)>0 ", sqlPat, explicitDbTable , field); } else { dyStringPrintf(dy, "%s.%s ", explicitDbTable, field); if (sqlWildcardIn(sqlPat)) dyStringAppend(dy, "like "); else dyStringAppend(dy, "= "); dyStringAppendC(dy, '\''); dyStringAppendEscapeQuotes(dy, sqlPat, '\'', '\\'); dyStringAppendC(dy, '\''); } freez(&sqlPat); } if (composite || neg) dyStringAppendC(dy, ')'); } } else if (sameString(type, filterCmpVar)) { char *patVar = filterPatternVarName(db, table, field); char *pat = trimSpaces(cartOptionalString(cart, patVar)); char *cmpVal = cartString(cart, var->name); if (cmpReal(pat, cmpVal)) { if (needAnd) dyStringAppend(dy, " and "); needAnd = TRUE; if (sameString(cmpVal, "in range")) { char *words[2]; int wordCount; char *dupe = cloneString(pat); wordCount = chopString(dupe, ", \t\n", words, ArraySize(words)); if (wordCount < 2) /* Fake short input */ words[1] = "2000000000"; if (strchr(pat, '.')) /* Assume floating point */ { double a = atof(words[0]), b = atof(words[1]); dyStringPrintf(dy, "%s.%s >= %f && %s.%s <= %f", explicitDbTable, field, a, explicitDbTable, field, b); } else { int a = atoi(words[0]), b = atoi(words[1]); dyStringPrintf(dy, "%s.%s >= %d && %s.%s <= %d", explicitDbTable, field, a, explicitDbTable, field, b); } freez(&dupe); } else { dyStringPrintf(dy, "%s.%s %s ", explicitDbTable, field, cmpVal); if (strchr(pat, '.')) /* Assume floating point. */ dyStringPrintf(dy, "%f", atof(pat)); else dyStringPrintf(dy, "%d", atoi(pat)); } } } } /* Handle rawQuery if any */ { char *varName; char *logic, *query; varName = filterFieldVarName(db, table, "", filterRawLogicVar); logic = cartUsualString(cart, varName, logOpMenu[0]); varName = filterFieldVarName(db, table, "", filterRawQueryVar); query = trimSpaces(cartOptionalString(cart, varName)); if (query != NULL && query[0] != 0) { if (needAnd) dyStringPrintf(dy, " %s ", logic); constrainFreeForm(query, dy); } } /* Clean up and return */ hFreeConn(&conn); hashElFreeList(&varList); if (dy->stringSize == 0) { dyStringFree(&dy); return cloneString(extraClause); } else { if (isNotEmpty(extraClause)) dyStringPrintf(dy, " and %s", extraClause); return dyStringCannibalize(&dy); } } void doTest(struct sqlConnection *conn) /* Put up a page to see what happens. */ { char *s = NULL; textOpen(); hPrintf("Doing test!\n"); s = filterClause("hg18", "knownGene", "chrX", NULL); if (s != NULL) hPrintf("%s\n", s); else hPrintf("%p\n", s); } void cgiToCharFilter(char *dd, char *pat, enum charFilterType *retCft, char **retVals, boolean *retInv) /* Given a "does/doesn't" and a (list of) literal chars from CGI, fill in * retCft, retVals and retInv to make a filter. */ { char *vals, *ptrs[32]; int numWords; int i; assert(retCft != NULL); assert(retVals != NULL); assert(retInv != NULL); assert(sameString(dd, "does") || sameString(dd, "doesn't")); /* catch null-constraint cases. ? will be treated as a literal match, * which would make sense for bed strand and maybe other single-char things: */ if (pat == NULL) pat = ""; pat = trimSpaces(pat); if ((pat[0] == 0) || sameString(pat, "*")) { *retCft = cftIgnore; return; } *retCft = cftMultiLiteral; numWords = chopByWhite(pat, ptrs, ArraySize(ptrs)); vals = needMem((numWords+1) * sizeof(char)); for (i=0; i < numWords; i++) vals[i] = ptrs[i][0]; vals[i] = 0; *retVals = vals; *retInv = sameString("doesn't", dd); } void cgiToStringFilter(char *dd, char *pat, enum stringFilterType *retSft, char ***retVals, boolean *retInv) /* Given a "does/doesn't" and a (list of) regexps from CGI, fill in * retCft, retVals and retInv to make a filter. */ { char **vals, *ptrs[32]; int numWords; int i; assert(retSft != NULL); assert(retVals != NULL); assert(retInv != NULL); assert(sameString(dd, "does") || sameString(dd, "doesn't")); /* catch null-constraint cases: */ if (pat == NULL) pat = ""; pat = trimSpaces(pat); if ((pat[0] == 0) || sameString(pat, "*")) { *retSft = sftIgnore; return; } *retSft = sftMultiRegexp; numWords = chopByWhite(pat, ptrs, ArraySize(ptrs)); vals = needMem((numWords+1) * sizeof(char *)); for (i=0; i < numWords; i++) vals[i] = cloneString(ptrs[i]); vals[i] = NULL; *retVals = vals; *retInv = sameString("doesn't", dd); } void cgiToDoubleFilter(char *cmp, char *pat, enum numericFilterType *retNft, double **retVals) /* Given a comparison operator and a (pair of) integers from CGI, fill in * retNft and retVals to make a filter. */ { char *ptrs[3]; double *vals; int numWords; assert(retNft != NULL); assert(retVals != NULL); /* catch null-constraint cases: */ if (pat == NULL) pat = ""; if (cmp == NULL) errAbort("Null cmp passed to cgiToDoubleFilter (pattern=%s)", pat); pat = trimSpaces(pat); if ((pat[0] == 0) || sameString(pat, "*") || sameString(cmp, "ignored")) { *retNft = nftIgnore; *retVals = NULL; return; } else if (sameString(cmp, "in range")) { *retNft = nftInRange; numWords = chopString(pat, " \t,", ptrs, ArraySize(ptrs)); if (numWords != 2) errAbort("For \"in range\" constraint, you must give two numbers separated by whitespace or comma."); vals = needMem(2 * sizeof(double)); vals[0] = atof(ptrs[0]); vals[1] = atof(ptrs[1]); if (vals[0] > vals[1]) { int tmp = vals[0]; vals[0] = vals[1]; vals[1] = tmp; } *retVals = vals; } else { if (sameString(cmp, "<")) *retNft = nftLessThan; else if (sameString(cmp, "<=")) *retNft = nftLTE; else if (sameString(cmp, "=")) *retNft = nftEqual; else if (sameString(cmp, "!=")) *retNft = nftNotEqual; else if (sameString(cmp, ">=")) *retNft = nftGTE; else if (sameString(cmp, ">")) *retNft = nftGreaterThan; else errAbort("Unrecognized comparison operator %s", cmp); vals = needMem(2*sizeof(double)); // 2* simplifies conversion elsewhere. vals[0] = atof(pat); *retVals = vals; } } void cgiToIntFilter(char *cmp, char *pat, enum numericFilterType *retNft, int **retVals) /* Given a comparison operator and a (pair of) integers from CGI, fill in * retNft and retVals to make a filter. */ { double *doubleVals; cgiToDoubleFilter(cmp, pat, retNft, &doubleVals); if (doubleVals == NULL) { *retVals = NULL; return; } int *intVals = needMem(2*sizeof(int)); intVals[0] = doubleVals[0]; intVals[1] = doubleVals[1]; freeMem(doubleVals); *retVals = intVals; } void cgiToLongFilter(char *cmp, char *pat, enum numericFilterType *retNft, long long **retVals) /* Given a comparison operator and a (pair of) integers from CGI, fill in * retNft and retVals to make a filter. */ { double *doubleVals; cgiToDoubleFilter(cmp, pat, retNft, &doubleVals); if (doubleVals == NULL) { *retVals = NULL; return; } long long *longVals = needMem(2*sizeof(long long)); longVals[0] = doubleVals[0]; longVals[1] = doubleVals[1]; freeMem(doubleVals); *retVals = longVals; }