Appearance
SQL Pipeline
SQL Practice runs on a decision that shapes everything else: queries execute entirely in the browser via sql.js (SQLite compiled to WASM). No server-side SQL, no sandboxing arbitrary queries against a real database, no round-trip for something that should feel instant.
The question format's special power: <!--SETUP-->
Each SQL question carries its own schema and seed data in a <!--SETUP--> block — plain SQL executed into a fresh in-memory SQLite instance when the question opens. Every question is therefore a self-contained database: authors design exactly the tables/rows a problem needs, and no two questions can interfere. The rest of the format matches the bank conventions (frontmatter, <!--STATEMENT-->, <!--SOLUTION-->, expected result).
Authoring: tools/sql_pipeline.py
Same shape as the CS/aptitude siblings: drafts in drafts/sql-practice/, validation (including that SETUP actually parses and the reference solution produces the expected result set against it), then move into the website's src/content/sql-practice/ behind the shared lint gate. 24 questions live. tools/sql_eval/ holds the offline checker.
Grading: deterministic first, AI second
Mirrors the aptitude split:
- Correctness — client-side: run the user's query and the reference in the same
sql.jsinstance, compare result sets (sqlGrading.js). Order-insensitive unless the question demands ORDER BY; column-order aware. No model involved. - Review —
functions/grade-sql-practice.jslayers AI feedback on quality (approach, unnecessary subqueries, missed indexes-in-spirit) on top of the known verdict, via the shared provider chain.
Note the contrast with CS-fundamentals concept questions about SQL: those are judged as text by grade-concept and never executed — the executed path exists only here, where sql.js provides a safe sandbox.
Shipping WASM: the three sequential build failures
Integrating sql.js broke the build three times, each one layer deeper — kept here because it's a reusable lesson in shipping WASM through Vite:
| # | Failure | Root cause → fix |
|---|---|---|
| 1 | Cannot find module 'sql.js' | dependency never added (dev had it hoisted from elsewhere) → add to package.json |
| 2 | runtime 404 on sql-wasm.wasm | the .wasm binary isn't part of the JS module graph; bundler ships the loader, not the binary → tools/copy-sql-wasm.mjs copies it into public/ during build, locateFile points at it |
| 3 | wrong file resolved in the browser | package exports browser condition resolves a different filename than the Node path the copy script assumed → copy script matches the browser-condition filename exactly |
The general rule extracted: WASM assets ride outside the bundle — treat them like fonts/images (explicit copy + explicit URL), not like imports.
Why not Judge0 with a SQL runtime?
Considered and rejected: it would round-trip every keystroke-run to the shared free-tier VM, add queueing latency to a feature whose whole appeal is immediacy, and require sandboxing discipline sql.js gives for free (the database is the user's own tab). The tradeoff — grading is result-set comparison rather than a full judge verdict — is the right scope for a practice tool, as noted in Code Execution.