OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/history/core/browser/thumbnail_database.h" | 5 #include "components/history/core/browser/thumbnail_database.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <string> | 10 #include <string> |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
75 namespace { | 75 namespace { |
76 | 76 |
77 // For this database, schema migrations are deprecated after two | 77 // For this database, schema migrations are deprecated after two |
78 // years. This means that the oldest non-deprecated version should be | 78 // years. This means that the oldest non-deprecated version should be |
79 // two years old or greater (thus the migrations to get there are | 79 // two years old or greater (thus the migrations to get there are |
80 // older). Databases containing deprecated versions will be cleared | 80 // older). Databases containing deprecated versions will be cleared |
81 // at startup. Since this database is a cache, losing old data is not | 81 // at startup. Since this database is a cache, losing old data is not |
82 // fatal (in fact, very old data may be expired immediately at startup | 82 // fatal (in fact, very old data may be expired immediately at startup |
83 // anyhow). | 83 // anyhow). |
84 | 84 |
85 // Version 8: ???????? by rogerm@chromium.org on 2015-??-?? | 85 // Version 8: 982ef2c1/r323176 by rogerm@chromium.org on 2015-03-31 |
86 // Version 7: 911a634d/r209424 by qsr@chromium.org on 2013-07-01 | 86 // Version 7: 911a634d/r209424 by qsr@chromium.org on 2013-07-01 |
87 // Version 6: 610f923b/r152367 by pkotwicz@chromium.org on 2012-08-20 | 87 // Version 6: 610f923b/r152367 by pkotwicz@chromium.org on 2012-08-20 (depr.) |
88 // Version 5: e2ee8ae9/r105004 by groby@chromium.org on 2011-10-12 (deprecated) | 88 // Version 5: e2ee8ae9/r105004 by groby@chromium.org on 2011-10-12 (deprecated) |
89 // Version 4: 5f104d76/r77288 by sky@chromium.org on 2011-03-08 (deprecated) | 89 // Version 4: 5f104d76/r77288 by sky@chromium.org on 2011-03-08 (deprecated) |
90 // Version 3: 09911bf3/r15 by initial.commit on 2008-07-26 (deprecated) | 90 // Version 3: 09911bf3/r15 by initial.commit on 2008-07-26 (deprecated) |
91 | 91 |
92 // Version number of the database. | 92 // Version number of the database. |
93 // NOTE(shess): When changing the version, add a new golden file for | 93 // NOTE(shess): When changing the version, add a new golden file for |
94 // the new version and a test to verify that Init() works with it. | 94 // the new version and a test to verify that Init() works with it. |
95 const int kCurrentVersionNumber = 8; | 95 const int kCurrentVersionNumber = 8; |
96 const int kCompatibleVersionNumber = 8; | 96 const int kCompatibleVersionNumber = 8; |
97 const int kDeprecatedVersionNumber = 5; // and earlier. | 97 const int kDeprecatedVersionNumber = 6; // and earlier. |
98 | 98 |
99 void FillIconMapping(const sql::Statement& statement, | 99 void FillIconMapping(const sql::Statement& statement, |
100 const GURL& page_url, | 100 const GURL& page_url, |
101 IconMapping* icon_mapping) { | 101 IconMapping* icon_mapping) { |
102 icon_mapping->mapping_id = statement.ColumnInt64(0); | 102 icon_mapping->mapping_id = statement.ColumnInt64(0); |
103 icon_mapping->icon_id = statement.ColumnInt64(1); | 103 icon_mapping->icon_id = statement.ColumnInt64(1); |
104 icon_mapping->icon_type = | 104 icon_mapping->icon_type = |
105 static_cast<favicon_base::IconType>(statement.ColumnInt(2)); | 105 static_cast<favicon_base::IconType>(statement.ColumnInt(2)); |
106 icon_mapping->icon_url = GURL(statement.ColumnString(3)); | 106 icon_mapping->icon_url = GURL(statement.ColumnString(3)); |
107 icon_mapping->page_url = page_url; | 107 icon_mapping->page_url = page_url; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
139 // TODO(shess): If this could be related to the time in the channel, then the | 139 // TODO(shess): If this could be related to the time in the channel, then the |
140 // rate could ramp up over time. Perhaps could remember the timestamp the | 140 // rate could ramp up over time. Perhaps could remember the timestamp the |
141 // first time upload is considered, and ramp up 1% per day? | 141 // first time upload is considered, and ramp up 1% per day? |
142 static const uint64_t kReportPercent = 5; | 142 static const uint64_t kReportPercent = 5; |
143 uint64_t rand = base::RandGenerator(100); | 143 uint64_t rand = base::RandGenerator(100); |
144 if (rand <= kReportPercent) | 144 if (rand <= kReportPercent) |
145 db->ReportDiagnosticInfo(extended_error, stmt); | 145 db->ReportDiagnosticInfo(extended_error, stmt); |
146 } | 146 } |
147 | 147 |
148 // NOTE(shess): Schema modifications must consider initial creation in | 148 // NOTE(shess): Schema modifications must consider initial creation in |
149 // |InitImpl()|, recovery in |RecoverDatabaseOrRaze()|, and history pruning in | 149 // |InitImpl()| and history pruning in |RetainDataForPageUrls()|. |
150 // |RetainDataForPageUrls()|. | |
151 bool InitTables(sql::Connection* db) { | 150 bool InitTables(sql::Connection* db) { |
152 const char kIconMappingSql[] = | 151 const char kIconMappingSql[] = |
153 "CREATE TABLE IF NOT EXISTS icon_mapping" | 152 "CREATE TABLE IF NOT EXISTS icon_mapping" |
154 "(" | 153 "(" |
155 "id INTEGER PRIMARY KEY," | 154 "id INTEGER PRIMARY KEY," |
156 "page_url LONGVARCHAR NOT NULL," | 155 "page_url LONGVARCHAR NOT NULL," |
157 "icon_id INTEGER" | 156 "icon_id INTEGER" |
158 ")"; | 157 ")"; |
159 if (!db->Execute(kIconMappingSql)) | 158 if (!db->Execute(kIconMappingSql)) |
160 return false; | 159 return false; |
(...skipping 22 matching lines...) Expand all Loading... |
183 // the same layout. | 182 // the same layout. |
184 "last_requested INTEGER DEFAULT 0" | 183 "last_requested INTEGER DEFAULT 0" |
185 ")"; | 184 ")"; |
186 if (!db->Execute(kFaviconBitmapsSql)) | 185 if (!db->Execute(kFaviconBitmapsSql)) |
187 return false; | 186 return false; |
188 | 187 |
189 return true; | 188 return true; |
190 } | 189 } |
191 | 190 |
192 // NOTE(shess): Schema modifications must consider initial creation in | 191 // NOTE(shess): Schema modifications must consider initial creation in |
193 // |InitImpl()|, recovery in |RecoverDatabaseOrRaze()|, and history pruning in | 192 // |InitImpl()| and history pruning in |RetainDataForPageUrls()|. |
194 // |RetainDataForPageUrls()|. | |
195 bool InitIndices(sql::Connection* db) { | 193 bool InitIndices(sql::Connection* db) { |
196 const char kIconMappingUrlIndexSql[] = | 194 const char kIconMappingUrlIndexSql[] = |
197 "CREATE INDEX IF NOT EXISTS icon_mapping_page_url_idx" | 195 "CREATE INDEX IF NOT EXISTS icon_mapping_page_url_idx" |
198 " ON icon_mapping(page_url)"; | 196 " ON icon_mapping(page_url)"; |
199 const char kIconMappingIdIndexSql[] = | 197 const char kIconMappingIdIndexSql[] = |
200 "CREATE INDEX IF NOT EXISTS icon_mapping_icon_id_idx" | 198 "CREATE INDEX IF NOT EXISTS icon_mapping_icon_id_idx" |
201 " ON icon_mapping(icon_id)"; | 199 " ON icon_mapping(icon_id)"; |
202 if (!db->Execute(kIconMappingUrlIndexSql) || | 200 if (!db->Execute(kIconMappingUrlIndexSql) || |
203 !db->Execute(kIconMappingIdIndexSql)) { | 201 !db->Execute(kIconMappingIdIndexSql)) { |
204 return false; | 202 return false; |
205 } | 203 } |
206 | 204 |
207 const char kFaviconsIndexSql[] = | 205 const char kFaviconsIndexSql[] = |
208 "CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)"; | 206 "CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)"; |
209 if (!db->Execute(kFaviconsIndexSql)) | 207 if (!db->Execute(kFaviconsIndexSql)) |
210 return false; | 208 return false; |
211 | 209 |
212 const char kFaviconBitmapsIndexSql[] = | 210 const char kFaviconBitmapsIndexSql[] = |
213 "CREATE INDEX IF NOT EXISTS favicon_bitmaps_icon_id ON " | 211 "CREATE INDEX IF NOT EXISTS favicon_bitmaps_icon_id ON " |
214 "favicon_bitmaps(icon_id)"; | 212 "favicon_bitmaps(icon_id)"; |
215 if (!db->Execute(kFaviconBitmapsIndexSql)) | 213 if (!db->Execute(kFaviconBitmapsIndexSql)) |
216 return false; | 214 return false; |
217 | 215 |
218 return true; | 216 return true; |
219 } | 217 } |
220 | 218 |
221 enum RecoveryEventType { | |
222 RECOVERY_EVENT_RECOVERED = 0, | |
223 RECOVERY_EVENT_FAILED_SCOPER, | |
224 RECOVERY_EVENT_FAILED_META_VERSION_ERROR, // obsolete | |
225 RECOVERY_EVENT_FAILED_META_VERSION_NONE, // obsolete | |
226 RECOVERY_EVENT_FAILED_META_WRONG_VERSION6, // obsolete | |
227 RECOVERY_EVENT_FAILED_META_WRONG_VERSION5, // obsolete | |
228 RECOVERY_EVENT_FAILED_META_WRONG_VERSION, | |
229 RECOVERY_EVENT_FAILED_RECOVER_META, // obsolete | |
230 RECOVERY_EVENT_FAILED_META_INSERT, // obsolete | |
231 RECOVERY_EVENT_FAILED_INIT, | |
232 RECOVERY_EVENT_FAILED_RECOVER_FAVICONS, // obsolete | |
233 RECOVERY_EVENT_FAILED_FAVICONS_INSERT, // obsolete | |
234 RECOVERY_EVENT_FAILED_RECOVER_FAVICON_BITMAPS, // obsolete | |
235 RECOVERY_EVENT_FAILED_FAVICON_BITMAPS_INSERT, // obsolete | |
236 RECOVERY_EVENT_FAILED_RECOVER_ICON_MAPPING, // obsolete | |
237 RECOVERY_EVENT_FAILED_ICON_MAPPING_INSERT, // obsolete | |
238 RECOVERY_EVENT_RECOVERED_VERSION6, // obsolete | |
239 RECOVERY_EVENT_FAILED_META_INIT, | |
240 RECOVERY_EVENT_FAILED_META_VERSION, | |
241 RECOVERY_EVENT_DEPRECATED, | |
242 RECOVERY_EVENT_FAILED_V5_INITSCHEMA, // obsolete | |
243 RECOVERY_EVENT_FAILED_V5_AUTORECOVER_FAVICONS, // obsolete | |
244 RECOVERY_EVENT_FAILED_V5_AUTORECOVER_ICON_MAPPING, // obsolete | |
245 RECOVERY_EVENT_RECOVERED_VERSION5, // obsolete | |
246 RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS, | |
247 RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS, | |
248 RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING, | |
249 RECOVERY_EVENT_FAILED_COMMIT, | |
250 | |
251 // Always keep this at the end. | |
252 RECOVERY_EVENT_MAX, | |
253 }; | |
254 | |
255 void RecordRecoveryEvent(RecoveryEventType recovery_event) { | |
256 UMA_HISTOGRAM_ENUMERATION("History.FaviconsRecovery", | |
257 recovery_event, RECOVERY_EVENT_MAX); | |
258 } | |
259 | |
260 // Recover the database to the extent possible, razing it if recovery | |
261 // is not possible. | |
262 // TODO(shess): This is mostly just a safe proof of concept. In the | |
263 // real world, this database is probably not worthwhile recovering, as | |
264 // opposed to just razing it and starting over whenever corruption is | |
265 // detected. So this database is a good test subject. | |
266 void RecoverDatabaseOrRaze(sql::Connection* db, const base::FilePath& db_path) { | |
267 // NOTE(shess): This code is currently specific to the version | |
268 // number. I am working on simplifying things to loosen the | |
269 // dependency, meanwhile contact me if you need to bump the version. | |
270 DCHECK_EQ(8, kCurrentVersionNumber); | |
271 | |
272 // TODO(shess): Reset back after? | |
273 db->reset_error_callback(); | |
274 | |
275 // For histogram purposes. | |
276 size_t favicons_rows_recovered = 0; | |
277 size_t favicon_bitmaps_rows_recovered = 0; | |
278 size_t icon_mapping_rows_recovered = 0; | |
279 int64_t original_size = 0; | |
280 base::GetFileSize(db_path, &original_size); | |
281 | |
282 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); | |
283 if (!recovery) { | |
284 // TODO(shess): Unable to create recovery connection. This | |
285 // implies something substantial is wrong. At this point |db| has | |
286 // been poisoned so there is nothing really to do. | |
287 // | |
288 // Possible responses are unclear. If the failure relates to a | |
289 // problem somehow specific to the temporary file used to back the | |
290 // database, then an in-memory database could possibly be used. | |
291 // This could potentially allow recovering the main database, and | |
292 // might be simple to implement w/in Begin(). | |
293 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_SCOPER); | |
294 return; | |
295 } | |
296 | |
297 // Setup the meta recovery table and fetch the version number from | |
298 // the corrupt database. | |
299 int version = 0; | |
300 if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) { | |
301 // TODO(shess): Prior histograms indicate all failures are in | |
302 // creating the recover virtual table for corrupt.meta. The table | |
303 // may not exist, or the database may be too far gone. Either | |
304 // way, unclear how to resolve. | |
305 sql::Recovery::Rollback(std::move(recovery)); | |
306 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_VERSION); | |
307 return; | |
308 } | |
309 | |
310 // This code may be able to fetch version information that the regular | |
311 // deprecation path cannot. | |
312 // NOTE(shess,rogerm): v6 is not currently deprecated in the normal Init() | |
313 // path, but is deprecated in the recovery path in the interest of keeping | |
314 // the code simple. http://crbug.com/327485 for numbers. | |
315 DCHECK_LE(kDeprecatedVersionNumber, 6); | |
316 if (version <= 6) { | |
317 sql::Recovery::Unrecoverable(std::move(recovery)); | |
318 RecordRecoveryEvent(RECOVERY_EVENT_DEPRECATED); | |
319 return; | |
320 } | |
321 | |
322 // Earlier versions have been handled or deprecated. | |
323 if (version < 7) { | |
324 sql::Recovery::Unrecoverable(std::move(recovery)); | |
325 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION); | |
326 return; | |
327 } | |
328 | |
329 // Recover to current schema version. | |
330 sql::MetaTable recover_meta_table; | |
331 if (!recover_meta_table.Init(recovery->db(), kCurrentVersionNumber, | |
332 kCompatibleVersionNumber)) { | |
333 sql::Recovery::Rollback(std::move(recovery)); | |
334 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT); | |
335 return; | |
336 } | |
337 | |
338 // Create a fresh version of the database. The recovery code uses | |
339 // conflict-resolution to handle duplicates, so the indices are | |
340 // necessary. | |
341 if (!InitTables(recovery->db()) || !InitIndices(recovery->db())) { | |
342 // TODO(shess): Unable to create the new schema in the new | |
343 // database. The new database should be a temporary file, so | |
344 // being unable to work with it is pretty unclear. | |
345 // | |
346 // What are the potential responses, even? The recovery database | |
347 // could be opened as in-memory. If the temp database had a | |
348 // filesystem problem and the temp filesystem differs from the | |
349 // main database, then that could fix it. | |
350 sql::Recovery::Rollback(std::move(recovery)); | |
351 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_INIT); | |
352 return; | |
353 } | |
354 | |
355 if (!recovery->AutoRecoverTable("favicons", &favicons_rows_recovered)) { | |
356 sql::Recovery::Rollback(std::move(recovery)); | |
357 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS); | |
358 return; | |
359 } | |
360 if (!recovery->AutoRecoverTable("favicon_bitmaps", | |
361 &favicon_bitmaps_rows_recovered)) { | |
362 sql::Recovery::Rollback(std::move(recovery)); | |
363 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS); | |
364 return; | |
365 } | |
366 if (!recovery->AutoRecoverTable("icon_mapping", | |
367 &icon_mapping_rows_recovered)) { | |
368 sql::Recovery::Rollback(std::move(recovery)); | |
369 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING); | |
370 return; | |
371 } | |
372 | |
373 // TODO(shess): Is it possible/likely to have broken foreign-key | |
374 // issues with the tables? | |
375 // - icon_mapping.icon_id maps to no favicons.id | |
376 // - favicon_bitmaps.icon_id maps to no favicons.id | |
377 // - favicons.id is referenced by no icon_mapping.icon_id | |
378 // - favicons.id is referenced by no favicon_bitmaps.icon_id | |
379 // This step is possibly not worth the effort necessary to develop | |
380 // and sequence the statements, as it is basically a form of garbage | |
381 // collection. | |
382 | |
383 if (!sql::Recovery::Recovered(std::move(recovery))) { | |
384 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_COMMIT); | |
385 return; | |
386 } | |
387 | |
388 // Track the size of the recovered database relative to the size of | |
389 // the input database. The size should almost always be smaller, | |
390 // unless the input database was empty to start with. If the | |
391 // percentage results are very low, something is awry. | |
392 int64_t final_size = 0; | |
393 if (original_size > 0 && | |
394 base::GetFileSize(db_path, &final_size) && | |
395 final_size > 0) { | |
396 int percentage = static_cast<int>(original_size * 100 / final_size); | |
397 UMA_HISTOGRAM_PERCENTAGE("History.FaviconsRecoveredPercentage", | |
398 std::max(100, percentage)); | |
399 } | |
400 | |
401 // Using 10,000 because these cases mostly care about "none | |
402 // recovered" and "lots recovered". More than 10,000 rows recovered | |
403 // probably means there's something wrong with the profile. | |
404 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFavicons", | |
405 static_cast<int>(favicons_rows_recovered)); | |
406 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFaviconBitmaps", | |
407 static_cast<int>(favicon_bitmaps_rows_recovered)); | |
408 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsIconMapping", | |
409 static_cast<int>(icon_mapping_rows_recovered)); | |
410 | |
411 RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED); | |
412 } | |
413 | |
414 void DatabaseErrorCallback(sql::Connection* db, | 219 void DatabaseErrorCallback(sql::Connection* db, |
415 const base::FilePath& db_path, | 220 const base::FilePath& db_path, |
416 HistoryBackendClient* backend_client, | 221 HistoryBackendClient* backend_client, |
417 int extended_error, | 222 int extended_error, |
418 sql::Statement* stmt) { | 223 sql::Statement* stmt) { |
419 // TODO(shess): Assert that this is running on a safe thread. | 224 // TODO(shess): Assert that this is running on a safe thread. |
420 // AFAICT, should be the history thread, but at this level I can't | 225 // AFAICT, should be the history thread, but at this level I can't |
421 // see how to reach that. | 226 // see how to reach that. |
422 | 227 |
423 if (backend_client && backend_client->ShouldReportDatabaseError()) { | 228 if (backend_client && backend_client->ShouldReportDatabaseError()) { |
424 GenerateDiagnostics(db, extended_error, stmt); | 229 GenerateDiagnostics(db, extended_error, stmt); |
425 } | 230 } |
426 | 231 |
427 // Attempt to recover corrupt databases. | 232 // Attempt to recover corrupt databases. |
428 int error = (extended_error & 0xFF); | 233 if (sql::Recovery::ShouldRecover(extended_error)) { |
429 if (error == SQLITE_CORRUPT || | 234 // NOTE(shess): This approach is valid as of version 8. When bumping the |
430 error == SQLITE_CANTOPEN || | 235 // version, it will PROBABLY remain valid, but consider whether any schema |
431 error == SQLITE_NOTADB) { | 236 // changes might break automated recovery. |
432 RecoverDatabaseOrRaze(db, db_path); | 237 DCHECK_EQ(8, kCurrentVersionNumber); |
| 238 |
| 239 // Prevent reentrant calls. |
| 240 db->reset_error_callback(); |
| 241 |
| 242 // TODO(shess): Is it possible/likely to have broken foreign-key |
| 243 // issues with the tables? |
| 244 // - icon_mapping.icon_id maps to no favicons.id |
| 245 // - favicon_bitmaps.icon_id maps to no favicons.id |
| 246 // - favicons.id is referenced by no icon_mapping.icon_id |
| 247 // - favicons.id is referenced by no favicon_bitmaps.icon_id |
| 248 // This step is possibly not worth the effort necessary to develop |
| 249 // and sequence the statements, as it is basically a form of garbage |
| 250 // collection. |
| 251 |
| 252 // After this call, the |db| handle is poisoned so that future calls will |
| 253 // return errors until the handle is re-opened. |
| 254 sql::Recovery::RecoverDatabaseWithMetaVersion(db, db_path); |
| 255 |
| 256 // The DLOG(FATAL) below is intended to draw immediate attention to errors |
| 257 // in newly-written code. Database corruption is generally a result of OS |
| 258 // or hardware issues, not coding errors at the client level, so displaying |
| 259 // the error would probably lead to confusion. The ignored call signals the |
| 260 // test-expectation framework that the error was handled. |
| 261 ignore_result(sql::Connection::IsExpectedSqliteError(extended_error)); |
| 262 return; |
433 } | 263 } |
434 | 264 |
435 // The default handling is to assert on debug and to ignore on release. | 265 // The default handling is to assert on debug and to ignore on release. |
436 if (!sql::Connection::IsExpectedSqliteError(extended_error)) | 266 if (!sql::Connection::IsExpectedSqliteError(extended_error)) |
437 DLOG(FATAL) << db->GetErrorMessage(); | 267 DLOG(FATAL) << db->GetErrorMessage(); |
438 } | 268 } |
439 | 269 |
440 } // namespace | 270 } // namespace |
441 | 271 |
442 ThumbnailDatabase::IconMappingEnumerator::IconMappingEnumerator() { | 272 ThumbnailDatabase::IconMappingEnumerator::IconMappingEnumerator() { |
(...skipping 794 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1237 meta_table_.SetVersionNumber(8); | 1067 meta_table_.SetVersionNumber(8); |
1238 meta_table_.SetCompatibleVersionNumber(std::min(8, kCompatibleVersionNumber)); | 1068 meta_table_.SetCompatibleVersionNumber(std::min(8, kCompatibleVersionNumber)); |
1239 return true; | 1069 return true; |
1240 } | 1070 } |
1241 | 1071 |
1242 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() { | 1072 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() { |
1243 return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons"); | 1073 return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons"); |
1244 } | 1074 } |
1245 | 1075 |
1246 } // namespace history | 1076 } // namespace history |
OLD | NEW |