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