Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/datastore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ test = ["spacetimedb-commitlog/test", "spacetimedb-schema/test"]
spacetimedb-lib = { path = "../lib", features = ["proptest"] }
spacetimedb-sats = { path = "../sats", features = ["proptest"] }
spacetimedb-commitlog = { path = "../commitlog", features = ["test"] }
spacetimedb-schema = { workspace = true, features = ["test"] }

# Also as dev-dependencies for use in _this_ crate's tests.
proptest.workspace = true
Expand Down
26 changes: 26 additions & 0 deletions crates/datastore/src/locking_tx_datastore/committed_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,32 @@ 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 accessor name alias changed. Change it back.
TableAlterAccessorName(table_id, old_alias) => {
let table = self.tables.get_mut(&table_id)?;
table.with_mut_schema(|s| s.alias = old_alias);
}
// A column accessor name alias changed. Change it back.
ColumnAlterAccessorName(table_id, col_id, old_alias) => {
let table = self.tables.get_mut(&table_id)?;
table.with_mut_schema(|s| {
if let Some(col) = s.columns.iter_mut().find(|x| x.col_pos == col_id) {
col.alias = old_alias;
}
});
}
// A table was removed. Add it back.
TableRemoved(table_id, table) => {
let is_view_table = table.schema.is_view();
Expand Down
56 changes: 56 additions & 0 deletions crates/datastore/src/locking_tx_datastore/datastore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,34 @@ 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::RawNamespacedIdentifier,
) -> Result<()> {
tx.alter_index_source_name(index_id, source_name)
}

pub fn alter_table_accessor_name_mut_tx(
&self,
tx: &mut MutTxId,
table_id: TableId,
new_alias: spacetimedb_schema::identifier::NamespacedIdentifier,
) -> Result<()> {
tx.alter_table_accessor_name(table_id, new_alias)
}

pub fn alter_column_accessor_name_mut_tx(
&self,
tx: &mut MutTxId,
table_id: TableId,
col_id: ColId,
new_alias: spacetimedb_schema::identifier::Identifier,
) -> Result<()> {
tx.alter_column_accessor_name(table_id, col_id, new_alias)
}

pub fn alter_table_row_type_mut_tx(
&self,
tx: &mut MutTxId,
Expand Down Expand Up @@ -551,6 +579,34 @@ 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::RawNamespacedIdentifier,
) -> Result<()> {
tx.alter_index_source_name(index_id, source_name)
}

fn alter_table_accessor_name_mut_tx(
&self,
tx: &mut Self::MutTx,
table_id: TableId,
new_alias: spacetimedb_schema::identifier::NamespacedIdentifier,
) -> Result<()> {
tx.alter_table_accessor_name(table_id, new_alias)
}

fn alter_column_accessor_name_mut_tx(
&self,
tx: &mut Self::MutTx,
table_id: TableId,
col_id: ColId,
new_alias: spacetimedb_schema::identifier::Identifier,
) -> Result<()> {
tx.alter_column_accessor_name(table_id, col_id, new_alias)
}

fn index_id_from_name_mut_tx(&self, tx: &Self::MutTx, index_name: &str) -> Result<Option<IndexId>> {
tx.index_id_from_name_or_alias(index_name)
}
Expand Down
156 changes: 140 additions & 16 deletions crates/datastore/src/locking_tx_datastore/mut_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use crate::{
locking_tx_datastore::state_view::ScanOrIndex,
traits::{InsertFlags, RowTypeForTable, TxData, UpdateFlags},
};
use core::{cell::RefCell, iter, mem, ops::RangeBounds};
use core::{cell::RefCell, iter, ops::RangeBounds};
use itertools::Either;
use smallvec::SmallVec;
use spacetimedb_data_structures::map::{HashMap, HashSet, IntMap};
Expand Down Expand Up @@ -1372,16 +1372,18 @@ impl MutTxId {
impl MutTxId {
/// Set the table access of `table_id` to `access`.
pub(crate) fn alter_table_access(&mut self, table_id: TableId, access: StAccess) -> Result<()> {
let old_access = self.find_st_table_row(table_id)?.table_access;

// Write to the table in the tx state.
let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;
tx_table.with_mut_schema_and_clone(commit_table, |s| s.table_access = access);

// Update system tables.
let old_access = self.update_st_table_row(table_id, |st| mem::replace(&mut st.table_access, access))?;

// Remember the pending change so we can undo if necessary.
// Remember the pending change so we can undo it if a later system-table update fails.
self.push_schema_change(PendingSchemaChange::TableAlterAccess(table_id, old_access));

// Update system tables.
self.update_st_table_row(table_id, |st| st.table_access = access)?;

Ok(())
}

Expand All @@ -1390,18 +1392,142 @@ impl MutTxId {
/// Updates both the in-memory schema and the `st_table` system table.
/// See: <https://github.com/clockworklabs/SpacetimeDB/issues/3934>
pub(crate) fn alter_table_primary_key(&mut self, table_id: TableId, new_primary_key: Option<ColId>) -> Result<()> {
let old_pk = self.find_st_table_row(table_id)?.table_primary_key;

// Write to the table in the tx state.
let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;
tx_table.with_mut_schema_and_clone(commit_table, |s| s.primary_key = new_primary_key);

// Update system tables.
// Remember the pending change so we can undo it if a later system-table update fails.
let new_pk_col_list = new_primary_key.map(|col| col.into());
let old_pk =
self.update_st_table_row(table_id, |st| mem::replace(&mut st.table_primary_key, new_pk_col_list))?;

// Remember the pending change so we can undo if necessary.
self.push_schema_change(PendingSchemaChange::TableAlterPrimaryKey(table_id, old_pk));

// Update system tables.
self.update_st_table_row(table_id, |st| st.table_primary_key = new_pk_col_list)?;

Ok(())
}

/// Change the source-name alias of the index identified by `index_id`.
pub(crate) fn alter_index_source_name(
&mut self,
index_id: IndexId,
source_name: RawNamespacedIdentifier,
) -> 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.push_schema_change(PendingSchemaChange::IndexAlterSourceName(table_id, index_id, old_alias));

self.drop_st_index_accessor(&st_index_row.index_name)?;
self.insert_st_index_accessor(&st_index_row.index_name, Some(&source_name))?;

Ok(())
}

/// Change the accessor name alias of the table identified by `table_id`.
pub(crate) fn alter_table_accessor_name(
&mut self,
table_id: TableId,
new_alias: NamespacedIdentifier,
) -> Result<()> {
let table_name = self.find_st_table_row(table_id)?.table_name;

let old_alias = self
.iter_by_col_eq(
ST_TABLE_ACCESSOR_ID,
StTableAccessorFields::TableName,
&table_name.as_ref().into(),
)?
.next()
.and_then(|row| StTableAccessorRow::try_from(row).ok())
.map(|row| row.accessor_name);

if old_alias.as_ref() == Some(&new_alias) {
return Ok(());
}

let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;
tx_table.with_mut_schema_and_clone(commit_table, |s| s.alias = Some(new_alias.clone()));
self.push_schema_change(PendingSchemaChange::TableAlterAccessorName(table_id, old_alias));

self.drop_st_table_accessor(&table_name)?;
self.insert_st_table_accessor(&table_name, Some(&new_alias))?;

Ok(())
}

/// Change the accessor name alias of the column identified by `table_id` and `col_id`.
pub(crate) fn alter_column_accessor_name(
&mut self,
table_id: TableId,
col_id: ColId,
new_alias: Identifier,
) -> Result<()> {
let table_name = self.find_st_table_row(table_id)?.table_name;

// Read old column info from the current schema
let col_info: Vec<(Identifier, Option<Identifier>, ColId)> = self
.schema_for_table(table_id)?
.columns
.iter()
.map(|c| (c.col_name.clone(), c.alias.clone(), c.col_pos))
.collect();

let old_alias = col_info
.iter()
.find(|(_, _, pos)| *pos == col_id)
.and_then(|(_, alias, _)| alias.clone());

if old_alias.as_ref() == Some(&new_alias) {
return Ok(());
}

// Update system tables: drop and re-insert all column accessor rows for this table
self.drop_st_column_accessor(&table_name)?;
for (col_name, alias, pos) in &col_info {
let name = if *pos == col_id {
&new_alias
} else {
alias.as_ref().unwrap_or(col_name)
};
self.insert_st_column_accessor(&table_name, col_name, Some(name))?;
}

// Update in-memory schema
let ((tx_table, ..), (commit_table, ..)) = self.get_or_create_insert_table_mut(table_id)?;
tx_table.with_mut_schema_and_clone(commit_table, |s| {
if let Some(col) = s.columns.iter_mut().find(|c| c.col_pos == col_id) {
col.alias = Some(new_alias);
}
});

self.push_schema_change(PendingSchemaChange::ColumnAlterAccessorName(
table_id, col_id, old_alias,
));

Ok(())
}

Expand Down Expand Up @@ -1437,6 +1563,8 @@ impl MutTxId {
// SAFETY: `commit_table` should have a schema identical to that of `tx_table`
// prior to changing it just now.
unsafe { commit_table.set_layout_and_schema_to(tx_table) };
// Remember the pending change so we can undo it if a later system-table update fails.
self.push_schema_change(PendingSchemaChange::TableAlterRowType(table_id, old_column_schemas));

// Update system tables.
// We'll simply remove all rows in `st_columns` and then add the new ones.
Expand All @@ -1446,9 +1574,6 @@ impl MutTxId {
self.drop_st_column_accessor(&table_name)?;
self.insert_st_column(&table_name, &column_schemas)?;

// Remember the pending change so we can undo if necessary.
self.push_schema_change(PendingSchemaChange::TableAlterRowType(table_id, old_column_schemas));

Ok(())
}

Expand Down Expand Up @@ -1477,6 +1602,8 @@ impl MutTxId {
commit_table
.change_columns_of_empty_table_to(column_schemas.clone())
.map_err(|_| TableError::EventTableNotEmpty(table_id))?;
// Remember the pending change so we can undo it if a later system-table update fails.
self.push_schema_change(PendingSchemaChange::ReschemaEventTable(table_id, old_column_schemas));

// Update system tables.
// We'll simply remove all rows in `st_columns` and then add the new ones.
Expand All @@ -1486,9 +1613,6 @@ impl MutTxId {
self.drop_st_column_accessor(&table_name)?;
self.insert_st_column(&table_name, &column_schemas)?;

// Remember the pending change so we can undo if necessary.
self.push_schema_change(PendingSchemaChange::ReschemaEventTable(table_id, old_column_schemas));

Ok(())
}

Expand Down
24 changes: 23 additions & 1 deletion crates/datastore/src/locking_tx_datastore/tx_state.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::{delete_table::DeleteTable, sequence::Sequence};
use spacetimedb_data_structures::map::IntMap;
use spacetimedb_lib::db::auth::StAccess;
use spacetimedb_primitives::{ColList, ConstraintId, IndexId, SequenceId, TableId};
use spacetimedb_primitives::{ColId, ColList, ConstraintId, IndexId, SequenceId, TableId};
use spacetimedb_sats::memory_usage::MemoryUsage;
use spacetimedb_sats::raw_identifier::RawNamespacedIdentifier;
use spacetimedb_schema::identifier::{Identifier, NamespacedIdentifier};
use spacetimedb_schema::schema::{ColumnSchema, ConstraintSchema, IndexSchema, SequenceSchema};
use spacetimedb_table::{
blob_store::{BlobStore, HashMapBlobStore},
Expand Down Expand Up @@ -111,6 +113,15 @@ pub enum PendingSchemaChange {
/// If adding this index caused the pointer map to be removed,
/// it will be present here.
IndexAdded(TableId, IndexId, Option<PointerMap>),
/// The source-name alias of the index with [`IndexId`] changed.
/// The old alias is stored for rollback.
IndexAlterSourceName(TableId, IndexId, Option<RawNamespacedIdentifier>),
/// The accessor name alias of the table with [`TableId`] changed.
/// The old alias is stored for rollback.
TableAlterAccessorName(TableId, Option<NamespacedIdentifier>),
/// The accessor name alias of the column with [`ColId`] in the table with [`TableId`] changed.
/// The old alias is stored for rollback.
ColumnAlterAccessorName(TableId, ColId, Option<Identifier>),
/// The [`Table`] with [`TableId`] was removed.
TableRemoved(TableId, Table),
/// The table with [`TableId`] was added.
Expand Down Expand Up @@ -153,6 +164,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.as_ref().map(|a| a.as_ref().len()).unwrap_or(0)
}
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(),
Expand All @@ -168,6 +182,14 @@ impl MemoryUsage for PendingSchemaChange {
table_id.heap_usage() + sequence.heap_usage() + sequence_schema.heap_usage()
}
Self::SequenceAdded(table_id, sequence_id) => table_id.heap_usage() + sequence_id.heap_usage(),
Self::TableAlterAccessorName(table_id, alias) => {
table_id.heap_usage() + alias.as_ref().map(|a| a.as_ref().len()).unwrap_or(0)
}
Self::ColumnAlterAccessorName(table_id, col_id, alias) => {
table_id.heap_usage()
+ col_id.heap_usage()
+ alias.as_ref().map(|a| a.as_raw().heap_usage()).unwrap_or(0)
}
Self::ReschemaEventTable(table_id, column_schemas) => table_id.heap_usage() + column_schemas.heap_usage(),
}
}
Expand Down
Loading
Loading