use crate::{bsatn, rt::ExplicitNames, sys, DeserializeOwned, IterBuf, Serialize, SpacetimeType, TableId};
use core::borrow::Borrow;
use core::convert::Infallible;
use core::fmt;
use core::marker::PhantomData;
pub use spacetimedb_lib::db::raw_def::v9::TableAccess;
use spacetimedb_lib::{
buffer::{BufReader, Cursor, DecodeError},
AlgebraicValue,
};
use spacetimedb_lib::{FilterableValue, IndexScanRangeBoundsTerminator};
pub use spacetimedb_primitives::{ColId, IndexId};
pub trait Table: TableInternal + ExplicitNames {
type Row: SpacetimeType + Serialize + DeserializeOwned + Sized + 'static;
fn count(&self) -> u64 {
count::<Self>()
}
#[inline]
fn iter(&self) -> impl Iterator<Item = Self::Row> {
let table_id = Self::table_id();
let iter = sys::datastore_table_scan_bsatn(table_id).expect("datastore_table_scan_bsatn() call failed");
TableIter::new(iter)
}
#[track_caller]
fn insert(&self, row: Self::Row) -> Self::Row {
self.try_insert(row).unwrap_or_else(|e| panic!("{e}"))
}
type UniqueConstraintViolation: MaybeError<UniqueConstraintViolation>;
type AutoIncOverflow: MaybeError<AutoIncOverflow>;
#[track_caller]
fn try_insert(&self, row: Self::Row) -> Result<Self::Row, TryInsertError<Self>> {
insert::<Self>(row, IterBuf::take())
}
fn delete(&self, row: Self::Row) -> bool {
let relation = std::slice::from_ref(&row);
let buf = IterBuf::serialize(relation).unwrap();
let count = sys::datastore_delete_all_by_eq_bsatn(Self::table_id(), &buf).unwrap();
count > 0
}
fn clear(&self) -> u64 {
sys::datastore_clear(Self::table_id()).expect("datastore_clear() call failed")
}
#[doc(hidden)]
fn integrate_generated_columns(row: &mut Self::Row, generated_cols: &[u8]);
}
#[doc(hidden)]
#[inline]
pub fn count<Tbl: Table>() -> u64 {
sys::datastore_table_row_count(Tbl::table_id()).expect("datastore_table_row_count() call failed")
}
#[doc(hidden)]
pub trait TableInternal: Sized {
const TABLE_NAME: &'static str;
const TABLE_ACCESS: TableAccess = TableAccess::Private;
const UNIQUE_COLUMNS: &'static [u16];
const INDEXES: &'static [IndexDesc<'static>];
const PRIMARY_KEY: Option<u16> = None;
const SEQUENCES: &'static [u16];
const SCHEDULE: Option<ScheduleDesc<'static>> = None;
const IS_EVENT: bool = false;
fn table_id() -> TableId;
fn get_default_col_values() -> Vec<ColumnDefault>;
}
#[derive(Clone, Copy)]
pub struct IndexDesc<'a> {
pub source_name: &'a str,
pub accessor_name: &'a str,
pub algo: IndexAlgo<'a>,
}
#[derive(Clone, Copy)]
pub enum IndexAlgo<'a> {
BTree { columns: &'a [u16] },
Hash { columns: &'a [u16] },
Direct { column: u16 },
}
pub struct ScheduleDesc<'a> {
pub reducer_or_procedure_name: &'a str,
pub scheduled_at_column: u16,
}
#[derive(Debug, Clone)]
pub struct ColumnDefault {
pub col_id: u16,
pub value: AlgebraicValue,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct UniqueConstraintViolation;
impl fmt::Display for UniqueConstraintViolation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "duplicate unique column")
}
}
impl std::error::Error for UniqueConstraintViolation {}
#[derive(Debug)]
#[non_exhaustive]
pub struct AutoIncOverflow;
impl fmt::Display for AutoIncOverflow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "auto-inc sequence overflowed its column type")
}
}
impl std::error::Error for AutoIncOverflow {}
pub enum TryInsertError<Tbl: Table> {
UniqueConstraintViolation(Tbl::UniqueConstraintViolation),
AutoIncOverflow(Tbl::AutoIncOverflow),
}
impl<Tbl: Table> fmt::Debug for TryInsertError<Tbl> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TryInsertError::<{}>::", Tbl::TABLE_NAME)?;
match self {
Self::UniqueConstraintViolation(e) => fmt::Debug::fmt(e, f),
Self::AutoIncOverflow(e) => fmt::Debug::fmt(e, f),
}
}
}
impl<Tbl: Table> fmt::Display for TryInsertError<Tbl> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "insertion error on table `{}`:", Tbl::TABLE_NAME)?;
match self {
Self::UniqueConstraintViolation(e) => fmt::Display::fmt(e, f),
Self::AutoIncOverflow(e) => fmt::Display::fmt(e, f),
}
}
}
impl<Tbl: Table> std::error::Error for TryInsertError<Tbl> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(match self {
Self::UniqueConstraintViolation(e) => e,
Self::AutoIncOverflow(e) => e,
})
}
}
impl<Tbl: Table> From<TryInsertError<Tbl>> for String {
fn from(err: TryInsertError<Tbl>) -> Self {
err.to_string()
}
}
#[doc(hidden)]
pub trait MaybeError<E = Self>: std::error::Error + Send + Sync + Sized + 'static {
fn get() -> Option<Self>;
}
impl<E> MaybeError<E> for Infallible {
fn get() -> Option<Self> {
None
}
}
impl MaybeError for UniqueConstraintViolation {
fn get() -> Option<Self> {
Some(UniqueConstraintViolation)
}
}
impl MaybeError for AutoIncOverflow {
fn get() -> Option<AutoIncOverflow> {
Some(AutoIncOverflow)
}
}
pub trait Column {
type Table: Table;
type ColType: SpacetimeType + Serialize + DeserializeOwned;
const COLUMN_NAME: &'static str;
fn get_field(row: &<Self::Table as Table>::Row) -> &Self::ColType;
}
pub trait PrimaryKey {}
pub struct UniqueColumn<Tbl, ColType, Col> {
_marker: PhantomData<(Tbl, ColType, Col)>,
}
impl<Tbl: Table, Col: Index + Column<Table = Tbl>> UniqueColumn<Tbl, Col::ColType, Col> {
#[doc(hidden)]
pub const __NEW: Self = Self { _marker: PhantomData };
#[inline]
pub fn find(&self, col_val: impl Borrow<Col::ColType>) -> Option<Tbl::Row>
where
for<'a> &'a Col::ColType: FilterableValue,
{
find::<Tbl, Col>(col_val.borrow())
}
#[inline]
pub fn delete(&self, col_val: impl Borrow<Col::ColType>) -> bool {
self._delete(col_val.borrow()).0
}
fn _delete(&self, col_val: &Col::ColType) -> (bool, IterBuf) {
let index_id = Col::index_id();
let point = IterBuf::serialize(col_val).unwrap();
let n_del = sys::datastore_delete_by_index_scan_point_bsatn(index_id, &point).unwrap_or_else(|e| {
panic!("unique: unexpected error from datastore_delete_by_index_scan_point_bsatn: {e}")
});
(n_del > 0, point)
}
#[track_caller]
pub fn update(&self, new_row: Tbl::Row) -> Tbl::Row
where
Col: PrimaryKey,
{
let buf = IterBuf::take();
update::<Tbl>(Col::index_id(), new_row, buf)
}
#[track_caller]
#[doc(alias = "try_upsert")]
#[cfg(feature = "unstable")]
pub fn try_insert_or_update(&self, new_row: Tbl::Row) -> Result<Tbl::Row, TryInsertError<Tbl>> {
let col_val = Col::get_field(&new_row);
let _ = self.delete(col_val);
let buf = IterBuf::take();
insert::<Tbl>(new_row, buf)
}
#[track_caller]
#[doc(alias = "upsert")]
#[cfg(feature = "unstable")]
pub fn insert_or_update(&self, new_row: Tbl::Row) -> Tbl::Row {
self.try_insert_or_update(new_row).unwrap_or_else(|e| panic!("{e}"))
}
}
#[inline]
fn find<Tbl: Table, Col: Index + Column<Table = Tbl>>(col_val: &Col::ColType) -> Option<Tbl::Row> {
let index_id = Col::index_id();
let point = IterBuf::serialize(col_val).unwrap();
let iter = datastore_index_scan_point_bsatn(index_id, &point);
let mut iter = TableIter::new_with_buf(iter, point);
let row = iter.next();
assert!(
iter.is_exhausted(),
"`datastore_index_scan_point_bsatn` on unique field cannot return >1 rows"
);
row
}
fn datastore_index_scan_point_bsatn(index_id: IndexId, point: &[u8]) -> sys::RowIter {
sys::datastore_index_scan_point_bsatn(index_id, point)
.unwrap_or_else(|e| panic!("unexpected error from `datastore_index_scan_point_bsatn`: {e}"))
}
pub struct UniqueColumnReadOnly<Tbl, ColType, Col> {
_marker: PhantomData<(Tbl, ColType, Col)>,
}
impl<Tbl: Table, Col: Index + Column<Table = Tbl>> UniqueColumnReadOnly<Tbl, Col::ColType, Col> {
#[doc(hidden)]
pub const __NEW: Self = Self { _marker: PhantomData };
#[inline]
pub fn find(&self, col_val: impl Borrow<Col::ColType>) -> Option<Tbl::Row>
where
for<'a> &'a Col::ColType: FilterableValue,
{
find::<Tbl, Col>(col_val.borrow())
}
}
pub trait Index {
const NUM_COLS_INDEXED: usize;
fn index_id() -> IndexId;
}
pub trait IndexIsPointed: Index {}
pub struct PointIndex<Tbl: Table, IndexType, Idx: Index> {
_marker: PhantomData<(Tbl, IndexType, Idx)>,
}
impl<Tbl: Table, IndexType, Idx: IndexIsPointed> PointIndex<Tbl, IndexType, Idx> {
#[doc(hidden)]
pub const __NEW: Self = Self { _marker: PhantomData };
pub fn filter<P, K>(&self, point: P) -> impl Iterator<Item = Tbl::Row> + use<P, K, Tbl, IndexType, Idx>
where
P: WithPointArg<K>,
{
filter_point::<Tbl, Idx, K>(point)
}
pub fn delete<P, K>(&self, point: P) -> u64
where
P: WithPointArg<K>,
{
let index_id = Idx::index_id();
point.with_point_arg(|point| {
sys::datastore_delete_by_index_scan_point_bsatn(index_id, point)
.unwrap_or_else(|e| panic!("unexpected error from `datastore_delete_by_index_scan_point_bsatn`: {e}"))
.into()
})
}
}
fn filter_point<Tbl, Idx, K>(point: impl WithPointArg<K>) -> impl Iterator<Item = Tbl::Row>
where
Tbl: Table,
Idx: IndexIsPointed,
{
let index_id = Idx::index_id();
let iter = point.with_point_arg(|point| datastore_index_scan_point_bsatn(index_id, point));
TableIter::new(iter)
}
pub struct PointIndexReadOnly<Tbl: Table, IndexType, Idx: Index> {
_marker: PhantomData<(Tbl, IndexType, Idx)>,
}
impl<Tbl: Table, IndexType, Idx: IndexIsPointed> PointIndexReadOnly<Tbl, IndexType, Idx> {
#[doc(hidden)]
pub const __NEW: Self = Self { _marker: PhantomData };
pub fn filter<P, K>(&self, point: P) -> impl Iterator<Item = Tbl::Row> + use<P, K, Tbl, IndexType, Idx>
where
P: WithPointArg<K>,
{
filter_point::<Tbl, Idx, K>(point)
}
}
pub trait WithPointArg<K = ()> {
#[doc(hidden)]
fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R;
}
impl<Arg: FilterableValue> WithPointArg<SingleBound> for Arg {
fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {
run(&IterBuf::serialize(self).unwrap())
}
}
macro_rules! impl_with_point_arg {
($($arg:ident),+) => {
impl<$($arg: FilterableValue),+> WithPointArg for ($($arg,)+) {
fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {
let mut data = IterBuf::take();
#[allow(non_snake_case)]
let ($($arg,)+) = self;
Ok(())
$(.and_then(|()| data.serialize_into($arg)))+
.unwrap();
run(&*data)
}
}
};
}
impl_with_point_arg!(A);
impl_with_point_arg!(A, B);
impl_with_point_arg!(A, B, C);
impl_with_point_arg!(A, B, C, D);
impl_with_point_arg!(A, B, C, D, E);
impl_with_point_arg!(A, B, C, D, E, F);
pub trait IndexIsRanged: Index {}
pub struct RangedIndex<Tbl: Table, IndexType, Idx: IndexIsRanged> {
_marker: PhantomData<(Tbl, IndexType, Idx)>,
}
impl<Tbl: Table, IndexType, Idx: IndexIsRanged> RangedIndex<Tbl, IndexType, Idx> {
#[doc(hidden)]
pub const __NEW: Self = Self { _marker: PhantomData };
pub fn filter<B, K>(&self, b: B) -> impl Iterator<Item = Tbl::Row> + use<B, K, Tbl, IndexType, Idx>
where
B: IndexScanRangeBounds<IndexType, K>,
{
filter::<Tbl, Idx, IndexType, B, K>(b)
}
pub fn delete<B, K>(&self, b: B) -> u64
where
B: IndexScanRangeBounds<IndexType, K>,
{
let index_id = Idx::index_id();
if const { is_point_scan::<Idx, B, _, _>() } {
b.with_point_arg(|point| {
sys::datastore_delete_by_index_scan_point_bsatn(index_id, point)
.unwrap_or_else(|e| {
panic!("unexpected error from `datastore_delete_by_index_scan_point_bsatn`: {e}")
})
.into()
})
} else {
let args = b.get_range_args();
let (prefix, prefix_elems, rstart, rend) = args.args_for_syscall();
sys::datastore_delete_by_index_scan_range_bsatn(index_id, prefix, prefix_elems, rstart, rend)
.unwrap_or_else(|e| panic!("unexpected error from `datastore_delete_by_index_scan_range_bsatn`: {e}"))
.into()
}
}
}
fn filter<Tbl, Idx, IndexType, B, K>(b: B) -> impl Iterator<Item = Tbl::Row>
where
Tbl: Table,
Idx: Index,
B: IndexScanRangeBounds<IndexType, K>,
{
let index_id = Idx::index_id();
let iter = if const { is_point_scan::<Idx, B, _, _>() } {
b.with_point_arg(|point| datastore_index_scan_point_bsatn(index_id, point))
} else {
let args = b.get_range_args();
let (prefix, prefix_elems, rstart, rend) = args.args_for_syscall();
sys::datastore_index_scan_range_bsatn(index_id, prefix, prefix_elems, rstart, rend)
.unwrap_or_else(|e| panic!("unexpected error from `datastore_index_scan_range_bsatn`: {e}"))
};
TableIter::new(iter)
}
pub struct RangedIndexReadOnly<Tbl: Table, IndexType, Idx: Index> {
_marker: PhantomData<(Tbl, IndexType, Idx)>,
}
impl<Tbl: Table, IndexType, Idx: Index> RangedIndexReadOnly<Tbl, IndexType, Idx> {
#[doc(hidden)]
pub const __NEW: Self = Self { _marker: PhantomData };
pub fn filter<B, K>(&self, b: B) -> impl Iterator<Item = Tbl::Row> + use<B, K, Tbl, IndexType, Idx>
where
B: IndexScanRangeBounds<IndexType, K>,
{
filter::<Tbl, Idx, IndexType, B, K>(b)
}
}
const fn is_point_scan<I: Index, B: IndexScanRangeBounds<T, K>, T, K>() -> bool {
B::POINT && B::COLS_PROVIDED == I::NUM_COLS_INDEXED
}
pub trait IndexScanRangeBounds<T, K = ()> {
#[doc(hidden)]
const POINT: bool;
#[doc(hidden)]
const COLS_PROVIDED: usize;
#[doc(hidden)]
fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R;
#[doc(hidden)]
fn get_range_args(&self) -> IndexScanRangeArgs;
}
#[doc(hidden)]
pub struct IndexScanRangeArgs {
data: IterBuf,
prefix_elems: usize,
rstart_idx: usize,
rend_idx: Option<usize>,
}
impl IndexScanRangeArgs {
pub(crate) fn args_for_syscall(&self) -> (&[u8], ColId, &[u8], &[u8]) {
let prefix = &self.data[..self.rstart_idx];
let (rstart, rend) = if let Some(rend_idx) = self.rend_idx {
(&self.data[self.rstart_idx..rend_idx], &self.data[rend_idx..])
} else {
let elem = &self.data[self.rstart_idx..];
(elem, elem)
};
(prefix, ColId::from(self.prefix_elems), rstart, rend)
}
}
macro_rules! impl_index_scan_range_bounds {
(($ColTerminator:ident $(, $ColPrefix:ident)*), ($ArgTerminator:ident $(, $ArgPrefix:ident)*)) => {
impl_index_scan_range_bounds!(@inner_recursion (), ($ColTerminator $(, $ColPrefix)*), ($ArgTerminator $(, $ArgPrefix)*));
impl_index_scan_range_bounds!(($($ColPrefix),*), ($($ArgPrefix),*));
};
((), ()) => {};
(@inner_recursion ($($ColUnused:ident),*), ($ColTerminator:ident $(, $ColPrefix:ident)+), ($ArgTerminator:ident $(, $ArgPrefix:ident)+)) => {
impl_index_scan_range_bounds!(@emit_impl ($($ColUnused),*), ($ColTerminator $(,$ColPrefix)*), ($ArgTerminator $(, $ArgPrefix)*));
impl_index_scan_range_bounds!(@inner_recursion ($($ColUnused,)* $ColTerminator), ($($ColPrefix),*), ($($ArgPrefix),*));
};
(@inner_recursion ($($ColUnused:ident),*), ($ColTerminator:ident), ($ArgTerminator:ident)) => {
impl<
$($ColUnused,)*
$ColTerminator,
Term: IndexScanRangeBoundsTerminator<Arg = $ArgTerminator>,
$ArgTerminator: FilterableValue<Column = $ColTerminator>,
> IndexScanRangeBounds<($ColTerminator, $($ColUnused,)*)> for (Term,) {
const POINT: bool = Term::POINT;
const COLS_PROVIDED: usize = 1;
fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {
IndexScanRangeBounds::<($ColTerminator, $($ColUnused,)*), SingleBound>::with_point_arg(&self.0, run)
}
fn get_range_args(&self) -> IndexScanRangeArgs {
IndexScanRangeBounds::<($ColTerminator, $($ColUnused,)*), SingleBound>::get_range_args(&self.0)
}
}
impl<
$($ColUnused,)*
$ColTerminator,
Term: IndexScanRangeBoundsTerminator<Arg = $ArgTerminator>,
$ArgTerminator: FilterableValue<Column = $ColTerminator>,
> IndexScanRangeBounds<($ColTerminator, $($ColUnused,)*), SingleBound> for Term {
const POINT: bool = Term::POINT;
const COLS_PROVIDED: usize = 1;
fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {
run(&IterBuf::serialize(self.point()).unwrap())
}
fn get_range_args(&self) -> IndexScanRangeArgs {
let mut data = IterBuf::take();
let rend_idx = self.bounds().serialize_into(&mut data);
IndexScanRangeArgs { data, prefix_elems: 0, rstart_idx: 0, rend_idx }
}
}
};
(@emit_impl ($($ColUnused:ident),*), ($ColTerminator:ident $(, $ColPrefix:ident)+), ($ArgTerminator:ident $(, $ArgPrefix:ident)+)) => {
impl<
$($ColUnused,)*
$ColTerminator,
$($ColPrefix,)*
Term: IndexScanRangeBoundsTerminator<Arg = $ArgTerminator>,
$ArgTerminator: FilterableValue<Column = $ColTerminator>,
$($ArgPrefix: FilterableValue<Column = $ColPrefix>,)+
> IndexScanRangeBounds<
($($ColPrefix,)+
$ColTerminator,
$($ColUnused,)*)
> for ($($ArgPrefix,)+ Term,) {
const POINT: bool = Term::POINT;
const COLS_PROVIDED: usize = 1 + impl_index_scan_range_bounds!(@count $($ColPrefix)+);
fn with_point_arg<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {
let mut data = IterBuf::take();
#[allow(non_snake_case)]
let ($($ArgPrefix,)+ term,) = self;
Ok(())
$(.and_then(|()| data.serialize_into($ArgPrefix)))+
.and_then(|()| data.serialize_into(term.point()))
.unwrap();
run(&*data)
}
fn get_range_args(&self) -> IndexScanRangeArgs {
let mut data = IterBuf::take();
let prefix_elems = impl_index_scan_range_bounds!(@count $($ColPrefix)+);
#[allow(non_snake_case)]
let ($($ArgPrefix,)+ term,) = self;
Ok(())
$(.and_then(|()| data.serialize_into($ArgPrefix)))+
.unwrap();
let rstart_idx = data.len();
let rend_idx = term.bounds().serialize_into(&mut data);
IndexScanRangeArgs { data, prefix_elems, rstart_idx, rend_idx }
}
}
};
(@count $($T:ident)*) => {
0 $(+ impl_index_scan_range_bounds!(@drop $T 1))*
};
(@drop $a:tt $b:tt) => { $b };
}
pub struct SingleBound;
impl_index_scan_range_bounds!(
(ColA, ColB, ColC, ColD, ColE, ColF),
(ArgA, ArgB, ArgC, ArgD, ArgE, ArgF)
);
pub trait SequenceTrigger: Sized {
fn is_sequence_trigger(&self) -> bool;
fn decode(reader: &mut &[u8]) -> Result<Self, DecodeError>;
#[inline(always)]
fn maybe_decode_into(&mut self, gen_cols: &mut &[u8]) {
if self.is_sequence_trigger() {
*self = Self::decode(gen_cols).unwrap_or_else(|_| sequence_decode_error())
}
}
}
#[cold]
#[inline(never)]
fn sequence_decode_error() -> ! {
unreachable!("a row was a sequence trigger but there was no generated column for it.")
}
macro_rules! impl_seq_trigger {
($($get:ident($t:ty),)*) => {
$(
impl SequenceTrigger for $t {
#[inline(always)]
fn is_sequence_trigger(&self) -> bool { *self == 0 }
#[inline(always)]
fn decode(reader: &mut &[u8]) -> Result<Self, DecodeError> {
reader.$get()
}
}
)*
};
}
impl_seq_trigger!(
get_u8(u8),
get_i8(i8),
get_u16(u16),
get_i16(i16),
get_u32(u32),
get_i32(i32),
get_u64(u64),
get_i64(i64),
get_u128(u128),
get_i128(i128),
);
impl SequenceTrigger for crate::sats::i256 {
#[inline(always)]
fn is_sequence_trigger(&self) -> bool {
*self == Self::ZERO
}
#[inline(always)]
fn decode(reader: &mut &[u8]) -> Result<Self, DecodeError> {
reader.get_i256()
}
}
impl SequenceTrigger for crate::sats::u256 {
#[inline(always)]
fn is_sequence_trigger(&self) -> bool {
*self == Self::ZERO
}
#[inline(always)]
fn decode(reader: &mut &[u8]) -> Result<Self, DecodeError> {
reader.get_u256()
}
}
#[track_caller]
fn insert<T: Table>(mut row: T::Row, mut buf: IterBuf) -> Result<T::Row, TryInsertError<T>> {
let table_id = T::table_id();
buf.clear();
buf.serialize_into(&row).unwrap();
let res = sys::datastore_insert_bsatn(table_id, &mut buf).map(|gen_cols| {
T::integrate_generated_columns(&mut row, gen_cols);
row
});
res.map_err(|e| {
let err = match e {
sys::Errno::UNIQUE_ALREADY_EXISTS => {
T::UniqueConstraintViolation::get().map(TryInsertError::UniqueConstraintViolation)
}
sys::Errno::AUTO_INC_OVERFLOW => T::AutoIncOverflow::get().map(TryInsertError::AutoIncOverflow),
_ => None,
};
err.unwrap_or_else(|| panic!("unexpected insertion error: {e}"))
})
}
#[track_caller]
fn update<T: Table>(index_id: IndexId, mut row: T::Row, mut buf: IterBuf) -> T::Row {
let table_id = T::table_id();
buf.clear();
buf.serialize_into(&row).unwrap();
let res = sys::datastore_update_bsatn(table_id, index_id, &mut buf).map(|gen_cols| {
T::integrate_generated_columns(&mut row, gen_cols);
row
});
res.unwrap_or_else(|e| panic!("unexpected update error: {e}"))
}
struct TableIter<T: DeserializeOwned> {
inner: sys::RowIter,
reader: Cursor<IterBuf>,
_marker: PhantomData<T>,
}
impl<T: DeserializeOwned> TableIter<T> {
#[inline]
fn new(iter: sys::RowIter) -> Self {
TableIter::new_with_buf(iter, IterBuf::take())
}
#[inline]
fn new_with_buf(iter: sys::RowIter, mut buf: IterBuf) -> Self {
buf.clear();
TableIter {
inner: iter,
reader: Cursor::new(buf),
_marker: PhantomData,
}
}
fn is_exhausted(&self) -> bool {
(&self.reader).remaining() == 0 && self.inner.is_exhausted()
}
}
impl<T: DeserializeOwned> Iterator for TableIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
loop {
if (&self.reader).remaining() > 0 {
let row = bsatn::from_reader(&mut &self.reader).expect("Failed to decode row!");
return Some(row);
}
if self.inner.is_exhausted() {
return None;
}
self.reader.buf.clear();
self.reader.pos.set(0);
self.inner.read(&mut self.reader.buf);
}
}
}