Chromium Code Reviews| 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/download_database.h" | 5 #include "components/history/core/browser/download_database.h" |
| 6 | 6 |
| 7 #include <inttypes.h> | 7 #include <inttypes.h> |
| 8 | 8 |
| 9 #include <limits> | 9 #include <limits> |
| 10 #include <memory> | 10 #include <memory> |
| (...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 | 305 |
| 306 const char kUrlChainSchema[] = | 306 const char kUrlChainSchema[] = |
| 307 "CREATE TABLE downloads_url_chains (" | 307 "CREATE TABLE downloads_url_chains (" |
| 308 "id INTEGER NOT NULL," // downloads.id. | 308 "id INTEGER NOT NULL," // downloads.id. |
| 309 "chain_index INTEGER NOT NULL," // Index of url in chain | 309 "chain_index INTEGER NOT NULL," // Index of url in chain |
| 310 // 0 is initial target, | 310 // 0 is initial target, |
| 311 // MAX is target after redirects. | 311 // MAX is target after redirects. |
| 312 "url LONGVARCHAR NOT NULL, " // URL. | 312 "url LONGVARCHAR NOT NULL, " // URL. |
| 313 "PRIMARY KEY (id, chain_index) )"; | 313 "PRIMARY KEY (id, chain_index) )"; |
| 314 | 314 |
| 315 bool ret; | |
| 315 if (GetDB().DoesTableExist("downloads")) { | 316 if (GetDB().DoesTableExist("downloads")) { |
| 316 return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && | 317 ret = EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && |
| 317 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); | 318 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); |
| 318 } else { | 319 } else { |
| 319 // If the "downloads" table doesn't exist, the downloads_url_chain | 320 // If the "downloads" table doesn't exist, the downloads_url_chain |
| 320 // table better not. | 321 // and the downloads_jobs table better not. |
| 321 return (!GetDB().DoesTableExist("downloads_url_chain") && | 322 ret = !GetDB().DoesTableExist("downloads_url_chain") && |
| 322 GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema)); | 323 !GetDB().DoesTableExist("downloads_jobs") && |
|
asanka
2017/02/02 19:06:27
This condition isn't relevant here since neither k
qinmin
2017/02/02 22:31:32
Done. removed, it just feels wierd that download_j
| |
| 324 GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema); | |
| 323 } | 325 } |
| 326 ret = ret && EnsureDownloadJobsTableExists(); | |
|
asanka
2017/02/02 19:06:27
Roll EnsureDownloadJobsTableExists here for consis
qinmin
2017/02/02 22:31:32
Done. Moved all the code here
| |
| 327 | |
| 328 return ret; | |
| 324 } | 329 } |
| 325 | 330 |
| 326 uint32_t DownloadDatabase::GetNextDownloadId() { | 331 uint32_t DownloadDatabase::GetNextDownloadId() { |
| 327 sql::Statement select_max_id(GetDB().GetUniqueStatement( | 332 sql::Statement select_max_id(GetDB().GetUniqueStatement( |
| 328 "SELECT max(id) FROM downloads")); | 333 "SELECT max(id) FROM downloads")); |
| 329 bool result = select_max_id.Step(); | 334 bool result = select_max_id.Step(); |
| 330 DCHECK(result); | 335 DCHECK(result); |
| 331 // If there are zero records in the downloads table, then max(id) will | 336 // If there are zero records in the downloads table, then max(id) will |
| 332 // return 0 = kInvalidDownloadId, so GetNextDownloadId() will set | 337 // return 0 = kInvalidDownloadId, so GetNextDownloadId() will set |
| 333 // *id = kInvalidDownloadId + 1. | 338 // *id = kInvalidDownloadId + 1. |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 466 url_chain->push_back(GURL()); | 471 url_chain->push_back(GURL()); |
| 467 current_chain_size++; | 472 current_chain_size++; |
| 468 } | 473 } |
| 469 if (current_chain_size > chain_index) | 474 if (current_chain_size > chain_index) |
| 470 continue; | 475 continue; |
| 471 | 476 |
| 472 // Save the record. | 477 // Save the record. |
| 473 url_chain->push_back(GURL(statement_chain.ColumnString(2))); | 478 url_chain->push_back(GURL(statement_chain.ColumnString(2))); |
| 474 } | 479 } |
| 475 | 480 |
| 481 sql::Statement statement_download_job(GetDB().GetCachedStatement( | |
| 482 SQL_FROM_HERE, | |
| 483 "SELECT id, job_id, start_position, length, received_bytes, " | |
| 484 "state, interrupt_reason FROM downloads_jobs " | |
| 485 "ORDER BY id, job_id")); | |
| 486 | |
| 487 while (statement_download_job.Step()) { | |
| 488 int column = 0; | |
| 489 // See the comment above about SQLITE lacking unsigned integers. | |
| 490 int64_t signed_id = statement_download_job.ColumnInt64(column++); | |
| 491 if (signed_id <= static_cast<int64_t>(kInvalidDownloadId)) | |
| 492 continue; | |
| 493 int id = IntToDownloadId(signed_id); | |
| 494 DownloadJobInfo info; | |
| 495 info.id = IntToDownloadId(signed_id); | |
| 496 info.job_id = statement_download_job.ColumnInt(column++); | |
| 497 info.start_position = statement_download_job.ColumnInt64(column++); | |
| 498 info.length = statement_download_job.ColumnInt64(column++); | |
| 499 info.received_bytes = statement_download_job.ColumnInt64(column++); | |
| 500 info.state = IntToDownloadState( | |
| 501 statement_download_job.ColumnInt(column++)); | |
| 502 info.interrupt_reason = IntToDownloadInterruptReason( | |
| 503 statement_download_job.ColumnInt(column++)); | |
| 504 // Confirm the id has already been seen--if it hasn't, discard the | |
| 505 // record. | |
| 506 DCHECK(base::ContainsKey(info_map, id)); | |
| 507 if (!base::ContainsKey(info_map, id)) | |
|
asanka
2017/02/02 19:06:27
Might as well front load this check. There's no po
qinmin
2017/02/02 22:31:32
Done. Added a new UMA entry
| |
| 508 continue; | |
| 509 info_map[id]->download_job_info.push_back(info); | |
| 510 } | |
| 511 | |
| 476 for (std::map<uint32_t, DownloadRow*>::iterator it = info_map.begin(); | 512 for (std::map<uint32_t, DownloadRow*>::iterator it = info_map.begin(); |
| 477 it != info_map.end(); ++it) { | 513 it != info_map.end(); ++it) { |
| 478 DownloadRow* row = it->second; | 514 DownloadRow* row = it->second; |
| 479 bool empty_url_chain = row->url_chain.empty(); | 515 bool empty_url_chain = row->url_chain.empty(); |
| 480 UMA_HISTOGRAM_BOOLEAN("Download.DatabaseEmptyUrlChain", empty_url_chain); | 516 UMA_HISTOGRAM_BOOLEAN("Download.DatabaseEmptyUrlChain", empty_url_chain); |
| 481 if (empty_url_chain) { | 517 if (empty_url_chain) { |
| 482 RemoveDownload(row->id); | 518 RemoveDownload(row->id); |
| 483 } else { | 519 } else { |
| 484 // Copy the contents of the stored info. | 520 // Copy the contents of the stored info. |
| 485 results->push_back(*row); | 521 results->push_back(*row); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 526 statement.BindBlob(column++, data.hash.data(), data.hash.size()); | 562 statement.BindBlob(column++, data.hash.data(), data.hash.size()); |
| 527 statement.BindInt64(column++, data.end_time.ToInternalValue()); | 563 statement.BindInt64(column++, data.end_time.ToInternalValue()); |
| 528 statement.BindInt64(column++, data.total_bytes); | 564 statement.BindInt64(column++, data.total_bytes); |
| 529 statement.BindInt(column++, (data.opened ? 1 : 0)); | 565 statement.BindInt(column++, (data.opened ? 1 : 0)); |
| 530 statement.BindString(column++, data.by_ext_id); | 566 statement.BindString(column++, data.by_ext_id); |
| 531 statement.BindString(column++, data.by_ext_name); | 567 statement.BindString(column++, data.by_ext_name); |
| 532 statement.BindString(column++, data.etag); | 568 statement.BindString(column++, data.etag); |
| 533 statement.BindString(column++, data.last_modified); | 569 statement.BindString(column++, data.last_modified); |
| 534 statement.BindInt(column++, DownloadIdToInt(data.id)); | 570 statement.BindInt(column++, DownloadIdToInt(data.id)); |
| 535 | 571 |
| 536 return statement.Run(); | 572 if (!statement.Run()) |
| 573 return false; | |
| 574 | |
| 575 for (size_t i = 0; i < data.download_job_info.size(); ++i) { | |
| 576 if (!UpdateDownloadJob(data.download_job_info[i], false)) | |
| 577 return false; | |
| 578 } | |
| 579 | |
| 580 return true; | |
| 537 } | 581 } |
| 538 | 582 |
| 539 void DownloadDatabase::EnsureInProgressEntriesCleanedUp() { | 583 void DownloadDatabase::EnsureInProgressEntriesCleanedUp() { |
| 540 if (in_progress_entry_cleanup_completed_) | 584 if (in_progress_entry_cleanup_completed_) |
| 541 return; | 585 return; |
| 542 | 586 |
| 543 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 587 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 544 "UPDATE downloads SET state=?, interrupt_reason=? WHERE state=?")); | 588 "UPDATE downloads SET state=?, interrupt_reason=? WHERE state=?")); |
| 545 statement.BindInt(0, DownloadStateToInt(DownloadState::INTERRUPTED)); | 589 statement.BindInt(0, DownloadStateToInt(DownloadState::INTERRUPTED)); |
| 546 statement.BindInt( | 590 statement.BindInt( |
| 547 1, DownloadInterruptReasonToInt(download_interrupt_reason_crash_)); | 591 1, DownloadInterruptReasonToInt(download_interrupt_reason_crash_)); |
| 548 statement.BindInt(2, DownloadStateToInt(DownloadState::IN_PROGRESS)); | 592 statement.BindInt(2, DownloadStateToInt(DownloadState::IN_PROGRESS)); |
| 549 | 593 |
| 550 statement.Run(); | 594 statement.Run(); |
| 595 | |
| 596 sql::Statement statement_download_job(GetDB().GetCachedStatement( | |
| 597 SQL_FROM_HERE, | |
| 598 "UPDATE downloads_jobs SET state=?, interrupt_reason=? WHERE state=?")); | |
| 599 statement_download_job.BindInt( | |
| 600 0, DownloadStateToInt(DownloadState::INTERRUPTED)); | |
| 601 statement_download_job.BindInt( | |
| 602 1, DownloadInterruptReasonToInt(download_interrupt_reason_crash_)); | |
| 603 statement_download_job.BindInt( | |
| 604 2, DownloadStateToInt(DownloadState::IN_PROGRESS)); | |
| 605 statement_download_job.Run(); | |
| 551 in_progress_entry_cleanup_completed_ = true; | 606 in_progress_entry_cleanup_completed_ = true; |
| 552 } | 607 } |
| 553 | 608 |
| 554 bool DownloadDatabase::CreateDownload(const DownloadRow& info) { | 609 bool DownloadDatabase::CreateDownload(const DownloadRow& info) { |
| 555 DCHECK_NE(kInvalidDownloadId, info.id); | 610 DCHECK_NE(kInvalidDownloadId, info.id); |
| 556 DCHECK(!info.guid.empty()); | 611 DCHECK(!info.guid.empty()); |
| 557 SCOPED_UMA_HISTOGRAM_TIMER("Download.Database.CreateDownloadDuration"); | 612 SCOPED_UMA_HISTOGRAM_TIMER("Download.Database.CreateDownloadDuration"); |
| 558 EnsureInProgressEntriesCleanedUp(); | 613 EnsureInProgressEntriesCleanedUp(); |
| 559 | 614 |
| 560 if (info.url_chain.empty()) | 615 if (info.url_chain.empty()) |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 643 statement_insert_chain.BindInt(1, static_cast<int>(i)); | 698 statement_insert_chain.BindInt(1, static_cast<int>(i)); |
| 644 statement_insert_chain.BindString(2, info.url_chain[i].spec()); | 699 statement_insert_chain.BindString(2, info.url_chain[i].spec()); |
| 645 if (!statement_insert_chain.Run()) { | 700 if (!statement_insert_chain.Run()) { |
| 646 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainInsertError", | 701 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainInsertError", |
| 647 GetDB().GetErrorCode() & 0xff, 50); | 702 GetDB().GetErrorCode() & 0xff, 50); |
| 648 RemoveDownload(info.id); | 703 RemoveDownload(info.id); |
| 649 return false; | 704 return false; |
| 650 } | 705 } |
| 651 statement_insert_chain.Reset(true); | 706 statement_insert_chain.Reset(true); |
| 652 } | 707 } |
| 708 | |
| 709 for (size_t i = 0; i < info.download_job_info.size(); ++i) { | |
| 710 if (!UpdateDownloadJob(info.download_job_info[i], true)) { | |
| 711 RemoveDownload(info.id); | |
| 712 return false; | |
| 713 } | |
| 714 } | |
| 715 | |
| 653 return true; | 716 return true; |
| 654 } | 717 } |
| 655 | 718 |
| 656 void DownloadDatabase::RemoveDownload(uint32_t id) { | 719 void DownloadDatabase::RemoveDownload(uint32_t id) { |
| 657 EnsureInProgressEntriesCleanedUp(); | 720 EnsureInProgressEntriesCleanedUp(); |
| 658 | 721 |
| 659 sql::Statement downloads_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 722 sql::Statement downloads_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 660 "DELETE FROM downloads WHERE id=?")); | 723 "DELETE FROM downloads WHERE id=?")); |
| 661 downloads_statement.BindInt(0, id); | 724 downloads_statement.BindInt(0, id); |
| 662 if (!downloads_statement.Run()) { | 725 if (!downloads_statement.Run()) { |
| 663 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseMainDeleteError", | 726 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseMainDeleteError", |
| 664 GetDB().GetErrorCode() & 0xff, 50); | 727 GetDB().GetErrorCode() & 0xff, 50); |
| 665 return; | 728 return; |
| 666 } | 729 } |
| 667 RemoveDownloadURLs(id); | 730 RemoveDownloadURLs(id); |
| 731 RemoveDownloadJobs(id); | |
| 668 } | 732 } |
| 669 | 733 |
| 670 void DownloadDatabase::RemoveDownloadURLs(uint32_t id) { | 734 void DownloadDatabase::RemoveDownloadURLs(uint32_t id) { |
| 671 sql::Statement urlchain_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 735 sql::Statement urlchain_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 672 "DELETE FROM downloads_url_chains WHERE id=?")); | 736 "DELETE FROM downloads_url_chains WHERE id=?")); |
| 673 urlchain_statement.BindInt(0, id); | 737 urlchain_statement.BindInt(0, id); |
| 674 if (!urlchain_statement.Run()) { | 738 if (!urlchain_statement.Run()) { |
| 675 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainDeleteError", | 739 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainDeleteError", |
| 676 GetDB().GetErrorCode() & 0xff, 50); | 740 GetDB().GetErrorCode() & 0xff, 50); |
| 677 } | 741 } |
| 678 } | 742 } |
| 679 | 743 |
| 680 size_t DownloadDatabase::CountDownloads() { | 744 size_t DownloadDatabase::CountDownloads() { |
| 681 EnsureInProgressEntriesCleanedUp(); | 745 EnsureInProgressEntriesCleanedUp(); |
| 682 | 746 |
| 683 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 747 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 684 "SELECT count(*) from downloads")); | 748 "SELECT count(*) from downloads")); |
| 685 statement.Step(); | 749 statement.Step(); |
| 686 return statement.ColumnInt(0); | 750 return statement.ColumnInt(0); |
| 687 } | 751 } |
| 688 | 752 |
| 753 bool DownloadDatabase::EnsureDownloadJobsTableExists() { | |
| 754 // TODO(qinmin): increment the current history database version by 1 once | |
| 755 // this feature is no longer experimental. | |
| 756 const char kJobsSchema[] = | |
| 757 "CREATE TABLE downloads_jobs (" | |
| 758 "id INTEGER NOT NULL," // downloads.id. | |
|
asanka
2017/02/02 19:06:27
Probably call this |download_id| since it doesn't
qinmin
2017/02/02 22:31:32
Done.
| |
| 759 "job_id INTEGER NOT NULL," // Download job id. | |
| 760 "start_position INTEGER NOT NULL," // The start request position of the | |
| 761 // download job. | |
| 762 "length INTEGER NOT NULL," // length of the request, -1 | |
| 763 // if not specified | |
| 764 "received_bytes INTEGER NOT NULL," // Total bytes downloaded. | |
| 765 "state INTEGER NOT NULL," // To be determined. | |
| 766 "interrupt_reason INTEGER NOT NULL, " // DownloadInterruptReason | |
|
asanka
2017/02/02 19:06:27
How do you plan to use |state| and |interrupt_reas
qinmin
2017/02/02 22:31:32
I was thinking whether we should keep the state fo
| |
| 767 "PRIMARY KEY (id, job_id) )"; | |
| 768 | |
| 769 return GetDB().DoesTableExist("downloads_jobs") || | |
| 770 GetDB().Execute(kJobsSchema); | |
| 771 } | |
| 772 | |
| 773 bool DownloadDatabase::UpdateDownloadJob( | |
| 774 const DownloadJobInfo& info, bool is_new_download) { | |
| 775 sql::Statement statement_query(GetDB().GetCachedStatement( | |
| 776 SQL_FROM_HERE, | |
| 777 "SELECT count(*) " | |
| 778 "FROM downloads_jobs " | |
| 779 "WHERE id=? AND job_id=?")); | |
| 780 statement_query.BindInt(0, info.id); | |
| 781 statement_query.BindInt(1, info.job_id); | |
| 782 | |
| 783 bool job_exists = false; | |
| 784 if (statement_query.Step()) { | |
| 785 job_exists = statement_query.ColumnInt(0) > 0; | |
|
asanka
2017/02/02 19:06:28
Separate UpdateDownloadJob and CreateDownloadJob.
qinmin
2017/02/02 22:31:32
Done. Separated UpdateDownloadJob from CreateDownl
| |
| 786 // There should not be any jobs in downloads_jobs for this id. If there | |
|
asanka
2017/02/02 19:06:27
This doesn't really follow from the (!is_new_downl
qinmin
2017/02/02 22:31:32
Done.
| |
| 787 // are, we don't want them to interfere with inserting the correct jobs, | |
| 788 // so just remove them. | |
| 789 if (is_new_download && job_exists) | |
| 790 RemoveDownloadJobs(info.id); | |
| 791 } | |
| 792 int column = 0; | |
| 793 | |
| 794 if (!is_new_download && job_exists) { | |
| 795 sql::Statement statement_update(GetDB().GetCachedStatement( | |
| 796 SQL_FROM_HERE, | |
| 797 "UPDATE downloads_jobs " | |
| 798 "SET start_position=?, length=?, received_bytes=?, state=?, " | |
| 799 "interrupt_reason=? " | |
| 800 "WHERE id=? AND job_id=?")); | |
| 801 statement_update.BindInt64(column++, info.start_position); | |
| 802 statement_update.BindInt64(column++, info.length); | |
| 803 statement_update.BindInt64(column++, info.received_bytes); | |
| 804 statement_update.BindInt(column++, DownloadStateToInt(info.state)); | |
| 805 statement_update.BindInt( | |
| 806 column++, DownloadInterruptReasonToInt(info.interrupt_reason)); | |
| 807 statement_update.BindInt(column++, info.id); | |
| 808 statement_update.BindInt(column++, info.job_id); | |
| 809 return statement_update.Run(); | |
| 810 } else { | |
| 811 sql::Statement statement_insert(GetDB().GetCachedStatement( | |
| 812 SQL_FROM_HERE, | |
| 813 "INSERT INTO downloads_jobs " | |
| 814 "(id, job_id, start_position, length, received_bytes, state, " | |
| 815 "interrupt_reason) " | |
| 816 "VALUES (?, ?, ?, ?, ?, ?, ?)")); | |
| 817 statement_insert.BindInt(column++, info.id); | |
| 818 statement_insert.BindInt(column++, info.job_id); | |
| 819 statement_insert.BindInt64(column++, info.start_position); | |
| 820 statement_insert.BindInt64(column++, info.length); | |
| 821 statement_insert.BindInt64(column++, info.received_bytes); | |
| 822 statement_insert.BindInt(column++, DownloadStateToInt(info.state)); | |
| 823 statement_insert.BindInt( | |
| 824 column++, DownloadInterruptReasonToInt(info.interrupt_reason)); | |
| 825 | |
| 826 return statement_insert.Run(); | |
| 827 } | |
| 828 } | |
| 829 | |
| 830 void DownloadDatabase::RemoveDownloadJobs(uint32_t id) { | |
| 831 sql::Statement statement_delete(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
| 832 "DELETE FROM downloads_jobs WHERE id=?")); | |
| 833 statement_delete.BindInt(0, id); | |
| 834 statement_delete.Run(); | |
| 835 } | |
| 836 | |
| 689 } // namespace history | 837 } // namespace history |
| OLD | NEW |