@@ -335,6 +335,16 @@ impl Locking {
335335 ) -> Result < ( ) > {
336336 tx. alter_table_row_type ( table_id, column_schemas)
337337 }
338+
339+ pub fn add_columns_to_table_mut_tx (
340+ & self ,
341+ tx : & mut MutTxId ,
342+ table_id : TableId ,
343+ column_schemas : Vec < ColumnSchema > ,
344+ defaults : Vec < AlgebraicValue > ,
345+ ) -> Result < TableId > {
346+ tx. add_columns_to_table ( table_id, column_schemas, defaults)
347+ }
338348}
339349
340350impl DataRow for Locking {
@@ -1264,6 +1274,7 @@ mod tests {
12641274 use core:: { fmt, mem} ;
12651275 use itertools:: Itertools ;
12661276 use pretty_assertions:: { assert_eq, assert_matches} ;
1277+ use spacetimedb_execution:: dml:: MutDatastore as _;
12671278 use spacetimedb_execution:: Datastore ;
12681279 use spacetimedb_lib:: db:: auth:: { StAccess , StTableType } ;
12691280 use spacetimedb_lib:: error:: ResultTest ;
@@ -3228,11 +3239,12 @@ mod tests {
32283239 // Now drop the table again and commit.
32293240 assert ! ( datastore. drop_table_mut_tx( & mut tx, table_id) . is_ok( ) ) ;
32303241 let tx_data = commit ( & datastore, tx) ?;
3231- let ( _, deleted ) = tx_data
3242+ let ( _, deleted_rows ) = tx_data
32323243 . deletes ( )
32333244 . find ( |( id, _) | * * id == table_id)
32343245 . expect ( "should have deleted rows for `table_id`" ) ;
3235- assert_eq ! ( & * * deleted, [ row] ) ;
3246+ assert_eq ! ( & * * deleted_rows, [ row] ) ;
3247+ assert ! ( tx_data. truncates( ) . contains( & table_id) , "table should be truncated" ) ;
32363248
32373249 // In the next transaction, the table doesn't exist.
32383250 assert ! (
@@ -3453,8 +3465,148 @@ mod tests {
34533465
34543466 Ok ( ( ) )
34553467 }
3456-
34573468 // TODO: Add the following tests
34583469 // - Create a tx that inserts 2000 rows with an auto_inc column
34593470 // - Create a tx that inserts 2000 rows with an auto_inc column and then rolls back
3471+
3472+ #[ test]
3473+ fn test_add_columns_to_table ( ) -> ResultTest < ( ) > {
3474+ let datastore = get_datastore ( ) ?;
3475+
3476+ let mut tx = begin_mut_tx ( & datastore) ;
3477+
3478+ let initial_sum_type = AlgebraicType :: sum ( [ ( "ba" , AlgebraicType :: U16 ) ] ) ;
3479+ let initial_columns = [
3480+ ColumnSchema :: for_test ( 0 , "a" , AlgebraicType :: U64 ) ,
3481+ ColumnSchema :: for_test ( 1 , "b" , initial_sum_type. clone ( ) ) ,
3482+ ] ;
3483+
3484+ let initial_indices = [
3485+ IndexSchema :: for_test ( "index_a" , BTreeAlgorithm :: from ( 0 ) ) ,
3486+ IndexSchema :: for_test ( "index_b" , BTreeAlgorithm :: from ( 1 ) ) ,
3487+ ] ;
3488+
3489+ let sequence = SequenceRow {
3490+ id : SequenceId :: SENTINEL . into ( ) ,
3491+ table : 0 ,
3492+ col_pos : 0 ,
3493+ name : "Foo_id_seq" ,
3494+ start : 5 ,
3495+ } ;
3496+
3497+ let schema = user_public_table (
3498+ initial_columns,
3499+ initial_indices. clone ( ) ,
3500+ [ ] ,
3501+ map_array ( [ sequence] ) ,
3502+ None ,
3503+ None ,
3504+ ) ;
3505+
3506+ let table_id = datastore. create_table_mut_tx ( & mut tx, schema) ?;
3507+
3508+ let mut columns_original = tx. get_schema ( table_id) . unwrap ( ) . columns . to_vec ( ) ;
3509+
3510+ // Insert initial rows
3511+ let initial_row = product ! [ 0u64 , AlgebraicValue :: sum( 0 , AlgebraicValue :: U16 ( 1 ) ) ] ;
3512+ insert ( & datastore, & mut tx, table_id, & initial_row) . unwrap ( ) ;
3513+ insert ( & datastore, & mut tx, table_id, & initial_row) . unwrap ( ) ;
3514+ commit ( & datastore, tx) ?;
3515+
3516+ // Alter Table: Add Variant and Column
3517+ //
3518+ // Change the `b` column, adding a variant.
3519+ let vars_ref = & mut columns_original[ 1 ] . col_type . as_sum_mut ( ) . unwrap ( ) . variants ;
3520+ let mut vars = Vec :: from ( mem:: take ( vars_ref) ) ;
3521+ vars. push ( SumTypeVariant :: new_named ( AlgebraicType :: U8 , "bb" ) ) ;
3522+ * vars_ref = vars. into ( ) ;
3523+ // Add column `c`
3524+ let mut new_columns = columns_original. clone ( ) ;
3525+ new_columns. push ( ColumnSchema :: for_test ( 2 , "c" , AlgebraicType :: U8 ) ) ;
3526+ let defaults = vec ! [ AlgebraicValue :: U8 ( 42 ) ] ;
3527+
3528+ let mut tx = begin_mut_tx ( & datastore) ;
3529+ // insert a row in tx_state before adding column
3530+ tx. insert_product_value ( table_id, & initial_row) . unwrap ( ) ;
3531+ // add column and then rollback
3532+ let rollback_table_id =
3533+ datastore. add_columns_to_table_mut_tx ( & mut tx, table_id, new_columns. clone ( ) , defaults. clone ( ) ) ?;
3534+ let _ = tx. rollback ( ) ;
3535+
3536+ let old_rows = [
3537+ product ! [ 5u64 , AlgebraicValue :: sum( 0 , 1u16 . into( ) ) ] ,
3538+ product ! [ 6u64 , AlgebraicValue :: sum( 0 , 1u16 . into( ) ) ] ,
3539+ ] ;
3540+
3541+ let mut tx = begin_mut_tx ( & datastore) ;
3542+ // check rollback was successful
3543+ let rows = tx
3544+ . table_scan ( table_id)
3545+ . unwrap ( )
3546+ . map ( |row| row. to_product_value ( ) )
3547+ . collect :: < Vec < _ > > ( ) ;
3548+ assert_eq ! ( rows, old_rows, "Rows shouldn't be changed if rolledback" ) ;
3549+ let table = tx. table ( rollback_table_id) ;
3550+ assert ! ( table. is_none( ) , "new table shouldn't be created if rolledback" ) ;
3551+
3552+ // Add column and actually commit this time.
3553+ tx. insert_product_value ( table_id, & initial_row) . unwrap ( ) ;
3554+ let new_table_id = datastore. add_columns_to_table_mut_tx ( & mut tx, table_id, new_columns. clone ( ) , defaults) ?;
3555+
3556+ let tx_data = commit ( & datastore, tx) ?;
3557+
3558+ assert_ne ! (
3559+ new_table_id, table_id,
3560+ "New table ID after migration should differ from old one"
3561+ ) ;
3562+
3563+ // Validate Commitlog Changes
3564+ let ( _, deletes) = tx_data
3565+ . deletes ( )
3566+ . find ( |( id, _) | * * id == table_id)
3567+ . expect ( "Expected delete log for original table" ) ;
3568+
3569+ assert_eq ! (
3570+ & * * deletes, & old_rows,
3571+ "Unexpected delete entries after altering the table"
3572+ ) ;
3573+
3574+ let inserted_rows = [
3575+ product ! [ 5u64 , AlgebraicValue :: sum( 0 , 1u16 . into( ) ) , 42u8 ] ,
3576+ product ! [ 6u64 , AlgebraicValue :: sum( 0 , 1u16 . into( ) ) , 42u8 ] ,
3577+ product ! [ 8u64 , AlgebraicValue :: sum( 0 , 1u16 . into( ) ) , 42u8 ] ,
3578+ ] ;
3579+
3580+ let ( _, inserts) = tx_data
3581+ . inserts ( )
3582+ . find ( |( id, _) | * * id == new_table_id)
3583+ . expect ( "Expected insert log for new table" ) ;
3584+
3585+ assert_eq ! (
3586+ & * * inserts, & inserted_rows,
3587+ "Unexpected insert entries after altering the table"
3588+ ) ;
3589+
3590+ // Insert Rows into New Table
3591+ let mut tx = begin_mut_tx ( & datastore) ;
3592+
3593+ let new_row = product ! [ 0u64 , AlgebraicValue :: sum( 0 , 1u16 . into( ) ) , 0u8 ] ;
3594+ tx. insert_product_value ( new_table_id, & new_row) . unwrap ( ) ;
3595+ commit ( & datastore, tx) ?;
3596+
3597+ // test for auto_inc feields
3598+ let tx = begin_mut_tx ( & datastore) ;
3599+ let rows = tx. table_scan ( new_table_id) . unwrap ( ) . map ( |row| row. to_product_value ( ) ) ;
3600+
3601+ let mut last_row_auto_inc = 0 ;
3602+ for row in rows {
3603+ let auto_inc_col = row. get_field ( 0 , None ) ?;
3604+ if let AlgebraicValue :: U64 ( val) = auto_inc_col {
3605+ assert ! ( val > & last_row_auto_inc, "Auto-increment value did not increase" ) ;
3606+ last_row_auto_inc = * val;
3607+ }
3608+ }
3609+
3610+ Ok ( ( ) )
3611+ }
34603612}
0 commit comments