use std::ops::{Bound, RangeBounds};
use std::sync::Arc;
use pigeonhole_engine::{FamilyId, TableInfo};
use crate::db::Db;
use crate::cell::CellRef;
use crate::{Error, ErrorCode, Family, Pigeonhole, Result, RowMutation, RowRead, Scan, Snapshot};
#[derive(Debug)]
pub struct TableBuilder<'db> {
db: &'db Pigeonhole,
name: String,
families: Vec<(String, Family)>,
}
impl<'db> TableBuilder<'db> {
pub(crate) fn new(db: &'db Pigeonhole, name: &str) -> Self {
Self {
db,
name: name.to_owned(),
families: Vec::new(),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Finish {
CreateIfMissing,
Create,
Open,
}
impl TableBuilder<'_> {
pub fn family(mut self, name: &str, family: Family) -> Self {
self.families.push((name.to_owned(), family));
self
}
pub fn create_if_missing(self) -> Result<Table> {
self.finish(Finish::CreateIfMissing)
}
pub fn create(self) -> Result<Table> {
self.finish(Finish::Create)
}
pub fn open(self) -> Result<Table> {
self.finish(Finish::Open)
}
fn finish(self, how: Finish) -> Result<Table> {
let db = &self.db.db;
db.check_open()?;
if self.name.is_empty() {
return Err(Error::new(ErrorCode::InvalidArgument, "empty table name"));
}
let engine = &db.engine;
let mut defs = Vec::with_capacity(self.families.len());
for (name, family) in &self.families {
if !defs.iter().any(|(n, _)| n == name) {
defs.push((name.clone(), family.to_engine()));
}
}
let mut info = match engine.table(&self.name) {
Some(info) if how == Finish::Create => {
return Err(pigeonhole_engine::Error::TableExists(info.name.clone()).into());
}
Some(info) => info,
None if how == Finish::Open => {
return Err(pigeonhole_engine::Error::TableNotFound(self.name).into());
}
None if defs.is_empty() => {
return Err(Error::new(
ErrorCode::InvalidArgument,
format!(
"table {:?} needs at least one family: declare one with .family(..)",
self.name
),
));
}
None => match engine.create_table(&self.name, &defs) {
Ok(info) => info,
Err(pigeonhole_engine::Error::TableExists(_)) if how != Finish::Create => engine
.table(&self.name)
.ok_or_else(|| pigeonhole_engine::Error::TableNotFound(self.name.clone()))?,
Err(e) => return Err(e.into()),
},
};
for (name, options) in defs {
if info.family(&name).is_some() {
continue;
}
info = match engine.add_family(info.id, &name, options) {
Ok(info) => info,
Err(pigeonhole_engine::Error::FamilyExists(_)) => engine
.table(&self.name)
.ok_or_else(|| pigeonhole_engine::Error::TableNotFound(self.name.clone()))?,
Err(e) => return Err(e.into()),
};
}
Ok(Table {
core: Arc::new(TableCore::new(Arc::clone(db), info)),
})
}
}
#[derive(Debug)]
pub(crate) struct TableCore {
pub(crate) db: Arc<Db>,
pub(crate) info: Arc<TableInfo>,
}
impl TableCore {
pub(crate) fn new(db: Arc<Db>, info: Arc<TableInfo>) -> Self {
Self { db, info }
}
pub(crate) fn current_info(&self) -> Arc<TableInfo> {
match self.db.engine.table(&self.info.name) {
Some(info) if info.id == self.info.id => info,
_ => Arc::clone(&self.info),
}
}
#[inline]
pub(crate) fn family_id(&self, name: &str) -> Result<FamilyId> {
if let Some(f) = self.info.family(name) {
return Ok(f.id);
}
self.current_info()
.family(name)
.map(|f| f.id)
.ok_or_else(|| family_not_found(&self.info.name, name))
}
#[inline]
pub(crate) fn get(
&self,
snapshot: Option<&Snapshot>,
row: &[u8],
family: &str,
qualifier: &[u8],
) -> Result<Option<CellRef<'_>>> {
self.db.check_open()?;
let family = self.family_id(family)?;
let table = self.info.id;
let data = match snapshot {
None => self.db.engine.get_latest(table, family, row, qualifier)?,
Some(s) => {
self.db.check_snapshot(s)?;
self.db
.engine
.get(&s.inner, table, family, row, qualifier)?
}
};
Ok(data.map(CellRef::owned))
}
fn scan(&self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Scan<'_> {
Scan::new(self, start.map(<[u8]>::to_vec), end.map(<[u8]>::to_vec))
}
fn scan_range<'k, K: AsRef<[u8]> + ?Sized + 'k>(
&self,
range: impl RangeBounds<&'k K>,
) -> Scan<'_> {
self.scan(
range.start_bound().map(|k| k.as_ref()),
range.end_bound().map(|k| k.as_ref()),
)
}
fn scan_prefix(&self, prefix: &[u8]) -> Scan<'_> {
let end = match prefix_end(prefix) {
Some(end) => Bound::Excluded(end),
None => Bound::Unbounded,
};
Scan::new(self, Bound::Included(prefix.to_vec()), end)
}
}
pub(crate) fn family_not_found(table: &str, family: &str) -> Error {
Error::new(
ErrorCode::FamilyNotFound,
format!("no such family {family:?} in table {table:?}"),
)
}
pub(crate) fn prefix_end(prefix: &[u8]) -> Option<Vec<u8>> {
let keep = prefix.iter().rposition(|&b| b != 0xff)?;
let mut end = prefix[..=keep].to_vec();
end[keep] += 1;
Some(end)
}
#[derive(Debug, Clone)]
pub struct Table {
pub(crate) core: Arc<TableCore>,
}
impl Table {
pub fn name(&self) -> &str {
&self.core.info.name
}
pub fn families(&self) -> Vec<&str> {
self.core
.info
.families
.iter()
.map(|f| f.name.as_str())
.collect()
}
pub fn mutate<'t>(&'t self, row: &[u8]) -> RowMutation<'t> {
RowMutation::new(self, row)
}
#[inline]
pub fn get(&self, row: &[u8], family: &str, qualifier: &[u8]) -> Result<Option<CellRef<'_>>> {
self.core.get(None, row, family, qualifier)
}
pub fn get_at(
&self,
snapshot: &Snapshot,
row: &[u8],
family: &str,
qualifier: &[u8],
) -> Result<Option<CellRef<'_>>> {
self.core.get(Some(snapshot), row, family, qualifier)
}
pub fn row<'t>(&'t self, row: &[u8]) -> RowRead<'t> {
RowRead::new(&self.core, row)
}
pub fn scan<'k, K: AsRef<[u8]> + ?Sized + 'k>(
&self,
range: impl RangeBounds<&'k K>,
) -> Scan<'_> {
self.core.scan_range(range)
}
pub fn scan_prefix(&self, prefix: &[u8]) -> Scan<'_> {
self.core.scan_prefix(prefix)
}
pub fn scan_bounds(&self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Scan<'_> {
self.core.scan(start, end)
}
}
#[derive(Debug, Clone)]
pub struct ReadTable {
pub(crate) core: Arc<TableCore>,
}
impl ReadTable {
pub fn name(&self) -> &str {
&self.core.info.name
}
pub fn get(&self, row: &[u8], family: &str, qualifier: &[u8]) -> Result<Option<CellRef<'_>>> {
self.core.get(None, row, family, qualifier)
}
pub fn get_at(
&self,
snapshot: &Snapshot,
row: &[u8],
family: &str,
qualifier: &[u8],
) -> Result<Option<CellRef<'_>>> {
self.core.get(Some(snapshot), row, family, qualifier)
}
pub fn row<'t>(&'t self, row: &[u8]) -> RowRead<'t> {
RowRead::new(&self.core, row)
}
pub fn scan<'k, K: AsRef<[u8]> + ?Sized + 'k>(
&self,
range: impl RangeBounds<&'k K>,
) -> Scan<'_> {
self.core.scan_range(range)
}
pub fn scan_prefix(&self, prefix: &[u8]) -> Scan<'_> {
self.core.scan_prefix(prefix)
}
pub fn scan_bounds(&self, start: Bound<&[u8]>, end: Bound<&[u8]>) -> Scan<'_> {
self.core.scan(start, end)
}
}
#[cfg(test)]
mod tests {
use super::prefix_end;
#[test]
fn prefix_end_is_the_next_key_after_the_prefix() {
assert_eq!(prefix_end(b"user:"), Some(b"user;".to_vec()));
assert_eq!(prefix_end(b"a\xff\xff"), Some(b"b".to_vec()));
assert_eq!(prefix_end(b"\xff\xff"), None);
assert_eq!(prefix_end(b""), None);
}
}