use crate::ast;
use crate::error::DbError;

pub use super::database::{
    CheckpointManager, Database, DatabaseInner, Engine, EngineInner, SqlAnalyzer, StatementExecutor,
};
pub use super::locks::SessionId;
pub use super::projection::deduplicate_projected_rows;
pub use super::result::QueryResult;
pub use super::session::SessionManager;

pub(crate) fn execute_set_op(
    left: QueryResult,
    right: QueryResult,
    op: ast::SetOpKind,
) -> Result<QueryResult, DbError> {
    if left.columns.len() != right.columns.len() {
        return Err(DbError::Execution(
            "set operations require same number of columns".into(),
        ));
    }

    let rows = match op {
        ast::SetOpKind::Union => {
            let mut rows = left.rows;
            rows.extend(right.rows);
            deduplicate_projected_rows(rows)
        }
        ast::SetOpKind::UnionAll => {
            let mut rows = left.rows;
            rows.extend(right.rows);
            rows
        }
        ast::SetOpKind::Intersect => {
            let left_set: std::collections::HashSet<_> = left.rows.iter().cloned().collect();
            right
                .rows
                .into_iter()
                .filter(|r| left_set.contains(r))
                .collect()
        }
        ast::SetOpKind::Except => {
            let right_set: std::collections::HashSet<_> = right.rows.iter().cloned().collect();
            left.rows
                .into_iter()
                .filter(|r| !right_set.contains(r))
                .collect()
        }
    };

    let column_nullabilities = left.column_nullabilities.clone();
    Ok(QueryResult {
        columns: left.columns,
        column_types: left.column_types,
        column_nullabilities,
        rows,
        ..Default::default()
    })
}
