Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3798)

Unified Diff: chrome/browser/history/download_database.cc

Issue 10823203: Fix downloads db state=3 corruption using version=23 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: version=23 Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/history/download_database.cc
diff --git a/chrome/browser/history/download_database.cc b/chrome/browser/history/download_database.cc
index 5039e186765eb606500d8cd6f5e8715703a1a14f..8eea517f2c4aad0c45c0ebcd1a47c151925f95b7 100644
--- a/chrome/browser/history/download_database.cc
+++ b/chrome/browser/history/download_database.cc
@@ -38,6 +38,43 @@ static const char kSchema[] =
"end_time INTEGER NOT NULL," // When the download completed.
"opened INTEGER NOT NULL)"; // 1 if it has ever been opened else 0
+// These constants and next two functions are used to allow
+// DownloadItem::DownloadState to change without breaking the database schema.
+// They guarantee that the values of the |state| field in the database are one
+// of the values returned by StateToInt, and that the values of the |state|
+// field of the DownloadPersistentStoreInfos returned by QueryDownloads() are
+// one of the values returned by IntToState().
+static const int kStateInvalid = -1;
+static const int kStateInProgress = 0;
+static const int kStateComplete = 1;
+static const int kStateCancelled = 2;
+static const int kStateBug140687 = 3;
+static const int kStateInterrupted = 4;
+
+int StateToInt(DownloadItem::DownloadState state) {
+ switch (state) {
+ case DownloadItem::IN_PROGRESS: return kStateInProgress;
+ case DownloadItem::COMPLETE: return kStateComplete;
+ case DownloadItem::CANCELLED: return kStateCancelled;
+ case DownloadItem::REMOVING: return kStateInvalid;
+ case DownloadItem::INTERRUPTED: return kStateInterrupted;
+ case DownloadItem::MAX_DOWNLOAD_STATE: return kStateInvalid;
+ default: return kStateInvalid;
+ }
+}
+
+DownloadItem::DownloadState IntToState(int state) {
+ switch (state) {
+ case kStateInProgress: return DownloadItem::IN_PROGRESS;
+ case kStateComplete: return DownloadItem::COMPLETE;
+ case kStateCancelled: return DownloadItem::CANCELLED;
+ // We should not need kStateBug140687 here because MigrateDownloadState()
+ // is called in HistoryDatabase::Init().
+ case kStateInterrupted: return DownloadItem::INTERRUPTED;
+ default: return DownloadItem::MAX_DOWNLOAD_STATE;
+ }
+}
+
#if defined(OS_POSIX)
// Binds/reads the given file path to the given column of the given statement.
@@ -92,6 +129,14 @@ bool DownloadDatabase::EnsureColumnExists(
GetDB().Execute(add_col.c_str());
}
+bool DownloadDatabase::MigrateDownloadsState() {
+ sql::Statement statement(GetDB().GetUniqueStatement(
+ "UPDATE downloads SET state=? WHERE state=?"));
+ statement.BindInt(0, kStateInterrupted);
+ statement.BindInt(1, kStateBug140687);
+ return statement.Run();
+}
+
bool DownloadDatabase::InitDownloadTable() {
CheckThread();
GetMetaTable().GetValue(kNextDownloadId, &next_id_);
@@ -130,9 +175,12 @@ void DownloadDatabase::QueryDownloads(
info.start_time = base::Time::FromTimeT(statement.ColumnInt64(3));
info.received_bytes = statement.ColumnInt64(4);
info.total_bytes = statement.ColumnInt64(5);
- info.state = statement.ColumnInt(6);
+ info.state = IntToState(statement.ColumnInt(6));
info.end_time = base::Time::FromTimeT(statement.ColumnInt64(7));
info.opened = statement.ColumnInt(8) != 0;
+ if (info.state == DownloadItem::MAX_DOWNLOAD_STATE) {
+ continue;
Randy Smith (Not in Mondays) 2012/08/07 20:36:16 UMA? (You're welcome to say not in this CL, but I
sky 2012/08/07 20:54:17 Why add this? At best we do a DCHECK for this sort
asanka 2012/08/07 20:56:14 If you skip this early, wouldn't you risk not upda
Randy Smith (Not in Mondays) 2012/08/07 21:01:35 The basic problem is that almost any sanitization
benjhayden 2012/08/08 01:11:52 This layer of abstraction separates the compiler-g
benjhayden 2012/08/08 01:11:52 Done.
benjhayden 2012/08/08 01:11:52 Done.
benjhayden 2012/08/08 01:11:52 This isn't just sanitization. It's a mapping, a la
Randy Smith (Not in Mondays) 2012/08/08 01:52:25 Good point. Ok.
+ }
results->push_back(info);
if (info.db_handle >= next_db_handle_)
next_db_handle_ = info.db_handle + 1;
@@ -147,11 +195,14 @@ void DownloadDatabase::QueryDownloads(
bool DownloadDatabase::UpdateDownload(const DownloadPersistentStoreInfo& data) {
CheckThread();
DCHECK(data.db_handle > 0);
+ int state = StateToInt(data.state);
+ if (state == kStateInvalid)
+ return false;
Randy Smith (Not in Mondays) 2012/08/07 20:36:16 DCHECK? If we get an invalid state here, it means
benjhayden 2012/08/08 01:11:52 Is it possible for UpdateDownload to see a downloa
Randy Smith (Not in Mondays) 2012/08/08 01:52:25 Not sure enough for this CL :-} :-|.
sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
"UPDATE downloads "
"SET received_bytes=?, state=?, end_time=?, opened=? WHERE id=?"));
statement.BindInt64(0, data.received_bytes);
- statement.BindInt(1, data.state);
+ statement.BindInt(1, state);
statement.BindInt64(2, data.end_time.ToTimeT());
statement.BindInt(3, (data.opened ? 1 : 0));
statement.BindInt64(4, data.db_handle);
@@ -175,8 +226,8 @@ bool DownloadDatabase::CleanUpInProgressEntries() {
CheckThread();
sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
"UPDATE downloads SET state=? WHERE state=?"));
- statement.BindInt(0, DownloadItem::CANCELLED);
- statement.BindInt(1, DownloadItem::IN_PROGRESS);
+ statement.BindInt(0, kStateCancelled);
+ statement.BindInt(1, kStateInProgress);
return statement.Run();
}
@@ -193,6 +244,10 @@ int64 DownloadDatabase::CreateDownload(
CHECK_NE(0, next_db_handle_);
}
+ int state = StateToInt(info.state);
+ if (state == kStateInvalid)
+ return false;
+
sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
"INSERT INTO downloads "
"(id, full_path, url, start_time, received_bytes, total_bytes, state, "
@@ -207,7 +262,7 @@ int64 DownloadDatabase::CreateDownload(
statement.BindInt64(3, info.start_time.ToTimeT());
statement.BindInt64(4, info.received_bytes);
statement.BindInt64(5, info.total_bytes);
- statement.BindInt(6, info.state);
+ statement.BindInt(6, state);
statement.BindInt64(7, info.end_time.ToTimeT());
statement.BindInt(8, info.opened ? 1 : 0);
@@ -245,9 +300,9 @@ bool DownloadDatabase::RemoveDownloadsBetween(base::Time delete_begin,
count.BindInt64(
1,
end_time ? end_time : std::numeric_limits<int64>::max());
- count.BindInt(2, DownloadItem::COMPLETE);
- count.BindInt(3, DownloadItem::CANCELLED);
- count.BindInt(4, DownloadItem::INTERRUPTED);
+ count.BindInt(2, kStateComplete);
+ count.BindInt(3, kStateCancelled);
+ count.BindInt(4, kStateInterrupted);
if (count.Step())
num_downloads_deleted = count.ColumnInt(0);
}
@@ -265,9 +320,9 @@ bool DownloadDatabase::RemoveDownloadsBetween(base::Time delete_begin,
statement.BindInt64(
1,
end_time ? end_time : std::numeric_limits<int64>::max());
- statement.BindInt(2, DownloadItem::COMPLETE);
- statement.BindInt(3, DownloadItem::CANCELLED);
- statement.BindInt(4, DownloadItem::INTERRUPTED);
+ statement.BindInt(2, kStateComplete);
+ statement.BindInt(3, kStateCancelled);
+ statement.BindInt(4, kStateInterrupted);
success = statement.Run();
}

Powered by Google App Engine
This is Rietveld 408576698