mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-18 17:14:20 +01:00
167 lines
5.7 KiB
Rust
167 lines
5.7 KiB
Rust
use crate::common::{do_flush, maybe_setup_tracing, TempDatabase};
|
|
use std::cell::RefCell;
|
|
use std::ops::Deref;
|
|
use std::rc::Rc;
|
|
use std::sync::{Arc, Mutex};
|
|
use turso_core::{Connection, LimboError, Result, StepResult};
|
|
|
|
#[allow(clippy::arc_with_non_send_sync)]
|
|
#[test]
|
|
fn test_wal_checkpoint_result() -> Result<()> {
|
|
maybe_setup_tracing();
|
|
let tmp_db = TempDatabase::new("test_wal.db", false);
|
|
let conn = tmp_db.connect_limbo();
|
|
conn.execute("CREATE TABLE t1 (id text);")?;
|
|
|
|
let res = execute_and_get_strings(&conn, "pragma journal_mode;")?;
|
|
assert_eq!(res, vec!["wal"]);
|
|
|
|
conn.execute("insert into t1(id) values (1), (2);")?;
|
|
do_flush(&conn, &tmp_db).unwrap();
|
|
conn.execute("select * from t1;")?;
|
|
do_flush(&conn, &tmp_db).unwrap();
|
|
|
|
// checkpoint result should return > 0 num pages now as database has data
|
|
let res = execute_and_get_ints(&conn, "pragma wal_checkpoint;")?;
|
|
println!("'pragma wal_checkpoint;' returns: {res:?}");
|
|
assert_eq!(res.len(), 3);
|
|
assert_eq!(res[0], 0); // checkpoint successfully
|
|
assert!(res[1] > 0); // num pages in wal
|
|
assert!(res[2] > 0); // num pages checkpointed successfully
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "ignored for now because it's flaky"]
|
|
fn test_wal_1_writer_1_reader() -> Result<()> {
|
|
maybe_setup_tracing();
|
|
let tmp_db = Arc::new(Mutex::new(TempDatabase::new("test_wal.db", false)));
|
|
let db = tmp_db.lock().unwrap().limbo_database(false);
|
|
|
|
{
|
|
let conn = db.connect().unwrap();
|
|
match conn.query("CREATE TABLE t (id)")? {
|
|
Some(ref mut rows) => loop {
|
|
match rows.step().unwrap() {
|
|
StepResult::Row => {}
|
|
StepResult::IO => {
|
|
rows.run_once().unwrap();
|
|
}
|
|
StepResult::Interrupt => break,
|
|
StepResult::Done => break,
|
|
StepResult::Busy => unreachable!(),
|
|
}
|
|
},
|
|
None => todo!(),
|
|
}
|
|
do_flush(&conn, tmp_db.lock().unwrap().deref()).unwrap();
|
|
}
|
|
let rows = Arc::new(std::sync::Mutex::new(0));
|
|
let rows_ = rows.clone();
|
|
const ROWS_WRITE: usize = 1000;
|
|
let tmp_db_w = db.clone();
|
|
let writer_thread = std::thread::spawn(move || {
|
|
let conn = tmp_db_w.connect().unwrap();
|
|
for i in 0..ROWS_WRITE {
|
|
conn.execute(format!("INSERT INTO t values({i})").as_str())
|
|
.unwrap();
|
|
let mut rows = rows_.lock().unwrap();
|
|
*rows += 1;
|
|
}
|
|
});
|
|
let rows_ = rows.clone();
|
|
let reader_thread = std::thread::spawn(move || {
|
|
let conn = db.connect().unwrap();
|
|
loop {
|
|
let rows = *rows_.lock().unwrap();
|
|
let mut i = 0;
|
|
match conn.query("SELECT * FROM t") {
|
|
Ok(Some(ref mut rows)) => loop {
|
|
match rows.step().unwrap() {
|
|
StepResult::Row => {
|
|
let row = rows.row().unwrap();
|
|
let id = row.get::<i64>(0).unwrap();
|
|
assert_eq!(id, i);
|
|
i += 1;
|
|
}
|
|
StepResult::IO => {
|
|
rows.run_once().unwrap();
|
|
}
|
|
StepResult::Interrupt => break,
|
|
StepResult::Done => break,
|
|
StepResult::Busy => unreachable!(),
|
|
}
|
|
},
|
|
Ok(None) => {}
|
|
Err(err) => {
|
|
eprintln!("{err}");
|
|
}
|
|
}
|
|
if rows == ROWS_WRITE {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
writer_thread.join().unwrap();
|
|
reader_thread.join().unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
/// Execute a statement and get strings result
|
|
pub(crate) fn execute_and_get_strings(conn: &Arc<Connection>, sql: &str) -> Result<Vec<String>> {
|
|
let statement = conn.prepare(sql)?;
|
|
let stmt = Rc::new(RefCell::new(statement));
|
|
let mut result = Vec::new();
|
|
|
|
let mut stmt = stmt.borrow_mut();
|
|
while let Ok(step_result) = stmt.step() {
|
|
match step_result {
|
|
StepResult::Row => {
|
|
let row = stmt.row().unwrap();
|
|
for el in row.get_values() {
|
|
result.push(format!("{el}"));
|
|
}
|
|
}
|
|
StepResult::Done => break,
|
|
StepResult::Interrupt => break,
|
|
StepResult::IO => stmt.run_once()?,
|
|
StepResult::Busy => stmt.run_once()?,
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
/// Execute a statement and get integers
|
|
pub(crate) fn execute_and_get_ints(conn: &Arc<Connection>, sql: &str) -> Result<Vec<i64>> {
|
|
let statement = conn.prepare(sql)?;
|
|
let stmt = Rc::new(RefCell::new(statement));
|
|
let mut result = Vec::new();
|
|
|
|
let mut stmt = stmt.borrow_mut();
|
|
while let Ok(step_result) = stmt.step() {
|
|
match step_result {
|
|
StepResult::Row => {
|
|
let row = stmt.row().unwrap();
|
|
for value in row.get_values() {
|
|
let out = match value {
|
|
turso_core::Value::Integer(i) => i,
|
|
_ => {
|
|
return Err(LimboError::ConversionError(format!(
|
|
"cannot convert {value} to int"
|
|
)))
|
|
}
|
|
};
|
|
result.push(*out);
|
|
}
|
|
}
|
|
StepResult::Done => break,
|
|
StepResult::Interrupt => break,
|
|
StepResult::IO => stmt.run_once()?,
|
|
StepResult::Busy => stmt.run_once()?,
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|