Skip to main content

sql_middleware/translation/
mod.rs

1use std::borrow::Cow;
2
3mod parsers;
4mod scanner;
5#[cfg(test)]
6mod tests;
7mod translate;
8
9/// Target placeholder style for translation.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum PlaceholderStyle {
12    /// PostgreSQL-style placeholders like `$1`.
13    Postgres,
14    /// SQLite-style placeholders like `?1` (also used by Turso).
15    Sqlite,
16}
17
18/// How to resolve translation for a call relative to the pool default.
19///
20/// # Examples
21/// ```rust
22/// use sql_middleware::prelude::*;
23///
24/// let options = QueryOptions::default()
25///     .with_translation(TranslationMode::ForceOn);
26/// # let _ = options;
27/// ```
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum TranslationMode {
30    /// Follow the pool's default setting.
31    PoolDefault,
32    /// Force translation on, regardless of pool default.
33    ForceOn,
34    /// Force translation off, regardless of pool default.
35    ForceOff,
36}
37
38impl TranslationMode {
39    #[must_use]
40    pub fn resolve(self, pool_default: bool) -> bool {
41        match self {
42            TranslationMode::PoolDefault => pool_default,
43            TranslationMode::ForceOn => true,
44            TranslationMode::ForceOff => false,
45        }
46    }
47}
48
49/// How to resolve prepared execution for a call.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
51pub enum PrepareMode {
52    /// Execute without preparing.
53    #[default]
54    Direct,
55    /// Prepare the statement before execution.
56    Prepared,
57}
58
59/// Per-call options for query/execute paths.
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct QueryOptions {
62    pub translation: TranslationMode,
63    pub prepare: PrepareMode,
64}
65
66impl Default for QueryOptions {
67    fn default() -> Self {
68        Self {
69            translation: TranslationMode::PoolDefault,
70            prepare: PrepareMode::default(),
71        }
72    }
73}
74
75impl QueryOptions {
76    #[must_use]
77    pub fn with_translation(mut self, translation: TranslationMode) -> Self {
78        self.translation = translation;
79        self
80    }
81
82    #[must_use]
83    pub fn with_prepare(mut self, prepare: PrepareMode) -> Self {
84        self.prepare = prepare;
85        self
86    }
87}
88
89/// Translate placeholders between Postgres-style `$N` and SQLite-style `?N`.
90///
91/// Warning: translation skips quoted strings, comments, and dollar-quoted blocks via a lightweight
92/// state machine; it may still miss edge cases in complex SQL. For dialect-specific SQL (e.g.,
93/// PL/pgSQL bodies), prefer backend-specific SQL instead of relying on translation:
94/// ```rust
95/// # use sql_middleware::prelude::*;
96/// # async fn demo(conn: &mut MiddlewarePoolConnection) -> Result<(), SqlMiddlewareDbError> {
97/// let query = match conn {
98///     MiddlewarePoolConnection::Postgres { .. } => r#"$function$
99/// BEGIN
100///     RETURN ($1 ~ $q$[\t\r\n\v\\]$q$);
101/// END;
102/// $function$"#,
103///     MiddlewarePoolConnection::Sqlite { .. } | MiddlewarePoolConnection::Turso { .. } => {
104///         include_str!("../sql/functions/sqlite/03_sp_get_scores.sql")
105///     }
106/// };
107/// # let _ = query;
108/// # Ok(())
109/// # }
110/// ```
111/// Returns a borrowed `Cow` when no changes are needed.
112#[must_use]
113pub fn translate_placeholders(sql: &str, target: PlaceholderStyle, enabled: bool) -> Cow<'_, str> {
114    translate::translate_sql(sql, target, enabled)
115}