1use std::fmt;
19
20use sqlx::QueryBuilder;
21
22use crate::{
23 Bind, Column,
24 query::{self, Query},
25};
26
27pub struct Bindings<T: Bind>(Vec<Column<T>>);
33
34impl<T: Bind> PartialEq for Bindings<T> {
35 fn eq(&self, other: &Self) -> bool {
36 if self.0.len() != other.0.len() {
37 return false;
38 }
39
40 for (i, a) in self.0.iter().enumerate() {
41 let Some(b) = other.0.get(i) else {
42 return false;
43 };
44
45 if a.field() != b.field() {
46 return false;
47 }
48
49 if a.sql() != b.sql() {
50 return false;
51 }
52 }
53
54 true
55 }
56}
57
58impl<T: Bind> Eq for Bindings<T> {}
59
60impl<T: Bind> fmt::Debug for Bindings<T> {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 let mut f = f.debug_tuple("Bindings");
63
64 for c in &self.0 {
65 f.field(&c.field());
66 }
67
68 f.finish()
69 }
70}
71
72impl<T: Bind> Bindings<T> {
73 pub fn columns(&self) -> &[Column<T>] {
74 &self.0
75 }
76
77 pub fn empty() -> Self {
78 Self(vec![])
79 }
80}
81
82fn table<T: Bind>() -> String {
83 #[cfg(not(feature = "sqlite"))]
84 return format!("\"{}\".\"{}\"", T::SCHEMA, T::TABLE);
85
86 #[cfg(feature = "sqlite")]
87 return format!("\"{}\"", T::TABLE);
88}
89
90pub fn select<T: Bind>() -> Query<T> {
94 select_by(Column::PrimaryKey(&T::PRIMARY_KEY))
95}
96
97pub fn select_by<T: Bind>(c: Column<T>) -> Query<T> {
101 let mut query = QueryBuilder::new("SELECT\n ");
102
103 let mut separated = query.separated(",\n ");
104
105 separated.push(T::PRIMARY_KEY.sql);
106
107 for fk in T::FOREIGN_KEYS {
108 separated.push(fk.sql);
109 }
110
111 for data in T::DATA_COLUMNS {
112 separated.push(data.sql);
113 }
114
115 for meta in T::TIMESTAMP_COLUMNS {
116 separated.push(meta.sql);
117 }
118
119 query.push(format!("\nFROM\n {}\n", table::<T>()));
120 query.push(format!("WHERE {} = $1", c.sql()));
121
122 Query::new(
123 query::Operation::Select,
124 query::Cardinality::One,
125 query,
126 Bindings(vec![c]),
127 )
128}
129
130pub fn select_all<T: Bind>() -> Query<T> {
134 let mut query = QueryBuilder::new("SELECT\n ");
135
136 let mut separated = query.separated(",\n ");
137
138 separated.push(T::PRIMARY_KEY.sql);
139
140 for fk in T::FOREIGN_KEYS {
141 separated.push(fk.sql);
142 }
143
144 for data in T::DATA_COLUMNS {
145 separated.push(data.sql);
146 }
147
148 for meta in T::TIMESTAMP_COLUMNS {
149 separated.push(meta.sql);
150 }
151
152 query.push(format!("\nFROM\n {}\n", table::<T>()));
153
154 Query::new(
155 query::Operation::Select,
156 query::Cardinality::Many,
157 query,
158 Bindings::empty(),
159 )
160}
161
162pub fn insert<T: Bind>() -> Query<T> {
166 let mut builder = QueryBuilder::new(format!("INSERT INTO {}\n (", table::<T>()));
167
168 let mut bindings = vec![];
169
170 let mut separated = builder.separated(", ");
171
172 separated.push(T::PRIMARY_KEY.sql.to_string());
173 bindings.push(Column::PrimaryKey(&T::PRIMARY_KEY));
174
175 for fk in T::FOREIGN_KEYS {
176 separated.push(fk.sql.to_string());
177 bindings.push(Column::ForeignKey(fk));
178 }
179
180 for data in T::DATA_COLUMNS {
181 separated.push(data.sql.to_string());
182 bindings.push(Column::Data(data));
183 }
184
185 for meta in T::TIMESTAMP_COLUMNS {
186 separated.push(meta.sql.to_string());
187 bindings.push(Column::Timestamp(meta));
188 }
189
190 separated.push_unseparated(")\nVALUES\n (");
191
192 separated.push_unseparated("$1");
193
194 let columns = 1 + T::FOREIGN_KEYS.len() + T::DATA_COLUMNS.len() + T::TIMESTAMP_COLUMNS.len();
195
196 for c in 2..=columns {
197 separated.push(format!("${c}"));
198 }
199
200 builder.push(")");
201
202 Query::new(
203 query::Operation::Insert,
204 query::Cardinality::One,
205 builder,
206 Bindings(bindings),
207 )
208}
209
210pub fn update<T: Bind>() -> Query<T> {
214 let mut builder = QueryBuilder::new(format!("UPDATE {} SET\n ", table::<T>()));
215 let mut bindings = vec![];
216
217 let mut separated = builder.separated(",\n ");
218
219 separated.push(format!("{} = $1", T::PRIMARY_KEY.sql));
220 bindings.push(Column::PrimaryKey(&T::PRIMARY_KEY));
221
222 let mut col = 2;
223
224 for fk in T::FOREIGN_KEYS {
225 separated.push(format!("{} = ${col}", fk.sql));
226 bindings.push(Column::ForeignKey(fk));
227 col += 1;
228 }
229
230 for data in T::DATA_COLUMNS {
231 separated.push(format!("{} = ${col}", data.sql));
232 bindings.push(Column::Data(data));
233 col += 1;
234 }
235
236 for meta in T::TIMESTAMP_COLUMNS {
237 separated.push(format!("{} = ${col}", meta.sql));
238 bindings.push(Column::Timestamp(meta));
239 col += 1;
240 }
241
242 builder.push(format!("\nWHERE\n {} = $1", T::PRIMARY_KEY.sql));
243
244 Query::new(
245 query::Operation::Update,
246 query::Cardinality::One,
247 builder,
248 Bindings(bindings),
249 )
250}
251
252pub fn upsert<T: Bind>() -> Query<T> {
256 let Query {
257 mut builder,
258 bindings,
259 ..
260 } = insert::<T>();
261
262 builder.push("\nON CONFLICT(");
263 builder.push(T::PRIMARY_KEY.sql);
264 builder.push(")\nDO UPDATE SET\n ");
265
266 let mut separated = builder.separated(",\n ");
267
268 for fk in T::FOREIGN_KEYS {
269 separated.push(format!("{} = EXCLUDED.{}", fk.sql, fk.sql));
270 }
271
272 for data in T::DATA_COLUMNS {
273 separated.push(format!("{} = EXCLUDED.{}", data.sql, data.sql));
274 }
275
276 for meta in T::TIMESTAMP_COLUMNS {
277 separated.push(format!("{} = EXCLUDED.{}", meta.sql, meta.sql));
278 }
279
280 Query::new(
281 query::Operation::Upsert,
282 query::Cardinality::One,
283 builder,
284 bindings,
285 )
286}
287
288pub fn delete<T: Bind>() -> Query<T> {
292 delete_by(T::PRIMARY_KEY.as_col())
293}
294
295pub fn delete_by<T: Bind>(c: Column<T>) -> Query<T> {
299 let mut builder = QueryBuilder::new(format!("DELETE FROM {} WHERE ", table::<T>()));
300
301 builder.push(c.sql());
302 builder.push(" = $1");
303
304 Query::new(
305 query::Operation::Delete,
306 query::Cardinality::One,
307 builder,
308 Bindings(vec![Column::PrimaryKey(&T::PRIMARY_KEY)]),
309 )
310}
311
312#[cfg(test)]
313mod tests {
314 use crate::{
315 Bind, Bindable, Column, DataColumn, ForeignKey, PrimaryKey, Table, TimestampColumn,
316 runtime::sql::{self, Bindings},
317 };
318
319 #[derive(sqlx::FromRow)]
320 #[allow(unused)]
321 struct TestTable {
322 id: i32,
323 fk: i32,
324 data: bool,
325 }
326
327 impl Table for TestTable {
328 type PrimaryKey = i32;
329
330 const SCHEMA: &'static str = "public";
331 const TABLE: &'static str = "test";
332
333 const PRIMARY_KEY: PrimaryKey<Self> = PrimaryKey::new("id", "id_sql_col");
334 const FOREIGN_KEYS: &'static [ForeignKey<Self>] = &[ForeignKey::new("fk", "fk_sql_col")];
335 const DATA_COLUMNS: &'static [DataColumn<Self>] =
336 &[DataColumn::new("data", "data_sql_col")];
337 const TIMESTAMP_COLUMNS: &'static [TimestampColumn<Self>] = &[];
338
339 fn pk(&self) -> &Self::PrimaryKey {
340 &self.id
341 }
342 }
343
344 impl Bind for TestTable {
345 fn bind<'q, Q: Bindable<'q>>(&'q self, c: &'q Column<Self>, query: Q) -> crate::Result<Q> {
346 match c.field() {
347 "id" => Ok(query.dyn_bind(self.id)),
348 "fk" => Ok(query.dyn_bind(self.fk)),
349 "data" => Ok(query.dyn_bind(self.data)),
350 _ => unimplemented!(),
351 }
352 }
353 }
354
355 #[test]
356 fn select() {
357 let sql::Query {
358 builder, bindings, ..
359 } = sql::select::<TestTable>();
360
361 assert_eq!(
362 builder.sql(),
363 "SELECT\n id_sql_col,\n fk_sql_col,\n data_sql_col\nFROM\n \"public\".\"test\"\nWHERE id_sql_col = $1"
364 );
365
366 assert_eq!(
367 bindings,
368 Bindings(vec![Column::PrimaryKey(&TestTable::PRIMARY_KEY),])
369 );
370 }
371
372 #[test]
373 fn insert() {
374 let sql::Query {
375 builder, bindings, ..
376 } = sql::insert::<TestTable>();
377
378 assert_eq!(
379 builder.sql(),
380 "INSERT INTO \"public\".\"test\"\n (id_sql_col, fk_sql_col, data_sql_col)\nVALUES\n ($1, $2, $3)"
381 );
382
383 assert_eq!(
384 bindings,
385 Bindings(vec![
386 Column::PrimaryKey(&TestTable::PRIMARY_KEY),
387 Column::ForeignKey(&TestTable::FOREIGN_KEYS[0]),
388 Column::Data(&TestTable::DATA_COLUMNS[0]),
389 ])
390 );
391 }
392
393 #[test]
394 fn update() {
395 let sql::Query {
396 builder, bindings, ..
397 } = sql::update::<TestTable>();
398
399 assert_eq!(
400 builder.sql(),
401 "UPDATE \"public\".\"test\" SET\n id_sql_col = $1,\n fk_sql_col = $2,\n data_sql_col = $3\nWHERE\n id_sql_col = $1"
402 );
403
404 assert_eq!(
405 bindings,
406 Bindings(vec![
407 Column::PrimaryKey(&TestTable::PRIMARY_KEY),
408 Column::ForeignKey(&TestTable::FOREIGN_KEYS[0]),
409 Column::Data(&TestTable::DATA_COLUMNS[0]),
410 ])
411 );
412 }
413
414 #[test]
415 fn upsert() {
416 let sql::Query {
417 builder, bindings, ..
418 } = sql::upsert::<TestTable>();
419
420 assert_eq!(
421 builder.sql(),
422 "INSERT INTO \"public\".\"test\"\n (id_sql_col, fk_sql_col, data_sql_col)\nVALUES\n ($1, $2, $3)\nON CONFLICT(id_sql_col)\nDO UPDATE SET\n fk_sql_col = EXCLUDED.fk_sql_col,\n data_sql_col = EXCLUDED.data_sql_col"
423 );
424
425 assert_eq!(
426 bindings,
427 Bindings(vec![
428 Column::PrimaryKey(&TestTable::PRIMARY_KEY),
429 Column::ForeignKey(&TestTable::FOREIGN_KEYS[0]),
430 Column::Data(&TestTable::DATA_COLUMNS[0]),
431 ])
432 );
433 }
434
435 #[test]
436 fn delete() {
437 let sql::Query {
438 builder, bindings, ..
439 } = sql::delete::<TestTable>();
440
441 assert_eq!(
442 builder.sql(),
443 "DELETE FROM \"public\".\"test\" WHERE id_sql_col = $1"
444 );
445 assert_eq!(
446 bindings,
447 Bindings(vec![Column::PrimaryKey(&TestTable::PRIMARY_KEY),])
448 );
449 }
450}