qail_core/transpiler/
traits.rs1pub const RESERVED_WORDS: &[&str] = &[
5 "order", "group", "user", "table", "select", "from", "where", "join",
6 "left", "right", "inner", "outer", "on", "and", "or", "not", "null",
7 "true", "false", "limit", "offset", "as", "in", "is", "like", "between",
8 "having", "union", "all", "distinct", "case", "when", "then", "else", "end",
9 "create", "alter", "drop", "insert", "update", "delete", "index", "key",
10 "primary", "foreign", "references", "default", "constraint", "check",
11];
12
13pub fn escape_identifier(name: &str) -> String {
16 let lower = name.to_lowercase();
17 let needs_escaping = RESERVED_WORDS.contains(&lower.as_str())
18 || name.chars().any(|c| !c.is_alphanumeric() && c != '_')
19 || name.chars().next().map(|c| c.is_numeric()).unwrap_or(false);
20
21 if needs_escaping {
22 format!("\"{}\"", name.replace('"', "\"\""))
23 } else {
24 name.to_string()
25 }
26}
27
28pub trait SqlGenerator {
30 fn quote_identifier(&self, name: &str) -> String;
32 fn placeholder(&self, index: usize) -> String;
34 fn fuzzy_operator(&self) -> &str;
36 fn bool_literal(&self, val: bool) -> String;
38 fn string_concat(&self, parts: &[&str]) -> String;
40 fn limit_offset(&self, limit: Option<usize>, offset: Option<usize>) -> String;
42 fn json_access(&self, col: &str, path: &[&str]) -> String {
46 let mut parts = vec![self.quote_identifier(col)];
47 for key in path {
48 parts.push(self.quote_identifier(key));
49 }
50 parts.join(".")
51 }
52 fn json_contains(&self, col: &str, value: &str) -> String {
55 format!("{} @> {}", col, value)
56 }
57 fn json_key_exists(&self, col: &str, key: &str) -> String {
60 format!("{} ? {}", col, key)
61 }
62
63 fn json_exists(&self, col: &str, path: &str) -> String {
65 format!("JSON_EXISTS({}, '{}')", col, path)
66 }
67
68 fn json_query(&self, col: &str, path: &str) -> String {
70 format!("JSON_QUERY({}, '{}')", col, path)
71 }
72
73 fn json_value(&self, col: &str, path: &str) -> String {
75 format!("JSON_VALUE({}, '{}')", col, path)
76 }
77}