storage: version querylog.db and migrate it in place, never reset a healthy file
querylog.db carries a schema version; migrations run at startup as one transaction after a vacuumed 0600 backup, and every failure refuses startup (exit 2, no systemd restart loop) instead of starting empty. corruption is the only automatic recreate left. the cut gate now requires a fixture-proven migration or an explicit versioned break with restore instructions, and locks shipped migration files and fixtures byte-for-byte.
This commit is contained in:
+88
@@ -0,0 +1,88 @@
|
||||
-- FROZEN FIXTURE. querylog.db schema version 1: byte-for-byte the `ddl` text of
|
||||
-- `src/storage/querylog_schema.zig`, which is the schema the 0.0.12 and 0.0.13
|
||||
-- binaries created and the one version 1 names.
|
||||
--
|
||||
-- IMMUTABLE once released. The release gate byte-compares this file against the
|
||||
-- previous tag and fails the cut on any edit, because it is the starting point
|
||||
-- every future migration is proved against: editing it would prove a migration
|
||||
-- against a file no operator ever had. A new schema version ships a NEW pair.
|
||||
--
|
||||
-- The `PRAGMA user_version` stamp is deliberately NOT part of this file. The
|
||||
-- loader applies it, which is what lets one fixture serve both the version-1
|
||||
-- stamp and the 0.0.12/0.0.13 legacy fingerprint.
|
||||
|
||||
CREATE TABLE domains (
|
||||
id INTEGER PRIMARY KEY,
|
||||
domain TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
CREATE TABLE query_log (
|
||||
id INTEGER PRIMARY KEY,
|
||||
timestamp INTEGER NOT NULL,
|
||||
domain_id INTEGER NOT NULL REFERENCES domains(id),
|
||||
client_ip TEXT NOT NULL, -- text, not a FK: log rows are immutable facts
|
||||
qtype INTEGER,
|
||||
blocked INTEGER NOT NULL,
|
||||
response_time_us INTEGER,
|
||||
cache_hit INTEGER,
|
||||
upstream TEXT,
|
||||
qclass INTEGER NOT NULL,
|
||||
rcode INTEGER NOT NULL,
|
||||
group_id INTEGER, -- text/id pairs, not FKs: a renamed
|
||||
group_name TEXT, -- group must not rewrite history
|
||||
policy_action TEXT NOT NULL,
|
||||
policy_reason TEXT NOT NULL,
|
||||
matched TEXT,
|
||||
source_id INTEGER,
|
||||
source_name TEXT,
|
||||
cname_target TEXT,
|
||||
safe_search_target TEXT,
|
||||
route_kind TEXT NOT NULL,
|
||||
forward_zone TEXT,
|
||||
CHECK (rcode BETWEEN 0 AND 4095) -- twelve bits (RFC 6891 6.1.3)
|
||||
);
|
||||
CREATE INDEX idx_query_log_ts ON query_log(timestamp);
|
||||
CREATE INDEX idx_query_log_client ON query_log(client_ip);
|
||||
CREATE INDEX idx_query_log_domain ON query_log(domain_id);
|
||||
|
||||
CREATE TABLE querylog_meta (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1), -- one row, enforced by the schema
|
||||
created_at INTEGER NOT NULL,
|
||||
available_since INTEGER NOT NULL
|
||||
);
|
||||
INSERT INTO querylog_meta (id, created_at, available_since)
|
||||
VALUES (1, unixepoch(), unixepoch() + 1);
|
||||
|
||||
CREATE TABLE bucket_totals (
|
||||
bucket INTEGER PRIMARY KEY,
|
||||
queries INTEGER NOT NULL,
|
||||
blocked INTEGER NOT NULL,
|
||||
cached INTEGER NOT NULL,
|
||||
rt_sum INTEGER NOT NULL, -- sum(response_time_us) over timed rows
|
||||
rt_count INTEGER NOT NULL -- count(response_time_us)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE bucket_clients (
|
||||
bucket INTEGER NOT NULL,
|
||||
client_ip TEXT NOT NULL,
|
||||
queries INTEGER NOT NULL,
|
||||
PRIMARY KEY (bucket, client_ip)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE bucket_types (
|
||||
bucket INTEGER NOT NULL,
|
||||
qtype INTEGER NOT NULL, -- -1 encodes a NULL qtype, losslessly
|
||||
count INTEGER NOT NULL,
|
||||
PRIMARY KEY (bucket, qtype)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE bucket_routes (
|
||||
bucket INTEGER NOT NULL,
|
||||
route_kind TEXT NOT NULL,
|
||||
source_present INTEGER NOT NULL, -- 0: source NULL; 1: source = source_text
|
||||
source_text TEXT NOT NULL, -- '' when source_present = 0
|
||||
count INTEGER NOT NULL,
|
||||
PRIMARY KEY (bucket, route_kind, source_present, source_text),
|
||||
CHECK (source_present IN (0, 1)),
|
||||
CHECK (source_present = 1 OR source_text = '')
|
||||
) WITHOUT ROWID;
|
||||
Reference in New Issue
Block a user