// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/webdata/common/web_database.h" #include <algorithm> #include "base/stl_util.h" #include "sql/statement.h" #include "sql/transaction.h" // Current version number. Note: when changing the current version number, // corresponding changes must happen in the unit tests, and new migration test // added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|. // static const int WebDatabase::kCurrentVersionNumber = 57; namespace { const int kCompatibleVersionNumber = 57; // Change the version number and possibly the compatibility version of // |meta_table_|. void ChangeVersion(sql::MetaTable* meta_table, int version_num, bool update_compatible_version_num) { meta_table->SetVersionNumber(version_num); if (update_compatible_version_num) { meta_table->SetCompatibleVersionNumber( std::min(version_num, kCompatibleVersionNumber)); } } // Outputs the failed version number as a warning and always returns // |sql::INIT_FAILURE|. sql::InitStatus FailedMigrationTo(int version_num) { LOG(WARNING) << "Unable to update web database to version " << version_num << "."; NOTREACHED(); return sql::INIT_FAILURE; } } // namespace WebDatabase::WebDatabase() {} WebDatabase::~WebDatabase() { } void WebDatabase::AddTable(WebDatabaseTable* table) { tables_[table->GetTypeKey()] = table; } WebDatabaseTable* WebDatabase::GetTable(WebDatabaseTable::TypeKey key) { return tables_[key]; } void WebDatabase::BeginTransaction() { db_.BeginTransaction(); } void WebDatabase::CommitTransaction() { db_.CommitTransaction(); } sql::Connection* WebDatabase::GetSQLConnection() { return &db_; } sql::InitStatus WebDatabase::Init(const base::FilePath& db_name) { db_.set_histogram_tag("Web"); // We don't store that much data in the tables so use a small page size. // This provides a large benefit for empty tables (which is very likely with // the tables we create). db_.set_page_size(2048); // We shouldn't have much data and what access we currently have is quite // infrequent. So we go with a small cache size. db_.set_cache_size(32); // Run the database in exclusive mode. Nobody else should be accessing the // database while we're running, and this will give somewhat improved perf. db_.set_exclusive_locking(); if (!db_.Open(db_name)) return sql::INIT_FAILURE; // Initialize various tables sql::Transaction transaction(&db_); if (!transaction.Begin()) return sql::INIT_FAILURE; // Version check. if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber)) return sql::INIT_FAILURE; if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { LOG(WARNING) << "Web database is too new."; return sql::INIT_TOO_NEW; } // Initialize the tables. for (TableMap::iterator it = tables_.begin(); it != tables_.end(); ++it) { it->second->Init(&db_, &meta_table_); } // If the file on disk is an older database version, bring it up to date. // If the migration fails we return an error to caller and do not commit // the migration. sql::InitStatus migration_status = MigrateOldVersionsAsNeeded(); if (migration_status != sql::INIT_OK) return migration_status; // Create the desired SQL tables if they do not already exist. // It's important that this happen *after* the migration code runs. // Otherwise, the migration code would have to explicitly check for empty // tables created in the new format, and skip the migration in that case. for (TableMap::iterator it = tables_.begin(); it != tables_.end(); ++it) { if (!it->second->CreateTablesIfNecessary()) { LOG(WARNING) << "Unable to initialize the web database."; return sql::INIT_FAILURE; } } return transaction.Commit() ? sql::INIT_OK : sql::INIT_FAILURE; } sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded() { // Some malware used to lower the version number, causing migration to // fail. Ensure the version number is at least as high as the compatible // version number. int current_version = std::max(meta_table_.GetVersionNumber(), meta_table_.GetCompatibleVersionNumber()); if (current_version > meta_table_.GetVersionNumber()) ChangeVersion(&meta_table_, current_version, false); if (current_version < 20) { // Versions 1 - 19 are unhandled. Version numbers greater than // kCurrentVersionNumber should have already been weeded out by the caller. // // When the version is too old, we return failure error code. The schema // is too out of date to migrate. // // There should not be a released product that makes a database too old to // migrate. If we do encounter such a legacy database, we will need a // better solution to handle it (i.e., pop up a dialog to tell the user, // erase all their prefs and start over, etc.). LOG(WARNING) << "Web database version " << current_version << " is too old to handle."; NOTREACHED(); return sql::INIT_FAILURE; } for (int next_version = current_version + 1; next_version <= kCurrentVersionNumber; ++next_version) { // Give each table a chance to migrate to this version. for (TableMap::iterator it = tables_.begin(); it != tables_.end(); ++it) { // Any of the tables may set this to true, but by default it is false. bool update_compatible_version = false; if (!it->second->MigrateToVersion(next_version, &update_compatible_version)) { return FailedMigrationTo(next_version); } ChangeVersion(&meta_table_, next_version, update_compatible_version); } } return sql::INIT_OK; }