Skip to content
This repository was archived by the owner on Aug 3, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["codegen", "examples", "performance_measurement", "performance_measur

[package]
name = "worktable"
version = "0.9.2"
version = "0.9.3"
edition = "2024"
authors = ["Handy-caT"]
license = "MIT"
Expand Down Expand Up @@ -49,7 +49,7 @@ tracing = "0.1"
url = { version = "2", optional = true }
uuid = { version = "1.10.0", features = ["v4", "v7"] }
walkdir = { version = "2", optional = true }
worktable_codegen = { path = "codegen", version = "=0.9.2" }
worktable_codegen = { path = "codegen", version = "=0.9.3" }

[dev-dependencies]
chrono = "0.4.43"
Expand Down
2 changes: 1 addition & 1 deletion codegen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "worktable_codegen"
version = "0.9.2"
version = "0.9.3"
edition = "2024"
license = "MIT"
description = "Proc-macro companion crate for worktable: the worktable! macro and its derives."
Expand Down
6 changes: 5 additions & 1 deletion codegen/src/generators/read_only/table/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,14 @@ impl ReadOnlyGenerator {
.collect::<Vec<_>>();
let pk_types_unsized = is_unsized_vec(pk_types);

// `read_only` and `pk_unsized` are independent: the first selects the read-only
// shape of the table (no persistence engine or task, sync `into_worktable`), the
// second selects the unsized primary index. A read-only table with an unsized key
// needs both, so `read_only` is unconditional here.
let derive = if pk_types_unsized {
quote! {
#[derive(Debug, PersistTable)]
#[table(pk_unsized)]
#[table(read_only, pk_unsized)]
}
} else {
quote! {
Expand Down
19 changes: 19 additions & 0 deletions codegen/src/worktable_version/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,25 @@ mod tests {
);
}

#[test]
fn test_unsized_primary_key_stays_read_only() {
let input = quote! {
name: ThingV1,
columns: {
id: String primary_key,
name: String,
},
};

let res = expand(input).unwrap();
let output = res.to_string();

assert!(
output.contains("table (read_only , pk_unsized)"),
"an unsized primary key must keep read_only, not replace it with pk_unsized"
);
}

#[test]
fn test_rejects_version_after_columns() {
let input = quote! {
Expand Down
1 change: 1 addition & 0 deletions tests/worktable_version/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod basic;
mod string_primary_key;
93 changes: 93 additions & 0 deletions tests/worktable_version/string_primary_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use crate::remove_dir_if_exists;

use worktable::prelude::*;
use worktable_codegen::{worktable, worktable_version};

// A primary key that is not generated and not fixed-size. The read-only table it produces
// needs both the read-only shape and the unsized primary index, so this exercises the
// `#[table(read_only, pk_unsized)]` pairing that a `u64 primary_key autoincrement` does not.
worktable!(
name: Doc,
persist: true,
columns: {
id: String primary_key,
title: String,
author: String,
},
indexes: {
author_idx: author,
},
);

worktable_version!(
name: DocV1,
columns: {
id: String primary_key,
title: String,
author: String,
},
indexes: {
author_idx: author,
},
);

#[test]
fn test_version_reads_persisted_data_with_string_primary_key() {
let config = DiskConfig::new_with_table_name(
"tests/data/version/string_primary_key",
DocWorkTable::name_snake_case(),
DocWorkTable::version(),
);

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/version/string_primary_key".to_string()).await;

{
let engine = DocPersistenceEngine::new(config.clone()).await.unwrap();
let table = DocWorkTable::load(engine).await.unwrap();

table
.insert(DocRow {
id: "doc-alpha".to_string(),
title: "Alpha".to_string(),
author: "Alice".to_string(),
})
.unwrap();

table
.insert(DocRow {
id: "doc-beta".to_string(),
title: "Beta".to_string(),
author: "Bob".to_string(),
})
.unwrap();

table.wait_for_ops().await
}

{
let engine = ReadOnlyPersistenceEngine::create(config.clone()).await.unwrap();
let table = DocV1WorkTable::load(engine).await.unwrap();

assert_eq!(table.count(), 2);

let rows = table.select_all().execute().unwrap();
assert_eq!(rows.len(), 2);

let titles: Vec<_> = rows.iter().map(|r| r.title.clone()).collect();
assert!(titles.contains(&"Alpha".to_string()));
assert!(titles.contains(&"Beta".to_string()));

// Look the row up by its string key, not just by scanning every row.
let alpha = table.select("doc-alpha".to_string()).unwrap();
assert_eq!(alpha.author, "Alice".to_string());
}
});
}
Loading