| Index: components/history/core/browser/download_database.cc
|
| diff --git a/components/history/core/browser/download_database.cc b/components/history/core/browser/download_database.cc
|
| index 5994bf5e2564fcf5b8d27d9ef830866cedaa29d0..e82f3947ed5efa17502afe183d3b168f3f747ddc 100644
|
| --- a/components/history/core/browser/download_database.cc
|
| +++ b/components/history/core/browser/download_database.cc
|
| @@ -22,6 +22,7 @@
|
| #include "build/build_config.h"
|
| #include "components/history/core/browser/download_constants.h"
|
| #include "components/history/core/browser/download_row.h"
|
| +#include "components/history/core/browser/download_slice_info.h"
|
| #include "components/history/core/browser/history_types.h"
|
| #include "sql/statement.h"
|
|
|
| @@ -312,15 +313,32 @@ bool DownloadDatabase::InitDownloadTable() {
|
| "url LONGVARCHAR NOT NULL, " // URL.
|
| "PRIMARY KEY (id, chain_index) )";
|
|
|
| + const char kSlicesSchema[] =
|
| + "CREATE TABLE downloads_slices ("
|
| + "download_id INTEGER NOT NULL," // downloads.id.
|
| + "offset INTEGER NOT NULL," // Offset in the target file.
|
| + "received_bytes INTEGER NOT NULL," // Total bytes downloaded.
|
| + "PRIMARY KEY (download_id, offset) )";
|
| +
|
| + bool ret;
|
| if (GetDB().DoesTableExist("downloads")) {
|
| - return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") &&
|
| - EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0");
|
| + // If the "downloads" table exists, "downloads_url_chain" might not be there
|
| + // as it is introduced in version 24. A migration function will be run to
|
| + // create it later.
|
| + ret = EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") &&
|
| + EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0");
|
| } else {
|
| - // If the "downloads" table doesn't exist, the downloads_url_chain
|
| - // table better not.
|
| - return (!GetDB().DoesTableExist("downloads_url_chain") &&
|
| - GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema));
|
| + // If the "downloads" table doesn't exist, neither should the
|
| + // "downloads_url_chain" table.
|
| + ret = !GetDB().DoesTableExist("downloads_url_chain");
|
| + // Recreate the "downloads" and "downloads_url_chain" table.
|
| + ret = ret && GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema);
|
| }
|
| +
|
| + // Making sure the "downloads_slices" table is created as it is introduced in
|
| + // version 33. This table doesn't require migration of existing tables.
|
| + return ret && (GetDB().DoesTableExist("downloads_slices") ||
|
| + GetDB().Execute(kSlicesSchema));
|
| }
|
|
|
| uint32_t DownloadDatabase::GetNextDownloadId() {
|
| @@ -356,7 +374,7 @@ void DownloadDatabase::QueryDownloads(std::vector<DownloadRow>* results) {
|
| results->clear();
|
| std::set<uint32_t> ids;
|
|
|
| - std::map<uint32_t, DownloadRow*> info_map;
|
| + DownloadRowMap info_map;
|
|
|
| sql::Statement statement_main(GetDB().GetCachedStatement(
|
| SQL_FROM_HERE,
|
| @@ -374,7 +392,7 @@ void DownloadDatabase::QueryDownloads(std::vector<DownloadRow>* results) {
|
| // |id|s instead of casting them to very large uint32s, which would break
|
| // the max(id) logic in GetNextDownloadId().
|
| int64_t signed_id = statement_main.ColumnInt64(column++);
|
| - info->id = IntToDownloadId(signed_id);
|
| + bool valid = ConvertIntToDownloadId(signed_id, &(info->id));
|
| info->guid = statement_main.ColumnString(column++);
|
| info->current_path = ColumnFilePath(statement_main, column++);
|
| info->target_path = ColumnFilePath(statement_main, column++);
|
| @@ -409,7 +427,7 @@ void DownloadDatabase::QueryDownloads(std::vector<DownloadRow>* results) {
|
| // If the record is corrupted, note that and drop it.
|
| // http://crbug.com/251269
|
| DroppedReason dropped_reason = DROPPED_REASON_MAX;
|
| - if (signed_id <= static_cast<int64_t>(kInvalidDownloadId)) {
|
| + if (!valid) {
|
| // SQLITE doesn't have unsigned integers.
|
| dropped_reason = DROPPED_REASON_BAD_ID;
|
| } else if (!ids.insert(info->id).second) {
|
| @@ -442,9 +460,9 @@ void DownloadDatabase::QueryDownloads(std::vector<DownloadRow>* results) {
|
| int64_t signed_id = statement_chain.ColumnInt64(column++);
|
| int chain_index = statement_chain.ColumnInt(column++);
|
|
|
| - if (signed_id <= static_cast<int64_t>(kInvalidDownloadId))
|
| + DownloadId id = kInvalidDownloadId;
|
| + if (!ConvertIntToDownloadId(signed_id, &id))
|
| continue;
|
| - uint32_t id = IntToDownloadId(signed_id);
|
|
|
| // Note that these DCHECKs may trip as a result of corrupted databases.
|
| // We have them because in debug builds the chances are higher there's
|
| @@ -473,8 +491,10 @@ void DownloadDatabase::QueryDownloads(std::vector<DownloadRow>* results) {
|
| url_chain->push_back(GURL(statement_chain.ColumnString(2)));
|
| }
|
|
|
| - for (std::map<uint32_t, DownloadRow*>::iterator it = info_map.begin();
|
| - it != info_map.end(); ++it) {
|
| + QueryDownloadSlices(&info_map);
|
| +
|
| + for (DownloadRowMap::iterator it = info_map.begin(); it != info_map.end();
|
| + ++it) {
|
| DownloadRow* row = it->second;
|
| bool empty_url_chain = row->url_chain.empty();
|
| UMA_HISTOGRAM_BOOLEAN("Download.DatabaseEmptyUrlChain", empty_url_chain);
|
| @@ -533,7 +553,19 @@ bool DownloadDatabase::UpdateDownload(const DownloadRow& data) {
|
| statement.BindString(column++, data.last_modified);
|
| statement.BindInt(column++, DownloadIdToInt(data.id));
|
|
|
| - return statement.Run();
|
| + if (!statement.Run())
|
| + return false;
|
| +
|
| + if (data.download_slice_info.size() == 0) {
|
| + RemoveDownloadSlices(data.id);
|
| + } else {
|
| + for (size_t i = 0; i < data.download_slice_info.size(); ++i) {
|
| + if (!CreateOrUpdateDownloadSlice(data.download_slice_info[i]))
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + return true;
|
| }
|
|
|
| void DownloadDatabase::EnsureInProgressEntriesCleanedUp() {
|
| @@ -650,10 +682,18 @@ bool DownloadDatabase::CreateDownload(const DownloadRow& info) {
|
| }
|
| statement_insert_chain.Reset(true);
|
| }
|
| +
|
| + for (size_t i = 0; i < info.download_slice_info.size(); ++i) {
|
| + if (!CreateOrUpdateDownloadSlice(info.download_slice_info[i])) {
|
| + RemoveDownload(info.id);
|
| + return false;
|
| + }
|
| + }
|
| +
|
| return true;
|
| }
|
|
|
| -void DownloadDatabase::RemoveDownload(uint32_t id) {
|
| +void DownloadDatabase::RemoveDownload(DownloadId id) {
|
| EnsureInProgressEntriesCleanedUp();
|
|
|
| sql::Statement downloads_statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
|
| @@ -665,9 +705,10 @@ void DownloadDatabase::RemoveDownload(uint32_t id) {
|
| return;
|
| }
|
| RemoveDownloadURLs(id);
|
| + RemoveDownloadSlices(id);
|
| }
|
|
|
| -void DownloadDatabase::RemoveDownloadURLs(uint32_t id) {
|
| +void DownloadDatabase::RemoveDownloadURLs(DownloadId id) {
|
| sql::Statement urlchain_statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
|
| "DELETE FROM downloads_url_chains WHERE id=?"));
|
| urlchain_statement.BindInt(0, id);
|
| @@ -686,4 +727,63 @@ size_t DownloadDatabase::CountDownloads() {
|
| return statement.ColumnInt(0);
|
| }
|
|
|
| +bool DownloadDatabase::CreateOrUpdateDownloadSlice(
|
| + const DownloadSliceInfo& info) {
|
| + // If the slice has no data, there is no need to insert it into the db. Note
|
| + // that for each slice, |received_bytes| can only go up. So if a slice is
|
| + // already in the db, its |received_bytes| should always be larger than 0.
|
| + if (info.received_bytes == 0)
|
| + return true;
|
| + sql::Statement statement_replace(GetDB().GetCachedStatement(
|
| + SQL_FROM_HERE,
|
| + "REPLACE INTO downloads_slices "
|
| + "(download_id, offset, received_bytes) "
|
| + "VALUES (?, ?, ?)"));
|
| + int column = 0;
|
| + statement_replace.BindInt(column++, info.download_id);
|
| + statement_replace.BindInt64(column++, info.offset);
|
| + statement_replace.BindInt64(column++, info.received_bytes);
|
| + return statement_replace.Run();
|
| +}
|
| +
|
| +void DownloadDatabase::RemoveDownloadSlices(DownloadId id) {
|
| + sql::Statement statement_delete(GetDB().GetCachedStatement(SQL_FROM_HERE,
|
| + "DELETE FROM downloads_slices WHERE download_id=?"));
|
| + statement_delete.BindInt(0, id);
|
| + statement_delete.Run();
|
| +}
|
| +
|
| +void DownloadDatabase::QueryDownloadSlices(DownloadRowMap* download_row_map) {
|
| + DCHECK(download_row_map);
|
| + sql::Statement statement_query(GetDB().GetCachedStatement(
|
| + SQL_FROM_HERE,
|
| + "SELECT download_id, offset, received_bytes "
|
| + "FROM downloads_slices "
|
| + "ORDER BY download_id, offset"));
|
| +
|
| + while (statement_query.Step()) {
|
| + int column = 0;
|
| + DownloadSliceInfo info;
|
| + // Convert signed integer from sqlite to unsigned DownloadId.
|
| + int64_t signed_id = statement_query.ColumnInt64(column++);
|
| + bool success = ConvertIntToDownloadId(signed_id, &info.download_id);
|
| + DCHECK(success) << "Invalid download ID found in downloads_slices table "
|
| + << signed_id;
|
| + info.offset = statement_query.ColumnInt64(column++);
|
| + info.received_bytes = statement_query.ColumnInt64(column++);
|
| +
|
| + // Confirm the download_id has already been seen--if it hasn't, discard the
|
| + // record.
|
| + DownloadRowMap::iterator it = download_row_map->find(info.download_id);
|
| + bool found = (it != download_row_map->end());
|
| + UMA_HISTOGRAM_BOOLEAN(
|
| + "Download.DatabaseDownloadExistsForDownloadSlice", found);
|
| + if (!found) {
|
| + RemoveDownloadSlices(info.download_id);
|
| + continue;
|
| + }
|
| + it->second->download_slice_info.push_back(info);
|
| + }
|
| +}
|
| +
|
| } // namespace history
|
|
|