qail_core/transpiler/
traits.rs1pub const RESERVED_WORDS: &[&str] = &[
5 "order",
6 "group",
7 "user",
8 "table",
9 "select",
10 "from",
11 "where",
12 "join",
13 "left",
14 "right",
15 "inner",
16 "outer",
17 "on",
18 "and",
19 "or",
20 "not",
21 "null",
22 "true",
23 "false",
24 "limit",
25 "offset",
26 "as",
27 "in",
28 "is",
29 "like",
30 "between",
31 "having",
32 "union",
33 "all",
34 "distinct",
35 "case",
36 "when",
37 "then",
38 "else",
39 "end",
40 "create",
41 "alter",
42 "drop",
43 "insert",
44 "update",
45 "delete",
46 "index",
47 "key",
48 "primary",
49 "foreign",
50 "references",
51 "default",
52 "constraint",
53 "check",
54];
55
56pub fn escape_identifier(name: &str) -> String {
60 if name.contains('.') {
62 return name
63 .split('.')
64 .map(escape_single_identifier)
65 .collect::<Vec<_>>()
66 .join(".");
67 }
68 escape_single_identifier(name)
69}
70
71fn escape_single_identifier(name: &str) -> String {
73 let lower = name.to_lowercase();
74 let needs_escaping = RESERVED_WORDS.contains(&lower.as_str())
75 || name.chars().any(|c| !c.is_alphanumeric() && c != '_')
76 || name.chars().next().map(|c| c.is_numeric()).unwrap_or(false);
77
78 if needs_escaping {
79 format!("\"{}\"", name.replace('"', "\"\""))
80 } else {
81 name.to_string()
82 }
83}
84
85pub trait SqlGenerator {
87 fn quote_identifier(&self, name: &str) -> String;
89 fn placeholder(&self, index: usize) -> String;
91 fn fuzzy_operator(&self) -> &str;
93 fn bool_literal(&self, val: bool) -> String;
95 fn string_concat(&self, parts: &[&str]) -> String;
97 fn limit_offset(&self, limit: Option<usize>, offset: Option<usize>) -> String;
99 fn json_access(&self, col: &str, path: &[&str]) -> String {
103 let mut parts = vec![self.quote_identifier(col)];
104 for key in path {
105 parts.push(self.quote_identifier(key));
106 }
107 parts.join(".")
108 }
109 fn json_contains(&self, col: &str, value: &str) -> String {
112 format!("{} @> {}", col, value)
113 }
114 fn json_key_exists(&self, col: &str, key: &str) -> String {
117 format!("{} ? {}", col, key)
118 }
119
120 fn json_exists(&self, col: &str, path: &str) -> String {
122 format!("JSON_EXISTS({}, '{}')", col, path)
123 }
124
125 fn json_query(&self, col: &str, path: &str) -> String {
127 format!("JSON_QUERY({}, '{}')", col, path)
128 }
129
130 fn json_value(&self, col: &str, path: &str) -> String {
132 format!("JSON_VALUE({}, '{}')", col, path)
133 }
134}