// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/history/download_database.h" #include <limits> #include <vector> #include "app/sql/statement.h" #include "base/file_path.h" #include "base/utf_string_conversions.h" #include "build/build_config.h" #include "chrome/browser/download/download_item.h" #include "chrome/browser/history/download_create_info.h" // Download schema: // // id SQLite-generated primary key. // full_path Location of the download on disk. // url URL of the downloaded file. // start_time When the download was started. // received_bytes Total size downloaded. // total_bytes Total size of the download. // state Identifies if this download is completed or not. Not used // directly by the history system. See DownloadItem's // DownloadState for where this is used. namespace history { namespace { #if defined(OS_POSIX) // Binds/reads the given file path to the given column of the given statement. void BindFilePath(sql::Statement& statement, const FilePath& path, int col) { statement.BindString(col, path.value()); } FilePath ColumnFilePath(sql::Statement& statement, int col) { return FilePath(statement.ColumnString(col)); } #else // See above. void BindFilePath(sql::Statement& statement, const FilePath& path, int col) { statement.BindString(col, UTF16ToUTF8(path.value())); } FilePath ColumnFilePath(sql::Statement& statement, int col) { return FilePath(UTF8ToUTF16(statement.ColumnString(col))); } #endif } // namespace DownloadDatabase::DownloadDatabase() { } DownloadDatabase::~DownloadDatabase() { } bool DownloadDatabase::InitDownloadTable() { if (!GetDB().DoesTableExist("downloads")) { if (!GetDB().Execute( "CREATE TABLE downloads (" "id INTEGER PRIMARY KEY," "full_path LONGVARCHAR NOT NULL," "url LONGVARCHAR NOT NULL," "start_time INTEGER NOT NULL," "received_bytes INTEGER NOT NULL," "total_bytes INTEGER NOT NULL," "state INTEGER NOT NULL)")) return false; } return true; } bool DownloadDatabase::DropDownloadTable() { return GetDB().Execute("DROP TABLE downloads"); } void DownloadDatabase::QueryDownloads( std::vector<DownloadCreateInfo>* results) { results->clear(); sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, "SELECT id, full_path, url, start_time, received_bytes, " "total_bytes, state " "FROM downloads " "ORDER BY start_time")); if (!statement) return; while (statement.Step()) { DownloadCreateInfo info; info.db_handle = statement.ColumnInt64(0); info.path = ColumnFilePath(statement, 1); info.url_chain.push_back(GURL(statement.ColumnString(2))); info.start_time = base::Time::FromTimeT(statement.ColumnInt64(3)); info.received_bytes = statement.ColumnInt64(4); info.total_bytes = statement.ColumnInt64(5); info.state = statement.ColumnInt(6); results->push_back(info); } } bool DownloadDatabase::UpdateDownload(int64 received_bytes, int32 state, DownloadID db_handle) { DCHECK(db_handle > 0); sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, "UPDATE downloads " "SET received_bytes=?, state=? WHERE id=?")); if (!statement) return false; statement.BindInt64(0, received_bytes); statement.BindInt(1, state); statement.BindInt64(2, db_handle); return statement.Run(); } bool DownloadDatabase::UpdateDownloadPath(const FilePath& path, DownloadID db_handle) { DCHECK(db_handle > 0); sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, "UPDATE downloads SET full_path=? WHERE id=?")); if (!statement) return false; BindFilePath(statement, path, 0); statement.BindInt64(1, db_handle); return statement.Run(); } bool DownloadDatabase::CleanUpInProgressEntries() { sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, "UPDATE downloads SET state=? WHERE state=?")); if (!statement) return false; statement.BindInt(0, DownloadItem::CANCELLED); statement.BindInt(1, DownloadItem::IN_PROGRESS); return statement.Run(); } int64 DownloadDatabase::CreateDownload(const DownloadCreateInfo& info) { sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, "INSERT INTO downloads " "(full_path, url, start_time, received_bytes, total_bytes, state) " "VALUES (?, ?, ?, ?, ?, ?)")); if (!statement) return 0; BindFilePath(statement, info.path, 0); statement.BindString(1, info.url().spec()); statement.BindInt64(2, info.start_time.ToTimeT()); statement.BindInt64(3, info.received_bytes); statement.BindInt64(4, info.total_bytes); statement.BindInt(5, info.state); if (statement.Run()) return GetDB().GetLastInsertRowId(); return 0; } void DownloadDatabase::RemoveDownload(DownloadID db_handle) { sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, "DELETE FROM downloads WHERE id=?")); if (!statement) return; statement.BindInt64(0, db_handle); statement.Run(); } void DownloadDatabase::RemoveDownloadsBetween(base::Time delete_begin, base::Time delete_end) { // This does not use an index. We currently aren't likely to have enough // downloads where an index by time will give us a lot of benefit. sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, "DELETE FROM downloads WHERE start_time >= ? AND start_time < ? " "AND (State = ? OR State = ? OR State = ?)")); if (!statement) return; time_t start_time = delete_begin.ToTimeT(); time_t end_time = delete_end.ToTimeT(); statement.BindInt64(0, start_time); statement.BindInt64( 1, end_time ? end_time : std::numeric_limits<int64>::max()); statement.BindInt(2, DownloadItem::COMPLETE); statement.BindInt(3, DownloadItem::CANCELLED); statement.BindInt(4, DownloadItem::INTERRUPTED); statement.Run(); } } // namespace history