1use rust_ef::provider::ISqlGenerator;
4
5#[derive(Debug, Clone)]
7pub struct PostgresSqlGenerator;
8
9impl PostgresSqlGenerator {
10 pub fn new() -> Self {
11 Self
12 }
13}
14
15impl Default for PostgresSqlGenerator {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21impl ISqlGenerator for PostgresSqlGenerator {
22 fn select(&self, table: &str, columns: &[&str]) -> String {
23 let cols = if columns.is_empty() {
24 "*".to_string()
25 } else {
26 columns
27 .iter()
28 .map(|c| self.quote_identifier(c))
29 .collect::<Vec<_>>()
30 .join(", ")
31 };
32 format!("SELECT {} FROM {}", cols, self.quote_identifier(table))
33 }
34
35 fn insert(&self, table: &str, columns: &[&str], returning: bool) -> String {
36 let cols = columns
37 .iter()
38 .map(|c| self.quote_identifier(c))
39 .collect::<Vec<_>>()
40 .join(", ");
41 let placeholders: Vec<String> = (1..=columns.len())
42 .map(|i| self.parameter_placeholder(i))
43 .collect();
44 let returning_clause = if returning {
45 " RETURNING *".to_string()
46 } else {
47 String::new()
48 };
49 format!(
50 "INSERT INTO {} ({}) VALUES ({}){}",
51 self.quote_identifier(table),
52 cols,
53 placeholders.join(", "),
54 returning_clause
55 )
56 }
57
58 fn insert_batch(&self, table: &str, columns: &[&str], row_count: usize) -> String {
59 let cols = columns
60 .iter()
61 .map(|c| self.quote_identifier(c))
62 .collect::<Vec<_>>()
63 .join(", ");
64 let cols_per_row = columns.len();
65 let mut idx = 1usize;
66 let rows: Vec<String> = (0..row_count)
67 .map(|_| {
68 let ph: Vec<String> = (0..cols_per_row)
69 .map(|_| {
70 let p = self.parameter_placeholder(idx);
71 idx += 1;
72 p
73 })
74 .collect();
75 format!("({})", ph.join(", "))
76 })
77 .collect();
78 format!(
79 "INSERT INTO {} ({}) VALUES {} RETURNING *",
80 self.quote_identifier(table),
81 cols,
82 rows.join(", "),
83 )
84 }
85
86 fn upsert_batch(
87 &self,
88 table: &str,
89 columns: &[&str],
90 conflict_cols: &[&str],
91 row_count: usize,
92 ) -> String {
93 let cols = columns
94 .iter()
95 .map(|c| self.quote_identifier(c))
96 .collect::<Vec<_>>()
97 .join(", ");
98 let cols_per_row = columns.len();
99 let mut idx = 1usize;
100 let rows: Vec<String> = (0..row_count)
101 .map(|_| {
102 let ph: Vec<String> = (0..cols_per_row)
103 .map(|_| {
104 let p = self.parameter_placeholder(idx);
105 idx += 1;
106 p
107 })
108 .collect();
109 format!("({})", ph.join(", "))
110 })
111 .collect();
112 let conflict = conflict_cols
113 .iter()
114 .map(|c| self.quote_identifier(c))
115 .collect::<Vec<_>>()
116 .join(", ");
117 let update_cols: Vec<&str> = columns
118 .iter()
119 .filter(|c| !conflict_cols.contains(c))
120 .copied()
121 .collect();
122 let sets = update_cols
123 .iter()
124 .map(|c| {
125 format!(
126 "{} = EXCLUDED.{}",
127 self.quote_identifier(c),
128 self.quote_identifier(c)
129 )
130 })
131 .collect::<Vec<_>>()
132 .join(", ");
133 format!(
134 "INSERT INTO {} ({}) VALUES {} ON CONFLICT({}) DO UPDATE SET {} RETURNING *",
135 self.quote_identifier(table),
136 cols,
137 rows.join(", "),
138 conflict,
139 sets,
140 )
141 }
142
143 fn update(&self, table: &str, set_columns: &[&str], where_clause: &str) -> String {
144 let sets: Vec<String> = set_columns
145 .iter()
146 .enumerate()
147 .map(|(i, c)| {
148 format!(
149 "{} = {}",
150 self.quote_identifier(c),
151 self.parameter_placeholder(i + 1)
152 )
153 })
154 .collect();
155 format!(
156 "UPDATE {} SET {} WHERE {}",
157 self.quote_identifier(table),
158 sets.join(", "),
159 where_clause
160 )
161 }
162
163 fn delete(&self, table: &str, where_clause: &str) -> String {
164 format!(
165 "DELETE FROM {} WHERE {}",
166 self.quote_identifier(table),
167 where_clause
168 )
169 }
170
171 fn create_table(&self, table: &str, columns: &[(String, String)]) -> String {
172 let col_defs: Vec<String> = columns
173 .iter()
174 .map(|(name, type_def)| format!("{} {}", self.quote_identifier(name), type_def))
175 .collect();
176 format!(
177 "CREATE TABLE {} (\n {}\n)",
178 self.quote_identifier(table),
179 col_defs.join(",\n ")
180 )
181 }
182
183 fn drop_table(&self, table: &str) -> String {
184 format!("DROP TABLE IF EXISTS {}", self.quote_identifier(table))
185 }
186
187 fn pagination(&self, skip: Option<usize>, take: Option<usize>) -> String {
188 match (skip, take) {
189 (Some(s), Some(t)) => format!("OFFSET {} LIMIT {}", s, t),
190 (Some(s), None) => format!("OFFSET {}", s),
191 (None, Some(t)) => format!("LIMIT {}", t),
192 (None, None) => String::new(),
193 }
194 }
195
196 fn parameter_placeholder(&self, index: usize) -> String {
197 format!("${}", index)
198 }
199
200 fn quote_identifier(&self, identifier: &str) -> String {
201 format!("\"{}\"", identifier)
202 }
203
204 fn auto_increment_syntax(&self) -> &'static str {
205 "SERIAL"
206 }
207
208 fn supports_returning(&self) -> bool {
209 true
210 }
211
212 fn uses_numbered_placeholders(&self) -> bool {
213 true
214 }
215}