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