1use teaql_core::{
2 Aggregate, AggregateFunction, BinaryOp, DataType, DeleteCommand, EntityDescriptor, Expr,
3 ExprFunction, OrderBy, PropertyDescriptor, RecoverCommand, SelectQuery, SortDirection, Value,
4};
5
6use crate::{CompiledQuery, DatabaseKind, SqlCompileError};
7
8const SQL_KEYWORDS: &[&str] = &[
9 "all", "alter", "and", "as", "asc", "between", "by", "case", "create", "delete", "desc",
10 "distinct", "drop", "exists", "false", "from", "group", "having", "in", "insert", "into", "is",
11 "join", "like", "limit", "not", "null", "offset", "on", "or", "order", "select", "set",
12 "table", "true", "type", "union", "update", "values", "where",
13];
14
15pub fn quote_identifier_if_needed(ident: &str, quote: char) -> String {
16 if is_wrapped_identifier(ident) {
17 return ident.to_owned();
18 }
19 if needs_quoted_identifier(ident) {
20 let quote_string = quote.to_string();
21 let escaped = ident.replace(quote, &(quote_string.clone() + "e_string));
22 return format!("{quote}{escaped}{quote}");
23 }
24 ident.to_owned()
25}
26
27fn is_wrapped_identifier(ident: &str) -> bool {
28 (ident.starts_with('"') && ident.ends_with('"'))
29 || (ident.starts_with('`') && ident.ends_with('`'))
30 || (ident.starts_with('[') && ident.ends_with(']'))
31}
32
33fn needs_quoted_identifier(ident: &str) -> bool {
34 if ident.is_empty()
35 || SQL_KEYWORDS
36 .binary_search(&ident.to_ascii_lowercase().as_str())
37 .is_ok()
38 {
39 return true;
40 }
41 let mut chars = ident.chars();
42 match chars.next() {
43 Some(first) if first == '_' || first.is_ascii_alphabetic() => {}
44 _ => return true,
45 }
46 chars.any(|ch| ch != '_' && !ch.is_ascii_alphanumeric())
47}
48
49pub trait SqlDialect {
50 fn kind(&self) -> DatabaseKind;
51 fn quote_ident(&self, ident: &str) -> String;
52 fn placeholder(&self, index: usize) -> String;
53
54 fn schema_setup_sqls(&self) -> &'static [&'static str] {
55 &[]
56 }
57
58 fn schema_type_sql(
59 &self,
60 data_type: DataType,
61 _property: &PropertyDescriptor,
62 ) -> Result<&'static str, SqlCompileError> {
63 match data_type {
64 DataType::Bool => Ok("BOOLEAN"),
65 DataType::I64 | DataType::U64 => Ok("INTEGER"),
66 DataType::F64 => Ok("REAL"),
67 DataType::Decimal => Ok("NUMERIC"),
68 DataType::Text | DataType::Json | DataType::Date | DataType::Timestamp => Ok("TEXT"),
69 }
70 }
71
72 fn column_definition_sql(
73 &self,
74 property: &PropertyDescriptor,
75 ) -> Result<String, SqlCompileError> {
76 let mut parts = vec![
77 self.quote_ident(&property.column_name),
78 self.schema_type_sql(property.data_type, property)?
79 .to_owned(),
80 ];
81
82 if property.is_id {
83 parts.push("PRIMARY KEY".to_owned());
84 }
85 if property.is_id || !property.nullable {
86 parts.push("NOT NULL".to_owned());
87 }
88
89 Ok(parts.join(" "))
90 }
91
92 fn compile_create_table(&self, entity: &EntityDescriptor) -> Result<String, SqlCompileError> {
93 let columns = entity
94 .properties
95 .iter()
96 .map(|property| self.column_definition_sql(property))
97 .collect::<Result<Vec<_>, _>>()?
98 .join(", ");
99 Ok(format!(
100 "CREATE TABLE IF NOT EXISTS {} ({columns})",
101 self.quote_ident(&entity.table_name)
102 ))
103 }
104
105 fn schema_indexes_sqls(
106 &self,
107 entity: &EntityDescriptor,
108 ) -> Result<Vec<String>, SqlCompileError> {
109 let mut sqls = Vec::new();
110 let table_name_upper = entity.table_name.to_uppercase();
111 let quoted_table = self.quote_ident(&entity.table_name);
112
113 if let Some(version_col) = entity.properties.iter().find(|p| p.is_version) {
114 let default_id = "id".to_string();
115 let id_col = entity
116 .properties
117 .iter()
118 .find(|p| p.is_id)
119 .map(|p| &p.column_name)
120 .unwrap_or(&default_id);
121 let idx_name = format!("PK_{}_ID_VERSION", table_name_upper);
122 sqls.push(format!(
123 "CREATE UNIQUE INDEX IF NOT EXISTS {} ON {} ({}, {})",
124 self.quote_ident(&idx_name),
125 quoted_table,
126 self.quote_ident(id_col),
127 self.quote_ident(&version_col.column_name)
128 ));
129 }
130
131 for p in &entity.properties {
132 if p.name.ends_with("Id")
133 || p.name.ends_with("Time")
134 || p.name.ends_with("_time")
135 || p.name == "create_time"
136 || p.name == "update_time"
137 {
138 let idx_name = format!("IDX_{}_{}", table_name_upper, p.column_name.to_uppercase());
139 sqls.push(format!(
140 "CREATE INDEX IF NOT EXISTS {} ON {} ({})",
141 self.quote_ident(&idx_name),
142 quoted_table,
143 self.quote_ident(&p.column_name)
144 ));
145 }
146 }
147
148 Ok(sqls)
149 }
150
151 fn fallback_default_value_sql(&self, data_type: DataType) -> &'static str {
152 match data_type {
153 DataType::Bool => "FALSE",
154 DataType::I64 | DataType::U64 | DataType::F64 | DataType::Decimal => "0",
155 DataType::Text => "''",
156 DataType::Json => "'{}'",
157 DataType::Date => "'1970-01-01'",
158 DataType::Timestamp => "'1970-01-01 00:00:00Z'",
159 }
160 }
161
162 fn compile_add_column(
163 &self,
164 entity: &EntityDescriptor,
165 property: &PropertyDescriptor,
166 ) -> Result<String, SqlCompileError> {
167 let mut def = self.column_definition_sql(property)?;
168 if !property.nullable && !property.is_id {
169 def.push_str(" DEFAULT ");
170 def.push_str(self.fallback_default_value_sql(property.data_type));
171 }
172 Ok(format!(
173 "ALTER TABLE {} ADD COLUMN {}",
174 self.quote_ident(&entity.table_name),
175 def
176 ))
177 }
178
179 fn compile_select(
180 &self,
181 entity: &EntityDescriptor,
182 query: &SelectQuery,
183 ) -> Result<CompiledQuery, SqlCompileError> {
184 let mut params = Vec::new();
185 let sql = self.compile_select_sql(entity, query, &mut params)?;
186 Ok(CompiledQuery {
187 sql,
188 params,
189 comment: query.comment.clone(),
190 })
191 }
192
193 fn compile_select_sql(
194 &self,
195 entity: &EntityDescriptor,
196 query: &SelectQuery,
197 params: &mut Vec<Value>,
198 ) -> Result<String, SqlCompileError> {
199 if let Some(raw_sql) = &query.raw_sql {
200 return Ok(raw_sql.clone());
201 }
202
203 let projection = self.compile_projection(entity, query, params)?;
204
205 let mut sql = format!(
206 "SELECT {projection} FROM {}",
207 self.quote_ident(&entity.table_name)
208 );
209
210 let mut where_parts = Vec::new();
211 if let Some(filter) = &query.filter {
212 where_parts.push(self.compile_expr(entity, filter, params)?);
213 }
214
215 if let Some(search_text) = &query.search_with_text {
216 let mut or_parts = Vec::new();
217 let like_value = format!("%{}%", search_text);
218 for property in &entity.properties {
219 if property.data_type == teaql_core::DataType::Text {
220 params.push(teaql_core::Value::from(like_value.clone()));
221 or_parts.push(format!(
222 "{} LIKE {}",
223 self.quote_ident(&property.column_name),
224 self.placeholder(params.len())
225 ));
226 }
227 }
228 if !or_parts.is_empty() {
229 where_parts.push(format!("({})", or_parts.join(" OR ")));
230 }
231 }
232
233 where_parts.extend(query.raw_sql_search_criteria.iter().cloned());
234 if !where_parts.is_empty() {
235 sql.push_str(" WHERE ");
236 sql.push_str(&where_parts.join(" AND "));
237 }
238
239 if !query.group_by.is_empty() {
240 let group_by = query
241 .group_by
242 .iter()
243 .map(|field| self.column_sql(entity, field))
244 .collect::<Result<Vec<_>, _>>()?
245 .join(", ");
246 sql.push_str(" GROUP BY ");
247 sql.push_str(&group_by);
248 }
249
250 if let Some(having) = &query.having {
251 let having_sql = self.compile_expr(entity, having, params)?;
252 sql.push_str(" HAVING ");
253 sql.push_str(&having_sql);
254 }
255
256 if !query.order_by.is_empty() {
257 let order_by = query
258 .order_by
259 .iter()
260 .map(|order| self.order_by_sql(entity, order, params))
261 .collect::<Result<Vec<_>, _>>()?
262 .join(", ");
263 sql.push_str(" ORDER BY ");
264 sql.push_str(&order_by);
265 }
266
267 if let Some(slice) = query.slice {
268 if let Some(limit) = slice.limit {
269 sql.push_str(&format!(" LIMIT {limit}"));
270 }
271 if slice.offset > 0 {
272 sql.push_str(&format!(" OFFSET {}", slice.offset));
273 }
274 }
275
276 Ok(sql)
277 }
278
279 fn compile_insert(
280 &self,
281 entity: &EntityDescriptor,
282 command: &teaql_core::InsertCommand,
283 ) -> Result<CompiledQuery, SqlCompileError> {
284 let mut columns = Vec::new();
285 let mut placeholders = Vec::new();
286 let mut params = Vec::new();
287
288 for property in &entity.properties {
289 if let Some(value) = command.values.get(&property.name) {
290 columns.push(self.quote_ident(&property.column_name));
291 params.push(value.clone());
292 placeholders.push(self.placeholder(params.len()));
293 }
294 }
295
296 if columns.is_empty() {
297 return Err(SqlCompileError::EmptyMutation("insert".to_owned()));
298 }
299
300 Ok(CompiledQuery {
301 sql: format!(
302 "INSERT INTO {} ({}) VALUES ({})",
303 self.quote_ident(&entity.table_name),
304 columns.join(", "),
305 placeholders.join(", ")
306 ),
307 params,
308 comment: None,
309 })
310 }
311
312 fn compile_batch_insert(
313 &self,
314 entity: &EntityDescriptor,
315 command: &teaql_core::BatchInsertCommand,
316 ) -> Result<CompiledQuery, SqlCompileError> {
317 if command.batch_values.is_empty() {
318 return Err(SqlCompileError::EmptyMutation("batch_insert".to_owned()));
319 }
320
321 let mut columns = Vec::new();
322 let first_record = &command.batch_values[0];
323
324 for property in &entity.properties {
325 if first_record.contains_key(&property.name) {
326 columns.push(property.clone());
327 }
328 }
329
330 if columns.is_empty() {
331 return Err(SqlCompileError::EmptyMutation("batch_insert".to_owned()));
332 }
333
334 let column_names: Vec<String> = columns
335 .iter()
336 .map(|p| self.quote_ident(&p.column_name))
337 .collect();
338 let mut params = Vec::new();
339 let mut values_clauses = Vec::new();
340
341 for record in &command.batch_values {
342 let mut row_placeholders = Vec::new();
343 for property in &columns {
344 let value = record
345 .get(&property.name)
346 .cloned()
347 .unwrap_or(teaql_core::Value::Null);
348 params.push(value);
349 row_placeholders.push(self.placeholder(params.len()));
350 }
351 values_clauses.push(format!("({})", row_placeholders.join(", ")));
352 }
353
354 Ok(CompiledQuery {
355 sql: format!(
356 "INSERT INTO {} ({}) VALUES {}",
357 self.quote_ident(&entity.table_name),
358 column_names.join(", "),
359 values_clauses.join(", ")
360 ),
361 params,
362 comment: None,
363 })
364 }
365
366 fn compile_update(
367 &self,
368 entity: &EntityDescriptor,
369 command: &teaql_core::UpdateCommand,
370 ) -> Result<CompiledQuery, SqlCompileError> {
371 let id_property = entity
372 .id_property()
373 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
374 let mut assignments = Vec::new();
375 let mut params = Vec::new();
376
377 for property in &entity.properties {
378 if property.is_id {
379 continue;
380 }
381 if property.is_version && command.expected_version.is_some() {
382 continue;
383 }
384 if let Some(value) = command.values.get(&property.name) {
385 params.push(value.clone());
386 assignments.push(format!(
387 "{} = {}",
388 self.quote_ident(&property.column_name),
389 self.placeholder(params.len())
390 ));
391 }
392 }
393
394 if let Some(expected_version) = command.expected_version {
395 let version_property = entity
396 .version_property()
397 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
398 params.push(Value::I64(expected_version + 1));
399 assignments.push(format!(
400 "{} = {}",
401 self.quote_ident(&version_property.column_name),
402 self.placeholder(params.len())
403 ));
404 }
405
406 if assignments.is_empty() {
407 return Err(SqlCompileError::EmptyMutation("update".to_owned()));
408 }
409
410 params.push(command.id.clone());
411 let mut predicates = vec![format!(
412 "{} = {}",
413 self.quote_ident(&id_property.column_name),
414 self.placeholder(params.len())
415 )];
416
417 if let Some(expected_version) = command.expected_version {
418 let version_property = entity
419 .version_property()
420 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
421 params.push(Value::I64(expected_version));
422 predicates.push(format!(
423 "{} = {}",
424 self.quote_ident(&version_property.column_name),
425 self.placeholder(params.len())
426 ));
427 }
428
429 Ok(CompiledQuery {
430 sql: format!(
431 "UPDATE {} SET {} WHERE {}",
432 self.quote_ident(&entity.table_name),
433 assignments.join(", "),
434 predicates.join(" AND ")
435 ),
436 params,
437 comment: None,
438 })
439 }
440
441 fn compile_batch_update(
442 &self,
443 entity: &EntityDescriptor,
444 command: &teaql_core::BatchUpdateCommand,
445 ) -> Result<CompiledQuery, SqlCompileError> {
446 if command.batch_values.is_empty() {
447 return Err(SqlCompileError::EmptyMutation("batch_update".to_owned()));
448 }
449
450 let id_property = entity
451 .id_property()
452 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
453
454 let mut params = Vec::new();
455 let mut set_clauses = Vec::new();
456
457 for field_name in &command.update_fields {
459 let property = entity
460 .property_by_name(field_name)
461 .ok_or_else(|| SqlCompileError::UnknownField(field_name.clone()))?;
462
463 let mut case_parts = Vec::new();
464 case_parts.push(format!(
465 "CASE {}",
466 self.quote_ident(&id_property.column_name)
467 ));
468
469 for (i, record) in command.batch_values.iter().enumerate() {
470 let id = &command.batch_ids[i];
471 let val = record
472 .get(field_name)
473 .cloned()
474 .unwrap_or(teaql_core::Value::Null);
475
476 params.push(id.clone());
477 let id_ph = self.placeholder(params.len());
478
479 params.push(val);
480 let val_ph = self.placeholder(params.len());
481
482 case_parts.push(format!("WHEN {} THEN {}", id_ph, val_ph));
483 }
484
485 case_parts.push(format!(
486 "ELSE {} END",
487 self.quote_ident(&property.column_name)
488 ));
489 set_clauses.push(format!(
490 "{} = {}",
491 self.quote_ident(&property.column_name),
492 case_parts.join(" ")
493 ));
494 }
495
496 let mut has_versions = false;
497 if let Some(version_property) = entity.version_property() {
498 let mut case_parts = Vec::new();
499 case_parts.push(format!(
500 "CASE {}",
501 self.quote_ident(&id_property.column_name)
502 ));
503
504 for (i, exp_ver_opt) in command.batch_expected_versions.iter().enumerate() {
505 if let Some(exp_ver) = exp_ver_opt {
506 has_versions = true;
507 let id = &command.batch_ids[i];
508
509 params.push(id.clone());
510 let id_ph = self.placeholder(params.len());
511
512 params.push(teaql_core::Value::I64(*exp_ver + 1));
513 let val_ph = self.placeholder(params.len());
514
515 case_parts.push(format!("WHEN {} THEN {}", id_ph, val_ph));
516 }
517 }
518
519 if has_versions {
520 case_parts.push(format!(
521 "ELSE {} END",
522 self.quote_ident(&version_property.column_name)
523 ));
524 set_clauses.push(format!(
525 "{} = {}",
526 self.quote_ident(&version_property.column_name),
527 case_parts.join(" ")
528 ));
529 }
530 }
531
532 if set_clauses.is_empty() {
533 return Err(SqlCompileError::EmptyMutation("batch_update".to_owned()));
534 }
535
536 let mut in_placeholders = Vec::new();
537 for id in &command.batch_ids {
538 params.push(id.clone());
539 in_placeholders.push(self.placeholder(params.len()));
540 }
541 let mut predicates = vec![format!(
542 "{} IN ({})",
543 self.quote_ident(&id_property.column_name),
544 in_placeholders.join(", ")
545 )];
546
547 if has_versions {
548 let version_property = entity.version_property().unwrap();
549 let mut case_parts = Vec::new();
550 case_parts.push(format!(
551 "CASE {}",
552 self.quote_ident(&id_property.column_name)
553 ));
554
555 for (i, exp_ver_opt) in command.batch_expected_versions.iter().enumerate() {
556 if let Some(exp_ver) = exp_ver_opt {
557 let id = &command.batch_ids[i];
558
559 params.push(id.clone());
560 let id_ph = self.placeholder(params.len());
561
562 params.push(teaql_core::Value::I64(*exp_ver));
563 let val_ph = self.placeholder(params.len());
564
565 case_parts.push(format!("WHEN {} THEN {}", id_ph, val_ph));
566 }
567 }
568 case_parts.push(format!(
569 "ELSE {} END",
570 self.quote_ident(&version_property.column_name)
571 ));
572
573 predicates.push(format!(
574 "{} = {}",
575 self.quote_ident(&version_property.column_name),
576 case_parts.join(" ")
577 ));
578 }
579
580 Ok(CompiledQuery {
581 sql: format!(
582 "UPDATE {} SET {} WHERE {}",
583 self.quote_ident(&entity.table_name),
584 set_clauses.join(", "),
585 predicates.join(" AND ")
586 ),
587 params,
588 comment: None,
589 })
590 }
591
592 fn compile_delete(
593 &self,
594 entity: &EntityDescriptor,
595 command: &DeleteCommand,
596 ) -> Result<CompiledQuery, SqlCompileError> {
597 let id_property = entity
598 .id_property()
599 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
600 let mut params = Vec::new();
601
602 if command.soft_delete {
603 let version_property = entity
604 .version_property()
605 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
606 params.push(match command.expected_version {
607 Some(version) => Value::I64(-(version + 1)),
608 None => Value::I64(-1),
609 });
610
611 params.push(command.id.clone());
612 let mut predicates = vec![format!(
613 "{} = {}",
614 self.quote_ident(&id_property.column_name),
615 self.placeholder(params.len())
616 )];
617
618 if let Some(expected_version) = command.expected_version {
619 params.push(Value::I64(expected_version));
620 predicates.push(format!(
621 "{} = {}",
622 self.quote_ident(&version_property.column_name),
623 self.placeholder(params.len())
624 ));
625 }
626
627 return Ok(CompiledQuery {
628 sql: format!(
629 "UPDATE {} SET {} = {} WHERE {}",
630 self.quote_ident(&entity.table_name),
631 self.quote_ident(&version_property.column_name),
632 self.placeholder(1),
633 predicates.join(" AND ")
634 ),
635 params,
636 comment: None,
637 });
638 }
639
640 params.push(command.id.clone());
641 let mut predicates = vec![format!(
642 "{} = {}",
643 self.quote_ident(&id_property.column_name),
644 self.placeholder(params.len())
645 )];
646
647 if let Some(expected_version) = command.expected_version {
648 let version_property = entity
649 .version_property()
650 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
651 params.push(Value::I64(expected_version));
652 predicates.push(format!(
653 "{} = {}",
654 self.quote_ident(&version_property.column_name),
655 self.placeholder(params.len())
656 ));
657 }
658
659 Ok(CompiledQuery {
660 sql: format!(
661 "DELETE FROM {} WHERE {}",
662 self.quote_ident(&entity.table_name),
663 predicates.join(" AND ")
664 ),
665 params,
666 comment: None,
667 })
668 }
669
670 fn compile_recover(
671 &self,
672 entity: &EntityDescriptor,
673 command: &RecoverCommand,
674 ) -> Result<CompiledQuery, SqlCompileError> {
675 if command.expected_version >= 0 {
676 return Err(SqlCompileError::InvalidRecoverVersion(
677 command.expected_version,
678 ));
679 }
680
681 let id_property = entity
682 .id_property()
683 .ok_or_else(|| SqlCompileError::MissingIdProperty(entity.name.clone()))?;
684 let version_property = entity
685 .version_property()
686 .ok_or_else(|| SqlCompileError::MissingVersionProperty(entity.name.clone()))?;
687 let params = vec![
688 Value::I64(-command.expected_version + 1),
689 command.id.clone(),
690 Value::I64(command.expected_version),
691 ];
692
693 Ok(CompiledQuery {
694 sql: format!(
695 "UPDATE {} SET {} = {} WHERE {} = {} AND {} = {}",
696 self.quote_ident(&entity.table_name),
697 self.quote_ident(&version_property.column_name),
698 self.placeholder(1),
699 self.quote_ident(&id_property.column_name),
700 self.placeholder(2),
701 self.quote_ident(&version_property.column_name),
702 self.placeholder(3),
703 ),
704 params,
705 comment: None,
706 })
707 }
708
709 fn column_sql(
710 &self,
711 entity: &EntityDescriptor,
712 field: &str,
713 ) -> Result<String, SqlCompileError> {
714 let property = entity
715 .property_by_name(field)
716 .ok_or_else(|| SqlCompileError::UnknownField(field.to_owned()))?;
717 Ok(self.quote_ident(&property.column_name))
718 }
719
720 fn order_by_sql(
721 &self,
722 entity: &EntityDescriptor,
723 order_by: &OrderBy,
724 params: &mut Vec<Value>,
725 ) -> Result<String, SqlCompileError> {
726 let field = self.resolve_order_field(entity, order_by, params)?;
727 let direction = match order_by.direction {
728 SortDirection::Asc => "ASC",
729 SortDirection::Desc => "DESC",
730 };
731 Ok(format!("{field} {direction}"))
732 }
733
734 fn select_projection(
735 &self,
736 entity: &EntityDescriptor,
737 query: &SelectQuery,
738 params: &mut Vec<Value>,
739 ) -> Result<String, SqlCompileError> {
740 let property_projection = |property: &PropertyDescriptor| self.column_with_alias(property);
741
742 if query.projection.is_empty()
743 && query.expr_projection.is_empty()
744 && query.raw_projections.is_empty()
745 && query.dynamic_properties.is_empty()
746 {
747 return Ok(entity
748 .properties
749 .iter()
750 .map(property_projection)
751 .collect::<Vec<_>>()
752 .join(", "));
753 }
754 let mut parts = query
755 .projection
756 .iter()
757 .map(|field| {
758 let property = entity
759 .property_by_name(field)
760 .ok_or_else(|| SqlCompileError::UnknownField(field.to_owned()))?;
761 Ok(property_projection(property))
762 })
763 .collect::<Result<Vec<_>, _>>()?;
764 for projection in &query.expr_projection {
765 let expr = self.compile_expr(entity, &projection.expr, params)?;
766 parts.push(format!("{expr} AS {}", self.quote_ident(&projection.alias)));
767 }
768 for projection in query
769 .raw_projections
770 .iter()
771 .chain(query.dynamic_properties.iter())
772 {
773 parts.push(format!(
774 "{} AS {}",
775 projection.raw_sql_segment,
776 self.quote_ident(&projection.property_name)
777 ));
778 }
779 Ok(parts.join(", "))
780 }
781
782 fn aggregate_projection(
783 &self,
784 entity: &EntityDescriptor,
785 query: &SelectQuery,
786 params: &mut Vec<Value>,
787 ) -> Result<String, SqlCompileError> {
788 let mut parts = Vec::new();
789 for field in query.group_by.iter().chain(query.projection.iter()) {
790 let column = self.column_sql(entity, field)?;
791 if !parts.contains(&column) {
792 parts.push(column);
793 }
794 }
795 for projection in &query.expr_projection {
796 let expr = self.compile_expr(entity, &projection.expr, params)?;
797 let aliased = format!("{expr} AS {}", self.quote_ident(&projection.alias));
798 if !parts.contains(&aliased) {
799 parts.push(aliased);
800 }
801 }
802 for projection in query
803 .raw_projections
804 .iter()
805 .chain(query.dynamic_properties.iter())
806 {
807 let aliased = format!(
808 "{} AS {}",
809 projection.raw_sql_segment,
810 self.quote_ident(&projection.property_name)
811 );
812 if !parts.contains(&aliased) {
813 parts.push(aliased);
814 }
815 }
816 parts.extend(
817 query
818 .aggregates
819 .iter()
820 .map(|aggregate| {
821 let field = self.resolve_aggregate_field(entity, aggregate)?;
822 let call = self.aggregate_call_sql(aggregate.function, &field);
823 Ok(format!("{call} AS {}", self.quote_ident(&aggregate.alias)))
824 })
825 .collect::<Result<Vec<_>, _>>()?,
826 );
827 Ok(parts.join(", "))
828 }
829
830 fn aggregate_call_sql(&self, function: AggregateFunction, field: &str) -> String {
831 let function_sql = self.aggregate_function_sql(function);
832 format!("{function_sql}({field})")
833 }
834
835 fn aggregate_function_sql(&self, function: AggregateFunction) -> &'static str {
836 match function {
837 AggregateFunction::Count => "COUNT",
838 AggregateFunction::Sum => "SUM",
839 AggregateFunction::Avg => "AVG",
840 AggregateFunction::Min => "MIN",
841 AggregateFunction::Max => "MAX",
842 AggregateFunction::Stddev => "STDDEV",
843 AggregateFunction::StddevPop => "STDDEV_POP",
844 AggregateFunction::VarSamp => "VAR_SAMP",
845 AggregateFunction::VarPop => "VAR_POP",
846 AggregateFunction::BitAnd => "BIT_AND",
847 AggregateFunction::BitOr => "BIT_OR",
848 AggregateFunction::BitXor => "BIT_XOR",
849 }
850 }
851
852 fn compile_expr(
853 &self,
854 entity: &EntityDescriptor,
855 expr: &Expr,
856 params: &mut Vec<Value>,
857 ) -> Result<String, SqlCompileError> {
858 match expr {
859 Expr::Column(name) => self.column_sql(entity, name),
860 Expr::Value(value) => {
861 params.push(value.clone());
862 Ok(self.placeholder(params.len()))
863 }
864 Expr::Function { function, args } => {
865 self.compile_function(entity, *function, args, params)
866 }
867 Expr::Binary { left, op, right } => {
868 if matches!(
869 op,
870 BinaryOp::In | BinaryOp::NotIn | BinaryOp::InLarge | BinaryOp::NotInLarge
871 ) {
872 return self.compile_in(entity, left, *op, right, params);
873 }
874 let lhs = self.compile_expr(entity, left, params)?;
875 let rhs = self.compile_expr(entity, right, params)?;
876 let op = match op {
877 BinaryOp::Eq => "=",
878 BinaryOp::Ne => "!=",
879 BinaryOp::Gt => ">",
880 BinaryOp::Gte => ">=",
881 BinaryOp::Lt => "<",
882 BinaryOp::Lte => "<=",
883 BinaryOp::Like => "LIKE",
884 BinaryOp::NotLike => "NOT LIKE",
885 BinaryOp::In | BinaryOp::NotIn | BinaryOp::InLarge | BinaryOp::NotInLarge => {
886 unreachable!()
887 }
888 };
889 Ok(format!("({lhs} {op} {rhs})"))
890 }
891 Expr::SubQuery {
892 left,
893 op,
894 entity: sub_entity,
895 query,
896 } => self.compile_subquery(entity, left, *op, sub_entity, query, params),
897 Expr::Between { expr, lower, upper } => {
898 let expr = self.compile_expr(entity, expr, params)?;
899 let lower = self.compile_expr(entity, lower, params)?;
900 let upper = self.compile_expr(entity, upper, params)?;
901 Ok(format!("({expr} BETWEEN {lower} AND {upper})"))
902 }
903 Expr::IsNull(expr) => {
904 let expr = self.compile_expr(entity, expr, params)?;
905 Ok(format!("({expr} IS NULL)"))
906 }
907 Expr::IsNotNull(expr) => {
908 let expr = self.compile_expr(entity, expr, params)?;
909 Ok(format!("({expr} IS NOT NULL)"))
910 }
911 Expr::And(parts) => self.compile_joined(entity, parts, "AND", params),
912 Expr::Or(parts) => self.compile_joined(entity, parts, "OR", params),
913 Expr::Not(expr) => {
914 let expr = self.compile_expr(entity, expr, params)?;
915 Ok(format!("(NOT {expr})"))
916 }
917 }
918 }
919
920 fn compile_function(
921 &self,
922 entity: &EntityDescriptor,
923 function: ExprFunction,
924 args: &[Expr],
925 params: &mut Vec<Value>,
926 ) -> Result<String, SqlCompileError> {
927 match function {
928 ExprFunction::Soundex => {
929 let [arg] = args else {
930 return Err(SqlCompileError::InvalidFunctionArguments(
931 "SOUNDEX expects exactly one argument".to_owned(),
932 ));
933 };
934 let arg = self.compile_expr(entity, arg, params)?;
935 Ok(format!("SOUNDEX({arg})"))
936 }
937 ExprFunction::Gbk => self.compile_gbk_function(entity, args, params),
938 ExprFunction::Count if args.is_empty() => Ok("COUNT(*)".to_owned()),
939 ExprFunction::Count => self.compile_single_arg_function(entity, "COUNT", args, params),
940 ExprFunction::Sum => self.compile_single_arg_function(entity, "SUM", args, params),
941 ExprFunction::Avg => self.compile_single_arg_function(entity, "AVG", args, params),
942 ExprFunction::Min => self.compile_single_arg_function(entity, "MIN", args, params),
943 ExprFunction::Max => self.compile_single_arg_function(entity, "MAX", args, params),
944 ExprFunction::Stddev => {
945 self.compile_single_arg_function(entity, "STDDEV", args, params)
946 }
947 ExprFunction::StddevPop => {
948 self.compile_single_arg_function(entity, "STDDEV_POP", args, params)
949 }
950 ExprFunction::VarSamp => {
951 self.compile_single_arg_function(entity, "VAR_SAMP", args, params)
952 }
953 ExprFunction::VarPop => {
954 self.compile_single_arg_function(entity, "VAR_POP", args, params)
955 }
956 ExprFunction::BitAnd => {
957 self.compile_single_arg_function(entity, "BIT_AND", args, params)
958 }
959 ExprFunction::BitOr => self.compile_single_arg_function(entity, "BIT_OR", args, params),
960 ExprFunction::BitXor => {
961 self.compile_single_arg_function(entity, "BIT_XOR", args, params)
962 }
963 }
964 }
965
966 fn compile_single_arg_function(
967 &self,
968 entity: &EntityDescriptor,
969 function: &str,
970 args: &[Expr],
971 params: &mut Vec<Value>,
972 ) -> Result<String, SqlCompileError> {
973 let [arg] = args else {
974 return Err(SqlCompileError::InvalidFunctionArguments(format!(
975 "{function} expects exactly one argument"
976 )));
977 };
978 let arg = self.compile_expr(entity, arg, params)?;
979 Ok(format!("{function}({arg})"))
980 }
981
982 fn compile_gbk_function(
986 &self,
987 entity: &EntityDescriptor,
988 args: &[Expr],
989 params: &mut Vec<Value>,
990 ) -> Result<String, SqlCompileError> {
991 let [arg] = args else {
992 return Err(SqlCompileError::InvalidFunctionArguments(
993 "GBK expects exactly one argument".to_owned(),
994 ));
995 };
996 let arg = self.compile_expr(entity, arg, params)?;
999 Ok(arg)
1000 }
1001
1002 fn compile_subquery(
1003 &self,
1004 entity: &EntityDescriptor,
1005 left: &Expr,
1006 op: BinaryOp,
1007 sub_entity: &EntityDescriptor,
1008 query: &SelectQuery,
1009 params: &mut Vec<Value>,
1010 ) -> Result<String, SqlCompileError> {
1011 let lhs = self.compile_expr(entity, left, params)?;
1012 let operator = match op {
1013 BinaryOp::In | BinaryOp::InLarge => "IN",
1014 BinaryOp::NotIn | BinaryOp::NotInLarge => "NOT IN",
1015 _ => return Err(SqlCompileError::InvalidSubQueryOperator(format!("{op:?}"))),
1016 };
1017 let subquery = self.compile_select_sql(sub_entity, query, params)?;
1018 Ok(format!("({lhs} {operator} ({subquery}))"))
1019 }
1020
1021 fn compile_joined(
1022 &self,
1023 entity: &EntityDescriptor,
1024 parts: &[Expr],
1025 joiner: &str,
1026 params: &mut Vec<Value>,
1027 ) -> Result<String, SqlCompileError> {
1028 let compiled = parts
1029 .iter()
1030 .map(|part| self.compile_expr(entity, part, params))
1031 .collect::<Result<Vec<_>, _>>()?;
1032 Ok(format!("({})", compiled.join(&format!(" {joiner} "))))
1033 }
1034
1035 fn compile_in(
1036 &self,
1037 entity: &EntityDescriptor,
1038 left: &Expr,
1039 op: BinaryOp,
1040 right: &Expr,
1041 params: &mut Vec<Value>,
1042 ) -> Result<String, SqlCompileError> {
1043 let lhs = self.compile_expr(entity, left, params)?;
1044 let operator = match op {
1045 BinaryOp::In | BinaryOp::InLarge => "IN",
1046 BinaryOp::NotIn | BinaryOp::NotInLarge => "NOT IN",
1047 _ => unreachable!(),
1048 };
1049 match right {
1050 Expr::Value(Value::List(values)) => {
1051 if values.is_empty() {
1052 return Err(SqlCompileError::EmptyInList);
1053 }
1054 let mut placeholders = Vec::with_capacity(values.len());
1055 for value in values {
1056 params.push(value.clone());
1057 placeholders.push(self.placeholder(params.len()));
1058 }
1059 Ok(format!("({lhs} {operator} ({}))", placeholders.join(", ")))
1060 }
1061 _ => {
1062 let rhs = self.compile_expr(entity, right, params)?;
1063 Ok(format!("({lhs} {operator} ({rhs}))"))
1064 }
1065 }
1066 }
1067
1068 fn compile_projection(
1069 &self,
1070 entity: &EntityDescriptor,
1071 query: &SelectQuery,
1072 params: &mut Vec<Value>,
1073 ) -> Result<String, SqlCompileError> {
1074 match query.aggregates.is_empty() {
1075 true => self.select_projection(entity, query, params),
1076 false => self.aggregate_projection(entity, query, params),
1077 }
1078 }
1079
1080 fn resolve_order_field(
1081 &self,
1082 entity: &EntityDescriptor,
1083 order_by: &OrderBy,
1084 params: &mut Vec<Value>,
1085 ) -> Result<String, SqlCompileError> {
1086 match &order_by.expr {
1087 Some(expr) => self.compile_expr(entity, expr, params),
1088 None => self.column_sql(entity, &order_by.field),
1089 }
1090 }
1091
1092 fn column_with_alias(&self, property: &PropertyDescriptor) -> String {
1093 let column = self.quote_ident(&property.column_name);
1094 match property.column_name == property.name {
1095 true => column,
1096 false => format!("{column} AS {}", self.quote_ident(&property.name)),
1097 }
1098 }
1099
1100 fn resolve_aggregate_field(
1101 &self,
1102 entity: &EntityDescriptor,
1103 aggregate: &Aggregate,
1104 ) -> Result<String, SqlCompileError> {
1105 match aggregate.function == AggregateFunction::Count && aggregate.field == "*" {
1106 true => Ok("*".to_owned()),
1107 false => self.column_sql(entity, &aggregate.field),
1108 }
1109 }
1110}