f6772f5e8a7399f1430561d708c3dcf033241cc8
hiram
  Wed Mar 20 15:27:49 2019 -0700
consolidate the tableDataOutput functions refs #18869

diff --git src/hg/hubApi/getData.c src/hg/hubApi/getData.c
index cb71a24..6b13bbb 100644
--- src/hg/hubApi/getData.c
+++ src/hg/hubApi/getData.c
@@ -1,22 +1,86 @@
 /* manage endpoint /getData/ functions */
 
 #include "dataApi.h"
 
-static void tableDataOutput(struct sqlConnection *conn, struct jsonWrite *jw, char *query, char *table)
+static void wigTableDataOutput(struct jsonWrite *jw, char *database, char *table, char *chrom, int start, int end,
+    unsigned maxItems)
+/* output wiggle data from the given table and specified chrom:start-end */
+{
+struct wiggleDataStream *wds = wiggleDataStreamNew();
+wds->setMaxOutput(wds, maxItems);
+wds->setChromConstraint(wds, chrom);
+wds->setPositionConstraint(wds, start, end);
+int operations = wigFetchAscii;
+long long valuesMatched = wds->getData(wds, database, table, operations);
+jsonWriteNumber(jw, "valuesMatched", valuesMatched);
+struct wigAsciiData *el;
+jsonWriteListStart(jw, chrom);
+unsigned itemCount = 0;
+for (el = wds->ascii; itemCount < maxItems && el; el = el->next)
+    {
+    jsonWriteObjectStart(jw, NULL);
+    int s = el->data->chromStart;
+    int e = s + el->span;
+    double val = el->data->value;
+    jsonWriteNumber(jw, "start", (long long)s);
+    jsonWriteNumber(jw, "end", (long long)e);
+    jsonWriteDouble(jw, "value", val);
+    jsonWriteObjectEnd(jw);
+    ++itemCount;
+    }
+jsonWriteListEnd(jw);
+}
+
+
+static void tableDataOutput(char *db, struct trackDb *tdb,
+    struct sqlConnection *conn, struct jsonWrite *jw, char *table,
+    char *chrom, unsigned start, unsigned end, unsigned maxItems)
 /* output the table data from the specified query string */
 {
+char query[4096];
+/* no chrom specified, return entire table */
+if (isEmpty(chrom))
+    sqlSafef(query, sizeof(query), "select * from %s", table);
+else if (0 == (start + end))	/* have chrom, no start,end == full chr */
+    {
+    /* need to extend the chrom column check to allow tName also */
+    if (! sqlColumnExists(conn, table, "chrom"))
+	apiErrAbort("track '%s' is not a position track, request track without chrom specification, genome: '%s'", table, db);
+    struct chromInfo *ci = hGetChromInfo(db, chrom);
+    jsonWriteNumber(jw, "start", (long long)0);
+    jsonWriteNumber(jw, "end", (long long)ci->size);
+    sqlSafef(query, sizeof(query), "select * from %s where chrom='%s'", table, chrom);
+    }
+else	/* fully specified chrom:start-end */
+    {
+    if (! sqlColumnExists(conn, table, "chrom"))
+	apiErrAbort("track '%s' is not a position track, request track without chrom specification, genome: '%s'", table, db);
+    jsonWriteNumber(jw, "start", (long long)start);
+    jsonWriteNumber(jw, "end", (long long)end);
+    if (startsWith("wig", tdb->type))
+	{
+        wigTableDataOutput(jw, db, table, chrom, start, end, maxItems);
+        return;	/* DONE */
+	}
+    else
+	{
+	sqlSafef(query, sizeof(query), "select * from %s where chrom='%s' AND chromEnd > %u AND chromStart < %u", table, chrom, start, end);
+	}
+    }
+
+/* continuing, not a wiggle output */
 char **columnNames = NULL;
 char **columnTypes = NULL;
 int columnCount = tableColumns(conn, jw, table, &columnNames, &columnTypes);
 jsonWriteListStart(jw, table);
 struct sqlResult *sr = sqlGetResult(conn, query);
 char **row = NULL;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     jsonWriteObjectStart(jw, NULL);
     int i = 0;
     for (i = 0; i < columnCount; ++i)
 	jsonWriteString(jw, columnNames[i], row[i]);
     jsonWriteObjectEnd(jw);
     }
 sqlFreeResult(&sr);
@@ -250,109 +314,116 @@
 	bedDataOutput(jw, bbi, chrom, uStart, uEnd, maxItems, fiList);
     jsonWriteListEnd(jw);
     }
 else if (startsWith("bigWig", thisTrack->type))
     {
     unsigned maxItems = 1000;	/* TBD will use this later for paging */
     jsonWriteObjectStart(jw, track);
     wigData(jw, bbi, chrom, uStart, uEnd, maxItems);
     jsonWriteObjectEnd(jw);
     }
 bbiFileClose(&bbi);
 jsonWriteObjectEnd(jw);
 fputs(jw->dy->string,stdout);
 }	/*	static void getHubTrackData(char *hubUrl)	*/
 
-static void getTrackData()
+static void getTrackData(unsigned maxItems)
 /* return data from a track, optionally just one chrom data,
  *  optionally just one section of that chrom data
  */
 {
 char *db = cgiOptionalString("db");
 char *chrom = cgiOptionalString("chrom");
 char *start = cgiOptionalString("start");
 char *end = cgiOptionalString("end");
-char *table = cgiOptionalString("track");
 /* 'track' name in trackDb refers to a SQL 'table' */
+char *table = cgiOptionalString("track");
+unsigned chromSize = 0;	/* maybe set later */
+unsigned uStart = 0;
+unsigned uEnd = chromSize;	/* maybe set later */
+if ( ! (isEmpty(start) || isEmpty(end)) )
+    {
+    uStart = sqlUnsigned(start);
+    uEnd = sqlUnsigned(end);
+    if (uEnd < uStart)
+	apiErrAbort("given start coordinate %u is greater than given end coordinate", uStart, uEnd);
+    }
 
 if (isEmpty(db))
     apiErrAbort("missing URL variable db=<ucscDb> name for endpoint '/getData/track");
 if (isEmpty(table))
     apiErrAbort("missing URL variable track=<trackName> name for endpoint '/getData/track");
 
 struct trackDb *thisTrack = hTrackDbForTrackAndAncestors(db, table);
 if (NULL == thisTrack)
     apiErrAbort("can not find track=%s name for endpoint '/getData/track", table);
 
 struct sqlConnection *conn = hAllocConn(db);
 if (! sqlTableExists(conn, table))
     apiErrAbort("can not find specified 'track=%s' for endpoint: /getData/track?db=%s&track=%s", table, db, table);
 
 struct jsonWrite *jw = apiStartOutput();
 jsonWriteString(jw, "db", db);
 // jsonWriteString(jw, "track", table);
 char *dataTime = sqlTableUpdate(conn, table);
 time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
 replaceChar(dataTime, ' ', 'T');	/*	ISO 8601	*/
 jsonWriteString(jw, "dataTime", dataTime);
 jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
 jsonWriteString(jw, "trackType", thisTrack->type);
 
 char query[4096];
-unsigned chromSize = 0;
 struct bbiFile *bbi = NULL;
 struct bbiChromInfo *chromList = NULL;
 
 if (startsWith("big", thisTrack->type))
     {
     char *bigDataUrl = trackDbSetting(thisTrack, "bigDataUrl");
     if (bigDataUrl)
 	bbi = bigFileOpen(thisTrack->type, bigDataUrl);
     else
 	{
 	char quickReturn[2048];
         sqlSafef(query, sizeof(query), "select fileName from %s", table);
         if (sqlQuickQuery(conn, query, quickReturn, sizeof(quickReturn)))
 	    {
 	    bigDataUrl = cloneString(quickReturn);
 	    bbi = bigFileOpen(thisTrack->type, bigDataUrl);
 	    }
 	}
     if (NULL == bbi)
 	apiErrAbort("failed to find bigDataUrl=%s for track=%s in database=%s for endpoint '/getdata/track'", table, db);
     if (isNotEmpty(chrom))
 	{
 //	jsonWriteString(jw, "chrom", chrom);
 	chromSize = bbiChromSize(bbi, chrom);
 	if (0 == chromSize)
 	    apiErrAbort("can not find specified chrom=%s in bigWig file URL %s", chrom, bigDataUrl);
+	uEnd = chromSize;
 	jsonWriteNumber(jw, "chromSize", (long long)chromSize);
 	}
 else
 	{
 	chromList = bbiChromList(bbi);
 	jsonWriteNumber(jw, "chromCount", (long long)slCount(chromList));
 	}
      jsonWriteString(jw, "bigDataUrl", bigDataUrl);
     }
 
-unsigned uStart = 0;
-unsigned uEnd = chromSize;
-if ( ! (isEmpty(start) || isEmpty(end)) )
+/* when start, end given, show them */
+if ( uEnd > uStart )
     {
-    uStart = sqlUnsigned(start);
-    uEnd = sqlUnsigned(end);
     jsonWriteNumber(jw, "start", uStart);
     jsonWriteNumber(jw, "end", uEnd);
     }
 
 if (startsWith("bigBed", thisTrack->type))
     {
     struct asObject *as = bigBedAsOrDefault(bbi);
     struct sqlFieldType *fiList = sqlFieldTypesFromAs(as);
     unsigned maxItems = 1000;	/* TBD will use this later for paging */
 #ifdef NOT
     jsonWriteListStart(jw, "columnNames");
 //    int columnCount = slCount(fiList);
     for (fi = fiList; fi; fi = fi->next)
 	{
 	jsonWriteObjectStart(jw, NULL);
@@ -364,62 +435,39 @@
     jsonWriteListStart(jw, table);
     if (isEmpty(chrom))
 	{
 	struct bbiChromInfo *bci;
 	for (bci = chromList; bci; bci = bci->next)
 	    {
 	    bedDataOutput(jw, bbi, bci->name, 0, bci->size, maxItems, fiList);
 	    }
 	}
     else
 	bedDataOutput(jw, bbi, chrom, uStart, uEnd, maxItems, fiList);
     jsonWriteListEnd(jw);
     }
 else if (startsWith("bigWig", thisTrack->type))
     {
-    unsigned maxItems = 1000;	/* TBD will use this later for paging */
     jsonWriteObjectStart(jw, table);
     wigData(jw, bbi, chrom, uStart, uEnd, maxItems);
     jsonWriteObjectEnd(jw);
     bbiFileClose(&bbi);
     }
-else if (isEmpty(chrom))
-    {
-    /* no chrom specified, return entire table */
-    sqlSafef(query, sizeof(query), "select * from %s", table);
-    tableDataOutput(conn, jw, query, table);
-    }
-else if (isEmpty(start) || isEmpty(end))
-    {
-    if (! sqlColumnExists(conn, table, "chrom"))
-	apiErrAbort("track '%s' is not a position track, request track without chrom specification, genome: '%s'", table, db);
-    struct chromInfo *ci = hGetChromInfo(db, chrom);
-    jsonWriteNumber(jw, "start", (long long)0);
-    jsonWriteNumber(jw, "end", (long long)ci->size);
-    sqlSafef(query, sizeof(query), "select * from %s where chrom='%s'", table, chrom);
-    tableDataOutput(conn, jw, query, table);
-    }
 else
-    {
-    if (! sqlColumnExists(conn, table, "chrom"))
-	apiErrAbort("track '%s' is not a position track, request track without chrom specification, genome: '%s'", table, db);
-    jsonWriteNumber(jw, "start", (long long)sqlSigned(start));
-    jsonWriteNumber(jw, "end", (long long)sqlSigned(end));
-    sqlSafef(query, sizeof(query), "select * from %s where chrom='%s' AND chromEnd > %d AND chromStart < %d", table, chrom, sqlSigned(start), sqlSigned(end));
-    tableDataOutput(conn, jw, query, table);
-    }
-jsonWriteObjectEnd(jw);
+    tableDataOutput(db, thisTrack, conn, jw, table, chrom, uStart, uEnd, maxItems);
+
+jsonWriteObjectEnd(jw);	/* closing the overall global object */
 fputs(jw->dy->string,stdout);
 hFreeConn(&conn);
 }
 
 static void getSequenceData()
 /* return DNA sequence, given at least a db=name and chrom=chr,
    optionally start and end  */
 {
 char *db = cgiOptionalString("db");
 char *chrom = cgiOptionalString("chrom");
 char *start = cgiOptionalString("start");
 char *end = cgiOptionalString("end");
 
 if (isEmpty(db))
     apiErrAbort("missing URL db=<ucscDb> name for endpoint '/getData/sequence");
@@ -448,28 +496,30 @@
         jsonWriteNumber(jw, "start", (long long)sqlSigned(start));
         jsonWriteNumber(jw, "end", (long long)sqlSigned(end));
 	}
     jsonWriteString(jw, "dna", seq->dna);
     jsonWriteObjectEnd(jw);
     fputs(jw->dy->string,stdout);
     freeDnaSeq(&seq);
     }
 else
     apiErrAbort("can not find specified chrom=%s in sequence for endpoint '/getData/sequence?db=%s&chrom=%s", chrom, db, chrom);
 }
 
 void apiGetData(char *words[MAX_PATH_INFO])
 /* 'getData' function, words[1] is the subCommand */
 {
+unsigned maxItems = 1000;	/* TBD XXX determine output limit */
+
 if (sameWord("track", words[1]))
     {
     char *hubUrl = cgiOptionalString("hubUrl");
     if (isNotEmpty(hubUrl))
 	getHubTrackData(hubUrl);
     else
-	getTrackData();
+	getTrackData(maxItems);
     }
 else if (sameWord("sequence", words[1]))
     getSequenceData();
 else
     apiErrAbort("do not recognize endpoint function: '/%s/%s'", words[0], words[1]);
 }