• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "sql/statement.h"
11 #include "sql/transaction.h"
12 
13 // Current version number.  Note: when changing the current version number,
14 // corresponding changes must happen in the unit tests, and new migration test
15 // added.  See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
16 // static
17 const int WebDatabase::kCurrentVersionNumber = 57;
18 
19 namespace {
20 
21 const int kCompatibleVersionNumber = 57;
22 
23 // Change the version number and possibly the compatibility version of
24 // |meta_table_|.
ChangeVersion(sql::MetaTable * meta_table,int version_num,bool update_compatible_version_num)25 void ChangeVersion(sql::MetaTable* meta_table,
26                    int version_num,
27                    bool update_compatible_version_num) {
28   meta_table->SetVersionNumber(version_num);
29   if (update_compatible_version_num) {
30     meta_table->SetCompatibleVersionNumber(
31           std::min(version_num, kCompatibleVersionNumber));
32   }
33 }
34 
35 // Outputs the failed version number as a warning and always returns
36 // |sql::INIT_FAILURE|.
FailedMigrationTo(int version_num)37 sql::InitStatus FailedMigrationTo(int version_num) {
38   LOG(WARNING) << "Unable to update web database to version "
39                << version_num << ".";
40   NOTREACHED();
41   return sql::INIT_FAILURE;
42 }
43 
44 }  // namespace
45 
WebDatabase()46 WebDatabase::WebDatabase() {}
47 
~WebDatabase()48 WebDatabase::~WebDatabase() {
49 }
50 
AddTable(WebDatabaseTable * table)51 void WebDatabase::AddTable(WebDatabaseTable* table) {
52   tables_[table->GetTypeKey()] = table;
53 }
54 
GetTable(WebDatabaseTable::TypeKey key)55 WebDatabaseTable* WebDatabase::GetTable(WebDatabaseTable::TypeKey key) {
56   return tables_[key];
57 }
58 
BeginTransaction()59 void WebDatabase::BeginTransaction() {
60   db_.BeginTransaction();
61 }
62 
CommitTransaction()63 void WebDatabase::CommitTransaction() {
64   db_.CommitTransaction();
65 }
66 
GetSQLConnection()67 sql::Connection* WebDatabase::GetSQLConnection() {
68   return &db_;
69 }
70 
Init(const base::FilePath & db_name)71 sql::InitStatus WebDatabase::Init(const base::FilePath& db_name) {
72   db_.set_histogram_tag("Web");
73 
74   // We don't store that much data in the tables so use a small page size.
75   // This provides a large benefit for empty tables (which is very likely with
76   // the tables we create).
77   db_.set_page_size(2048);
78 
79   // We shouldn't have much data and what access we currently have is quite
80   // infrequent. So we go with a small cache size.
81   db_.set_cache_size(32);
82 
83   // Run the database in exclusive mode. Nobody else should be accessing the
84   // database while we're running, and this will give somewhat improved perf.
85   db_.set_exclusive_locking();
86 
87   if (!db_.Open(db_name))
88     return sql::INIT_FAILURE;
89 
90   // Initialize various tables
91   sql::Transaction transaction(&db_);
92   if (!transaction.Begin())
93     return sql::INIT_FAILURE;
94 
95   // Version check.
96   if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber))
97     return sql::INIT_FAILURE;
98   if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
99     LOG(WARNING) << "Web database is too new.";
100     return sql::INIT_TOO_NEW;
101   }
102 
103   // Initialize the tables.
104   for (TableMap::iterator it = tables_.begin(); it != tables_.end(); ++it) {
105     it->second->Init(&db_, &meta_table_);
106   }
107 
108   // If the file on disk is an older database version, bring it up to date.
109   // If the migration fails we return an error to caller and do not commit
110   // the migration.
111   sql::InitStatus migration_status = MigrateOldVersionsAsNeeded();
112   if (migration_status != sql::INIT_OK)
113     return migration_status;
114 
115   // Create the desired SQL tables if they do not already exist.
116   // It's important that this happen *after* the migration code runs.
117   // Otherwise, the migration code would have to explicitly check for empty
118   // tables created in the new format, and skip the migration in that case.
119   for (TableMap::iterator it = tables_.begin(); it != tables_.end(); ++it) {
120     if (!it->second->CreateTablesIfNecessary()) {
121       LOG(WARNING) << "Unable to initialize the web database.";
122       return sql::INIT_FAILURE;
123     }
124   }
125 
126   return transaction.Commit() ? sql::INIT_OK : sql::INIT_FAILURE;
127 }
128 
MigrateOldVersionsAsNeeded()129 sql::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(); it != tables_.end(); ++it) {
160       // Any of the tables may set this to true, but by default it is false.
161       bool update_compatible_version = false;
162       if (!it->second->MigrateToVersion(next_version,
163                                         &update_compatible_version)) {
164         return FailedMigrationTo(next_version);
165       }
166 
167       ChangeVersion(&meta_table_, next_version, update_compatible_version);
168     }
169   }
170   return sql::INIT_OK;
171 }
172