//! The `querylog.db` schema version chain: comptime metadata and nothing else. //! //! Separate from `querylog_migrations.zig` so `tools/cut.zig` can import it //! without linking SQLite. Nothing in this file may reach for `db.zig`, for a //! C symbol, or for an allocator — the release gate reads these constants at //! build time, and a dependency here would drag the whole storage layer into //! the cut tool. //! //! **There are no migration hooks.** A step is a SQL file, period. Every //! shipped step is therefore byte-comparable against the previous tag, which is //! what lets the cut gate prove a released migration was never edited. A future //! change that genuinely cannot be expressed in SQL must amend this design in //! its own spec rather than adding a code path here. const std = @import("std"); /// The version a file created by this build carries in `PRAGMA user_version`. pub const current_version: i32 = 1; /// The oldest stamped version this build can reach `current_version` from. /// A file stamped below this refuses to open. /// /// An EXPLICIT BREAK in a future release is expressed here and only here: bump /// `current_version`, set `minimum_supported_version = current_version`, and /// ship no step. The chain then cannot reach the new version from below the /// minimum, so `open` refuses the old file by the ordinary rules. A break is /// always versioned, always refused at runtime, and never silent. pub const minimum_supported_version: i32 = 1; /// The literal `user_version` the 0.0.12 and 0.0.13 binaries stamped: the CRC32 /// of their DDL text, under the pre-migration policy where a stamp mismatch /// meant "replace the file". /// /// FROZEN. It is derived from nothing at build time on purpose — recomputing it /// from today's DDL would silently stop recognising the files it exists to /// recognise the moment the schema moves. Editing it strands every 0.0.12 and /// 0.0.13 file that has not yet been opened by a migration-aware build, which /// is why the cut gate fails on any change to this line. pub const legacy_fingerprint: i32 = 1975011655; /// Logical versions live far below any plausible CRC32 stamp. A value above /// this is not a version this project ever wrote, so it classifies as /// unsupported rather than as a from-the-future schema. pub const version_floor_guard: i32 = 1_000_000; /// One entry per shipped step: `step_sql[i]` migrates version /// `minimum_supported_version + i` to `minimum_supported_version + i + 1`. /// Each entry is `@embedFile("migrations/v.sql")`, and each such file is /// immutable once released. /// /// **Step-authoring rules** (the runner enforces the first, the equivalence /// oracle catches violations of the rest): /// /// - A step contains no transaction statement. No `BEGIN`, no `COMMIT`, no /// `ROLLBACK`, no `SAVEPOINT`: the runner wraps the whole chain in one /// transaction and installs an authorizer that denies them outright. /// - A step that changes a table's shape must REBUILD it, so that the CREATE /// text SQLite stores ends up byte-identical to the fresh DDL's: /// `DROP` every view over `` first; `ALTER TABLE RENAME TO _old`; /// `CREATE TABLE ...` pasted verbatim from `querylog_schema.ddl`; /// `INSERT INTO SELECT ... FROM _old`; `DROP TABLE _old`; recreate /// every index and trigger of `` verbatim; recreate the dropped views /// verbatim last. /// - `ALTER TABLE ... ADD COLUMN` and `ALTER TABLE ... RENAME COLUMN` on a kept /// table are forbidden. SQLite rewrites the stored CREATE text under them, /// and the oracle's exact-text layer would rightly call the result unequal. pub const step_sql: []const [:0]const u8 = &.{}; comptime { std.debug.assert(minimum_supported_version >= 1); std.debug.assert(minimum_supported_version <= current_version); std.debug.assert(current_version <= version_floor_guard); std.debug.assert(legacy_fingerprint < 0 or legacy_fingerprint > version_floor_guard); std.debug.assert(step_sql.len == @as(usize, @intCast(current_version - minimum_supported_version))); } test "the chain covers exactly the supported range" { try std.testing.expectEqual( @as(usize, @intCast(current_version - minimum_supported_version)), step_sql.len, ); } test "the legacy anchor is the literal 0.0.12 stamp" { // Not `fingerprintOf(ddl)`. The number is a historical fact about released // binaries, so a test that recomputed it would move with the schema and // prove nothing. try std.testing.expectEqual(@as(i32, 1975011655), legacy_fingerprint); }