Skip to content

Commit 3a31c33

Browse files
authored
Add delete_by_{nonunique_column} method to #[spacetimedb(table)] (#859)
- Refactor `bindings::query::delete_by_field` to require `FilterableValue` and return `u32` count of deleted rows. - Add `bindings::query::delete_by_unique_field` with the previous behavior of `delete_by_field`, i.e. requiring `UniqueValue` and returning `bool` with `true = deleted`. This is implemented in terms of `delete_by_field`. - Macro-generated unique filter funcs call `delete_by_unique_field` rather than `delete_by_field`. - The table macro now generates non-unique delete functions alongside the existing non-unique filter functions for non-unique fields with primitive types. - Add uses of `::delete_by_{field}` for both unique and non-unique columns to the `rust-wasm-test` module. Note that this PR does not include any host-side changes; the diff is localized to the `bindings` and `bindings-macro` crates.
1 parent 4c63b82 commit 3a31c33

File tree

3 files changed

+64
-28
lines changed

3 files changed

+64
-28
lines changed

crates/bindings-macro/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ fn spacetimedb_tabletype_impl(item: syn::DeriveInput) -> syn::Result<TokenStream
633633

634634
unique_delete_funcs.push(quote! {
635635
#vis fn #delete_func_ident(#column_ident: &#column_type) -> bool {
636-
spacetimedb::query::delete_by_field::<Self, #column_type, #column_index>(#column_ident)
636+
spacetimedb::query::delete_by_unique_field::<Self, #column_type, #column_index>(#column_ident)
637637
}
638638
});
639639
}
@@ -645,6 +645,7 @@ fn spacetimedb_tabletype_impl(item: syn::DeriveInput) -> syn::Result<TokenStream
645645
let column_index = column.index;
646646

647647
let filter_func_ident = format_ident!("filter_by_{}", column_ident);
648+
let delete_func_ident = format_ident!("delete_by_{}", column_ident);
648649

649650
let skip = if let syn::Type::Path(p) = column_type {
650651
// TODO: this is janky as heck
@@ -665,6 +666,9 @@ fn spacetimedb_tabletype_impl(item: syn::DeriveInput) -> syn::Result<TokenStream
665666
#vis fn #filter_func_ident<'a>(#column_ident: &#column_type) -> impl Iterator<Item = Self> {
666667
spacetimedb::query::filter_by_field::<Self, #column_type, #column_index>(#column_ident)
667668
}
669+
#vis fn #delete_func_ident(#column_ident: &#column_type) -> u32 {
670+
spacetimedb::query::delete_by_field::<Self, #column_type, #column_index>(#column_ident)
671+
}
668672
})
669673
});
670674
let non_primary_filter_func = non_primary_filter_func.collect::<Vec<_>>();

crates/bindings/src/lib.rs

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -505,30 +505,36 @@ pub mod query {
505505
}
506506
}
507507

508+
/// Deletes all rows of `Table` where the column at `COL_IDX` matches `val`,
509+
/// as defined by decoding to an `AlgebraicValue`
510+
/// according to the column's schema and then `Ord for AlgebraicValue`.
511+
///
512+
/// Returns the number of deleted rows.
513+
///
514+
/// **NOTE:** Do not use directly.
515+
/// This is exposed as `delete_by_{$field_name}` on types with `#[spacetimedb(table)]`
516+
/// where the field does not have a unique constraint.
517+
#[doc(hidden)]
518+
pub fn delete_by_field<Table: TableType, T: FilterableValue, const COL_IDX: u8>(val: &T) -> u32 {
519+
delete_by_col_eq(Table::table_id(), COL_IDX, val)
520+
// TODO: Returning `Err` here was supposed to signify an error,
521+
// but it can also return `Err(_)` when there is nothing to delete.
522+
.unwrap_or(0)
523+
}
524+
508525
/// Deletes the row of `Table` where the column at `COL_IDX` matches `val`,
509526
/// as defined by decoding to an `AlgebraicValue`
510527
/// according to the column's schema and then `Ord for AlgebraicValue`.
511528
///
512529
/// Returns whether any rows were deleted.
513530
///
514531
/// **NOTE:** Do not use directly.
515-
/// This is exposed as `delete_by_{$field_name}` on types with `#[spacetimedb(table)]`.
516-
#[doc(hidden)]
517-
pub fn delete_by_field<Table: TableType, T: UniqueValue, const COL_IDX: u8>(val: &T) -> bool {
518-
let result = delete_by_col_eq(Table::table_id(), COL_IDX, val);
519-
match result {
520-
Err(_) => {
521-
// TODO: Returning here was supposed to signify an error,
522-
// but it can also return `Err(_)` when there is nothing to delete.
523-
//spacetimedb::println!("Internal server error on equatable type: {}", #primary_key_tuple_type_str);
524-
false
525-
}
526-
// Should never be `> 1`.
527-
Ok(count) => {
528-
debug_assert!(count <= 1);
529-
count > 0
530-
}
531-
}
532+
/// This is exposed as `delete_by_{$field_name}` on types with `#[spacetimedb(table)]`
533+
/// where the field has a unique constraint.
534+
pub fn delete_by_unique_field<Table: TableType, T: UniqueValue, const COL_IDX: u8>(val: &T) -> bool {
535+
let count = delete_by_field::<Table, T, COL_IDX>(val);
536+
debug_assert!(count <= 1);
537+
count > 0
532538
}
533539

534540
/// Updates the row of `Table`, where the column at `COL_IDX` matches `old`, to be `new` instead.

modules/rust-wasm-test/src/lib.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
#![allow(clippy::disallowed_names)]
2-
use spacetimedb::{
3-
delete_by_col_eq, query, spacetimedb, AlgebraicValue, Deserialize, ReducerContext, SpacetimeType, TableType,
4-
Timestamp,
5-
};
2+
use spacetimedb::{query, spacetimedb, Deserialize, ReducerContext, SpacetimeType, Timestamp};
63
use spacetimedb_lib::bsatn;
74

85
#[spacetimedb(table)]
@@ -94,7 +91,6 @@ pub fn test(ctx: ReducerContext, arg: TestAlias, arg2: TestB, arg3: TestC) -> an
9491
TestC::Foo => log::info!("Foo"),
9592
TestC::Bar => log::info!("Bar"),
9693
}
97-
let table_id = TestA::table_id();
9894
for i in 0..1000 {
9995
TestA::insert(TestA {
10096
x: i + arg.x,
@@ -103,15 +99,25 @@ pub fn test(ctx: ReducerContext, arg: TestAlias, arg2: TestB, arg3: TestC) -> an
10399
});
104100
}
105101

106-
let row_count = TestA::iter().count();
102+
let row_count_before_delete = TestA::iter().count();
107103

108-
log::info!("Row count before delete: {:?}", row_count);
104+
log::info!("Row count before delete: {:?}", row_count_before_delete);
109105

106+
let mut num_deleted = 0;
110107
for row in 5..10 {
111-
delete_by_col_eq(table_id, 0, &AlgebraicValue::U32(row))?;
108+
num_deleted += TestA::delete_by_x(&row);
112109
}
113110

114-
let row_count = TestA::iter().count();
111+
let row_count_after_delete = TestA::iter().count();
112+
113+
if row_count_before_delete != row_count_after_delete + num_deleted as usize {
114+
log::error!(
115+
"Started with {} rows, deleted {}, and wound up with {} rows... huh?",
116+
row_count_before_delete,
117+
num_deleted,
118+
row_count_after_delete,
119+
);
120+
}
115121

116122
match TestE::insert(TestE {
117123
id: 0,
@@ -121,7 +127,7 @@ pub fn test(ctx: ReducerContext, arg: TestAlias, arg2: TestB, arg3: TestC) -> an
121127
Err(err) => log::info!("Error: {:?}", err),
122128
}
123129

124-
log::info!("Row count after delete: {:?}", row_count);
130+
log::info!("Row count after delete: {:?}", row_count_after_delete);
125131

126132
let other_row_count = query!(|row: TestA| row.x >= 0 && row.x <= u32::MAX).count();
127133

@@ -150,6 +156,26 @@ pub fn add_player(name: String) -> Result<(), String> {
150156
Ok(())
151157
}
152158

159+
#[spacetimedb(reducer)]
160+
pub fn delete_player(id: u64) -> Result<(), String> {
161+
if TestE::delete_by_id(&id) {
162+
Ok(())
163+
} else {
164+
Err(format!("No TestE row with id {}", id))
165+
}
166+
}
167+
168+
#[spacetimedb(reducer)]
169+
pub fn delete_players_by_name(name: String) -> Result<(), String> {
170+
match TestE::delete_by_name(&name) {
171+
0 => Err(format!("No TestE row with name {:?}", name)),
172+
num_deleted => {
173+
log::info!("Deleted {} player(s) with name {:?}", num_deleted, name);
174+
Ok(())
175+
}
176+
}
177+
}
178+
153179
#[spacetimedb(connect)]
154180
fn on_connect(_ctx: ReducerContext) {}
155181

0 commit comments

Comments
 (0)