mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-13 11:17:50 -04:00
8a0cd87c4f
# Description of Changes To resolve #3875, we added exact-match unique index point lookup support to the C# bindings by introducing and using `datastore_index_scan_point_bsatn`. Previously, generated unique index `Find()` was (in at least one codepath) implemented as: * A range scan (`datastore_index_scan_range_bsatn`) over a BTree bound, then * `SingleOrDefault()` to collapse the results into a single row. When the scan is empty, `SingleOrDefault()` returns `default(T)`. For value-type rows this can manifest as a default-initialized row instead of “missing”, which is what surfaced as “default-ish row” behavior in views. Using `datastore_index_scan_point_bsatn` makes the C# implementation match Rust semantics more closely by performing an exact point lookup and returning: * `null` when no rows are found * the row when exactly one row is found * (defensively) an error if >1 row is returned (unique index invariant violation) Similarly, `datastore_delete_by_index_scan_point_bsatn` was added and used so deletes-by-unique-key are also exact-match point operations rather than range deletes. Runtime updates were made to utilize point scan in `FindSingle(key)` and in both mutable/read-only unique-index paths. To keep this non-breaking for existing modules, codegen now detects whether the table row is a struct or a class and chooses the appropriate base type: * Struct rows: `Find()` returns `Row?` (`Nullable<Row>`). * Class rows: `Find()` returns `Row?` (nullable reference, `null` on miss). # API and ABI breaking changes This change is non-breaking with respect to row type kinds, because class/record table rows continue to work via RefUniqueIndex/ReadOnlyRefUniqueIndex while struct rows use UniqueIndex/ReadOnlyUniqueIndex. API surface changes: * Generated `Find()` return type is now nullable (`Row?`) to correctly represent “missing”. ABI/runtime: * Requires the point-scan hostcall import (`datastore_index_scan_point_bsatn`) to be available; the runtime uses point-scan for unique lookup (and point delete for unique delete). # Expected complexity level and risk Low 2 # Testing - [X] Local testing: repro module + client validate view and direct Find() behavior --------- Signed-off-by: rekhoff <r.ekhoff@clockworklabs.io>
SpacetimeDB.Runtime
This project contains the core SpacetimeDB SATS typesystem, attributes for the codegen as well as runtime bindings for SpacetimeDB WebAssembly modules.
The runtime bindings are currently implementing via Wasi.Sdk package, which is a .NET implementation of the WASI standard. This is likely to change in the future.
While not really documented, it allows to build raw WebAssembly modules with custom bindings as well, which is what we're using here. The process is somewhat complicated, but here are the steps:
bindings.cdeclares raw C bindings to the SpacetimeDB FFI imports and marks them with attributes like__attribute__((import_module("spacetime"), import_name("_insert")))that make them WebAssembly imports. (unfortunately, function name duplication is currently unavoidable)bindings.cimplements a bunch of Mono-compatible wrappers that convert between Mono types and raw types expected by the SpacetimeDB FFI and invoke corresponding raw bindings.Runtime.csdeclares corresponding functions with compatible signatures for Mono-compatible wrappers to attach to. It marks them all with[MethodImpl(MethodImplOptions.InternalCall)].bindings.cattaches all those Mono-compatible wrappers to their C# declarations in amono_stdb_attach_bindingsfunction.bindings.cadds FFI-compatible exports that search for a method by assembly name, namespace, class name and a method name in the Mono runtime and invoke it. Those exports are marked with attributes like__attribute__((export_name("__call_reducer__")))so that they're exported from Wasm by the linker.- Finally,
bindings.cimplements no-op shims for all the WASI APIs so that they're linked internally and not attempted to be imported from the runtime itself.
The result is a WebAssembly module FFI-compatible with SpacetimeDB and with no WASI imports, which is what we need.
Regenerating RawModuleDef
To regenenerate the Autogen folder, run:
cargo run -p spacetimedb-codegen --example regen-csharp-moduledef