Skip to content

Commit 608d00c

Browse files
committed
rebase main
1 parent 82ff8d9 commit 608d00c

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

sqlx-sqlite/src/connection/mod.rs

+56-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use futures_core::future::BoxFuture;
22
use futures_intrusive::sync::MutexGuard;
33
use futures_util::future;
4-
use libsqlite3_sys::sqlite3;
4+
use libsqlite3_sys::{sqlite3, sqlite3_progress_handler};
55
use sqlx_core::common::StatementCache;
66
use sqlx_core::error::Error;
77
use sqlx_core::transaction::Transaction;
88
use std::cmp::Ordering;
99
use std::fmt::{self, Debug, Formatter};
1010
use std::ptr::NonNull;
11+
use std::os::raw::{c_int, c_void};
1112

1213
use crate::connection::establish::EstablishParams;
1314
use crate::connection::worker::ConnectionWorker;
@@ -89,6 +90,45 @@ impl SqliteConnection {
8990

9091
Ok(LockedSqliteHandle { guard })
9192
}
93+
94+
/// Sets a progress handler that is invoked periodically during long running calls. If the progress callback
95+
/// returns `false`, then the operation is interrupted.
96+
///
97+
/// `num_ops` is the approximate number of [virtual machine instructions](https://www.sqlite.org/opcode.html)
98+
/// that are evaluated between successive invocations of the callback. If `num_ops` is less than one then the
99+
/// progress handler is disabled.
100+
///
101+
/// Only a single progress handler may be defined at one time per database connection; setting a new progress
102+
/// handler cancels the old one.
103+
///
104+
/// The progress handler callback must not do anything that will modify the database connection that invoked
105+
/// the progress handler. Note that sqlite3_prepare_v2() and sqlite3_step() both modify their database connections
106+
/// in this context.
107+
pub async fn set_progress_handler<F>(&mut self, num_ops: i32, callback: F)
108+
where
109+
F: FnMut() -> bool + Send + 'static,
110+
{
111+
unsafe {
112+
let callback = Box::new(callback);
113+
if let Ok(mut lock_conn) = self.lock_handle().await {
114+
sqlite3_progress_handler(
115+
lock_conn.as_raw_handle().as_mut(),
116+
num_ops,
117+
Some(progress_callback::<F>),
118+
&*callback as *const F as *mut F as *mut _,
119+
);
120+
}
121+
}
122+
}
123+
124+
/// Removes a previously set progress handler on a database connection.
125+
pub async fn remove_progress_handler(&mut self) {
126+
unsafe {
127+
if let Ok(mut lock_conn) = self.lock_handle().await {
128+
sqlite3_progress_handler(lock_conn.as_raw_handle().as_mut(), 0, None, 0 as *mut _);
129+
}
130+
}
131+
}
92132
}
93133

94134
impl Debug for SqliteConnection {
@@ -172,6 +212,21 @@ impl Connection for SqliteConnection {
172212
}
173213
}
174214

215+
/// Implements a C binding to a progress callback. The function returns `0` if the
216+
/// user-provided callback returns `true`, and `1` otherwise to signal an interrupt.
217+
extern "C" fn progress_callback<F>(callback: *mut c_void) -> c_int
218+
where
219+
F: FnMut() -> bool,
220+
{
221+
unsafe {
222+
if (*(callback as *mut F))() {
223+
0
224+
} else {
225+
1
226+
}
227+
}
228+
}
229+
175230
impl LockedSqliteHandle<'_> {
176231
/// Returns the underlying sqlite3* connection handle.
177232
///

tests/sqlite/sqlite.rs

+16
Original file line numberDiff line numberDiff line change
@@ -725,3 +725,19 @@ async fn concurrent_read_and_write() {
725725
read.await;
726726
write.await;
727727
}
728+
729+
#[sqlx_macros::test]
730+
async fn test_query_with_progress_handler() -> anyhow::Result<()> {
731+
let mut conn = new::<Sqlite>().await?;
732+
conn.set_progress_handler(1, || false).await;
733+
734+
match sqlx::query("SELECT 'hello' AS title")
735+
.fetch_all(&mut conn)
736+
.await
737+
{
738+
Err(sqlx::Error::Database(err)) => assert_eq!(err.message(), String::from("interrupted")),
739+
_ => panic!("expected an interrupt"),
740+
}
741+
742+
Ok(())
743+
}

0 commit comments

Comments
 (0)