Skip to content

Commit 3b6f8a2

Browse files
committed
Implement btree api on new abi
1 parent c8f8e35 commit 3b6f8a2

File tree

2 files changed

+135
-54
lines changed

2 files changed

+135
-54
lines changed

crates/bindings-macro/src/lib.rs

Lines changed: 99 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use heck::ToSnakeCase;
1515
use module::{derive_deserialize, derive_satstype, derive_serialize};
1616
use proc_macro::TokenStream as StdTokenStream;
1717
use proc_macro2::{Span, TokenStream};
18-
use quote::{format_ident, quote, quote_spanned};
18+
use quote::{format_ident, quote, quote_spanned, ToTokens};
1919
use std::collections::HashMap;
2020
use std::time::Duration;
2121
use syn::ext::IdentExt;
@@ -567,12 +567,8 @@ impl IndexArg {
567567
Ok(IndexArg { kind, name })
568568
}
569569

570-
fn to_desc_and_accessor(
571-
&self,
572-
index_index: u32,
573-
cols: &[Column],
574-
) -> Result<(TokenStream, TokenStream), syn::Error> {
575-
let (algo, accessor) = match &self.kind {
570+
fn validate<'a>(&'a self, table_name: &str, cols: &'a [Column<'a>]) -> syn::Result<ValidatedIndex<'_>> {
571+
let kind = match &self.kind {
576572
IndexType::BTree { columns } => {
577573
let cols = columns
578574
.iter()
@@ -585,28 +581,97 @@ impl IndexArg {
585581
})
586582
.collect::<syn::Result<Vec<_>>>()?;
587583

584+
ValidatedIndexType::BTree { cols }
585+
}
586+
};
587+
let index_name = match &kind {
588+
ValidatedIndexType::BTree { cols } => ([table_name, "btree"].into_iter())
589+
.chain(cols.iter().map(|col| col.field.name.as_deref().unwrap()))
590+
.collect::<Vec<_>>()
591+
.join("_"),
592+
};
593+
Ok(ValidatedIndex {
594+
index_name,
595+
accessor_name: &self.name,
596+
kind,
597+
})
598+
}
599+
}
600+
601+
struct ValidatedIndex<'a> {
602+
index_name: String,
603+
accessor_name: &'a Ident,
604+
kind: ValidatedIndexType<'a>,
605+
}
606+
607+
enum ValidatedIndexType<'a> {
608+
BTree { cols: Vec<&'a Column<'a>> },
609+
}
610+
611+
impl ValidatedIndex<'_> {
612+
fn desc(&self) -> TokenStream {
613+
let algo = match &self.kind {
614+
ValidatedIndexType::BTree { cols } => {
588615
let col_ids = cols.iter().map(|col| col.index);
589-
let algo = quote!(spacetimedb::table::IndexAlgo::BTree {
616+
quote!(spacetimedb::table::IndexAlgo::BTree {
590617
columns: &[#(#col_ids),*]
591-
});
618+
})
619+
}
620+
};
621+
let index_name = &self.index_name;
622+
let accessor_name = ident_to_litstr(self.accessor_name);
623+
quote!(spacetimedb::table::IndexDesc {
624+
name: #index_name,
625+
accessor_name: #accessor_name,
626+
algo: #algo,
627+
})
628+
}
592629

593-
let index_ident = &self.name;
630+
fn accessor(&self, vis: &syn::Visibility, row_type_ident: &Ident) -> TokenStream {
631+
match &self.kind {
632+
ValidatedIndexType::BTree { cols } => {
633+
let index_ident = self.accessor_name;
594634
let col_tys = cols.iter().map(|col| col.ty);
595-
let accessor = quote! {
596-
fn #index_ident(&self) -> spacetimedb::BTreeIndex<Self, (#(#col_tys,)*), #index_index> {
635+
let mut doc = format!(
636+
"Gets the `{index_ident}` [`BTreeIndex`][spacetimedb::BTreeIndex] as defined \
637+
on this table. \n\
638+
\n\
639+
This B-tree index is defined on the following columns, in order:\n"
640+
);
641+
for col in cols {
642+
use std::fmt::Write;
643+
write!(
644+
doc,
645+
"- [`{ident}`][{row_type_ident}#structfield.{ident}]: [`{ty}`]\n",
646+
ident = col.field.ident.unwrap(),
647+
ty = col.ty.to_token_stream()
648+
)
649+
.unwrap();
650+
}
651+
quote! {
652+
#[doc = #doc]
653+
#vis fn #index_ident(&self) -> spacetimedb::BTreeIndex<Self, (#(#col_tys,)*), __indices::#index_ident> {
597654
spacetimedb::BTreeIndex::__new()
598655
}
599-
};
656+
}
657+
}
658+
}
659+
}
600660

601-
(algo, accessor)
661+
fn marker_type(&self) -> TokenStream {
662+
let index_ident = self.accessor_name;
663+
let index_name = &self.index_name;
664+
quote! {
665+
pub struct #index_ident;
666+
impl spacetimedb::table::Index for #index_ident {
667+
fn index_id() -> spacetimedb::table::IndexId {
668+
static INDEX_ID: std::sync::OnceLock<spacetimedb::table::IndexId> = std::sync::OnceLock::new();
669+
*INDEX_ID.get_or_init(|| {
670+
spacetimedb::sys::index_id_from_name(#index_name).unwrap()
671+
})
672+
}
602673
}
603-
};
604-
let accessor_name = ident_to_litstr(&self.name);
605-
let desc = quote!(spacetimedb::table::IndexDesc {
606-
accessor_name: #accessor_name,
607-
algo: #algo,
608-
});
609-
Ok((desc, accessor))
674+
}
610675
}
611676
}
612677

@@ -821,16 +886,15 @@ fn table_impl(mut args: TableArgs, mut item: MutItem<syn::DeriveInput>) -> syn::
821886

822887
let row_type = quote!(#original_struct_ident);
823888

824-
let (indexes, index_accessors) = args
889+
let indices = args
825890
.indices
826891
.iter()
827-
.enumerate()
828-
.map(|(i, index)| index.to_desc_and_accessor(i as u32, &columns))
829-
// TODO: stabilized in 1.79
830-
// .collect::<syn::Result<(Vec<_>, Vec<_>)>>()?;
831-
.collect::<syn::Result<Vec<_>>>()?
832-
.into_iter()
833-
.unzip::<_, _, Vec<_>, Vec<_>>();
892+
.map(|index| index.validate(&table_name, &columns))
893+
.collect::<syn::Result<Vec<_>>>()?;
894+
895+
let index_descs = indices.iter().map(|index| index.desc());
896+
let index_accessors = indices.iter().map(|index| index.accessor(vis, original_struct_ident));
897+
let index_marker_types = indices.iter().map(|index| index.marker_type());
834898

835899
let unique_field_accessors = unique_columns.iter().map(|unique| {
836900
let column_index = unique.index;
@@ -907,7 +971,7 @@ fn table_impl(mut args: TableArgs, mut item: MutItem<syn::DeriveInput>) -> syn::
907971
// the default value if not specified is Private
908972
#(const TABLE_ACCESS: spacetimedb::table::TableAccess = #table_access;)*
909973
const UNIQUE_COLUMNS: &'static [u16] = &[#(#unique_col_ids),*];
910-
const INDEXES: &'static [spacetimedb::table::IndexDesc<'static>] = &[#(#indexes),*];
974+
const INDEXES: &'static [spacetimedb::table::IndexDesc<'static>] = &[#(#index_descs),*];
911975
#(const PRIMARY_KEY: Option<u16> = Some(#primary_col_id);)*
912976
const SEQUENCES: &'static [u16] = &[#(#sequence_col_ids),*];
913977
#(const SCHEDULED_REDUCER_NAME: Option<&'static str> = Some(<#scheduled_reducer_ident as spacetimedb::rt::ReducerInfo>::NAME);)*
@@ -1006,6 +1070,11 @@ fn table_impl(mut args: TableArgs, mut item: MutItem<syn::DeriveInput>) -> syn::
10061070

10071071
#tabletype_impl
10081072

1073+
#[allow(non_camel_case_types)]
1074+
mod __indices {
1075+
#(#index_marker_types)*
1076+
}
1077+
10091078
#describe_table_func
10101079
};
10111080

crates/bindings/src/table.rs

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use std::marker::PhantomData;
44
use std::{fmt, ops};
55

66
use spacetimedb_lib::buffer::{BufReader, Cursor};
7+
78
pub use spacetimedb_lib::db::raw_def::v9::TableAccess;
8-
use spacetimedb_primitives::ColId;
9+
pub use spacetimedb_primitives::{ColId, IndexId};
910

1011
use crate::{bsatn, sys, DeserializeOwned, IterBuf, Serialize, SpacetimeType, TableId};
1112

@@ -132,6 +133,7 @@ pub trait TableInternal: Sized {
132133
/// Describe a named index with an index type over a set of columns identified by their IDs.
133134
#[derive(Clone, Copy)]
134135
pub struct IndexDesc<'a> {
136+
pub name: &'a str,
135137
pub accessor_name: &'a str,
136138
pub algo: IndexAlgo<'a>,
137139
}
@@ -337,11 +339,15 @@ where
337339
}
338340
}
339341

340-
pub struct BTreeIndex<Tbl: Table, IndexType, const INDEX_INDEX: u32> {
341-
_marker: PhantomData<(Tbl, IndexType)>,
342+
pub trait Index {
343+
fn index_id() -> IndexId;
344+
}
345+
346+
pub struct BTreeIndex<Tbl: Table, IndexType, Idx: Index> {
347+
_marker: PhantomData<(Tbl, IndexType, Idx)>,
342348
}
343349

344-
impl<Tbl: Table, IndexType, const INDEX_INDEX: u32> BTreeIndex<Tbl, IndexType, INDEX_INDEX> {
350+
impl<Tbl: Table, IndexType, Idx: Index> BTreeIndex<Tbl, IndexType, Idx> {
345351
#[doc(hidden)]
346352
pub fn __new() -> Self {
347353
Self { _marker: PhantomData }
@@ -353,13 +359,16 @@ impl<Tbl: Table, IndexType, const INDEX_INDEX: u32> BTreeIndex<Tbl, IndexType, I
353359
/// - A value for the first indexed column.
354360
/// - A range of values for the first indexed column.
355361
/// - A tuple of values for any prefix of the indexed columns, optionally terminated by a range for the next.
356-
pub fn filter<B: BTreeIndexBounds<IndexType, K>, K>(&self, b: B) -> impl Iterator<Item = Tbl::Row> {
362+
pub fn filter<B, K>(&self, b: B) -> impl Iterator<Item = Tbl::Row>
363+
where
364+
B: BTreeIndexBounds<IndexType, K>,
365+
{
366+
let index_id = Idx::index_id();
357367
let args = b.get_args();
358368
let (prefix, prefix_elems, rstart, rend) = args.args_for_syscall();
359-
#[allow(unreachable_code)]
360-
TableIter::new(todo!(
361-
"once implemented: datastore_btree_scan_bsatn({prefix:?}, {prefix_elems:?}, {rstart:?}, {rend:?})"
362-
))
369+
let iter = sys::datastore_btree_scan_bsatn(index_id, prefix, prefix_elems, rstart, rend)
370+
.unwrap_or_else(|e| panic!("unexpected error from datastore_btree_scan_bsatn: {e}"));
371+
TableIter::new(iter)
363372
}
364373

365374
/// Deletes all rows in the database state where the indexed column(s) match the bounds `b`.
@@ -371,10 +380,16 @@ impl<Tbl: Table, IndexType, const INDEX_INDEX: u32> BTreeIndex<Tbl, IndexType, I
371380
///
372381
/// May panic if deleting any one of the rows would violate a constraint,
373382
/// though as of proposing no such constraints exist.
374-
pub fn delete<B: BTreeIndexBounds<IndexType, K>, K>(&self, b: B) -> u64 {
383+
pub fn delete<B, K>(&self, b: B) -> u64
384+
where
385+
B: BTreeIndexBounds<IndexType, K>,
386+
{
387+
let index_id = Idx::index_id();
375388
let args = b.get_args();
376389
let (prefix, prefix_elems, rstart, rend) = args.args_for_syscall();
377-
todo!("once implemented: datastore_delete_by_btree_scan_bsatn({prefix:?}, {prefix_elems:?}, {rstart:?}, {rend:?})")
390+
sys::datastore_delete_by_btree_scan_bsatn(index_id, prefix, prefix_elems, rstart, rend)
391+
.unwrap_or_else(|e| panic!("unexpected error from datastore_delete_by_btree_scan_bsatn: {e}"))
392+
.into()
378393
}
379394
}
380395

@@ -394,13 +409,14 @@ pub struct BTreeScanArgs {
394409

395410
impl BTreeScanArgs {
396411
pub(crate) fn args_for_syscall(&self) -> (&[u8], ColId, &[u8], &[u8]) {
397-
let len = self.data.len();
398-
(
399-
&self.data[..self.rstart_idx],
400-
ColId::from(self.prefix_elems),
401-
&self.data[self.rstart_idx..self.rend_idx.unwrap_or(len)],
402-
&self.data[self.rend_idx.unwrap_or(self.rstart_idx)..],
403-
)
412+
let prefix = &self.data[..self.rstart_idx];
413+
let (rstart, rend) = if let Some(rend_idx) = self.rend_idx {
414+
(&self.data[self.rstart_idx..rend_idx], &self.data[rend_idx..])
415+
} else {
416+
let elem = &self.data[self.rstart_idx..];
417+
(elem, elem)
418+
};
419+
(prefix, ColId::from(self.prefix_elems), rstart, rend)
404420
}
405421
}
406422

@@ -464,14 +480,10 @@ impl<T: Serialize> TermBound<&T> {
464480
TermBound::Single(elem) => (elem, None),
465481
TermBound::Range(start, end) => (start, Some(end)),
466482
};
467-
let serialize_bound = |_buf: &mut Vec<u8>, _bound: &ops::Bound<&T>| {
468-
// bsatn::to_writer(buf, bound).unwrap();
469-
todo!();
470-
};
471-
serialize_bound(buf, start);
483+
bsatn::to_writer(buf, start).unwrap();
472484
end.map(|end| {
473485
let rend_idx = buf.len();
474-
serialize_bound(buf, end);
486+
bsatn::to_writer(buf, end).unwrap();
475487
rend_idx
476488
})
477489
}

0 commit comments

Comments
 (0)