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}