@@ -27,12 +27,12 @@ use anyhow::{anyhow, Context};
27
27
use core:: { cell:: RefCell , ops:: RangeBounds } ;
28
28
use parking_lot:: { Mutex , RwLock } ;
29
29
use spacetimedb_commitlog:: payload:: { txdata, Txdata } ;
30
- use spacetimedb_data_structures:: map:: { HashCollectionExt , HashMap } ;
30
+ use spacetimedb_data_structures:: map:: { HashCollectionExt , HashMap , IntMap } ;
31
31
use spacetimedb_durability:: TxOffset ;
32
32
use spacetimedb_lib:: { db:: auth:: StAccess , metrics:: ExecutionMetrics } ;
33
33
use spacetimedb_lib:: { ConnectionId , Identity } ;
34
34
use spacetimedb_paths:: server:: SnapshotDirPath ;
35
- use spacetimedb_primitives:: { ColList , ConstraintId , IndexId , SequenceId , TableId } ;
35
+ use spacetimedb_primitives:: { ColId , ColList , ConstraintId , IndexId , SequenceId , TableId } ;
36
36
use spacetimedb_sats:: memory_usage:: MemoryUsage ;
37
37
use spacetimedb_sats:: { bsatn, buffer:: BufReader , AlgebraicValue , ProductValue } ;
38
38
use spacetimedb_schema:: schema:: { ColumnSchema , IndexSchema , SequenceSchema , TableSchema } ;
@@ -313,6 +313,16 @@ impl Locking {
313
313
) -> Result < ( ) > {
314
314
tx. alter_table_row_type ( table_id, column_schemas)
315
315
}
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
+ }
316
326
}
317
327
318
328
impl DataRow for Locking {
@@ -1159,6 +1169,8 @@ mod tests {
1159
1169
use core:: { fmt, mem} ;
1160
1170
use itertools:: Itertools ;
1161
1171
use pretty_assertions:: { assert_eq, assert_matches} ;
1172
+ use spacetimedb_data_structures:: map:: IntMap ;
1173
+ use spacetimedb_execution:: dml:: MutDatastore as _;
1162
1174
use spacetimedb_execution:: Datastore ;
1163
1175
use spacetimedb_lib:: db:: auth:: { StAccess , StTableType } ;
1164
1176
use spacetimedb_lib:: error:: ResultTest ;
@@ -3320,8 +3332,139 @@ mod tests {
3320
3332
3321
3333
Ok ( ( ) )
3322
3334
}
3323
-
3324
3335
// TODO: Add the following tests
3325
3336
// - Create a tx that inserts 2000 rows with an auto_inc column
3326
3337
// - 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
+ }
3327
3470
}
0 commit comments