use std::time::Instant;
use stoolap::api::Database;
#[test]
fn test_parallel_filter_large_table() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE large_test (id INTEGER PRIMARY KEY, value INTEGER, category TEXT)",
(),
)
.unwrap();
let start = Instant::now();
db.execute("BEGIN", ()).unwrap();
for i in 0..50_000 {
let category = match i % 5 {
0 => "A",
1 => "B",
2 => "C",
3 => "D",
_ => "E",
};
db.execute(
&format!(
"INSERT INTO large_test VALUES ({}, {}, '{}')",
i,
i % 100,
category
),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let insert_time = start.elapsed();
println!("Insert 50,000 rows: {:?}", insert_time);
let start = Instant::now();
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM large_test WHERE value < 50 AND UPPER(category) = 'A'",
(),
)
.unwrap();
let query_time = start.elapsed();
println!("Parallel filter query: {:?}", query_time);
assert_eq!(count, 5_000);
}
#[test]
fn test_parallel_vs_sequential_filter() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE small_test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..1_000 {
db.execute(
&format!("INSERT INTO small_test VALUES ({}, {})", i, i % 10),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let count: i64 = db
.query_one("SELECT COUNT(*) FROM small_test WHERE value < 5", ())
.unwrap();
assert_eq!(count, 500);
db.execute(
"CREATE TABLE big_test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..20_000 {
db.execute(
&format!("INSERT INTO big_test VALUES ({}, {})", i, i % 10),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let count: i64 = db
.query_one("SELECT COUNT(*) FROM big_test WHERE value < 5", ())
.unwrap();
assert_eq!(count, 10_000); }
#[test]
fn test_parallel_filter_complex_expression() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE complex_test (id INTEGER PRIMARY KEY, name TEXT, score INTEGER, grade TEXT)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..30_000 {
let name = format!("User{}", i);
let score = i % 100;
let grade = if score >= 90 {
"A"
} else if score >= 80 {
"B"
} else if score >= 70 {
"C"
} else {
"D"
};
db.execute(
&format!(
"INSERT INTO complex_test VALUES ({}, '{}', {}, '{}')",
i, name, score, grade
),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let start = Instant::now();
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM complex_test
WHERE score >= 70 AND score < 90
AND (UPPER(grade) = 'B' OR UPPER(grade) = 'C')",
(),
)
.unwrap();
let query_time = start.elapsed();
println!("Complex parallel filter: {:?}", query_time);
assert_eq!(count, 6_000);
}
#[test]
fn test_parallel_filter_with_aggregation() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE agg_test (id INTEGER PRIMARY KEY, category TEXT, amount INTEGER)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..25_000 {
let category = match i % 3 {
0 => "X",
1 => "Y",
_ => "Z",
};
db.execute(
&format!("INSERT INTO agg_test VALUES ({}, '{}', {})", i, category, i),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let mut rows = db
.query(
"SELECT category, COUNT(*), SUM(amount) FROM agg_test
WHERE LOWER(category) IN ('x', 'y')
GROUP BY category ORDER BY category",
(),
)
.unwrap();
let row = rows.next().unwrap().unwrap();
let category: String = row.get(0).unwrap();
assert_eq!(category, "X");
let x_count: i64 = row.get(1).unwrap();
assert!(x_count > 8000 && x_count < 9000);
let row = rows.next().unwrap().unwrap();
let category: String = row.get(0).unwrap();
assert_eq!(category, "Y");
let y_count: i64 = row.get(1).unwrap();
assert!(y_count > 8000 && y_count < 9000);
assert!(rows.next().is_none());
}
#[test]
fn test_parallel_scaling() {
let db = Database::open_in_memory().unwrap();
let sizes = [5_000, 10_000, 20_000, 50_000];
for size in sizes {
let table_name = format!("scale_test_{}", size);
db.execute(
&format!(
"CREATE TABLE {} (id INTEGER PRIMARY KEY, value INTEGER, data TEXT)",
table_name
),
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..size {
db.execute(
&format!(
"INSERT INTO {} VALUES ({}, {}, 'data{}')",
table_name,
i,
i % 100,
i
),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let start = Instant::now();
let count: i64 = db
.query_one(
&format!(
"SELECT COUNT(*) FROM {} WHERE value < 50 AND LENGTH(data) > 4",
table_name
),
(),
)
.unwrap();
let query_time = start.elapsed();
println!(
"Size {}: {} rows filtered in {:?} ({} passed)",
size, size, query_time, count
);
assert!(count > (size as i64 / 3));
}
}
#[test]
fn test_explain_analyze_shows_parallel() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE explain_parallel_test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER)",
(),
)
.unwrap();
for batch in 0..15 {
let mut values = Vec::new();
for i in 0..1000 {
let id = batch * 1000 + i + 1;
values.push(format!("({}, 'name_{}', {})", id, id, id % 100));
}
let sql = format!(
"INSERT INTO explain_parallel_test VALUES {}",
values.join(", ")
);
db.execute(&sql, ()).unwrap();
}
let count: i64 = db
.query_one("SELECT COUNT(*) FROM explain_parallel_test", ())
.unwrap();
assert_eq!(count, 15000, "Should have 15000 rows");
let mut rows = db
.query(
"EXPLAIN ANALYZE SELECT * FROM explain_parallel_test WHERE value > 50",
(),
)
.unwrap();
let mut found_parallel = false;
let mut explain_output = Vec::new();
while let Some(Ok(row)) = rows.next() {
let plan_line: String = row.get(0).unwrap();
explain_output.push(plan_line.clone());
if plan_line.contains("Parallel Seq Scan") {
found_parallel = true;
assert!(
plan_line.contains("workers="),
"Parallel scan should show worker count: {}",
plan_line
);
}
}
println!("EXPLAIN ANALYZE output:");
for line in &explain_output {
println!(" {}", line);
}
assert!(
found_parallel,
"EXPLAIN ANALYZE should show 'Parallel Seq Scan' for large table with filter"
);
}
#[test]
fn test_explain_analyze_sequential_for_small() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE small_explain_test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
let values: Vec<String> = (1..=100).map(|i| format!("({}, {})", i, i % 10)).collect();
let sql = format!(
"INSERT INTO small_explain_test VALUES {}",
values.join(", ")
);
db.execute(&sql, ()).unwrap();
let mut rows = db
.query(
"EXPLAIN ANALYZE SELECT * FROM small_explain_test WHERE value > 5",
(),
)
.unwrap();
let mut found_sequential = false;
let mut found_parallel = false;
while let Some(Ok(row)) = rows.next() {
let plan_line: String = row.get(0).unwrap();
println!(" {}", plan_line);
if plan_line.contains("Seq Scan") && !plan_line.contains("Parallel") {
found_sequential = true;
}
if plan_line.contains("Parallel") {
found_parallel = true;
}
}
assert!(
found_sequential && !found_parallel,
"Small table should use regular Seq Scan, not Parallel Seq Scan"
);
}
#[test]
fn test_parallel_hash_join() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE orders (id INTEGER PRIMARY KEY, customer_id INTEGER, amount INTEGER)",
(),
)
.unwrap();
db.execute(
"CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..10_000 {
db.execute(
&format!("INSERT INTO customers VALUES ({}, 'Customer_{}')", i, i),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..20_000 {
db.execute(
&format!(
"INSERT INTO orders VALUES ({}, {}, {})",
i,
i % 10_000, (i * 17) % 1000 ),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let customer_count: i64 = db.query_one("SELECT COUNT(*) FROM customers", ()).unwrap();
let order_count: i64 = db.query_one("SELECT COUNT(*) FROM orders", ()).unwrap();
assert_eq!(customer_count, 10_000);
assert_eq!(order_count, 20_000);
let start = Instant::now();
let inner_count: i64 = db
.query_one(
"SELECT COUNT(*) FROM orders o INNER JOIN customers c ON o.customer_id = c.id",
(),
)
.unwrap();
let inner_time = start.elapsed();
println!("INNER JOIN count: {}, time: {:?}", inner_count, inner_time);
assert_eq!(inner_count, 20_000);
let start = Instant::now();
let left_count: i64 = db
.query_one(
"SELECT COUNT(*) FROM orders o LEFT JOIN customers c ON o.customer_id = c.id",
(),
)
.unwrap();
let left_time = start.elapsed();
println!("LEFT JOIN count: {}, time: {:?}", left_count, left_time);
assert_eq!(left_count, 20_000);
let start = Instant::now();
let right_count: i64 = db
.query_one(
"SELECT COUNT(*) FROM orders o RIGHT JOIN customers c ON o.customer_id = c.id",
(),
)
.unwrap();
let right_time = start.elapsed();
println!("RIGHT JOIN count: {}, time: {:?}", right_count, right_time);
assert_eq!(right_count, 20_000);
println!("Parallel hash join test completed successfully!");
}
#[test]
fn test_parallel_hash_join_outer() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE left_table (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute(
"CREATE TABLE right_table (id INTEGER PRIMARY KEY, left_id INTEGER)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..6_000 {
db.execute(
&format!("INSERT INTO left_table VALUES ({}, {})", i, i * 10),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..8_000 {
let left_id = if i < 3_000 { i } else { 10_000 + i };
db.execute(
&format!("INSERT INTO right_table VALUES ({}, {})", i, left_id),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let inner_count: i64 = db
.query_one(
"SELECT COUNT(*) FROM left_table l INNER JOIN right_table r ON l.id = r.left_id",
(),
)
.unwrap();
assert_eq!(inner_count, 3_000, "INNER JOIN should have 3000 matches");
let left_count: i64 = db
.query_one(
"SELECT COUNT(*) FROM left_table l LEFT JOIN right_table r ON l.id = r.left_id",
(),
)
.unwrap();
assert_eq!(
left_count, 6_000,
"LEFT JOIN should have 6000 rows (all left rows)"
);
let right_count: i64 = db
.query_one(
"SELECT COUNT(*) FROM left_table l RIGHT JOIN right_table r ON l.id = r.left_id",
(),
)
.unwrap();
assert_eq!(
right_count, 8_000,
"RIGHT JOIN should have 8000 rows (all right rows)"
);
let full_count: i64 = db
.query_one(
"SELECT COUNT(*) FROM left_table l FULL OUTER JOIN right_table r ON l.id = r.left_id",
(),
)
.unwrap();
assert_eq!(full_count, 11_000, "FULL OUTER JOIN should have 11000 rows");
println!("Parallel hash join OUTER test completed successfully!");
}
#[test]
fn test_indexed_query_not_parallel_bypassed() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE indexed_test (id INTEGER PRIMARY KEY, value INTEGER, name TEXT)",
(),
)
.unwrap();
db.execute("CREATE INDEX idx_value ON indexed_test(value)", ())
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..15_000 {
db.execute(
&format!(
"INSERT INTO indexed_test VALUES ({}, {}, 'name_{}')",
i,
i % 100, i
),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let start = Instant::now();
let count: i64 = db
.query_one("SELECT COUNT(*) FROM indexed_test WHERE value = 50", ())
.unwrap();
let indexed_time = start.elapsed();
assert_eq!(count, 150);
let start = Instant::now();
let count2: i64 = db
.query_one(
"SELECT COUNT(*) FROM indexed_test WHERE name LIKE 'name_1%'",
(),
)
.unwrap();
let _non_indexed_time = start.elapsed();
assert_eq!(count2, 6111);
println!(
"Indexed query time: {:?}, should be much faster than full scan",
indexed_time
);
}
#[test]
fn test_parallel_order_by_sql() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE sort_test (id INTEGER PRIMARY KEY, value INTEGER, name TEXT)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..60_000 {
db.execute(
&format!(
"INSERT INTO sort_test VALUES ({}, {}, 'name_{}')",
i,
(60_000 - i) % 1000, i
),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let start = Instant::now();
let rows = db
.query(
"SELECT id, value FROM sort_test ORDER BY value ASC LIMIT 10",
(),
)
.unwrap();
let mut results = Vec::new();
for row in rows {
let row = row.unwrap();
let value: i64 = row.get(1).unwrap();
results.push(value);
}
let sort_time = start.elapsed();
assert!(!results.is_empty());
assert_eq!(results[0], 0, "First value should be 0 (smallest)");
for i in 1..results.len() {
assert!(
results[i] >= results[i - 1],
"Results should be in ascending order"
);
}
println!("Parallel ORDER BY time for 60K rows: {:?}", sort_time);
}
#[test]
fn test_parallel_distinct_sql() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE distinct_test (id INTEGER PRIMARY KEY, category TEXT)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..50_000 {
db.execute(
&format!(
"INSERT INTO distinct_test VALUES ({}, 'cat_{}')",
i,
i % 100
),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let start = Instant::now();
let count: i64 = db
.query_one("SELECT COUNT(DISTINCT category) FROM distinct_test", ())
.unwrap();
let distinct_time = start.elapsed();
assert_eq!(count, 100, "Should have exactly 100 distinct categories");
println!("Parallel DISTINCT time for 50K rows: {:?}", distinct_time);
let start = Instant::now();
let rows = db
.query("SELECT DISTINCT category FROM distinct_test", ())
.unwrap();
let mut distinct_values = Vec::new();
for row in rows {
let row = row.unwrap();
let cat: String = row.get(0).unwrap();
distinct_values.push(cat);
}
let select_distinct_time = start.elapsed();
assert_eq!(
distinct_values.len(),
100,
"SELECT DISTINCT should return 100 rows"
);
println!(
"SELECT DISTINCT time for 50K rows: {:?}",
select_distinct_time
);
}
#[test]
fn test_parallel_empty_tables() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE empty_test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
let count: i64 = db
.query_one("SELECT COUNT(*) FROM empty_test WHERE value > 50", ())
.unwrap();
assert_eq!(count, 0, "Empty table should return 0 count");
let count: i64 = db
.query_one("SELECT COUNT(DISTINCT value) FROM empty_test", ())
.unwrap();
assert_eq!(count, 0, "Empty DISTINCT should return 0");
let mut rows = db
.query("SELECT * FROM empty_test ORDER BY value", ())
.unwrap();
assert!(
rows.next().is_none(),
"Empty ORDER BY should return no rows"
);
db.execute(
"CREATE TABLE non_empty (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute("INSERT INTO non_empty VALUES (1, 100), (2, 200)", ())
.unwrap();
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM empty_test e JOIN non_empty n ON e.id = n.id",
(),
)
.unwrap();
assert_eq!(count, 0, "JOIN with empty table should return 0");
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM empty_test e LEFT JOIN non_empty n ON e.id = n.id",
(),
)
.unwrap();
assert_eq!(count, 0, "LEFT JOIN from empty table should return 0");
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM non_empty n LEFT JOIN empty_test e ON n.id = e.id",
(),
)
.unwrap();
assert_eq!(
count, 2,
"LEFT JOIN to empty table should preserve non-empty rows"
);
println!("Empty table tests passed!");
}
#[test]
fn test_parallel_error_handling() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE error_test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..15_000 {
db.execute(&format!("INSERT INTO error_test VALUES ({}, {})", i, i), ())
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let result: Result<i64, _> = db.query_one(
"SELECT COUNT(*) FROM error_test WHERE value / (id - 5000) > 0",
(),
);
match result {
Ok(count) => {
println!("Query succeeded with count: {}", count);
}
Err(e) => {
println!("Query failed as expected: {:?}", e);
}
}
let result = db.query("SELECT * FROM error_test WHERE value = 'not_a_number'", ());
match result {
Ok(mut rows) => {
let count = rows.by_ref().count();
println!("Type mismatch query returned {} rows", count);
}
Err(e) => {
println!("Type mismatch query failed: {:?}", e);
}
}
println!("Error handling tests completed!");
}
#[test]
fn test_complex_expression_memory_filter() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE complex_test (id INTEGER PRIMARY KEY, value INTEGER, name TEXT)",
(),
)
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..15_000 {
db.execute(
&format!(
"INSERT INTO complex_test VALUES ({}, {}, 'name_{}')",
i,
i % 100,
i
),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let start = Instant::now();
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM complex_test WHERE UPPER(name) LIKE 'NAME_1%'",
(),
)
.unwrap();
let complex_time = start.elapsed();
assert_eq!(count, 6111);
println!(
"Complex expression (UPPER) filter time for 15K rows: {:?}",
complex_time
);
let start = Instant::now();
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM complex_test WHERE value = 50 AND LENGTH(name) > 5",
(),
)
.unwrap();
let combined_time = start.elapsed();
assert_eq!(count, 150);
println!(
"Combined index + complex filter time for 15K rows: {:?}",
combined_time
);
}
#[test]
fn test_partial_pushdown_optimization() {
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE partial_test (id INTEGER PRIMARY KEY, indexed_val INTEGER, text_col TEXT)",
(),
)
.unwrap();
db.execute("CREATE INDEX idx_partial ON partial_test(indexed_val)", ())
.unwrap();
db.execute("BEGIN", ()).unwrap();
for i in 0..20_000 {
db.execute(
&format!(
"INSERT INTO partial_test VALUES ({}, {}, 'text_{}')",
i,
i % 100,
i
),
(),
)
.unwrap();
}
db.execute("COMMIT", ()).unwrap();
let start = Instant::now();
let indexed_only: i64 = db
.query_one(
"SELECT COUNT(*) FROM partial_test WHERE indexed_val = 42",
(),
)
.unwrap();
let indexed_time = start.elapsed();
assert_eq!(indexed_only, 200);
let start = Instant::now();
let mixed: i64 = db
.query_one(
"SELECT COUNT(*) FROM partial_test WHERE indexed_val = 42 AND LENGTH(text_col) > 6",
(),
)
.unwrap();
let mixed_time = start.elapsed();
assert_eq!(mixed, 200);
let start = Instant::now();
let complex_only: i64 = db
.query_one(
"SELECT COUNT(*) FROM partial_test WHERE LENGTH(text_col) > 6",
(),
)
.unwrap();
let complex_time = start.elapsed();
assert_eq!(complex_only, 19_990);
println!("Partial pushdown test:");
println!(
" - Pure indexed query (indexed_val = 42): {:?}",
indexed_time
);
println!(" - Mixed query (indexed_val + LENGTH): {:?}", mixed_time);
println!(" - Complex-only query (LENGTH only): {:?}", complex_time);
println!(
"Speedup from partial pushdown: {:.1}x (20K rows → 200 rows via index)",
complex_time.as_secs_f64() / mixed_time.as_secs_f64().max(0.0001)
);
}
#[test]
fn test_debug_upper_like_pattern() {
use stoolap::Database;
let db = Database::open_in_memory().unwrap();
db.execute(
"CREATE TABLE test_upper (id INTEGER PRIMARY KEY, name TEXT)",
(),
)
.unwrap();
db.execute("INSERT INTO test_upper VALUES (1, 'name_1')", ())
.unwrap();
db.execute("INSERT INTO test_upper VALUES (2, 'name_10')", ())
.unwrap();
db.execute("INSERT INTO test_upper VALUES (3, 'name_2')", ())
.unwrap();
let upper_val: String = db
.query_one("SELECT UPPER(name) FROM test_upper WHERE id = 1", ())
.unwrap();
println!("UPPER('name_1'): '{}'", upper_val);
let eq_test: i64 = db
.query_one(
"SELECT COUNT(*) FROM test_upper WHERE UPPER(name) = 'NAME_1'",
(),
)
.unwrap();
println!("UPPER(name) = 'NAME_1': {}", eq_test);
let like_no_underscore: i64 = db
.query_one(
"SELECT COUNT(*) FROM test_upper WHERE UPPER(name) LIKE 'NAME%'",
(),
)
.unwrap();
println!("UPPER(name) LIKE 'NAME%': {}", like_no_underscore);
let like_simple_underscore: i64 = db
.query_one(
"SELECT COUNT(*) FROM test_upper WHERE name LIKE 'name__'",
(),
)
.unwrap();
println!("name LIKE 'name__': {}", like_simple_underscore);
let like_middle_underscore: i64 = db
.query_one(
"SELECT COUNT(*) FROM test_upper WHERE UPPER(name) LIKE 'NAME__'",
(),
)
.unwrap();
println!("UPPER(name) LIKE 'NAME__': {}", like_middle_underscore);
let debug_query = "SELECT id, name, UPPER(name) as upper_name,
CASE WHEN UPPER(name) LIKE 'NAME__' THEN 1 ELSE 0 END as matches
FROM test_upper";
let rows = db.query(debug_query, ()).unwrap();
println!("Debug output:");
for row in rows.flatten() {
println!(
" id={:?}, name={:?}, UPPER={:?}, matches={:?}",
row.get::<i64>(0),
row.get::<String>(1),
row.get::<String>(2),
row.get::<i64>(3)
);
}
let like_test: i64 = db
.query_one(
"SELECT COUNT(*) FROM test_upper WHERE 'NAME_1' LIKE 'NAME_1%'",
(),
)
.unwrap();
println!("'NAME_1' LIKE 'NAME_1%': {}", like_test);
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM test_upper WHERE name LIKE 'name_1%'",
(),
)
.unwrap();
println!("name LIKE 'name_1%': {}", count);
let count2: i64 = db
.query_one(
"SELECT COUNT(*) FROM test_upper WHERE UPPER(name) LIKE 'NAME_1%'",
(),
)
.unwrap();
println!("UPPER(name) LIKE 'NAME_1%': {}", count2);
assert_eq!(count, 2, "Direct LIKE should match name_1 and name_10");
assert_eq!(count2, 2, "UPPER LIKE should match NAME_1 and NAME_10");
}