Skip to content

Commit

Permalink
WASM ABI: add datastore_delete_by_btree_scan_bsatn (#1704)
Browse files Browse the repository at this point in the history
  • Loading branch information
Centril authored Sep 18, 2024
1 parent 14e5098 commit db29ded
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 5 deletions.
95 changes: 95 additions & 0 deletions crates/bindings-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,54 @@ pub mod raw {
out: *mut u32,
) -> u16;

/// Deletes all rows found in the index identified by `index_id`,
/// according to the:
/// - `prefix = prefix_ptr[..prefix_len]`,
/// - `rstart = rstart_ptr[..rstart_len]`,
/// - `rend = rend_ptr[..rend_len]`,
/// in WASM memory.
///
/// This syscall will delete all the rows found by
/// [`datastore_btree_scan_bsatn`] with the same arguments passed,
/// including `prefix_elems`.
/// See `datastore_btree_scan_bsatn` for details.
///
/// The number of rows deleted is written to the WASM pointer `out`.
///
/// # Traps
///
/// Traps if:
/// - `prefix_elems > 0`
/// and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).
/// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.
/// - `rend` is NULL or `rend` is not in bounds of WASM memory.
/// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.
///
/// # Errors
///
/// Returns an error:
///
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
/// - `WRONG_INDEX_ALGO` if the index is not a btree index.
/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
/// a `prefix_elems` number of `AlgebraicValue`
/// typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
/// Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
/// where the inner `AlgebraicValue`s are
/// typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
pub fn _datastore_delete_by_btree_scan_bsatn(
index_id: IndexId,
prefix_ptr: *const u8,
prefix_len: usize,
prefix_elems: ColId,
rstart_ptr: *const u8, // Bound<AlgebraicValue>
rstart_len: usize,
rend_ptr: *const u8, // Bound<AlgebraicValue>
rend_len: usize,
out: *mut u32,
) -> u16;

/// Deletes those rows, in the table identified by `table_id`,
/// that match any row in the byte string `rel = rel_ptr[..rel_len]` in WASM memory.
///
Expand Down Expand Up @@ -898,6 +946,53 @@ pub fn datastore_btree_scan_bsatn(
Ok(RowIter { raw })
}

/// Deletes all rows found in the index identified by `index_id`,
/// according to the `prefix`, `rstart`, and `rend`.
///
/// This syscall will delete all the rows found by
/// [`datastore_btree_scan_bsatn`] with the same arguments passed,
/// including `prefix_elems`.
/// See `datastore_btree_scan_bsatn` for details.
///
/// The number of rows deleted is returned on success.
///
/// # Errors
///
/// Returns an error:
///
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
/// - `WRONG_INDEX_ALGO` if the index is not a btree index.
/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
/// a `prefix_elems` number of `AlgebraicValue`
/// typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
/// Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
/// where the inner `AlgebraicValue`s are
/// typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
pub fn datastore_delete_by_btree_scan_bsatn(
index_id: IndexId,
prefix: &[u8],
prefix_elems: ColId,
rstart: &[u8],
rend: &[u8],
) -> Result<u32, Errno> {
unsafe {
call(|out| {
raw::_datastore_delete_by_btree_scan_bsatn(
index_id,
prefix.as_ptr(),
prefix.len(),
prefix_elems,
rstart.as_ptr(),
rstart.len(),
rend.as_ptr(),
rend.len(),
out,
)
})
}
}

/// Iterate through a table, filtering by an encoded `spacetimedb_lib::filter::Expr`.
///
/// # Errors
Expand Down
7 changes: 4 additions & 3 deletions crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ impl MutTxId {
prefix_elems: ColId,
rstart: &[u8],
rend: &[u8],
) -> Result<impl Iterator<Item = RowRef<'a>>> {
) -> Result<(TableId, impl Iterator<Item = RowRef<'a>>)> {
// Extract the table and index type for the tx state.
let (table_id, col_list, tx_idx_key_type) = self
.get_table_and_index_type(index_id)
Expand Down Expand Up @@ -548,15 +548,16 @@ impl MutTxId {
}
}
}
Ok(match commit_iter {
let iter = match commit_iter {
None => Choice::A(tx_iter),
Some(commit_iter) => match self.tx_state.delete_tables.get(&table_id) {
None => Choice::B(tx_iter.chain(commit_iter)),
Some(tx_dels) => {
Choice::C(tx_iter.chain(commit_iter.filter(move |row| !tx_dels.contains(&row.pointer()))))
}
},
})
};
Ok((table_id, iter))
}

/// Translate `index_id` to the table id, the column list and index key type.
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/db/relational_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ impl RelationalDB {
prefix_elems: ColId,
rstart: &[u8],
rend: &[u8],
) -> Result<impl Iterator<Item = RowRef<'a>>, DBError> {
) -> Result<(TableId, impl Iterator<Item = RowRef<'a>>), DBError> {
tx.btree_scan(index_id, prefix, prefix_elems, rstart, rend)
}

Expand Down
23 changes: 22 additions & 1 deletion crates/core/src/host/instance_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,27 @@ impl InstanceEnv {
Ok(stdb.delete(tx, table_id, rows_to_delete))
}

#[tracing::instrument(skip_all)]
pub fn datastore_delete_by_btree_scan_bsatn(
&self,
index_id: IndexId,
prefix: &[u8],
prefix_elems: ColId,
rstart: &[u8],
rend: &[u8],
) -> Result<u32, NodesError> {
let stdb = &*self.dbic.relational_db;
let tx = &mut *self.tx.get()?;

// Find all rows in the table to delete.
let (table_id, iter) = stdb.btree_scan(tx, index_id, prefix, prefix_elems, rstart, rend)?;
// Re. `SmallVec`, `delete_by_field` only cares about 1 element, so optimize for that.
let rows_to_delete = iter.map(|row_ref| row_ref.pointer()).collect::<SmallVec<[_; 1]>>();

// Delete them and count how many we deleted.
Ok(stdb.delete(tx, table_id, rows_to_delete))
}

/// Deletes all rows in the table identified by `table_id`
/// where the rows match one in `relation`
/// which is a bsatn encoding of `Vec<ProductValue>`.
Expand Down Expand Up @@ -289,7 +310,7 @@ impl InstanceEnv {
let stdb = &*self.dbic.relational_db;
let tx = &mut *self.tx.get()?;

let iter = stdb.btree_scan(tx, index_id, prefix, prefix_elems, rstart, rend)?;
let (_, iter) = stdb.btree_scan(tx, index_id, prefix, prefix_elems, rstart, rend)?;
let chunks = ChunkedWriter::collect_iter(iter);
Ok(chunks)
}
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/host/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ pub enum AbiCall {
RowIterBsatnAdvance,
RowIterBsatnClose,
DatastoreInsertBsatn,
DatastoreDeleteByBtreeScanBsatn,
DatastoreDeleteAllByEqBsatn,
BytesSourceRead,
BytesSinkWrite,
Expand Down
72 changes: 72 additions & 0 deletions crates/core/src/host/wasmtime/wasm_instance_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,78 @@ impl WasmInstanceEnv {
})
}

/// Deletes all rows found in the index identified by `index_id`,
/// according to the:
/// - `prefix = prefix_ptr[..prefix_len]`,
/// - `rstart = rstart_ptr[..rstart_len]`,
/// - `rend = rend_ptr[..rend_len]`,
/// in WASM memory.
///
/// This syscall will delete all the rows found by
/// [`datastore_btree_scan_bsatn`] with the same arguments passed,
/// including `prefix_elems`.
/// See `datastore_btree_scan_bsatn` for details.
///
/// The number of rows deleted is written to the WASM pointer `out`.
///
/// # Traps
///
/// Traps if:
/// - `prefix_elems > 0`
/// and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).
/// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.
/// - `rend` is NULL or `rend` is not in bounds of WASM memory.
/// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.
///
/// # Errors
///
/// Returns an error:
///
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
/// - `WRONG_INDEX_ALGO` if the index is not a btree index.
/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
/// a `prefix_elems` number of `AlgebraicValue`
/// typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
/// Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
/// where the inner `AlgebraicValue`s are
/// typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
pub fn datastore_delete_by_btree_scan_bsatn(
caller: Caller<'_, Self>,
index_id: u32,
prefix_ptr: WasmPtr<u8>,
prefix_len: u32,
prefix_elems: u32,
rstart_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>
rstart_len: u32,
rend_ptr: WasmPtr<u8>, // Bound<AlgebraicValue>
rend_len: u32,
out: WasmPtr<u32>,
) -> RtResult<u32> {
Self::cvt_ret(caller, AbiCall::DatastoreDeleteByBtreeScanBsatn, out, |caller| {
let prefix_elems = Self::convert_u32_to_col_id(prefix_elems)?;

let (mem, env) = Self::mem_env(caller);
// Read the prefix and range start & end from WASM memory.
let prefix = if prefix_elems.idx() == 0 {
&[]
} else {
mem.deref_slice(prefix_ptr, prefix_len)?
};
let rstart = mem.deref_slice(rstart_ptr, rstart_len)?;
let rend = mem.deref_slice(rend_ptr, rend_len)?;

// Delete the relevant rows.
Ok(env.instance_env.datastore_delete_by_btree_scan_bsatn(
index_id.into(),
prefix,
prefix_elems,
rstart,
rend,
)?)
})
}

/// Deletes those rows, in the table identified by `table_id`,
/// that match any row in the byte string `rel = rel_ptr[..rel_len]` in WASM memory.
///
Expand Down

2 comments on commit db29ded

@github-actions
Copy link

@github-actions github-actions bot commented on db29ded Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarking failed. Please check the workflow run for details.

@github-actions
Copy link

@github-actions github-actions bot commented on db29ded Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarking failed. Please check the workflow run for details.

Please sign in to comment.