qail_core/transpiler/sql/
postgres.rs

1use crate::transpiler::traits::SqlGenerator;
2use crate::transpiler::escape_identifier;
3
4/// PostgreSQL Generator (Default).
5pub struct PostgresGenerator;
6
7impl PostgresGenerator {
8    pub fn new() -> Self {
9        Self
10    }
11}
12
13impl SqlGenerator for PostgresGenerator {
14    fn quote_identifier(&self, name: &str) -> String {
15        escape_identifier(name)
16    }
17
18    fn placeholder(&self, index: usize) -> String {
19        format!("${}", index)
20    }
21
22    fn fuzzy_operator(&self) -> &str {
23        "ILIKE"
24    }
25
26    fn bool_literal(&self, val: bool) -> String {
27        if val { "true".to_string() } else { "false".to_string() }
28    }
29
30    fn string_concat(&self, parts: &[&str]) -> String {
31        parts.join(" || ")
32    }
33
34    fn limit_offset(&self, limit: Option<usize>, offset: Option<usize>) -> String {
35        let mut sql = String::new();
36        if let Some(n) = limit {
37            sql.push_str(&format!(" LIMIT {}", n));
38        }
39        if let Some(n) = offset {
40            sql.push_str(&format!(" OFFSET {}", n));
41        }
42        sql
43    }
44
45    fn json_access(&self, col: &str, path: &[&str]) -> String {
46        let mut sql = self.quote_identifier(col);
47        
48        for (i, key) in path.iter().enumerate() {
49            let is_last = i == path.len() - 1;
50            // Use -> (json) for intermediates, ->> (text) for last
51            // Note: If the column is not text, an explicit cast may be required.
52            // Postgres ->> returns text, suitable for comparisons.
53            let op = if is_last { "->>" } else { "->" };
54            sql.push_str(&format!("{}'{}'", op, key));
55        }
56        sql
57    }
58}