qail_core/transpiler/sql/
postgres.rs

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