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 let Some(value) = command.values.get(&property.name) {
253 params.push(value.clone());
254 assignments.push(format!(
255 "{} = {}",
256 self.quote_ident(&property.column_name),
257 self.placeholder(params.len())
258 ));
259 }
260 }
261
262 if let Some(expected_version) = command.expected_version {
263 let version_property = entity
264 .version_property()
265 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
266 params.push(Value::I64(expected_version + 1));
267 assignments.push(format!(
268 "{} = {}",
269 self.quote_ident(&version_property.column_name),
270 self.placeholder(params.len())
271 ));
272 }
273
274 if assignments.is_empty() {
275 return Err(SqlCompileError::EmptyMutation("update".to_owned()));
276 }
277
278 params.push(command.id.clone());
279 let mut predicates = vec![format!(
280 "{} = {}",
281 self.quote_ident(&id_property.column_name),
282 self.placeholder(params.len())
283 )];
284
285 if let Some(expected_version) = command.expected_version {
286 let version_property = entity
287 .version_property()
288 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
289 params.push(Value::I64(expected_version));
290 predicates.push(format!(
291 "{} = {}",
292 self.quote_ident(&version_property.column_name),
293 self.placeholder(params.len())
294 ));
295 }
296
297 Ok(CompiledQuery {
298 sql: format!(
299 "UPDATE {} SET {} WHERE {}",
300 self.quote_ident(&entity.table_name),
301 assignments.join(", "),
302 predicates.join(" AND ")
303 ),
304 params,
305 comment: None,
306 })
307 }
308
309 fn compile_delete(
310 &self,
311 entity: &EntityDescriptor,
312 command: &DeleteCommand,
313 ) -> Result<CompiledQuery, SqlCompileError> {
314 let id_property = entity
315 .id_property()
316 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
317 let mut params = Vec::new();
318
319 if command.soft_delete {
320 let version_property = entity
321 .version_property()
322 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
323 params.push(match command.expected_version {
324 Some(version) => Value::I64(-(version + 1)),
325 None => Value::I64(-1),
326 });
327
328 params.push(command.id.clone());
329 let mut predicates = vec![format!(
330 "{} = {}",
331 self.quote_ident(&id_property.column_name),
332 self.placeholder(params.len())
333 )];
334
335 if let Some(expected_version) = command.expected_version {
336 params.push(Value::I64(expected_version));
337 predicates.push(format!(
338 "{} = {}",
339 self.quote_ident(&version_property.column_name),
340 self.placeholder(params.len())
341 ));
342 }
343
344 return Ok(CompiledQuery {
345 sql: format!(
346 "UPDATE {} SET {} = {} WHERE {}",
347 self.quote_ident(&entity.table_name),
348 self.quote_ident(&version_property.column_name),
349 self.placeholder(1),
350 predicates.join(" AND ")
351 ),
352 params,
353 comment: None,
354 });
355 }
356
357 params.push(command.id.clone());
358 let mut predicates = vec![format!(
359 "{} = {}",
360 self.quote_ident(&id_property.column_name),
361 self.placeholder(params.len())
362 )];
363
364 if let Some(expected_version) = command.expected_version {
365 let version_property = entity
366 .version_property()
367 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
368 params.push(Value::I64(expected_version));
369 predicates.push(format!(
370 "{} = {}",
371 self.quote_ident(&version_property.column_name),
372 self.placeholder(params.len())
373 ));
374 }
375
376 Ok(CompiledQuery {
377 sql: format!(
378 "DELETE FROM {} WHERE {}",
379 self.quote_ident(&entity.table_name),
380 predicates.join(" AND ")
381 ),
382 params,
383 comment: None,
384 })
385 }
386
387 fn compile_recover(
388 &self,
389 entity: &EntityDescriptor,
390 command: &RecoverCommand,
391 ) -> Result<CompiledQuery, SqlCompileError> {
392 if command.expected_version >= 0 {
393 return Err(SqlCompileError::InvalidRecoverVersion(
394 command.expected_version,
395 ));
396 }
397
398 let id_property = entity
399 .id_property()
400 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
401 let version_property = entity
402 .version_property()
403 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
404 let params = vec![
405 Value::I64(-command.expected_version + 1),
406 command.id.clone(),
407 Value::I64(command.expected_version),
408 ];
409
410 Ok(CompiledQuery {
411 sql: format!(
412 "UPDATE {} SET {} = {} WHERE {} = {} AND {} = {}",
413 self.quote_ident(&entity.table_name),
414 self.quote_ident(&version_property.column_name),
415 self.placeholder(1),
416 self.quote_ident(&id_property.column_name),
417 self.placeholder(2),
418 self.quote_ident(&version_property.column_name),
419 self.placeholder(3),
420 ),
421 params,
422 comment: None,
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}