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