1use {
2 super::{
3 expr::{
4 check_assignable,
5 Binding,
6 Expr,
7 ExprType,
8 },
9 select_body::{
10 Returning,
11 SelectBody,
12 SelectJunction,
13 SelectJunctionOperator,
14 },
15 },
16 crate::{
17 sqlite::{
18 schema::{
19 field::{
20 Field,
21 FieldType,
22 Field_,
23 SchemaFieldId,
24 },
25 table::{
26 SchemaTableId,
27 Table,
28 Table_,
29 },
30 },
31 types::Type,
32 QueryResCount,
33 },
34 utils::{
35 Errs,
36 Tokens,
37 },
38 },
39 proc_macro2::TokenStream,
40 std::{
41 collections::{
42 HashMap,
43 HashSet,
44 },
45 rc::Rc,
46 },
47};
48
49pub struct SqliteQueryCtx {
50 pub(crate) tables: HashMap<Table, HashSet<Field>>,
51 pub errs: Errs,
52 pub(crate) rust_arg_lookup: HashMap<String, (usize, Type)>,
53 pub(crate) rust_args: Vec<TokenStream>,
54 pub(crate) query_args: Vec<TokenStream>,
55}
56
57impl<'a> SqliteQueryCtx {
58 pub(crate) fn new(errs: Errs, tables: HashMap<Table, HashSet<Field>>) -> Self {
59 Self {
60 tables: tables,
61 errs: errs,
62 rust_arg_lookup: Default::default(),
63 rust_args: Default::default(),
64 query_args: Default::default(),
65 }
66 }
67}
68
69pub trait QueryBody {
70 fn build(
71 &self,
72 ctx: &mut SqliteQueryCtx,
73 path: &rpds::Vector<String>,
74 res_count: QueryResCount,
75 ) -> (ExprType, Tokens);
76}
77
78pub fn build_set(
79 ctx: &mut SqliteQueryCtx,
80 path: &rpds::Vector<String>,
81 scope: &HashMap<Binding, Type>,
82 out: &mut Tokens,
83 values: &Vec<(Field, Expr)>,
84) {
85 out.s("set");
86 for (i, (field, val)) in values.iter().enumerate() {
87 let path = path.push_back(format!("Set field {}", i));
88 if i > 0 {
89 out.s(",");
90 }
91 out.id(&field.id).s("=");
92 let res = val.build(ctx, &path, &scope);
93 let field = match ctx.tables.get(&field.table).and_then(|t| t.get(&field)) {
94 Some(t) => t,
95 None => {
96 ctx.errs.err(&path, format!("Update destination value field {} is not known", field));
97 continue;
98 },
99 };
100 check_assignable(&mut ctx.errs, &path, &field.type_.type_, &res.0);
101 out.s(&res.1.to_string());
102 }
103}
104
105pub fn build_returning_values(
106 ctx: &mut SqliteQueryCtx,
107 path: &rpds::Vector<String>,
108 scope: &HashMap<Binding, Type>,
109 out: &mut Tokens,
110 outputs: &Vec<Returning>,
111 res_count: QueryResCount,
112) -> ExprType {
113 if outputs.is_empty() {
114 if !matches!(res_count, QueryResCount::None) {
115 ctx.errs.err(path, format!("Query has no outputs but res_count is, {:?}, not None", res_count));
116 }
117 } else {
118 if matches!(res_count, QueryResCount::None) {
119 ctx.errs.err(&path, format!("Query has outputs so res_count must be not None, but is {:?}", res_count));
120 }
121 }
122 let mut out_rec: Vec<(Binding, Type)> = vec![];
123 for (i, o) in outputs.iter().enumerate() {
124 let path = path.push_back(format!("Result {}", i));
125 if i > 0 {
126 out.s(",");
127 }
128 let res = o.e.build(ctx, &path, scope);
129 out.s(&res.1.to_string());
130 let (res_name, res_type) = match res.0.assert_scalar(&mut ctx.errs, &path) {
131 Some(x) => x,
132 None => continue,
133 };
134 if let Some(rename) = &o.rename {
135 out.s("as").id(rename);
136 out_rec.push((Binding::local(rename.clone()), res_type));
137 } else {
138 out_rec.push((res_name, res_type));
139 }
140 }
141 ExprType(out_rec)
142}
143
144pub fn build_returning(
145 ctx: &mut SqliteQueryCtx,
146 path: &rpds::Vector<String>,
147 scope: &HashMap<Binding, Type>,
148 out: &mut Tokens,
149 outputs: &Vec<Returning>,
150 res_count: QueryResCount,
151) -> ExprType {
152 if !outputs.is_empty() {
153 out.s("returning");
154 }
155 build_returning_values(ctx, path, scope, out, outputs, res_count)
156}
157
158#[derive(Clone, Debug)]
159pub struct With {
160 pub recursive: bool,
161 pub ctes: Vec<Cte>,
162}
163
164#[derive(Clone, Debug)]
165pub struct Cte {
166 pub table: Table,
167 pub columns: Vec<Field>,
168 pub body: SelectBody,
169 pub body_junctions: Vec<SelectJunction>,
170}
171
172pub struct CteBuilder {
173 table: Table,
174 cte: Cte,
175}
176
177impl CteBuilder {
178 pub fn new(id: impl AsRef<str>, body: SelectBody) -> Self {
179 let table = Table(Rc::new(Table_ {
180 schema_id: SchemaTableId("".to_string()),
181 id: id.as_ref().to_string(),
182 }));
183 return Self {
184 table: table.clone(),
185 cte: Cte {
186 table: table,
187 columns: vec![],
188 body: body,
189 body_junctions: vec![],
190 },
191 };
192 }
193
194 pub fn body_junction(&mut self, j: SelectJunction) {
195 self.cte.body_junctions.push(j);
196 }
197
198 pub fn field(&mut self, id: impl AsRef<str>, type_: Type) -> Field {
199 let f = Field(Rc::new(Field_ {
200 table: self.table.clone(),
201 schema_id: SchemaFieldId(id.as_ref().to_string()),
202 id: id.as_ref().to_string(),
203 type_: FieldType {
204 type_: type_,
205 migration_default: None,
206 },
207 }));
208 if self.cte.columns.contains(&f) {
209 panic!("Duplicate field {} in CTE definition", id.as_ref());
210 }
211 self.cte.columns.push(f.clone());
212 return f;
213 }
214
215 pub fn build(self) -> (Table, Cte) {
216 return (self.table, self.cte);
217 }
218}
219
220pub fn build_with(ctx: &mut SqliteQueryCtx, path: &rpds::Vector<String>, with: &With) -> Tokens {
221 let mut out = Tokens::new();
222 out.s("with");
223 for (i, cte) in with.ctes.iter().enumerate() {
224 if i > 0 {
225 out.s(",");
226 }
227 let path = path.push_back(format!("CTE {}", i));
228 out.s(&cte.table.id);
229 out.s("(");
230 for (i, c) in cte.columns.iter().enumerate() {
231 if i > 0 {
232 out.s(",");
233 }
234 out.s(&c.id);
235 }
236 out.s(")");
237 out.s("as");
238 out.s("(");
239 let body = cte.body.build(ctx, &HashMap::new(), &path, QueryResCount::Many);
240 for (i, ((_, got), want)) in Iterator::zip(body.0.0.iter(), cte.columns.iter()).enumerate() {
241 let path = path.push_back(format!("Select return {}", i));
242 check_assignable(
243 &mut ctx.errs,
244 &path,
245 &want.type_.type_,
246 &ExprType(vec![(Binding::empty(), got.clone())]),
247 );
248 }
249 out.s(&body.1.to_string());
250 if body.0.0.len() != cte.columns.len() {
251 ctx
252 .errs
253 .err(
254 &path,
255 format!(
256 "Select returns {} columns but the CTE needs exactly {} columns",
257 body.0.0.len(),
258 cte.columns.len()
259 ),
260 );
261 continue;
262 }
263 ctx.tables.insert(cte.table.clone(), cte.columns.iter().cloned().collect());
264 for (i, j) in cte.body_junctions.iter().enumerate() {
265 let path = path.push_back(format!("Junction clause {} - {:?}", i, j.op));
266 match j.op {
267 SelectJunctionOperator::Union => {
268 out.s("union");
269 },
270 SelectJunctionOperator::UnionAll => {
271 out.s("union all");
272 },
273 SelectJunctionOperator::Intersect => {
274 out.s("intersect");
275 },
276 SelectJunctionOperator::Except => {
277 out.s("except");
278 },
279 }
280 let j_body = j.body.build(ctx, &HashMap::new(), &path, QueryResCount::Many);
281 if j_body.0.0.len() != cte.columns.len() {
282 ctx
283 .errs
284 .err(
285 &path,
286 format!(
287 "Select returns {} columns but the CTE needs exactly {} columns",
288 j_body.0.0.len(),
289 cte.columns.len()
290 ),
291 );
292 continue;
293 }
294 for (i, ((_, got), want)) in Iterator::zip(j_body.0.0.iter(), cte.columns.iter()).enumerate() {
295 let path = path.push_back(format!("Select return {}", i));
296 check_assignable(
297 &mut ctx.errs,
298 &path,
299 &want.type_.type_,
300 &ExprType(vec![(Binding::empty(), got.clone())]),
301 );
302 }
303 }
304 out.s(")");
305 }
306 return out;
307}