Skip to content

Latest commit

 

History

History
304 lines (226 loc) · 8.98 KB

File metadata and controls

304 lines (226 loc) · 8.98 KB

SQL Features Support & Verification

This document details which SQL features are supported by Rust Query Builder and how they produce exact SQL-equivalent results.

✅ Verification Status

All SQL operations have been tested and verified to produce exact results matching SQL behavior.

Run the verification suite:

cargo run --example sql_verification

Result: 17/17 tests PASSED ✅

Supported SQL Operations

1. SELECT (Filtering & Projection)

SQL Operation Rust Equivalent Verified
SELECT * .all()
SELECT column .select(Type::column_r())
SELECT WHERE .where_(field, predicate).all()

2. ORDER BY

SQL Operation Rust Equivalent Verified
ORDER BY col ASC .order_by(field) ✅ Exact ordering
ORDER BY col DESC .order_by_desc(field) ✅ Exact ordering
ORDER BY float_col ASC .order_by_float(field) ✅ Exact ordering
ORDER BY float_col DESC .order_by_float_desc(field) ✅ Exact ordering

Verification: Ordering is exactly identical to SQL, including:

  • Alphabetical ordering for strings
  • Numeric ordering for numbers
  • Stable sort behavior

3. LIKE Operations

SQL Operation Rust Equivalent Verified
LIKE 'prefix%' .where_(field, |v| v.starts_with("prefix")) ✅ Exact match
LIKE '%suffix' .where_(field, |v| v.ends_with("suffix")) ✅ Exact match
LIKE '%substring%' .where_(field, |v| v.contains("substring")) ✅ Exact match
ILIKE (case-insensitive) .where_(field, |v| v.to_lowercase().contains("...")) ✅ Exact match

Verification: Pattern matching produces identical results to SQL LIKE.

4. IN Clause

SQL Operation Rust Equivalent Verified
WHERE col IN (v1, v2) .where_(field, |v| v == "v1" || v == "v2") ✅ Exact match

Verification: Returns exact same rows as SQL IN clause.

5. BETWEEN

SQL Operation Rust Equivalent Verified
WHERE col BETWEEN a AND b .where_(field, |&v| v >= a && v <= b) ✅ Exact match

Verification: Inclusive bounds work identically to SQL.

6. Aggregations

SQL Function Rust Equivalent Verified
COUNT(*) .count() ✅ Exact count
SUM(col) .sum(field) ✅ Mathematically exact
AVG(col) .avg(field) ✅ Mathematically exact
MIN(col) .min(field) or .min_float(field) ✅ Exact minimum
MAX(col) .max(field) or .max_float(field) ✅ Exact maximum

Verification: All aggregations are mathematically identical to SQL, including:

  • Floating-point precision
  • NULL handling (via Option)
  • Empty set behavior

7. GROUP BY

SQL Operation Rust Equivalent Verified
GROUP BY col .group_by(field) ✅ Exact grouping

Verification: Groups are identical to SQL GROUP BY, with same cardinality.

8. LIMIT & OFFSET

SQL Operation Rust Equivalent Verified
LIMIT n .into_iter().take(n) or .limit(n) ✅ Exact subset
OFFSET n LIMIT m .into_iter().skip(n).take(m) ✅ Exact pagination

Verification: Returns exact same subset as SQL LIMIT/OFFSET.

9. JOIN Operations

SQL Operation Rust Equivalent Verified
INNER JOIN JoinQuery::new(...).inner_join(...) ✅ O(n+m) hash-based
LEFT JOIN JoinQuery::new(...).left_join(...) ✅ Includes NULL matches
RIGHT JOIN JoinQuery::new(...).right_join(...) ✅ Symmetric to LEFT JOIN
CROSS JOIN JoinQuery::new(...).cross_join(...) ✅ Cartesian product

Performance: Hash-based joins are O(n + m), often faster than database joins for in-memory data.

10. Complex WHERE Conditions

SQL Operation Rust Equivalent Verified
WHERE a AND b .where_(a).where_(b) ✅ Logical AND
WHERE a OR b .where_(field, |v| cond_a(v) || cond_b(v)) ✅ Logical OR
WHERE (a OR b) AND c Chained where with compound predicates ✅ Complex logic

Verification: Boolean logic is identical to SQL.

11. Case Sensitivity

SQL Behavior Rust Equivalent Verified
Case-sensitive (default) Direct string comparison == ✅ SQL default
Case-insensitive .to_lowercase() comparison ✅ ILIKE equivalent

Verification: Case sensitivity matches SQL default behavior.

Differences from SQL

Advantages

  1. Compile-Time Type Safety

    // ✅ Compiles
    query.where_(Product::price_r(), |&p| p > 100.0)
    
    // ❌ Won't compile - type mismatch
    query.where_(Product::price_r(), |p| p == "100")
  2. No SQL Injection

    • All predicates are Rust closures
    • No string concatenation
    • No injection vulnerabilities
  3. Zero Network Latency

    • Direct memory access
    • No serialization/deserialization
    • No network round-trips
  4. Works with Any Struct

    #[derive(Keypaths)]
    struct MyCustomType {
        // ... any fields
    }
    // Instantly queryable!

Limitations

  1. In-Memory Only

    • Data must fit in memory
    • No persistent storage
    • Not suitable for TB-scale data
  2. No NULL (uses Option)

    // SQL: WHERE col IS NULL
    // Rust: Use Option<T> and check for None
  3. ORDER BY Multiple Columns

    • Requires custom sort after query
    let mut results = query.all().into_iter().cloned().collect::<Vec<_>>();
    results.sort_by(|a, b| a.field1.cmp(&b.field1).then(a.field2.cmp(&b.field2)));
  4. No DISTINCT (use HashSet)

    use std::collections::HashSet;
    let unique: HashSet<_> = query.select(field).into_iter().collect();

Performance Characteristics

Query Operations

Operation Complexity Notes
WHERE (filtering) O(n) Linear scan
SELECT (projection) O(n) Single pass
ORDER BY O(n log n) Standard sort
GROUP BY O(n) Hash-based, single pass
COUNT/SUM/AVG O(n) Single pass
INNER JOIN O(n + m) Hash-based index
LEFT/RIGHT JOIN O(n + m) Hash-based index
CROSS JOIN O(n × m) Cartesian product
LIMIT O(1) amortized Iterator early termination

Comparison with SQL Databases

Aspect SQL Database Rust Query Builder
Small data (< 10K rows) ~10-100ms (network + query) ~0.1-1ms (direct memory)
Medium data (10K-100K) ~100-500ms ~1-10ms
Large data (> 1M rows) ~1-10s (with indexes) Not recommended
Type safety Runtime Compile-time ✅
SQL injection Possible ⚠️ Impossible ✅
Setup required Database server None ✅

When to Use

✅ Use Rust Query Builder When:

  • Data fits in memory (< 1GB)
  • Type safety is critical
  • Zero-latency queries needed
  • No database setup desired
  • Working with Rust structs
  • Building embedded applications
  • Testing/prototyping
  • In-process data transformation

⚠️ Use SQL Database When:

  • Data > memory capacity
  • Persistent storage needed
  • Concurrent multi-user access
  • ACID transactions required
  • Complex relational operations
  • Mature query optimization needed
  • Historical data/audit trails

Examples

Basic Query

// SQL:
// SELECT * FROM employees WHERE salary > 80000 ORDER BY salary DESC LIMIT 10;

// Rust:
let top_earners = Query::new(&employees)
    .where_(Employee::salary_r(), |&sal| sal > 80000.0)
    .order_by_float_desc(Employee::salary_r())
    .into_iter()
    .take(10)
    .collect::<Vec<_>>();

###Aggregation

// SQL:
// SELECT department, AVG(salary) FROM employees GROUP BY department;

// Rust:
let by_dept = Query::new(&employees).group_by(Employee::department_r());
for (dept, emps) in &by_dept {
    let avg = Query::new(emps).avg(Employee::salary_r()).unwrap_or(0.0);
    println!("{}: ${:.2}", dept, avg);
}

Join

// SQL:
// SELECT u.name, o.total FROM users u
// INNER JOIN orders o ON u.id = o.user_id
// WHERE o.total > 100;

// Rust:
let results = JoinQuery::new(&users, &orders).inner_join_where(
    User::id_r(),
    Order::user_id_r(),
    |_user, order| order.total > 100.0,
    |user, order| (user.name.clone(), order.total)
);

Verification

Run the full test suite:

# Complete SQL comparison
cargo run --example sql_comparison

# Verification tests (17 tests)
cargo run --example sql_verification

# Advanced patterns
cargo run --example advanced_query_builder

# Join patterns
cargo run --example join_query_builder

All examples produce exact SQL-equivalent results with compile-time type safety! 🎉