use std::ops::Bound;
use reifydb_core::common::CommitVersion;
use rusqlite::{Result as SqliteResult, ToSql, types};
#[inline]
pub(super) fn version_to_bytes(version: CommitVersion) -> [u8; 8] {
version.0.to_be_bytes()
}
pub(super) fn build_versioned_range_query(
table_name: &str,
start: Bound<&[u8]>,
end: Bound<&[u8]>,
version: CommitVersion,
reverse: bool,
limit: usize,
) -> (String, Vec<QueryParam>) {
let mut conditions = Vec::new();
let mut params: Vec<QueryParam> = Vec::new();
match start {
Bound::Included(v) => {
conditions.push(format!("key >= ?{}", params.len() + 1));
params.push(QueryParam::Blob(v.to_vec()));
}
Bound::Excluded(v) => {
conditions.push(format!("key > ?{}", params.len() + 1));
params.push(QueryParam::Blob(v.to_vec()));
}
Bound::Unbounded => {}
}
match end {
Bound::Included(v) => {
conditions.push(format!("key <= ?{}", params.len() + 1));
params.push(QueryParam::Blob(v.to_vec()));
}
Bound::Excluded(v) => {
conditions.push(format!("key < ?{}", params.len() + 1));
params.push(QueryParam::Blob(v.to_vec()));
}
Bound::Unbounded => {}
}
conditions.push(format!("version <= ?{}", params.len() + 1));
params.push(QueryParam::Version(version_to_bytes(version)));
let where_clause = format!(" WHERE {}", conditions.join(" AND "));
let order = if reverse {
"DESC"
} else {
"ASC"
};
let query = format!(
"SELECT key, version, value FROM (\
SELECT key, version, value, \
ROW_NUMBER() OVER (PARTITION BY key ORDER BY version DESC) as rn \
FROM \"{}\"{}\
) WHERE rn = 1 ORDER BY key {} LIMIT {}",
table_name, where_clause, order, limit
);
(query, params)
}
#[derive(Debug, Clone)]
pub(super) enum QueryParam {
Blob(Vec<u8>),
Version([u8; 8]),
}
impl ToSql for QueryParam {
fn to_sql(&self) -> SqliteResult<types::ToSqlOutput<'_>> {
match self {
QueryParam::Blob(v) => v.to_sql(),
QueryParam::Version(v) => v.as_slice().to_sql(),
}
}
}