gluesql_core/plan/statement/query/
project.rs1use {
2 super::{AggregationPlan, FilterPlan, HavingPlan},
3 crate::plan::{InnerJoinPlan, LeftOuterJoinPlan, ProjectionPlan, SourcePlan},
4 serde::{Deserialize, Serialize},
5};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum ProjectInputPlan {
9 Source(SourcePlan),
10 InnerJoin(Box<InnerJoinPlan>),
11 LeftOuterJoin(Box<LeftOuterJoinPlan>),
12 Filter(FilterPlan),
13 Aggregation(AggregationPlan),
14 Having(HavingPlan),
15}
16
17impl ProjectInputPlan {
18 pub fn base_source(&self) -> &SourcePlan {
19 match self {
20 Self::Source(source) => source,
21 Self::InnerJoin(join) => join.base_source(),
22 Self::LeftOuterJoin(join) => join.base_source(),
23 Self::Filter(filter) => filter.input.base_source(),
24 Self::Aggregation(aggregation) => aggregation.input.base_source(),
25 Self::Having(having) => having.input.input.base_source(),
26 }
27 }
28
29 pub(crate) fn joined_sources(&self) -> Vec<&SourcePlan> {
30 match self {
31 Self::Source(_) => Vec::new(),
32 Self::InnerJoin(join) => join.joined_sources(),
33 Self::LeftOuterJoin(join) => join.joined_sources(),
34 Self::Filter(filter) => filter.input.joined_sources(),
35 Self::Aggregation(aggregation) => aggregation.input.joined_sources(),
36 Self::Having(having) => having.input.input.joined_sources(),
37 }
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
42pub struct ProjectPlan {
43 pub input: ProjectInputPlan,
44 pub projection: ProjectionPlan,
45}
46
47#[cfg(test)]
48mod tests {
49 use {
50 super::{ProjectInputPlan, ProjectPlan},
51 crate::{
52 data::Value,
53 plan::{
54 AggregationInputPlan, AggregationPlan, ExprPlan, FilterInputPlan, FilterPlan,
55 HavingPlan, InnerJoinInputPlan, InnerJoinPlan, LeftOuterJoinInputPlan,
56 LeftOuterJoinPlan, NestedLoopJoinInputPlan, NestedLoopJoinPlan, ProjectionPlan,
57 SourcePlan, TableAccessPlan, TableSourcePlan,
58 },
59 },
60 pretty_assertions::assert_eq,
61 };
62
63 fn table(name: &str) -> SourcePlan {
64 SourcePlan::Table(TableSourcePlan {
65 name: name.to_owned(),
66 alias: None,
67 access: TableAccessPlan::FullScan,
68 })
69 }
70
71 #[test]
72 fn project_accepts_each_typed_source_input() {
73 let inner_join = InnerJoinPlan {
74 input: InnerJoinInputPlan::NestedLoop(NestedLoopJoinPlan {
75 input: NestedLoopJoinInputPlan::Source(table("A")),
76 right: table("B"),
77 }),
78 };
79 let left_outer_join = LeftOuterJoinPlan {
80 input: LeftOuterJoinInputPlan::NestedLoop(NestedLoopJoinPlan {
81 input: NestedLoopJoinInputPlan::Source(table("A")),
82 right: table("B"),
83 }),
84 };
85 let filter = FilterPlan {
86 input: FilterInputPlan::InnerJoin(Box::new(inner_join.clone())),
87 expr: ExprPlan::Value(Value::Bool(true)),
88 };
89 let aggregation = AggregationPlan {
90 input: AggregationInputPlan::Filter(filter.clone()),
91 group_by: Vec::new(),
92 aggregate_slots: Vec::new(),
93 };
94 let having_plan = HavingPlan {
95 input: aggregation.clone(),
96 expr: ExprPlan::Value(Value::Bool(true)),
97 };
98 let projection = ProjectionPlan::SelectItems(Vec::new());
99
100 let relation = ProjectPlan {
101 input: ProjectInputPlan::Source(table("A")),
102 projection: projection.clone(),
103 };
104 let inner = ProjectPlan {
105 input: ProjectInputPlan::InnerJoin(Box::new(inner_join.clone())),
106 projection: projection.clone(),
107 };
108 let left_outer = ProjectPlan {
109 input: ProjectInputPlan::LeftOuterJoin(Box::new(left_outer_join.clone())),
110 projection: projection.clone(),
111 };
112 let filtered = ProjectPlan {
113 input: ProjectInputPlan::Filter(filter.clone()),
114 projection: projection.clone(),
115 };
116 let aggregated = ProjectPlan {
117 input: ProjectInputPlan::Aggregation(aggregation.clone()),
118 projection: projection.clone(),
119 };
120 let having = ProjectPlan {
121 input: ProjectInputPlan::Having(having_plan.clone()),
122 projection,
123 };
124
125 assert_eq!(relation.input, ProjectInputPlan::Source(table("A")));
126 assert_eq!(
127 inner.input,
128 ProjectInputPlan::InnerJoin(Box::new(inner_join))
129 );
130 assert_eq!(
131 left_outer.input,
132 ProjectInputPlan::LeftOuterJoin(Box::new(left_outer_join))
133 );
134 assert_eq!(filtered.input, ProjectInputPlan::Filter(filter));
135 assert_eq!(aggregated.input, ProjectInputPlan::Aggregation(aggregation));
136 assert_eq!(having.input, ProjectInputPlan::Having(having_plan));
137
138 assert_eq!(relation.input.base_source(), &table("A"));
139 assert_eq!(relation.input.joined_sources(), Vec::<&SourcePlan>::new());
140 assert_eq!(inner.input.base_source(), &table("A"));
141 let expected = [table("B")];
142 assert_eq!(
143 inner.input.joined_sources(),
144 expected.iter().collect::<Vec<_>>()
145 );
146 assert_eq!(left_outer.input.base_source(), &table("A"));
147 let expected = [table("B")];
148 assert_eq!(
149 left_outer.input.joined_sources(),
150 expected.iter().collect::<Vec<_>>()
151 );
152 assert_eq!(filtered.input.base_source(), &table("A"));
153 let expected = [table("B")];
154 assert_eq!(
155 filtered.input.joined_sources(),
156 expected.iter().collect::<Vec<_>>()
157 );
158 assert_eq!(aggregated.input.base_source(), &table("A"));
159 let expected = [table("B")];
160 assert_eq!(
161 aggregated.input.joined_sources(),
162 expected.iter().collect::<Vec<_>>()
163 );
164 assert_eq!(having.input.base_source(), &table("A"));
165 let expected = [table("B")];
166 assert_eq!(
167 having.input.joined_sources(),
168 expected.iter().collect::<Vec<_>>()
169 );
170 }
171}