Skip to content

Commit 4eaa6a6

Browse files
committed
[TS] Implement and use point scan ABI
1 parent df035c4 commit 4eaa6a6

File tree

4 files changed

+178
-49
lines changed

4 files changed

+178
-49
lines changed

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

Lines changed: 116 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as _syscalls1_0 from 'spacetime:sys@1.0';
22
import * as _syscalls1_2 from 'spacetime:sys@1.2';
3+
import * as _syscalls1_3 from 'spacetime:sys@1.3';
34

45
import type { ModuleHooks, u16, u32 } from 'spacetime:sys@1.0';
56
import { AlgebraicType, ProductType } from '../lib/algebraic_type';
@@ -48,7 +49,9 @@ import ViewResultHeader from '../lib/autogen/view_result_header_type';
4849

4950
const { freeze } = Object;
5051

51-
export const sys = freeze(wrapSyscalls(_syscalls1_0, _syscalls1_2));
52+
export const sys = freeze(
53+
wrapSyscalls(_syscalls1_0, _syscalls1_2, _syscalls1_3)
54+
);
5255

5356
export function parseJsonObject(json: string): JsonObject {
5457
let value: unknown;
@@ -220,10 +223,8 @@ export const hooks: ModuleHooks = {
220223
return writer.getBuffer();
221224
},
222225
__call_reducer__(reducerId, sender, connId, timestamp, argsBuf) {
223-
const argsType = AlgebraicType.Product(
224-
MODULE_DEF.reducers[reducerId].params
225-
);
226-
const args = AlgebraicType.deserializeValue(
226+
const argsType = MODULE_DEF.reducers[reducerId].params;
227+
const args = ProductType.deserializeValue(
227228
new BinaryReader(argsBuf),
228229
argsType,
229230
MODULE_DEF.typespace
@@ -499,15 +500,30 @@ function makeTableView(
499500
prefix: any[],
500501
prefix_elems: number
501502
) => {
502-
if (prefix_elems > numColumns - 1)
503-
throw new TypeError('too many elements in prefix');
504503
for (let i = 0; i < prefix_elems; i++) {
505504
const elemType = indexType.value.elements[i].algebraicType;
506505
AlgebraicType.serializeValue(writer, elemType, prefix[i], typespace);
507506
}
508507
return writer;
509508
};
510509

510+
const serializePoint = (colVal: any[]): Uint8Array => {
511+
const writer = new BinaryWriter(baseSize);
512+
serializePrefix(writer, colVal, numColumns);
513+
return writer.getBuffer();
514+
};
515+
516+
const singleElement =
517+
numColumns === 1 ? indexType.value.elements[0].algebraicType : null;
518+
519+
const serializeSinglePoint =
520+
singleElement &&
521+
((colVal: any): Uint8Array => {
522+
const writer = new BinaryWriter(baseSize);
523+
AlgebraicType.serializeValue(writer, singleElement, colVal, typespace);
524+
return writer.getBuffer();
525+
});
526+
511527
type IndexScanArgs = [
512528
prefix: Uint8Array,
513529
prefix_elems: u16,
@@ -516,33 +532,51 @@ function makeTableView(
516532
];
517533

518534
let index: Index<any, any>;
519-
if (isUnique) {
520-
const serializeBound = (colVal: any[]): IndexScanArgs => {
521-
if (colVal.length !== numColumns)
522-
throw new TypeError('wrong number of elements');
523-
524-
const writer = new BinaryWriter(baseSize + 1);
525-
const prefix_elems = numColumns - 1;
526-
serializePrefix(writer, colVal, prefix_elems);
527-
const rstartOffset = writer.offset;
528-
writer.writeU8(0);
529-
AlgebraicType.serializeValue(
530-
writer,
531-
indexType.value.elements[numColumns - 1].algebraicType,
532-
colVal[numColumns - 1],
533-
typespace
534-
);
535-
const buffer = writer.getBuffer();
536-
const prefix = buffer.slice(0, rstartOffset);
537-
const rstart = buffer.slice(rstartOffset);
538-
return [prefix, prefix_elems, rstart, rstart];
539-
};
535+
if (isUnique && serializeSinglePoint) {
536+
index = {
537+
find: (colVal: IndexVal<any, any>): RowType<any> | null => {
538+
const point = serializeSinglePoint(colVal);
539+
const iter = tableIterator(
540+
sys.datastore_index_scan_point_bsatn(index_id, point),
541+
rowType
542+
);
543+
const { value, done } = iter.next();
544+
if (done) return null;
545+
if (!iter.next().done)
546+
throw new Error(
547+
'`datastore_index_scan_range_bsatn` on unique field cannot return >1 rows'
548+
);
549+
return value;
550+
},
551+
delete: (colVal: IndexVal<any, any>): boolean => {
552+
const point = serializeSinglePoint(colVal);
553+
const num = sys.datastore_delete_by_index_scan_point_bsatn(
554+
index_id,
555+
point
556+
);
557+
return num > 0;
558+
},
559+
update: (row: RowType<any>): RowType<any> => {
560+
const writer = new BinaryWriter(baseSize);
561+
AlgebraicType.serializeValue(writer, rowType, row, typespace);
562+
const ret_buf = sys.datastore_update_bsatn(
563+
table_id,
564+
index_id,
565+
writer.getBuffer()
566+
);
567+
integrateGeneratedColumns?.(row, ret_buf);
568+
return row;
569+
},
570+
} as UniqueIndex<any, any>;
571+
} else if (isUnique) {
540572
index = {
541573
find: (colVal: IndexVal<any, any>): RowType<any> | null => {
542-
if (numColumns === 1) colVal = [colVal];
543-
const args = serializeBound(colVal);
574+
if (colVal.length !== numColumns)
575+
throw new TypeError('wrong number of elements');
576+
577+
const point = serializePoint(colVal);
544578
const iter = tableIterator(
545-
sys.datastore_index_scan_range_bsatn(index_id, ...args),
579+
sys.datastore_index_scan_point_bsatn(index_id, point),
546580
rowType
547581
);
548582
const { value, done } = iter.next();
@@ -554,11 +588,13 @@ function makeTableView(
554588
return value;
555589
},
556590
delete: (colVal: IndexVal<any, any>): boolean => {
557-
if (numColumns === 1) colVal = [colVal];
558-
const args = serializeBound(colVal);
559-
const num = sys.datastore_delete_by_index_scan_range_bsatn(
591+
if (colVal.length !== numColumns)
592+
throw new TypeError('wrong number of elements');
593+
594+
const point = serializePoint(colVal);
595+
const num = sys.datastore_delete_by_index_scan_point_bsatn(
560596
index_id,
561-
...args
597+
point
562598
);
563599
return num > 0;
564600
},
@@ -574,6 +610,23 @@ function makeTableView(
574610
return row;
575611
},
576612
} as UniqueIndex<any, any>;
613+
} else if (serializeSinglePoint) {
614+
index = {
615+
filter: (range: any): IteratorObject<RowType<any>> => {
616+
const point = serializeSinglePoint(range);
617+
return tableIterator(
618+
sys.datastore_index_scan_point_bsatn(index_id, point),
619+
rowType
620+
);
621+
},
622+
delete: (range: any): u32 => {
623+
const point = serializeSinglePoint(range);
624+
return sys.datastore_delete_by_index_scan_point_bsatn(
625+
index_id,
626+
point
627+
);
628+
},
629+
} as RangedIndex<any, any>;
577630
} else {
578631
const serializeRange = (range: any[]): IndexScanArgs => {
579632
if (range.length > numColumns) throw new TypeError('too many elements');
@@ -613,21 +666,35 @@ function makeTableView(
613666
return [prefix, prefix_elems, rstart, rend];
614667
};
615668
index = {
616-
filter: (range: any): IteratorObject<RowType<any>> => {
617-
if (numColumns === 1) range = [range];
618-
const args = serializeRange(range);
619-
return tableIterator(
620-
sys.datastore_index_scan_range_bsatn(index_id, ...args),
621-
rowType
622-
);
669+
filter: (range: any[]): IteratorObject<RowType<any>> => {
670+
if (range.length === numColumns) {
671+
const point = serializePoint(range);
672+
return tableIterator(
673+
sys.datastore_index_scan_point_bsatn(index_id, point),
674+
rowType
675+
);
676+
} else {
677+
const args = serializeRange(range);
678+
return tableIterator(
679+
sys.datastore_index_scan_range_bsatn(index_id, ...args),
680+
rowType
681+
);
682+
}
623683
},
624-
delete: (range: any): u32 => {
625-
if (numColumns === 1) range = [range];
626-
const args = serializeRange(range);
627-
return sys.datastore_delete_by_index_scan_range_bsatn(
628-
index_id,
629-
...args
630-
);
684+
delete: (range: any[]): u32 => {
685+
if (range.length === numColumns) {
686+
const point = serializePoint(range);
687+
return sys.datastore_delete_by_index_scan_point_bsatn(
688+
index_id,
689+
point
690+
);
691+
} else {
692+
const args = serializeRange(range);
693+
return sys.datastore_delete_by_index_scan_range_bsatn(
694+
index_id,
695+
...args
696+
);
697+
}
631698
},
632699
} as RangedIndex<any, any>;
633700
}

crates/bindings-typescript/src/server/sys.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,15 @@ declare module 'spacetime:sys@1.2' {
100100

101101
export function procedure_abort_mut_tx();
102102
}
103+
104+
declare module 'spacetime:sys@1.3' {
105+
function datastore_index_scan_point_bsatn(
106+
index_id: u32,
107+
point: Uint8Array
108+
): u32;
109+
110+
function datastore_delete_by_index_scan_point_bsatn(
111+
index_id: u32,
112+
point: Uint8Array
113+
): u32;
114+
}

crates/core/src/host/v8/syscall/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ fn resolve_sys_module_inner<'scope>(
5757
(1, 0) => Ok(v1::sys_v1_0(scope)),
5858
(1, 1) => Ok(v1::sys_v1_1(scope)),
5959
(1, 2) => Ok(v1::sys_v1_2(scope)),
60+
(1, 3) => Ok(v1::sys_v1_3(scope)),
6061
_ => Err(TypeError(format!(
6162
"Could not import {spec:?}, likely because this module was built for a newer version of SpacetimeDB.\n\
6263
It requires sys module v{major}.{minor}, but that version is not supported by the database."

crates/core/src/host/v8/syscall/v1.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,23 @@ pub(super) fn sys_v1_2<'scope>(scope: &mut PinScope<'scope, '_>) -> Local<'scope
150150
)
151151
}
152152

153+
pub(super) fn sys_v1_3<'scope>(scope: &mut PinScope<'scope, '_>) -> Local<'scope, Module> {
154+
create_synthetic_module!(
155+
scope,
156+
"spacetime:sys@1.2",
157+
(
158+
with_sys_result_ret,
159+
AbiCall::DatastoreIndexScanPointBsatn,
160+
datastore_index_scan_point_bsatn
161+
),
162+
(
163+
with_sys_result_ret,
164+
AbiCall::DatastoreDeleteByIndexScanPointBsatn,
165+
datastore_delete_by_index_scan_point_bsatn
166+
),
167+
)
168+
}
169+
153170
/// Registers a function in `module`
154171
/// where the function has `name` and does `body`.
155172
fn register_module_fun(
@@ -1694,3 +1711,35 @@ fn procedure_commit_mut_tx(scope: &mut PinScope<'_, '_>, _args: FunctionCallback
16941711

16951712
Ok(())
16961713
}
1714+
1715+
fn datastore_index_scan_point_bsatn(
1716+
scope: &mut PinScope<'_, '_>,
1717+
args: FunctionCallbackArguments<'_>,
1718+
) -> SysCallResult<u32> {
1719+
let index_id: IndexId = deserialize_js(scope, args.get(0))?;
1720+
let point: &[u8] = deserialize_js(scope, args.get(1))?;
1721+
1722+
let env = get_env(scope)?;
1723+
1724+
// Find the relevant rows.
1725+
let chunks = env
1726+
.instance_env
1727+
.datastore_index_scan_point_bsatn_chunks(&mut env.chunk_pool, index_id, point)?;
1728+
1729+
// Insert the encoded + concatenated rows into a new buffer and return its id.
1730+
Ok(env.iters.insert(chunks.into_iter()).0)
1731+
}
1732+
1733+
fn datastore_delete_by_index_scan_point_bsatn(
1734+
scope: &mut PinScope<'_, '_>,
1735+
args: FunctionCallbackArguments<'_>,
1736+
) -> SysCallResult<u32> {
1737+
let index_id: IndexId = deserialize_js(scope, args.get(0))?;
1738+
let point: &[u8] = deserialize_js(scope, args.get(1))?;
1739+
1740+
// Delete the relevant rows.
1741+
let count = get_env(scope)?
1742+
.instance_env
1743+
.datastore_delete_by_index_scan_point_bsatn(index_id, point)?;
1744+
Ok(count)
1745+
}

0 commit comments

Comments
 (0)