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 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
59 statement.BindString16(col, path.value()); | 59 statement.BindString16(col, path.value()); |
60 } | 60 } |
61 base::FilePath ColumnFilePath(sql::Statement& statement, int col) { | 61 base::FilePath ColumnFilePath(sql::Statement& statement, int col) { |
62 return base::FilePath(statement.ColumnString16(col)); | 62 return base::FilePath(statement.ColumnString16(col)); |
63 } | 63 } |
64 | 64 |
65 #endif | 65 #endif |
66 | 66 |
67 } // namespace | 67 } // namespace |
68 | 68 |
69 // static | |
70 bool DownloadDatabase::IsParallelDownloadingEnabled() { | |
71 return base::FeatureList::IsEnabled(kParallelDownloading); | |
72 } | |
73 | |
69 DownloadDatabase::DownloadDatabase( | 74 DownloadDatabase::DownloadDatabase( |
70 DownloadInterruptReason download_interrupt_reason_none, | 75 DownloadInterruptReason download_interrupt_reason_none, |
71 DownloadInterruptReason download_interrupt_reason_crash) | 76 DownloadInterruptReason download_interrupt_reason_crash) |
72 : owning_thread_set_(false), | 77 : owning_thread_set_(false), |
73 owning_thread_(0), | 78 owning_thread_(0), |
74 in_progress_entry_cleanup_completed_(false), | 79 in_progress_entry_cleanup_completed_(false), |
75 download_interrupt_reason_none_(download_interrupt_reason_none), | 80 download_interrupt_reason_none_(download_interrupt_reason_none), |
76 download_interrupt_reason_crash_(download_interrupt_reason_crash) { | 81 download_interrupt_reason_crash_(download_interrupt_reason_crash) { |
77 } | 82 } |
78 | 83 |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
305 | 310 |
306 const char kUrlChainSchema[] = | 311 const char kUrlChainSchema[] = |
307 "CREATE TABLE downloads_url_chains (" | 312 "CREATE TABLE downloads_url_chains (" |
308 "id INTEGER NOT NULL," // downloads.id. | 313 "id INTEGER NOT NULL," // downloads.id. |
309 "chain_index INTEGER NOT NULL," // Index of url in chain | 314 "chain_index INTEGER NOT NULL," // Index of url in chain |
310 // 0 is initial target, | 315 // 0 is initial target, |
311 // MAX is target after redirects. | 316 // MAX is target after redirects. |
312 "url LONGVARCHAR NOT NULL, " // URL. | 317 "url LONGVARCHAR NOT NULL, " // URL. |
313 "PRIMARY KEY (id, chain_index) )"; | 318 "PRIMARY KEY (id, chain_index) )"; |
314 | 319 |
320 bool ret; | |
315 if (GetDB().DoesTableExist("downloads")) { | 321 if (GetDB().DoesTableExist("downloads")) { |
316 return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && | 322 ret = EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && |
317 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); | 323 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); |
318 } else { | 324 } else { |
319 // If the "downloads" table doesn't exist, the downloads_url_chain | 325 // If the "downloads" table doesn't exist, the downloads_url_chain |
320 // table better not. | 326 // and the downloads_jobs table better not. |
321 return (!GetDB().DoesTableExist("downloads_url_chain") && | 327 ret = !GetDB().DoesTableExist("downloads_url_chain") && |
322 GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema)); | 328 !GetDB().DoesTableExist("downloads_jobs") && |
329 GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema); | |
323 } | 330 } |
331 if (IsParallelDownloadingEnabled()) | |
332 ret = ret && EnsureDownloadJobsTableExists(); | |
333 | |
334 return ret; | |
324 } | 335 } |
325 | 336 |
326 uint32_t DownloadDatabase::GetNextDownloadId() { | 337 uint32_t DownloadDatabase::GetNextDownloadId() { |
327 sql::Statement select_max_id(GetDB().GetUniqueStatement( | 338 sql::Statement select_max_id(GetDB().GetUniqueStatement( |
328 "SELECT max(id) FROM downloads")); | 339 "SELECT max(id) FROM downloads")); |
329 bool result = select_max_id.Step(); | 340 bool result = select_max_id.Step(); |
330 DCHECK(result); | 341 DCHECK(result); |
331 // If there are zero records in the downloads table, then max(id) will | 342 // If there are zero records in the downloads table, then max(id) will |
332 // return 0 = kInvalidDownloadId, so GetNextDownloadId() will set | 343 // return 0 = kInvalidDownloadId, so GetNextDownloadId() will set |
333 // *id = kInvalidDownloadId + 1. | 344 // *id = kInvalidDownloadId + 1. |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
466 url_chain->push_back(GURL()); | 477 url_chain->push_back(GURL()); |
467 current_chain_size++; | 478 current_chain_size++; |
468 } | 479 } |
469 if (current_chain_size > chain_index) | 480 if (current_chain_size > chain_index) |
470 continue; | 481 continue; |
471 | 482 |
472 // Save the record. | 483 // Save the record. |
473 url_chain->push_back(GURL(statement_chain.ColumnString(2))); | 484 url_chain->push_back(GURL(statement_chain.ColumnString(2))); |
474 } | 485 } |
475 | 486 |
487 if (IsParallelDownloadingEnabled()) { | |
asanka
2017/02/01 22:34:10
The DB schema shouldn't depend on a feature state.
qinmin
2017/02/01 23:37:26
removed all feature state checking in this file. S
| |
488 sql::Statement statement_download_job(GetDB().GetCachedStatement( | |
489 SQL_FROM_HERE, | |
490 "SELECT id, job_id, start_position, length, received_bytes, " | |
491 "state, interrupt_reason FROM downloads_jobs " | |
492 "ORDER BY id, job_id")); | |
493 | |
494 while (statement_download_job.Step()) { | |
495 int column = 0; | |
496 // See the comment above about SQLITE lacking unsigned integers. | |
497 int64_t signed_id = statement_download_job.ColumnInt64(column++); | |
498 if (signed_id <= static_cast<int64_t>(kInvalidDownloadId)) | |
499 continue; | |
500 int id = IntToDownloadId(signed_id); | |
501 DownloadJobInfo info; | |
502 info.id = IntToDownloadId(signed_id); | |
503 info.job_id = statement_download_job.ColumnInt(column++); | |
504 info.start_position = statement_download_job.ColumnInt64(column++); | |
505 info.length = statement_download_job.ColumnInt64(column++); | |
506 info.received_bytes = statement_download_job.ColumnInt64(column++); | |
507 info.state = IntToDownloadState( | |
508 statement_download_job.ColumnInt(column++)); | |
509 info.interrupt_reason = IntToDownloadInterruptReason( | |
510 statement_download_job.ColumnInt(column++)); | |
511 // Confirm the id has already been seen--if it hasn't, discard the | |
512 // record. | |
513 DCHECK(base::ContainsKey(info_map, id)); | |
514 if (!base::ContainsKey(info_map, id)) | |
515 continue; | |
516 info_map[id]->download_job_info.push_back(info); | |
517 } | |
518 } | |
519 | |
476 for (std::map<uint32_t, DownloadRow*>::iterator it = info_map.begin(); | 520 for (std::map<uint32_t, DownloadRow*>::iterator it = info_map.begin(); |
477 it != info_map.end(); ++it) { | 521 it != info_map.end(); ++it) { |
478 DownloadRow* row = it->second; | 522 DownloadRow* row = it->second; |
479 bool empty_url_chain = row->url_chain.empty(); | 523 bool empty_url_chain = row->url_chain.empty(); |
480 UMA_HISTOGRAM_BOOLEAN("Download.DatabaseEmptyUrlChain", empty_url_chain); | 524 UMA_HISTOGRAM_BOOLEAN("Download.DatabaseEmptyUrlChain", empty_url_chain); |
481 if (empty_url_chain) { | 525 if (empty_url_chain) { |
482 RemoveDownload(row->id); | 526 RemoveDownload(row->id); |
483 } else { | 527 } else { |
484 // Copy the contents of the stored info. | 528 // Copy the contents of the stored info. |
485 results->push_back(*row); | 529 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()); | 570 statement.BindBlob(column++, data.hash.data(), data.hash.size()); |
527 statement.BindInt64(column++, data.end_time.ToInternalValue()); | 571 statement.BindInt64(column++, data.end_time.ToInternalValue()); |
528 statement.BindInt64(column++, data.total_bytes); | 572 statement.BindInt64(column++, data.total_bytes); |
529 statement.BindInt(column++, (data.opened ? 1 : 0)); | 573 statement.BindInt(column++, (data.opened ? 1 : 0)); |
530 statement.BindString(column++, data.by_ext_id); | 574 statement.BindString(column++, data.by_ext_id); |
531 statement.BindString(column++, data.by_ext_name); | 575 statement.BindString(column++, data.by_ext_name); |
532 statement.BindString(column++, data.etag); | 576 statement.BindString(column++, data.etag); |
533 statement.BindString(column++, data.last_modified); | 577 statement.BindString(column++, data.last_modified); |
534 statement.BindInt(column++, DownloadIdToInt(data.id)); | 578 statement.BindInt(column++, DownloadIdToInt(data.id)); |
535 | 579 |
536 return statement.Run(); | 580 if (!statement.Run()) |
581 return false; | |
582 | |
583 if (IsParallelDownloadingEnabled()) { | |
584 for (size_t i = 0; i < data.download_job_info.size(); ++i) { | |
585 if (!UpdateDownloadJob(data.id, data.download_job_info[i], false)) | |
586 return false; | |
587 } | |
588 } | |
589 | |
590 return true; | |
537 } | 591 } |
538 | 592 |
539 void DownloadDatabase::EnsureInProgressEntriesCleanedUp() { | 593 void DownloadDatabase::EnsureInProgressEntriesCleanedUp() { |
540 if (in_progress_entry_cleanup_completed_) | 594 if (in_progress_entry_cleanup_completed_) |
541 return; | 595 return; |
542 | 596 |
543 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 597 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
544 "UPDATE downloads SET state=?, interrupt_reason=? WHERE state=?")); | 598 "UPDATE downloads SET state=?, interrupt_reason=? WHERE state=?")); |
545 statement.BindInt(0, DownloadStateToInt(DownloadState::INTERRUPTED)); | 599 statement.BindInt(0, DownloadStateToInt(DownloadState::INTERRUPTED)); |
546 statement.BindInt( | 600 statement.BindInt( |
547 1, DownloadInterruptReasonToInt(download_interrupt_reason_crash_)); | 601 1, DownloadInterruptReasonToInt(download_interrupt_reason_crash_)); |
548 statement.BindInt(2, DownloadStateToInt(DownloadState::IN_PROGRESS)); | 602 statement.BindInt(2, DownloadStateToInt(DownloadState::IN_PROGRESS)); |
549 | 603 |
550 statement.Run(); | 604 statement.Run(); |
605 | |
606 if (IsParallelDownloadingEnabled()) { | |
607 sql::Statement statement_download_job(GetDB().GetCachedStatement( | |
608 SQL_FROM_HERE, | |
609 "UPDATE downloads_jobs SET state=?, interrupt_reason=? WHERE state=?")); | |
610 statement_download_job.BindInt( | |
611 0, DownloadStateToInt(DownloadState::INTERRUPTED)); | |
612 statement_download_job.BindInt( | |
613 1, DownloadInterruptReasonToInt(download_interrupt_reason_crash_)); | |
614 statement_download_job.BindInt( | |
615 2, DownloadStateToInt(DownloadState::IN_PROGRESS)); | |
616 statement_download_job.Run(); | |
617 } | |
551 in_progress_entry_cleanup_completed_ = true; | 618 in_progress_entry_cleanup_completed_ = true; |
552 } | 619 } |
553 | 620 |
554 bool DownloadDatabase::CreateDownload(const DownloadRow& info) { | 621 bool DownloadDatabase::CreateDownload(const DownloadRow& info) { |
555 DCHECK_NE(kInvalidDownloadId, info.id); | 622 DCHECK_NE(kInvalidDownloadId, info.id); |
556 DCHECK(!info.guid.empty()); | 623 DCHECK(!info.guid.empty()); |
557 SCOPED_UMA_HISTOGRAM_TIMER("Download.Database.CreateDownloadDuration"); | 624 SCOPED_UMA_HISTOGRAM_TIMER("Download.Database.CreateDownloadDuration"); |
558 EnsureInProgressEntriesCleanedUp(); | 625 EnsureInProgressEntriesCleanedUp(); |
559 | 626 |
560 if (info.url_chain.empty()) | 627 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)); | 710 statement_insert_chain.BindInt(1, static_cast<int>(i)); |
644 statement_insert_chain.BindString(2, info.url_chain[i].spec()); | 711 statement_insert_chain.BindString(2, info.url_chain[i].spec()); |
645 if (!statement_insert_chain.Run()) { | 712 if (!statement_insert_chain.Run()) { |
646 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainInsertError", | 713 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainInsertError", |
647 GetDB().GetErrorCode() & 0xff, 50); | 714 GetDB().GetErrorCode() & 0xff, 50); |
648 RemoveDownload(info.id); | 715 RemoveDownload(info.id); |
649 return false; | 716 return false; |
650 } | 717 } |
651 statement_insert_chain.Reset(true); | 718 statement_insert_chain.Reset(true); |
652 } | 719 } |
720 | |
721 if (IsParallelDownloadingEnabled()) { | |
722 for (size_t i = 0; i < info.download_job_info.size(); ++i) { | |
723 if (!UpdateDownloadJob(info.id, info.download_job_info[i], true)) { | |
724 RemoveDownload(info.id); | |
725 return false; | |
726 } | |
727 } | |
728 } | |
729 | |
653 return true; | 730 return true; |
654 } | 731 } |
655 | 732 |
656 void DownloadDatabase::RemoveDownload(uint32_t id) { | 733 void DownloadDatabase::RemoveDownload(uint32_t id) { |
657 EnsureInProgressEntriesCleanedUp(); | 734 EnsureInProgressEntriesCleanedUp(); |
658 | 735 |
659 sql::Statement downloads_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 736 sql::Statement downloads_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
660 "DELETE FROM downloads WHERE id=?")); | 737 "DELETE FROM downloads WHERE id=?")); |
661 downloads_statement.BindInt(0, id); | 738 downloads_statement.BindInt(0, id); |
662 if (!downloads_statement.Run()) { | 739 if (!downloads_statement.Run()) { |
663 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseMainDeleteError", | 740 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseMainDeleteError", |
664 GetDB().GetErrorCode() & 0xff, 50); | 741 GetDB().GetErrorCode() & 0xff, 50); |
665 return; | 742 return; |
666 } | 743 } |
667 RemoveDownloadURLs(id); | 744 RemoveDownloadURLs(id); |
745 if (IsParallelDownloadingEnabled()) { | |
746 RemoveDownloadJobs(id); | |
747 } | |
668 } | 748 } |
669 | 749 |
670 void DownloadDatabase::RemoveDownloadURLs(uint32_t id) { | 750 void DownloadDatabase::RemoveDownloadURLs(uint32_t id) { |
671 sql::Statement urlchain_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 751 sql::Statement urlchain_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
672 "DELETE FROM downloads_url_chains WHERE id=?")); | 752 "DELETE FROM downloads_url_chains WHERE id=?")); |
673 urlchain_statement.BindInt(0, id); | 753 urlchain_statement.BindInt(0, id); |
674 if (!urlchain_statement.Run()) { | 754 if (!urlchain_statement.Run()) { |
675 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainDeleteError", | 755 UMA_HISTOGRAM_ENUMERATION("Download.DatabaseURLChainDeleteError", |
676 GetDB().GetErrorCode() & 0xff, 50); | 756 GetDB().GetErrorCode() & 0xff, 50); |
677 } | 757 } |
678 } | 758 } |
679 | 759 |
680 size_t DownloadDatabase::CountDownloads() { | 760 size_t DownloadDatabase::CountDownloads() { |
681 EnsureInProgressEntriesCleanedUp(); | 761 EnsureInProgressEntriesCleanedUp(); |
682 | 762 |
683 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 763 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
684 "SELECT count(*) from downloads")); | 764 "SELECT count(*) from downloads")); |
685 statement.Step(); | 765 statement.Step(); |
686 return statement.ColumnInt(0); | 766 return statement.ColumnInt(0); |
687 } | 767 } |
688 | 768 |
769 bool DownloadDatabase::EnsureDownloadJobsTableExists() { | |
770 // TODO(qinmin): increment the current history database version by 1 once | |
771 // this feature is no longer experimental. | |
772 const char kJobsSchema[] = | |
773 "CREATE TABLE downloads_jobs (" | |
774 "id INTEGER NOT NULL," // downloads.id. | |
775 "job_id INTEGER NOT NULL," // Download job id. | |
776 "start_position INTEGER NOT NULL," // The start request position of the | |
777 // download job. | |
778 "length INTEGER NOT NULL," // length of the request, -1 | |
779 // if not specified | |
780 "received_bytes INTEGER NOT NULL," // Total bytes downloaded. | |
781 "state INTEGER NOT NULL," // To be determined. | |
782 "interrupt_reason INTEGER NOT NULL, " // DownloadInterruptReason | |
783 "PRIMARY KEY (id, job_id) )"; | |
784 | |
785 return GetDB().DoesTableExist("downloads_jobs") || | |
786 GetDB().Execute(kJobsSchema); | |
787 } | |
788 | |
789 bool DownloadDatabase::UpdateDownloadJob( | |
790 const DownloadJobInfo& info, bool is_new_download) { | |
791 sql::Statement statement_query(GetDB().GetCachedStatement( | |
792 SQL_FROM_HERE, | |
793 "SELECT count(*) " | |
794 "FROM downloads_jobs " | |
795 "WHERE id=? AND job_id=?")); | |
796 statement_query.BindInt(0, info.id); | |
797 statement_query.BindInt(1, info.job_id); | |
798 | |
799 bool job_exists = false; | |
800 if (statement_query.Step()) { | |
801 job_exists = statement_query.ColumnInt(0) > 0; | |
802 // There should not be any jobs in downloads_jobs for this id. If there | |
803 // are, we don't want them to interfere with inserting the correct jobs, | |
804 // so just remove them. | |
805 if (is_new_download && job_exists) | |
806 RemoveDownloadJobs(info.id); | |
807 } | |
808 int column = 0; | |
809 | |
810 if (!is_new_download && job_exists) { | |
811 sql::Statement statement_update(GetDB().GetCachedStatement( | |
812 SQL_FROM_HERE, | |
813 "UPDATE downloads_jobs " | |
814 "SET start_position=?, length=?, received_bytes=?, state=?, " | |
815 "interrupt_reason=? " | |
816 "WHERE id=? AND job_id=?")); | |
817 statement_update.BindInt64(column++, info.start_position); | |
818 statement_update.BindInt64(column++, info.length); | |
819 statement_update.BindInt64(column++, info.received_bytes); | |
820 statement_update.BindInt(column++, DownloadStateToInt(info.state)); | |
821 statement_update.BindInt( | |
822 column++, DownloadInterruptReasonToInt(info.interrupt_reason)); | |
823 statement_update.BindInt(column++, info.id); | |
824 statement_update.BindInt(column++, info.job_id); | |
825 return statement_update.Run(); | |
826 } else { | |
827 sql::Statement statement_insert(GetDB().GetCachedStatement( | |
828 SQL_FROM_HERE, | |
829 "INSERT INTO downloads_jobs " | |
830 "(id, job_id, start_position, length, received_bytes, state, " | |
831 "interrupt_reason) " | |
832 "VALUES (?, ?, ?, ?, ?, ?, ?)")); | |
833 statement_insert.BindInt(column++, info.id); | |
834 statement_insert.BindInt(column++, info.job_id); | |
835 statement_insert.BindInt64(column++, info.start_position); | |
836 statement_insert.BindInt64(column++, info.length); | |
837 statement_insert.BindInt64(column++, info.received_bytes); | |
838 statement_insert.BindInt(column++, DownloadStateToInt(info.state)); | |
839 statement_insert.BindInt( | |
840 column++, DownloadInterruptReasonToInt(info.interrupt_reason)); | |
841 | |
842 return statement_insert.Run(); | |
843 } | |
844 } | |
845 | |
846 void DownloadDatabase::RemoveDownloadJobs(uint32_t id) { | |
847 sql::Statement statement_delete(GetDB().GetCachedStatement(SQL_FROM_HERE, | |
848 "DELETE FROM downloads_jobs WHERE id=?")); | |
849 statement_delete.BindInt(0, id); | |
850 statement_delete.Run(); | |
851 } | |
852 | |
689 } // namespace history | 853 } // namespace history |
OLD | NEW |