Skip to content

Commit db29ded

Browse files
authored
WASM ABI: add datastore_delete_by_btree_scan_bsatn (#1704)
1 parent 14e5098 commit db29ded

File tree

6 files changed

+195
-5
lines changed

6 files changed

+195
-5
lines changed

crates/bindings-sys/src/lib.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,54 @@ pub mod raw {
214214
out: *mut u32,
215215
) -> u16;
216216

217+
/// Deletes all rows found in the index identified by `index_id`,
218+
/// according to the:
219+
/// - `prefix = prefix_ptr[..prefix_len]`,
220+
/// - `rstart = rstart_ptr[..rstart_len]`,
221+
/// - `rend = rend_ptr[..rend_len]`,
222+
/// in WASM memory.
223+
///
224+
/// This syscall will delete all the rows found by
225+
/// [`datastore_btree_scan_bsatn`] with the same arguments passed,
226+
/// including `prefix_elems`.
227+
/// See `datastore_btree_scan_bsatn` for details.
228+
///
229+
/// The number of rows deleted is written to the WASM pointer `out`.
230+
///
231+
/// # Traps
232+
///
233+
/// Traps if:
234+
/// - `prefix_elems > 0`
235+
/// and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).
236+
/// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.
237+
/// - `rend` is NULL or `rend` is not in bounds of WASM memory.
238+
/// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.
239+
///
240+
/// # Errors
241+
///
242+
/// Returns an error:
243+
///
244+
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
245+
/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
246+
/// - `WRONG_INDEX_ALGO` if the index is not a btree index.
247+
/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
248+
/// a `prefix_elems` number of `AlgebraicValue`
249+
/// typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
250+
/// Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
251+
/// where the inner `AlgebraicValue`s are
252+
/// typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
253+
pub fn _datastore_delete_by_btree_scan_bsatn(
254+
index_id: IndexId,
255+
prefix_ptr: *const u8,
256+
prefix_len: usize,
257+
prefix_elems: ColId,
258+
rstart_ptr: *const u8, // Bound<AlgebraicValue>
259+
rstart_len: usize,
260+
rend_ptr: *const u8, // Bound<AlgebraicValue>
261+
rend_len: usize,
262+
out: *mut u32,
263+
) -> u16;
264+
217265
/// Deletes those rows, in the table identified by `table_id`,
218266
/// that match any row in the byte string `rel = rel_ptr[..rel_len]` in WASM memory.
219267
///
@@ -898,6 +946,53 @@ pub fn datastore_btree_scan_bsatn(
898946
Ok(RowIter { raw })
899947
}
900948

949+
/// Deletes all rows found in the index identified by `index_id`,
950+
/// according to the `prefix`, `rstart`, and `rend`.
951+
///
952+
/// This syscall will delete all the rows found by
953+
/// [`datastore_btree_scan_bsatn`] with the same arguments passed,
954+
/// including `prefix_elems`.
955+
/// See `datastore_btree_scan_bsatn` for details.
956+
///
957+
/// The number of rows deleted is returned on success.
958+
///
959+
/// # Errors
960+
///
961+
/// Returns an error:
962+
///
963+
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
964+
/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
965+
/// - `WRONG_INDEX_ALGO` if the index is not a btree index.
966+
/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
967+
/// a `prefix_elems` number of `AlgebraicValue`
968+
/// typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
969+
/// Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
970+
/// where the inner `AlgebraicValue`s are
971+
/// typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
972+
pub fn datastore_delete_by_btree_scan_bsatn(
973+
index_id: IndexId,
974+
prefix: &[u8],
975+
prefix_elems: ColId,
976+
rstart: &[u8],
977+
rend: &[u8],
978+
) -> Result<u32, Errno> {
979+
unsafe {
980+
call(|out| {
981+
raw::_datastore_delete_by_btree_scan_bsatn(
982+
index_id,
983+
prefix.as_ptr(),
984+
prefix.len(),
985+
prefix_elems,
986+
rstart.as_ptr(),
987+
rstart.len(),
988+
rend.as_ptr(),
989+
rend.len(),
990+
out,
991+
)
992+
})
993+
}
994+
}
995+
901996
/// Iterate through a table, filtering by an encoded `spacetimedb_lib::filter::Expr`.
902997
///
903998
/// # Errors

crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ impl MutTxId {
514514
prefix_elems: ColId,
515515
rstart: &[u8],
516516
rend: &[u8],
517-
) -> Result<impl Iterator<Item = RowRef<'a>>> {
517+
) -> Result<(TableId, impl Iterator<Item = RowRef<'a>>)> {
518518
// Extract the table and index type for the tx state.
519519
let (table_id, col_list, tx_idx_key_type) = self
520520
.get_table_and_index_type(index_id)
@@ -548,15 +548,16 @@ impl MutTxId {
548548
}
549549
}
550550
}
551-
Ok(match commit_iter {
551+
let iter = match commit_iter {
552552
None => Choice::A(tx_iter),
553553
Some(commit_iter) => match self.tx_state.delete_tables.get(&table_id) {
554554
None => Choice::B(tx_iter.chain(commit_iter)),
555555
Some(tx_dels) => {
556556
Choice::C(tx_iter.chain(commit_iter.filter(move |row| !tx_dels.contains(&row.pointer()))))
557557
}
558558
},
559-
})
559+
};
560+
Ok((table_id, iter))
560561
}
561562

562563
/// Translate `index_id` to the table id, the column list and index key type.

crates/core/src/db/relational_db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,7 @@ impl RelationalDB {
10731073
prefix_elems: ColId,
10741074
rstart: &[u8],
10751075
rend: &[u8],
1076-
) -> Result<impl Iterator<Item = RowRef<'a>>, DBError> {
1076+
) -> Result<(TableId, impl Iterator<Item = RowRef<'a>>), DBError> {
10771077
tx.btree_scan(index_id, prefix, prefix_elems, rstart, rend)
10781078
}
10791079

crates/core/src/host/instance_env.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,27 @@ impl InstanceEnv {
175175
Ok(stdb.delete(tx, table_id, rows_to_delete))
176176
}
177177

178+
#[tracing::instrument(skip_all)]
179+
pub fn datastore_delete_by_btree_scan_bsatn(
180+
&self,
181+
index_id: IndexId,
182+
prefix: &[u8],
183+
prefix_elems: ColId,
184+
rstart: &[u8],
185+
rend: &[u8],
186+
) -> Result<u32, NodesError> {
187+
let stdb = &*self.dbic.relational_db;
188+
let tx = &mut *self.tx.get()?;
189+
190+
// Find all rows in the table to delete.
191+
let (table_id, iter) = stdb.btree_scan(tx, index_id, prefix, prefix_elems, rstart, rend)?;
192+
// Re. `SmallVec`, `delete_by_field` only cares about 1 element, so optimize for that.
193+
let rows_to_delete = iter.map(|row_ref| row_ref.pointer()).collect::<SmallVec<[_; 1]>>();
194+
195+
// Delete them and count how many we deleted.
196+
Ok(stdb.delete(tx, table_id, rows_to_delete))
197+
}
198+
178199
/// Deletes all rows in the table identified by `table_id`
179200
/// where the rows match one in `relation`
180201
/// which is a bsatn encoding of `Vec<ProductValue>`.
@@ -289,7 +310,7 @@ impl InstanceEnv {
289310
let stdb = &*self.dbic.relational_db;
290311
let tx = &mut *self.tx.get()?;
291312

292-
let iter = stdb.btree_scan(tx, index_id, prefix, prefix_elems, rstart, rend)?;
313+
let (_, iter) = stdb.btree_scan(tx, index_id, prefix, prefix_elems, rstart, rend)?;
293314
let chunks = ChunkedWriter::collect_iter(iter);
294315
Ok(chunks)
295316
}

crates/core/src/host/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ pub enum AbiCall {
149149
RowIterBsatnAdvance,
150150
RowIterBsatnClose,
151151
DatastoreInsertBsatn,
152+
DatastoreDeleteByBtreeScanBsatn,
152153
DatastoreDeleteAllByEqBsatn,
153154
BytesSourceRead,
154155
BytesSinkWrite,

crates/core/src/host/wasmtime/wasm_instance_env.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,78 @@ impl WasmInstanceEnv {
824824
})
825825
}
826826

827+
/// Deletes all rows found in the index identified by `index_id`,
828+
/// according to the:
829+
/// - `prefix = prefix_ptr[..prefix_len]`,
830+
/// - `rstart = rstart_ptr[..rstart_len]`,
831+
/// - `rend = rend_ptr[..rend_len]`,
832+
/// in WASM memory.
833+
///
834+
/// This syscall will delete all the rows found by
835+
/// [`datastore_btree_scan_bsatn`] with the same arguments passed,
836+
/// including `prefix_elems`.
837+
/// See `datastore_btree_scan_bsatn` for details.
838+
///
839+
/// The number of rows deleted is written to the WASM pointer `out`.
840+
///
841+
/// # Traps
842+
///
843+
/// Traps if:
844+
/// - `prefix_elems > 0`
845+
/// and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).
846+
/// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.
847+
/// - `rend` is NULL or `rend` is not in bounds of WASM memory.
848+
/// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.
849+
///
850+
/// # Errors
851+
///
852+
/// Returns an error:
853+
///
854+
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
855+
/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
856+
/// - `WRONG_INDEX_ALGO` if the index is not a btree index.
857+
/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
858+
/// a `prefix_elems` number of `AlgebraicValue`
859+
/// typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
860+
/// Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
861+
/// where the inner `AlgebraicValue`s are
862+
/// typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
863+
pub fn datastore_delete_by_btree_scan_bsatn(
864+
caller: Caller<'_, Self>,
865+
index_id: u32,
866+
prefix_ptr: WasmPtr<u8>,
867+
prefix_len: u32,
868+
prefix_elems: u32,
869+
rstart_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>
870+
rstart_len: u32,
871+
rend_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>
872+
rend_len: u32,
873+
out: WasmPtr<u32>,
874+
) -> RtResult<u32> {
875+
Self::cvt_ret(caller, AbiCall::DatastoreDeleteByBtreeScanBsatn, out, |caller| {
876+
let prefix_elems = Self::convert_u32_to_col_id(prefix_elems)?;
877+
878+
let (mem, env) = Self::mem_env(caller);
879+
// Read the prefix and range start & end from WASM memory.
880+
let prefix = if prefix_elems.idx() == 0 {
881+
&[]
882+
} else {
883+
mem.deref_slice(prefix_ptr, prefix_len)?
884+
};
885+
let rstart = mem.deref_slice(rstart_ptr, rstart_len)?;
886+
let rend = mem.deref_slice(rend_ptr, rend_len)?;
887+
888+
// Delete the relevant rows.
889+
Ok(env.instance_env.datastore_delete_by_btree_scan_bsatn(
890+
index_id.into(),
891+
prefix,
892+
prefix_elems,
893+
rstart,
894+
rend,
895+
)?)
896+
})
897+
}
898+
827899
/// Deletes those rows, in the table identified by `table_id`,
828900
/// that match any row in the byte string `rel = rel_ptr[..rel_len]` in WASM memory.
829901
///

0 commit comments

Comments
 (0)