Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(353)

Side by Side Diff: webkit/dom_storage/dom_storage_database.cc

Issue 9963107: Persist sessionStorage on disk. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698