lsor_core/
row.rs

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        // TODO: For now, the PRQL compiler is unable to handle insert
39        // statements. To work around this, we inject raw SQL into the driver
40        // (and later - during execution - we directly execute the PRQL without
41        // compilation).
42        //
43        // As such, we need to ensure that the driver is empty before we push
44        // anything into it, because raw SQL is obvious not compatible with
45        // PRQL. We have tried using the S-string in PRQL but that also does not
46        // work, because all PRQL expressions must start with select statements.
47        //
48        // This is a temporary solution until the PRQL compiler is able to
49        // handle insert statements.
50        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}