sql_middleware/postgres/
config.rs

1use crate::middleware::{ConfigAndPool, DatabaseType, MiddlewarePool, SqlMiddlewareDbError};
2use deadpool_postgres::Config as PgConfig;
3use tokio_postgres::NoTls;
4
5impl ConfigAndPool {
6    /// Asynchronous initializer for `ConfigAndPool` with Postgres
7    ///
8    /// # Errors
9    /// Returns `SqlMiddlewareDbError::ConfigError` if required config fields are missing or `SqlMiddlewareDbError::ConnectionError` if pool creation fails.
10    #[allow(clippy::unused_async)]
11    pub async fn new_postgres(pg_config: PgConfig) -> Result<Self, SqlMiddlewareDbError> {
12        Self::new_postgres_with_translation(pg_config, false).await
13    }
14
15    /// Asynchronous initializer for `ConfigAndPool` with Postgres and optional translation default.
16    ///
17    /// # Errors
18    /// Returns `SqlMiddlewareDbError::ConfigError` if required config fields are missing or `SqlMiddlewareDbError::ConnectionError` if pool creation fails.
19    ///
20    /// Warning: translation skips placeholders inside quoted strings, comments, and dollar-quoted
21    /// blocks via a lightweight state machine; it may miss edge cases in complex SQL (e.g.,
22    /// PL/pgSQL bodies). Prefer backend-specific SQL instead of relying on translation:
23    /// ```rust
24    /// # use sql_middleware::prelude::*;
25    /// let query = match &conn {
26    ///     MiddlewarePoolConnection::Postgres { .. } => r#"$function$
27    /// BEGIN
28    ///     RETURN ($1 ~ $q$[\t\r\n\v\\]$q$);
29    /// END;
30    /// $function$"#,
31    ///     MiddlewarePoolConnection::Sqlite { .. } | MiddlewarePoolConnection::Turso { .. } => {
32    ///         include_str!("../sql/functions/sqlite/03_sp_get_scores.sql")
33    ///     }
34    /// };
35    /// ```
36    #[allow(clippy::unused_async)]
37    pub async fn new_postgres_with_translation(
38        pg_config: PgConfig,
39        translate_placeholders: bool,
40    ) -> Result<Self, SqlMiddlewareDbError> {
41        // Validate all required config fields are present
42        if pg_config.dbname.is_none() {
43            return Err(SqlMiddlewareDbError::ConfigError(
44                "dbname is required".to_string(),
45            ));
46        }
47
48        if pg_config.host.is_none() {
49            return Err(SqlMiddlewareDbError::ConfigError(
50                "host is required".to_string(),
51            ));
52        }
53        if pg_config.port.is_none() {
54            return Err(SqlMiddlewareDbError::ConfigError(
55                "port is required".to_string(),
56            ));
57        }
58        if pg_config.user.is_none() {
59            return Err(SqlMiddlewareDbError::ConfigError(
60                "user is required".to_string(),
61            ));
62        }
63        if pg_config.password.is_none() {
64            return Err(SqlMiddlewareDbError::ConfigError(
65                "password is required".to_string(),
66            ));
67        }
68
69        // Attempt to create connection pool
70        let pg_pool = pg_config
71            .create_pool(Some(deadpool_postgres::Runtime::Tokio1), NoTls)
72            .map_err(|e| {
73                SqlMiddlewareDbError::ConnectionError(format!(
74                    "Failed to create Postgres pool: {e}"
75                ))
76            })?;
77
78        Ok(ConfigAndPool {
79            pool: MiddlewarePool::Postgres(pg_pool),
80            db_type: DatabaseType::Postgres,
81            translate_placeholders,
82        })
83    }
84}