1use crate::{
2 diagnostics::Report,
3 ids::{DbIdent, Ident},
4 names::{ColumnIdent, DbProcedure, FieldIdent, FieldKind, TypeIdent, TypeKind},
5 uid::{RenameExt, RenameMap},
6 HasIdent, SchemaItem, SchemaType,
7};
8
9#[derive(Debug, Clone)]
10pub enum SqlUnOp {
11 Plus,
12 Minus,
13 Not,
14}
15impl SqlUnOp {
16 pub fn format(&self) -> &'static str {
17 match self {
18 SqlUnOp::Plus => "+",
19 SqlUnOp::Minus => "-",
20 SqlUnOp::Not => "NOT ",
21 }
22 }
23}
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum SqlOp {
26 Lt,
27 Gt,
28 Le,
29 Ge,
30 Eq,
31 Ne,
32 SEq,
33 SNe,
34
35 And,
36 Or,
37
38 Plus,
39 Minus,
40 Div,
41 Mul,
42 Mod,
43
44 Like,
45}
46impl SqlOp {
47 pub fn format(&self) -> &'static str {
48 match self {
49 SqlOp::Lt => "<",
50 SqlOp::Gt => ">",
51 SqlOp::Le => "<=",
52 SqlOp::Ge => ">=",
53 SqlOp::Eq => "=",
54 SqlOp::Ne => "<>",
55 SqlOp::SEq => "IS NOT DISTINCT FROM",
56 SqlOp::SNe => "IS DISTINCT FROM",
57 SqlOp::And => "AND",
58 SqlOp::Or => "OR",
59 SqlOp::Plus => "+",
60 SqlOp::Minus => "-",
61 SqlOp::Div => "/",
62 SqlOp::Mul => "*",
63 SqlOp::Mod => "%",
64 SqlOp::Like => "LIKE",
65 }
66 }
67}
68#[derive(Debug, Clone)]
69pub enum Sql {
70 Cast(Box<Sql>, TypeIdent),
71 Call(DbProcedure, Vec<Sql>),
72 String(String),
73 Number(i128),
74 Ident(ColumnIdent),
75 UnOp(SqlUnOp, Box<Sql>),
76 BinOp(Box<Sql>, SqlOp, Box<Sql>),
77 GetField(Box<Sql>, FieldIdent),
78 Parened(Box<Sql>),
79 Tuple(Vec<Sql>),
80 If(Box<Sql>, Box<Sql>, Box<Sql>),
81 Boolean(bool),
82 Placeholder,
83 Null,
84}
85impl Sql {
86 pub fn replace_placeholder(&mut self, to: Sql) {
87 struct ReplacePlaceholder {
88 to: Sql,
89 }
90 impl SqlVisitor for ReplacePlaceholder {
91 fn handle_placeholder(&mut self, placeholder: &mut Sql) {
92 *placeholder = self.to.clone()
93 }
94 }
95 self.visit(&mut ReplacePlaceholder { to })
96 }
97 pub fn affected_columns(&self) -> Vec<ColumnIdent> {
98 struct ColumnCollector {
99 columns: Vec<ColumnIdent>,
100 }
101 impl SqlVisitor for ColumnCollector {
102 fn handle_column(&mut self, column: &mut ColumnIdent) {
103 if self.columns.contains(column) {
104 return;
105 }
106 self.columns.push(*column)
107 }
108 fn handle_placeholder(&mut self, _placeholder: &mut Sql) {
109 panic!("placeholder was not normalized")
110 }
111 }
112 let mut collector = ColumnCollector { columns: vec![] };
113 let mut copy = self.clone();
114 copy.visit(&mut collector);
115 collector.columns
116 }
117 fn visit(&mut self, v: &mut impl SqlVisitor) {
118 v.handle(self);
119 match self {
120 Sql::Cast(s, t) => {
121 s.visit(v);
122 v.handle_type(t);
123 }
124 Sql::Call(_p, args) => {
125 for arg in args {
126 arg.visit(v);
127 }
128 }
129 Sql::String(_s) => {}
130 Sql::Number(_n) => {}
131 Sql::Ident(i) => v.handle_column(i),
132 Sql::UnOp(_o, s) => s.visit(v),
133 Sql::BinOp(a, _o, b) => {
134 a.visit(v);
135 b.visit(v);
136 }
137 Sql::Parened(s) => s.visit(v),
138 Sql::Placeholder => v.handle_placeholder(self),
139 Sql::Boolean(_) => {}
140 Sql::GetField(s, _) => s.visit(v),
141 Sql::Null => {}
142 Sql::Tuple(els) => {
143 for ele in els.iter_mut() {
144 ele.visit(v)
145 }
146 }
147 Sql::If(a, b, c) => {
148 a.visit(v);
149 b.visit(v);
150 c.visit(v);
151 }
152 }
153 }
154 pub fn all(s: impl IntoIterator<Item = Self>) -> Self {
155 let mut s = s.into_iter();
156 let mut v = s.next().unwrap_or(Self::Boolean(true));
157 for i in s {
158 v = Sql::BinOp(Box::new(v), SqlOp::And, Box::new(i))
159 }
160 v
161 }
162 pub fn any(s: impl IntoIterator<Item = Self>) -> Self {
163 let mut s = s.into_iter();
164 let mut v = s.next().unwrap_or(Self::Boolean(false));
165 for i in s {
166 v = Sql::BinOp(Box::new(v), SqlOp::Or, Box::new(i))
167 }
168 v
169 }
170 pub fn field_name(
171 &self,
172 context: &SchemaItem<'_>,
173 field: Ident<FieldKind>,
174 rn: &RenameMap,
175 report: &mut Report,
176 ) -> Option<DbIdent<FieldKind>> {
177 let this = self.type_ident_of_expr(context, report)?;
178 let ty = context.schema().schema_ty(this);
179 Some(match ty {
180 SchemaType::Enum(e) => {
181 report
182 .error("enum variants can't be observed yet")
183 .annotate("happened here", field.span())
184 .annotate("enum defined here", e.id().span());
185 return None;
186 }
187 SchemaType::Scalar(s) => {
188 report
189 .error("invalid field access")
190 .annotate("happened here", field.span())
191 .annotate("scalars can't have fields", s.id().span());
192 return None;
193 }
194 SchemaType::Composite(c) => c.field(field).db(rn),
195 })
196 }
197 pub fn context_ident_name<'s>(
198 context: &'s SchemaItem<'s>,
199 ident: ColumnIdent,
200 rn: &RenameMap,
201 report: &mut Report,
202 ) -> Option<DbIdent<FieldKind>> {
203 Some(match context {
204 SchemaItem::Table(t) => {
205 let column = t.schema_column(ident);
206 DbIdent::unchecked_from(column.db(rn))
207 }
208 SchemaItem::Enum(e) => {
209 report
210 .error("enum variants can't be observed yet")
211 .annotate("happened here", ident.span())
212 .annotate("enum defined here", e.id().span());
213 return None;
214 }
215 SchemaItem::Scalar(s) => {
216 report
217 .error("invalid field access")
218 .annotate("happened here", ident.span())
219 .annotate("scalars can't have fields", s.id().span());
220 return None;
221 }
222 SchemaItem::Composite(c) => {
223 let field = c.field(Ident::unchecked_cast(ident));
224 field.db(rn)
225 }
226 SchemaItem::View(v) => {
227 report
228 .error("invalid field access")
229 .annotate("happened here", ident.span())
230 .annotate("views are opaque", v.id().span());
231 return None;
232 }
233 })
234 }
235 pub fn ident_name<'s>(
237 &self,
238 context: &'s SchemaItem<'s>,
239 rn: &RenameMap,
240 report: &mut Report,
241 ) -> Option<DbIdent<FieldKind>> {
242 let Sql::Ident(f) = self else {
243 panic!("not ident");
244 };
245 Self::context_ident_name(context, *f, rn, report)
246 }
247 fn type_ident_of_expr<'s>(
248 &self,
249 context: &'s SchemaItem<'s>,
250 report: &mut Report,
251 ) -> Option<Ident<TypeKind>> {
252 Some(match self {
253 Sql::Cast(_, t) => *t,
254 Sql::UnOp(_, _)
255 | Sql::BinOp(_, _, _)
256 | Sql::Call(_, _)
257 | Sql::String(_)
258 | Sql::Number(_)
259 | Sql::Boolean(_)
260 | Sql::Null
261 | Sql::Tuple(_)
262 | Sql::If(_, _, _) => {
263 panic!("cannot determine call return type")
264 }
265 Sql::Ident(f) => match context {
266 SchemaItem::Table(t) => {
267 let column = t.schema_column(*f);
268 column.ty
269 }
270 SchemaItem::Enum(e) => {
271 report
272 .error("invalid value reference")
273 .annotate("enums can't contain fields", f.span())
274 .annotate("enum defined here", e.id().span());
275 return None;
276 }
277 SchemaItem::Scalar(s) => {
278 report
279 .error("invalid value reference")
280 .annotate("scalars can't contain fields", f.span())
281 .annotate("scalar defined here", s.id().span());
282 return None;
283 }
284 SchemaItem::Composite(c) => {
285 let field = c.field(Ident::unchecked_cast(*f));
286 field.ty
287 }
288 SchemaItem::View(v) => {
289 report
290 .error("invalid value reference")
291 .annotate("views can't contain fields", f.span())
292 .annotate("scalar defined here", v.id().span());
293 return None;
294 }
295 },
296 Sql::GetField(f, t) => {
297 let ty_id = f.type_ident_of_expr(context, report)?;
298 let ty = context.schema().schema_ty(ty_id);
299 Sql::Ident(Ident::unchecked_cast(*t))
300 .type_ident_of_expr(&ty.as_schema_item(), report)?
301 }
302 Sql::Parened(v) => v.type_ident_of_expr(context, report)?,
303 Sql::Placeholder => match context {
304 SchemaItem::Table(_) | SchemaItem::View(_) => {
305 panic!("can't refer to table fields using this notation")
306 }
307 SchemaItem::Enum(e) => e.id(),
308 SchemaItem::Scalar(s) => s.id(),
309 SchemaItem::Composite(c) => c.id(),
310 },
311 })
312 }
313}
314#[allow(unused_variables)]
315pub trait SqlVisitor {
316 fn handle(&mut self, sql: &mut Sql) {}
317 fn handle_type(&mut self, ty: &mut TypeIdent) {}
318 fn handle_column(&mut self, column: &mut ColumnIdent) {}
319 fn handle_placeholder(&mut self, placeholder: &mut Sql) {}
320}