alopex-sql 0.6.0

SQL parser components for the Alopex DB dialect
#![cfg(all(feature = "tokio", not(target_arch = "wasm32")))]

use std::sync::{Arc, RwLock};

use alopex_core::kv::memory::MemoryKV;
use alopex_sql::ast::expr::Literal;
use alopex_sql::catalog::{ColumnMetadata, PersistentCatalog, TableMetadata};
use alopex_sql::executor::{ExecutionResult, Executor};
use alopex_sql::planner::typed_expr::TypedExpr;
use alopex_sql::planner::types::ResolvedType;
use alopex_sql::{
    AsyncSqlTransaction, AsyncTxnBridge, LogicalPlan, Projection, Span, TokioAsyncTxnBridge,
};
use tokio::time::{Duration, timeout};

#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[tokio::test]
async fn dropping_stream_allows_commit_to_finish() {
    let store = Arc::new(MemoryKV::new());
    let catalog = Arc::new(RwLock::new(PersistentCatalog::new(store.clone())));
    let mut executor = Executor::new(store.clone(), catalog.clone());

    let table = TableMetadata::new(
        "users",
        vec![
            ColumnMetadata::new("id", ResolvedType::Integer)
                .with_primary_key(true)
                .with_not_null(true),
            ColumnMetadata::new("name", ResolvedType::Text).with_not_null(true),
        ],
    )
    .with_primary_key(vec!["id".into()]);

    let create = LogicalPlan::CreateTable {
        table,
        if_not_exists: false,
        with_options: vec![],
    };
    let result = executor.execute(create).unwrap();
    assert!(matches!(result, ExecutionResult::Success));

    let mut values = Vec::new();
    for id in 0..256 {
        let row = vec![
            TypedExpr::literal(
                Literal::Number(id.to_string()),
                ResolvedType::Integer,
                Span::default(),
            ),
            TypedExpr::literal(
                Literal::String(format!("name_{id}")),
                ResolvedType::Text,
                Span::default(),
            ),
        ];
        values.push(row);
    }

    let insert = LogicalPlan::Insert {
        table: "users".into(),
        columns: vec!["id".into(), "name".into()],
        values,
    };
    let result = executor.execute(insert).unwrap();
    assert!(matches!(result, ExecutionResult::RowsAffected(256)));

    let bridge = TokioAsyncTxnBridge::new(store, catalog);
    let mut txn = bridge.begin_read().await.unwrap();

    let stream = txn
        .query_stream(LogicalPlan::Scan {
            table: "users".into(),
            projection: Projection::All(vec!["id".into(), "name".into()]),
        })
        .await
        .unwrap();

    drop(stream);

    let commit = timeout(Duration::from_millis(200), txn.commit()).await;
    assert!(commit.is_ok());
    assert!(commit.unwrap().is_ok());
}