-
-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
293 additions
and
188 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
use core::ptr; | ||
|
||
use crate::alloc::string::ToString; | ||
use alloc::format; | ||
use alloc::string::String; | ||
use core::ffi::{c_char, c_int}; | ||
use sqlite::ResultCode; | ||
use sqlite::StrRef; | ||
use sqlite::{sqlite3, Stmt}; | ||
use sqlite_nostd as sqlite; | ||
|
||
use crate::c::crsql_ExtData; | ||
use crate::c::crsql_fetchPragmaDataVersion; | ||
use crate::c::crsql_fetchPragmaSchemaVersion; | ||
use crate::c::DB_VERSION_SCHEMA_VERSION; | ||
use crate::consts::MIN_POSSIBLE_DB_VERSION; | ||
use crate::ext_data::recreate_db_version_stmt; | ||
|
||
#[no_mangle] | ||
pub extern "C" fn crsql_fill_db_version_if_needed( | ||
db: *mut sqlite3, | ||
ext_data: *mut crsql_ExtData, | ||
errmsg: *mut *mut c_char, | ||
) -> c_int { | ||
match fill_db_version_if_needed(db, ext_data) { | ||
Ok(rc) => rc as c_int, | ||
Err(msg) => { | ||
errmsg.set(&msg); | ||
ResultCode::ERROR as c_int | ||
} | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "C" fn crsql_next_db_version( | ||
db: *mut sqlite3, | ||
ext_data: *mut crsql_ExtData, | ||
merging_version: sqlite::int64, | ||
errmsg: *mut *mut c_char, | ||
) -> sqlite::int64 { | ||
match next_db_version(db, ext_data, Some(merging_version)) { | ||
Ok(version) => version, | ||
Err(msg) => { | ||
errmsg.set(&msg); | ||
-1 | ||
} | ||
} | ||
} | ||
|
||
pub fn next_db_version( | ||
db: *mut sqlite3, | ||
ext_data: *mut crsql_ExtData, | ||
merging_version: Option<i64>, | ||
) -> Result<i64, String> { | ||
fill_db_version_if_needed(db, ext_data)?; | ||
|
||
let mut ret = unsafe { (*ext_data).dbVersion + 1 }; | ||
if ret < unsafe { (*ext_data).pendingDbVersion } { | ||
ret = unsafe { (*ext_data).pendingDbVersion }; | ||
} | ||
if let Some(merging_version) = merging_version { | ||
if ret < merging_version { | ||
ret = merging_version; | ||
} | ||
} | ||
unsafe { | ||
(*ext_data).pendingDbVersion = ret; | ||
} | ||
Ok(ret) | ||
} | ||
|
||
pub fn fill_db_version_if_needed( | ||
db: *mut sqlite3, | ||
ext_data: *mut crsql_ExtData, | ||
) -> Result<ResultCode, String> { | ||
unsafe { | ||
let rc = crsql_fetchPragmaDataVersion(db, ext_data); | ||
if rc == -1 { | ||
return Err("failed to fetch PRAGMA data_version".to_string()); | ||
} | ||
if (*ext_data).dbVersion != -1 && rc == 0 { | ||
return Ok(ResultCode::OK); | ||
} | ||
fetch_db_version_from_storage(db, ext_data) | ||
} | ||
} | ||
|
||
pub fn fetch_db_version_from_storage( | ||
db: *mut sqlite3, | ||
ext_data: *mut crsql_ExtData, | ||
) -> Result<ResultCode, String> { | ||
unsafe { | ||
let mut schema_changed = 0; | ||
if (*ext_data).pDbVersionStmt == ptr::null_mut() { | ||
schema_changed = 1; | ||
} else { | ||
schema_changed = | ||
crsql_fetchPragmaSchemaVersion(db, ext_data, DB_VERSION_SCHEMA_VERSION); | ||
} | ||
|
||
if schema_changed < 0 { | ||
return Err("failed to fetch the pragma schema version".to_string()); | ||
} | ||
|
||
if schema_changed > 0 { | ||
match recreate_db_version_stmt(db, ext_data) { | ||
Ok(ResultCode::DONE) => { | ||
// this means there are no clock tables / this is a clean db | ||
(*ext_data).dbVersion = 0; | ||
return Ok(ResultCode::OK); | ||
} | ||
Ok(_) => {} | ||
Err(rc) => return Err(format!("failed to recreate db version stmt: {}", rc)), | ||
} | ||
} | ||
|
||
let db_version_stmt = (*ext_data).pDbVersionStmt; | ||
let rc = db_version_stmt.step(); | ||
match rc { | ||
// no rows? We're a fresh db with the min starting version | ||
Ok(ResultCode::DONE) => { | ||
db_version_stmt.reset().or_else(|rc| { | ||
Err(format!( | ||
"failed to reset db version stmt after DONE: {}", | ||
rc | ||
)) | ||
})?; | ||
(*ext_data).dbVersion = MIN_POSSIBLE_DB_VERSION; | ||
Ok(ResultCode::OK) | ||
} | ||
// got a row? It is our db version. | ||
Ok(ResultCode::ROW) => { | ||
(*ext_data).dbVersion = db_version_stmt.column_int64(0); | ||
db_version_stmt | ||
.reset() | ||
.or_else(|rc| Err(format!("failed to reset db version stmt after ROW: {}", rc))) | ||
} | ||
// Not row or done? Something went wrong. | ||
Ok(rc) | Err(rc) => { | ||
db_version_stmt.reset().or_else(|rc| { | ||
Err(format!("failed to reset db version stmt after ROW: {}", rc)) | ||
})?; | ||
Err(format!("failed to step db version stmt: {}", rc)) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
pub use crate::bootstrap; | ||
pub use crate::c; | ||
pub use crate::db_version; | ||
pub use crate::pack_columns; | ||
pub use crate::tableinfo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,4 @@ pub mod sync_bit_honored; | |
pub mod tableinfo; | ||
pub mod teardown; | ||
pub mod test_cl_set_vtab; | ||
pub mod test_db_version; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
extern crate alloc; | ||
use alloc::{ffi::CString, string::String}; | ||
use core::ffi::c_char; | ||
use crsql_bundle::test_exports; | ||
use sqlite::{Connection, ResultCode}; | ||
use sqlite_nostd as sqlite; | ||
|
||
fn make_site() -> *mut c_char { | ||
let inner_ptr: *mut c_char = CString::new("0000000000000000").unwrap().into_raw(); | ||
inner_ptr | ||
} | ||
|
||
fn test_fetch_db_version_from_storage() -> Result<ResultCode, String> { | ||
let c = crate::opendb().expect("db opened"); | ||
let db = &c.db; | ||
let raw_db = db.db; | ||
let ext_data = unsafe { test_exports::c::crsql_newExtData(raw_db, make_site()) }; | ||
|
||
test_exports::db_version::fetch_db_version_from_storage(raw_db, ext_data)?; | ||
// no clock tables, no version. | ||
assert_eq!(0, unsafe { (*ext_data).dbVersion }); | ||
|
||
// this was a bug where calling twice on a fresh db would fail the second | ||
// time. | ||
test_exports::db_version::fetch_db_version_from_storage(raw_db, ext_data)?; | ||
// should still return same data on a subsequent call with no schema | ||
assert_eq!(0, unsafe { (*ext_data).dbVersion }); | ||
|
||
// create some schemas | ||
db.exec_safe("CREATE TABLE foo (a primary key not null, b);") | ||
.expect("made foo"); | ||
db.exec_safe("SELECT crsql_as_crr('foo');") | ||
.expect("made foo crr"); | ||
test_exports::db_version::fetch_db_version_from_storage(raw_db, ext_data)?; | ||
// still v0 since no rows are inserted | ||
assert_eq!(0, unsafe { (*ext_data).dbVersion }); | ||
|
||
// version is bumped due to insert | ||
db.exec_safe("INSERT INTO foo (a, b) VALUES (1, 2);") | ||
.expect("inserted"); | ||
test_exports::db_version::fetch_db_version_from_storage(raw_db, ext_data)?; | ||
assert_eq!(1, unsafe { (*ext_data).dbVersion }); | ||
|
||
db.exec_safe("CREATE TABLE bar (a primary key not null, b);") | ||
.expect("created bar"); | ||
db.exec_safe("SELECT crsql_as_crr('bar');") | ||
.expect("bar as crr"); | ||
db.exec_safe("INSERT INTO bar VALUES (1, 2)") | ||
.expect("inserted into bar"); | ||
test_exports::db_version::fetch_db_version_from_storage(raw_db, ext_data)?; | ||
assert_eq!(2, unsafe { (*ext_data).dbVersion }); | ||
|
||
test_exports::db_version::fetch_db_version_from_storage(raw_db, ext_data)?; | ||
assert_eq!(2, unsafe { (*ext_data).dbVersion }); | ||
|
||
unsafe { | ||
test_exports::c::crsql_freeExtData(ext_data); | ||
}; | ||
|
||
Ok(ResultCode::OK) | ||
} | ||
|
||
fn test_next_db_version() -> Result<(), String> { | ||
let c = crate::opendb().expect("db opened"); | ||
let db = &c.db; | ||
let raw_db = db.db; | ||
let ext_data = unsafe { test_exports::c::crsql_newExtData(raw_db, make_site()) }; | ||
|
||
// is current + 1 | ||
// doesn't bump forward on successive calls | ||
assert_eq!( | ||
1, | ||
test_exports::db_version::next_db_version(raw_db, ext_data, None)? | ||
); | ||
assert_eq!( | ||
1, | ||
test_exports::db_version::next_db_version(raw_db, ext_data, None)? | ||
); | ||
// doesn't roll back with new provideds | ||
assert_eq!( | ||
1, | ||
test_exports::db_version::next_db_version(raw_db, ext_data, Some(-1))? | ||
); | ||
assert_eq!( | ||
1, | ||
test_exports::db_version::next_db_version(raw_db, ext_data, Some(0))? | ||
); | ||
// sets to max of current and provided | ||
assert_eq!( | ||
3, | ||
test_exports::db_version::next_db_version(raw_db, ext_data, Some(3))? | ||
); | ||
assert_eq!( | ||
3, | ||
test_exports::db_version::next_db_version(raw_db, ext_data, Some(2))? | ||
); | ||
|
||
// existing db version not touched | ||
assert_eq!(0, unsafe { (*ext_data).dbVersion }); | ||
|
||
unsafe { | ||
test_exports::c::crsql_freeExtData(ext_data); | ||
}; | ||
Ok(()) | ||
} | ||
|
||
pub fn run_suite() -> Result<(), String> { | ||
test_fetch_db_version_from_storage()?; | ||
test_next_db_version()?; | ||
Ok(()) | ||
} |
Oops, something went wrong.