Skip to content

Commit 7cd7dbb

Browse files
committed
Address review comments on submodule support
Submodule tables were stored under the wrong name ------------------------------------------------- `create_table_from_def_with_prefix` set a submodule table's canonical name from its *accessor* name and then dropped the alias entirely. Accessor names exist for client codegen; the host must never key on them. This made the host resolve submodule tables by a codegen-only name and left `st_table_accessor` with no row to recover the real accessor from. The view path already did this correctly and said so in a comment, so the two disagreed with each other; codegen had the same split, emitting `prefix + accessor_name` as a table's wire name but `prefix + name` for views. It was invisible only because accessor == canonical in the test fixture. Submodule tables are now stored as `prefix + canonical name`, with the accessor name kept as a namespaced alias, matching root tables. `check_compatible` goes back to an exact name match rather than accepting either name. `submodule_table_is_stored_under_canonical_name` covers this with a case-converted name, where the two actually differ. Local names were recovered by splitting on the last `.` ------------------------------------------------------- `check_compatible` recovered an index, constraint, sequence or schedule function's local name with `rsplit('.')`. A V9 sub-object name may itself contain dots -- the `wacky_names` test builds `"wacky.index()"` -- so the last segment is not the local name, and republishing such a module failed with "Index 0 not found in definition". Master compared the stored name to `def.name` outright and was unaffected. Now that every def records the namespace it is mounted under, the local name is recovered by stripping exactly that prefix, via `NamespacePath::strip_from`, which borrows rather than allocating. `wacky_names` covers the round-trip. The table-name check was loose in the same place: it compared only the last segment, so a table stored as `lib.my_table` validated against a root def named `my_table`. It now checks the whole name, and `check_compatible_submodule_table_name` covers a def actually mounted in `lib`. The related *panic* is gone too: `auto_migrate_indexes` and friends built keys with `Identifier::new(name).expect("names in a validated ModuleDef are valid identifiers")`, which aborts outright on `"wacky.index()"`. Keying by `(namespace, name)` removes the need to parse the name at all. `ModuleDefLookup::key()` restored --------------------------------- That naming split is why `key()` had to go: a table had two candidate keys. With it fixed, each def records the `NamespacePath` its module is mounted under (stamped by `ModuleDef::apply_namespace` once the module tree is assembled) and its key is `(namespace, name)` -- a `Copy` pair, so `AutoMigrateStep` and `AutoMigratePrecheck` go back to borrowing `Key<'def>` as they do on master. The local name is stored once; the qualified form is built where it is actually needed, which is the few places that hand a name to the database. That is 19 `format!`s at the same migration and table-creation sites this PR already had them. They are per migration step and per table, never per row, and `table_id_from_name` already allocates an owned `AlgebraicValue::String` to probe the index, so the marginal cost is one short string on top of one that was unavoidable. Sub-objects (indexes, sequences, constraints) key on `(namespace, local name)` rather than a dot-joined name, because a V9 sub-object name may itself contain dots -- `wacky_names` covers exactly that -- and `stored_in_table_def` already maps a sub-object to its table, so the key does not need to name the table. Schedules are 1:1 with tables, so a table's key identifies its schedule. `ModuleDef` now knows the path it is mounted under, so a namespaced key resolves relative to whichever module it is looked up in -- from the root, or from the submodule that owns the def. This replaces the `find_*_by_full_name(&str)` helpers, which scanned every table in the tree and `format!`ed each candidate, with `find_table`, `find_view` and `find_storing_table`, which take keys and walk the submodule tree by segment. `ensure_same_schema` filtered submodule steps with `name.contains('.')`; it now asks whether the namespace is empty. Type safety for namespaced names -------------------------------- `RawIdentifier` was used for dot-delimited names, which is wrong: a name containing `.` can never be validated into one `Identifier`. Add `RawNamespacedIdentifier` and use it where a name may carry a namespace: `TableName`, `SqlIdent`, index/constraint/sequence names and their `st_*` rows, and query-planner relvar names. `ScheduleSchema::function_name` becomes a `NamespacedIdentifier`. `ReducerName` becomes fully qualified, so `local()` gives the name within its own module and the `Deref<str>` gives the wire name. Previously it held a single `Identifier`, so converting it to a `NamespacedIdentifier` yielded a one-segment name -- `verify_token`, not `myauth.verify_token`. The v1/v2 websocket message types are deliberately left as `RawIdentifier`. They have always carried the table name as an opaque string and this feature does not change that, so the narrowing happens at the boundary in `core` rather than churning a published protocol crate. This removes the `rsplit('.')` "bare name" hacks in `schema.rs` in favour of `local_name()`, and namespace prefixing now goes through typed `NamespacePath::{join, join_raw, join_namespaced}` instead of `format!` into a `RawIdentifier`. `Identifier::new_assume_valid` is renamed to `new_unsafe_assume_valid` so it reads as the escape hatch it is; the schedule-name call site that used it to smuggle a dotted name into an `Identifier` is gone. Note this makes `TxDataTableEntry` and `MutTxId` grow, which the static size assertions record. `TableName` and `ReducerName` wrap a `NamespacedIdentifier`, which stores both the segments and their joined rendering. Those names genuinely are qualified -- they are the database and wire identities -- so shrinking them would mean changing how `NamespacedIdentifier` itself is represented. Host boundary ------------- `InstanceOp::name()` returned an owned `NamespacedIdentifier`, so every call cloned; it returns a reference again. `ProcedureOp`/`HttpHandlerOp` build theirs once at construction and `ReducerOp` borrows its `ReducerName`. Only `start_funcall`, which takes the name by value, still clones. `start_funcall` itself took `&str`/`RawIdentifier` and now takes a `NamespacedIdentifier` in v8, wasmtime and `wasm_instance_env`. A submodule reducer is now named by its qualified name in logs and metrics rather than its bare local name. Also fixes stale-view backing-table recreation, which iterated only root views and looked them up in `st_view` by local name, so submodule views were never repaired. TypeScript ---------- - camelCase throughout `lib_submodule.ts`, `index.ts` and the submodules doc - drop the `Anonymous extends true ? ... : ...` generic on `registerView` and split it into `registerView` / `registerAnonymousView`, removing the `as unknown as ViewFn<any, any, any>` cast; what remains is a single documented schema-erasure assertion per flavour - generated internals use `__`-prefixed names (`__qb`, `__reducerAccessors`, `__procedureAccessors`), matching the rest of the emitted code - the docs' subscription example used `addQuery`, which this branch removed The camelCase rename is to the TypeScript *exports*; the wire name is the canonical snake_case form and does not change. The docs now spell that out, since the HTTP and CLI paths take the canonical name while the generated bindings expose the camelCase accessor. `submodule_reducer_wire_name_is_qualified_once` pins the codegen side, as no snapshot fixture mounts a submodule. Other ----- - restore the step-ordering note inside `AutoMigrateStep` where it was - restore the named bindings in `successful_auto_migration` to shrink the diff - drop an unrelated `sender` -> `s` rename in the view-call path - `RawNamespacedIdentifier::segments` collected a `Vec` per call to be an `ExactSizeIterator`; no caller reads the size, so it just yields the `split` - the docs said a submodule view is reachable as `<namespace>.<viewName>` in SQL. SQL takes the canonical snake_case name; only the generated bindings expose the camelCase accessor
1 parent 0dde407 commit 7cd7dbb

48 files changed

Lines changed: 1733 additions & 1088 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/bench/src/spacetime_raw.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl BenchDatabase for SpacetimeRaw {
6969
IndexSchema {
7070
index_id: IndexId::SENTINEL,
7171
table_id,
72-
index_name: column.name.clone().unwrap(),
72+
index_name: column.name.clone().unwrap().into(),
7373
index_algorithm: IndexAlgorithm::BTree(BTreeAlgorithm {
7474
columns: ColId(i as _).into(),
7575
}),

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

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function makeViewExport<
5151
fn.bind() as ViewExport<F>;
5252
viewExport[exportContext] = ctx;
5353
viewExport[registerExport] = (ctx, exportName) => {
54-
registerView(ctx, opts, exportName, false, params, ret, fn);
54+
registerView(ctx, opts, exportName, params, ret, fn);
5555
};
5656
return viewExport;
5757
}
@@ -73,7 +73,7 @@ export function makeAnonViewExport<
7373
fn.bind() as ViewExport<F>;
7474
viewExport[exportContext] = ctx;
7575
viewExport[registerExport] = (ctx, exportName) => {
76-
registerView(ctx, opts, exportName, true, params, ret, fn);
76+
registerAnonymousView(ctx, opts, exportName, params, ret, fn);
7777
};
7878
return viewExport;
7979
}
@@ -194,28 +194,64 @@ export type ViewReturnTypeBuilder =
194194

195195
export function registerView<
196196
S extends UntypedSchemaDef,
197-
const Anonymous extends boolean,
198197
Params extends ParamsObj,
199198
Ret extends ViewReturnTypeBuilder,
200199
>(
201200
ctx: SchemaInner,
202201
opts: ViewOpts,
203202
exportName: string,
204-
anon: Anonymous,
205203
params: Params,
206204
ret: Ret,
207-
fn: Anonymous extends true
208-
? AnonymousViewFn<S, Params, Ret>
209-
: ViewFn<S, Params, Ret>
205+
fn: ViewFn<S, Params, Ret>
206+
) {
207+
const described = describeView(ctx, opts, exportName, false, params, ret);
208+
// `ctx.views` is schema-erased. `ViewCtx<S>` and `ViewCtx<any>` describe the same
209+
// shape, but TypeScript cannot relate two instantiations of the mapped type
210+
// `ReadonlyDbView` while the schema is still a type parameter, so erasing `S` here
211+
// needs an assertion. It is sound because the runtime builds the context it passes
212+
// back in from this very schema.
213+
ctx.views.push(buildViewInfo(ctx, described, fn as AnyViewFn));
214+
}
215+
216+
export function registerAnonymousView<
217+
S extends UntypedSchemaDef,
218+
Params extends ParamsObj,
219+
Ret extends ViewReturnTypeBuilder,
220+
>(
221+
ctx: SchemaInner,
222+
opts: ViewOpts,
223+
exportName: string,
224+
params: Params,
225+
ret: Ret,
226+
fn: AnonymousViewFn<S, Params, Ret>
227+
) {
228+
const described = describeView(ctx, opts, exportName, true, params, ret);
229+
// Schema-erased for the same reason as `registerView` above.
230+
ctx.anonViews.push(buildViewInfo(ctx, described, fn as AnyAnonymousViewFn));
231+
}
232+
233+
/**
234+
* The flavor-independent part of registering a view: register its types, record
235+
* it in the module def, and validate its primary key. Everything here is the
236+
* same for regular and anonymous views, so it is factored out of both.
237+
*/
238+
function describeView<
239+
Params extends ParamsObj,
240+
Ret extends ViewReturnTypeBuilder,
241+
>(
242+
ctx: SchemaInner,
243+
opts: ViewOpts,
244+
exportName: string,
245+
anon: boolean,
246+
params: Params,
247+
ret: Ret
210248
) {
211249
ctx.defineFunction(exportName);
212250
const paramsBuilder = new RowBuilder(params, toPascalCase(exportName));
213251

214252
// Register return types if they are product types
215253
let returnType = ctx.registerTypesRecursively(ret).algebraicType;
216254

217-
const { typespace } = ctx;
218-
219255
const { value: paramType } = ctx.resolveType(
220256
ctx.registerTypesRecursively(paramsBuilder)
221257
);
@@ -255,24 +291,39 @@ export function registerView<
255291
});
256292
}
257293

258-
// If it is an option, we wrap the function to make the return look like an array.
294+
// An option-returning view is presented to the host as an array of zero or one
295+
// rows, so its function needs wrapping and its return type rewriting.
296+
const wrapOption = returnType.tag == 'Sum';
297+
// Tested directly rather than via `wrapOption` so that TypeScript narrows `returnType`.
259298
if (returnType.tag == 'Sum') {
260-
const originalFn = fn;
261-
fn = ((ctx: ViewCtx<S>, args: InferTypeOfRow<Params>) => {
262-
const ret = originalFn(ctx, args);
263-
return ret == null ? [] : [ret];
264-
}) as any;
265299
returnType = AlgebraicType.Array(
266300
returnType.value.variants[0].algebraicType
267301
);
268302
}
269303

270-
(anon ? ctx.anonViews : ctx.views).push({
271-
fn: fn as unknown as ViewFn<any, any, any>,
304+
return { paramType, returnType, wrapOption };
305+
}
306+
307+
// Build the stored `ViewInfo` for `fn`. Generic over the stored function type so
308+
// the regular and anonymous cases each keep their own context shape here, instead
309+
// of being collapsed into one type by a cast.
310+
function buildViewInfo<F extends (viewCtx: any, params: any) => any>(
311+
ctx: SchemaInner,
312+
{ paramType, returnType, wrapOption }: ReturnType<typeof describeView>,
313+
fn: F
314+
): ViewInfo<F> {
315+
const { typespace } = ctx;
316+
return {
317+
fn: wrapOption
318+
? (((viewCtx, args) => {
319+
const ret = fn(viewCtx, args);
320+
return ret == null ? [] : [ret];
321+
}) as F)
322+
: fn,
272323
deserializeParams: ProductType.makeDeserializer(paramType, typespace),
273324
serializeReturn: AlgebraicType.makeSerializer(returnType, typespace),
274325
returnTypeBaseSize: bsatnBaseSize(typespace, returnType),
275-
});
326+
};
276327
}
277328

278329
// Inspect the returned row builder and collect the column property names marked

crates/cli/src/subcommands/call.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ fn add_reducer_procedure_ctx_to_err(error: &mut String, module_def: &ModuleDef,
285285
.all_reducers_with_prefix()
286286
.into_iter()
287287
.filter(|(_, _, r)| r.lifecycle.is_none())
288-
.map(|(prefix, _, r)| format!("{prefix}{}", &*r.name))
288+
.map(|(_, _, r)| r.name.to_string())
289289
.collect::<Vec<_>>();
290290
let reducers = reducer_names.iter().map(String::as_str).collect::<Vec<_>>();
291291

crates/codegen/src/typescript.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ impl Lang for TypeScript {
320320
}
321321
// Namespace tables from submodules
322322
for (prefix, owning_def, table) in &ns_tables {
323-
let source_name = submodule_source_name(prefix, table.accessor_name.deref());
323+
let source_name = submodule_source_name(prefix, table.name.deref());
324324
let row_type = submodule_row_type_name(prefix, table.accessor_name.deref());
325325
let type_ref = table.product_type_ref;
326326
writeln!(out, "\"{source_name}\": __table({{");
@@ -377,7 +377,8 @@ impl Lang for TypeScript {
377377
if !is_reducer_invokable(reducer) {
378378
continue;
379379
}
380-
let wire_name = format!("{}{}", prefix, reducer.name);
380+
// `reducer.name` is already qualified; do not prefix it again.
381+
let wire_name = reducer.name.to_string();
381382
let args_type = submodule_reducer_args_type_name(prefix, &reducer.accessor_name);
382383
writeln!(out, "__reducerSchema(\"{wire_name}\", {args_type}),");
383384
}
@@ -593,18 +594,18 @@ impl Lang for TypeScript {
593594
);
594595
}
595596
} else {
596-
writeln!(out, "const _qb = __makeQueryBuilder(tablesSchema.schemaType);");
597+
writeln!(out, "const __qb = __makeQueryBuilder(tablesSchema.schemaType);");
597598
writeln!(out, "export const tables = {{");
598599
out.indent(1);
599600
// Root tables (use camelCase accessor, matching tablesSchema keys)
600601
for table in iter_tables(module, options.visibility) {
601602
let key = table.accessor_name.deref().to_case(Case::Camel);
602-
writeln!(out, "{key}: _qb.{key},");
603+
writeln!(out, "{key}: __qb.{key},");
603604
}
604605
// Root views
605606
for view in iter_views(module) {
606607
let key = view.accessor_name.deref().to_case(Case::Camel);
607-
writeln!(out, "{key}: _qb.{key},");
608+
writeln!(out, "{key}: __qb.{key},");
608609
}
609610
// Build and emit namespace tree
610611
let tree = build_ns_tree(&ns_tables, &ns_views);
@@ -622,7 +623,7 @@ impl Lang for TypeScript {
622623
} else {
623624
writeln!(
624625
out,
625-
"const _reducers = __convertToAccessorMap(reducersSchema.reducersType.reducers);"
626+
"const __reducerAccessors = __convertToAccessorMap(reducersSchema.reducersType.reducers);"
626627
);
627628
writeln!(out, "export const reducers = {{");
628629
out.indent(1);
@@ -631,10 +632,10 @@ impl Lang for TypeScript {
631632
continue;
632633
}
633634
let key = reducer.accessor_name.deref().to_case(Case::Camel);
634-
writeln!(out, "{key}: _reducers.{key},");
635+
writeln!(out, "{key}: __reducerAccessors.{key},");
635636
}
636637
let tree = build_reducer_ns_tree(&ns_reducers);
637-
emit_fn_ns_tree(out, "_reducers", &tree);
638+
emit_fn_ns_tree(out, "__reducerAccessors", &tree);
638639
out.dedent(1);
639640
writeln!(out, "}} as const;");
640641
}
@@ -652,16 +653,16 @@ impl Lang for TypeScript {
652653
} else {
653654
writeln!(
654655
out,
655-
"const _procedures = __convertToAccessorMap(proceduresSchema.procedures);"
656+
"const __procedureAccessors = __convertToAccessorMap(proceduresSchema.procedures);"
656657
);
657658
writeln!(out, "export const procedures = {{");
658659
out.indent(1);
659660
for procedure in iter_procedures(module, options.visibility) {
660661
let key = procedure.accessor_name.deref().to_case(Case::Camel);
661-
writeln!(out, "{key}: _procedures.{key},");
662+
writeln!(out, "{key}: __procedureAccessors.{key},");
662663
}
663664
let tree = build_procedure_ns_tree(&ns_procedures);
664-
emit_fn_ns_tree(out, "_procedures", &tree);
665+
emit_fn_ns_tree(out, "__procedureAccessors", &tree);
665666
out.dedent(1);
666667
writeln!(out, "}} as const;");
667668
}
@@ -1328,9 +1329,11 @@ fn table_module_name(table_name: &Identifier) -> String {
13281329
}
13291330

13301331
/// Source name (wire name) for a submodule namespace table/view.
1331-
/// E.g. namespace="alias.", accessor_name="tableName" → "alias.tableName"
1332-
fn submodule_source_name(namespace: &NamespacePath, accessor_name: &str) -> String {
1333-
format!("{}{}", namespace, accessor_name)
1332+
///
1333+
/// This is the *canonical* name, not the accessor name: it is what the host stores and
1334+
/// what appears on the wire. E.g. namespace="lib.", name="fruit_basket" → "lib.fruit_basket".
1335+
fn submodule_source_name(namespace: &NamespacePath, canonical_name: &str) -> String {
1336+
format!("{}{}", namespace, canonical_name)
13341337
}
13351338

13361339
/// TypeScript import symbol for a submodule namespace table/view row type.
@@ -1413,7 +1416,7 @@ fn build_ns_tree<'a>(
14131416
) -> BTreeMap<String, NsTree> {
14141417
let mut tree: BTreeMap<String, NsTree> = BTreeMap::new();
14151418
for (prefix, _, table) in ns_tables {
1416-
let source_name = submodule_source_name(prefix, table.accessor_name.deref());
1419+
let source_name = submodule_source_name(prefix, table.name.deref());
14171420
let local = table.accessor_name.deref().to_case(Case::Camel);
14181421
let segs: Vec<&str> = prefix.segments().iter().map(|s| &**s).collect();
14191422
if let Some((first, rest)) = segs.split_first() {
@@ -1442,7 +1445,7 @@ fn emit_ns_tree(out: &mut Indenter, tree: &BTreeMap<String, NsTree>) {
14421445
writeln!(out, "{ns}: {{");
14431446
out.indent(1);
14441447
for (qb_key, local_key) in &node.entries {
1445-
writeln!(out, "{local_key}: _qb[\"{qb_key}\"],");
1448+
writeln!(out, "{local_key}: __qb[\"{qb_key}\"],");
14461449
}
14471450
emit_ns_tree(out, &node.children);
14481451
out.dedent(1);

crates/codegen/tests/codegen.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,29 @@ fn test_typescript_table_handles_are_camel_case() {
6060
r#"/** @deprecated Use `loggedOutPlayer` instead. This alias will be removed in the next major version. */"#
6161
));
6262
}
63+
64+
/// A submodule reducer's wire name must be qualified exactly once.
65+
///
66+
/// `ReducerDef::name` is fully qualified, so any code that also prepends the namespace
67+
/// path would emit `lib.lib.libInsert`. Nothing in the snapshot fixtures mounts a
68+
/// submodule, so this checks the TypeScript output of one that does.
69+
#[test]
70+
fn submodule_reducer_wire_name_is_qualified_once() {
71+
let module = CompiledModule::compile("module-test-ts", CompilationMode::Debug).extract_schema_blocking();
72+
let code = generate(&module, &TypeScript, &CodegenOptions::default())
73+
.into_iter()
74+
.map(|f| f.code)
75+
.collect::<Vec<_>>()
76+
.join("\n");
77+
78+
let reducer_lines: Vec<_> = code.lines().filter(|l| l.contains("__reducerSchema(")).collect();
79+
assert!(
80+
code.contains(r#"__reducerSchema("lib.lib_insert""#),
81+
"expected a singly-qualified wire name for the submodule reducer; got:\n{}",
82+
reducer_lines.join("\n")
83+
);
84+
assert!(
85+
!code.contains("lib.lib."),
86+
"namespace was applied twice somewhere in the generated bindings"
87+
);
88+
}

crates/core/src/client/messages.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use spacetimedb_lib::ser::serde::SerializeWrapper;
1717
use spacetimedb_lib::{AlgebraicValue, ConnectionId, TimeDuration, Timestamp};
1818
use spacetimedb_primitives::TableId;
1919
use spacetimedb_sats::bsatn;
20+
use spacetimedb_sats::raw_identifier::RawIdentifier;
2021
use spacetimedb_schema::table_name::TableName;
2122
use std::sync::Arc;
2223
use std::time::Instant;
@@ -608,7 +609,7 @@ impl ToProtocol for SubscriptionMessage {
608609
query_id,
609610
rows: ws_v1::SubscribeRows {
610611
table_id: result.table_id,
611-
table_name: result.table_name.into(),
612+
table_name: RawIdentifier::new(&*result.table_name),
612613
table_rows,
613614
},
614615
}
@@ -621,7 +622,7 @@ impl ToProtocol for SubscriptionMessage {
621622
query_id,
622623
rows: ws_v1::SubscribeRows {
623624
table_id: result.table_id,
624-
table_name: result.table_name.into(),
625+
table_name: RawIdentifier::new(&*result.table_name),
625626
table_rows,
626627
},
627628
}
@@ -639,7 +640,7 @@ impl ToProtocol for SubscriptionMessage {
639640
query_id,
640641
rows: ws_v1::SubscribeRows {
641642
table_id: result.table_id,
642-
table_name: result.table_name.into(),
643+
table_name: RawIdentifier::new(&*result.table_name),
643644
table_rows,
644645
},
645646
}
@@ -652,7 +653,7 @@ impl ToProtocol for SubscriptionMessage {
652653
query_id,
653654
rows: ws_v1::SubscribeRows {
654655
table_id: result.table_id,
655-
table_name: result.table_name.into(),
656+
table_name: RawIdentifier::new(&*result.table_name),
656657
table_rows,
657658
},
658659
}

crates/core/src/host/instance_env.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ use spacetimedb_datastore::locking_tx_datastore::{FuncCallType, IndexScanPointOr
2121
use spacetimedb_datastore::traits::IsolationLevel;
2222
use spacetimedb_lib::{http as st_http, ConnectionId, Identity, Timestamp};
2323
use spacetimedb_primitives::{ColId, ColList, IndexId, TableId};
24-
use spacetimedb_sats::raw_identifier::RawIdentifier;
2524
use spacetimedb_sats::{
2625
bsatn::{self, ToBsatn},
2726
buffer::CountWriter,
2827
AlgebraicValue, ProductValue,
2928
};
29+
use spacetimedb_schema::identifier::NamespacedIdentifier;
3030
use spacetimedb_table::indexes::RowPointer;
3131
use spacetimedb_table::table::RowRef;
3232
use std::fmt::Display;
@@ -48,7 +48,7 @@ pub struct InstanceEnv {
4848
/// The type of the last, including current, function to be executed by this environment.
4949
pub func_type: FuncCallType,
5050
/// The name of the last, including current, function to be executed by this environment.
51-
pub func_name: Option<RawIdentifier>,
51+
pub func_name: Option<NamespacedIdentifier>,
5252
/// Are we in an anonymous tx context?
5353
in_anon_tx: bool,
5454
/// A procedure's last known transaction offset.
@@ -246,7 +246,7 @@ impl InstanceEnv {
246246
}
247247

248248
/// Signal to this `InstanceEnv` that a function call is beginning.
249-
pub fn start_funcall(&mut self, name: RawIdentifier, ts: Timestamp, func_type: FuncCallType) {
249+
pub fn start_funcall(&mut self, name: NamespacedIdentifier, ts: Timestamp, func_type: FuncCallType) {
250250
self.start_time = ts;
251251
self.start_instant = Instant::now();
252252
self.func_type = func_type;

0 commit comments

Comments
 (0)