From 8fa858405aebb7ef5ced2a6832aee3cdb7c4a4f9 Mon Sep 17 00:00:00 2001 From: Shubham Mishra Date: Fri, 29 May 2026 16:01:16 +0530 Subject: [PATCH 1/6] index alias fix on restart --- .../src/locking_tx_datastore/state_view.rs | 27 ++++- crates/smoketests/tests/smoketests/mod.rs | 1 + .../typescript_index_source_name.rs | 105 ++++++++++++++++++ 3 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 crates/smoketests/tests/smoketests/typescript_index_source_name.rs diff --git a/crates/datastore/src/locking_tx_datastore/state_view.rs b/crates/datastore/src/locking_tx_datastore/state_view.rs index 8c4c978aaa0..6595a0b72c9 100644 --- a/crates/datastore/src/locking_tx_datastore/state_view.rs +++ b/crates/datastore/src/locking_tx_datastore/state_view.rs @@ -16,7 +16,7 @@ use core::ops::RangeBounds; use spacetimedb_lib::ConnectionId; use spacetimedb_primitives::{ColList, TableId}; use spacetimedb_sats::AlgebraicValue; -use spacetimedb_schema::schema::{ColumnSchema, TableSchema, ViewDefInfo}; +use spacetimedb_schema::schema::{ColumnSchema, IndexSchema, TableSchema, ViewDefInfo}; use spacetimedb_table::table::IndexScanPointIter; use spacetimedb_table::{ blob_store::HashMapBlobStore, @@ -120,6 +120,22 @@ pub trait StateView { .transpose() } + /// Look up an `st_index_accessor` row by its canonical index name. + fn find_st_index_accessor_row_by_index_name(&self, index_name: &str) -> Result> { + match self.iter_by_col_eq( + ST_INDEX_ACCESSOR_ID, + StIndexAccessorFields::IndexName, + &index_name.into(), + ) { + Ok(mut iter) => iter.next().map(StIndexAccessorRow::try_from).transpose(), + // `schema_for_table_raw` is called while restoring snapshots, + // before `migrate_system_tables` creates newer system tables. + // Treat a missing `st_index_accessor` as "no aliases yet" here. + Err(DatastoreError::Table(TableError::IdNotFound(..))) => Ok(None), + Err(e) => Err(e), + } + } + /// Look up an `st_column_accessor` row by its canonical table and column names fn find_st_column_accessor_row(&self, table_name: &str, col_name: &str) -> Result> { match self.iter_by_col_eq( @@ -187,7 +203,14 @@ pub trait StateView { // Look up the indexes for the table in question. let indexes = self .iter_by_col_eq(ST_INDEX_ID, StIndexFields::TableId, value_eq)? - .map(|row| StIndexRow::try_from(row).map(Into::into)) + .map(|row| { + let row = StIndexRow::try_from(row)?; + let mut index_schema = IndexSchema::from(row); + index_schema.alias = self + .find_st_index_accessor_row_by_index_name(index_schema.index_name.as_ref())? + .map(|row| row.accessor_name); + Ok(index_schema) + }) .collect::>>()?; let schedule = self diff --git a/crates/smoketests/tests/smoketests/mod.rs b/crates/smoketests/tests/smoketests/mod.rs index 959d43eab8f..741512e0714 100644 --- a/crates/smoketests/tests/smoketests/mod.rs +++ b/crates/smoketests/tests/smoketests/mod.rs @@ -39,4 +39,5 @@ mod sql; mod sql_connect_hook; mod templates; mod timestamp_route; +mod typescript_index_source_name; mod views; diff --git a/crates/smoketests/tests/smoketests/typescript_index_source_name.rs b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs new file mode 100644 index 00000000000..ecf23982ab9 --- /dev/null +++ b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs @@ -0,0 +1,105 @@ +use spacetimedb_smoketests::{random_string, require_local_server, require_pnpm, Smoketest}; + +const TYPESCRIPT_MODULE_WITHOUT_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server"; + +const users = table( + { name: "users", public: false }, + { + id: t.u64().primaryKey().autoInc(), + name: t.string(), + emailAddress: t.string().index("btree"), + }, +); + +const spacetimedb = schema({ + users, +}); +export default spacetimedb; + +export const insert_user = spacetimedb.reducer( + { + name: t.string(), + emailAddress: t.string(), + }, + (ctx, { name, emailAddress }) => { + ctx.db.users.insert({ + id: 0n, + name, + emailAddress, + }); + }, +); +"#; + +const TYPESCRIPT_MODULE_WITH_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server"; + +const users = table( + { name: "users", public: false }, + { + id: t.u64().primaryKey().autoInc(), + name: t.string(), + emailAddress: t.string().index("btree"), + age: t.number().optional().default(undefined), + isActive: t.bool().default(false).index(), + }, +); + +const spacetimedb = schema({ + users, +}); +export default spacetimedb; + +export const find_user_by_email = spacetimedb.reducer( + { emailAddress: t.string() }, + (ctx, { emailAddress }) => { + let count = 0; + for (const _row of ctx.db.users.emailAddress.filter(emailAddress)) { + count += 1; + } + console.info(`matched ${count}`); + }, +); + +export const find_users_by_active_status = spacetimedb.reducer( + { isActive: t.bool() }, + (ctx, { isActive }) => { + let count = 0; + for (const _row of ctx.db.users.isActive.filter(isActive)) { + count += 1; + } + console.info(`matched active users ${count}`); + }, +); +"#; + +#[test] +fn test_typescript_add_optional_columns() { + require_pnpm!(); + require_local_server!(); + + let mut test = Smoketest::builder().autopublish(false).build(); + let module_name = format!("typescript-add-optional-columns-{}", random_string()); + + let database_identity = test + .publish_typescript_module_source( + "typescript-add-optional-columns-v1", + &module_name, + TYPESCRIPT_MODULE_WITHOUT_NEW_COLUMNS, + ) + .unwrap(); + + test.call("insert_user", &["Alice", "alice@example.com"]).unwrap(); + + test.restart_server(); + + test.publish_typescript_module_source_clear( + "typescript-add-optional-columns-v2", + &database_identity, + TYPESCRIPT_MODULE_WITH_NEW_COLUMNS, + false, + ) + .unwrap(); + + test.call("find_user_by_email", &["alice@example.com"]).unwrap(); + test.call("find_users_by_active_status", &["false"]).unwrap(); +} From e2a196630d4305a058e747defd46127694da5ab8 Mon Sep 17 00:00:00 2001 From: Shubham Mishra Date: Tue, 2 Jun 2026 18:08:12 +0530 Subject: [PATCH 2/6] index source name change migration step --- crates/core/src/db/relational_db.rs | 9 ++ crates/core/src/db/update.rs | 138 +++++++++++++++++- .../locking_tx_datastore/committed_state.rs | 12 ++ .../src/locking_tx_datastore/datastore.rs | 18 +++ .../src/locking_tx_datastore/mut_tx.rs | 40 +++++ .../src/locking_tx_datastore/tx_state.rs | 10 ++ crates/datastore/src/traits.rs | 6 + crates/schema/src/auto_migrate.rs | 41 ++++++ crates/schema/src/auto_migrate/formatter.rs | 4 + .../typescript_index_source_name.rs | 75 ++++++++-- 10 files changed, 340 insertions(+), 13 deletions(-) diff --git a/crates/core/src/db/relational_db.rs b/crates/core/src/db/relational_db.rs index c145a9b79bd..f239645b047 100644 --- a/crates/core/src/db/relational_db.rs +++ b/crates/core/src/db/relational_db.rs @@ -1014,6 +1014,15 @@ impl RelationalDB { Ok(self.inner.alter_table_primary_key_mut_tx(tx, name, primary_key)?) } + pub(crate) fn alter_index_source_name( + &self, + tx: &mut MutTx, + index_id: IndexId, + source_name: spacetimedb_sats::raw_identifier::RawIdentifier, + ) -> Result<(), DBError> { + Ok(self.inner.alter_index_source_name_mut_tx(tx, index_id, source_name)?) + } + pub(crate) fn alter_table_row_type( &self, tx: &mut MutTx, diff --git a/crates/core/src/db/update.rs b/crates/core/src/db/update.rs index f9ca4c110d9..d53a396859b 100644 --- a/crates/core/src/db/update.rs +++ b/crates/core/src/db/update.rs @@ -209,11 +209,34 @@ fn auto_migrate_database( .indexes .iter() .find(|index| index.index_name[..] == index_name[..]) - .unwrap(); + .ok_or_else(|| anyhow::anyhow!("Index `{index_name}` not found in table `{}`", table_def.name))?; log!(logger, "Dropping index `{}` on table `{}`", index_name, table_def.name); stdb.drop_index(tx, index_schema.index_id)?; } + spacetimedb_schema::auto_migrate::AutoMigrateStep::ChangeIndexSourceName(index_name) => { + let old_table_def = plan.old.stored_in_table_def(index_name).unwrap(); + let new_table_def = plan.new.stored_in_table_def(index_name).unwrap(); + let new_index_def = new_table_def.indexes.get(index_name).unwrap(); + + let table_id = stdb.table_id_from_name_mut(tx, &old_table_def.name)?.unwrap(); + let table_schema = stdb.schema_for_table_mut(tx, table_id)?; + let index_schema = table_schema + .indexes + .iter() + .find(|index| index.index_name[..] == index_name[..]) + .ok_or_else(|| anyhow::anyhow!("Index `{index_name}` not found in table `{}`", old_table_def.name))?; + + log!( + logger, + "Changing index source name for `{}` on table `{}` from `{}` to `{}`", + index_name, + old_table_def.name, + index_schema.alias.as_deref().unwrap_or(""), + new_index_def.source_name, + ); + stdb.alter_index_source_name(tx, index_schema.index_id, new_index_def.source_name.clone())?; + } spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveConstraint(constraint_name) => { let table_def = plan.old.stored_in_table_def(constraint_name).unwrap(); @@ -340,9 +363,13 @@ mod test { host::module_host::create_table_from_def, }; use spacetimedb_datastore::locking_tx_datastore::PendingSchemaChange; - use spacetimedb_lib::db::raw_def::v9::{btree, RawIndexAlgorithm, RawModuleDefV9Builder, TableAccess}; - use spacetimedb_sats::{product, AlgebraicType, AlgebraicType::U64}; - use spacetimedb_schema::{auto_migrate::ponder_migrate, def::ModuleDef}; + use spacetimedb_lib::db::raw_def::{ + v10::{ExplicitNames, RawModuleDefV10Builder}, + v9::{btree, RawIndexAlgorithm, RawModuleDefV9Builder, TableAccess}, + }; + use spacetimedb_sats::{product, raw_identifier::RawIdentifier, AlgebraicType, AlgebraicType::U64, ProductType}; + use spacetimedb_schema::auto_migrate::{ponder_migrate, AutoMigrateStep, MigratePlan}; + use spacetimedb_schema::def::ModuleDef; struct TestLogger; impl UpdateLogger for TestLogger { @@ -424,6 +451,109 @@ mod test { Ok(()) } + #[test] + fn update_db_change_index_source_name_updates_lookup_and_persists() -> anyhow::Result<()> { + let auth_ctx = AuthCtx::for_testing(); + let stdb = TestDB::durable()?; + + fn module_def(table_source_name: &str, index_source_name: &str) -> ModuleDef { + let mut builder = RawModuleDefV10Builder::new(); + builder + .build_table_with_new_type( + table_source_name.to_owned(), + ProductType::from([("id", U64), ("emailAddress", AlgebraicType::String)]), + true, + ) + .with_access(TableAccess::Public) + .with_index(btree(1), index_source_name.to_owned(), "emailAddress") + .finish(); + + if table_source_name != "users" { + let mut explicit_names = ExplicitNames::default(); + explicit_names.insert_table(table_source_name.to_owned(), "users"); + builder.add_explicit_names(explicit_names); + } + + builder + .finish() + .try_into() + .expect("builder should create a valid database definition") + } + + let old_source_name = "users_emailAddress_idx_btree"; + let new_source_name = "appUsers_emailAddress_idx_btree"; + let old = module_def("users", old_source_name); + let new = module_def("appUsers", new_source_name); + + let mut tx = begin_mut_tx(&stdb); + for def in old.tables() { + create_table_from_def(&stdb, &mut tx, &old, def)?; + } + stdb.commit_tx(tx)?; + + let tx = begin_mut_tx(&stdb); + let table_id = stdb + .table_id_from_name_mut(&tx, "users")? + .expect("there should be a table named users"); + let table_schema = stdb.schema_for_table_mut(&tx, table_id)?; + let index_schema = table_schema + .indexes + .first() + .expect("there should be a single index") + .clone(); + let canonical_index_name = index_schema.index_name.to_string(); + let index_id = index_schema.index_id; + assert_eq!(stdb.index_id_from_name_mut(&tx, old_source_name)?, Some(index_id)); + assert_eq!(stdb.index_id_from_name_mut(&tx, new_source_name)?, None); + assert_eq!(stdb.index_id_from_name_mut(&tx, &canonical_index_name)?, Some(index_id)); + drop(tx); + + let MigratePlan::Auto(plan) = ponder_migrate(&old, &new)? else { + panic!("expected automatic migration"); + }; + let index_name = RawIdentifier::new(canonical_index_name.as_str()); + assert!( + plan.steps.contains(&AutoMigrateStep::ChangeIndexSourceName(&index_name)), + "plan steps: {:?}", + plan.steps + ); + assert!( + !plan.steps.contains(&AutoMigrateStep::RemoveIndex(&index_name)), + "plan steps: {:?}", + plan.steps + ); + assert!( + !plan.steps.contains(&AutoMigrateStep::AddIndex(&index_name)), + "plan steps: {:?}", + plan.steps + ); + let mut tx = begin_mut_tx(&stdb); + let res = update_database(&stdb, &mut tx, auth_ctx, MigratePlan::Auto(plan), &TestLogger)?; + assert!(matches!(res, UpdateResult::Success)); + + assert_eq!(stdb.index_id_from_name_mut(&tx, old_source_name)?, None); + assert_eq!(stdb.index_id_from_name_mut(&tx, new_source_name)?, Some(index_id)); + assert_eq!(stdb.index_id_from_name_mut(&tx, &canonical_index_name)?, Some(index_id)); + assert!( + tx.pending_schema_changes().iter().any(|change| matches!( + change, + PendingSchemaChange::IndexAlterSourceName(tid, iid, Some(old_alias)) + if *tid == table_id && *iid == index_id && old_alias.as_ref() == old_source_name + )), + "pending schema changes: {:?}", + tx.pending_schema_changes() + ); + stdb.commit_tx(tx)?; + + let stdb = stdb.reopen()?; + let tx = begin_mut_tx(&stdb); + assert_eq!(stdb.index_id_from_name_mut(&tx, old_source_name)?, None); + assert_eq!(stdb.index_id_from_name_mut(&tx, new_source_name)?, Some(index_id)); + assert_eq!(stdb.index_id_from_name_mut(&tx, &canonical_index_name)?, Some(index_id)); + + Ok(()) + } + /// Regression test for #3934: removing a primary key annotation and then /// re-publishing causes "Primary key mismatch" on the NEXT publish. #[test] diff --git a/crates/datastore/src/locking_tx_datastore/committed_state.rs b/crates/datastore/src/locking_tx_datastore/committed_state.rs index c479be085d9..4086bc93ec2 100644 --- a/crates/datastore/src/locking_tx_datastore/committed_state.rs +++ b/crates/datastore/src/locking_tx_datastore/committed_state.rs @@ -707,6 +707,18 @@ impl CommittedState { table.with_mut_schema(|s| s.remove_index(index_id)); self.index_id_map.remove(&index_id); } + // An index alias/source-name changed. Change it back. + IndexAlterSourceName(table_id, index_id, old_alias) => { + let table = self.tables.get_mut(&table_id)?; + let mut index_schema = table + .get_schema() + .indexes + .iter() + .find(|x| x.index_id == index_id)? + .clone(); + index_schema.alias = old_alias; + table.with_mut_schema(|s| s.update_index(index_schema)); + } // A table was removed. Add it back. TableRemoved(table_id, table) => { let is_view_table = table.schema.is_view(); diff --git a/crates/datastore/src/locking_tx_datastore/datastore.rs b/crates/datastore/src/locking_tx_datastore/datastore.rs index e9d67103b16..667990b3e75 100644 --- a/crates/datastore/src/locking_tx_datastore/datastore.rs +++ b/crates/datastore/src/locking_tx_datastore/datastore.rs @@ -294,6 +294,15 @@ impl Locking { tx.alter_table_primary_key(table_id, primary_key) } + pub fn alter_index_source_name_mut_tx( + &self, + tx: &mut MutTxId, + index_id: IndexId, + source_name: spacetimedb_sats::raw_identifier::RawIdentifier, + ) -> Result<()> { + tx.alter_index_source_name(index_id, source_name) + } + pub fn alter_table_row_type_mut_tx( &self, tx: &mut MutTxId, @@ -527,6 +536,15 @@ impl MutTxDatastore for Locking { tx.drop_index(index_id) } + fn alter_index_source_name_mut_tx( + &self, + tx: &mut Self::MutTx, + index_id: IndexId, + source_name: spacetimedb_sats::raw_identifier::RawIdentifier, + ) -> Result<()> { + tx.alter_index_source_name(index_id, source_name) + } + fn index_id_from_name_mut_tx(&self, tx: &Self::MutTx, index_name: &str) -> Result> { tx.index_id_from_name_or_alias(index_name) } diff --git a/crates/datastore/src/locking_tx_datastore/mut_tx.rs b/crates/datastore/src/locking_tx_datastore/mut_tx.rs index 3a36331dc4d..90e00e2f0c4 100644 --- a/crates/datastore/src/locking_tx_datastore/mut_tx.rs +++ b/crates/datastore/src/locking_tx_datastore/mut_tx.rs @@ -1163,6 +1163,46 @@ impl MutTxId { Ok(()) } + /// Change the runtime source-name alias of the index identified by `index_id`. + pub(crate) fn alter_index_source_name( + &mut self, + index_id: IndexId, + source_name: spacetimedb_sats::raw_identifier::RawIdentifier, + ) -> Result<()> { + let st_index_ref = self + .iter_by_col_eq(ST_INDEX_ID, StIndexFields::IndexId, &index_id.into())? + .next() + .ok_or_else(|| TableError::IdNotFound(SystemTable::st_index, index_id.into()))?; + let st_index_row = StIndexRow::try_from(st_index_ref)?; + let table_id = st_index_row.table_id; + + let old_alias = self + .find_st_index_accessor_row_by_index_name(st_index_row.index_name.as_ref())? + .map(|row| row.accessor_name); + + if old_alias.as_ref() == Some(&source_name) { + return Ok(()); + } + + let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?; + let mut index_schema = tx_table + .get_schema() + .indexes + .iter() + .find(|index| index.index_id == index_id) + .cloned() + .ok_or_else(|| TableError::IdNotFound(SystemTable::st_index, index_id.into()))?; + index_schema.alias = Some(source_name.clone()); + tx_table.with_mut_schema_and_clone(commit_table, |s| s.update_index(index_schema)); + + self.drop_st_index_accessor(&st_index_row.index_name)?; + self.insert_st_index_accessor(&st_index_row.index_name, Some(&source_name))?; + + self.push_schema_change(PendingSchemaChange::IndexAlterSourceName(table_id, index_id, old_alias)); + + Ok(()) + } + /// Change the row type of the table identified by `table_id`. /// /// In practice, this should not error, diff --git a/crates/datastore/src/locking_tx_datastore/tx_state.rs b/crates/datastore/src/locking_tx_datastore/tx_state.rs index 4e712bcf684..4b56aa1dfca 100644 --- a/crates/datastore/src/locking_tx_datastore/tx_state.rs +++ b/crates/datastore/src/locking_tx_datastore/tx_state.rs @@ -111,6 +111,13 @@ pub enum PendingSchemaChange { /// If adding this index caused the pointer map to be removed, /// it will be present here. IndexAdded(TableId, IndexId, Option), + /// The source-name alias of the index with [`IndexId`] changed. + /// The old alias is stored for rollback. + IndexAlterSourceName( + TableId, + IndexId, + Option, + ), /// The [`Table`] with [`TableId`] was removed. TableRemoved(TableId, Table), /// The table with [`TableId`] was added. @@ -145,6 +152,9 @@ impl MemoryUsage for PendingSchemaChange { Self::IndexAdded(table_id, index_id, pointer_map) => { table_id.heap_usage() + index_id.heap_usage() + pointer_map.heap_usage() } + Self::IndexAlterSourceName(table_id, index_id, alias) => { + table_id.heap_usage() + index_id.heap_usage() + alias.heap_usage() + } Self::TableRemoved(table_id, table) => table_id.heap_usage() + table.heap_usage(), Self::TableAdded(table_id) => table_id.heap_usage(), Self::TableAlterAccess(table_id, st_access) => table_id.heap_usage() + st_access.heap_usage(), diff --git a/crates/datastore/src/traits.rs b/crates/datastore/src/traits.rs index e1b99825ae2..07a1d633124 100644 --- a/crates/datastore/src/traits.rs +++ b/crates/datastore/src/traits.rs @@ -627,6 +627,12 @@ pub trait MutTxDatastore: TxDatastore + MutTx { fn create_index_mut_tx(&self, tx: &mut Self::MutTx, index_schema: IndexSchema, is_unique: bool) -> Result; fn drop_index_mut_tx(&self, tx: &mut Self::MutTx, index_id: IndexId) -> Result<()>; + fn alter_index_source_name_mut_tx( + &self, + tx: &mut Self::MutTx, + index_id: IndexId, + source_name: spacetimedb_sats::raw_identifier::RawIdentifier, + ) -> Result<()>; fn index_id_from_name_mut_tx(&self, tx: &Self::MutTx, index_name: &str) -> super::Result>; // TODO: Index data diff --git a/crates/schema/src/auto_migrate.rs b/crates/schema/src/auto_migrate.rs index f2fba813fa8..bb69c4bea55 100644 --- a/crates/schema/src/auto_migrate.rs +++ b/crates/schema/src/auto_migrate.rs @@ -284,6 +284,9 @@ pub enum AutoMigrateStep<'def> { /// no `ChangeColumns` steps will be, for the same table. AddColumns(::Key<'def>), + /// Change the runtime source-name alias of an existing index. + ChangeIndexSourceName(::Key<'def>), + /// Add a table, including all indexes, constraints, and sequences. /// There will NOT be separate steps in the plan for adding indexes, constraints, and sequences. AddTable(::Key<'def>), @@ -981,6 +984,8 @@ fn auto_migrate_indexes( if old.algorithm != new.algorithm { plan.steps.push(AutoMigrateStep::RemoveIndex(old.key())); plan.steps.push(AutoMigrateStep::AddIndex(old.key())); + } else if old.source_name != new.source_name { + plan.steps.push(AutoMigrateStep::ChangeIndexSourceName(old.key())); } Ok(()) } @@ -2039,6 +2044,42 @@ mod tests { ); } + #[test] + fn migrate_index_with_changed_source_name() { + fn module_def(source_name: &str) -> ModuleDef { + create_module_def_v10(|builder| { + builder + .build_table_with_new_type( + "FruitBasket", + ProductType::from([("basket_id", AlgebraicType::U64), ("fruit_name", AlgebraicType::String)]), + true, + ) + .with_index(btree([0, 1]), source_name.to_owned(), "fruitNameIndex") + .finish(); + }) + } + + let old_def = module_def("OldBasketLookup"); + let new_def = module_def("NewBasketLookup"); + let index_name = RawIdentifier::new("fruit_basket_basket_id_fruit_name_idx_btree"); + + let plan = ponder_auto_migrate(&old_def, &new_def).expect("auto migration should succeed"); + let steps = &plan.steps[..]; + + assert!( + steps.contains(&AutoMigrateStep::ChangeIndexSourceName(&index_name)), + "steps: {steps:?}" + ); + assert!( + !steps.contains(&AutoMigrateStep::RemoveIndex(&index_name)), + "steps: {steps:?}" + ); + assert!( + !steps.contains(&AutoMigrateStep::AddIndex(&index_name)), + "steps: {steps:?}" + ); + } + #[test] fn migrate_view_disconnect_clients() { struct TestCase { diff --git a/crates/schema/src/auto_migrate/formatter.rs b/crates/schema/src/auto_migrate/formatter.rs index 1f7377209bf..7296c89df44 100644 --- a/crates/schema/src/auto_migrate/formatter.rs +++ b/crates/schema/src/auto_migrate/formatter.rs @@ -59,6 +59,10 @@ fn format_step( let index_info = extract_index_info(*index, plan.old)?; f.format_index(&index_info, Action::Removed) } + AutoMigrateStep::ChangeIndexSourceName(index) => { + let index_info = extract_index_info(*index, plan.new)?; + f.format_index(&index_info, Action::Changed) + } AutoMigrateStep::RemoveConstraint(constraint) => { let constraint_info = extract_constraint_info(*constraint, plan.old)?; f.format_constraint(&constraint_info, Action::Removed) diff --git a/crates/smoketests/tests/smoketests/typescript_index_source_name.rs b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs index ecf23982ab9..04de365bb59 100644 --- a/crates/smoketests/tests/smoketests/typescript_index_source_name.rs +++ b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs @@ -1,8 +1,8 @@ use spacetimedb_smoketests::{random_string, require_local_server, require_pnpm, Smoketest}; -const TYPESCRIPT_MODULE_WITHOUT_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server"; +const TYPESCRIPT_MODULE_V1: &str = r#"import { schema, table, t } from "spacetimedb/server"; -const users = table( +const appUsers = table( { name: "users", public: false }, { id: t.u64().primaryKey().autoInc(), @@ -12,7 +12,7 @@ const users = table( ); const spacetimedb = schema({ - users, + appUsers, }); export default spacetimedb; @@ -22,7 +22,7 @@ export const insert_user = spacetimedb.reducer( emailAddress: t.string(), }, (ctx, { name, emailAddress }) => { - ctx.db.users.insert({ + ctx.db.appUsers.insert({ id: 0n, name, emailAddress, @@ -33,7 +33,7 @@ export const insert_user = spacetimedb.reducer( const TYPESCRIPT_MODULE_WITH_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server"; -const users = table( +const appUsers = table( { name: "users", public: false }, { id: t.u64().primaryKey().autoInc(), @@ -45,7 +45,7 @@ const users = table( ); const spacetimedb = schema({ - users, + appUsers, }); export default spacetimedb; @@ -53,7 +53,7 @@ export const find_user_by_email = spacetimedb.reducer( { emailAddress: t.string() }, (ctx, { emailAddress }) => { let count = 0; - for (const _row of ctx.db.users.emailAddress.filter(emailAddress)) { + for (const _row of ctx.db.appUsers.emailAddress.filter(emailAddress)) { count += 1; } console.info(`matched ${count}`); @@ -64,7 +64,7 @@ export const find_users_by_active_status = spacetimedb.reducer( { isActive: t.bool() }, (ctx, { isActive }) => { let count = 0; - for (const _row of ctx.db.users.isActive.filter(isActive)) { + for (const _row of ctx.db.appUsers.isActive.filter(isActive)) { count += 1; } console.info(`matched active users ${count}`); @@ -72,6 +72,34 @@ export const find_users_by_active_status = spacetimedb.reducer( ); "#; +const TYPESCRIPT_MODULE_V2_RENAMED_ACCESSOR: &str = r#"import { schema, table, t } from "spacetimedb/server"; + +const renamedUsers = table( + { name: "users", public: false }, + { + id: t.u64().primaryKey().autoInc(), + name: t.string(), + emailAddress: t.string().index("btree"), + }, +); + +const spacetimedb = schema({ + renamedUsers, +}); +export default spacetimedb; + +export const find_user_by_email = spacetimedb.reducer( + { emailAddress: t.string() }, + (ctx, { emailAddress }) => { + let count = 0; + for (const _row of ctx.db.renamedUsers.emailAddress.filter(emailAddress)) { + count += 1; + } + console.info(`matched ${count}`); + }, +); +"#; + #[test] fn test_typescript_add_optional_columns() { require_pnpm!(); @@ -84,7 +112,7 @@ fn test_typescript_add_optional_columns() { .publish_typescript_module_source( "typescript-add-optional-columns-v1", &module_name, - TYPESCRIPT_MODULE_WITHOUT_NEW_COLUMNS, + TYPESCRIPT_MODULE_V1, ) .unwrap(); @@ -103,3 +131,32 @@ fn test_typescript_add_optional_columns() { test.call("find_user_by_email", &["alice@example.com"]).unwrap(); test.call("find_users_by_active_status", &["false"]).unwrap(); } + +#[test] +fn test_typescript_change_index_source_name() { + require_pnpm!(); + require_local_server!(); + + let mut test = Smoketest::builder().autopublish(false).build(); + let module_name = format!("typescript-change-source-name-{}", random_string()); + + let database_identity = test + .publish_typescript_module_source( + "typescript-change-source-name-v1", + &module_name, + TYPESCRIPT_MODULE_V1, + ) + .unwrap(); + + test.call("insert_user", &["Alice", "alice@example.com"]).unwrap(); + + test.publish_typescript_module_source_clear( + "typescript-change-source-name-v2", + &database_identity, + TYPESCRIPT_MODULE_V2_RENAMED_ACCESSOR, + false, + ) + .unwrap(); + + test.call("find_user_by_email", &["alice@example.com"]).unwrap(); +} From 147c1b64502949f681efcfc4eb40bdc544fb0fb3 Mon Sep 17 00:00:00 2001 From: Shubham Mishra Date: Wed, 3 Jun 2026 21:41:05 +0530 Subject: [PATCH 3/6] fmt --- crates/core/src/db/update.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/core/src/db/update.rs b/crates/core/src/db/update.rs index d53a396859b..0e6dd10a88b 100644 --- a/crates/core/src/db/update.rs +++ b/crates/core/src/db/update.rs @@ -225,7 +225,9 @@ fn auto_migrate_database( .indexes .iter() .find(|index| index.index_name[..] == index_name[..]) - .ok_or_else(|| anyhow::anyhow!("Index `{index_name}` not found in table `{}`", old_table_def.name))?; + .ok_or_else(|| { + anyhow::anyhow!("Index `{index_name}` not found in table `{}`", old_table_def.name) + })?; log!( logger, @@ -513,7 +515,8 @@ mod test { }; let index_name = RawIdentifier::new(canonical_index_name.as_str()); assert!( - plan.steps.contains(&AutoMigrateStep::ChangeIndexSourceName(&index_name)), + plan.steps + .contains(&AutoMigrateStep::ChangeIndexSourceName(&index_name)), "plan steps: {:?}", plan.steps ); From acebb71df4bca79d17cf1591552ab50bc0e47647 Mon Sep 17 00:00:00 2001 From: Shubham Mishra Date: Wed, 3 Jun 2026 21:52:08 +0530 Subject: [PATCH 4/6] fmt --- .../tests/smoketests/typescript_index_source_name.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/crates/smoketests/tests/smoketests/typescript_index_source_name.rs b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs index 04de365bb59..c600e6282d6 100644 --- a/crates/smoketests/tests/smoketests/typescript_index_source_name.rs +++ b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs @@ -109,11 +109,7 @@ fn test_typescript_add_optional_columns() { let module_name = format!("typescript-add-optional-columns-{}", random_string()); let database_identity = test - .publish_typescript_module_source( - "typescript-add-optional-columns-v1", - &module_name, - TYPESCRIPT_MODULE_V1, - ) + .publish_typescript_module_source("typescript-add-optional-columns-v1", &module_name, TYPESCRIPT_MODULE_V1) .unwrap(); test.call("insert_user", &["Alice", "alice@example.com"]).unwrap(); @@ -141,11 +137,7 @@ fn test_typescript_change_index_source_name() { let module_name = format!("typescript-change-source-name-{}", random_string()); let database_identity = test - .publish_typescript_module_source( - "typescript-change-source-name-v1", - &module_name, - TYPESCRIPT_MODULE_V1, - ) + .publish_typescript_module_source("typescript-change-source-name-v1", &module_name, TYPESCRIPT_MODULE_V1) .unwrap(); test.call("insert_user", &["Alice", "alice@example.com"]).unwrap(); From bd0ae58cf216dc65477e0f5c9ea57f3f0377ed32 Mon Sep 17 00:00:00 2001 From: Shubham Mishra Date: Wed, 3 Jun 2026 22:43:59 +0530 Subject: [PATCH 5/6] Revert source name change --- crates/core/src/db/relational_db.rs | 9 -- crates/core/src/db/update.rs | 141 +----------------- .../locking_tx_datastore/committed_state.rs | 12 -- .../src/locking_tx_datastore/datastore.rs | 18 --- .../src/locking_tx_datastore/mut_tx.rs | 40 ----- .../src/locking_tx_datastore/tx_state.rs | 10 -- crates/datastore/src/traits.rs | 6 - crates/schema/src/auto_migrate.rs | 41 ----- crates/schema/src/auto_migrate/formatter.rs | 4 - .../typescript_index_source_name.rs | 75 ++-------- 10 files changed, 17 insertions(+), 339 deletions(-) diff --git a/crates/core/src/db/relational_db.rs b/crates/core/src/db/relational_db.rs index f239645b047..c145a9b79bd 100644 --- a/crates/core/src/db/relational_db.rs +++ b/crates/core/src/db/relational_db.rs @@ -1014,15 +1014,6 @@ impl RelationalDB { Ok(self.inner.alter_table_primary_key_mut_tx(tx, name, primary_key)?) } - pub(crate) fn alter_index_source_name( - &self, - tx: &mut MutTx, - index_id: IndexId, - source_name: spacetimedb_sats::raw_identifier::RawIdentifier, - ) -> Result<(), DBError> { - Ok(self.inner.alter_index_source_name_mut_tx(tx, index_id, source_name)?) - } - pub(crate) fn alter_table_row_type( &self, tx: &mut MutTx, diff --git a/crates/core/src/db/update.rs b/crates/core/src/db/update.rs index 0e6dd10a88b..f9ca4c110d9 100644 --- a/crates/core/src/db/update.rs +++ b/crates/core/src/db/update.rs @@ -209,36 +209,11 @@ fn auto_migrate_database( .indexes .iter() .find(|index| index.index_name[..] == index_name[..]) - .ok_or_else(|| anyhow::anyhow!("Index `{index_name}` not found in table `{}`", table_def.name))?; + .unwrap(); log!(logger, "Dropping index `{}` on table `{}`", index_name, table_def.name); stdb.drop_index(tx, index_schema.index_id)?; } - spacetimedb_schema::auto_migrate::AutoMigrateStep::ChangeIndexSourceName(index_name) => { - let old_table_def = plan.old.stored_in_table_def(index_name).unwrap(); - let new_table_def = plan.new.stored_in_table_def(index_name).unwrap(); - let new_index_def = new_table_def.indexes.get(index_name).unwrap(); - - let table_id = stdb.table_id_from_name_mut(tx, &old_table_def.name)?.unwrap(); - let table_schema = stdb.schema_for_table_mut(tx, table_id)?; - let index_schema = table_schema - .indexes - .iter() - .find(|index| index.index_name[..] == index_name[..]) - .ok_or_else(|| { - anyhow::anyhow!("Index `{index_name}` not found in table `{}`", old_table_def.name) - })?; - - log!( - logger, - "Changing index source name for `{}` on table `{}` from `{}` to `{}`", - index_name, - old_table_def.name, - index_schema.alias.as_deref().unwrap_or(""), - new_index_def.source_name, - ); - stdb.alter_index_source_name(tx, index_schema.index_id, new_index_def.source_name.clone())?; - } spacetimedb_schema::auto_migrate::AutoMigrateStep::RemoveConstraint(constraint_name) => { let table_def = plan.old.stored_in_table_def(constraint_name).unwrap(); @@ -365,13 +340,9 @@ mod test { host::module_host::create_table_from_def, }; use spacetimedb_datastore::locking_tx_datastore::PendingSchemaChange; - use spacetimedb_lib::db::raw_def::{ - v10::{ExplicitNames, RawModuleDefV10Builder}, - v9::{btree, RawIndexAlgorithm, RawModuleDefV9Builder, TableAccess}, - }; - use spacetimedb_sats::{product, raw_identifier::RawIdentifier, AlgebraicType, AlgebraicType::U64, ProductType}; - use spacetimedb_schema::auto_migrate::{ponder_migrate, AutoMigrateStep, MigratePlan}; - use spacetimedb_schema::def::ModuleDef; + use spacetimedb_lib::db::raw_def::v9::{btree, RawIndexAlgorithm, RawModuleDefV9Builder, TableAccess}; + use spacetimedb_sats::{product, AlgebraicType, AlgebraicType::U64}; + use spacetimedb_schema::{auto_migrate::ponder_migrate, def::ModuleDef}; struct TestLogger; impl UpdateLogger for TestLogger { @@ -453,110 +424,6 @@ mod test { Ok(()) } - #[test] - fn update_db_change_index_source_name_updates_lookup_and_persists() -> anyhow::Result<()> { - let auth_ctx = AuthCtx::for_testing(); - let stdb = TestDB::durable()?; - - fn module_def(table_source_name: &str, index_source_name: &str) -> ModuleDef { - let mut builder = RawModuleDefV10Builder::new(); - builder - .build_table_with_new_type( - table_source_name.to_owned(), - ProductType::from([("id", U64), ("emailAddress", AlgebraicType::String)]), - true, - ) - .with_access(TableAccess::Public) - .with_index(btree(1), index_source_name.to_owned(), "emailAddress") - .finish(); - - if table_source_name != "users" { - let mut explicit_names = ExplicitNames::default(); - explicit_names.insert_table(table_source_name.to_owned(), "users"); - builder.add_explicit_names(explicit_names); - } - - builder - .finish() - .try_into() - .expect("builder should create a valid database definition") - } - - let old_source_name = "users_emailAddress_idx_btree"; - let new_source_name = "appUsers_emailAddress_idx_btree"; - let old = module_def("users", old_source_name); - let new = module_def("appUsers", new_source_name); - - let mut tx = begin_mut_tx(&stdb); - for def in old.tables() { - create_table_from_def(&stdb, &mut tx, &old, def)?; - } - stdb.commit_tx(tx)?; - - let tx = begin_mut_tx(&stdb); - let table_id = stdb - .table_id_from_name_mut(&tx, "users")? - .expect("there should be a table named users"); - let table_schema = stdb.schema_for_table_mut(&tx, table_id)?; - let index_schema = table_schema - .indexes - .first() - .expect("there should be a single index") - .clone(); - let canonical_index_name = index_schema.index_name.to_string(); - let index_id = index_schema.index_id; - assert_eq!(stdb.index_id_from_name_mut(&tx, old_source_name)?, Some(index_id)); - assert_eq!(stdb.index_id_from_name_mut(&tx, new_source_name)?, None); - assert_eq!(stdb.index_id_from_name_mut(&tx, &canonical_index_name)?, Some(index_id)); - drop(tx); - - let MigratePlan::Auto(plan) = ponder_migrate(&old, &new)? else { - panic!("expected automatic migration"); - }; - let index_name = RawIdentifier::new(canonical_index_name.as_str()); - assert!( - plan.steps - .contains(&AutoMigrateStep::ChangeIndexSourceName(&index_name)), - "plan steps: {:?}", - plan.steps - ); - assert!( - !plan.steps.contains(&AutoMigrateStep::RemoveIndex(&index_name)), - "plan steps: {:?}", - plan.steps - ); - assert!( - !plan.steps.contains(&AutoMigrateStep::AddIndex(&index_name)), - "plan steps: {:?}", - plan.steps - ); - let mut tx = begin_mut_tx(&stdb); - let res = update_database(&stdb, &mut tx, auth_ctx, MigratePlan::Auto(plan), &TestLogger)?; - assert!(matches!(res, UpdateResult::Success)); - - assert_eq!(stdb.index_id_from_name_mut(&tx, old_source_name)?, None); - assert_eq!(stdb.index_id_from_name_mut(&tx, new_source_name)?, Some(index_id)); - assert_eq!(stdb.index_id_from_name_mut(&tx, &canonical_index_name)?, Some(index_id)); - assert!( - tx.pending_schema_changes().iter().any(|change| matches!( - change, - PendingSchemaChange::IndexAlterSourceName(tid, iid, Some(old_alias)) - if *tid == table_id && *iid == index_id && old_alias.as_ref() == old_source_name - )), - "pending schema changes: {:?}", - tx.pending_schema_changes() - ); - stdb.commit_tx(tx)?; - - let stdb = stdb.reopen()?; - let tx = begin_mut_tx(&stdb); - assert_eq!(stdb.index_id_from_name_mut(&tx, old_source_name)?, None); - assert_eq!(stdb.index_id_from_name_mut(&tx, new_source_name)?, Some(index_id)); - assert_eq!(stdb.index_id_from_name_mut(&tx, &canonical_index_name)?, Some(index_id)); - - Ok(()) - } - /// Regression test for #3934: removing a primary key annotation and then /// re-publishing causes "Primary key mismatch" on the NEXT publish. #[test] diff --git a/crates/datastore/src/locking_tx_datastore/committed_state.rs b/crates/datastore/src/locking_tx_datastore/committed_state.rs index 4086bc93ec2..c479be085d9 100644 --- a/crates/datastore/src/locking_tx_datastore/committed_state.rs +++ b/crates/datastore/src/locking_tx_datastore/committed_state.rs @@ -707,18 +707,6 @@ impl CommittedState { table.with_mut_schema(|s| s.remove_index(index_id)); self.index_id_map.remove(&index_id); } - // An index alias/source-name changed. Change it back. - IndexAlterSourceName(table_id, index_id, old_alias) => { - let table = self.tables.get_mut(&table_id)?; - let mut index_schema = table - .get_schema() - .indexes - .iter() - .find(|x| x.index_id == index_id)? - .clone(); - index_schema.alias = old_alias; - table.with_mut_schema(|s| s.update_index(index_schema)); - } // A table was removed. Add it back. TableRemoved(table_id, table) => { let is_view_table = table.schema.is_view(); diff --git a/crates/datastore/src/locking_tx_datastore/datastore.rs b/crates/datastore/src/locking_tx_datastore/datastore.rs index 667990b3e75..e9d67103b16 100644 --- a/crates/datastore/src/locking_tx_datastore/datastore.rs +++ b/crates/datastore/src/locking_tx_datastore/datastore.rs @@ -294,15 +294,6 @@ impl Locking { tx.alter_table_primary_key(table_id, primary_key) } - pub fn alter_index_source_name_mut_tx( - &self, - tx: &mut MutTxId, - index_id: IndexId, - source_name: spacetimedb_sats::raw_identifier::RawIdentifier, - ) -> Result<()> { - tx.alter_index_source_name(index_id, source_name) - } - pub fn alter_table_row_type_mut_tx( &self, tx: &mut MutTxId, @@ -536,15 +527,6 @@ impl MutTxDatastore for Locking { tx.drop_index(index_id) } - fn alter_index_source_name_mut_tx( - &self, - tx: &mut Self::MutTx, - index_id: IndexId, - source_name: spacetimedb_sats::raw_identifier::RawIdentifier, - ) -> Result<()> { - tx.alter_index_source_name(index_id, source_name) - } - fn index_id_from_name_mut_tx(&self, tx: &Self::MutTx, index_name: &str) -> Result> { tx.index_id_from_name_or_alias(index_name) } diff --git a/crates/datastore/src/locking_tx_datastore/mut_tx.rs b/crates/datastore/src/locking_tx_datastore/mut_tx.rs index 90e00e2f0c4..3a36331dc4d 100644 --- a/crates/datastore/src/locking_tx_datastore/mut_tx.rs +++ b/crates/datastore/src/locking_tx_datastore/mut_tx.rs @@ -1163,46 +1163,6 @@ impl MutTxId { Ok(()) } - /// Change the runtime source-name alias of the index identified by `index_id`. - pub(crate) fn alter_index_source_name( - &mut self, - index_id: IndexId, - source_name: spacetimedb_sats::raw_identifier::RawIdentifier, - ) -> Result<()> { - let st_index_ref = self - .iter_by_col_eq(ST_INDEX_ID, StIndexFields::IndexId, &index_id.into())? - .next() - .ok_or_else(|| TableError::IdNotFound(SystemTable::st_index, index_id.into()))?; - let st_index_row = StIndexRow::try_from(st_index_ref)?; - let table_id = st_index_row.table_id; - - let old_alias = self - .find_st_index_accessor_row_by_index_name(st_index_row.index_name.as_ref())? - .map(|row| row.accessor_name); - - if old_alias.as_ref() == Some(&source_name) { - return Ok(()); - } - - let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?; - let mut index_schema = tx_table - .get_schema() - .indexes - .iter() - .find(|index| index.index_id == index_id) - .cloned() - .ok_or_else(|| TableError::IdNotFound(SystemTable::st_index, index_id.into()))?; - index_schema.alias = Some(source_name.clone()); - tx_table.with_mut_schema_and_clone(commit_table, |s| s.update_index(index_schema)); - - self.drop_st_index_accessor(&st_index_row.index_name)?; - self.insert_st_index_accessor(&st_index_row.index_name, Some(&source_name))?; - - self.push_schema_change(PendingSchemaChange::IndexAlterSourceName(table_id, index_id, old_alias)); - - Ok(()) - } - /// Change the row type of the table identified by `table_id`. /// /// In practice, this should not error, diff --git a/crates/datastore/src/locking_tx_datastore/tx_state.rs b/crates/datastore/src/locking_tx_datastore/tx_state.rs index 4b56aa1dfca..4e712bcf684 100644 --- a/crates/datastore/src/locking_tx_datastore/tx_state.rs +++ b/crates/datastore/src/locking_tx_datastore/tx_state.rs @@ -111,13 +111,6 @@ pub enum PendingSchemaChange { /// If adding this index caused the pointer map to be removed, /// it will be present here. IndexAdded(TableId, IndexId, Option), - /// The source-name alias of the index with [`IndexId`] changed. - /// The old alias is stored for rollback. - IndexAlterSourceName( - TableId, - IndexId, - Option, - ), /// The [`Table`] with [`TableId`] was removed. TableRemoved(TableId, Table), /// The table with [`TableId`] was added. @@ -152,9 +145,6 @@ impl MemoryUsage for PendingSchemaChange { Self::IndexAdded(table_id, index_id, pointer_map) => { table_id.heap_usage() + index_id.heap_usage() + pointer_map.heap_usage() } - Self::IndexAlterSourceName(table_id, index_id, alias) => { - table_id.heap_usage() + index_id.heap_usage() + alias.heap_usage() - } Self::TableRemoved(table_id, table) => table_id.heap_usage() + table.heap_usage(), Self::TableAdded(table_id) => table_id.heap_usage(), Self::TableAlterAccess(table_id, st_access) => table_id.heap_usage() + st_access.heap_usage(), diff --git a/crates/datastore/src/traits.rs b/crates/datastore/src/traits.rs index 07a1d633124..e1b99825ae2 100644 --- a/crates/datastore/src/traits.rs +++ b/crates/datastore/src/traits.rs @@ -627,12 +627,6 @@ pub trait MutTxDatastore: TxDatastore + MutTx { fn create_index_mut_tx(&self, tx: &mut Self::MutTx, index_schema: IndexSchema, is_unique: bool) -> Result; fn drop_index_mut_tx(&self, tx: &mut Self::MutTx, index_id: IndexId) -> Result<()>; - fn alter_index_source_name_mut_tx( - &self, - tx: &mut Self::MutTx, - index_id: IndexId, - source_name: spacetimedb_sats::raw_identifier::RawIdentifier, - ) -> Result<()>; fn index_id_from_name_mut_tx(&self, tx: &Self::MutTx, index_name: &str) -> super::Result>; // TODO: Index data diff --git a/crates/schema/src/auto_migrate.rs b/crates/schema/src/auto_migrate.rs index bb69c4bea55..f2fba813fa8 100644 --- a/crates/schema/src/auto_migrate.rs +++ b/crates/schema/src/auto_migrate.rs @@ -284,9 +284,6 @@ pub enum AutoMigrateStep<'def> { /// no `ChangeColumns` steps will be, for the same table. AddColumns(::Key<'def>), - /// Change the runtime source-name alias of an existing index. - ChangeIndexSourceName(::Key<'def>), - /// Add a table, including all indexes, constraints, and sequences. /// There will NOT be separate steps in the plan for adding indexes, constraints, and sequences. AddTable(::Key<'def>), @@ -984,8 +981,6 @@ fn auto_migrate_indexes( if old.algorithm != new.algorithm { plan.steps.push(AutoMigrateStep::RemoveIndex(old.key())); plan.steps.push(AutoMigrateStep::AddIndex(old.key())); - } else if old.source_name != new.source_name { - plan.steps.push(AutoMigrateStep::ChangeIndexSourceName(old.key())); } Ok(()) } @@ -2044,42 +2039,6 @@ mod tests { ); } - #[test] - fn migrate_index_with_changed_source_name() { - fn module_def(source_name: &str) -> ModuleDef { - create_module_def_v10(|builder| { - builder - .build_table_with_new_type( - "FruitBasket", - ProductType::from([("basket_id", AlgebraicType::U64), ("fruit_name", AlgebraicType::String)]), - true, - ) - .with_index(btree([0, 1]), source_name.to_owned(), "fruitNameIndex") - .finish(); - }) - } - - let old_def = module_def("OldBasketLookup"); - let new_def = module_def("NewBasketLookup"); - let index_name = RawIdentifier::new("fruit_basket_basket_id_fruit_name_idx_btree"); - - let plan = ponder_auto_migrate(&old_def, &new_def).expect("auto migration should succeed"); - let steps = &plan.steps[..]; - - assert!( - steps.contains(&AutoMigrateStep::ChangeIndexSourceName(&index_name)), - "steps: {steps:?}" - ); - assert!( - !steps.contains(&AutoMigrateStep::RemoveIndex(&index_name)), - "steps: {steps:?}" - ); - assert!( - !steps.contains(&AutoMigrateStep::AddIndex(&index_name)), - "steps: {steps:?}" - ); - } - #[test] fn migrate_view_disconnect_clients() { struct TestCase { diff --git a/crates/schema/src/auto_migrate/formatter.rs b/crates/schema/src/auto_migrate/formatter.rs index 7296c89df44..1f7377209bf 100644 --- a/crates/schema/src/auto_migrate/formatter.rs +++ b/crates/schema/src/auto_migrate/formatter.rs @@ -59,10 +59,6 @@ fn format_step( let index_info = extract_index_info(*index, plan.old)?; f.format_index(&index_info, Action::Removed) } - AutoMigrateStep::ChangeIndexSourceName(index) => { - let index_info = extract_index_info(*index, plan.new)?; - f.format_index(&index_info, Action::Changed) - } AutoMigrateStep::RemoveConstraint(constraint) => { let constraint_info = extract_constraint_info(*constraint, plan.old)?; f.format_constraint(&constraint_info, Action::Removed) diff --git a/crates/smoketests/tests/smoketests/typescript_index_source_name.rs b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs index c600e6282d6..ecf23982ab9 100644 --- a/crates/smoketests/tests/smoketests/typescript_index_source_name.rs +++ b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs @@ -1,8 +1,8 @@ use spacetimedb_smoketests::{random_string, require_local_server, require_pnpm, Smoketest}; -const TYPESCRIPT_MODULE_V1: &str = r#"import { schema, table, t } from "spacetimedb/server"; +const TYPESCRIPT_MODULE_WITHOUT_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server"; -const appUsers = table( +const users = table( { name: "users", public: false }, { id: t.u64().primaryKey().autoInc(), @@ -12,7 +12,7 @@ const appUsers = table( ); const spacetimedb = schema({ - appUsers, + users, }); export default spacetimedb; @@ -22,7 +22,7 @@ export const insert_user = spacetimedb.reducer( emailAddress: t.string(), }, (ctx, { name, emailAddress }) => { - ctx.db.appUsers.insert({ + ctx.db.users.insert({ id: 0n, name, emailAddress, @@ -33,7 +33,7 @@ export const insert_user = spacetimedb.reducer( const TYPESCRIPT_MODULE_WITH_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server"; -const appUsers = table( +const users = table( { name: "users", public: false }, { id: t.u64().primaryKey().autoInc(), @@ -45,7 +45,7 @@ const appUsers = table( ); const spacetimedb = schema({ - appUsers, + users, }); export default spacetimedb; @@ -53,7 +53,7 @@ export const find_user_by_email = spacetimedb.reducer( { emailAddress: t.string() }, (ctx, { emailAddress }) => { let count = 0; - for (const _row of ctx.db.appUsers.emailAddress.filter(emailAddress)) { + for (const _row of ctx.db.users.emailAddress.filter(emailAddress)) { count += 1; } console.info(`matched ${count}`); @@ -64,7 +64,7 @@ export const find_users_by_active_status = spacetimedb.reducer( { isActive: t.bool() }, (ctx, { isActive }) => { let count = 0; - for (const _row of ctx.db.appUsers.isActive.filter(isActive)) { + for (const _row of ctx.db.users.isActive.filter(isActive)) { count += 1; } console.info(`matched active users ${count}`); @@ -72,34 +72,6 @@ export const find_users_by_active_status = spacetimedb.reducer( ); "#; -const TYPESCRIPT_MODULE_V2_RENAMED_ACCESSOR: &str = r#"import { schema, table, t } from "spacetimedb/server"; - -const renamedUsers = table( - { name: "users", public: false }, - { - id: t.u64().primaryKey().autoInc(), - name: t.string(), - emailAddress: t.string().index("btree"), - }, -); - -const spacetimedb = schema({ - renamedUsers, -}); -export default spacetimedb; - -export const find_user_by_email = spacetimedb.reducer( - { emailAddress: t.string() }, - (ctx, { emailAddress }) => { - let count = 0; - for (const _row of ctx.db.renamedUsers.emailAddress.filter(emailAddress)) { - count += 1; - } - console.info(`matched ${count}`); - }, -); -"#; - #[test] fn test_typescript_add_optional_columns() { require_pnpm!(); @@ -109,7 +81,11 @@ fn test_typescript_add_optional_columns() { let module_name = format!("typescript-add-optional-columns-{}", random_string()); let database_identity = test - .publish_typescript_module_source("typescript-add-optional-columns-v1", &module_name, TYPESCRIPT_MODULE_V1) + .publish_typescript_module_source( + "typescript-add-optional-columns-v1", + &module_name, + TYPESCRIPT_MODULE_WITHOUT_NEW_COLUMNS, + ) .unwrap(); test.call("insert_user", &["Alice", "alice@example.com"]).unwrap(); @@ -127,28 +103,3 @@ fn test_typescript_add_optional_columns() { test.call("find_user_by_email", &["alice@example.com"]).unwrap(); test.call("find_users_by_active_status", &["false"]).unwrap(); } - -#[test] -fn test_typescript_change_index_source_name() { - require_pnpm!(); - require_local_server!(); - - let mut test = Smoketest::builder().autopublish(false).build(); - let module_name = format!("typescript-change-source-name-{}", random_string()); - - let database_identity = test - .publish_typescript_module_source("typescript-change-source-name-v1", &module_name, TYPESCRIPT_MODULE_V1) - .unwrap(); - - test.call("insert_user", &["Alice", "alice@example.com"]).unwrap(); - - test.publish_typescript_module_source_clear( - "typescript-change-source-name-v2", - &database_identity, - TYPESCRIPT_MODULE_V2_RENAMED_ACCESSOR, - false, - ) - .unwrap(); - - test.call("find_user_by_email", &["alice@example.com"]).unwrap(); -} From f6ce7377688183e4726a7aa60a838c376a7158b2 Mon Sep 17 00:00:00 2001 From: Shubham Mishra Date: Wed, 3 Jun 2026 22:57:14 +0530 Subject: [PATCH 6/6] use different accessor name --- .../smoketests/typescript_index_source_name.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/smoketests/tests/smoketests/typescript_index_source_name.rs b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs index ecf23982ab9..8c550f81aeb 100644 --- a/crates/smoketests/tests/smoketests/typescript_index_source_name.rs +++ b/crates/smoketests/tests/smoketests/typescript_index_source_name.rs @@ -2,7 +2,7 @@ use spacetimedb_smoketests::{random_string, require_local_server, require_pnpm, const TYPESCRIPT_MODULE_WITHOUT_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server"; -const users = table( +const AppUsers = table( { name: "users", public: false }, { id: t.u64().primaryKey().autoInc(), @@ -12,7 +12,7 @@ const users = table( ); const spacetimedb = schema({ - users, + AppUsers, }); export default spacetimedb; @@ -22,7 +22,7 @@ export const insert_user = spacetimedb.reducer( emailAddress: t.string(), }, (ctx, { name, emailAddress }) => { - ctx.db.users.insert({ + ctx.db.AppUsers.insert({ id: 0n, name, emailAddress, @@ -33,7 +33,7 @@ export const insert_user = spacetimedb.reducer( const TYPESCRIPT_MODULE_WITH_NEW_COLUMNS: &str = r#"import { schema, table, t } from "spacetimedb/server"; -const users = table( +const AppUsers = table( { name: "users", public: false }, { id: t.u64().primaryKey().autoInc(), @@ -45,7 +45,7 @@ const users = table( ); const spacetimedb = schema({ - users, + AppUsers, }); export default spacetimedb; @@ -53,7 +53,7 @@ export const find_user_by_email = spacetimedb.reducer( { emailAddress: t.string() }, (ctx, { emailAddress }) => { let count = 0; - for (const _row of ctx.db.users.emailAddress.filter(emailAddress)) { + for (const _row of ctx.db.AppUsers.emailAddress.filter(emailAddress)) { count += 1; } console.info(`matched ${count}`); @@ -64,7 +64,7 @@ export const find_users_by_active_status = spacetimedb.reducer( { isActive: t.bool() }, (ctx, { isActive }) => { let count = 0; - for (const _row of ctx.db.users.isActive.filter(isActive)) { + for (const _row of ctx.db.AppUsers.isActive.filter(isActive)) { count += 1; } console.info(`matched active users ${count}`);