places/import/ios/
history.rs1use std::time::Instant;
6
7use crate::error::{info, Result};
8use crate::history_sync::engine::LAST_SYNC_META_KEY;
9use crate::import::common::{
10 attached_database, define_history_migration_functions, select_count, HistoryMigrationResult,
11};
12use crate::storage::{put_meta, update_all_frecencies_at_once};
13use crate::PlacesDb;
14use types::Timestamp;
15use url::Url;
16
17pub fn import(
35 conn: &PlacesDb,
36 path: impl AsRef<std::path::Path>,
37 last_sync_timestamp: i64,
38) -> Result<HistoryMigrationResult> {
39 let url = crate::util::ensure_url_path(path)?;
40 do_import(conn, url, last_sync_timestamp)
41}
42
43fn do_import(
44 conn: &PlacesDb,
45 ios_db_file_url: Url,
46 last_sync_timestamp: i64,
47) -> Result<HistoryMigrationResult> {
48 let scope = conn.begin_interrupt_scope()?;
49 define_history_migration_functions(conn)?;
50 let import_start = Instant::now();
58 info!("Attaching database {}", ios_db_file_url);
59 let auto_detach = attached_database(conn, &ios_db_file_url, "ios")?;
60 let tx = conn.begin_transaction()?;
61 let num_total = select_count(conn, &COUNT_IOS_HISTORY_VISITS)?;
62 info!("The number of visits is: {:?}", num_total);
63
64 info!("Creating and populating staging table");
65
66 tx.execute_batch(&CREATE_TEMP_VISIT_TABLE)?;
67 tx.execute_batch(&FILL_VISIT_TABLE)?;
68 tx.execute_batch(&CREATE_STAGING_TABLE)?;
69 tx.execute_batch(&FILL_STAGING)?;
70 scope.err_if_interrupted()?;
71
72 info!("Updating old titles that may be missing, but now are available");
73 tx.execute_batch(&UPDATE_PLACES_TITLES)?;
74 scope.err_if_interrupted()?;
75
76 info!("Populating missing entries in moz_places");
77 tx.execute_batch(&FILL_MOZ_PLACES)?;
78 scope.err_if_interrupted()?;
79
80 info!("Inserting the history visits");
81 tx.execute_batch(&INSERT_HISTORY_VISITS)?;
82 scope.err_if_interrupted()?;
83
84 info!("Insert all new entries into stale frecencies");
85 let now = Timestamp::now().as_millis();
86 tx.execute(&ADD_TO_STALE_FRECENCIES, &[(":now", &now)])?;
87 scope.err_if_interrupted()?;
88
89 put_meta(conn, LAST_SYNC_META_KEY, &last_sync_timestamp)?;
92
93 tx.commit()?;
94 info!("Successfully imported history visits!");
95
96 info!("Counting Places history visits");
97
98 let num_succeeded = select_count(conn, &COUNT_PLACES_HISTORY_VISITS)?;
99 let num_failed = num_total.saturating_sub(num_succeeded);
100
101 info!("Updating all frecencies");
106 update_all_frecencies_at_once(conn, &scope)?;
107 info!("Frecencies updated!");
108 auto_detach.execute_now()?;
109
110 Ok(HistoryMigrationResult {
111 num_total,
112 num_succeeded,
113 num_failed,
114 total_duration: import_start.elapsed().as_millis() as u64,
115 })
116}
117
118lazy_static::lazy_static! {
119 static ref COUNT_IOS_HISTORY_VISITS: &'static str =
121 "SELECT COUNT(*) FROM ios.visits v
122 LEFT JOIN ios.history h on v.siteID = h.id
123 WHERE h.is_deleted = 0"
124 ;
125
126 static ref CREATE_TEMP_VISIT_TABLE: &'static str = "
128 CREATE TEMP TABLE IF NOT EXISTS temp.latestVisits(
129 id INTEGER PRIMARY KEY,
130 siteID INTEGER NOT NULL,
131 date REAL NOT NULL,
132 type INTEGER NOT NULL,
133 is_local TINYINT NOT NULL
134 ) WITHOUT ROWID;
135 ";
136
137 static ref FILL_VISIT_TABLE: &'static str = "
139 INSERT OR IGNORE INTO temp.latestVisits(id, siteID, date, type, is_local)
140 SELECT
141 id,
142 siteID,
143 date,
144 type,
145 is_local
146 FROM ios.visits
147 ORDER BY date DESC
148 LIMIT 10000
149 ";
150
151 static ref CREATE_STAGING_TABLE: &'static str = "
154 CREATE TEMP TABLE IF NOT EXISTS temp.iOSHistoryStaging(
155 id INTEGER PRIMARY KEY,
156 url TEXT,
157 url_hash INTEGER NOT NULL,
158 title TEXT
159 ) WITHOUT ROWID;";
160
161 static ref FILL_STAGING: &'static str = "
162 INSERT OR IGNORE INTO temp.iOSHistoryStaging(id, url, url_hash, title)
163 SELECT
164 h.id,
165 validate_url(h.url),
166 hash(validate_url(h.url)),
167 sanitize_utf8(h.title)
168 FROM temp.latestVisits v
169 JOIN ios.history h on v.siteID = h.id
170 WHERE h.url IS NOT NULL
171 AND h.is_deleted = 0
172 "
173 ;
174
175 static ref UPDATE_PLACES_TITLES: &'static str =
178 "UPDATE main.moz_places
179 SET title = IFNULL((SELECT t.title
180 FROM temp.iOSHistoryStaging t
181 WHERE t.url_hash = main.moz_places.url_hash AND t.url = main.moz_places.url), title)"
182 ;
183
184 static ref FILL_MOZ_PLACES: &'static str =
186 "INSERT OR IGNORE INTO main.moz_places(guid, url, url_hash, title, frecency, sync_change_counter)
187 SELECT
188 IFNULL(
189 (SELECT p.guid FROM main.moz_places p WHERE p.url_hash = t.url_hash AND p.url = t.url),
190 generate_guid()
191 ),
192 t.url,
193 t.url_hash,
194 t.title,
195 -1,
196 1
197 FROM temp.iOSHistoryStaging t
198 "
199 ;
200
201 static ref INSERT_HISTORY_VISITS: &'static str =
203 "INSERT OR IGNORE INTO main.moz_historyvisits(from_visit, place_id, visit_date, visit_type, is_local)
204 SELECT
205 NULL, -- iOS does not store enough information to rebuild redirect chains.
206 (SELECT p.id FROM main.moz_places p WHERE p.url_hash = t.url_hash AND p.url = t.url),
207 sanitize_float_timestamp(v.date),
208 v.type, -- iOS stores visit types that map 1:1 to ours.
209 v.is_local
210 FROM temp.latestVisits v
211 JOIN temp.iOSHistoryStaging t on v.siteID = t.id
212 "
213 ;
214
215
216 static ref COUNT_PLACES_HISTORY_VISITS: &'static str =
218 "SELECT COUNT(*) FROM main.moz_historyvisits"
219 ;
220
221 static ref ADD_TO_STALE_FRECENCIES: &'static str =
223 "INSERT OR IGNORE INTO main.moz_places_stale_frecencies(place_id, stale_at)
224 SELECT
225 p.id,
226 :now
227 FROM main.moz_places p
228 WHERE p.frecency = -1"
229 ;
230}