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