Skip to content

Commit

Permalink
[SQLite] Add option to execute PRAGMA optimize; on close of a conne…
Browse files Browse the repository at this point in the history
…ction (#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>
  • Loading branch information
miles170 and abonander committed Feb 18, 2023
1 parent 43f84d6 commit 738aebf
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
13 changes: 13 additions & 0 deletions sqlx-sqlite/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand All @@ -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,
}
Expand Down Expand Up @@ -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,
})
Expand Down Expand Up @@ -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
Expand Down
60 changes: 60 additions & 0 deletions sqlx-sqlite/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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,
Expand All @@ -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,
}
Expand Down Expand Up @@ -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.
///
/// ```
Expand Down

0 comments on commit 738aebf

Please sign in to comment.