ba33f2c600399d5b4b97dd5fd987ed2db3c891ba chmalee Tue May 19 13:40:42 2026 -0700 myVariants related fixes from code review. Standardize how the code determines we are about to do something myVariants related by #defining some strings and using sameOk to check for them, remove a dead code block, and have hgc use autoSql to load the myVariants item rather than check the raw sql result, refs #37528 diff --git src/hg/lib/myVariants.c src/hg/lib/myVariants.c index 1ea8eb03d54..02ddd3bf6cf 100644 --- src/hg/lib/myVariants.c +++ src/hg/lib/myVariants.c @@ -1,29 +1,47 @@ #include "common.h" #include "linefile.h" #include "dystring.h" #include "jksql.h" #include "myVariants.h" #include "myVariantsShare.h" #include "customTrack.h" #include "hdb.h" #include "hgConfig.h" #include "cheapcgi.h" #include "trashDir.h" #include "obscure.h" #include "wikiLink.h" +boolean isMyVariantsType(char *type) +/* TRUE if type names the myVariants custom-track type. NULL-safe. */ +{ +return sameOk(type, MYVARIANTS_TYPE); +} + +boolean isMyVariantsTrack(char *trackName) +/* TRUE if trackName is a myVariants custom track (own or shared). NULL-safe. */ +{ +return trackName != NULL && startsWith(MYVARIANTS_TRACK_PREFIX, trackName); +} + +boolean isMyVariantsSharedTrack(char *trackName) +/* TRUE if trackName is a myVariants shared custom track. NULL-safe. */ +{ +return trackName != NULL && startsWith(MYVARIANTS_SHARED_TRACK_PREFIX, trackName); +} + void myVariantsStaticLoad(char **row, struct myVariants *ret) /* Load a row from myVariants table into ret. The contents of ret will be replaced at the next call to this function. */ { int sizeOne; ret->bin = sqlUnsigned(row[0]); ret->chrom = row[1]; ret->chromStart = sqlUnsigned(row[2]); ret->chromEnd = sqlUnsigned(row[3]); ret->name = row[4]; ret->score = sqlUnsigned(row[5]); safecpy(ret->strand, sizeof(ret->strand), row[6]); ret->thickStart = sqlUnsigned(row[7]); ret->thickEnd = sqlUnsigned(row[8]); ret->itemRgb = sqlUnsigned(row[9]); ret->blockCount = sqlUnsigned(row[10]); @@ -403,31 +421,31 @@ dyStringAppendC(dy, c); else dyStringPrintf(dy, "_%02x", c); } return dyStringCannibalize(&dy); } char *myVariantsGetTableName(char *userName) /* Build the SQL table name for this user's myVariants. Encodes the user * name so non-identifier characters (e.g. '@' in email-style logins) don't * fail sqlCheckIdentifier. */ { if (!userName) return NULL; char *encoded = encodeUserNameForIdentifier(userName); -struct dyString *tableName = dyStringCreate("myVariants_%s", encoded); +struct dyString *tableName = dyStringCreate(MYVARIANTS_TRACK_PREFIX "%s", encoded); freeMem(encoded); return dyStringCannibalize(&tableName); } char *myVariantsGetDbTable(char *userName) /* Return the string db.tableName based on the userName for use in sql statements * without specifying the database */ { char *db = myVariantsGetDatabaseForUser(userName); char *tbl = myVariantsGetTableName(userName); if (isNotEmpty(db) && isNotEmpty(tbl)) { struct dyString *ret = dyStringCreate("%s.%s", db, tbl); return dyStringCannibalize(&ret); } @@ -448,33 +466,33 @@ dbTable, targetDb); sqlUpdate(conn, del->string); dyStringFree(&del); hFreeConn(&conn); } struct myVariantsShare *myVariantsResolveSharedTrack(char *trackName, struct cart *cart) /* For a "myVariants_shared_*" custom-track name, look up and revalidate the * share record from hgcentral. Returns NULL if the track is not a shared * track, the cart cookie is missing, the share has been revoked, or the * current user is not authorized (target user mismatch). The returned share * carries the validated owner/db/project/permission; callers should use these * (not the cart-supplied values) for authorization or scoping decisions. * Caller frees with myVariantsShareFree. */ { -if (isEmpty(trackName) || !startsWith("myVariants_shared_", trackName)) +if (isEmpty(trackName) || !isMyVariantsSharedTrack(trackName)) return NULL; -char *token = trackName + strlen("myVariants_shared_"); +char *token = trackName + strlen(MYVARIANTS_SHARED_TRACK_PREFIX); char cartVar[256]; safef(cartVar, sizeof(cartVar), MYVAR_SHARED_CART_PREFIX "%s", token); /* The cart-cookie presence gate is belt-and-suspenders: it ensures the share * was once accepted into this session before we hit hgcentral. The real * authorization is the targetUser check below against the live share row. */ if (cart == NULL || !cartVarExists(cart, cartVar)) return NULL; struct sqlConnection *conn = hConnectCentral(); if (!sqlTableExists(conn, "myVariantsShares")) { hDisconnectCentral(&conn); return NULL; } struct myVariantsShare *share = myVariantsGetShareByToken(conn, token); hDisconnectCentral(&conn); @@ -489,70 +507,70 @@ return NULL; } } return share; } char *myVariantsResolveDbTableForCustomTrack(char *trackName, struct cart *cart) /* For a custom-track name of the form "myVariants_*", return the fully * qualified SQL table (db.tableName) holding the items. Handles both own * tracks and shared tracks. For shared tracks, revalidates the share against * hgcentral; returns NULL if the share has been revoked, downgraded out of * scope, or is not for the current user. */ { if (isEmpty(trackName)) return NULL; -if (startsWith("myVariants_shared_", trackName)) +if (isMyVariantsSharedTrack(trackName)) { struct myVariantsShare *share = myVariantsResolveSharedTrack(trackName, cart); if (share == NULL) return NULL; char *dbTable = myVariantsGetDbTable(share->ownerUser); myVariantsShareFree(&share); return dbTable; } -if (startsWith("myVariants_", trackName)) +if (isMyVariantsTrack(trackName)) { /* trackName is the SQL-identifier-encoded form "myVariants_". * Resolve via the current logged-in user (an own track is only viewable * by its owner) and verify the trackName matches the encoded form for * that user before returning their db.tableName. */ char *userName = getUserName(); if (isEmpty(userName)) return NULL; char *expected = myVariantsGetTableName(userName); boolean match = sameOk(expected, trackName); freeMem(expected); if (!match) return NULL; return myVariantsGetDbTable(userName); } return NULL; } char *myVariantsSharedScopeWhere(char *trackName, struct cart *cart) /* For a "myVariants_shared_*" custom-track, return a SQL WHERE-clause * fragment that limits a query to the share's authorized project and db * (e.g. "db='hg38' and project='Variants'", or "db='hg38'" alone when the * share's project is "*"). Returns NULL for non-shared tracks or revoked * shares. Memoized per-process: callers receive a fresh cloneString that * they own. */ { static struct hash *cache = NULL; if (cache == NULL) cache = hashNew(0); -if (isEmpty(trackName) || !startsWith("myVariants_shared_", trackName)) +if (isEmpty(trackName) || !isMyVariantsSharedTrack(trackName)) return NULL; char *cached = hashFindVal(cache, trackName); if (cached != NULL) return cached[0] ? cloneString(cached) : NULL; char *result = NULL; struct myVariantsShare *share = myVariantsResolveSharedTrack(trackName, cart); if (share != NULL && isNotEmpty(share->db)) { struct dyString *dy = sqlDyStringCreate("db='%s'", share->db); if (isNotEmpty(share->project) && !sameString(share->project, "*")) sqlDyStringPrintf(dy, " and project='%s'", share->project); result = dyStringCannibalize(&dy); } myVariantsShareFree(&share); @@ -724,31 +742,31 @@ * ${persistentDir}//.bed. */ char *subdir = isNotEmpty(encodedTableName) ? encodedTableName : "shared"; char *sep = endsWith(persistentDir, "/") ? "" : "/"; char path[PATH_LEN]; safef(path, sizeof path, "%s%s%s/%s.bed", persistentDir, sep, subdir, targetDb); char dirPart[PATH_LEN]; splitPath(path, dirPart, NULL, NULL); makeDirsOnPath(dirPart); safef(tn->forCgi, sizeof tn->forCgi, "%s", path); safef(tn->forHtml, sizeof tn->forHtml, "%s", path); } else { char base[PATH_LEN]; char *hostPort = cgiServerNamePort(); - safef(base, sizeof base, "myVariants_%s_%s_%s", + safef(base, sizeof base, MYVARIANTS_TRACK_PREFIX "%s_%s_%s", hostPort ? hostPort : "localhost", targetDb, isNotEmpty(encodedTableName) ? encodedTableName : "shared"); for (char *p = base; *p; p++) if (*p == '/') *p = '_'; trashDirReusableFile(tn, "ct", base, ".bed"); } } void myVariantsUnlinkCtFile(char *userName, char *targetDb) /* Delete the on-disk ctfile for this user+assembly if it exists. Uses the * same path resolution as myVariantsWriteCtFile so it targets either the * persistent dir (myVariantsDataDir) or the trash fallback. */ { if (isEmpty(userName) || isEmpty(targetDb)) return; char *encodedTableName = myVariantsGetTableName(userName); @@ -793,31 +811,31 @@ hFreeConn(&conn); } /* Collect shared track lines from cart. All authoritative metadata * (owner/db/project/label) comes from the live share row, never from * cart-parsed values. */ struct dyString *sharedLines = dyStringNew(0); if (cart != NULL) { struct hashEl *shareVars = cartFindPrefix(cart, MYVAR_SHARED_CART_PREFIX); struct hashEl *el; for (el = shareVars; el != NULL; el = el->next) { char *token = el->name + strlen(MYVAR_SHARED_CART_PREFIX); char trackName[512]; - safef(trackName, sizeof(trackName), "myVariants_shared_%s", token); + safef(trackName, sizeof(trackName), MYVARIANTS_SHARED_TRACK_PREFIX "%s", token); struct myVariantsShare *share = myVariantsResolveSharedTrack(trackName, cart); if (share == NULL) continue; if (!sameString(share->db, targetDb)) { myVariantsShareFree(&share); continue; } /* Skip if the sharer is the current user - they already see their own track */ if (isNotEmpty(userName) && sameString(share->ownerUser, userName)) { myVariantsShareFree(&share); continue; } /* Verify the owner's table exists */ @@ -834,31 +852,32 @@ * of additional trackDb settings via the CT file track line. */ char *owner = cloneString(share->ownerUser); char *project = cloneString(share->project); char *label = isNotEmpty(share->label) ? cloneString(share->label) : NULL; stripChar(owner, '"'); stripChar(project, '"'); if (label != NULL) stripChar(label, '"'); char *projectLabel = sameString(project, "*") ? "All" : project; char shortLabel[64]; if (isNotEmpty(label)) safef(shortLabel, sizeof(shortLabel), "%s", label); else safef(shortLabel, sizeof(shortLabel), "%s's %s", owner, projectLabel); dyStringPrintf(sharedLines, - "track name=\"myVariants_shared_%s\" type=\"myVariants\" itemRgb=\"on\"" + "track name=\"" MYVARIANTS_SHARED_TRACK_PREFIX "%s\"" + " type=\"" MYVARIANTS_TYPE "\" itemRgb=\"on\"" " visibility=\"pack\"" " shortLabel=\"%s\"" " longLabel=\"Shared annotations: %s (from %s)\"\n", token, shortLabel, projectLabel, owner); freeMem(owner); freeMem(project); freeMem(label); myVariantsShareFree(&share); } hashElFreeList(&shareVars); } if (!hasOwnItems && dyStringLen(sharedLines) == 0) { /* Nothing to write: remove any stale on-disk file so it doesn't leak @@ -903,71 +922,71 @@ if (shortLabel == NULL) shortLabel = sanitizeLabel(diskShort, 80); if (longLabel == NULL) longLabel = sanitizeLabel(diskLong, 200); freeMem(diskShort); freeMem(diskLong); } if (shortLabel == NULL) shortLabel = cloneString("My Annotations"); if (longLabel == NULL) longLabel = cloneString("My Annotations"); } FILE *f = mustOpen(tn.forCgi, "w"); if (hasOwnItems) - fprintf(f, "track name=\"%s\" type=\"myVariants\" itemRgb=\"on\"" + fprintf(f, "track name=\"%s\" type=\"" MYVARIANTS_TYPE "\" itemRgb=\"on\"" " visibility=\"pack\" shortLabel=\"%s\"" " longLabel=\"%s\"\n", encodedTableName, shortLabel, longLabel); if (dyStringLen(sharedLines) > 0) fprintf(f, "%s", dyStringContents(sharedLines)); carefulClose(&f); dyStringFree(&sharedLines); freeMem(encodedTableName); freeMem(shortLabel); freeMem(longLabel); return cloneString(tn.forCgi); } boolean myVariantsHandleCtRemoval(struct customTrack *ct, struct cart *cart, char *database) /* If ct is a myVariants own track: delete the user's rows for the current * assembly, unlink the persistent ctfile, drop the per-db renamed labels * and the visibility var. If ct is a myVariants shared track: drop the * share-acceptance cart var and the visibility var. Returns TRUE when ct * was a myVariants track and this function fully handled the cart cleanup * (caller must skip its own per-track cart cleanup so other-assembly * labels are preserved); FALSE otherwise. */ { if (ct == NULL || ct->tdb == NULL || isEmpty(ct->tdb->track)) return FALSE; char *trackName = ct->tdb->track; -if (!startsWith("myVariants_", trackName)) +if (!isMyVariantsTrack(trackName)) return FALSE; /* Cleanup common to any myVariants removal: drop the track's visibility var * and invalidate the on-disk ctfile pointer so the next entry-point visit * regenerates from current SQL/share state. */ cartRemove(cart, trackName); char mvVar[256]; safef(mvVar, sizeof mvVar, MYVARIANTS_FILE_VAR_PREFIX "%s", database); cartRemove(cart, mvVar); -if (startsWith("myVariants_shared_", trackName)) +if (isMyVariantsSharedTrack(trackName)) { /* Drop the share-acceptance var so the share isn't re-imported. */ - char *token = trackName + strlen("myVariants_shared_"); + char *token = trackName + strlen(MYVARIANTS_SHARED_TRACK_PREFIX); char shareCartVar[256]; safef(shareCartVar, sizeof shareCartVar, MYVAR_SHARED_CART_PREFIX "%s", token); cartRemove(cart, shareCartVar); return TRUE; } /* Own track: delete the user's SQL rows for this assembly, unlink the * persisted ctfile, and drop any renamed-label cart vars so a freshly * created track on this assembly starts at "My Annotations" again. */ char *userName = wikiLinkUserName(); if (isNotEmpty(userName)) { myVariantsDeleteForDb(userName, database); myVariantsUnlinkCtFile(userName, database);