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_|.
ChangeVersion(sql::MetaTable * meta_table,int version_num,bool update_compatible_version_num)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|.
FailedMigrationTo(int version_num)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
WebDatabase()45 WebDatabase::WebDatabase() {}
46
~WebDatabase()47 WebDatabase::~WebDatabase() {}
48
BeginTransaction()49 void WebDatabase::BeginTransaction() {
50 db_.BeginTransaction();
51 }
52
CommitTransaction()53 void WebDatabase::CommitTransaction() {
54 db_.CommitTransaction();
55 }
56
GetAutofillTable()57 AutofillTable* WebDatabase::GetAutofillTable() {
58 return autofill_table_.get();
59 }
60
GetKeywordTable()61 KeywordTable* WebDatabase::GetKeywordTable() {
62 return keyword_table_.get();
63 }
64
GetLoginsTable()65 LoginsTable* WebDatabase::GetLoginsTable() {
66 return logins_table_.get();
67 }
68
GetTokenServiceTable()69 TokenServiceTable* WebDatabase::GetTokenServiceTable() {
70 return token_service_table_.get();
71 }
72
GetWebAppsTable()73 WebAppsTable* WebDatabase::GetWebAppsTable() {
74 return web_apps_table_.get();
75 }
76
GetSQLConnection()77 sql::Connection* WebDatabase::GetSQLConnection() {
78 return &db_;
79 }
80
Init(const FilePath & db_name)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
MigrateOldVersionsAsNeeded()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