Skip to main content

teaql_sql/
dialect.rs

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() + &quote_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}