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.
5 #include "components/webdata/common/web_database.h"
9 #include "base/stl_util.h"
10 #include "sql/statement.h"
11 #include "sql/transaction.h"
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|.
17 const int WebDatabase::kCurrentVersionNumber
= 56;
21 const int kCompatibleVersionNumber
= 56;
23 // Change the version number and possibly the compatibility version of
25 void ChangeVersion(sql::MetaTable
* meta_table
,
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
));
35 // Outputs the failed version number as a warning and always returns
36 // |sql::INIT_FAILURE|.
37 sql::InitStatus
FailedMigrationTo(int version_num
) {
38 LOG(WARNING
) << "Unable to update web database to version "
39 << version_num
<< ".";
41 return sql::INIT_FAILURE
;
46 WebDatabase::WebDatabase() {}
48 WebDatabase::~WebDatabase() {
51 void WebDatabase::AddTable(WebDatabaseTable
* table
) {
52 tables_
[table
->GetTypeKey()] = table
;
55 WebDatabaseTable
* WebDatabase::GetTable(WebDatabaseTable::TypeKey key
) {
59 void WebDatabase::BeginTransaction() {
60 db_
.BeginTransaction();
63 void WebDatabase::CommitTransaction() {
64 db_
.CommitTransaction();
67 sql::Connection
* WebDatabase::GetSQLConnection() {
71 sql::InitStatus
WebDatabase::Init(const base::FilePath
& db_name
) {
72 db_
.set_histogram_tag("Web");
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);
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);
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();
87 if (!db_
.Open(db_name
))
88 return sql::INIT_FAILURE
;
90 // Initialize various tables
91 sql::Transaction
transaction(&db_
);
92 if (!transaction
.Begin())
93 return sql::INIT_FAILURE
;
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
;
103 // Initialize the tables.
104 for (TableMap::iterator it
= tables_
.begin(); it
!= tables_
.end(); ++it
) {
105 it
->second
->Init(&db_
, &meta_table_
);
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
111 sql::InitStatus migration_status
= MigrateOldVersionsAsNeeded();
112 if (migration_status
!= sql::INIT_OK
)
113 return migration_status
;
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
;
126 return transaction
.Commit() ? sql::INIT_OK
: sql::INIT_FAILURE
;
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
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);
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.
142 // When the version is too old, we return failure error code. The schema
143 // is too out of date to migrate.
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.";
152 return sql::INIT_FAILURE
;
155 for (int next_version
= current_version
+ 1;
156 next_version
<= kCurrentVersionNumber
;
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
);
167 ChangeVersion(&meta_table_
, next_version
, update_compatible_version
);