alopex-sql 0.6.0

SQL parser components for the Alopex DB dialect
use std::sync::{Arc, RwLock};

use alopex_core::lsm::LsmKVConfig;
use alopex_core::lsm::wal::{SyncMode, WalConfig};
use alopex_core::{StorageFactory, StorageMode};
use alopex_sql::catalog::MemoryCatalog;
use alopex_sql::dialect::AlopexDialect;
use alopex_sql::executor::{ExecutionResult, Executor, QueryResult};
use alopex_sql::parser::Parser;
use alopex_sql::planner::Planner;
use tempfile::TempDir;

mod aggregate_functions_test;
mod group_by_edge_cases_test;
mod group_by_test;

pub struct TestHarness {
    executor: Executor<alopex_core::kv::AnyKV, MemoryCatalog>,
    catalog: Arc<RwLock<MemoryCatalog>>,
    _temp_dir: TempDir,
}

impl TestHarness {
    pub fn new() -> Self {
        let temp_dir = tempfile::tempdir().expect("tempdir");
        let cfg = LsmKVConfig {
            wal: WalConfig {
                segment_size: 4096,
                max_segments: 2,
                sync_mode: SyncMode::NoSync,
            },
            ..Default::default()
        };
        let store = Arc::new(
            StorageFactory::create(StorageMode::Disk {
                path: temp_dir.path().to_path_buf(),
                config: Some(cfg),
            })
            .expect("disk store"),
        );
        let catalog = Arc::new(RwLock::new(MemoryCatalog::new()));
        let executor = Executor::new(store, Arc::clone(&catalog));
        Self {
            executor,
            catalog,
            _temp_dir: temp_dir,
        }
    }

    pub fn execute_sql(&mut self, sql: &str) -> Vec<ExecutionResult> {
        let dialect = AlopexDialect;
        let statements = Parser::parse_sql(&dialect, sql).expect("parse sql");
        let mut results = Vec::with_capacity(statements.len());
        for stmt in statements {
            let plan = {
                let guard = self.catalog.read().expect("catalog read");
                let planner = Planner::new(&*guard);
                planner.plan(&stmt).expect("plan")
            };
            let result = self.executor.execute(plan).expect("execute");
            results.push(result);
        }
        results
    }

    pub fn query_sql(&mut self, sql: &str) -> QueryResult {
        let results = self.execute_sql(sql);
        results
            .into_iter()
            .rev()
            .find_map(|result| match result {
                ExecutionResult::Query(query) => Some(query),
                _ => None,
            })
            .expect("query result")
    }

    pub fn catalog(&self) -> &Arc<RwLock<MemoryCatalog>> {
        &self.catalog
    }
}