mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-29 14:04:22 +01:00
Merge 'Add libsql_wal_frame_count() API' from Pekka Enberg
Reviewed-by: Pere Diaz Bou <pere-altea@homail.com> Closes #1489
This commit is contained in:
@@ -486,6 +486,10 @@ impl Connection {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn wal_frame_count(&self) -> Result<u64> {
|
||||
self.pager.wal_frame_count()
|
||||
}
|
||||
|
||||
pub fn cacheflush(&self) -> Result<CheckpointStatus> {
|
||||
self.pager.cacheflush()
|
||||
}
|
||||
|
||||
@@ -398,6 +398,15 @@ impl Pager {
|
||||
dirty_pages.insert(page_id);
|
||||
}
|
||||
|
||||
pub fn wal_frame_count(&self) -> Result<u64> {
|
||||
let mut frame_count = 0;
|
||||
let wal = self.wal.clone();
|
||||
if let Some(wal) = &wal {
|
||||
frame_count = wal.borrow().get_max_frame_in_wal();
|
||||
}
|
||||
Ok(frame_count)
|
||||
}
|
||||
|
||||
pub fn cacheflush(&self) -> Result<CheckpointStatus> {
|
||||
let mut checkpoint_result = CheckpointResult::default();
|
||||
loop {
|
||||
|
||||
34
docs/manual.md
Normal file
34
docs/manual.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Limbo Documentation
|
||||
|
||||
## SQLite C API
|
||||
|
||||
### WAL manipulation
|
||||
|
||||
#### `libsql_wal_frame_count`
|
||||
|
||||
Get the number of frames in the WAL.
|
||||
|
||||
**Synopsis:**
|
||||
|
||||
```c
|
||||
int libsql_wal_frame_count(sqlite3 *db, uint32_t *p_frame_count);
|
||||
```
|
||||
|
||||
**Description:**
|
||||
|
||||
The `libsql_wal_frame_count` function returns the number of frames in the WAL
|
||||
in the `p_frame_count` parameter.
|
||||
|
||||
**Return Values:**
|
||||
|
||||
* `SQLITE_OK` if the number of frames in the WAL file is successfully returned.
|
||||
* `SQLITE_MISUSE` if the `db` is NULL.
|
||||
* SQLITE_ERROR if an error occurs while getting the number of frames in the WAL
|
||||
file.
|
||||
|
||||
**Safety Requirements:**
|
||||
|
||||
* The `db` parameter must be a valid pointer to a `sqlite3` database
|
||||
connection.
|
||||
* The `p_frame_count` must be a valid pointer to a `u32` that will store the
|
||||
* number of frames in the WAL file.
|
||||
@@ -266,6 +266,28 @@ int sqlite3_wal_checkpoint(sqlite3 *_db, const char *_db_name);
|
||||
|
||||
int sqlite3_wal_checkpoint_v2(sqlite3 *db, const char *_db_name, int _mode, int *_log_size, int *_checkpoint_count);
|
||||
|
||||
/**
|
||||
* Get the number of frames in the WAL.
|
||||
*
|
||||
* The `libsql_wal_frame_count` function returns the number of frames
|
||||
* in the WAL in the `p_frame_count` parameter.
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* - `SQLITE_OK` if the number of frames in the WAL file is
|
||||
* successfully returned.
|
||||
* - `SQLITE_MISUSE` if the `db` is `NULL`.
|
||||
* - `SQLITE_ERROR` if an error occurs while getting the number of frames
|
||||
* in the WAL file.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* - The `db` must be a valid pointer to a `sqlite3` database connection.
|
||||
* - The `p_frame_count` must be a valid pointer to a `u32` that will store
|
||||
* the number of frames in the WAL file.
|
||||
*/
|
||||
int libsql_wal_frame_count(sqlite3 *db, uint32_t *p_frame_count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
@@ -40,6 +40,7 @@ pub mod util;
|
||||
use util::sqlite3_safety_check_sick_or_ok;
|
||||
|
||||
pub struct sqlite3 {
|
||||
pub(crate) io: Arc<dyn limbo_core::IO>,
|
||||
pub(crate) _db: Arc<limbo_core::Database>,
|
||||
pub(crate) conn: Rc<limbo_core::Connection>,
|
||||
pub(crate) err_code: ffi::c_int,
|
||||
@@ -50,8 +51,13 @@ pub struct sqlite3 {
|
||||
}
|
||||
|
||||
impl sqlite3 {
|
||||
pub fn new(db: Arc<limbo_core::Database>, conn: Rc<limbo_core::Connection>) -> Self {
|
||||
pub fn new(
|
||||
io: Arc<dyn limbo_core::IO>,
|
||||
db: Arc<limbo_core::Database>,
|
||||
conn: Rc<limbo_core::Connection>,
|
||||
) -> Self {
|
||||
Self {
|
||||
io,
|
||||
_db: db,
|
||||
conn,
|
||||
err_code: SQLITE_OK,
|
||||
@@ -64,12 +70,13 @@ impl sqlite3 {
|
||||
}
|
||||
|
||||
pub struct sqlite3_stmt {
|
||||
pub(crate) db: *mut sqlite3,
|
||||
pub(crate) stmt: limbo_core::Statement,
|
||||
}
|
||||
|
||||
impl sqlite3_stmt {
|
||||
pub fn new(stmt: limbo_core::Statement) -> Self {
|
||||
Self { stmt }
|
||||
pub fn new(db: *mut sqlite3, stmt: limbo_core::Statement) -> Self {
|
||||
Self { db, stmt }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,10 +124,10 @@ pub unsafe extern "C" fn sqlite3_open(
|
||||
Err(_) => return SQLITE_CANTOPEN,
|
||||
},
|
||||
};
|
||||
match limbo_core::Database::open_file(io, filename, false) {
|
||||
match limbo_core::Database::open_file(io.clone(), filename, false) {
|
||||
Ok(db) => {
|
||||
let conn = db.connect().unwrap();
|
||||
*db_out = Box::leak(Box::new(sqlite3::new(db, conn)));
|
||||
*db_out = Box::leak(Box::new(sqlite3::new(io, db, conn)));
|
||||
SQLITE_OK
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -219,7 +226,7 @@ pub unsafe extern "C" fn sqlite3_prepare_v2(
|
||||
Ok(stmt) => stmt,
|
||||
Err(_) => return SQLITE_ERROR,
|
||||
};
|
||||
*out_stmt = Box::leak(Box::new(sqlite3_stmt::new(stmt)));
|
||||
*out_stmt = Box::leak(Box::new(sqlite3_stmt::new(db, stmt)));
|
||||
SQLITE_OK
|
||||
}
|
||||
|
||||
@@ -235,16 +242,23 @@ pub unsafe extern "C" fn sqlite3_finalize(stmt: *mut sqlite3_stmt) -> ffi::c_int
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_step(stmt: *mut sqlite3_stmt) -> ffi::c_int {
|
||||
let stmt = &mut *stmt;
|
||||
if let Ok(result) = stmt.stmt.step() {
|
||||
match result {
|
||||
limbo_core::StepResult::IO => SQLITE_BUSY,
|
||||
limbo_core::StepResult::Done => SQLITE_DONE,
|
||||
limbo_core::StepResult::Interrupt => SQLITE_INTERRUPT,
|
||||
limbo_core::StepResult::Row => SQLITE_ROW,
|
||||
limbo_core::StepResult::Busy => SQLITE_BUSY,
|
||||
let db = &mut *stmt.db;
|
||||
loop {
|
||||
if let Ok(result) = stmt.stmt.step() {
|
||||
match result {
|
||||
limbo_core::StepResult::IO => {
|
||||
let io = db.io.clone();
|
||||
io.run_once().unwrap();
|
||||
continue;
|
||||
}
|
||||
limbo_core::StepResult::Done => return SQLITE_DONE,
|
||||
limbo_core::StepResult::Interrupt => return SQLITE_INTERRUPT,
|
||||
limbo_core::StepResult::Row => return SQLITE_ROW,
|
||||
limbo_core::StepResult::Busy => return SQLITE_BUSY,
|
||||
}
|
||||
} else {
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
} else {
|
||||
SQLITE_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,3 +1097,38 @@ pub unsafe extern "C" fn sqlite3_wal_checkpoint_v2(
|
||||
}
|
||||
SQLITE_OK
|
||||
}
|
||||
|
||||
/// Get the number of frames in the WAL.
|
||||
///
|
||||
/// The `libsql_wal_frame_count` function returns the number of frames
|
||||
/// in the WAL in the `p_frame_count` parameter.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `SQLITE_OK` if the number of frames in the WAL file is
|
||||
/// successfully returned.
|
||||
/// - `SQLITE_MISUSE` if the `db` is `NULL`.
|
||||
/// - `SQLITE_ERROR` if an error occurs while getting the number of frames
|
||||
/// in the WAL file.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The `db` must be a valid pointer to a `sqlite3` database connection.
|
||||
/// - The `p_frame_count` must be a valid pointer to a `u32` that will store
|
||||
/// the number of frames in the WAL file.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn libsql_wal_frame_count(
|
||||
db: *mut sqlite3,
|
||||
p_frame_count: *mut u32,
|
||||
) -> ffi::c_int {
|
||||
if db.is_null() {
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
let db: &mut sqlite3 = &mut *db;
|
||||
let frame_count = match db.conn.wal_frame_count() {
|
||||
Ok(count) => count as u32,
|
||||
Err(_) => return SQLITE_ERROR,
|
||||
};
|
||||
*p_frame_count = frame_count;
|
||||
SQLITE_OK
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::ptr;
|
||||
|
||||
@@ -26,6 +27,7 @@ extern "C" {
|
||||
stmt: *mut *mut sqlite3_stmt,
|
||||
tail: *mut *const libc::c_char,
|
||||
) -> i32;
|
||||
fn sqlite3_step(stmt: *mut sqlite3_stmt) -> i32;
|
||||
fn sqlite3_finalize(stmt: *mut sqlite3_stmt) -> i32;
|
||||
fn sqlite3_wal_checkpoint(db: *mut sqlite3, db_name: *const libc::c_char) -> i32;
|
||||
fn sqlite3_wal_checkpoint_v2(
|
||||
@@ -35,10 +37,13 @@ extern "C" {
|
||||
log_size: *mut i32,
|
||||
checkpoint_count: *mut i32,
|
||||
) -> i32;
|
||||
fn libsql_wal_frame_count(db: *mut sqlite3, p_frame_count: *mut u32) -> i32;
|
||||
}
|
||||
|
||||
const SQLITE_OK: i32 = 0;
|
||||
const SQLITE_CANTOPEN: i32 = 14;
|
||||
const SQLITE_DONE: i32 = 101;
|
||||
|
||||
const SQLITE_CHECKPOINT_PASSIVE: i32 = 0;
|
||||
const SQLITE_CHECKPOINT_FULL: i32 = 1;
|
||||
const SQLITE_CHECKPOINT_RESTART: i32 = 2;
|
||||
@@ -195,4 +200,55 @@ mod tests {
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sqlite3"))]
|
||||
mod libsql_ext {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_wal_frame_count() {
|
||||
unsafe {
|
||||
let mut db = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_open(b"../testing/testing.db\0".as_ptr() as *const i8, &mut db),
|
||||
SQLITE_OK
|
||||
);
|
||||
// Ensure that WAL is initially empty.
|
||||
let mut frame_count = 0;
|
||||
assert_eq!(libsql_wal_frame_count(db, &mut frame_count), SQLITE_OK);
|
||||
assert_eq!(frame_count, 0);
|
||||
// Create a table and insert a row.
|
||||
let mut stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
b"CREATE TABLE test (id INTEGER PRIMARY KEY)\0".as_ptr() as *const i8,
|
||||
-1,
|
||||
&mut stmt,
|
||||
ptr::null_mut()
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_DONE);
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
let mut stmt = ptr::null_mut();
|
||||
assert_eq!(
|
||||
sqlite3_prepare_v2(
|
||||
db,
|
||||
b"INSERT INTO test (id) VALUES (1)\0".as_ptr() as *const i8,
|
||||
-1,
|
||||
&mut stmt,
|
||||
ptr::null_mut()
|
||||
),
|
||||
SQLITE_OK
|
||||
);
|
||||
assert_eq!(sqlite3_step(stmt), SQLITE_DONE);
|
||||
assert_eq!(sqlite3_finalize(stmt), SQLITE_OK);
|
||||
// Check that WAL has three frames.
|
||||
assert_eq!(libsql_wal_frame_count(db, &mut frame_count), SQLITE_OK);
|
||||
assert_eq!(frame_count, 3);
|
||||
assert_eq!(sqlite3_close(db), SQLITE_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user