web_database.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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 "components/webdata/common/web_database.h"
6
7#include <algorithm>
8
9#include "base/stl_util.h"
10#include "content/public/browser/notification_service.h"
11#include "sql/statement.h"
12#include "sql/transaction.h"
13
14// Current version number.  Note: when changing the current version number,
15// corresponding changes must happen in the unit tests, and new migration test
16// added.  See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
17// static
18const int WebDatabase::kCurrentVersionNumber = 50;
19
20namespace {
21
22const int kCompatibleVersionNumber = 48;
23
24// Change the version number and possibly the compatibility version of
25// |meta_table_|.
26void ChangeVersion(sql::MetaTable* meta_table,
27                   int version_num,
28                   bool update_compatible_version_num) {
29  meta_table->SetVersionNumber(version_num);
30  if (update_compatible_version_num) {
31    meta_table->SetCompatibleVersionNumber(
32          std::min(version_num, kCompatibleVersionNumber));
33  }
34}
35
36// Outputs the failed version number as a warning and always returns
37// |sql::INIT_FAILURE|.
38sql::InitStatus FailedMigrationTo(int version_num) {
39  LOG(WARNING) << "Unable to update web database to version "
40               << version_num << ".";
41  NOTREACHED();
42  return sql::INIT_FAILURE;
43}
44
45}  // namespace
46
47WebDatabase::WebDatabase() {}
48
49WebDatabase::~WebDatabase() {
50}
51
52void WebDatabase::AddTable(WebDatabaseTable* table) {
53  tables_[table->GetTypeKey()] = table;
54}
55
56WebDatabaseTable* WebDatabase::GetTable(WebDatabaseTable::TypeKey key) {
57  return tables_[key];
58}
59
60void WebDatabase::BeginTransaction() {
61  db_.BeginTransaction();
62}
63
64void WebDatabase::CommitTransaction() {
65  db_.CommitTransaction();
66}
67
68sql::Connection* WebDatabase::GetSQLConnection() {
69  return &db_;
70}
71
72sql::InitStatus WebDatabase::Init(const base::FilePath& db_name) {
73  // When running in unit tests, there is already a NotificationService object.
74  // Since only one can exist at a time per thread, check first.
75  if (!content::NotificationService::current())
76    notification_service_.reset(content::NotificationService::Create());
77
78  db_.set_error_histogram_name("Sqlite.Web.Error");
79
80  // We don't store that much data in the tables so use a small page size.
81  // This provides a large benefit for empty tables (which is very likely with
82  // the tables we create).
83  db_.set_page_size(2048);
84
85  // We shouldn't have much data and what access we currently have is quite
86  // infrequent. So we go with a small cache size.
87  db_.set_cache_size(32);
88
89  // Run the database in exclusive mode. Nobody else should be accessing the
90  // database while we're running, and this will give somewhat improved perf.
91  db_.set_exclusive_locking();
92
93  if (!db_.Open(db_name))
94    return sql::INIT_FAILURE;
95
96  // Initialize various tables
97  sql::Transaction transaction(&db_);
98  if (!transaction.Begin())
99    return sql::INIT_FAILURE;
100
101  // Version check.
102  if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber))
103    return sql::INIT_FAILURE;
104  if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
105    LOG(WARNING) << "Web database is too new.";
106    return sql::INIT_TOO_NEW;
107  }
108
109  // Initialize the tables.
110  for (TableMap::iterator it = tables_.begin();
111       it != tables_.end();
112       ++it) {
113    if (!it->second->Init(&db_, &meta_table_)) {
114      LOG(WARNING) << "Unable to initialize the web database.";
115      return sql::INIT_FAILURE;
116    }
117  }
118
119  // If the file on disk is an older database version, bring it up to date.
120  // If the migration fails we return an error to caller and do not commit
121  // the migration.
122  sql::InitStatus migration_status = MigrateOldVersionsAsNeeded();
123  if (migration_status != sql::INIT_OK)
124    return migration_status;
125
126  return transaction.Commit() ? sql::INIT_OK : sql::INIT_FAILURE;
127}
128
129sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded() {
130  // Some malware used to lower the version number, causing migration to
131  // fail. Ensure the version number is at least as high as the compatible
132  // version number.
133  int current_version = std::max(meta_table_.GetVersionNumber(),
134                                 meta_table_.GetCompatibleVersionNumber());
135  if (current_version > meta_table_.GetVersionNumber())
136    ChangeVersion(&meta_table_, current_version, false);
137
138  if (current_version < 20) {
139    // Versions 1 - 19 are unhandled.  Version numbers greater than
140    // kCurrentVersionNumber should have already been weeded out by the caller.
141    //
142    // When the version is too old, we return failure error code.  The schema
143    // is too out of date to migrate.
144    //
145    // There should not be a released product that makes a database too old to
146    // migrate. If we do encounter such a legacy database, we will need a
147    // better solution to handle it (i.e., pop up a dialog to tell the user,
148    // erase all their prefs and start over, etc.).
149    LOG(WARNING) << "Web database version " << current_version <<
150        " is too old to handle.";
151    NOTREACHED();
152    return sql::INIT_FAILURE;
153  }
154
155  for (int next_version = current_version + 1;
156       next_version <= kCurrentVersionNumber;
157       ++next_version) {
158    // Give each table a chance to migrate to this version.
159    for (TableMap::iterator it = tables_.begin();
160         it != tables_.end();
161         ++it) {
162      // Any of the tables may set this to true, but by default it is false.
163      bool update_compatible_version = false;
164      if (!it->second->MigrateToVersion(next_version,
165                                        &update_compatible_version)) {
166        return FailedMigrationTo(next_version);
167      }
168
169      ChangeVersion(&meta_table_, next_version, update_compatible_version);
170    }
171  }
172  return sql::INIT_OK;
173}
174