Skip to content

Commit 050e12a

Browse files
committed
fix(sqlite): have StatementWorker keep a strong ref to ConnectionHandle
this should prevent the database handle from being finalized before all statement handles have been finalized
1 parent 0bc06e5 commit 050e12a

File tree

5 files changed

+40
-10
lines changed

5 files changed

+40
-10
lines changed

sqlx-core/src/sqlite/connection/establish.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ pub(crate) async fn establish(options: &SqliteConnectOptions) -> Result<SqliteCo
8787
// https://www.sqlite.org/c3ref/extended_result_codes.html
8888
unsafe {
8989
// NOTE: ignore the failure here
90-
sqlite3_extended_result_codes(handle.0.as_ptr(), 1);
90+
sqlite3_extended_result_codes(handle.as_ptr(), 1);
9191
}
9292

9393
// Configure a busy timeout
@@ -99,7 +99,7 @@ pub(crate) async fn establish(options: &SqliteConnectOptions) -> Result<SqliteCo
9999
let ms =
100100
i32::try_from(busy_timeout.as_millis()).expect("Given busy timeout value is too big.");
101101

102-
status = unsafe { sqlite3_busy_timeout(handle.0.as_ptr(), ms) };
102+
status = unsafe { sqlite3_busy_timeout(handle.as_ptr(), ms) };
103103

104104
if status != SQLITE_OK {
105105
return Err(Error::Database(Box::new(SqliteError::new(handle.as_ptr()))));
@@ -109,8 +109,8 @@ pub(crate) async fn establish(options: &SqliteConnectOptions) -> Result<SqliteCo
109109
})?;
110110

111111
Ok(SqliteConnection {
112+
worker: StatementWorker::new(handle.to_ref()),
112113
handle,
113-
worker: StatementWorker::new(),
114114
statements: StatementCache::new(options.statement_cache_capacity),
115115
statement: None,
116116
transaction_depth: 0,

sqlx-core/src/sqlite/connection/handle.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,23 @@ use std::ptr::NonNull;
33
use libsqlite3_sys::{sqlite3, sqlite3_close, SQLITE_OK};
44

55
use crate::sqlite::SqliteError;
6+
use std::sync::Arc;
67

78
/// Managed handle to the raw SQLite3 database handle.
8-
/// The database handle will be closed when this is dropped.
9+
/// The database handle will be closed when this is dropped and no `ConnectionHandleRef`s exist.
910
#[derive(Debug)]
10-
pub(crate) struct ConnectionHandle(pub(super) NonNull<sqlite3>);
11+
pub(crate) struct ConnectionHandle(Arc<HandleInner>);
12+
13+
/// A wrapper around `ConnectionHandle` which only exists for a `StatementWorker` to own
14+
/// which prevents the `sqlite3` handle from being finalized while it is running `sqlite3_step()`
15+
/// or `sqlite3_reset()`.
16+
///
17+
/// Note that this does *not* actually give access to the database handle!
18+
pub(crate) struct ConnectionHandleRef(Arc<HandleInner>);
19+
20+
// Wrapper for `*mut sqlite3` which finalizes the handle on-drop.
21+
#[derive(Debug)]
22+
struct HandleInner(NonNull<sqlite3>);
1123

1224
// A SQLite3 handle is safe to send between threads, provided not more than
1325
// one is accessing it at the same time. This is upheld as long as [SQLITE_CONFIG_MULTITHREAD] is
@@ -20,19 +32,32 @@ pub(crate) struct ConnectionHandle(pub(super) NonNull<sqlite3>);
2032

2133
unsafe impl Send for ConnectionHandle {}
2234

35+
// SAFETY: `Arc<T>` normally only implements `Send` where `T: Sync` because it allows
36+
// concurrent access.
37+
//
38+
// However, in this case we're only using `Arc` to prevent the database handle from being
39+
// finalized while the worker still holds a statement handle; `ConnectionHandleRef` thus
40+
// should *not* actually provide access to the database handle.
41+
unsafe impl Send for ConnectionHandleRef {}
42+
2343
impl ConnectionHandle {
2444
#[inline]
2545
pub(super) unsafe fn new(ptr: *mut sqlite3) -> Self {
26-
Self(NonNull::new_unchecked(ptr))
46+
Self(Arc::new(HandleInner(NonNull::new_unchecked(ptr))))
2747
}
2848

2949
#[inline]
3050
pub(crate) fn as_ptr(&self) -> *mut sqlite3 {
31-
self.0.as_ptr()
51+
self.0 .0.as_ptr()
52+
}
53+
54+
#[inline]
55+
pub(crate) fn to_ref(&self) -> ConnectionHandleRef {
56+
ConnectionHandleRef(Arc::clone(&self.0))
3257
}
3358
}
3459

35-
impl Drop for ConnectionHandle {
60+
impl Drop for HandleInner {
3661
fn drop(&mut self) {
3762
unsafe {
3863
// https://sqlite.org/c3ref/close.html

sqlx-core/src/sqlite/connection/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mod executor;
1717
mod explain;
1818
mod handle;
1919

20-
pub(crate) use handle::ConnectionHandle;
20+
pub(crate) use handle::{ConnectionHandle, ConnectionHandleRef};
2121

2222
/// A connection to a [Sqlite] database.
2323
pub struct SqliteConnection {

sqlx-core/src/sqlite/statement/worker.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use futures_channel::oneshot;
66
use std::sync::{Arc, Weak};
77
use std::thread;
88

9+
use crate::sqlite::connection::ConnectionHandleRef;
10+
911
use libsqlite3_sys::{sqlite3_reset, sqlite3_step, SQLITE_DONE, SQLITE_ROW};
1012
use std::future::Future;
1113

@@ -31,7 +33,7 @@ enum StatementWorkerCommand {
3133
}
3234

3335
impl StatementWorker {
34-
pub(crate) fn new() -> Self {
36+
pub(crate) fn new(conn: ConnectionHandleRef) -> Self {
3537
let (tx, rx) = unbounded();
3638

3739
thread::spawn(move || {
@@ -72,6 +74,9 @@ impl StatementWorker {
7274
}
7375
}
7476
}
77+
78+
// SAFETY: we need to make sure a strong ref to `conn` always outlives anything in `rx`
79+
drop(conn);
7580
});
7681

7782
Self { tx }

tests/sqlite/sqlite.db

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)