@@ -27,12 +27,12 @@ use anyhow::{anyhow, Context};
2727use core:: { cell:: RefCell , ops:: RangeBounds } ;
2828use parking_lot:: { Mutex , RwLock } ;
2929use spacetimedb_commitlog:: payload:: { txdata, Txdata } ;
30- use spacetimedb_data_structures:: map:: { HashCollectionExt , HashMap } ;
30+ use spacetimedb_data_structures:: map:: { HashCollectionExt , HashMap , IntMap } ;
3131use spacetimedb_durability:: TxOffset ;
3232use spacetimedb_lib:: { db:: auth:: StAccess , metrics:: ExecutionMetrics } ;
3333use spacetimedb_lib:: { ConnectionId , Identity } ;
3434use spacetimedb_paths:: server:: SnapshotDirPath ;
35- use spacetimedb_primitives:: { ColList , ConstraintId , IndexId , SequenceId , TableId } ;
35+ use spacetimedb_primitives:: { ColId , ColList , ConstraintId , IndexId , SequenceId , TableId } ;
3636use spacetimedb_sats:: memory_usage:: MemoryUsage ;
3737use spacetimedb_sats:: { bsatn, buffer:: BufReader , AlgebraicValue , ProductValue } ;
3838use spacetimedb_schema:: schema:: { ColumnSchema , IndexSchema , SequenceSchema , TableSchema } ;
@@ -313,6 +313,16 @@ impl Locking {
313313 ) -> Result < ( ) > {
314314 tx. alter_table_row_type ( table_id, column_schemas)
315315 }
316+
317+ pub fn add_columns_to_table_mut_tx (
318+ & self ,
319+ tx : & mut MutTxId ,
320+ table_id : TableId ,
321+ column_schemas : Vec < ColumnSchema > ,
322+ defaults : IntMap < ColId , AlgebraicValue > ,
323+ ) -> Result < TableId > {
324+ tx. add_columns_to_table ( table_id, column_schemas, defaults)
325+ }
316326}
317327
318328impl DataRow for Locking {
@@ -1159,6 +1169,8 @@ mod tests {
11591169 use core:: { fmt, mem} ;
11601170 use itertools:: Itertools ;
11611171 use pretty_assertions:: { assert_eq, assert_matches} ;
1172+ use spacetimedb_data_structures:: map:: IntMap ;
1173+ use spacetimedb_execution:: dml:: MutDatastore as _;
11621174 use spacetimedb_execution:: Datastore ;
11631175 use spacetimedb_lib:: db:: auth:: { StAccess , StTableType } ;
11641176 use spacetimedb_lib:: error:: ResultTest ;
@@ -3320,8 +3332,139 @@ mod tests {
33203332
33213333 Ok ( ( ) )
33223334 }
3323-
33243335 // TODO: Add the following tests
33253336 // - Create a tx that inserts 2000 rows with an auto_inc column
33263337 // - Create a tx that inserts 2000 rows with an auto_inc column and then rolls back
3338+
3339+ #[ test]
3340+ fn test_add_columns_to_table ( ) -> ResultTest < ( ) > {
3341+ let datastore = get_datastore ( ) ?;
3342+
3343+ // ─── Setup Initial Table ─────────────────────────────────────────────────────
3344+ let mut tx = begin_mut_tx ( & datastore) ;
3345+
3346+ let initial_sum_type = AlgebraicType :: sum ( [ ( "ba" , AlgebraicType :: U16 ) ] ) ;
3347+ let initial_columns = [
3348+ ColumnSchema :: for_test ( 0 , "a" , AlgebraicType :: U64 ) ,
3349+ ColumnSchema :: for_test ( 1 , "b" , initial_sum_type. clone ( ) ) ,
3350+ ] ;
3351+
3352+ let initial_indices = [
3353+ IndexSchema :: for_test ( "index_a" , BTreeAlgorithm :: from ( 0 ) ) ,
3354+ IndexSchema :: for_test ( "index_b" , BTreeAlgorithm :: from ( 1 ) ) ,
3355+ ] ;
3356+
3357+ let sequence = SequenceRow {
3358+ id : SequenceId :: SENTINEL . into ( ) ,
3359+ table : 0 ,
3360+ col_pos : 0 ,
3361+ name : "Foo_id_seq" ,
3362+ start : 5 ,
3363+ } ;
3364+
3365+ let schema = user_public_table (
3366+ initial_columns,
3367+ initial_indices. clone ( ) ,
3368+ [ ] ,
3369+ map_array ( [ sequence] ) ,
3370+ None ,
3371+ None ,
3372+ ) ;
3373+
3374+ let table_id = datastore. create_table_mut_tx ( & mut tx, schema) ?;
3375+
3376+ let columns_original = tx. get_schema ( table_id) . unwrap ( ) . columns . to_vec ( ) ;
3377+
3378+ // Insert initial rows
3379+ let initial_row = ProductValue {
3380+ elements : [ AlgebraicValue :: U64 ( 0 ) , AlgebraicValue :: sum ( 0 , AlgebraicValue :: U16 ( 1 ) ) ] . into ( ) ,
3381+ } ;
3382+ tx. insert_product_value ( table_id, & initial_row) . unwrap ( ) ;
3383+ tx. insert_product_value ( table_id, & initial_row) . unwrap ( ) ;
3384+
3385+ commit ( & datastore, tx) ?;
3386+
3387+ // Alter Table: Add Variant and Column
3388+ let mut new_columns = columns_original. clone ( ) ;
3389+ new_columns. push ( ColumnSchema :: for_test ( 2 , "c" , AlgebraicType :: U8 ) ) ;
3390+ let defaults = IntMap :: from_iter ( [ ( 2 . into ( ) , AlgebraicValue :: U8 ( 42 ) ) ] ) ;
3391+
3392+ let mut tx = begin_mut_tx ( & datastore) ;
3393+ let new_table_id = datastore. add_columns_to_table_mut_tx ( & mut tx, table_id, new_columns. clone ( ) , defaults) ?;
3394+ let tx_data = commit ( & datastore, tx) ?;
3395+
3396+ assert_ne ! (
3397+ new_table_id, table_id,
3398+ "New table ID after migration should differ from old one"
3399+ ) ;
3400+
3401+ // Validate Commitlog Changes
3402+ let ( _, deletes) = tx_data
3403+ . deletes ( )
3404+ . find ( |( id, _) | * * id == table_id)
3405+ . expect ( "Expected delete log for original table" ) ;
3406+
3407+ let deleted_rows = [
3408+ product ! [ AlgebraicValue :: U64 ( 5 ) , AlgebraicValue :: sum( 0 , AlgebraicValue :: U16 ( 1 ) ) ] ,
3409+ product ! [ AlgebraicValue :: U64 ( 6 ) , AlgebraicValue :: sum( 0 , AlgebraicValue :: U16 ( 1 ) ) ] ,
3410+ ] ;
3411+
3412+ assert_eq ! (
3413+ & * * deletes, & deleted_rows,
3414+ "Unexpected delete entries after altering the table"
3415+ ) ;
3416+
3417+ let inserted_rows = [
3418+ product ! [
3419+ AlgebraicValue :: U64 ( 5 ) ,
3420+ AlgebraicValue :: sum( 0 , AlgebraicValue :: U16 ( 1 ) ) ,
3421+ AlgebraicValue :: U8 ( 42 )
3422+ ] ,
3423+ product ! [
3424+ AlgebraicValue :: U64 ( 6 ) ,
3425+ AlgebraicValue :: sum( 0 , AlgebraicValue :: U16 ( 1 ) ) ,
3426+ AlgebraicValue :: U8 ( 42 )
3427+ ] ,
3428+ ] ;
3429+
3430+ let ( _, inserts) = tx_data
3431+ . inserts ( )
3432+ . find ( |( id, _) | * * id == new_table_id)
3433+ . expect ( "Expected insert log for new table" ) ;
3434+
3435+ assert_eq ! (
3436+ & * * inserts, & inserted_rows,
3437+ "Unexpected insert entries after altering the table"
3438+ ) ;
3439+
3440+ // Insert Rows into Altered Table
3441+ let mut tx = begin_mut_tx ( & datastore) ;
3442+
3443+ let new_row = ProductValue {
3444+ elements : [
3445+ AlgebraicValue :: U64 ( 0 ) ,
3446+ AlgebraicValue :: sum ( 0 , AlgebraicValue :: U16 ( 1 ) ) ,
3447+ AlgebraicValue :: U8 ( 0 ) ,
3448+ ]
3449+ . into ( ) ,
3450+ } ;
3451+
3452+ tx. insert_product_value ( new_table_id, & new_row) . unwrap ( ) ;
3453+ commit ( & datastore, tx) ?;
3454+
3455+ // Scan and Print Final Rows
3456+ let tx = begin_mut_tx ( & datastore) ;
3457+ let rows = tx. table_scan ( new_table_id) . unwrap ( ) . map ( |row| row. to_product_value ( ) ) ;
3458+
3459+ let mut last_row_auto_inc = 0 ;
3460+ for row in rows {
3461+ let auto_inc_col = row. get_field ( 0 , None ) ?;
3462+ if let AlgebraicValue :: U64 ( val) = auto_inc_col {
3463+ assert ! ( val > & last_row_auto_inc, "Auto-increment value did not increase" ) ;
3464+ last_row_auto_inc = * val;
3465+ }
3466+ }
3467+
3468+ Ok ( ( ) )
3469+ }
33273470}
0 commit comments