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 "chrome/browser/history/thumbnail_database.h" | 5 #include "chrome/browser/history/thumbnail_database.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 // want once-only handling? Sqlite.Error.Thumbnail shows | 268 // want once-only handling? Sqlite.Error.Thumbnail shows |
269 // CORRUPT and READONLY as almost 95% of all reports on these | 269 // CORRUPT and READONLY as almost 95% of all reports on these |
270 // channels, so probably easier to just harvest from the field. | 270 // channels, so probably easier to just harvest from the field. |
271 if (rand < kReportsPerMillion) { | 271 if (rand < kReportsPerMillion) { |
272 reported = true; | 272 reported = true; |
273 ReportError(db, extended_error); | 273 ReportError(db, extended_error); |
274 } | 274 } |
275 } | 275 } |
276 } | 276 } |
277 | 277 |
| 278 // Create v5 schema for recovery code. |
| 279 bool InitSchemaV5(sql::Connection* db) { |
| 280 // This schema was derived from the strings used when v5 was in |
| 281 // force. The [favicons] index and the [icon_mapping] items were |
| 282 // copied from the current strings, after verifying that the |
| 283 // resulting schema exactly matches the schema created by the |
| 284 // original versions of those strings. This allows the linker to |
| 285 // share the strings if they match, while preferring correctness of |
| 286 // the current versions change. |
| 287 |
| 288 const char kFaviconsV5[] = |
| 289 "CREATE TABLE IF NOT EXISTS favicons(" |
| 290 "id INTEGER PRIMARY KEY," |
| 291 "url LONGVARCHAR NOT NULL," |
| 292 "last_updated INTEGER DEFAULT 0," |
| 293 "image_data BLOB," |
| 294 "icon_type INTEGER DEFAULT 1," |
| 295 "sizes LONGVARCHAR" |
| 296 ")"; |
| 297 const char kFaviconsIndexV5[] = |
| 298 "CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)"; |
| 299 if (!db->Execute(kFaviconsV5) || !db->Execute(kFaviconsIndexV5)) |
| 300 return false; |
| 301 |
| 302 const char kIconMappingV5[] = |
| 303 "CREATE TABLE IF NOT EXISTS icon_mapping" |
| 304 "(" |
| 305 "id INTEGER PRIMARY KEY," |
| 306 "page_url LONGVARCHAR NOT NULL," |
| 307 "icon_id INTEGER" |
| 308 ")"; |
| 309 const char kIconMappingUrlIndexV5[] = |
| 310 "CREATE INDEX IF NOT EXISTS icon_mapping_page_url_idx" |
| 311 " ON icon_mapping(page_url)"; |
| 312 const char kIconMappingIdIndexV5[] = |
| 313 "CREATE INDEX IF NOT EXISTS icon_mapping_icon_id_idx" |
| 314 " ON icon_mapping(icon_id)"; |
| 315 if (!db->Execute(kIconMappingV5) || |
| 316 !db->Execute(kIconMappingUrlIndexV5) || |
| 317 !db->Execute(kIconMappingIdIndexV5)) { |
| 318 return false; |
| 319 } |
| 320 |
| 321 return true; |
| 322 } |
| 323 |
| 324 // TODO(shess): Consider InitSchemaV7(). InitSchemaV5() is worthwhile |
| 325 // because there appear to be 10s of thousands of marooned v5 |
| 326 // databases in the wild. Once recovery reaches stable, the number of |
| 327 // corrupt-but-recoverable databases should drop, possibly to the |
| 328 // point where it is not worthwhile to maintain previous-version |
| 329 // recovery code. |
| 330 // TODO(shess): Alternately, think on a way to more cleanly represent |
| 331 // versioned schema going forward. |
278 bool InitTables(sql::Connection* db) { | 332 bool InitTables(sql::Connection* db) { |
279 const char kIconMappingSql[] = | 333 const char kIconMappingSql[] = |
280 "CREATE TABLE IF NOT EXISTS icon_mapping" | 334 "CREATE TABLE IF NOT EXISTS icon_mapping" |
281 "(" | 335 "(" |
282 "id INTEGER PRIMARY KEY," | 336 "id INTEGER PRIMARY KEY," |
283 "page_url LONGVARCHAR NOT NULL," | 337 "page_url LONGVARCHAR NOT NULL," |
284 "icon_id INTEGER" | 338 "icon_id INTEGER" |
285 ")"; | 339 ")"; |
286 if (!db->Execute(kIconMappingSql)) | 340 if (!db->Execute(kIconMappingSql)) |
287 return false; | 341 return false; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 "favicon_bitmaps(icon_id)"; | 389 "favicon_bitmaps(icon_id)"; |
336 if (!db->Execute(kFaviconBitmapsIndexSql)) | 390 if (!db->Execute(kFaviconBitmapsIndexSql)) |
337 return false; | 391 return false; |
338 | 392 |
339 return true; | 393 return true; |
340 } | 394 } |
341 | 395 |
342 enum RecoveryEventType { | 396 enum RecoveryEventType { |
343 RECOVERY_EVENT_RECOVERED = 0, | 397 RECOVERY_EVENT_RECOVERED = 0, |
344 RECOVERY_EVENT_FAILED_SCOPER, | 398 RECOVERY_EVENT_FAILED_SCOPER, |
345 RECOVERY_EVENT_FAILED_META_VERSION_ERROR, | 399 RECOVERY_EVENT_FAILED_META_VERSION_ERROR, // obsolete |
346 RECOVERY_EVENT_FAILED_META_VERSION_NONE, | 400 RECOVERY_EVENT_FAILED_META_VERSION_NONE, // obsolete |
347 RECOVERY_EVENT_FAILED_META_WRONG_VERSION6, // obsolete | 401 RECOVERY_EVENT_FAILED_META_WRONG_VERSION6, // obsolete |
348 RECOVERY_EVENT_FAILED_META_WRONG_VERSION5, | 402 RECOVERY_EVENT_FAILED_META_WRONG_VERSION5, // obsolete |
349 RECOVERY_EVENT_FAILED_META_WRONG_VERSION, | 403 RECOVERY_EVENT_FAILED_META_WRONG_VERSION, |
350 RECOVERY_EVENT_FAILED_RECOVER_META, | 404 RECOVERY_EVENT_FAILED_RECOVER_META, // obsolete |
351 RECOVERY_EVENT_FAILED_META_INSERT, // obsolete | 405 RECOVERY_EVENT_FAILED_META_INSERT, // obsolete |
352 RECOVERY_EVENT_FAILED_INIT, | 406 RECOVERY_EVENT_FAILED_INIT, |
353 RECOVERY_EVENT_FAILED_RECOVER_FAVICONS, | 407 RECOVERY_EVENT_FAILED_RECOVER_FAVICONS, // obsolete |
354 RECOVERY_EVENT_FAILED_FAVICONS_INSERT, | 408 RECOVERY_EVENT_FAILED_FAVICONS_INSERT, // obsolete |
355 RECOVERY_EVENT_FAILED_RECOVER_FAVICON_BITMAPS, | 409 RECOVERY_EVENT_FAILED_RECOVER_FAVICON_BITMAPS, // obsolete |
356 RECOVERY_EVENT_FAILED_FAVICON_BITMAPS_INSERT, | 410 RECOVERY_EVENT_FAILED_FAVICON_BITMAPS_INSERT, // obsolete |
357 RECOVERY_EVENT_FAILED_RECOVER_ICON_MAPPING, | 411 RECOVERY_EVENT_FAILED_RECOVER_ICON_MAPPING, // obsolete |
358 RECOVERY_EVENT_FAILED_ICON_MAPPING_INSERT, | 412 RECOVERY_EVENT_FAILED_ICON_MAPPING_INSERT, // obsolete |
359 RECOVERY_EVENT_RECOVERED_VERSION6, | 413 RECOVERY_EVENT_RECOVERED_VERSION6, |
360 RECOVERY_EVENT_FAILED_META_INIT, | 414 RECOVERY_EVENT_FAILED_META_INIT, |
| 415 RECOVERY_EVENT_FAILED_META_VERSION, |
| 416 RECOVERY_EVENT_DEPRECATED, |
| 417 RECOVERY_EVENT_FAILED_V5_INITSCHEMA, |
| 418 RECOVERY_EVENT_FAILED_V5_AUTORECOVER_FAVICONS, |
| 419 RECOVERY_EVENT_FAILED_V5_AUTORECOVER_ICON_MAPPING, |
| 420 RECOVERY_EVENT_RECOVERED_VERSION5, |
| 421 RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS, |
| 422 RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS, |
| 423 RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING, |
361 | 424 |
362 // Always keep this at the end. | 425 // Always keep this at the end. |
363 RECOVERY_EVENT_MAX, | 426 RECOVERY_EVENT_MAX, |
364 }; | 427 }; |
365 | 428 |
366 void RecordRecoveryEvent(RecoveryEventType recovery_event) { | 429 void RecordRecoveryEvent(RecoveryEventType recovery_event) { |
367 UMA_HISTOGRAM_ENUMERATION("History.FaviconsRecovery", | 430 UMA_HISTOGRAM_ENUMERATION("History.FaviconsRecovery", |
368 recovery_event, RECOVERY_EVENT_MAX); | 431 recovery_event, RECOVERY_EVENT_MAX); |
369 } | 432 } |
370 | 433 |
(...skipping 27 matching lines...) Expand all Loading... |
398 // | 461 // |
399 // Possible responses are unclear. If the failure relates to a | 462 // Possible responses are unclear. If the failure relates to a |
400 // problem somehow specific to the temporary file used to back the | 463 // problem somehow specific to the temporary file used to back the |
401 // database, then an in-memory database could possibly be used. | 464 // database, then an in-memory database could possibly be used. |
402 // This could potentially allow recovering the main database, and | 465 // This could potentially allow recovering the main database, and |
403 // might be simple to implement w/in Begin(). | 466 // might be simple to implement w/in Begin(). |
404 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_SCOPER); | 467 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_SCOPER); |
405 return; | 468 return; |
406 } | 469 } |
407 | 470 |
408 // Setup the meta recovery table, and check that the version number | 471 // Setup the meta recovery table and fetch the version number from |
409 // is covered by the recovery code. | 472 // the corrupt database. |
410 // TODO(shess): sql::Recovery should provide a helper to handle meta. | 473 int version = 0; |
411 int version = 0; // For reporting which version was recovered. | 474 if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) { |
412 { | 475 // TODO(shess): Prior histograms indicate all failures are in |
413 const char kRecoverySql[] = | 476 // creating the recover virtual table for corrupt.meta. The table |
414 "CREATE VIRTUAL TABLE temp.recover_meta USING recover" | 477 // may not exist, or the database may be too far gone. Either |
415 "(" | 478 // way, unclear how to resolve. |
416 "corrupt.meta," | 479 sql::Recovery::Rollback(recovery.Pass()); |
417 "key TEXT NOT NULL," | 480 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_VERSION); |
418 "value TEXT" // Really? Never int? | 481 return; |
419 ")"; | 482 } |
420 if (!recovery->db()->Execute(kRecoverySql)) { | |
421 // TODO(shess): Failure to create the recover_meta table could | |
422 // mean that the main database is too corrupt to access, or that | |
423 // the meta table doesn't exist. | |
424 sql::Recovery::Rollback(recovery.Pass()); | |
425 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_RECOVER_META); | |
426 return; | |
427 } | |
428 | 483 |
429 { | 484 // Recover v5 database to v5 schema. Next pass through Init() will |
430 const char kRecoveryVersionSql[] = | 485 // migrate to v7. |
431 "SELECT value FROM recover_meta WHERE key = 'version'"; | 486 if (version == 5) { |
432 sql::Statement recovery_version( | |
433 recovery->db()->GetUniqueStatement(kRecoveryVersionSql)); | |
434 if (!recovery_version.Step()) { | |
435 if (!recovery_version.Succeeded()) { | |
436 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_VERSION_ERROR); | |
437 // TODO(shess): An error while processing the statement is | |
438 // probably not recoverable. | |
439 } else { | |
440 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_VERSION_NONE); | |
441 // TODO(shess): If a positive version lock cannot be achieved, | |
442 // the database could still be recovered by optimistically | |
443 // attempting to copy things. In the limit, the schema found | |
444 // could be inspected. Less clear is whether optimistic | |
445 // recovery really makes sense. | |
446 } | |
447 recovery_version.Clear(); | |
448 sql::Recovery::Rollback(recovery.Pass()); | |
449 return; | |
450 } | |
451 version = recovery_version.ColumnInt(0); | |
452 | |
453 // Recovery code is generally schema-dependent. Version 7 and | |
454 // version 6 are very similar, so can be handled together. | |
455 // Track version 5, to see whether it's worth writing recovery | |
456 // code for. | |
457 if (version != 7 && version != 6) { | |
458 if (version == 5) { | |
459 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION5); | |
460 } else { | |
461 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION); | |
462 } | |
463 recovery_version.Clear(); | |
464 sql::Recovery::Rollback(recovery.Pass()); | |
465 return; | |
466 } | |
467 } | |
468 | |
469 // Either version 6 or version 7 recovers to current. | |
470 sql::MetaTable recover_meta_table; | 487 sql::MetaTable recover_meta_table; |
471 if (!recover_meta_table.Init(recovery->db(), kCurrentVersionNumber, | 488 if (!recover_meta_table.Init(recovery->db(), version, version)) { |
472 kCompatibleVersionNumber)) { | |
473 sql::Recovery::Rollback(recovery.Pass()); | 489 sql::Recovery::Rollback(recovery.Pass()); |
474 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT); | 490 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT); |
475 return; | 491 return; |
476 } | 492 } |
| 493 |
| 494 // TODO(shess): These tests are separate for histogram purposes, |
| 495 // but once things look stable it can be tightened up. |
| 496 if (!InitSchemaV5(recovery->db())) { |
| 497 sql::Recovery::Rollback(recovery.Pass()); |
| 498 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_V5_INITSCHEMA); |
| 499 return; |
| 500 } |
| 501 |
| 502 if (!recovery->AutoRecoverTable("favicons", 0, &favicons_rows_recovered)) { |
| 503 sql::Recovery::Rollback(recovery.Pass()); |
| 504 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_V5_AUTORECOVER_FAVICONS); |
| 505 return; |
| 506 } |
| 507 |
| 508 if (!recovery->AutoRecoverTable("icon_mapping", 0, |
| 509 &icon_mapping_rows_recovered)) { |
| 510 sql::Recovery::Rollback(recovery.Pass()); |
| 511 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_V5_AUTORECOVER_ICON_MAPPING); |
| 512 return; |
| 513 } |
| 514 |
| 515 ignore_result(sql::Recovery::Recovered(recovery.Pass())); |
| 516 |
| 517 // TODO(shess): Could this code be shared with the v6/7 code |
| 518 // without requiring too much state to be carried? |
| 519 |
| 520 // Track the size of the recovered database relative to the size of |
| 521 // the input database. The size should almost always be smaller, |
| 522 // unless the input database was empty to start with. If the |
| 523 // percentage results are very low, something is awry. |
| 524 int64 final_size = 0; |
| 525 if (original_size > 0 && |
| 526 file_util::GetFileSize(db_path, &final_size) && |
| 527 final_size > 0) { |
| 528 int percentage = static_cast<int>(original_size * 100 / final_size); |
| 529 UMA_HISTOGRAM_PERCENTAGE("History.FaviconsRecoveredPercentage", |
| 530 std::max(100, percentage)); |
| 531 } |
| 532 |
| 533 // Using 10,000 because these cases mostly care about "none |
| 534 // recovered" and "lots recovered". More than 10,000 rows recovered |
| 535 // probably means there's something wrong with the profile. |
| 536 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFavicons", |
| 537 favicons_rows_recovered); |
| 538 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsIconMapping", |
| 539 icon_mapping_rows_recovered); |
| 540 |
| 541 RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED_VERSION5); |
| 542 return; |
| 543 } |
| 544 |
| 545 // This code may be able to fetch versions that the regular |
| 546 // deprecation path cannot. |
| 547 if (version <= kDeprecatedVersionNumber) { |
| 548 sql::Recovery::Unrecoverable(recovery.Pass()); |
| 549 RecordRecoveryEvent(RECOVERY_EVENT_DEPRECATED); |
| 550 return; |
| 551 } |
| 552 |
| 553 // TODO(shess): Earlier versions have been handled or deprecated, |
| 554 // later versions should be impossible. Unrecoverable() seems |
| 555 // reasonable. |
| 556 if (version != 6 && version != 7) { |
| 557 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION); |
| 558 sql::Recovery::Rollback(recovery.Pass()); |
| 559 return; |
| 560 } |
| 561 |
| 562 // Both v6 and v7 recover to current schema version. |
| 563 sql::MetaTable recover_meta_table; |
| 564 if (!recover_meta_table.Init(recovery->db(), kCurrentVersionNumber, |
| 565 kCompatibleVersionNumber)) { |
| 566 sql::Recovery::Rollback(recovery.Pass()); |
| 567 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT); |
| 568 return; |
477 } | 569 } |
478 | 570 |
479 // Create a fresh version of the database. The recovery code uses | 571 // Create a fresh version of the database. The recovery code uses |
480 // conflict-resolution to handle duplicates, so the indices are | 572 // conflict-resolution to handle duplicates, so the indices are |
481 // necessary. | 573 // necessary. |
482 if (!InitTables(recovery->db()) || !InitIndices(recovery->db())) { | 574 if (!InitTables(recovery->db()) || !InitIndices(recovery->db())) { |
483 // TODO(shess): Unable to create the new schema in the new | 575 // TODO(shess): Unable to create the new schema in the new |
484 // database. The new database should be a temporary file, so | 576 // database. The new database should be a temporary file, so |
485 // being unable to work with it is pretty unclear. | 577 // being unable to work with it is pretty unclear. |
486 // | 578 // |
487 // What are the potential responses, even? The recovery database | 579 // What are the potential responses, even? The recovery database |
488 // could be opened as in-memory. If the temp database had a | 580 // could be opened as in-memory. If the temp database had a |
489 // filesystem problem and the temp filesystem differs from the | 581 // filesystem problem and the temp filesystem differs from the |
490 // main database, then that could fix it. | 582 // main database, then that could fix it. |
491 sql::Recovery::Rollback(recovery.Pass()); | 583 sql::Recovery::Rollback(recovery.Pass()); |
492 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_INIT); | 584 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_INIT); |
493 return; | 585 return; |
494 } | 586 } |
495 | 587 |
496 // Setup favicons table. | 588 // [favicons] differs because v6 had an unused [sizes] column which |
497 { | 589 // was removed in v7. |
498 // Version 6 had the |sizes| column, version 7 removed it. The | 590 if (!recovery->AutoRecoverTable("favicons", 1, &favicons_rows_recovered)) { |
499 // recover virtual table treats more columns than expected as an | 591 sql::Recovery::Rollback(recovery.Pass()); |
500 // error, but if _fewer_ columns are present, they can be treated | 592 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS); |
501 // as NULL. SQLite requires this because ALTER TABLE adds columns | 593 return; |
502 // to the schema, but not to the actual table storage. | |
503 const char kRecoverySql[] = | |
504 "CREATE VIRTUAL TABLE temp.recover_favicons USING recover" | |
505 "(" | |
506 "corrupt.favicons," | |
507 "id ROWID," | |
508 "url TEXT NOT NULL," | |
509 "icon_type INTEGER," | |
510 "sizes TEXT" | |
511 ")"; | |
512 if (!recovery->db()->Execute(kRecoverySql)) { | |
513 // TODO(shess): Failure to create the recovery table probably | |
514 // means unrecoverable. | |
515 sql::Recovery::Rollback(recovery.Pass()); | |
516 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_RECOVER_FAVICONS); | |
517 return; | |
518 } | |
519 | |
520 // TODO(shess): Check if the DEFAULT 1 will just cover the | |
521 // COALESCE(). Either way, the new code has a literal 1 rather | |
522 // than a NULL, right? | |
523 const char kCopySql[] = | |
524 "INSERT OR REPLACE INTO main.favicons " | |
525 "SELECT id, url, COALESCE(icon_type, 1) FROM recover_favicons"; | |
526 if (!recovery->db()->Execute(kCopySql)) { | |
527 // TODO(shess): The recover_favicons table should mask problems | |
528 // with the source file, so this implies failure to write to the | |
529 // recovery database. | |
530 sql::Recovery::Rollback(recovery.Pass()); | |
531 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_FAVICONS_INSERT); | |
532 return; | |
533 } | |
534 favicons_rows_recovered = recovery->db()->GetLastChangeCount(); | |
535 } | 594 } |
536 | 595 if (!recovery->AutoRecoverTable("favicon_bitmaps", 0, |
537 // Setup favicons_bitmaps table. | 596 &favicon_bitmaps_rows_recovered)) { |
538 { | 597 sql::Recovery::Rollback(recovery.Pass()); |
539 const char kRecoverySql[] = | 598 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS); |
540 "CREATE VIRTUAL TABLE temp.recover_favicons_bitmaps USING recover" | 599 return; |
541 "(" | |
542 "corrupt.favicon_bitmaps," | |
543 "id ROWID," | |
544 "icon_id INTEGER STRICT NOT NULL," | |
545 "last_updated INTEGER," | |
546 "image_data BLOB," | |
547 "width INTEGER," | |
548 "height INTEGER" | |
549 ")"; | |
550 if (!recovery->db()->Execute(kRecoverySql)) { | |
551 // TODO(shess): Failure to create the recovery table probably | |
552 // means unrecoverable. | |
553 sql::Recovery::Rollback(recovery.Pass()); | |
554 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_RECOVER_FAVICON_BITMAPS); | |
555 return; | |
556 } | |
557 | |
558 const char kCopySql[] = | |
559 "INSERT OR REPLACE INTO main.favicon_bitmaps " | |
560 "SELECT id, icon_id, COALESCE(last_updated, 0), image_data, " | |
561 " COALESCE(width, 0), COALESCE(height, 0) " | |
562 "FROM recover_favicons_bitmaps"; | |
563 if (!recovery->db()->Execute(kCopySql)) { | |
564 // TODO(shess): The recover_faviconbitmaps table should mask | |
565 // problems with the source file, so this implies failure to | |
566 // write to the recovery database. | |
567 sql::Recovery::Rollback(recovery.Pass()); | |
568 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_FAVICON_BITMAPS_INSERT); | |
569 return; | |
570 } | |
571 favicon_bitmaps_rows_recovered = recovery->db()->GetLastChangeCount(); | |
572 } | 600 } |
573 | 601 if (!recovery->AutoRecoverTable("icon_mapping", 0, |
574 // Setup icon_mapping table. | 602 &icon_mapping_rows_recovered)) { |
575 { | 603 sql::Recovery::Rollback(recovery.Pass()); |
576 const char kRecoverySql[] = | 604 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING); |
577 "CREATE VIRTUAL TABLE temp.recover_icon_mapping USING recover" | 605 return; |
578 "(" | |
579 "corrupt.icon_mapping," | |
580 "id ROWID," | |
581 "page_url TEXT STRICT NOT NULL," | |
582 "icon_id INTEGER STRICT" | |
583 ")"; | |
584 if (!recovery->db()->Execute(kRecoverySql)) { | |
585 // TODO(shess): Failure to create the recovery table probably | |
586 // means unrecoverable. | |
587 sql::Recovery::Rollback(recovery.Pass()); | |
588 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_RECOVER_ICON_MAPPING); | |
589 return; | |
590 } | |
591 | |
592 const char kCopySql[] = | |
593 "INSERT OR REPLACE INTO main.icon_mapping " | |
594 "SELECT id, page_url, icon_id FROM recover_icon_mapping"; | |
595 if (!recovery->db()->Execute(kCopySql)) { | |
596 // TODO(shess): The recover_icon_mapping table should mask | |
597 // problems with the source file, so this implies failure to | |
598 // write to the recovery database. | |
599 sql::Recovery::Rollback(recovery.Pass()); | |
600 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_ICON_MAPPING_INSERT); | |
601 return; | |
602 } | |
603 icon_mapping_rows_recovered = recovery->db()->GetLastChangeCount(); | |
604 } | 606 } |
605 | 607 |
606 // TODO(shess): Is it possible/likely to have broken foreign-key | 608 // TODO(shess): Is it possible/likely to have broken foreign-key |
607 // issues with the tables? | 609 // issues with the tables? |
608 // - icon_mapping.icon_id maps to no favicons.id | 610 // - icon_mapping.icon_id maps to no favicons.id |
609 // - favicon_bitmaps.icon_id maps to no favicons.id | 611 // - favicon_bitmaps.icon_id maps to no favicons.id |
610 // - favicons.id is referenced by no icon_mapping.icon_id | 612 // - favicons.id is referenced by no icon_mapping.icon_id |
611 // - favicons.id is referenced by no favicon_bitmaps.icon_id | 613 // - favicons.id is referenced by no favicon_bitmaps.icon_id |
612 // This step is possibly not worth the effort necessary to develop | 614 // This step is possibly not worth the effort necessary to develop |
613 // and sequence the statements, as it is basically a form of garbage | 615 // and sequence the statements, as it is basically a form of garbage |
(...skipping 807 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1421 meta_table_.SetVersionNumber(7); | 1423 meta_table_.SetVersionNumber(7); |
1422 meta_table_.SetCompatibleVersionNumber(std::min(7, kCompatibleVersionNumber)); | 1424 meta_table_.SetCompatibleVersionNumber(std::min(7, kCompatibleVersionNumber)); |
1423 return true; | 1425 return true; |
1424 } | 1426 } |
1425 | 1427 |
1426 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() { | 1428 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() { |
1427 return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons"); | 1429 return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons"); |
1428 } | 1430 } |
1429 | 1431 |
1430 } // namespace history | 1432 } // namespace history |
OLD | NEW |