tegdb 0.5.0

The name TegridyDB (short for TegDB) is inspired by the Tegridy Farm in South Park and tries to correct some of the wrong database implementations, such as null support, implicit conversion support, etc.
Documentation
//! Demonstration of the new streaming ResultSet::Select implementation
//!
//! This example shows how the ResultSet::Select variant now uses a streaming
//! iterator instead of materializing all rows upfront, providing significant
//! memory efficiency improvements for large result sets.

use std::time::Instant;
use tegdb::{Database, Result};

fn main() -> Result<()> {
    println!("=== Streaming ResultSet::Select Demo ===\n");

    // Create test database
    let mut db = Database::open("file:///tmp/streaming_resultset_demo.teg")?;

    // Create test table
    println!("1. Setting up test data...");
    db.execute("CREATE TABLE measurements (id INTEGER PRIMARY KEY, sensor_id INTEGER, timestamp INTEGER, value REAL)")?;

    // Insert a substantial amount of test data
    let start = Instant::now();
    for i in 1..=1000 {
        let sensor_id = (i % 50) + 1; // 50 different sensors
        let timestamp = 1640995200 + (i * 60); // One reading per minute
        let value = 20.0 + (i as f64 % 30.0); // Temperature readings 20-50°C

        db.execute(&format!(
            "INSERT INTO measurements (id, sensor_id, timestamp, value) VALUES ({i}, {sensor_id}, {timestamp}, {value})"
        ))?;
    }
    let insert_time = start.elapsed();
    println!("   ✓ Inserted 1,000 rows in {insert_time:?}\n");

    // Demonstrate streaming behavior with the new ResultSet::Select
    println!("2. Testing streaming ResultSet::Select behavior...\n");

    // Example 1: Process rows one by one using the iterator
    println!("--- Example 1: Processing rows one-by-one (streaming) ---");
    let start = Instant::now();
    let qr = db.query("SELECT * FROM measurements WHERE sensor_id <= 5")?;
    println!(
        "   ✓ Query created in {:?} (no rows materialized yet)",
        start.elapsed()
    );

    let start = Instant::now();
    let mut count = 0;
    for (i, row) in qr.rows().iter().enumerate() {
        count += 1;
        // Show streaming in action - process only first 3 rows
        if i < 3 {
            println!(
                "   • Processing row {}: sensor_id={:?}, value={:?}",
                i + 1,
                row.get(1),
                row.get(3)
            );
        }
        // Early termination example
        if count >= 5 {
            println!("   • Early termination after 5 rows");
            break;
        }
    }
    let process_time = start.elapsed();
    println!("   ✓ Processed {count} rows in {process_time:?} (streaming)\n");

    // Example 2: Collect all rows for backward compatibility
    println!("--- Example 2: Backward compatibility (collect all rows) ---");
    let start = Instant::now();
    let qr2 = db.query("SELECT id, sensor_id, value FROM measurements WHERE value > 45.0")?;
    let rows = qr2.rows().to_vec();
    let collect_time = start.elapsed();
    println!("   ✓ Collected {} rows in {:?}", rows.len(), collect_time);
    if let Some(first_row) = rows.first() {
        println!(
            "   • First row: id={:?}, sensor_id={:?}, value={:?}",
            first_row.first(),
            first_row.get(1),
            first_row.get(2)
        );
    }
    println!();

    // Example 3: Memory efficiency comparison
    println!("--- Example 3: Memory efficiency demonstration ---");

    // Show that we can process large result sets with constant memory
    println!("Processing all 1,000 rows with streaming (constant memory usage):");
    let start = Instant::now();
    let qr3 = db.query("SELECT value FROM measurements")?;
    let mut sum = 0.0;
    let mut processed_count = 0;
    for row in qr3.rows().iter() {
        if let Some(value) = row.first() {
            let numeric_value = match value {
                tegdb::SqlValue::Real(val) => *val,
                tegdb::SqlValue::Integer(val) => *val as f64,
                _ => continue,
            };
            sum += numeric_value;
            processed_count += 1;
        }
    }
    let streaming_time = start.elapsed();

    let average = sum / processed_count as f64;
    println!("   ✓ Processed {processed_count} rows in {streaming_time:?}");
    println!("   ✓ Average temperature: {average:.2}°C");
    println!("   ✓ Memory usage: O(1) - only one row in memory at a time");
    println!();

    // Example 4: LIMIT optimization
    println!("--- Example 4: LIMIT optimization (early termination) ---");
    let start = Instant::now();
    let qr4 = db.query("SELECT * FROM measurements LIMIT 10")?;
    let rows = qr4.rows().to_vec();
    let limit_time = start.elapsed();
    println!(
        "   ✓ Retrieved {} rows with LIMIT 10 in {:?}",
        rows.len(),
        limit_time
    );
    println!("   ✓ Streaming implementation stopped after 10 rows (no unnecessary work)");
    println!();

    // Summary
    println!("=== Summary ===");
    println!("🚀 ResultSet::Select now uses streaming implementation:");
    println!("   • Memory efficient: O(1) memory usage instead of O(n)");
    println!("   • Early termination: LIMIT and WHERE clauses can stop processing early");
    println!("   • Lazy evaluation: Rows are processed only when consumed");
    println!("   • Backward compatible: Can still collect() to Vec when needed");
    println!("   • Better performance: No upfront materialization cost");
    println!();

    println!("🔧 Usage patterns:");
    println!("   • Use iterator methods for streaming: for row in result {{ ... }}");
    println!("   • Use .collect() for Vec<Vec<SqlValue>> when all rows needed");
    println!("   • Use .take(n) for processing only first N rows");
    println!("   • Use .filter() for additional row filtering");
    println!();

    println!("✅ The streaming ResultSet::Select is now ready for production use!");

    // Cleanup
    std::fs::remove_file("streaming_resultset_demo.db").ok();

    Ok(())
}