use serde::Serialize;
use smol_str::SmolStr;

use super::{
    ArenaType, Delete, Except, GroupBy, Having, Insert, Intersect, Join, Limit, Motion,
    NodeAligned, NodeId, OrderBy, Projection, ScanCte, ScanRelation, ScanSubQuery,
    SelectWithoutScan, Selection, Union, UnionAll, Update, Values, ValuesRow,
};
use crate::{
    errors::{Entity, SbroadError},
    ir::api::children::{Children, MutChildren},
};

#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub enum RelOwned {
    ScanCte(ScanCte),
    Except(Except),
    Delete(Delete),
    Insert(Insert),
    Intersect(Intersect),
    Update(Update),
    Join(Join),
    Limit(Limit),
    Motion(Motion),
    Projection(Projection),
    ScanRelation(ScanRelation),
    ScanSubQuery(ScanSubQuery),
    Selection(Selection),
    SelectWithoutScan(SelectWithoutScan),
    GroupBy(GroupBy),
    Having(Having),
    OrderBy(OrderBy),
    UnionAll(UnionAll),
    Union(Union),
    Values(Values),
    ValuesRow(ValuesRow),
}

impl From<RelOwned> for NodeAligned {
    fn from(value: RelOwned) -> Self {
        match value {
            RelOwned::ScanCte(scan_cte) => scan_cte.into(),
            RelOwned::Delete(delete) => delete.into(),
            RelOwned::Except(except) => except.into(),
            RelOwned::GroupBy(group_by) => group_by.into(),
            RelOwned::Having(having) => having.into(),
            RelOwned::Insert(insert) => insert.into(),
            RelOwned::Intersect(intersect) => intersect.into(),
            RelOwned::Join(join) => join.into(),
            RelOwned::Limit(limit) => limit.into(),
            RelOwned::Motion(motion) => motion.into(),
            RelOwned::OrderBy(order_by) => order_by.into(),
            RelOwned::Projection(proj) => proj.into(),
            RelOwned::ScanRelation(scan_rel) => scan_rel.into(),
            RelOwned::ScanSubQuery(scan_squery) => scan_squery.into(),
            RelOwned::Selection(selection) => selection.into(),
            RelOwned::SelectWithoutScan(select) => select.into(),
            RelOwned::Union(un) => un.into(),
            RelOwned::UnionAll(union_all) => union_all.into(),
            RelOwned::Update(update) => update.into(),
            RelOwned::Values(values) => values.into(),
            RelOwned::ValuesRow(values_row) => values_row.into(),
        }
    }
}

impl RelOwned {
    pub fn has_output(&self) -> bool {
        !matches!(self, RelOwned::Delete(Delete { output: None, .. }))
    }

    pub fn arena_type(&self) -> ArenaType {
        match self {
            RelOwned::Union(_)
            | RelOwned::UnionAll(_)
            | RelOwned::Except(_)
            | RelOwned::Values(_)
            | RelOwned::Intersect(_)
            | RelOwned::Limit(_)
            | RelOwned::SelectWithoutScan(_) => ArenaType::Arena32,
            RelOwned::ScanCte(_)
            | RelOwned::Selection(_)
            | RelOwned::Having(_)
            | RelOwned::ValuesRow(_)
            | RelOwned::OrderBy(_)
            | RelOwned::Join(_)
            | RelOwned::Delete(_)
            | RelOwned::ScanSubQuery(_)
            | RelOwned::GroupBy(_) => ArenaType::Arena64,
            RelOwned::Insert(_) | RelOwned::Projection(_) | RelOwned::ScanRelation(_) => {
                ArenaType::Arena96
            }
            RelOwned::Update(_) | RelOwned::Motion(_) => ArenaType::Arena136,
        }
    }

    /// Sets new children to relational node.
    ///
    /// # Panics
    /// - wrong number of children for the given node
    pub fn set_children(&mut self, children: Vec<NodeId>) {
        match self {
            RelOwned::Values(Values {
                children: ref mut old,
                ..
            }) => {
                *old = children;
            }
            RelOwned::Except(Except { left, right, .. })
            | RelOwned::UnionAll(UnionAll { left, right, .. })
            | RelOwned::Intersect(Intersect { left, right, .. })
            | RelOwned::Union(Union { left, right, .. })
            | RelOwned::Join(Join { left, right, .. }) => {
                if children.len() != 2 {
                    unreachable!("Node has only two children!");
                }
                *left = children[0];
                *right = children[1];
            }
            RelOwned::Delete(Delete { ref mut child, .. })
            | RelOwned::Motion(Motion { ref mut child, .. }) => {
                if children.len() != 1 {
                    unreachable!("{self:?} may have only a single relational child");
                }
                // It is safe to unwrap here, because the length is already checked above.
                *child = Some(children[0]);
            }
            RelOwned::Update(Update { ref mut child, .. })
            | RelOwned::Insert(Insert { ref mut child, .. })
            | RelOwned::ScanSubQuery(ScanSubQuery { ref mut child, .. })
            | RelOwned::ScanCte(ScanCte { ref mut child, .. })
            | RelOwned::Limit(Limit { ref mut child, .. })
            | RelOwned::Selection(Selection { ref mut child, .. })
            | RelOwned::Having(Having { ref mut child, .. })
            | RelOwned::GroupBy(GroupBy { ref mut child, .. })
            | RelOwned::OrderBy(OrderBy { ref mut child, .. }) => {
                if children.len() != 1 {
                    unreachable!("{self:?} may have only a single relational child");
                }
                // It is safe to unwrap here, because the length is already checked above.
                *child = children[0];
            }
            RelOwned::SelectWithoutScan(SelectWithoutScan { .. })
            | RelOwned::ValuesRow(ValuesRow { .. })
            | RelOwned::ScanRelation(ScanRelation { .. }) => {
                assert!(children.is_empty(), "scan must have no children!");
            }
            RelOwned::Projection(Projection {
                child,
                group_by,
                having,
                ..
            }) => {
                if having.is_some() || group_by.is_some() {
                    assert!(
                        children.is_empty(),
                        "projection with having or group_by must have no children!"
                    );
                } else {
                    *child = Some(children[0]);
                }
            }
        }
    }

    // Gets an immutable reference to the children nodes.
    #[must_use]
    pub fn children(&self) -> Children<'_> {
        match self {
            RelOwned::Limit(Limit { child, .. })
            | RelOwned::Delete(Delete {
                child: Some(child), ..
            })
            | RelOwned::Update(Update { child, .. })
            | RelOwned::Insert(Insert { child, .. })
            | RelOwned::Motion(Motion {
                child: Some(child), ..
            })
            | RelOwned::ScanSubQuery(ScanSubQuery { child, .. })
            | RelOwned::ScanCte(ScanCte { child, .. })
            | RelOwned::Having(Having { child, .. })
            | RelOwned::Selection(Selection { child, .. })
            | RelOwned::GroupBy(GroupBy { child, .. })
            | RelOwned::OrderBy(OrderBy { child, .. }) => Children::Single(child),
            RelOwned::Except(Except { left, right, .. })
            | RelOwned::Intersect(Intersect { left, right, .. })
            | RelOwned::UnionAll(UnionAll { left, right, .. })
            | RelOwned::Union(Union { left, right, .. })
            | RelOwned::Join(Join { left, right, .. }) => Children::Couple(left, right),
            RelOwned::Values(Values { children, .. }) => Children::Many(children),
            RelOwned::Delete(Delete { child: None, .. })
            | RelOwned::Motion(Motion { child: None, .. })
            | RelOwned::ScanRelation(_)
            | RelOwned::SelectWithoutScan(_)
            | RelOwned::ValuesRow(_) => Children::None,

            RelOwned::Projection(Projection {
                child,
                having,
                group_by,
                ..
            }) => {
                if having.is_some() || group_by.is_some() {
                    Children::None
                } else if child.is_some() {
                    Children::Single(child.as_ref().unwrap())
                } else {
                    unreachable!("nothing was set")
                }
            }
        }
    }

    #[must_use]
    pub fn mut_children(&mut self) -> MutChildren<'_> {
        match self {
            RelOwned::Limit(Limit { ref mut child, .. })
            | RelOwned::ScanCte(ScanCte { ref mut child, .. })
            | RelOwned::Update(Update { ref mut child, .. })
            | RelOwned::Delete(Delete {
                child: Some(ref mut child),
                ..
            })
            | RelOwned::Motion(Motion {
                child: Some(ref mut child),
                ..
            })
            | RelOwned::Insert(Insert { ref mut child, .. })
            | RelOwned::ScanSubQuery(ScanSubQuery { ref mut child, .. })
            | RelOwned::Having(Having { ref mut child, .. })
            | RelOwned::Selection(Selection { ref mut child, .. })
            | RelOwned::GroupBy(GroupBy { ref mut child, .. })
            | RelOwned::OrderBy(OrderBy { ref mut child, .. }) => MutChildren::Single(child),
            RelOwned::Except(Except {
                ref mut left,
                ref mut right,
                ..
            })
            | RelOwned::Intersect(Intersect {
                ref mut left,
                ref mut right,
                ..
            })
            | RelOwned::UnionAll(UnionAll {
                ref mut left,
                ref mut right,
                ..
            })
            | RelOwned::Union(Union {
                ref mut left,
                ref mut right,
                ..
            })
            | RelOwned::Join(Join {
                ref mut left,
                ref mut right,
                ..
            }) => MutChildren::Couple(left, right),
            RelOwned::Values(Values {
                ref mut children, ..
            }) => MutChildren::Many(children),
            RelOwned::Delete(Delete { child: None, .. })
            | RelOwned::Motion(Motion { child: None, .. })
            | RelOwned::ScanRelation(_)
            | RelOwned::SelectWithoutScan(_)
            | RelOwned::ValuesRow(_) => MutChildren::None,
            RelOwned::Projection(Projection {
                ref mut child,
                group_by,
                having,
                ..
            }) => {
                if group_by.is_some() || having.is_some() {
                    MutChildren::None
                } else if child.is_some() {
                    MutChildren::Single(child.as_mut().unwrap())
                } else {
                    unreachable!("nothing was set")
                }
            }
        }
    }

    #[must_use]
    pub fn mut_subqueries(&mut self) -> MutChildren<'_> {
        match self {
            RelOwned::Join(Join {
                ref mut subqueries, ..
            })
            | RelOwned::Having(Having {
                ref mut subqueries, ..
            })
            | RelOwned::Selection(Selection {
                ref mut subqueries, ..
            })
            | RelOwned::SelectWithoutScan(SelectWithoutScan {
                ref mut subqueries, ..
            })
            | RelOwned::ValuesRow(ValuesRow {
                ref mut subqueries, ..
            })
            | RelOwned::GroupBy(GroupBy {
                ref mut subqueries, ..
            })
            | RelOwned::OrderBy(OrderBy {
                ref mut subqueries, ..
            })
            | RelOwned::Projection(Projection {
                ref mut subqueries, ..
            }) => MutChildren::Many(subqueries),
            _ => MutChildren::None,
        }
    }

    /// Gets an mutable reference to the output tuple node id.
    #[must_use]
    pub fn mut_output(&mut self) -> &mut NodeId {
        match self {
            RelOwned::Delete(Delete { output, .. }) => output
                .as_mut()
                .expect("DELETE without WHERE clause doesn't have an output."),
            RelOwned::ScanCte(ScanCte { output, .. })
            | RelOwned::Except(Except { output, .. })
            | RelOwned::GroupBy(GroupBy { output, .. })
            | RelOwned::OrderBy(OrderBy { output, .. })
            | RelOwned::Update(Update { output, .. })
            | RelOwned::Having(Having { output, .. })
            | RelOwned::Join(Join { output, .. })
            | RelOwned::Limit(Limit { output, .. })
            | RelOwned::Insert(Insert { output, .. })
            | RelOwned::Intersect(Intersect { output, .. })
            | RelOwned::Motion(Motion { output, .. })
            | RelOwned::Projection(Projection { output, .. })
            | RelOwned::ScanRelation(ScanRelation { output, .. })
            | RelOwned::ScanSubQuery(ScanSubQuery { output, .. })
            | RelOwned::Selection(Selection { output, .. })
            | RelOwned::SelectWithoutScan(SelectWithoutScan { output, .. })
            | RelOwned::Union(Union { output, .. })
            | RelOwned::UnionAll(UnionAll { output, .. })
            | RelOwned::Values(Values { output, .. })
            | RelOwned::ValuesRow(ValuesRow { output, .. }) => output,
        }
    }
}

/// Relational algebra operator returning a new tuple.
///
/// Transforms input tuple(s) into the output one using the
/// relation algebra logic.
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub enum Relational<'a> {
    ScanCte(&'a ScanCte),
    Except(&'a Except),
    Delete(&'a Delete),
    Insert(&'a Insert),
    Intersect(&'a Intersect),
    Update(&'a Update),
    Join(&'a Join),
    Limit(&'a Limit),
    Motion(&'a Motion),
    Projection(&'a Projection),
    ScanRelation(&'a ScanRelation),
    ScanSubQuery(&'a ScanSubQuery),
    Selection(&'a Selection),
    SelectWithoutScan(&'a SelectWithoutScan),
    GroupBy(&'a GroupBy),
    Having(&'a Having),
    OrderBy(&'a OrderBy),
    UnionAll(&'a UnionAll),
    Union(&'a Union),
    Values(&'a Values),
    ValuesRow(&'a ValuesRow),
}

#[allow(clippy::module_name_repetitions)]
#[derive(Debug, PartialEq, Eq, Serialize)]
pub enum MutRelational<'a> {
    ScanCte(&'a mut ScanCte),
    Except(&'a mut Except),
    Delete(&'a mut Delete),
    Insert(&'a mut Insert),
    Intersect(&'a mut Intersect),
    Update(&'a mut Update),
    Join(&'a mut Join),
    Limit(&'a mut Limit),
    Motion(&'a mut Motion),
    Projection(&'a mut Projection),
    ScanRelation(&'a mut ScanRelation),
    ScanSubQuery(&'a mut ScanSubQuery),
    Selection(&'a mut Selection),
    SelectWithoutScan(&'a mut SelectWithoutScan),
    GroupBy(&'a mut GroupBy),
    Having(&'a mut Having),
    OrderBy(&'a mut OrderBy),
    UnionAll(&'a mut UnionAll),
    Union(&'a mut Union),
    Values(&'a mut Values),
    ValuesRow(&'a mut ValuesRow),
}

impl MutRelational<'_> {
    /// Gets an mutable reference to the output tuple node id.
    #[must_use]
    pub fn mut_output(&mut self) -> &mut NodeId {
        match self {
            MutRelational::Delete(Delete { output, .. }) => output
                .as_mut()
                .expect("DELETE without WHERE clause doesn't have an output."),
            MutRelational::ScanCte(ScanCte { output, .. })
            | MutRelational::Except(Except { output, .. })
            | MutRelational::GroupBy(GroupBy { output, .. })
            | MutRelational::OrderBy(OrderBy { output, .. })
            | MutRelational::Update(Update { output, .. })
            | MutRelational::Having(Having { output, .. })
            | MutRelational::Join(Join { output, .. })
            | MutRelational::Limit(Limit { output, .. })
            | MutRelational::Insert(Insert { output, .. })
            | MutRelational::Intersect(Intersect { output, .. })
            | MutRelational::Motion(Motion { output, .. })
            | MutRelational::Projection(Projection { output, .. })
            | MutRelational::ScanRelation(ScanRelation { output, .. })
            | MutRelational::ScanSubQuery(ScanSubQuery { output, .. })
            | MutRelational::Selection(Selection { output, .. })
            | MutRelational::SelectWithoutScan(SelectWithoutScan { output, .. })
            | MutRelational::Union(Union { output, .. })
            | MutRelational::UnionAll(UnionAll { output, .. })
            | MutRelational::Values(Values { output, .. })
            | MutRelational::ValuesRow(ValuesRow { output, .. }) => output,
        }
    }

    // Gets a mutable reference to the children nodes.
    #[must_use]
    pub fn mut_children(&mut self) -> MutChildren<'_> {
        // return MutChildren { node: self };
        match self {
            MutRelational::Limit(Limit { child, .. })
            | MutRelational::ScanCte(ScanCte { child, .. })
            | MutRelational::Update(Update { child, .. })
            | MutRelational::Delete(Delete {
                child: Some(child), ..
            })
            | MutRelational::Motion(Motion {
                child: Some(child), ..
            })
            | MutRelational::Insert(Insert { child, .. })
            | MutRelational::ScanSubQuery(ScanSubQuery { child, .. })
            | MutRelational::Selection(Selection { child, .. })
            | MutRelational::Having(Having { child, .. })
            | MutRelational::GroupBy(GroupBy { child, .. })
            | MutRelational::OrderBy(OrderBy { child, .. }) => MutChildren::Single(child),
            MutRelational::Except(Except { left, right, .. })
            | MutRelational::Intersect(Intersect { left, right, .. })
            | MutRelational::UnionAll(UnionAll { left, right, .. })
            | MutRelational::Union(Union { left, right, .. })
            | MutRelational::Join(Join { left, right, .. }) => MutChildren::Couple(left, right),
            MutRelational::Values(Values {
                ref mut children, ..
            }) => MutChildren::Many(children),
            MutRelational::Delete(Delete { child: None, .. })
            | MutRelational::Motion(Motion { child: None, .. })
            | MutRelational::ScanRelation(_)
            | MutRelational::SelectWithoutScan(_)
            | MutRelational::ValuesRow(_) => MutChildren::None,
            MutRelational::Projection(Projection {
                child,
                group_by,
                having,
                ..
            }) => {
                if group_by.is_some() || having.is_some() {
                    MutChildren::None
                } else if child.is_some() {
                    MutChildren::Single(child.as_mut().unwrap())
                } else {
                    unreachable!("nothing was set")
                }
            }
        }
    }

    /// Sets new children to relational node.
    ///
    /// # Panics
    /// - wrong number of children for the given node
    pub fn set_children(&mut self, children: Vec<NodeId>) {
        match self {
            MutRelational::Values(Values {
                children: ref mut old,
                ..
            }) => {
                *old = children;
            }
            MutRelational::Except(Except { left, right, .. })
            | MutRelational::UnionAll(UnionAll { left, right, .. })
            | MutRelational::Intersect(Intersect { left, right, .. })
            | MutRelational::Union(Union { left, right, .. })
            | MutRelational::Join(Join { left, right, .. }) => {
                if children.len() != 2 {
                    unreachable!("Node has only two children!");
                }
                *left = children[0];
                *right = children[1];
            }
            MutRelational::ScanCte(ScanCte { child, .. })
            | MutRelational::Limit(Limit { child, .. })
            | MutRelational::Update(Update { child, .. })
            | MutRelational::Insert(Insert { child, .. })
            | MutRelational::ScanSubQuery(ScanSubQuery { child, .. })
            | MutRelational::Having(Having { child, .. })
            | MutRelational::Selection(Selection { child, .. })
            | MutRelational::GroupBy(GroupBy { child, .. })
            | MutRelational::OrderBy(OrderBy { child, .. }) => {
                *child = children[0];
            }
            MutRelational::Delete(Delete { child, .. })
            | MutRelational::Motion(Motion { child, .. }) => {
                *child = Some(children[0]);
            }
            MutRelational::SelectWithoutScan(SelectWithoutScan { .. })
            | MutRelational::ValuesRow(ValuesRow { .. })
            | MutRelational::ScanRelation(ScanRelation { .. }) => {
                assert!(children.is_empty(), "scan must have no children!");
            }
            MutRelational::Projection(Projection {
                child,
                having,
                group_by,
                ..
            }) => {
                if having.is_some() || group_by.is_some() {
                    assert!(
                        children.is_empty(),
                        "projection with group_by or having must have no children!"
                    );
                } else {
                    *child = Some(children[0]);
                }
            }
        }
    }

    pub fn set_subqueries(&mut self, subqueries: Vec<NodeId>) {
        match self {
            MutRelational::Join(Join {
                subqueries: ref mut old,
                ..
            })
            | MutRelational::Having(Having {
                subqueries: ref mut old,
                ..
            })
            | MutRelational::Selection(Selection {
                subqueries: ref mut old,
                ..
            })
            | MutRelational::SelectWithoutScan(SelectWithoutScan {
                subqueries: ref mut old,
                ..
            })
            | MutRelational::ValuesRow(ValuesRow {
                subqueries: ref mut old,
                ..
            })
            | MutRelational::OrderBy(OrderBy {
                subqueries: ref mut old,
                ..
            })
            | MutRelational::GroupBy(GroupBy {
                subqueries: ref mut old,
                ..
            })
            | MutRelational::Projection(Projection {
                subqueries: ref mut old,
                ..
            }) => *old = subqueries,

            _ => unreachable!("Node has no subqueries!"),
        }
    }

    /// Add `SubQuery` to the list of relational children.
    ///
    /// # Panics
    /// - Trying to add subquery to inapplicable relational node.
    pub fn add_sq_child(&mut self, sq_id: NodeId) {
        match self {
            MutRelational::Join(Join { subqueries, .. })
            | MutRelational::Selection(Selection { subqueries, .. })
            | MutRelational::Having(Having { subqueries, .. })
            | MutRelational::SelectWithoutScan(SelectWithoutScan { subqueries, .. })
            | MutRelational::ValuesRow(ValuesRow { subqueries, .. })
            | MutRelational::GroupBy(GroupBy { subqueries, .. })
            | MutRelational::OrderBy(OrderBy { subqueries, .. })
            | MutRelational::Projection(Projection { subqueries, .. }) => subqueries.push(sq_id),
            _ => panic!("Unable to add SubQuery child to {self:?}."),
        }
    }

    /// Sets new scan name to relational node.
    ///
    /// # Errors
    /// - relational node is not a scan.
    ///
    /// # Panics
    /// - CTE must have a name.
    pub fn set_scan_name(&mut self, name: Option<SmolStr>) -> Result<(), SbroadError> {
        match self {
            MutRelational::ScanRelation(ScanRelation { ref mut alias, .. })
            | MutRelational::ScanSubQuery(ScanSubQuery { ref mut alias, .. }) => {
                *alias = name;
                Ok(())
            }
            MutRelational::ScanCte(ScanCte { ref mut alias, .. }) => {
                let name = name.expect("CTE must have a name");
                *alias = name;
                Ok(())
            }
            _ => Err(SbroadError::Invalid(
                Entity::Relational,
                Some("Relational node is not a Scan.".into()),
            )),
        }
    }
}

#[allow(dead_code)]
impl Relational<'_> {
    pub fn has_output(&self) -> bool {
        !matches!(self, Relational::Delete(Delete { output: None, .. }))
    }

    /// Gets an immutable id of the output tuple node of the plan's arena.
    #[must_use]
    pub fn output(&self) -> NodeId {
        match self {
            Relational::Delete(Delete { output, .. }) => {
                output.expect("DELETE without WHERE clause doesn't have an output.")
            }
            Relational::ScanCte(ScanCte { output, .. })
            | Relational::Except(Except { output, .. })
            | Relational::GroupBy(GroupBy { output, .. })
            | Relational::OrderBy(OrderBy { output, .. })
            | Relational::Having(Having { output, .. })
            | Relational::Update(Update { output, .. })
            | Relational::Limit(Limit { output, .. })
            | Relational::Join(Join { output, .. })
            | Relational::Insert(Insert { output, .. })
            | Relational::Intersect(Intersect { output, .. })
            | Relational::Motion(Motion { output, .. })
            | Relational::Projection(Projection { output, .. })
            | Relational::ScanRelation(ScanRelation { output, .. })
            | Relational::ScanSubQuery(ScanSubQuery { output, .. })
            | Relational::Selection(Selection { output, .. })
            | Relational::SelectWithoutScan(SelectWithoutScan { output, .. })
            | Relational::Union(Union { output, .. })
            | Relational::UnionAll(UnionAll { output, .. })
            | Relational::Values(Values { output, .. })
            | Relational::ValuesRow(ValuesRow { output, .. }) => *output,
        }
    }

    // Gets an immutable reference to the children nodes.
    #[must_use]
    pub fn children(&self) -> Children<'_> {
        match self {
            Relational::Limit(Limit { child, .. })
            | Relational::ScanSubQuery(ScanSubQuery { child, .. })
            | Relational::Update(Update { child, .. })
            | Relational::Delete(Delete {
                child: Some(child), ..
            })
            | Relational::Motion(Motion {
                child: Some(child), ..
            })
            | Relational::Insert(Insert { child, .. })
            | Relational::ScanCte(ScanCte { child, .. })
            | Relational::Having(Having { child, .. })
            | Relational::Selection(Selection { child, .. })
            | Relational::GroupBy(GroupBy { child, .. })
            | Relational::OrderBy(OrderBy { child, .. }) => Children::Single(child),
            Relational::Except(Except { left, right, .. })
            | Relational::Intersect(Intersect { left, right, .. })
            | Relational::UnionAll(UnionAll { left, right, .. })
            | Relational::Union(Union { left, right, .. })
            | Relational::Join(Join { left, right, .. }) => Children::Couple(left, right),
            Relational::Values(Values { children, .. }) => Children::Many(children),
            Relational::Delete(Delete { child: None, .. })
            | Relational::Motion(Motion { child: None, .. })
            | Relational::ScanRelation(_)
            | Relational::SelectWithoutScan(SelectWithoutScan { .. })
            | Relational::ValuesRow(ValuesRow { .. }) => Children::None,
            Relational::Projection(Projection {
                child,
                having,
                group_by,
                ..
            }) => {
                if having.is_some() || group_by.is_some() {
                    Children::None
                } else if child.is_some() {
                    Children::Single(child.as_ref().unwrap())
                } else {
                    unreachable!("nothing was set")
                }
            }
        }
    }

    pub fn subqueries(&self) -> Children<'_> {
        match self {
            Relational::Join(Join { subqueries, .. })
            | Relational::Having(Having { subqueries, .. })
            | Relational::Selection(Selection { subqueries, .. })
            | Relational::SelectWithoutScan(SelectWithoutScan { subqueries, .. })
            | Relational::ValuesRow(ValuesRow { subqueries, .. })
            | Relational::GroupBy(GroupBy { subqueries, .. })
            | Relational::OrderBy(OrderBy { subqueries, .. })
            | Relational::Projection(Projection { subqueries, .. }) => Children::Many(subqueries),
            _ => Children::None,
        }
    }

    pub fn children_len(&self) -> usize {
        if matches!(
            self,
            Relational::Projection(Projection {
                group_by: Some(_),
                ..
            }) | Relational::Projection(Projection {
                having: Some(_),
                ..
            })
        ) {
            self.children().len() + 1
        } else {
            self.children().len()
        }
    }

    /// Checks if the node is deletion.
    #[must_use]
    pub fn is_delete(&self) -> bool {
        matches!(self, Relational::Delete { .. })
    }
    /// Checks if the node is an insertion.
    #[must_use]
    pub fn is_insert(&self) -> bool {
        matches!(self, Relational::Insert { .. })
    }

    /// Checks if the node is dml node
    #[must_use]
    pub fn is_dml(&self) -> bool {
        matches!(
            self,
            Relational::Insert { .. } | Relational::Update { .. } | Relational::Delete { .. }
        )
    }

    /// Checks that the node is a motion.
    #[must_use]
    pub fn is_motion(&self) -> bool {
        matches!(self, &Relational::Motion { .. })
    }

    /// Checks that the node is a local motion.
    /// Such motions are not dispatched by executor
    #[must_use]
    pub fn is_local_motion(&self) -> bool {
        use crate::ir::MotionPolicy;

        matches!(
            self,
            &Relational::Motion(Motion {
                policy: MotionPolicy::Local | MotionPolicy::LocalSegment(_),
                ..
            })
        )
    }

    #[must_use]
    pub fn is_non_local_motion(&self) -> bool {
        self.is_motion() && !self.is_local_motion()
    }

    /// Checks that the node is a sub-query or CTE scan.
    #[must_use]
    pub fn is_subquery_or_cte(&self) -> bool {
        matches!(
            self,
            &Relational::ScanSubQuery { .. } | &Relational::ScanCte { .. }
        )
    }

    #[must_use]
    pub fn name(&self) -> &str {
        match self {
            Relational::Except { .. } => "Except",
            Relational::Delete { .. } => "Delete",
            Relational::Insert { .. } => "Insert",
            Relational::Intersect { .. } => "Intersect",
            Relational::Update { .. } => "Update",
            Relational::Join { .. } => "Join",
            Relational::Limit { .. } => "Limit",
            Relational::Motion { .. } => "Motion",
            Relational::Projection { .. } => "Projection",
            Relational::ScanCte { .. } => "CTE",
            Relational::ScanRelation { .. } => "Scan",
            Relational::ScanSubQuery { .. } => "Subquery",
            Relational::Selection { .. } => "Selection",
            Relational::SelectWithoutScan { .. } => "SelectWithoutScan",
            Relational::GroupBy { .. } => "GroupBy",
            Relational::OrderBy { .. } => "OrderBy",
            Relational::Having { .. } => "Having",
            Relational::Union { .. } => "Union",
            Relational::UnionAll { .. } => "UnionAll",
            Relational::Values { .. } => "Values",
            Relational::ValuesRow { .. } => "ValuesRow",
        }
    }

    #[must_use]
    pub fn get_rel_owned(&self) -> RelOwned {
        match self {
            Relational::Delete(del) => RelOwned::Delete((*del).clone()),
            Relational::Except(except) => RelOwned::Except((*except).clone()),
            Relational::GroupBy(group_by) => RelOwned::GroupBy((*group_by).clone()),
            Relational::Having(having) => RelOwned::Having((*having).clone()),
            Relational::Insert(insert) => RelOwned::Insert((*insert).clone()),
            Relational::Intersect(intersect) => RelOwned::Intersect((*intersect).clone()),
            Relational::Join(join) => RelOwned::Join((*join).clone()),
            Relational::Limit(join) => RelOwned::Limit((*join).clone()),
            Relational::Motion(motion) => RelOwned::Motion((*motion).clone()),
            Relational::OrderBy(order_by) => RelOwned::OrderBy((*order_by).clone()),
            Relational::Projection(proj) => RelOwned::Projection((*proj).clone()),
            Relational::ScanCte(scan_cte) => RelOwned::ScanCte((*scan_cte).clone()),
            Relational::ScanRelation(scan_rel) => RelOwned::ScanRelation((*scan_rel).clone()),
            Relational::ScanSubQuery(ssubquery) => RelOwned::ScanSubQuery((*ssubquery).clone()),
            Relational::Selection(sel) => RelOwned::Selection((*sel).clone()),
            Relational::SelectWithoutScan(sel) => RelOwned::SelectWithoutScan((*sel).clone()),
            Relational::Union(un) => RelOwned::Union((*un).clone()),
            Relational::UnionAll(union_all) => RelOwned::UnionAll((*union_all).clone()),
            Relational::Update(upd) => RelOwned::Update((*upd).clone()),
            Relational::Values(values) => RelOwned::Values((*values).clone()),
            Relational::ValuesRow(values_row) => RelOwned::ValuesRow((*values_row).clone()),
        }
    }
}
