diff --git a/codegen/src/persist_index/space.rs b/codegen/src/persist_index/space.rs index 44b9b767..273c1573 100644 --- a/codegen/src/persist_index/space.rs +++ b/codegen/src/persist_index/space.rs @@ -34,7 +34,7 @@ impl Generator { .collect(); quote! { - #[derive(Debug)] + #[derive(Debug, Default)] pub struct #ident { #(#fields)* } diff --git a/codegen/src/worktable/generator/queries/update.rs b/codegen/src/worktable/generator/queries/update.rs index ee5274f0..21e2d36d 100644 --- a/codegen/src/worktable/generator/queries/update.rs +++ b/codegen/src/worktable/generator/queries/update.rs @@ -34,7 +34,6 @@ impl Generator { fn gen_full_row_update(&mut self) -> TokenStream { let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); let row_ident = name_generator.get_row_type_ident(); - let avt_type_ident = name_generator.get_available_type_ident(); let lock_ident = name_generator.get_lock_type_ident(); let row_updates = self @@ -55,25 +54,9 @@ impl Generator { .map(|idx| idx.field.clone()) .collect(); - let diff_container = quote! { - let row_old = self.select(pk.clone()).unwrap(); - let row_new = row.clone(); - let updated_bytes: Vec = vec![]; - let mut diffs: std::collections::HashMap<&str, Difference<#avt_type_ident>> = std::collections::HashMap::new(); - }; - let diff_process = self.gen_process_diffs_on_index(idents.as_slice(), idents.as_slice()); - let persist_call = if self.is_persist { - quote! { - if let Operation::Update(op) = &mut op { - op.bytes = self.0.data.select_raw(link)?; - } else { - unreachable!("") - }; - self.2.apply_operation(op); - } - } else { - quote! {} - }; + let diff_process = self.gen_process_diffs_on_index(idents.as_slice(), Some(&idents)); + let persist_call = self.gen_persist_call(); + let persist_op = self.gen_persist_op(); quote! { pub async fn update(&self, row: #row_ident) -> core::result::Result<(), WorkTableError> { @@ -94,8 +77,8 @@ impl Generator { .map(|v| v.get().value) .ok_or(WorkTableError::NotFound)?; - #diff_container #diff_process + #persist_op unsafe { self.0.data.with_mut_ref(link, move |archived| { #(#row_updates)* @@ -142,9 +125,21 @@ impl Generator { let index_name = &index.name; if index.is_unique { - self.gen_unique_update(snake_case_name, name, index_name, idents) + self.gen_unique_update( + snake_case_name, + name, + index_name, + idents, + indexes_columns.as_ref(), + ) } else { - self.gen_non_unique_update(snake_case_name, name, index_name, idents) + self.gen_non_unique_update( + snake_case_name, + name, + index_name, + idents, + indexes_columns.as_ref(), + ) } } else if self.columns.primary_keys.len() == 1 { if *self.columns.primary_keys.first().unwrap() == op.by { @@ -163,36 +158,28 @@ impl Generator { } } - fn gen_process_diffs_on_index(&self, idents: &[Ident], idx_idents: &[Ident]) -> TokenStream { + fn gen_persist_call(&self) -> TokenStream { + if self.is_persist { + quote! { + if let Operation::Update(op) = &mut op { + op.bytes = self.0.data.select_raw(link)?; + } else { + unreachable!("") + }; + self.2.apply_operation(op); + } + } else { + quote! {} + } + } + + fn gen_persist_op(&self) -> TokenStream { let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); - let avt_type_ident = name_generator.get_available_type_ident(); let secondary_events_ident = name_generator.get_space_secondary_index_events_ident(); let primary_key_ident = name_generator.get_primary_key_type_ident(); - let diff = idents - .iter() - .filter(|i| idx_idents.contains(i)) - .map(|i| { - let diff_key = Literal::string(i.to_string().as_str()); - quote! { - let old = &row_old.#i; - let new = &row_new.#i; - - if old != new { - let diff = Difference::<#avt_type_ident> { - old: old.clone().into(), - new: new.clone().into(), - }; - - diffs.insert(#diff_key, diff); - } - } - }) - .collect::>(); - - let process_difference = if self.is_persist { + if self.is_persist { quote! { - let secondary_keys_events = self.0.indexes.process_difference_cdc(link, diffs)?; let mut op: Operation< <<#primary_key_ident as TablePrimaryKey>::Generator as PrimaryKeyGeneratorState>::State, #primary_key_ident, @@ -205,12 +192,75 @@ impl Generator { }); } } else { + quote! {} + } + } + + fn gen_process_diffs_on_index( + &self, + idents: &[Ident], + idx_idents: Option<&Vec>, + ) -> TokenStream { + let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); + let avt_type_ident = name_generator.get_available_type_ident(); + let diff_container = if idx_idents.is_some() { + quote! { + let row_old = self.select(pk.clone()).unwrap(); + let row_new = row.clone(); + let updated_bytes: Vec = vec![]; + let mut diffs: std::collections::HashMap<&str, Difference<#avt_type_ident>> = std::collections::HashMap::new(); + } + } else { + quote! { + let updated_bytes: Vec = vec![]; + } + }; + + let diff = if let Some(idx_idents) = idx_idents { + idents + .iter() + .filter(|i| idx_idents.contains(i)) + .map(|i| { + let diff_key = Literal::string(i.to_string().as_str()); + quote! { + let old = &row_old.#i; + let new = &row_new.#i; + + if old != new { + let diff = Difference::<#avt_type_ident> { + old: old.clone().into(), + new: new.clone().into(), + }; + + diffs.insert(#diff_key, diff); + } + } + }) + .collect::>() + } else { + vec![] + }; + + let process_difference = if self.is_persist { + if idx_idents.is_some() { + quote! { + let secondary_keys_events = self.0.indexes.process_difference_cdc(link, diffs)?; + } + } else { + quote! { + let secondary_keys_events = core::default::Default::default(); + } + } + } else if idx_idents.is_some() { quote! { self.0.indexes.process_difference(link, diffs)?; } + } else { + quote! {} }; quote! { + #diff_container #(#diff)* #process_difference } @@ -255,63 +305,38 @@ impl Generator { }) .collect::>(); - let diff_process = if let Some(idx_idents) = idx_idents { - let avt_type_ident = name_generator.get_available_type_ident(); - let diff_container = quote! { - let row_old = self.select(by.clone()).unwrap(); - let row_new = row.clone(); - let updated_bytes: Vec = vec![]; - let mut diffs: std::collections::HashMap<&str, Difference<#avt_type_ident>> = std::collections::HashMap::new(); - }; - - let process = self.gen_process_diffs_on_index(idents, idx_idents.as_slice()); - quote! { - #diff_container - #process - } - } else { - quote! {} - }; - let persist_call = if self.is_persist { - quote! { - if let Operation::Update(op) = &mut op { - op.bytes = self.0.data.select_raw(link)?; - } else { - unreachable!("") - }; - self.2.apply_operation(op); - } - } else { - quote! {} - }; + let diff_process = self.gen_process_diffs_on_index(idents, idx_idents); + let persist_call = self.gen_persist_call(); + let persist_op = self.gen_persist_op(); quote! { - pub async fn #method_ident(&self, row: #query_ident, by: #pk_ident) -> core::result::Result<(), WorkTableError> { - if let Some(lock) = self.0.lock_map.get(&by) { + pub async fn #method_ident(&self, row: #query_ident, pk: #pk_ident) -> core::result::Result<(), WorkTableError> { + if let Some(lock) = self.0.lock_map.get(&pk) { lock.#lock_await_ident().await; // Waiting for all locks released } let lock_id = self.0.lock_map.next_id(); let mut lock = #lock_type_ident::new(lock_id.into()); //Creates new LockType with None lock.#lock_ident(); - self.0.lock_map.insert(by.clone(), std::sync::Arc::new(lock.clone())); + self.0.lock_map.insert(pk.clone(), std::sync::Arc::new(lock.clone())); let mut bytes = rkyv::to_bytes::(&row).map_err(|_| WorkTableError::SerializeError)?; let mut archived_row = unsafe { rkyv::access_unchecked_mut::<<#query_ident as rkyv::Archive>::Archived>(&mut bytes[..]).unseal_unchecked() }; let link = self.0 .pk_map - .get(&by) + .get(&pk) .map(|v| v.get().value) .ok_or(WorkTableError::NotFound)?; #diff_process + #persist_op unsafe { self.0.data.with_mut_ref(link, |archived| { #(#row_updates)* }).map_err(WorkTableError::PagesError)? }; lock.#unlock_ident(); - self.0.lock_map.remove(&by); + self.0.lock_map.remove(&pk); #persist_call @@ -326,6 +351,7 @@ impl Generator { name: &Ident, index: &Ident, idents: &[Ident], + idx_idents: Option<&Vec>, ) -> TokenStream { let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); let lock_type_ident = name_generator.get_lock_type_ident(); @@ -355,11 +381,15 @@ impl Generator { .iter() .map(|i| { quote! { - std::mem::swap(&mut archived.inner.#i, &mut row.#i); + std::mem::swap(&mut archived.inner.#i, &mut archived_row.#i); } }) .collect::>(); + let diff_process = self.gen_process_diffs_on_index(idents, idx_idents); + let persist_call = self.gen_persist_call(); + let persist_op = self.gen_persist_op(); + quote! { pub async fn #method_ident(&self, row: #query_ident, by: #by_ident) -> core::result::Result<(), WorkTableError> { for (_, link) in self.0.indexes.#index.get(&by) { @@ -378,18 +408,26 @@ impl Generator { } for (_, link) in self.0.indexes.#index.get(&by) { + let link = *link; + let pk = self.0.data.select(link)?.get_primary_key().clone(); let mut bytes = rkyv::to_bytes::(&row) - .map_err(|_| WorkTableError::SerializeError)?; + .map_err(|_| WorkTableError::SerializeError)?; + + let mut archived_row = unsafe { + rkyv::access_unchecked_mut::<<#query_ident as rkyv::Archive>::Archived>(&mut bytes[..]) + .unseal_unchecked() + }; + + #diff_process + #persist_op - let mut row = unsafe { - rkyv::access_unchecked_mut::<<#query_ident as rkyv::Archive>::Archived>(&mut bytes[..]) - .unseal_unchecked() - }; unsafe { - self.0.data.with_mut_ref(*link, |archived| { + self.0.data.with_mut_ref(link, |archived| { #(#row_updates)* }).map_err(WorkTableError::PagesError)?; } + + #persist_call } for (_, link) in self.0.indexes.#index.get(&by) { let pk = self.0.data.select(*link)?.get_primary_key(); @@ -409,6 +447,7 @@ impl Generator { name: &Ident, index: &Ident, idents: &[Ident], + idx_idents: Option<&Vec>, ) -> TokenStream { let name_generator = WorktableNameGenerator::from_table_name(self.name.to_string()); let lock_type_ident = name_generator.get_lock_type_ident(); @@ -438,17 +477,20 @@ impl Generator { .iter() .map(|i| { quote! { - std::mem::swap(&mut archived.inner.#i, &mut row.#i); + std::mem::swap(&mut archived.inner.#i, &mut archived_new_row.#i); } }) .collect::>(); + let diff_process = self.gen_process_diffs_on_index(idents, idx_idents); + let persist_call = self.gen_persist_call(); + let persist_op = self.gen_persist_op(); quote! { pub async fn #method_ident(&self, row: #query_ident, by: #by_ident) -> core::result::Result<(), WorkTableError> { let mut bytes = rkyv::to_bytes::(&row) .map_err(|_| WorkTableError::SerializeError)?; - let mut row = unsafe { + let mut archived_new_row = unsafe { rkyv::access_unchecked_mut::<<#query_ident as rkyv::Archive>::Archived>(&mut bytes[..]) .unseal_unchecked() }; @@ -457,7 +499,6 @@ impl Generator { .get(&by) .map(|kv| kv.get().value) .ok_or(WorkTableError::NotFound)?; - let pk = self.0.data.select(link)?.get_primary_key(); if let Some(lock) = self.0.lock_map.get(&pk) { @@ -468,6 +509,9 @@ impl Generator { lock.#lock_ident(); self.0.lock_map.insert(pk.clone(), std::sync::Arc::new(lock.clone())); + #diff_process + #persist_op + unsafe { self.0.data.with_mut_ref(link, |archived| { #(#row_updates)* @@ -477,6 +521,8 @@ impl Generator { lock.#unlock_ident(); self.0.lock_map.remove(&pk); + #persist_call + core::result::Result::Ok(()) } } diff --git a/tests/persistence/sync/mod.rs b/tests/persistence/sync/mod.rs index ccc96f31..432fa9d7 100644 --- a/tests/persistence/sync/mod.rs +++ b/tests/persistence/sync/mod.rs @@ -1,9 +1,35 @@ -use crate::persistence::{AnotherByIdQuery, TestPersistRow, TestPersistWorkTable}; use crate::remove_dir_if_exists; -use worktable::prelude::{PersistenceConfig, PrimaryKeyGeneratorState}; + +use worktable::prelude::*; +use worktable::worktable; mod string_re_read; +worktable! ( + name: TestSync, + persist: true, + columns: { + id: u64 primary_key autoincrement, + another: u64, + non_unique: u32, + field: f64, + }, + indexes: { + another_idx: another unique, + non_unique_idx: non_unique + }, + queries: { + update: { + AnotherById(another) by id, + FieldByAnother(field) by another, + AnotherByNonUnique(another) by non_unique + }, + delete: { + ByAnother() by another, + } + } +); + #[test] fn test_space_insert_sync() { let config = PersistenceConfig::new("tests/data/sync/insert", "tests/data/sync/insert"); @@ -19,11 +45,13 @@ fn test_space_insert_sync() { remove_dir_if_exists("tests/data/sync/insert".to_string()).await; let pk = { - let table = TestPersistWorkTable::load_from_file(config.clone()) + let table = TestSyncWorkTable::load_from_file(config.clone()) .await .unwrap(); - let row = TestPersistRow { + let row = TestSyncRow { another: 42, + non_unique: 0, + field: 0.234, id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -31,7 +59,7 @@ fn test_space_insert_sync() { row.id }; { - let table = TestPersistWorkTable::load_from_file(config).await.unwrap(); + let table = TestSyncWorkTable::load_from_file(config).await.unwrap(); assert!(table.select(pk.into()).is_some()); assert_eq!(table.0.pk_gen.get_state(), pk + 1) } @@ -55,13 +83,15 @@ fn test_space_insert_many_sync() { let mut pks = vec![]; { - let table = TestPersistWorkTable::load_from_file(config.clone()) + let table = TestSyncWorkTable::load_from_file(config.clone()) .await .unwrap(); for i in 0..20 { let pk = { - let row = TestPersistRow { + let row = TestSyncRow { another: i, + non_unique: (i % 4) as u32, + field: i as f64 / 100.0, id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -73,7 +103,7 @@ fn test_space_insert_many_sync() { } { - let table = TestPersistWorkTable::load_from_file(config).await.unwrap(); + let table = TestSyncWorkTable::load_from_file(config).await.unwrap(); let last = *pks.last().unwrap(); for pk in pks { assert!(table.select(pk.into()).is_some()); @@ -99,17 +129,21 @@ fn test_space_update_full_sync() { remove_dir_if_exists("tests/data/sync/update_full".to_string()).await; let pk = { - let table = TestPersistWorkTable::load_from_file(config.clone()) + let table = TestSyncWorkTable::load_from_file(config.clone()) .await .unwrap(); - let row = TestPersistRow { + let row = TestSyncRow { another: 42, + non_unique: 0, + field: 0.0, id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); table - .update(TestPersistRow { + .update(TestSyncRow { another: 13, + non_unique: 0, + field: 0.0, id: row.id, }) .await @@ -118,7 +152,7 @@ fn test_space_update_full_sync() { row.id }; { - let table = TestPersistWorkTable::load_from_file(config).await.unwrap(); + let table = TestSyncWorkTable::load_from_file(config).await.unwrap(); assert!(table.select(pk.into()).is_some()); assert_eq!(table.select(pk.into()).unwrap().another, 13); assert_eq!(table.0.pk_gen.get_state(), pk + 1) @@ -127,10 +161,10 @@ fn test_space_update_full_sync() { } #[test] -fn test_space_update_query_sync() { +fn test_space_update_query_pk_sync() { let config = PersistenceConfig::new( - "tests/data/sync/update_query", - "tests/data/sync/update_query", + "tests/data/sync/update_query_pk", + "tests/data/sync/update_query_pk", ); let runtime = tokio::runtime::Builder::new_multi_thread() @@ -141,14 +175,16 @@ fn test_space_update_query_sync() { .unwrap(); runtime.block_on(async { - remove_dir_if_exists("tests/data/sync/update_query".to_string()).await; + remove_dir_if_exists("tests/data/sync/update_query_pk".to_string()).await; let pk = { - let table = TestPersistWorkTable::load_from_file(config.clone()) + let table = TestSyncWorkTable::load_from_file(config.clone()) .await .unwrap(); - let row = TestPersistRow { + let row = TestSyncRow { another: 42, + non_unique: 0, + field: 0.0, id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -160,7 +196,95 @@ fn test_space_update_query_sync() { row.id }; { - let table = TestPersistWorkTable::load_from_file(config).await.unwrap(); + let table = TestSyncWorkTable::load_from_file(config).await.unwrap(); + assert!(table.select(pk.into()).is_some()); + assert_eq!(table.select(pk.into()).unwrap().another, 13); + assert_eq!(table.0.pk_gen.get_state(), pk + 1) + } + }); +} + +#[test] +fn test_space_update_query_unique_sync() { + let config = PersistenceConfig::new( + "tests/data/sync/update_query_unique", + "tests/data/sync/update_query_unique", + ); + + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_io() + .enable_time() + .build() + .unwrap(); + + runtime.block_on(async { + remove_dir_if_exists("tests/data/sync/update_query_unique".to_string()).await; + + let pk = { + let table = TestSyncWorkTable::load_from_file(config.clone()) + .await + .unwrap(); + let row = TestSyncRow { + another: 42, + non_unique: 0, + field: 0.0, + id: table.get_next_pk().0, + }; + table.insert(row.clone()).unwrap(); + table + .update_field_by_another(FieldByAnotherQuery { field: 1.0 }, 42) + .await + .unwrap(); + table.wait_for_ops().await; + row.id + }; + { + let table = TestSyncWorkTable::load_from_file(config).await.unwrap(); + assert!(table.select(pk.into()).is_some()); + assert_eq!(table.select(pk.into()).unwrap().field, 1.0); + assert_eq!(table.0.pk_gen.get_state(), pk + 1) + } + }); +} + +#[test] +fn test_space_update_query_non_unique_sync() { + let config = PersistenceConfig::new( + "tests/data/sync/update_query_non_unique", + "tests/data/sync/update_query_non_unique", + ); + + let runtime = tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_io() + .enable_time() + .build() + .unwrap(); + + runtime.block_on(async { + remove_dir_if_exists("tests/data/sync/update_query_non_unique".to_string()).await; + + let pk = { + let table = TestSyncWorkTable::load_from_file(config.clone()) + .await + .unwrap(); + let row = TestSyncRow { + another: 42, + non_unique: 10, + field: 0.0, + id: table.get_next_pk().0, + }; + table.insert(row.clone()).unwrap(); + table + .update_another_by_non_unique(AnotherByNonUniqueQuery { another: 13 }, 10) + .await + .unwrap(); + table.wait_for_ops().await; + row.id + }; + { + let table = TestSyncWorkTable::load_from_file(config).await.unwrap(); assert!(table.select(pk.into()).is_some()); assert_eq!(table.select(pk.into()).unwrap().another, 13); assert_eq!(table.0.pk_gen.get_state(), pk + 1) @@ -183,11 +307,13 @@ fn test_space_delete_sync() { remove_dir_if_exists("tests/data/sync/delete".to_string()).await; let pk = { - let table = TestPersistWorkTable::load_from_file(config.clone()) + let table = TestSyncWorkTable::load_from_file(config.clone()) .await .unwrap(); - let row = TestPersistRow { + let row = TestSyncRow { another: 42, + non_unique: 0, + field: 0.0, id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -196,7 +322,7 @@ fn test_space_delete_sync() { row.id }; { - let table = TestPersistWorkTable::load_from_file(config).await.unwrap(); + let table = TestSyncWorkTable::load_from_file(config).await.unwrap(); assert!(table.select(pk.into()).is_none()); assert_eq!(table.0.pk_gen.get_state(), pk + 1) } @@ -221,11 +347,13 @@ fn test_space_delete_query_sync() { remove_dir_if_exists("tests/data/sync/delete_query".to_string()).await; let pk = { - let table = TestPersistWorkTable::load_from_file(config.clone()) + let table = TestSyncWorkTable::load_from_file(config.clone()) .await .unwrap(); - let row = TestPersistRow { + let row = TestSyncRow { another: 42, + non_unique: 0, + field: 0.0, id: table.get_next_pk().0, }; table.insert(row.clone()).unwrap(); @@ -234,7 +362,7 @@ fn test_space_delete_query_sync() { row.id }; { - let table = TestPersistWorkTable::load_from_file(config).await.unwrap(); + let table = TestSyncWorkTable::load_from_file(config).await.unwrap(); assert!(table.select(pk.into()).is_none()); assert_eq!(table.0.pk_gen.get_state(), pk + 1) } diff --git a/tests/worktable/index.rs b/tests/worktable/index/mod.rs similarity index 62% rename from tests/worktable/index.rs rename to tests/worktable/index/mod.rs index d10901b1..9479c671 100644 --- a/tests/worktable/index.rs +++ b/tests/worktable/index/mod.rs @@ -1,9 +1,39 @@ +mod update_by_pk; +mod update_full; +mod update_query; + use worktable::prelude::*; use worktable::worktable; // The test checks updates for 3 indecies at once worktable!( - name: Test3, + name: Test3Unique, + columns: { + id: u64 primary_key autoincrement, + val: i64, + attr1: String, + attr2: i16, + attr3: u64, + }, + indexes: { + idx1: attr1 unique, + idx2: attr2 unique, + idx3: attr3 unique, + }, + queries: { + update: { + UniqueThreeAttrById(attr1, attr2, attr3) by id, + UniqueTwoAttrByThird(attr1, attr2) by attr3, + }, + delete: { + ById() by id, + } + } +); + +// The test checks updates for 3 indecies at once +worktable!( + name: Test3NonUnique, columns: { id: u64 primary_key autoincrement, val: i64, @@ -19,6 +49,7 @@ worktable!( queries: { update: { ThreeAttrById(attr1, attr2, attr3) by id, + TwoAttrByThird(attr1, attr2) by attr3, }, delete: { ById() by id, @@ -26,141 +57,6 @@ worktable!( } ); -#[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(); - 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()) - .execute() - .expect("rows"); - assert_eq!(updated.first().unwrap().attr1, attr1_new); - let updated = test_table - .select_by_attr2(attr2_new) - .execute() - .expect("rows"); - assert_eq!(updated.first().unwrap().attr2, attr2_new); - let updated = test_table - .select_by_attr3(attr3_new) - .execute() - .expect("rows"); - assert_eq!(updated.first().unwrap().attr3, attr3_new); - - // Check old idx removed - let updated = test_table - .select_by_attr1(attr1_old.clone()) - .execute() - .expect("rows"); - assert_eq!(updated.first(), None); - let updated = test_table - .select_by_attr2(attr2_old) - .execute() - .expect("rows"); - assert_eq!(updated.first(), None); - let updated = test_table - .select_by_attr3(attr3_old) - .execute() - .expect("rows"); - assert_eq!(updated.first(), None); -} - -#[tokio::test] -async fn update_3_idx_full_row() { - 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(); - test_table - .update(Test3Row { - attr1: attr1_new.clone(), - id: pk.clone().into(), - val: row.val, - attr2: attr2_new, - attr3: attr3_new, - }) - .await - .unwrap(); - - // Checks idx updated - let updated = test_table - .select_by_attr1(attr1_new.clone()) - .execute() - .expect("rows"); - assert_eq!(updated.first().unwrap().attr1, attr1_new); - let updated = test_table - .select_by_attr2(attr2_new) - .execute() - .expect("rows"); - assert_eq!(updated.first().unwrap().attr2, attr2_new); - let updated = test_table - .select_by_attr3(attr3_new) - .execute() - .expect("rows"); - assert_eq!(updated.first().unwrap().attr3, attr3_new); - - // Check old idx removed - let updated = test_table - .select_by_attr1(attr1_old.clone()) - .execute() - .expect("rows"); - assert_eq!(updated.first(), None); - let updated = test_table - .select_by_attr2(attr2_old) - .execute() - .expect("rows"); - assert_eq!(updated.first(), None); - let updated = test_table - .select_by_attr3(attr3_old) - .execute() - .expect("rows"); - assert_eq!(updated.first(), None); -} - // The test checks updates for 2 indecies at once worktable!( diff --git a/tests/worktable/index/update_by_pk.rs b/tests/worktable/index/update_by_pk.rs new file mode 100644 index 00000000..72307357 --- /dev/null +++ b/tests/worktable/index/update_by_pk.rs @@ -0,0 +1,124 @@ +use worktable::prelude::SelectQueryExecutor; + +use crate::worktable::index::{ + Test3NonUniqueRow, Test3NonUniqueWorkTable, Test3UniqueRow, Test3UniqueWorkTable, + ThreeAttrByIdQuery, UniqueThreeAttrByIdQuery, +}; + +#[tokio::test] +async fn update_by_pk_unique_indexes() { + let test_table = Test3UniqueWorkTable::default(); + + let attr1_old = "TEST".to_string(); + let attr2_old = 1000; + let attr3_old = 65000; + + let row = Test3UniqueRow { + 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(); + test_table + .update_unique_three_attr_by_id( + UniqueThreeAttrByIdQuery { + 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()); + assert_eq!(updated.unwrap().attr1, attr1_new); + let updated = test_table.select_by_attr2(attr2_new); + assert_eq!(updated.unwrap().attr2, attr2_new); + let updated = test_table.select_by_attr3(attr3_new); + assert_eq!(updated.unwrap().attr3, attr3_new); + + // Check old idx removed + let updated = test_table.select_by_attr1(attr1_old.clone()); + assert_eq!(updated, None); + let updated = test_table.select_by_attr2(attr2_old); + assert_eq!(updated, None); + let updated = test_table.select_by_attr3(attr3_old); + assert_eq!(updated, None); +} + +#[tokio::test] +async fn update_by_pk_non_unique_indexes() { + let test_table = Test3NonUniqueWorkTable::default(); + + let attr1_old = "TEST".to_string(); + let attr2_old = 1000; + let attr3_old = 65000; + + let row = Test3NonUniqueRow { + 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(); + 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()) + .execute() + .expect("rows"); + assert_eq!(updated.first().unwrap().attr1, attr1_new); + let updated = test_table + .select_by_attr2(attr2_new) + .execute() + .expect("rows"); + assert_eq!(updated.first().unwrap().attr2, attr2_new); + let updated = test_table + .select_by_attr3(attr3_new) + .execute() + .expect("rows"); + assert_eq!(updated.first().unwrap().attr3, attr3_new); + + // Check old idx removed + let updated = test_table + .select_by_attr1(attr1_old.clone()) + .execute() + .expect("rows"); + assert_eq!(updated.first(), None); + let updated = test_table + .select_by_attr2(attr2_old) + .execute() + .expect("rows"); + assert_eq!(updated.first(), None); + let updated = test_table + .select_by_attr3(attr3_old) + .execute() + .expect("rows"); + assert_eq!(updated.first(), None); +} diff --git a/tests/worktable/index/update_full.rs b/tests/worktable/index/update_full.rs new file mode 100644 index 00000000..c3df65b8 --- /dev/null +++ b/tests/worktable/index/update_full.rs @@ -0,0 +1,120 @@ +use crate::worktable::index::{ + Test3NonUniqueRow, Test3NonUniqueWorkTable, Test3UniqueRow, Test3UniqueWorkTable, +}; +use worktable::prelude::SelectQueryExecutor; + +#[tokio::test] +async fn update_by_full_row_unique_indexes() { + let test_table = Test3UniqueWorkTable::default(); + + let attr1_old = "TEST".to_string(); + let attr2_old = 1000; + let attr3_old = 65000; + + let row = Test3UniqueRow { + 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(); + test_table + .update(Test3UniqueRow { + attr1: attr1_new.clone(), + id: pk.clone().into(), + val: row.val, + attr2: attr2_new, + attr3: attr3_new, + }) + .await + .unwrap(); + + // Checks idx updated + let updated = test_table.select_by_attr1(attr1_new.clone()); + assert_eq!(updated.unwrap().attr1, attr1_new); + let updated = test_table.select_by_attr2(attr2_new); + assert_eq!(updated.unwrap().attr2, attr2_new); + let updated = test_table.select_by_attr3(attr3_new); + assert_eq!(updated.unwrap().attr3, attr3_new); + + // Check old idx removed + let updated = test_table.select_by_attr1(attr1_old.clone()); + assert_eq!(updated, None); + let updated = test_table.select_by_attr2(attr2_old); + assert_eq!(updated, None); + let updated = test_table.select_by_attr3(attr3_old); + assert_eq!(updated, None); +} + +#[tokio::test] +async fn update_by_full_row_non_unique_indexes() { + let test_table = Test3NonUniqueWorkTable::default(); + + let attr1_old = "TEST".to_string(); + let attr2_old = 1000; + let attr3_old = 65000; + + let row = Test3NonUniqueRow { + 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(); + test_table + .update(Test3NonUniqueRow { + attr1: attr1_new.clone(), + id: pk.clone().into(), + val: row.val, + attr2: attr2_new, + attr3: attr3_new, + }) + .await + .unwrap(); + + // Checks idx updated + let updated = test_table + .select_by_attr1(attr1_new.clone()) + .execute() + .expect("rows"); + assert_eq!(updated.first().unwrap().attr1, attr1_new); + let updated = test_table + .select_by_attr2(attr2_new) + .execute() + .expect("rows"); + assert_eq!(updated.first().unwrap().attr2, attr2_new); + let updated = test_table + .select_by_attr3(attr3_new) + .execute() + .expect("rows"); + assert_eq!(updated.first().unwrap().attr3, attr3_new); + + // Check old idx removed + let updated = test_table + .select_by_attr1(attr1_old.clone()) + .execute() + .expect("rows"); + assert_eq!(updated.first(), None); + let updated = test_table + .select_by_attr2(attr2_old) + .execute() + .expect("rows"); + assert_eq!(updated.first(), None); + let updated = test_table + .select_by_attr3(attr3_old) + .execute() + .expect("rows"); + assert_eq!(updated.first(), None); +} diff --git a/tests/worktable/index/update_query.rs b/tests/worktable/index/update_query.rs new file mode 100644 index 00000000..76dbf93b --- /dev/null +++ b/tests/worktable/index/update_query.rs @@ -0,0 +1,88 @@ +use crate::worktable::index::{ + Test3NonUniqueRow, Test3NonUniqueWorkTable, Test3UniqueRow, Test3UniqueWorkTable, + TwoAttrByThirdQuery, UniqueTwoAttrByThirdQuery, +}; +use worktable::prelude::SelectQueryExecutor; + +#[tokio::test] +async fn update_two_via_query_unique_indexes() { + let test_table = Test3UniqueWorkTable::default(); + + let attr1_old = "TEST".to_string(); + let attr2_old = 1000; + let attr3_old = 65000; + + let row = Test3UniqueRow { + 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 _ = test_table.insert(row.clone()).unwrap(); + test_table + .update_unique_two_attr_by_third( + UniqueTwoAttrByThirdQuery { + attr1: attr1_new, + attr2: attr2_new, + }, + attr3_old, + ) + .await + .unwrap(); + + // Check old idx removed + let updated = test_table.select_by_attr1(attr1_old.clone()); + assert_eq!(updated, None); + let updated = test_table.select_by_attr2(attr2_old); + assert_eq!(updated, None); + let updated = test_table.select_by_attr3(attr3_old); + assert!(updated.is_some()); +} + +#[tokio::test] +async fn update_two_via_query_non_unique_indexes() { + let test_table = Test3NonUniqueWorkTable::default(); + + let attr1_old = "TEST".to_string(); + let attr2_old = 1000; + let attr3_old = 65000; + + let row = Test3NonUniqueRow { + 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 _ = test_table.insert(row.clone()).unwrap(); + test_table + .update_two_attr_by_third( + TwoAttrByThirdQuery { + attr1: attr1_new, + attr2: attr2_new, + }, + attr3_old, + ) + .await + .unwrap(); + + // Check old idx removed + let updated = test_table + .select_by_attr1(attr1_old.clone()) + .execute() + .unwrap(); + assert!(updated.is_empty()); + let updated = test_table.select_by_attr2(attr2_old).execute().unwrap(); + assert!(updated.is_empty()); + let updated = test_table.select_by_attr3(attr3_old).execute().unwrap(); + assert!(!updated.is_empty()); +}