1 // Copyright (c) 2011 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 "chrome/browser/webdata/web_database.h" 6 7 #include <algorithm> 8 9 #include "app/sql/statement.h" 10 #include "app/sql/transaction.h" 11 #include "chrome/browser/diagnostics/sqlite_diagnostics.h" 12 #include "content/common/notification_service.h" 13 14 namespace { 15 16 // Current version number. Note: when changing the current version number, 17 // corresponding changes must happen in the unit tests, and new migration test 18 // added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|. 19 const int kCurrentVersionNumber = 37; 20 const int kCompatibleVersionNumber = 37; 21 22 // Change the version number and possibly the compatibility version of 23 // |meta_table_|. 24 void ChangeVersion(sql::MetaTable* meta_table, 25 int version_num, 26 bool update_compatible_version_num) { 27 meta_table->SetVersionNumber(version_num); 28 if (update_compatible_version_num) { 29 meta_table->SetCompatibleVersionNumber( 30 std::min(version_num, kCompatibleVersionNumber)); 31 } 32 } 33 34 // Outputs the failed version number as a warning and always returns 35 // |sql::INIT_FAILURE|. 36 sql::InitStatus FailedMigrationTo(int version_num) { 37 LOG(WARNING) << "Unable to update web database to version " 38 << version_num << "."; 39 NOTREACHED(); 40 return sql::INIT_FAILURE; 41 } 42 43 } // namespace 44 45 WebDatabase::WebDatabase() {} 46 47 WebDatabase::~WebDatabase() {} 48 49 void WebDatabase::BeginTransaction() { 50 db_.BeginTransaction(); 51 } 52 53 void WebDatabase::CommitTransaction() { 54 db_.CommitTransaction(); 55 } 56 57 AutofillTable* WebDatabase::GetAutofillTable() { 58 return autofill_table_.get(); 59 } 60 61 KeywordTable* WebDatabase::GetKeywordTable() { 62 return keyword_table_.get(); 63 } 64 65 LoginsTable* WebDatabase::GetLoginsTable() { 66 return logins_table_.get(); 67 } 68 69 TokenServiceTable* WebDatabase::GetTokenServiceTable() { 70 return token_service_table_.get(); 71 } 72 73 WebAppsTable* WebDatabase::GetWebAppsTable() { 74 return web_apps_table_.get(); 75 } 76 77 sql::Connection* WebDatabase::GetSQLConnection() { 78 return &db_; 79 } 80 81 sql::InitStatus WebDatabase::Init(const FilePath& db_name) { 82 // When running in unit tests, there is already a NotificationService object. 83 // Since only one can exist at a time per thread, check first. 84 if (!NotificationService::current()) 85 notification_service_.reset(new NotificationService); 86 87 // Set the exceptional sqlite error handler. 88 db_.set_error_delegate(GetErrorHandlerForWebDb()); 89 90 // We don't store that much data in the tables so use a small page size. 91 // This provides a large benefit for empty tables (which is very likely with 92 // the tables we create). 93 db_.set_page_size(2048); 94 95 // We shouldn't have much data and what access we currently have is quite 96 // infrequent. So we go with a small cache size. 97 db_.set_cache_size(32); 98 99 // Run the database in exclusive mode. Nobody else should be accessing the 100 // database while we're running, and this will give somewhat improved perf. 101 db_.set_exclusive_locking(); 102 103 if (!db_.Open(db_name)) 104 return sql::INIT_FAILURE; 105 106 // Initialize various tables 107 sql::Transaction transaction(&db_); 108 if (!transaction.Begin()) 109 return sql::INIT_FAILURE; 110 111 // Version check. 112 if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber)) 113 return sql::INIT_FAILURE; 114 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { 115 LOG(WARNING) << "Web database is too new."; 116 return sql::INIT_TOO_NEW; 117 } 118 119 // Create the tables. 120 autofill_table_.reset(new AutofillTable(&db_, &meta_table_)); 121 keyword_table_.reset(new KeywordTable(&db_, &meta_table_)); 122 logins_table_.reset(new LoginsTable(&db_, &meta_table_)); 123 token_service_table_.reset(new TokenServiceTable(&db_, &meta_table_)); 124 web_apps_table_.reset(new WebAppsTable(&db_, &meta_table_)); 125 126 // Initialize the tables. 127 if (!keyword_table_->Init() || !autofill_table_->Init() || 128 !logins_table_->Init() || !web_apps_table_->Init() || 129 !token_service_table_->Init()) { 130 LOG(WARNING) << "Unable to initialize the web database."; 131 return sql::INIT_FAILURE; 132 } 133 134 // If the file on disk is an older database version, bring it up to date. 135 // If the migration fails we return an error to caller and do not commit 136 // the migration. 137 sql::InitStatus migration_status = MigrateOldVersionsAsNeeded(); 138 if (migration_status != sql::INIT_OK) 139 return migration_status; 140 141 return transaction.Commit() ? sql::INIT_OK : sql::INIT_FAILURE; 142 } 143 144 sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded() { 145 // Migrate if necessary. 146 int current_version = meta_table_.GetVersionNumber(); 147 switch (current_version) { 148 // Versions 1 - 19 are unhandled. Version numbers greater than 149 // kCurrentVersionNumber should have already been weeded out by the caller. 150 default: 151 // When the version is too old, we return failure error code. The schema 152 // is too out of date to migrate. 153 // There should not be a released product that makes a database too old to 154 // migrate. If we do encounter such a legacy database, we will need a 155 // better solution to handle it (i.e., pop up a dialog to tell the user, 156 // erase all their prefs and start over, etc.). 157 LOG(WARNING) << "Web database version " << current_version << 158 " is too old to handle."; 159 NOTREACHED(); 160 return sql::INIT_FAILURE; 161 162 case 20: 163 if (!keyword_table_->MigrateToVersion21AutoGenerateKeywordColumn()) 164 return FailedMigrationTo(21); 165 166 ChangeVersion(&meta_table_, 21, true); 167 // FALL THROUGH 168 169 case 21: 170 if (!autofill_table_->ClearAutofillEmptyValueElements()) 171 return FailedMigrationTo(22); 172 173 ChangeVersion(&meta_table_, 22, false); 174 // FALL THROUGH 175 176 case 22: 177 if (!autofill_table_->MigrateToVersion23AddCardNumberEncryptedColumn()) 178 return FailedMigrationTo(23); 179 180 ChangeVersion(&meta_table_, 23, false); 181 // FALL THROUGH 182 183 case 23: 184 if (!autofill_table_->MigrateToVersion24CleanupOversizedStringFields()) 185 return FailedMigrationTo(24); 186 187 ChangeVersion(&meta_table_, 24, false); 188 // FALL THROUGH 189 190 case 24: 191 if (!keyword_table_->MigrateToVersion25AddLogoIDColumn()) 192 return FailedMigrationTo(25); 193 194 ChangeVersion(&meta_table_, 25, true); 195 // FALL THROUGH 196 197 case 25: 198 if (!keyword_table_->MigrateToVersion26AddCreatedByPolicyColumn()) 199 return FailedMigrationTo(26); 200 201 ChangeVersion(&meta_table_, 26, true); 202 // FALL THROUGH 203 204 case 26: 205 if (!autofill_table_->MigrateToVersion27UpdateLegacyCreditCards()) 206 return FailedMigrationTo(27); 207 208 ChangeVersion(&meta_table_, 27, true); 209 // FALL THROUGH 210 211 case 27: 212 if (!keyword_table_->MigrateToVersion28SupportsInstantColumn()) 213 return FailedMigrationTo(28); 214 215 ChangeVersion(&meta_table_, 28, true); 216 // FALL THROUGH 217 218 case 28: 219 if (!keyword_table_->MigrateToVersion29InstantUrlToSupportsInstant()) 220 return FailedMigrationTo(29); 221 222 ChangeVersion(&meta_table_, 29, true); 223 // FALL THROUGH 224 225 case 29: 226 if (!autofill_table_->MigrateToVersion30AddDateModifed()) 227 return FailedMigrationTo(30); 228 229 ChangeVersion(&meta_table_, 30, true); 230 // FALL THROUGH 231 232 case 30: 233 if (!autofill_table_->MigrateToVersion31AddGUIDToCreditCardsAndProfiles()) 234 return FailedMigrationTo(31); 235 236 ChangeVersion(&meta_table_, 31, true); 237 // FALL THROUGH 238 239 case 31: 240 if (!autofill_table_->MigrateToVersion32UpdateProfilesAndCreditCards()) 241 return FailedMigrationTo(32); 242 243 ChangeVersion(&meta_table_, 32, true); 244 // FALL THROUGH 245 246 case 32: 247 if (!autofill_table_->MigrateToVersion33ProfilesBasedOnFirstName()) 248 return FailedMigrationTo(33); 249 250 ChangeVersion(&meta_table_, 33, true); 251 // FALL THROUGH 252 253 case 33: 254 if (!autofill_table_->MigrateToVersion34ProfilesBasedOnCountryCode()) 255 return FailedMigrationTo(34); 256 257 ChangeVersion(&meta_table_, 34, true); 258 // FALL THROUGH 259 260 case 34: 261 if (!autofill_table_->MigrateToVersion35GreatBritainCountryCodes()) 262 return FailedMigrationTo(35); 263 264 ChangeVersion(&meta_table_, 35, true); 265 // FALL THROUGH 266 267 // Combine migrations 35 and 36. This is due to enhancements to the merge 268 // step when migrating profiles. The original migration from 35 to 36 did 269 // not merge profiles with identical addresses, but the migration from 36 to 270 // 37 does. The step from 35 to 36 should only happen on the Chrome 12 dev 271 // channel. Chrome 12 beta and release users will jump from 35 to 37 272 // directly getting the full benefits of the multi-valued merge as well as 273 // the culling of bad data. 274 case 35: 275 case 36: 276 if (!autofill_table_->MigrateToVersion37MergeAndCullOlderProfiles()) 277 return FailedMigrationTo(37); 278 279 ChangeVersion(&meta_table_, 37, true); 280 // FALL THROUGH 281 282 // Add successive versions here. Each should set the version number and 283 // compatible version number as appropriate, then fall through to the next 284 // case. 285 286 case kCurrentVersionNumber: 287 // No migration needed. 288 return sql::INIT_OK; 289 } 290 } 291