diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 3bdea326..c9725b65 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -4,7 +4,6 @@ mod persist_table; mod worktable; use proc_macro::TokenStream; - // TODO: Refactor this codegen stuff because it's now too strange. #[proc_macro] diff --git a/codegen/src/name_generator.rs b/codegen/src/name_generator.rs index 61c642d8..cdc3fe96 100644 --- a/codegen/src/name_generator.rs +++ b/codegen/src/name_generator.rs @@ -29,6 +29,13 @@ impl WorktableNameGenerator { Ident::new(format!("{}Row", self.name).as_str(), Span::mixed_site()) } + pub fn get_available_type_ident(&self) -> Ident { + Ident::new( + format!("{}AvaiableTypes", self.name).as_str(), + Span::mixed_site(), + ) + } + pub fn get_work_table_ident(&self) -> Ident { Ident::new( format!("{}WorkTable", self.name).as_str(), diff --git a/codegen/src/persist_table/generator/space_file/mod.rs b/codegen/src/persist_table/generator/space_file/mod.rs index 37e32d4a..c4586d4a 100644 --- a/codegen/src/persist_table/generator/space_file/mod.rs +++ b/codegen/src/persist_table/generator/space_file/mod.rs @@ -128,7 +128,8 @@ impl Generator { pk_gen: PrimaryKeyGeneratorState::from_state(self.info.inner.pk_gen_state), lock_map: LockMap::new(), table_name: "", - pk_phantom: std::marker::PhantomData + pk_phantom: std::marker::PhantomData, + types_phantom: std::marker::PhantomData, }; #wt_ident( diff --git a/codegen/src/worktable/generator/index.rs b/codegen/src/worktable/generator/index.rs index 5897282c..c0389a67 100644 --- a/codegen/src/worktable/generator/index.rs +++ b/codegen/src/worktable/generator/index.rs @@ -1,7 +1,7 @@ use crate::name_generator::WorktableNameGenerator; use crate::worktable::generator::Generator; -use proc_macro2::{Literal, TokenStream}; +use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::quote; impl Generator { @@ -51,14 +51,17 @@ impl Generator { let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); let row_type_ident = name_generator.get_row_type_ident(); let index_type_ident = name_generator.get_index_type_ident(); + let avt_type_ident = name_generator.get_available_type_ident(); let save_row_fn = self.gen_save_row_index_fn(); let delete_row_fn = self.gen_delete_row_index_fn(); + let process_difference_fn = self.gen_process_difference_index_fn(); quote! { - impl TableSecondaryIndex<#row_type_ident> for #index_type_ident { + impl TableSecondaryIndex<#row_type_ident, #avt_type_ident> for #index_type_ident { #save_row_fn #delete_row_fn + #process_difference_fn } } } @@ -128,6 +131,58 @@ impl Generator { } } } + + /// Generates `process_difference` function of `TableIndex` trait for index. It updates `Link` for all secondary indexes. + /// Uses HashMap<&str, Difference> for storing all changes + fn gen_process_difference_index_fn(&self) -> TokenStream { + let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); + let avt_type_ident = name_generator.get_available_type_ident(); + + let process_difference_rows = self.columns.indexes.iter().map(|(i, idx)| { + let index_field_name = &idx.name; + let diff_key = Literal::string(i.to_string().as_str()); + + let match_arm = if let Some(t) = self.columns.columns_map.get(&idx.field) { + let type_str = t.to_string(); + let variant_ident = Ident::new(&type_str.to_uppercase(), Span::mixed_site()); + + let (new_value_expr, old_value_expr) = if type_str == "String" { + (quote! { new.to_string() }, quote! { old.to_string() }) + } else { + (quote! { *new }, quote! { *old }) + }; + + quote! { + if let Some(diff) = difference.get(#diff_key) { + if let #avt_type_ident::#variant_ident(old) = &diff.old { + let key_old = #old_value_expr; + TableIndex::remove(&self.#index_field_name, key_old, link); + } + + if let #avt_type_ident::#variant_ident(new) = &diff.new { + let key_new = #new_value_expr; + TableIndex::insert(&self.#index_field_name, key_new, link); + } + } + } + } else { + quote! {} + }; + + match_arm + }); + + quote! { + fn process_difference( + &self, + link: Link, + difference: std::collections::HashMap<&str, Difference<#avt_type_ident>> + ) -> core::result::Result<(), WorkTableError> { + #(#process_difference_rows)* + core::result::Result::Ok(()) + } + } + } } // TODO: tests... diff --git a/codegen/src/worktable/generator/mod.rs b/codegen/src/worktable/generator/mod.rs index 83b02a09..a07f4d0e 100644 --- a/codegen/src/worktable/generator/mod.rs +++ b/codegen/src/worktable/generator/mod.rs @@ -4,6 +4,7 @@ mod queries; mod row; mod table; //mod table_old; +//mod table_index; mod wrapper; use proc_macro2::Ident; diff --git a/codegen/src/worktable/generator/queries/type.rs b/codegen/src/worktable/generator/queries/type.rs index 5505f982..b50dc5ea 100644 --- a/codegen/src/worktable/generator/queries/type.rs +++ b/codegen/src/worktable/generator/queries/type.rs @@ -1,9 +1,46 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; +use crate::name_generator::WorktableNameGenerator; use crate::worktable::generator::Generator; impl Generator { + pub fn gen_available_types_def(&mut self) -> syn::Result { + let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); + let avt_type_ident = name_generator.get_available_type_ident(); + + let rows: Vec<_> = self + .columns + .indexes + .iter() + .filter_map(|(_, idx)| self.columns.columns_map.get(&idx.field)) + .map(|s| { + let type_ident = Ident::new(s.to_string().as_str(), Span::mixed_site()); + let type_upper = + Ident::new(&s.to_string().to_uppercase().as_str(), Span::mixed_site()); + Some(quote! { + #[from] + #type_upper(#type_ident), + }) + }) + .collect(); + + if !rows.is_empty() { + Ok(quote! { + #[derive(rkyv::Archive, Debug, derive_more::Display, rkyv::Deserialize, Clone, rkyv::Serialize)] + #[derive(From, PartialEq)] + #[non_exhaustive] + pub enum #avt_type_ident { + #(#rows)* + } + }) + } else { + Ok(quote! { + type #avt_type_ident = (); + }) + } + } + pub fn gen_result_types_def(&mut self) -> syn::Result { if let Some(queries) = &self.queries { let query_defs = queries @@ -31,6 +68,7 @@ impl Generator { .collect::, _>>()?; Ok::<_, syn::Error>(quote! { + #[derive(rkyv::Archive, Debug, rkyv::Deserialize, Clone, rkyv::Serialize)] #[repr(C)] pub struct #ident { diff --git a/codegen/src/worktable/generator/queries/update.rs b/codegen/src/worktable/generator/queries/update.rs index 5013e149..cd03c309 100644 --- a/codegen/src/worktable/generator/queries/update.rs +++ b/codegen/src/worktable/generator/queries/update.rs @@ -1,3 +1,4 @@ +use proc_macro2::Literal; use std::collections::HashMap; use crate::name_generator::WorktableNameGenerator; @@ -100,6 +101,22 @@ impl Generator { .values() .find(|idx| idx.field.to_string() == op.by.to_string()); + let indexes_columns: Option> = { + let columns: Vec<_> = self + .columns + .indexes + .values() + .filter(|idx| op.columns.contains(&idx.field)) + .map(|idx| idx.field.clone()) + .collect(); + + if columns.is_empty() { + None + } else { + Some(columns) + } + }; + let idents = &op.columns; if let Some(index) = index { let index_name = &index.name; @@ -114,7 +131,12 @@ impl Generator { if self.columns.primary_keys.first().unwrap().to_string() == op.by.to_string() { - self.gen_pk_update(snake_case_name, name, idents) + self.gen_pk_update( + snake_case_name, + name, + idents, + indexes_columns.as_ref(), + ) } else { todo!() } @@ -135,6 +157,7 @@ impl Generator { snake_case_name: String, name: &Ident, idents: &Vec, + idx_idents: Option<&Vec>, ) -> TokenStream { let pk_ident = &self.pk.as_ref().unwrap().ident; let method_ident = Ident::new( @@ -160,6 +183,10 @@ impl Generator { format!("verify_{snake_case_name}_lock").as_str(), Span::mixed_site(), ); + + let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); + let avt_type_ident = name_generator.get_available_type_ident(); + let row_updates = idents .iter() .map(|i| { @@ -169,6 +196,46 @@ impl Generator { }) .collect::>(); + let diff = idx_idents.map(|columns| { + idents + .iter() + .filter(|i| columns.contains(i)) + .map(|i| { + let diff_key = Literal::string(i.to_string().as_str()); + quote! { + let old: #avt_type_ident = row_old.clone().#i.into(); + let new: #avt_type_ident = row_new.#i.into(); + + let diff = Difference { + old: old.clone(), + new: new.clone(), + }; + + diffs.insert(#diff_key, diff); + } + }) + .collect::>() + }); + + let diff_container_ident = if let Some(ref diff) = diff { + quote! { + let row_old = self.select(by.clone()).unwrap(); + let row_new = row.clone(); + let mut diffs: std::collections::HashMap<&str, Difference<#avt_type_ident>> = std::collections::HashMap::new(); + #(#diff)* + } + } else { + quote! {} + }; + + let process_diff_ident = if let Some(_) = diff { + quote! { + self.0.indexes.process_difference(link, diffs)?; + } + } else { + quote! {} + }; + quote! { pub async fn #method_ident(&self, row: #query_ident, by: #pk_ident) -> core::result::Result<(), WorkTableError> { let op_id = self.0.lock_map.next_id(); @@ -176,6 +243,8 @@ impl Generator { self.0.lock_map.insert(op_id.into(), lock.clone()); + #diff_container_ident + let mut bytes = rkyv::to_bytes::(&row).map_err(|_| WorkTableError::SerializeError)?; let mut row = unsafe { rkyv::access_unchecked_mut::<<#query_ident as rkyv::Archive>::Archived>(&mut bytes[..]).unseal_unchecked() }; let link = self.0 @@ -183,6 +252,9 @@ impl Generator { .get(&by) .map(|v| v.get().value) .ok_or(WorkTableError::NotFound)?; + + #process_diff_ident + let id = self.0.data.with_ref(link, |archived| { archived.#check_ident() }).map_err(WorkTableError::PagesError)?; diff --git a/codegen/src/worktable/generator/table/mod.rs b/codegen/src/worktable/generator/table/mod.rs index 70e01db1..5d073ae6 100644 --- a/codegen/src/worktable/generator/table/mod.rs +++ b/codegen/src/worktable/generator/table/mod.rs @@ -56,6 +56,7 @@ impl Generator { let primary_key_type = name_generator.get_primary_key_type_ident(); let index_type = name_generator.get_index_type_ident(); let inner_const_name = name_generator.get_page_inner_size_const_ident(); + let avt_type_ident = name_generator.get_available_type_ident(); let derive = if self.is_persist { quote! { @@ -81,6 +82,7 @@ impl Generator { WorkTable< #row_type, #primary_key_type, + #avt_type_ident, #index_type, <#primary_key_type as TablePrimaryKey>::Generator, #inner_const_name @@ -95,6 +97,7 @@ impl Generator { WorkTable< #row_type, #primary_key_type, + #avt_type_ident, #index_type > #persist_type_part diff --git a/codegen/src/worktable/mod.rs b/codegen/src/worktable/mod.rs index 21a7b752..0f82964b 100644 --- a/codegen/src/worktable/mod.rs +++ b/codegen/src/worktable/mod.rs @@ -53,6 +53,7 @@ pub fn expand(input: TokenStream) -> syn::Result { let index_def = generator.gen_index_def(); let table_def = generator.gen_table_def()?; let query_types_def = generator.gen_result_types_def()?; + let query_available_def = generator.gen_available_types_def()?; let query_locks_impls = generator.gen_query_locks_impl()?; let select_impls = generator.gen_query_select_impl()?; let update_impls = generator.gen_query_update_impl()?; @@ -61,6 +62,7 @@ pub fn expand(input: TokenStream) -> syn::Result { Ok(TokenStream::from(quote! { #pk_def #row_def + #query_available_def #wrapper_def #index_def #table_def diff --git a/examples/src/main.rs b/examples/src/main.rs index 97d46068..45854cad 100644 --- a/examples/src/main.rs +++ b/examples/src/main.rs @@ -1,6 +1,4 @@ -use std::fmt::Debug; - -use async_std::task; +use futures::executor::block_on; use worktable::prelude::*; use worktable::worktable; @@ -11,18 +9,23 @@ fn main() { columns: { id: u64 primary_key autoincrement, val: i64, - attribute: String, + test: u8, + attr: String, + attr2: i16, }, indexes: { - attribute_idx: attribute, - } + idx1: attr, + idx2: attr2, + }, queries: { update: { - ValByAttr(val) by attribute, + ValById(val) by id, + AllAttrById(attr, attr2) by id, + UpdateOptionalById(test) by id, }, delete: { - ByAttr() by attribute, + ByAttr() by attr, ById() by id, } } @@ -33,72 +36,38 @@ fn main() { // WT rows (has prefix My because of table name) let row = MyRow { - val: 1, - attribute: "TEST".to_string(), + val: 777, + attr: "Attribute1".to_string(), + attr2: 345, + test: 1, id: 0, }; - let row1 = MyRow { - val: 2, - attribute: "TEST2".to_string(), - id: 1, - }; - - let row2 = MyRow { - val: 1337, - attribute: "TEST2".to_string(), - id: 2, - }; - - let row3 = MyRow { - val: 555, - attribute: "TEST3".to_string(), - id: 3, - }; - // insert - let _ = my_table.insert(row); - let _ = my_table.insert(row1); - let _ = my_table.insert(row2); - let _ = my_table.insert(row3); + let pk: MyPrimaryKey = my_table.insert(row).expect("primary key"); // Select ALL records from WT let select_all = my_table.select_all().execute(); println!("Select All {:?}", select_all); - // Select All records with attribute TEST2 - let select_by_attr = my_table.select_by_attribute("TEST2".to_string()); - println!( - "Select by Attribute TEST2: {:?}", - select_by_attr.unwrap().vals - ); - - // Update all recrods val by attr TEST2 - let update_val = my_table.update_val_by_attr(ValByAttrQuery { val: 777 }, "TEST2".to_string()); - let _ = task::block_on(update_val); + // Select All records with attribute TEST + let select_all = my_table.select_all().execute(); + println!("Select All {:?}", select_all); - let select_updated = my_table.select_by_attribute("TEST2".to_string()); - println!( - "Select updated by Attribute TEST2: {:?}", - select_updated.unwrap().vals - ); + // Select by Idx + let select_by_attr = my_table.select_by_attr("Attribute1".to_string()); + println!("Select by idx {:?}", select_by_attr.unwrap().vals); - // Update record attribute TEST2 -> TEST3 with id 1 - let update_exchange = - my_table.update_val_by_attr(ValByAttrQuery { val: 7777 }, "TEST2".to_string()); - let _ = task::block_on(update_exchange); + // Update Value query + let update = my_table.update_val_by_id(ValByIdQuery { val: 1337 }, pk.clone()); + let _ = block_on(update); - let select_all_after_update = my_table.select_all(); - println!( - "Select After Val Update by Attribute: {:?}", - select_all_after_update.execute() - ); + let select_all = my_table.select_all().execute(); + println!("Select after update val {:?}", select_all); - let test_delete = my_table.delete_by_attr("TEST3".to_string()); - let _ = task::block_on(test_delete); + let delete = my_table.delete(pk); + let _ = block_on(delete); - println!( - "Select after deleted TEST3 {:?}", - my_table.select_all().execute() - ); + let select_all = my_table.select_all().execute(); + println!("Select after delete {:?}", select_all); } diff --git a/performance_measurement/src/profiler.rs b/performance_measurement/src/profiler.rs index 7e4700e6..6c78532b 100644 --- a/performance_measurement/src/profiler.rs +++ b/performance_measurement/src/profiler.rs @@ -36,7 +36,7 @@ pub struct PerformanceProfiler; impl PerformanceProfiler { pub fn store_measurement(function_name: &'static str, duration: Duration) { - let mut global_performance_measurements = &GLOBAL_PERFORMANCE_MEASUREMENTS; + let global_performance_measurements = &GLOBAL_PERFORMANCE_MEASUREMENTS; let duration_ms = duration.as_nanos() as f64 / 1_000_000.0; diff --git a/src/index/mod.rs b/src/index/mod.rs index 72f11413..e1d151a1 100644 --- a/src/index/mod.rs +++ b/src/index/mod.rs @@ -1,3 +1,4 @@ +use std::hash::Hash; use std::sync::Arc; use data_bucket::Link; @@ -15,3 +16,40 @@ pub enum IndexType<'a, T> { Unique(&'a TreeIndex), NonUnique(&'a TreeIndex>>), } + +#[derive(Debug)] +pub struct Difference { + pub old: AvailableTypes, + pub new: AvailableTypes, +} + +pub trait TableIndex { + fn insert(&self, value: T, link: Link) -> Option; + fn remove(&self, value: T, link: Link) -> Option<(T, Link)>; +} + +impl TableIndex for IndexMultiMap +where + T: Eq + Hash + Clone + std::marker::Send + std::cmp::Ord, +{ + fn insert(&self, value: T, link: Link) -> Option { + self.insert(value, link) + } + + fn remove(&self, value: T, link: Link) -> Option<(T, Link)> { + self.remove(&value, &link) + } +} + +impl TableIndex for IndexMap +where + T: Eq + Hash + Clone + std::marker::Send + std::cmp::Ord, +{ + fn insert(&self, value: T, link: Link) -> Option { + self.insert(value, link) + } + + fn remove(&self, value: T, _link: Link) -> Option<(T, Link)> { + self.remove(&value) + } +} diff --git a/src/index/table_secondary_index.rs b/src/index/table_secondary_index.rs index a40279b0..0e4f67a7 100644 --- a/src/index/table_secondary_index.rs +++ b/src/index/table_secondary_index.rs @@ -1,14 +1,29 @@ +use std::collections::HashMap; + use data_bucket::Link; +use crate::Difference; use crate::WorkTableError; -pub trait TableSecondaryIndex { +pub trait TableSecondaryIndex +where + AvailableTypes: 'static, +{ fn save_row(&self, row: Row, link: Link) -> Result<(), WorkTableError>; fn delete_row(&self, row: Row, link: Link) -> Result<(), WorkTableError>; + + fn process_difference( + &self, + link: Link, + differences: HashMap<&str, Difference>, + ) -> Result<(), WorkTableError>; } -impl TableSecondaryIndex for () { +impl TableSecondaryIndex for () +where + AvailableTypes: 'static, +{ fn save_row(&self, _: Row, _: Link) -> Result<(), WorkTableError> { Ok(()) } @@ -16,4 +31,12 @@ impl TableSecondaryIndex for () { fn delete_row(&self, _: Row, _: Link) -> Result<(), WorkTableError> { Ok(()) } + + fn process_difference( + &self, + _: Link, + _: HashMap<&str, Difference>, + ) -> Result<(), WorkTableError> { + Ok(()) + } } diff --git a/src/lib.rs b/src/lib.rs index 237198ab..725ada04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,8 +27,8 @@ pub mod prelude { Order, SelectQueryBuilder, SelectQueryExecutor, SelectResult, SelectResultExecutor, }; pub use crate::{ - lock::Lock, IndexMap, IndexMultiMap, TableRow, TableSecondaryIndex, WorkTable, - WorkTableError, + lock::Lock, Difference, IndexMap, IndexMultiMap, TableIndex, TableRow, TableSecondaryIndex, + WorkTable, WorkTableError, }; pub use data_bucket::{ align, map_data_pages_to_general, map_index_pages_to_general, map_tree_index, diff --git a/src/table/mod.rs b/src/table/mod.rs index 2b0519df..3075310a 100644 --- a/src/table/mod.rs +++ b/src/table/mod.rs @@ -23,6 +23,7 @@ use crate::{in_memory, IndexMap, TableRow, TableSecondaryIndex}; pub struct WorkTable< Row, PrimaryKey, + AvailableTypes = (), SecondaryIndexes = (), PkGen = ::Generator, const DATA_LENGTH: usize = INNER_PAGE_SIZE, @@ -43,11 +44,13 @@ pub struct WorkTable< pub table_name: &'static str, pub pk_phantom: PhantomData, + + pub types_phantom: PhantomData, } // Manual implementations to avoid unneeded trait bounds. -impl Default - for WorkTable +impl Default + for WorkTable where PrimaryKey: Clone + Ord + Send + TablePrimaryKey, SecondaryIndexes: Default, @@ -64,12 +67,13 @@ where lock_map: LockMap::new(), table_name: "", pk_phantom: PhantomData, + types_phantom: PhantomData, } } } -impl - WorkTable +impl + WorkTable where Row: TableRow, PrimaryKey: Clone + Ord + Send + TablePrimaryKey, @@ -117,7 +121,8 @@ where Strategy, Share>, rkyv::rancor::Error>, >, PrimaryKey: Clone, - SecondaryIndexes: TableSecondaryIndex, + AvailableTypes: 'static, + SecondaryIndexes: TableSecondaryIndex, { let pk = row.get_primary_key().clone(); let link = self diff --git a/tests/worktable/index.rs b/tests/worktable/index.rs new file mode 100644 index 00000000..eb8c34a5 --- /dev/null +++ b/tests/worktable/index.rs @@ -0,0 +1,210 @@ +use worktable::prelude::*; +use worktable::worktable; + +// The test checks updates for 3 indecies at once +worktable!( + name: Test3, + columns: { + id: u64 primary_key autoincrement, + val: i64, + attr1: String, + attr2: i16, + attr3: u64, + }, + indexes: { + idx1: attr1, + idx2: attr2, + idx3: attr3, + }, + queries: { + update: { + ThreeAttrById(attr1, attr2, attr3) by id, + }, + delete: { + ById() by id, + } + } +); + +#[tokio::test] +async fn update_3_idx() { + let test_table = Test3WorkTable::default(); + + let attr1_old = "TEST".to_string(); + let attr2_old = 1000; + let attr3_old = 65000; + + let row = Test3Row { + val: 1, + attr1: attr1_old.clone(), + attr2: attr2_old, + attr3: attr3_old, + id: 0, + }; + + let attr1_new = "1337".to_string(); + let attr2_new = 1337; + let attr3_new = 1337; + + let pk = test_table.insert(row.clone()).unwrap(); + let _ = test_table + .update_three_attr_by_id( + ThreeAttrByIdQuery { + attr1: attr1_new.clone(), + attr2: attr2_new, + attr3: attr3_new, + }, + pk.clone(), + ) + .await + .unwrap(); + + // Checks idx updated + let updated = test_table.select_by_attr1(attr1_new.clone()).unwrap(); + assert_eq!(updated.vals.first().unwrap().attr1, attr1_new); + + let updated = test_table.select_by_attr2(attr2_new).unwrap(); + assert_eq!(updated.vals.first().unwrap().attr2, attr2_new); + + let updated = test_table.select_by_attr3(attr3_new).unwrap(); + assert_eq!(updated.vals.first().unwrap().attr3, attr3_new); + + // Check old idx removed + let updated = test_table.select_by_attr1(attr1_old.clone()).unwrap(); + assert_eq!(updated.vals.first(), None); + + let updated = test_table.select_by_attr2(attr2_old.clone()).unwrap(); + assert_eq!(updated.vals.first(), None); + + let updated = test_table.select_by_attr3(attr3_old.clone()).unwrap(); + assert_eq!(updated.vals.first(), None); +} + +// The test checks updates for 2 indecies at once + +worktable!( + name: Test2, + columns: { + id: u64 primary_key autoincrement, + val: i64, + attr1: String, + attr2: i16, + }, + indexes: { + idx1: attr1, + idx2: attr2, + }, + queries: { + update: { + AllAttrById(attr1, attr2) by id, + }, + delete: { + ById() by id, + } + } +); + +#[tokio::test] +async fn update_2_idx() { + let test_table = Test2WorkTable::default(); + + let attr1_old = "TEST".to_string(); + let attr2_old = 1000; + + let row = Test2Row { + val: 1, + attr1: attr1_old.clone(), + attr2: attr2_old, + id: 0, + }; + + let attr1_new = "OK".to_string(); + let attr2_new = 1337; + + let pk = test_table.insert(row.clone()).unwrap(); + let _ = test_table + .update_all_attr_by_id( + AllAttrByIdQuery { + attr1: attr1_new.clone(), + attr2: attr2_new, + }, + pk.clone(), + ) + .await + .unwrap(); + + // Checks idx updated + let updated = test_table.select_by_attr1(attr1_new.clone()).unwrap(); + assert_eq!(updated.vals.first().unwrap().attr1, attr1_new); + + let updated = test_table.select_by_attr2(attr2_new).unwrap(); + assert_eq!(updated.vals.first().unwrap().attr2, attr2_new); + + // Check old idx removed + let updated = test_table.select_by_attr1(attr1_old.clone()).unwrap(); + assert_eq!(updated.vals.first(), None); + + let updated = test_table.select_by_attr2(attr2_old.clone()).unwrap(); + assert_eq!(updated.vals.first(), None); +} + +// The test checks updates for 1 index + +worktable!( + name: Test, + columns: { + id: u64 primary_key autoincrement, + val: i64, + attr1: String, + attr2: i16, + }, + indexes: { + idx1: attr1, + }, + queries: { + update: { + ValByAttr(val) by attr1, + Attr1ById(attr1) by id, + + }, + delete: { + ById() by id, + } + } +); + +#[tokio::test] +async fn update_1_idx() { + let test_table = TestWorkTable::default(); + + let attr1_old = "TEST".to_string(); + let attr2_old = 1000; + + let row = TestRow { + val: 1, + attr1: attr1_old.clone(), + attr2: attr2_old, + id: 0, + }; + + let attr1_new = "OK".to_string(); + + let pk = test_table.insert(row.clone()).unwrap(); + let _ = test_table + .update_attr_1_by_id( + Attr1ByIdQuery { + attr1: attr1_new.clone(), + }, + pk.clone(), + ) + .await + .unwrap(); + + // Checks idx updated + let updated = test_table.select_by_attr1(attr1_new.clone()).unwrap(); + assert_eq!(updated.vals.first().unwrap().attr1, attr1_new); + + // Check old idx removed + let updated = test_table.select_by_attr1(attr1_old.clone()).unwrap(); + assert_eq!(updated.vals.first(), None); +} diff --git a/tests/worktable/mod.rs b/tests/worktable/mod.rs index 3ef0f209..d144f4c8 100644 --- a/tests/worktable/mod.rs +++ b/tests/worktable/mod.rs @@ -2,6 +2,7 @@ mod array; mod base; mod config; mod custom_pk; +mod index; mod option; mod tuple_primary_key; mod uuid;