diff --git a/mbtiles/src/diff.rs b/mbtiles/src/diff.rs index e54a7b028..dd9c81b1a 100644 --- a/mbtiles/src/diff.rs +++ b/mbtiles/src/diff.rs @@ -1,38 +1,48 @@ use log::debug; use sqlite_hashes::rusqlite::Connection; +use sqlx::{query, Executor, Row, SqliteConnection}; use std::path::PathBuf; use crate::MbtType::{Flat, FlatWithHash, Normalized}; -use crate::{is_empty_database, CopyDuplicateMode, MbtError, MbtResult, MbtType, Mbtiles}; +use crate::{ + create_tiles_with_hash_view, init_mbtiles_schema, is_empty_database, reset_db_settings, + CopyDuplicateMode, MbtError, MbtResult, MbtType, Mbtiles, +}; pub const AGG_TILES_HASH: &str = "agg_tiles_hash"; pub const AGG_TILES_HASH_IN_DIFF: &str = "agg_tiles_hash_after_apply"; -pub async fn diff(src: PathBuf, dst: PathBuf, diff_with: PathBuf) -> MbtResult<()> { - if src == dst { - return Err(MbtError::SameSourceAndDestination(src.clone())); +pub async fn diff(file: PathBuf, dif_result: PathBuf, diff_with: PathBuf) -> MbtResult<()> { + if file == dif_result { + return Err(MbtError::SameSourceAndDestination(file.clone())); + } + if file == diff_with { + return Err(MbtError::SameDiffAndSourceOrDestination(file.clone())); + } + if dif_result == diff_with { + return Err(MbtError::SameDiffAndSourceOrDestination(dif_result.clone())); } - if dst.exists() { - return Err(MbtError::DestinationFileExists(dst.clone())); + if dif_result.exists() { + return Err(MbtError::DestinationFileExists(dif_result.clone())); } - let src_mbt = Mbtiles::new(src)?; - let src_type = src_mbt.open_and_detect_type().await?; + let file_mbt = Mbtiles::new(file)?; + let file_type = file_mbt.open_and_detect_type().await?; - let dif_mbt = Mbtiles::new(diff_with)?; - let dif_type = dif_mbt.open_and_detect_type().await?; + let dif_with_mbt = Mbtiles::new(diff_with)?; + let dif_with_type = dif_with_mbt.open_and_detect_type().await?; - let dst_mbt = Mbtiles::new(dst.clone())?; - let mut conn = dst_mbt.open_or_new().await?; + let dif_result_mbt = Mbtiles::new(dif_result.clone())?; + let mut conn = dif_result_mbt.open_or_new().await?; if !is_empty_database(&mut conn).await? { - return Err(MbtError::DestinationFileExists(dst.clone())); + return Err(MbtError::DestinationFileExists(dif_result.clone())); } - let dst_type = src_type.clone(); - - src_mbt.attach_to(&mut conn, "sourceDb").await?; - dst_mbt.attach_to(&mut conn, "diffDb").await?; + let dst_type = file_type.clone(); // the result type should be same with source file + file_mbt.attach_to(&mut conn, "sourceDb").await?; + dif_with_mbt.attach_to(&mut conn, "diffDb").await?; + init_new_schema(&mut conn, file_type, dst_type).await?; { let mut handle_lock = conn.lock_handle().await?; let handle = handle_lock.as_raw_handle().as_ptr(); @@ -41,16 +51,16 @@ pub async fn diff(src: PathBuf, dst: PathBuf, diff_with: PathBuf) -> MbtResult<( let rusqlite_conn = unsafe { Connection::from_handle(handle) }?; write_tiles_diff( &rusqlite_conn, - dif_type, - src_type, + dif_with_type, + file_type, dst_type, CopyDuplicateMode::Override, ) .await?; - write_meta_diff(&rusqlite_conn, dif_type, CopyDuplicateMode::Override).await?; + write_meta_diff(&rusqlite_conn, dif_with_type, CopyDuplicateMode::Override).await?; } - todo!() + Ok(()) } async fn write_meta_diff( rusqlite_conn: &Connection, @@ -192,7 +202,39 @@ fn get_select_from_with_diff(dif_type: MbtType, dst_type: MbtType) -> String { OR difTiles.tile_data ISNULL)" ) } +async fn init_new_schema(conn: &mut SqliteConnection, src: MbtType, dst: MbtType) -> MbtResult<()> { + if src == dst { + reset_db_settings(conn).await?; + debug!("Copying DB schema verbatim"); + // DB objects must be created in a specific order: tables, views, triggers, indexes. + let sql_objects = conn + .fetch_all( + "SELECT sql + FROM sourceDb.sqlite_schema + WHERE tbl_name IN ('metadata', 'tiles', 'map', 'images', 'tiles_with_hash') + AND type IN ('table', 'view', 'trigger', 'index') + ORDER BY CASE + WHEN type = 'table' THEN 1 + WHEN type = 'view' THEN 2 + WHEN type = 'trigger' THEN 3 + WHEN type = 'index' THEN 4 + ELSE 5 END;", + ) + .await?; + for row in sql_objects { + query(row.get(0)).execute(&mut *conn).await?; + } + if dst.is_normalized() { + // Some normalized mbtiles files might not have this view, so even if src == dst, it might not exist + create_tiles_with_hash_view(&mut *conn).await?; + } + } else { + init_mbtiles_schema(&mut *conn, dst).await?; + }; + + Ok(()) +} #[cfg(test)] mod tests { use sqlx::Connection; @@ -203,7 +245,7 @@ mod tests { #[actix_rt::test] async fn diff_flat_to_flat() -> MbtResult<()> { let src = PathBuf::from("../tests/fixtures/mbtiles/world_cities.mbtiles"); - let diff = PathBuf::from("../tests/fixtures/mbtiles/world_cities_diff.mbtiles"); + let diff = PathBuf::from("../tests/fixtures/mbtiles/zoomed_world_cities.mbtiles"); let dst = PathBuf::from("diff_flat_to_flat.mbtiles"); super::diff(src, dst, diff).await