Skip to content
This repository was archived by the owner on Oct 18, 2023. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 819a055

Browse files
committedJul 28, 2023
treewide: migrate from rusqlite to libsql and libsql_sys
This huge patch migrates sqld from rusqlite to libsql crate. Since both rusqlite and libsql link with compiled sqlite3.c, the migration has to be done in one piece. Good luck, reviewers!
1 parent f50803f commit 819a055

26 files changed

+655
-1720
lines changed
 

‎Cargo.lock

Lines changed: 350 additions & 1352 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,4 @@ members = [
1010
]
1111

1212
[workspace.dependencies]
13-
rusqlite = { version = "0.29.0", git = "https://github.com/psarna/rusqlite", rev = "477264453b", default-features = false, features = [
14-
"buildtime_bindgen",
15-
"bundled-libsql-wasm-experimental",
16-
"column_decltype",
17-
"load_extension"
18-
] }
13+
libsql = { version = "0.1.6", default-features = false }

‎bottomless/src/lib.rs

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,18 @@ pub extern "C" fn xOpen(
5757
let rc = unsafe {
5858
(orig_methods.xOpen.unwrap())(vfs, db_file, wal_name, no_shm_mode, max_size, methods, wal)
5959
};
60-
if rc != ffi::SQLITE_OK {
60+
if rc != ffi::SQLITE_OK as i32 {
6161
return rc;
6262
}
6363

6464
if !is_regular(vfs) {
6565
tracing::error!("Bottomless WAL is currently only supported for regular VFS");
66-
return ffi::SQLITE_CANTOPEN;
66+
return ffi::SQLITE_CANTOPEN as i32;
6767
}
6868

6969
if is_local() {
7070
tracing::info!("Running in local-mode only, without any replication");
71-
return ffi::SQLITE_OK;
71+
return ffi::SQLITE_OK as i32;
7272
}
7373

7474
let runtime = match tokio::runtime::Builder::new_current_thread()
@@ -78,7 +78,7 @@ pub extern "C" fn xOpen(
7878
Ok(runtime) => runtime,
7979
Err(e) => {
8080
tracing::error!("Failed to initialize async runtime: {}", e);
81-
return ffi::SQLITE_CANTOPEN;
81+
return ffi::SQLITE_CANTOPEN as i32;
8282
}
8383
};
8484

@@ -88,7 +88,7 @@ pub extern "C" fn xOpen(
8888
Ok(path) => path,
8989
Err(e) => {
9090
tracing::error!("Failed to parse the main database path: {}", e);
91-
return ffi::SQLITE_CANTOPEN;
91+
return ffi::SQLITE_CANTOPEN as i32;
9292
}
9393
}
9494
};
@@ -98,12 +98,12 @@ pub extern "C" fn xOpen(
9898
Ok(repl) => repl,
9999
Err(e) => {
100100
tracing::error!("Failed to initialize replicator: {}", e);
101-
return ffi::SQLITE_CANTOPEN;
101+
return ffi::SQLITE_CANTOPEN as i32;
102102
}
103103
};
104104

105105
let rc = block_on!(runtime, try_restore(&mut replicator));
106-
if rc != ffi::SQLITE_OK {
106+
if rc != ffi::SQLITE_OK as i32 {
107107
return rc;
108108
}
109109

@@ -114,7 +114,7 @@ pub extern "C" fn xOpen(
114114
let context_ptr = Box::into_raw(Box::new(context)) as *mut c_void;
115115
unsafe { (*(*wal)).pMethodsData = context_ptr };
116116

117-
ffi::SQLITE_OK
117+
ffi::SQLITE_OK as i32
118118
}
119119

120120
fn get_orig_methods(wal: *mut Wal) -> &'static libsql_wal_methods {
@@ -138,7 +138,7 @@ pub extern "C" fn xClose(
138138
let orig_methods = get_orig_methods(wal);
139139
let methods_data = unsafe { (*wal).pMethodsData as *mut replicator::Context };
140140
let rc = unsafe { (orig_methods.xClose.unwrap())(wal, db, sync_flags, n_buf, z_buf) };
141-
if rc != ffi::SQLITE_OK {
141+
if rc != ffi::SQLITE_OK as i32 {
142142
return rc;
143143
}
144144
if !is_local() && !methods_data.is_null() {
@@ -194,7 +194,7 @@ pub extern "C" fn xUndo(
194194
) -> i32 {
195195
let orig_methods = get_orig_methods(wal);
196196
let rc = unsafe { (orig_methods.xUndo.unwrap())(wal, func, ctx) };
197-
if is_local() || rc != ffi::SQLITE_OK {
197+
if is_local() || rc != ffi::SQLITE_OK as i32 {
198198
return rc;
199199
}
200200

@@ -207,7 +207,7 @@ pub extern "C" fn xUndo(
207207
);
208208
ctx.replicator.rollback_to_frame(last_valid_frame);
209209

210-
ffi::SQLITE_OK
210+
ffi::SQLITE_OK as i32
211211
}
212212

213213
pub extern "C" fn xSavepoint(wal: *mut Wal, wal_data: *mut u32) {
@@ -218,7 +218,7 @@ pub extern "C" fn xSavepoint(wal: *mut Wal, wal_data: *mut u32) {
218218
pub extern "C" fn xSavepointUndo(wal: *mut Wal, wal_data: *mut u32) -> i32 {
219219
let orig_methods = get_orig_methods(wal);
220220
let rc = unsafe { (orig_methods.xSavepointUndo.unwrap())(wal, wal_data) };
221-
if is_local() || rc != ffi::SQLITE_OK {
221+
if is_local() || rc != ffi::SQLITE_OK as i32 {
222222
return rc;
223223
}
224224

@@ -231,7 +231,7 @@ pub extern "C" fn xSavepointUndo(wal: *mut Wal, wal_data: *mut u32) -> i32 {
231231
);
232232
ctx.replicator.rollback_to_frame(last_valid_frame);
233233

234-
ffi::SQLITE_OK
234+
ffi::SQLITE_OK as i32
235235
}
236236

237237
pub extern "C" fn xFrames(
@@ -253,7 +253,7 @@ pub extern "C" fn xFrames(
253253
// supported by bottomless storage.
254254
if let Err(e) = ctx.replicator.set_page_size(page_size as usize) {
255255
tracing::error!("{}", e);
256-
return ffi::SQLITE_IOERR_WRITE;
256+
return ffi::SQLITE_IOERR_WRITE as i32;
257257
}
258258
let frame_count = ffi::PageHdrIter::new(page_headers, page_size as usize).count();
259259
if size_after != 0 {
@@ -273,11 +273,11 @@ pub extern "C" fn xFrames(
273273
sync_flags,
274274
)
275275
};
276-
if is_local() || rc != ffi::SQLITE_OK {
276+
if is_local() || rc != ffi::SQLITE_OK as i32 {
277277
return rc;
278278
}
279279

280-
ffi::SQLITE_OK
280+
ffi::SQLITE_OK as i32
281281
}
282282

283283
extern "C" fn always_wait(_busy_param: *mut c_void) -> i32 {
@@ -307,9 +307,9 @@ pub extern "C" fn xCheckpoint(
307307
** In order to avoid autocheckpoint on close (that's too often),
308308
** checkpoint attempts weaker than TRUNCATE are ignored.
309309
*/
310-
if emode < ffi::SQLITE_CHECKPOINT_TRUNCATE {
310+
if emode < ffi::SQLITE_CHECKPOINT_TRUNCATE as i32 {
311311
tracing::trace!("Ignoring a checkpoint request weaker than TRUNCATE");
312-
return ffi::SQLITE_OK;
312+
return ffi::SQLITE_OK as i32;
313313
}
314314
/* If there's no busy handler, let's provide a default one,
315315
** since we auto-upgrade the passive checkpoint
@@ -335,14 +335,14 @@ pub extern "C" fn xCheckpoint(
335335
)
336336
};
337337

338-
if is_local() || rc != ffi::SQLITE_OK {
338+
if is_local() || rc != ffi::SQLITE_OK as i32 {
339339
return rc;
340340
}
341341

342342
let ctx = get_replicator_context(wal);
343343
if ctx.replicator.commits_in_current_generation() == 0 {
344344
tracing::debug!("No commits happened in this generation, not snapshotting");
345-
return ffi::SQLITE_OK;
345+
return ffi::SQLITE_OK as i32;
346346
}
347347

348348
let last_known_frame = ctx.replicator.last_known_frame();
@@ -352,7 +352,7 @@ pub extern "C" fn xCheckpoint(
352352
ctx.replicator.wait_until_committed(last_known_frame)
353353
) {
354354
tracing::error!("Failed to finalize replication: {}", e);
355-
return ffi::SQLITE_IOERR_WRITE;
355+
return ffi::SQLITE_IOERR_WRITE as i32;
356356
}
357357

358358
ctx.replicator.new_generation();
@@ -363,10 +363,10 @@ pub extern "C" fn xCheckpoint(
363363
"Failed to snapshot the main db file during checkpoint: {}",
364364
e
365365
);
366-
return ffi::SQLITE_IOERR_WRITE;
366+
return ffi::SQLITE_IOERR_WRITE as i32;
367367
}
368368

369-
ffi::SQLITE_OK
369+
ffi::SQLITE_OK as i32
370370
}
371371

372372
pub extern "C" fn xCallback(wal: *mut Wal) -> i32 {
@@ -416,42 +416,42 @@ async fn try_restore(replicator: &mut replicator::Replicator) -> i32 {
416416
replicator.new_generation();
417417
if let Err(e) = replicator.snapshot_main_db_file().await {
418418
tracing::error!("Failed to snapshot the main db file: {}", e);
419-
return ffi::SQLITE_CANTOPEN;
419+
return ffi::SQLITE_CANTOPEN as i32;
420420
}
421421
// Restoration process only leaves the local WAL file if it was
422422
// detected to be newer than its remote counterpart.
423423
if let Err(e) = replicator.maybe_replicate_wal().await {
424424
tracing::error!("Failed to replicate local WAL: {}", e);
425-
return ffi::SQLITE_CANTOPEN;
425+
return ffi::SQLITE_CANTOPEN as i32;
426426
}
427427
}
428428
Ok(replicator::RestoreAction::ReuseGeneration(gen)) => {
429429
replicator.set_generation(gen);
430430
}
431431
Err(e) => {
432432
tracing::error!("Failed to restore the database: {}", e);
433-
return ffi::SQLITE_CANTOPEN;
433+
return ffi::SQLITE_CANTOPEN as i32;
434434
}
435435
}
436436

437-
ffi::SQLITE_OK
437+
ffi::SQLITE_OK as i32
438438
}
439439

440440
pub extern "C" fn xPreMainDbOpen(_methods: *mut libsql_wal_methods, path: *const c_char) -> i32 {
441441
if is_local() {
442442
tracing::info!("Running in local-mode only, without any replication");
443-
return ffi::SQLITE_OK;
443+
return ffi::SQLITE_OK as i32;
444444
}
445445

446446
if path.is_null() {
447-
return ffi::SQLITE_OK;
447+
return ffi::SQLITE_OK as i32;
448448
}
449449
let path = unsafe {
450450
match std::ffi::CStr::from_ptr(path).to_str() {
451451
Ok(path) => path,
452452
Err(e) => {
453453
tracing::error!("Failed to parse the main database path: {}", e);
454-
return ffi::SQLITE_CANTOPEN;
454+
return ffi::SQLITE_CANTOPEN as i32;
455455
}
456456
}
457457
};
@@ -464,23 +464,23 @@ pub extern "C" fn xPreMainDbOpen(_methods: *mut libsql_wal_methods, path: *const
464464
Ok(runtime) => runtime,
465465
Err(e) => {
466466
tracing::error!("Failed to initialize async runtime: {}", e);
467-
return ffi::SQLITE_CANTOPEN;
467+
return ffi::SQLITE_CANTOPEN as i32;
468468
}
469469
};
470470

471471
let options = match replicator::Options::from_env() {
472472
Ok(options) => options,
473473
Err(e) => {
474474
tracing::error!("Failed to parse replicator options: {}", e);
475-
return ffi::SQLITE_CANTOPEN;
475+
return ffi::SQLITE_CANTOPEN as i32;
476476
}
477477
};
478478
let replicator = block_on!(runtime, replicator::Replicator::with_options(path, options));
479479
let mut replicator = match replicator {
480480
Ok(repl) => repl,
481481
Err(e) => {
482482
tracing::error!("Failed to initialize replicator: {}", e);
483-
return ffi::SQLITE_CANTOPEN;
483+
return ffi::SQLITE_CANTOPEN as i32;
484484
}
485485
};
486486
block_on!(runtime, try_restore(&mut replicator))
@@ -561,7 +561,7 @@ pub mod static_init {
561561
if orig_methods.is_null() {}
562562
let methods = crate::bottomless_methods(orig_methods);
563563
let rc = unsafe { libsql_wal_methods_register(methods) };
564-
if rc != crate::ffi::SQLITE_OK {
564+
if rc != crate::ffi::SQLITE_OK as i32 {
565565
let _box = unsafe { Box::from_raw(methods as *mut libsql_wal_methods) };
566566
tracing::warn!("Failed to instantiate bottomless WAL methods");
567567
}

‎sqld-libsql-bindings/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ edition = "2021"
77

88
[dependencies]
99
anyhow = "1.0.66"
10-
rusqlite = { workspace = true }
1110
tracing = "0.1.37"
1211
once_cell = "1.17.1"
12+
libsql = { workspace = true }
1313

1414
[features]
1515
unix-excl-vfs = []

‎sqld-libsql-bindings/src/ffi/mod.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,9 @@
22

33
pub mod types;
44

5-
pub use rusqlite::ffi::{
6-
libsql_wal_methods, libsql_wal_methods_find, libsql_wal_methods_register,
7-
libsql_wal_methods_unregister, sqlite3, sqlite3_file, sqlite3_hard_heap_limit64,
8-
sqlite3_io_methods, sqlite3_soft_heap_limit64, sqlite3_vfs, WalIndexHdr, SQLITE_CANTOPEN,
9-
SQLITE_CHECKPOINT_FULL, SQLITE_CHECKPOINT_TRUNCATE, SQLITE_IOERR_WRITE, SQLITE_OK,
10-
};
11-
12-
pub use rusqlite::ffi::libsql_pghdr as PgHdr;
13-
pub use rusqlite::ffi::libsql_wal as Wal;
14-
pub use rusqlite::ffi::*;
5+
pub use libsql::ffi::libsql_pghdr as PgHdr;
6+
pub use libsql::ffi::libsql_wal as Wal;
7+
pub use libsql::ffi::*;
158

169
pub struct PageHdrIter {
1710
current_ptr: *const PgHdr,

‎sqld-libsql-bindings/src/ffi/types.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use std::ffi::{c_char, c_int, c_uint, c_void};
33

44
use super::{libsql_wal_methods, sqlite3_file, sqlite3_vfs, PgHdr, Wal};
5-
use rusqlite::ffi::sqlite3;
5+
use libsql::ffi::sqlite3;
66

77
// WAL methods
88
pub type XWalLimitFn = extern "C" fn(wal: *mut Wal, limit: i64);
@@ -18,7 +18,7 @@ pub type XWalSavepointFn = extern "C" fn(wal: *mut Wal, wal_data: *mut u32);
1818
pub type XWalSavePointUndoFn = unsafe extern "C" fn(wal: *mut Wal, wal_data: *mut u32) -> c_int;
1919
pub type XWalCheckpointFn = unsafe extern "C" fn(
2020
wal: *mut Wal,
21-
db: *mut rusqlite::ffi::sqlite3,
21+
db: *mut libsql::ffi::sqlite3,
2222
emode: c_int,
2323
busy_handler: Option<unsafe extern "C" fn(busy_param: *mut c_void) -> c_int>,
2424
busy_arg: *mut c_void,
@@ -32,7 +32,7 @@ pub type XWalCallbackFn = extern "C" fn(wal: *mut Wal) -> c_int;
3232
pub type XWalExclusiveModeFn = extern "C" fn(wal: *mut Wal, op: c_int) -> c_int;
3333
pub type XWalHeapMemoryFn = extern "C" fn(wal: *mut Wal) -> c_int;
3434
pub type XWalFileFn = extern "C" fn(wal: *mut Wal) -> *mut sqlite3_file;
35-
pub type XWalDbFn = extern "C" fn(wal: *mut Wal, db: *mut rusqlite::ffi::sqlite3);
35+
pub type XWalDbFn = extern "C" fn(wal: *mut Wal, db: *mut libsql::ffi::sqlite3);
3636
pub type XWalPathNameLenFn = extern "C" fn(orig_len: c_int) -> c_int;
3737
pub type XWalGetPathNameFn = extern "C" fn(buf: *mut c_char, orig: *const c_char, orig_len: c_int);
3838
pub type XWalPreMainDbOpen =

‎sqld-libsql-bindings/src/lib.rs

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
pub mod ffi;
44
pub mod wal_hook;
55

6-
use std::{ffi::CString, marker::PhantomData, ops::Deref, time::Duration};
6+
use std::{ffi::CString, marker::PhantomData, ops::Deref};
77

88
pub use crate::wal_hook::WalMethodsHook;
99
pub use once_cell::sync::Lazy;
1010

11+
pub use libsql;
12+
1113
use self::{
1214
ffi::{libsql_wal_methods, libsql_wal_methods_find},
1315
wal_hook::WalHook,
@@ -23,41 +25,25 @@ pub fn get_orig_wal_methods() -> anyhow::Result<*mut libsql_wal_methods> {
2325
}
2426

2527
pub struct Connection<'a> {
26-
conn: rusqlite::Connection,
28+
conn: libsql::Connection,
2729
_pth: PhantomData<&'a mut ()>,
2830
}
2931

3032
impl Deref for Connection<'_> {
31-
type Target = rusqlite::Connection;
33+
type Target = libsql::Connection;
3234

3335
fn deref(&self) -> &Self::Target {
3436
&self.conn
3537
}
3638
}
3739

38-
impl Drop for Connection<'_> {
39-
fn drop(&mut self) {
40-
unsafe {
41-
let db = self.conn.handle();
42-
if db.is_null() {
43-
return;
44-
}
45-
let mut stmt = ffi::sqlite3_next_stmt(db, std::ptr::null_mut());
46-
while !stmt.is_null() {
47-
let rc = ffi::sqlite3_finalize(stmt);
48-
if rc != ffi::SQLITE_OK {
49-
tracing::error!("Failed to finalize a dangling statement: {rc}")
50-
}
51-
stmt = ffi::sqlite3_next_stmt(db, stmt);
52-
}
53-
}
54-
}
55-
}
56-
5740
impl<'a> Connection<'a> {
5841
/// returns a dummy, in-memory connection. For testing purposes only
5942
pub fn test(_: &mut ()) -> Self {
60-
let conn = rusqlite::Connection::open_in_memory().unwrap();
43+
let conn = libsql::Database::open(":memory:")
44+
.unwrap()
45+
.connect()
46+
.unwrap();
6147
Self {
6248
conn,
6349
_pth: PhantomData,
@@ -67,12 +53,12 @@ impl<'a> Connection<'a> {
6753
/// Opens a database with the regular wal methods in the directory pointed to by path
6854
pub fn open<W: WalHook>(
6955
path: impl AsRef<std::path::Path>,
70-
flags: rusqlite::OpenFlags,
56+
flags: std::ffi::c_int,
7157
// we technically _only_ need to know about W, but requiring a static ref to the wal_hook ensures that
7258
// it has been instanciated and lives for long enough
7359
_wal_hook: &'static WalMethodsHook<W>,
7460
hook_ctx: &'a mut W::Context,
75-
) -> Result<Self, rusqlite::Error> {
61+
) -> Result<Self, std::ffi::c_int> {
7662
let path = path.as_ref().join("data");
7763
tracing::trace!(
7864
"Opening a connection with regular WAL at {}",
@@ -81,34 +67,31 @@ impl<'a> Connection<'a> {
8167

8268
let conn_str = format!("file:{}?_journal_mode=WAL", path.display());
8369
let filename = CString::new(conn_str).unwrap();
84-
let mut db: *mut rusqlite::ffi::sqlite3 = std::ptr::null_mut();
70+
let mut db: *mut ffi::sqlite3 = std::ptr::null_mut();
8571

8672
unsafe {
8773
// We pass a pointer to the WAL methods data to the database connection. This means
8874
// that the reference must outlive the connection. This is guaranteed by the marker in
8975
// the returned connection.
90-
let rc = rusqlite::ffi::libsql_open_v2(
76+
let rc = ffi::libsql_open_v2(
9177
filename.as_ptr(),
9278
&mut db as *mut _,
93-
flags.bits(),
79+
flags,
9480
std::ptr::null_mut(),
9581
W::name().as_ptr(),
9682
hook_ctx as *mut _ as *mut _,
9783
);
9884

9985
if rc != 0 {
100-
rusqlite::ffi::sqlite3_close(db);
101-
return Err(rusqlite::Error::SqliteFailure(
102-
rusqlite::ffi::Error::new(rc),
103-
None,
104-
));
86+
ffi::sqlite3_close(db);
87+
return Err(rc);
10588
}
10689

10790
assert!(!db.is_null());
91+
ffi::sqlite3_busy_timeout(db, 5000);
10892
};
10993

110-
let conn = unsafe { rusqlite::Connection::from_handle_owned(db)? };
111-
conn.busy_timeout(Duration::from_millis(5000))?;
94+
let conn = libsql::Connection::from_handle(db);
11295

11396
Ok(Connection {
11497
conn,

‎sqld-libsql-bindings/src/wal_hook.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ fn get_methods<T>(wal: &mut Wal) -> &mut WalMethodsHook<T> {
227227
#[allow(non_snake_case)]
228228
pub extern "C" fn xClose<T: WalHook>(
229229
wal: *mut Wal,
230-
db: *mut rusqlite::ffi::sqlite3,
230+
db: *mut libsql::ffi::sqlite3,
231231
sync_flags: i32,
232232
n_buf: c_int,
233233
z_buf: *mut u8,
@@ -343,7 +343,7 @@ pub extern "C" fn xFrames<T: WalHook>(
343343
#[allow(non_snake_case)]
344344
pub extern "C" fn xCheckpoint<T: WalHook>(
345345
wal: *mut Wal,
346-
db: *mut rusqlite::ffi::sqlite3,
346+
db: *mut libsql::ffi::sqlite3,
347347
emode: c_int,
348348
busy_handler: Option<unsafe extern "C" fn(busy_param: *mut c_void) -> c_int>,
349349
busy_arg: *mut c_void,
@@ -395,7 +395,7 @@ pub extern "C" fn xFile<T: WalHook>(wal: *mut Wal) -> *mut sqlite3_file {
395395
}
396396

397397
#[allow(non_snake_case)]
398-
pub extern "C" fn xDb<T: WalHook>(wal: *mut Wal, db: *mut rusqlite::ffi::sqlite3) {
398+
pub extern "C" fn xDb<T: WalHook>(wal: *mut Wal, db: *mut libsql::ffi::sqlite3) {
399399
let orig_methods = unsafe { get_orig_methods::<T>(&mut *wal) };
400400
unsafe { (orig_methods.xDb.unwrap())(wal, db) }
401401
}

‎sqld/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ hyper = { version = "0.14.23", features = ["http2"] }
2727
hyper-tungstenite = "0.10"
2828
itertools = "0.10.5"
2929
jsonwebtoken = "8.2.0"
30+
libsql = { workspace = true }
3031
memmap = "0.7.0"
3132
mimalloc = { version = "0.1.36", default-features = false }
3233
nix = { version = "0.26.2", features = ["fs"] }
@@ -37,7 +38,6 @@ prost = "0.11.3"
3738
rand = "0.8"
3839
regex = "1.7.0"
3940
reqwest = { version = "0.11.16", features = ["json", "rustls-tls"], default-features = false }
40-
rusqlite = { workspace = true }
4141
serde = { version = "1.0.149", features = ["derive", "rc"] }
4242
serde_json = { version = "1.0.91", features = ["preserve_order"] }
4343
sha2 = "0.10"
@@ -76,4 +76,4 @@ vergen = { version = "8", features = ["build", "git", "gitcl"] }
7676

7777
[features]
7878
unix-excl-vfs = ["sqld-libsql-bindings/unix-excl-vfs"]
79-
debug-tools = ["console-subscriber", "rusqlite/trace", "tokio/tracing"]
79+
debug-tools = ["console-subscriber", "tokio/tracing"]

‎sqld/src/database/dump/exporter.rs

Lines changed: 41 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,25 @@ use std::ffi::CString;
33
use std::fmt::{Display, Write as _};
44
use std::io::Write;
55

6-
use anyhow::bail;
7-
use rusqlite::types::ValueRef;
8-
use rusqlite::OptionalExtension;
6+
use anyhow::{bail, Context};
7+
use libsql::params::{Params, ValueRef};
98

109
struct DumpState<W: Write> {
1110
/// true if db is in writable_schema mode
1211
writable_schema: bool,
1312
writer: W,
1413
}
1514

16-
use rusqlite::ffi::{sqlite3_keyword_check, sqlite3_table_column_metadata, SQLITE_OK};
15+
use sqld_libsql_bindings::ffi::{sqlite3_keyword_check, sqlite3_table_column_metadata, SQLITE_OK};
1716

1817
impl<W: Write> DumpState<W> {
1918
fn run_schema_dump_query(
2019
&mut self,
21-
txn: &rusqlite::Connection,
20+
txn: &libsql::Connection,
2221
stmt: &str,
2322
) -> anyhow::Result<()> {
24-
let mut stmt = txn.prepare(stmt)?;
25-
let mut rows = stmt.query(())?;
23+
let stmt = txn.prepare(stmt)?;
24+
let rows = stmt.execute(&Params::None).context("Empty response")?;
2625
while let Some(row) = rows.next()? {
2726
let ValueRef::Text(table) = row.get_ref(0)? else { bail!("invalid schema table") };
2827
let ValueRef::Text(ty) = row.get_ref(1)? else { bail!("invalid schema table") };
@@ -92,8 +91,8 @@ impl<W: Write> DumpState<W> {
9291

9392
write!(&mut select, " FROM {}", Quoted(table_str))?;
9493

95-
let mut stmt = txn.prepare(&select)?;
96-
let mut rows = stmt.query(())?;
94+
let stmt = txn.prepare(&select)?;
95+
let rows = stmt.execute(&Params::None).context("Empty response")?;
9796
while let Some(row) = rows.next()? {
9897
write!(self.writer, "{insert}")?;
9998
if row_id_col.is_some() {
@@ -105,7 +104,7 @@ impl<W: Write> DumpState<W> {
105104
if i != 0 || row_id_col.is_some() {
106105
write!(self.writer, ",")?;
107106
}
108-
write_value_ref(&mut self.writer, row.get_ref(i)?)?;
107+
write_value_ref(&mut self.writer, row.get_ref(i as i32)?)?;
109108
}
110109
writeln!(self.writer, ");")?;
111110
}
@@ -115,15 +114,15 @@ impl<W: Write> DumpState<W> {
115114
Ok(())
116115
}
117116

118-
fn run_table_dump_query(&mut self, txn: &rusqlite::Connection, q: &str) -> anyhow::Result<()> {
119-
let mut stmt = txn.prepare(q)?;
117+
fn run_table_dump_query(&mut self, txn: &libsql::Connection, q: &str) -> anyhow::Result<()> {
118+
let stmt = txn.prepare(q)?;
120119
let col_count = stmt.column_count();
121-
let mut rows = stmt.query(())?;
120+
let rows = stmt.execute(&Params::None).context("Empty response")?;
122121
while let Some(row) = rows.next()? {
123122
let ValueRef::Text(sql) = row.get_ref(0)? else { bail!("the first row in a table dump query should be of type text") };
124123
self.writer.write_all(sql)?;
125124
for i in 1..col_count {
126-
let ValueRef::Text(s) = row.get_ref(i)? else { bail!("row {i} in table dump query should be of type text") };
125+
let ValueRef::Text(s) = row.get_ref(i as i32)? else { bail!("row {i} in table dump query should be of type text") };
127126
let s = std::str::from_utf8(s)?;
128127
write!(self.writer, ",{s}")?;
129128
}
@@ -134,7 +133,7 @@ impl<W: Write> DumpState<W> {
134133

135134
fn list_table_columns(
136135
&self,
137-
txn: &rusqlite::Connection,
136+
txn: &libsql::Connection,
138137
table: &str,
139138
) -> anyhow::Result<(Option<String>, Vec<String>)> {
140139
let mut cols = Vec::new();
@@ -143,18 +142,18 @@ impl<W: Write> DumpState<W> {
143142
let mut preserve_row_id = false;
144143
let mut row_id_col = None;
145144

146-
txn.pragma(None, "table_info", table, |row| {
147-
let name: String = row.get_unwrap(1);
148-
cols.push(name);
149-
// this is a primary key col
150-
if row.get_unwrap::<_, usize>(5) != 0 {
151-
num_primary_keys += 1;
152-
is_integer_primary_key = num_primary_keys == 1
153-
&& matches!(row.get_ref_unwrap(2), ValueRef::Text(b"INTEGER"));
145+
if let Some(results) = txn.execute("pragma table_info", ())? {
146+
while let Ok(Some(row)) = results.next() {
147+
let name: String = row.get(1).unwrap();
148+
cols.push(name);
149+
// this is a primary key col
150+
if row.get::<i64>(5).unwrap() != 0 {
151+
num_primary_keys += 1;
152+
is_integer_primary_key = num_primary_keys == 1
153+
&& matches!(row.get_ref(2).unwrap(), ValueRef::Text(b"INTEGER"));
154+
}
154155
}
155-
156-
Ok(())
157-
})?;
156+
}
158157

159158
// from sqlite:
160159
// > The decision of whether or not a rowid really needs to be preserved
@@ -171,16 +170,12 @@ impl<W: Write> DumpState<W> {
171170
// > there is a "pk" entry in "PRAGMA index_list". There will be
172171
// > no "pk" index if the PRIMARY KEY really is an alias for the ROWID.
173172

174-
txn.query_row(
175-
"SELECT 1 FROM pragma_index_list(?) WHERE origin='pk'",
176-
[table],
177-
|_| {
178-
// re-set preserve_row_id iif there is a row
179-
preserve_row_id = true;
180-
Ok(())
181-
},
182-
)
183-
.optional()?;
173+
if let Ok(Some(rows)) = txn.execute(
174+
"SELECT 1 FROM pragma_index_list(?) WHERE origin='pk'",
175+
Params::Positional(vec![table.into()]),
176+
) {
177+
preserve_row_id = rows.next()?.is_none();
178+
}
184179
}
185180

186181
if preserve_row_id {
@@ -206,7 +201,7 @@ impl<W: Write> DumpState<W> {
206201
)
207202
};
208203

209-
if rc == SQLITE_OK {
204+
if rc == SQLITE_OK as i32 {
210205
row_id_col = Some(row_id_name.to_owned());
211206
break;
212207
}
@@ -420,10 +415,11 @@ fn find_unused_str(haystack: &str, needle1: &str, needle2: &str) -> String {
420415
}
421416
}
422417

423-
pub fn export_dump(mut db: rusqlite::Connection, writer: impl Write) -> anyhow::Result<()> {
424-
let mut txn = db.transaction()?;
425-
txn.execute("PRAGMA writable_schema=ON", ())?;
426-
let savepoint = txn.savepoint_with_name("dump")?;
418+
pub fn export_dump(mut db: libsql::Connection, writer: impl Write) -> anyhow::Result<()> {
419+
db.execute("BEGIN", ())?;
420+
db.execute("PRAGMA writable_schema=ON", ())?;
421+
// FIXME: savepoint logic is lost during the out-of-rusqlite migration, we need to restore it
422+
db.execute("SAVEPOINT dump", ())?;
427423
let mut state = DumpState {
428424
writable_schema: false,
429425
writer,
@@ -441,21 +437,21 @@ pub fn export_dump(mut db: rusqlite::Connection, writer: impl Write) -> anyhow::
441437
WHERE type=='table'
442438
AND sql NOT NULL
443439
ORDER BY tbl_name='sqlite_sequence', rowid";
444-
state.run_schema_dump_query(&savepoint, q)?;
440+
state.run_schema_dump_query(&mut db, q)?;
445441

446442
let q = "SELECT sql FROM sqlite_schema AS o
447443
WHERE sql NOT NULL
448444
AND type IN ('index','trigger','view')";
449-
state.run_table_dump_query(&savepoint, q)?;
445+
state.run_table_dump_query(&mut db, q)?;
450446

451447
if state.writable_schema {
452448
writeln!(state.writer, "PRAGMA writable_schema=OFF;")?;
453449
}
454450

455451
writeln!(state.writer, "COMMIT;")?;
456452

457-
let _ = savepoint.execute("PRAGMA writable_schema = OFF;", ());
458-
let _ = savepoint.finish();
453+
db.execute("PRAGMA writable_schema = OFF;", ())?;
454+
db.execute("COMMIT", ())?;
459455

460456
Ok(())
461457
}

‎sqld/src/database/dump/loader.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ use std::sync::Arc;
55
use std::time::Duration;
66

77
use anyhow::anyhow;
8-
use rusqlite::ErrorCode;
98
use tokio::sync::{mpsc, oneshot};
109

1110
use crate::database::libsql::open_db;
1211
use crate::replication::primary::logger::{ReplicationLoggerHookCtx, REPLICATION_METHODS};
1312
use crate::replication::ReplicationLogger;
1413

15-
type OpMsg = Box<dyn FnOnce(&rusqlite::Connection) + 'static + Send + Sync>;
14+
type OpMsg = Box<dyn FnOnce(&libsql::Connection) + 'static + Send + Sync>;
1615

1716
#[derive(Debug)]
1817
pub struct DumpLoader {
@@ -25,6 +24,7 @@ impl DumpLoader {
2524
logger: Arc<ReplicationLogger>,
2625
bottomless_replicator: Option<Arc<std::sync::Mutex<bottomless::replicator::Replicator>>>,
2726
) -> anyhow::Result<Self> {
27+
const BUSY: i32 = sqld_libsql_bindings::ffi::SQLITE_BUSY as i32;
2828
let (sender, mut receiver) = mpsc::channel::<OpMsg>(1);
2929

3030
let (ok_snd, ok_rcv) = oneshot::channel::<anyhow::Result<()>>();
@@ -43,13 +43,7 @@ impl DumpLoader {
4343
// Creating the loader database can, in rare occurences, return sqlite busy,
4444
// because of a race condition opening the monitor thread db. This is there to
4545
// retry a bunch of times if that happens.
46-
Err(rusqlite::Error::SqliteFailure(
47-
rusqlite::ffi::Error {
48-
code: ErrorCode::DatabaseBusy,
49-
..
50-
},
51-
_,
52-
)) if retries < 10 => {
46+
Err(libsql::Error::LibError(BUSY)) if retries < 10 => {
5347
retries += 1;
5448
std::thread::sleep(Duration::from_millis(100));
5549
}
@@ -93,7 +87,7 @@ impl DumpLoader {
9387
const WASM_TABLE_CREATE: &str =
9488
"CREATE TABLE libsql_wasm_func_table (name text PRIMARY KEY, body text) WITHOUT ROWID;";
9589

96-
fn perform_load_dump(conn: &rusqlite::Connection, path: PathBuf) -> anyhow::Result<()> {
90+
fn perform_load_dump(conn: &libsql::Connection, path: PathBuf) -> anyhow::Result<()> {
9791
let mut f = BufReader::new(File::open(path)?);
9892
let mut curr = String::new();
9993
let mut line = String::new();

‎sqld/src/database/libsql.rs

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::sync::Arc;
33
use std::time::{Duration, Instant};
44

55
use crossbeam::channel::RecvTimeoutError;
6-
use rusqlite::{ErrorCode, OpenFlags, StatementStatus};
76
use sqld_libsql_bindings::wal_hook::WalMethodsHook;
87
use tokio::sync::oneshot;
98
use tracing::warn;
@@ -78,18 +77,11 @@ where
7877
async fn try_create_db(&self) -> Result<LibSqlDb> {
7978
// try 100 times to acquire initial db connection.
8079
let mut retries = 0;
80+
const BUSY: i32 = sqld_libsql_bindings::ffi::SQLITE_BUSY as std::ffi::c_int;
8181
loop {
8282
match self.create_database().await {
8383
Ok(conn) => return Ok(conn),
84-
Err(
85-
err @ Error::RusqliteError(rusqlite::Error::SqliteFailure(
86-
rusqlite::ffi::Error {
87-
code: ErrorCode::DatabaseBusy,
88-
..
89-
},
90-
_,
91-
)),
92-
) => {
84+
Err(err @ Error::LibSqlError(libsql::Error::LibError(BUSY))) => {
9385
if retries < 100 {
9486
tracing::warn!("Database file is busy, retrying...");
9587
retries += 1;
@@ -141,19 +133,20 @@ pub fn open_db<'a, W>(
141133
path: &Path,
142134
wal_methods: &'static WalMethodsHook<W>,
143135
hook_ctx: &'a mut W::Context,
144-
flags: Option<OpenFlags>,
145-
) -> Result<sqld_libsql_bindings::Connection<'a>, rusqlite::Error>
136+
flags: Option<std::ffi::c_int>,
137+
) -> Result<sqld_libsql_bindings::Connection<'a>, libsql::Error>
146138
where
147139
W: WalHook,
148140
{
149141
let flags = flags.unwrap_or(
150-
OpenFlags::SQLITE_OPEN_READ_WRITE
151-
| OpenFlags::SQLITE_OPEN_CREATE
152-
| OpenFlags::SQLITE_OPEN_URI
153-
| OpenFlags::SQLITE_OPEN_NO_MUTEX,
142+
(sqld_libsql_bindings::ffi::SQLITE_OPEN_READWRITE
143+
| sqld_libsql_bindings::ffi::SQLITE_OPEN_CREATE
144+
| sqld_libsql_bindings::ffi::SQLITE_OPEN_URI
145+
| sqld_libsql_bindings::ffi::SQLITE_OPEN_NOMUTEX) as i32,
154146
);
155147

156148
sqld_libsql_bindings::Connection::open(path, flags, wal_methods, hook_ctx)
149+
.map_err(|rc| libsql::Error::LibError(rc))
157150
}
158151

159152
impl LibSqlDb {
@@ -261,14 +254,21 @@ impl<'a> Connection<'a> {
261254
};
262255

263256
for ext in extensions {
264-
unsafe {
265-
let _guard = rusqlite::LoadExtensionGuard::new(&this.conn).unwrap();
266-
if let Err(e) = this.conn.load_extension(&ext, None) {
267-
tracing::error!("failed to load extension: {}", ext.display());
268-
Err(e)?;
269-
}
270-
tracing::debug!("Loaded extension {}", ext.display());
257+
let rc = unsafe {
258+
// FIXME: gather the error message from the 4th param and print/return it
259+
// if applicable.
260+
libsql::ffi::sqlite3_load_extension(
261+
this.conn.handle(),
262+
ext.to_str().unwrap().as_ptr() as *const _,
263+
std::ptr::null(),
264+
std::ptr::null_mut(),
265+
)
266+
};
267+
if rc != libsql::ffi::SQLITE_OK as i32 {
268+
tracing::error!("failed to load extension: {}", ext.display());
269+
Err(libsql::Error::LibError(rc))?;
271270
}
271+
tracing::debug!("Loaded extension {}", ext.display());
272272
}
273273

274274
Ok(this)
@@ -354,26 +354,28 @@ impl<'a> Connection<'a> {
354354

355355
let cols = stmt.columns();
356356
let cols_count = cols.len();
357-
builder.cols_description(cols.iter())?;
358-
drop(cols);
357+
builder.cols_description(cols.into_iter())?;
359358

360359
query
361360
.params
362361
.bind(&mut stmt)
363362
.map_err(Error::LibSqlInvalidQueryParams)?;
364363

365-
let mut qresult = stmt.raw_query();
366-
builder.begin_rows()?;
367-
while let Some(row) = qresult.next()? {
368-
builder.begin_row()?;
369-
for i in 0..cols_count {
370-
let val = row.get_ref(i)?;
371-
builder.add_row_value(val)?;
364+
// FIXME: in current libsql implementation, the error will only be returned
365+
// upon first call to `next()`. Let's reconsider? That's not very intuitive.
366+
if let Some(qresult) = stmt.execute(&libsql::Params::None) {
367+
builder.begin_rows()?;
368+
while let Some(row) = qresult.next()? {
369+
builder.begin_row()?;
370+
for i in 0..cols_count {
371+
let val = row.get_ref(i as i32)?;
372+
builder.add_row_value(val)?;
373+
}
374+
builder.finish_row()?;
372375
}
373-
builder.finish_row()?;
374-
}
375376

376-
builder.finish_rows()?;
377+
builder.finish_rows()?;
378+
}
377379

378380
// sqlite3_changes() is only modified for INSERT, UPDATE or DELETE; it is not reset for SELECT,
379381
// but we want to return 0 in that case.
@@ -389,8 +391,6 @@ impl<'a> Connection<'a> {
389391
false => None,
390392
};
391393

392-
drop(qresult);
393-
394394
self.update_stats(&stmt);
395395

396396
Ok((affected_row_count, last_insert_rowid))
@@ -400,9 +400,10 @@ impl<'a> Connection<'a> {
400400
let _ = self.conn.execute("ROLLBACK", ());
401401
}
402402

403-
fn update_stats(&self, stmt: &rusqlite::Statement) {
404-
let rows_read = stmt.get_status(StatementStatus::RowsRead);
405-
let rows_written = stmt.get_status(StatementStatus::RowsWritten);
403+
fn update_stats(&self, stmt: &libsql::Statement) {
404+
use sqld_libsql_bindings::ffi;
405+
let rows_read = stmt.get_status(ffi::LIBSQL_STMTSTATUS_ROWS_READ as i32);
406+
let rows_written = stmt.get_status(ffi::LIBSQL_STMTSTATUS_ROWS_WRITTEN as i32);
406407
let rows_read = if rows_read == 0 && rows_written == 0 {
407408
1
408409
} else {
@@ -417,7 +418,7 @@ impl<'a> Connection<'a> {
417418

418419
let params = (1..=stmt.parameter_count())
419420
.map(|param_i| {
420-
let name = stmt.parameter_name(param_i).map(|n| n.into());
421+
let name = stmt.parameter_name(param_i as i32).map(|n| n.into());
421422
DescribeParam { name }
422423
})
423424
.collect();

‎sqld/src/database/write_proxy.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::path::PathBuf;
22
use std::sync::Arc;
33

44
use parking_lot::Mutex as PMutex;
5-
use rusqlite::types::ValueRef;
65
use sqld_libsql_bindings::wal_hook::TRANSPARENT_METHODS;
76
use tokio::sync::{watch, Mutex};
87
use tonic::transport::Channel;
@@ -25,6 +24,7 @@ use crate::Result;
2524
use super::config::DatabaseConfigStore;
2625
use super::Program;
2726
use super::{factory::DbFactory, libsql::LibSqlDb, Database, DescribeResult};
27+
use libsql::params::ValueRef;
2828

2929
#[derive(Clone)]
3030
pub struct WriteProxyDbFactory {
@@ -108,7 +108,7 @@ fn execute_results_to_builder<B: QueryResultBuilder>(
108108
builder.begin_step()?;
109109
builder.cols_description(rows.column_descriptions.iter().map(|c| Column {
110110
name: &c.name,
111-
decl_ty: c.decltype.as_deref(),
111+
decl_type: c.decltype.as_deref(),
112112
}))?;
113113

114114
builder.begin_rows()?;

‎sqld/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub enum Error {
1212
#[error(transparent)]
1313
IOError(#[from] std::io::Error),
1414
#[error(transparent)]
15-
RusqliteError(#[from] rusqlite::Error),
15+
LibSqlError(#[from] libsql::Error),
1616
#[error("Failed to execute query via RPC. Error code: {}, message: {}", .0.code, .0.message)]
1717
RpcQueryError(crate::rpc::proxy::rpc::Error),
1818
#[error("Failed to execute queries via RPC protocol: `{0}`")]

‎sqld/src/hrana/result_builder.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt::{self, Write as _};
22
use std::io;
33

44
use bytes::Bytes;
5-
use rusqlite::types::ValueRef;
5+
use libsql::params::ValueRef;
66

77
use crate::hrana::stmt::{proto_error_from_stmt_error, stmt_error_from_sqld_error};
88
use crate::query_result_builder::{
@@ -115,7 +115,7 @@ impl QueryResultBuilder for SingleStatementBuilder {
115115
cols_size += estimate_cols_json_size(&c);
116116
proto::Col {
117117
name: Some(c.name.to_owned()),
118-
decltype: c.decl_ty.map(ToString::to_string),
118+
decltype: c.decl_type.map(ToString::to_string),
119119
}
120120
}));
121121

@@ -207,7 +207,7 @@ fn estimate_cols_json_size(c: &Column) -> u64 {
207207
&mut f,
208208
r#"{{"name":"{}","decltype":"{}"}}"#,
209209
c.name,
210-
c.decl_ty.unwrap_or("null")
210+
c.decl_type.unwrap_or("null")
211211
)
212212
.unwrap();
213213
f.0

‎sqld/src/hrana/stmt.rs

Lines changed: 34 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use anyhow::{anyhow, bail, Result};
2+
use sqld_libsql_bindings::ffi;
23
use std::collections::HashMap;
34

45
use super::result_builder::SingleStatementBuilder;
@@ -31,16 +32,9 @@ pub enum StmtError {
3132
TransactionBusy,
3233
#[error("SQLite error: {message}")]
3334
SqliteError {
34-
source: rusqlite::ffi::Error,
35+
source: libsql::Error,
3536
message: String,
3637
},
37-
#[error("SQL input error: {message} (at offset {offset})")]
38-
SqlInputError {
39-
source: rusqlite::ffi::Error,
40-
message: String,
41-
offset: i32,
42-
},
43-
4438
#[error("Operation was blocked{}", .reason.as_ref().map(|msg| format!(": {}", msg)).unwrap_or_default())]
4539
Blocked { reason: Option<String> },
4640
#[error("Response is too large")]
@@ -202,26 +196,9 @@ pub fn stmt_error_from_sqld_error(sqld_error: SqldError) -> Result<StmtError, Sq
202196
StmtError::ResponseTooLarge
203197
}
204198
SqldError::Blocked(reason) => StmtError::Blocked { reason },
205-
SqldError::RusqliteError(rusqlite_error) => match rusqlite_error {
206-
rusqlite::Error::SqliteFailure(sqlite_error, Some(message)) => StmtError::SqliteError {
207-
source: sqlite_error,
208-
message,
209-
},
210-
rusqlite::Error::SqliteFailure(sqlite_error, None) => StmtError::SqliteError {
211-
message: sqlite_error.to_string(),
212-
source: sqlite_error,
213-
},
214-
rusqlite::Error::SqlInputError {
215-
error: sqlite_error,
216-
msg: message,
217-
offset,
218-
..
219-
} => StmtError::SqlInputError {
220-
source: sqlite_error,
221-
message,
222-
offset,
223-
},
224-
rusqlite_error => return Err(SqldError::RusqliteError(rusqlite_error)),
199+
SqldError::LibSqlError(libsql::Error::LibError(rc)) => StmtError::SqliteError {
200+
message: libsql::errors::error_from_code(rc),
201+
source: libsql::Error::LibError(rc),
225202
},
226203
sqld_error => return Err(sqld_error),
227204
})
@@ -244,40 +221,41 @@ impl StmtError {
244221
Self::ArgsBothPositionalAndNamed => "ARGS_BOTH_POSITIONAL_AND_NAMED",
245222
Self::TransactionTimeout => "TRANSACTION_TIMEOUT",
246223
Self::TransactionBusy => "TRANSACTION_BUSY",
247-
Self::SqliteError { source, .. } => sqlite_error_code(source.code),
248-
Self::SqlInputError { .. } => "SQL_INPUT_ERROR",
224+
Self::SqliteError { source, .. } => sqlite_error_code(source),
249225
Self::Blocked { .. } => "BLOCKED",
250226
Self::ResponseTooLarge => "RESPONSE_TOO_LARGE",
251227
}
252228
}
253229
}
254230

255-
fn sqlite_error_code(code: rusqlite::ffi::ErrorCode) -> &'static str {
256-
match code {
257-
rusqlite::ErrorCode::InternalMalfunction => "SQLITE_INTERNAL",
258-
rusqlite::ErrorCode::PermissionDenied => "SQLITE_PERM",
259-
rusqlite::ErrorCode::OperationAborted => "SQLITE_ABORT",
260-
rusqlite::ErrorCode::DatabaseBusy => "SQLITE_BUSY",
261-
rusqlite::ErrorCode::DatabaseLocked => "SQLITE_LOCKED",
262-
rusqlite::ErrorCode::OutOfMemory => "SQLITE_NOMEM",
263-
rusqlite::ErrorCode::ReadOnly => "SQLITE_READONLY",
264-
rusqlite::ErrorCode::OperationInterrupted => "SQLITE_INTERRUPT",
265-
rusqlite::ErrorCode::SystemIoFailure => "SQLITE_IOERR",
266-
rusqlite::ErrorCode::DatabaseCorrupt => "SQLITE_CORRUPT",
267-
rusqlite::ErrorCode::NotFound => "SQLITE_NOTFOUND",
268-
rusqlite::ErrorCode::DiskFull => "SQLITE_FULL",
269-
rusqlite::ErrorCode::CannotOpen => "SQLITE_CANTOPEN",
270-
rusqlite::ErrorCode::FileLockingProtocolFailed => "SQLITE_PROTOCOL",
271-
rusqlite::ErrorCode::SchemaChanged => "SQLITE_SCHEMA",
272-
rusqlite::ErrorCode::TooBig => "SQLITE_TOOBIG",
273-
rusqlite::ErrorCode::ConstraintViolation => "SQLITE_CONSTRAINT",
274-
rusqlite::ErrorCode::TypeMismatch => "SQLITE_MISMATCH",
275-
rusqlite::ErrorCode::ApiMisuse => "SQLITE_MISUSE",
276-
rusqlite::ErrorCode::NoLargeFileSupport => "SQLITE_NOLFS",
277-
rusqlite::ErrorCode::AuthorizationForStatementDenied => "SQLITE_AUTH",
278-
rusqlite::ErrorCode::ParameterOutOfRange => "SQLITE_RANGE",
279-
rusqlite::ErrorCode::NotADatabase => "SQLITE_NOTADB",
280-
rusqlite::ErrorCode::Unknown => "SQLITE_UNKNOWN",
231+
fn sqlite_error_code(err: &libsql::Error) -> &'static str {
232+
match err {
233+
libsql::Error::LibError(code) => match (*code) as u32 {
234+
ffi::SQLITE_INTERNAL => "SQLITE_INTERNAL",
235+
ffi::SQLITE_PERM => "SQLITE_PERM",
236+
ffi::SQLITE_ABORT => "SQLITE_ABORT",
237+
ffi::SQLITE_BUSY => "SQLITE_BUSY",
238+
ffi::SQLITE_LOCKED => "SQLITE_LOCKED",
239+
ffi::SQLITE_NOMEM => "SQLITE_NOMEM",
240+
ffi::SQLITE_READONLY => "SQLITE_READONLY",
241+
ffi::SQLITE_INTERRUPT => "SQLITE_INTERRUPT",
242+
ffi::SQLITE_IOERR => "SQLITE_IOERR",
243+
ffi::SQLITE_CORRUPT => "SQLITE_CORRUPT",
244+
ffi::SQLITE_NOTFOUND => "SQLITE_NOTFOUND",
245+
ffi::SQLITE_FULL => "SQLITE_FULL",
246+
ffi::SQLITE_CANTOPEN => "SQLITE_CANTOPEN",
247+
ffi::SQLITE_PROTOCOL => "SQLITE_PROTOCOL",
248+
ffi::SQLITE_SCHEMA => "SQLITE_SCHEMA",
249+
ffi::SQLITE_TOOBIG => "SQLITE_TOOBIG",
250+
ffi::SQLITE_CONSTRAINT => "SQLITE_CONSTRAINT",
251+
ffi::SQLITE_MISMATCH => "SQLITE_MISMATCH",
252+
ffi::SQLITE_MISUSE => "SQLITE_MISUSE",
253+
ffi::SQLITE_NOLFS => "SQLITE_NOLFS",
254+
ffi::SQLITE_AUTH => "SQLITE_AUTH",
255+
ffi::SQLITE_RANGE => "SQLITE_RANGE",
256+
ffi::SQLITE_NOTADB => "SQLITE_NOTADB",
257+
_ => "SQLITE_UNKNOWN",
258+
},
281259
_ => "SQLITE_UNKNOWN",
282260
}
283261
}

‎sqld/src/http/hrana_over_http_1.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ fn response_error_response(err: ResponseError) -> hyper::Response<hyper::Body> {
137137
| StmtError::SqlNoStmt
138138
| StmtError::SqlManyStmts
139139
| StmtError::ArgsInvalid { .. }
140-
| StmtError::SqlInputError { .. }
141140
| StmtError::ResponseTooLarge
142141
| StmtError::Blocked { .. } => hyper::StatusCode::BAD_REQUEST,
143142
StmtError::ArgsBothPositionalAndNamed => hyper::StatusCode::NOT_IMPLEMENTED,

‎sqld/src/http/result_builder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
use libsql::params::ValueRef;
12
use std::io;
23
use std::ops::{Deref, DerefMut};
34

4-
use rusqlite::types::ValueRef;
55
use serde::{Serialize, Serializer};
66
use serde_json::ser::{CompactFormatter, Formatter};
77

@@ -70,7 +70,7 @@ impl io::Write for LimitBuffer {
7070
}
7171
}
7272

73-
struct HttpJsonValueSerializer<'a>(&'a ValueRef<'a>);
73+
struct HttpJsonValueSerializer<'a>(&'a libsql::params::ValueRef<'a>);
7474

7575
impl JsonHttpPayloadBuilder {
7676
pub fn new() -> Self {

‎sqld/src/lib.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ use std::sync::mpsc::RecvTimeoutError;
55
use std::sync::Arc;
66
use std::time::Duration;
77

8+
use crate::libsql::wal_hook::TRANSPARENT_METHODS;
89
use anyhow::Context as AnyhowContext;
910
use enclose::enclose;
1011
use futures::never::Never;
11-
use libsql::wal_hook::TRANSPARENT_METHODS;
1212
use once_cell::sync::Lazy;
1313
use rpc::run_rpc_server;
1414
use tokio::sync::{mpsc, Notify};
@@ -551,7 +551,7 @@ async fn run_storage_monitor(db_path: PathBuf, stats: Stats) -> anyhow::Result<(
551551
// initialize a connection here, and keep it alive for the entirety of the program. If we
552552
// fail to open it, we wait for `duration` and try again later.
553553
let ctx = &mut ();
554-
let maybe_conn = match open_db(&db_path, &TRANSPARENT_METHODS, ctx, Some(rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY)) {
554+
let maybe_conn = match open_db(&db_path, &TRANSPARENT_METHODS, ctx, Some(sqld_libsql_bindings::ffi::SQLITE_OPEN_READONLY as i32)) {
555555
Ok(conn) => Some(conn),
556556
Err(e) => {
557557
tracing::warn!("failed to open connection for storager monitor: {e}, trying again in {duration:?}");
@@ -561,12 +561,14 @@ async fn run_storage_monitor(db_path: PathBuf, stats: Stats) -> anyhow::Result<(
561561

562562
loop {
563563
if let Some(ref conn) = maybe_conn {
564-
if let Ok(storage_bytes_used) =
565-
conn.query_row("select sum(pgsize) from dbstat;", [], |row| {
566-
row.get::<usize, u64>(0)
567-
})
564+
if let Ok(Some(rows)) =
565+
conn.execute("select sum(pgsize) from dbstat;", ())
568566
{
569-
stats.set_storage_bytes_used(storage_bytes_used);
567+
if let Ok(Some(storage_bytes_used)) = rows.next() {
568+
if let Ok(storage_bytes_used) = storage_bytes_used.get(0) {
569+
stats.set_storage_bytes_used(storage_bytes_used);
570+
}
571+
}
570572
}
571573
}
572574

‎sqld/src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ fn perform_dump(dump_path: Option<&Path>, db_path: &Path) -> anyhow::Result<()>
312312
}
313313
None => Box::new(stdout()),
314314
};
315-
let conn = rusqlite::Connection::open(db_path.join("data"))?;
315+
let conn = libsql::Database::open(db_path.join("data").to_str().unwrap())?.connect()?;
316316

317317
export_dump(conn, out)?;
318318

@@ -329,9 +329,11 @@ fn enable_libsql_logging() {
329329
tracing::error!("sqlite error {code}: {msg}");
330330
}
331331

332+
/* FIXME: introduce tracing to libsql/libsql_sys
332333
ONCE.call_once(|| unsafe {
333334
rusqlite::trace::config_log(Some(libsql_log)).unwrap();
334335
});
336+
*/
335337
}
336338

337339
#[tokio::main]

‎sqld/src/query.rs

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use std::collections::HashMap;
22

33
use anyhow::{anyhow, ensure, Context};
4-
use rusqlite::types::{ToSqlOutput, ValueRef};
5-
use rusqlite::ToSql;
64
use serde::{Deserialize, Serialize};
75

86
use crate::query_analysis::Statement;
@@ -18,28 +16,28 @@ pub enum Value {
1816
Blob(Vec<u8>),
1917
}
2018

21-
impl<'a> From<&'a Value> for ValueRef<'a> {
19+
impl<'a> From<&'a Value> for libsql::params::ValueRef<'a> {
2220
fn from(value: &'a Value) -> Self {
2321
match value {
24-
Value::Null => ValueRef::Null,
25-
Value::Integer(i) => ValueRef::Integer(*i),
26-
Value::Real(x) => ValueRef::Real(*x),
27-
Value::Text(s) => ValueRef::Text(s.as_bytes()),
28-
Value::Blob(b) => ValueRef::Blob(b.as_slice()),
22+
Value::Null => libsql::params::ValueRef::Null,
23+
Value::Integer(i) => libsql::params::ValueRef::Integer(*i),
24+
Value::Real(x) => libsql::params::ValueRef::Real(*x),
25+
Value::Text(s) => libsql::params::ValueRef::Text(s.as_bytes()),
26+
Value::Blob(b) => libsql::params::ValueRef::Blob(b),
2927
}
3028
}
3129
}
3230

33-
impl TryFrom<rusqlite::types::ValueRef<'_>> for Value {
31+
impl TryFrom<libsql::params::ValueRef<'_>> for Value {
3432
type Error = anyhow::Error;
3533

36-
fn try_from(value: rusqlite::types::ValueRef<'_>) -> anyhow::Result<Value> {
34+
fn try_from(value: libsql::params::ValueRef<'_>) -> anyhow::Result<Value> {
3735
let val = match value {
38-
rusqlite::types::ValueRef::Null => Value::Null,
39-
rusqlite::types::ValueRef::Integer(i) => Value::Integer(i),
40-
rusqlite::types::ValueRef::Real(x) => Value::Real(x),
41-
rusqlite::types::ValueRef::Text(s) => Value::Text(String::from_utf8(Vec::from(s))?),
42-
rusqlite::types::ValueRef::Blob(b) => Value::Blob(Vec::from(b)),
36+
libsql::params::ValueRef::Null => Value::Null,
37+
libsql::params::ValueRef::Integer(i) => Value::Integer(i),
38+
libsql::params::ValueRef::Real(x) => Value::Real(x),
39+
libsql::params::ValueRef::Text(s) => Value::Text(String::from_utf8(Vec::from(s))?),
40+
libsql::params::ValueRef::Blob(b) => Value::Blob(Vec::from(b)),
4341
};
4442

4543
Ok(val)
@@ -53,20 +51,6 @@ pub struct Query {
5351
pub want_rows: bool,
5452
}
5553

56-
impl ToSql for Value {
57-
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
58-
let val = match self {
59-
Value::Null => ToSqlOutput::Owned(rusqlite::types::Value::Null),
60-
Value::Integer(i) => ToSqlOutput::Owned(rusqlite::types::Value::Integer(*i)),
61-
Value::Real(x) => ToSqlOutput::Owned(rusqlite::types::Value::Real(*x)),
62-
Value::Text(s) => ToSqlOutput::Borrowed(rusqlite::types::ValueRef::Text(s.as_bytes())),
63-
Value::Blob(b) => ToSqlOutput::Borrowed(rusqlite::types::ValueRef::Blob(b)),
64-
};
65-
66-
Ok(val)
67-
}
68-
}
69-
7054
#[derive(Debug, Serialize, Clone)]
7155
pub enum Params {
7256
Named(HashMap<String, Value>),
@@ -108,7 +92,7 @@ impl Params {
10892
}
10993
}
11094

111-
pub fn bind(&self, stmt: &mut rusqlite::Statement) -> anyhow::Result<()> {
95+
pub fn bind(&self, stmt: &mut libsql::Statement) -> anyhow::Result<()> {
11296
let param_count = stmt.parameter_count();
11397
ensure!(
11498
param_count >= self.len(),
@@ -120,7 +104,7 @@ impl Params {
120104
for index in 1..=param_count {
121105
let mut param_name = None;
122106
// get by name
123-
let maybe_value = match stmt.parameter_name(index) {
107+
let maybe_value = match stmt.parameter_name(index as i32) {
124108
Some(name) => {
125109
param_name = Some(name);
126110
let mut chars = name.chars();
@@ -140,7 +124,7 @@ impl Params {
140124
};
141125

142126
if let Some(value) = maybe_value {
143-
stmt.raw_bind_parameter(index, value)?;
127+
stmt.bind_value(index as i32, value.try_into()?);
144128
} else if let Some(name) = param_name {
145129
return Err(anyhow!("value for parameter {} not found", name));
146130
} else {
@@ -159,7 +143,10 @@ mod test {
159143

160144
#[test]
161145
fn test_bind_params_positional_simple() {
162-
let con = rusqlite::Connection::open_in_memory().unwrap();
146+
let con = libsql::Database::open(":memory:")
147+
.unwrap()
148+
.connect()
149+
.unwrap();
163150
let mut stmt = con.prepare("SELECT ?").unwrap();
164151
let params = Params::new_positional(vec![Value::Integer(10)]);
165152
params.bind(&mut stmt).unwrap();
@@ -169,7 +156,10 @@ mod test {
169156

170157
#[test]
171158
fn test_bind_params_positional_numbered() {
172-
let con = rusqlite::Connection::open_in_memory().unwrap();
159+
let con = libsql::Database::open(":memory:")
160+
.unwrap()
161+
.connect()
162+
.unwrap();
173163
let mut stmt = con.prepare("SELECT ? || ?2 || ?1").unwrap();
174164
let params = Params::new_positional(vec![Value::Integer(10), Value::Integer(20)]);
175165
params.bind(&mut stmt).unwrap();
@@ -179,7 +169,10 @@ mod test {
179169

180170
#[test]
181171
fn test_bind_params_positional_named() {
182-
let con = rusqlite::Connection::open_in_memory().unwrap();
172+
let con = libsql::Database::open(":memory:")
173+
.unwrap()
174+
.connect()
175+
.unwrap();
183176
let mut stmt = con.prepare("SELECT :first || $second").unwrap();
184177
let mut params = HashMap::new();
185178
params.insert(":first".to_owned(), Value::Integer(10));
@@ -192,7 +185,10 @@ mod test {
192185

193186
#[test]
194187
fn test_bind_params_positional_named_no_prefix() {
195-
let con = rusqlite::Connection::open_in_memory().unwrap();
188+
let con = libsql::Database::open(":memory:")
189+
.unwrap()
190+
.connect()
191+
.unwrap();
196192
let mut stmt = con.prepare("SELECT :first || $second").unwrap();
197193
let mut params = HashMap::new();
198194
params.insert("first".to_owned(), Value::Integer(10));
@@ -205,7 +201,10 @@ mod test {
205201

206202
#[test]
207203
fn test_bind_params_positional_named_conflict() {
208-
let con = rusqlite::Connection::open_in_memory().unwrap();
204+
let con = libsql::Database::open(":memory:")
205+
.unwrap()
206+
.connect()
207+
.unwrap();
209208
let mut stmt = con.prepare("SELECT :first || $first").unwrap();
210209
let mut params = HashMap::new();
211210
params.insert("first".to_owned(), Value::Integer(10));
@@ -218,7 +217,10 @@ mod test {
218217

219218
#[test]
220219
fn test_bind_params_positional_named_repeated() {
221-
let con = rusqlite::Connection::open_in_memory().unwrap();
220+
let con = libsql::Database::open(":memory:")
221+
.unwrap()
222+
.connect()
223+
.unwrap();
222224
let mut stmt = con
223225
.prepare("SELECT :first || $second || $first || $second")
224226
.unwrap();
@@ -233,7 +235,10 @@ mod test {
233235

234236
#[test]
235237
fn test_bind_params_too_many_params() {
236-
let con = rusqlite::Connection::open_in_memory().unwrap();
238+
let con = libsql::Database::open(":memory:")
239+
.unwrap()
240+
.connect()
241+
.unwrap();
237242
let mut stmt = con.prepare("SELECT :first || $second").unwrap();
238243
let mut params = HashMap::new();
239244
params.insert(":first".to_owned(), Value::Integer(10));
@@ -245,7 +250,10 @@ mod test {
245250

246251
#[test]
247252
fn test_bind_params_too_few_params() {
248-
let con = rusqlite::Connection::open_in_memory().unwrap();
253+
let con = libsql::Database::open(":memory:")
254+
.unwrap()
255+
.connect()
256+
.unwrap();
249257
let mut stmt = con.prepare("SELECT :first || $second").unwrap();
250258
let mut params = HashMap::new();
251259
params.insert(":first".to_owned(), Value::Integer(10));
@@ -255,7 +263,10 @@ mod test {
255263

256264
#[test]
257265
fn test_bind_params_invalid_positional() {
258-
let con = rusqlite::Connection::open_in_memory().unwrap();
266+
let con = libsql::Database::open(":memory:")
267+
.unwrap()
268+
.connect()
269+
.unwrap();
259270
let mut stmt = con.prepare("SELECT ?invalid").unwrap();
260271
let params = Params::empty();
261272
assert!(params.bind(&mut stmt).is_err());

‎sqld/src/query_result_builder.rs

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ use std::io::{self, ErrorKind};
33
use std::ops::{Deref, DerefMut};
44

55
use bytesize::ByteSize;
6-
use rusqlite::types::ValueRef;
6+
use libsql::params::ValueRef;
77
use serde::Serialize;
88
use serde_json::ser::Formatter;
99

10+
pub use libsql::Column;
11+
1012
#[derive(Debug)]
1113
pub enum QueryResultBuilderError {
1214
ResponseTooLarge(u64),
@@ -54,28 +56,6 @@ impl From<io::Error> for QueryResultBuilderError {
5456
}
5557
}
5658

57-
/// Identical to rusqlite::Column, with visible fields.
58-
#[cfg_attr(test, derive(arbitrary::Arbitrary))]
59-
pub struct Column<'a> {
60-
pub(crate) name: &'a str,
61-
pub(crate) decl_ty: Option<&'a str>,
62-
}
63-
64-
impl<'a> From<(&'a str, Option<&'a str>)> for Column<'a> {
65-
fn from((name, decl_ty): (&'a str, Option<&'a str>)) -> Self {
66-
Self { name, decl_ty }
67-
}
68-
}
69-
70-
impl<'a> From<&'a rusqlite::Column<'a>> for Column<'a> {
71-
fn from(value: &'a rusqlite::Column<'a>) -> Self {
72-
Self {
73-
name: value.name(),
74-
decl_ty: value.decl_type(),
75-
}
76-
}
77-
}
78-
7959
#[derive(Debug, Clone, Copy, Default)]
8060
pub struct QueryBuilderConfig {
8161
pub max_size: Option<u64>,
@@ -107,7 +87,8 @@ pub trait QueryResultBuilder: Send + 'static {
10787
/// begin a new row for the current step
10888
fn begin_row(&mut self) -> Result<(), QueryResultBuilderError>;
10989
/// add value to current row
110-
fn add_row_value(&mut self, v: ValueRef) -> Result<(), QueryResultBuilderError>;
90+
fn add_row_value(&mut self, v: libsql::params::ValueRef)
91+
-> Result<(), QueryResultBuilderError>;
11192
/// finish current row
11293
fn finish_row(&mut self) -> Result<(), QueryResultBuilderError>;
11394
/// end adding rows
@@ -588,14 +569,14 @@ pub mod test {
588569
Blob(&'a [u8]),
589570
}
590571

591-
impl<'a> From<ValueRef<'a>> for rusqlite::types::ValueRef<'a> {
572+
impl<'a> From<ValueRef<'a>> for libsql::params::ValueRef<'a> {
592573
fn from(value: ValueRef<'a>) -> Self {
593574
match value {
594-
ValueRef::Null => rusqlite::types::ValueRef::Null,
595-
ValueRef::Integer(i) => rusqlite::types::ValueRef::Integer(i),
596-
ValueRef::Real(x) => rusqlite::types::ValueRef::Real(x),
597-
ValueRef::Text(s) => rusqlite::types::ValueRef::Text(s.as_bytes()),
598-
ValueRef::Blob(b) => rusqlite::types::ValueRef::Blob(b),
575+
ValueRef::Null => libsql::params::Value::Null,
576+
ValueRef::Integer(i) => libsql::params::Value::Integer(i),
577+
ValueRef::Real(x) => libsql::params::Value::Float(x),
578+
ValueRef::Text(s) => libsql::params::Value::Text(s.as_bytes()),
579+
ValueRef::Blob(b) => libsql::params::Value::Blob(b),
599580
}
600581
}
601582
}

‎sqld/src/replication/primary/logger.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ unsafe impl WalHook for ReplicationLoggerHook {
8686
if let Err(e) = ctx.flush(ntruncate) {
8787
tracing::error!("error writing to replication log: {e}");
8888
// returning IO_ERR ensure that xUndo will be called by sqlite.
89-
return SQLITE_IOERR;
89+
return SQLITE_IOERR as c_int;
9090
}
9191

9292
let rc = unsafe {
@@ -149,7 +149,7 @@ unsafe impl WalHook for ReplicationLoggerHook {
149149

150150
fn on_savepoint_undo(wal: &mut Wal, wal_data: *mut u32, orig: XWalSavePointUndoFn) -> i32 {
151151
let rc = unsafe { orig(wal, wal_data) };
152-
if rc != SQLITE_OK {
152+
if rc != SQLITE_OK as i32 {
153153
return rc;
154154
};
155155

@@ -193,9 +193,9 @@ unsafe impl WalHook for ReplicationLoggerHook {
193193
** In order to avoid autocheckpoint on close (that's too often),
194194
** checkpoint attempts weaker than TRUNCATE are ignored.
195195
*/
196-
if emode < SQLITE_CHECKPOINT_TRUNCATE {
196+
if emode < SQLITE_CHECKPOINT_TRUNCATE as i32 {
197197
tracing::trace!("Ignoring a checkpoint request weaker than TRUNCATE");
198-
return SQLITE_OK;
198+
return SQLITE_OK as i32;
199199
}
200200
}
201201
let rc = unsafe {
@@ -213,7 +213,7 @@ unsafe impl WalHook for ReplicationLoggerHook {
213213
)
214214
};
215215

216-
if rc != SQLITE_OK {
216+
if rc != SQLITE_OK as i32 {
217217
return rc;
218218
}
219219

@@ -226,7 +226,7 @@ unsafe impl WalHook for ReplicationLoggerHook {
226226
let mut replicator = replicator.lock().unwrap();
227227
if replicator.commits_in_current_generation() == 0 {
228228
tracing::debug!("No commits happened in this generation, not snapshotting");
229-
return SQLITE_OK;
229+
return SQLITE_OK as i32;
230230
}
231231
let last_known_frame = replicator.last_known_frame();
232232
replicator.request_flush();
@@ -237,18 +237,18 @@ unsafe impl WalHook for ReplicationLoggerHook {
237237
last_known_frame,
238238
e
239239
);
240-
return SQLITE_IOERR_WRITE;
240+
return SQLITE_IOERR_WRITE as i32;
241241
}
242242
replicator.new_generation();
243243
if let Err(e) =
244244
runtime.block_on(async move { replicator.snapshot_main_db_file().await })
245245
{
246246
tracing::error!("Failed to snapshot the main db file during checkpoint: {e}");
247-
return SQLITE_IOERR_WRITE;
247+
return SQLITE_IOERR_WRITE as i32;
248248
}
249249
}
250250
}
251-
SQLITE_OK
251+
SQLITE_OK as i32
252252
}
253253
}
254254

@@ -887,21 +887,24 @@ impl ReplicationLogger {
887887

888888
fn checkpoint_db(data_path: &Path) -> anyhow::Result<()> {
889889
unsafe {
890-
let conn = rusqlite::Connection::open(data_path)?;
891-
conn.pragma_query(None, "page_size", |row| {
892-
let page_size = row.get::<_, i32>(0).unwrap();
890+
// unwrap: data_path is expected to be a validated path
891+
let conn = libsql::Database::open(data_path.to_str().unwrap())?.connect()?;
892+
if let Ok(row) = conn
893+
.prepare("pragma page_size")?
894+
.query_row(&libsql::Params::None)
895+
{
896+
let page_size: i32 = row.get(0).unwrap();
893897
assert_eq!(
894898
page_size, WAL_PAGE_SIZE,
895899
"invalid database file, expected page size to be {}, but found {} instead",
896900
WAL_PAGE_SIZE, page_size
897901
);
898-
Ok(())
899-
})?;
902+
}
900903
let mut num_checkpointed: c_int = 0;
901-
let rc = rusqlite::ffi::sqlite3_wal_checkpoint_v2(
904+
let rc = sqld_libsql_bindings::ffi::sqlite3_wal_checkpoint_v2(
902905
conn.handle(),
903906
std::ptr::null(),
904-
SQLITE_CHECKPOINT_TRUNCATE,
907+
SQLITE_CHECKPOINT_TRUNCATE as i32,
905908
&mut num_checkpointed as *mut _,
906909
std::ptr::null_mut(),
907910
);

‎sqld/src/replication/replica/hook.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use std::ffi::{c_int, CStr};
22
use std::marker::PhantomData;
33

4-
use rusqlite::ffi::{PgHdr, SQLITE_ERROR};
5-
use sqld_libsql_bindings::ffi::Wal;
4+
use sqld_libsql_bindings::ffi::{PgHdr, Wal, SQLITE_ERROR};
65
use sqld_libsql_bindings::init_static_wal_method;
76
use sqld_libsql_bindings::{ffi::types::XWalFrameFn, wal_hook::WalHook};
87

@@ -167,7 +166,7 @@ unsafe impl WalHook for InjectorHook {
167166

168167
if let Err(e) = ret {
169168
tracing::error!("replication error: {e}");
170-
return SQLITE_ERROR;
169+
return SQLITE_ERROR as c_int;
171170
}
172171

173172
if !ctx.is_txn {

‎sqld/src/replication/replica/injector.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1+
use sqld_libsql_bindings::ffi;
12
use std::path::Path;
23

3-
use rusqlite::OpenFlags;
4-
54
use crate::replication::replica::hook::{SQLITE_CONTINUE_REPLICATION, SQLITE_EXIT_REPLICATION};
65

76
use super::hook::{InjectorHookCtx, INJECTOR_METHODS};
@@ -14,30 +13,31 @@ impl<'a> FrameInjector<'a> {
1413
pub fn new(db_path: &Path, hook_ctx: &'a mut InjectorHookCtx) -> anyhow::Result<Self> {
1514
let conn = sqld_libsql_bindings::Connection::open(
1615
db_path,
17-
OpenFlags::SQLITE_OPEN_READ_WRITE
18-
| OpenFlags::SQLITE_OPEN_CREATE
19-
| OpenFlags::SQLITE_OPEN_URI
20-
| OpenFlags::SQLITE_OPEN_NO_MUTEX,
16+
(ffi::SQLITE_OPEN_READWRITE
17+
| ffi::SQLITE_OPEN_CREATE
18+
| ffi::SQLITE_OPEN_URI
19+
| ffi::SQLITE_OPEN_NOMUTEX) as i32,
2120
&INJECTOR_METHODS,
2221
hook_ctx,
23-
)?;
22+
)
23+
.map_err(|rc| libsql::Error::LibError(rc))?;
2424

2525
Ok(Self { conn })
2626
}
2727

2828
pub fn step(&mut self) -> anyhow::Result<bool> {
29-
self.conn.pragma_update(None, "writable_schema", "on")?;
29+
self.conn.execute("pragma writable_schema=on", ())?;
3030
let res = self.conn.execute("create table __dummy__ (dummy);", ());
3131

3232
match res {
3333
Ok(_) => panic!("replication hook was not called"),
3434
Err(e) => {
35-
if let Some(e) = e.sqlite_error() {
36-
if e.extended_code == SQLITE_EXIT_REPLICATION {
37-
self.conn.pragma_update(None, "writable_schema", "reset")?;
35+
if let libsql::Error::LibError(rc) = e {
36+
if rc == SQLITE_EXIT_REPLICATION {
37+
self.conn.execute("pragma writable_schema=reset", ())?;
3838
return Ok(false);
3939
}
40-
if e.extended_code == SQLITE_CONTINUE_REPLICATION {
40+
if rc == SQLITE_CONTINUE_REPLICATION {
4141
return Ok(true);
4242
}
4343
}

‎sqld/src/rpc/proxy.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,15 +353,15 @@ impl QueryResultBuilder for ExecuteResultBuilder {
353353
for col in cols {
354354
let col = col.into();
355355
let col_len =
356-
(col.decl_ty.map(|s| s.len()).unwrap_or_default() + col.name.len()) as u64;
356+
(col.decl_type.map(|s| s.len()).unwrap_or_default() + col.name.len()) as u64;
357357
if col_len + self.current_step_size + self.current_size > self.max_size {
358358
return Err(QueryResultBuilderError::ResponseTooLarge(self.max_size));
359359
}
360360
self.current_step_size += col_len;
361361

362362
let col = rpc::Column {
363363
name: col.name.to_owned(),
364-
decltype: col.decl_ty.map(ToString::to_string),
364+
decltype: col.decl_type.map(ToString::to_string),
365365
};
366366

367367
self.current_col_description.push(col);
@@ -380,7 +380,7 @@ impl QueryResultBuilder for ExecuteResultBuilder {
380380

381381
fn add_row_value(
382382
&mut self,
383-
v: rusqlite::types::ValueRef,
383+
v: libsql::params::ValueRef,
384384
) -> Result<(), QueryResultBuilderError> {
385385
let data = bincode::serialize(
386386
&crate::query::Value::try_from(v).map_err(QueryResultBuilderError::from_any)?,

0 commit comments

Comments
 (0)
This repository has been archived.