|
| 1 | +# Architecture |
| 2 | + |
| 3 | +A short map of how `sqlite-predict` is put together, for contributors. |
| 4 | + |
| 5 | +## Shape |
| 6 | + |
| 7 | +The extension is pure C99, built as a loadable SQLite/libSQL extension |
| 8 | +(`predict0.{dylib,so,dll}`) with a single entry point, |
| 9 | +`sqlite3_predict_init`. It has no third-party runtime dependencies; the |
| 10 | +SQLite amalgamation headers are fetched at build time and never |
| 11 | +redistributed. |
| 12 | + |
| 13 | +Source layout: |
| 14 | + |
| 15 | +| File | Responsibility | |
| 16 | +| --- | --- | |
| 17 | +| `sqlite-predict.c` | entry point, function registration, shared helpers (timestamp parse/format, ULID, options parsing, normal-quantile) | |
| 18 | +| `predict-forecast.c` | `forecast()` and `detect_anomalies()` vtabs, the statistical models, and the shared `collect_series()` helper | |
| 19 | +| `predict-tabular.c` | `predict()` vtab and the in-context k-NN model | |
| 20 | +| `predict-receipts.c` | model registry, receipts, canonical hashing, the logical-digest anchor, and `predict_replay()` | |
| 21 | +| `predict-internal.h` | shared types, contract constants, error codes, internal prototypes | |
| 22 | +| `vendor/sha256.c` | a self-contained FIPS 180-4 SHA-256 | |
| 23 | + |
| 24 | +## Operations are table-valued functions |
| 25 | + |
| 26 | +Each operation is an [eponymous virtual table module][tvf]. Arguments |
| 27 | +arrive as hidden columns (`query`, `horizon`, `options`), resolved in |
| 28 | +`xBestIndex` and consumed in `xFilter`, which does all the work and |
| 29 | +materializes result rows the cursor then walks. This is the same mechanism |
| 30 | +SQLite's own `generate_series` uses. |
| 31 | + |
| 32 | +`forecast()` and `detect_anomalies()` share `collect_series()`: prepare and |
| 33 | +validate the inner query (read-only, single statement), resolve or infer |
| 34 | +the time/value/group columns, and collect rows into per-series buffers. |
| 35 | + |
| 36 | +## Models |
| 37 | + |
| 38 | +Models are looked up in a registry table (`_predict_models`) by id, with a |
| 39 | +content hash and a license tag. The bundled models are pure-C statistical |
| 40 | +methods. Foundation models are out-of-process teachers reached via |
| 41 | +distillation (roadmap), not per-query serving paths. The benchmarks in |
| 42 | +`benchmarks/` drove that decision. |
| 43 | + |
| 44 | +## Receipts, anchoring, and replay |
| 45 | + |
| 46 | +Every operation writes a row to `_predict_receipts` (unless |
| 47 | +`'{"receipt":0}'`): the model id and hash, an anchor for the data state |
| 48 | +read, the canonical call parameters, the inner SQL, and a hash of the |
| 49 | +result set. |
| 50 | + |
| 51 | +- **Canonical result hash** (`predict-receipts.c`): rows are hashed in a |
| 52 | + defined order with type-tagged fields, separated by `0x1F` between fields |
| 53 | + and `0x1E` between rows. Integers hash as decimal text, reals as their |
| 54 | + big-endian IEEE-754 bit pattern, and text as UTF-8. This keeps the hash |
| 55 | + stable across runs on the same machine. |
| 56 | +- **Logical-digest anchor**: a hash of the user tables' schema and rows |
| 57 | + (excluding `_predict_%` and `sqlite_%`). A page-level file digest can |
| 58 | + never replay-match, because writing the receipt itself changes the file; |
| 59 | + the logical digest is indifferent to receipt writes and to `VACUUM`. |
| 60 | +- **Replay** re-executes the recorded call read-only against the anchored |
| 61 | + state and compares result hashes. It refuses to run if the current state |
| 62 | + no longer matches the anchor. |
| 63 | + |
| 64 | +## Determinism |
| 65 | + |
| 66 | +Statistical inference is deterministic on a given machine, which is what |
| 67 | +makes replay meaningful. Cross-machine and cross-backend determinism is |
| 68 | +not guaranteed (see the notes on GPU backends in `benchmarks/notes.md`); |
| 69 | +replay verification is same-machine. |
| 70 | + |
| 71 | +## Deviations from the spec |
| 72 | + |
| 73 | +The implementation surfaced amendments queued for the design spec: the |
| 74 | +`ts-stat`/`tabular-stat` model kinds, the `logical-digest` anchor kind, a |
| 75 | +`{"train","apply"}` JSON `input_sql` for two-query operations, and null |
| 76 | +option values meaning "key omitted." These are noted at the top of the |
| 77 | +files that introduce them. |
| 78 | + |
| 79 | +[tvf]: https://www.sqlite.org/vtab.html#tabfunc2 |
0 commit comments