| OLD | NEW |
| (Empty) |
| 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 "chrome/browser/webdata/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 | |
| 18 const int WebDatabase::kCurrentVersionNumber = 49; | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 const int kCompatibleVersionNumber = 48; | |
| 23 | |
| 24 // Change the version number and possibly the compatibility version of | |
| 25 // |meta_table_|. | |
| 26 void 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|. | |
| 38 sql::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 | |
| 47 WebDatabase::WebDatabase() {} | |
| 48 | |
| 49 WebDatabase::~WebDatabase() { | |
| 50 } | |
| 51 | |
| 52 void WebDatabase::AddTable(WebDatabaseTable* table) { | |
| 53 tables_[table->GetTypeKey()] = table; | |
| 54 } | |
| 55 | |
| 56 WebDatabaseTable* WebDatabase::GetTable(WebDatabaseTable::TypeKey key) { | |
| 57 return tables_[key]; | |
| 58 } | |
| 59 | |
| 60 void WebDatabase::BeginTransaction() { | |
| 61 db_.BeginTransaction(); | |
| 62 } | |
| 63 | |
| 64 void WebDatabase::CommitTransaction() { | |
| 65 db_.CommitTransaction(); | |
| 66 } | |
| 67 | |
| 68 sql::Connection* WebDatabase::GetSQLConnection() { | |
| 69 return &db_; | |
| 70 } | |
| 71 | |
| 72 sql::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 | |
| 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(); | |
| 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 } | |
| OLD | NEW |