use rudb_common::Result;
use rudb_plan::{Node, NodeRef, Plan};
use crate::pass::{Context, Pass};
use crate::walk::restack;
#[derive(Debug, Clone, Copy)]
pub struct UnusedMaterialization;
impl Pass for UnusedMaterialization {
fn name(&self) -> &'static str {
"materialized_cte"
}
fn run(&self, plan: &mut Plan, _context: &Context) -> Result<()> {
drop_unread(plan);
Ok(())
}
}
pub fn drop_unread(plan: &mut Plan) {
let mut changed = false;
let root = restack(plan, plan.root(), &mut changed, &mut |plan, at| {
let Node::MaterializedCte { body, cte, .. } = *plan.node(at) else { return None };
if reads(plan, body, cte) {
return None;
}
Some(body)
});
plan.set_root(root);
}
fn reads(plan: &Plan, at: NodeRef, cte: u32) -> bool {
if let Node::CteScan { cte: read, .. } = *plan.node(at) {
return read == cte;
}
plan.node(at).children().into_iter().flatten().any(|child| reads(plan, child, cte))
}
#[cfg(test)]
mod tests {
use rudb_plan::Plan;
use super::drop_unread;
fn dropped(text: &str) -> String {
let mut plan = Plan::parse(text).expect("a plan the reader accepts");
drop_unread(&mut plan);
plan.to_string()
}
#[test]
fn a_materialisation_the_body_reads_stays() {
let text = dropped(
"MaterializedCte c @0 [n::INTEGER]\n \
Values #0 [n::INTEGER] rows=[[1::INTEGER]]\n \
Project #2 [#1.0::INTEGER AS n]\n \
CteScan c @0 #1 [n::INTEGER]\n",
);
assert!(text.contains("MaterializedCte c @0"), "{text}");
assert!(text.contains("CteScan c @0"), "{text}");
}
#[test]
fn a_materialisation_nothing_reads_goes_and_takes_its_definition_with_it() {
let text = dropped(
"MaterializedCte c @0 [n::INTEGER]\n \
Values #0 [n::INTEGER] rows=[[1::INTEGER]]\n \
Project #2 [2::INTEGER AS two]\n \
Dummy\n",
);
assert_eq!(text, "Project #2 [2::INTEGER AS two]\n Dummy\n", "{text}");
}
#[test]
fn one_run_drops_a_pair_where_the_only_read_was_in_the_other_definition() {
let text = dropped(
"MaterializedCte a @0 [n::INTEGER]\n \
Values #0 [n::INTEGER] rows=[[1::INTEGER]]\n \
MaterializedCte b @1 [n::INTEGER]\n \
Project #3 [#2.0::INTEGER AS n]\n \
CteScan a @0 #2 [n::INTEGER]\n \
Project #4 [2::INTEGER AS two]\n \
Dummy\n",
);
assert_eq!(text, "Project #4 [2::INTEGER AS two]\n Dummy\n", "{text}");
}
}