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> |
11 #include <string> | 11 #include <string> |
12 #include <vector> | 12 #include <vector> |
13 | 13 |
14 #include "base/debug/alias.h" | 14 #include "base/debug/alias.h" |
15 #include "base/files/file_path.h" | 15 #include "base/files/file_path.h" |
16 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
17 #include "base/rand_util.h" | 17 #include "base/rand_util.h" |
18 #include "base/stl_util.h" | 18 #include "base/stl_util.h" |
19 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
20 #include "base/strings/utf_string_conversions.h" | 20 #include "base/strings/utf_string_conversions.h" |
21 #include "base/time/time.h" | 21 #include "base/time/time.h" |
22 #include "build/build_config.h" | 22 #include "build/build_config.h" |
23 #include "components/history/core/browser/download_constants.h" | 23 #include "components/history/core/browser/download_constants.h" |
24 #include "components/history/core/browser/download_job_info.h" | |
24 #include "components/history/core/browser/download_row.h" | 25 #include "components/history/core/browser/download_row.h" |
25 #include "components/history/core/browser/history_types.h" | 26 #include "components/history/core/browser/history_types.h" |
26 #include "sql/statement.h" | 27 #include "sql/statement.h" |
27 | 28 |
28 namespace history { | 29 namespace history { |
29 | 30 |
30 namespace { | 31 namespace { |
31 | 32 |
32 // Reason for dropping a particular record. Used for UMA. | 33 // Reason for dropping a particular record. Used for UMA. |
33 enum DroppedReason { | 34 enum DroppedReason { |
(...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
305 | 306 |
306 const char kUrlChainSchema[] = | 307 const char kUrlChainSchema[] = |
307 "CREATE TABLE downloads_url_chains (" | 308 "CREATE TABLE downloads_url_chains (" |
308 "id INTEGER NOT NULL," // downloads.id. | 309 "id INTEGER NOT NULL," // downloads.id. |
309 "chain_index INTEGER NOT NULL," // Index of url in chain | 310 "chain_index INTEGER NOT NULL," // Index of url in chain |
310 // 0 is initial target, | 311 // 0 is initial target, |
311 // MAX is target after redirects. | 312 // MAX is target after redirects. |
312 "url LONGVARCHAR NOT NULL, " // URL. | 313 "url LONGVARCHAR NOT NULL, " // URL. |
313 "PRIMARY KEY (id, chain_index) )"; | 314 "PRIMARY KEY (id, chain_index) )"; |
314 | 315 |
316 const char kJobsSchema[] = | |
317 "CREATE TABLE downloads_jobs (" | |
318 "download_id INTEGER NOT NULL," // downloads.id. | |
319 "job_id INTEGER NOT NULL," // Download job id. | |
320 "start_position INTEGER NOT NULL," // The start request position of the | |
321 // download job. | |
322 "length INTEGER NOT NULL," // Length of the request, -1 | |
323 // if not specified | |
324 "received_bytes INTEGER NOT NULL," // Total bytes downloaded. | |
325 "state INTEGER NOT NULL," // State of the download job. | |
326 "interrupt_reason INTEGER NOT NULL, " // DownloadInterruptReason | |
327 "PRIMARY KEY (download_id, job_id) )"; | |
328 | |
329 bool ret; | |
315 if (GetDB().DoesTableExist("downloads")) { | 330 if (GetDB().DoesTableExist("downloads")) { |
316 return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && | 331 ret = EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && |
317 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); | 332 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); |
318 } else { | 333 } else { |
319 // If the "downloads" table doesn't exist, the downloads_url_chain | 334 // If the "downloads" table doesn't exist, the downloads_url_chain |
320 // table better not. | 335 // table better not. |
321 return (!GetDB().DoesTableExist("downloads_url_chain") && | 336 ret = (!GetDB().DoesTableExist("downloads_url_chain") && |
322 GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema)); | 337 GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema)); |
323 } | 338 } |
339 | |
340 return ret && (GetDB().DoesTableExist("downloads_jobs") || | |
341 GetDB().Execute(kJobsSchema)); | |
324 } | 342 } |
325 | 343 |
326 uint32_t DownloadDatabase::GetNextDownloadId() { | 344 uint32_t DownloadDatabase::GetNextDownloadId() { |
327 sql::Statement select_max_id(GetDB().GetUniqueStatement( | 345 sql::Statement select_max_id(GetDB().GetUniqueStatement( |
328 "SELECT max(id) FROM downloads")); | 346 "SELECT max(id) FROM downloads")); |
329 bool result = select_max_id.Step(); | 347 bool result = select_max_id.Step(); |
330 DCHECK(result); | 348 DCHECK(result); |
331 // If there are zero records in the downloads table, then max(id) will | 349 // If there are zero records in the downloads table, then max(id) will |
332 // return 0 = kInvalidDownloadId, so GetNextDownloadId() will set | 350 // return 0 = kInvalidDownloadId, so GetNextDownloadId() will set |
333 // *id = kInvalidDownloadId + 1. | 351 // *id = kInvalidDownloadId + 1. |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
466 url_chain->push_back(GURL()); | 484 url_chain->push_back(GURL()); |
467 current_chain_size++; | 485 current_chain_size++; |
468 } | 486 } |
469 if (current_chain_size > chain_index) | 487 if (current_chain_size > chain_index) |
470 continue; | 488 continue; |
471 | 489 |
472 // Save the record. | 490 // Save the record. |
473 url_chain->push_back(GURL(statement_chain.ColumnString(2))); | 491 url_chain->push_back(GURL(statement_chain.ColumnString(2))); |
474 } | 492 } |
475 | 493 |
494 QueryDownloadJobs(&info_map); | |
495 | |
476 for (std::map<uint32_t, DownloadRow*>::iterator it = info_map.begin(); | 496 for (std::map<uint32_t, DownloadRow*>::iterator it = info_map.begin(); |
477 it != info_map.end(); ++it) { | 497 it != info_map.end(); ++it) { |
478 DownloadRow* row = it->second; | 498 DownloadRow* row = it->second; |
479 bool empty_url_chain = row->url_chain.empty(); | 499 bool empty_url_chain = row->url_chain.empty(); |
480 UMA_HISTOGRAM_BOOLEAN("Download.DatabaseEmptyUrlChain", empty_url_chain); | 500 UMA_HISTOGRAM_BOOLEAN("Download.DatabaseEmptyUrlChain", empty_url_chain); |
481 if (empty_url_chain) { | 501 if (empty_url_chain) { |
482 RemoveDownload(row->id); | 502 RemoveDownload(row->id); |
483 } else { | 503 } else { |
484 // Copy the contents of the stored info. | 504 // Copy the contents of the stored info. |
485 results->push_back(*row); | 505 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()); | 546 statement.BindBlob(column++, data.hash.data(), data.hash.size()); |
527 statement.BindInt64(column++, data.end_time.ToInternalValue()); | 547 statement.BindInt64(column++, data.end_time.ToInternalValue()); |
528 statement.BindInt64(column++, data.total_bytes); | 548 statement.BindInt64(column++, data.total_bytes); |
529 statement.BindInt(column++, (data.opened ? 1 : 0)); | 549 statement.BindInt(column++, (data.opened ? 1 : 0)); |
530 statement.BindString(column++, data.by_ext_id); | 550 statement.BindString(column++, data.by_ext_id); |
531 statement.BindString(column++, data.by_ext_name); | 551 statement.BindString(column++, data.by_ext_name); |
532 statement.BindString(column++, data.etag); | 552 statement.BindString(column++, data.etag); |
533 statement.BindString(column++, data.last_modified); | 553 statement.BindString(column++, data.last_modified); |
534 statement.BindInt(column++, DownloadIdToInt(data.id)); | 554 statement.BindInt(column++, DownloadIdToInt(data.id)); |
535 | 555 |
536 return statement.Run(); | 556 if (!statement.Run()) |
557 return false; | |
558 for (size_t i = 0; i < data.download_job_info.size(); ++i) { | |
559 if (!UpdateDownloadJob(data.download_job_info[i]) && | |
560 !CreateDownloadJob(data.download_job_info[i])) { | |
561 return false; | |
562 } | |
563 } | |
564 | |
565 return true; | |
537 } | 566 } |
538 | 567 |
539 void DownloadDatabase::EnsureInProgressEntriesCleanedUp() { | 568 void DownloadDatabase::EnsureInProgressEntriesCleanedUp() { |
540 if (in_progress_entry_cleanup_completed_) | 569 if (in_progress_entry_cleanup_completed_) |
541 return; | 570 return; |
542 | 571 |
543 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 572 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
544 "UPDATE downloads SET state=?, interrupt_reason=? WHERE state=?")); | 573 "UPDATE downloads SET state=?, interrupt_reason=? WHERE state=?")); |
545 statement.BindInt(0, DownloadStateToInt(DownloadState::INTERRUPTED)); | 574 statement.BindInt(0, DownloadStateToInt(DownloadState::INTERRUPTED)); |
546 statement.BindInt( | 575 statement.BindInt( |
547 1, DownloadInterruptReasonToInt(download_interrupt_reason_crash_)); | 576 1, DownloadInterruptReasonToInt(download_interrupt_reason_crash_)); |
548 statement.BindInt(2, DownloadStateToInt(DownloadState::IN_PROGRESS)); | 577 statement.BindInt(2, DownloadStateToInt(DownloadState::IN_PROGRESS)); |
549 | 578 |
550 statement.Run(); | 579 statement.Run(); |
580 | |
581 sql::Statement statement_download_job(GetDB().GetCachedStatement( | |
582 SQL_FROM_HERE, | |
583 "UPDATE downloads_jobs SET state=?, interrupt_reason=? WHERE state=?")); | |
584 statement_download_job.BindInt( | |
585 0, DownloadStateToInt(DownloadState::INTERRUPTED)); | |
586 statement_download_job.BindInt( | |
587 1, DownloadInterruptReasonToInt(download_interrupt_reason_crash_)); | |
588 statement_download_job.BindInt( | |
589 2, DownloadStateToInt(DownloadState::IN_PROGRESS)); | |
590 statement_download_job.Run(); | |
551 in_progress_entry_cleanup_completed_ = true; | 591 in_progress_entry_cleanup_completed_ = true; |
552 } | 592 } |
553 | 593 |
554 bool DownloadDatabase::CreateDownload(const DownloadRow& info) { | 594 bool DownloadDatabase::CreateDownload(const DownloadRow& info) { |
555 DCHECK_NE(kInvalidDownloadId, info.id); | 595 DCHECK_NE(kInvalidDownloadId, info.id); |
556 DCHECK(!info.guid.empty()); | 596 DCHECK(!info.guid.empty()); |
557 SCOPED_UMA_HISTOGRAM_TIMER("Download.Database.CreateDownloadDuration"); | 597 SCOPED_UMA_HISTOGRAM_TIMER("Download.Database.CreateDownloadDuration"); |
558 EnsureInProgressEntriesCleanedUp(); | 598 EnsureInProgressEntriesCleanedUp(); |
559 | 599 |
560 if (info.url_chain.empty()) | 600 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)); | 683 statement_insert_chain.BindInt(1, static_cast<int>(i)); |
644 statement_insert_chain.BindString(2, info.url_chain[i].spec()); | 684 statement_insert_chain.BindString(2, info.url_chain[i].spec()); |
645 if (!statement_insert_chain.Run()) { | 685 if (!statement_insert_chain.Run()) { |
646 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainInsertError", | 686 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainInsertError", |
647 GetDB().GetErrorCode() & 0xff, 50); | 687 GetDB().GetErrorCode() & 0xff, 50); |
648 RemoveDownload(info.id); | 688 RemoveDownload(info.id); |
649 return false; | 689 return false; |
650 } | 690 } |
651 statement_insert_chain.Reset(true); | 691 statement_insert_chain.Reset(true); |
652 } | 692 } |
693 | |
694 for (size_t i = 0; i < info.download_job_info.size(); ++i) { | |
695 if (!CreateDownloadJob(info.download_job_info[i])) { | |
696 RemoveDownload(info.id); | |
697 return false; | |
698 } | |
699 } | |
700 | |
653 return true; | 701 return true; |
654 } | 702 } |
655 | 703 |
656 void DownloadDatabase::RemoveDownload(uint32_t id) { | 704 void DownloadDatabase::RemoveDownload(uint32_t id) { |
657 EnsureInProgressEntriesCleanedUp(); | 705 EnsureInProgressEntriesCleanedUp(); |
658 | 706 |
659 sql::Statement downloads_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 707 sql::Statement downloads_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
660 "DELETE FROM downloads WHERE id=?")); | 708 "DELETE FROM downloads WHERE id=?")); |
661 downloads_statement.BindInt(0, id); | 709 downloads_statement.BindInt(0, id); |
662 if (!downloads_statement.Run()) { | 710 if (!downloads_statement.Run()) { |
663 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseMainDeleteError", | 711 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseMainDeleteError", |
664 GetDB().GetErrorCode() & 0xff, 50); | 712 GetDB().GetErrorCode() & 0xff, 50); |
665 return; | 713 return; |
666 } | 714 } |
667 RemoveDownloadURLs(id); | 715 RemoveDownloadURLs(id); |
716 RemoveDownloadJobs(id); | |
668 } | 717 } |
669 | 718 |
670 void DownloadDatabase::RemoveDownloadURLs(uint32_t id) { | 719 void DownloadDatabase::RemoveDownloadURLs(uint32_t id) { |
671 sql::Statement urlchain_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 720 sql::Statement urlchain_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
672 "DELETE FROM downloads_url_chains WHERE id=?")); | 721 "DELETE FROM downloads_url_chains WHERE id=?")); |
673 urlchain_statement.BindInt(0, id); | 722 urlchain_statement.BindInt(0, id); |
674 if (!urlchain_statement.Run()) { | 723 if (!urlchain_statement.Run()) { |
675 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainDeleteError", | 724 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainDeleteError", |
676 GetDB().GetErrorCode() & 0xff, 50); | 725 GetDB().GetErrorCode() & 0xff, 50); |
677 } | 726 } |
678 } | 727 } |
679 | 728 |
680 size_t DownloadDatabase::CountDownloads() { | 729 size_t DownloadDatabase::CountDownloads() { |
681 EnsureInProgressEntriesCleanedUp(); | 730 EnsureInProgressEntriesCleanedUp(); |
682 | 731 |
683 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 732 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
684 "SELECT count(*) from downloads")); | 733 "SELECT count(*) from downloads")); |
685 statement.Step(); | 734 statement.Step(); |
686 return statement.ColumnInt(0); | 735 return statement.ColumnInt(0); |
687 } | 736 } |
688 | 737 |
738 bool DownloadDatabase::CreateDownloadJob(const DownloadJobInfo& info) { | |
739 sql::Statement statement_insert(GetDB().GetCachedStatement( | |
740 SQL_FROM_HERE, | |
741 "INSERT INTO downloads_jobs " | |
742 "(download_id, job_id, start_position, length, received_bytes, state, " | |
743 "interrupt_reason) " | |
744 "VALUES (?, ?, ?, ?, ?, ?, ?)")); | |
745 int column = 0; | |
746 statement_insert.BindInt(column++, info.download_id); | |
747 statement_insert.BindInt(column++, info.job_id); | |
748 statement_insert.BindInt64(column++, info.start_position); | |
749 statement_insert.BindInt64(column++, info.length); | |
750 statement_insert.BindInt64(column++, info.received_bytes); | |
751 statement_insert.BindInt(column++, DownloadStateToInt(info.state)); | |
752 statement_insert.BindInt( | |
753 column++, DownloadInterruptReasonToInt(info.interrupt_reason)); | |
754 return statement_insert.Run(); | |
755 } | |
756 | |
757 bool DownloadDatabase::UpdateDownloadJob(const DownloadJobInfo& info) { | |
758 // Need to check whether the entry exists first. Unfortunately, running an | |
759 // UPDATE statement will always return true even if the entry doesn't exist. | |
760 sql::Statement statement_query(GetDB().GetCachedStatement( | |
qinmin
2017/02/04 00:06:57
Unfortunately, update statement always return true
sky
2017/02/06 16:29:04
http://stackoverflow.com/questions/15277373/sqlite
qinmin
2017/02/06 16:41:53
INSERT OR IGNORE doesn't improve the performance o
qinmin
2017/02/06 19:25:02
Compared the performance of "SELECT changes" and "
| |
761 SQL_FROM_HERE, | |
762 "SELECT count(*) " | |
763 "FROM downloads_jobs " | |
764 "WHERE download_id=? AND job_id=?")); | |
765 statement_query.BindInt(0, info.download_id); | |
766 statement_query.BindInt(1, info.job_id); | |
767 if (statement_query.Step() && statement_query.ColumnInt(0) == 0) | |
768 return false; | |
769 | |
770 sql::Statement statement_update(GetDB().GetCachedStatement( | |
771 SQL_FROM_HERE, | |
772 "UPDATE downloads_jobs " | |
773 "SET start_position=?, length=?, received_bytes=?, state=?, " | |
774 "interrupt_reason=? " | |
775 "WHERE download_id=? AND job_id=?")); | |
776 int column = 0; | |
777 statement_update.BindInt64(column++, info.start_position); | |
778 statement_update.BindInt64(column++, info.length); | |
779 statement_update.BindInt64(column++, info.received_bytes); | |
780 statement_update.BindInt(column++, DownloadStateToInt(info.state)); | |
781 statement_update.BindInt( | |
782 column++, DownloadInterruptReasonToInt(info.interrupt_reason)); | |
783 statement_update.BindInt(column++, info.download_id); | |
784 statement_update.BindInt(column++, info.job_id); | |
785 return statement_update.Run(); | |
786 } | |
787 | |
788 void DownloadDatabase::RemoveDownloadJobs(uint32_t id) { | |
789 sql::Statement statement_delete(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
790 "DELETE FROM downloads_jobs WHERE download_id=?")); | |
791 statement_delete.BindInt(0, id); | |
792 statement_delete.Run(); | |
793 } | |
794 | |
795 void DownloadDatabase::QueryDownloadJobs( | |
796 std::map<uint32_t, DownloadRow*>* download_row_map) { | |
797 sql::Statement statement_download_job(GetDB().GetCachedStatement( | |
798 SQL_FROM_HERE, | |
799 "SELECT download_id, job_id, start_position, length, received_bytes, " | |
800 "state, interrupt_reason FROM downloads_jobs " | |
801 "ORDER BY download_id, job_id")); | |
802 | |
803 while (statement_download_job.Step()) { | |
804 int column = 0; | |
805 // See the comment above about SQLITE lacking unsigned integers. | |
806 int64_t signed_id = statement_download_job.ColumnInt64(column++); | |
807 if (signed_id <= static_cast<int64_t>(kInvalidDownloadId)) { | |
808 LOG(ERROR) << "Invalid download ID found in downloads_jobs table " | |
809 << signed_id; | |
810 continue; | |
811 } | |
812 int download_id = IntToDownloadId(signed_id); | |
813 // Confirm the download_id has already been seen--if it hasn't, discard the | |
814 // record. | |
815 bool found = base::ContainsKey(*download_row_map, download_id); | |
816 UMA_HISTOGRAM_BOOLEAN( | |
817 "Download.DatabaseDownloadExistsForDownloadJob", found); | |
818 DCHECK(found); | |
819 if (!found) | |
820 continue; | |
821 | |
822 DownloadJobInfo info; | |
823 info.download_id = download_id; | |
824 info.job_id = statement_download_job.ColumnInt(column++); | |
825 info.start_position = statement_download_job.ColumnInt64(column++); | |
826 info.length = statement_download_job.ColumnInt64(column++); | |
827 info.received_bytes = statement_download_job.ColumnInt64(column++); | |
828 info.state = IntToDownloadState( | |
829 statement_download_job.ColumnInt(column++)); | |
830 info.interrupt_reason = IntToDownloadInterruptReason( | |
831 statement_download_job.ColumnInt(column++)); | |
832 (*download_row_map)[download_id]->download_job_info.push_back(info); | |
833 } | |
834 } | |
835 | |
689 } // namespace history | 836 } // namespace history |
OLD | NEW |