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_|. 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|. 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 46 WebDatabase::WebDatabase() {} 47 48 WebDatabase::~WebDatabase() { 49 } 50 51 void WebDatabase::AddTable(WebDatabaseTable* table) { 52 tables_[table->GetTypeKey()] = table; 53 } 54 55 WebDatabaseTable* WebDatabase::GetTable(WebDatabaseTable::TypeKey key) { 56 return tables_[key]; 57 } 58 59 void WebDatabase::BeginTransaction() { 60 db_.BeginTransaction(); 61 } 62 63 void WebDatabase::CommitTransaction() { 64 db_.CommitTransaction(); 65 } 66 67 sql::Connection* WebDatabase::GetSQLConnection() { 68 return &db_; 69 } 70 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 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