@@ -43,7 +43,7 @@ use spacetimedb_schema::{
43
43
use spacetimedb_table:: {
44
44
blob_store:: { BlobStore , HashMapBlobStore } ,
45
45
indexes:: { RowPointer , SquashedOffset } ,
46
- table:: { IndexScanIter , InsertError , RowRef , Table } ,
46
+ table:: { IndexScanIter , InsertError , RowRef , Table , TableAndIndex } ,
47
47
} ;
48
48
use std:: {
49
49
sync:: Arc ,
@@ -395,15 +395,15 @@ impl MutTxId {
395
395
//
396
396
// Ensure adding the index does not cause a unique constraint violation due to
397
397
// the existing rows having the same value for some column(s).
398
- let mut insert_index = table. new_index ( index . index_id , & columns, is_unique) ?;
398
+ let mut insert_index = table. new_index ( columns. clone ( ) , is_unique) ?;
399
399
let mut build_from_rows = |table : & Table , bs : & dyn BlobStore | -> Result < ( ) > {
400
- if let Some ( violation) = insert_index. build_from_rows ( & columns , table. scan_rows ( bs) ) ? {
400
+ if let Some ( violation) = insert_index. build_from_rows ( table. scan_rows ( bs) ) ? {
401
401
let violation = table
402
402
. get_row_ref ( bs, violation)
403
403
. expect ( "row came from scanning the table" )
404
404
. project ( & columns)
405
405
. expect ( "`cols` should consist of valid columns for this table" ) ;
406
- return Err ( IndexError :: from ( table. build_error_unique ( & insert_index, & columns , violation) ) . into ( ) ) ;
406
+ return Err ( IndexError :: from ( table. build_error_unique ( & insert_index, index_id , violation) ) . into ( ) ) ;
407
407
}
408
408
Ok ( ( ) )
409
409
} ;
@@ -421,16 +421,17 @@ impl MutTxId {
421
421
build_from_rows ( commit_table, commit_blob_store) ?;
422
422
}
423
423
424
- table. add_index ( columns. clone ( ) , insert_index) ;
425
- // Associate `index_id -> (table_id, col_list)` for fast lookup.
426
- idx_map. insert ( index_id, ( table_id, columns. clone ( ) ) ) ;
427
-
428
424
log:: trace!(
429
425
"INDEX CREATED: {} for table: {} and col(s): {:?}" ,
430
426
index_id,
431
427
table_id,
432
428
columns
433
429
) ;
430
+
431
+ table. add_index ( index_id, insert_index) ;
432
+ // Associate `index_id -> table_id` for fast lookup.
433
+ idx_map. insert ( index_id, table_id) ;
434
+
434
435
// Update the table's schema.
435
436
// This won't clone-write when creating a table but likely to otherwise.
436
437
table. with_mut_schema ( |s| s. indexes . push ( index) ) ;
@@ -455,16 +456,10 @@ impl MutTxId {
455
456
// Remove the index in the transaction's insert table.
456
457
// By altering the insert table, this gets moved over to the committed state on merge.
457
458
let ( table, blob_store, idx_map, ..) = self . get_or_create_insert_table_mut ( table_id) ?;
458
- if let Some ( col) = table
459
- . indexes
460
- . iter ( )
461
- . find ( |( _, idx) | idx. index_id == index_id)
462
- . map ( |( cols, _) | cols. clone ( ) )
463
- {
459
+ if table. delete_index ( blob_store, index_id) {
464
460
// This likely will do a clone-write as over time?
465
461
// The schema might have found other referents.
466
- table. with_mut_schema ( |s| s. indexes . retain ( |x| x. index_algorithm . columns ( ) != & col) ) ;
467
- table. delete_index ( blob_store, & col) ;
462
+ table. with_mut_schema ( |s| s. indexes . retain ( |x| x. index_id != index_id) ) ;
468
463
}
469
464
// Remove the `index_id -> (table_id, col_list)` association.
470
465
idx_map. remove ( & index_id) ;
@@ -497,21 +492,20 @@ impl MutTxId {
497
492
rstart : & [ u8 ] ,
498
493
rend : & [ u8 ] ,
499
494
) -> Result < ( TableId , BTreeScan < ' a > ) > {
500
- // Extract the table and index type for the tx state.
501
- let ( table_id, col_list, tx_idx_key_type) = self
502
- . get_table_and_index_type ( index_id)
503
- . ok_or_else ( || IndexError :: NotFound ( index_id) ) ?;
495
+ // Extract the table id, index type, and commit/tx indices.
496
+ let ( table_id_and_index_ty, commit_index, tx_index) = self . get_table_and_index_type ( index_id) ;
497
+ let ( table_id, index_ty) = table_id_and_index_ty. ok_or_else ( || IndexError :: NotFound ( index_id) ) ?;
504
498
505
499
// TODO(centril): Once we have more index types than `btree`,
506
500
// we'll need to enforce that `index_id` refers to a btree index.
507
501
508
502
// We have the index key type, so we can decode everything.
509
- let bounds = Self :: btree_decode_bounds ( tx_idx_key_type , prefix , prefix_elems , rstart , rend )
510
- . map_err ( IndexError :: Decode ) ?;
503
+ let bounds =
504
+ Self :: btree_decode_bounds ( index_ty , prefix , prefix_elems , rstart , rend ) . map_err ( IndexError :: Decode ) ?;
511
505
512
506
// Get an index seek iterator for the tx and committed state.
513
- let tx_iter = self . tx_state . index_seek ( table_id , col_list , & bounds) ;
514
- let commit_iter = self . committed_state_write_lock . index_seek ( table_id , col_list , & bounds) ;
507
+ let tx_iter = tx_index . map ( |i| i . seek ( & bounds) ) ;
508
+ let commit_iter = commit_index . map ( |i| i . seek ( & bounds) ) ;
515
509
516
510
// Chain together the indexed rows in the tx and committed state,
517
511
// but don't yield rows deleted in the tx state.
@@ -521,7 +515,7 @@ impl MutTxId {
521
515
None => Left ( iter) ,
522
516
Some ( deletes) => Right ( IndexScanFilterDeleted { iter, deletes } ) ,
523
517
} ) ;
524
- // this is effectively just `tx_iter.into_iter().flatten().chain(commit_iter.into_iter().flatten())`,
518
+ // This is effectively just `tx_iter.into_iter().flatten().chain(commit_iter.into_iter().flatten())`,
525
519
// but with all the branching and `Option`s flattened to just one layer.
526
520
let iter = match ( tx_iter, commit_iter) {
527
521
( None , None ) => Empty ( iter:: empty ( ) ) ,
@@ -534,34 +528,46 @@ impl MutTxId {
534
528
Ok ( ( table_id, BTreeScan { inner : iter } ) )
535
529
}
536
530
537
- /// Translate `index_id` to the table id, the column list and index key type.
538
- fn get_table_and_index_type ( & self , index_id : IndexId ) -> Option < ( TableId , & ColList , & AlgebraicType ) > {
531
+ /// Translate `index_id` to the table id, index type, and commit/tx indices.
532
+ fn get_table_and_index_type (
533
+ & self ,
534
+ index_id : IndexId ,
535
+ ) -> (
536
+ Option < ( TableId , & AlgebraicType ) > ,
537
+ Option < TableAndIndex < ' _ > > ,
538
+ Option < TableAndIndex < ' _ > > ,
539
+ ) {
539
540
// The order of querying the committed vs. tx state for the translation is not important.
540
541
// But it is vastly more likely that it is in the committed state,
541
542
// so query that first to avoid two lookups.
542
- let & ( table_id, ref col_list) = self
543
- . committed_state_write_lock
544
- . index_id_map
545
- . get ( & index_id)
546
- . or_else ( || self . tx_state . index_id_map . get ( & index_id) ) ?;
547
-
548
- // The tx state must have the index.
543
+ //
544
+ // Also, the tx state must have the index.
549
545
// If the index was e.g., dropped from the tx state but exists physically in the committed state,
550
546
// the index does not exist, semantically.
551
547
// TODO: handle the case where the table has been dropped in this transaction.
552
- let key_ty = if let Some ( key_ty ) = self
548
+ let commit_table_id = self
553
549
. committed_state_write_lock
554
- . get_table_and_index_type ( table_id, col_list)
555
- {
556
- if self . tx_state_removed_index ( index_id) {
557
- return None ;
558
- }
559
- key_ty
550
+ . get_table_for_index ( index_id)
551
+ . filter ( |_| !self . tx_state_removed_index ( index_id) ) ;
552
+
553
+ let ( table_id, commit_index, tx_index) = if let t_id @ Some ( table_id) = commit_table_id {
554
+ // Index found for commit state, might also exist for tx state.
555
+ let commit_index = self
556
+ . committed_state_write_lock
557
+ . get_index_by_id_with_table ( table_id, index_id) ;
558
+ let tx_index = self . tx_state . get_index_by_id_with_table ( table_id, index_id) ;
559
+ ( t_id, commit_index, tx_index)
560
+ } else if let t_id @ Some ( table_id) = self . tx_state . get_table_for_index ( index_id) {
561
+ // Index might exist for tx state.
562
+ let tx_index = self . tx_state . get_index_by_id_with_table ( table_id, index_id) ;
563
+ ( t_id, None , tx_index)
560
564
} else {
561
- self . tx_state . get_table_and_index_type ( table_id, col_list) ?
565
+ // No index in either side.
566
+ ( None , None , None )
562
567
} ;
563
-
564
- Some ( ( table_id, col_list, key_ty) )
568
+ let index_ty = commit_index. or ( tx_index) . map ( |index| & index. index ( ) . key_type ) ;
569
+ let table_id_and_index_ty = table_id. zip ( index_ty) ;
570
+ ( table_id_and_index_ty, commit_index, tx_index)
565
571
}
566
572
567
573
/// Returns whether the index with `index_id` was removed in this transaction.
@@ -1540,7 +1546,7 @@ impl StateView for MutTxId {
1540
1546
// TODO(george): It's unclear that we truly support dynamically creating an index
1541
1547
// yet. In particular, I don't know if creating an index in a transaction and
1542
1548
// rolling it back will leave the index in place.
1543
- if let Some ( inserted_rows) = self . tx_state . index_seek ( table_id, & cols, & range) {
1549
+ if let Some ( inserted_rows) = self . tx_state . index_seek_by_cols ( table_id, & cols, & range) {
1544
1550
let committed_rows = self . committed_state_write_lock . index_seek ( table_id, & cols, & range) ;
1545
1551
// The current transaction has modified this table, and the table is indexed.
1546
1552
Ok ( if let Some ( del_table) = self . tx_state . get_delete_table ( table_id) {
0 commit comments