1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
mod filter;
mod group_by;
mod having;
mod join;
mod limit;
mod limit_offset;
mod offset;
mod offset_limit;
mod order_by;
mod project;
mod root;

pub use {
    filter::FilterNode,
    group_by::GroupByNode,
    having::HavingNode,
    join::{HashJoinNode, JoinConstraintNode, JoinNode},
    limit::LimitNode,
    limit_offset::LimitOffsetNode,
    offset::OffsetNode,
    offset_limit::OffsetLimitNode,
    order_by::OrderByNode,
    project::ProjectNode,
    root::SelectNode,
};

use {
    super::Build,
    crate::{
        ast::{
            Expr, Join, OrderByExpr, Query, Select, SelectItem, SetExpr, Statement, TableFactor,
            TableWithJoins,
        },
        result::Result,
    },
};

pub trait Prebuild {
    fn prebuild(self) -> Result<NodeData>;
}

#[derive(Clone)]
pub struct NodeData {
    pub projection: Vec<SelectItem>,
    pub relation: TableFactor,
    pub joins: Vec<Join>,
    pub filter: Option<Expr>,
    /// WHERE
    pub group_by: Vec<Expr>,
    pub having: Option<Expr>,
    pub order_by: Vec<OrderByExpr>,
    pub limit: Option<Expr>,
    pub offset: Option<Expr>,
}

impl NodeData {
    pub fn build_query(self) -> Query {
        let NodeData {
            projection,
            relation,
            group_by,
            having,
            order_by,
            offset,
            limit,
            joins,
            filter,
        } = self;

        let selection = filter.map(Expr::try_from).and_then(|expr| expr.ok());
        let from = TableWithJoins { relation, joins };

        let select = Select {
            projection,
            from,
            selection,
            group_by,
            having,
        };

        Query {
            body: SetExpr::Select(Box::new(select)),
            order_by,
            offset,
            limit,
        }
    }
    fn build_stmt(self) -> Statement {
        let query = self.build_query();

        Statement::Query(query)
    }
}

impl<T> Build for T
where
    T: Prebuild,
{
    fn build(self) -> Result<Statement> {
        self.prebuild().map(NodeData::build_stmt)
    }
}