Skip to content

Commit 8239f5e

Browse files
fix: single-column BTree index Range filter/delete (#4737)
Fixes #4736 # Description of Changes Single-column BTree indexes accepted `Range<T>` in their type signatures (via `IndexScanRangeBounds`), but the runtime `filter()` and `delete()` implementations always serialized the argument as a point value using `datastore_index_scan_point_bsatn`. Passing a `Range` object caused `SyntaxError: Cannot convert [object Object] to a BigInt` because the Range object was fed directly to the column serializer. The fix adds `Range` detection to the single-column BTree index code path in `runtime.ts`. When a `Range` is passed, it serializes the bounds and calls `datastore_index_scan_range_bsatn` / `datastore_delete_by_index_scan_range_bsatn` instead. Point queries continue to use the existing fast path. The multi-column index code already handled `Range` correctly — this brings single-column indexes to parity. # API and ABI breaking changes None. This is a pure bugfix. Existing queries are unaffected, and Range queries that previously crashed now work as the types imply. # Expected complexity level and risk Low. The change is localized to one code path in `runtime.ts` and mirrors the existing multi-column range serialization logic. All 170 existing tests pass. # Testing - All 170 vitest tests pass - Manually confirmed the fix resolves the reported `SyntaxError` in a production SpacetimeDB TypeScript module using `ctx.db.players.fooId.filter(new Range(...))` on a single-column BTree index
1 parent fca10ae commit 8239f5e

1 file changed

Lines changed: 36 additions & 0 deletions

File tree

crates/bindings-typescript/src/server/runtime.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,9 +667,37 @@ function makeTableView(
667667
index = base as UniqueIndex<any, any>;
668668
} else if (serializeSinglePoint) {
669669
// numColumns == 1
670+
671+
const serializeSingleRange = !isHashIndex
672+
? (buffer: ResizableBuffer, range: Range<any>): IndexScanArgs => {
673+
BINARY_WRITER.reset(buffer);
674+
const writer = BINARY_WRITER;
675+
const writeBound = (bound: Bound<any>) => {
676+
const tags = { included: 0, excluded: 1, unbounded: 2 };
677+
writer.writeU8(tags[bound.tag]);
678+
if (bound.tag !== 'unbounded')
679+
serializeSingleElement!(writer, bound.value);
680+
};
681+
writeBound(range.from);
682+
const rstartLen = writer.offset;
683+
writeBound(range.to);
684+
const rendLen = writer.offset - rstartLen;
685+
return [0, 0, rstartLen, rendLen];
686+
}
687+
: null;
688+
670689
const rawIndex = {
671690
filter: (range: any): IteratorObject<RowType<any>> => {
672691
const buf = LEAF_BUF;
692+
if (serializeSingleRange && range instanceof Range) {
693+
const args = serializeSingleRange(buf, range);
694+
const iter_id = sys.datastore_index_scan_range_bsatn(
695+
index_id,
696+
buf.buffer,
697+
...args
698+
);
699+
return tableIterator(iter_id, deserializeRow);
700+
}
673701
const point_len = serializeSinglePoint(buf, range);
674702
const iter_id = sys.datastore_index_scan_point_bsatn(
675703
index_id,
@@ -680,6 +708,14 @@ function makeTableView(
680708
},
681709
delete: (range: any): u32 => {
682710
const buf = LEAF_BUF;
711+
if (serializeSingleRange && range instanceof Range) {
712+
const args = serializeSingleRange(buf, range);
713+
return sys.datastore_delete_by_index_scan_range_bsatn(
714+
index_id,
715+
buf.buffer,
716+
...args
717+
);
718+
}
683719
const point_len = serializeSinglePoint(buf, range);
684720
return sys.datastore_delete_by_index_scan_point_bsatn(
685721
index_id,

0 commit comments

Comments
 (0)