42cfb8cd4a22209d8183ffcad97e5ca5e9ae070b braney Fri Sep 4 12:47:47 2026 -0700 chainNetDbLoad: add chainLoadRange, every chain in a range with its blocks chainLoadIdRange loads one chain by id, and the bigChain reader takes -1 to mean every chain in the range, but there was no SQL equivalent of the latter. This is it: one query for the headers, one for the links, and each link handed to the chain it belongs to. The blocks are sorted rather than just reversed. A bin indexed range query does not return rows in position order, and anything that walks a chain's blocks expects them ascending, which is why chainLinkAddResult sorts them too. refs #38249 diff --git src/hg/lib/chainNetDbLoad.c src/hg/lib/chainNetDbLoad.c index 5dd8f80f02b..7a938bf6ac0 100644 --- src/hg/lib/chainNetDbLoad.c +++ src/hg/lib/chainNetDbLoad.c @@ -208,30 +208,86 @@ struct chainLink link; while ((row = sqlNextRow(sr)) != NULL) { chainLinkStaticLoad(row+rowOffset, &link); AllocVar(b); b->tStart = link.tStart; b->tEnd = link.tEnd; b->qStart = link.qStart; b->qEnd = link.qStart + (link.tEnd - link.tStart); slAddHead(&list, b); } slSort(&list, cBlockCmpTarget); chain->blockList = list; } +struct chain *chainLoadRange(char *database, char *track, char *chrom, int start, int end) +/* Load every chain in a range from the database, each carrying the blocks that overlap the + * range. As with chainLoadIdRange the chain header still describes the whole chain, not + * just the part in range. */ +{ +struct sqlConnection *conn = sqlConnect(database); +struct hash *chainHash = newHash(0); +struct chain *chainList = NULL, *chain; +struct sqlResult *sr; +char **row; +int rowOffset; +char id[32]; + +/* Load the chain headers in range, and index them so the links can find them. */ +sr = hRangeQuery(conn, track, chrom, start, end, NULL, &rowOffset); +while ((row = sqlNextRow(sr)) != NULL) + { + chain = chainHeadLoad(row + rowOffset); + slAddHead(&chainList, chain); + safef(id, sizeof id, "%d", chain->id); + hashAdd(chainHash, id, chain); + } +sqlFreeResult(&sr); + +/* One pass over the links in range, handing each to the chain it belongs to. */ +if (chainList != NULL) + { + char linkTable[HDB_MAX_TABLE_STRING]; + safef(linkTable, sizeof linkTable, "%sLink", track); + sr = hRangeQuery(conn, linkTable, chrom, start, end, NULL, &rowOffset); + while ((row = sqlNextRow(sr)) != NULL) + { + struct chainLink link; + chainLinkStaticLoad(row + rowOffset, &link); + if ((chain = hashFindVal(chainHash, row[rowOffset + 4])) != NULL) + { + struct cBlock *cBlock; + AllocVar(cBlock); + cBlock->tStart = link.tStart; + cBlock->tEnd = link.tEnd; + cBlock->qStart = link.qStart; + cBlock->qEnd = link.qStart + (link.tEnd - link.tStart); + slAddHead(&chain->blockList, cBlock); + } + } + sqlFreeResult(&sr); + for (chain = chainList; chain != NULL; chain = chain->next) + slSort(&chain->blockList, cBlockCmpTarget); + } + +hashFree(&chainHash); +sqlDisconnect(&conn); +slReverse(&chainList); +return chainList; +} + struct chain *chainLoadIdRangeHub(char *db, char *fileName, char *linkFileName, char *chrom, int start, int end, int id) /* Load parts of chain of given ID from bigChain file. Note the chain header * including score, tStart, tEnd, will still reflect the whole chain, * not just the part in range. However only the blocks of the chain * overlapping the range will be loaded. */ { struct lm *lm = lmInit(0); struct bbiFile *bbi = bigBedFileOpenAlias(fileName, chromAliasFindAliases); struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chrom, start, end, 0, lm); struct bbiFile *linkBbi = bigBedFileOpenAlias(linkFileName, chromAliasFindAliases); struct bigBedInterval *linkBb, *linkBbList = bigBedIntervalQuery(linkBbi, chrom, start, end, 0, lm); char *bedRow[12]; char startBuf[16], endBuf[16]; struct chain *chainList = NULL;