llkv-sql 0.8.5-alpha

SQL interface for the LLKV toolkit.
Documentation
use std::sync::Arc;

use arrow::array::{Array, ArrayRef, Int64Array};
use arrow::compute::cast;
use arrow::datatypes::DataType;
use llkv_sql::SqlEngine;
use llkv_storage::pager::MemPager;

#[test]
fn select_without_from_handles_constant_expression() {
    let engine = SqlEngine::new(Arc::new(MemPager::default()));

    let batches = engine
        .sql("SELECT 20 / - - 96 + CAST ( 90 AS INTEGER ) AS col2")
        .expect("execute constant expression select");

    assert_eq!(batches.len(), 1, "expected single record batch");
    let batch = &batches[0];
    assert_eq!(batch.num_rows(), 1, "expected single result row");
    assert_eq!(batch.num_columns(), 1, "expected single projection");

    assert_eq!(
        value_as_i64(batch.column(0), 0),
        Some(90),
        "20 / - - 96 + CAST(90 AS INTEGER) should yield 90"
    );
}

#[test]
fn select_without_from_handles_logical_and() {
    let engine = SqlEngine::new(Arc::new(MemPager::default()));

    let batches = engine
        .sql("SELECT 1 AND 1 AS all_true, 1 AND 0 AS mixed, NULL AND 1 AS null_branch")
        .expect("execute logical AND select");

    assert_eq!(batches.len(), 1, "expected single record batch");
    let batch = &batches[0];
    assert_eq!(batch.num_rows(), 1, "expected single result row");
    assert_eq!(batch.num_columns(), 3, "expected three projections");

    assert_eq!(
        value_as_i64(batch.column(0), 0),
        Some(1),
        "1 AND 1 should yield 1"
    );

    assert_eq!(
        value_as_i64(batch.column(1), 0),
        Some(0),
        "1 AND 0 should yield 0"
    );

    assert!(
        value_as_i64(batch.column(2), 0).is_none(),
        "NULL AND 1 should yield NULL"
    );
}

#[test]
fn select_without_from_handles_logical_or() {
    let engine = SqlEngine::new(Arc::new(MemPager::default()));

    let batches = engine
        .sql("SELECT 0 OR 0 AS all_false, 0 OR 1 AS truthy, 0 OR NULL AS false_or_null, NULL OR 1 AS null_or_true")
        .expect("execute logical OR select");

    assert_eq!(batches.len(), 1, "expected single record batch");
    let batch = &batches[0];
    assert_eq!(batch.num_rows(), 1, "expected single result row");
    assert_eq!(batch.num_columns(), 4, "expected four projections");

    assert_eq!(
        value_as_i64(batch.column(0), 0),
        Some(0),
        "0 OR 0 should yield 0"
    );

    assert_eq!(
        value_as_i64(batch.column(1), 0),
        Some(1),
        "0 OR 1 should yield 1"
    );

    assert!(
        value_as_i64(batch.column(2), 0).is_none(),
        "0 OR NULL should yield NULL"
    );

    assert_eq!(
        value_as_i64(batch.column(3), 0),
        Some(1),
        "NULL OR 1 should yield 1"
    );
}

fn value_as_i64(column: &ArrayRef, idx: usize) -> Option<i64> {
    if matches!(column.data_type(), DataType::Null) {
        return None;
    }

    if column.is_null(idx) {
        return None;
    }

    if let Some(values) = column.as_any().downcast_ref::<Int64Array>() {
        return Some(values.value(idx));
    }

    let casted = cast(column, &DataType::Int64).expect("cast value to Int64");
    let values = casted
        .as_any()
        .downcast_ref::<Int64Array>()
        .expect("cast produced Int64Array");
    Some(values.value(idx))
}