diff --git a/src/database/db_connection.rs b/src/database/db_connection.rs index a14cbaa052..ed4c455bce 100644 --- a/src/database/db_connection.rs +++ b/src/database/db_connection.rs @@ -458,6 +458,42 @@ impl DatabaseConnection { } } + /// Set the default persistent prepared statement caching behavior + /// + /// When set to Some(false), prepared statement caching will be disabled by default + /// unless explicitly enabled on individual queries. + /// When set to Some(true), prepared statement caching will be enabled by default. + /// When set to None, uses sqlx default behavior (enabled). + /// + /// This is particularly useful when using `from_sqlx_*_pool` methods to create + /// a connection from an existing sqlx pool. + /// + /// # Examples + /// + /// ```ignore + /// let pool = MySqlPoolOptions::new().connect("mysql://...").await?; + /// let mut db = Database::connect_with_pool(pool); + /// db.with_default_persistent(false); // Disable statement caching by default + /// ``` + pub fn with_default_persistent(&mut self, value: bool) -> &mut Self { + match self { + #[cfg(feature = "sqlx-mysql")] + DatabaseConnection::SqlxMySqlPoolConnection(conn) => { + conn.with_default_persistent(value); + } + #[cfg(feature = "sqlx-postgres")] + DatabaseConnection::SqlxPostgresPoolConnection(conn) => { + conn.with_default_persistent(value); + } + #[cfg(feature = "sqlx-sqlite")] + DatabaseConnection::SqlxSqlitePoolConnection(conn) => { + conn.with_default_persistent(value); + } + _ => {} + } + self + } + /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { match self { diff --git a/src/database/mod.rs b/src/database/mod.rs index d8c5937bf4..5f9f572add 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -80,6 +80,9 @@ pub struct ConnectOptions { /// be created using SQLx's [connect_lazy](https://docs.rs/sqlx/latest/sqlx/struct.Pool.html#method.connect_lazy) /// method. pub(crate) connect_lazy: bool, + /// Default value for prepared statement caching when not explicitly set on queries + /// None means use sqlx default (true), Some(false) disables by default, Some(true) enables by default + pub(crate) default_persistent: Option, #[cfg(feature = "sqlx-mysql")] #[debug(skip)] pub(crate) mysql_opts_fn: @@ -194,6 +197,7 @@ impl ConnectOptions { schema_search_path: None, test_before_acquire: true, connect_lazy: false, + default_persistent: None, #[cfg(feature = "sqlx-mysql")] mysql_opts_fn: None, #[cfg(feature = "sqlx-postgres")] @@ -353,6 +357,30 @@ impl ConnectOptions { self.connect_lazy } + /// Set the default persistent prepared statement caching behavior + /// + /// When set to Some(false), prepared statement caching will be disabled by default + /// unless explicitly enabled on individual queries. + /// When set to Some(true), prepared statement caching will be enabled by default. + /// When set to None, uses sqlx default behavior (enabled). + /// + /// # Examples + /// + /// ``` + /// # use sea_orm::ConnectOptions; + /// let mut opt = ConnectOptions::new("protocol://localhost/database"); + /// opt.default_persistent(false); // Disable statement caching by default + /// ``` + pub fn default_persistent(&mut self, value: bool) -> &mut Self { + self.default_persistent = Some(value); + self + } + + /// Get the default persistent prepared statement caching behavior + pub fn get_default_persistent(&self) -> Option { + self.default_persistent + } + #[cfg(feature = "sqlx-mysql")] #[cfg_attr(docsrs, doc(cfg(feature = "sqlx-mysql")))] /// Apply a function to modify the underlying [`MySqlConnectOptions`] before diff --git a/src/database/statement.rs b/src/database/statement.rs index d001ab242d..30fc10b127 100644 --- a/src/database/statement.rs +++ b/src/database/statement.rs @@ -13,6 +13,9 @@ pub struct Statement { /// The database backend this statement is constructed for. /// The SQL dialect and values should be valid for the DbBackend. pub db_backend: DbBackend, + /// Whether to use prepared statement caching (persistent prepared statements) + /// None means use the default behavior, Some(true) enables caching, Some(false) disables it + pub persistent: Option, } /// Any type that can build a [Statement] @@ -31,6 +34,7 @@ impl Statement { sql: stmt.into(), values: None, db_backend, + persistent: None, } } @@ -52,6 +56,7 @@ impl Statement { sql: stmt.0.into(), values: Some(stmt.1), db_backend, + persistent: None, } } } diff --git a/src/database/transaction.rs b/src/database/transaction.rs index 078596622d..673e627bdd 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -19,6 +19,7 @@ pub struct DatabaseTransaction { backend: DbBackend, open: bool, metric_callback: Option, + default_persistent: Option, } impl std::fmt::Debug for DatabaseTransaction { @@ -35,12 +36,14 @@ impl DatabaseTransaction { metric_callback: Option, isolation_level: Option, access_mode: Option, + default_persistent: Option, ) -> Result { let res = DatabaseTransaction { conn, backend, open: true, metric_callback, + default_persistent, }; match *res.conn.lock().await { #[cfg(feature = "sqlx-mysql")] @@ -246,9 +249,14 @@ impl ConnectionTrait for DatabaseTransaction { #[instrument(level = "trace")] #[allow(unused_variables)] - async fn execute(&self, stmt: Statement) -> Result { + async fn execute(&self, mut stmt: Statement) -> Result { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] InnerConnection::MySql(conn) => { @@ -335,9 +343,14 @@ impl ConnectionTrait for DatabaseTransaction { #[instrument(level = "trace")] #[allow(unused_variables)] - async fn query_one(&self, stmt: Statement) -> Result, DbErr> { + async fn query_one(&self, mut stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] InnerConnection::MySql(conn) => { @@ -380,9 +393,14 @@ impl ConnectionTrait for DatabaseTransaction { #[instrument(level = "trace")] #[allow(unused_variables)] - async fn query_all(&self, stmt: Statement) -> Result, DbErr> { + async fn query_all(&self, mut stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + match &mut *self.conn.lock().await { #[cfg(feature = "sqlx-mysql")] InnerConnection::MySql(conn) => { @@ -436,9 +454,14 @@ impl StreamTrait for DatabaseTransaction { #[instrument(level = "trace")] fn stream<'a>( &'a self, - stmt: Statement, + mut stmt: Statement, ) -> Pin, DbErr>> + 'a + Send>> { Box::pin(async move { + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let conn = self.conn.lock().await; Ok(crate::TransactionStream::build( conn, @@ -459,6 +482,7 @@ impl TransactionTrait for DatabaseTransaction { self.metric_callback.clone(), None, None, + self.default_persistent, ) .await } @@ -475,6 +499,7 @@ impl TransactionTrait for DatabaseTransaction { self.metric_callback.clone(), isolation_level, access_mode, + self.default_persistent, ) .await } diff --git a/src/driver/mock.rs b/src/driver/mock.rs index ecba6f6de4..cb8ca91a4c 100644 --- a/src/driver/mock.rs +++ b/src/driver/mock.rs @@ -252,6 +252,7 @@ impl crate::DatabaseTransaction { metric_callback, None, None, + None, ) .await } diff --git a/src/driver/proxy.rs b/src/driver/proxy.rs index 3415a3fe91..7d4acb10a0 100644 --- a/src/driver/proxy.rs +++ b/src/driver/proxy.rs @@ -148,6 +148,7 @@ impl crate::DatabaseTransaction { metric_callback, None, None, + None, ) .await } diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index 6ce21bc37b..38046a3bfc 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -27,6 +27,7 @@ pub struct SqlxMySqlConnector; #[derive(Clone)] pub struct SqlxMySqlPoolConnection { pub(crate) pool: MySqlPool, + pub(crate) default_persistent: Option, metric_callback: Option, } @@ -41,6 +42,7 @@ impl From for SqlxMySqlPoolConnection { SqlxMySqlPoolConnection { pool, metric_callback: None, + default_persistent: None, } } } @@ -79,6 +81,7 @@ impl SqlxMySqlConnector { if let Some(f) = &options.mysql_opts_fn { sqlx_opts = f(sqlx_opts); } + let default_persistent = options.default_persistent; let pool = if options.connect_lazy { options.sqlx_pool_options().connect_lazy_with(sqlx_opts) } else { @@ -92,6 +95,7 @@ impl SqlxMySqlConnector { SqlxMySqlPoolConnection { pool, metric_callback: None, + default_persistent, }, )) } @@ -103,6 +107,7 @@ impl SqlxMySqlConnector { DatabaseConnection::SqlxMySqlPoolConnection(SqlxMySqlPoolConnection { pool, metric_callback: None, + default_persistent: None, }) } } @@ -110,9 +115,14 @@ impl SqlxMySqlConnector { impl SqlxMySqlPoolConnection { /// Execute a [Statement] on a MySQL backend #[instrument(level = "trace")] - pub async fn execute(&self, stmt: Statement) -> Result { + pub async fn execute(&self, mut stmt: Statement) -> Result { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let query = sqlx_query(&stmt); let mut conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { @@ -137,9 +147,14 @@ impl SqlxMySqlPoolConnection { /// Get one result from a SQL query. Returns [Option::None] if no match was found #[instrument(level = "trace")] - pub async fn query_one(&self, stmt: Statement) -> Result, DbErr> { + pub async fn query_one(&self, mut stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let query = sqlx_query(&stmt); let mut conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { @@ -155,9 +170,14 @@ impl SqlxMySqlPoolConnection { /// Get the results of a query returning them as a Vec<[QueryResult]> #[instrument(level = "trace")] - pub async fn query_all(&self, stmt: Statement) -> Result, DbErr> { + pub async fn query_all(&self, mut stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let query = sqlx_query(&stmt); let mut conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { @@ -170,9 +190,14 @@ impl SqlxMySqlPoolConnection { /// Stream the results of executing a SQL query #[instrument(level = "trace")] - pub async fn stream(&self, stmt: Statement) -> Result { + pub async fn stream(&self, mut stmt: Statement) -> Result { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; Ok(QueryStream::from(( conn, @@ -194,6 +219,7 @@ impl SqlxMySqlPoolConnection { self.metric_callback.clone(), isolation_level, access_mode, + self.default_persistent, ) .await } @@ -220,6 +246,7 @@ impl SqlxMySqlPoolConnection { self.metric_callback.clone(), isolation_level, access_mode, + self.default_persistent, ) .await .map_err(|e| TransactionError::Connection(e))?; @@ -233,6 +260,24 @@ impl SqlxMySqlPoolConnection { self.metric_callback = Some(Arc::new(callback)); } + /// Set the default persistent prepared statement caching behavior + /// + /// When set to Some(false), prepared statement caching will be disabled by default + /// unless explicitly enabled on individual queries. + /// When set to Some(true), prepared statement caching will be enabled by default. + /// When set to None, uses sqlx default behavior (enabled). + /// + /// # Examples + /// + /// ```ignore + /// let mut conn = SqlxMySqlConnector::from_sqlx_mysql_pool(pool); + /// conn.with_default_persistent(false); // Disable statement caching by default + /// ``` + pub fn with_default_persistent(&mut self, value: bool) -> &mut Self { + self.default_persistent = Some(value); + self + } + /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { let conn = &mut self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; @@ -276,7 +321,11 @@ pub(crate) fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, MySql, Sqlx .values .as_ref() .map_or(Values(Vec::new()), |values| values.clone()); - sqlx::query_with(&stmt.sql, SqlxValues(values)) + let mut query = sqlx::query_with(&stmt.sql, SqlxValues(values)); + if let Some(persistent) = stmt.persistent { + query = query.persistent(persistent); + } + query } pub(crate) async fn set_transaction_config( @@ -299,6 +348,7 @@ pub(crate) async fn set_transaction_config( sql: format!("SET TRANSACTION {}", settings.join(", ")), values: None, db_backend: DbBackend::MySql, + persistent: None, }; let query = sqlx_query(&stmt); conn.execute(query).await.map_err(sqlx_error_to_exec_err)?; @@ -330,6 +380,7 @@ impl crate::DatabaseTransaction { metric_callback: Option, isolation_level: Option, access_mode: Option, + default_persistent: Option, ) -> Result { Self::begin( Arc::new(Mutex::new(crate::InnerConnection::MySql(inner))), @@ -337,6 +388,7 @@ impl crate::DatabaseTransaction { metric_callback, isolation_level, access_mode, + default_persistent, ) .await } diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index 14fd6c01ad..d35b6c1978 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -27,6 +27,7 @@ pub struct SqlxPostgresConnector; #[derive(Clone)] pub struct SqlxPostgresPoolConnection { pub(crate) pool: PgPool, + pub(crate) default_persistent: Option, metric_callback: Option, } @@ -41,6 +42,7 @@ impl From for SqlxPostgresPoolConnection { SqlxPostgresPoolConnection { pool, metric_callback: None, + default_persistent: None, } } } @@ -100,6 +102,7 @@ impl SqlxPostgresConnector { string }); let lazy = options.connect_lazy; + let default_persistent = options.default_persistent; let mut pool_options = options.sqlx_pool_options(); if let Some(sql) = set_search_path_sql { pool_options = pool_options.after_connect(move |conn, _| { @@ -123,6 +126,7 @@ impl SqlxPostgresConnector { SqlxPostgresPoolConnection { pool, metric_callback: None, + default_persistent, }, )) } @@ -134,6 +138,7 @@ impl SqlxPostgresConnector { DatabaseConnection::SqlxPostgresPoolConnection(SqlxPostgresPoolConnection { pool, metric_callback: None, + default_persistent: None, }) } } @@ -141,9 +146,14 @@ impl SqlxPostgresConnector { impl SqlxPostgresPoolConnection { /// Execute a [Statement] on a PostgreSQL backend #[instrument(level = "trace")] - pub async fn execute(&self, stmt: Statement) -> Result { + pub async fn execute(&self, mut stmt: Statement) -> Result { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let query = sqlx_query(&stmt); let mut conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { @@ -168,9 +178,14 @@ impl SqlxPostgresPoolConnection { /// Get one result from a SQL query. Returns [Option::None] if no match was found #[instrument(level = "trace")] - pub async fn query_one(&self, stmt: Statement) -> Result, DbErr> { + pub async fn query_one(&self, mut stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let query = sqlx_query(&stmt); let mut conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { @@ -186,9 +201,14 @@ impl SqlxPostgresPoolConnection { /// Get the results of a query returning them as a Vec<[QueryResult]> #[instrument(level = "trace")] - pub async fn query_all(&self, stmt: Statement) -> Result, DbErr> { + pub async fn query_all(&self, mut stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let query = sqlx_query(&stmt); let mut conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { @@ -201,9 +221,14 @@ impl SqlxPostgresPoolConnection { /// Stream the results of executing a SQL query #[instrument(level = "trace")] - pub async fn stream(&self, stmt: Statement) -> Result { + pub async fn stream(&self, mut stmt: Statement) -> Result { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; Ok(QueryStream::from(( conn, @@ -225,6 +250,7 @@ impl SqlxPostgresPoolConnection { self.metric_callback.clone(), isolation_level, access_mode, + self.default_persistent, ) .await } @@ -251,6 +277,7 @@ impl SqlxPostgresPoolConnection { self.metric_callback.clone(), isolation_level, access_mode, + self.default_persistent, ) .await .map_err(|e| TransactionError::Connection(e))?; @@ -264,6 +291,24 @@ impl SqlxPostgresPoolConnection { self.metric_callback = Some(Arc::new(callback)); } + /// Set the default persistent prepared statement caching behavior + /// + /// When set to Some(false), prepared statement caching will be disabled by default + /// unless explicitly enabled on individual queries. + /// When set to Some(true), prepared statement caching will be enabled by default. + /// When set to None, uses sqlx default behavior (enabled). + /// + /// # Examples + /// + /// ```ignore + /// let mut conn = SqlxPostgresConnector::from_sqlx_postgres_pool(pool); + /// conn.with_default_persistent(false); // Disable statement caching by default + /// ``` + pub fn with_default_persistent(&mut self, value: bool) -> &mut Self { + self.default_persistent = Some(value); + self + } + /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { let conn = &mut self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; @@ -307,7 +352,11 @@ pub(crate) fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, Postgres, S .values .as_ref() .map_or(Values(Vec::new()), |values| values.clone()); - sqlx::query_with(&stmt.sql, SqlxValues(values)) + let mut query = sqlx::query_with(&stmt.sql, SqlxValues(values)); + if let Some(persistent) = stmt.persistent { + query = query.persistent(persistent); + } + query } pub(crate) async fn set_transaction_config( @@ -320,6 +369,7 @@ pub(crate) async fn set_transaction_config( sql: format!("SET TRANSACTION ISOLATION LEVEL {isolation_level}"), values: None, db_backend: DbBackend::Postgres, + persistent: None, }; let query = sqlx_query(&stmt); conn.execute(query).await.map_err(sqlx_error_to_exec_err)?; @@ -329,6 +379,7 @@ pub(crate) async fn set_transaction_config( sql: format!("SET TRANSACTION {access_mode}"), values: None, db_backend: DbBackend::Postgres, + persistent: None, }; let query = sqlx_query(&stmt); conn.execute(query).await.map_err(sqlx_error_to_exec_err)?; @@ -364,6 +415,7 @@ impl crate::DatabaseTransaction { metric_callback: Option, isolation_level: Option, access_mode: Option, + default_persistent: Option, ) -> Result { Self::begin( Arc::new(Mutex::new(crate::InnerConnection::Postgres(inner))), @@ -371,6 +423,7 @@ impl crate::DatabaseTransaction { metric_callback, isolation_level, access_mode, + default_persistent, ) .await } diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 16cb6f1d72..7e88a04567 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -28,6 +28,7 @@ pub struct SqlxSqliteConnector; #[derive(Clone)] pub struct SqlxSqlitePoolConnection { pub(crate) pool: SqlitePool, + pub(crate) default_persistent: Option, metric_callback: Option, } @@ -42,6 +43,7 @@ impl From for SqlxSqlitePoolConnection { SqlxSqlitePoolConnection { pool, metric_callback: None, + default_persistent: None, } } } @@ -90,6 +92,7 @@ impl SqlxSqliteConnector { sqlx_opts = f(sqlx_opts); } + let default_persistent = options.default_persistent; let pool = if options.connect_lazy { options.sqlx_pool_options().connect_lazy_with(sqlx_opts) } else { @@ -103,6 +106,7 @@ impl SqlxSqliteConnector { let pool = SqlxSqlitePoolConnection { pool, metric_callback: None, + default_persistent, }; #[cfg(feature = "sqlite-use-returning-for-3_35")] @@ -121,6 +125,7 @@ impl SqlxSqliteConnector { DatabaseConnection::SqlxSqlitePoolConnection(SqlxSqlitePoolConnection { pool, metric_callback: None, + default_persistent: None, }) } } @@ -128,9 +133,14 @@ impl SqlxSqliteConnector { impl SqlxSqlitePoolConnection { /// Execute a [Statement] on a SQLite backend #[instrument(level = "trace")] - pub async fn execute(&self, stmt: Statement) -> Result { + pub async fn execute(&self, mut stmt: Statement) -> Result { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let query = sqlx_query(&stmt); let mut conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { @@ -155,9 +165,14 @@ impl SqlxSqlitePoolConnection { /// Get one result from a SQL query. Returns [Option::None] if no match was found #[instrument(level = "trace")] - pub async fn query_one(&self, stmt: Statement) -> Result, DbErr> { + pub async fn query_one(&self, mut stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let query = sqlx_query(&stmt); let mut conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { @@ -173,9 +188,14 @@ impl SqlxSqlitePoolConnection { /// Get the results of a query returning them as a Vec<[QueryResult]> #[instrument(level = "trace")] - pub async fn query_all(&self, stmt: Statement) -> Result, DbErr> { + pub async fn query_all(&self, mut stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let query = sqlx_query(&stmt); let mut conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; crate::metric::metric!(self.metric_callback, &stmt, { @@ -188,9 +208,14 @@ impl SqlxSqlitePoolConnection { /// Stream the results of executing a SQL query #[instrument(level = "trace")] - pub async fn stream(&self, stmt: Statement) -> Result { + pub async fn stream(&self, mut stmt: Statement) -> Result { debug_print!("{}", stmt); + // Apply default persistent if not explicitly set + if stmt.persistent.is_none() { + stmt.persistent = self.default_persistent; + } + let conn = self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; Ok(QueryStream::from(( conn, @@ -212,6 +237,7 @@ impl SqlxSqlitePoolConnection { self.metric_callback.clone(), isolation_level, access_mode, + self.default_persistent, ) .await } @@ -238,6 +264,7 @@ impl SqlxSqlitePoolConnection { self.metric_callback.clone(), isolation_level, access_mode, + self.default_persistent, ) .await .map_err(|e| TransactionError::Connection(e))?; @@ -251,6 +278,24 @@ impl SqlxSqlitePoolConnection { self.metric_callback = Some(Arc::new(callback)); } + /// Set the default persistent prepared statement caching behavior + /// + /// When set to Some(false), prepared statement caching will be disabled by default + /// unless explicitly enabled on individual queries. + /// When set to Some(true), prepared statement caching will be enabled by default. + /// When set to None, uses sqlx default behavior (enabled). + /// + /// # Examples + /// + /// ```ignore + /// let mut conn = SqlxSqliteConnector::from_sqlx_sqlite_pool(pool); + /// conn.with_default_persistent(false); // Disable statement caching by default + /// ``` + pub fn with_default_persistent(&mut self, value: bool) -> &mut Self { + self.default_persistent = Some(value); + self + } + /// Checks if a connection to the database is still valid. pub async fn ping(&self) -> Result<(), DbErr> { let conn = &mut self.pool.acquire().await.map_err(sqlx_conn_acquire_err)?; @@ -294,7 +339,11 @@ pub(crate) fn sqlx_query(stmt: &Statement) -> sqlx::query::Query<'_, Sqlite, Sql .values .as_ref() .map_or(Values(Vec::new()), |values| values.clone()); - sqlx::query_with(&stmt.sql, SqlxValues(values)) + let mut query = sqlx::query_with(&stmt.sql, SqlxValues(values)); + if let Some(persistent) = stmt.persistent { + query = query.persistent(persistent); + } + query } pub(crate) async fn set_transaction_config( @@ -317,6 +366,7 @@ async fn get_version(conn: &SqlxSqlitePoolConnection) -> Result { sql: "SELECT sqlite_version()".to_string(), values: None, db_backend: crate::DbBackend::Sqlite, + persistent: None, }; conn.query_one(stmt) .await? @@ -382,6 +432,7 @@ impl crate::DatabaseTransaction { metric_callback: Option, isolation_level: Option, access_mode: Option, + default_persistent: Option, ) -> Result { Self::begin( Arc::new(Mutex::new(crate::InnerConnection::Sqlite(inner))), @@ -389,6 +440,7 @@ impl crate::DatabaseTransaction { metric_callback, isolation_level, access_mode, + default_persistent, ) .await } diff --git a/src/executor/cursor.rs b/src/executor/cursor.rs index 0c2c10e927..9851a685b0 100644 --- a/src/executor/cursor.rs +++ b/src/executor/cursor.rs @@ -29,6 +29,7 @@ where after: Option, sort_asc: bool, is_result_reversed: bool, + persistent: Option, phantom: PhantomData, } @@ -51,6 +52,7 @@ where before: None, sort_asc: true, is_result_reversed: false, + persistent: None, phantom: PhantomData, secondary_order_by: Default::default(), } @@ -283,7 +285,8 @@ where self.apply_order_by(); self.apply_filters(); - let stmt = db.get_database_backend().build(&self.query); + let mut stmt = db.get_database_backend().build(&self.query); + stmt.persistent = self.persistent; let rows = db.query_all(stmt).await?; let mut buffer = Vec::with_capacity(rows.len()); for row in rows.into_iter() { @@ -310,6 +313,7 @@ where before: self.before, sort_asc: self.sort_asc, is_result_reversed: self.is_result_reversed, + persistent: self.persistent, phantom: PhantomData, secondary_order_by: self.secondary_order_by, } @@ -336,6 +340,7 @@ where before: self.before, sort_asc: self.sort_asc, is_result_reversed: self.is_result_reversed, + persistent: self.persistent, phantom: PhantomData, secondary_order_by: self.secondary_order_by, } @@ -346,6 +351,12 @@ where self.secondary_order_by = tbl_col; self } + + /// Set whether to use prepared statement caching (persistent prepared statements) + pub fn persistent(&mut self, persistent: bool) -> &mut Self { + self.persistent = Some(persistent); + self + } } impl QuerySelect for Cursor @@ -394,7 +405,9 @@ where where C: IntoIdentity, { - Cursor::new(self.query, SeaRc::new(E::default()), order_columns) + let mut cursor = Cursor::new(self.query, SeaRc::new(E::default()), order_columns); + cursor.persistent = self.persistent; + cursor } } @@ -446,6 +459,7 @@ where order_columns.identity_of(), ); cursor.set_secondary_order_by(primary_keys); + cursor.persistent = self.persistent; cursor } @@ -468,6 +482,7 @@ where order_columns.identity_of(), ); cursor.set_secondary_order_by(primary_keys); + cursor.persistent = self.persistent; cursor } } @@ -513,6 +528,7 @@ where .collect(); cursor.set_secondary_order_by(primary_keys); } + cursor.persistent = self.persistent; cursor } } diff --git a/src/executor/delete.rs b/src/executor/delete.rs index 0877ff021a..ed74c4599a 100644 --- a/src/executor/delete.rs +++ b/src/executor/delete.rs @@ -11,6 +11,7 @@ use super::{SelectModel, SelectorRaw}; #[derive(Clone, Debug)] pub struct Deleter { query: DeleteStatement, + persistent: Option, } /// The result of a DELETE operation @@ -30,7 +31,7 @@ where C: ConnectionTrait, { // so that self is dropped before entering await - exec_delete_only(self.query, db) + exec_delete_only(self.query, self.persistent, db) } /// Execute an delete operation and return the deleted model @@ -45,7 +46,7 @@ where where C: ConnectionTrait, { - exec_delete_with_returning_one::(self.query, db) + exec_delete_with_returning_one::(self.query, self.persistent, db) } } @@ -59,7 +60,7 @@ where C: ConnectionTrait, { // so that self is dropped before entering await - exec_delete_only(self.query, db) + exec_delete_only(self.query, self.persistent, db) } /// Execute an delete operation and return the deleted model @@ -75,14 +76,14 @@ where E: EntityTrait, C: ConnectionTrait, { - exec_delete_with_returning_many::(self.query, db) + exec_delete_with_returning_many::(self.query, self.persistent, db) } } impl Deleter { /// Instantiate a new [Deleter] by passing it a [DeleteStatement] - pub fn new(query: DeleteStatement) -> Self { - Self { query } + pub fn new(query: DeleteStatement, persistent: Option) -> Self { + Self { query, persistent } } /// Execute a DELETE operation @@ -90,7 +91,7 @@ impl Deleter { where C: ConnectionTrait, { - exec_delete(self.query, db) + exec_delete(self.query, self.persistent, db) } /// Execute an delete operation and return the deleted model @@ -106,23 +107,24 @@ impl Deleter { E: EntityTrait, C: ConnectionTrait, { - exec_delete_with_returning_many::(self.query, db) + exec_delete_with_returning_many::(self.query, self.persistent, db) } } -async fn exec_delete_only(query: DeleteStatement, db: &C) -> Result +async fn exec_delete_only(query: DeleteStatement, persistent: Option, db: &C) -> Result where C: ConnectionTrait, { - Deleter::new(query).exec(db).await + Deleter::new(query, persistent).exec(db).await } -async fn exec_delete(query: DeleteStatement, db: &C) -> Result +async fn exec_delete(query: DeleteStatement, persistent: Option, db: &C) -> Result where C: ConnectionTrait, { let builder = db.get_database_backend(); - let statement = builder.build(&query); + let mut statement = builder.build(&query); + statement.persistent = persistent; let result = db.execute(statement).await?; Ok(DeleteResult { @@ -132,6 +134,7 @@ where async fn exec_delete_with_returning_one( mut query: DeleteStatement, + persistent: Option, db: &C, ) -> Result, DbErr> where @@ -141,7 +144,8 @@ where let models = match db.support_returning() { true => { let db_backend = db.get_database_backend(); - let delete_statement = db_backend.build(&query.returning_all().to_owned()); + let mut delete_statement = db_backend.build(&query.returning_all().to_owned()); + delete_statement.persistent = persistent; SelectorRaw::::Model>>::from_statement(delete_statement) .one(db) .await? @@ -153,6 +157,7 @@ where async fn exec_delete_with_returning_many( mut query: DeleteStatement, + persistent: Option, db: &C, ) -> Result, DbErr> where @@ -166,7 +171,8 @@ where E::Column::iter().map(|c| c.select_enum_as(c.into_returning_expr(db_backend))), ); let query = query.returning(returning); - let delete_statement = db_backend.build(&query.to_owned()); + let mut delete_statement = db_backend.build(&query.to_owned()); + delete_statement.persistent = persistent; SelectorRaw::::Model>>::from_statement(delete_statement) .all(db) .await? diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 7cac5e1086..98f0c54b9a 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -17,6 +17,7 @@ where primary_key: Option, query: InsertStatement, model: PhantomData, + persistent: Option, } /// The result of an INSERT operation on an ActiveModel @@ -180,7 +181,7 @@ where })); query.returning(returning); } - Inserter::::new(self.primary_key, query).exec(db) + Inserter::::new(self.primary_key, query, self.persistent).exec(db) } /// Execute an insert operation without returning (don't use `RETURNING` syntax) @@ -194,7 +195,7 @@ where C: ConnectionTrait, A: 'a, { - Inserter::::new(self.primary_key, self.query).exec_without_returning(db) + Inserter::::new(self.primary_key, self.query, self.persistent).exec_without_returning(db) } /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if supported) @@ -210,7 +211,7 @@ where C: ConnectionTrait, A: 'a, { - Inserter::::new(self.primary_key, self.query).exec_with_returning(db) + Inserter::::new(self.primary_key, self.query, self.persistent).exec_with_returning(db) } /// Execute an insert operation and return primary keys of inserted models @@ -227,7 +228,7 @@ where C: ConnectionTrait, A: 'a, { - Inserter::::new(self.primary_key, self.query).exec_with_returning_keys(db) + Inserter::::new(self.primary_key, self.query, self.persistent).exec_with_returning_keys(db) } /// Execute an insert operation and return all inserted models @@ -244,7 +245,7 @@ where C: ConnectionTrait, A: 'a, { - Inserter::::new(self.primary_key, self.query).exec_with_returning_many(db) + Inserter::::new(self.primary_key, self.query, self.persistent).exec_with_returning_many(db) } } @@ -253,11 +254,12 @@ where A: ActiveModelTrait, { /// Instantiate a new insert operation - pub fn new(primary_key: Option, query: InsertStatement) -> Self { + pub fn new(primary_key: Option, query: InsertStatement, persistent: Option) -> Self { Self { primary_key, query, model: PhantomData, + persistent, } } @@ -267,7 +269,7 @@ where C: ConnectionTrait, A: 'a, { - exec_insert(self.primary_key, self.query, db) + exec_insert(self.primary_key, self.query, self.persistent, db) } /// Execute an insert operation @@ -279,7 +281,7 @@ where C: ConnectionTrait, A: 'a, { - exec_insert_without_returning(self.query, db) + exec_insert_without_returning(self.query, self.persistent, db) } /// Execute an insert operation and return the inserted model (use `RETURNING` syntax if supported) @@ -292,7 +294,7 @@ where C: ConnectionTrait, A: 'a, { - exec_insert_with_returning::(self.primary_key, self.query, db) + exec_insert_with_returning::(self.primary_key, self.query, self.persistent, db) } /// Execute an insert operation and return primary keys of inserted models @@ -309,7 +311,7 @@ where C: ConnectionTrait, A: 'a, { - exec_insert_with_returning_keys::(self.query, db) + exec_insert_with_returning_keys::(self.query, self.persistent, db) } /// Execute an insert operation and return all inserted models @@ -326,13 +328,14 @@ where C: ConnectionTrait, A: 'a, { - exec_insert_with_returning_many::(self.query, db) + exec_insert_with_returning_many::(self.query, self.persistent, db) } } async fn exec_insert( primary_key: Option, statement: InsertStatement, + persistent: Option, db: &C, ) -> Result, DbErr> where @@ -342,7 +345,8 @@ where type ValueTypeOf = as PrimaryKeyTrait>::ValueType; let db_backend = db.get_database_backend(); - let statement = db_backend.build(&statement); + let mut statement = db_backend.build(&statement); + statement.persistent = persistent; let last_insert_id = match (primary_key, db.support_returning()) { (Some(value_tuple), _) => { @@ -387,13 +391,15 @@ where async fn exec_insert_without_returning( insert_statement: InsertStatement, + persistent: Option, db: &C, ) -> Result where C: ConnectionTrait, { let db_backend = db.get_database_backend(); - let insert_statement = db_backend.build(&insert_statement); + let mut insert_statement = db_backend.build(&insert_statement); + insert_statement.persistent = persistent; let exec_result = db.execute(insert_statement).await?; Ok(exec_result.rows_affected()) } @@ -401,6 +407,7 @@ where async fn exec_insert_with_returning( primary_key: Option, mut insert_statement: InsertStatement, + persistent: Option, db: &C, ) -> Result<::Model, DbErr> where @@ -416,7 +423,8 @@ where .map(|c| c.select_as(c.into_returning_expr(db_backend))), ); insert_statement.returning(returning); - let insert_statement = db_backend.build(&insert_statement); + let mut insert_statement = db_backend.build(&insert_statement); + insert_statement.persistent = persistent; SelectorRaw::::Model>>::from_statement( insert_statement, ) @@ -424,7 +432,7 @@ where .await? } false => { - let insert_res = exec_insert::(primary_key, insert_statement, db).await?; + let insert_res = exec_insert::(primary_key, insert_statement, persistent, db).await?; ::find_by_id(insert_res.last_insert_id) .one(db) .await? @@ -440,6 +448,7 @@ where async fn exec_insert_with_returning_keys( mut insert_statement: InsertStatement, + persistent: Option, db: &C, ) -> Result as PrimaryKeyTrait>::ValueType>, DbErr> where @@ -456,7 +465,8 @@ where .select_as(c.into_column().into_returning_expr(db_backend)) })); insert_statement.returning(returning); - let statement = db_backend.build(&insert_statement); + let mut statement = db_backend.build(&insert_statement); + statement.persistent = persistent; let rows = db.query_all(statement).await?; let cols = PrimaryKey::::iter() .map(|col| col.to_string()) @@ -476,6 +486,7 @@ where async fn exec_insert_with_returning_many( mut insert_statement: InsertStatement, + persistent: Option, db: &C, ) -> Result::Model>, DbErr> where @@ -491,7 +502,8 @@ where .map(|c| c.select_as(c.into_returning_expr(db_backend))), ); insert_statement.returning(returning); - let insert_statement = db_backend.build(&insert_statement); + let mut insert_statement = db_backend.build(&insert_statement); + insert_statement.persistent = persistent; SelectorRaw::::Model>>::from_statement( insert_statement, ) diff --git a/src/executor/paginator.rs b/src/executor/paginator.rs index 03879205f0..afbbcaa1d6 100644 --- a/src/executor/paginator.rs +++ b/src/executor/paginator.rs @@ -21,6 +21,7 @@ where pub(crate) page: u64, pub(crate) page_size: u64, pub(crate) db: &'db C, + pub(crate) persistent: Option, pub(crate) selector: PhantomData, } @@ -49,7 +50,8 @@ where .offset(self.page_size * page) .to_owned(); let builder = self.db.get_database_backend(); - let stmt = builder.build(&query); + let mut stmt = builder.build(&query); + stmt.persistent = self.persistent; let rows = self.db.query_all(stmt).await?; let mut buffer = Vec::with_capacity(rows.len()); for row in rows.into_iter() { @@ -79,8 +81,9 @@ where "sub_query", ) .to_owned(); - let stmt = builder.build(&stmt); - let result = match self.db.query_one(stmt).await? { + let mut built_stmt = builder.build(&stmt); + built_stmt.persistent = self.persistent; + let result = match self.db.query_one(built_stmt).await? { Some(res) => res, None => return Ok(0), }; @@ -124,6 +127,12 @@ where self.page } + /// Set whether to use prepared statement caching (persistent prepared statements) + pub fn persistent(&mut self, persistent: bool) -> &mut Self { + self.persistent = Some(persistent); + self + } + /// Fetch one page and increment the page counter /// /// ``` @@ -241,6 +250,7 @@ where page: 0, page_size, db, + persistent: self.persistent, selector: PhantomData, } } @@ -267,6 +277,7 @@ where page: 0, page_size, db, + persistent: self.stmt.persistent, selector: PhantomData, } } diff --git a/src/executor/select.rs b/src/executor/select.rs index fa149eed8e..e3f534581f 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -20,6 +20,7 @@ where { pub(crate) query: SelectStatement, selector: S, + pub(crate) persistent: Option, } /// Performs a raw `SELECT` operation on a model @@ -180,6 +181,7 @@ where Selector { query: self.query, selector: SelectModel { model: PhantomData }, + persistent: self.persistent, } } @@ -228,6 +230,7 @@ where Selector { query: self.query, selector: SelectModel { model: PhantomData }, + persistent: self.persistent, } } @@ -493,6 +496,7 @@ where Selector { query: self.query, selector: SelectTwoModel { model: PhantomData }, + persistent: self.persistent, } } @@ -514,6 +518,7 @@ where Selector { query: self.query, selector: SelectTwoModel { model: PhantomData }, + persistent: self.persistent, } } @@ -572,6 +577,7 @@ where Selector { query: self.query, selector: SelectTwoModel { model: PhantomData }, + persistent: self.persistent, } } @@ -593,6 +599,7 @@ where Selector { query: self.query, selector: SelectTwoModel { model: PhantomData }, + persistent: self.persistent, } } @@ -662,6 +669,7 @@ where Selector { query: self.query, selector: SelectThreeModel { model: PhantomData }, + persistent: self.persistent, } } @@ -684,6 +692,7 @@ where Selector { query: self.query, selector: SelectThreeModel { model: PhantomData }, + persistent: self.persistent, } } @@ -755,6 +764,7 @@ where columns: PhantomData, model: PhantomData, }, + persistent: None, } } @@ -766,15 +776,36 @@ where Selector { query, selector: SelectGetableTuple { model: PhantomData }, + persistent: None, } } + /// Set whether to use prepared statement caching (persistent prepared statements) + /// + /// When set to `Some(false)`, prepared statement caching will be disabled for this query. + /// When set to `Some(true)`, prepared statement caching will be enabled for this query. + /// When set to `None`, uses the connection's default behavior. + /// + /// # Examples + /// + /// ```ignore + /// use sea_orm::{entity::*, query::*, tests_cfg::cake, DbBackend}; + /// + /// let query = cake::Entity::find().into_tuple::<(i32, String)>(query) + /// .persistent(false); + /// ``` + pub fn persistent(mut self, value: bool) -> Self { + self.persistent = Some(value); + self + } + fn into_selector_raw(self, db: &C) -> SelectorRaw where C: ConnectionTrait, { let builder = db.get_database_backend(); - let stmt = builder.build(&self.query); + let mut stmt = builder.build(&self.query); + stmt.persistent = self.persistent; SelectorRaw { stmt, selector: self.selector, @@ -783,7 +814,9 @@ where /// Get the SQL statement pub fn into_statement(self, builder: DbBackend) -> Statement { - builder.build(&self.query) + let mut stmt = builder.build(&self.query); + stmt.persistent = self.persistent; + stmt } /// Get an item from the Select query diff --git a/src/executor/update.rs b/src/executor/update.rs index 528643074b..e9608d723d 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -9,6 +9,7 @@ use sea_query::{FromValueTuple, Query, UpdateStatement}; pub struct Updater { query: UpdateStatement, check_record_exists: bool, + persistent: Option, } /// The result of an update operation on an ActiveModel @@ -28,7 +29,7 @@ where ::Model: IntoActiveModel, C: ConnectionTrait, { - Updater::new(self.query) + Updater::new(self.query, self.persistent) .exec_update_and_return_updated(self.model, db) .await } @@ -43,7 +44,7 @@ where where C: ConnectionTrait, { - Updater::new(self.query).exec(db).await + Updater::new(self.query, self.persistent).exec(db).await } /// Execute an update operation and return the updated model (use `RETURNING` syntax if supported) @@ -55,7 +56,7 @@ where where C: ConnectionTrait, { - Updater::new(self.query) + Updater::new(self.query, self.persistent) .exec_update_with_returning::(db) .await } @@ -63,10 +64,11 @@ where impl Updater { /// Instantiate an update using an [UpdateStatement] - pub fn new(query: UpdateStatement) -> Self { + pub fn new(query: UpdateStatement, persistent: Option) -> Self { Self { query, check_record_exists: false, + persistent, } } @@ -85,7 +87,8 @@ impl Updater { return Ok(UpdateResult::default()); } let builder = db.get_database_backend(); - let statement = builder.build(&self.query); + let mut statement = builder.build(&self.query); + statement.persistent = self.persistent; let result = db.execute(statement).await?; if self.check_record_exists && result.rows_affected() == 0 { return Err(DbErr::RecordNotUpdated); @@ -119,8 +122,10 @@ impl Updater { Column::::iter().map(|c| c.select_as(c.into_returning_expr(db_backend))), ); self.query.returning(returning); + let mut statement = db_backend.build(&self.query); + statement.persistent = self.persistent; let found: Option> = SelectorRaw::>>::from_statement( - db_backend.build(&self.query), + statement, ) .one(db) .await?; @@ -154,8 +159,10 @@ impl Updater { E::Column::iter().map(|c| c.select_as(c.into_returning_expr(db_backend))), ); self.query.returning(returning); + let mut statement = db_backend.build(&self.query); + statement.persistent = self.persistent; let models: Vec = SelectorRaw::>::from_statement( - db_backend.build(&self.query), + statement, ) .all(db) .await?; diff --git a/src/query/combine.rs b/src/query/combine.rs index 917ec24ebf..f294894081 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -113,6 +113,7 @@ where Self { query, entity: PhantomData, + persistent: None, } } @@ -137,6 +138,7 @@ where Self { query, entity: PhantomData, + persistent: None, } } @@ -167,6 +169,7 @@ where Self { query, entity: PhantomData, + persistent: None, } } diff --git a/src/query/delete.rs b/src/query/delete.rs index 73cd430241..e9bd8813f7 100644 --- a/src/query/delete.rs +++ b/src/query/delete.rs @@ -17,6 +17,7 @@ where { pub(crate) query: DeleteStatement, pub(crate) model: A, + pub(crate) persistent: Option, } /// Perform a delete operation on multiple models @@ -27,6 +28,7 @@ where { pub(crate) query: DeleteStatement, pub(crate) entity: PhantomData, + pub(crate) persistent: Option, } impl Delete { @@ -71,6 +73,7 @@ impl Delete { .from_table(A::Entity::default().table_ref()) .to_owned(), model: model.into_active_model(), + persistent: None, }; myself.prepare() } @@ -97,6 +100,7 @@ impl Delete { .from_table(entity.table_ref()) .to_owned(), entity: PhantomData, + persistent: None, } } } @@ -118,6 +122,24 @@ where } self } + + /// Set whether to use prepared statement caching + /// + /// # Examples + /// + /// ``` + /// # use sea_orm::{entity::*, query::*, tests_cfg::cake, DbBackend}; + /// # + /// let res = Delete::one(cake::Model { + /// id: 1, + /// name: "Apple Pie".to_owned(), + /// }) + /// .persistent(false); + /// ``` + pub fn persistent(mut self, value: bool) -> Self { + self.persistent = Some(value); + self + } } impl QueryFilter for DeleteOne @@ -159,6 +181,16 @@ where fn into_query(self) -> DeleteStatement { self.query } + + fn build(&self, db_backend: crate::DbBackend) -> crate::Statement { + let query_builder = db_backend.get_query_builder(); + let mut statement = crate::Statement::from_string_values_tuple( + db_backend, + self.as_query().build_any(query_builder.as_ref()), + ); + statement.persistent = self.persistent; + statement + } } impl QueryTrait for DeleteMany @@ -178,6 +210,37 @@ where fn into_query(self) -> DeleteStatement { self.query } + + fn build(&self, db_backend: crate::DbBackend) -> crate::Statement { + let query_builder = db_backend.get_query_builder(); + let mut statement = crate::Statement::from_string_values_tuple( + db_backend, + self.as_query().build_any(query_builder.as_ref()), + ); + statement.persistent = self.persistent; + statement + } +} + +impl DeleteMany +where + E: EntityTrait, +{ + /// Set whether to use prepared statement caching + /// + /// # Examples + /// + /// ``` + /// # use sea_orm::{entity::*, query::*, tests_cfg::fruit, DbBackend}; + /// # + /// let res = Delete::many(fruit::Entity) + /// .filter(fruit::Column::Name.contains("Apple")) + /// .persistent(false); + /// ``` + pub fn persistent(mut self, value: bool) -> Self { + self.persistent = Some(value); + self + } } #[cfg(test)] diff --git a/src/query/insert.rs b/src/query/insert.rs index 697f208d2a..d1a20f744a 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -15,6 +15,7 @@ where pub(crate) columns: Vec, pub(crate) primary_key: Option, pub(crate) model: PhantomData, + pub(crate) persistent: Option, } impl Default for Insert @@ -39,6 +40,7 @@ where columns: Vec::new(), primary_key: None, model: PhantomData, + persistent: None, } } @@ -346,6 +348,27 @@ where TryInsert::from_insert(self) } + + /// Set whether to use prepared statement caching + /// + /// # Examples + /// + /// ``` + /// # use sea_orm::{error::*, query::*, tests_cfg::cake, DbBackend}; + /// # + /// let insert = cake::ActiveModel { + /// name: ActiveValue::set("Apple Pie".to_owned()), + /// ..Default::default() + /// } + /// .into_active_model(); + /// + /// let res = Insert::one(insert) + /// .persistent(false); + /// ``` + pub fn persistent(mut self, value: bool) -> Self { + self.persistent = Some(value); + self + } } impl QueryTrait for Insert @@ -365,6 +388,16 @@ where fn into_query(self) -> InsertStatement { self.query } + + fn build(&self, db_backend: crate::DbBackend) -> crate::Statement { + let query_builder = db_backend.get_query_builder(); + let mut statement = crate::Statement::from_string_values_tuple( + db_backend, + self.as_query().build_any(query_builder.as_ref()), + ); + statement.persistent = self.persistent; + statement + } } /// Performs INSERT operations on a ActiveModel, will do nothing if input is empty. diff --git a/src/query/join.rs b/src/query/join.rs index 9c3a282c47..929e82d9e1 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -93,7 +93,9 @@ where .join_as(JoinType::LeftJoin, table_ref, to_tbl, condition); } slf = slf.apply_alias(SelectA.as_str()); + let persistent = slf.persistent; let mut select_two = SelectTwo::new_without_prepare(slf.query); + select_two.persistent = persistent; for col in ::iter() { let alias = format!("{}{}", SelectB.as_str(), col.as_str()); let expr = Expr::col(( @@ -139,7 +141,9 @@ where .join_as(JoinType::LeftJoin, table_ref, to_tbl, condition); } slf = slf.apply_alias(SelectA.as_str()); + let persistent = slf.persistent; let mut select_two_many = SelectTwoMany::new_without_prepare(slf.query); + select_two_many.persistent = persistent; for col in ::iter() { let alias = format!("{}{}", SelectB.as_str(), col.as_str()); let expr = Expr::col(( diff --git a/src/query/select.rs b/src/query/select.rs index 66f590d351..561b323ce9 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -11,6 +11,7 @@ where { pub(crate) query: SelectStatement, pub(crate) entity: PhantomData, + pub(crate) persistent: Option, } /// Defines a structure to perform a SELECT operation on two Models @@ -22,6 +23,7 @@ where { pub(crate) query: SelectStatement, pub(crate) entity: PhantomData<(E, F)>, + pub(crate) persistent: Option, } /// Defines a structure to perform a SELECT operation on many Models @@ -33,6 +35,7 @@ where { pub(crate) query: SelectStatement, pub(crate) entity: PhantomData<(E, F)>, + pub(crate) persistent: Option, } /// Defines a structure to perform a SELECT operation on two Models @@ -45,6 +48,7 @@ where { pub(crate) query: SelectStatement, pub(crate) entity: PhantomData<(E, F, G)>, + pub(crate) persistent: Option, } /// Performs a conversion to [SimpleExpr] @@ -166,6 +170,7 @@ where Self { query: SelectStatement::new(), entity: PhantomData, + persistent: None, } .prepare_select() .prepare_from() @@ -186,6 +191,32 @@ where self.query.from(E::default().table_ref()); self } + + /// Set whether to use prepared statement caching + /// + /// # Examples + /// + /// ``` + /// # use sea_orm::{error::*, tests_cfg::*, *}; + /// # + /// # #[smol_potat::main] + /// # #[cfg(feature = "mock")] + /// # pub async fn main() -> Result<(), DbErr> { + /// # + /// # let db = MockDatabase::new(DbBackend::Postgres).into_connection(); + /// # + /// use sea_orm::QuerySelect; + /// + /// let _select = cake::Entity::find() + /// .persistent(false); + /// + /// # Ok(()) + /// # } + /// ``` + pub fn persistent(mut self, value: bool) -> Self { + self.persistent = Some(value); + self + } } impl QueryTrait for Select @@ -202,6 +233,15 @@ where fn into_query(self) -> SelectStatement { self.query } + fn build(&self, db_backend: crate::DbBackend) -> crate::Statement { + let query_builder = db_backend.get_query_builder(); + let mut statement = crate::Statement::from_string_values_tuple( + db_backend, + self.as_query().build_any(query_builder.as_ref()), + ); + statement.persistent = self.persistent; + statement + } } macro_rules! select_two { @@ -221,6 +261,15 @@ macro_rules! select_two { fn into_query(self) -> SelectStatement { self.query } + fn build(&self, db_backend: crate::DbBackend) -> crate::Statement { + let query_builder = db_backend.get_query_builder(); + let mut statement = crate::Statement::from_string_values_tuple( + db_backend, + self.as_query().build_any(query_builder.as_ref()), + ); + statement.persistent = self.persistent; + statement + } } }; } @@ -244,4 +293,13 @@ where fn into_query(self) -> SelectStatement { self.query } + fn build(&self, db_backend: crate::DbBackend) -> crate::Statement { + let query_builder = db_backend.get_query_builder(); + let mut statement = crate::Statement::from_string_values_tuple( + db_backend, + self.as_query().build_any(query_builder.as_ref()), + ); + statement.persistent = self.persistent; + statement + } } diff --git a/src/query/update.rs b/src/query/update.rs index a9b21aaa0a..14976d0372 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -17,6 +17,7 @@ where { pub(crate) query: UpdateStatement, pub(crate) model: A, + pub(crate) persistent: Option, } /// Defines an UPDATE operation on multiple ActiveModels @@ -27,6 +28,7 @@ where { pub(crate) query: UpdateStatement, pub(crate) entity: PhantomData, + pub(crate) persistent: Option, } impl Update { @@ -55,6 +57,7 @@ impl Update { .table(A::Entity::default().table_ref()) .to_owned(), model, + persistent: None, } .prepare_filters() .prepare_values() @@ -81,6 +84,7 @@ impl Update { UpdateMany { query: UpdateStatement::new().table(entity.table_ref()).to_owned(), entity: PhantomData, + persistent: None, } } } @@ -117,6 +121,24 @@ where } self } + + /// Set whether to use prepared statement caching + /// + /// # Examples + /// + /// ``` + /// # use sea_orm::{entity::*, query::*, tests_cfg::cake, DbBackend}; + /// # + /// let res = Update::one(cake::ActiveModel { + /// id: ActiveValue::set(1), + /// name: ActiveValue::set("Apple Pie".to_owned()), + /// }) + /// .persistent(false); + /// ``` + pub fn persistent(mut self, value: bool) -> Self { + self.persistent = Some(value); + self + } } impl QueryFilter for UpdateOne @@ -158,6 +180,16 @@ where fn into_query(self) -> UpdateStatement { self.query } + + fn build(&self, db_backend: crate::DbBackend) -> crate::Statement { + let query_builder = db_backend.get_query_builder(); + let mut statement = crate::Statement::from_string_values_tuple( + db_backend, + self.as_query().build_any(query_builder.as_ref()), + ); + statement.persistent = self.persistent; + statement + } } impl QueryTrait for UpdateMany @@ -177,6 +209,16 @@ where fn into_query(self) -> UpdateStatement { self.query } + + fn build(&self, db_backend: crate::DbBackend) -> crate::Statement { + let query_builder = db_backend.get_query_builder(); + let mut statement = crate::Statement::from_string_values_tuple( + db_backend, + self.as_query().build_any(query_builder.as_ref()), + ); + statement.persistent = self.persistent; + statement + } } impl UpdateMany @@ -208,6 +250,22 @@ where self.query.value(col, expr); self } + + /// Set whether to use prepared statement caching + /// + /// # Examples + /// + /// ``` + /// # use sea_orm::{entity::*, query::*, sea_query::Expr, tests_cfg::fruit, DbBackend}; + /// # + /// let res = Update::many(fruit::Entity) + /// .col_expr(fruit::Column::CakeId, Expr::value(1)) + /// .persistent(false); + /// ``` + pub fn persistent(mut self, value: bool) -> Self { + self.persistent = Some(value); + self + } } #[cfg(test)]