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
29namespace history {
30
31namespace {
32
33#if defined(OS_POSIX)
34
35// Binds/reads the given file path to the given column of the given statement.
36void BindFilePath(sql::Statement& statement, const FilePath& path, int col) {
37  statement.BindString(col, path.value());
38}
39FilePath ColumnFilePath(sql::Statement& statement, int col) {
40  return FilePath(statement.ColumnString(col));
41}
42
43#else
44
45// See above.
46void BindFilePath(sql::Statement& statement, const FilePath& path, int col) {
47  statement.BindString(col, UTF16ToUTF8(path.value()));
48}
49FilePath ColumnFilePath(sql::Statement& statement, int col) {
50  return FilePath(UTF8ToUTF16(statement.ColumnString(col)));
51}
52
53#endif
54
55}  // namespace
56
57DownloadDatabase::DownloadDatabase() {
58}
59
60DownloadDatabase::~DownloadDatabase() {
61}
62
63bool 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
79bool DownloadDatabase::DropDownloadTable() {
80  return GetDB().Execute("DROP TABLE downloads");
81}
82
83void 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
109bool 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
125bool 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
138bool 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
148int64 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
168void 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
178void 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