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