Skip to content

Commit c5ea6c4

Browse files
feat: sqlx sqlite expose de/serialize (#3745)
* feat: implement serialze no copy on lockedsqlitehandle * feat: implement serialize on sqliteconnection * feat: implement deserialize on sqliteconnection and add sqlitebuf wrapper type * refactor: misc sqlite type and deserialize refactoring * chore: misc clippy refactoring * fix: misc refactoring and fixes - pass non-owned byte slice to deserialize - `SqliteBufError` and better error handling - more impl for `SqliteOnwedBuf` so it can be used as a slice - default serialize for `SqliteConnection` * refactor: move serialize and deserialize on worker thread This implements `Command::Serialize` and `Command::Deserialize` and moves the serialize and deserialize logic to the worker thread. `Serialize` will need some more iterations as it's not clear whether it would need to wait for other write transactions before running. * refactor: misc refactoring and changes - Merged deserialize module with serialize module - Moved `SqliteOwnedBuf` into serialize module - Fixed rustdocs * chore: API tweaks, better docs, tests * fix: unused import * fix: export `SqliteOwnedBuf`, docs and safety tweaks --------- Co-authored-by: Austin Bonander <[email protected]>
1 parent 5d6d698 commit c5ea6c4

File tree

12 files changed

+549
-44
lines changed

12 files changed

+549
-44
lines changed

sqlx-core/src/error.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ pub enum Error {
3434
#[error("error with configuration: {0}")]
3535
Configuration(#[source] BoxDynError),
3636

37+
/// One or more of the arguments to the called function was invalid.
38+
///
39+
/// The string contains more information.
40+
#[error("{0}")]
41+
InvalidArgument(String),
42+
3743
/// Error returned from the database.
3844
#[error("error returned from database: {0}")]
3945
Database(#[source] Box<dyn DatabaseError>),
@@ -79,7 +85,7 @@ pub enum Error {
7985
},
8086

8187
/// Error occured while encoding a value.
82-
#[error("error occured while encoding a value: {0}")]
88+
#[error("error occurred while encoding a value: {0}")]
8389
Encode(#[source] BoxDynError),
8490

8591
/// Error occurred while decoding a value.
@@ -136,6 +142,12 @@ impl Error {
136142
Error::Protocol(err.to_string())
137143
}
138144

145+
#[doc(hidden)]
146+
#[inline]
147+
pub fn database(err: impl DatabaseError) -> Self {
148+
Error::Database(Box::new(err))
149+
}
150+
139151
#[doc(hidden)]
140152
#[inline]
141153
pub fn config(err: impl StdError + Send + Sync + 'static) -> Self {

sqlx-sqlite/src/connection/collation.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use libsqlite3_sys::{sqlite3_create_collation_v2, SQLITE_OK, SQLITE_UTF8};
1010

1111
use crate::connection::handle::ConnectionHandle;
1212
use crate::error::Error;
13-
use crate::SqliteError;
1413

1514
#[derive(Clone)]
1615
pub struct Collation {
@@ -67,7 +66,7 @@ impl Collation {
6766
} else {
6867
// The xDestroy callback is not called if the sqlite3_create_collation_v2() function fails.
6968
drop(unsafe { Arc::from_raw(raw_f) });
70-
Err(Error::Database(Box::new(SqliteError::new(handle.as_ptr()))))
69+
Err(handle.expect_error().into())
7170
}
7271
}
7372
}
@@ -112,7 +111,7 @@ where
112111
} else {
113112
// The xDestroy callback is not called if the sqlite3_create_collation_v2() function fails.
114113
drop(unsafe { Box::from_raw(boxed_f) });
115-
Err(Error::Database(Box::new(SqliteError::new(handle.as_ptr()))))
114+
Err(handle.expect_error().into())
116115
}
117116
}
118117

sqlx-sqlite/src/connection/establish.rs

+16-20
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,10 @@ impl EstablishParams {
204204

205205
// SAFE: tested for NULL just above
206206
// This allows any returns below to close this handle with RAII
207-
let handle = unsafe { ConnectionHandle::new(handle) };
207+
let mut handle = unsafe { ConnectionHandle::new(handle) };
208208

209209
if status != SQLITE_OK {
210-
return Err(Error::Database(Box::new(SqliteError::new(handle.as_ptr()))));
210+
return Err(Error::Database(Box::new(handle.expect_error())));
211211
}
212212

213213
// Enable extended result codes
@@ -226,33 +226,29 @@ impl EstablishParams {
226226
for ext in self.extensions.iter() {
227227
// `sqlite3_load_extension` is unusual as it returns its errors via an out-pointer
228228
// rather than by calling `sqlite3_errmsg`
229-
let mut error = null_mut();
229+
let mut error_msg = null_mut();
230230
status = unsafe {
231231
sqlite3_load_extension(
232232
handle.as_ptr(),
233233
ext.0.as_ptr(),
234234
ext.1.as_ref().map_or(null(), |e| e.as_ptr()),
235-
addr_of_mut!(error),
235+
addr_of_mut!(error_msg),
236236
)
237237
};
238238

239239
if status != SQLITE_OK {
240+
let mut e = handle.expect_error();
241+
240242
// SAFETY: We become responsible for any memory allocation at `&error`, so test
241243
// for null and take an RAII version for returns
242-
let err_msg = if !error.is_null() {
243-
unsafe {
244-
let e = CStr::from_ptr(error).into();
245-
sqlite3_free(error as *mut c_void);
246-
e
247-
}
248-
} else {
249-
CString::new("Unknown error when loading extension")
250-
.expect("text should be representable as a CString")
251-
};
252-
return Err(Error::Database(Box::new(SqliteError::extension(
253-
handle.as_ptr(),
254-
&err_msg,
255-
))));
244+
if !error_msg.is_null() {
245+
e = e.with_message(unsafe {
246+
let msg = CStr::from_ptr(error_msg).to_string_lossy().into();
247+
sqlite3_free(error_msg as *mut c_void);
248+
msg
249+
});
250+
}
251+
return Err(Error::Database(Box::new(e)));
256252
}
257253
} // Preempt any hypothetical security issues arising from leaving ENABLE_LOAD_EXTENSION
258254
// on by disabling the flag again once we've loaded all the requested modules.
@@ -271,7 +267,7 @@ impl EstablishParams {
271267
// configure a `regexp` function for sqlite, it does not come with one by default
272268
let status = crate::regexp::register(handle.as_ptr());
273269
if status != SQLITE_OK {
274-
return Err(Error::Database(Box::new(SqliteError::new(handle.as_ptr()))));
270+
return Err(Error::Database(Box::new(handle.expect_error())));
275271
}
276272
}
277273

@@ -286,7 +282,7 @@ impl EstablishParams {
286282
status = unsafe { sqlite3_busy_timeout(handle.as_ptr(), ms) };
287283

288284
if status != SQLITE_OK {
289-
return Err(Error::Database(Box::new(SqliteError::new(handle.as_ptr()))));
285+
return Err(Error::Database(Box::new(handle.expect_error())));
290286
}
291287

292288
Ok(ConnectionState {

sqlx-sqlite/src/connection/handle.rs

+11
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ impl ConnectionHandle {
4646
unsafe { sqlite3_last_insert_rowid(self.as_ptr()) }
4747
}
4848

49+
pub(crate) fn last_error(&mut self) -> Option<SqliteError> {
50+
// SAFETY: we have exclusive access to the database handle
51+
unsafe { SqliteError::try_new(self.as_ptr()) }
52+
}
53+
54+
#[track_caller]
55+
pub(crate) fn expect_error(&mut self) -> SqliteError {
56+
self.last_error()
57+
.expect("expected error code to be set in current context")
58+
}
59+
4960
pub(crate) fn exec(&mut self, query: impl Into<String>) -> Result<(), Error> {
5061
let query = query.into();
5162
let query = CString::new(query).map_err(|_| err_protocol!("query contains nul bytes"))?;

sqlx-sqlite/src/connection/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ mod handle;
4040
pub(crate) mod intmap;
4141
#[cfg(feature = "preupdate-hook")]
4242
mod preupdate_hook;
43+
pub(crate) mod serialize;
4344

4445
mod worker;
4546

@@ -544,7 +545,7 @@ impl LockedSqliteHandle<'_> {
544545
}
545546

546547
pub fn last_error(&mut self) -> Option<SqliteError> {
547-
SqliteError::try_new(self.guard.handle.as_ptr())
548+
self.guard.handle.last_error()
548549
}
549550
}
550551

0 commit comments

Comments
 (0)