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

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: Incorporated Ben's comments. Created 8 years 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
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/memory/scoped_ptr.h"
13 #include "base/metrics/histogram.h" 14 #include "base/metrics/histogram.h"
15 #include "base/stl_util.h"
16 #include "base/stringprintf.h"
14 #include "base/time.h" 17 #include "base/time.h"
15 #include "base/utf_string_conversions.h" 18 #include "base/utf_string_conversions.h"
16 #include "build/build_config.h" 19 #include "build/build_config.h"
17 #include "chrome/browser/history/download_row.h" 20 #include "chrome/browser/history/download_row.h"
21 #include "chrome/browser/history/history_types.h"
18 #include "content/public/browser/browser_thread.h" 22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/download_interrupt_reasons.h"
19 #include "content/public/browser/download_item.h" 24 #include "content/public/browser/download_item.h"
20 #include "sql/statement.h" 25 #include "sql/statement.h"
21 26
22 using content::DownloadItem; 27 using content::DownloadItem;
23 28
24 namespace history { 29 namespace history {
25 30
26 // static 31 // static
27 const int64 DownloadDatabase::kUninitializedHandle = -1; 32 const int64 DownloadDatabase::kUninitializedHandle = -1;
28 33
29 namespace { 34 namespace {
30 35
31 static const char kSchema[] = 36 static const char kSchema[] =
32 "CREATE TABLE downloads (" 37 "CREATE TABLE downloads ("
33 "id INTEGER PRIMARY KEY," // SQLite-generated primary key. 38 "id INTEGER PRIMARY KEY," // Primary key.
34 "full_path LONGVARCHAR NOT NULL," // Location of the download on disk. 39 "current_path LONGVARCHAR NOT NULL," // Current location of the download
35 "url LONGVARCHAR NOT NULL," // URL of the downloaded file. 40 // on disk.
41 "target_path LONGVARCHAR NOT NULL," // Final location of the download on disk.
36 "start_time INTEGER NOT NULL," // When the download was started. 42 "start_time INTEGER NOT NULL," // When the download was started.
37 "received_bytes INTEGER NOT NULL," // Total size downloaded. 43 "received_bytes INTEGER NOT NULL," // Total size downloaded.
38 "total_bytes INTEGER NOT NULL," // Total size of the download. 44 "total_bytes INTEGER NOT NULL," // Total size of the download.
39 "state INTEGER NOT NULL," // 1=complete, 2=cancelled, 4=interrupted 45 "state INTEGER NOT NULL," // 1=complete, 4=interrupted
46 "interrupt_reason INTEGER NOT NULL,"// Reason the download was interrupted.
40 "end_time INTEGER NOT NULL," // When the download completed. 47 "end_time INTEGER NOT NULL," // When the download completed.
41 "opened INTEGER NOT NULL)"; // 1 if it has ever been opened else 0 48 "opened INTEGER NOT NULL)"; // 1 if it has ever been opened else 0
42 49
50 static const char kUrlChainSchema[] =
51 "CREATE TABLE downloads_url_chains ("
52 "id INTEGER NOT NULL," // downloads.id.
53 "chain_index INTEGER NOT NULL," // Index of url in chain
54 // 0 is initial target,
55 // MAX is target after redirects.
56 "url LONGVARCHAR NOT NULL, " // URL.
57 "PRIMARY KEY (id, chain_index) )";
58
43 // These constants and next two functions are used to allow 59 // These constants and next two functions are used to allow
44 // DownloadItem::DownloadState to change without breaking the database schema. 60 // DownloadItem::DownloadState to change without breaking the database schema.
45 // They guarantee that the values of the |state| field in the database are one 61 // They guarantee that the values of the |state| field in the database are one
46 // of the values returned by StateToInt, and that the values of the |state| 62 // of the values returned by StateToInt, and that the values of the |state|
47 // field of the DownloadRows returned by QueryDownloads() are one of the values 63 // field of the DownloadRows returned by QueryDownloads() are one of the values
48 // returned by IntToState(). 64 // returned by IntToState().
49 static const int kStateInvalid = -1; 65 static const int kStateInvalid = -1;
50 static const int kStateInProgress = 0; 66 static const int kStateInProgress = 0;
51 static const int kStateComplete = 1; 67 static const int kStateComplete = 1;
52 static const int kStateCancelled = 2; 68 static const int kStateCancelled = 2;
53 static const int kStateBug140687 = 3; 69 static const int kStateBug140687 = 3;
54 static const int kStateInterrupted = 4; 70 static const int kStateInterrupted = 4;
55 71
56 int StateToInt(DownloadItem::DownloadState state) { 72 int StateToInt(DownloadItem::DownloadState state) {
57 switch (state) { 73 switch (state) {
58 case DownloadItem::IN_PROGRESS: return kStateInProgress; 74 case DownloadItem::IN_PROGRESS: return kStateInProgress;
59 case DownloadItem::COMPLETE: return kStateComplete; 75 case DownloadItem::COMPLETE: return kStateComplete;
60 case DownloadItem::CANCELLED: return kStateCancelled; 76 case DownloadItem::CANCELLED: return kStateCancelled;
61 case DownloadItem::INTERRUPTED: return kStateInterrupted; 77 case DownloadItem::INTERRUPTED: return kStateInterrupted;
62 case DownloadItem::MAX_DOWNLOAD_STATE: return kStateInvalid; 78 case DownloadItem::MAX_DOWNLOAD_STATE: return kStateInvalid;
63 default: return kStateInvalid; 79 default: return kStateInvalid;
64 } 80 }
65 } 81 }
66 82
67 DownloadItem::DownloadState IntToState(int state) { 83 DownloadItem::DownloadState IntToState(int state) {
68 switch (state) { 84 switch (state) {
69 case kStateInProgress: return DownloadItem::IN_PROGRESS; 85 case kStateInProgress: return DownloadItem::IN_PROGRESS;
70 case kStateComplete: return DownloadItem::COMPLETE; 86 case kStateComplete: return DownloadItem::COMPLETE;
71 case kStateCancelled: return DownloadItem::CANCELLED; 87 case kStateCancelled: return DownloadItem::CANCELLED;
72 // We should not need kStateBug140687 here because MigrateDownloadState() 88 // We should not need kStateBug140687 here because MigrateDownloadsState()
73 // is called in HistoryDatabase::Init(). 89 // is called in HistoryDatabase::Init().
74 case kStateInterrupted: return DownloadItem::INTERRUPTED; 90 case kStateInterrupted: return DownloadItem::INTERRUPTED;
75 default: return DownloadItem::MAX_DOWNLOAD_STATE; 91 default: return DownloadItem::MAX_DOWNLOAD_STATE;
76 } 92 }
77 } 93 }
78 94
79 #if defined(OS_POSIX) 95 #if defined(OS_POSIX)
80 96
81 // Binds/reads the given file path to the given column of the given statement. 97 // Binds/reads the given file path to the given column of the given statement.
82 void BindFilePath(sql::Statement& statement, const FilePath& path, int col) { 98 void BindFilePath(sql::Statement& statement, const FilePath& path, int col) {
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 } 138 }
123 139
124 bool DownloadDatabase::MigrateDownloadsState() { 140 bool DownloadDatabase::MigrateDownloadsState() {
125 sql::Statement statement(GetDB().GetUniqueStatement( 141 sql::Statement statement(GetDB().GetUniqueStatement(
126 "UPDATE downloads SET state=? WHERE state=?")); 142 "UPDATE downloads SET state=? WHERE state=?"));
127 statement.BindInt(0, kStateInterrupted); 143 statement.BindInt(0, kStateInterrupted);
128 statement.BindInt(1, kStateBug140687); 144 statement.BindInt(1, kStateBug140687);
129 return statement.Run(); 145 return statement.Run();
130 } 146 }
131 147
148 bool DownloadDatabase::MigrateDownloadsReasonAndPaths() {
149 // We need to rename the table and copy back from it because SQLite
150 // provides no way to rename or delete a column.
151 if (!GetDB().Execute("ALTER TABLE downloads RENAME TO downloads_tmp"))
152 return false;
153
154 // Recreate main table.
155 if (!GetDB().Execute(kSchema))
156 return false;
157
158 // Populate it. As we do so, we transform the time values from time_t
159 // (seconds since 1/1/1970 UTC), to our internal measure (microseconds
160 // since the Windows Epoch). Note that this is dependent on the
161 // internal representation of base::Time and needs to change if that changes.
162 sql::Statement statement_populate(GetDB().GetUniqueStatement(
163 "INSERT INTO downloads "
164 "( id, current_path, target_path, start_time, received_bytes, total_bytes, "
165 " state, interrupt_reason, end_time, opened ) "
166 "SELECT id, full_path, full_path, "
167 " CASE start_time WHEN 0 THEN 0 ELSE "
168 " (start_time + 11644473600) * 1000000 END, "
169 " received_bytes, total_bytes, "
170 " state, ?, "
171 " CASE end_time WHEN 0 THEN 0 ELSE "
172 " (end_time + 11644473600) * 1000000 END, "
173 " opened "
174 "FROM downloads_tmp"));
175 statement_populate.BindInt(0, content::DOWNLOAD_INTERRUPT_REASON_NONE);
176 if (!statement_populate.Run())
177 return false;
178
179 // Create new chain table and populate it.
180 if (!GetDB().Execute(kUrlChainSchema))
181 return false;
182
183 if (!GetDB().Execute("INSERT INTO downloads_url_chains "
184 " ( id, chain_index, url) "
185 " SELECT id, 0, url from downloads_tmp"))
186 return false;
187
188 // Get rid of temporary table.
189 if (!GetDB().Execute("DROP TABLE downloads_tmp"))
190 return false;
191
192 return true;
193 }
194
132 bool DownloadDatabase::InitDownloadTable() { 195 bool DownloadDatabase::InitDownloadTable() {
133 GetMetaTable().GetValue(kNextDownloadId, &next_id_); 196 GetMetaTable().GetValue(kNextDownloadId, &next_id_);
134 if (GetDB().DoesTableExist("downloads")) { 197 if (GetDB().DoesTableExist("downloads")) {
135 return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") && 198 return EnsureColumnExists("end_time", "INTEGER NOT NULL DEFAULT 0") &&
136 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0"); 199 EnsureColumnExists("opened", "INTEGER NOT NULL DEFAULT 0");
137 } else { 200 } else {
138 return GetDB().Execute(kSchema); 201 // If the "downloads" table doesn't exist, the downloads_url_chain
202 // table better not.
203 return (!GetDB().DoesTableExist("downloads_url_chain") &&
204 GetDB().Execute(kSchema) && GetDB().Execute(kUrlChainSchema));
139 } 205 }
140 } 206 }
141 207
142 bool DownloadDatabase::DropDownloadTable() { 208 bool DownloadDatabase::DropDownloadTable() {
143 return GetDB().Execute("DROP TABLE downloads"); 209 return GetDB().Execute("DROP TABLE downloads");
144 } 210 }
145 211
146 void DownloadDatabase::QueryDownloads( 212 void DownloadDatabase::QueryDownloads(
147 std::vector<DownloadRow>* results) { 213 std::vector<DownloadRow>* results) {
148 DCHECK(results); 214 DCHECK(results);
benjhayden 2012/12/11 19:48:13 Is this necessary? Doesn't the dereference in the
Randy Smith (Not in Mondays) 2012/12/12 19:19:17 Done.
149 results->clear(); 215 results->clear();
150 if (next_db_handle_ < 1) 216 if (next_db_handle_ < 1)
151 next_db_handle_ = 1; 217 next_db_handle_ = 1;
152 std::set<int64> db_handles; 218 std::set<int64> db_handles;
153 219
154 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 220 std::map<DownloadID, DownloadRow*> info_map;
155 "SELECT id, full_path, url, start_time, received_bytes, " 221
156 "total_bytes, state, end_time, opened " 222 sql::Statement statement_main(GetDB().GetCachedStatement(SQL_FROM_HERE,
223 "SELECT id, current_path, target_path, start_time, received_bytes, "
224 "total_bytes, state, interrupt_reason, end_time, opened "
157 "FROM downloads " 225 "FROM downloads "
158 "ORDER BY start_time")); 226 "ORDER BY start_time"));
159 227
160 while (statement.Step()) { 228 while (statement_main.Step()) {
161 DownloadRow info; 229 scoped_ptr<DownloadRow> info(new DownloadRow());
162 info.db_handle = statement.ColumnInt64(0); 230 int column = 0;
163 info.path = ColumnFilePath(statement, 1); 231
164 info.url = GURL(statement.ColumnString(2)); 232 int db_handle = statement_main.ColumnInt64(column++);
165 info.start_time = base::Time::FromTimeT(statement.ColumnInt64(3)); 233 info->db_handle = db_handle;
166 info.received_bytes = statement.ColumnInt64(4); 234 info->current_path = ColumnFilePath(statement_main, column++);
167 info.total_bytes = statement.ColumnInt64(5); 235 info->target_path = ColumnFilePath(statement_main, column++);
168 int state = statement.ColumnInt(6); 236 info->start_time = base::Time::FromInternalValue(
169 info.state = IntToState(state); 237 statement_main.ColumnInt64(column++));
170 info.end_time = base::Time::FromTimeT(statement.ColumnInt64(7)); 238 info->received_bytes = statement_main.ColumnInt64(column++);
171 info.opened = statement.ColumnInt(8) != 0; 239 info->total_bytes = statement_main.ColumnInt64(column++);
172 if (info.db_handle >= next_db_handle_) 240 int state = statement_main.ColumnInt(column++);
173 next_db_handle_ = info.db_handle + 1; 241 info->state = IntToState(state);
174 if (!db_handles.insert(info.db_handle).second) { 242 info->interrupt_reason = static_cast<content::DownloadInterruptReason>(
175 // info.db_handle was already in db_handles. The database is corrupt. 243 statement_main.ColumnInt(column++));
176 base::debug::Alias(&info.db_handle); 244 info->end_time = base::Time::FromInternalValue(
245 statement_main.ColumnInt64(column++));
246 info->opened = statement_main.ColumnInt(column++) != 0;
247 if (info->db_handle >= next_db_handle_)
248 next_db_handle_ = info->db_handle + 1;
249 if (!db_handles.insert(info->db_handle).second) {
250 // info->db_handle was already in db_handles. The database is corrupt.
251 base::debug::Alias(&info->db_handle);
177 DCHECK(false); 252 DCHECK(false);
178 } 253 }
179 if (info.state == DownloadItem::MAX_DOWNLOAD_STATE) { 254 if (info->state == DownloadItem::MAX_DOWNLOAD_STATE) {
180 UMA_HISTOGRAM_COUNTS("Download.DatabaseInvalidState", state); 255 UMA_HISTOGRAM_COUNTS("Download.DatabaseInvalidState", state);
181 continue; 256 continue;
182 } 257 }
183 results->push_back(info); 258 DCHECK(!ContainsKey(info_map, info->db_handle));
259 info_map[db_handle] = info.release();
260 }
261
262 sql::Statement statement_chain(GetDB().GetCachedStatement(
263 SQL_FROM_HERE,
264 "SELECT id, chain_index, url FROM downloads_url_chains "
265 "ORDER BY id, chain_index"));
266
267 while (statement_chain.Step()) {
268 int column = 0;
269 int64 db_handle = statement_chain.ColumnInt64(column++);
270 int chain_index = statement_chain.ColumnInt(column++);
271
272 // Note that these DCHECKs may trip as a result of corrupted databases.
273 // We have them because in debug builds the chances are higher there's
274 // an actual bug than that the database is corrupt, but we handle the
275 // DB corruption case in production code.
276
277 // Confirm the handle has already been seen--if it hasn't, discard the
278 // record.
279 DCHECK(ContainsKey(info_map, db_handle));
280 if (!ContainsKey(info_map, db_handle))
281 continue;
282
283 // Confirm all previous URLs in the chain have already been seen;
284 // if not, fill in with null or discard record.
285 int current_chain_size = info_map[db_handle]->url_chain.size();
286 std::vector<GURL>* url_chain(&info_map[db_handle]->url_chain);
287 DCHECK_EQ(chain_index, current_chain_size);
288 while (current_chain_size < chain_index) {
289 url_chain->push_back(GURL());
290 current_chain_size++;
291 }
292 if (current_chain_size > chain_index)
293 continue;
294
295 // Save the record.
296 url_chain->push_back(GURL(statement_chain.ColumnString(2)));
297 }
298
299 for (std::map<DownloadID, DownloadRow*>::iterator
300 it = info_map.begin(); it != info_map.end(); ++it) {
301 // Copy the contents of the stored info.
302 results->push_back(*it->second);
303 delete it->second;
304 it->second = NULL;
184 } 305 }
185 } 306 }
186 307
187 bool DownloadDatabase::UpdateDownload(const DownloadRow& data) { 308 bool DownloadDatabase::UpdateDownload(const DownloadRow& data) {
188 DCHECK(data.db_handle > 0); 309 DCHECK(data.db_handle > 0);
189 int state = StateToInt(data.state); 310 int state = StateToInt(data.state);
190 if (state == kStateInvalid) { 311 if (state == kStateInvalid) {
191 // TODO(benjhayden) [D]CHECK instead. 312 // TODO(benjhayden) [D]CHECK instead.
192 return false; 313 return false;
193 } 314 }
194 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 315 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
195 "UPDATE downloads " 316 "UPDATE downloads "
196 "SET full_path=?, received_bytes=?, state=?, end_time=?, total_bytes=?, " 317 "SET current_path=?, target_path=?, received_bytes=?, state=?, "
197 "opened=? WHERE id=?")); 318 "interrupt_reason=?, end_time=?, total_bytes=?, opened=? WHERE id=?"));
198 BindFilePath(statement, data.path, 0); 319 int column = 0;
199 statement.BindInt64(1, data.received_bytes); 320 BindFilePath(statement, data.current_path, column++);
200 statement.BindInt(2, state); 321 BindFilePath(statement, data.target_path, column++);
201 statement.BindInt64(3, data.end_time.ToTimeT()); 322 statement.BindInt64(column++, data.received_bytes);
202 statement.BindInt(4, data.total_bytes); 323 statement.BindInt(column++, state);
203 statement.BindInt(5, (data.opened ? 1 : 0)); 324 statement.BindInt(column++, static_cast<int>(data.interrupt_reason));
204 statement.BindInt64(6, data.db_handle); 325 statement.BindInt64(column++, data.end_time.ToInternalValue());
326 statement.BindInt(column++, data.total_bytes);
327 statement.BindInt(column++, (data.opened ? 1 : 0));
328 statement.BindInt64(column++, data.db_handle);
205 329
206 return statement.Run(); 330 return statement.Run();
207 } 331 }
208 332
209 bool DownloadDatabase::CleanUpInProgressEntries() { 333 bool DownloadDatabase::CleanUpInProgressEntries() {
210 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 334 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
211 "UPDATE downloads SET state=? WHERE state=?")); 335 "UPDATE downloads SET state=? WHERE state=?"));
212 statement.BindInt(0, kStateCancelled); 336 statement.BindInt(0, kStateCancelled);
213 statement.BindInt(1, kStateInProgress); 337 statement.BindInt(1, kStateInProgress);
214 338
215 return statement.Run(); 339 return statement.Run();
216 } 340 }
217 341
218 int64 DownloadDatabase::CreateDownload( 342 int64 DownloadDatabase::CreateDownload(
219 const DownloadRow& info) { 343 const DownloadRow& info) {
220 if (next_db_handle_ == 0) { 344 if (next_db_handle_ == 0) {
221 // This is unlikely. All current known tests and users already call 345 // This is unlikely. All current known tests and users already call
222 // QueryDownloads() before CreateDownload(). 346 // QueryDownloads() before CreateDownload().
223 std::vector<DownloadRow> results; 347 std::vector<DownloadRow> results;
224 QueryDownloads(&results); 348 QueryDownloads(&results);
225 CHECK_NE(0, next_db_handle_); 349 CHECK_NE(0, next_db_handle_);
226 } 350 }
227 351
228 int state = StateToInt(info.state); 352 int state = StateToInt(info.state);
229 if (state == kStateInvalid) 353 if (state == kStateInvalid)
230 return kUninitializedHandle; 354 return kUninitializedHandle;
231 355
232 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
233 "INSERT INTO downloads "
234 "(id, full_path, url, start_time, received_bytes, total_bytes, state, "
235 "end_time, opened) "
236 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"));
237
238 int db_handle = next_db_handle_++; 356 int db_handle = next_db_handle_++;
239 357
240 statement.BindInt64(0, db_handle); 358 {
241 BindFilePath(statement, info.path, 1); 359 sql::Statement statement_insert(GetDB().GetCachedStatement(
242 statement.BindString(2, info.url.spec()); 360 SQL_FROM_HERE,
243 statement.BindInt64(3, info.start_time.ToTimeT()); 361 "INSERT INTO downloads "
244 statement.BindInt64(4, info.received_bytes); 362 "(id, current_path, target_path, start_time, "
245 statement.BindInt64(5, info.total_bytes); 363 " received_bytes, total_bytes, state, interrupt_reason, "
246 statement.BindInt(6, state); 364 " end_time, opened) "
247 statement.BindInt64(7, info.end_time.ToTimeT()); 365 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
248 statement.BindInt(8, info.opened ? 1 : 0);
249 366
250 if (statement.Run()) { 367 int column = 0;
251 // TODO(benjhayden) if(info.id>next_id_){setvalue;next_id_=info.id;} 368 statement_insert.BindInt64(column++, db_handle);
252 GetMetaTable().SetValue(kNextDownloadId, ++next_id_); 369 BindFilePath(statement_insert, info.current_path, column++);
370 BindFilePath(statement_insert, info.target_path, column++);
371 statement_insert.BindInt64(column++, info.start_time.ToInternalValue());
372 statement_insert.BindInt64(column++, info.received_bytes);
373 statement_insert.BindInt64(column++, info.total_bytes);
374 statement_insert.BindInt(column++, state);
375 statement_insert.BindInt(column++, content::DOWNLOAD_INTERRUPT_REASON_NONE);
376 statement_insert.BindInt64(column++, info.end_time.ToInternalValue());
377 statement_insert.BindInt(column++, info.opened ? 1 : 0);
378 if (!statement_insert.Run()) {
379 LOG(WARNING) << "Main insertion for download create failed.";
380 return kUninitializedHandle;
381 }
382 }
253 383
254 return db_handle; 384 sql::Statement statement_insert_chain(
385 GetDB().GetCachedStatement(SQL_FROM_HERE,
386 "INSERT INTO downloads_url_chains "
387 "(id, chain_index, url) "
388 "VALUES (?, ?, ?)"));
389 for (size_t i = 0; i < info.url_chain.size(); ++i) {
390 statement_insert_chain.BindInt64(0, db_handle);
391 statement_insert_chain.BindInt(1, i);
392 statement_insert_chain.BindString(2, info.url_chain[i].spec());
393 if (!statement_insert_chain.Run()) {
394 LOG(WARNING) << "Url insertion for download create failed.";
395 return kUninitializedHandle;
396 }
397 statement_insert_chain.Reset(true);
255 } 398 }
256 return kUninitializedHandle; 399
400 // TODO(benjhayden) if(info.id>next_id_){setvalue;next_id_=info.id;}
401 GetMetaTable().SetValue(kNextDownloadId, ++next_id_);
402
403 return db_handle;
257 } 404 }
258 405
259 void DownloadDatabase::RemoveDownload(int64 handle) { 406 void DownloadDatabase::RemoveDownload(int64 handle) {
260 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 407 sql::Statement downloads_statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
261 "DELETE FROM downloads WHERE id=?")); 408 "DELETE FROM downloads WHERE id=?"));
262 statement.BindInt64(0, handle); 409 downloads_statement.BindInt64(0, handle);
263 statement.Run(); 410 downloads_statement.Run();
411
412 sql::Statement urlchain_statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
413 "DELETE FROM downloads_url_chains WHERE id=?"));
414 urlchain_statement.BindInt64(0, handle);
415 urlchain_statement.Run();
264 } 416 }
265 417
266 int DownloadDatabase::CountDownloads() { 418 int DownloadDatabase::CountDownloads() {
267 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, 419 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
268 "SELECT count(*) from downloads")); 420 "SELECT count(*) from downloads"));
269 statement.Step(); 421 statement.Step();
270 return statement.ColumnInt(0); 422 return statement.ColumnInt(0);
271 } 423 }
272 424
273 } // namespace history 425 } // namespace history
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698