use anyhow::Context;
use rusqlite::{params, Connection, OptionalExtension};
use std::{path::PathBuf, u64};
use crate::Error;
pub fn create(db: &PathBuf) -> anyhow::Result<()> {
let conn = Connection::open(db).with_context(|| "create db")?;
conn.execute_batch("PRAGMA journal_mode = wal;")
.expect("PRAGMA");
conn.execute(
"CREATE TABLE IF NOT EXISTS db_object (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
hashkey VARCHAR NOT NULL,
compressed BOOLEAN NOT NULL,
size INTEGER NOT NULL,
offset INTEGER NOT NULL,
length INTEGER NOT NULL,
pack_id INTEGER NOT NULL
)",
[],
)?;
conn.execute(
"CREATE UNIQUE INDEX ix_db_object_hashkey ON db_object (hashkey)",
[],
)
.with_context(|| "execute create SQL")?;
Ok(())
}
pub fn print_table(db: &PathBuf) -> anyhow::Result<()> {
let conn = Connection::open(db)
.with_context(|| format!("Open db {} for printing", db.to_string_lossy()))?;
let mut stmt = conn.prepare("SELECT id, hashkey, compressed, size, offset, length, pack_id FROM db_object")?;
let rows = stmt.query_map([], |row| {
Ok((
row.get::<_, i64>(0)?, row.get::<_, String>(1)?, row.get::<_, bool>(2)?, row.get::<_, i64>(3)?, row.get::<_, i64>(4)?, row.get::<_, i64>(5)?, row.get::<_, i64>(6)?, ))
})?;
println!("id | hashkey | compressed | size | offset | length | pack_id");
println!("-----------------------------------------------------------");
for row in rows {
let (id, hashkey, compressed, size, offset, length, pack_id) = row?;
println!(
"{} | {} | {} | {} | {} | {} | {}",
id, hashkey, compressed, size, offset, length, pack_id
);
}
Ok(())
}
pub fn stat(db: &PathBuf) -> anyhow::Result<(u64, u64)> {
let conn = Connection::open(db)
.with_context(|| format!("Open db {} for auditing", db.to_string_lossy()))?;
let mut stmt = conn.prepare("SELECT size FROM db_object")?;
let rows = stmt
.query([])
.with_context(|| "query size of objects")?
.mapped(|row| row.get::<_, u64>(0));
let mut count = 0;
let mut total_size = 0;
for size in rows {
total_size += size?;
count += 1;
}
Ok((count, total_size))
}
pub fn insert_packin(conn: &Connection, packin: &PackEntry) -> anyhow::Result<()> {
let mut stmt = conn.prepare_cached("INSERT OR IGNORE INTO db_object (hashkey, compressed, size, offset, length, pack_id) VALUES (?1, ?2, ?3, ?4, ?5, ?6)")?;
stmt.execute(params![
packin.hashkey,
packin.compressed,
packin.size,
packin.offset,
packin.raw_size,
packin.pack_id
])
.with_context(|| format!("insert {packin:?} to db"))?;
Ok(())
}
pub fn insert(
conn: &Connection,
hashkey: &str,
compressed: bool,
size: u64,
offset: u64,
length: u64,
pack_id: u64,
) -> anyhow::Result<()> {
let mut stmt = conn.prepare_cached("INSERT OR IGNORE INTO db_object (hashkey, compressed, size, offset, length, pack_id) VALUES (?1, ?2, ?3, ?4, ?5, ?6)")?;
stmt.execute(params![hashkey, compressed, size, offset, length, pack_id])
.with_context(|| "insert to db")?;
Ok(())
}
#[derive(Debug, Clone)]
pub struct PackEntry {
pub hashkey: String,
pub compressed: bool,
pub raw_size: u64,
pub size: u64,
pub offset: u64,
pub pack_id: u64,
}
pub fn select(conn: &Connection, hash_hex: &str) -> Result<Option<PackEntry>, Error> {
let mut stmt = conn.
prepare_cached("SELECT hashkey, compressed, size, offset, length, pack_id FROM db_object WHERE hashkey = ?1").
map_err(|err| Error::SQLiteSelectError{source: err})?;
let entry = stmt
.query_row(params![hash_hex], |row| {
Ok(PackEntry {
hashkey: row.get(0)?,
compressed: row.get(1)?,
raw_size: row.get(2)?,
offset: row.get(3)?,
size: row.get(4)?,
pack_id: row.get(5)?,
})
})
.optional()
.map_err(|err| Error::SQLiteSelectError { source: err })?;
Ok(entry)
}