eb7e6ccb9b23c2dd5cd82de0237c6f25aecd2da1 max Thu Sep 17 06:02:11 2026 -0700 Hub tracks must not claim an undecorated name that a native track owns #Preview2 week - bugs introduced now will need a build patch to fix A hub track can be named on a URL, or found in the cart, without its "hub__" prefix, so that hub links stay readable. Nothing checked whether the assembly already had a track of that name, so when it did, the hub track took the variable: with the MethBase2 hub attached, setting the native hg38 Methbase track's visibility moved it to hub__Methbase and dropped it from the native track, which then did not draw. The two share the name "Methbase", and their subtracks share names too, so the same went for the _sel checkboxes. hubTrackOwnsBareName() now gates the seven places that fall back to the bare name - track and subtrack visibility, supertrack visibility, the two _hideKids variables, and the _sel checkbox. The bare name is the hub track's only where the assembly has no track of that name; otherwise it belongs to the native one, whose own pass over the track list consumes it. It answers from the track list loadFromTrackDb() has already built, so it costs a hash lookup: hub tracks are always prefixed in that list, so an undecorated name can only match a native track. A caller with no list to register (hgTrackUi) falls back to a single-row trackDb query per distinct bare name, cached. With 500 hub subtracks visible and 500 bare names on the URL - far past anything real - that is 189 queries either way, against 1189 for a query per name. Also clarifies the comments on hTrackDbWithCartVersion() and tdbForTrack(): the "this result is cached" note means the shared-memory trackDb cache only, which is off unless cacheTrackDbDir is set, and there is no memoizing besides it - so hTrackDbForTrack() per name reloads the whole trackDb each call. Measured at 57 seconds for 500 names before I abandoned that approach. refs #34246 diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c index cc51014f454..b1a0dd632a5 100644 --- src/hg/hgTracks/hgTracks.c +++ src/hg/hgTracks/hgTracks.c @@ -6331,31 +6331,38 @@ warn("No draw handler for %s", tdb->track); else slAddHead(pTrackList, track); } } int loadFromTrackDb(struct track **pTrackList) /* Load tracks from database, consulting handler list. */ /* returns cartVersion desired. */ { char *trackNameFilter = cartOptionalString(cart, "hgt.trackNameFilter"); struct trackDb *tdbList; int trackDbCartVersion = 0; if(trackNameFilter == NULL) + { tdbList = hTrackDbWithCartVersion(database, &trackDbCartVersion); + // getTrackList() asks whether an undecorated cart/CGI variable belongs to a native + // track or to an attached hub's track of the same name. Answer it from the assembly's + // track list, which we have right here, instead of a trackDb query per name. Only + // the unfiltered list can answer it, so the filtered branch leaves it to the query. + hubTrackBareNamesFromTdbList(tdbList); + } else { tdbList = hTrackDbForTrack(database, trackNameFilter); if (tdbList && tdbList->parent) // we want to give the composite parent a chance to load and set options { while(tdbList->parent) { if (tdbList->parent->subtracks == NULL) // we don't want to go up to a supertrack break; tdbList = tdbList->parent; } trackNameFilter = tdbList->track; } } @@ -7700,46 +7707,59 @@ // QuickLifted tracks are exempt from hideTracks -- their visibility is // carried over from the source assembly's cart (and may still be under the // undecorated name until migrated below), so consult the cart, not the URL. boolean superFromCart = !hideTracks || (trackDbSetting(track->tdb, "quickLiftUrl") != NULL); // first deal with visibility of super track char *s = superFromCart ? cartOptionalString(cart, track->tdb->parent->track) : cgiOptionalString(track->tdb->parent->track); if (s) { track->tdb->parent->visibility = hTvFromString(s) ; cartSetString(cart, track->tdb->parent->track, s); } else if (startsWith("hub_", track->tdb->parent->track)) { s = superFromCart ? cartOptionalString( cart, trackHubSkipHubName(track->tdb->parent->track)) : cgiOptionalString( trackHubSkipHubName(track->tdb->parent->track)); + // the bare name is the native track's if the assembly has one by that name + if (s != NULL && !hubTrackOwnsBareName(database, track->tdb->parent->track)) + s = NULL; if (s) { cartSetString(cart, track->tdb->parent->track, s); cartRemove(cart, trackHubSkipHubName(track->tdb->parent->track)); // remove the undecorated version track->tdb->parent->visibility = hTvFromString(s) ; } } // now look to see if we have a _hideKids statement to turn off all subtracks (including the current one) unsigned hideKids = 0; char *usedThis = buffer; safef(buffer, sizeof buffer, "%s_hideKids", track->tdb->parent->track); s = cartOptionalString(cart, buffer); if (s == NULL && startsWith("hub_", track->tdb->parent->track)) - s = cartOptionalString(cart, usedThis = trackHubSkipHubName(buffer)); + { + char *bare = trackHubSkipHubName(buffer); + char *bareVal = cartOptionalString(cart, bare); + // the bare name is the native track's if the assembly has one by that name + if (bareVal != NULL + && hubTrackOwnsBareName(database, track->tdb->parent->track)) + { + s = bareVal; + usedThis = bare; + } + } if (s != NULL) { hideKids = 1; cartRemove(cart, usedThis); // we don't want this hanging out in the cart } // mark this as having been addressed hel = hashAddInt(superTrackHash, track->tdb->parent->track, hideKids ); } if ( ptToInt(hel->val) == 1) // we want to hide this track { if (tvHide == track->tdb->visibility) /* remove if setting to default vis */ @@ -7752,69 +7772,89 @@ // we use cgiOptionString because the above code may have turned off the track in the cart if // the user requested that all the default tracks be turned off char *s = hideTracks ? cgiOptionalString(track->track) : cartOptionalString(cart, track->track); if (s != NULL) { if (!track->limitedVisSet) { track->visibility = hTvFromString(s); cartSetString(cart, track->track, s); } } else { - // maybe this track is on the URL without the hub_ prefix + // maybe this track is on the URL without the hub_ prefix - but not if the + // assembly has a track of that name, in which case the bare name is that one's if (startsWith("hub_", track->track)) + { s = cgiOptionalString(trackHubSkipHubName(track->track)); + if (s && !hubTrackOwnsBareName(database, track->track)) + s = NULL; + } if (s != NULL && !track->limitedVisSet) { track->visibility = hTvFromString(s); cartSetString(cart, track->track, s); // add the decorated visibility to the cart cartRemove(cart, trackHubSkipHubName(track->track)); // remove the undecorated version } } // now deal with composite track children if (tdbIsComposite(track->tdb) || tdbIsMultiTrack(track->tdb)) { char *usedThis = buffer; // first check to see if we've been asked to hide all the subtracks boolean hideKids = FALSE; safef(buffer, sizeof buffer, "%s_hideKids", track->track); s = cartOptionalString(cart, buffer); if (s == NULL && startsWith("hub_", track->track)) - s = cartOptionalString(cart, usedThis = trackHubSkipHubName(buffer)); + { + char *bare = trackHubSkipHubName(buffer); + char *bareVal = cartOptionalString(cart, bare); + // the bare name is the native track's if the assembly has one by that name + if (bareVal != NULL && hubTrackOwnsBareName(database, track->track)) + { + s = bareVal; + usedThis = bare; + } + } if (s != NULL) hideKids = TRUE; cartRemove(cart, usedThis); // we don't want these _hideKids variables in the cart boolean facetedParent = tdbIsFacetedComposite(track->tdb); // now see if we have any specified visibilities struct track *subtrack; for (subtrack = track->subtracks; subtrack != NULL; subtrack = subtrack->next) { boolean undecoratedVis = FALSE; char *s = hideTracks ? cgiOptionalString( subtrack->track) : cartOptionalString(cart, subtrack->track); if (s == NULL && startsWith("hub_", subtrack->track)) { - undecoratedVis = TRUE; + // the bare name is the native track's if the assembly has one by that + // name. Look the value up before asking: this runs for every subtrack of + // the container, and almost no request has a bare name on it at all. s = hideTracks ? cgiOptionalString(trackHubSkipHubName(subtrack->track)) : cartOptionalString(cart, trackHubSkipHubName(subtrack->track)); + if (s != NULL && hubTrackOwnsBareName(database, subtrack->track)) + undecoratedVis = TRUE; + else + s = NULL; } safef(buffer, sizeof buffer, "%s_sel", subtrack->track); if (s != NULL) { subtrack->visibility = hTvFromString(s); if (facetedParent && sameString("hide", s)) { // A faceted composite's child holds a standing display mode rather // than inheriting one, and a standing mode of "hide" would survive // being re-selected in the facet table and look like a bug. So take // the child out of the selection instead of storing that. cartRemove(cart, subtrack->track); cartSetString(cart, buffer, "0"); }