Skip to main content

sofie_db/
raw.rs

1use std::marker::PhantomData;
2
3use sofie_core::{Cast, Error, Result, SqlValue};
4
5use crate::{BoxFuture, Decoding, Read, RowSink, Source};
6
7/// `'static` text takes input only as parameters.
8pub struct Sql<'r, T> {
9    read: &'r Read,
10    statement: &'static str,
11    params: Vec<SqlValue>,
12    marker: PhantomData<fn() -> T>,
13}
14
15impl Read {
16    /// Read-only; an owned area cannot use it, as nothing would keep it to its
17    /// principal's records.
18    #[must_use]
19    pub fn sql<T: Source>(&self, sql: &'static str) -> Sql<'_, T> {
20        Sql {
21            read: self,
22            statement: sql,
23            params: Vec::new(),
24            marker: PhantomData,
25        }
26    }
27}
28
29impl<T: Source> Sql<'_, T> {
30    #[must_use]
31    pub fn bind<V: Cast>(mut self, value: &V) -> Self {
32        value.encode(&mut self.params);
33        self
34    }
35
36    pub async fn all(self) -> Result<Vec<T>> {
37        if self.read.owner().is_some() {
38            return Err(Error::internal_message(format!(
39                "raw SQL cannot be kept to one owner's records; read `{}` with the query \
40                 builder in an owned area",
41                std::any::type_name::<T>()
42            )));
43        }
44        let role = cfg!(feature = "module-roles").then_some(<T::Base as crate::Aggregate>::MODULE);
45        let mut rows = Decoding::new(T::column_types(), T::decode);
46        self.read
47            .query_read_only(role, self.statement, &self.params, &mut rows)
48            .await?;
49        Ok(rows.into_decoded())
50    }
51}
52
53pub(crate) fn refuse_read_only<'a>(sql: &str) -> BoxFuture<'a, Result<()>> {
54    Box::pin(std::future::ready(Err(Error::internal_message(format!(
55        "this database cannot run raw SQL read-only, so `{sql}` was not run"
56    )))))
57}
58
59impl Read {
60    async fn query_read_only(
61        &self,
62        role: Option<&str>,
63        sql: &str,
64        params: &[SqlValue],
65        rows: &mut dyn RowSink,
66    ) -> Result<()> {
67        #[cfg(feature = "live")]
68        crate::live::read_anything();
69        let driver = self.db.driver();
70        #[cfg(feature = "etag")]
71        if crate::read_versions::tracking() {
72            let mut fingerprinted = crate::read_versions::Fingerprinted::new(rows);
73            driver
74                .query_read_only(role, sql, params, &mut fingerprinted)
75                .await?;
76            fingerprinted.record();
77            return Ok(());
78        }
79        driver.query_read_only(role, sql, params, rows).await
80    }
81}