Skip to main content

teaql_sql/
dialect.rs

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() + &quote_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        // Build CASE statement for each updated field
408        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    /// Compile a GBK sort expression. The default implementation returns an error
920    /// because GBK encoding conversion is dialect-specific. PostgreSQL dialects
921    /// should override this to use `convert_to(arg, 'GBK')`.
922    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        // Default: pass through the column as-is (no GBK conversion).
934        // Dialects with GBK support (e.g. PostgreSQL) should override this method.
935        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}