From 835cbd363573d353815124420fc1c75720133306 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 27 Jan 2026 22:26:52 +0000 Subject: [PATCH] define `TableName` backed by `EcoString` and optimize with it define `ReducerName` backed by `EcoString` --- Cargo.lock | 10 ++++ Cargo.toml | 1 + crates/bench/benches/special.rs | 3 +- crates/bench/benches/subscription.rs | 3 +- crates/bench/src/schemas.rs | 5 +- crates/bench/src/spacetime_module.rs | 5 +- crates/bench/src/spacetime_raw.rs | 2 +- crates/bench/src/sqlite.rs | 3 +- crates/codegen/src/rust.rs | 11 ++-- crates/codegen/src/typescript.rs | 7 +-- crates/core/src/client/message_handlers.rs | 3 +- crates/core/src/client/messages.rs | 14 +++--- crates/core/src/db/durability.rs | 3 +- crates/core/src/db/mod.rs | 5 +- crates/core/src/db/relational_db.rs | 19 +++---- crates/core/src/error.rs | 10 ++-- crates/core/src/host/module_host.rs | 12 +++-- crates/core/src/host/scheduler.rs | 4 +- crates/core/src/host/v8/mod.rs | 3 +- .../src/host/wasm_common/module_host_actor.rs | 9 ++-- crates/core/src/sql/ast.rs | 3 +- crates/core/src/sql/execute.rs | 4 +- .../core/src/subscription/execution_unit.rs | 9 ++-- crates/core/src/subscription/metrics.rs | 8 +-- crates/core/src/subscription/mod.rs | 25 +++++++--- .../subscription/module_subscription_actor.rs | 32 ++++-------- .../module_subscription_manager.rs | 24 +++++---- crates/core/src/subscription/query.rs | 22 ++++---- crates/core/src/subscription/subscription.rs | 15 +++--- crates/core/src/vm.rs | 9 ++-- crates/datastore/src/execution_context.rs | 17 +++---- .../locking_tx_datastore/committed_state.rs | 4 +- .../src/locking_tx_datastore/datastore.rs | 18 ++++--- .../src/locking_tx_datastore/mut_tx.rs | 10 ++-- .../datastore/src/locking_tx_datastore/tx.rs | 4 +- crates/datastore/src/system_tables.rs | 5 +- crates/datastore/src/traits.rs | 35 +++++-------- crates/expr/src/statement.rs | 5 +- crates/physical-plan/src/dml.rs | 5 +- crates/physical-plan/src/plan.rs | 3 +- crates/query/Cargo.toml | 1 + crates/query/src/lib.rs | 3 +- crates/schema/Cargo.toml | 1 + crates/schema/src/def.rs | 8 +-- crates/schema/src/def/error.rs | 7 +-- crates/schema/src/def/validate/v8.rs | 12 ++--- crates/schema/src/def/validate/v9.rs | 30 +++++------ crates/schema/src/lib.rs | 2 + crates/schema/src/reducer_name.rs | 50 +++++++++++++++++++ crates/schema/src/relation.rs | 7 +-- crates/schema/src/schema.rs | 19 +++---- crates/schema/src/table_name.rs | 48 ++++++++++++++++++ crates/subscription/Cargo.toml | 2 + crates/subscription/src/lib.rs | 47 +---------------- crates/table/benches/page_manager.rs | 3 +- crates/table/src/table.rs | 7 +-- crates/vm/src/eval.rs | 15 ++++-- crates/vm/src/expr.rs | 15 +++--- sdks/rust/Cargo.toml | 1 + 59 files changed, 389 insertions(+), 278 deletions(-) create mode 100644 crates/schema/src/reducer_name.rs create mode 100644 crates/schema/src/table_name.rs diff --git a/Cargo.lock b/Cargo.lock index bc2343a7cdc..da28ef6a5bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1906,6 +1906,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "ecow" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78e4f79b296fbaab6ce2e22d52cb4c7f010fe0ebe7a32e34fa25885fd797bd02" + [[package]] name = "educe" version = "0.4.23" @@ -8075,6 +8081,7 @@ dependencies = [ "spacetimedb-lib 1.12.0", "spacetimedb-physical-plan", "spacetimedb-primitives 1.12.0", + "spacetimedb-schema", "spacetimedb-sql-parser", "spacetimedb-table", ] @@ -8153,6 +8160,7 @@ version = "1.12.0" dependencies = [ "anyhow", "derive_more 0.99.20", + "ecow", "enum-as-inner", "enum-map", "indexmap 2.12.0", @@ -8202,6 +8210,7 @@ dependencies = [ "spacetimedb-metrics", "spacetimedb-query-builder", "spacetimedb-sats 1.12.0", + "spacetimedb-schema", "spacetimedb-testing", "thiserror 1.0.69", "tokio", @@ -8304,6 +8313,7 @@ dependencies = [ "spacetimedb-physical-plan", "spacetimedb-primitives 1.12.0", "spacetimedb-query", + "spacetimedb-schema", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9b2a4b29070..d1b57371642 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -179,6 +179,7 @@ derive_more = "0.99" dialoguer = { version = "0.11", default-features = false } dirs = "5.0.1" duct = "0.13.5" +ecow = "0.2.6" either = "1.9" email_address = "0.2.4" enum-as-inner = "0.6" diff --git a/crates/bench/benches/special.rs b/crates/bench/benches/special.rs index 2b8acc2c27c..f9ec7c842a9 100644 --- a/crates/bench/benches/special.rs +++ b/crates/bench/benches/special.rs @@ -8,6 +8,7 @@ use spacetimedb_bench::{ use spacetimedb_lib::sats::{self, bsatn}; use spacetimedb_lib::{bsatn::ToBsatn as _, ProductValue}; use spacetimedb_schema::schema::TableSchema; +use spacetimedb_schema::table_name::TableName; use spacetimedb_table::page_pool::PagePool; use spacetimedb_testing::modules::{Csharp, ModuleLanguage, Rust}; use std::sync::Arc; @@ -136,7 +137,7 @@ fn serialize_benchmarks< }); let mut table_schema = TableSchema::from_product_type(T::product_type()); - table_schema.table_name = name.into(); + table_schema.table_name = TableName::new_from_str(name); let mut table = spacetimedb_table::table::Table::new( Arc::new(table_schema), spacetimedb_table::indexes::SquashedOffset::COMMITTED_STATE, diff --git a/crates/bench/benches/subscription.rs b/crates/bench/benches/subscription.rs index 065916f78b2..48bb452a02c 100644 --- a/crates/bench/benches/subscription.rs +++ b/crates/bench/benches/subscription.rs @@ -19,6 +19,7 @@ use spacetimedb_primitives::{col_list, TableId}; use spacetimedb_query::compile_subscription; use spacetimedb_sats::{bsatn, product, AlgebraicType, AlgebraicValue, ProductValue}; +use spacetimedb_schema::table_name::TableName; #[cfg(not(target_env = "msvc"))] use tikv_jemallocator::Jemalloc; @@ -54,7 +55,7 @@ fn create_table_footprint(db: &RelationalDB) -> Result { fn insert_op(table_id: TableId, table_name: &str, row: ProductValue) -> DatabaseTableUpdate { DatabaseTableUpdate { table_id, - table_name: table_name.into(), + table_name: TableName::new_from_str(table_name), inserts: [row].into(), deletes: [].into(), } diff --git a/crates/bench/src/schemas.rs b/crates/bench/src/schemas.rs index b97d423d4ad..04b4fcb049a 100644 --- a/crates/bench/src/schemas.rs +++ b/crates/bench/src/schemas.rs @@ -3,6 +3,7 @@ use serde::Deserialize; use spacetimedb_lib::de::Deserialize as SatsDeserializer; use spacetimedb_lib::sats; +use spacetimedb_schema::table_name::TableName; use std::fmt::Debug; use std::hash::Hash; @@ -159,11 +160,11 @@ impl IndexStrategy { } } -pub fn table_name(style: IndexStrategy) -> String { +pub fn table_name(style: IndexStrategy) -> TableName { let prefix = style.name(); let name = T::name(); - format!("{prefix}_{name}") + TableName::new_from_str(&format!("{prefix}_{name}")) } // ---------- data synthesis ---------- diff --git a/crates/bench/src/spacetime_module.rs b/crates/bench/src/spacetime_module.rs index f106ec32e26..3aefcbdc5d1 100644 --- a/crates/bench/src/spacetime_module.rs +++ b/crates/bench/src/spacetime_module.rs @@ -7,6 +7,7 @@ use spacetimedb_lib::{ }; use spacetimedb_paths::RootDir; use spacetimedb_primitives::ColId; +use spacetimedb_schema::table_name::TableName; use spacetimedb_testing::modules::{start_runtime, LoggerRecord, ModuleHandle, ModuleLanguage}; use tokio::runtime::Runtime; @@ -192,6 +193,6 @@ impl BenchDatabase for SpacetimeModule { #[derive(Debug, Clone)] pub struct TableId { - pascal_case: String, - snake_case: String, + pascal_case: TableName, + snake_case: TableName, } diff --git a/crates/bench/src/spacetime_raw.rs b/crates/bench/src/spacetime_raw.rs index 804dccfe88b..a606f7c17b8 100644 --- a/crates/bench/src/spacetime_raw.rs +++ b/crates/bench/src/spacetime_raw.rs @@ -42,7 +42,7 @@ impl BenchDatabase for SpacetimeRaw { let name = table_name::(index_strategy); self.db.with_auto_commit(Workload::Internal, |tx| { let mut table_schema = TableSchema::from_product_type(T::product_type()); - table_schema.table_name = name.clone().into(); + table_schema.table_name = name.clone(); let table_id = self.db.create_table(tx, table_schema)?; self.db.rename_table(tx, table_id, &name)?; match index_strategy { diff --git a/crates/bench/src/sqlite.rs b/crates/bench/src/sqlite.rs index b51f5958812..8bb288d4dbe 100644 --- a/crates/bench/src/sqlite.rs +++ b/crates/bench/src/sqlite.rs @@ -8,6 +8,7 @@ use rusqlite::Connection; use spacetimedb_data_structures::map::HashMap; use spacetimedb_lib::sats::{AlgebraicType, AlgebraicValue, ProductType}; use spacetimedb_primitives::ColId; +use spacetimedb_schema::table_name::TableName; use std::{ fmt::Write, hint::black_box, @@ -47,7 +48,7 @@ impl BenchDatabase for SQLite { }) } - type TableId = String; + type TableId = TableName; /// We derive the SQLite schema from the AlgebraicType of the table. fn create_table( diff --git a/crates/codegen/src/rust.rs b/crates/codegen/src/rust.rs index 082cce7a339..47c8097aa37 100644 --- a/crates/codegen/src/rust.rs +++ b/crates/codegen/src/rust.rs @@ -11,6 +11,7 @@ use spacetimedb_lib::sats::layout::PrimitiveType; use spacetimedb_lib::sats::AlgebraicTypeRef; use spacetimedb_schema::def::{ModuleDef, ProcedureDef, ReducerDef, ScopedTypeName, TableDef, TypeDef}; use spacetimedb_schema::identifier::Identifier; +use spacetimedb_schema::reducer_name::ReducerName; use spacetimedb_schema::schema::TableSchema; use spacetimedb_schema::type_for_generate::{AlgebraicTypeDef, AlgebraicTypeUse}; use std::collections::BTreeSet; @@ -1089,19 +1090,19 @@ fn table_access_trait_name(table_name: &Identifier) -> String { table_name.deref().to_case(Case::Pascal) + "TableAccess" } -fn function_args_type_name(function_name: &Identifier) -> String { - function_name.deref().to_case(Case::Pascal) + "Args" +fn function_args_type_name(function_name: &str) -> String { + function_name.to_case(Case::Pascal) + "Args" } -fn reducer_variant_name(reducer_name: &Identifier) -> String { +fn reducer_variant_name(reducer_name: &ReducerName) -> String { reducer_name.deref().to_case(Case::Pascal) } -fn reducer_callback_id_name(reducer_name: &Identifier) -> String { +fn reducer_callback_id_name(reducer_name: &ReducerName) -> String { reducer_name.deref().to_case(Case::Pascal) + "CallbackId" } -fn reducer_module_name(reducer_name: &Identifier) -> String { +fn reducer_module_name(reducer_name: &ReducerName) -> String { reducer_name.deref().to_case(Case::Snake) + "_reducer" } diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index 5059330882a..e153d756996 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -17,6 +17,7 @@ use spacetimedb_lib::sats::AlgebraicTypeRef; use spacetimedb_primitives::ColId; use spacetimedb_schema::def::{ConstraintDef, IndexDef, ModuleDef, ReducerDef, ScopedTypeName, TableDef, TypeDef}; use spacetimedb_schema::identifier::Identifier; +use spacetimedb_schema::reducer_name::ReducerName; use spacetimedb_schema::schema::TableSchema; use spacetimedb_schema::type_for_generate::{AlgebraicTypeDef, AlgebraicTypeUse, ProductTypeDef}; @@ -213,7 +214,7 @@ impl Lang for TypeScript { for reducer in iter_reducers(module) { let reducer_name = &reducer.name; let reducer_module_name = reducer_module_name(reducer_name); - let args_type = reducer_args_type_name(&reducer.name); + let args_type = reducer_args_type_name(reducer_name); writeln!(out, "import {args_type} from \"./{reducer_module_name}\";"); writeln!(out, "export {{ {args_type} }};"); } @@ -818,7 +819,7 @@ fn table_module_name(table_name: &Identifier) -> String { table_name.deref().to_case(Case::Snake) + "_table" } -fn reducer_args_type_name(reducer_name: &Identifier) -> String { +fn reducer_args_type_name(reducer_name: &ReducerName) -> String { reducer_name.deref().to_case(Case::Pascal) + "Reducer" } @@ -826,7 +827,7 @@ fn procedure_args_type_name(reducer_name: &Identifier) -> String { reducer_name.deref().to_case(Case::Pascal) + "Procedure" } -fn reducer_module_name(reducer_name: &Identifier) -> String { +fn reducer_module_name(reducer_name: &ReducerName) -> String { reducer_name.deref().to_case(Case::Snake) + "_reducer" } diff --git a/crates/core/src/client/message_handlers.rs b/crates/core/src/client/message_handlers.rs index 313b87d0281..416b629fca5 100644 --- a/crates/core/src/client/message_handlers.rs +++ b/crates/core/src/client/message_handlers.rs @@ -11,6 +11,7 @@ use spacetimedb_datastore::execution_context::WorkloadType; use spacetimedb_lib::de::serde::DeserializeWrapper; use spacetimedb_lib::identity::RequestId; use spacetimedb_lib::{bsatn, ConnectionId, Timestamp}; +use spacetimedb_schema::reducer_name::ReducerName; use std::borrow::Cow; use std::sync::Arc; use std::time::{Duration, Instant}; @@ -178,7 +179,7 @@ impl MessageExecutionError { caller_identity: self.caller_identity, caller_connection_id: self.caller_connection_id, function_call: ModuleFunctionCall { - reducer: self.reducer.unwrap_or_else(|| "".into()).into(), + reducer: ReducerName::new_from_str(&self.reducer.unwrap_or_else(|| "".into())), reducer_id: self.reducer_id.unwrap_or(u32::MAX.into()), args: Default::default(), }, diff --git a/crates/core/src/client/messages.rs b/crates/core/src/client/messages.rs index 4511afb9ec2..59994bf2b06 100644 --- a/crates/core/src/client/messages.rs +++ b/crates/core/src/client/messages.rs @@ -7,6 +7,7 @@ use crate::subscription::row_list_builder_pool::BsatnRowListBuilderPool; use crate::subscription::websocket_building::{brotli_compress, decide_compression, gzip_compress}; use bytes::{BufMut, Bytes, BytesMut}; use bytestring::ByteString; +use core::ops::Deref; use derive_more::From; use spacetimedb_client_api_messages::websocket::{ BsatnFormat, Compression, FormatSwitch, JsonFormat, OneOffTable, RowListLen, WebsocketFormat, @@ -18,6 +19,7 @@ use spacetimedb_lib::ser::serde::SerializeWrapper; use spacetimedb_lib::{AlgebraicValue, ConnectionId, TimeDuration, Timestamp}; use spacetimedb_primitives::TableId; use spacetimedb_sats::bsatn; +use spacetimedb_schema::table_name::TableName; use std::sync::Arc; use std::time::Instant; @@ -281,7 +283,7 @@ impl ToProtocol for TransactionUpdateMessage { status, caller_identity: event.caller_identity, reducer_call: ws::ReducerCallInfo { - reducer_name: event.function_call.reducer.to_owned().into(), + reducer_name: event.function_call.reducer.deref().into(), reducer_id: event.function_call.reducer_id.into(), args, request_id, @@ -378,7 +380,7 @@ pub struct SubscriptionData { #[derive(Debug, Clone)] pub struct SubscriptionRows { pub table_id: TableId, - pub table_name: Box, + pub table_name: TableName, pub table_rows: FormatSwitch, ws::TableUpdate>, } @@ -449,7 +451,7 @@ impl ToProtocol for SubscriptionMessage { query_id, rows: ws::SubscribeRows { table_id: result.table_id, - table_name: result.table_name, + table_name: result.table_name.to_boxed_str(), table_rows, }, } @@ -462,7 +464,7 @@ impl ToProtocol for SubscriptionMessage { query_id, rows: ws::SubscribeRows { table_id: result.table_id, - table_name: result.table_name, + table_name: result.table_name.to_boxed_str(), table_rows, }, } @@ -480,7 +482,7 @@ impl ToProtocol for SubscriptionMessage { query_id, rows: ws::SubscribeRows { table_id: result.table_id, - table_name: result.table_name, + table_name: result.table_name.to_boxed_str(), table_rows, }, } @@ -493,7 +495,7 @@ impl ToProtocol for SubscriptionMessage { query_id, rows: ws::SubscribeRows { table_id: result.table_id, - table_name: result.table_name, + table_name: result.table_name.to_boxed_str(), table_rows, }, } diff --git a/crates/core/src/db/durability.rs b/crates/core/src/db/durability.rs index 5f925824953..a3e6c1de989 100644 --- a/crates/core/src/db/durability.rs +++ b/crates/core/src/db/durability.rs @@ -254,6 +254,7 @@ mod tests { use futures::FutureExt as _; use pretty_assertions::assert_matches; use spacetimedb_sats::product; + use spacetimedb_schema::table_name::TableName; use tokio::sync::watch; use super::*; @@ -317,7 +318,7 @@ mod tests { let mut txdata = TxData::default(); txdata.set_tx_offset(i); // Ensure the transaction is non-empty. - txdata.set_inserts_for_table(4000.into(), "foo", [product![42u8]].into()); + txdata.set_inserts_for_table(4000.into(), &TableName::new_from_str("foo"), [product![42u8]].into()); worker.request_durability(None, &Arc::new(txdata)); } diff --git a/crates/core/src/db/mod.rs b/crates/core/src/db/mod.rs index 993588004e7..a86ab0bfbf5 100644 --- a/crates/core/src/db/mod.rs +++ b/crates/core/src/db/mod.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use enum_map::EnumMap; +use spacetimedb_schema::reducer_name::ReducerName; use tokio::sync::mpsc; use crate::subscription::ExecutionCounters; @@ -37,7 +38,7 @@ pub struct Config { /// We use a separate task to record metrics to avoid blocking transactions. pub struct MetricsMessage { /// The reducer the produced these metrics. - reducer: String, + reducer: ReducerName, /// Metrics from a mutable transaction. metrics_for_writer: Option, /// Metrics from a read-only transaction. @@ -60,7 +61,7 @@ pub struct MetricsRecorderQueue { impl MetricsRecorderQueue { pub fn send_metrics( &self, - reducer: String, + reducer: ReducerName, metrics_for_writer: Option, metrics_for_reader: Option, tx_data: Option>, diff --git a/crates/core/src/db/relational_db.rs b/crates/core/src/db/relational_db.rs index b409a8121cd..e8301163eea 100644 --- a/crates/core/src/db/relational_db.rs +++ b/crates/core/src/db/relational_db.rs @@ -46,6 +46,7 @@ use spacetimedb_sats::algebraic_type::fmt::fmt_algebraic_type; use spacetimedb_sats::memory_usage::MemoryUsage; use spacetimedb_sats::{AlgebraicType, AlgebraicValue, ProductType, ProductValue}; use spacetimedb_schema::def::{ModuleDef, TableDef, ViewDef}; +use spacetimedb_schema::reducer_name::ReducerName; use spacetimedb_schema::schema::{ ColumnSchema, IndexSchema, RowLevelSecuritySchema, Schema, SequenceSchema, TableSchema, }; @@ -769,7 +770,7 @@ impl RelationalDB { } #[tracing::instrument(level = "trace", skip_all)] - pub fn rollback_mut_tx(&self, tx: MutTx) -> (TxOffset, TxMetrics, String) { + pub fn rollback_mut_tx(&self, tx: MutTx) -> (TxOffset, TxMetrics, ReducerName) { log::trace!("ROLLBACK MUT TX"); self.inner.rollback_mut_tx(tx) } @@ -781,14 +782,14 @@ impl RelationalDB { } #[tracing::instrument(level = "trace", skip_all)] - pub fn release_tx(&self, tx: Tx) -> (TxOffset, TxMetrics, String) { + pub fn release_tx(&self, tx: Tx) -> (TxOffset, TxMetrics, ReducerName) { log::trace!("RELEASE TX"); self.inner.release_tx(tx) } #[tracing::instrument(level = "trace", skip_all)] #[allow(clippy::type_complexity)] - pub fn commit_tx(&self, tx: MutTx) -> Result, TxMetrics, String)>, DBError> { + pub fn commit_tx(&self, tx: MutTx) -> Result, TxMetrics, ReducerName)>, DBError> { log::trace!("COMMIT MUT TX"); let reducer_context = tx.ctx.reducer_context().cloned(); @@ -1002,7 +1003,7 @@ impl RelationalDB { /// Should only be called after the tx lock has been fully released. pub(crate) fn report_tx_metrics( &self, - reducer: String, + reducer: ReducerName, tx_data: Option>, metrics_for_writer: Option, metrics_for_reader: Option, @@ -1469,12 +1470,12 @@ impl RelationalDB { } /// Reports the metrics for `reducer`, using counters provided by `db`. - pub fn report_mut_tx_metrics(&self, reducer: String, metrics: TxMetrics, tx_data: Option>) { + pub fn report_mut_tx_metrics(&self, reducer: ReducerName, metrics: TxMetrics, tx_data: Option>) { self.report_tx_metrics(reducer, tx_data, Some(metrics), None); } /// Reports subscription metrics for `reducer`, using counters provided by `db`. - pub fn report_read_tx_metrics(&self, reducer: String, metrics: TxMetrics) { + pub fn report_read_tx_metrics(&self, reducer: ReducerName, metrics: TxMetrics) { self.report_tx_metrics(reducer, None, None, Some(metrics)); } @@ -3277,7 +3278,7 @@ mod tests { let timestamp = Timestamp::now(); let workload = |name: &str| { Workload::Reducer(ReducerContext { - name: name.into(), + name: ReducerName::new_from_str(name), caller_identity: Identity::__dummy(), caller_connection_id: ConnectionId::ZERO, timestamp, @@ -3468,9 +3469,9 @@ mod tests { arg_bsatn, } = ReducerContext::try_from(&input).unwrap(); if i == 0 { - assert_eq!(reducer_name, "__identity_connected__"); + assert_eq!(&*reducer_name, "__identity_connected__"); } else { - assert_eq!(reducer_name, "abstract_concrete_proxy_factory_impl"); + assert_eq!(&*reducer_name, "abstract_concrete_proxy_factory_impl"); } assert!( arg_bsatn.is_empty(), diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index f635585145e..7b3ed973d2e 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -11,6 +11,7 @@ use spacetimedb_expr::errors::TypingError; use spacetimedb_fs_utils::lockfile::advisory::LockError; use spacetimedb_lib::Identity; use spacetimedb_schema::error::ValidationErrors; +use spacetimedb_schema::table_name::TableName; use spacetimedb_snapshot::SnapshotError; use spacetimedb_table::table::ReadViaBsatnError; use thiserror::Error; @@ -59,11 +60,14 @@ pub enum PlanError { #[error("Qualified Table `{expect}` not found")] TableNotFoundQualified { expect: String }, #[error("Unknown field: `{field}` not found in the table(s): `{tables:?}`")] - UnknownField { field: String, tables: Vec> }, + UnknownField { field: String, tables: Vec }, #[error("Unknown field name: `{field}` not found in the table(s): `{tables:?}`")] - UnknownFieldName { field: FieldName, tables: Vec> }, + UnknownFieldName { field: FieldName, tables: Vec }, #[error("Field(s): `{fields:?}` not found in the table(s): `{tables:?}`")] - UnknownFields { fields: Vec, tables: Vec> }, + UnknownFields { + fields: Vec, + tables: Vec, + }, #[error("Ambiguous field: `{field}`. Also found in {found:?}")] AmbiguousField { field: String, found: Vec }, #[error("Plan error: `{0}`")] diff --git a/crates/core/src/host/module_host.rs b/crates/core/src/host/module_host.rs index 40f425ba13e..5d819caa2a3 100644 --- a/crates/core/src/host/module_host.rs +++ b/crates/core/src/host/module_host.rs @@ -60,7 +60,9 @@ use spacetimedb_query::compile_subscription; use spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, ProductValue}; use spacetimedb_schema::auto_migrate::{AutoMigrateError, MigrationPolicy}; use spacetimedb_schema::def::{ModuleDef, ProcedureDef, ReducerDef, TableDef, ViewDef}; +use spacetimedb_schema::reducer_name::ReducerName; use spacetimedb_schema::schema::{Schema, TableSchema}; +use spacetimedb_schema::table_name::TableName; use spacetimedb_vm::relation::RelValue; use std::collections::{HashSet, VecDeque}; use std::fmt; @@ -112,7 +114,7 @@ impl DatabaseUpdate { #[derive(Debug, Clone, PartialEq, Eq)] pub struct DatabaseTableUpdate { pub table_id: TableId, - pub table_name: Box, + pub table_name: TableName, // Note: `Arc<[ProductValue]>` allows to cheaply // use the values from `TxData` without cloning the // contained `ProductValue`s. @@ -128,7 +130,7 @@ pub struct DatabaseUpdateRelValue<'a> { #[derive(PartialEq, Debug)] pub struct DatabaseTableUpdateRelValue<'a> { pub table_id: TableId, - pub table_name: Box, + pub table_name: TableName, pub updates: UpdatesRelValue<'a>, } @@ -180,7 +182,7 @@ impl EventStatus { #[derive(Debug, Clone, Default)] pub struct ModuleFunctionCall { - pub reducer: String, + pub reducer: ReducerName, pub reducer_id: ReducerId, pub args: ArgsTuple, } @@ -188,7 +190,7 @@ pub struct ModuleFunctionCall { impl ModuleFunctionCall { pub fn update() -> Self { Self { - reducer: String::from("update"), + reducer: ReducerName::new_from_str("update"), reducer_id: u32::MAX.into(), args: ArgsTuple::nullary(), } @@ -2074,6 +2076,8 @@ impl ModuleHost { .map(PipelinedProject::from) .collect::>(); + let table_name = table_name.to_boxed_str(); + if returns_view_table && num_private_cols > 0 { let optimized = optimized .into_iter() diff --git a/crates/core/src/host/scheduler.rs b/crates/core/src/host/scheduler.rs index 5a46ae01bef..6812f93c950 100644 --- a/crates/core/src/host/scheduler.rs +++ b/crates/core/src/host/scheduler.rs @@ -433,11 +433,11 @@ pub(super) async fn call_scheduled_function( // Background: Scheduled reducers start with Workload::Internal, but // call_reducer_with_tx only sets ReducerContext when tx is None. // Since we pass Some(tx), we must set it here. - let reducer_name = &*module_info.module_def.reducer_by_id(params.reducer_id).name; + let reducer_name = &module_info.module_def.reducer_by_id(params.reducer_id).name; tx.ctx = ExecutionContext::with_workload( tx.ctx.database_identity(), Workload::Reducer(ReducerContext { - name: reducer_name.into(), + name: reducer_name.clone(), caller_identity: params.caller_identity, caller_connection_id: params.caller_connection_id, timestamp: params.timestamp, diff --git a/crates/core/src/host/v8/mod.rs b/crates/core/src/host/v8/mod.rs index 4792b3005d4..01cf840cf10 100644 --- a/crates/core/src/host/v8/mod.rs +++ b/crates/core/src/host/v8/mod.rs @@ -886,6 +886,7 @@ mod test { use crate::host::ArgsTuple; use spacetimedb_lib::{ConnectionId, Identity}; use spacetimedb_primitives::ReducerId; + use spacetimedb_schema::reducer_name::ReducerName; fn with_module_catch( code: &str, @@ -909,7 +910,7 @@ mod test { let hooks = get_hooks(scope).unwrap(); let op = ReducerOp { id: ReducerId(42), - name: "foobar", + name: &ReducerName::new_from_str("foobar"), caller_identity: &Identity::ONE, caller_connection_id: &ConnectionId::ZERO, timestamp: Timestamp::from_micros_since_unix_epoch(24), diff --git a/crates/core/src/host/wasm_common/module_host_actor.rs b/crates/core/src/host/wasm_common/module_host_actor.rs index 6952e4687dd..fedbcd31921 100644 --- a/crates/core/src/host/wasm_common/module_host_actor.rs +++ b/crates/core/src/host/wasm_common/module_host_actor.rs @@ -51,6 +51,7 @@ use spacetimedb_sats::{AlgebraicType, AlgebraicTypeRef, Deserialize, ProductValu use spacetimedb_schema::auto_migrate::{MigratePlan, MigrationPolicy, MigrationPolicyError}; use spacetimedb_schema::def::deserialize::FunctionDef; use spacetimedb_schema::def::{ModuleDef, ViewDef}; +use spacetimedb_schema::reducer_name::ReducerName; use spacetimedb_subscription::SubscriptionPlan; use std::sync::Arc; use tracing::span::EnteredSpan; @@ -798,7 +799,7 @@ impl InstanceCommon { let stdb = &*replica_ctx.relational_db.clone(); let info = self.info.clone(); let reducer_def = info.module_def.reducer_by_id(reducer_id); - let reducer_name = &*reducer_def.name; + let reducer_name = &reducer_def.name; // Do some `with_label_values`. // TODO(perf, centril): consider caching this. @@ -905,7 +906,7 @@ impl InstanceCommon { caller_identity, caller_connection_id: caller_connection_id_opt, function_call: ModuleFunctionCall { - reducer: reducer_name.to_string(), + reducer: reducer_name.clone(), reducer_id, args, }, @@ -1620,7 +1621,7 @@ impl InstanceOp for AnonymousViewOp<'_> { #[derive(Clone, Debug)] pub struct ReducerOp<'a> { pub id: ReducerId, - pub name: &'a str, + pub name: &'a ReducerName, pub caller_identity: &'a Identity, pub caller_connection_id: &'a ConnectionId, pub timestamp: Timestamp, @@ -1652,7 +1653,7 @@ impl From> for execution_context::ReducerContext { }: ReducerOp<'_>, ) -> Self { Self { - name: name.to_owned(), + name: name.clone(), caller_identity: *caller_identity, caller_connection_id: *caller_connection_id, timestamp, diff --git a/crates/core/src/sql/ast.rs b/crates/core/src/sql/ast.rs index 26c52d9b126..d9a9f48dca2 100644 --- a/crates/core/src/sql/ast.rs +++ b/crates/core/src/sql/ast.rs @@ -12,6 +12,7 @@ use spacetimedb_sats::{AlgebraicType, AlgebraicValue}; use spacetimedb_schema::def::error::RelationError; use spacetimedb_schema::relation::{ColExpr, FieldName}; use spacetimedb_schema::schema::{ColumnSchema, TableOrViewSchema, TableSchema}; +use spacetimedb_schema::table_name::TableName; use spacetimedb_vm::errors::ErrorVm; use spacetimedb_vm::expr::{Expr, FieldExpr, FieldOp}; use spacetimedb_vm::operator::{OpCmp, OpLogic, OpQuery}; @@ -170,7 +171,7 @@ impl From { } /// Returns all the table names as a `Vec`, including the ones inside the joins. - pub fn table_names(&self) -> Vec> { + pub fn table_names(&self) -> Vec { self.iter_tables().map(|x| x.table_name.clone()).collect() } diff --git a/crates/core/src/sql/execute.rs b/crates/core/src/sql/execute.rs index 62d2bc00780..11e63d79a85 100644 --- a/crates/core/src/sql/execute.rs +++ b/crates/core/src/sql/execute.rs @@ -137,7 +137,7 @@ pub fn execute_sql( caller_identity: auth.caller(), caller_connection_id: None, function_call: ModuleFunctionCall { - reducer: String::new(), + reducer: <_>::default(), reducer_id: u32::MAX.into(), args: ArgsTuple::default(), }, @@ -342,7 +342,7 @@ fn run_inner( caller_identity: auth.caller(), caller_connection_id: None, function_call: ModuleFunctionCall { - reducer: String::new(), + reducer: <_>::default(), reducer_id: u32::MAX.into(), args: ArgsTuple::default(), }, diff --git a/crates/core/src/subscription/execution_unit.rs b/crates/core/src/subscription/execution_unit.rs index 0f11f0ff01d..f4fc7b8e85d 100644 --- a/crates/core/src/subscription/execution_unit.rs +++ b/crates/core/src/subscription/execution_unit.rs @@ -17,6 +17,7 @@ use spacetimedb_primitives::TableId; use spacetimedb_sats::{u256, ProductValue}; use spacetimedb_schema::def::error::AuthError; use spacetimedb_schema::relation::DbTable; +use spacetimedb_schema::table_name::TableName; use spacetimedb_vm::eval::IterRows; use spacetimedb_vm::expr::{AuthAccess, NoInMemUsed, Query, QueryExpr, SourceExpr, SourceId}; use spacetimedb_vm::rel_ops::RelOps; @@ -203,8 +204,8 @@ impl ExecutionUnit { self.return_db_table().table_id } - pub fn return_name(&self) -> Box { - self.return_db_table().head.table_name.clone() + pub fn return_name(&self) -> &TableName { + &self.return_db_table().head.table_name } /// The table on which this query filters rows. @@ -260,7 +261,7 @@ impl ExecutionUnit { let update = F::into_query_update(qu, compression); TableUpdate::new( self.return_table(), - self.return_name(), + self.return_name().to_boxed_str(), SingleQueryUpdate { update, num_rows }, ) }) @@ -283,7 +284,7 @@ impl ExecutionUnit { updates.has_updates().then(|| DatabaseTableUpdateRelValue { table_id: self.return_table(), - table_name: self.return_name(), + table_name: self.return_name().clone(), updates, }) } diff --git a/crates/core/src/subscription/metrics.rs b/crates/core/src/subscription/metrics.rs index 9abe3e12430..b367a57056b 100644 --- a/crates/core/src/subscription/metrics.rs +++ b/crates/core/src/subscription/metrics.rs @@ -1,5 +1,5 @@ use spacetimedb_physical_plan::plan::PhysicalPlan; -use spacetimedb_schema::schema::TableSchema; +use spacetimedb_schema::{schema::TableSchema, table_name::TableName}; use std::sync::Arc; /// Scan strategy types for subscription queries @@ -21,7 +21,7 @@ enum ScanStrategy { #[derive(Debug)] pub struct QueryMetrics { pub scan_type: String, - pub table_name: String, + pub table_name: TableName, pub unindexed_columns: String, pub rows_scanned: u64, pub execution_time_micros: u64, @@ -70,7 +70,7 @@ fn extract_columns( /// Analyzes subscription scan strategy and creates QueryMetrics pub fn get_query_metrics( - table_name: &str, + table_name: TableName, plan: &PhysicalPlan, rows_scanned: u64, execution_time_micros: u64, @@ -112,7 +112,7 @@ pub fn get_query_metrics( QueryMetrics { scan_type: strategy.to_string(), - table_name: table_name.to_string(), + table_name, unindexed_columns: columns.join(","), rows_scanned, execution_time_micros, diff --git a/crates/core/src/subscription/mod.rs b/crates/core/src/subscription/mod.rs index 32abd7fec9d..c63a8d65c80 100644 --- a/crates/core/src/subscription/mod.rs +++ b/crates/core/src/subscription/mod.rs @@ -18,6 +18,7 @@ use spacetimedb_lib::{metrics::ExecutionMetrics, Identity}; use spacetimedb_primitives::TableId; use spacetimedb_sats::bsatn::ToBsatn; use spacetimedb_sats::Serialize; +use spacetimedb_schema::table_name::TableName; use std::sync::Arc; pub mod delta; @@ -174,7 +175,7 @@ pub enum TableUpdateType { pub fn collect_table_update_for_view( plan_fragments: &[ViewProject], table_id: TableId, - table_name: Box, + table_name: TableName, tx: &Tx, update_type: TableUpdateType, rlb_pool: &impl RowListBuilderSource, @@ -200,7 +201,11 @@ where // There's no need to compress the inner table update too. let update = F::into_query_update(qu, Compression::None); ( - TableUpdate::new(table_id, table_name, SingleQueryUpdate { update, num_rows }), + TableUpdate::new( + table_id, + table_name.to_boxed_str(), + SingleQueryUpdate { update, num_rows }, + ), metrics, ) }) @@ -210,7 +215,7 @@ where pub fn collect_table_update( plan_fragments: &[PipelinedProject], table_id: TableId, - table_name: Box, + table_name: TableName, tx: &(impl Datastore + DeltaStore), update_type: TableUpdateType, rlb_pool: &impl RowListBuilderSource, @@ -232,7 +237,11 @@ pub fn collect_table_update( // There's no need to compress the inner table update too. let update = F::into_query_update(qu, Compression::None); ( - TableUpdate::new(table_id, table_name, SingleQueryUpdate { update, num_rows }), + TableUpdate::new( + table_id, + table_name.to_boxed_str(), + SingleQueryUpdate { update, num_rows }, + ), metrics, ) }) @@ -268,7 +277,7 @@ pub fn execute_plans( collect_table_update_for_view( &[view_plan], table_id, - (&**table_name).into(), + table_name.clone(), tx, update_type, rlb_pool, @@ -278,7 +287,7 @@ pub fn execute_plans( collect_table_update( &[pipelined_plan], table_id, - (&**table_name).into(), + table_name.clone(), tx, update_type, rlb_pool, @@ -289,7 +298,7 @@ pub fn execute_plans( collect_table_update( &[pipelined_plan], table_id, - (&**table_name).into(), + table_name.clone(), tx, update_type, rlb_pool, @@ -300,7 +309,7 @@ pub fn execute_plans( let (ref _table_update, ref metrics) = result; let query_metrics = metrics::get_query_metrics( - table_name, + table_name.clone(), &plan, metrics.rows_scanned as u64, elapsed.as_micros() as u64, diff --git a/crates/core/src/subscription/module_subscription_actor.rs b/crates/core/src/subscription/module_subscription_actor.rs index 38df7cea554..1b420860965 100644 --- a/crates/core/src/subscription/module_subscription_actor.rs +++ b/crates/core/src/subscription/module_subscription_actor.rs @@ -332,7 +332,7 @@ impl ModuleSubscriptions { )?; let table_id = query.subscribed_table_id(); - let table_name = query.subscribed_table_name(); + let table_name = query.subscribed_table_name().clone(); let plans = query .plans_fragments() @@ -364,27 +364,13 @@ impl ModuleSubscriptions { .map(PipelinedProject::from) .map(|plan| ViewProject::new(plan, num_cols, view_info.num_private_cols())) .collect::>(); - collect_table_update_for_view( - &plans, - table_id, - table_name.into(), - &tx, - update_type, - &self.bsatn_rlb_pool, - ) - .map(|(table_update, metrics)| (FormatSwitch::Bsatn(table_update), metrics)) + collect_table_update_for_view(&plans, table_id, table_name, &tx, update_type, &self.bsatn_rlb_pool) + .map(|(table_update, metrics)| (FormatSwitch::Bsatn(table_update), metrics)) } (Protocol::Binary, None) => { let plans = plans.into_iter().map(PipelinedProject::from).collect::>(); - collect_table_update( - &plans, - table_id, - table_name.into(), - &tx, - update_type, - &self.bsatn_rlb_pool, - ) - .map(|(table_update, metrics)| (FormatSwitch::Bsatn(table_update), metrics)) + collect_table_update(&plans, table_id, table_name, &tx, update_type, &self.bsatn_rlb_pool) + .map(|(table_update, metrics)| (FormatSwitch::Bsatn(table_update), metrics)) } (Protocol::Text, Some(view_info)) => { let plans = plans @@ -395,7 +381,7 @@ impl ModuleSubscriptions { collect_table_update_for_view( &plans, table_id, - table_name.into(), + table_name, &tx, update_type, &JsonRowListBuilderFakePool, @@ -407,7 +393,7 @@ impl ModuleSubscriptions { collect_table_update( &plans, table_id, - table_name.into(), + table_name, &tx, update_type, &JsonRowListBuilderFakePool, @@ -590,7 +576,7 @@ impl ModuleSubscriptions { timer: Some(timer), result: SubscriptionResult::Subscribe(SubscriptionRows { table_id: query.subscribed_table_id(), - table_name: query.subscribed_table_name().into(), + table_name: query.subscribed_table_name().clone(), table_rows, }), }, @@ -663,7 +649,7 @@ impl ModuleSubscriptions { timer: Some(timer), result: SubscriptionResult::Unsubscribe(SubscriptionRows { table_id: query.subscribed_table_id(), - table_name: query.subscribed_table_name().into(), + table_name: query.subscribed_table_name().clone(), table_rows, }), }, diff --git a/crates/core/src/subscription/module_subscription_manager.rs b/crates/core/src/subscription/module_subscription_manager.rs index a97f282eb0d..cd8deeb3bd4 100644 --- a/crates/core/src/subscription/module_subscription_manager.rs +++ b/crates/core/src/subscription/module_subscription_manager.rs @@ -29,7 +29,8 @@ use spacetimedb_expr::expr::CollectViews; use spacetimedb_lib::metrics::ExecutionMetrics; use spacetimedb_lib::{AlgebraicValue, ConnectionId, Identity, ProductValue}; use spacetimedb_primitives::{ColId, IndexId, TableId, ViewId}; -use spacetimedb_subscription::{JoinEdge, SubscriptionPlan, TableName}; +use spacetimedb_schema::table_name::TableName; +use spacetimedb_subscription::{JoinEdge, SubscriptionPlan}; use std::collections::BTreeMap; use std::fmt::Debug; use std::sync::atomic::{AtomicBool, Ordering}; @@ -84,7 +85,7 @@ impl Plan { /// A subscription query return rows from a single table. /// This method returns the name of that table. - pub fn subscribed_table_name(&self) -> &str { + pub fn subscribed_table_name(&self) -> &TableName { self.plans[0].subscribed_table_name() } @@ -1553,14 +1554,15 @@ impl SendWorker { .filter(|upd| !clients_with_errors.contains(&upd.id)) // Do the aggregation. .fold(client_table_id_updates, |mut tables, upd| { + let table_name = upd.table_name.to_boxed_str(); match tables.entry((upd.id, upd.table_id)) { Entry::Occupied(mut entry) => match entry.get_mut().zip_mut(upd.update) { Bsatn((tbl_upd, update)) => tbl_upd.push(update), Json((tbl_upd, update)) => tbl_upd.push(update), }, Entry::Vacant(entry) => drop(entry.insert(match upd.update { - Bsatn(update) => Bsatn(TableUpdate::new(upd.table_id, (&*upd.table_name).into(), update)), - Json(update) => Json(TableUpdate::new(upd.table_id, (&*upd.table_name).into(), update)), + Bsatn(update) => Bsatn(TableUpdate::new(upd.table_id, table_name, update)), + Json(update) => Json(TableUpdate::new(upd.table_id, table_name, update)), })), } tables @@ -1667,6 +1669,8 @@ mod tests { use spacetimedb_lib::{error::ResultTest, identity::AuthCtx, AlgebraicType, ConnectionId, Identity, Timestamp}; use spacetimedb_primitives::{ColId, TableId}; use spacetimedb_sats::product; + use spacetimedb_schema::reducer_name::ReducerName; + use spacetimedb_schema::table_name::TableName; use spacetimedb_subscription::SubscriptionPlan; use tokio::sync::oneshot; @@ -2176,7 +2180,7 @@ mod tests { // select * from t let table_update = DatabaseTableUpdate { table_id, - table_name: "t".into(), + table_name: TableName::new_from_str("t"), inserts: [product![2u8]].into(), deletes: [product![3u8]].into(), }; @@ -2194,7 +2198,7 @@ mod tests { // Only: select * from t let table_update = DatabaseTableUpdate { table_id, - table_name: "t".into(), + table_name: TableName::new_from_str("t"), inserts: [product![8u8]].into(), deletes: [product![9u8]].into(), }; @@ -2235,7 +2239,7 @@ mod tests { // Therefore we must evaluate it for any update on `t`. let table_update = DatabaseTableUpdate { table_id: t_id, - table_name: "t".into(), + table_name: TableName::new_from_str("t"), inserts: [product![0u8, 0u8]].into(), deletes: [].into(), }; @@ -2251,7 +2255,7 @@ mod tests { // Yes, because `s.a = 1`. let table_update = DatabaseTableUpdate { table_id: s_id, - table_name: "s".into(), + table_name: TableName::new_from_str("s"), inserts: [product![0u8, 1u8]].into(), deletes: [].into(), }; @@ -2267,7 +2271,7 @@ mod tests { // No, because `s.a != 1`. let table_update = DatabaseTableUpdate { table_id: s_id, - table_name: "s".into(), + table_name: TableName::new_from_str("s"), inserts: [product![0u8, 2u8]].into(), deletes: [].into(), }; @@ -2522,7 +2526,7 @@ mod tests { caller_identity: id0, caller_connection_id: Some(client0.id.connection_id), function_call: ModuleFunctionCall { - reducer: "DummyReducer".into(), + reducer: ReducerName::new_from_str("DummyReducer"), reducer_id: u32::MAX.into(), args: ArgsTuple::nullary(), }, diff --git a/crates/core/src/subscription/query.rs b/crates/core/src/subscription/query.rs index eef449f3db5..e28329b1eab 100644 --- a/crates/core/src/subscription/query.rs +++ b/crates/core/src/subscription/query.rs @@ -175,6 +175,7 @@ mod tests { use spacetimedb_sats::{product, AlgebraicType, ProductType, ProductValue}; use spacetimedb_schema::relation::FieldName; use spacetimedb_schema::schema::*; + use spacetimedb_schema::table_name::TableName; use spacetimedb_vm::eval::run_ast; use spacetimedb_vm::eval::test_helpers::{mem_table, mem_table_without_table_name, scalar}; use spacetimedb_vm::expr::{Expr, SourceSet}; @@ -204,7 +205,7 @@ mod tests { fn insert_op(table_id: TableId, table_name: &str, row: ProductValue) -> DatabaseTableUpdate { DatabaseTableUpdate { table_id, - table_name: table_name.into(), + table_name: TableName::new_from_str(table_name), deletes: [].into(), inserts: [row].into(), } @@ -213,7 +214,7 @@ mod tests { fn delete_op(table_id: TableId, table_name: &str, row: ProductValue) -> DatabaseTableUpdate { DatabaseTableUpdate { table_id, - table_name: table_name.into(), + table_name: TableName::new_from_str(table_name), deletes: [row].into(), inserts: [].into(), } @@ -241,7 +242,7 @@ mod tests { let data = DatabaseTableUpdate { table_id: schema.table_id, - table_name: table_name.into(), + table_name: TableName::new_from_str(table_name), deletes: [].into(), inserts: [row.clone()].into(), }; @@ -448,7 +449,7 @@ mod tests { let update = DatabaseUpdate { tables: [DatabaseTableUpdate { table_id, - table_name: "test".into(), + table_name: TableName::new_from_str("test"), deletes: deletes.into(), inserts: [].into(), }] @@ -531,7 +532,7 @@ mod tests { let data = DatabaseTableUpdate { table_id: schema.table_id, - table_name: "inventory".into(), + table_name: TableName::new_from_str("inventory"), deletes: [].into(), inserts: [row.clone()].into(), }; @@ -645,14 +646,14 @@ mod tests { let data1 = DatabaseTableUpdate { table_id: schema_1.table_id, - table_name: "inventory".into(), + table_name: TableName::new_from_str("inventory"), deletes: [row_1].into(), inserts: [].into(), }; let data2 = DatabaseTableUpdate { table_id: schema_2.table_id, - table_name: "player".into(), + table_name: TableName::new_from_str("player"), deletes: [].into(), inserts: [row_2].into(), }; @@ -1015,7 +1016,7 @@ mod tests { result.tables[0], DatabaseTableUpdate { table_id: lhs_id, - table_name: "lhs".into(), + table_name: TableName::new_from_str("lhs"), deletes: [lhs_old].into(), inserts: [lhs_new].into(), }, @@ -1094,8 +1095,7 @@ mod tests { let (data, _, tx) = db.commit_tx_downgrade(tx, Workload::ForTests); let table_id = plan.subscribed_table_id(); - // This awful construction to convert `Arc` into `Box`. - let table_name = (&**plan.subscribed_table_name()).into(); + let table_name = plan.subscribed_table_name().clone(); let tx = DeltaTx::new(&tx, &data, &QueriedTableIndexIds::from_iter(plan.index_ids())); // IMPORTANT: FOR TESTING ONLY! @@ -1437,7 +1437,7 @@ mod tests { result.tables[0], DatabaseTableUpdate { table_id: lhs_id, - table_name: "lhs".into(), + table_name: TableName::new_from_str("lhs"), deletes: [lhs_old].into(), inserts: [lhs_new].into(), }, diff --git a/crates/core/src/subscription/subscription.rs b/crates/core/src/subscription/subscription.rs index d96aee8fc93..0fcb203bc17 100644 --- a/crates/core/src/subscription/subscription.rs +++ b/crates/core/src/subscription/subscription.rs @@ -43,6 +43,7 @@ use spacetimedb_sats::ProductValue; use spacetimedb_schema::def::error::AuthError; use spacetimedb_schema::relation::DbTable; use spacetimedb_schema::schema::TableSchema; +use spacetimedb_schema::table_name::TableName; use spacetimedb_subscription::SubscriptionPlan; use spacetimedb_vm::expr::{self, AuthAccess, IndexJoin, Query, QueryExpr, SourceExpr, SourceProvider, SourceSet}; use spacetimedb_vm::rel_ops::RelOps; @@ -81,8 +82,8 @@ impl SupportedQuery { self.expr.source.get_db_table().unwrap().table_id } - pub fn return_name(&self) -> String { - self.expr.source.table_name().to_owned() + pub fn return_name(&self) -> &TableName { + self.expr.source.table_name() } /// This is the same as the return table unless this is a join. @@ -711,7 +712,7 @@ mod tests { panic!("unexpected result from compilation: {exp:#?}"); }; - assert_eq!(expr.source.table_name(), "lhs"); + assert_eq!(&**expr.source.table_name(), "lhs"); assert_eq!(expr.query.len(), 1); let join = expr.query.pop().unwrap(); @@ -726,7 +727,7 @@ mod tests { let (expr, _sources) = with_delta_table(join, Some(delta), None); let expr: QueryExpr = expr.into(); let mut expr = expr.optimize(&|_, _| i64::MAX); - assert_eq!(expr.source.table_name(), "lhs"); + assert_eq!(&**expr.source.table_name(), "lhs"); assert_eq!(expr.query.len(), 1); let join = expr.query.pop().unwrap(); @@ -791,7 +792,7 @@ mod tests { panic!("unexpected result from compilation: {exp:#?}"); }; - assert_eq!(expr.source.table_name(), "lhs"); + assert_eq!(&**expr.source.table_name(), "lhs"); assert_eq!(expr.query.len(), 1); let join = expr.query.pop().unwrap(); @@ -807,7 +808,7 @@ mod tests { let expr = QueryExpr::from(expr); let mut expr = expr.optimize(&|_, _| i64::MAX); - assert_eq!(expr.source.table_name(), "lhs"); + assert_eq!(&**expr.source.table_name(), "lhs"); assert_eq!(expr.query.len(), 1); assert!(expr.source.is_db_table()); @@ -878,7 +879,7 @@ mod tests { panic!("unexpected result from compilation: {exp:#?}"); }; - assert_eq!(expr.source.table_name(), "lhs"); + assert_eq!(&**expr.source.table_name(), "lhs"); assert_eq!(expr.query.len(), 1); let src_join = &expr.query[0]; diff --git a/crates/core/src/vm.rs b/crates/core/src/vm.rs index 39b4c7f8506..3c07f901bf8 100644 --- a/crates/core/src/vm.rs +++ b/crates/core/src/vm.rs @@ -3,7 +3,7 @@ use crate::db::relational_db::{MutTx, RelationalDB, Tx}; use crate::error::DBError; use crate::estimation; -use core::ops::{Bound, RangeBounds}; +use core::ops::{Bound, Deref, RangeBounds}; use itertools::Itertools; use spacetimedb_data_structures::map::IntMap; use spacetimedb_datastore::execution_context::ExecutionContext; @@ -499,7 +499,7 @@ impl<'db, 'tx> DbProgram<'db, 'tx> { } let table_access = query.source.table_access(); - tracing::trace!(table = query.source.table_name()); + tracing::trace!(table = query.source.table_name().deref()); let head = query.head().clone(); let rows = build_query(self.db, self.tx, query, &mut |id| { @@ -656,6 +656,7 @@ pub(crate) mod tests { use spacetimedb_schema::def::{BTreeAlgorithm, IndexAlgorithm}; use spacetimedb_schema::relation::{FieldName, Header}; use spacetimedb_schema::schema::{ColumnSchema, IndexSchema, TableSchema}; + use spacetimedb_schema::table_name::TableName; use spacetimedb_vm::eval::run_ast; use spacetimedb_vm::eval::test_helpers::{mem_table, mem_table_one_u64, scalar}; use spacetimedb_vm::operator::OpCmp; @@ -685,7 +686,7 @@ pub(crate) mod tests { tx, TableSchema::new( TableId::SENTINEL, - table_name.into(), + TableName::new_from_str(table_name), None, columns, vec![], @@ -798,7 +799,7 @@ pub(crate) mod tests { .unwrap(); let st_table_row = StTableRow { table_id: ST_TABLE_ID, - table_name: ST_TABLE_NAME.into(), + table_name: TableName::new_from_str(ST_TABLE_NAME), table_type: StTableType::System, table_access: StAccess::Public, table_primary_key: Some(StTableFields::TableId.into()), diff --git a/crates/datastore/src/execution_context.rs b/crates/datastore/src/execution_context.rs index 25343f3d1fc..9c46d4e01ee 100644 --- a/crates/datastore/src/execution_context.rs +++ b/crates/datastore/src/execution_context.rs @@ -5,6 +5,7 @@ use derive_more::Display; use spacetimedb_commitlog::{payload::txdata, Varchar}; use spacetimedb_lib::{ConnectionId, Identity, Timestamp}; use spacetimedb_sats::bsatn; +use spacetimedb_schema::reducer_name::ReducerName; /// Represents the context under which a database runtime method is executed. /// In particular it provides details about the currently executing txn to runtime operations. @@ -25,7 +26,7 @@ pub struct ExecutionContext { #[derive(Clone)] pub struct ReducerContext { /// The name of the reducer. - pub name: String, + pub name: ReducerName, /// The [`Identity`] of the caller. pub caller_identity: Identity, /// The [`ConnectionId`] of the caller. @@ -49,7 +50,7 @@ impl From for txdata::Inputs { arg_bsatn, }: ReducerContext, ) -> Self { - let reducer_name = Arc::new(Varchar::from_string_truncate(name)); + let reducer_name = Arc::new(Varchar::from_str_truncate(&name)); let cap = arg_bsatn.len() /* caller_identity */ + 32 @@ -80,7 +81,7 @@ impl TryFrom<&txdata::Inputs> for ReducerContext { let timestamp = bsatn::from_reader(args)?; Ok(Self { - name: inputs.reducer_name.to_string(), + name: ReducerName::new_from_str(&inputs.reducer_name), caller_identity, caller_connection_id, timestamp, @@ -109,7 +110,7 @@ impl Workload { /// and the current timestamp. pub fn reducer_no_args(name: &str, id: Identity, conn_id: ConnectionId) -> Self { Self::Reducer(ReducerContext { - name: name.into(), + name: ReducerName::new_from_str(name), caller_identity: id, caller_connection_id: conn_id, timestamp: Timestamp::now(), @@ -186,13 +187,7 @@ impl ExecutionContext { /// If this is a reducer context, returns the name of the reducer. #[inline] - pub fn reducer_name(&self) -> &str { - self.reducer.as_ref().map(|ctx| ctx.name.as_str()).unwrap_or_default() - } - - /// If this is a reducer context, returns the name of the reducer. - #[inline] - pub fn into_reducer_name(self) -> String { + pub fn into_reducer_name(self) -> ReducerName { self.reducer.map(|ctx| ctx.name).unwrap_or_default() } diff --git a/crates/datastore/src/locking_tx_datastore/committed_state.rs b/crates/datastore/src/locking_tx_datastore/committed_state.rs index 8c156eb166c..bc23f9ae0ac 100644 --- a/crates/datastore/src/locking_tx_datastore/committed_state.rs +++ b/crates/datastore/src/locking_tx_datastore/committed_state.rs @@ -1126,7 +1126,7 @@ impl CommittedState { } if !deletes.is_empty() { - let table_name = &*table.get_schema().table_name; + let table_name = &table.get_schema().table_name; tx_data.set_deletes_for_table(table_id, table_name, deletes.into()); let truncated = table.row_count == 0; if truncated { @@ -1203,7 +1203,7 @@ impl CommittedState { // Add the table to `TxData` if there were insertions. if !inserts.is_empty() { - let table_name = &*commit_table.get_schema().table_name; + let table_name = &commit_table.get_schema().table_name; tx_data.set_inserts_for_table(table_id, table_name, inserts.into()); // if table has inserted rows, it cannot be truncated diff --git a/crates/datastore/src/locking_tx_datastore/datastore.rs b/crates/datastore/src/locking_tx_datastore/datastore.rs index 6c41ec483f0..04b8d3de3b9 100644 --- a/crates/datastore/src/locking_tx_datastore/datastore.rs +++ b/crates/datastore/src/locking_tx_datastore/datastore.rs @@ -39,7 +39,11 @@ use spacetimedb_sats::{ algebraic_value::de::ValueDeserializer, bsatn, buffer::BufReader, AlgebraicValue, ProductValue, }; use spacetimedb_sats::{memory_usage::MemoryUsage, Deserialize}; -use spacetimedb_schema::schema::{ColumnSchema, IndexSchema, SequenceSchema, TableSchema}; +use spacetimedb_schema::table_name::TableName; +use spacetimedb_schema::{ + reducer_name::ReducerName, + schema::{ColumnSchema, IndexSchema, SequenceSchema, TableSchema}, +}; use spacetimedb_snapshot::{ReconstructedSnapshot, SnapshotRepository}; use spacetimedb_table::{ indexes::RowPointer, @@ -389,7 +393,7 @@ impl Tx for Locking { /// - [`TxOffset`], the smallest transaction offset visible to this transaction. /// - [`TxMetrics`], various measurements of the work performed by this transaction. /// - `String`, the name of the reducer which ran within this transaction. - fn release_tx(&self, tx: Self::Tx) -> (TxOffset, TxMetrics, String) { + fn release_tx(&self, tx: Self::Tx) -> (TxOffset, TxMetrics, ReducerName) { tx.release() } } @@ -934,13 +938,13 @@ impl MutTx for Locking { } } - fn rollback_mut_tx(&self, tx: Self::MutTx) -> (TxOffset, TxMetrics, String) { + fn rollback_mut_tx(&self, tx: Self::MutTx) -> (TxOffset, TxMetrics, ReducerName) { tx.rollback() } /// This method only updates the in-memory `committed_state`. /// For durability, see `RelationalDB::commit_tx`. - fn commit_mut_tx(&self, tx: Self::MutTx) -> Result> { + fn commit_mut_tx(&self, tx: Self::MutTx) -> Result> { Ok(Some(tx.commit())) } } @@ -1088,7 +1092,7 @@ struct ReplayVisitor<'a, F> { // Since deletes are handled before truncation / drop, sometimes the schema // info is gone. We save the name on the first delete of that table so metrics // can still show a name. - dropped_table_names: IntMap>, + dropped_table_names: IntMap, error_behavior: ErrorBehavior, } @@ -1446,7 +1450,7 @@ mod tests { fn from(value: TableRow<'_>) -> Self { Self { table_id: value.id.into(), - table_name: value.name.into(), + table_name: TableName::new_from_str(value.name), table_type: value.ty, table_access: value.access, table_primary_key: value.primary_key.map(ColList::new), @@ -1597,7 +1601,7 @@ mod tests { ) -> TableSchema { TableSchema::new( TableId::SENTINEL, - "Foo".into(), + TableName::new_from_str("Foo"), None, cols.into(), indices.into(), diff --git a/crates/datastore/src/locking_tx_datastore/mut_tx.rs b/crates/datastore/src/locking_tx_datastore/mut_tx.rs index 05f98693503..b1b4a699ae6 100644 --- a/crates/datastore/src/locking_tx_datastore/mut_tx.rs +++ b/crates/datastore/src/locking_tx_datastore/mut_tx.rs @@ -58,7 +58,9 @@ use spacetimedb_sats::{ }; use spacetimedb_schema::{ def::{ModuleDef, ViewColumnDef, ViewDef, ViewParamDef}, + reducer_name::ReducerName, schema::{ColumnSchema, ConstraintSchema, IndexSchema, RowLevelSecuritySchema, SequenceSchema, TableSchema}, + table_name::TableName, }; use spacetimedb_table::{ blob_store::BlobStore, @@ -741,7 +743,7 @@ impl MutTxId { /// Insert a row into `st_view`, auto-increments and returns the [`ViewId`]. fn insert_into_st_view( &mut self, - view_name: Box, + view_name: TableName, table_id: TableId, is_public: bool, is_anonymous: bool, @@ -912,7 +914,7 @@ impl MutTxId { // TODO(centril): remove this. It doesn't seem to be used by anything. pub fn rename_table(&mut self, table_id: TableId, new_name: &str) -> Result<()> { // Update the table's name in st_tables. - self.update_st_table_row(table_id, |st| st.table_name = new_name.into()) + self.update_st_table_row(table_id, |st| st.table_name = TableName::new_from_str(new_name)) } fn update_st_table_row(&mut self, table_id: TableId, updater: impl FnOnce(&mut StTableRow) -> R) -> Result { @@ -1933,7 +1935,7 @@ impl MutTxId { /// - [`TxData`], the set of inserts and deletes performed by this transaction. /// - [`TxMetrics`], various measurements of the work performed by this transaction. /// - `String`, the name of the reducer which ran during this transaction. - pub(super) fn commit(mut self) -> (TxOffset, TxData, TxMetrics, String) { + pub(super) fn commit(mut self) -> (TxOffset, TxData, TxMetrics, ReducerName) { let tx_offset = self.committed_state_write_lock.next_tx_offset; let tx_data = self .committed_state_write_lock @@ -2019,7 +2021,7 @@ impl MutTxId { /// Returns: /// - [`TxMetrics`], various measurements of the work performed by this transaction. /// - `String`, the name of the reducer which ran during this transaction. - pub fn rollback(mut self) -> (TxOffset, TxMetrics, String) { + pub fn rollback(mut self) -> (TxOffset, TxMetrics, ReducerName) { let offset = self .committed_state_write_lock .rollback(&mut self.sequence_state_lock, self.tx_state); diff --git a/crates/datastore/src/locking_tx_datastore/tx.rs b/crates/datastore/src/locking_tx_datastore/tx.rs index 6a720496cd8..0edba3cc0c7 100644 --- a/crates/datastore/src/locking_tx_datastore/tx.rs +++ b/crates/datastore/src/locking_tx_datastore/tx.rs @@ -10,7 +10,7 @@ use spacetimedb_execution::Datastore; use spacetimedb_lib::metrics::ExecutionMetrics; use spacetimedb_primitives::{ColList, IndexId, TableId}; use spacetimedb_sats::AlgebraicValue; -use spacetimedb_schema::schema::TableSchema; +use spacetimedb_schema::{reducer_name::ReducerName, schema::TableSchema}; use spacetimedb_table::{ table::{IndexScanPointIter, IndexScanRangeIter, TableAndIndex, TableScanIter}, table_index::IndexCannotSeekRange, @@ -149,7 +149,7 @@ impl TxId { /// - [`TxOffset`], the smallest transaction offset visible to this transaction. /// - [`TxMetrics`], various measurements of the work performed by this transaction. /// - `String`, the name of the reducer which ran within this transaction. - pub(super) fn release(self) -> (TxOffset, TxMetrics, String) { + pub(super) fn release(self) -> (TxOffset, TxMetrics, ReducerName) { // A read tx doesn't consume `next_tx_offset`, so subtract one to obtain // the offset that was visible to the transaction. // diff --git a/crates/datastore/src/system_tables.rs b/crates/datastore/src/system_tables.rs index 14658890cde..ddbf0bc601b 100644 --- a/crates/datastore/src/system_tables.rs +++ b/crates/datastore/src/system_tables.rs @@ -31,6 +31,7 @@ use spacetimedb_schema::schema::{ ColumnSchema, ConstraintSchema, IndexSchema, RowLevelSecuritySchema, ScheduleSchema, Schema, SequenceSchema, TableSchema, }; +use spacetimedb_schema::table_name::TableName; use spacetimedb_table::table::RowRef; use std::borrow::Cow; use std::cell::RefCell; @@ -830,7 +831,7 @@ pub(crate) fn system_table_schema(table_id: TableId) -> Option { #[sats(crate = spacetimedb_lib)] pub struct StTableRow { pub table_id: TableId, - pub table_name: Box, + pub table_name: TableName, pub table_type: StTableType, pub table_access: StAccess, /// The primary key of the table. @@ -863,7 +864,7 @@ pub struct StViewRow { /// An auto-inc id for each view pub view_id: ViewId, /// The name of the view function as defined in the module - pub view_name: Box, + pub view_name: TableName, /// The [`TableId`] for this view if materialized. /// Currently all views are materialized and therefore are assigned a [`TableId`] by default. pub table_id: Option, diff --git a/crates/datastore/src/traits.rs b/crates/datastore/src/traits.rs index af99a36ceb8..a2e6f5880ea 100644 --- a/crates/datastore/src/traits.rs +++ b/crates/datastore/src/traits.rs @@ -14,7 +14,9 @@ use spacetimedb_lib::{hash_bytes, Identity}; use spacetimedb_primitives::*; use spacetimedb_sats::hash::Hash; use spacetimedb_sats::{AlgebraicValue, ProductType, ProductValue}; +use spacetimedb_schema::reducer_name::ReducerName; use spacetimedb_schema::schema::{IndexSchema, SequenceSchema, TableSchema}; +use spacetimedb_schema::table_name::TableName; use spacetimedb_table::static_assert_size; use spacetimedb_table::table::RowRef; @@ -169,9 +171,6 @@ pub enum IsolationLevel { pub type EphemeralTables = IntSet; -type TableName = Box; -type TableNameRef<'a> = &'a str; - /// The [`TxData`] entry for one table. /// /// All information about a table is stored in one place @@ -255,30 +254,20 @@ impl TxData { /// Ensures that an entry for `table_id` exists /// or initializes it with `table_name`. - fn init_entry(&mut self, table_id: TableId, table_name: TableNameRef<'_>) -> &mut TxDataTableEntry { + fn init_entry(&mut self, table_id: TableId, table_name: &TableName) -> &mut TxDataTableEntry { self.entries - .get_or_insert(table_id, || TxDataTableEntry::new(table_name.into())) + .get_or_insert(table_id, || TxDataTableEntry::new(table_name.clone())) } /// Set `rows` as the inserted rows for `(table_id, table_name)`. - pub fn set_inserts_for_table( - &mut self, - table_id: TableId, - table_name: TableNameRef<'_>, - rows: Arc<[ProductValue]>, - ) { + pub fn set_inserts_for_table(&mut self, table_id: TableId, table_name: &TableName, rows: Arc<[ProductValue]>) { self.init_entry(table_id, table_name).inserts = rows; } /// Set `rows` as the deleted rows for `(table_id, table_name)`. /// /// When `truncated` is set, the table has been emptied in this transaction. - pub fn set_deletes_for_table( - &mut self, - table_id: TableId, - table_name: TableNameRef<'_>, - rows: Arc<[ProductValue]>, - ) { + pub fn set_deletes_for_table(&mut self, table_id: TableId, table_name: &TableName, rows: Arc<[ProductValue]>) { self.init_entry(table_id, table_name).deletes = rows; } @@ -462,8 +451,8 @@ pub trait Tx { /// observed transactions. /// /// - [`TxMetrics`], various measurements of the work performed by this transaction. - /// - `String`, the name of the reducer which ran within this transaction. - fn release_tx(&self, tx: Self::Tx) -> (TxOffset, TxMetrics, String); + /// - `ReducerName`, the name of the reducer which ran within this transaction. + fn release_tx(&self, tx: Self::Tx) -> (TxOffset, TxMetrics, ReducerName); } pub trait MutTx { @@ -487,15 +476,15 @@ pub trait MutTx { /// /// - [`TxData`], the set of inserts and deletes performed by this transaction. /// - [`TxMetrics`], various measurements of the work performed by this transaction. - /// - `String`, the name of the reducer which ran during this transaction. - fn commit_mut_tx(&self, tx: Self::MutTx) -> Result>; + /// - `ReducerName`, the name of the reducer which ran during this transaction. + fn commit_mut_tx(&self, tx: Self::MutTx) -> Result>; /// Rolls back this transaction, discarding its changes. /// /// Returns: /// - [`TxMetrics`], various measurements of the work performed by this transaction. - /// - `String`, the name of the reducer which ran within this transaction. - fn rollback_mut_tx(&self, tx: Self::MutTx) -> (TxOffset, TxMetrics, String); + /// - `ReducerName`, the name of the reducer which ran within this transaction. + fn rollback_mut_tx(&self, tx: Self::MutTx) -> (TxOffset, TxMetrics, ReducerName); } /// Standard metadata associated with a database. diff --git a/crates/expr/src/statement.rs b/crates/expr/src/statement.rs index 46db2b02053..c31e5160075 100644 --- a/crates/expr/src/statement.rs +++ b/crates/expr/src/statement.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use spacetimedb_lib::{identity::AuthCtx, st_var::StVarValue, AlgebraicType, AlgebraicValue, ProductValue}; use spacetimedb_primitives::{ColId, TableId}; use spacetimedb_schema::schema::{ColumnSchema, TableOrViewSchema}; +use spacetimedb_schema::table_name::TableName; use spacetimedb_sql_parser::{ ast::{ sql::{SqlAst, SqlDelete, SqlInsert, SqlSelect, SqlSet, SqlShow, SqlUpdate}, @@ -53,8 +54,8 @@ impl DML { } /// Returns the name of the table on which this mutation applies - pub fn table_name(&self) -> Box { - self.table_schema().table_name.clone() + pub fn table_name(&self) -> &TableName { + &self.table_schema().table_name } } diff --git a/crates/physical-plan/src/dml.rs b/crates/physical-plan/src/dml.rs index e1d84aad305..680ec8e4efe 100644 --- a/crates/physical-plan/src/dml.rs +++ b/crates/physical-plan/src/dml.rs @@ -1,3 +1,4 @@ +use core::ops::Deref; use std::sync::Arc; use anyhow::Result; @@ -61,7 +62,7 @@ impl DeletePlan { pub(crate) fn compile(delete: TableDelete) -> Self { let TableDelete { table, filter } = delete; let schema = table.clone(); - let alias = table.table_name.clone(); + let alias = table.table_name.deref().into(); let relvar = RelExpr::RelVar(Relvar { schema, alias, @@ -95,7 +96,7 @@ impl UpdatePlan { pub(crate) fn compile(update: TableUpdate) -> Self { let TableUpdate { table, columns, filter } = update; let schema = table.clone(); - let alias = table.table_name.clone(); + let alias = table.table_name.deref().into(); let relvar = RelExpr::RelVar(Relvar { schema, alias, diff --git a/crates/physical-plan/src/plan.rs b/crates/physical-plan/src/plan.rs index db919235e97..7b0c143e2ca 100644 --- a/crates/physical-plan/src/plan.rs +++ b/crates/physical-plan/src/plan.rs @@ -1427,6 +1427,7 @@ mod tests { use spacetimedb_schema::{ def::{BTreeAlgorithm, ConstraintData, IndexAlgorithm, UniqueConstraintData}, schema::{ColumnSchema, ConstraintSchema, IndexSchema, TableOrViewSchema, TableSchema}, + table_name::TableName, }; use spacetimedb_sql_parser::ast::BinOp; @@ -1468,7 +1469,7 @@ mod tests { ) -> TableOrViewSchema { TableOrViewSchema::from(Arc::new(TableSchema::new( table_id, - table_name.to_owned().into_boxed_str(), + TableName::new_from_str(table_name), None, columns .iter() diff --git a/crates/query/Cargo.toml b/crates/query/Cargo.toml index 87a465ba0fb..7de32844fd5 100644 --- a/crates/query/Cargo.toml +++ b/crates/query/Cargo.toml @@ -16,6 +16,7 @@ spacetimedb-expr.workspace = true spacetimedb-lib.workspace = true spacetimedb-primitives.workspace = true spacetimedb-physical-plan.workspace = true +spacetimedb-schema.workspace = true spacetimedb-sql-parser.workspace = true spacetimedb-table.workspace = true diff --git a/crates/query/src/lib.rs b/crates/query/src/lib.rs index d75f2516a2c..4a1bea08c9c 100644 --- a/crates/query/src/lib.rs +++ b/crates/query/src/lib.rs @@ -16,6 +16,7 @@ use spacetimedb_physical_plan::{ plan::{ProjectListPlan, ProjectPlan}, }; use spacetimedb_primitives::TableId; +use spacetimedb_schema::table_name::TableName; /// DIRTY HACK ALERT: Maximum allowed length, in UTF-8 bytes, of SQL queries. /// Any query longer than this will be rejected. @@ -26,7 +27,7 @@ pub fn compile_subscription( sql: &str, tx: &impl SchemaView, auth: &AuthCtx, -) -> Result<(Vec, TableId, Box, bool)> { +) -> Result<(Vec, TableId, TableName, bool)> { if sql.len() > MAX_SQL_LENGTH { bail!("SQL query exceeds maximum allowed length: \"{sql:.120}...\"") } diff --git a/crates/schema/Cargo.toml b/crates/schema/Cargo.toml index c49482f70b5..a0e14bb2f74 100644 --- a/crates/schema/Cargo.toml +++ b/crates/schema/Cargo.toml @@ -19,6 +19,7 @@ spacetimedb-sql-parser.workspace = true anyhow.workspace = true derive_more.workspace = true +ecow.workspace = true indexmap.workspace = true itertools.workspace = true lazy_static.workspace = true diff --git a/crates/schema/src/def.rs b/crates/schema/src/def.rs index 3000b41a108..4f24720d66d 100644 --- a/crates/schema/src/def.rs +++ b/crates/schema/src/def.rs @@ -21,6 +21,7 @@ use std::hash::Hash; use crate::error::{IdentifierError, ValidationErrors}; use crate::identifier::Identifier; +use crate::reducer_name::ReducerName; use crate::schema::{Schema, TableSchema}; use crate::type_for_generate::{AlgebraicTypeUse, ProductTypeDef, TypespaceForGenerate}; use deserialize::ArgsSeed; @@ -1277,7 +1278,7 @@ impl From for RawMiscModuleExportV9 { #[non_exhaustive] pub struct ReducerDef { /// The name of the reducer. This must be unique within the module's set of reducers and procedures. - pub name: Identifier, + pub name: ReducerName, /// The parameters of the reducer. /// @@ -1296,7 +1297,7 @@ pub struct ReducerDef { impl From for RawReducerDefV9 { fn from(val: ReducerDef) -> Self { RawReducerDefV9 { - name: val.name.into(), + name: val.name.to_string().into(), params: val.params, lifecycle: val.lifecycle, } @@ -1492,13 +1493,14 @@ impl ModuleDefLookup for TypeDef { } impl ModuleDefLookup for ReducerDef { - type Key<'a> = &'a Identifier; + type Key<'a> = &'a ReducerName; fn key(&self) -> Self::Key<'_> { &self.name } fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> { + let key = &**key; module_def.reducers.get(key) } } diff --git a/crates/schema/src/def/error.rs b/crates/schema/src/def/error.rs index 7acdfd3a937..d1008bbd7a7 100644 --- a/crates/schema/src/def/error.rs +++ b/crates/schema/src/def/error.rs @@ -1,4 +1,5 @@ use crate::relation::{FieldName, Header}; +use crate::table_name::TableName; use derive_more::Display; use spacetimedb_lib::db::raw_def::IndexType; use spacetimedb_primitives::{ColId, ColList, TableId}; @@ -114,12 +115,12 @@ pub enum SchemaError { #[error("{ty} {name} columns `{columns:?}` not found in table `{table}`")] ColumnsNotFound { name: Box, - table: Box, + table: TableName, columns: Vec, ty: DefType, }, #[error("table `{table}` {ty} should have name. {ty} id: {id}")] - EmptyName { table: Box, ty: DefType, id: u32 }, + EmptyName { table: TableName, ty: DefType, id: u32 }, #[error("table `{table}` have `Constraints::unset()` for columns: {columns:?}")] ConstraintUnset { table: Box, @@ -127,7 +128,7 @@ pub enum SchemaError { columns: ColList, }, #[error("Attempt to define a column with more than 1 auto_inc sequence: Table: `{table}`, Field: `{field}`")] - OneAutoInc { table: Box, field: Box }, + OneAutoInc { table: TableName, field: Box }, #[error("Only Btree Indexes are supported: Table: `{table}`, Index: `{index}` is a `{index_type}`")] OnlyBtree { table: Box, diff --git a/crates/schema/src/def/validate/v8.rs b/crates/schema/src/def/validate/v8.rs index 2f469cc1eaa..382f0c43280 100644 --- a/crates/schema/src/def/validate/v8.rs +++ b/crates/schema/src/def/validate/v8.rs @@ -554,11 +554,11 @@ mod tests { assert_eq!(def.types[&deliveries_type_name].ty, delivery_def.product_type_ref); let init_name = expect_identifier(INIT_NAME); - assert_eq!(def.reducers[&init_name].name, init_name); + assert_eq!(&*def.reducers[&init_name].name, &*init_name); assert_eq!(def.reducers[&init_name].lifecycle, Some(Lifecycle::Init)); let identity_connected_name = expect_identifier(IDENTITY_CONNECTED_NAME); - assert_eq!(def.reducers[&identity_connected_name].name, identity_connected_name); + assert_eq!(&*def.reducers[&identity_connected_name].name, &*identity_connected_name); assert_eq!( def.reducers[&identity_connected_name].lifecycle, Some(Lifecycle::OnConnect) @@ -566,8 +566,8 @@ mod tests { let identity_disconnected_name = expect_identifier(IDENTITY_DISCONNECTED_NAME); assert_eq!( - def.reducers[&identity_disconnected_name].name, - identity_disconnected_name + &*def.reducers[&identity_disconnected_name].name, + &*identity_disconnected_name ); assert_eq!( def.reducers[&identity_disconnected_name].lifecycle, @@ -575,7 +575,7 @@ mod tests { ); let extra_reducer_name = expect_identifier("extra_reducer"); - assert_eq!(def.reducers[&extra_reducer_name].name, extra_reducer_name); + assert_eq!(&*def.reducers[&extra_reducer_name].name, &*extra_reducer_name); assert_eq!(def.reducers[&extra_reducer_name].lifecycle, None); assert_eq!( def.reducers[&extra_reducer_name].params, @@ -583,7 +583,7 @@ mod tests { ); let check_deliveries_name = expect_identifier("check_deliveries"); - assert_eq!(def.reducers[&check_deliveries_name].name, check_deliveries_name); + assert_eq!(&*def.reducers[&check_deliveries_name].name, &*check_deliveries_name); assert_eq!(def.reducers[&check_deliveries_name].lifecycle, None); assert_eq!( def.reducers[&check_deliveries_name].params, diff --git a/crates/schema/src/def/validate/v9.rs b/crates/schema/src/def/validate/v9.rs index 77db786b067..1499a52c01e 100644 --- a/crates/schema/src/def/validate/v9.rs +++ b/crates/schema/src/def/validate/v9.rs @@ -46,11 +46,7 @@ pub fn validate(def: RawModuleDefV9) -> Result { let reducers = reducers .into_iter() .enumerate() - .map(|(idx, reducer)| { - validator - .validate_reducer_def(reducer, ReducerId(idx as u32)) - .map(|reducer_def| (reducer_def.name.clone(), reducer_def)) - }) + .map(|(idx, reducer)| validator.validate_reducer_def(reducer, ReducerId(idx as u32))) // Collect into a `Vec` first to preserve duplicate names. // Later on, in `check_function_names_are_unique`, we'll transform this into an `IndexMap`. .collect_all_errors::>(); @@ -370,7 +366,11 @@ impl ModuleValidator<'_> { } /// Validate a reducer definition. - fn validate_reducer_def(&mut self, reducer_def: RawReducerDefV9, reducer_id: ReducerId) -> Result { + fn validate_reducer_def( + &mut self, + reducer_def: RawReducerDefV9, + reducer_id: ReducerId, + ) -> Result<(Identifier, ReducerDef)> { let RawReducerDefV9 { name, params, @@ -397,8 +397,9 @@ impl ModuleValidator<'_> { Some(_) => Err(ValidationError::DuplicateLifecycle { lifecycle }.into()), }) .transpose(); - let (name, params_for_generate, lifecycle) = (name, params_for_generate, lifecycle).combine_errors()?; - Ok(ReducerDef { + let (reducer_name, params_for_generate, lifecycle) = (name, params_for_generate, lifecycle).combine_errors()?; + let name = ReducerName::new_from_str(&reducer_name); + let def = ReducerDef { name, params: params.clone(), params_for_generate: ProductTypeDef { @@ -406,7 +407,8 @@ impl ModuleValidator<'_> { recursive: false, // A ProductTypeDef not stored in a Typespace cannot be recursive. }, lifecycle, - }) + }; + Ok((reducer_name, def)) } fn validate_procedure_def(&mut self, procedure_def: RawProcedureDefV9) -> Result { @@ -1600,22 +1602,22 @@ mod tests { assert_eq!(def.types[&deliveries_type_name].ty, delivery_def.product_type_ref); let init_name = expect_identifier("init"); - assert_eq!(def.reducers[&init_name].name, init_name); + assert_eq!(&*def.reducers[&init_name].name, &*init_name); assert_eq!(def.reducers[&init_name].lifecycle, Some(Lifecycle::Init)); let on_connect_name = expect_identifier("on_connect"); - assert_eq!(def.reducers[&on_connect_name].name, on_connect_name); + assert_eq!(&*def.reducers[&on_connect_name].name, &*on_connect_name); assert_eq!(def.reducers[&on_connect_name].lifecycle, Some(Lifecycle::OnConnect)); let on_disconnect_name = expect_identifier("on_disconnect"); - assert_eq!(def.reducers[&on_disconnect_name].name, on_disconnect_name); + assert_eq!(&*def.reducers[&on_disconnect_name].name, &*on_disconnect_name); assert_eq!( def.reducers[&on_disconnect_name].lifecycle, Some(Lifecycle::OnDisconnect) ); let extra_reducer_name = expect_identifier("extra_reducer"); - assert_eq!(def.reducers[&extra_reducer_name].name, extra_reducer_name); + assert_eq!(&*def.reducers[&extra_reducer_name].name, &*extra_reducer_name); assert_eq!(def.reducers[&extra_reducer_name].lifecycle, None); assert_eq!( def.reducers[&extra_reducer_name].params, @@ -1623,7 +1625,7 @@ mod tests { ); let check_deliveries_name = expect_identifier("check_deliveries"); - assert_eq!(def.reducers[&check_deliveries_name].name, check_deliveries_name); + assert_eq!(&*def.reducers[&check_deliveries_name].name, &*check_deliveries_name); assert_eq!(def.reducers[&check_deliveries_name].lifecycle, None); assert_eq!( def.reducers[&check_deliveries_name].params, diff --git a/crates/schema/src/lib.rs b/crates/schema/src/lib.rs index 923301fc5a7..aa703e37b2d 100644 --- a/crates/schema/src/lib.rs +++ b/crates/schema/src/lib.rs @@ -6,6 +6,8 @@ pub mod auto_migrate; pub mod def; pub mod error; pub mod identifier; +pub mod reducer_name; pub mod relation; pub mod schema; +pub mod table_name; pub mod type_for_generate; diff --git a/crates/schema/src/reducer_name.rs b/crates/schema/src/reducer_name.rs new file mode 100644 index 00000000000..189170cc910 --- /dev/null +++ b/crates/schema/src/reducer_name.rs @@ -0,0 +1,50 @@ +use core::ops::Deref; +use core::{borrow::Borrow, fmt}; +use ecow::EcoString; +use spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st, AlgebraicType}; + +/// The name of a reducer. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct ReducerName( + // TODO(perf, centril): Use this sort of optimization + // in RawIdentifier and `Identifier` and more places. + // TODO(perf): Consider `lean_string` instead for `&'static str` optimization. + // This could be useful in e.g., `SumType` and friends. + pub EcoString, +); + +impl_st!([] ReducerName, _ts => AlgebraicType::String); +impl_serialize!([] ReducerName, (self, ser) => ser.serialize_str(&self.0)); +impl_deserialize!([] ReducerName, de => >::deserialize(de).map(|s| Self(EcoString::from(s.as_ref())))); + +impl ReducerName { + pub fn new_from_str(name: &str) -> Self { + Self(EcoString::from(name)) + } +} + +impl Deref for ReducerName { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl AsRef for ReducerName { + fn as_ref(&self) -> &str { + &self.0 + } +} + +impl Borrow for ReducerName { + fn borrow(&self) -> &str { + &self.0 + } +} + +impl fmt::Display for ReducerName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", &self.0) + } +} diff --git a/crates/schema/src/relation.rs b/crates/schema/src/relation.rs index 539f48515d8..e8e32ccff4e 100644 --- a/crates/schema/src/relation.rs +++ b/crates/schema/src/relation.rs @@ -1,4 +1,5 @@ use crate::def::error::{RelationError, TypeError}; +use crate::table_name::TableName; use core::fmt; use core::hash::Hash; use derive_more::From; @@ -92,7 +93,7 @@ impl Column { #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct Header { pub table_id: TableId, - pub table_name: Box, + pub table_name: TableName, pub fields: Vec, pub constraints: BTreeMap, } @@ -103,7 +104,7 @@ impl Header { /// `uncombined_constraints` will be normalized using [`combine_constraints`]. pub fn new( table_id: TableId, - table_name: Box, + table_name: TableName, fields: Vec, uncombined_constraints: impl IntoIterator, ) -> Self { @@ -310,7 +311,7 @@ mod tests { let id = id.into(); let fields = [fields.0, fields.1].map(|col| Column::new(FieldName::new(id, col), AlgebraicType::I8)); - Header::new(id, name.into(), fields.into(), ct) + Header::new(id, TableName::new_from_str(name), fields.into(), ct) } #[test] diff --git a/crates/schema/src/schema.rs b/crates/schema/src/schema.rs index 58c1ec16a22..0a8219e5e7d 100644 --- a/crates/schema/src/schema.rs +++ b/crates/schema/src/schema.rs @@ -8,7 +8,9 @@ use crate::def::error::{DefType, SchemaError}; use crate::relation::{combine_constraints, Column, DbTable, FieldName, Header}; +use crate::table_name::TableName; use core::mem; +use core::ops::Deref; use itertools::Itertools; use spacetimedb_lib::db::auth::{StAccess, StTableType}; use spacetimedb_lib::db::raw_def::v9::RawSql; @@ -68,7 +70,7 @@ impl ViewDefInfo { pub struct TableOrViewSchema { pub table_id: TableId, pub view_info: Option, - pub table_name: Box, + pub table_name: TableName, pub table_access: StAccess, inner: Arc, } @@ -151,8 +153,7 @@ pub struct TableSchema { pub table_id: TableId, /// The name of the table. - // TODO(perf): This should likely be an `Arc`, not a `Box`, as we `Clone` it somewhat frequently. - pub table_name: Box, + pub table_name: TableName, /// Is this the backing table of a view? pub view_info: Option, @@ -201,7 +202,7 @@ impl TableSchema { #[allow(clippy::too_many_arguments)] pub fn new( table_id: TableId, - table_name: Box, + table_name: TableName, view_info: Option, columns: Vec, indexes: Vec, @@ -246,7 +247,7 @@ impl TableSchema { TableSchema::new( TableId::SENTINEL, - "TestTable".into(), + TableName::new_from_str("TestTable"), None, columns, vec![], @@ -650,7 +651,7 @@ impl TableSchema { /// This method works around this problem by copying the column types from the module def into the table schema. /// It can be removed once v8 is removed, since v9 will reject modules with an inconsistency like this. pub fn janky_fix_column_defs(&mut self, module_def: &ModuleDef) { - let table_name = Identifier::new(self.table_name.clone()).unwrap(); + let table_name = Identifier::new(self.table_name.deref().into()).unwrap(); for col in &mut self.columns { let def: &ColumnDef = module_def .lookup((&table_name, &Identifier::new(col.col_name.clone()).unwrap())) @@ -746,7 +747,7 @@ impl TableSchema { TableSchema::new( TableId::SENTINEL, - (*name).clone().into(), + TableName::new_from_str(name), Some(view_info), columns, vec![], @@ -868,7 +869,7 @@ impl TableSchema { TableSchema::new( TableId::SENTINEL, - (*name).clone().into(), + TableName::new_from_str(name), Some(view_info), columns, indexes, @@ -934,7 +935,7 @@ impl Schema for TableSchema { TableSchema::new( table_id, - (*name).clone().into(), + TableName::new_from_str(name), None, columns, indexes, diff --git a/crates/schema/src/table_name.rs b/crates/schema/src/table_name.rs new file mode 100644 index 00000000000..6778f742bc2 --- /dev/null +++ b/crates/schema/src/table_name.rs @@ -0,0 +1,48 @@ +use core::fmt; +use core::ops::Deref; +use ecow::EcoString; +use spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st, AlgebraicType}; + +/// The name of a table. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TableName( + // TODO(perf, centril): Use this sort of optimization + // in RawIdentifier and `Identifier` and more places. + // TODO(perf): Consider `lean_string` instead for `&'static str` optimization. + // This could be useful in e.g., `SumType` and friends. + EcoString, +); + +impl_st!([] TableName, _ts => AlgebraicType::String); +impl_serialize!([] TableName, (self, ser) => ser.serialize_str(&self.0)); +impl_deserialize!([] TableName, de => >::deserialize(de).map(|s| Self(EcoString::from(s.as_ref())))); + +impl TableName { + pub fn new_from_str(name: &str) -> Self { + Self(EcoString::from(name)) + } + + pub fn to_boxed_str(&self) -> Box { + self.as_ref().into() + } +} + +impl Deref for TableName { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl AsRef for TableName { + fn as_ref(&self) -> &str { + &self.0 + } +} + +impl fmt::Display for TableName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", &self.0) + } +} diff --git a/crates/subscription/Cargo.toml b/crates/subscription/Cargo.toml index 54f29e864ae..ed207f856af 100644 --- a/crates/subscription/Cargo.toml +++ b/crates/subscription/Cargo.toml @@ -14,5 +14,7 @@ spacetimedb-lib.workspace = true spacetimedb-primitives.workspace = true spacetimedb-physical-plan.workspace = true spacetimedb-query.workspace = true +spacetimedb-schema.workspace = true + [lints] workspace = true diff --git a/crates/subscription/src/lib.rs b/crates/subscription/src/lib.rs index 78a1bdd4453..41c789cb182 100644 --- a/crates/subscription/src/lib.rs +++ b/crates/subscription/src/lib.rs @@ -11,7 +11,7 @@ use spacetimedb_lib::{identity::AuthCtx, metrics::ExecutionMetrics, query::Delta use spacetimedb_physical_plan::plan::{IxJoin, IxScan, Label, PhysicalPlan, ProjectPlan, Sarg, TableScan, TupleField}; use spacetimedb_primitives::{ColId, ColList, IndexId, TableId, ViewId}; use spacetimedb_query::compile_subscription; -use std::sync::Arc; +use spacetimedb_schema::table_name::TableName; use std::{collections::HashSet, ops::RangeBounds}; /// A subscription is a view over a particular table. @@ -252,49 +252,6 @@ impl Fragments { } } -/// Newtype wrapper for table names. -/// -/// Uses an `Arc` internally, so `Clone` is cheap. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TableName(Arc); - -impl From> for TableName { - fn from(name: Arc) -> Self { - TableName(name) - } -} - -impl From> for TableName { - fn from(name: Box) -> Self { - TableName(name.into()) - } -} - -impl From for TableName { - fn from(name: String) -> Self { - TableName(name.into()) - } -} - -impl std::ops::Deref for TableName { - type Target = str; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl TableName { - pub fn table_name_from_str(name: &str) -> Self { - TableName(name.into()) - } -} - -impl std::fmt::Display for TableName { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - self.0.fmt(f) - } -} - /// A join edge is used for pruning queries when evaluating subscription updates. /// /// If we have the following subscriptions: @@ -549,8 +506,6 @@ impl SubscriptionPlan { let mut subscriptions = vec![]; - let return_name = TableName::from(return_name); - for plan in plans { let plan_opt = plan.clone().optimize(auth)?; diff --git a/crates/table/benches/page_manager.rs b/crates/table/benches/page_manager.rs index dff6edaf017..7224980654c 100644 --- a/crates/table/benches/page_manager.rs +++ b/crates/table/benches/page_manager.rs @@ -15,6 +15,7 @@ use spacetimedb_sats::{AlgebraicType, AlgebraicValue, ProductType, ProductValue} use spacetimedb_schema::def::BTreeAlgorithm; use spacetimedb_schema::def::ModuleDef; use spacetimedb_schema::schema::TableSchema; +use spacetimedb_schema::table_name::TableName; use spacetimedb_table::blob_store::NullBlobStore; use spacetimedb_table::indexes::{Byte, Bytes, PageOffset, RowPointer, SquashedOffset, PAGE_DATA_SIZE}; use spacetimedb_table::page_pool::PagePool; @@ -490,7 +491,7 @@ criterion_group!( fn schema_from_ty(ty: ProductType, name: &str) -> TableSchema { let mut result = TableSchema::from_product_type(ty); - result.table_name = name.into(); + result.table_name = TableName::new_from_str(name); result } diff --git a/crates/table/src/table.rs b/crates/table/src/table.rs index 5054ed10d62..1609b578182 100644 --- a/crates/table/src/table.rs +++ b/crates/table/src/table.rs @@ -52,6 +52,7 @@ use spacetimedb_sats::{ use spacetimedb_schema::{ def::{BTreeAlgorithm, IndexAlgorithm}, schema::{columns_to_row_type, ColumnSchema, IndexSchema, TableSchema}, + table_name::TableName, }; use std::{ collections::{btree_map, BTreeMap}, @@ -281,7 +282,7 @@ pub enum ReadViaBsatnError { #[error("Cannot change the columns of table `{table_name}` with id {table_id} from `{old:?}` to `{new:?}`: {reason}")] pub struct ChangeColumnsError { table_id: TableId, - table_name: Box, + table_name: TableName, old: Vec, new: Vec, reason: ChangeColumnsErrorReason, @@ -317,7 +318,7 @@ pub enum ChangeColumnsErrorReason { #[error("Cannot change the columns of table `{table_name}` with id {table_id} from `{old:?}` to `{new:?}`: {reason}")] pub struct AddColumnsError { table_id: TableId, - table_name: Box, + table_name: TableName, old: Vec, new: Vec, default_values: Vec, @@ -2179,7 +2180,7 @@ impl<'a> Iterator for IndexScanRangeIter<'a> { #[error("Unique constraint violation '{}' in table '{}': column(s): '{:?}' value: {}", constraint_name, table_name, cols, value.to_satn())] pub struct UniqueConstraintViolation { pub constraint_name: Box, - pub table_name: Box, + pub table_name: TableName, pub cols: Vec>, pub value: AlgebraicValue, } diff --git a/crates/vm/src/eval.rs b/crates/vm/src/eval.rs index f93bce34961..729a3f57b6d 100644 --- a/crates/vm/src/eval.rs +++ b/crates/vm/src/eval.rs @@ -98,7 +98,10 @@ pub mod test_helpers { use spacetimedb_data_structures::map::DefaultHashBuilder; use spacetimedb_primitives::TableId; use spacetimedb_sats::{product, AlgebraicType, AlgebraicValue, ProductType, ProductValue}; - use spacetimedb_schema::relation::{Column, FieldName, Header}; + use spacetimedb_schema::{ + relation::{Column, FieldName, Header}, + table_name::TableName, + }; use std::sync::Arc; pub fn mem_table_without_table_name(mem: &MemTable) -> (&[Column], &[ProductValue]) { @@ -107,7 +110,7 @@ pub mod test_helpers { pub fn header_for_mem_table(table_id: TableId, fields: ProductType) -> Header { let hash = DefaultHashBuilder::default().hash_one(&fields); - let table_name = format!("mem#{hash:x}").into(); + let table_name = TableName::new_from_str(&format!("mem#{hash:x}")); let cols = Vec::from(fields.elements) .into_iter() @@ -190,6 +193,7 @@ pub mod tests { use spacetimedb_sats::{product, AlgebraicType, ProductType}; use spacetimedb_schema::def::error::RelationError; use spacetimedb_schema::relation::{FieldName, Header}; + use spacetimedb_schema::table_name::TableName; /// From an original source of `result`s, applies `queries` and returns a final set of results. fn build_query<'a, const N: usize>( @@ -315,7 +319,12 @@ pub mod tests { let result = run_query(q.into(), sources); // The expected result. - let head = Header::new(table_id, "".into(), [field.clone(), field].into(), Vec::new()); + let head = Header::new( + table_id, + TableName::new_from_str(""), + [field.clone(), field].into(), + Vec::new(), + ); let input = MemTable::from_iter(head.into(), [product!(1u64, 1u64)]); println!("{}", &result.head); diff --git a/crates/vm/src/expr.rs b/crates/vm/src/expr.rs index 23cc59e12d2..99c7366b8c6 100644 --- a/crates/vm/src/expr.rs +++ b/crates/vm/src/expr.rs @@ -15,6 +15,7 @@ use spacetimedb_sats::{AlgebraicType, AlgebraicValue, ProductValue}; use spacetimedb_schema::def::error::{AuthError, RelationError}; use spacetimedb_schema::relation::{ColExpr, DbTable, FieldName, Header}; use spacetimedb_schema::schema::TableSchema; +use spacetimedb_schema::table_name::TableName; use std::borrow::Cow; use std::cmp::Reverse; use std::collections::btree_map::Entry; @@ -566,7 +567,7 @@ impl SourceExpr { } } - pub fn table_name(&self) -> &str { + pub fn table_name(&self) -> &TableName { &self.head().table_name } @@ -2049,7 +2050,7 @@ impl AuthAccess for CrudExpr { #[derive(Debug, PartialEq)] pub struct Update { pub table_id: TableId, - pub table_name: Box, + pub table_name: TableName, pub inserts: Vec, pub deletes: Vec, } @@ -2128,7 +2129,7 @@ mod tests { source_id: SourceId(0), header: Arc::new(Header { table_id: 42.into(), - table_name: "foo".into(), + table_name: TableName::new_from_str("foo"), fields: vec![], constraints: Default::default(), }), @@ -2138,7 +2139,7 @@ mod tests { SourceExpr::DbTable(DbTable { head: Arc::new(Header { table_id: 42.into(), - table_name: "foo".into(), + table_name: TableName::new_from_str("foo"), fields: vec![], constraints: [(ColId(42).into(), Constraints::indexed())].into_iter().collect(), }), @@ -2165,7 +2166,7 @@ mod tests { index_side: SourceExpr::DbTable(DbTable { head: Arc::new(Header { table_id: db_table.head().table_id, - table_name: db_table.table_name().into(), + table_name: db_table.table_name().clone(), fields: vec![], constraints: Default::default(), }), @@ -2214,7 +2215,7 @@ mod tests { let table_access = StAccess::Public; let head = Header::new( id, - name.into(), + TableName::new_from_str(name), fields .iter() .map(|(col, ty, _)| Column::new(FieldName::new(id, (*col).into()), ty.clone())) @@ -2289,7 +2290,7 @@ mod tests { let head1 = Header::new( table_id, - "t1".into(), + TableName::new_from_str("t1"), columns.to_vec(), vec![ // Index a diff --git a/sdks/rust/Cargo.toml b/sdks/rust/Cargo.toml index 4bd913f7bde..421a80d10ee 100644 --- a/sdks/rust/Cargo.toml +++ b/sdks/rust/Cargo.toml @@ -15,6 +15,7 @@ spacetimedb-lib = { workspace = true, features = ["serde", "metrics_impls"]} spacetimedb-client-api-messages.workspace = true spacetimedb-metrics.workspace = true spacetimedb-query-builder.workspace = true +spacetimedb-schema.workspace = true thiserror.workspace = true anymap.workspace = true