Files
nxdns/src/storage/testdata/querylog-v1-schema.sql
T
mokhtar 272655f60c 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.
2026-08-28 17:56:19 +02:00

89 lines
3.2 KiB
SQL

-- 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;