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
50fn with_comment(sql: String, comment: Option<&str>) -> String {
51 match comment {
52 Some(comment) if !comment.is_empty() => {
53 let escaped = comment.replace("*/", "* /");
54 format!("/* {escaped} */ {sql}")
55 }
56 _ => sql,
57 }
58}
59
60pub trait SqlDialect {
61 fn kind(&self) -> DatabaseKind;
62 fn quote_ident(&self, ident: &str) -> String;
63 fn placeholder(&self, index: usize) -> String;
64
65 fn schema_setup_sqls(&self) -> &'static [&'static str] {
66 &[]
67 }
68
69 fn schema_type_sql(
70 &self,
71 data_type: DataType,
72 _property: &PropertyDescriptor,
73 ) -> Result<&'static str, SqlCompileError> {
74 match data_type {
75 DataType::Bool => Ok("BOOLEAN"),
76 DataType::I64 | DataType::U64 => Ok("INTEGER"),
77 DataType::F64 => Ok("REAL"),
78 DataType::Decimal => Ok("NUMERIC"),
79 DataType::Text | DataType::Json | DataType::Date | DataType::Timestamp => Ok("TEXT"),
80 }
81 }
82
83 fn column_definition_sql(
84 &self,
85 property: &PropertyDescriptor,
86 ) -> Result<String, SqlCompileError> {
87 let mut parts = vec![
88 self.quote_ident(&property.column_name),
89 self.schema_type_sql(property.data_type, property)?
90 .to_owned(),
91 ];
92
93 if property.is_id {
94 parts.push("PRIMARY KEY".to_owned());
95 }
96 if property.is_id || !property.nullable {
97 parts.push("NOT NULL".to_owned());
98 }
99
100 Ok(parts.join(" "))
101 }
102
103 fn compile_create_table(&self, entity: &EntityDescriptor) -> Result<String, SqlCompileError> {
104 let columns = entity
105 .properties
106 .iter()
107 .map(|property| self.column_definition_sql(property))
108 .collect::<Result<Vec<_>, _>>()?
109 .join(", ");
110 Ok(format!(
111 "CREATE TABLE IF NOT EXISTS {} ({columns})",
112 self.quote_ident(&entity.table_name)
113 ))
114 }
115
116 fn compile_add_column(
117 &self,
118 entity: &EntityDescriptor,
119 property: &PropertyDescriptor,
120 ) -> Result<String, SqlCompileError> {
121 Ok(format!(
122 "ALTER TABLE {} ADD COLUMN {}",
123 self.quote_ident(&entity.table_name),
124 self.column_definition_sql(property)?
125 ))
126 }
127
128 fn compile_select(
129 &self,
130 entity: &EntityDescriptor,
131 query: &SelectQuery,
132 ) -> Result<CompiledQuery, SqlCompileError> {
133 let mut params = Vec::new();
134 let sql = self.compile_select_sql(entity, query, &mut params)?;
135 Ok(CompiledQuery { sql, params })
136 }
137
138 fn compile_select_sql(
139 &self,
140 entity: &EntityDescriptor,
141 query: &SelectQuery,
142 params: &mut Vec<Value>,
143 ) -> Result<String, SqlCompileError> {
144 if let Some(raw_sql) = &query.raw_sql {
145 return Ok(with_comment(raw_sql.clone(), query.comment.as_deref()));
146 }
147
148 let projection = if query.aggregates.is_empty() {
149 self.select_projection(entity, query, params)?
150 } else {
151 self.aggregate_projection(entity, query, params)?
152 };
153
154 let mut sql = format!(
155 "SELECT {projection} FROM {}",
156 self.quote_ident(&entity.table_name)
157 );
158
159 let mut where_parts = Vec::new();
160 if let Some(filter) = &query.filter {
161 where_parts.push(self.compile_expr(entity, filter, params)?);
162 }
163 where_parts.extend(query.raw_sql_search_criteria.iter().cloned());
164 if let Some(json_expr) = &query.json_expr {
165 where_parts.push(json_expr.clone());
166 }
167 if !where_parts.is_empty() {
168 sql.push_str(" WHERE ");
169 sql.push_str(&where_parts.join(" AND "));
170 }
171
172 if !query.group_by.is_empty() {
173 let group_by = query
174 .group_by
175 .iter()
176 .map(|field| self.column_sql(entity, field))
177 .collect::<Result<Vec<_>, _>>()?
178 .join(", ");
179 sql.push_str(" GROUP BY ");
180 sql.push_str(&group_by);
181 }
182
183 if let Some(having) = &query.having {
184 let having_sql = self.compile_expr(entity, having, params)?;
185 sql.push_str(" HAVING ");
186 sql.push_str(&having_sql);
187 }
188
189 if !query.order_by.is_empty() {
190 let order_by = query
191 .order_by
192 .iter()
193 .map(|order| self.order_by_sql(entity, order, params))
194 .collect::<Result<Vec<_>, _>>()?
195 .join(", ");
196 sql.push_str(" ORDER BY ");
197 sql.push_str(&order_by);
198 }
199
200 if let Some(slice) = query.slice {
201 if let Some(limit) = slice.limit {
202 sql.push_str(&format!(" LIMIT {limit}"));
203 }
204 if slice.offset > 0 {
205 sql.push_str(&format!(" OFFSET {}", slice.offset));
206 }
207 }
208
209 Ok(with_comment(sql, query.comment.as_deref()))
210 }
211
212 fn compile_insert(
213 &self,
214 entity: &EntityDescriptor,
215 command: &InsertCommand,
216 ) -> Result<CompiledQuery, SqlCompileError> {
217 let mut columns = Vec::new();
218 let mut placeholders = Vec::new();
219 let mut params = Vec::new();
220
221 for property in &entity.properties {
222 if let Some(value) = command.values.get(&property.name) {
223 columns.push(self.quote_ident(&property.column_name));
224 params.push(value.clone());
225 placeholders.push(self.placeholder(params.len()));
226 }
227 }
228
229 if columns.is_empty() {
230 return Err(SqlCompileError::EmptyMutation("insert".to_owned()));
231 }
232
233 Ok(CompiledQuery {
234 sql: format!(
235 "INSERT INTO {} ({}) VALUES ({})",
236 self.quote_ident(&entity.table_name),
237 columns.join(", "),
238 placeholders.join(", ")
239 ),
240 params,
241 })
242 }
243
244 fn compile_update(
245 &self,
246 entity: &EntityDescriptor,
247 command: &UpdateCommand,
248 ) -> Result<CompiledQuery, SqlCompileError> {
249 let id_property = entity
250 .id_property()
251 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
252 let mut assignments = Vec::new();
253 let mut params = Vec::new();
254
255 for property in &entity.properties {
256 if property.is_id {
257 continue;
258 }
259 if let Some(value) = command.values.get(&property.name) {
260 params.push(value.clone());
261 assignments.push(format!(
262 "{} = {}",
263 self.quote_ident(&property.column_name),
264 self.placeholder(params.len())
265 ));
266 }
267 }
268
269 if let Some(expected_version) = command.expected_version {
270 let version_property = entity
271 .version_property()
272 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
273 params.push(Value::I64(expected_version + 1));
274 assignments.push(format!(
275 "{} = {}",
276 self.quote_ident(&version_property.column_name),
277 self.placeholder(params.len())
278 ));
279 }
280
281 if assignments.is_empty() {
282 return Err(SqlCompileError::EmptyMutation("update".to_owned()));
283 }
284
285 params.push(command.id.clone());
286 let mut predicates = vec![format!(
287 "{} = {}",
288 self.quote_ident(&id_property.column_name),
289 self.placeholder(params.len())
290 )];
291
292 if let Some(expected_version) = command.expected_version {
293 let version_property = entity
294 .version_property()
295 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
296 params.push(Value::I64(expected_version));
297 predicates.push(format!(
298 "{} = {}",
299 self.quote_ident(&version_property.column_name),
300 self.placeholder(params.len())
301 ));
302 }
303
304 Ok(CompiledQuery {
305 sql: format!(
306 "UPDATE {} SET {} WHERE {}",
307 self.quote_ident(&entity.table_name),
308 assignments.join(", "),
309 predicates.join(" AND ")
310 ),
311 params,
312 })
313 }
314
315 fn compile_delete(
316 &self,
317 entity: &EntityDescriptor,
318 command: &DeleteCommand,
319 ) -> Result<CompiledQuery, SqlCompileError> {
320 let id_property = entity
321 .id_property()
322 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
323 let mut params = Vec::new();
324
325 if command.soft_delete {
326 let version_property = entity
327 .version_property()
328 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
329 params.push(match command.expected_version {
330 Some(version) => Value::I64(-(version + 1)),
331 None => Value::I64(-1),
332 });
333
334 params.push(command.id.clone());
335 let mut predicates = vec![format!(
336 "{} = {}",
337 self.quote_ident(&id_property.column_name),
338 self.placeholder(params.len())
339 )];
340
341 if let Some(expected_version) = command.expected_version {
342 params.push(Value::I64(expected_version));
343 predicates.push(format!(
344 "{} = {}",
345 self.quote_ident(&version_property.column_name),
346 self.placeholder(params.len())
347 ));
348 }
349
350 return Ok(CompiledQuery {
351 sql: format!(
352 "UPDATE {} SET {} = {} WHERE {}",
353 self.quote_ident(&entity.table_name),
354 self.quote_ident(&version_property.column_name),
355 self.placeholder(1),
356 predicates.join(" AND ")
357 ),
358 params,
359 });
360 }
361
362 params.push(command.id.clone());
363 let mut predicates = vec![format!(
364 "{} = {}",
365 self.quote_ident(&id_property.column_name),
366 self.placeholder(params.len())
367 )];
368
369 if let Some(expected_version) = command.expected_version {
370 let version_property = entity
371 .version_property()
372 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
373 params.push(Value::I64(expected_version));
374 predicates.push(format!(
375 "{} = {}",
376 self.quote_ident(&version_property.column_name),
377 self.placeholder(params.len())
378 ));
379 }
380
381 Ok(CompiledQuery {
382 sql: format!(
383 "DELETE FROM {} WHERE {}",
384 self.quote_ident(&entity.table_name),
385 predicates.join(" AND ")
386 ),
387 params,
388 })
389 }
390
391 fn compile_recover(
392 &self,
393 entity: &EntityDescriptor,
394 command: &RecoverCommand,
395 ) -> Result<CompiledQuery, SqlCompileError> {
396 if command.expected_version >= 0 {
397 return Err(SqlCompileError::InvalidRecoverVersion(
398 command.expected_version,
399 ));
400 }
401
402 let id_property = entity
403 .id_property()
404 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
405 let version_property = entity
406 .version_property()
407 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
408 let params = vec![
409 Value::I64(-command.expected_version + 1),
410 command.id.clone(),
411 Value::I64(command.expected_version),
412 ];
413
414 Ok(CompiledQuery {
415 sql: format!(
416 "UPDATE {} SET {} = {} WHERE {} = {} AND {} = {}",
417 self.quote_ident(&entity.table_name),
418 self.quote_ident(&version_property.column_name),
419 self.placeholder(1),
420 self.quote_ident(&id_property.column_name),
421 self.placeholder(2),
422 self.quote_ident(&version_property.column_name),
423 self.placeholder(3),
424 ),
425 params,
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}