054cf0f3a8507c04aecde986ecee4f2979b8d4f3
chmalee
  Mon Nov 10 15:00:32 2025 -0800
Make listExisting endpoint to hubApi/liftOver respect fromGenome and toGenome arguments, taking precedence over the filter argument. If both fromGenome and toGenome arguments are present they are ANDed together

diff --git src/hg/hubApi/liftOver.c src/hg/hubApi/liftOver.c
index 8ce55f0346e..8592d415495 100644
--- src/hg/hubApi/liftOver.c
+++ src/hg/hubApi/liftOver.c
@@ -58,55 +58,67 @@
     jsonWriteListStart(jw, NULL);
     jsonWriteString(jw, NULL, toDb);
     jsonWriteString(jw, NULL, position);
     jsonWriteString(jw, NULL, coverage);
     jsonWriteListEnd(jw);
     }
 jsonWriteListEnd(jw);
 apiFinishOutput(0, NULL, jw);
 }
 
 static void listExisting()
 /* output the fromDb,toDb from liftOverChain.hgcentral SQL table */
 {
 long long itemCount = 0;
 char *filter = cgiOptionalString(argFilter);
+char *fromDb = cgiOptionalString(argFromGenome);
+char *toDb = cgiOptionalString(argToGenome);
 
 struct sqlConnection *conn = hConnectCentral();
 char *tableName = cloneString(liftOverChainTable());
-char query[1024];
-sqlSafef(query, sizeof(query), "SELECT count(*) FROM %s", tableName);
-long long totalRows = sqlQuickLongLong(conn, query);
+struct dyString *query = newDyString(0);
+sqlDyStringPrintf(query, "SELECT count(*) FROM %s", tableName);
+long long totalRows = sqlQuickLongLong(conn, dyStringContents(query));
+dyStringClear(query);
 
-if (isEmpty(filter))
+if (isNotEmpty(fromDb) || isNotEmpty(toDb))
     {
-    sqlSafef(query, sizeof(query), "SELECT fromDb,toDb FROM %s LIMIT %d", tableName, maxItemsOutput);
+    sqlDyStringPrintf(query, "SELECT fromDb,toDb FROM %s WHERE ", tableName);
+    if (isNotEmpty(fromDb))
+        sqlDyStringPrintf(query, "LOWER(fromDb) = LOWER('%s') %s ", fromDb, isNotEmpty(toDb) ? "AND" : "");
+    if (isNotEmpty(toDb))
+        sqlDyStringPrintf(query, "LOWER(toDb) = LOWER('%s') ", toDb);
+    sqlDyStringPrintf(query, "LIMIT %d;", maxItemsOutput);
+    }
+else if (isNotEmpty(filter))
+    {
+    sqlDyStringPrintf(query, "SELECT fromDb,toDb FROM %s WHERE LOWER(fromDb) = LOWER('%s') OR LOWER(toDb) = LOWER('%s') LIMIT %d;", tableName, filter, filter, maxItemsOutput);
     }
 else
     {
-    sqlSafef(query, sizeof(query), "SELECT fromDb,toDb FROM %s WHERE LOWER(fromDb) = LOWER('%s') OR LOWER(toDb) = LOWER('%s') LIMIT %d;", tableName, filter, filter, maxItemsOutput);
+    sqlDyStringPrintf(query, "SELECT fromDb,toDb FROM %s LIMIT %d", tableName, maxItemsOutput);
     }
 
 char *dataTime = sqlTableUpdate(conn, tableName);
 time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
 replaceChar(dataTime, ' ', 'T');	/* ISO 8601 */
 struct jsonWrite *jw = apiStartOutput();
 jsonWriteString(jw, "dataTime", dataTime);
 jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
 
 jsonWriteListStart(jw, "existingLiftOvers");
-struct sqlResult *sr = sqlGetResult(conn, query);
+struct sqlResult *sr = sqlGetResult(conn, dyStringCannibalize(&query));
 char **row;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     ++itemCount;
     jsonWriteListStart(jw, NULL);
     jsonWriteString(jw, NULL, row[0]);
     jsonWriteString(jw, NULL, row[1]);
     jsonWriteListEnd(jw);
     }
 jsonWriteListEnd(jw);
 jsonWriteNumber(jw, "totalLiftOvers", totalRows);
 jsonWriteNumber(jw, "itemsReturned", itemCount);
 
 apiFinishOutput(0, NULL, jw);
 hDisconnectCentral(&conn);