use std::any::type_name;
use std::fmt::Debug;
use std::marker::PhantomData;
use vortex_error::VortexResult;
use crate::plan::Plan;
use crate::plan::PlanRef;
use crate::plan::PlanVTable;
pub trait PlanReduceRule<P: PlanVTable>: Debug + Send + Sync + 'static {
fn reduce(&self, plan: &Plan<P>) -> VortexResult<Option<PlanRef>>;
}
pub trait DynPlanReduceRule: Debug + Send + Sync + 'static {
fn matches(&self, plan: &PlanRef) -> bool;
fn reduce(&self, plan: &PlanRef) -> VortexResult<Option<PlanRef>>;
}
pub struct PlanReduceRuleAdapter<P, R> {
rule: R,
_plan: PhantomData<fn() -> P>,
}
impl<P, R> PlanReduceRuleAdapter<P, R> {
pub const fn new(rule: R) -> Self {
Self {
rule,
_plan: PhantomData,
}
}
}
impl<P, R> Debug for PlanReduceRuleAdapter<P, R>
where
P: PlanVTable,
R: PlanReduceRule<P>,
{
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("PlanReduceRuleAdapter")
.field("plan", &type_name::<P>())
.field("rule", &self.rule)
.finish()
}
}
impl<P, R> DynPlanReduceRule for PlanReduceRuleAdapter<P, R>
where
P: PlanVTable,
R: PlanReduceRule<P>,
{
fn matches(&self, plan: &PlanRef) -> bool {
plan.is::<P>()
}
fn reduce(&self, plan: &PlanRef) -> VortexResult<Option<PlanRef>> {
let Some(plan) = plan.as_opt::<P>() else {
return Ok(None);
};
self.rule.reduce(plan)
}
}
pub struct PlanRuleSet {
rules: &'static [&'static dyn DynPlanReduceRule],
}
impl PlanRuleSet {
pub const fn new(rules: &'static [&'static dyn DynPlanReduceRule]) -> Self {
Self { rules }
}
pub fn evaluate(&self, plan: &PlanRef) -> VortexResult<Option<PlanRef>> {
for rule in self.rules {
if !rule.matches(plan) {
continue;
}
let Some(reduced) = rule.reduce(plan)? else {
continue;
};
#[cfg(debug_assertions)]
{
vortex_error::vortex_ensure!(
reduced.row_count() == plan.row_count(),
"Plan rewrite from {rule:?} changed row count from {} to {}",
plan.row_count(),
reduced.row_count()
);
vortex_error::vortex_ensure!(
reduced.dtype() == plan.dtype(),
"Plan rewrite from {rule:?} changed dtype from {} to {}",
plan.dtype(),
reduced.dtype()
);
}
return Ok(Some(reduced));
}
Ok(None)
}
}
pub trait PlanParentReduceRule<C: PlanVTable>: Debug + Send + Sync + 'static {
type Parent: PlanVTable;
fn reduce_parent(
&self,
child: &Plan<C>,
parent: &Plan<Self::Parent>,
child_idx: usize,
) -> VortexResult<Option<PlanRef>>;
}
pub trait DynPlanParentReduceRule: Debug + Send + Sync + 'static {
fn matches(&self, child: &PlanRef, parent: &PlanRef) -> bool;
fn reduce_parent(
&self,
child: &PlanRef,
parent: &PlanRef,
child_idx: usize,
) -> VortexResult<Option<PlanRef>>;
}
pub struct PlanParentReduceRuleAdapter<C, R> {
rule: R,
_child: PhantomData<fn() -> C>,
}
impl<C, R> PlanParentReduceRuleAdapter<C, R> {
pub const fn new(rule: R) -> Self {
Self {
rule,
_child: PhantomData,
}
}
}
impl<C, R> Debug for PlanParentReduceRuleAdapter<C, R>
where
C: PlanVTable,
R: PlanParentReduceRule<C>,
{
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("PlanParentReduceRuleAdapter")
.field("parent", &type_name::<R::Parent>())
.field("child", &type_name::<C>())
.field("rule", &self.rule)
.finish()
}
}
impl<C, R> DynPlanParentReduceRule for PlanParentReduceRuleAdapter<C, R>
where
C: PlanVTable,
R: PlanParentReduceRule<C>,
{
fn matches(&self, child: &PlanRef, parent: &PlanRef) -> bool {
child.is::<C>() && parent.is::<R::Parent>()
}
fn reduce_parent(
&self,
child: &PlanRef,
parent: &PlanRef,
child_idx: usize,
) -> VortexResult<Option<PlanRef>> {
let Some(child) = child.as_opt::<C>() else {
return Ok(None);
};
let Some(parent) = parent.as_opt::<R::Parent>() else {
return Ok(None);
};
self.rule.reduce_parent(child, parent, child_idx)
}
}
pub struct PlanParentRuleSet {
rules: &'static [&'static dyn DynPlanParentReduceRule],
}
impl PlanParentRuleSet {
pub const fn new(rules: &'static [&'static dyn DynPlanParentReduceRule]) -> Self {
Self { rules }
}
pub fn evaluate(
&self,
child: &PlanRef,
parent: &PlanRef,
child_idx: usize,
) -> VortexResult<Option<PlanRef>> {
for rule in self.rules {
if !rule.matches(child, parent) {
continue;
}
let Some(reduced) = rule.reduce_parent(child, parent, child_idx)? else {
continue;
};
#[cfg(debug_assertions)]
{
vortex_error::vortex_ensure!(
reduced.row_count() == parent.row_count(),
"Plan rewrite from {rule:?} changed row count from {} to {}",
parent.row_count(),
reduced.row_count()
);
vortex_error::vortex_ensure!(
reduced.dtype() == parent.dtype(),
"Plan rewrite from {rule:?} changed dtype from {} to {}",
parent.dtype(),
reduced.dtype()
);
}
return Ok(Some(reduced));
}
Ok(None)
}
}