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 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}