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}