1use crate::{
2 column::ColumnName,
3 driver::{Driver, PushPrql},
4 table::{Table, TableName},
5};
6
7pub type IsPk = bool;
8
9pub trait Row {
10 fn column_names() -> impl Iterator<Item = (ColumnName, IsPk)>;
11 fn push_column_values(&self, driver: &mut Driver);
12}
13
14pub fn upsert<R>(row: R) -> Upsert<R>
15where
16 R: Table,
17{
18 Upsert {
19 table_name: R::table_name(),
20 row,
21 }
22}
23
24pub fn upsert_into<R>(table_name: TableName, row: R) -> Upsert<R> {
25 Upsert { table_name, row }
26}
27
28pub struct Upsert<R> {
29 pub table_name: TableName,
30 pub row: R,
31}
32
33impl<R> PushPrql for Upsert<R>
34where
35 R: Row,
36{
37 fn push_to_driver(&self, driver: &mut Driver) {
38 assert!(driver.is_empty());
51
52 driver.push("INSERT INTO ");
53 self.table_name.push_to_driver(driver);
54 driver.push(" (");
55 for (i, (column_name, _)) in R::column_names().enumerate() {
56 if i > 0 {
57 driver.push(", ");
58 }
59 column_name.push_to_driver(driver);
60 }
61 driver.push(") VALUES (");
62 self.row.push_column_values(driver);
63 driver.push(") ON CONFLICT (");
64 for (i, (column_name, _)) in R::column_names().filter(|(_, pk)| *pk).enumerate() {
65 if i > 0 {
66 driver.push(", ");
67 }
68 column_name.push_to_driver(driver);
69 }
70 driver.push(") DO UPDATE SET (");
71 for (i, (column_name, _)) in R::column_names().filter(|(_, pk)| !*pk).enumerate() {
72 if i > 0 {
73 driver.push(", ");
74 }
75 column_name.push_to_driver(driver);
76 }
77 driver.push(") = (");
78 for (i, (j, _)) in R::column_names()
79 .enumerate()
80 .filter(|(_, (_, pk))| !*pk)
81 .enumerate()
82 {
83 if i > 0 {
84 driver.push(", ");
85 }
86 driver.push("$");
87 driver.push(j + 1);
88 }
89 driver.push(")");
90 }
91}