Skip to content
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.0-beta0.2.1"
version = "0.9.0-beta0.2.2"
edition = "2024"
authors = ["Handy-caT"]
license = "MIT"
Expand Down Expand Up @@ -46,7 +46,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.0-beta0.2.1" }
worktable_codegen = { path = "codegen", version = "=0.9.0-beta0.2.2" }

[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.0-beta0.2.1"
version = "0.9.0-beta0.2.2"
edition = "2024"
license = "MIT"
description = "WorkTable codegeneration crate"
Expand Down
17 changes: 15 additions & 2 deletions codegen/src/generators/in_memory/table/impls.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use proc_macro2::TokenStream;
use convert_case::{Case, Casing};
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

use crate::common::model::GeneratorType;
Expand Down Expand Up @@ -83,6 +84,18 @@ impl InMemoryGenerator {
let column_range_type = name_generator.get_column_range_type_ident();
let row_fields_ident = name_generator.get_row_fields_enum_ident();

let pk_sorted_by = if self.columns.primary_keys.len() == 1 {
let pk_field = &self.columns.primary_keys[0];
let pk_pascal = Ident::new(&pk_field.to_string().to_case(Case::Pascal), Span::mixed_site());
quote! {
SelectQueryBuilder::new_sorted(rows, #row_fields_ident::#pk_pascal)
}
} else {
quote! {
SelectQueryBuilder::new(rows)
}
};

quote! {
pub fn select_by_pk_range<R, Pk>(&self, range: R) -> SelectQueryBuilder<#row_type,
impl DoubleEndedIterator<Item = #row_type> + '_,
Expand All @@ -101,7 +114,7 @@ impl InMemoryGenerator {
.range(converted_range)
.filter_map(|(_, link)| self.0.data.select_non_ghosted(link.0).ok());

SelectQueryBuilder::new(rows)
#pk_sorted_by
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion codegen/src/generators/in_memory/table/index_fns.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::HashMap;

use convert_case::{Case, Casing};
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

Expand Down Expand Up @@ -130,6 +131,7 @@ impl InMemoryGenerator {
let type_ = columns_map.get(i).ok_or(syn::Error::new(i.span(), "Row not found"))?;
let fn_name = Ident::new(format!("select_by_{i}_range").as_str(), Span::mixed_site());
let field_ident = &idx.name;
let column_pascal = Ident::new(&i.to_string().to_case(Case::Pascal), Span::mixed_site());

let (range_bounds, range_arg) = if is_float(type_.to_string().as_str()) {
(
Expand Down Expand Up @@ -157,7 +159,7 @@ impl InMemoryGenerator {
.range(#range_arg)
.filter_map(|(_, link)| self.0.data.select_non_ghosted(link.0).ok());

SelectQueryBuilder::new(rows)
SelectQueryBuilder::new_sorted(rows, #row_fields_ident::#column_pascal)
}
})
}
Expand Down
47 changes: 36 additions & 11 deletions codegen/src/generators/in_memory/table/select_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,20 @@ impl InMemoryGenerator {
}
};

let fallback_sort = quote! {
let mut items: Vec<#row_type> = iter.collect();
items.sort_by(|a, b| {
for (order, col) in &self.params.order {
match col {
#(#order_matches)*
_ => continue,
}
}
std::cmp::Ordering::Equal
});
iter = Box::new(items.into_iter());
};

quote! {
impl<I> SelectQueryExecutor<#row_type, I, #column_range_type, #row_fields_ident>
for SelectQueryBuilder<#row_type, I, #column_range_type, #row_fields_ident>
Expand All @@ -181,19 +195,30 @@ impl InMemoryGenerator {
#range

if !self.params.order.is_empty() {
let mut items: Vec<#row_type> = iter.collect();

items.sort_by(|a, b| {
for (order, col) in &self.params.order {
match col {
#(#order_matches)*
_ => continue,
// Optimization: single order on pre-sorted column with no additional range filters
let can_optimize = self.params.sorted_by.is_some()
&& self.params.range.is_empty()
&& self.params.order.len() == 1;

if can_optimize {
let (order, col) = &self.params.order[0];
let sorted_col = self.params.sorted_by.as_ref().unwrap();

if col == sorted_col {
match order {
Order::Desc => {
iter = Box::new(iter.rev());
}
Order::Asc => {
// Already sorted correctly, no action needed
}
}
} else {
#fallback_sort
}
std::cmp::Ordering::Equal
});

iter = Box::new(items.into_iter());
} else {
#fallback_sort
}
}

let iter_result: Box<dyn Iterator<Item = #row_type>> = if let Some(offset) = self.params.offset {
Expand Down
17 changes: 15 additions & 2 deletions codegen/src/generators/persist/table/impls.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use proc_macro2::TokenStream;
use convert_case::{Case, Casing};
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

use crate::common::model::GeneratorType;
Expand Down Expand Up @@ -167,6 +168,18 @@ impl PersistGenerator {
let column_range_type = name_generator.get_column_range_type_ident();
let row_fields_ident = name_generator.get_row_fields_enum_ident();

let pk_sorted_by = if self.columns.primary_keys.len() == 1 {
let pk_field = &self.columns.primary_keys[0];
let pk_pascal = Ident::new(&pk_field.to_string().to_case(Case::Pascal), Span::mixed_site());
quote! {
SelectQueryBuilder::new_sorted(rows, #row_fields_ident::#pk_pascal)
}
} else {
quote! {
SelectQueryBuilder::new(rows)
}
};

quote! {
pub fn select_by_pk_range<R, Pk>(&self, range: R) -> SelectQueryBuilder<#row_type,
impl DoubleEndedIterator<Item = #row_type> + '_,
Expand All @@ -185,7 +198,7 @@ impl PersistGenerator {
.range(converted_range)
.filter_map(|(_, link)| self.0.data.select_non_ghosted(link.0).ok());

SelectQueryBuilder::new(rows)
#pk_sorted_by
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion codegen/src/generators/persist/table/index_fns.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::HashMap;

use convert_case::{Case, Casing};
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

Expand Down Expand Up @@ -130,6 +131,7 @@ impl PersistGenerator {
let type_ = columns_map.get(i).ok_or(syn::Error::new(i.span(), "Row not found"))?;
let fn_name = Ident::new(format!("select_by_{i}_range").as_str(), Span::mixed_site());
let field_ident = &idx.name;
let column_pascal = Ident::new(&i.to_string().to_case(Case::Pascal), Span::mixed_site());

let (range_bounds, range_arg) = if is_float(type_.to_string().as_str()) {
(
Expand Down Expand Up @@ -157,7 +159,7 @@ impl PersistGenerator {
.range(#range_arg)
.filter_map(|(_, link)| self.0.data.select_non_ghosted(link.0).ok());

SelectQueryBuilder::new(rows)
SelectQueryBuilder::new_sorted(rows, #row_fields_ident::#column_pascal)
}
})
}
Expand Down
47 changes: 36 additions & 11 deletions codegen/src/generators/persist/table/select_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,20 @@ impl PersistGenerator {
}
};

let fallback_sort = quote! {
let mut items: Vec<#row_type> = iter.collect();
items.sort_by(|a, b| {
for (order, col) in &self.params.order {
match col {
#(#order_matches)*
_ => continue,
}
}
std::cmp::Ordering::Equal
});
iter = Box::new(items.into_iter());
};

quote! {
impl<I> SelectQueryExecutor<#row_type, I, #column_range_type, #row_fields_ident>
for SelectQueryBuilder<#row_type, I, #column_range_type, #row_fields_ident>
Expand All @@ -181,19 +195,30 @@ impl PersistGenerator {
#range

if !self.params.order.is_empty() {
let mut items: Vec<#row_type> = iter.collect();

items.sort_by(|a, b| {
for (order, col) in &self.params.order {
match col {
#(#order_matches)*
_ => continue,
// Optimization: single order on pre-sorted column with no additional range filters
let can_optimize = self.params.sorted_by.is_some()
&& self.params.range.is_empty()
&& self.params.order.len() == 1;

if can_optimize {
let (order, col) = &self.params.order[0];
let sorted_col = self.params.sorted_by.as_ref().unwrap();

if col == sorted_col {
match order {
Order::Desc => {
iter = Box::new(iter.rev());
}
Order::Asc => {
// Already sorted correctly, no action needed
}
}
} else {
#fallback_sort
}
std::cmp::Ordering::Equal
});

iter = Box::new(items.into_iter());
} else {
#fallback_sort
}
}

let iter_result: Box<dyn Iterator<Item = #row_type>> = if let Some(offset) = self.params.offset {
Expand Down
17 changes: 15 additions & 2 deletions codegen/src/generators/read_only/table/impls.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use proc_macro2::TokenStream;
use convert_case::{Case, Casing};
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

use crate::common::name_generator::{WorktableNameGenerator, is_unsized_vec};
Expand Down Expand Up @@ -163,6 +164,18 @@ impl ReadOnlyGenerator {
let column_range_type = name_generator.get_column_range_type_ident();
let row_fields_ident = name_generator.get_row_fields_enum_ident();

let pk_sorted_by = if self.columns.primary_keys.len() == 1 {
let pk_field = &self.columns.primary_keys[0];
let pk_pascal = Ident::new(&pk_field.to_string().to_case(Case::Pascal), Span::mixed_site());
quote! {
SelectQueryBuilder::new_sorted(rows, #row_fields_ident::#pk_pascal)
}
} else {
quote! {
SelectQueryBuilder::new(rows)
}
};

quote! {
pub fn select_by_pk_range<R, Pk>(&self, range: R) -> SelectQueryBuilder<#row_type,
impl DoubleEndedIterator<Item = #row_type> + '_,
Expand All @@ -181,7 +194,7 @@ impl ReadOnlyGenerator {
.range(converted_range)
.filter_map(|(_, link)| self.0.data.select_non_ghosted(link.0).ok());

SelectQueryBuilder::new(rows)
#pk_sorted_by
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion codegen/src/generators/read_only/table/index_fns.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::HashMap;

use convert_case::{Case, Casing};
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

Expand Down Expand Up @@ -130,6 +131,7 @@ impl ReadOnlyGenerator {
let type_ = columns_map.get(i).ok_or(syn::Error::new(i.span(), "Row not found"))?;
let fn_name = Ident::new(format!("select_by_{i}_range").as_str(), Span::mixed_site());
let field_ident = &idx.name;
let column_pascal = Ident::new(&i.to_string().to_case(Case::Pascal), Span::mixed_site());

let (range_bounds, range_arg) = if is_float(type_.to_string().as_str()) {
(
Expand Down Expand Up @@ -157,7 +159,7 @@ impl ReadOnlyGenerator {
.range(#range_arg)
.filter_map(|(_, link)| self.0.data.select_non_ghosted(link.0).ok());

SelectQueryBuilder::new(rows)
SelectQueryBuilder::new_sorted(rows, #row_fields_ident::#column_pascal)
}
})
}
Expand Down
47 changes: 36 additions & 11 deletions codegen/src/generators/read_only/table/select_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,20 @@ impl ReadOnlyGenerator {
}
};

let fallback_sort = quote! {
let mut items: Vec<#row_type> = iter.collect();
items.sort_by(|a, b| {
for (order, col) in &self.params.order {
match col {
#(#order_matches)*
_ => continue,
}
}
std::cmp::Ordering::Equal
});
iter = Box::new(items.into_iter());
};

quote! {
impl<I> SelectQueryExecutor<#row_type, I, #column_range_type, #row_fields_ident>
for SelectQueryBuilder<#row_type, I, #column_range_type, #row_fields_ident>
Expand All @@ -181,19 +195,30 @@ impl ReadOnlyGenerator {
#range

if !self.params.order.is_empty() {
let mut items: Vec<#row_type> = iter.collect();

items.sort_by(|a, b| {
for (order, col) in &self.params.order {
match col {
#(#order_matches)*
_ => continue,
// Optimization: single order on pre-sorted column with no additional range filters
let can_optimize = self.params.sorted_by.is_some()
&& self.params.range.is_empty()
&& self.params.order.len() == 1;

if can_optimize {
let (order, col) = &self.params.order[0];
let sorted_col = self.params.sorted_by.as_ref().unwrap();

if col == sorted_col {
match order {
Order::Desc => {
iter = Box::new(iter.rev());
}
Order::Asc => {
// Already sorted correctly, no action needed
}
}
} else {
#fallback_sort
}
std::cmp::Ordering::Equal
});

iter = Box::new(items.into_iter());
} else {
#fallback_sort
}
}

let iter_result: Box<dyn Iterator<Item = #row_type>> = if let Some(offset) = self.params.offset {
Expand Down
1 change: 1 addition & 0 deletions src/table/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ pub struct QueryParams<ColumnRange, RowFields> {
pub offset: Option<usize>,
pub order: VecDeque<(Order, RowFields)>,
pub range: VecDeque<(ColumnRange, RowFields)>,
pub sorted_by: Option<RowFields>,
}
Loading
Loading