544f7054d812254a0883a412b98a08dcd78f1b84 braney Fri Sep 4 11:39:41 2026 -0700 hgTracks: draw quickLifted psl and bigPsl tracks pslTrack.c had no quickLift path at all. It now reads the alignments out of the assembly the track came from through quickLiftSql and maps them onto the reference. The chromFilter clause and the sort-and-filter tail moved into helpers so the two loaders cannot drift apart. bigBedTrack.c built the alignment out of the source interval but labelled it with the reference sequence name and never lifted it, so a quickLifted bigPsl track drew its items in the wrong place. It now takes the real source name out of the bigBed, lifts, counts a failure the way the bed path does, and rewrites the coordinate fields so a $chromStart in a mouseOver reports where the item is drawn. cds.c asked the assembly on screen for the CDS and for the sequence the alignment is to. Both belong to the assembly the alignment came from. This was already the wrong answer for a quickLifted track on any target, and on a hub-backed target it fails outright, because the name of such an assembly is not a database. The table-exists answer is now remembered per assembly rather than once for the process, since one page can hold both native and lifted alignment tracks. The mRNA filter reads the same source assembly, for the same reason: the accessions and the gbCdnaInfo ids are the source assembly's. refs #38249 diff --git src/hg/hgTracks/cds.c src/hg/hgTracks/cds.c index a307ebf0e8c..484711d074a 100644 --- src/hg/hgTracks/cds.c +++ src/hg/hgTracks/cds.c @@ -778,60 +778,76 @@ { char rnaCodon = baseColorLookupCodon(rna); if (genomicCodon != rnaCodon) { if (genomicCodon != 'X' && rnaCodon != 'X' && protEquivalent(genomicCodon, rnaCodon)) return(GRAYIX_CDS_SYN_PROT); // yellow, "synonymous" protein else return(GRAYIX_CDS_STOP); } else return peptideToGrayIx(genomicCodon, codonFirstColor); } -static void getGenbankCds(char *acc, struct genbankCds* cds) +static char *cdsDb(struct track *tg) +/* The assembly whose CDS and sequence tables go with this track's items. A quickLifted + * track's items came from another assembly, and that is where their CDS is described. + * The destination is not merely the wrong answer: when it is a hub-backed assembly its + * name is not a database at all, and asking for a connection to it fails. */ +{ +char *liftDb = (tg->tdb != NULL) ? trackDbSetting(tg->tdb, "quickLiftDb") : NULL; + +return (liftDb != NULL) ? liftDb : database; +} + +static void getGenbankCds(char *db, char *acc, struct genbankCds* cds) /* Get cds start and stop from genbank tables, if available. Otherwise it * does nothing */ { -static boolean first = TRUE, haveGbCdnaInfo = FALSE; -struct sqlConnection *conn = hAllocConn(database); -if (first) +// Remember whether the table is there per assembly, not once for the process: one page +// can hold both native and quickLifted alignment tracks, which read different assemblies. +static struct hash *haveGbCdnaInfoHash = NULL; +struct sqlConnection *conn = hAllocConn(db); +if (haveGbCdnaInfoHash == NULL) + haveGbCdnaInfoHash = hashNew(0); +int haveGbCdnaInfo = hashIntValDefault(haveGbCdnaInfoHash, db, -1); +if (haveGbCdnaInfo < 0) { haveGbCdnaInfo = sqlTableExists(conn, gbCdnaInfoTable); - first = FALSE; + hashAddInt(haveGbCdnaInfoHash, db, haveGbCdnaInfo); } if (haveGbCdnaInfo) { char query[4096], buf[4096], *cdsStr; sqlSafef(query, sizeof query, "select c.name from %s g,%s c where (acc = '%s') and (g.cds = c.id)", gbCdnaInfoTable, cdsTable, acc); cdsStr = sqlQuickQuery(conn, query, buf, sizeof(buf)); if (cdsStr != NULL) genbankCdsParse(cdsStr, cds); } hFreeConn(&conn); } -static void getCdsFromTbl(char *acc, char *baseColorSetting, struct genbankCds* cds) +static void getCdsFromTbl(char *db, char *acc, char *baseColorSetting, struct genbankCds* cds) /* Get CDS from a specified table, doing nothing if not found */ { char *p = skipToSpaces(baseColorSetting); char *cdsSpecTbl = skipLeadingSpaces(p); if (*cdsSpecTbl == '\0') errAbort("%s table requires a table name as an argument", BASE_COLOR_USE_CDS); -struct sqlConnection *conn = hAllocConnDbTbl(cdsSpecTbl, &cdsSpecTbl, database); +struct sqlConnection *conn = hAllocConnDbTbl(cdsSpecTbl, &cdsSpecTbl, db); // allow multiple, but only use the first, since transMapGene table might have // multiple entries for same gene from different source dbs. struct cdsSpec *cdsSpec = sqlQueryObjs(conn, (sqlLoadFunc)cdsSpecLoad, sqlQueryMulti, "SELECT * FROM %s WHERE id=\"%s\"", cdsSpecTbl, acc); hFreeConn(&conn); if (cdsSpec != NULL) genbankCdsParse(cdsSpec->cds, cds); if (cds != NULL) cdsSpecFreeList(&cdsSpec); } static void getPslCds(struct psl *psl, struct track *tg, struct linkedFeatures *lf, struct genbankCds *cds) /* get CDS defintion for a PSL */ @@ -843,33 +859,33 @@ cds->start=0; cds->end=psl->qSize*3; cds->startComplete = TRUE; cds->endComplete = TRUE; } else if (startsWith("bigPsl", tg->tdb->type)) { if (lf->cds) genbankCdsParse(lf->cds, cds); } else { char *setting = trackDbSetting(tg->tdb, BASE_COLOR_USE_CDS); char *dataName = getItemDataName(tg, psl->qName); if ((setting != NULL) && startsWith("table", setting)) - getCdsFromTbl(dataName, setting, cds); + getCdsFromTbl(cdsDb(tg), dataName, setting, cds); else - getGenbankCds(dataName, cds); + getGenbankCds(cdsDb(tg), dataName, cds); } } static void getHiddenGaps(struct psl *psl, unsigned *gaps) /*return the insertions in the query sequence of a psl that are 'hidden' in the browser. This lets these insertions be indicated with the color orange.*/ { int i; gaps[0] = psl->qStarts[0] - psl->qStart; for (i=1; i<psl->blockCount; i++) { gaps[i] = psl->qStarts[i] - (psl->qStarts[i-1] + psl->blockSizes[i-1]); } @@ -1108,88 +1124,91 @@ if (tpsl->strand[0] == '-') reverseComplement(seq->dna, seq->size); } else { seq = hChromSeq(database, chromName, lf->start, lf->end); if (lf->orientation < 0) reverseComplement(seq->dna, seq->size); CopyArray(fPrimer, seq->dna, fPrimerSize); CopyArray(rPrimer, (seq->dna + seq->size - rPrimerSize), rPrimerSize); } return seq; } #endif /* GBROWSE */ -static struct dnaSeq *maybeGetExtFileSeq(char *seqSource, char *name) +static struct dnaSeq *maybeGetExtFileSeq(char *db, char *seqSource, char *name) /* look up sequence name in seq and extFile tables specified in seqSource */ { /* seqSource is: extFile seqTbl extFileTbl */ static struct dyString *buf = NULL; if (buf == NULL) buf = dyStringNew(0); dyStringClear(buf); dyStringAppend(buf, seqSource); char *words[3]; int nwords = chopByWhite(buf->string, words, ArraySize(words)); if ((nwords != ArraySize(words)) || !sameString(words[0], "extFile")) errAbort("invalid %s track setting: %s", BASE_COLOR_USE_SEQUENCE, seqSource); -return hDnaSeqGet(database, name, words[1], words[2]); +return hDnaSeqGet(db, name, words[1], words[2]); } struct cacheTwoBitRanges *cdsQueryCache = NULL; static struct dnaSeq *fetchCachedTwoBitSeq(char *url, char *seqName, int seqStart, int seqEnd, boolean doRc, int *retSeqOffset) /* fetch a sequence from a 2bit. Caches open two bit files and sequence in * both forward and reverse strand */ { /* Init static url cache */ if (cdsQueryCache == NULL) cdsQueryCache = cacheTwoBitRangesNew(TRUE); return cacheTwoBitRangesMayFetch(cdsQueryCache, url, seqName, seqStart, seqEnd, doRc, retSeqOffset); } static struct dnaSeq *maybeGetSeqUpper(struct linkedFeatures *lf, char *mrnaName, int mrnaStart, int mrnaEnd, char *tableName, struct track *tg, boolean doRc, int *retMrnaOffset) /* Look up the sequence in genbank tables (hGenBankGetMrna also searches * seq if it can't find it in GB tables) or user's blat sequence, * uppercase and return it if we find it, return NULL if we don't find it. */ { boolean doUpper = TRUE; struct dnaSeq *mrnaSeq = NULL; char *name = getItemDataName(tg, mrnaName); +// The sequence the alignment is to belongs with the alignment, so on a quickLifted track +// it comes from the assembly the alignment came from, not the one on screen. +char *seqDb = cdsDb(tg); if (sameString(tableName,"refGene") || sameString(tableName,"refSeqAli")) - mrnaSeq = hGenBankGetMrna(database, name, "refMrna"); + mrnaSeq = hGenBankGetMrna(seqDb, name, "refMrna"); else { char *seqSource = trackDbSetting(tg->tdb, BASE_COLOR_USE_SEQUENCE); if (seqSource != NULL) { if (sameString(seqSource, "ss")) mrnaSeq = maybeGetUserSeq(name); #ifndef GBROWSE else if (sameString(seqSource, PCR_RESULT_TRACK_NAME)) mrnaSeq = maybeGetPcrResultSeq(lf); #endif /* GBROWSE */ else if (startsWith("extFile", seqSource)) - mrnaSeq = maybeGetExtFileSeq(seqSource, name); + mrnaSeq = maybeGetExtFileSeq(seqDb, seqSource, name); else if (endsWith("ExtFile", seqSource)) - mrnaSeq = maybeGetExtFileSeq(seqSource, name); + mrnaSeq = maybeGetExtFileSeq(seqDb, seqSource, name); else if (sameString("nameIsSequence", seqSource)) { mrnaSeq = newDnaSeq(cloneString(name), strlen(name), name); if (lf->orientation == -1) reverseComplement(mrnaSeq->dna, mrnaSeq->size); } else if (sameString("seq1Seq2", seqSource)) { mrnaSeq = lf->extra; if (lf->orientation == -1) reverseComplement(mrnaSeq->dna, mrnaSeq->size); } else if (sameString("lfExtra", seqSource)) { if (lf->extra == NULL) @@ -1204,42 +1223,42 @@ mrnaSeq = lrgReconstructSequence(lrg, database); } else if (sameString("2bit", seqSource)) { char *url = trackDbSetting(tg->tdb, "otherTwoBitUrl"); if (url == NULL) errAbort("missing otherTwoBitUrl in baseColorUseSequence 2bit trackDb setting"); mrnaSeq = fetchCachedTwoBitSeq(url, name, mrnaStart, mrnaEnd, doRc, retMrnaOffset); doRc = FALSE; // Handled it already doUpper = FALSE; // Handled it already } else if (startsWith("table ", seqSource)) { char *table = seqSource; nextWord(&table); - mrnaSeq = hGenBankGetMrna(database, name, table); + mrnaSeq = hGenBankGetMrna(seqDb, name, table); } else if (startsWithWord("db", seqSource)) { char *sourceDb = seqSource; nextWord(&sourceDb); if (isEmpty(sourceDb)) - sourceDb = database; + sourceDb = seqDb; mrnaSeq = hChromSeq(sourceDb, name, 0, 0); } else - mrnaSeq = hGenBankGetMrna(database, name, NULL); + mrnaSeq = hGenBankGetMrna(seqDb, name, NULL); } } if (mrnaSeq != NULL && doUpper) touppers(mrnaSeq->dna); if (mrnaSeq != NULL && doRc) reverseComplement(mrnaSeq->dna, mrnaSeq->size); return mrnaSeq; } static void makeCdsShades(struct hvGfx *hvg, Color *cdsColor) /* setup CDS colors */ { cdsColor[CDS_ERROR] = hvGfxFindColorIx(hvg,0,0,0);