1use teaql_core::{
2 AggregateFunction, BinaryOp, DataType, DeleteCommand, EntityDescriptor, Expr, ExprFunction,
3 InsertCommand, OrderBy, PropertyDescriptor, RecoverCommand, SelectQuery, SortDirection,
4 UpdateCommand, Value,
5};
6
7use crate::{CompiledQuery, DatabaseKind, SqlCompileError};
8
9const SQL_KEYWORDS: &[&str] = &[
10 "all", "alter", "and", "as", "asc", "between", "by", "case", "create", "delete", "desc",
11 "distinct", "drop", "exists", "false", "from", "group", "having", "in", "insert", "into", "is",
12 "join", "like", "limit", "not", "null", "offset", "on", "or", "order", "select", "set",
13 "table", "true", "type", "union", "update", "values", "where",
14];
15
16pub fn quote_identifier_if_needed(ident: &str, quote: char) -> String {
17 if is_wrapped_identifier(ident) {
18 return ident.to_owned();
19 }
20 if needs_quoted_identifier(ident) {
21 let quote_string = quote.to_string();
22 let escaped = ident.replace(quote, &(quote_string.clone() + "e_string));
23 return format!("{quote}{escaped}{quote}");
24 }
25 ident.to_owned()
26}
27
28fn is_wrapped_identifier(ident: &str) -> bool {
29 (ident.starts_with('"') && ident.ends_with('"'))
30 || (ident.starts_with('`') && ident.ends_with('`'))
31 || (ident.starts_with('[') && ident.ends_with(']'))
32}
33
34fn needs_quoted_identifier(ident: &str) -> bool {
35 if ident.is_empty()
36 || SQL_KEYWORDS
37 .binary_search(&ident.to_ascii_lowercase().as_str())
38 .is_ok()
39 {
40 return true;
41 }
42 let mut chars = ident.chars();
43 match chars.next() {
44 Some(first) if first == '_' || first.is_ascii_alphabetic() => {}
45 _ => return true,
46 }
47 chars.any(|ch| ch != '_' && !ch.is_ascii_alphanumeric())
48}
49
50
51pub trait SqlDialect {
52 fn kind(&self) -> DatabaseKind;
53 fn quote_ident(&self, ident: &str) -> String;
54 fn placeholder(&self, index: usize) -> String;
55
56 fn schema_setup_sqls(&self) -> &'static [&'static str] {
57 &[]
58 }
59
60 fn schema_type_sql(
61 &self,
62 data_type: DataType,
63 _property: &PropertyDescriptor,
64 ) -> Result<&'static str, SqlCompileError> {
65 match data_type {
66 DataType::Bool => Ok("BOOLEAN"),
67 DataType::I64 | DataType::U64 => Ok("INTEGER"),
68 DataType::F64 => Ok("REAL"),
69 DataType::Decimal => Ok("NUMERIC"),
70 DataType::Text | DataType::Json | DataType::Date | DataType::Timestamp => Ok("TEXT"),
71 }
72 }
73
74 fn column_definition_sql(
75 &self,
76 property: &PropertyDescriptor,
77 ) -> Result<String, SqlCompileError> {
78 let mut parts = vec![
79 self.quote_ident(&property.column_name),
80 self.schema_type_sql(property.data_type, property)?
81 .to_owned(),
82 ];
83
84 if property.is_id {
85 parts.push("PRIMARY KEY".to_owned());
86 }
87 if property.is_id || !property.nullable {
88 parts.push("NOT NULL".to_owned());
89 }
90
91 Ok(parts.join(" "))
92 }
93
94 fn compile_create_table(&self, entity: &EntityDescriptor) -> Result<String, SqlCompileError> {
95 let columns = entity
96 .properties
97 .iter()
98 .map(|property| self.column_definition_sql(property))
99 .collect::<Result<Vec<_>, _>>()?
100 .join(", ");
101 Ok(format!(
102 "CREATE TABLE IF NOT EXISTS {} ({columns})",
103 self.quote_ident(&entity.table_name)
104 ))
105 }
106
107 fn compile_add_column(
108 &self,
109 entity: &EntityDescriptor,
110 property: &PropertyDescriptor,
111 ) -> Result<String, SqlCompileError> {
112 Ok(format!(
113 "ALTER TABLE {} ADD COLUMN {}",
114 self.quote_ident(&entity.table_name),
115 self.column_definition_sql(property)?
116 ))
117 }
118
119 fn compile_select(
120 &self,
121 entity: &EntityDescriptor,
122 query: &SelectQuery,
123 ) -> Result<CompiledQuery, SqlCompileError> {
124 let mut params = Vec::new();
125 let sql = self.compile_select_sql(entity, query, &mut params)?;
126 Ok(CompiledQuery {
127 sql,
128 params,
129 comment: query.comment.clone(),
130 })
131 }
132
133 fn compile_select_sql(
134 &self,
135 entity: &EntityDescriptor,
136 query: &SelectQuery,
137 params: &mut Vec<Value>,
138 ) -> Result<String, SqlCompileError> {
139 if let Some(raw_sql) = &query.raw_sql {
140 return Ok(raw_sql.clone());
141 }
142
143 let projection = if query.aggregates.is_empty() {
144 self.select_projection(entity, query, params)?
145 } else {
146 self.aggregate_projection(entity, query, params)?
147 };
148
149 let mut sql = format!(
150 "SELECT {projection} FROM {}",
151 self.quote_ident(&entity.table_name)
152 );
153
154 let mut where_parts = Vec::new();
155 if let Some(filter) = &query.filter {
156 where_parts.push(self.compile_expr(entity, filter, params)?);
157 }
158 where_parts.extend(query.raw_sql_search_criteria.iter().cloned());
159 if !where_parts.is_empty() {
160 sql.push_str(" WHERE ");
161 sql.push_str(&where_parts.join(" AND "));
162 }
163
164 if !query.group_by.is_empty() {
165 let group_by = query
166 .group_by
167 .iter()
168 .map(|field| self.column_sql(entity, field))
169 .collect::<Result<Vec<_>, _>>()?
170 .join(", ");
171 sql.push_str(" GROUP BY ");
172 sql.push_str(&group_by);
173 }
174
175 if let Some(having) = &query.having {
176 let having_sql = self.compile_expr(entity, having, params)?;
177 sql.push_str(" HAVING ");
178 sql.push_str(&having_sql);
179 }
180
181 if !query.order_by.is_empty() {
182 let order_by = query
183 .order_by
184 .iter()
185 .map(|order| self.order_by_sql(entity, order, params))
186 .collect::<Result<Vec<_>, _>>()?
187 .join(", ");
188 sql.push_str(" ORDER BY ");
189 sql.push_str(&order_by);
190 }
191
192 if let Some(slice) = query.slice {
193 if let Some(limit) = slice.limit {
194 sql.push_str(&format!(" LIMIT {limit}"));
195 }
196 if slice.offset > 0 {
197 sql.push_str(&format!(" OFFSET {}", slice.offset));
198 }
199 }
200
201 Ok(sql)
202 }
203
204 fn compile_insert(
205 &self,
206 entity: &EntityDescriptor,
207 command: &InsertCommand,
208 ) -> Result<CompiledQuery, SqlCompileError> {
209 let mut columns = Vec::new();
210 let mut placeholders = Vec::new();
211 let mut params = Vec::new();
212
213 for property in &entity.properties {
214 if let Some(value) = command.values.get(&property.name) {
215 columns.push(self.quote_ident(&property.column_name));
216 params.push(value.clone());
217 placeholders.push(self.placeholder(params.len()));
218 }
219 }
220
221 if columns.is_empty() {
222 return Err(SqlCompileError::EmptyMutation("insert".to_owned()));
223 }
224
225 Ok(CompiledQuery {
226 sql: format!(
227 "INSERT INTO {} ({}) VALUES ({})",
228 self.quote_ident(&entity.table_name),
229 columns.join(", "),
230 placeholders.join(", ")
231 ),
232 params,
233 comment: None,
234 })
235 }
236
237 fn compile_update(
238 &self,
239 entity: &EntityDescriptor,
240 command: &UpdateCommand,
241 ) -> Result<CompiledQuery, SqlCompileError> {
242 let id_property = entity
243 .id_property()
244 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
245 let mut assignments = Vec::new();
246 let mut params = Vec::new();
247
248 for property in &entity.properties {
249 if property.is_id {
250 continue;
251 }
252 if property.is_version && command.expected_version.is_some() {
253 continue;
254 }
255 if let Some(value) = command.values.get(&property.name) {
256 params.push(value.clone());
257 assignments.push(format!(
258 "{} = {}",
259 self.quote_ident(&property.column_name),
260 self.placeholder(params.len())
261 ));
262 }
263 }
264
265 if let Some(expected_version) = command.expected_version {
266 let version_property = entity
267 .version_property()
268 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
269 params.push(Value::I64(expected_version + 1));
270 assignments.push(format!(
271 "{} = {}",
272 self.quote_ident(&version_property.column_name),
273 self.placeholder(params.len())
274 ));
275 }
276
277 if assignments.is_empty() {
278 return Err(SqlCompileError::EmptyMutation("update".to_owned()));
279 }
280
281 params.push(command.id.clone());
282 let mut predicates = vec![format!(
283 "{} = {}",
284 self.quote_ident(&id_property.column_name),
285 self.placeholder(params.len())
286 )];
287
288 if let Some(expected_version) = command.expected_version {
289 let version_property = entity
290 .version_property()
291 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
292 params.push(Value::I64(expected_version));
293 predicates.push(format!(
294 "{} = {}",
295 self.quote_ident(&version_property.column_name),
296 self.placeholder(params.len())
297 ));
298 }
299
300 Ok(CompiledQuery {
301 sql: format!(
302 "UPDATE {} SET {} WHERE {}",
303 self.quote_ident(&entity.table_name),
304 assignments.join(", "),
305 predicates.join(" AND ")
306 ),
307 params,
308 comment: None,
309 })
310 }
311
312 fn compile_delete(
313 &self,
314 entity: &EntityDescriptor,
315 command: &DeleteCommand,
316 ) -> Result<CompiledQuery, SqlCompileError> {
317 let id_property = entity
318 .id_property()
319 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
320 let mut params = Vec::new();
321
322 if command.soft_delete {
323 let version_property = entity
324 .version_property()
325 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
326 params.push(match command.expected_version {
327 Some(version) => Value::I64(-(version + 1)),
328 None => Value::I64(-1),
329 });
330
331 params.push(command.id.clone());
332 let mut predicates = vec![format!(
333 "{} = {}",
334 self.quote_ident(&id_property.column_name),
335 self.placeholder(params.len())
336 )];
337
338 if let Some(expected_version) = command.expected_version {
339 params.push(Value::I64(expected_version));
340 predicates.push(format!(
341 "{} = {}",
342 self.quote_ident(&version_property.column_name),
343 self.placeholder(params.len())
344 ));
345 }
346
347 return Ok(CompiledQuery {
348 sql: format!(
349 "UPDATE {} SET {} = {} WHERE {}",
350 self.quote_ident(&entity.table_name),
351 self.quote_ident(&version_property.column_name),
352 self.placeholder(1),
353 predicates.join(" AND ")
354 ),
355 params,
356 comment: None,
357 });
358 }
359
360 params.push(command.id.clone());
361 let mut predicates = vec![format!(
362 "{} = {}",
363 self.quote_ident(&id_property.column_name),
364 self.placeholder(params.len())
365 )];
366
367 if let Some(expected_version) = command.expected_version {
368 let version_property = entity
369 .version_property()
370 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
371 params.push(Value::I64(expected_version));
372 predicates.push(format!(
373 "{} = {}",
374 self.quote_ident(&version_property.column_name),
375 self.placeholder(params.len())
376 ));
377 }
378
379 Ok(CompiledQuery {
380 sql: format!(
381 "DELETE FROM {} WHERE {}",
382 self.quote_ident(&entity.table_name),
383 predicates.join(" AND ")
384 ),
385 params,
386 comment: None,
387 })
388 }
389
390 fn compile_recover(
391 &self,
392 entity: &EntityDescriptor,
393 command: &RecoverCommand,
394 ) -> Result<CompiledQuery, SqlCompileError> {
395 if command.expected_version >= 0 {
396 return Err(SqlCompileError::InvalidRecoverVersion(
397 command.expected_version,
398 ));
399 }
400
401 let id_property = entity
402 .id_property()
403 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
404 let version_property = entity
405 .version_property()
406 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
407 let params = vec![
408 Value::I64(-command.expected_version + 1),
409 command.id.clone(),
410 Value::I64(command.expected_version),
411 ];
412
413 Ok(CompiledQuery {
414 sql: format!(
415 "UPDATE {} SET {} = {} WHERE {} = {} AND {} = {}",
416 self.quote_ident(&entity.table_name),
417 self.quote_ident(&version_property.column_name),
418 self.placeholder(1),
419 self.quote_ident(&id_property.column_name),
420 self.placeholder(2),
421 self.quote_ident(&version_property.column_name),
422 self.placeholder(3),
423 ),
424 params,
425 comment: None,
426 })
427 }
428
429 fn column_sql(
430 &self,
431 entity: &EntityDescriptor,
432 field: &str,
433 ) -> Result<String, SqlCompileError> {
434 let property = entity
435 .property_by_name(field)
436 .ok_or_else(|| SqlCompileError::UnknownField(field.to_owned()))?;
437 Ok(self.quote_ident(&property.column_name))
438 }
439
440 fn order_by_sql(
441 &self,
442 entity: &EntityDescriptor,
443 order_by: &OrderBy,
444 params: &mut Vec<Value>,
445 ) -> Result<String, SqlCompileError> {
446 let field = if let Some(expr) = &order_by.expr {
447 self.compile_expr(entity, expr, params)?
448 } else {
449 self.column_sql(entity, &order_by.field)?
450 };
451 let direction = match order_by.direction {
452 SortDirection::Asc => "ASC",
453 SortDirection::Desc => "DESC",
454 };
455 Ok(format!("{field} {direction}"))
456 }
457
458 fn select_projection(
459 &self,
460 entity: &EntityDescriptor,
461 query: &SelectQuery,
462 params: &mut Vec<Value>,
463 ) -> Result<String, SqlCompileError> {
464 let property_projection = |property: &PropertyDescriptor| {
465 let column = self.quote_ident(&property.column_name);
466 if property.column_name == property.name {
467 column
468 } else {
469 format!("{column} AS {}", self.quote_ident(&property.name))
470 }
471 };
472
473 if query.projection.is_empty()
474 && query.expr_projection.is_empty()
475 && query.raw_projections.is_empty()
476 && query.dynamic_properties.is_empty()
477 {
478 return Ok(entity
479 .properties
480 .iter()
481 .map(property_projection)
482 .collect::<Vec<_>>()
483 .join(", "));
484 }
485 let mut parts = query
486 .projection
487 .iter()
488 .map(|field| {
489 let property = entity
490 .property_by_name(field)
491 .ok_or_else(|| SqlCompileError::UnknownField(field.to_owned()))?;
492 Ok(property_projection(property))
493 })
494 .collect::<Result<Vec<_>, _>>()?;
495 for projection in &query.expr_projection {
496 let expr = self.compile_expr(entity, &projection.expr, params)?;
497 parts.push(format!("{expr} AS {}", self.quote_ident(&projection.alias)));
498 }
499 for projection in query
500 .raw_projections
501 .iter()
502 .chain(query.dynamic_properties.iter())
503 {
504 parts.push(format!(
505 "{} AS {}",
506 projection.raw_sql_segment,
507 self.quote_ident(&projection.property_name)
508 ));
509 }
510 Ok(parts.join(", "))
511 }
512
513 fn aggregate_projection(
514 &self,
515 entity: &EntityDescriptor,
516 query: &SelectQuery,
517 params: &mut Vec<Value>,
518 ) -> Result<String, SqlCompileError> {
519 let mut parts = Vec::new();
520 for field in query.group_by.iter().chain(query.projection.iter()) {
521 let column = self.column_sql(entity, field)?;
522 if !parts.contains(&column) {
523 parts.push(column);
524 }
525 }
526 for projection in &query.expr_projection {
527 let expr = self.compile_expr(entity, &projection.expr, params)?;
528 let aliased = format!("{expr} AS {}", self.quote_ident(&projection.alias));
529 if !parts.contains(&aliased) {
530 parts.push(aliased);
531 }
532 }
533 for projection in query
534 .raw_projections
535 .iter()
536 .chain(query.dynamic_properties.iter())
537 {
538 let aliased = format!(
539 "{} AS {}",
540 projection.raw_sql_segment,
541 self.quote_ident(&projection.property_name)
542 );
543 if !parts.contains(&aliased) {
544 parts.push(aliased);
545 }
546 }
547 parts.extend(
548 query
549 .aggregates
550 .iter()
551 .map(|aggregate| {
552 let field = if aggregate.function == AggregateFunction::Count
553 && aggregate.field == "*"
554 {
555 "*".to_owned()
556 } else {
557 self.column_sql(entity, &aggregate.field)?
558 };
559 let call = self.aggregate_call_sql(aggregate.function, &field);
560 Ok(format!("{call} AS {}", self.quote_ident(&aggregate.alias)))
561 })
562 .collect::<Result<Vec<_>, _>>()?,
563 );
564 Ok(parts.join(", "))
565 }
566
567 fn aggregate_call_sql(&self, function: AggregateFunction, field: &str) -> String {
568 let function_sql = self.aggregate_function_sql(function);
569 format!("{function_sql}({field})")
570 }
571
572 fn aggregate_function_sql(&self, function: AggregateFunction) -> &'static str {
573 match function {
574 AggregateFunction::Count => "COUNT",
575 AggregateFunction::Sum => "SUM",
576 AggregateFunction::Avg => "AVG",
577 AggregateFunction::Min => "MIN",
578 AggregateFunction::Max => "MAX",
579 AggregateFunction::Stddev => "STDDEV",
580 AggregateFunction::StddevPop => "STDDEV_POP",
581 AggregateFunction::VarSamp => "VAR_SAMP",
582 AggregateFunction::VarPop => "VAR_POP",
583 AggregateFunction::BitAnd => "BIT_AND",
584 AggregateFunction::BitOr => "BIT_OR",
585 AggregateFunction::BitXor => "BIT_XOR",
586 }
587 }
588
589 fn compile_expr(
590 &self,
591 entity: &EntityDescriptor,
592 expr: &Expr,
593 params: &mut Vec<Value>,
594 ) -> Result<String, SqlCompileError> {
595 match expr {
596 Expr::Column(name) => self.column_sql(entity, name),
597 Expr::Value(value) => {
598 params.push(value.clone());
599 Ok(self.placeholder(params.len()))
600 }
601 Expr::Function { function, args } => {
602 self.compile_function(entity, *function, args, params)
603 }
604 Expr::Binary { left, op, right } => {
605 if matches!(
606 op,
607 BinaryOp::In | BinaryOp::NotIn | BinaryOp::InLarge | BinaryOp::NotInLarge
608 ) {
609 return self.compile_in(entity, left, *op, right, params);
610 }
611 let lhs = self.compile_expr(entity, left, params)?;
612 let rhs = self.compile_expr(entity, right, params)?;
613 let op = match op {
614 BinaryOp::Eq => "=",
615 BinaryOp::Ne => "!=",
616 BinaryOp::Gt => ">",
617 BinaryOp::Gte => ">=",
618 BinaryOp::Lt => "<",
619 BinaryOp::Lte => "<=",
620 BinaryOp::Like => "LIKE",
621 BinaryOp::NotLike => "NOT LIKE",
622 BinaryOp::In | BinaryOp::NotIn | BinaryOp::InLarge | BinaryOp::NotInLarge => {
623 unreachable!()
624 }
625 };
626 Ok(format!("({lhs} {op} {rhs})"))
627 }
628 Expr::SubQuery {
629 left,
630 op,
631 entity: sub_entity,
632 query,
633 } => self.compile_subquery(entity, left, *op, sub_entity, query, params),
634 Expr::Between { expr, lower, upper } => {
635 let expr = self.compile_expr(entity, expr, params)?;
636 let lower = self.compile_expr(entity, lower, params)?;
637 let upper = self.compile_expr(entity, upper, params)?;
638 Ok(format!("({expr} BETWEEN {lower} AND {upper})"))
639 }
640 Expr::IsNull(expr) => {
641 let expr = self.compile_expr(entity, expr, params)?;
642 Ok(format!("({expr} IS NULL)"))
643 }
644 Expr::IsNotNull(expr) => {
645 let expr = self.compile_expr(entity, expr, params)?;
646 Ok(format!("({expr} IS NOT NULL)"))
647 }
648 Expr::And(parts) => self.compile_joined(entity, parts, "AND", params),
649 Expr::Or(parts) => self.compile_joined(entity, parts, "OR", params),
650 Expr::Not(expr) => {
651 let expr = self.compile_expr(entity, expr, params)?;
652 Ok(format!("(NOT {expr})"))
653 }
654 }
655 }
656
657 fn compile_function(
658 &self,
659 entity: &EntityDescriptor,
660 function: ExprFunction,
661 args: &[Expr],
662 params: &mut Vec<Value>,
663 ) -> Result<String, SqlCompileError> {
664 match function {
665 ExprFunction::Soundex => {
666 let [arg] = args else {
667 return Err(SqlCompileError::InvalidFunctionArguments(
668 "SOUNDEX expects exactly one argument".to_owned(),
669 ));
670 };
671 let arg = self.compile_expr(entity, arg, params)?;
672 Ok(format!("SOUNDEX({arg})"))
673 }
674 ExprFunction::Gbk => {
675 let [arg] = args else {
676 return Err(SqlCompileError::InvalidFunctionArguments(
677 "GBK expects exactly one argument".to_owned(),
678 ));
679 };
680 let arg = self.compile_expr(entity, arg, params)?;
681 Ok(format!("convert_to({arg}, 'GBK')"))
682 }
683 ExprFunction::Count if args.is_empty() => Ok("COUNT(*)".to_owned()),
684 ExprFunction::Count => self.compile_single_arg_function(entity, "COUNT", args, params),
685 ExprFunction::Sum => self.compile_single_arg_function(entity, "SUM", args, params),
686 ExprFunction::Avg => self.compile_single_arg_function(entity, "AVG", args, params),
687 ExprFunction::Min => self.compile_single_arg_function(entity, "MIN", args, params),
688 ExprFunction::Max => self.compile_single_arg_function(entity, "MAX", args, params),
689 ExprFunction::Stddev => {
690 self.compile_single_arg_function(entity, "STDDEV", args, params)
691 }
692 ExprFunction::StddevPop => {
693 self.compile_single_arg_function(entity, "STDDEV_POP", args, params)
694 }
695 ExprFunction::VarSamp => {
696 self.compile_single_arg_function(entity, "VAR_SAMP", args, params)
697 }
698 ExprFunction::VarPop => {
699 self.compile_single_arg_function(entity, "VAR_POP", args, params)
700 }
701 ExprFunction::BitAnd => {
702 self.compile_single_arg_function(entity, "BIT_AND", args, params)
703 }
704 ExprFunction::BitOr => self.compile_single_arg_function(entity, "BIT_OR", args, params),
705 ExprFunction::BitXor => {
706 self.compile_single_arg_function(entity, "BIT_XOR", args, params)
707 }
708 }
709 }
710
711 fn compile_single_arg_function(
712 &self,
713 entity: &EntityDescriptor,
714 function: &str,
715 args: &[Expr],
716 params: &mut Vec<Value>,
717 ) -> Result<String, SqlCompileError> {
718 let [arg] = args else {
719 return Err(SqlCompileError::InvalidFunctionArguments(format!(
720 "{function} expects exactly one argument"
721 )));
722 };
723 let arg = self.compile_expr(entity, arg, params)?;
724 Ok(format!("{function}({arg})"))
725 }
726
727 fn compile_subquery(
728 &self,
729 entity: &EntityDescriptor,
730 left: &Expr,
731 op: BinaryOp,
732 sub_entity: &EntityDescriptor,
733 query: &SelectQuery,
734 params: &mut Vec<Value>,
735 ) -> Result<String, SqlCompileError> {
736 let lhs = self.compile_expr(entity, left, params)?;
737 let operator = match op {
738 BinaryOp::In | BinaryOp::InLarge => "IN",
739 BinaryOp::NotIn | BinaryOp::NotInLarge => "NOT IN",
740 _ => return Err(SqlCompileError::InvalidSubQueryOperator(format!("{op:?}"))),
741 };
742 let subquery = self.compile_select_sql(sub_entity, query, params)?;
743 Ok(format!("({lhs} {operator} ({subquery}))"))
744 }
745
746 fn compile_joined(
747 &self,
748 entity: &EntityDescriptor,
749 parts: &[Expr],
750 joiner: &str,
751 params: &mut Vec<Value>,
752 ) -> Result<String, SqlCompileError> {
753 let compiled = parts
754 .iter()
755 .map(|part| self.compile_expr(entity, part, params))
756 .collect::<Result<Vec<_>, _>>()?;
757 Ok(format!("({})", compiled.join(&format!(" {joiner} "))))
758 }
759
760 fn compile_in(
761 &self,
762 entity: &EntityDescriptor,
763 left: &Expr,
764 op: BinaryOp,
765 right: &Expr,
766 params: &mut Vec<Value>,
767 ) -> Result<String, SqlCompileError> {
768 let lhs = self.compile_expr(entity, left, params)?;
769 let operator = match op {
770 BinaryOp::In | BinaryOp::InLarge => "IN",
771 BinaryOp::NotIn | BinaryOp::NotInLarge => "NOT IN",
772 _ => unreachable!(),
773 };
774 match right {
775 Expr::Value(Value::List(values)) => {
776 if values.is_empty() {
777 return Err(SqlCompileError::EmptyInList);
778 }
779 let mut placeholders = Vec::with_capacity(values.len());
780 for value in values {
781 params.push(value.clone());
782 placeholders.push(self.placeholder(params.len()));
783 }
784 Ok(format!("({lhs} {operator} ({}))", placeholders.join(", ")))
785 }
786 _ => {
787 let rhs = self.compile_expr(entity, right, params)?;
788 Ok(format!("({lhs} {operator} ({rhs}))"))
789 }
790 }
791 }
792}