sql_middleware/query_builder/
mod.rs

1use std::borrow::Cow;
2
3use crate::executor::QueryTarget;
4use crate::pool::MiddlewarePoolConnection;
5use crate::translation::{QueryOptions, TranslationMode, translate_placeholders};
6use crate::types::RowValues;
7
8mod dml;
9mod select;
10
11/// Fluent builder for query execution with optional placeholder translation.
12pub struct QueryBuilder<'conn, 'q> {
13    pub(crate) target: QueryTarget<'conn>,
14    pub(crate) sql: Cow<'q, str>,
15    pub(crate) params: Cow<'q, [RowValues]>,
16    pub(crate) options: QueryOptions,
17}
18
19impl<'conn, 'q> QueryBuilder<'conn, 'q> {
20    pub(crate) fn new(conn: &'conn mut MiddlewarePoolConnection, sql: &'q str) -> Self {
21        Self {
22            target: conn.into(),
23            sql: Cow::Borrowed(sql),
24            params: Cow::Borrowed(&[]),
25            options: QueryOptions::default(),
26        }
27    }
28
29    pub(crate) fn new_target(target: QueryTarget<'conn>, sql: &'q str) -> Self {
30        Self {
31            target,
32            sql: Cow::Borrowed(sql),
33            params: Cow::Borrowed(&[]),
34            options: QueryOptions::default(),
35        }
36    }
37
38    /// Provide parameters for this statement.
39    #[must_use]
40    pub fn params(mut self, params: &'q [RowValues]) -> Self {
41        self.params = Cow::Borrowed(params);
42        self
43    }
44
45    /// Override translation using `QueryOptions`.
46    #[must_use]
47    pub fn options(mut self, options: QueryOptions) -> Self {
48        self.options = options;
49        self
50    }
51
52    /// Override translation mode directly.
53    ///
54    /// Warning: translation skips placeholders inside quoted strings, comments, and dollar-quoted
55    /// blocks via a lightweight state machine; it may miss edge cases in complex SQL (e.g.,
56    /// PL/pgSQL bodies). Prefer backend-specific SQL instead of relying on translation:
57    /// ```rust
58    /// # use sql_middleware::prelude::*;
59    /// # async fn demo(conn: &mut MiddlewarePoolConnection) -> Result<(), SqlMiddlewareDbError> {
60    /// let query = match conn {
61    ///     MiddlewarePoolConnection::Postgres { .. } => r#"$function$
62    /// BEGIN
63    ///     RETURN ($1 ~ $q$[\t\r\n\v\\]$q$);
64    /// END;
65    /// $function$"#,
66    ///     MiddlewarePoolConnection::Sqlite { .. } | MiddlewarePoolConnection::Turso { .. } => {
67    ///         include_str!("../sql/functions/sqlite/03_sp_get_scores.sql")
68    ///     }
69    /// };
70    /// # let _ = query;
71    /// # Ok(())
72    /// # }
73    /// ```
74    #[must_use]
75    pub fn translation(mut self, translation: TranslationMode) -> Self {
76        self.options.translation = translation;
77        self
78    }
79}
80
81pub(super) fn translate_query_for_target<'a>(
82    target: &QueryTarget<'_>,
83    query: &'a str,
84    params: &[RowValues],
85    options: QueryOptions,
86) -> Cow<'a, str> {
87    if params.is_empty() {
88        return Cow::Borrowed(query);
89    }
90
91    let Some(style) = target.translation_target() else {
92        return Cow::Borrowed(query);
93    };
94
95    let enabled = options.translation.resolve(target.translation_default());
96    translate_placeholders(query, style, enabled)
97}