use compact_str::CompactString;
use crate::api_request::range::Range;
use crate::api_request::types::{Alias, FieldName, Hint, JoinType};
use crate::schema_cache::relationship::AnyRelationship;
use crate::types::identifiers::QualifiedIdentifier;
use super::types::*;
pub type NodeName = CompactString;
#[derive(Debug, Clone)]
pub struct ReadPlanTree {
pub node: ReadPlan,
pub forest: Vec<ReadPlanTree>,
}
impl ReadPlanTree {
pub fn leaf(node: ReadPlan) -> Self {
Self {
node,
forest: Vec::new(),
}
}
pub fn with_children(node: ReadPlan, forest: Vec<ReadPlanTree>) -> Self {
Self { node, forest }
}
pub fn node_mut(&mut self) -> &mut ReadPlan {
&mut self.node
}
pub fn children(&self) -> &[ReadPlanTree] {
&self.forest
}
pub fn children_mut(&mut self) -> &mut Vec<ReadPlanTree> {
&mut self.forest
}
pub fn iter(&self) -> ReadPlanTreeIter<'_> {
ReadPlanTreeIter { stack: vec![self] }
}
pub fn node_count(&self) -> usize {
1 + self.forest.iter().map(|c| c.node_count()).sum::<usize>()
}
pub fn max_depth(&self) -> usize {
if self.forest.is_empty() {
self.node.depth
} else {
self.forest.iter().map(|c| c.max_depth()).max().unwrap_or(0)
}
}
}
pub struct ReadPlanTreeIter<'a> {
stack: Vec<&'a ReadPlanTree>,
}
impl<'a> Iterator for ReadPlanTreeIter<'a> {
type Item = &'a ReadPlan;
fn next(&mut self) -> Option<Self::Item> {
let tree = self.stack.pop()?;
for child in tree.forest.iter().rev() {
self.stack.push(child);
}
Some(&tree.node)
}
}
#[derive(Debug, Clone)]
pub struct JoinCondition {
pub parent: (QualifiedIdentifier, FieldName),
pub child: (QualifiedIdentifier, FieldName),
}
#[derive(Debug, Clone)]
pub struct ReadPlan {
pub select: Vec<CoercibleSelectField>,
pub from: QualifiedIdentifier,
pub from_alias: Option<Alias>,
pub where_: Vec<CoercibleLogicTree>,
pub order: Vec<CoercibleOrderTerm>,
pub range: Range,
pub rel_name: NodeName,
pub rel_to_parent: Option<AnyRelationship>,
pub rel_join_conds: Vec<JoinCondition>,
pub rel_alias: Option<Alias>,
pub rel_agg_alias: Alias,
pub rel_hint: Option<Hint>,
pub rel_join_type: Option<JoinType>,
pub rel_spread: Option<SpreadType>,
pub rel_select: Vec<RelSelectField>,
pub depth: usize,
}
impl ReadPlan {
pub fn root(qi: QualifiedIdentifier) -> Self {
let name = qi.name.clone();
Self {
select: Vec::new(),
from: qi,
from_alias: None,
where_: Vec::new(),
order: Vec::new(),
range: Range::all(),
rel_name: name,
rel_to_parent: None,
rel_join_conds: Vec::new(),
rel_alias: None,
rel_agg_alias: CompactString::from("dbrst_agg"),
rel_hint: None,
rel_join_type: None,
rel_spread: None,
rel_select: Vec::new(),
depth: 0,
}
}
pub fn child(qi: QualifiedIdentifier, rel_name: NodeName, depth: usize) -> Self {
Self {
select: Vec::new(),
from: qi,
from_alias: None,
where_: Vec::new(),
order: Vec::new(),
range: Range::all(),
rel_name,
rel_to_parent: None,
rel_join_conds: Vec::new(),
rel_alias: None,
rel_agg_alias: CompactString::from(format!("dbrst_agg_{}", depth)),
rel_hint: None,
rel_join_type: None,
rel_spread: None,
rel_select: Vec::new(),
depth,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_qi(name: &str) -> QualifiedIdentifier {
QualifiedIdentifier::new("public", name)
}
#[test]
fn test_read_plan_root() {
let plan = ReadPlan::root(test_qi("users"));
assert_eq!(plan.from.name.as_str(), "users");
assert_eq!(plan.depth, 0);
assert!(plan.rel_to_parent.is_none());
assert_eq!(plan.rel_name.as_str(), "users");
}
#[test]
fn test_read_plan_child() {
let plan = ReadPlan::child(test_qi("posts"), "posts".into(), 1);
assert_eq!(plan.depth, 1);
assert_eq!(plan.rel_agg_alias.as_str(), "dbrst_agg_1");
}
#[test]
fn test_read_plan_tree_leaf() {
let tree = ReadPlanTree::leaf(ReadPlan::root(test_qi("users")));
assert_eq!(tree.node_count(), 1);
assert!(tree.children().is_empty());
}
#[test]
fn test_read_plan_tree_with_children() {
let root = ReadPlan::root(test_qi("users"));
let child1 = ReadPlanTree::leaf(ReadPlan::child(test_qi("posts"), "posts".into(), 1));
let child2 = ReadPlanTree::leaf(ReadPlan::child(test_qi("comments"), "comments".into(), 1));
let tree = ReadPlanTree::with_children(root, vec![child1, child2]);
assert_eq!(tree.node_count(), 3);
assert_eq!(tree.children().len(), 2);
}
#[test]
fn test_read_plan_tree_nested() {
let grandchild = ReadPlanTree::leaf(ReadPlan::child(test_qi("tags"), "tags".into(), 2));
let child = ReadPlanTree::with_children(
ReadPlan::child(test_qi("posts"), "posts".into(), 1),
vec![grandchild],
);
let tree = ReadPlanTree::with_children(ReadPlan::root(test_qi("users")), vec![child]);
assert_eq!(tree.node_count(), 3);
assert_eq!(tree.max_depth(), 2);
}
#[test]
fn test_read_plan_tree_iter() {
let child = ReadPlanTree::leaf(ReadPlan::child(test_qi("posts"), "posts".into(), 1));
let tree = ReadPlanTree::with_children(ReadPlan::root(test_qi("users")), vec![child]);
let names: Vec<&str> = tree.iter().map(|p| p.rel_name.as_str()).collect();
assert_eq!(names, vec!["users", "posts"]);
}
#[test]
fn test_join_condition() {
let jc = JoinCondition {
parent: (test_qi("users"), "id".into()),
child: (test_qi("posts"), "user_id".into()),
};
assert_eq!(jc.parent.1.as_str(), "id");
assert_eq!(jc.child.1.as_str(), "user_id");
}
}