1dfa3b9f97577f088af82ed02340c07886d690fd galt Fri Oct 7 16:52:26 2022 -0700 Various bugfixes for sqlSafef v2 and some of Jims refactoring like adding fuseCsvFields so it can support multiple tables in the from list. Also fixed an encoding issue for sample labels that had a double-quote in them. Updated jquery.min.js to point to newer version 1.12. diff --git src/hg/lib/tablesTables.c src/hg/lib/tablesTables.c index ce546f2..16f16ac 100644 --- src/hg/lib/tablesTables.c +++ src/hg/lib/tablesTables.c @@ -106,31 +106,33 @@ } static void printSuggestScript(char *id, struct slName *suggestList) /* Print out a little javascript to wrap auto-suggester around control with given ID */ { struct dyString *dy = dyStringNew(256); dyStringPrintf(dy,"$(document).ready(function() {\n"); dyStringPrintf(dy," $('#%s').autocomplete({\n", id); dyStringPrintf(dy," delay: 100,\n"); dyStringPrintf(dy," minLength: 0,\n"); dyStringPrintf(dy," source: ["); char *separator = ""; struct slName *suggest; for (suggest = suggestList; suggest != NULL; suggest = suggest->next) { - dyStringPrintf(dy,"%s\"%s\"", separator, suggest->name); + dyStringPrintf(dy,"%s\"", separator); + dyStringAppendEscapeQuotes(dy, suggest->name, '"', '\\'); + dyStringPrintf(dy, "\""); separator = ","; } dyStringPrintf(dy,"]\n"); dyStringPrintf(dy," });\n"); dyStringPrintf(dy,"});\n"); jsInline(dy->string); dyStringFree(&dy); } #ifdef NOT_CURRENTLY_USED static void printWatermark(char *id, char *watermark) /* Print light text filter prompt as watermark. */ { jsInlineF( "$(function() {\n" @@ -819,30 +821,31 @@ void webTableBuildQuery(struct cart *cart, char *from, char *initialWhere, char *varPrefix, char *fields, boolean withFilters, struct dyString **retQuery, struct dyString **retWhere) /* Construct select, from and where clauses in query, keeping an additional copy of where * Returns the SQL query and the SQL where expression as two dyStrings (need to be freed) */ { struct dyString *query = dyStringNew(0); struct dyString *where = dyStringNew(0); struct slName *field, *fieldList = commaSepToSlNames(fields); boolean gotWhere = FALSE; sqlCkIl(fieldsSafe,fields) sqlCkIl(fromSafe,from) +// from can be a list of tables if joining sqlDyStringPrintf(query, "select %-s from %-s", fieldsSafe, fromSafe); if (!isEmpty(initialWhere)) { sqlDyStringPrintf(where, " where "); sqlSanityCheckWhere(initialWhere, where); gotWhere = TRUE; } /* If we're doing filters, have to loop through the row of filter controls */ if (withFilters) { for (field = fieldList; field != NULL; field = field->next) { char varName[128]; safef(varName, sizeof(varName), "%s_f_%s", varPrefix, field->name); @@ -902,71 +905,71 @@ safef(orderVar, sizeof(orderVar), "%s_order", varPrefix); char *orderFields = cartUsualString(cart, orderVar, ""); if (!isEmpty(orderFields)) { if (orderFields[0] == '-') sqlDyStringPrintf(query, " order by %s desc", orderFields+1); else sqlDyStringPrintf(query, " order by %s", orderFields); } // return query and where expression *retQuery = query; *retWhere = where; } -struct dyString *fuseCsvFields(struct sqlConnection *conn, char *table, +struct dyString *fuseCsvFields(struct sqlConnection *conn, char *tables, char *firstCsv, char *secondCsv) /* Return a list that is firstCsv followed by any fields in secondCsv not already in firstCsv * "a,b,c,d", "b,f,d,e" yeilds "a,b,c,d,f,e" * order is preserved in firstCsv and when possible in second */ { struct hash *uniq = hashNew(0); struct dyString *result = dyStringNew(0); /* Add everything in aList to both hash and result */ struct slName *el; struct slName *aList = slNameListFromComma(firstCsv); for (el = aList; el != NULL; el = el->next) { - if (sqlColumnExists(conn, table, el->name)) + if (sqlColumnExistsInTablesList(conn, tables, el->name)) csvEscapeAndAppend(result, el->name); hashAdd(uniq, el->name, NULL); } /* Only add bList if it's not in there already and it is in database */ struct slName *bList = slNameListFromComma(secondCsv); for (el = bList; el != NULL; el = el->next) { if (!hashLookup(uniq, el->name)) { - if (sqlColumnExists(conn, table, el->name)) + if (sqlColumnExistsInTablesList(conn, tables, el->name)) csvEscapeAndAppend(result, el->name); hashAdd(uniq, el->name, NULL); } } /* Clean up and return with result */ slFreeList(&aList); slFreeList(&bList); hashFree(&uniq); return result; } void webFilteredSqlTable(struct cart *cart, /* User set preferences here */ struct sqlConnection *conn, /* Connection to database */ - char *fields, char *from, char *initialWhere, /* Our query in three parts */ + char *fields, char *from, char *initialWhere, /* Our query in three parts, from can be a table list if joining */ char *returnUrl, char *varPrefix, /* Url to get back to us, and cart var prefix */ int maxFieldWidth, /* How big do we let fields get in characters */ struct hash *tagOutWrappers, /* A hash full of callbacks, one for each column */ void *wrapperContext, /* Gets passed to callbacks in tagOutWrappers */ boolean withFilters, /* If TRUE put up filter controls under labels */ char *pluralInstructions, /* If non-NULL put up instructions and clear/search buttons */ int pageSize, /* How many items per page */ int facetUsualSize, /* How many items in a facet before opening */ struct hash *suggestHash, /* If using filter can put suggestions for categorical items here */ char *visibleFacetList, /* Comma separated list of fields to facet on */ void (*addFunc)(int) ) /* Callback relevant with pluralInstructions only */ /* Turn sql query into a nice interactive table, possibly with facets. It constructs * a query to the database in conn that is basically a select query broken into * separate clauses, construct and display an HTML table around results. Optionally table * may have a faceted search to the left or fields that can filter under the labels. The table @@ -997,30 +1000,31 @@ page = cartUsualInt(cart, pageVar, 0) - 1; if (page < 0) page = 0; context.tableOffset = page * pageSize; if (visibleFacetList) { table = fieldedTableAndCountsFromDbQuery(conn, query->string, pageSize, context.tableOffset, selectedFacetValues, &ffArray, &context.tableSize); } else { /* Figure out size of query result */ sqlCkIl(fromSafe,from) struct dyString *countQuery = sqlDyStringCreate("select count(*) from %-s", fromSafe); + if (!isEmpty(where->string)) sqlDyStringPrintf(countQuery, "%-s", where->string); // trust context.tableSize = sqlQuickNum(conn, countQuery->string); dyStringFree(&countQuery); } if (context.tableSize > pageSize) { int lastPage = (context.tableSize-1)/pageSize; if (page > lastPage) page = lastPage; context.tableOffset = page * pageSize; } if (!visibleFacetList) {