diff --git a/Cargo.toml b/Cargo.toml index 91f2775..e82e3e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["codegen", "examples", "performance_measurement", "performance_measur [package] name = "worktable" -version = "0.8.1" +version = "0.8.3" edition = "2024" authors = ["Handy-caT"] license = "MIT" @@ -27,14 +27,14 @@ lockfree = { version = "0.5.1" } fastrand = "2.3.0" futures = "0.3.30" uuid = { version = "1.10.0", features = ["v4", "v7"] } -data_bucket = "0.3.1" +data_bucket = "0.3.2" # data_bucket = { git = "https://github.com/pathscale/DataBucket", branch = "page_cdc_correction", version = "0.2.7" } -# data_bucket = { path = "../DataBucket", version = "0.3.0" } +# data_bucket = { path = "../DataBucket", version = "0.3.1" } performance_measurement_codegen = { path = "performance_measurement/codegen", version = "0.1.0", optional = true } performance_measurement = { path = "performance_measurement", version = "0.1.0", optional = true } # indexset = { version = "0.12.3", features = ["concurrent", "cdc", "multimap"] } -# indexset = { package = "wt-indexset", path = "../indexset", version = "0.12.5", features = ["concurrent", "cdc", "multimap"] } -indexset = { package = "wt-indexset", version = "0.12.7", features = ["concurrent", "cdc", "multimap"] } +# indexset = { package = "wt-indexset", path = "../indexset", version = "0.12.8", features = ["concurrent", "cdc", "multimap"] } +indexset = { package = "wt-indexset", version = "0.12.8", features = ["concurrent", "cdc", "multimap"] } convert_case = "0.6.0" ordered-float = "5.0.0" parking_lot = "0.12.3" diff --git a/codegen/src/worktable/generator/queries/delete.rs b/codegen/src/worktable/generator/queries/delete.rs index 89bf166..9da2983 100644 --- a/codegen/src/worktable/generator/queries/delete.rs +++ b/codegen/src/worktable/generator/queries/delete.rs @@ -152,7 +152,7 @@ impl Generator { self.iter_with_async(|row| { if row.#field == by { futures::future::Either::Left(async move { - self.delete(row.id.into()).await + self.delete(row.get_primary_key()).await }) } else { futures::future::Either::Right(async { @@ -180,7 +180,7 @@ impl Generator { let rows_to_update = self.0.indexes.#index.get(#by).map(|kv| kv.1).collect::>(); for link in rows_to_update { let row = self.0.data.select_non_ghosted(*link).map_err(WorkTableError::PagesError)?; - self.delete(row.id.into()).await?; + self.delete(row.get_primary_key()).await?; } core::result::Result::Ok(()) } @@ -202,7 +202,7 @@ impl Generator { let row_to_update = self.0.indexes.#index.get(#by).map(|v| v.get().value); if let Some(link) = row_to_update { let row = self.0.data.select_non_ghosted(link).map_err(WorkTableError::PagesError)?; - self.delete(row.id.into()).await?; + self.delete(row.get_primary_key()).await?; } core::result::Result::Ok(()) } diff --git a/src/index/unsized_node.rs b/src/index/unsized_node.rs index f73835d..c66e89f 100644 --- a/src/index/unsized_node.rs +++ b/src/index/unsized_node.rs @@ -204,6 +204,10 @@ where self.inner.last() } + fn pre_max(&self) -> Option<&T> { + self.inner.pre_max() + } + fn min(&self) -> Option<&T> { self.inner.first() } diff --git a/src/persistence/space/index/unsized_.rs b/src/persistence/space/index/unsized_.rs index 0c0f660..e1d5cd7 100644 --- a/src/persistence/space/index/unsized_.rs +++ b/src/persistence/space/index/unsized_.rs @@ -401,14 +401,12 @@ where let page_to_update = if let Some(page) = page { page } else { - // println!("Try to parse page: {:?} {:?}", page_index, page_id); let page = parse_page::, INNER_PAGE_SIZE>( &mut self.index_file, page_index.into(), ) .await?; - // println!("Page {:?} {:?} parsed", page_index, page_id); pages.insert(page_index, page); pages .get_mut(&page_index) diff --git a/src/table/mod.rs b/src/table/mod.rs index 0bb06cb..19f4668 100644 --- a/src/table/mod.rs +++ b/src/table/mod.rs @@ -161,11 +161,6 @@ where if let Some(link) = link { self.data.select(link).ok() } else { - println!( - "{:?} Unavailable in primary index, vals available {:?}", - pk, - self.pk_map.iter().collect::>() - ); None } } diff --git a/tests/persistence/sync/many_strings.rs b/tests/persistence/sync/many_strings.rs new file mode 100644 index 0000000..393bb80 --- /dev/null +++ b/tests/persistence/sync/many_strings.rs @@ -0,0 +1,86 @@ +use worktable::prelude::*; +use worktable_codegen::worktable; + +use crate::remove_dir_if_exists; + +worktable! ( + name: TestSync, + persist: true, + columns: { + id: String primary_key, + field: String, + another: u64, + }, + queries: { + update: { + FieldAnotherById(field, another) by id, + }, + } +); + +#[test] +fn test_space_update_query_pk_sync() { + let config = PersistenceConfig::new( + "tests/data/unsized_primary_and_other_sync/update_query_pk", + "tests/data/unsized_primary_and_other_sync/update_query_pk", + ); + + 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/unsized_primary_and_other_sync/update_query_pk".to_string(), + ) + .await; + + let pk = { + let table = TestSyncWorkTable::load_from_file(config.clone()) + .await + .unwrap(); + let row = TestSyncRow { + another: 42, + field: "".to_string(), + id: "Some string before".to_string(), + }; + table.insert(row.clone()).unwrap(); + let row = TestSyncRow { + another: 43, + field: "".to_string(), + id: "Some string before 2".to_string(), + }; + table.insert(row.clone()).unwrap(); + table.wait_for_ops().await; + row.id + }; + { + let table = TestSyncWorkTable::load_from_file(config.clone()) + .await + .unwrap(); + assert!(table.select(pk.clone()).is_some()); + assert_eq!(table.select(pk.clone()).unwrap().another, 43); + let q = FieldAnotherByIdQuery { + field: "Some field value".to_string(), + another: 0, + }; + table + .update_field_another_by_id(q, pk.clone()) + .await + .unwrap(); + table.wait_for_ops().await; + } + { + let table = TestSyncWorkTable::load_from_file(config).await.unwrap(); + assert!(table.select(pk.clone()).is_some()); + assert_eq!(table.select(pk.clone()).unwrap().another, 0); + assert_eq!( + table.select(pk).unwrap().field, + "Some field value".to_string() + ); + } + }); +} diff --git a/tests/persistence/sync/mod.rs b/tests/persistence/sync/mod.rs index ba20696..ce3e97b 100644 --- a/tests/persistence/sync/mod.rs +++ b/tests/persistence/sync/mod.rs @@ -4,6 +4,7 @@ use std::time::Duration; use worktable::prelude::*; use worktable::worktable; +mod many_strings; mod string_primary_index; mod string_re_read; mod string_secondary_index; diff --git a/tests/worktable/delete.rs b/tests/worktable/delete.rs new file mode 100644 index 0000000..0da0c41 --- /dev/null +++ b/tests/worktable/delete.rs @@ -0,0 +1,23 @@ +use worktable::prelude::*; +use worktable::worktable; + +worktable!( + name: DeleteTest, + columns: { + token: String primary_key, + val1: u64, + val2: u64, + }, + indexes: { + val1_idx: val1, + val2_idx: val2, + }, + queries: { + update: { + Val1ByToken(val1) by token, + }, + delete: { + ByVal1() by val1, + } + } +); diff --git a/tests/worktable/mod.rs b/tests/worktable/mod.rs index ba143fd..253569d 100644 --- a/tests/worktable/mod.rs +++ b/tests/worktable/mod.rs @@ -4,6 +4,7 @@ mod bench; mod config; mod count; mod custom_pk; +mod delete; mod float; mod in_place; mod index; diff --git a/util/find_println.sh b/util/find_println.sh new file mode 100755 index 0000000..8c3b158 --- /dev/null +++ b/util/find_println.sh @@ -0,0 +1,169 @@ +#!/bin/bash + +# Script to find println! statements in staged Rust files outside of tests +# Usage: ./find_println.sh [--all-modified] +# Default: Only checks files that are staged (added to git) but not yet committed +# --all-modified: Checks all modified files (staged and unstaged) +# Excludes: +# - Files in tests/ directories +# - Code inside #[cfg(test)] modules +# - Test functions marked with #[test] + +set -e + +# Parse command line arguments +CHECK_ALL_MODIFIED=false +if [[ "$1" == "--all-modified" ]]; then + CHECK_ALL_MODIFIED=true +fi + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +if [ "$CHECK_ALL_MODIFIED" = true ]; then + echo -e "${BLUE}🔍 Searching for println! statements in all modified files outside of tests...${NC}" + echo + + # Get all modified .rs files (staged and unstaged) that are not in tests/ directories + modified_files=$(git diff --name-only HEAD | grep '\.rs$' | grep -v '/tests/' || true) +else + echo -e "${BLUE}🔍 Searching for println! statements in staged files outside of tests...${NC}" + echo + + # Get staged .rs files that are not in tests/ directories + modified_files=$(git diff --cached --name-only --diff-filter=AM | grep '\.rs$' | grep -v '/tests/' || true) +fi + +if [ -z "$modified_files" ]; then + if [ "$CHECK_ALL_MODIFIED" = true ]; then + echo -e "${GREEN}✅ No modified Rust files found to check${NC}" + else + echo -e "${GREEN}✅ No staged Rust files found to check${NC}" + fi + exit 0 +fi + +if [ "$CHECK_ALL_MODIFIED" = true ]; then + echo -e "${BLUE}Modified files to check:${NC}" +else + echo -e "${BLUE}Staged files to check:${NC}" +fi +for file in $modified_files; do + echo -e " • $file" +done +echo + +rust_files="$modified_files" + +total_files=0 +files_with_println=0 +total_println_count=0 + +# Track if we're inside a cfg(test) module or test function +in_test_module=0 +brace_depth=0 +test_module_start_depth=0 + +for file in $rust_files; do + total_files=$((total_files + 1)) + file_has_println=0 + println_count=0 + + # Reset state for each file + in_test_module=0 + brace_depth=0 + test_module_start_depth=0 + + line_num=0 + while IFS= read -r line || [[ -n "$line" ]]; do + line_num=$((line_num + 1)) + + # Count braces to track scope depth + open_braces=$(echo "$line" | grep -o '{' | wc -l) + close_braces=$(echo "$line" | grep -o '}' | wc -l) + brace_depth=$((brace_depth + open_braces - close_braces)) + + # Check if we're entering a cfg(test) module + if echo "$line" | grep -E '^\s*#\s*\[\s*cfg\s*\(\s*test\s*\)\s*\]' > /dev/null; then + # Look ahead to see if next non-empty, non-comment line is a module + temp_line_num=$line_num + found_mod=0 + while IFS= read -r next_line; do + temp_line_num=$((temp_line_num + 1)) + # Skip empty lines and comments + if echo "$next_line" | grep -E '^\s*(//.*)?$' > /dev/null; then + continue + fi + # Check if it's a module declaration + if echo "$next_line" | grep -E '^\s*mod\s+' > /dev/null; then + found_mod=1 + fi + break + done < <(tail -n +$((line_num + 1)) "$file") + + if [ $found_mod -eq 1 ]; then + in_test_module=1 + test_module_start_depth=$brace_depth + fi + fi + + # Check if we're exiting a cfg(test) module + if [ $in_test_module -eq 1 ] && [ $brace_depth -lt $test_module_start_depth ]; then + in_test_module=0 + fi + + # Skip if we're in a test module + if [ $in_test_module -eq 1 ]; then + continue + fi + + # Skip lines that are test functions (simple heuristic) + if echo "$line" | grep -E '^\s*#\s*\[\s*test\s*\]' > /dev/null; then + # Skip the next few lines until we find the function and its body + continue + fi + + # Check for println! (but not in comments) + if echo "$line" | grep -E '^\s*[^/]*println!' > /dev/null; then + # Make sure it's not in a comment + if ! echo "$line" | grep -E '^\s*//' > /dev/null; then + if [ $file_has_println -eq 0 ]; then + echo -e "${YELLOW}📁 $file${NC}" + file_has_println=1 + files_with_println=$((files_with_println + 1)) + fi + echo -e " ${RED}Line $line_num:${NC} $(echo "$line" | sed 's/^[[:space:]]*//')" + println_count=$((println_count + 1)) + total_println_count=$((total_println_count + 1)) + fi + fi + + done < "$file" + + if [ $file_has_println -eq 1 ]; then + echo -e " ${GREEN}Found $println_count println! statement(s)${NC}" + echo + fi +done + +# Summary +echo -e "${BLUE}📊 Summary:${NC}" +if [ "$CHECK_ALL_MODIFIED" = true ]; then + echo -e " • Modified files scanned: ${total_files}" +else + echo -e " • Staged files scanned: ${total_files}" +fi +echo -e " • Files with println!: ${files_with_println}" +echo -e " • Total println! statements: ${total_println_count}" + +if [ $total_println_count -eq 0 ]; then + echo -e "${GREEN}✅ No println! statements found outside of tests!${NC}" + exit 0 +else + echo -e "${RED}⚠️ Found println! statements that should be reviewed${NC}" + exit 1 +fi \ No newline at end of file