use rusqlite::Connection;
use crate::error::SqliteError;
pub(super) fn register_writer_clock(conn: &Connection) -> Result<(), SqliteError> {
conn.create_scalar_function(
"khive_now_micros",
0,
rusqlite::functions::FunctionFlags::SQLITE_UTF8,
|_| Ok(chrono::Utc::now().timestamp_micros()),
)?;
Ok(())
}
pub(crate) fn rfc3339_instant_key(instant: chrono::DateTime<chrono::Utc>) -> Vec<u8> {
let mut key = Vec::with_capacity(12);
key.extend_from_slice(&((instant.timestamp() as u64) ^ (1_u64 << 63)).to_be_bytes());
key.extend_from_slice(&instant.timestamp_subsec_nanos().to_be_bytes());
key
}
pub(crate) fn strict_rfc3339_key(text: &str) -> Option<Vec<u8>> {
chrono::DateTime::parse_from_rfc3339(text)
.ok()
.map(|instant| rfc3339_instant_key(instant.with_timezone(&chrono::Utc)))
}
pub(crate) fn register_rfc3339_key(conn: &Connection) -> rusqlite::Result<()> {
use rusqlite::functions::FunctionFlags;
use rusqlite::types::ValueRef;
conn.create_scalar_function(
"khive_rfc3339_key",
1,
FunctionFlags::SQLITE_UTF8
| FunctionFlags::SQLITE_DETERMINISTIC
| FunctionFlags::SQLITE_INNOCUOUS,
|ctx| {
let text = match ctx.get_raw(0) {
ValueRef::Text(bytes) => std::str::from_utf8(bytes).ok(),
_ => None,
};
let key = text
.and_then(|text| text.parse::<chrono::DateTime<chrono::Utc>>().ok())
.map(rfc3339_instant_key);
Ok(key)
},
)?;
conn.create_scalar_function(
"khive_rfc3339_strict_key",
1,
FunctionFlags::SQLITE_UTF8
| FunctionFlags::SQLITE_DETERMINISTIC
| FunctionFlags::SQLITE_INNOCUOUS,
|ctx| {
let text = match ctx.get_raw(0) {
ValueRef::Text(bytes) => std::str::from_utf8(bytes).ok(),
_ => None,
};
let key = text.and_then(strict_rfc3339_key);
Ok(key)
},
)?;
Ok(())
}
pub(super) fn register_read_functions(conn: &Connection) -> rusqlite::Result<()> {
register_rfc3339_key(conn)?;
conn.create_scalar_function(
"khive_tag_contains",
2,
rusqlite::functions::FunctionFlags::SQLITE_UTF8
| rusqlite::functions::FunctionFlags::SQLITE_DETERMINISTIC
| rusqlite::functions::FunctionFlags::SQLITE_INNOCUOUS,
|ctx| {
let tags = match ctx.get_raw(0) {
rusqlite::types::ValueRef::Text(bytes) => Some(String::from_utf8_lossy(bytes)),
_ => None,
};
let marker = match ctx.get_raw(1) {
rusqlite::types::ValueRef::Text(bytes) => Some(String::from_utf8_lossy(bytes)),
_ => None,
};
Ok(marker.is_some_and(|marker| {
khive_types::tag_contains(tags.as_deref().unwrap_or(""), &marker)
}))
},
)
}
#[cfg(test)]
mod tests;