Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add signature of datastore_update_bsatn host call #2102

Merged
merged 2 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/bindings-csharp/Runtime/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ public class NoSuchIndexException : StdbException
public override string Message => "No such index";
}

public class IndexNotUniqueException : StdbException
{
public override string Message => "The index was not unique";
}

public class NoSuchRowException : StdbException
{
public override string Message => "The row was not found, e.g., in an update call";
}

public class UniqueConstraintViolationException : StdbException
{
public override string Message => "Value with given unique identifier already exists";
Expand Down
4 changes: 4 additions & 0 deletions crates/bindings-csharp/Runtime/Internal/FFI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public enum Errno : short
BUFFER_TOO_SMALL = 11,
UNIQUE_ALREADY_EXISTS = 12,
SCHEDULE_AT_DELAY_TOO_LONG = 13,
INDEX_NOT_UNIQUE = 14,
NO_SUCH_ROW = 15,
}

#pragma warning disable IDE1006 // Naming Styles - Not applicable to FFI stuff.
Expand Down Expand Up @@ -84,6 +86,8 @@ public static CheckedStatus ConvertToManaged(Errno status)
Errno.BUFFER_TOO_SMALL => new BufferTooSmallException(),
Errno.UNIQUE_ALREADY_EXISTS => new UniqueConstraintViolationException(),
Errno.SCHEDULE_AT_DELAY_TOO_LONG => new ScheduleAtDelayTooLongException(),
Errno.INDEX_NOT_UNIQUE => new IndexNotUniqueException(),
Errno.NO_SUCH_ROW => new NoSuchRowException(),
_ => new UnknownException(status),
};
}
Expand Down
75 changes: 74 additions & 1 deletion crates/bindings-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,11 +332,59 @@ pub mod raw {
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
/// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`.
/// typed at the `ProductType` the table's schema specifies.
/// typed at the `ProductType` the table's schema specifies.
/// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.
/// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.
pub fn datastore_insert_bsatn(table_id: TableId, row_ptr: *mut u8, row_len_ptr: *mut usize) -> u16;

/// Updates a row in the table identified by `table_id` to `row`
/// where the row is read from the byte string `row = row_ptr[..row_len]` in WASM memory
/// where `row_len = row_len_ptr[..size_of::<usize>()]` stores the capacity of `row`.
///
/// The byte string `row` must be a BSATN-encoded `ProductValue`
/// typed at the table's `ProductType` row-schema.
///
/// The row to update is found by projecting `row`
/// to the type of the *unique* index identified by `index_id`.
/// If no row is found, the error `NO_SUCH_ROW` is returned.
///
/// To handle auto-incrementing columns,
/// when the call is successful,
/// the `row` is written back to with the generated sequence values.
/// These values are written as a BSATN-encoded `pv: ProductValue`.
/// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.
/// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.
/// When the table has no sequences,
/// this implies that the `pv`, and thus `row`, will be empty.
/// The `row_len` is set to the length of `bsatn(pv)`.
///
/// # Traps
///
/// Traps if:
/// - `row_len_ptr` is NULL or `row_len` is not in bounds of WASM memory.
/// - `row_ptr` is NULL or `row` is not in bounds of WASM memory.
///
/// # Errors
///
/// Returns an error:
///
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
/// - `INDEX_NOT_UNIQUE`, when the index was not unique.
/// - `NO_SUCH_ROW`, when the row was not found in the unique index.
/// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`
/// typed at the `ProductType` the table's schema specifies
/// or when it cannot be projected to the index identified by `index_id`.
/// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.
/// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.
pub fn datastore_update_bsatn(
table_id: TableId,
index_id: IndexId,
row_ptr: *mut u8,
row_len_ptr: *mut usize,
) -> u16;

/// Schedules a reducer to be called asynchronously, nonatomically,
/// and immediately on a best effort basis.
///
Expand Down Expand Up @@ -752,6 +800,31 @@ pub fn datastore_insert_bsatn(table_id: TableId, row: &mut [u8]) -> Result<&[u8]
cvt(unsafe { raw::datastore_insert_bsatn(table_id, row_ptr, row_len) }).map(|()| &row[..*row_len])
}

/// Updates a row into the table identified by `table_id`,
/// where the row is a BSATN-encoded `ProductValue`
/// matching the table's `ProductType` row-schema.
///
/// The row to update is found by projecting `row`
/// to the type of the *unique* index identified by `index_id`.
/// If no row is found, `row` is inserted.
///
/// The `row` is `&mut` due to auto-incrementing columns.
/// So `row` is written to with the updated row re-encoded.
///
/// Returns an error if
/// - a table with the provided `table_id` doesn't exist
/// - an index with the provided `index_id` doesn't exist
/// - there were unique constraint violations
/// - `row` doesn't decode from BSATN to a `ProductValue`
/// according to the `ProductType` that the table's schema specifies
/// or if `row` cannot project to the index's type.
#[inline]
pub fn datastore_update_bsatn(table_id: TableId, index_id: IndexId, row: &mut [u8]) -> Result<&[u8], Errno> {
let row_ptr = row.as_mut_ptr();
let row_len = &mut row.len();
cvt(unsafe { raw::datastore_update_bsatn(table_id, index_id, row_ptr, row_len) }).map(|()| &row[..*row_len])
}

/// Deletes those rows, in the table identified by `table_id`,
/// that match any row in the byte string `relation`.
///
Expand Down
2 changes: 2 additions & 0 deletions crates/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ pub enum NodesError {
TableNotFound,
#[error("index with provided name or id doesn't exist")]
IndexNotFound,
#[error("index was not unique")]
IndexNotUnique,
#[error("column is out of bounds")]
BadColumn,
#[error("can't perform operation; not inside transaction")]
Expand Down
9 changes: 9 additions & 0 deletions crates/core/src/host/instance_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ impl InstanceEnv {
Ok(row_len)
}

pub fn update(&self, table_id: TableId, index_id: IndexId, buffer: &mut [u8]) -> Result<usize, NodesError> {
#![allow(unused)]

let stdb = &*self.replica_ctx.relational_db;
let tx = &mut *self.get_tx()?;

Ok(todo!())
}

#[tracing::instrument(skip_all)]
pub fn datastore_delete_by_btree_scan_bsatn(
&self,
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 @@ -153,6 +153,7 @@ pub enum AbiCall {
RowIterBsatnAdvance,
RowIterBsatnClose,
DatastoreInsertBsatn,
DatastoreUpdateBsatn,
DatastoreDeleteByBtreeScanBsatn,
DatastoreDeleteAllByEqBsatn,
BytesSourceRead,
Expand Down
2 changes: 2 additions & 0 deletions crates/core/src/host/wasm_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ pub fn err_to_errno(err: &NodesError) -> Option<NonZeroU16> {
NodesError::DecodeRow(_) => Some(errno::BSATN_DECODE_ERROR),
NodesError::TableNotFound => Some(errno::NO_SUCH_TABLE),
NodesError::IndexNotFound => Some(errno::NO_SUCH_INDEX),
NodesError::IndexNotUnique => Some(errno::INDEX_NOT_UNIQUE),
NodesError::ScheduleError(ScheduleError::DelayTooLong(_)) => Some(errno::SCHEDULE_AT_DELAY_TOO_LONG),
NodesError::AlreadyExists(_) => Some(errno::UNIQUE_ALREADY_EXISTS),
NodesError::Internal(internal) => match **internal {
Expand Down Expand Up @@ -382,6 +383,7 @@ macro_rules! abi_funcs {
"spacetime_10.0"::row_iter_bsatn_advance,
"spacetime_10.0"::row_iter_bsatn_close,
"spacetime_10.0"::datastore_insert_bsatn,
"spacetime_10.0"::datastore_update_bsatn,
"spacetime_10.0"::datastore_delete_all_by_eq_bsatn,
"spacetime_10.0"::bytes_source_read,
"spacetime_10.0"::bytes_sink_write,
Expand Down
64 changes: 64 additions & 0 deletions crates/core/src/host/wasmtime/wasm_instance_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,70 @@ impl WasmInstanceEnv {
})
}

/// Updates a row in the table identified by `table_id` to `row`
/// where the row is read from the byte string `row = row_ptr[..row_len]` in WASM memory
/// where `row_len = row_len_ptr[..size_of::<usize>()]` stores the capacity of `row`.
///
/// The byte string `row` must be a BSATN-encoded `ProductValue`
/// typed at the table's `ProductType` row-schema.
///
/// The row to update is found by projecting `row`
/// to the type of the *unique* index identified by `index_id`.
/// If no row is found, the error `NO_SUCH_ROW` is returned.
///
/// To handle auto-incrementing columns,
/// when the call is successful,
/// the `row` is written back to with the generated sequence values.
/// These values are written as a BSATN-encoded `pv: ProductValue`.
/// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.
/// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.
/// When the table has no sequences,
/// this implies that the `pv`, and thus `row`, will be empty.
/// The `row_len` is set to the length of `bsatn(pv)`.
///
/// # Traps
///
/// Traps if:
/// - `row_len_ptr` is NULL or `row_len` is not in bounds of WASM memory.
/// - `row_ptr` is NULL or `row` is not in bounds of WASM memory.
///
/// # Errors
///
/// Returns an error:
///
/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
/// - `INDEX_NOT_UNIQUE`, when the index was not unique.
/// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`
/// typed at the `ProductType` the table's schema specifies
/// or when it cannot be projected to the index identified by `index_id`.
/// - `NO_SUCH_ROW`, when the row was not found in the unique index.
/// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.
/// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.
#[tracing::instrument(skip_all)]
pub fn datastore_update_bsatn(
caller: Caller<'_, Self>,
table_id: u32,
index_id: u32,
row_ptr: WasmPtr<u8>,
row_len_ptr: WasmPtr<u32>,
) -> RtResult<u32> {
Self::cvt(caller, AbiCall::DatastoreUpdateBsatn, |caller| {
let (mem, env) = Self::mem_env(caller);

// Read `row-len`, i.e., the capacity of `row` pointed to by `row_ptr`.
let row_len = u32::read_from(mem, row_len_ptr)?;
// Get a mutable view to the `row`.
let row = mem.deref_slice_mut(row_ptr, row_len)?;

// Update the row in the DB and write back the generated column values.
let row_len = env.instance_env.update(table_id.into(), index_id.into(), row)?;
u32::try_from(row_len).unwrap().write_to(mem, row_len_ptr)?;
Ok(())
})
}

/// Deletes all rows found in the index identified by `index_id`,
/// according to the:
/// - `prefix = prefix_ptr[..prefix_len]`,
Expand Down
2 changes: 2 additions & 0 deletions crates/primitives/src/errno.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ macro_rules! errnos {
BUFFER_TOO_SMALL(11, "The provided buffer is not large enough to store the data"),
UNIQUE_ALREADY_EXISTS(12, "Value with given unique identifier already exists"),
SCHEDULE_AT_DELAY_TOO_LONG(13, "Specified delay in scheduling row was too long"),
INDEX_NOT_UNIQUE(14, "The index was not unique"),
NO_SUCH_ROW(15, "The row was not found, e.g., in an update call"),
);
};
}
Expand Down
Loading