752e20a9a1dc08793c5b529bbeac4ce5836a6be4
braney
  Fri Sep 4 17:13:55 2026 -0700
hgc: stop five details-page handlers from aborting over a missing piece.

chromSeqFileExists() opened its connection with sqlConnect() and only then
asked whether the database existed, so it aborted before it could answer.
The otherDb of a chain or net track is often not a local database at all --
a GenArk hub assembly, or one long retired -- so use sqlMayConnect() and
return FALSE. This is 112 of the failures the TrackCheck robot reports, most
of them hg38 net tracks against HPRC assemblies. Also moved the disconnect
out of the if, where it leaked a connection whenever a database had no
chromInfo table, and dropped the now-redundant sqlDatabaseExists() call,
which was itself a second connection.

mgcCloneInfoLoad() aborted when a clone carried no MGC: id in
hgFixed.mrnaClone. Nothing on the page reads that field, and a clone can
legitimately have only an IMAGE: id, so the check went away rather than the
page. hDbOrganism() aborted for an assembly that has left dbDb but is still
named by a maf component, which hg16.evofold does via mm3; it now falls back
on the database name.

The pgSnp SIFT and Polyphen prediction tables are loaded separately from the
tracks that name them, so a machine can have the track and not the table, as
hgwbeta and the RR do for hg18. Check with hTableExists first and say the
predictions are unavailable instead of letting the query take the page down.
Same treatment for the RNA fold diagram: a non-zero ghostscript exit now
drops only the diagram and keeps the rest of the page, including the
PseudoViewer link.

Two of these report an unavailable piece through warn(), which still marks
the page for the robot. That is deliberate -- the missing hg18 tables and
the RNA fold diagram are real defects, and the log should keep naming them
until they are fixed. On the RNA fold diagram in particular: RNAplot
truncates the sequence id it is given to 42 characters, and the trash path
we build is already 41, so it has never written the file ghostscript is
asked to convert. That is worth its own fix.

refs #37424

diff --git src/hg/cgilib/pgSiftPred.c src/hg/cgilib/pgSiftPred.c
index b8a64f2ba2b..0be05428c59 100644
--- src/hg/cgilib/pgSiftPred.c
+++ src/hg/cgilib/pgSiftPred.c
@@ -1,261 +1,271 @@
 /* pgSiftPred.c was originally generated by the autoSql program, which also 
  * generated pgSiftPred.h and pgSiftPred.sql.  This module links the database and
  * the RAM representation of objects. */
 
 /* Copyright (C) 2014 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "linefile.h"
 #include "dystring.h"
 #include "jksql.h"
 #include "hdb.h"
 #include "pgSiftPred.h"
 
 
 void pgSiftPredStaticLoadWithNull(char **row, struct pgSiftPred *ret)
 /* Load a row from pgSiftPred table into ret.  The contents of ret will
  * be replaced at the next call to this function. */
 {
 
 ret->chrom = row[0];
 if (row[1] != NULL)
     {
     ret->chromStart = needMem(sizeof(*(ret->chromStart)));
     *(ret->chromStart) = sqlUnsigned(row[1]);
     }
 else
     {
     ret->chromStart = NULL;
     }
 if (row[2] != NULL)
     {
     ret->chromEnd = needMem(sizeof(*(ret->chromEnd)));
     *(ret->chromEnd) = sqlUnsigned(row[2]);
     }
 else
     {
     ret->chromEnd = NULL;
     }
 ret->prediction = row[3];
 ret->geneId = row[4];
 ret->geneName = row[5];
 ret->geneDesc = row[6];
 ret->protFamDesc = row[7];
 ret->omimDisease = row[8];
 ret->aveAlleleFreq = row[9];
 ret->ceuAlleleFreq = row[10];
 }
 
 struct pgSiftPred *pgSiftPredLoadWithNull(char **row)
 /* Load a pgSiftPred from row fetched with select * from pgSiftPred
  * from database.  Dispose of this with pgSiftPredFree(). */
 {
 struct pgSiftPred *ret;
 
 AllocVar(ret);
 ret->chrom = cloneString(row[0]);
 if (row[1] != NULL)
     {
     ret->chromStart = needMem(sizeof(*(ret->chromStart)));
     *(ret->chromStart) = sqlUnsigned(row[1]);
     }
 else
     {
     ret->chromStart = NULL;
     }
 if (row[2] != NULL)
     {
     ret->chromEnd = needMem(sizeof(*(ret->chromEnd)));
     *(ret->chromEnd) = sqlUnsigned(row[2]);
     }
 else
     {
     ret->chromEnd = NULL;
     }
 ret->prediction = cloneString(row[3]);
 ret->geneId = cloneString(row[4]);
 ret->geneName = cloneString(row[5]);
 ret->geneDesc = cloneString(row[6]);
 ret->protFamDesc = cloneString(row[7]);
 ret->omimDisease = cloneString(row[8]);
 ret->aveAlleleFreq = cloneString(row[9]);
 ret->ceuAlleleFreq = cloneString(row[10]);
 return ret;
 }
 
 struct pgSiftPred *pgSiftPredLoadAll(char *fileName) 
 /* Load all pgSiftPred from a whitespace-separated file.
  * Dispose of this with pgSiftPredFreeList(). */
 {
 struct pgSiftPred *list = NULL, *el;
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 char *row[11];
 
 while (lineFileRow(lf, row))
     {
     el = pgSiftPredLoadWithNull(row);
     slAddHead(&list, el);
     }
 lineFileClose(&lf);
 slReverse(&list);
 return list;
 }
 
 struct pgSiftPred *pgSiftPredLoadAllByChar(char *fileName, char chopper) 
 /* Load all pgSiftPred from a chopper separated file.
  * Dispose of this with pgSiftPredFreeList(). */
 {
 struct pgSiftPred *list = NULL, *el;
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 char *row[11];
 
 while (lineFileNextCharRow(lf, chopper, row, ArraySize(row)))
     {
     el = pgSiftPredLoadWithNull(row);
     slAddHead(&list, el);
     }
 lineFileClose(&lf);
 slReverse(&list);
 return list;
 }
 
 struct pgSiftPred *pgSiftPredCommaIn(char **pS, struct pgSiftPred *ret)
 /* Create a pgSiftPred out of a comma separated string. 
  * This will fill in ret if non-null, otherwise will
  * return a new pgSiftPred */
 {
 char *s = *pS;
 
 if (ret == NULL)
     AllocVar(ret);
 ret->chrom = sqlStringComma(&s);
 ret->chromStart = needMem(sizeof(unsigned));
 *(ret->chromStart) = sqlUnsignedComma(&s);
 ret->chromEnd = needMem(sizeof(unsigned));
 *(ret->chromEnd) = sqlUnsignedComma(&s);
 ret->prediction = sqlStringComma(&s);
 ret->geneId = sqlStringComma(&s);
 ret->geneName = sqlStringComma(&s);
 ret->geneDesc = sqlStringComma(&s);
 ret->protFamDesc = sqlStringComma(&s);
 ret->omimDisease = sqlStringComma(&s);
 ret->aveAlleleFreq = sqlStringComma(&s);
 ret->ceuAlleleFreq = sqlStringComma(&s);
 *pS = s;
 return ret;
 }
 
 void pgSiftPredFree(struct pgSiftPred **pEl)
 /* Free a single dynamically allocated pgSiftPred such as created
  * with pgSiftPredLoad(). */
 {
 struct pgSiftPred *el;
 
 if ((el = *pEl) == NULL) return;
 freeMem(el->chrom);
 freeMem(el->prediction);
 freeMem(el->geneId);
 freeMem(el->geneName);
 freeMem(el->geneDesc);
 freeMem(el->protFamDesc);
 freeMem(el->omimDisease);
 freeMem(el->aveAlleleFreq);
 freeMem(el->ceuAlleleFreq);
 freez(pEl);
 }
 
 void pgSiftPredFreeList(struct pgSiftPred **pList)
 /* Free a list of dynamically allocated pgSiftPred's */
 {
 struct pgSiftPred *el, *next;
 
 for (el = *pList; el != NULL; el = next)
     {
     next = el->next;
     pgSiftPredFree(&el);
     }
 *pList = NULL;
 }
 
 void pgSiftPredOutput(struct pgSiftPred *el, FILE *f, char sep, char lastSep) 
 /* Print out pgSiftPred.  Separate fields with sep. Follow last field with lastSep. */
 {
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->chrom);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 fprintf(f, "%u", *(el->chromStart));
 fputc(sep,f);
 fprintf(f, "%u", *(el->chromEnd));
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->prediction);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->geneId);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->geneName);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->geneDesc);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->protFamDesc);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->omimDisease);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->aveAlleleFreq);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->ceuAlleleFreq);
 if (sep == ',') fputc('"',f);
 fputc(lastSep,f);
 }
 
 /* -------------------------------- End autoSql Generated Code -------------------------------- */
 
 void printPgSiftPred (char *db, char *tableName, struct pgSnp *item)
 /* print the predictions for an hgc item click for a pgSnp track */
 {
 struct pgSiftPred *el;
 struct sqlResult *sr;
 char **row;
 char query[512];
+/* The prediction table is loaded separately from the pgSnp track that names it, so a
+ * machine can have the track and not the table (the hg18 pgSnp tables are missing on
+ * hgwbeta and on the RR).  Say so instead of aborting the details page.  refs #37424 */
+if (!hTableExists(db, tableName))
+    {
+    warn("SIFT predictions are not available: table %s, named by the pgSiftPredTab "
+        "setting, is missing from %s.", tableName, db);
+    return;
+    }
+
 struct sqlConnection *conn = hAllocConn(db);
 
 sqlSafef(query, sizeof(query), "select * from %s where chrom = '%s' and chromStart = %d and chromEnd = %d",
     tableName, item->chrom, item->chromStart, item->chromEnd);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     el = pgSiftPredLoadWithNull(row);
     printf("<br><b>SIFT prediction</b>: %s\n", el->prediction);
     printf("<ul>");
     if (el->geneId != NULL && differentString(el->geneId, ""))
         printf("<li>Gene ID: %s</li>\n", el->geneId);
     if (el->geneName != NULL && differentString(el->geneName, ""))
         printf("<li>Gene name: %s</li>\n", el->geneName);
     if (el->geneDesc != NULL && differentString(el->geneDesc, ""))
         printf("<li>Gene description: %s</li>\n", el->geneDesc);
     if (el->protFamDesc != NULL && differentString(el->protFamDesc, ""))
         printf("<li>Protein family description: %s</li>\n", el->protFamDesc);
     if (el->omimDisease != NULL && differentString(el->omimDisease, ""))
         printf("<li>OMIM disease: %s</li>\n", el->omimDisease);
     if (el->aveAlleleFreq != NULL && differentString(el->aveAlleleFreq, ""))
         printf("<li>Average allele frequency: %s</li>\n", el->aveAlleleFreq);
     if (el->ceuAlleleFreq != NULL && differentString(el->ceuAlleleFreq, ""))
 	printf("<li>CEU allele frequency: %s</li>\n", el->ceuAlleleFreq);
     printf("</ul>\n");
     }
 }