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 !where_parts.is_empty() {
165 sql.push_str(" WHERE ");
166 sql.push_str(&where_parts.join(" AND "));
167 }
168
169 if !query.group_by.is_empty() {
170 let group_by = query
171 .group_by
172 .iter()
173 .map(|field| self.column_sql(entity, field))
174 .collect::<Result<Vec<_>, _>>()?
175 .join(", ");
176 sql.push_str(" GROUP BY ");
177 sql.push_str(&group_by);
178 }
179
180 if let Some(having) = &query.having {
181 let having_sql = self.compile_expr(entity, having, params)?;
182 sql.push_str(" HAVING ");
183 sql.push_str(&having_sql);
184 }
185
186 if !query.order_by.is_empty() {
187 let order_by = query
188 .order_by
189 .iter()
190 .map(|order| self.order_by_sql(entity, order, params))
191 .collect::<Result<Vec<_>, _>>()?
192 .join(", ");
193 sql.push_str(" ORDER BY ");
194 sql.push_str(&order_by);
195 }
196
197 if let Some(slice) = query.slice {
198 if let Some(limit) = slice.limit {
199 sql.push_str(&format!(" LIMIT {limit}"));
200 }
201 if slice.offset > 0 {
202 sql.push_str(&format!(" OFFSET {}", slice.offset));
203 }
204 }
205
206 Ok(with_comment(sql, query.comment.as_deref()))
207 }
208
209 fn compile_insert(
210 &self,
211 entity: &EntityDescriptor,
212 command: &InsertCommand,
213 ) -> Result<CompiledQuery, SqlCompileError> {
214 let mut columns = Vec::new();
215 let mut placeholders = Vec::new();
216 let mut params = Vec::new();
217
218 for property in &entity.properties {
219 if let Some(value) = command.values.get(&property.name) {
220 columns.push(self.quote_ident(&property.column_name));
221 params.push(value.clone());
222 placeholders.push(self.placeholder(params.len()));
223 }
224 }
225
226 if columns.is_empty() {
227 return Err(SqlCompileError::EmptyMutation("insert".to_owned()));
228 }
229
230 Ok(CompiledQuery {
231 sql: format!(
232 "INSERT INTO {} ({}) VALUES ({})",
233 self.quote_ident(&entity.table_name),
234 columns.join(", "),
235 placeholders.join(", ")
236 ),
237 params,
238 })
239 }
240
241 fn compile_update(
242 &self,
243 entity: &EntityDescriptor,
244 command: &UpdateCommand,
245 ) -> Result<CompiledQuery, SqlCompileError> {
246 let id_property = entity
247 .id_property()
248 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
249 let mut assignments = Vec::new();
250 let mut params = Vec::new();
251
252 for property in &entity.properties {
253 if property.is_id {
254 continue;
255 }
256 if let Some(value) = command.values.get(&property.name) {
257 params.push(value.clone());
258 assignments.push(format!(
259 "{} = {}",
260 self.quote_ident(&property.column_name),
261 self.placeholder(params.len())
262 ));
263 }
264 }
265
266 if let Some(expected_version) = command.expected_version {
267 let version_property = entity
268 .version_property()
269 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
270 params.push(Value::I64(expected_version + 1));
271 assignments.push(format!(
272 "{} = {}",
273 self.quote_ident(&version_property.column_name),
274 self.placeholder(params.len())
275 ));
276 }
277
278 if assignments.is_empty() {
279 return Err(SqlCompileError::EmptyMutation("update".to_owned()));
280 }
281
282 params.push(command.id.clone());
283 let mut predicates = vec![format!(
284 "{} = {}",
285 self.quote_ident(&id_property.column_name),
286 self.placeholder(params.len())
287 )];
288
289 if let Some(expected_version) = command.expected_version {
290 let version_property = entity
291 .version_property()
292 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
293 params.push(Value::I64(expected_version));
294 predicates.push(format!(
295 "{} = {}",
296 self.quote_ident(&version_property.column_name),
297 self.placeholder(params.len())
298 ));
299 }
300
301 Ok(CompiledQuery {
302 sql: format!(
303 "UPDATE {} SET {} WHERE {}",
304 self.quote_ident(&entity.table_name),
305 assignments.join(", "),
306 predicates.join(" AND ")
307 ),
308 params,
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 });
357 }
358
359 params.push(command.id.clone());
360 let mut predicates = vec![format!(
361 "{} = {}",
362 self.quote_ident(&id_property.column_name),
363 self.placeholder(params.len())
364 )];
365
366 if let Some(expected_version) = command.expected_version {
367 let version_property = entity
368 .version_property()
369 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
370 params.push(Value::I64(expected_version));
371 predicates.push(format!(
372 "{} = {}",
373 self.quote_ident(&version_property.column_name),
374 self.placeholder(params.len())
375 ));
376 }
377
378 Ok(CompiledQuery {
379 sql: format!(
380 "DELETE FROM {} WHERE {}",
381 self.quote_ident(&entity.table_name),
382 predicates.join(" AND ")
383 ),
384 params,
385 })
386 }
387
388 fn compile_recover(
389 &self,
390 entity: &EntityDescriptor,
391 command: &RecoverCommand,
392 ) -> Result<CompiledQuery, SqlCompileError> {
393 if command.expected_version >= 0 {
394 return Err(SqlCompileError::InvalidRecoverVersion(
395 command.expected_version,
396 ));
397 }
398
399 let id_property = entity
400 .id_property()
401 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
402 let version_property = entity
403 .version_property()
404 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
405 let params = vec![
406 Value::I64(-command.expected_version + 1),
407 command.id.clone(),
408 Value::I64(command.expected_version),
409 ];
410
411 Ok(CompiledQuery {
412 sql: format!(
413 "UPDATE {} SET {} = {} WHERE {} = {} AND {} = {}",
414 self.quote_ident(&entity.table_name),
415 self.quote_ident(&version_property.column_name),
416 self.placeholder(1),
417 self.quote_ident(&id_property.column_name),
418 self.placeholder(2),
419 self.quote_ident(&version_property.column_name),
420 self.placeholder(3),
421 ),
422 params,
423 })
424 }
425
426 fn column_sql(
427 &self,
428 entity: &EntityDescriptor,
429 field: &str,
430 ) -> Result<String, SqlCompileError> {
431 let property = entity
432 .property_by_name(field)
433 .ok_or_else(|| SqlCompileError::UnknownField(field.to_owned()))?;
434 Ok(self.quote_ident(&property.column_name))
435 }
436
437 fn order_by_sql(
438 &self,
439 entity: &EntityDescriptor,
440 order_by: &OrderBy,
441 params: &mut Vec<Value>,
442 ) -> Result<String, SqlCompileError> {
443 let field = if let Some(expr) = &order_by.expr {
444 self.compile_expr(entity, expr, params)?
445 } else {
446 self.column_sql(entity, &order_by.field)?
447 };
448 let direction = match order_by.direction {
449 SortDirection::Asc => "ASC",
450 SortDirection::Desc => "DESC",
451 };
452 Ok(format!("{field} {direction}"))
453 }
454
455 fn select_projection(
456 &self,
457 entity: &EntityDescriptor,
458 query: &SelectQuery,
459 params: &mut Vec<Value>,
460 ) -> Result<String, SqlCompileError> {
461 let property_projection = |property: &PropertyDescriptor| {
462 let column = self.quote_ident(&property.column_name);
463 if property.column_name == property.name {
464 column
465 } else {
466 format!("{column} AS {}", self.quote_ident(&property.name))
467 }
468 };
469
470 if query.projection.is_empty()
471 && query.expr_projection.is_empty()
472 && query.raw_projections.is_empty()
473 && query.dynamic_properties.is_empty()
474 {
475 return Ok(entity
476 .properties
477 .iter()
478 .map(property_projection)
479 .collect::<Vec<_>>()
480 .join(", "));
481 }
482 let mut parts = query
483 .projection
484 .iter()
485 .map(|field| {
486 let property = entity
487 .property_by_name(field)
488 .ok_or_else(|| SqlCompileError::UnknownField(field.to_owned()))?;
489 Ok(property_projection(property))
490 })
491 .collect::<Result<Vec<_>, _>>()?;
492 for projection in &query.expr_projection {
493 let expr = self.compile_expr(entity, &projection.expr, params)?;
494 parts.push(format!("{expr} AS {}", self.quote_ident(&projection.alias)));
495 }
496 for projection in query
497 .raw_projections
498 .iter()
499 .chain(query.dynamic_properties.iter())
500 {
501 parts.push(format!(
502 "{} AS {}",
503 projection.raw_sql_segment,
504 self.quote_ident(&projection.property_name)
505 ));
506 }
507 Ok(parts.join(", "))
508 }
509
510 fn aggregate_projection(
511 &self,
512 entity: &EntityDescriptor,
513 query: &SelectQuery,
514 params: &mut Vec<Value>,
515 ) -> Result<String, SqlCompileError> {
516 let mut parts = Vec::new();
517 for field in query.group_by.iter().chain(query.projection.iter()) {
518 let column = self.column_sql(entity, field)?;
519 if !parts.contains(&column) {
520 parts.push(column);
521 }
522 }
523 for projection in &query.expr_projection {
524 let expr = self.compile_expr(entity, &projection.expr, params)?;
525 let aliased = format!("{expr} AS {}", self.quote_ident(&projection.alias));
526 if !parts.contains(&aliased) {
527 parts.push(aliased);
528 }
529 }
530 for projection in query
531 .raw_projections
532 .iter()
533 .chain(query.dynamic_properties.iter())
534 {
535 let aliased = format!(
536 "{} AS {}",
537 projection.raw_sql_segment,
538 self.quote_ident(&projection.property_name)
539 );
540 if !parts.contains(&aliased) {
541 parts.push(aliased);
542 }
543 }
544 parts.extend(
545 query
546 .aggregates
547 .iter()
548 .map(|aggregate| {
549 let field = if aggregate.function == AggregateFunction::Count
550 && aggregate.field == "*"
551 {
552 "*".to_owned()
553 } else {
554 self.column_sql(entity, &aggregate.field)?
555 };
556 let call = self.aggregate_call_sql(aggregate.function, &field);
557 Ok(format!("{call} AS {}", self.quote_ident(&aggregate.alias)))
558 })
559 .collect::<Result<Vec<_>, _>>()?,
560 );
561 Ok(parts.join(", "))
562 }
563
564 fn aggregate_call_sql(&self, function: AggregateFunction, field: &str) -> String {
565 let function_sql = self.aggregate_function_sql(function);
566 format!("{function_sql}({field})")
567 }
568
569 fn aggregate_function_sql(&self, function: AggregateFunction) -> &'static str {
570 match function {
571 AggregateFunction::Count => "COUNT",
572 AggregateFunction::Sum => "SUM",
573 AggregateFunction::Avg => "AVG",
574 AggregateFunction::Min => "MIN",
575 AggregateFunction::Max => "MAX",
576 AggregateFunction::Stddev => "STDDEV",
577 AggregateFunction::StddevPop => "STDDEV_POP",
578 AggregateFunction::VarSamp => "VAR_SAMP",
579 AggregateFunction::VarPop => "VAR_POP",
580 AggregateFunction::BitAnd => "BIT_AND",
581 AggregateFunction::BitOr => "BIT_OR",
582 AggregateFunction::BitXor => "BIT_XOR",
583 }
584 }
585
586 fn compile_expr(
587 &self,
588 entity: &EntityDescriptor,
589 expr: &Expr,
590 params: &mut Vec<Value>,
591 ) -> Result<String, SqlCompileError> {
592 match expr {
593 Expr::Column(name) => self.column_sql(entity, name),
594 Expr::Value(value) => {
595 params.push(value.clone());
596 Ok(self.placeholder(params.len()))
597 }
598 Expr::Function { function, args } => {
599 self.compile_function(entity, *function, args, params)
600 }
601 Expr::Binary { left, op, right } => {
602 if matches!(
603 op,
604 BinaryOp::In | BinaryOp::NotIn | BinaryOp::InLarge | BinaryOp::NotInLarge
605 ) {
606 return self.compile_in(entity, left, *op, right, params);
607 }
608 let lhs = self.compile_expr(entity, left, params)?;
609 let rhs = self.compile_expr(entity, right, params)?;
610 let op = match op {
611 BinaryOp::Eq => "=",
612 BinaryOp::Ne => "!=",
613 BinaryOp::Gt => ">",
614 BinaryOp::Gte => ">=",
615 BinaryOp::Lt => "<",
616 BinaryOp::Lte => "<=",
617 BinaryOp::Like => "LIKE",
618 BinaryOp::NotLike => "NOT LIKE",
619 BinaryOp::In | BinaryOp::NotIn | BinaryOp::InLarge | BinaryOp::NotInLarge => {
620 unreachable!()
621 }
622 };
623 Ok(format!("({lhs} {op} {rhs})"))
624 }
625 Expr::SubQuery {
626 left,
627 op,
628 entity: sub_entity,
629 query,
630 } => self.compile_subquery(entity, left, *op, sub_entity, query, params),
631 Expr::Between { expr, lower, upper } => {
632 let expr = self.compile_expr(entity, expr, params)?;
633 let lower = self.compile_expr(entity, lower, params)?;
634 let upper = self.compile_expr(entity, upper, params)?;
635 Ok(format!("({expr} BETWEEN {lower} AND {upper})"))
636 }
637 Expr::IsNull(expr) => {
638 let expr = self.compile_expr(entity, expr, params)?;
639 Ok(format!("({expr} IS NULL)"))
640 }
641 Expr::IsNotNull(expr) => {
642 let expr = self.compile_expr(entity, expr, params)?;
643 Ok(format!("({expr} IS NOT NULL)"))
644 }
645 Expr::And(parts) => self.compile_joined(entity, parts, "AND", params),
646 Expr::Or(parts) => self.compile_joined(entity, parts, "OR", params),
647 Expr::Not(expr) => {
648 let expr = self.compile_expr(entity, expr, params)?;
649 Ok(format!("(NOT {expr})"))
650 }
651 }
652 }
653
654 fn compile_function(
655 &self,
656 entity: &EntityDescriptor,
657 function: ExprFunction,
658 args: &[Expr],
659 params: &mut Vec<Value>,
660 ) -> Result<String, SqlCompileError> {
661 match function {
662 ExprFunction::Soundex => {
663 let [arg] = args else {
664 return Err(SqlCompileError::InvalidFunctionArguments(
665 "SOUNDEX expects exactly one argument".to_owned(),
666 ));
667 };
668 let arg = self.compile_expr(entity, arg, params)?;
669 Ok(format!("SOUNDEX({arg})"))
670 }
671 ExprFunction::Gbk => {
672 let [arg] = args else {
673 return Err(SqlCompileError::InvalidFunctionArguments(
674 "GBK expects exactly one argument".to_owned(),
675 ));
676 };
677 let arg = self.compile_expr(entity, arg, params)?;
678 Ok(format!("convert_to({arg}, 'GBK')"))
679 }
680 ExprFunction::Count if args.is_empty() => Ok("COUNT(*)".to_owned()),
681 ExprFunction::Count => self.compile_single_arg_function(entity, "COUNT", args, params),
682 ExprFunction::Sum => self.compile_single_arg_function(entity, "SUM", args, params),
683 ExprFunction::Avg => self.compile_single_arg_function(entity, "AVG", args, params),
684 ExprFunction::Min => self.compile_single_arg_function(entity, "MIN", args, params),
685 ExprFunction::Max => self.compile_single_arg_function(entity, "MAX", args, params),
686 ExprFunction::Stddev => {
687 self.compile_single_arg_function(entity, "STDDEV", args, params)
688 }
689 ExprFunction::StddevPop => {
690 self.compile_single_arg_function(entity, "STDDEV_POP", args, params)
691 }
692 ExprFunction::VarSamp => {
693 self.compile_single_arg_function(entity, "VAR_SAMP", args, params)
694 }
695 ExprFunction::VarPop => {
696 self.compile_single_arg_function(entity, "VAR_POP", args, params)
697 }
698 ExprFunction::BitAnd => {
699 self.compile_single_arg_function(entity, "BIT_AND", args, params)
700 }
701 ExprFunction::BitOr => self.compile_single_arg_function(entity, "BIT_OR", args, params),
702 ExprFunction::BitXor => {
703 self.compile_single_arg_function(entity, "BIT_XOR", args, params)
704 }
705 }
706 }
707
708 fn compile_single_arg_function(
709 &self,
710 entity: &EntityDescriptor,
711 function: &str,
712 args: &[Expr],
713 params: &mut Vec<Value>,
714 ) -> Result<String, SqlCompileError> {
715 let [arg] = args else {
716 return Err(SqlCompileError::InvalidFunctionArguments(format!(
717 "{function} expects exactly one argument"
718 )));
719 };
720 let arg = self.compile_expr(entity, arg, params)?;
721 Ok(format!("{function}({arg})"))
722 }
723
724 fn compile_subquery(
725 &self,
726 entity: &EntityDescriptor,
727 left: &Expr,
728 op: BinaryOp,
729 sub_entity: &EntityDescriptor,
730 query: &SelectQuery,
731 params: &mut Vec<Value>,
732 ) -> Result<String, SqlCompileError> {
733 let lhs = self.compile_expr(entity, left, params)?;
734 let operator = match op {
735 BinaryOp::In | BinaryOp::InLarge => "IN",
736 BinaryOp::NotIn | BinaryOp::NotInLarge => "NOT IN",
737 _ => return Err(SqlCompileError::InvalidSubQueryOperator(format!("{op:?}"))),
738 };
739 let subquery = self.compile_select_sql(sub_entity, query, params)?;
740 Ok(format!("({lhs} {operator} ({subquery}))"))
741 }
742
743 fn compile_joined(
744 &self,
745 entity: &EntityDescriptor,
746 parts: &[Expr],
747 joiner: &str,
748 params: &mut Vec<Value>,
749 ) -> Result<String, SqlCompileError> {
750 let compiled = parts
751 .iter()
752 .map(|part| self.compile_expr(entity, part, params))
753 .collect::<Result<Vec<_>, _>>()?;
754 Ok(format!("({})", compiled.join(&format!(" {joiner} "))))
755 }
756
757 fn compile_in(
758 &self,
759 entity: &EntityDescriptor,
760 left: &Expr,
761 op: BinaryOp,
762 right: &Expr,
763 params: &mut Vec<Value>,
764 ) -> Result<String, SqlCompileError> {
765 let lhs = self.compile_expr(entity, left, params)?;
766 let operator = match op {
767 BinaryOp::In | BinaryOp::InLarge => "IN",
768 BinaryOp::NotIn | BinaryOp::NotInLarge => "NOT IN",
769 _ => unreachable!(),
770 };
771 match right {
772 Expr::Value(Value::List(values)) => {
773 if values.is_empty() {
774 return Err(SqlCompileError::EmptyInList);
775 }
776 let mut placeholders = Vec::with_capacity(values.len());
777 for value in values {
778 params.push(value.clone());
779 placeholders.push(self.placeholder(params.len()));
780 }
781 Ok(format!("({lhs} {operator} ({}))", placeholders.join(", ")))
782 }
783 _ => {
784 let rhs = self.compile_expr(entity, right, params)?;
785 Ok(format!("({lhs} {operator} ({rhs}))"))
786 }
787 }
788 }
789}