• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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