44b8e80672284df508a24fe193129c9673dc898d
chmalee
  Fri Apr 1 11:43:20 2022 -0700
Fix hgVai bug where position flag resets some memory initialized elsewhere, and also handle NULL mysql result better, refs Lou email

diff --git src/hg/cgilib/annoStreamDb.c src/hg/cgilib/annoStreamDb.c
index fb5ff3e..173a8bd 100644
--- src/hg/cgilib/annoStreamDb.c
+++ src/hg/cgilib/annoStreamDb.c
@@ -111,30 +111,39 @@
 self->mergeBins = FALSE;
 self->bigItemQueue = self->smallItemQueue = NULL;
 lmCleanup(&(self->qLm));
 self->gotFinestBin = FALSE;
 }
 
 static void resetRowBuf(struct rowBuf *rowBuf)
 /* Reset temporary storage for chunked query rows. */
 {
 rowBuf->buf = NULL;
 rowBuf->size = 0;
 rowBuf->ix = 0;
 lmCleanup(&(rowBuf->lm));
 }
 
+static void rowBufInit(struct rowBuf *rowBuf, int size)
+/* Clean up rowBuf and give it a new lm and buffer[size]. */
+{
+resetRowBuf(rowBuf);
+rowBuf->lm = lmInit(0);
+rowBuf->size = size;
+lmAllocArray(rowBuf->lm, rowBuf->buf, size);
+}
+
 static void resetChunkState(struct annoStreamDb *self)
 /* Reset members that track chunked queries. */
 {
 self->queryChrom = NULL;
 self->eof = FALSE;
 self->doNextChunk = FALSE;
 self->needQuery = TRUE;
 resetRowBuf(&self->rowBuf);
 }
 
 static void startMerging(struct annoStreamDb *self)
 /* Set self->mergeBins flag and create self->qLm if necessary. */
 {
 self->mergeBins = TRUE;
 self->gotFinestBin = FALSE;
@@ -164,37 +173,45 @@
 if (differentString(self->table, self->trackTable))
     {
     char newSplitTable[PATH_LEN];
     safef(newSplitTable, sizeof(newSplitTable), "%s_%s", chrom, self->trackTable);
     freeMem(self->table);
     self->table = cloneString(newSplitTable);
     }
 resetQueryState(self);
 asdUpdateBaselineQuery(self);
 }
 
 static char **nextRowFromSqlResult(struct annoStreamDb *self)
 /* Stream rows directly from self->sr, but copy into rowBuf in case we need extra columns for
  * hashJoin. */
 {
+// If we passed a position filter, we may have reset the rowBuf, so re-initialize
+if (self->rowBuf.buf == NULL)
+    rowBufInit(&self->rowBuf, 1);
 // Use only the first row in rowBuf.
 if (self->rowBuf.buf[0] == NULL)
     lmAllocArray(self->rowBuf.lm, self->rowBuf.buf[0], self->bigRowSize);
 char **row = sqlNextRow(self->sr);
+if (row)
+    {
     CopyArray(row, self->rowBuf.buf[0], self->sqlRowSize);
     return self->rowBuf.buf[0];
     }
+else
+    return NULL;
+}
 
 INLINE boolean useSplitTable(struct annoStreamDb *self, struct joinerDtf *dtf)
 /* Return TRUE if dtf matches self->{db,table} and table is split. */
 {
 return (sameString(dtf->database, self->db) &&
         sameString(dtf->table, self->trackTable) &&
         differentString(self->table, self->trackTable));
 }
 
 static void appendFieldList(struct annoStreamDb *self, struct dyString *query)
 /* Append SQL field list to query. */
 {
 struct joinerDtf *fieldList = self->joinMixer ? self->joinMixer->sqlFieldList :
                                                 self->mainTableDtfList;
 struct joinerDtf *dtf;
@@ -437,39 +454,30 @@
                     hasWhere);
     if (self->notSorted || self->hasLeftJoin)
 	sqlDyStringPrintf(query, " order by %s.%s", self->table, self->startField);
     }
 else if (self->notSorted || self->hasLeftJoin)
     sqlDyStringPrintf(query, " order by %s.%s,%s.%s",
                       self->table, self->chromField, self->table, self->startField);
 if (self->maxOutRows > 0)
     dyStringPrintf(query, " limit %d", self->maxOutRows);
 struct sqlResult *sr = sqlGetResult(self->conn, query->string);
 dyStringFree(&query);
 self->sr = sr;
 self->needQuery = FALSE;
 }
 
-static void rowBufInit(struct rowBuf *rowBuf, int size)
-/* Clean up rowBuf and give it a new lm and buffer[size]. */
-{
-resetRowBuf(rowBuf);
-rowBuf->lm = lmInit(0);
-rowBuf->size = size;
-lmAllocArray(rowBuf->lm, rowBuf->buf, size);
-}
-
 static void updateNextChunkState(struct annoStreamDb *self, int queryMaxItems)
 /* If the just-fetched interval list was limited to ASD_CHUNK_SIZE, set doNextChunk
  * and trim the last row(s) so that when we query the next chunk, we don't get
  * repeat rows due to querying a start coord that was already returned. */
 {
 struct rowBuf *rowBuf = &self->rowBuf;
 if (queryMaxItems == ASD_CHUNK_SIZE && rowBuf->size == ASD_CHUNK_SIZE)
     {
     self->doNextChunk = TRUE;
     // Starting at the last row in rowBuf, work back to find a value with a different start.
     int ix = rowBuf->size - 1;
     char **words = rowBuf->buf[ix];
     int startIx = self->startIx + self->omitBin;
     uint lastStart = atoll(words[startIx]);
     for (ix = rowBuf->size - 2;  ix >= 0;  ix--)