1use {
2 super::{
3 expr::{
4 check_assignable,
5 check_bool,
6 check_general_same,
7 Expr,
8 ExprType,
9 Binding,
10 },
11 utils::{
12 build_returning_values,
13 SqliteQueryCtx,
14 },
15 },
16 crate::{
17 sqlite::{
18 schema::table::Table,
19 types::{
20 type_i64,
21 Type,
22 },
23 QueryResCount,
24 },
25 utils::Tokens,
26 },
27 std::collections::HashMap,
28};
29
30#[derive(Clone, Debug)]
31pub enum Order {
32 Asc,
33 Desc,
34}
35
36#[derive(Clone, Debug)]
37pub enum JoinSource {
38 Subsel(Box<SelectBody>),
39 Table(Table),
40}
41
42#[derive(Clone, Debug)]
43pub struct NamedSelectSource {
44 pub source: JoinSource,
45 pub alias: Option<String>,
46}
47
48impl NamedSelectSource {
49 fn build(&self, ctx: &mut SqliteQueryCtx, path: &rpds::Vector<String>) -> (Vec<(Binding, Type)>, Tokens) {
50 let mut out = Tokens::new();
51 let mut new_fields: Vec<(Binding, Type)> = match &self.source {
52 JoinSource::Subsel(s) => {
53 let res =
54 s.build(ctx, &HashMap::new(), &path.push_back(format!("From subselect")), QueryResCount::Many);
55 out.s("(").s(&res.1.to_string()).s(")");
56 res.0.0.clone()
57 },
58 JoinSource::Table(s) => {
59 let new_fields = match ctx.tables.get(&s) {
60 Some(f) => f,
61 None => {
62 ctx.errs.err(&path.push_back(format!("From")), format!("No known table with id {}", s));
63 return (vec![], Tokens::new());
64 },
65 };
66 out.id(&s.id);
67 new_fields.iter().map(|e| (Binding::field(e), e.type_.type_.clone())).collect()
68 },
69 };
70 if let Some(s) = &self.alias {
71 out.s("as").id(s);
72 let mut new_fields2 = vec![];
73 for (k, v) in new_fields {
74 new_fields2.push((k.with_alias(s), v));
75 }
76 new_fields = new_fields2;
77 }
78 (new_fields, out)
79 }
80}
81
82#[derive(Clone, Debug)]
83pub enum JoinType {
84 Left,
85 Inner,
86}
87
88#[derive(Clone, Debug)]
89pub struct Join {
90 pub source: Box<NamedSelectSource>,
91 pub type_: JoinType,
92 pub on: Expr,
93}
94
95#[derive(Clone, Debug)]
96pub struct Returning {
97 pub e: Expr,
98 pub rename: Option<String>,
99}
100
101#[derive(Clone, Debug)]
102pub struct SelectBody {
103 pub table: NamedSelectSource,
104 pub distinct: bool,
105 pub returning: Vec<Returning>,
106 pub join: Vec<Join>,
107 pub where_: Option<Expr>,
108 pub group: Vec<Expr>,
109 pub order: Vec<(Expr, Order)>,
110 pub limit: Option<Expr>,
111}
112
113impl SelectBody {
114 pub fn build(
115 &self,
116 ctx: &mut super::utils::SqliteQueryCtx,
117 inject_scope: &HashMap<Binding, Type>,
118 path: &rpds::Vector<String>,
119 res_count: QueryResCount,
120 ) -> (ExprType, Tokens) {
121 let source = self.table.build(ctx, path);
123 let mut scope = inject_scope.clone();
124 for (k, v) in source.0 {
125 scope.insert(k, v);
126 }
127 let mut joins = vec![];
128 for (i, je) in self.join.iter().enumerate() {
129 let path = path.push_back(format!("Join {}", i));
130 let mut out = Tokens::new();
131 match je.type_ {
132 JoinType::Left => out.s("left"),
133 JoinType::Inner => out.s("inner"),
134 };
135 out.s("join");
136 let source = je.source.build(ctx, &path);
137 out.s(&source.1.to_string());
138 match je.type_ {
139 JoinType::Left => {
140 for (k, mut v) in source.0 {
141 if !v.opt {
142 v = Type {
143 opt: true,
144 type_: v.type_,
145 array: false,
146 };
147 }
148 scope.insert(k, v);
149 }
150 },
151 JoinType::Inner => {
152 for (k, v) in source.0 {
153 scope.insert(k, v);
154 }
155 },
156 }
157 out.s("on").s(&je.on.build(ctx, &path, &scope).1.to_string());
158 joins.push(out.to_string());
159 }
160
161 let mut out = Tokens::new();
163 out.s("select");
164 if self.distinct {
165 out.s("distinct");
166 }
167 if self.returning.is_empty() {
168 ctx.errs.err(path, format!("Select must have at least one output, but outputs are empty"));
169 }
170 let out_type = build_returning_values(ctx, path, &scope, &mut out, &self.returning, res_count);
171 out.s("from");
172 out.s(&source.1.to_string());
173 for join in joins {
174 out.s(&join);
175 }
176 if let Some(where_) = &self.where_ {
177 out.s("where");
178 let path = path.push_back("Where".into());
179 let (where_t, where_tokens) = where_.build(ctx, &path, &scope);
180 check_bool(ctx, &path, &where_t);
181 out.s(&where_tokens.to_string());
182 }
183 if self.group.len() > 0 {
184 out.s("group by");
185 for (i, g) in self.group.iter().enumerate() {
186 let path = path.push_back(format!("Group by clause {}", i));
187 if i > 0 {
188 out.s(",");
189 }
190 let (_, g_tokens) = g.build(ctx, &path, &scope);
191 out.s(&g_tokens.to_string());
192 }
193 }
194 if !self.order.is_empty() {
195 out.s("order by");
196 for (i, o) in self.order.iter().enumerate() {
197 let path = path.push_back(format!("Order by clause {}", i));
198 if i > 0 {
199 out.s(",");
200 }
201 let (_, o_tokens) = o.0.build(ctx, &path, &scope);
202 out.s(&o_tokens.to_string());
203 out.s(match o.1 {
204 Order::Asc => "asc",
205 Order::Desc => "desc",
206 });
207 }
208 }
209 if let Some(l) = &self.limit {
210 out.s("limit");
211 let path = path.push_back("Limit".into());
212 let (limit_t, limit_tokens) = l.build(ctx, &path, &scope);
213 check_general_same(ctx, &path, &limit_t, &ExprType(vec![(Binding::empty(), type_i64().build())]));
214 out.s(&limit_tokens.to_string());
215 }
216 (out_type, out)
217 }
218}
219
220#[derive(Clone, Debug, Copy)]
221pub enum SelectJunctionOperator {
222 Union,
223 UnionAll,
224 Intersect,
225 Except,
226}
227
228#[derive(Clone, Debug)]
229pub struct SelectJunction {
230 pub op: SelectJunctionOperator,
231 pub body: SelectBody,
232}
233
234pub fn build_select_junction(
235 ctx: &mut super::utils::SqliteQueryCtx,
236 path: &rpds::Vector<String>,
237 base_type: &ExprType,
238 body_junctions: &[SelectJunction],
239) -> Tokens {
240 let mut out = Tokens::new();
241 for (i, j) in body_junctions.iter().enumerate() {
242 let path = path.push_back(format!("Junction clause {} - {:?}", i, j.op));
243 match j.op {
244 SelectJunctionOperator::Union => {
245 out.s("union");
246 },
247 SelectJunctionOperator::UnionAll => {
248 out.s("union all");
249 },
250 SelectJunctionOperator::Intersect => {
251 out.s("intersect");
252 },
253 SelectJunctionOperator::Except => {
254 out.s("except");
255 },
256 }
257 let j_body = j.body.build(ctx, &HashMap::new(), &path, QueryResCount::Many);
258 if j_body.0.0.len() != base_type.0.len() {
259 ctx
260 .errs
261 .err(
262 &path,
263 format!(
264 "Select returns {} columns but the base select has {} columns and these must match exactly",
265 j_body.0.0.len(),
266 base_type.0.len()
267 ),
268 );
269 continue;
270 }
271 for (i, ((_, got), (_, want))) in Iterator::zip(j_body.0.0.iter(), base_type.0.iter()).enumerate() {
272 let path = path.push_back(format!("Select return {}", i));
273 check_assignable(&mut ctx.errs, &path, want, &ExprType(vec![(Binding::empty(), got.clone())]));
274 }
275 out.s(&j_body.1.to_string());
276 }
277 return out;
278}