8b4f30c62401568eb2b560d42a145418f44968fe
hiram
  Thu Apr 18 15:46:50 2019 -0700
objects for tableColumns information refs #18869

diff --git src/hg/hubApi/getData.c src/hg/hubApi/getData.c
index d45f568..90a2fc1 100644
--- src/hg/hubApi/getData.c
+++ src/hg/hubApi/getData.c
@@ -45,33 +45,52 @@
     int jsonType)
 /* output a json item, determine type, appropriate output, name can be NULL */
 {
 if (JSON_DOUBLE == jsonType)
     jsonWriteDouble(jw, name, sqlDouble(val));
 else if (JSON_NUMBER == jsonType)
     jsonWriteNumber(jw, name, sqlLongLong(val));
 else
     jsonWriteString(jw, name, val);
 }
 
 static void wigColumnTypes(struct jsonWrite *jw)
 /* output column headers for a wiggle data output schema */
 {
 jsonWriteListStart(jw, "columnTypes");
-jsonWriteString(jw, NULL, "start - int - number");
-jsonWriteString(jw, NULL, "end - int - number");
-jsonWriteString(jw, NULL, "value - float - number");
+
+jsonWriteObjectStart(jw, NULL);
+jsonWriteString(jw, "name", "start");
+jsonWriteString(jw, "sqlType", "int");
+jsonWriteString(jw, "jsonType", "number");
+jsonWriteString(jw, "description", "chromStart: 0-based chromosome start position");
+jsonWriteObjectEnd(jw);
+
+jsonWriteObjectStart(jw, NULL);
+jsonWriteString(jw, "name", "end");
+jsonWriteString(jw, "sqlType", "int");
+jsonWriteString(jw, "jsonType", "number");
+jsonWriteString(jw, "description", "chromEnd: 1-based chromosome end position");
+jsonWriteObjectEnd(jw);
+
+jsonWriteObjectStart(jw, NULL);
+jsonWriteString(jw, "name", "value");
+jsonWriteString(jw, "sqlType", "float");
+jsonWriteString(jw, "jsonType", "number");
+jsonWriteString(jw, "description", "numerical data value for this location:start-end");
+jsonWriteObjectEnd(jw);
+
 jsonWriteListEnd(jw);
 }	/* static void wigColumnTypes(struct jsonWrite jw) */
 
 static unsigned sqlQueryJsonOutput(struct sqlConnection *conn,
     struct jsonWrite *jw, char *query, int columnCount, char **columnNames,
 	int *jsonTypes, unsigned itemsDone)
 /* with the SQL query set up, run through those selected items */
 {
 struct sqlResult *sr = sqlGetResult(conn, query);
 char **row = NULL;
 unsigned itemCount = 0;
 while ((itemCount+itemsDone) < maxItemsOutput && (row = sqlNextRow(sr)) != NULL)
     {
     int i = 0;
     if (jsonOutputArrays)
@@ -117,38 +136,38 @@
 char *tableName = trackDbSetting(tdb, "table");
 if (isNotEmpty(tableName))
     {
     freeMem(sqlTable);
     sqlTable = cloneString(tableName);
     jsonWriteString(jw, "sqlTable", sqlTable);
     }
 
 /* to be determined if this name is used or changes */
 char *splitSqlTable = cloneString(sqlTable);
 
 /* this function knows how to deal with split chromosomes, the NULL
  * here for the chrom name means to use the first chrom name in chromInfo
  */
 struct hTableInfo *hti = hFindTableInfoWithConn(conn, NULL, sqlTable);
-if (debug)
+if (debug && hti)
     {
     jsonWriteBoolean(jw, "isPos", hti->isPos);
     jsonWriteBoolean(jw, "isSplit", hti->isSplit);
     jsonWriteBoolean(jw, "hasBin", hti->hasBin);
     }
 /* check if table name needs to be modified */
-if (hti->isSplit)
+if (hti && hti->isSplit)
     {
     if (isNotEmpty(chrom))
 	{
 	char fullTableName[256];
 	safef(fullTableName, sizeof(fullTableName), "%s_%s", chrom, hti->rootName);
 	freeMem(splitSqlTable);
 	splitSqlTable = cloneString(fullTableName);
 	if (debug)
 	    jsonWriteString(jw, "splitSqlTable", splitSqlTable);
 	}
     else
 	{
 	char *defaultChrom = hDefaultChrom(db);
 	char fullTableName[256];
 	safef(fullTableName, sizeof(fullTableName), "%s_%s", defaultChrom, hti->rootName);
@@ -228,49 +247,59 @@
 
 /* continuing, not a wiggle output */
 char **columnNames = NULL;
 char **columnTypes = NULL;
 int *jsonTypes = NULL;
 struct asObject *as = asForTable(conn, splitSqlTable, tdb);
 struct asColumn *columnEl = as->columnList;
 int asColumnCount = slCount(columnEl);
 int columnCount = tableColumns(conn, jw, splitSqlTable, &columnNames, &columnTypes, &jsonTypes);
 if (jsonOutputArrays || debug)
     {
     jsonWriteListStart(jw, "columnTypes");
     int i = 0;
     for (i = 0; i < columnCount; ++i)
 	{
-	char typeString[1024];
-	if ((0 == i) && (asColumnCount == (columnCount - 1)) && (sameWord("bin", columnNames[i])))
-	    safef(typeString, sizeof(typeString), "%s - %s - %s - Indexing field to speed chromosome range queries", columnNames[i], columnTypes[i], jsonTypeStrings[jsonTypes[i]]);
+	jsonWriteObjectStart(jw, NULL);
+	jsonWriteString(jw, "name", columnNames[i]);
+	jsonWriteString(jw, "sqlType", columnTypes[i]);
+	jsonWriteString(jw, "jsonType", jsonTypeStrings[jsonTypes[i]]);
+	if ((0 == i) && (hti && hti->hasBin))
+	    jsonWriteString(jw, "description", "Indexing field to speed chromosome range queries");
 	else if (columnEl && isNotEmpty(columnEl->comment))
-	    safef(typeString, sizeof(typeString), "%s - %s - %s - %s", columnNames[i], columnTypes[i], jsonTypeStrings[jsonTypes[i]], columnEl->comment);
+	    jsonWriteString(jw, "description", columnEl->comment);
 	else
-	    safef(typeString, sizeof(typeString), "%s - %s - %s", columnNames[i], columnTypes[i], jsonTypeStrings[jsonTypes[i]]);
-	jsonWriteString(jw, NULL, typeString);
-        if (columnEl && ! ((0 == i) && (sameWord("bin", columnNames[i]))))
+	    jsonWriteString(jw, "description", "");
+
+	/* perhaps move the comment pointer forward */
+        if (columnEl)
+	    {
+	    if (asColumnCount == columnCount)
+		columnEl = columnEl->next;
+	    else if (! ((0 == i) && (hti && hti->hasBin)))
 		columnEl = columnEl->next;
 	    }
+	jsonWriteObjectEnd(jw);
+	}
     jsonWriteListEnd(jw);
     }
 jsonWriteListStart(jw, track);
 
 unsigned itemsDone = 0;
 
 /* empty chrom and isSplit, needs to run through all chrom names */
-if (hti->isSplit && isEmpty(chrom))
+if ((hti && hti->isSplit) && isEmpty(chrom))
     {
     struct chromInfo *ciList = createChromInfoList(NULL, db);
     slSort(ciList, chromInfoCmp);
     struct chromInfo *el = ciList;
     char fullTableName[256];
     for ( ; itemsDone < maxItemsOutput && el != NULL; el = el->next )
 	{
 	freeDyString(&query);
 	query = dyStringNew(64);
 	safef(fullTableName, sizeof(fullTableName), "%s_%s", el->chrom, hti->rootName);
 	sqlDyStringPrintf(query, "select * from %s", fullTableName);
 	itemsDone += sqlQueryJsonOutput(conn, jw, query->string, columnCount,
 	    columnNames, jsonTypes, itemsDone);
 	}
     }
@@ -412,37 +441,40 @@
 	}
     }
     else
 	wigDataOutput(jw, bwf, chrom, start, end);
 }
 
 static void bigColumnTypes(struct jsonWrite *jw, struct sqlFieldType *fiList,
     struct asObject *as)
 /* show the column types from a big file autoSql definitions */
 {
 struct asColumn *columnEl = as->columnList;
 jsonWriteListStart(jw, "columnTypes");
 struct sqlFieldType *fi = fiList;
 for ( ; fi; fi = fi->next, columnEl = columnEl->next)
     {
-    char typeString[1024];
     int jsonType = autoSqlToJsonType(fi->type);
-    if (columnEl->comment)
-	safef(typeString, sizeof(typeString), "%s - %s - %s - %s", fi->name, fi->type, jsonTypeStrings[jsonType], columnEl->comment);
+    jsonWriteObjectStart(jw, NULL);
+    jsonWriteString(jw, "name", fi->name);
+    jsonWriteString(jw, "sqlType", fi->type);
+    jsonWriteString(jw, "jsonType",jsonTypeStrings[jsonType]);
+    if (columnEl && isNotEmpty(columnEl->comment))
+	jsonWriteString(jw, "description", columnEl->comment);
     else
-	safef(typeString, sizeof(typeString), "%s - %s - %s", fi->name, fi->type, jsonTypeStrings[jsonType]);
-    jsonWriteString(jw, NULL, typeString);
+	jsonWriteString(jw, "description", "");
+    jsonWriteObjectEnd(jw);
     }
 jsonWriteListEnd(jw);
 }
 
 static void getHubTrackData(char *hubUrl)
 /* return data from a hub track, optionally just one chrom data,
  *  optionally just one section of that chrom data
  */
 {
 char *genome = cgiOptionalString("genome");
 char *track = cgiOptionalString("track");
 char *chrom = cgiOptionalString("chrom");
 char *start = cgiOptionalString("start");
 char *end = cgiOptionalString("end");
 
@@ -583,61 +615,61 @@
 char *tableName = trackDbSetting(thisTrack, "table");
 if (isNotEmpty(tableName))
     {
     freeMem(sqlTable);
     sqlTable = cloneString(tableName);
     }
 
 struct sqlConnection *conn = hAllocConnMaybe(db);
 if (NULL == conn)
     apiErrAbort(err400, err400Msg, "can not find database 'db=%s' for endpoint '/getData/track", db);
 
 struct hTableInfo *hti = hFindTableInfoWithConn(conn, NULL, sqlTable);
 
 char *splitSqlTable = NULL;
 
-if (hti->isSplit)
+if (hti && hti->isSplit)
     {
     if (isNotEmpty(chrom))
 	{
 	char fullTableName[256];
 	safef(fullTableName, sizeof(fullTableName), "%s_%s", chrom, hti->rootName);
 	splitSqlTable = cloneString(fullTableName);
 	}
     else
 	{
 	char *defaultChrom = hDefaultChrom(db);
 	char fullTableName[256];
 	safef(fullTableName, sizeof(fullTableName), "%s_%s", defaultChrom, hti->rootName);
 	splitSqlTable = cloneString(fullTableName);
 	}
     }
 
 if (! hTableOrSplitExists(db, sqlTable))
     {
     if (! bigDataUrl)
 	apiErrAbort(err400, err400Msg, "can not find specified 'track=%s' for endpoint: /getData/track?db=%s;track=%s", track, db, track);
     else
 	tableTrack = FALSE;
     }
 
 struct jsonWrite *jw = apiStartOutput();
 jsonWriteString(jw, "db", db);
 if (tableTrack)
     {
     char *dataTime = NULL;
-    if (hti->isSplit)
+    if (hti && hti->isSplit)
 	dataTime = sqlTableUpdate(conn, splitSqlTable);
     else
 	dataTime = sqlTableUpdate(conn, sqlTable);
     time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
     replaceChar(dataTime, ' ', 'T');	/*	ISO 8601	*/
     jsonWriteString(jw, "dataTime", dataTime);
     jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
     if (differentStringNullOk(sqlTable,track))
 	jsonWriteString(jw, "sqlTable", sqlTable);
     }
 jsonWriteString(jw, "trackType", thisTrack->type);
 jsonWriteString(jw, "track", track);
 if (debug)
     jsonWriteBoolean(jw, "jsonOutputArrays", jsonOutputArrays);