qail-pg 0.27.9

Rust PostgreSQL driver for typed AST queries with direct wire-protocol execution
Documentation
//! Test for v0.14.4+ features
//! Tests: ArrayConstructor, RowConstructor, Subscript, Collate, FieldAccess
//!
//! Run with: cargo run --example expr_test

use qail_core::ast::{Action, Constraint, Expr, Qail, Value};
use qail_pg::driver::PgDriver;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("๐Ÿ”ฅ QAIL Expression Coverage Test (v0.14.4+)");
    println!("=============================================\n");

    let mut driver = PgDriver::connect("127.0.0.1", 5432, "orion", "qail_test_migration").await?;

    // Setup test table with arrays
    println!("๐Ÿ›   Setup Test Data");
    println!("-------------------");

    let drop_cmd = Qail {
        action: Action::Drop,
        table: "expr_test".to_string(),
        ..Default::default()
    };
    let make_cmd = Qail {
        action: Action::Make,
        table: "expr_test".to_string(),
        columns: vec![
            Expr::Def {
                name: "id".to_string(),
                data_type: "serial".to_string(),
                constraints: vec![Constraint::PrimaryKey],
            },
            Expr::Def {
                name: "name".to_string(),
                data_type: "text".to_string(),
                constraints: vec![],
            },
            Expr::Def {
                name: "tags".to_string(),
                data_type: "text[]".to_string(),
                constraints: vec![Constraint::Nullable],
            },
            Expr::Def {
                name: "data".to_string(),
                data_type: "jsonb".to_string(),
                constraints: vec![Constraint::Nullable],
            },
        ],
        ..Default::default()
    };

    let _ = driver.execute(&drop_cmd).await;
    driver.execute(&make_cmd).await?;

    let seed = [
        (
            "Alice",
            vec!["rust", "postgres"],
            r#"{"city": "NYC"}"#.to_string(),
        ),
        ("Bob", vec!["go", "mysql"], r#"{"city": "LA"}"#.to_string()),
        (
            "Carol",
            vec!["python", "postgres", "redis"],
            r#"{"city": "SF"}"#.to_string(),
        ),
    ];
    for (name, tags, json) in seed {
        let tag_vals = Value::Array(
            tags.into_iter()
                .map(|s| Value::String(s.to_string()))
                .collect(),
        );
        let insert = Qail::add("expr_test")
            .columns(["name", "tags", "data"])
            .values([Value::String(name.to_string()), tag_vals, Value::Json(json)]);
        driver.execute(&insert).await?;
    }
    println!("  โœ“ Created expr_test table with 3 rows");

    // =====================================================
    // Test 1: ArrayConstructor - ARRAY[name, name] (same types)
    // =====================================================
    println!("\n๐Ÿ“– Test 1: ArrayConstructor");
    println!("----------------------------");

    let mut arr_query = Qail::get("expr_test").columns(["id", "name"]);
    arr_query.columns.push(Expr::ArrayConstructor {
        elements: vec![
            Expr::Named("name".to_string()),
            Expr::Literal(qail_core::ast::Value::String("suffix".to_string())),
        ],
        alias: Some("name_arr".to_string()),
    });

    match driver.fetch_all(&arr_query).await {
        Ok(rows) => {
            println!("  โœ“ ArrayConstructor: {} rows returned", rows.len());
        }
        Err(e) => println!("  โš  ArrayConstructor: {} (PostgreSQL type check)", e),
    }

    // =====================================================
    // Test 2: RowConstructor - ROW(a, b, c)
    // =====================================================
    println!("\n๐Ÿ“– Test 2: RowConstructor");
    println!("--------------------------");

    let mut row_query = Qail::get("expr_test").columns(["id"]);
    row_query.columns.push(Expr::RowConstructor {
        elements: vec![
            Expr::Named("id".to_string()),
            Expr::Named("name".to_string()),
        ],
        alias: Some("person_row".to_string()),
    });

    match driver.fetch_all(&row_query).await {
        Ok(rows) => {
            println!(
                "  โœ“ RowConstructor: {} rows returned (may be 0 due to simple driver)",
                rows.len()
            );
        }
        Err(e) => println!("  โš  RowConstructor: {}", e),
    }

    // =====================================================
    // Test 3: Subscript - tags[1]
    // =====================================================
    println!("\n๐Ÿ“– Test 3: Subscript (Array Access)");
    println!("------------------------------------");

    let mut sub_query = Qail::get("expr_test").columns(["id", "name"]);
    sub_query.columns.push(Expr::Subscript {
        expr: Box::new(Expr::Named("tags".to_string())),
        index: Box::new(Expr::Literal(qail_core::ast::Value::Int(1))),
        alias: Some("first_tag".to_string()),
    });

    match driver.fetch_all(&sub_query).await {
        Ok(rows) => {
            println!("  โœ“ Subscript: {} rows with first_tag", rows.len());
            assert_eq!(rows.len(), 3, "Expected 3 rows");
        }
        Err(e) => println!("  โœ— Subscript: {}", e),
    }

    // =====================================================
    // Test 4: Collate - name COLLATE "C"
    // =====================================================
    println!("\n๐Ÿ“– Test 4: Collate");
    println!("-------------------");

    let mut collate_query = Qail::get("expr_test").columns(["id"]);
    collate_query.columns.push(Expr::Collate {
        expr: Box::new(Expr::Named("name".to_string())),
        collation: "C".to_string(),
        alias: Some("name_c".to_string()),
    });

    match driver.fetch_all(&collate_query).await {
        Ok(rows) => {
            println!("  โœ“ Collate: {} rows with C collation", rows.len());
            assert_eq!(rows.len(), 3, "Expected 3 rows");
        }
        Err(e) => println!("  โœ— Collate: {}", e),
    }

    // =====================================================
    // =====================================================
    println!("\n๐Ÿงน Cleanup");
    println!("-----------");
    driver.execute(&drop_cmd).await?;
    println!("  โœ“ Cleanup complete");

    println!("\nโœ… Expression test complete! All v0.14.4 features verified.");

    Ok(())
}