The problem
Viewing and transforming data requires pulling it out of the database first. Faster application code still pays that extraction cost.
autoSQL turns a user’s data selections into SQL that runs inside the database, with a comparison path that exposes numerical disagreements.
Extracting data just to reshape it creates an extra boundary to move, manage, and check. GIMS had two Python paths paying that cost. autoSQL moves the computation into the database and tests whether the result still agrees with the original calculation.
Public on GitHubPython compiler · generated SQL runtime · PostgresViewing and transforming data requires pulling it out of the database first. Faster application code still pays that extraction cost.
A compiler turns a declarative selection into SQL. The demo runs the same selection through SQL and Python and flags any row where the two disagree.
A silent wrong number becomes a visible discrepancy. Correctness evidence is available; performance remains a separate measurement question.
The same specification drives the widget and artifact renderers. A Python compiler emits SQL for PostgreSQL, moving viewing, window creation, and transformation out of application-side post-processing. The side-by-side demo below compares every row and marks any that disagree. The documented timing run remains open; this portfolio does not claim a measured speedup.
This state points at key edge-01, column biggest. The two engines once returned different numbers here, and nothing on the SQL side reported an error; on its own it returned a plausible number. Only the side-by-side comparison caught it, which is the entire reason both answers are on screen.
That divergence has since been fixed. Both engines now read the value as 123, and all 10 rows are identical. The comparison still runs on every pick: if a row ever differs again, both panes mark it and the pair opens at it, so a disagreement is located, not merely announced.
The picking column on the left with its nine numbered operations: a source, computed columns, one filter, a sort field, a row cap, an aggregate, time buckets. The verdict banner spanning both panes. The four-stage GATE / COMPILE / PROBES / EXECUTE strip. The generated SQL last and full width: session values set on every connection, the probes, then the pick’s own query.
Seven states are reachable by URL fragment: agreement, time buckets, only what changed, reconciled (the row that used to disagree), refused at the expression (the static gate), refused at the column name, refused while running (the runtime probe). All seven paint against live data.
8 rows compared, 8 identical, value for value, column for column, over the whole result and not the page below it. There is no tolerance anywhere in the comparison: two numbers agree when they are the same number. Both sides round half-up to six decimal places, which is why a value like 48.333333 is the same six digits in both panes rather than nearly the same.
GIMS’s dashboard expression language is a real tokeniser and parser producing a tagged-tuple AST, not eval. Three properties make it the integration point. It already has two runtimes, one in Python and one in JavaScript, held identical by a shared vector fixture, so a SQL compiler is a third runtime for the same AST with a ready-made equivalence oracle. It stops exactly where autoSQL starts: per-record scalars only; windowing and aggregation are the missing tier. And field types are declared, not guessed, driven by the schema, which is what makes typed compilation feasible.
Artifact generation is already a stage separate from data acquisition. It keeps making artifacts from templates; it simply receives an exact result set instead of a scanned-and-filtered pile. Same spec, two renderers.
A hybrid is required: compile what compiles, fall back in-memory otherwise, and report which happened. A silent fallback would recreate the very problem being solved.
┌──────────────── autoSQL ────────────────┐
UI ───▶ │ DataSource spec + window/aggregate │
(pick) │ ↓ compiler │
│ generated SQL │
└────────────────┬────────────────────────┘
│ runs in the DB
┌───────────┴───────────┐
▼ ▼
dashboards resolve() pphrase db_inputs
(live widgets) │
▼
post_doc + template
(artifacts)
autoSQL owns the read/query plane and owns no writes, no locks, and no invariants. Postgres-as-single-datastore is a different, larger claim, argued against elsewhere, and out of scope here.
The compiled SQL and the Python evaluator agree: 0 wrong numbers over 11,367 expressions, contract fixture 130 of 130, and the numbers no longer depend on a Postgres session setting at all. Two directories are the source of truth, the generated SQL runtime and the Python compiler, and everything under spikes/ is frozen evidence with checksums the tests assert.
The first spike ruled no-go on the standalone-compiler-plus-thin-adapter architecture as scoped. Not “impossible”, not “discard the work”: do not fund this on this evidence; run the two follow-up experiments first. It turns on three facts: the resolver in GIMS has no field through which a fallback could ever be reported; the compiled path measured 3.79×–7.15× slower than today’s Python, with no crossover; and 18 of 33 ways the two engines can disagree cannot be detected at query time by any mechanism.
One ticket remains, the timing run, and it is blocked for a measured reason: it needs a quiet machine, because it is measured in absolute milliseconds and numbers taken at an elevated load are not weaker, they are void. It is the last thing between this project and the GIMS gate.
A declared field type is not a guarantee about stored content: six of seven GIMS write paths never check it. Any design reaching for a per-path typed expression index must guard it or expect the failure. Written down so it is not rediscovered as a surprise.
Show me one task your team does by hand. In 20 minutes, I’ll tell you if it’s worth automating and what it would cost.