good_ormning/pg/query/
select.rs

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        // Prep
121        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        // Build query
161        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}