[SQLite] Add option to execute PRAGMA optimize; on close of a connection (#2116)

* CHANGELOG: mention that users should upgrade CLI

* [SQLite] Add option to execute `PRAGMA optimize;` on close of a connection

* Update sqlx-sqlite/src/options/mod.rs

* Update sqlx-sqlite/src/options/mod.rs

* Update sqlx-sqlite/src/options/mod.rs

---------

Co-authored-by: Austin Bonander <austin.bonander@gmail.com>
This commit is contained in:
miles 2023-02-16 05:48:24 +08:00 committed by Austin Bonander
parent ab2ae26189
commit 1fd05716af
2 changed files with 73 additions and 0 deletions

View File

@ -11,8 +11,11 @@ use std::ptr::NonNull;
use crate::connection::establish::EstablishParams;
use crate::connection::worker::ConnectionWorker;
use crate::options::OptimizeOnClose;
use crate::statement::VirtualStatement;
use crate::{Sqlite, SqliteConnectOptions};
use sqlx_core::executor::Executor;
use std::fmt::Write;
pub(crate) use sqlx_core::connection::*;
@ -39,6 +42,7 @@ mod worker;
/// You can explicitly call [`.close()`][Self::close] to ensure the database is closed successfully
/// or get an error otherwise.
pub struct SqliteConnection {
optimize_on_close: OptimizeOnClose,
pub(crate) worker: ConnectionWorker,
pub(crate) row_channel_size: usize,
}
@ -70,6 +74,7 @@ impl SqliteConnection {
let params = EstablishParams::from_options(options)?;
let worker = ConnectionWorker::establish(params).await?;
Ok(Self {
optimize_on_close: options.optimize_on_close.clone(),
worker,
row_channel_size: options.row_channel_size,
})
@ -102,6 +107,14 @@ impl Connection for SqliteConnection {
fn close(mut self) -> BoxFuture<'static, Result<(), Error>> {
Box::pin(async move {
if let OptimizeOnClose::Enabled { analysis_limit } = self.optimize_on_close {
let mut pragma_string = String::new();
if let Some(limit) = analysis_limit {
write!(pragma_string, "PRAGMA analysis_limit = {}; ", limit).ok();
}
pragma_string.push_str("PRAGMA optimize;");
self.execute(&*pragma_string).await?;
}
let shutdown = self.worker.shutdown();
// Drop the statement worker, which should
// cover all references to the connection handle outside of the worker thread

View File

@ -80,10 +80,18 @@ pub struct SqliteConnectOptions {
pub(crate) serialized: bool,
pub(crate) thread_name: Arc<DebugFn<dyn Fn(u64) -> String + Send + Sync + 'static>>,
pub(crate) optimize_on_close: OptimizeOnClose,
#[cfg(feature = "regexp")]
pub(crate) register_regexp_function: bool,
}
#[derive(Clone, Debug)]
pub enum OptimizeOnClose {
Enabled { analysis_limit: Option<u32> },
Disabled,
}
impl Default for SqliteConnectOptions {
fn default() -> Self {
Self::new()
@ -170,6 +178,9 @@ impl SqliteConnectOptions {
pragmas.insert("auto_vacuum".into(), None);
// Soft limit on the number of rows that `ANALYZE` touches per index.
pragmas.insert("analysis_limit".into(), None);
Self {
filename: Cow::Borrowed(Path::new(":memory:")),
in_memory: false,
@ -188,6 +199,7 @@ impl SqliteConnectOptions {
thread_name: Arc::new(DebugFn(|id| format!("sqlx-sqlite-worker-{}", id))),
command_channel_size: 50,
row_channel_size: 50,
optimize_on_close: OptimizeOnClose::Disabled,
#[cfg(feature = "regexp")]
register_regexp_function: false,
}
@ -464,6 +476,54 @@ impl SqliteConnectOptions {
self
}
/// Execute `PRAGMA optimize;` on the SQLite connection before closing.
///
/// The SQLite manual recommends using this for long-lived databases.
///
/// This will collect and store statistics about the layout of data in your tables to help the query planner make better decisions.
/// Over the connection's lifetime, the query planner will make notes about which tables could use up-to-date statistics so this
/// command doesn't have to scan the whole database every time. Thus, the best time to execute this is on connection close.
///
/// `analysis_limit` sets a soft limit on the maximum number of rows to scan per index.
/// It is equivalent to setting [`Self::analysis_limit`] but only takes effect for the `PRAGMA optimize;` call
/// and does not affect the behavior of any `ANALYZE` statements made during the connection's lifetime.
///
/// If not `None`, the `analysis_limit` here overrides the global `analysis_limit` setting,
/// but only for the `PRAGMA optimize;` call.
///
/// Not enabled by default.
///
/// See [the SQLite manual](https://www.sqlite.org/lang_analyze.html#automatically_running_analyze) for details.
pub fn optimize_on_close(
mut self,
enabled: bool,
analysis_limit: impl Into<Option<u32>>,
) -> Self {
self.optimize_on_close = if enabled {
OptimizeOnClose::Enabled {
analysis_limit: (analysis_limit.into()),
}
} else {
OptimizeOnClose::Disabled
};
self
}
/// Set a soft limit on the number of rows that `ANALYZE` touches per index.
///
/// This also affects `PRAGMA optimize` which is set by [Self::optimize_on_close].
///
/// The value recommended by SQLite is `400`. There is no default.
///
/// See [the SQLite manual](https://www.sqlite.org/lang_analyze.html#approx) for details.
pub fn analysis_limit(mut self, limit: impl Into<Option<u32>>) -> Self {
if let Some(limit) = limit.into() {
return self.pragma("analysis_limit", limit.to_string());
}
self.pragmas.insert("analysis_limit".into(), None);
self
}
/// Register a regexp function that allows using regular expressions in queries.
///
/// ```