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 "chrome/browser/history/download_database.h" | 5 #include "chrome/browser/history/download_database.h" |
6 | 6 |
7 #include <limits> | 7 #include <limits> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
(...skipping 20 matching lines...) Expand all Loading... | |
31 "id INTEGER PRIMARY KEY," // SQLite-generated primary key. | 31 "id INTEGER PRIMARY KEY," // SQLite-generated primary key. |
32 "full_path LONGVARCHAR NOT NULL," // Location of the download on disk. | 32 "full_path LONGVARCHAR NOT NULL," // Location of the download on disk. |
33 "url LONGVARCHAR NOT NULL," // URL of the downloaded file. | 33 "url LONGVARCHAR NOT NULL," // URL of the downloaded file. |
34 "start_time INTEGER NOT NULL," // When the download was started. | 34 "start_time INTEGER NOT NULL," // When the download was started. |
35 "received_bytes INTEGER NOT NULL," // Total size downloaded. | 35 "received_bytes INTEGER NOT NULL," // Total size downloaded. |
36 "total_bytes INTEGER NOT NULL," // Total size of the download. | 36 "total_bytes INTEGER NOT NULL," // Total size of the download. |
37 "state INTEGER NOT NULL," // 1=complete, 2=cancelled, 4=interrupted | 37 "state INTEGER NOT NULL," // 1=complete, 2=cancelled, 4=interrupted |
38 "end_time INTEGER NOT NULL," // When the download completed. | 38 "end_time INTEGER NOT NULL," // When the download completed. |
39 "opened INTEGER NOT NULL)"; // 1 if it has ever been opened else 0 | 39 "opened INTEGER NOT NULL)"; // 1 if it has ever been opened else 0 |
40 | 40 |
41 // These constants and next two functions are used to allow | |
42 // DownloadItem::DownloadState to change without breaking the database schema. | |
43 // They guarantee that the values of the |state| field in the database are one | |
44 // of the values returned by StateToInt, and that the values of the |state| | |
45 // field of the DownloadPersistentStoreInfos returned by QueryDownloads() are | |
46 // one of the values returned by IntToState(). | |
47 static const int kStateInvalid = -1; | |
48 static const int kStateInProgress = 0; | |
49 static const int kStateComplete = 1; | |
50 static const int kStateCancelled = 2; | |
51 static const int kStateBug140687 = 3; | |
52 static const int kStateInterrupted = 4; | |
53 | |
54 int StateToInt(DownloadItem::DownloadState state) { | |
55 switch (state) { | |
56 case DownloadItem::IN_PROGRESS: return kStateInProgress; | |
57 case DownloadItem::COMPLETE: return kStateComplete; | |
58 case DownloadItem::CANCELLED: return kStateCancelled; | |
59 case DownloadItem::REMOVING: return kStateInvalid; | |
60 case DownloadItem::INTERRUPTED: return kStateInterrupted; | |
61 case DownloadItem::MAX_DOWNLOAD_STATE: return kStateInvalid; | |
62 default: return kStateInvalid; | |
63 } | |
64 } | |
65 | |
66 DownloadItem::DownloadState IntToState(int state) { | |
67 switch (state) { | |
68 case kStateInProgress: return DownloadItem::IN_PROGRESS; | |
69 case kStateComplete: return DownloadItem::COMPLETE; | |
70 case kStateCancelled: return DownloadItem::CANCELLED; | |
71 // We should not need kStateBug140687 here because MigrateDownloadState() | |
72 // is called in HistoryDatabase::Init(). | |
73 case kStateInterrupted: return DownloadItem::INTERRUPTED; | |
74 default: return DownloadItem::MAX_DOWNLOAD_STATE; | |
75 } | |
76 } | |
77 | |
41 #if defined(OS_POSIX) | 78 #if defined(OS_POSIX) |
42 | 79 |
43 // Binds/reads the given file path to the given column of the given statement. | 80 // Binds/reads the given file path to the given column of the given statement. |
44 void BindFilePath(sql::Statement& statement, const FilePath& path, int col) { | 81 void BindFilePath(sql::Statement& statement, const FilePath& path, int col) { |
45 statement.BindString(col, path.value()); | 82 statement.BindString(col, path.value()); |
46 } | 83 } |
47 FilePath ColumnFilePath(sql::Statement& statement, int col) { | 84 FilePath ColumnFilePath(sql::Statement& statement, int col) { |
48 return FilePath(statement.ColumnString(col)); | 85 return FilePath(statement.ColumnString(col)); |
49 } | 86 } |
50 | 87 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
85 } | 122 } |
86 } | 123 } |
87 | 124 |
88 bool DownloadDatabase::EnsureColumnExists( | 125 bool DownloadDatabase::EnsureColumnExists( |
89 const std::string& name, const std::string& type) { | 126 const std::string& name, const std::string& type) { |
90 std::string add_col = "ALTER TABLE downloads ADD COLUMN " + name + " " + type; | 127 std::string add_col = "ALTER TABLE downloads ADD COLUMN " + name + " " + type; |
91 return GetDB().DoesColumnExist("downloads", name.c_str()) || | 128 return GetDB().DoesColumnExist("downloads", name.c_str()) || |
92 GetDB().Execute(add_col.c_str()); | 129 GetDB().Execute(add_col.c_str()); |
93 } | 130 } |
94 | 131 |
132 bool DownloadDatabase::MigrateDownloadsState() { | |
133 sql::Statement statement(GetDB().GetUniqueStatement( | |
134 "UPDATE downloads SET state=? WHERE state=?")); | |
135 statement.BindInt(0, kStateInterrupted); | |
136 statement.BindInt(1, kStateBug140687); | |
137 return statement.Run(); | |
138 } | |
139 | |
95 bool DownloadDatabase::InitDownloadTable() { | 140 bool DownloadDatabase::InitDownloadTable() { |
96 CheckThread(); | 141 CheckThread(); |
97 GetMetaTable().GetValue(kNextDownloadId, &next_id_); | 142 GetMetaTable().GetValue(kNextDownloadId, &next_id_); |
98 if (GetDB().DoesTableExist("downloads")) { | 143 if (GetDB().DoesTableExist("downloads")) { |
99 return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && | 144 return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && |
100 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); | 145 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); |
101 } else { | 146 } else { |
102 return GetDB().Execute(kSchema); | 147 return GetDB().Execute(kSchema); |
103 } | 148 } |
104 } | 149 } |
(...skipping 18 matching lines...) Expand all Loading... | |
123 "ORDER BY start_time")); | 168 "ORDER BY start_time")); |
124 | 169 |
125 while (statement.Step()) { | 170 while (statement.Step()) { |
126 DownloadPersistentStoreInfo info; | 171 DownloadPersistentStoreInfo info; |
127 info.db_handle = statement.ColumnInt64(0); | 172 info.db_handle = statement.ColumnInt64(0); |
128 info.path = ColumnFilePath(statement, 1); | 173 info.path = ColumnFilePath(statement, 1); |
129 info.url = GURL(statement.ColumnString(2)); | 174 info.url = GURL(statement.ColumnString(2)); |
130 info.start_time = base::Time::FromTimeT(statement.ColumnInt64(3)); | 175 info.start_time = base::Time::FromTimeT(statement.ColumnInt64(3)); |
131 info.received_bytes = statement.ColumnInt64(4); | 176 info.received_bytes = statement.ColumnInt64(4); |
132 info.total_bytes = statement.ColumnInt64(5); | 177 info.total_bytes = statement.ColumnInt64(5); |
133 info.state = statement.ColumnInt(6); | 178 info.state = IntToState(statement.ColumnInt(6)); |
134 info.end_time = base::Time::FromTimeT(statement.ColumnInt64(7)); | 179 info.end_time = base::Time::FromTimeT(statement.ColumnInt64(7)); |
135 info.opened = statement.ColumnInt(8) != 0; | 180 info.opened = statement.ColumnInt(8) != 0; |
181 if (info.state == DownloadItem::MAX_DOWNLOAD_STATE) { | |
182 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.
| |
183 } | |
136 results->push_back(info); | 184 results->push_back(info); |
137 if (info.db_handle >= next_db_handle_) | 185 if (info.db_handle >= next_db_handle_) |
138 next_db_handle_ = info.db_handle + 1; | 186 next_db_handle_ = info.db_handle + 1; |
139 if (!db_handles.insert(info.db_handle).second) { | 187 if (!db_handles.insert(info.db_handle).second) { |
140 // info.db_handle was already in db_handles. The database is corrupt. | 188 // info.db_handle was already in db_handles. The database is corrupt. |
141 base::debug::Alias(&info.db_handle); | 189 base::debug::Alias(&info.db_handle); |
142 DCHECK(false); | 190 DCHECK(false); |
143 } | 191 } |
144 } | 192 } |
145 } | 193 } |
146 | 194 |
147 bool DownloadDatabase::UpdateDownload(const DownloadPersistentStoreInfo& data) { | 195 bool DownloadDatabase::UpdateDownload(const DownloadPersistentStoreInfo& data) { |
148 CheckThread(); | 196 CheckThread(); |
149 DCHECK(data.db_handle > 0); | 197 DCHECK(data.db_handle > 0); |
198 int state = StateToInt(data.state); | |
199 if (state == kStateInvalid) | |
200 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 :-} :-|.
| |
150 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 201 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
151 "UPDATE downloads " | 202 "UPDATE downloads " |
152 "SET received_bytes=?, state=?, end_time=?, opened=? WHERE id=?")); | 203 "SET received_bytes=?, state=?, end_time=?, opened=? WHERE id=?")); |
153 statement.BindInt64(0, data.received_bytes); | 204 statement.BindInt64(0, data.received_bytes); |
154 statement.BindInt(1, data.state); | 205 statement.BindInt(1, state); |
155 statement.BindInt64(2, data.end_time.ToTimeT()); | 206 statement.BindInt64(2, data.end_time.ToTimeT()); |
156 statement.BindInt(3, (data.opened ? 1 : 0)); | 207 statement.BindInt(3, (data.opened ? 1 : 0)); |
157 statement.BindInt64(4, data.db_handle); | 208 statement.BindInt64(4, data.db_handle); |
158 | 209 |
159 return statement.Run(); | 210 return statement.Run(); |
160 } | 211 } |
161 | 212 |
162 bool DownloadDatabase::UpdateDownloadPath(const FilePath& path, | 213 bool DownloadDatabase::UpdateDownloadPath(const FilePath& path, |
163 DownloadID db_handle) { | 214 DownloadID db_handle) { |
164 CheckThread(); | 215 CheckThread(); |
165 DCHECK(db_handle > 0); | 216 DCHECK(db_handle > 0); |
166 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 217 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
167 "UPDATE downloads SET full_path=? WHERE id=?")); | 218 "UPDATE downloads SET full_path=? WHERE id=?")); |
168 BindFilePath(statement, path, 0); | 219 BindFilePath(statement, path, 0); |
169 statement.BindInt64(1, db_handle); | 220 statement.BindInt64(1, db_handle); |
170 | 221 |
171 return statement.Run(); | 222 return statement.Run(); |
172 } | 223 } |
173 | 224 |
174 bool DownloadDatabase::CleanUpInProgressEntries() { | 225 bool DownloadDatabase::CleanUpInProgressEntries() { |
175 CheckThread(); | 226 CheckThread(); |
176 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 227 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
177 "UPDATE downloads SET state=? WHERE state=?")); | 228 "UPDATE downloads SET state=? WHERE state=?")); |
178 statement.BindInt(0, DownloadItem::CANCELLED); | 229 statement.BindInt(0, kStateCancelled); |
179 statement.BindInt(1, DownloadItem::IN_PROGRESS); | 230 statement.BindInt(1, kStateInProgress); |
180 | 231 |
181 return statement.Run(); | 232 return statement.Run(); |
182 } | 233 } |
183 | 234 |
184 int64 DownloadDatabase::CreateDownload( | 235 int64 DownloadDatabase::CreateDownload( |
185 const DownloadPersistentStoreInfo& info) { | 236 const DownloadPersistentStoreInfo& info) { |
186 CheckThread(); | 237 CheckThread(); |
187 | 238 |
188 if (next_db_handle_ == 0) { | 239 if (next_db_handle_ == 0) { |
189 // This is unlikely. All current known tests and users already call | 240 // This is unlikely. All current known tests and users already call |
190 // QueryDownloads() before CreateDownload(). | 241 // QueryDownloads() before CreateDownload(). |
191 std::vector<DownloadPersistentStoreInfo> results; | 242 std::vector<DownloadPersistentStoreInfo> results; |
192 QueryDownloads(&results); | 243 QueryDownloads(&results); |
193 CHECK_NE(0, next_db_handle_); | 244 CHECK_NE(0, next_db_handle_); |
194 } | 245 } |
195 | 246 |
247 int state = StateToInt(info.state); | |
248 if (state == kStateInvalid) | |
249 return false; | |
250 | |
196 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 251 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
197 "INSERT INTO downloads " | 252 "INSERT INTO downloads " |
198 "(id, full_path, url, start_time, received_bytes, total_bytes, state, " | 253 "(id, full_path, url, start_time, received_bytes, total_bytes, state, " |
199 "end_time, opened) " | 254 "end_time, opened) " |
200 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")); | 255 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")); |
201 | 256 |
202 int db_handle = next_db_handle_++; | 257 int db_handle = next_db_handle_++; |
203 | 258 |
204 statement.BindInt64(0, db_handle); | 259 statement.BindInt64(0, db_handle); |
205 BindFilePath(statement, info.path, 1); | 260 BindFilePath(statement, info.path, 1); |
206 statement.BindString(2, info.url.spec()); | 261 statement.BindString(2, info.url.spec()); |
207 statement.BindInt64(3, info.start_time.ToTimeT()); | 262 statement.BindInt64(3, info.start_time.ToTimeT()); |
208 statement.BindInt64(4, info.received_bytes); | 263 statement.BindInt64(4, info.received_bytes); |
209 statement.BindInt64(5, info.total_bytes); | 264 statement.BindInt64(5, info.total_bytes); |
210 statement.BindInt(6, info.state); | 265 statement.BindInt(6, state); |
211 statement.BindInt64(7, info.end_time.ToTimeT()); | 266 statement.BindInt64(7, info.end_time.ToTimeT()); |
212 statement.BindInt(8, info.opened ? 1 : 0); | 267 statement.BindInt(8, info.opened ? 1 : 0); |
213 | 268 |
214 if (statement.Run()) { | 269 if (statement.Run()) { |
215 // TODO(benjhayden) if(info.id>next_id_){setvalue;next_id_=info.id;} | 270 // TODO(benjhayden) if(info.id>next_id_){setvalue;next_id_=info.id;} |
216 GetMetaTable().SetValue(kNextDownloadId, ++next_id_); | 271 GetMetaTable().SetValue(kNextDownloadId, ++next_id_); |
217 | 272 |
218 return db_handle; | 273 return db_handle; |
219 } | 274 } |
220 return 0; | 275 return 0; |
(...skipping 17 matching lines...) Expand all Loading... | |
238 | 293 |
239 int num_downloads_deleted = -1; | 294 int num_downloads_deleted = -1; |
240 { | 295 { |
241 sql::Statement count(GetDB().GetCachedStatement(SQL_FROM_HERE, | 296 sql::Statement count(GetDB().GetCachedStatement(SQL_FROM_HERE, |
242 "SELECT count(*) FROM downloads WHERE start_time >= ? " | 297 "SELECT count(*) FROM downloads WHERE start_time >= ? " |
243 "AND start_time < ? AND (State = ? OR State = ? OR State = ?)")); | 298 "AND start_time < ? AND (State = ? OR State = ? OR State = ?)")); |
244 count.BindInt64(0, start_time); | 299 count.BindInt64(0, start_time); |
245 count.BindInt64( | 300 count.BindInt64( |
246 1, | 301 1, |
247 end_time ? end_time : std::numeric_limits<int64>::max()); | 302 end_time ? end_time : std::numeric_limits<int64>::max()); |
248 count.BindInt(2, DownloadItem::COMPLETE); | 303 count.BindInt(2, kStateComplete); |
249 count.BindInt(3, DownloadItem::CANCELLED); | 304 count.BindInt(3, kStateCancelled); |
250 count.BindInt(4, DownloadItem::INTERRUPTED); | 305 count.BindInt(4, kStateInterrupted); |
251 if (count.Step()) | 306 if (count.Step()) |
252 num_downloads_deleted = count.ColumnInt(0); | 307 num_downloads_deleted = count.ColumnInt(0); |
253 } | 308 } |
254 | 309 |
255 | 310 |
256 bool success = false; | 311 bool success = false; |
257 base::TimeTicks started_removing = base::TimeTicks::Now(); | 312 base::TimeTicks started_removing = base::TimeTicks::Now(); |
258 { | 313 { |
259 // This does not use an index. We currently aren't likely to have enough | 314 // This does not use an index. We currently aren't likely to have enough |
260 // downloads where an index by time will give us a lot of benefit. | 315 // downloads where an index by time will give us a lot of benefit. |
261 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 316 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
262 "DELETE FROM downloads WHERE start_time >= ? AND start_time < ? " | 317 "DELETE FROM downloads WHERE start_time >= ? AND start_time < ? " |
263 "AND (State = ? OR State = ? OR State = ?)")); | 318 "AND (State = ? OR State = ? OR State = ?)")); |
264 statement.BindInt64(0, start_time); | 319 statement.BindInt64(0, start_time); |
265 statement.BindInt64( | 320 statement.BindInt64( |
266 1, | 321 1, |
267 end_time ? end_time : std::numeric_limits<int64>::max()); | 322 end_time ? end_time : std::numeric_limits<int64>::max()); |
268 statement.BindInt(2, DownloadItem::COMPLETE); | 323 statement.BindInt(2, kStateComplete); |
269 statement.BindInt(3, DownloadItem::CANCELLED); | 324 statement.BindInt(3, kStateCancelled); |
270 statement.BindInt(4, DownloadItem::INTERRUPTED); | 325 statement.BindInt(4, kStateInterrupted); |
271 | 326 |
272 success = statement.Run(); | 327 success = statement.Run(); |
273 } | 328 } |
274 | 329 |
275 base::TimeTicks finished_removing = base::TimeTicks::Now(); | 330 base::TimeTicks finished_removing = base::TimeTicks::Now(); |
276 | 331 |
277 if (num_downloads_deleted >= 0) { | 332 if (num_downloads_deleted >= 0) { |
278 UMA_HISTOGRAM_COUNTS("Download.DatabaseRemoveDownloadsCount", | 333 UMA_HISTOGRAM_COUNTS("Download.DatabaseRemoveDownloadsCount", |
279 num_downloads_deleted); | 334 num_downloads_deleted); |
280 base::TimeDelta micros = (1000 * (finished_removing - started_removing)); | 335 base::TimeDelta micros = (1000 * (finished_removing - started_removing)); |
281 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTime", micros); | 336 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTime", micros); |
282 if (num_downloads_deleted > 0) { | 337 if (num_downloads_deleted > 0) { |
283 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTimePerRecord", | 338 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTimePerRecord", |
284 (1000 * micros) / num_downloads_deleted); | 339 (1000 * micros) / num_downloads_deleted); |
285 } | 340 } |
286 } | 341 } |
287 | 342 |
288 return success; | 343 return success; |
289 } | 344 } |
290 | 345 |
291 } // namespace history | 346 } // namespace history |
OLD | NEW |