Genomic colocation: add Short Variant, repair five Oracle-isms - #22
Open
jbrestel wants to merge 9 commits into
Open
Genomic colocation: add Short Variant, repair five Oracle-isms#22jbrestel wants to merge 9 commits into
jbrestel wants to merge 9 commits into
Conversation
An unregistered record class now throws rather than silently falling back to a bare apidb.FeatureLocation join that was never validated for it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Also drops the synthetic is_top_level/feature_type columns, which existed only to satisfy filters the one-row-per-record builder no longer applies. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
regexp_substr returns text, and makeRegion does arithmetic on start_min/ end_max. Oracle coerced implicitly; PostgreSQL raises 'operator does not exist: text + integer', so segment colocation failed even after the DECODE fix. Text comparison would also have ordered spans lexicographically. Also: make the fl-alias assertion match the FROM clause instead of any occurrence, lowercase the Oracle-ism checks, and guard spanSourceFor(null). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…paths - getSpanSql looks up a SpanSource instead of branching on record-class name - Flag.hasSnp -> Flag.strandless, named after the property not one record type - deletes the unreachable SnpRecordClass branch: every SNP import is commented out of apiCommonModel.xml, so that record class cannot exist at runtime - deletes the rownum subquery rather than porting it
Oracle accepts "FROM (table_name) alias"; PostgreSQL raises 'syntax error at or near ")"'. composeSql is shared by every colocation regardless of record type, so this broke ALL of them on Postgres -- including gene <-> gene, which the earlier analysis had assumed working. Found by live QA, not by the unit tests: they asserted on the per-source CREATE TABLE statements and never touched the join composed from them. composeSql is now package-private with a regression test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
getDefaultSchema() means different things per platform: Oracle returns the
login user's schema -- exactly where an unqualified CREATE TABLE lands, so
create and drop agreed. PostgreSQL hardcodes "public", while an unqualified
CREATE follows search_path ("$user").
So the temp tables were created in the login schema and the drop looked in
public, failing with 'table "spanlogic<n>" does not exist' AFTER the results
were computed. A working colocation surfaced to the user as an error, and
leaked one table per run (38 found in the appDb).
Passing null makes dropTable emit a bare table name, resolving the same way
the CREATE did, on either platform.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8 tasks
jbrestel
requested review from
aurreco-uga,
ryanrdoherty and
steve-fischer-200
August 15, 2026 18:05
| * Where a record type's genomic coordinates come from. One implementation per record | ||
| * class that may be an input to colocation. | ||
| * | ||
| * Implementations MUST alias their location table "fl" -- makeRegion() hardcodes that |
Contributor
There was a problem hiding this comment.
in what class is makeRegion()? i can't find any reference to it, either being defined or called
steve-fischer-200
approved these changes
Aug 17, 2026
steve-fischer-200
left a comment
Contributor
There was a problem hiding this comment.
i don't understand all the commentary about 'fl' and makeRegion()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Two things, and the second is bigger than the title suggests:
Pairs with VEuPathDB/ApiCommonModel#220 — these must merge together, and this one should go first. The model PR adds a question that references plugin behaviour introduced here.
The five Oracle-isms
rownumgetStandardSpanSqlDECODEregexp_substrreturnstext, andmakeRegiondoes arithmetic on itFROM (table_name) aliascomposeSqlgetDefaultSchema()create/drop mismatchexecutecleanupWorth noting how each was found, because it says something about the test strategy: #1 and #2 by reading the code, #3 by code review, and #4 and #5 only by running it. Unit tests asserting
contains()on generated SQL fragments passed throughout — they never composed the final join and never executed anything.rownumis deleted, not ported. It filtered all rows to thefeature_typeof one arbitrary joined row — a defensive no-op even on Oracle (only 95feature_source_idvalues out of 46M rows inapidb.FeatureLocationhave more than one distinctfeature_type) — and after this refactor it sits only on paths that are one-row-per-record by construction.regexp_substrwith Oracle's 4-arg signature is kept: it works on PostgreSQL 18 (select regexp_substr('chr1:100-200:r','[^:]+',1,3)→r).This is the change I most want a second opinion on. The diff shows only
schema→null, which hides a platform-semantics decision.getSpanSqlissues an unqualifiedCREATE TABLE spanlogic<n>, which followssearch_path—"$user"on our appDbs. The cleanup dropped viagetDefaultSchema(). Those coincide on Oracle and diverge on PostgreSQL, so tables were created in the login schema and the drop looked inpublic, raisingtable "spanlogic<n>" does not existafter the results were computed. A working colocation surfaced to the user as an error and leaked one table per run — 44 orphans were found ingenomicsdb_071n.Chosen fix: pass
null, sodropTableemits a bare table name that resolves exactly as theCREATEdid, on either platform.Rejected alternatives, and why:
CREATEwithgetDefaultSchema(), sending tables topublic. Since PostgreSQL 15 thePUBLICrole has noCREATEonpublicby default, so this may fail outright on some deployments, and it puts per-request scratch tables in a shared schema.PostgreSQL.getDefaultSchemain FgpUtil to return the login schema. Arguably the real defect — the two implementations do not mean the same thing and callers cannot tell. Scoped out because that method is used across WDK andpublicmay be correct for its other callers. If you prefer this, it should be its own change with a call-site audit.The trade being made: scratch tables are a per-request implementation detail, so "wherever the login can write" is defensible — but the plugin now states nowhere which schema it writes to. Worth a follow-up ticket on the FgpUtil asymmetry either way.
The refactor
The record-class
if/elseingetSpanSqlis replaced by aSpanSourceinterface plus a registry keyed by record-class full name:is_top_levelfilterTranscriptRecordClassapidb.FeatureLocation, joined ongene_source_id,feature_type='GeneFeature'DynSpanRecordClassVariantRecordClassApidbTuning.VariationAttributesAn unregistered record class now throws, naming the class, rather than silently falling back to a bare
apidb.FeatureLocationjoin nobody validated for it.Two invariants are documented in the code because they are easy to break:
SpanSourcemust alias its location tablefl—makeRegionhardcodes that prefix.is_top_level = 1must stay on the Transcript source. 2,503 rows inapidb.FeatureLocationhaveis_top_level = 0; they are human pseudoautosomal-region genes carrying duplicate chrX/chrY placements. Dropping the filter double-counts them. Do not "clean this up".Flag.hasSnp→Flag.strandless: same behaviour, named after the property rather than one record type. It suppresses the strand filter for the whole comparison, which is required — a variant is a point withis_reversed = 0, so "same strand" would otherwise silently return forward-strand genes only. Side effect worth knowing: the strand selector is inert for any comparison involving variants. Correct, but the UI still offers the control.The unreachable
SnpRecordClasses.SnpRecordClassbranch is deleted — every SNP import is commented out ofapiCommonModel.xml, so that record class cannot exist at runtime.Test plan
mvn -pl WSFPlugin test— 59 tests, 0 failures, 2 pre-existing skipsgenomicsdb_071n(PostgreSQL 18.4)DynSpanSource, i.e. theDECODEport and the numeric cast)RecordsBySpanLogicdeclareswsColumn project_idunconditionally whileVariantRecordClassexcludesproject_idfrom its PK on UniDB.DynSpanRecordClasshas the same shape and shares the query, so this is pre-existing — but DynSpan colocation has never worked on PostgreSQL, so nobody has exercised it. The shared query is deliberately untouched.🤖 Generated with Claude Code