blob: bc037b7f6768162f1e3cb67922b26b46c2850aad [file] [log] [blame]
// Copyright 2014 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 "components/history/core/browser/in_memory_database.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
namespace history {
InMemoryDatabase::InMemoryDatabase() {}
InMemoryDatabase::~InMemoryDatabase() {
}
bool InMemoryDatabase::InitDB() {
// Set the database page size to 4K for better performance.
db_.set_page_size(4096);
if (!db_.OpenInMemory()) {
NOTREACHED() << "Cannot open databse " << GetDB().GetErrorMessage();
return false;
}
// No reason to leave data behind in memory when rows are removed.
ignore_result(db_.Execute("PRAGMA auto_vacuum=1"));
// Ensure this is really an in-memory-only cache.
ignore_result(db_.Execute("PRAGMA temp_store=MEMORY"));
// Create the URL table, but leave it empty for now.
if (!CreateURLTable(false)) {
NOTREACHED() << "Unable to create table";
db_.Close();
return false;
}
// Create the keyword search terms table.
if (!InitKeywordSearchTermsTable()) {
NOTREACHED() << "Unable to create keyword search terms";
db_.Close();
return false;
}
return true;
}
bool InMemoryDatabase::InitFromScratch() {
if (!InitDB())
return false;
// InitDB doesn't create the index so in the disk-loading case, it can be
// added afterwards.
CreateMainURLIndex();
CreateKeywordSearchTermsIndices();
return true;
}
bool InMemoryDatabase::InitFromDisk(const base::FilePath& history_name) {
if (!InitDB())
return false;
// Attach to the history database on disk. (We can't ATTACH in the middle of
// a transaction.)
sql::Statement attach(GetDB().GetUniqueStatement("ATTACH ? AS history"));
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
attach.BindString(0, history_name.value());
#else
attach.BindString(0, base::WideToUTF8(history_name.value()));
#endif
if (!attach.Run())
return false;
// Copy URL data to memory.
base::TimeTicks begin_load = base::TimeTicks::Now();
// Need to explicitly specify the column names here since databases on disk
// may or may not have a favicon_id column, but the in-memory one will never
// have it. Therefore, the columns aren't guaranteed to match.
//
// TODO(https://crbug.com/736136) Once we can guarantee that the favicon_id
// column doesn't exist with migration code, this can be replaced with the
// simpler:
// "INSERT INTO urls SELECT * FROM history.urls WHERE typed_count > 0"
// which does not require us to keep the list of columns in sync. However,
// we may still want to keep the explicit columns as a safety measure.
if (!db_.Execute(
"INSERT INTO urls "
"(id, url, title, visit_count, typed_count, last_visit_time, hidden) "
"SELECT "
"id, url, title, visit_count, typed_count, last_visit_time, hidden "
"FROM history.urls WHERE typed_count > 0")) {
// Unable to get data from the history database. This is OK, the file may
// just not exist yet.
}
base::TimeTicks end_load = base::TimeTicks::Now();
UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBPopulate",
end_load - begin_load);
UMA_HISTOGRAM_COUNTS_1M("History.InMemoryDBItemCount",
db_.GetLastChangeCount());
{
// This calculation should be fast (since it's on an in-memory DB with
// an average of only 35 rows).
sql::Statement visit_count(db_.GetUniqueStatement(
"SELECT sum(visit_count) FROM urls"));
if (visit_count.Step()) {
UMA_HISTOGRAM_COUNTS_1M("History.InMemoryTypedUrlVisitCount",
visit_count.ColumnInt(0));
}
}
// Insert keyword search related URLs.
begin_load = base::TimeTicks::Now();
if (!db_.Execute("INSERT OR IGNORE INTO urls SELECT u.id, u.url, u.title, "
"u.visit_count, u.typed_count, u.last_visit_time, u.hidden "
"FROM history.urls u JOIN history.keyword_search_terms kst "
"WHERE u.typed_count = 0 AND u.id = kst.url_id")) {
// Unable to get data from the history database. This is OK, the file may
// just not exist yet.
}
end_load = base::TimeTicks::Now();
UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordURLPopulate",
end_load - begin_load);
UMA_HISTOGRAM_COUNTS_1M("History.InMemoryDBKeywordURLItemCount",
db_.GetLastChangeCount());
// Copy search terms to memory.
begin_load = base::TimeTicks::Now();
if (!db_.Execute(
"INSERT INTO keyword_search_terms SELECT * FROM "
"history.keyword_search_terms")) {
// Unable to get data from the history database. This is OK, the file may
// just not exist yet.
}
end_load = base::TimeTicks::Now();
UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordTermsPopulate",
end_load - begin_load);
UMA_HISTOGRAM_COUNTS_1M("History.InMemoryDBKeywordTermsCount",
db_.GetLastChangeCount());
// Detach from the history database on disk.
if (!db_.Execute("DETACH history")) {
NOTREACHED() << "Unable to detach from history database.";
return false;
}
// Index the table, this is faster than creating the index first and then
// inserting into it.
CreateMainURLIndex();
CreateKeywordSearchTermsIndices();
return true;
}
sql::Database& InMemoryDatabase::GetDB() {
return db_;
}
} // namespace history