graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
use super::{engine::Session, rule::Transformed};
use crate::Operand;
pub use graphrecords_macros::{
    MatchInputs, OperationInputs, OptimizePlan, OptimizerHints, PlanIdentity, PlanInputs, PlanNode,
};
use std::{
    any::Any,
    hash::{Hash, Hasher},
};

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum EmptyRule {
    Never,
    IfAnyInput,
    IfAllInputs,
}

pub trait OptimizerHints {
    fn commutes_with_filter(&self) -> bool {
        false
    }

    fn allows_limit_pushdown(&self) -> bool {
        false
    }

    fn is_volatile(&self) -> bool {
        false
    }

    fn empty_rule(&self) -> EmptyRule {
        EmptyRule::Never
    }
}

#[diagnostic::on_unimplemented(
    message = "`{Self}` is not a plan node",
    note = "implement `PlanNode` for `{Self}` or derive it with `#[derive(PlanNode)]`"
)]
pub trait PlanNode: Any + OptimizerHints {
    fn inputs(&self) -> Vec<&dyn PlanNode> {
        Vec::new()
    }

    fn contains_volatile(&self) -> bool {
        self.is_volatile() || self.inputs().into_iter().any(PlanNode::contains_volatile)
    }

    #[allow(unused_variables)]
    fn dyn_eq(&self, other: &dyn PlanNode) -> bool {
        false
    }

    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
        self.type_id().hash(&mut state);
    }
}

impl dyn PlanNode {
    pub fn downcast<T: PlanNode>(&self) -> Option<&T> {
        (self as &dyn Any).downcast_ref()
    }
}

#[diagnostic::on_unimplemented(
    message = "`{Self}` does not expose its inputs",
    note = "implement `MatchInputs` for `{Self}` or derive it with `#[derive(MatchInputs)]`"
)]
pub trait MatchInputs {
    type Inputs<'a>
    where
        Self: 'a;

    fn inputs(&self) -> Self::Inputs<'_>;
}

#[diagnostic::on_unimplemented(
    message = "`{Self}` cannot produce its operand",
    note = "implement `OptimizePlan` for `{Self}` or derive it with `#[derive(OptimizePlan)]`",
    note = "the derive requires a `#[plan(operand = ...)]` attribute"
)]
pub trait OptimizePlan {
    type Output: Operand;

    fn optimize(&self, original: &Self::Output, session: &Session) -> Transformed<Self::Output>;
}

pub trait PlanIdentity {
    fn identity_eq(&self, other: &Self) -> bool;

    fn identity_hash<H: Hasher>(&self, state: &mut H);
}

impl<T: Operand> PlanIdentity for T {
    fn identity_eq(&self, other: &Self) -> bool {
        self.as_plan_node().dyn_eq(other.as_plan_node())
    }

    fn identity_hash<H: Hasher>(&self, state: &mut H) {
        self.as_plan_node().dyn_hash(state);
    }
}

pub trait PlanInputs: Clone {
    fn inputs(&self) -> Vec<&dyn PlanNode> {
        Vec::new()
    }

    #[allow(unused_variables)]
    fn optimize(&self, session: &Session) -> Transformed<Self> {
        Transformed::unchanged(self.clone())
    }
}

impl<T: Operand> PlanInputs for T {
    fn inputs(&self) -> Vec<&dyn PlanNode> {
        vec![self.as_plan_node()]
    }

    fn optimize(&self, session: &Session) -> Transformed<Self> {
        session.optimize(self)
    }
}

pub trait OperationInputs: 'static + PlanIdentity + PlanInputs + OptimizerHints {
    type Inputs<'a, I: 'a>;

    fn inputs<'a, I: 'a>(&'a self, primary: &'a I) -> Self::Inputs<'a, I>;
}