diff --git a/Cargo.toml b/Cargo.toml index 86f3d58..485f3c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["codegen", "examples", "performance_measurement", "performance_measur [package] name = "worktable" -version = "0.7.1" +version = "0.7.2" edition = "2024" authors = ["Handy-caT"] license = "MIT" @@ -16,7 +16,7 @@ perf_measurements = ["dep:performance_measurement", "dep:performance_measurement # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -worktable_codegen = { path = "codegen", version = "0.7.1" } +worktable_codegen = { path = "codegen", version = "0.7.2" } eyre = "0.6.12" derive_more = { version = "1.0.0", features = ["from", "error", "display", "into"] } diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 18eddf2..bd9f0da 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "worktable_codegen" -version = "0.7.1" +version = "0.7.2" edition = "2024" license = "MIT" description = "WorkTable codegeneration crate" diff --git a/codegen/src/worktable/generator/index/cdc.rs b/codegen/src/worktable/generator/index/cdc.rs index f2239df..18e6acd 100644 --- a/codegen/src/worktable/generator/index/cdc.rs +++ b/codegen/src/worktable/generator/index/cdc.rs @@ -119,22 +119,28 @@ impl Generator { #index_field_name.extend(events.into_iter().map(|ev| ev.into()).collect::>()); } }; - let insert = quote! { - let mut #index_field_name = if row_new.#i != row_old.#i { - let #index_field_name: Vec<_> = if let Some(events) = self.#index_field_name.insert_checked_cdc(row_new.#i.clone(), link_new) { - events.into_iter().map(|ev| ev.into()).collect() + let insert = if idx.is_unique { + quote! { + let mut #index_field_name = if row_new.#i != row_old.#i { + let #index_field_name: Vec<_> = if let Some(events) = self.#index_field_name.insert_checked_cdc(row_new.#i.clone(), link_new) { + events.into_iter().map(|ev| ev.into()).collect() + } else { + return Err(IndexError::AlreadyExists { + at: #available_index_ident::#index_variant, + inserted_already: inserted_indexes.clone(), + }); + }; + inserted_indexes.push(#available_index_ident::#index_variant); + + #index_field_name } else { - return Err(IndexError::AlreadyExists { - at: #available_index_ident::#index_variant, - inserted_already: inserted_indexes.clone(), - }); + vec![] }; - inserted_indexes.push(#available_index_ident::#index_variant); - - #index_field_name - } else { - vec![] - }; + } + } else { + quote! { + let mut #index_field_name = vec![]; + } }; (insert, remove) }) diff --git a/codegen/src/worktable/generator/index/usual.rs b/codegen/src/worktable/generator/index/usual.rs index 4f89c98..381309d 100644 --- a/codegen/src/worktable/generator/index/usual.rs +++ b/codegen/src/worktable/generator/index/usual.rs @@ -136,20 +136,24 @@ impl Generator { TableIndex::remove(&self.#index_field_name, val_old, link_old); } }; - let insert = quote! { - let row = &row_new; - let val_new = #row.clone(); - let row = &row_old; - let val_old = #row.clone(); - if val_new != val_old { - if self.#index_field_name.insert_checked(val_new.clone(), link_new).is_none() { - return Err(IndexError::AlreadyExists { - at: #available_index_ident::#index_variant, - inserted_already: inserted_indexes.clone(), - }) + let insert = if idx.is_unique { + quote! { + let row = &row_new; + let val_new = #row.clone(); + let row = &row_old; + let val_old = #row.clone(); + if val_new != val_old { + if self.#index_field_name.insert_checked(val_new.clone(), link_new).is_none() { + return Err(IndexError::AlreadyExists { + at: #available_index_ident::#index_variant, + inserted_already: inserted_indexes.clone(), + }) + } + inserted_indexes.push(#available_index_ident::#index_variant); } - inserted_indexes.push(#available_index_ident::#index_variant); } + } else { + quote! {} }; let remove = quote! { let row = &row_new; diff --git a/src/table/mod.rs b/src/table/mod.rs index 97d172a..09c6bbc 100644 --- a/src/table/mod.rs +++ b/src/table/mod.rs @@ -358,6 +358,7 @@ where at, inserted_already, } => { + self.pk_map.insert(pk.clone(), old_link); self.indexes .delete_from_indexes(row_new, new_link, inserted_already)?; self.data @@ -432,6 +433,7 @@ where at, inserted_already, } => { + self.pk_map.insert(pk.clone(), old_link); self.indexes .delete_from_indexes(row_new, new_link, inserted_already)?; self.data diff --git a/tests/worktable/index/update_full.rs b/tests/worktable/index/update_full.rs index ceb2661..b441111 100644 --- a/tests/worktable/index/update_full.rs +++ b/tests/worktable/index/update_full.rs @@ -310,6 +310,7 @@ async fn update_by_full_row_with_reinsert_and_primary_key_violation() { update.attr1 = "TEST_______________________1".to_string(); assert!(test_table.update(update).await.is_err()); + assert_eq!(test_table.select(row1.id).unwrap(), row1); assert_eq!( test_table.select_by_attr1(row1.attr1.clone()).unwrap(), row1 @@ -317,6 +318,7 @@ async fn update_by_full_row_with_reinsert_and_primary_key_violation() { assert_eq!(test_table.select_by_attr2(row1.attr2).unwrap(), row1); assert_eq!(test_table.select_by_attr3(row1.attr3).unwrap(), row1); + assert_eq!(test_table.select(row2.id).unwrap(), row2); assert_eq!( test_table.select_by_attr1(row2.attr1.clone()).unwrap(), row2 @@ -349,6 +351,7 @@ async fn update_by_full_row_with_reinsert_and_secondary_unique_violation() { update.attr1 = row2.attr1.clone(); assert!(test_table.update(update).await.is_err()); + assert_eq!(test_table.select(row1.id).unwrap(), row1); assert_eq!( test_table.select_by_attr1(row1.attr1.clone()).unwrap(), row1 @@ -356,6 +359,7 @@ async fn update_by_full_row_with_reinsert_and_secondary_unique_violation() { assert_eq!(test_table.select_by_attr2(row1.attr2).unwrap(), row1); assert_eq!(test_table.select_by_attr3(row1.attr3).unwrap(), row1); + assert_eq!(test_table.select(row2.id).unwrap(), row2); assert_eq!( test_table.select_by_attr1(row2.attr1.clone()).unwrap(), row2 @@ -388,6 +392,7 @@ async fn update_by_full_row_with_secondary_unique_violation() { update.attr2 = row2.attr2.clone(); assert!(test_table.update(update).await.is_err()); + assert_eq!(test_table.select(row1.id).unwrap(), row1); assert_eq!( test_table.select_by_attr1(row1.attr1.clone()).unwrap(), row1 @@ -395,6 +400,7 @@ async fn update_by_full_row_with_secondary_unique_violation() { assert_eq!(test_table.select_by_attr2(row1.attr2).unwrap(), row1); assert_eq!(test_table.select_by_attr3(row1.attr3).unwrap(), row1); + assert_eq!(test_table.select(row2.id).unwrap(), row2); assert_eq!( test_table.select_by_attr1(row2.attr1.clone()).unwrap(), row2 diff --git a/tests/worktable/index/update_query.rs b/tests/worktable/index/update_query.rs index 76dbf93..c51a683 100644 --- a/tests/worktable/index/update_query.rs +++ b/tests/worktable/index/update_query.rs @@ -27,7 +27,7 @@ async fn update_two_via_query_unique_indexes() { test_table .update_unique_two_attr_by_third( UniqueTwoAttrByThirdQuery { - attr1: attr1_new, + attr1: attr1_new.clone(), attr2: attr2_new, }, attr3_old, @@ -35,6 +35,10 @@ async fn update_two_via_query_unique_indexes() { .await .unwrap(); + let mut new_row = row.clone(); + new_row.attr1 = attr1_new; + new_row.attr2 = attr2_new; + // Check old idx removed let updated = test_table.select_by_attr1(attr1_old.clone()); assert_eq!(updated, None); @@ -42,6 +46,95 @@ async fn update_two_via_query_unique_indexes() { assert_eq!(updated, None); let updated = test_table.select_by_attr3(attr3_old); assert!(updated.is_some()); + assert_eq!(updated, Some(new_row)) +} + +#[tokio::test] +async fn update_with_reinsert_and_secondary_unique_violation() { + let test_table = Test3UniqueWorkTable::default(); + + let row1 = Test3UniqueRow { + val: 1, + attr1: "TEST".to_string(), + attr2: 1000, + attr3: 65000, + id: 0, + }; + test_table.insert(row1.clone()).unwrap(); + let row2 = Test3UniqueRow { + val: 1, + attr1: "TEST__________________1".to_string(), + attr2: 1001, + attr3: 65001, + id: 1, + }; + test_table.insert(row2.clone()).unwrap(); + let update = UniqueTwoAttrByThirdQuery { + attr1: row2.attr1.clone(), + attr2: 999, + }; + assert!(test_table + .update_unique_two_attr_by_third(update, row1.attr3,) + .await + .is_err()); + + assert_eq!( + test_table.select_by_attr1(row1.attr1.clone()).unwrap(), + row1 + ); + assert_eq!(test_table.select_by_attr2(row1.attr2).unwrap(), row1); + assert_eq!(test_table.select_by_attr3(row1.attr3).unwrap(), row1); + + assert_eq!( + test_table.select_by_attr1(row2.attr1.clone()).unwrap(), + row2 + ); + assert_eq!(test_table.select_by_attr2(row2.attr2).unwrap(), row2); + assert_eq!(test_table.select_by_attr3(row2.attr3).unwrap(), row2); +} + +#[tokio::test] +async fn update_with_secondary_unique_violation() { + let test_table = Test3UniqueWorkTable::default(); + + let row1 = Test3UniqueRow { + val: 1, + attr1: "TEST".to_string(), + attr2: 1000, + attr3: 65000, + id: 0, + }; + test_table.insert(row1.clone()).unwrap(); + let row2 = Test3UniqueRow { + val: 1, + attr1: "TEST__________________1".to_string(), + attr2: 1001, + attr3: 65001, + id: 1, + }; + test_table.insert(row2.clone()).unwrap(); + let update = UniqueTwoAttrByThirdQuery { + attr1: row1.attr1.clone(), + attr2: row2.attr2, + }; + assert!(test_table + .update_unique_two_attr_by_third(update, row1.attr3) + .await + .is_err()); + + assert_eq!( + test_table.select_by_attr1(row1.attr1.clone()).unwrap(), + row1 + ); + assert_eq!(test_table.select_by_attr2(row1.attr2).unwrap(), row1); + assert_eq!(test_table.select_by_attr3(row1.attr3).unwrap(), row1); + + assert_eq!( + test_table.select_by_attr1(row2.attr1.clone()).unwrap(), + row2 + ); + assert_eq!(test_table.select_by_attr2(row2.attr2).unwrap(), row2); + assert_eq!(test_table.select_by_attr3(row2.attr3).unwrap(), row2); } #[tokio::test]