Skip to content

Commit 1ffff46

Browse files
committed
Implement btree api on new abi
1 parent 739fd3c commit 1ffff46

File tree

2 files changed

+136
-54
lines changed

2 files changed

+136
-54
lines changed

crates/bindings-macro/src/lib.rs

Lines changed: 100 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,98 @@ 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 doc_columns = cols
636+
.iter()
637+
.map(|col| {
638+
format!(
639+
"- [`{ident}`][{row_type_ident}#structfield.{ident}]: [`{ty}`]\n",
640+
ident = col.field.ident.unwrap(),
641+
ty = col.ty.to_token_stream()
642+
)
643+
})
644+
.collect::<String>();
645+
let doc = format!(
646+
"Gets the `{index_ident}` [`BTreeIndex`][spacetimedb::BTreeIndex] as defined \
647+
on this table. \n\
648+
\n\
649+
This B-tree index is defined on the following columns, in order:\n\
650+
{doc_columns}"
651+
);
652+
quote! {
653+
#[doc = #doc]
654+
#vis fn #index_ident(&self) -> spacetimedb::BTreeIndex<Self, (#(#col_tys,)*), __indices::#index_ident> {
597655
spacetimedb::BTreeIndex::__new()
598656
}
599-
};
657+
}
658+
}
659+
}
660+
}
600661

601-
(algo, accessor)
662+
fn marker_type(&self) -> TokenStream {
663+
let index_ident = self.accessor_name;
664+
let index_name = &self.index_name;
665+
quote! {
666+
pub struct #index_ident;
667+
impl spacetimedb::table::Index for #index_ident {
668+
fn index_id() -> spacetimedb::table::IndexId {
669+
static INDEX_ID: std::sync::OnceLock<spacetimedb::table::IndexId> = std::sync::OnceLock::new();
670+
*INDEX_ID.get_or_init(|| {
671+
spacetimedb::sys::index_id_from_name(#index_name).unwrap()
672+
})
673+
}
602674
}
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))
675+
}
610676
}
611677
}
612678

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

822888
let row_type = quote!(#original_struct_ident);
823889

824-
let (indexes, index_accessors) = args
890+
let indices = args
825891
.indices
826892
.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<_>>();
893+
.map(|index| index.validate(&table_name, &columns))
894+
.collect::<syn::Result<Vec<_>>>()?;
895+
896+
let index_descs = indices.iter().map(|index| index.desc());
897+
let index_accessors = indices.iter().map(|index| index.accessor(vis, original_struct_ident));
898+
let index_marker_types = indices.iter().map(|index| index.marker_type());
834899

835900
let unique_field_accessors = unique_columns.iter().map(|unique| {
836901
let column_index = unique.index;
@@ -907,7 +972,7 @@ fn table_impl(mut args: TableArgs, mut item: MutItem<syn::DeriveInput>) -> syn::
907972
// the default value if not specified is Private
908973
#(const TABLE_ACCESS: spacetimedb::table::TableAccess = #table_access;)*
909974
const UNIQUE_COLUMNS: &'static [u16] = &[#(#unique_col_ids),*];
910-
const INDEXES: &'static [spacetimedb::table::IndexDesc<'static>] = &[#(#indexes),*];
975+
const INDEXES: &'static [spacetimedb::table::IndexDesc<'static>] = &[#(#index_descs),*];
911976
#(const PRIMARY_KEY: Option<u16> = Some(#primary_col_id);)*
912977
const SEQUENCES: &'static [u16] = &[#(#sequence_col_ids),*];
913978
#(const SCHEDULED_REDUCER_NAME: Option<&'static str> = Some(<#scheduled_reducer_ident as spacetimedb::rt::ReducerInfo>::NAME);)*
@@ -1006,6 +1071,11 @@ fn table_impl(mut args: TableArgs, mut item: MutItem<syn::DeriveInput>) -> syn::
10061071

10071072
#tabletype_impl
10081073

1074+
#[allow(non_camel_case_types)]
1075+
mod __indices {
1076+
#(#index_marker_types)*
1077+
}
1078+
10091079
#describe_table_func
10101080
};
10111081

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)