1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/history/download_database.h"
6
7 #include <limits>
8 #include <vector>
9
10 #include "app/sql/statement.h"
11 #include "base/file_path.h"
12 #include "base/utf_string_conversions.h"
13 #include "build/build_config.h"
14 #include "chrome/browser/download/download_item.h"
15 #include "chrome/browser/history/download_create_info.h"
16
17 // Download schema:
18 //
19 // id SQLite-generated primary key.
20 // full_path Location of the download on disk.
21 // url URL of the downloaded file.
22 // start_time When the download was started.
23 // received_bytes Total size downloaded.
24 // total_bytes Total size of the download.
25 // state Identifies if this download is completed or not. Not used
26 // directly by the history system. See DownloadItem's
27 // DownloadState for where this is used.
28
29 namespace history {
30
31 namespace {
32
33 #if defined(OS_POSIX)
34
35 // Binds/reads the given file path to the given column of the given statement.
BindFilePath(sql::Statement & statement,const FilePath & path,int col)36 void BindFilePath(sql::Statement& statement, const FilePath& path, int col) {
37 statement.BindString(col, path.value());
38 }
ColumnFilePath(sql::Statement & statement,int col)39 FilePath ColumnFilePath(sql::Statement& statement, int col) {
40 return FilePath(statement.ColumnString(col));
41 }
42
43 #else
44
45 // See above.
46 void BindFilePath(sql::Statement& statement, const FilePath& path, int col) {
47 statement.BindString(col, UTF16ToUTF8(path.value()));
48 }
49 FilePath ColumnFilePath(sql::Statement& statement, int col) {
50 return FilePath(UTF8ToUTF16(statement.ColumnString(col)));
51 }
52
53 #endif
54
55 } // namespace
56
DownloadDatabase()57 DownloadDatabase::DownloadDatabase() {
58 }
59
~DownloadDatabase()60 DownloadDatabase::~DownloadDatabase() {
61 }
62
InitDownloadTable()63 bool DownloadDatabase::InitDownloadTable() {
64 if (!GetDB().DoesTableExist("downloads")) {
65 if (!GetDB().Execute(
66 "CREATE TABLE downloads ("
67 "id INTEGER PRIMARY KEY,"
68 "full_path LONGVARCHAR NOT NULL,"
69 "url LONGVARCHAR NOT NULL,"
70 "start_time INTEGER NOT NULL,"
71 "received_bytes INTEGER NOT NULL,"
72 "total_bytes INTEGER NOT NULL,"
73 "state INTEGER NOT NULL)"))
74 return false;
75 }
76 return true;
77 }
78
DropDownloadTable()79 bool DownloadDatabase::DropDownloadTable() {
80 return GetDB().Execute("DROP TABLE downloads");
81 }
82
QueryDownloads(std::vector<DownloadCreateInfo> * results)83 void DownloadDatabase::QueryDownloads(
84 std::vector<DownloadCreateInfo>* results) {
85 results->clear();
86
87 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
88 "SELECT id, full_path, url, start_time, received_bytes, "
89 "total_bytes, state "
90 "FROM downloads "
91 "ORDER BY start_time"));
92 if (!statement)
93 return;
94
95 while (statement.Step()) {
96 DownloadCreateInfo info;
97 info.db_handle = statement.ColumnInt64(0);
98
99 info.path = ColumnFilePath(statement, 1);
100 info.url_chain.push_back(GURL(statement.ColumnString(2)));
101 info.start_time = base::Time::FromTimeT(statement.ColumnInt64(3));
102 info.received_bytes = statement.ColumnInt64(4);
103 info.total_bytes = statement.ColumnInt64(5);
104 info.state = statement.ColumnInt(6);
105 results->push_back(info);
106 }
107 }
108
UpdateDownload(int64 received_bytes,int32 state,DownloadID db_handle)109 bool DownloadDatabase::UpdateDownload(int64 received_bytes,
110 int32 state,
111 DownloadID db_handle) {
112 DCHECK(db_handle > 0);
113 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
114 "UPDATE downloads "
115 "SET received_bytes=?, state=? WHERE id=?"));
116 if (!statement)
117 return false;
118
119 statement.BindInt64(0, received_bytes);
120 statement.BindInt(1, state);
121 statement.BindInt64(2, db_handle);
122 return statement.Run();
123 }
124
UpdateDownloadPath(const FilePath & path,DownloadID db_handle)125 bool DownloadDatabase::UpdateDownloadPath(const FilePath& path,
126 DownloadID db_handle) {
127 DCHECK(db_handle > 0);
128 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
129 "UPDATE downloads SET full_path=? WHERE id=?"));
130 if (!statement)
131 return false;
132
133 BindFilePath(statement, path, 0);
134 statement.BindInt64(1, db_handle);
135 return statement.Run();
136 }
137
CleanUpInProgressEntries()138 bool DownloadDatabase::CleanUpInProgressEntries() {
139 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
140 "UPDATE downloads SET state=? WHERE state=?"));
141 if (!statement)
142 return false;
143 statement.BindInt(0, DownloadItem::CANCELLED);
144 statement.BindInt(1, DownloadItem::IN_PROGRESS);
145 return statement.Run();
146 }
147
CreateDownload(const DownloadCreateInfo & info)148 int64 DownloadDatabase::CreateDownload(const DownloadCreateInfo& info) {
149 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
150 "INSERT INTO downloads "
151 "(full_path, url, start_time, received_bytes, total_bytes, state) "
152 "VALUES (?, ?, ?, ?, ?, ?)"));
153 if (!statement)
154 return 0;
155
156 BindFilePath(statement, info.path, 0);
157 statement.BindString(1, info.url().spec());
158 statement.BindInt64(2, info.start_time.ToTimeT());
159 statement.BindInt64(3, info.received_bytes);
160 statement.BindInt64(4, info.total_bytes);
161 statement.BindInt(5, info.state);
162
163 if (statement.Run())
164 return GetDB().GetLastInsertRowId();
165 return 0;
166 }
167
RemoveDownload(DownloadID db_handle)168 void DownloadDatabase::RemoveDownload(DownloadID db_handle) {
169 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
170 "DELETE FROM downloads WHERE id=?"));
171 if (!statement)
172 return;
173
174 statement.BindInt64(0, db_handle);
175 statement.Run();
176 }
177
RemoveDownloadsBetween(base::Time delete_begin,base::Time delete_end)178 void DownloadDatabase::RemoveDownloadsBetween(base::Time delete_begin,
179 base::Time delete_end) {
180 // This does not use an index. We currently aren't likely to have enough
181 // downloads where an index by time will give us a lot of benefit.
182 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
183 "DELETE FROM downloads WHERE start_time >= ? AND start_time < ? "
184 "AND (State = ? OR State = ? OR State = ?)"));
185 if (!statement)
186 return;
187
188 time_t start_time = delete_begin.ToTimeT();
189 time_t end_time = delete_end.ToTimeT();
190 statement.BindInt64(0, start_time);
191 statement.BindInt64(
192 1,
193 end_time ? end_time : std::numeric_limits<int64>::max());
194 statement.BindInt(2, DownloadItem::COMPLETE);
195 statement.BindInt(3, DownloadItem::CANCELLED);
196 statement.BindInt(4, DownloadItem::INTERRUPTED);
197 statement.Run();
198 }
199
200 } // namespace history
201