1use teaql_core::{
2 AggregateFunction, BinaryOp, DataType, DeleteCommand, EntityDescriptor, Expr, ExprFunction,
3 OrderBy, PropertyDescriptor, RecoverCommand, SelectQuery, SortDirection,
4 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() + "e_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: &teaql_core::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_batch_insert(
238 &self,
239 entity: &EntityDescriptor,
240 command: &teaql_core::BatchInsertCommand,
241 ) -> Result<CompiledQuery, SqlCompileError> {
242 if command.batch_values.is_empty() {
243 return Err(SqlCompileError::EmptyMutation("batch_insert".to_owned()));
244 }
245
246 let mut columns = Vec::new();
247 let first_record = &command.batch_values[0];
248
249 for property in &entity.properties {
250 if first_record.contains_key(&property.name) {
251 columns.push(property.clone());
252 }
253 }
254
255 if columns.is_empty() {
256 return Err(SqlCompileError::EmptyMutation("batch_insert".to_owned()));
257 }
258
259 let column_names: Vec<String> = columns.iter().map(|p| self.quote_ident(&p.column_name)).collect();
260 let mut params = Vec::new();
261 let mut values_clauses = Vec::new();
262
263 for record in &command.batch_values {
264 let mut row_placeholders = Vec::new();
265 for property in &columns {
266 let value = record.get(&property.name).cloned().unwrap_or(teaql_core::Value::Null);
267 params.push(value);
268 row_placeholders.push(self.placeholder(params.len()));
269 }
270 values_clauses.push(format!("({})", row_placeholders.join(", ")));
271 }
272
273 Ok(CompiledQuery {
274 sql: format!(
275 "INSERT INTO {} ({}) VALUES {}",
276 self.quote_ident(&entity.table_name),
277 column_names.join(", "),
278 values_clauses.join(", ")
279 ),
280 params,
281 comment: None,
282 })
283 }
284
285 fn compile_update(
286 &self,
287 entity: &EntityDescriptor,
288 command: &teaql_core::UpdateCommand,
289 ) -> Result<CompiledQuery, SqlCompileError> {
290 let id_property = entity
291 .id_property()
292 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
293 let mut assignments = Vec::new();
294 let mut params = Vec::new();
295
296 for property in &entity.properties {
297 if property.is_id {
298 continue;
299 }
300 if property.is_version && command.expected_version.is_some() {
301 continue;
302 }
303 if let Some(value) = command.values.get(&property.name) {
304 params.push(value.clone());
305 assignments.push(format!(
306 "{} = {}",
307 self.quote_ident(&property.column_name),
308 self.placeholder(params.len())
309 ));
310 }
311 }
312
313 if let Some(expected_version) = command.expected_version {
314 let version_property = entity
315 .version_property()
316 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
317 params.push(Value::I64(expected_version + 1));
318 assignments.push(format!(
319 "{} = {}",
320 self.quote_ident(&version_property.column_name),
321 self.placeholder(params.len())
322 ));
323 }
324
325 if assignments.is_empty() {
326 return Err(SqlCompileError::EmptyMutation("update".to_owned()));
327 }
328
329 params.push(command.id.clone());
330 let mut predicates = vec![format!(
331 "{} = {}",
332 self.quote_ident(&id_property.column_name),
333 self.placeholder(params.len())
334 )];
335
336 if let Some(expected_version) = command.expected_version {
337 let version_property = entity
338 .version_property()
339 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
340 params.push(Value::I64(expected_version));
341 predicates.push(format!(
342 "{} = {}",
343 self.quote_ident(&version_property.column_name),
344 self.placeholder(params.len())
345 ));
346 }
347
348 Ok(CompiledQuery {
349 sql: format!(
350 "UPDATE {} SET {} WHERE {}",
351 self.quote_ident(&entity.table_name),
352 assignments.join(", "),
353 predicates.join(" AND ")
354 ),
355 params,
356 comment: None,
357 })
358 }
359
360 fn compile_batch_update(
361 &self,
362 entity: &EntityDescriptor,
363 command: &teaql_core::BatchUpdateCommand,
364 ) -> Result<CompiledQuery, SqlCompileError> {
365 if command.batch_values.is_empty() {
366 return Err(SqlCompileError::EmptyMutation("batch_update".to_owned()));
367 }
368
369 let id_property = entity
370 .id_property()
371 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
372
373 let mut params = Vec::new();
374 let mut set_clauses = Vec::new();
375
376 for field_name in &command.update_fields {
378 let property = entity.property_by_name(field_name)
379 .ok_or_else(|| SqlCompileError::UnknownField(field_name.clone()))?;
380
381 let mut case_parts = Vec::new();
382 case_parts.push(format!("CASE {}", self.quote_ident(&id_property.column_name)));
383
384 for (i, record) in command.batch_values.iter().enumerate() {
385 let id = &command.batch_ids[i];
386 let val = record.get(field_name).cloned().unwrap_or(teaql_core::Value::Null);
387
388 params.push(id.clone());
389 let id_ph = self.placeholder(params.len());
390
391 params.push(val);
392 let val_ph = self.placeholder(params.len());
393
394 case_parts.push(format!("WHEN {} THEN {}", id_ph, val_ph));
395 }
396
397 case_parts.push(format!("ELSE {} END", self.quote_ident(&property.column_name)));
398 set_clauses.push(format!("{} = {}", self.quote_ident(&property.column_name), case_parts.join(" ")));
399 }
400
401 let mut has_versions = false;
402 if let Some(version_property) = entity.version_property() {
403 let mut case_parts = Vec::new();
404 case_parts.push(format!("CASE {}", self.quote_ident(&id_property.column_name)));
405
406 for (i, exp_ver_opt) in command.batch_expected_versions.iter().enumerate() {
407 if let Some(exp_ver) = exp_ver_opt {
408 has_versions = true;
409 let id = &command.batch_ids[i];
410
411 params.push(id.clone());
412 let id_ph = self.placeholder(params.len());
413
414 params.push(teaql_core::Value::I64(*exp_ver + 1));
415 let val_ph = self.placeholder(params.len());
416
417 case_parts.push(format!("WHEN {} THEN {}", id_ph, val_ph));
418 }
419 }
420
421 if has_versions {
422 case_parts.push(format!("ELSE {} END", self.quote_ident(&version_property.column_name)));
423 set_clauses.push(format!("{} = {}", self.quote_ident(&version_property.column_name), case_parts.join(" ")));
424 }
425 }
426
427 if set_clauses.is_empty() {
428 return Err(SqlCompileError::EmptyMutation("batch_update".to_owned()));
429 }
430
431 let mut in_placeholders = Vec::new();
432 for id in &command.batch_ids {
433 params.push(id.clone());
434 in_placeholders.push(self.placeholder(params.len()));
435 }
436 let mut predicates = vec![format!(
437 "{} IN ({})",
438 self.quote_ident(&id_property.column_name),
439 in_placeholders.join(", ")
440 )];
441
442 if has_versions {
443 let version_property = entity.version_property().unwrap();
444 let mut case_parts = Vec::new();
445 case_parts.push(format!("CASE {}", self.quote_ident(&id_property.column_name)));
446
447 for (i, exp_ver_opt) in command.batch_expected_versions.iter().enumerate() {
448 if let Some(exp_ver) = exp_ver_opt {
449 let id = &command.batch_ids[i];
450
451 params.push(id.clone());
452 let id_ph = self.placeholder(params.len());
453
454 params.push(teaql_core::Value::I64(*exp_ver));
455 let val_ph = self.placeholder(params.len());
456
457 case_parts.push(format!("WHEN {} THEN {}", id_ph, val_ph));
458 }
459 }
460 case_parts.push(format!("ELSE {} END", self.quote_ident(&version_property.column_name)));
461
462 predicates.push(format!(
463 "{} = {}",
464 self.quote_ident(&version_property.column_name),
465 case_parts.join(" ")
466 ));
467 }
468
469 Ok(CompiledQuery {
470 sql: format!(
471 "UPDATE {} SET {} WHERE {}",
472 self.quote_ident(&entity.table_name),
473 set_clauses.join(", "),
474 predicates.join(" AND ")
475 ),
476 params,
477 comment: None,
478 })
479 }
480
481 fn compile_delete(
482 &self,
483 entity: &EntityDescriptor,
484 command: &DeleteCommand,
485 ) -> Result<CompiledQuery, SqlCompileError> {
486 let id_property = entity
487 .id_property()
488 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
489 let mut params = Vec::new();
490
491 if command.soft_delete {
492 let version_property = entity
493 .version_property()
494 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
495 params.push(match command.expected_version {
496 Some(version) => Value::I64(-(version + 1)),
497 None => Value::I64(-1),
498 });
499
500 params.push(command.id.clone());
501 let mut predicates = vec![format!(
502 "{} = {}",
503 self.quote_ident(&id_property.column_name),
504 self.placeholder(params.len())
505 )];
506
507 if let Some(expected_version) = command.expected_version {
508 params.push(Value::I64(expected_version));
509 predicates.push(format!(
510 "{} = {}",
511 self.quote_ident(&version_property.column_name),
512 self.placeholder(params.len())
513 ));
514 }
515
516 return Ok(CompiledQuery {
517 sql: format!(
518 "UPDATE {} SET {} = {} WHERE {}",
519 self.quote_ident(&entity.table_name),
520 self.quote_ident(&version_property.column_name),
521 self.placeholder(1),
522 predicates.join(" AND ")
523 ),
524 params,
525 comment: None,
526 });
527 }
528
529 params.push(command.id.clone());
530 let mut predicates = vec![format!(
531 "{} = {}",
532 self.quote_ident(&id_property.column_name),
533 self.placeholder(params.len())
534 )];
535
536 if let Some(expected_version) = command.expected_version {
537 let version_property = entity
538 .version_property()
539 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
540 params.push(Value::I64(expected_version));
541 predicates.push(format!(
542 "{} = {}",
543 self.quote_ident(&version_property.column_name),
544 self.placeholder(params.len())
545 ));
546 }
547
548 Ok(CompiledQuery {
549 sql: format!(
550 "DELETE FROM {} WHERE {}",
551 self.quote_ident(&entity.table_name),
552 predicates.join(" AND ")
553 ),
554 params,
555 comment: None,
556 })
557 }
558
559 fn compile_recover(
560 &self,
561 entity: &EntityDescriptor,
562 command: &RecoverCommand,
563 ) -> Result<CompiledQuery, SqlCompileError> {
564 if command.expected_version >= 0 {
565 return Err(SqlCompileError::InvalidRecoverVersion(
566 command.expected_version,
567 ));
568 }
569
570 let id_property = entity
571 .id_property()
572 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
573 let version_property = entity
574 .version_property()
575 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
576 let params = vec![
577 Value::I64(-command.expected_version + 1),
578 command.id.clone(),
579 Value::I64(command.expected_version),
580 ];
581
582 Ok(CompiledQuery {
583 sql: format!(
584 "UPDATE {} SET {} = {} WHERE {} = {} AND {} = {}",
585 self.quote_ident(&entity.table_name),
586 self.quote_ident(&version_property.column_name),
587 self.placeholder(1),
588 self.quote_ident(&id_property.column_name),
589 self.placeholder(2),
590 self.quote_ident(&version_property.column_name),
591 self.placeholder(3),
592 ),
593 params,
594 comment: None,
595 })
596 }
597
598 fn column_sql(
599 &self,
600 entity: &EntityDescriptor,
601 field: &str,
602 ) -> Result<String, SqlCompileError> {
603 let property = entity
604 .property_by_name(field)
605 .ok_or_else(|| SqlCompileError::UnknownField(field.to_owned()))?;
606 Ok(self.quote_ident(&property.column_name))
607 }
608
609 fn order_by_sql(
610 &self,
611 entity: &EntityDescriptor,
612 order_by: &OrderBy,
613 params: &mut Vec<Value>,
614 ) -> Result<String, SqlCompileError> {
615 let field = if let Some(expr) = &order_by.expr {
616 self.compile_expr(entity, expr, params)?
617 } else {
618 self.column_sql(entity, &order_by.field)?
619 };
620 let direction = match order_by.direction {
621 SortDirection::Asc => "ASC",
622 SortDirection::Desc => "DESC",
623 };
624 Ok(format!("{field} {direction}"))
625 }
626
627 fn select_projection(
628 &self,
629 entity: &EntityDescriptor,
630 query: &SelectQuery,
631 params: &mut Vec<Value>,
632 ) -> Result<String, SqlCompileError> {
633 let property_projection = |property: &PropertyDescriptor| {
634 let column = self.quote_ident(&property.column_name);
635 if property.column_name == property.name {
636 column
637 } else {
638 format!("{column} AS {}", self.quote_ident(&property.name))
639 }
640 };
641
642 if query.projection.is_empty()
643 && query.expr_projection.is_empty()
644 && query.raw_projections.is_empty()
645 && query.dynamic_properties.is_empty()
646 {
647 return Ok(entity
648 .properties
649 .iter()
650 .map(property_projection)
651 .collect::<Vec<_>>()
652 .join(", "));
653 }
654 let mut parts = query
655 .projection
656 .iter()
657 .map(|field| {
658 let property = entity
659 .property_by_name(field)
660 .ok_or_else(|| SqlCompileError::UnknownField(field.to_owned()))?;
661 Ok(property_projection(property))
662 })
663 .collect::<Result<Vec<_>, _>>()?;
664 for projection in &query.expr_projection {
665 let expr = self.compile_expr(entity, &projection.expr, params)?;
666 parts.push(format!("{expr} AS {}", self.quote_ident(&projection.alias)));
667 }
668 for projection in query
669 .raw_projections
670 .iter()
671 .chain(query.dynamic_properties.iter())
672 {
673 parts.push(format!(
674 "{} AS {}",
675 projection.raw_sql_segment,
676 self.quote_ident(&projection.property_name)
677 ));
678 }
679 Ok(parts.join(", "))
680 }
681
682 fn aggregate_projection(
683 &self,
684 entity: &EntityDescriptor,
685 query: &SelectQuery,
686 params: &mut Vec<Value>,
687 ) -> Result<String, SqlCompileError> {
688 let mut parts = Vec::new();
689 for field in query.group_by.iter().chain(query.projection.iter()) {
690 let column = self.column_sql(entity, field)?;
691 if !parts.contains(&column) {
692 parts.push(column);
693 }
694 }
695 for projection in &query.expr_projection {
696 let expr = self.compile_expr(entity, &projection.expr, params)?;
697 let aliased = format!("{expr} AS {}", self.quote_ident(&projection.alias));
698 if !parts.contains(&aliased) {
699 parts.push(aliased);
700 }
701 }
702 for projection in query
703 .raw_projections
704 .iter()
705 .chain(query.dynamic_properties.iter())
706 {
707 let aliased = format!(
708 "{} AS {}",
709 projection.raw_sql_segment,
710 self.quote_ident(&projection.property_name)
711 );
712 if !parts.contains(&aliased) {
713 parts.push(aliased);
714 }
715 }
716 parts.extend(
717 query
718 .aggregates
719 .iter()
720 .map(|aggregate| {
721 let field = if aggregate.function == AggregateFunction::Count
722 && aggregate.field == "*"
723 {
724 "*".to_owned()
725 } else {
726 self.column_sql(entity, &aggregate.field)?
727 };
728 let call = self.aggregate_call_sql(aggregate.function, &field);
729 Ok(format!("{call} AS {}", self.quote_ident(&aggregate.alias)))
730 })
731 .collect::<Result<Vec<_>, _>>()?,
732 );
733 Ok(parts.join(", "))
734 }
735
736 fn aggregate_call_sql(&self, function: AggregateFunction, field: &str) -> String {
737 let function_sql = self.aggregate_function_sql(function);
738 format!("{function_sql}({field})")
739 }
740
741 fn aggregate_function_sql(&self, function: AggregateFunction) -> &'static str {
742 match function {
743 AggregateFunction::Count => "COUNT",
744 AggregateFunction::Sum => "SUM",
745 AggregateFunction::Avg => "AVG",
746 AggregateFunction::Min => "MIN",
747 AggregateFunction::Max => "MAX",
748 AggregateFunction::Stddev => "STDDEV",
749 AggregateFunction::StddevPop => "STDDEV_POP",
750 AggregateFunction::VarSamp => "VAR_SAMP",
751 AggregateFunction::VarPop => "VAR_POP",
752 AggregateFunction::BitAnd => "BIT_AND",
753 AggregateFunction::BitOr => "BIT_OR",
754 AggregateFunction::BitXor => "BIT_XOR",
755 }
756 }
757
758 fn compile_expr(
759 &self,
760 entity: &EntityDescriptor,
761 expr: &Expr,
762 params: &mut Vec<Value>,
763 ) -> Result<String, SqlCompileError> {
764 match expr {
765 Expr::Column(name) => self.column_sql(entity, name),
766 Expr::Value(value) => {
767 params.push(value.clone());
768 Ok(self.placeholder(params.len()))
769 }
770 Expr::Function { function, args } => {
771 self.compile_function(entity, *function, args, params)
772 }
773 Expr::Binary { left, op, right } => {
774 if matches!(
775 op,
776 BinaryOp::In | BinaryOp::NotIn | BinaryOp::InLarge | BinaryOp::NotInLarge
777 ) {
778 return self.compile_in(entity, left, *op, right, params);
779 }
780 let lhs = self.compile_expr(entity, left, params)?;
781 let rhs = self.compile_expr(entity, right, params)?;
782 let op = match op {
783 BinaryOp::Eq => "=",
784 BinaryOp::Ne => "!=",
785 BinaryOp::Gt => ">",
786 BinaryOp::Gte => ">=",
787 BinaryOp::Lt => "<",
788 BinaryOp::Lte => "<=",
789 BinaryOp::Like => "LIKE",
790 BinaryOp::NotLike => "NOT LIKE",
791 BinaryOp::In | BinaryOp::NotIn | BinaryOp::InLarge | BinaryOp::NotInLarge => {
792 unreachable!()
793 }
794 };
795 Ok(format!("({lhs} {op} {rhs})"))
796 }
797 Expr::SubQuery {
798 left,
799 op,
800 entity: sub_entity,
801 query,
802 } => self.compile_subquery(entity, left, *op, sub_entity, query, params),
803 Expr::Between { expr, lower, upper } => {
804 let expr = self.compile_expr(entity, expr, params)?;
805 let lower = self.compile_expr(entity, lower, params)?;
806 let upper = self.compile_expr(entity, upper, params)?;
807 Ok(format!("({expr} BETWEEN {lower} AND {upper})"))
808 }
809 Expr::IsNull(expr) => {
810 let expr = self.compile_expr(entity, expr, params)?;
811 Ok(format!("({expr} IS NULL)"))
812 }
813 Expr::IsNotNull(expr) => {
814 let expr = self.compile_expr(entity, expr, params)?;
815 Ok(format!("({expr} IS NOT NULL)"))
816 }
817 Expr::And(parts) => self.compile_joined(entity, parts, "AND", params),
818 Expr::Or(parts) => self.compile_joined(entity, parts, "OR", params),
819 Expr::Not(expr) => {
820 let expr = self.compile_expr(entity, expr, params)?;
821 Ok(format!("(NOT {expr})"))
822 }
823 }
824 }
825
826 fn compile_function(
827 &self,
828 entity: &EntityDescriptor,
829 function: ExprFunction,
830 args: &[Expr],
831 params: &mut Vec<Value>,
832 ) -> Result<String, SqlCompileError> {
833 match function {
834 ExprFunction::Soundex => {
835 let [arg] = args else {
836 return Err(SqlCompileError::InvalidFunctionArguments(
837 "SOUNDEX expects exactly one argument".to_owned(),
838 ));
839 };
840 let arg = self.compile_expr(entity, arg, params)?;
841 Ok(format!("SOUNDEX({arg})"))
842 }
843 ExprFunction::Gbk => self.compile_gbk_function(entity, args, params),
844 ExprFunction::Count if args.is_empty() => Ok("COUNT(*)".to_owned()),
845 ExprFunction::Count => self.compile_single_arg_function(entity, "COUNT", args, params),
846 ExprFunction::Sum => self.compile_single_arg_function(entity, "SUM", args, params),
847 ExprFunction::Avg => self.compile_single_arg_function(entity, "AVG", args, params),
848 ExprFunction::Min => self.compile_single_arg_function(entity, "MIN", args, params),
849 ExprFunction::Max => self.compile_single_arg_function(entity, "MAX", args, params),
850 ExprFunction::Stddev => {
851 self.compile_single_arg_function(entity, "STDDEV", args, params)
852 }
853 ExprFunction::StddevPop => {
854 self.compile_single_arg_function(entity, "STDDEV_POP", args, params)
855 }
856 ExprFunction::VarSamp => {
857 self.compile_single_arg_function(entity, "VAR_SAMP", args, params)
858 }
859 ExprFunction::VarPop => {
860 self.compile_single_arg_function(entity, "VAR_POP", args, params)
861 }
862 ExprFunction::BitAnd => {
863 self.compile_single_arg_function(entity, "BIT_AND", args, params)
864 }
865 ExprFunction::BitOr => self.compile_single_arg_function(entity, "BIT_OR", args, params),
866 ExprFunction::BitXor => {
867 self.compile_single_arg_function(entity, "BIT_XOR", args, params)
868 }
869 }
870 }
871
872 fn compile_single_arg_function(
873 &self,
874 entity: &EntityDescriptor,
875 function: &str,
876 args: &[Expr],
877 params: &mut Vec<Value>,
878 ) -> Result<String, SqlCompileError> {
879 let [arg] = args else {
880 return Err(SqlCompileError::InvalidFunctionArguments(format!(
881 "{function} expects exactly one argument"
882 )));
883 };
884 let arg = self.compile_expr(entity, arg, params)?;
885 Ok(format!("{function}({arg})"))
886 }
887
888 fn compile_gbk_function(
892 &self,
893 entity: &EntityDescriptor,
894 args: &[Expr],
895 params: &mut Vec<Value>,
896 ) -> Result<String, SqlCompileError> {
897 let [arg] = args else {
898 return Err(SqlCompileError::InvalidFunctionArguments(
899 "GBK expects exactly one argument".to_owned(),
900 ));
901 };
902 let arg = self.compile_expr(entity, arg, params)?;
905 Ok(arg)
906 }
907
908 fn compile_subquery(
909 &self,
910 entity: &EntityDescriptor,
911 left: &Expr,
912 op: BinaryOp,
913 sub_entity: &EntityDescriptor,
914 query: &SelectQuery,
915 params: &mut Vec<Value>,
916 ) -> Result<String, SqlCompileError> {
917 let lhs = self.compile_expr(entity, left, params)?;
918 let operator = match op {
919 BinaryOp::In | BinaryOp::InLarge => "IN",
920 BinaryOp::NotIn | BinaryOp::NotInLarge => "NOT IN",
921 _ => return Err(SqlCompileError::InvalidSubQueryOperator(format!("{op:?}"))),
922 };
923 let subquery = self.compile_select_sql(sub_entity, query, params)?;
924 Ok(format!("({lhs} {operator} ({subquery}))"))
925 }
926
927 fn compile_joined(
928 &self,
929 entity: &EntityDescriptor,
930 parts: &[Expr],
931 joiner: &str,
932 params: &mut Vec<Value>,
933 ) -> Result<String, SqlCompileError> {
934 let compiled = parts
935 .iter()
936 .map(|part| self.compile_expr(entity, part, params))
937 .collect::<Result<Vec<_>, _>>()?;
938 Ok(format!("({})", compiled.join(&format!(" {joiner} "))))
939 }
940
941 fn compile_in(
942 &self,
943 entity: &EntityDescriptor,
944 left: &Expr,
945 op: BinaryOp,
946 right: &Expr,
947 params: &mut Vec<Value>,
948 ) -> Result<String, SqlCompileError> {
949 let lhs = self.compile_expr(entity, left, params)?;
950 let operator = match op {
951 BinaryOp::In | BinaryOp::InLarge => "IN",
952 BinaryOp::NotIn | BinaryOp::NotInLarge => "NOT IN",
953 _ => unreachable!(),
954 };
955 match right {
956 Expr::Value(Value::List(values)) => {
957 if values.is_empty() {
958 return Err(SqlCompileError::EmptyInList);
959 }
960 let mut placeholders = Vec::with_capacity(values.len());
961 for value in values {
962 params.push(value.clone());
963 placeholders.push(self.placeholder(params.len()));
964 }
965 Ok(format!("({lhs} {operator} ({}))", placeholders.join(", ")))
966 }
967 _ => {
968 let rhs = self.compile_expr(entity, right, params)?;
969 Ok(format!("({lhs} {operator} ({rhs}))"))
970 }
971 }
972 }
973}