qail_core/transpiler/sql/
postgres.rs

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