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