KeiSeiKit-1.0/_primitives/_rust/kei-token-tracker/src/schema.rs
Parfii-bot 0be354a920 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

68 lines
2 KiB
Rust

//! SQLite schema runner. One table + three indexes, applied at `open`.
use rusqlite::Connection;
use crate::error::Error;
pub const SCHEMA_VERSION: u32 = 1;
const DDL_V1: &str = "
CREATE TABLE IF NOT EXISTS token_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts INTEGER NOT NULL,
agent_id TEXT NOT NULL,
conversation_id TEXT,
model TEXT NOT NULL,
role TEXT NOT NULL,
input_tokens INTEGER NOT NULL,
output_tokens INTEGER NOT NULL,
micro_cents INTEGER NOT NULL,
category TEXT,
source_kind TEXT,
latency_ms INTEGER
);
CREATE INDEX IF NOT EXISTS idx_token_events_ts
ON token_events(ts);
CREATE INDEX IF NOT EXISTS idx_token_events_model_ts
ON token_events(model, ts);
CREATE INDEX IF NOT EXISTS idx_token_events_agent_ts
ON token_events(agent_id, ts);
";
const MIGRATIONS: &[&str] = &[DDL_V1];
/// Apply pending migrations. Idempotent: re-running on an up-to-date
/// database is a no-op. Each migration runs in its own transaction so a
/// partial failure rolls back rather than wedging the schema.
pub fn migrate(conn: &Connection) -> Result<(), Error> {
let current: i64 = conn
.query_row("PRAGMA user_version", [], |r| r.get(0))
.unwrap_or(0);
for (i, sql) in MIGRATIONS.iter().enumerate() {
let target = (i + 1) as i64;
if current < target {
apply_one(conn, sql, target)?;
}
}
Ok(())
}
fn apply_one(conn: &Connection, sql: &str, target: i64) -> Result<(), Error> {
conn.execute_batch("BEGIN IMMEDIATE")?;
let step = (|| -> Result<(), rusqlite::Error> {
conn.execute_batch(sql)?;
conn.pragma_update(None, "user_version", target)?;
Ok(())
})();
match step {
Ok(()) => {
conn.execute_batch("COMMIT")?;
Ok(())
}
Err(e) => {
let _ = conn.execute_batch("ROLLBACK");
Err(Error::Migration(format!("v{target}: {e}")))
}
}
}