OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/dom_storage/dom_storage_database.h" | 5 #include "webkit/dom_storage/dom_storage_database.h" |
6 | 6 |
7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "sql/diagnostic_error_delegate.h" | 9 #include "sql/diagnostic_error_delegate.h" |
10 #include "sql/statement.h" | 10 #include "sql/statement.h" |
11 #include "sql/transaction.h" | 11 #include "sql/transaction.h" |
12 #include "third_party/sqlite/sqlite3.h" | 12 #include "third_party/sqlite/sqlite3.h" |
13 | 13 |
14 namespace { | 14 namespace { |
15 | 15 |
16 const FilePath::CharType kJournal[] = FILE_PATH_LITERAL("-journal"); | 16 const FilePath::CharType kJournal[] = FILE_PATH_LITERAL("-journal"); |
17 | 17 |
18 class HistogramUniquifier { | 18 class HistogramUniquifier { |
19 public: | 19 public: |
20 static const char* name() { return "Sqlite.DomStorageDatabase.Error"; } | 20 static const char* name() { return "Sqlite.DomStorageDatabase.Error"; } |
21 }; | 21 }; |
22 | 22 |
23 sql::ErrorDelegate* GetErrorHandlerForDomStorageDatabase() { | 23 sql::ErrorDelegate* GetErrorHandlerForLocalStorageDatabase() { |
24 return new sql::DiagnosticErrorDelegate<HistogramUniquifier>(); | 24 return new sql::DiagnosticErrorDelegate<HistogramUniquifier>(); |
25 } | 25 } |
26 | 26 |
27 } // anon namespace | 27 } // anon namespace |
28 | 28 |
29 namespace dom_storage { | 29 namespace dom_storage { |
30 | 30 |
31 // static | 31 // static |
32 FilePath DomStorageDatabase::GetJournalFilePath( | 32 FilePath LocalStorageDatabase::GetJournalFilePath( |
33 const FilePath& database_path) { | 33 const FilePath& database_path) { |
34 FilePath::StringType journal_file_name = | 34 FilePath::StringType journal_file_name = |
35 database_path.BaseName().value() + kJournal; | 35 database_path.BaseName().value() + kJournal; |
36 return database_path.DirName().Append(journal_file_name); | 36 return database_path.DirName().Append(journal_file_name); |
37 } | 37 } |
38 | 38 |
39 DomStorageDatabase::DomStorageDatabase(const FilePath& file_path) | 39 DomStorageDatabase::DomStorageDatabase(const FilePath& file_path) |
40 : file_path_(file_path) { | 40 : file_path_(file_path), |
| 41 failed_to_open_(false) { } |
| 42 |
| 43 DomStorageDatabase::~DomStorageDatabase() { } |
| 44 |
| 45 LocalStorageDatabase::LocalStorageDatabase(const FilePath& file_path) |
| 46 : DomStorageDatabase(file_path) { |
41 // Note: in normal use we should never get an empty backing path here. | 47 // Note: in normal use we should never get an empty backing path here. |
42 // However, the unit test for this class can contruct an instance | 48 // However, the unit test for this class can contruct an instance |
43 // with an empty path. | 49 // with an empty path. |
44 Init(); | 50 Init(); |
45 } | 51 } |
46 | 52 |
47 DomStorageDatabase::DomStorageDatabase() { | 53 LocalStorageDatabase::LocalStorageDatabase() |
| 54 : DomStorageDatabase(FilePath(FILE_PATH_LITERAL(""))) { |
48 Init(); | 55 Init(); |
49 } | 56 } |
50 | 57 |
51 void DomStorageDatabase::Init() { | 58 void LocalStorageDatabase::Init() { |
52 failed_to_open_ = false; | 59 failed_to_open_ = false; |
53 tried_to_recreate_ = false; | 60 tried_to_recreate_ = false; |
54 known_to_be_empty_ = false; | 61 known_to_be_empty_ = false; |
55 } | 62 } |
56 | 63 |
57 DomStorageDatabase::~DomStorageDatabase() { | 64 LocalStorageDatabase::~LocalStorageDatabase() { |
58 if (known_to_be_empty_ && !file_path_.empty()) { | 65 if (known_to_be_empty_ && !file_path_.empty()) { |
59 // Delete the db and any lingering journal file from disk. | 66 // Delete the db and any lingering journal file from disk. |
60 Close(); | 67 Close(); |
61 file_util::Delete(file_path_, false); | 68 file_util::Delete(file_path_, false); |
62 file_util::Delete(GetJournalFilePath(file_path_), false); | 69 file_util::Delete(GetJournalFilePath(file_path_), false); |
63 } | 70 } |
64 } | 71 } |
65 | 72 |
66 void DomStorageDatabase::ReadAllValues(ValuesMap* result) { | 73 void LocalStorageDatabase::ReadAllValues( |
| 74 int64 namespace_id, const GURL& origin, ValuesMap* result) { |
67 if (!LazyOpen(false)) | 75 if (!LazyOpen(false)) |
68 return; | 76 return; |
69 | 77 |
70 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, | 78 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, |
71 "SELECT * from ItemTable")); | 79 "SELECT * from ItemTable")); |
72 DCHECK(statement.is_valid()); | 80 DCHECK(statement.is_valid()); |
73 | 81 |
74 while (statement.Step()) { | 82 while (statement.Step()) { |
75 string16 key = statement.ColumnString16(0); | 83 string16 key = statement.ColumnString16(0); |
76 string16 value; | 84 string16 value; |
77 statement.ColumnBlobAsString16(1, &value); | 85 statement.ColumnBlobAsString16(1, &value); |
78 (*result)[key] = NullableString16(value, false); | 86 (*result)[key] = NullableString16(value, false); |
79 } | 87 } |
80 known_to_be_empty_ = result->empty(); | 88 known_to_be_empty_ = result->empty(); |
81 } | 89 } |
82 | 90 |
83 bool DomStorageDatabase::CommitChanges(bool clear_all_first, | 91 bool LocalStorageDatabase::CommitChanges( |
84 const ValuesMap& changes) { | 92 int64 namespace_id, const GURL& origin, bool clear_all_first, |
| 93 const ValuesMap& changes) { |
85 if (!LazyOpen(!changes.empty())) { | 94 if (!LazyOpen(!changes.empty())) { |
86 // If we're being asked to commit changes that will result in an | 95 // If we're being asked to commit changes that will result in an |
87 // empty database, we return true if the database file doesn't exist. | 96 // empty database, we return true if the database file doesn't exist. |
88 return clear_all_first && changes.empty() && | 97 return clear_all_first && changes.empty() && |
89 !file_util::PathExists(file_path_); | 98 !file_util::PathExists(file_path_); |
90 } | 99 } |
91 | 100 |
92 bool old_known_to_be_empty = known_to_be_empty_; | 101 bool old_known_to_be_empty = known_to_be_empty_; |
93 sql::Transaction transaction(db_.get()); | 102 sql::Transaction transaction(db_.get()); |
94 if (!transaction.Begin()) | 103 if (!transaction.Begin()) |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 if (statement.Step()) | 140 if (statement.Step()) |
132 known_to_be_empty_ = statement.ColumnInt(0) == 0; | 141 known_to_be_empty_ = statement.ColumnInt(0) == 0; |
133 } | 142 } |
134 | 143 |
135 bool success = transaction.Commit(); | 144 bool success = transaction.Commit(); |
136 if (!success) | 145 if (!success) |
137 known_to_be_empty_ = old_known_to_be_empty; | 146 known_to_be_empty_ = old_known_to_be_empty; |
138 return success; | 147 return success; |
139 } | 148 } |
140 | 149 |
141 bool DomStorageDatabase::LazyOpen(bool create_if_needed) { | 150 bool LocalStorageDatabase::LazyOpen(bool create_if_needed) { |
142 if (failed_to_open_) { | 151 if (failed_to_open_) { |
143 // Don't try to open a database that we know has failed | 152 // Don't try to open a database that we know has failed |
144 // already. | 153 // already. |
145 return false; | 154 return false; |
146 } | 155 } |
147 | 156 |
148 if (IsOpen()) | 157 if (IsOpen()) |
149 return true; | 158 return true; |
150 | 159 |
151 bool database_exists = file_util::PathExists(file_path_); | 160 bool database_exists = file_util::PathExists(file_path_); |
152 | 161 |
153 if (!database_exists && !create_if_needed) { | 162 if (!database_exists && !create_if_needed) { |
154 // If the file doesn't exist already and we haven't been asked to create | 163 // If the file doesn't exist already and we haven't been asked to create |
155 // a file on disk, then we don't bother opening the database. This means | 164 // a file on disk, then we don't bother opening the database. This means |
156 // we wait until we absolutely need to put something onto disk before we | 165 // we wait until we absolutely need to put something onto disk before we |
157 // do so. | 166 // do so. |
158 return false; | 167 return false; |
159 } | 168 } |
160 | 169 |
161 db_.reset(new sql::Connection()); | 170 db_.reset(new sql::Connection()); |
162 db_->set_error_delegate(GetErrorHandlerForDomStorageDatabase()); | 171 db_->set_error_delegate(GetErrorHandlerForLocalStorageDatabase()); |
163 | 172 |
164 if (file_path_.empty()) { | 173 if (file_path_.empty()) { |
165 // This code path should only be triggered by unit tests. | 174 // This code path should only be triggered by unit tests. |
166 if (!db_->OpenInMemory()) { | 175 if (!db_->OpenInMemory()) { |
167 NOTREACHED() << "Unable to open DOM storage database in memory."; | 176 NOTREACHED() << "Unable to open DOM storage database in memory."; |
168 failed_to_open_ = true; | 177 failed_to_open_ = true; |
169 return false; | 178 return false; |
170 } | 179 } |
171 } else { | 180 } else { |
172 if (!db_->Open(file_path_)) { | 181 if (!db_->Open(file_path_)) { |
(...skipping 27 matching lines...) Expand all Loading... |
200 return true; | 209 return true; |
201 } | 210 } |
202 } | 211 } |
203 | 212 |
204 // This is the exceptional case - to try and recover we'll attempt | 213 // This is the exceptional case - to try and recover we'll attempt |
205 // to delete the file and start again. | 214 // to delete the file and start again. |
206 Close(); | 215 Close(); |
207 return DeleteFileAndRecreate(); | 216 return DeleteFileAndRecreate(); |
208 } | 217 } |
209 | 218 |
210 DomStorageDatabase::SchemaVersion DomStorageDatabase::DetectSchemaVersion() { | 219 LocalStorageDatabase::SchemaVersion |
| 220 LocalStorageDatabase::DetectSchemaVersion() { |
211 DCHECK(IsOpen()); | 221 DCHECK(IsOpen()); |
212 | 222 |
213 // Connection::Open() may succeed even if the file we try and open is not a | 223 // Connection::Open() may succeed even if the file we try and open is not a |
214 // database, however in the case that the database is corrupted to the point | 224 // database, however in the case that the database is corrupted to the point |
215 // that SQLite doesn't actually think it's a database, | 225 // that SQLite doesn't actually think it's a database, |
216 // sql::Connection::GetCachedStatement will DCHECK when we later try and | 226 // sql::Connection::GetCachedStatement will DCHECK when we later try and |
217 // run statements. So we run a query here that will not DCHECK but fail | 227 // run statements. So we run a query here that will not DCHECK but fail |
218 // on an invalid database to verify that what we've opened is usable. | 228 // on an invalid database to verify that what we've opened is usable. |
219 if (db_->ExecuteAndReturnErrorCode("PRAGMA auto_vacuum") != SQLITE_OK) | 229 if (db_->ExecuteAndReturnErrorCode("PRAGMA auto_vacuum") != SQLITE_OK) |
220 return INVALID; | 230 return INVALID; |
(...skipping 15 matching lines...) Expand all Loading... |
236 return V2; | 246 return V2; |
237 case sql::COLUMN_TYPE_TEXT: | 247 case sql::COLUMN_TYPE_TEXT: |
238 return V1; | 248 return V1; |
239 default: | 249 default: |
240 return INVALID; | 250 return INVALID; |
241 } | 251 } |
242 NOTREACHED(); | 252 NOTREACHED(); |
243 return INVALID; | 253 return INVALID; |
244 } | 254 } |
245 | 255 |
246 bool DomStorageDatabase::CreateTableV2() { | 256 bool LocalStorageDatabase::CreateTableV2() { |
247 DCHECK(IsOpen()); | 257 DCHECK(IsOpen()); |
248 | 258 |
249 return db_->Execute( | 259 return db_->Execute( |
250 "CREATE TABLE ItemTable (" | 260 "CREATE TABLE ItemTable (" |
251 "key TEXT UNIQUE ON CONFLICT REPLACE, " | 261 "key TEXT UNIQUE ON CONFLICT REPLACE, " |
252 "value BLOB NOT NULL ON CONFLICT FAIL)"); | 262 "value BLOB NOT NULL ON CONFLICT FAIL)"); |
253 } | 263 } |
254 | 264 |
255 bool DomStorageDatabase::DeleteFileAndRecreate() { | 265 bool LocalStorageDatabase::DeleteFileAndRecreate() { |
256 DCHECK(!IsOpen()); | 266 DCHECK(!IsOpen()); |
257 DCHECK(file_util::PathExists(file_path_)); | 267 DCHECK(file_util::PathExists(file_path_)); |
258 | 268 |
259 // We should only try and do this once. | 269 // We should only try and do this once. |
260 if (tried_to_recreate_) | 270 if (tried_to_recreate_) |
261 return false; | 271 return false; |
262 | 272 |
263 tried_to_recreate_ = true; | 273 tried_to_recreate_ = true; |
264 | 274 |
265 // If it's not a directory and we can delete the file, try and open it again. | 275 // If it's not a directory and we can delete the file, try and open it again. |
266 if (!file_util::DirectoryExists(file_path_) && | 276 if (!file_util::DirectoryExists(file_path_) && |
267 file_util::Delete(file_path_, false)) | 277 file_util::Delete(file_path_, false)) |
268 return LazyOpen(true); | 278 return LazyOpen(true); |
269 | 279 |
270 failed_to_open_ = true; | 280 failed_to_open_ = true; |
271 return false; | 281 return false; |
272 } | 282 } |
273 | 283 |
274 bool DomStorageDatabase::UpgradeVersion1To2() { | 284 bool LocalStorageDatabase::UpgradeVersion1To2() { |
275 DCHECK(IsOpen()); | 285 DCHECK(IsOpen()); |
276 DCHECK(DetectSchemaVersion() == V1); | 286 DCHECK(DetectSchemaVersion() == V1); |
277 | 287 |
278 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, | 288 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, |
279 "SELECT * FROM ItemTable")); | 289 "SELECT * FROM ItemTable")); |
280 DCHECK(statement.is_valid()); | 290 DCHECK(statement.is_valid()); |
281 | 291 |
282 // Need to migrate from TEXT value column to BLOB. | 292 // Need to migrate from TEXT value column to BLOB. |
283 // Store the current database content so we can re-insert | 293 // Store the current database content so we can re-insert |
284 // the data into the new V2 table. | 294 // the data into the new V2 table. |
285 ValuesMap values; | 295 ValuesMap values; |
286 while (statement.Step()) { | 296 while (statement.Step()) { |
287 string16 key = statement.ColumnString16(0); | 297 string16 key = statement.ColumnString16(0); |
288 NullableString16 value(statement.ColumnString16(1), false); | 298 NullableString16 value(statement.ColumnString16(1), false); |
289 values[key] = value; | 299 values[key] = value; |
290 } | 300 } |
291 | 301 |
292 sql::Transaction migration(db_.get()); | 302 sql::Transaction migration(db_.get()); |
293 return migration.Begin() && | 303 return migration.Begin() && |
294 db_->Execute("DROP TABLE ItemTable") && | 304 db_->Execute("DROP TABLE ItemTable") && |
295 CreateTableV2() && | 305 CreateTableV2() && |
296 CommitChanges(false, values) && | 306 CommitChanges(0, GURL(""), false, values) && |
297 migration.Commit(); | 307 migration.Commit(); |
298 } | 308 } |
299 | 309 |
300 void DomStorageDatabase::Close() { | 310 bool LocalStorageDatabase::IsOpen() const { |
| 311 return db_.get() ? db_->is_open() : false; |
| 312 } |
| 313 |
| 314 void LocalStorageDatabase::Close() { |
301 db_.reset(NULL); | 315 db_.reset(NULL); |
302 } | 316 } |
303 | 317 |
304 } // namespace dom_storage | 318 } // namespace dom_storage |
OLD | NEW |