Add crsql changes metadata status
This commit is contained in:
parent
e039a4f81f
commit
677945073b
8 changed files with 182 additions and 15 deletions
|
|
@ -12,9 +12,43 @@ pub struct DbResource {
|
|||
pub path_exists: bool,
|
||||
pub size_bytes: Option<u64>,
|
||||
pub schema_metadata: String,
|
||||
pub crsqlite_changes: CrSqliteChangeMetadata,
|
||||
pub sync_status: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CrSqliteChangeMetadata {
|
||||
pub available: bool,
|
||||
pub change_count: Option<u64>,
|
||||
pub max_db_version: Option<i64>,
|
||||
pub columns: Vec<String>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl CrSqliteChangeMetadata {
|
||||
#[must_use]
|
||||
pub fn unavailable() -> Self {
|
||||
Self {
|
||||
available: false,
|
||||
change_count: None,
|
||||
max_db_version: None,
|
||||
columns: Vec::new(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn error(error: impl Into<String>) -> Self {
|
||||
Self {
|
||||
available: false,
|
||||
change_count: None,
|
||||
max_db_version: None,
|
||||
columns: Vec::new(),
|
||||
error: Some(error.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DbError {
|
||||
#[error("invalid db resource name: {0}")]
|
||||
|
|
@ -78,6 +112,43 @@ pub fn schema_metadata(path: &Path) -> Result<String, DbError> {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn crsqlite_change_metadata(path: &Path) -> Result<CrSqliteChangeMetadata, DbError> {
|
||||
let conn = Connection::open_with_flags(path, OpenFlags::SQLITE_OPEN_READ_ONLY)?;
|
||||
let available: bool = conn.query_row(
|
||||
r#"SELECT EXISTS(
|
||||
SELECT 1 FROM sqlite_master
|
||||
WHERE name = 'crsql_changes' AND type IN ('table', 'view')
|
||||
)"#,
|
||||
[],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
if !available {
|
||||
return Ok(CrSqliteChangeMetadata::unavailable());
|
||||
}
|
||||
|
||||
let mut columns_stmt = conn.prepare("PRAGMA table_info('crsql_changes')")?;
|
||||
let columns = columns_stmt
|
||||
.query_map([], |row| row.get::<_, String>(1))?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let change_count: u64 =
|
||||
conn.query_row("SELECT COUNT(*) FROM crsql_changes", [], |row| row.get(0))?;
|
||||
let max_db_version = if columns.iter().any(|column| column == "db_version") {
|
||||
conn.query_row("SELECT MAX(db_version) FROM crsql_changes", [], |row| {
|
||||
row.get(0)
|
||||
})?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(CrSqliteChangeMetadata {
|
||||
available: true,
|
||||
change_count: Some(change_count),
|
||||
max_db_version,
|
||||
columns,
|
||||
error: None,
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn crsqlite_sync_roadmap() -> &'static str {
|
||||
"future db sync reads crsql_changes, exchanges changes over Iroh, and applies through crsql_changes"
|
||||
|
|
@ -119,4 +190,39 @@ mod tests {
|
|||
assert!(first.contains("indexes=1"));
|
||||
assert!(first.contains("schema_hash="));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crsqlite_change_metadata_reports_mock_change_table() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let path = dir.path().join("notes.sqlite");
|
||||
let conn = Connection::open(&path).expect("open sqlite");
|
||||
conn.execute(
|
||||
r#"CREATE TABLE crsql_changes(
|
||||
table_name TEXT NOT NULL,
|
||||
pk TEXT NOT NULL,
|
||||
cid TEXT NOT NULL,
|
||||
val BLOB,
|
||||
col_version INTEGER NOT NULL,
|
||||
db_version INTEGER NOT NULL,
|
||||
site_id BLOB,
|
||||
cl INTEGER,
|
||||
seq INTEGER
|
||||
)"#,
|
||||
[],
|
||||
)
|
||||
.expect("create crsql_changes table");
|
||||
conn.execute(
|
||||
"INSERT INTO crsql_changes(table_name, pk, cid, val, col_version, db_version) VALUES ('notes', '1', 'body', 'hello', 1, 7)",
|
||||
[],
|
||||
)
|
||||
.expect("insert change");
|
||||
drop(conn);
|
||||
|
||||
let metadata = crsqlite_change_metadata(&path).expect("change metadata");
|
||||
|
||||
assert!(metadata.available);
|
||||
assert_eq!(metadata.change_count, Some(1));
|
||||
assert_eq!(metadata.max_db_version, Some(7));
|
||||
assert!(metadata.columns.contains(&"db_version".to_owned()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue