• 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 = 58;
18 
19 namespace {
20 
21 const int kCompatibleVersionNumber = 58;
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 
159     // Do any database-wide migrations.
160     bool update_compatible_version = false;
161     if (!MigrateToVersion(next_version, &update_compatible_version))
162       return FailedMigrationTo(next_version);
163 
164     ChangeVersion(&meta_table_, next_version, update_compatible_version);
165 
166     // Give each table a chance to migrate to this version.
167     for (TableMap::iterator it = tables_.begin(); it != tables_.end(); ++it) {
168       // Any of the tables may set this to true, but by default it is false.
169       update_compatible_version = false;
170       if (!it->second->MigrateToVersion(next_version,
171                                         &update_compatible_version)) {
172         return FailedMigrationTo(next_version);
173       }
174 
175       ChangeVersion(&meta_table_, next_version, update_compatible_version);
176     }
177   }
178   return sql::INIT_OK;
179 }
180 
MigrateToVersion(int version,bool * update_compatible_version)181 bool WebDatabase::MigrateToVersion(int version,
182                       bool* update_compatible_version) {
183   // Migrate if necessary.
184   switch (version) {
185     case 58:
186       *update_compatible_version = true;
187       return MigrateToVersion58DropWebAppsAndIntents();
188   }
189 
190   return true;
191 }
192 
MigrateToVersion58DropWebAppsAndIntents()193 bool WebDatabase::MigrateToVersion58DropWebAppsAndIntents() {
194   sql::Transaction transaction(&db_);
195   return transaction.Begin() &&
196       db_.Execute("DROP TABLE IF EXISTS web_apps") &&
197       db_.Execute("DROP TABLE IF EXISTS web_app_icons") &&
198       db_.Execute("DROP TABLE IF EXISTS web_intents") &&
199       db_.Execute("DROP TABLE IF EXISTS web_intents_defaults") &&
200       transaction.Commit();
201 }
202