icydb-core 0.264.2

IcyDB — A schema-first typed query engine and persistence runtime for Internet Computer canisters
Documentation
//! This module defines the canonical boolean IR for the planner.
//! All downstream predicate extraction and execution depend on this shape.
//!
//! Invariants:
//! - `normalize_bool_expr` preserves SQL three-valued logic inside
//!   subexpressions.
//! - associative `AND` / `OR` trees are flattened, deterministically sorted,
//!   deduplicated, and rebuilt as stable left-associated binary chains.
//! - scalar `WHERE` and grouped `HAVING` canonicalization remain distinct
//!   truth-admission behaviors.
//! - searched `CASE` lowering is bounded and only rewrites shapes proven
//!   equivalent under the owning boolean context.
//!
//! Planner boolean canonicalization pipeline:
//! 1. affine numeric rewrite
//! 2. boolean normalization
//! 3. CASE lowering
//! 4. constant simplification

mod case;
mod normalize;
mod ordering;
mod truth_admission;

use crate::db::query::plan::expr::canonicalize::{
    case::canonicalize_normalized_bool_case_in_bool_context, truth_admission::TruthWrapperScope,
};
use crate::db::{
    QueryError,
    query::{plan::expr::Expr, preparation::PreparationWork},
};

pub(in crate::db) use normalize::is_normalized_bool_expr;
#[cfg(feature = "sql")]
pub(in crate::db) use normalize::simplify_bool_expr_constants;
#[cfg(feature = "sql")]
pub(in crate::db) use truth_admission::scalar_where_truth_condition_is_admitted;
pub(in crate::db) use truth_admission::truth_condition_binary_compare_op;

///
/// CanonicalExpr
///
/// Stage artifact for expressions that have passed the canonicalization
/// boundary. The wrapper keeps the invariant visible inside this stage while
/// planner and lowering boundaries continue to exchange plain `Expr` values.
///

#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct CanonicalExpr {
    expr: Expr,
}

impl CanonicalExpr {
    // Build one canonical expression artifact after the entrypoint has already
    // performed the normal-form assertion for the owning context.
    const fn new(expr: Expr) -> Self {
        Self { expr }
    }

    /// Return the canonical expression as a plain planner expression for
    /// boundaries that still exchange the shared expression tree directly.
    pub(in crate::db) fn into_expr(self) -> Expr {
        self.expr
    }
}

/// Normalize one planner-owned boolean expression and assert that the emitted
/// shape satisfies the canonical boolean IR invariant.
pub(in crate::db) fn normalize_bool_expr_artifact(
    expr: Expr,
    work: &PreparationWork<'_>,
) -> Result<CanonicalExpr, QueryError> {
    let expr = normalize::normalize_bool_expr_impl(expr, work)?;

    debug_assert!(is_normalized_bool_expr(&expr));

    Ok(CanonicalExpr::new(expr))
}

/// Normalize one planner-owned boolean expression and return the plain `Expr`
/// surface after producing the canonical stage artifact.
pub(in crate::db) fn normalize_bool_expr(
    expr: Expr,
    work: &PreparationWork<'_>,
) -> Result<Expr, QueryError> {
    normalize_bool_expr_artifact(expr, work).map(CanonicalExpr::into_expr)
}

/// Canonicalize one scalar-WHERE boolean expression into the canonical stage
/// artifact used by downstream predicate subset derivation.
pub(in crate::db) fn canonicalize_scalar_where_bool_expr_artifact(
    expr: Expr,
    work: &PreparationWork<'_>,
) -> Result<CanonicalExpr, QueryError> {
    let expr = normalize_bool_expr(expr, work)?;
    debug_assert!(is_normalized_bool_expr(&expr));

    let expr = canonicalize_normalized_bool_case_in_bool_context(
        expr,
        true,
        Some(TruthWrapperScope::ScalarWhere),
        work,
    )?;
    let expr = normalize_bool_expr(expr, work)?;

    debug_assert!(is_normalized_bool_expr(&expr));

    Ok(CanonicalExpr::new(expr))
}

/// Canonicalize one scalar-WHERE boolean expression onto the current
/// searched-`CASE` boolean seam after the shared structural normalization pass
/// has already settled the planner-owned tree shape.
pub(in crate::db) fn canonicalize_scalar_where_bool_expr(
    expr: Expr,
    work: &PreparationWork<'_>,
) -> Result<Expr, QueryError> {
    canonicalize_scalar_where_bool_expr_artifact(expr, work).map(CanonicalExpr::into_expr)
}

/// Canonicalize one grouped-HAVING boolean expression into the canonical stage
/// artifact used by downstream grouped predicate and HAVING paths.
///
/// Unlike scalar `WHERE`, grouped `HAVING` does not collapse a final
/// `ELSE NULL` arm to `FALSE`. Grouped canonicalization therefore preserves
/// the explicit grouped boolean result tree unless the shipped searched-`CASE`
/// expansion is already semantically identical without null-arm collapse.
pub(in crate::db) fn canonicalize_grouped_having_bool_expr_artifact(
    expr: Expr,
    work: &PreparationWork<'_>,
) -> Result<CanonicalExpr, QueryError> {
    let expr = normalize_bool_expr(expr, work)?;
    debug_assert!(is_normalized_bool_expr(&expr));

    let expr = canonicalize_normalized_bool_case_in_bool_context(
        expr,
        false,
        Some(TruthWrapperScope::GroupedHaving),
        work,
    )?;
    let expr = normalize_bool_expr(expr, work)?;

    debug_assert!(is_normalized_bool_expr(&expr));

    Ok(CanonicalExpr::new(expr))
}

/// Canonicalize one grouped-HAVING boolean expression onto the bounded
/// searched-`CASE` boolean seam after the shared structural normalization pass
/// has already settled the planner-owned grouped tree shape.
pub(in crate::db) fn canonicalize_grouped_having_bool_expr(
    expr: Expr,
    work: &PreparationWork<'_>,
) -> Result<Expr, QueryError> {
    canonicalize_grouped_having_bool_expr_artifact(expr, work).map(CanonicalExpr::into_expr)
}