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

Side by Side Diff: chrome/browser/history/download_database.cc

Issue 11363222: Persist download interrupt reason, both target and current paths, and url_chain. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix initialize download table logic. Created 8 years, 1 month 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/history/download_database.h ('k') | chrome/browser/history/history.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
11 #include "base/debug/alias.h" 11 #include "base/debug/alias.h"
12 #include "base/file_path.h" 12 #include "base/file_path.h"
13 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
14 #include "base/stl_util.h"
15 #include "base/stringprintf.h"
14 #include "base/time.h" 16 #include "base/time.h"
15 #include "base/utf_string_conversions.h" 17 #include "base/utf_string_conversions.h"
16 #include "build/build_config.h" 18 #include "build/build_config.h"
17 #include "content/public/browser/browser_thread.h" 19 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/download_item.h" 20 #include "content/public/browser/download_item.h"
19 #include "content/public/browser/download_persistent_store_info.h" 21 #include "content/public/browser/download_persistent_store_info.h"
22 #include "content/public/browser/download_interrupt_reasons.h"
20 #include "sql/statement.h" 23 #include "sql/statement.h"
21 24
22 using content::DownloadItem; 25 using content::DownloadItem;
23 using content::DownloadPersistentStoreInfo; 26 using content::DownloadPersistentStoreInfo;
24 27
25 namespace history { 28 namespace history {
26 29
27 namespace { 30 namespace {
28 31
29 static const char kSchema[] = 32 static const char kSchema[] =
30 "CREATE TABLE downloads (" 33 "CREATE TABLE downloads ("
31 "id INTEGER PRIMARY KEY," // SQLite-generated primary key. 34 "id INTEGER PRIMARY KEY," // Primary key.
32 "full_path LONGVARCHAR NOT NULL," // Location of the download on disk. 35 "target_path LONGVARCHAR NOT NULL," // Final location of the download on disk.
33 "url LONGVARCHAR NOT NULL," // URL of the downloaded file. 36 "current_path LONGVARCHAR NOT NULL," // Current location of the download
37 // on disk.
34 "start_time INTEGER NOT NULL," // When the download was started. 38 "start_time INTEGER NOT NULL," // When the download was started.
35 "received_bytes INTEGER NOT NULL," // Total size downloaded. 39 "received_bytes INTEGER NOT NULL," // Total size downloaded.
36 "total_bytes INTEGER NOT NULL," // Total size of the download. 40 "total_bytes INTEGER NOT NULL," // Total size of the download.
37 "state INTEGER NOT NULL," // 1=complete, 2=cancelled, 4=interrupted 41 "state INTEGER NOT NULL," // 1=complete, 4=interrupted
42 "interrupt_reason INTEGER NOT NULL,"// Reason the download was interrupted.
38 "end_time INTEGER NOT NULL," // When the download completed. 43 "end_time INTEGER NOT NULL," // When the download completed.
39 "opened INTEGER NOT NULL)"; // 1 if it has ever been opened else 0 44 "opened INTEGER NOT NULL)"; // 1 if it has ever been opened else 0
40 45
46 static const char kUrlChainSchema[] =
47 "CREATE TABLE downloads_url_chains ("
48 "id INTEGER NOT NULL," // downloads.id.
49 "chain_index INTEGER NOT NULL," // Index of url in chain
50 // 0 is initial target,
51 // MAX is target after redirects.
52 "url LONGVARCHAR NOT NULL, " // URL.
53 "PRIMARY KEY (id, chain_index) )";
54
41 // These constants and next two functions are used to allow 55 // These constants and next two functions are used to allow
42 // DownloadItem::DownloadState to change without breaking the database schema. 56 // DownloadItem::DownloadState to change without breaking the database schema.
43 // They guarantee that the values of the |state| field in the database are one 57 // 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| 58 // of the values returned by StateToInt, and that the values of the |state|
45 // field of the DownloadPersistentStoreInfos returned by QueryDownloads() are 59 // field of the DownloadPersistentStoreInfos returned by QueryDownloads() are
46 // one of the values returned by IntToState(). 60 // one of the values returned by IntToState().
47 static const int kStateInvalid = -1; 61 static const int kStateInvalid = -1;
48 static const int kStateInProgress = 0; 62 static const int kStateInProgress = 0;
49 static const int kStateComplete = 1; 63 static const int kStateComplete = 1;
50 static const int kStateCancelled = 2; 64 static const int kStateCancelled = 2;
51 static const int kStateBug140687 = 3; 65 static const int kStateBug140687 = 3;
52 static const int kStateInterrupted = 4; 66 static const int kStateInterrupted = 4;
53 67
54 int StateToInt(DownloadItem::DownloadState state) { 68 int StateToInt(DownloadItem::DownloadState state) {
55 switch (state) { 69 switch (state) {
56 case DownloadItem::IN_PROGRESS: return kStateInProgress; 70 case DownloadItem::IN_PROGRESS: return kStateInProgress;
57 case DownloadItem::COMPLETE: return kStateComplete; 71 case DownloadItem::COMPLETE: return kStateComplete;
58 case DownloadItem::CANCELLED: return kStateCancelled; 72 case DownloadItem::CANCELLED: return kStateCancelled;
59 case DownloadItem::INTERRUPTED: return kStateInterrupted; 73 case DownloadItem::INTERRUPTED: return kStateInterrupted;
60 case DownloadItem::MAX_DOWNLOAD_STATE: return kStateInvalid; 74 case DownloadItem::MAX_DOWNLOAD_STATE: return kStateInvalid;
61 default: return kStateInvalid; 75 default: return kStateInvalid;
62 } 76 }
63 } 77 }
64 78
65 DownloadItem::DownloadState IntToState(int state) { 79 DownloadItem::DownloadState IntToState(int state) {
66 switch (state) { 80 switch (state) {
67 case kStateInProgress: return DownloadItem::IN_PROGRESS; 81 case kStateInProgress: return DownloadItem::IN_PROGRESS;
68 case kStateComplete: return DownloadItem::COMPLETE; 82 case kStateComplete: return DownloadItem::COMPLETE;
69 case kStateCancelled: return DownloadItem::CANCELLED; 83 case kStateCancelled: return DownloadItem::CANCELLED;
70 // We should not need kStateBug140687 here because MigrateDownloadState() 84 // We should not need kStateBug140687 here because MigrateDownloadsState()
71 // is called in HistoryDatabase::Init(). 85 // is called in HistoryDatabase::Init().
72 case kStateInterrupted: return DownloadItem::INTERRUPTED; 86 case kStateInterrupted: return DownloadItem::INTERRUPTED;
73 default: return DownloadItem::MAX_DOWNLOAD_STATE; 87 default: return DownloadItem::MAX_DOWNLOAD_STATE;
74 } 88 }
75 } 89 }
76 90
77 #if defined(OS_POSIX) 91 #if defined(OS_POSIX)
78 92
79 // Binds/reads the given file path to the given column of the given statement. 93 // Binds/reads the given file path to the given column of the given statement.
80 void BindFilePath(sql::Statement& statement, const FilePath& path, int col) { 94 void BindFilePath(sql::Statement& statement, const FilePath& path, int col) {
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 } 143 }
130 144
131 bool DownloadDatabase::MigrateDownloadsState() { 145 bool DownloadDatabase::MigrateDownloadsState() {
132 sql::Statement statement(GetDB().GetUniqueStatement( 146 sql::Statement statement(GetDB().GetUniqueStatement(
133 "UPDATE downloads SET state=? WHERE state=?")); 147 "UPDATE downloads SET state=? WHERE state=?"));
134 statement.BindInt(0, kStateInterrupted); 148 statement.BindInt(0, kStateInterrupted);
135 statement.BindInt(1, kStateBug140687); 149 statement.BindInt(1, kStateBug140687);
136 return statement.Run(); 150 return statement.Run();
137 } 151 }
138 152
153 bool DownloadDatabase::MigrateDownloadsReasonAndPaths() {
154 // We need to rename the table and copy back from it because SQLite
155 // provides no way to rename or delete a column.
156 if (!GetDB().Execute("ALTER TABLE downloads RENAME TO downloads_tmp"))
157 return false;
158
159 // Recreate main table.
160 if (!GetDB().Execute(kSchema))
161 return false;
162
163 // Populate it. As we do so, we transform the time values from time_t
164 // (seconds since 1/1/1970 UTC), to our internal measure (microseconds
165 // since the Windows Epoch). Note that this is dependent in the
166 // implementation of base::Time and needs to change if that changes.
167 sql::Statement statement_populate(GetDB().GetUniqueStatement(
168 "INSERT INTO downloads "
169 "( id, target_path, current_path, start_time, received_bytes, total_bytes, "
170 " state, interrupt_reason, end_time, opened ) "
171 "SELECT id, full_path, full_path, "
172 " CASE start_time WHEN 0 THEN 0 ELSE "
173 " (start_time + 11644473600) * 1000000 END, "
174 " received_bytes, total_bytes, "
175 " state, ?, "
176 " CASE end_time WHEN 0 THEN 0 ELSE "
177 " (end_time + 11644473600) * 1000000 END, "
178 " opened "
179 "FROM downloads_tmp"));
180 statement_populate.BindInt(0, content::DOWNLOAD_INTERRUPT_REASON_NONE);
181 if (!statement_populate.Run())
182 return false;
183
184 // Create new chain table and populate it.
185 if (!GetDB().Execute(kUrlChainSchema))
186 return false;
187
188 if (!GetDB().Execute("INSERT INTO downloads_url_chains "
189 " ( id, chain_index, url) "
190 " SELECT id, 0, url from downloads_tmp"))
191 return false;
192
193 // Get rid of temporary table.
194 if (!GetDB().Execute("DROP TABLE downloads_tmp"))
195 return false;
196
197 return true;
198 }
199
139 bool DownloadDatabase::InitDownloadTable() { 200 bool DownloadDatabase::InitDownloadTable() {
140 CheckThread(); 201 CheckThread();
141 GetMetaTable().GetValue(kNextDownloadId, &next_id_); 202 GetMetaTable().GetValue(kNextDownloadId, &next_id_);
142 if (GetDB().DoesTableExist("downloads")) { 203 if (GetDB().DoesTableExist("downloads")) {
143 return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && 204 return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") &&
144 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); 205 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0");
145 } else { 206 } else {
146 return GetDB().Execute(kSchema); 207 // If the "downloads" table doesn't exist, the downloads_url_chain
208 // table better not.
209 return (!GetDB().DoesTableExist("downloads") &&
benjhayden 2012/11/15 21:57:54 I don't think this is quite right. I think a more
210 GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema));
147 } 211 }
148 } 212 }
149 213
150 bool DownloadDatabase::DropDownloadTable() { 214 bool DownloadDatabase::DropDownloadTable() {
151 CheckThread(); 215 CheckThread();
152 return GetDB().Execute("DROP TABLE downloads"); 216 return GetDB().Execute("DROP TABLE downloads");
153 } 217 }
154 218
155 void DownloadDatabase::QueryDownloads( 219 void DownloadDatabase::QueryDownloads(
156 std::vector<DownloadPersistentStoreInfo>* results) { 220 std::vector<DownloadPersistentStoreInfo>* results) {
157 CheckThread(); 221 CheckThread();
158 results->clear(); 222 results->clear();
159 if (next_db_handle_ < 1) 223 if (next_db_handle_ < 1)
160 next_db_handle_ = 1; 224 next_db_handle_ = 1;
161 std::set<DownloadID> db_handles; 225 std::set<DownloadID> db_handles;
162 226
163 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 227 std::map<DownloadID, DownloadPersistentStoreInfo*> info_map;
164 "SELECT id, full_path, url, start_time, received_bytes, " 228
165 "total_bytes, state, end_time, opened " 229 sql::Statement statement_main(GetDB().GetCachedStatement(SQL_FROM_HERE,
230 "SELECT id, target_path, current_path, start_time, received_bytes, "
231 "total_bytes, state, interrupt_reason, end_time, opened "
166 "FROM downloads " 232 "FROM downloads "
167 "ORDER BY start_time")); 233 "ORDER BY start_time"));
168 234
169 while (statement.Step()) { 235 while (statement_main.Step()) {
170 DownloadPersistentStoreInfo info; 236 DownloadPersistentStoreInfo* info(new DownloadPersistentStoreInfo);
171 info.db_handle = statement.ColumnInt64(0); 237 int column = 0;
172 info.path = ColumnFilePath(statement, 1); 238
173 info.url = GURL(statement.ColumnString(2)); 239 info->db_handle = statement_main.ColumnInt64(column++);
174 info.start_time = base::Time::FromTimeT(statement.ColumnInt64(3)); 240 info->target_path = ColumnFilePath(statement_main, column++);
175 info.received_bytes = statement.ColumnInt64(4); 241 info->current_path = ColumnFilePath(statement_main, column++);
176 info.total_bytes = statement.ColumnInt64(5); 242 info->start_time = base::Time::FromInternalValue(
177 int state = statement.ColumnInt(6); 243 statement_main.ColumnInt64(column++));
178 info.state = IntToState(state); 244 info->received_bytes = statement_main.ColumnInt64(column++);
179 info.end_time = base::Time::FromTimeT(statement.ColumnInt64(7)); 245 info->total_bytes = statement_main.ColumnInt64(column++);
180 info.opened = statement.ColumnInt(8) != 0; 246 int state = statement_main.ColumnInt(column++);
181 if (info.db_handle >= next_db_handle_) 247 info->state = IntToState(state);
182 next_db_handle_ = info.db_handle + 1; 248 info->interrupt_reason = static_cast<content::DownloadInterruptReason>(
183 if (!db_handles.insert(info.db_handle).second) { 249 statement_main.ColumnInt(column++));
184 // info.db_handle was already in db_handles. The database is corrupt. 250 info->end_time = base::Time::FromInternalValue(
185 base::debug::Alias(&info.db_handle); 251 statement_main.ColumnInt64(column++));
252 info->opened = statement_main.ColumnInt(column++) != 0;
253 if (info->db_handle >= next_db_handle_)
254 next_db_handle_ = info->db_handle + 1;
255 if (!db_handles.insert(info->db_handle).second) {
256 // info->db_handle was already in db_handles. The database is corrupt.
257 base::debug::Alias(&info->db_handle);
186 DCHECK(false); 258 DCHECK(false);
187 } 259 }
188 if (info.state == DownloadItem::MAX_DOWNLOAD_STATE) { 260 if (info->state == DownloadItem::MAX_DOWNLOAD_STATE) {
189 UMA_HISTOGRAM_COUNTS("Download.DatabaseInvalidState", state); 261 UMA_HISTOGRAM_COUNTS("Download.DatabaseInvalidState", state);
190 continue; 262 continue;
191 } 263 }
192 results->push_back(info); 264 DCHECK(!ContainsKey(info_map, info->db_handle));
265 info_map[info->db_handle] = info;
266 }
267
268 sql::Statement statement_chain(GetDB().GetCachedStatement(
269 SQL_FROM_HERE,
270 "SELECT id, chain_index, url FROM downloads_url_chains "
271 "ORDER BY id, chain_index"));
272
273 while (statement_chain.Step()) {
274 int64 db_handle = statement_chain.ColumnInt64(0);
275 int chain_index = statement_chain.ColumnInt(1);
276
277 // Note that these DCHECKs may trip as a result of corrupted databases.
278 // We have them because in debug builds the chances are higher there's
279 // an actual bug than that the database is corrupt, but we handle the
280 // DB corruption case in production code.
281
282 // Confirm the handle has already been seen--if it hasn't, discard the
283 // record.
284 DCHECK(ContainsKey(info_map, db_handle));
285 if (!ContainsKey(info_map, db_handle))
286 continue;
287
288 // Confirm all previous URLs in the chain have already been seen;
289 // if not, fill in with null or discard record.
290 int current_chain_size = info_map[db_handle]->url_chain.size();
291 std::vector<GURL>* url_chain(&info_map[db_handle]->url_chain);
292 DCHECK_EQ(chain_index, current_chain_size);
293 while (current_chain_size < chain_index) {
294 url_chain->push_back(GURL());
295 current_chain_size++;
296 }
297 if (current_chain_size > chain_index)
298 continue;
299
300 // Save the record.
301 url_chain->push_back(GURL(statement_chain.ColumnString(2)));
302 }
303
304 for (std::map<DownloadID, DownloadPersistentStoreInfo*>::iterator
305 it = info_map.begin(); it != info_map.end(); ++it) {
306 results->push_back(*it->second);
307 delete it->second;
308 it->second = NULL;
193 } 309 }
194 } 310 }
195 311
196 bool DownloadDatabase::UpdateDownload(const DownloadPersistentStoreInfo& data) { 312 bool DownloadDatabase::UpdateDownload(const DownloadPersistentStoreInfo& data) {
197 CheckThread(); 313 CheckThread();
198 DCHECK(data.db_handle > 0); 314 DCHECK(data.db_handle > 0);
199 int state = StateToInt(data.state); 315 int state = StateToInt(data.state);
200 if (state == kStateInvalid) { 316 if (state == kStateInvalid) {
201 // TODO(benjhayden) [D]CHECK instead. 317 // TODO(benjhayden) [D]CHECK instead.
202 return false; 318 return false;
203 } 319 }
204 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 320 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
205 "UPDATE downloads " 321 "UPDATE downloads "
206 "SET received_bytes=?, state=?, end_time=?, opened=? WHERE id=?")); 322 "SET received_bytes=?, state=?, interrupt_reason=?, end_time=?, "
207 statement.BindInt64(0, data.received_bytes); 323 " opened=? WHERE id=?"));
208 statement.BindInt(1, state); 324
209 statement.BindInt64(2, data.end_time.ToTimeT()); 325 int column = 0;
210 statement.BindInt(3, (data.opened ? 1 : 0)); 326 statement.BindInt64(column++, data.received_bytes);
211 statement.BindInt64(4, data.db_handle); 327 statement.BindInt(column++, state);
328 statement.BindInt(column++, data.interrupt_reason);
329 statement.BindInt64(column++, data.end_time.ToInternalValue());
330 statement.BindInt(column++, (data.opened ? 1 : 0));
331 statement.BindInt64(column++, data.db_handle);
212 332
213 return statement.Run(); 333 return statement.Run();
214 } 334 }
215 335
216 bool DownloadDatabase::UpdateDownloadPath(const FilePath& path, 336 bool DownloadDatabase::UpdateDownloadPath(
217 DownloadID db_handle) { 337 const FilePath& target_path, const FilePath& current_path,
338 DownloadID db_handle) {
218 CheckThread(); 339 CheckThread();
219 DCHECK(db_handle > 0); 340 DCHECK(db_handle > 0);
220 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 341 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
221 "UPDATE downloads SET full_path=? WHERE id=?")); 342 "UPDATE downloads SET target_path=?, current_path=? WHERE id=?"));
222 BindFilePath(statement, path, 0); 343 BindFilePath(statement, target_path, 0);
223 statement.BindInt64(1, db_handle); 344 BindFilePath(statement, current_path, 1);
345 statement.BindInt64(2, db_handle);
224 346
225 return statement.Run(); 347 return statement.Run();
226 } 348 }
227 349
228 bool DownloadDatabase::CleanUpInProgressEntries() { 350 bool DownloadDatabase::CleanUpInProgressEntries() {
229 CheckThread(); 351 CheckThread();
230 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 352 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
231 "UPDATE downloads SET state=? WHERE state=?")); 353 "UPDATE downloads SET state=? WHERE state=?"));
232 statement.BindInt(0, kStateCancelled); 354 statement.BindInt(0, kStateCancelled);
233 statement.BindInt(1, kStateInProgress); 355 statement.BindInt(1, kStateInProgress);
(...skipping 10 matching lines...) Expand all
244 // QueryDownloads() before CreateDownload(). 366 // QueryDownloads() before CreateDownload().
245 std::vector<DownloadPersistentStoreInfo> results; 367 std::vector<DownloadPersistentStoreInfo> results;
246 QueryDownloads(&results); 368 QueryDownloads(&results);
247 CHECK_NE(0, next_db_handle_); 369 CHECK_NE(0, next_db_handle_);
248 } 370 }
249 371
250 int state = StateToInt(info.state); 372 int state = StateToInt(info.state);
251 if (state == kStateInvalid) 373 if (state == kStateInvalid)
252 return false; 374 return false;
253 375
254 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
255 "INSERT INTO downloads "
256 "(id, full_path, url, start_time, received_bytes, total_bytes, state, "
257 "end_time, opened) "
258 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"));
259
260 int db_handle = next_db_handle_++; 376 int db_handle = next_db_handle_++;
261 377
262 statement.BindInt64(0, db_handle); 378 {
263 BindFilePath(statement, info.path, 1); 379 sql::Statement statement_insert(GetDB().GetCachedStatement(
264 statement.BindString(2, info.url.spec()); 380 SQL_FROM_HERE,
265 statement.BindInt64(3, info.start_time.ToTimeT()); 381 "INSERT INTO downloads "
266 statement.BindInt64(4, info.received_bytes); 382 "(id, target_path, current_path, start_time, "
267 statement.BindInt64(5, info.total_bytes); 383 " received_bytes, total_bytes, state, interrupt_reason, "
268 statement.BindInt(6, state); 384 " end_time, opened) "
269 statement.BindInt64(7, info.end_time.ToTimeT()); 385 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
270 statement.BindInt(8, info.opened ? 1 : 0);
271 386
272 if (statement.Run()) { 387 int column = 0;
273 // TODO(benjhayden) if(info.id>next_id_){setvalue;next_id_=info.id;} 388 statement_insert.BindInt64(column++, db_handle);
274 GetMetaTable().SetValue(kNextDownloadId, ++next_id_); 389 BindFilePath(statement_insert, info.target_path, column++);
390 BindFilePath(statement_insert, info.current_path, column++);
391 statement_insert.BindInt64(column++, info.start_time.ToInternalValue());
392 statement_insert.BindInt64(column++, info.received_bytes);
393 statement_insert.BindInt64(column++, info.total_bytes);
394 statement_insert.BindInt(column++, state);
395 statement_insert.BindInt(column++, content::DOWNLOAD_INTERRUPT_REASON_NONE);
396 statement_insert.BindInt64(column++, info.end_time.ToInternalValue());
397 statement_insert.BindInt(column++, info.opened ? 1 : 0);
398 if (!statement_insert.Run()) {
399 LOG(WARNING) << "Main insertion for download create failed.";
400 return 0;
401 }
402 }
275 403
276 return db_handle; 404 sql::Statement statement_insert_chain(
405 GetDB().GetCachedStatement(SQL_FROM_HERE,
406 "INSERT INTO downloads_url_chains "
407 "(id, chain_index, url) "
408 "VALUES (?, ?, ?)"));
409 for (size_t i = 0; i < info.url_chain.size(); ++i) {
410 statement_insert_chain.BindInt64(0, db_handle);
411 statement_insert_chain.BindInt(1, i);
412 statement_insert_chain.BindString(2, info.url_chain[i].spec());
413 if (!statement_insert_chain.Run()) {
414 LOG(WARNING) << "Url insertion for download create failed.";
415 return 0;
416 }
417 statement_insert_chain.Reset(true);
277 } 418 }
278 return 0; 419
420 // TODO(benjhayden) if(info.id>next_id_){setvalue;next_id_=info.id;}
421 GetMetaTable().SetValue(kNextDownloadId, ++next_id_);
422
423 return db_handle;
279 } 424 }
280 425
281 void DownloadDatabase::RemoveDownload(DownloadID db_handle) { 426 void DownloadDatabase::RemoveDownload(DownloadID db_handle) {
282 CheckThread(); 427 CheckThread();
283 428
284 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 429 sql::Statement downloads_statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
285 "DELETE FROM downloads WHERE id=?")); 430 "DELETE FROM downloads WHERE id=?"));
286 statement.BindInt64(0, db_handle); 431 downloads_statement.BindInt64(0, db_handle);
432 downloads_statement.Run();
287 433
288 statement.Run(); 434 sql::Statement urlchain_statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
435 "DELETE FROM downloads_url_chains WHERE id=?"));
436 urlchain_statement.BindInt64(0, db_handle);
437 urlchain_statement.Run();
289 } 438 }
290 439
291 bool DownloadDatabase::RemoveDownloadsBetween(base::Time delete_begin, 440 bool DownloadDatabase::RemoveDownloadsBetween(base::Time delete_begin,
292 base::Time delete_end) { 441 base::Time delete_end) {
293 CheckThread(); 442 CheckThread();
294 time_t start_time = delete_begin.ToTimeT(); 443 time_t start_time = delete_begin.ToInternalValue();
295 time_t end_time = delete_end.ToTimeT(); 444 time_t end_time = delete_end.ToInternalValue();
296 445
297 int num_downloads_deleted = -1; 446 int num_downloads_deleted = -1;
298 { 447 {
299 sql::Statement count(GetDB().GetCachedStatement(SQL_FROM_HERE, 448 sql::Statement count(GetDB().GetCachedStatement(SQL_FROM_HERE,
300 "SELECT count(*) FROM downloads WHERE start_time >= ? " 449 "SELECT count(*) FROM downloads WHERE start_time >= ? "
301 "AND start_time < ? AND (State = ? OR State = ? OR State = ?)")); 450 "AND start_time < ? AND (State = ? OR State = ? OR State = ?)"));
302 count.BindInt64(0, start_time); 451 count.BindInt64(0, start_time);
303 count.BindInt64( 452 count.BindInt64(
304 1, 453 1,
305 end_time ? end_time : std::numeric_limits<int64>::max()); 454 end_time ? end_time : std::numeric_limits<int64>::max());
306 count.BindInt(2, kStateComplete); 455 count.BindInt(2, kStateComplete);
307 count.BindInt(3, kStateCancelled); 456 count.BindInt(3, kStateCancelled);
308 count.BindInt(4, kStateInterrupted); 457 count.BindInt(4, kStateInterrupted);
309 if (count.Step()) 458 if (count.Step())
310 num_downloads_deleted = count.ColumnInt(0); 459 num_downloads_deleted = count.ColumnInt(0);
311 } 460 }
312 461
313
314 bool success = false; 462 bool success = false;
315 base::TimeTicks started_removing = base::TimeTicks::Now(); 463 base::TimeTicks started_removing = base::TimeTicks::Now();
316 { 464 {
317 // This does not use an index. We currently aren't likely to have enough 465 // This does not use an index. We currently aren't likely to have enough
318 // downloads where an index by time will give us a lot of benefit. 466 // downloads where an index by time will give us a lot of benefit.
319 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 467 const char* deletion_conditional =
320 "DELETE FROM downloads WHERE start_time >= ? AND start_time < ? " 468 "WHERE start_time >= ? AND start_time < ? AND "
321 "AND (State = ? OR State = ? OR State = ?)")); 469 " (State = ? OR STATE = ? OR State = ?)";
322 statement.BindInt64(0, start_time); 470
323 statement.BindInt64( 471 sql::Statement statement_url_chains(GetDB().GetCachedStatement(
472 SQL_FROM_HERE,
473 base::StringPrintf("DELETE FROM downloads_url_chains WHERE id IN "
474 " (SELECT id FROM downloads %s)",
475 deletion_conditional).c_str()));
476 statement_url_chains.BindInt64(0, start_time);
477 statement_url_chains.BindInt64(
324 1, 478 1,
325 end_time ? end_time : std::numeric_limits<int64>::max()); 479 end_time ? end_time : std::numeric_limits<int64>::max());
326 statement.BindInt(2, kStateComplete); 480 statement_url_chains.BindInt(2, kStateComplete);
327 statement.BindInt(3, kStateCancelled); 481 statement_url_chains.BindInt(3, kStateCancelled);
328 statement.BindInt(4, kStateInterrupted); 482 statement_url_chains.BindInt(4, kStateInterrupted);
483 success = statement_url_chains.Run();
329 484
330 success = statement.Run(); 485 sql::Statement statement_downloads(GetDB().GetCachedStatement(
486 SQL_FROM_HERE, base::StringPrintf("DELETE FROM downloads %s",
487 deletion_conditional).c_str()));
488 statement_downloads.BindInt64(0, start_time);
489 statement_downloads.BindInt64(
490 1,
491 end_time ? end_time : std::numeric_limits<int64>::max());
492 statement_downloads.BindInt(2, kStateComplete);
493 statement_downloads.BindInt(3, kStateCancelled);
494 statement_downloads.BindInt(4, kStateInterrupted);
495
496 success = statement_downloads.Run() && success;
331 } 497 }
332 498
333 base::TimeTicks finished_removing = base::TimeTicks::Now(); 499 base::TimeTicks finished_removing = base::TimeTicks::Now();
334 500
335 if (num_downloads_deleted >= 0) { 501 if (num_downloads_deleted >= 0) {
336 UMA_HISTOGRAM_COUNTS("Download.DatabaseRemoveDownloadsCount", 502 UMA_HISTOGRAM_COUNTS("Download.DatabaseRemoveDownloadsCount",
337 num_downloads_deleted); 503 num_downloads_deleted);
338 base::TimeDelta micros = (1000 * (finished_removing - started_removing)); 504 base::TimeDelta micros = (1000 * (finished_removing - started_removing));
339 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTime", micros); 505 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTime", micros);
340 if (num_downloads_deleted > 0) { 506 if (num_downloads_deleted > 0) {
341 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTimePerRecord", 507 UMA_HISTOGRAM_TIMES("Download.DatabaseRemoveDownloadsTimePerRecord",
342 (1000 * micros) / num_downloads_deleted); 508 (1000 * micros) / num_downloads_deleted);
343 } 509 }
344 } 510 }
345 511
346 return success; 512 return success;
347 } 513 }
348 514
349 } // namespace history 515 } // namespace history
OLDNEW
« no previous file with comments | « chrome/browser/history/download_database.h ('k') | chrome/browser/history/history.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698