1use std::collections::HashMap;
2use crate::{
3 utils::Tokens,
4 pg::{
5 types::{
6 Type,
7 type_i64,
8 },
9 QueryResCount,
10 schema::{
11 table::Table,
12 },
13 },
14};
15use super::{
16 utils::{
17 QueryBody,
18 PgQueryCtx,
19 build_returning_values,
20 },
21 expr::{
22 Expr,
23 ExprType,
24 check_bool,
25 ExprValName,
26 check_general_same,
27 },
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<Select>),
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 PgQueryCtx, path: &rpds::Vector<String>) -> (Vec<(ExprValName, Type)>, Tokens) {
50 let mut out = Tokens::new();
51 let mut new_fields: Vec<(ExprValName, Type)> = match &self.source {
52 JoinSource::Subsel(s) => {
53 let res = s.build(ctx, &path.push_back(format!("From subselect")), QueryResCount::Many);
54 out.s("(").s(&res.1.to_string()).s(")");
55 res.0.0.clone()
56 },
57 JoinSource::Table(s) => {
58 let new_fields = match ctx.tables.get(&s) {
59 Some(f) => f,
60 None => {
61 ctx
62 .errs
63 .err(&path.push_back(format!("From")), format!("No table with id {} in version", s));
64 return (vec![], Tokens::new());
65 },
66 };
67 out.id(&s.id);
68 new_fields.iter().map(|e| (ExprValName::field(e.0), e.1.clone())).collect()
69 },
70 };
71 if let Some(s) = &self.alias {
72 out.s("as").id(s);
73 let mut new_fields2 = vec![];
74 for (k, v) in new_fields {
75 new_fields2.push((k.with_alias(s), v));
76 }
77 new_fields = new_fields2;
78 }
79 (new_fields, out)
80 }
81}
82
83#[derive(Clone, Debug)]
84pub enum JoinType {
85 Left,
86 Inner,
87}
88
89#[derive(Clone, Debug)]
90pub struct Join {
91 pub source: Box<NamedSelectSource>,
92 pub type_: JoinType,
93 pub on: Expr,
94}
95
96#[derive(Clone, Debug)]
97pub struct Returning {
98 pub e: Expr,
99 pub rename: Option<String>,
100}
101
102#[derive(Clone, Debug)]
103pub struct Select {
104 pub table: NamedSelectSource,
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(crate) limit: Option<Expr>,
111}
112
113impl QueryBody for Select {
114 fn build(
115 &self,
116 ctx: &mut super::utils::PgQueryCtx,
117 path: &rpds::Vector<String>,
118 res_count: QueryResCount,
119 ) -> (ExprType, Tokens) {
120 let source = self.table.build(ctx, path);
122 let mut fields = HashMap::new();
123 for (k, v) in source.0 {
124 fields.insert(k, v);
125 }
126 let mut scope = fields.clone();
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 };
146 }
147 scope.insert(k, v);
148 }
149 },
150 JoinType::Inner => {
151 for (k, v) in source.0 {
152 scope.insert(k, v);
153 }
154 },
155 }
156 out.s("on").s(&je.on.build(ctx, &path, &scope).1.to_string());
157 joins.push(out.to_string());
158 }
159
160 let mut out = Tokens::new();
162 out.s("select");
163 if self.returning.is_empty() {
164 ctx.errs.err(path, format!("Select must have at least one output, but outputs are empty"));
165 }
166 let out_type = build_returning_values(ctx, path, &scope, &mut out, &self.returning, res_count);
167 out.s("from");
168 out.s(&source.1.to_string());
169 for join in joins {
170 out.s(&join);
171 }
172 if let Some(where_) = &self.where_ {
173 out.s("where");
174 let path = path.push_back("Where".into());
175 let (where_t, where_tokens) = where_.build(ctx, &path, &scope);
176 check_bool(ctx, &path, &where_t);
177 out.s(&where_tokens.to_string());
178 }
179 if self.group.len() > 0 {
180 out.s("group by");
181 for (i, g) in self.group.iter().enumerate() {
182 let path = path.push_back(format!("Group by clause {}", i));
183 if i > 0 {
184 out.s(",");
185 }
186 let (_, g_tokens) = g.build(ctx, &path, &scope);
187 out.s(&g_tokens.to_string());
188 }
189 }
190 if !self.order.is_empty() {
191 out.s("order by");
192 for (i, o) in self.order.iter().enumerate() {
193 let path = path.push_back(format!("Order by clause {}", i));
194 if i > 0 {
195 out.s(",");
196 }
197 let (_, o_tokens) = o.0.build(ctx, &path, &scope);
198 out.s(&o_tokens.to_string());
199 out.s(match o.1 {
200 Order::Asc => "asc",
201 Order::Desc => "desc",
202 });
203 }
204 }
205 if let Some(l) = &self.limit {
206 out.s("limit");
207 let path = path.push_back("Limit".into());
208 let (limit_t, limit_tokens) = l.build(ctx, &path, &scope);
209 check_general_same(ctx, &path, &limit_t, &ExprType(vec![(ExprValName::empty(), type_i64().build())]));
210 out.s(&limit_tokens.to_string());
211 }
212 (out_type, out)
213 }
214}