e47b56dbd11cb3936098df9694704fc8ea086a67
angie
  Wed Dec 21 15:11:55 2016 -0800
hgTables was missing support for "comma" or "separator" field in non-primary field lines of all.joiner.  refs #18586

diff --git src/hg/hgTables/joining.c src/hg/hgTables/joining.c
index 75a25e2..0b2e94b 100644
--- src/hg/hgTables/joining.c
+++ src/hg/hgTables/joining.c
@@ -552,31 +552,31 @@
 }
 
 void dyStringAddList(struct dyString *dy, char *s)
 /* Add s to end of string.  If string is non-empty
  * first add comma.  This make it relatively easy to
  * build up comma-separates lists. */
 {
 if (dy->stringSize > 0)
     dyStringAppendC(dy, ',');
 dyStringAppend(dy, s);
 }
 
 void tjLoadSome(struct region *regionList,
     struct joinedTables *joined, int fieldOffset, int keyOffset,
     char *idField, struct hash *idHash, 
-    struct slName *chopBefore, struct slName *chopAfter,
+    struct joinerField *jf,
     struct tableJoiner *tj, boolean isPositional, boolean isFirst)
 /* Load up rows. */
 {
 struct region *region;
 struct dyString *sqlFields = dyStringNew(0);
 struct joinerDtf *dtf;
 struct slRef *ref;
 struct joinerPair *jp;
 int fieldCount = 0, keyCount = 0;
 int idFieldIx = -1;
 struct sqlConnection *conn = hAllocConn(tj->database);
 char *identifierFilter = NULL;
 boolean needUpdateFilter = FALSE;
 struct joinedRow *jr;
 
@@ -646,43 +646,59 @@
 	        break;
 	    }
 	else
 	    {
 	    char *id = row[idFieldIx];
 	    if (isFirst)
 		{
 		if (hashLookup(idHash, id))
 		    {
 		    if (jrRowAdd(joined, row, fieldCount, keyCount) == NULL)
 		        break;
 		    }
 		}
 	    else
 		{
+                // chop id column by jf->separator if necessary
+                int sepCount = 0;
+                if (jf && isNotEmpty(jf->separator))
+                    sepCount = countChars(id, jf->separator[0]);
+                int wordCount = sepCount + 1;
+                char *idWords[wordCount];
+                if (wordCount > 1)
+                    chopString(id, jf->separator, idWords, ArraySize(idWords));
+                else
+                    idWords[0] = id;
+                int i;
+                for (i = 0;  i < wordCount;  i++)
+                    {
+                    struct slName *chopBefore = jf ? jf->chopBefore : NULL;
+                    struct slName *chopAfter = jf ? jf->chopAfter : NULL;
+                    idWords[i] = chopKey(chopBefore, chopAfter, trimSpaces(idWords[i]));
                     struct hashEl *bucket;
-		id = chopKey(chopBefore, chopAfter, id);
-		for (bucket = hashLookup(idHash, id); bucket != NULL;
+                    for (bucket = hashLookup(idHash, idWords[i]); bucket != NULL;
                          bucket = hashLookupNext(bucket))
                         {
                         jr = bucket->val;
                         jr->hitThisTable = TRUE;
                         jrRowExpand(joined, jr, row, 
                                     fieldOffset, fieldCount, keyOffset, keyCount);
                         }
                     }
 		}
 	    }
+	}
     sqlFreeResult(&sr);
     freez(&filter);
     if (!isPositional)
         break;
     }
 if (isFirst)
     slReverse(&joined->rowList);
 if (needUpdateFilter)
     {
     for (jr = joined->rowList; jr != NULL; jr = jr->next)
 	{
 	jr->passedFilter &= jr->hitThisTable;
 	}
     }
 tj->loaded = TRUE;
@@ -691,31 +707,31 @@
 	
 struct joinedTables *tjLoadFirst(struct region *regionList,
 	struct tableJoiner *tj, int totalFieldCount,
 	int totalKeyCount, int maxRowCount)
 /* Load up first table in series of joined tables.  This allocates
  * field and key arrays big enough for all. */
 {
 struct joinedTables *joined = joinedTablesNew(totalFieldCount, 
 	totalKeyCount, maxRowCount);
 struct hash *idHash = NULL;
 struct hTableInfo *hti = getHtiOnDb(tj->database, tj->table);
 char *idField = getIdField(tj->database, curTrack, tj->table, hti);
 if (idField != NULL)
     idHash = identifierHash(tj->database, tj->table);
 tjLoadSome(regionList, joined, 0, 0, 
-	idField, idHash, NULL, NULL, tj, 
+	idField, idHash, NULL, tj,
 	isPositional(tj->database, tj->table), TRUE);
 hashFree(&idHash);
 return joined;
 }
 
 int findDtfIndex(struct joinerDtf *list, struct joinerDtf *dtf)
 /* Find dtf on list. */
 {
 struct joinerDtf *el;
 int ix;
 for (el = list, ix=0; el != NULL; el = el->next, ++ix)
     {
     if (sameString(el->database, dtf->database)
      && sameString(el->table, dtf->table)
      && sameString(el->field, dtf->field))
@@ -870,31 +886,31 @@
 	internalErr();
 	}
     jfB = findJoinerField(route->identifier, route->b);
     if (jfB == NULL)
 	internalErr();
     if (!tj->loaded)
 	{
 	int keyIx;
 	struct hash *keyHash = NULL;
 	keyIx = findDtfIndex(joined->keyList, route->a);
 	if (keyIx < 0)
 	    internalErr();
 	keyHash = hashKeyField(joined, keyIx, jfA);
 	tjLoadSome(regionList, joined, curFieldCount, curKeyCount,
 	    route->b->field, keyHash, 
-	    jfB->chopBefore, jfB->chopAfter, 
+	    jfB,
 	    tj, isPositional(tj->database, tj->table),  FALSE);
 	curKeyCount += slCount(tj->keysOut);
 	curFieldCount += slCount(tj->fieldList);
 	hashFree(&keyHash);
 	}
     }
 joinerDtfFreeList(&tableDtfs);
 hashFree(&tableHash);
 tableJoinerFreeList(&tjList);
 return joined;
 }
 
 static boolean allSameTable(struct joinerDtf *fieldList, struct joinerDtf *filterTables)
 /* Return TRUE if all db/tables in fieldList and filterTables are same. */
 {