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
3// found in the LICENSE file.
4
5#include <algorithm>
6#include <string>
7
8#include "base/strings/string_util.h"
9#include "chrome/browser/history/archived_database.h"
10#include "sql/transaction.h"
11
12namespace history {
13
14namespace {
15
16static const int kCurrentVersionNumber = 4;
17static const int kCompatibleVersionNumber = 2;
18
19}  // namespace
20
21ArchivedDatabase::ArchivedDatabase() {
22}
23
24ArchivedDatabase::~ArchivedDatabase() {
25}
26
27bool ArchivedDatabase::Init(const base::FilePath& file_name) {
28  // Set the database page size to something a little larger to give us
29  // better performance (we're typically seek rather than bandwidth limited).
30  // This only has an effect before any tables have been created, otherwise
31  // this is a NOP. Must be a power of 2 and a max of 8192.
32  db_.set_page_size(4096);
33
34  // Don't use very much memory caching this database. We seldom use it for
35  // anything important.
36  db_.set_cache_size(64);
37
38  // Run the database in exclusive mode. Nobody else should be accessing the
39  // database while we're running, and this will give somewhat improved perf.
40  db_.set_exclusive_locking();
41
42  if (!db_.Open(file_name))
43    return false;
44
45  if (!InitTables()) {
46    db_.Close();
47    return false;
48  }
49
50  return true;
51}
52
53bool ArchivedDatabase::InitTables() {
54  sql::Transaction transaction(&db_);
55  if (!transaction.Begin())
56    return false;
57
58  // Version check.
59  if (!meta_table_.Init(&db_, kCurrentVersionNumber,
60                        kCompatibleVersionNumber))
61    return false;
62
63  // Create the tables.
64  if (!CreateURLTable(false) || !InitVisitTable() ||
65      !InitKeywordSearchTermsTable())
66    return false;
67
68  CreateMainURLIndex();
69  CreateKeywordSearchTermsIndices();
70
71  if (EnsureCurrentVersion() != sql::INIT_OK)
72    return false;
73
74  return transaction.Commit();
75}
76
77void ArchivedDatabase::TrimMemory(bool aggressively) {
78  db_.TrimMemory(aggressively);
79}
80
81void ArchivedDatabase::BeginTransaction() {
82  db_.BeginTransaction();
83}
84
85void ArchivedDatabase::CommitTransaction() {
86  db_.CommitTransaction();
87}
88
89sql::Connection& ArchivedDatabase::GetDB() {
90  return db_;
91}
92
93// static
94int ArchivedDatabase::GetCurrentVersion() {
95  return kCurrentVersionNumber;
96}
97
98// Migration -------------------------------------------------------------------
99
100sql::InitStatus ArchivedDatabase::EnsureCurrentVersion() {
101  // We can't read databases newer than we were designed for.
102  if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
103    LOG(WARNING) << "Archived database is too new.";
104    return sql::INIT_TOO_NEW;
105  }
106
107  // NOTICE: If you are changing structures for things shared with the archived
108  // history file like URLs, visits, or downloads, that will need migration as
109  // well. Instead of putting such migration code in this class, it should be
110  // in the corresponding file (url_database.cc, etc.) and called from here and
111  // from the archived_database.cc.
112
113  int cur_version = meta_table_.GetVersionNumber();
114  if (cur_version == 1) {
115    if (!DropStarredIDFromURLs()) {
116      LOG(WARNING) << "Unable to update archived database to version 2.";
117      return sql::INIT_FAILURE;
118    }
119    ++cur_version;
120    meta_table_.SetVersionNumber(cur_version);
121    meta_table_.SetCompatibleVersionNumber(
122        std::min(cur_version, kCompatibleVersionNumber));
123  }
124
125  if (cur_version == 2) {
126    // This is the version prior to adding visit_source table.
127    ++cur_version;
128    meta_table_.SetVersionNumber(cur_version);
129  }
130
131  if (cur_version == 3) {
132    // This is the version prior to adding the visit_duration field in visits
133    // database. We need to migrate the database.
134    if (!MigrateVisitsWithoutDuration()) {
135      LOG(WARNING) << "Unable to update archived database to version 4.";
136      return sql::INIT_FAILURE;
137    }
138    ++cur_version;
139    meta_table_.SetVersionNumber(cur_version);
140  }
141
142  // Put future migration cases here.
143
144  // When the version is too old, we just try to continue anyway, there should
145  // not be a released product that makes a database too old for us to handle.
146  LOG_IF(WARNING, cur_version < kCurrentVersionNumber) <<
147      "Archived database version " << cur_version << " is too old to handle.";
148
149  return sql::INIT_OK;
150}
151}  // namespace history
152