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

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

Issue 9159020: Create a class to represent a DOM Storage Database. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Address comments and add handling of a corrupt database file in LazyOpen Created 8 years, 10 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
OLDNEW
(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 "webkit/dom_storage/dom_storage_database.h"
6
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "sql/diagnostic_error_delegate.h"
10 #include "sql/statement.h"
11 #include "sql/transaction.h"
12 #include "third_party/sqlite/sqlite3.h"
13
14 namespace {
15
16 class HistogramUniquifier {
17 public:
18 static const char* name() { return "Sqlite.DomStorageDatabase.Error"; }
19 };
20
21 sql::ErrorDelegate* GetErrorHandlerForDomStorageDatabase() {
22 return new sql::DiagnosticErrorDelegate<HistogramUniquifier>();
23 }
24
25 } // anon namespace
26
27 namespace dom_storage {
28
29 DomStorageDatabase::DomStorageDatabase(const FilePath& file_path)
30 : file_path_(file_path),
31 db_(NULL) {
32 DCHECK(!file_path_.empty());
33 }
34
35 DomStorageDatabase::~DomStorageDatabase() {
36 }
37
38 void DomStorageDatabase::ReadAllValues(
39 DomStorageDatabase::ValuesMap* result) {
40 if (!LazyOpen(false))
41 return;
42
43 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
44 "SELECT * from ItemTable"));
45 DCHECK(statement.is_valid());
46
47 while (statement.Step()) {
48 string16 key = statement.ColumnString16(0);
49 string16 value;
50 statement.ColumnBlobAsString16(1, &value);
51 (*result)[key] = NullableString16(value, false);
52 }
53 }
54
55 bool DomStorageDatabase::CommitChanges(bool clear_all_first,
56 const ValuesMap& changes) {
57 if (!LazyOpen(!changes.empty()))
58 return false;
59
60 sql::Transaction transaction(db_.get());
61 if (!transaction.Begin())
62 return false;
63
64 if (clear_all_first) {
65 if (!db_->Execute("DELETE FROM ItemTable"))
66 return false;
67 }
68
69 ValuesMap::const_iterator it = changes.begin();
70 for(; it != changes.end(); ++it) {
71 sql::Statement statement;
72 string16 key = it->first;
73 NullableString16 value = it->second;
74 if (value.is_null()) {
75 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
76 "DELETE FROM ItemTable WHERE key=?"));
77 statement.BindString16(0, key);
78 } else {
79 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
80 "INSERT INTO ItemTable VALUES (?,?)"));
81 statement.BindString16(0, key);
82 statement.BindBlob(1, value.string().data(),
83 value.string().length() * sizeof(char16));
84 }
85 DCHECK(statement.is_valid());
86 statement.Run();
87 }
88 return transaction.Commit();
89 }
90
91 bool DomStorageDatabase::LazyOpen(bool create_if_needed) {
92 if (IsOpen())
93 return true;
94
95 bool database_exists = file_util::PathExists(file_path_);
96
97 if (!database_exists && !create_if_needed) {
98 // If the file doesn't exist already and we haven't been asked to create
99 // a file on disk, then we don't bother opening the database. This means
100 // we wait until we absolutely need to put something onto disk before we
101 // do so.
102 return false;
103 }
104
105 db_.reset(new sql::Connection());
106 db_->set_error_delegate(GetErrorHandlerForDomStorageDatabase());
107 if (!db_->Open(file_path_)) {
108 // TODO(benm): We should handle the error condition better. At the very
109 // least we should set a flag so that we don't try to repeatedly open
110 // the database each time LazyOpen is called. We may want to try and
111 // delete the file and start from scratch.
112 LOG(ERROR) << "Unable to open DOM storage database at "
113 << file_path_.value()
114 << " error: " << db_->GetErrorMessage();
115 return false;
116 }
117
118 // Open() may succeed even if the file we try and open is not a database so
119 // we check here that what we've got is something sensible.
120 // TODO(benm): It might be useful to actually verify the output of the
121 // quick_check too and try to recover in the case errors are detected.
122 // However, in the case that the database is corrupted to the point that
123 // SQLite doesn't actually think it's a database,
124 // sql::Connection::GetCachedStatement will DCHECK.
125 if (db_->ExecuteAndReturnErrorCode("PRAGMA quick_check(1)") != SQLITE_OK) {
michaeln 2012/02/04 00:21:34 oh... this is probably a good idea, but i wonder h
benm (inactive) 2012/02/06 14:02:36 As it stands at the moment, we're executing the qu
126 // TODO(benm): What to do in this case? Delete the file and try again?
127 Close();
128 return false;
129 }
130
131 // sql::Connection uses UTF-8 encoding, but WebCore style databases use
132 // UTF-16, so ensure we match.
133 ignore_result(db_->Execute("PRAGMA encoding=\"UTF-16\""));
michaeln 2012/02/04 00:21:34 does this pragma need to be applied prior to the p
benm (inactive) 2012/02/06 14:02:36 Mm, good question. The SQLite docs aren't very cle
134
135 // If the table doesn't exist, try to create it at the current version.
136 if (!db_->DoesTableExist("ItemTable")) {
137 if (CreateTable())
138 return true;
139 // Couldn't create the table.
140 // TODO(benm): Handle this better. Delete the file on disk and try again?
141 Close();
142 return false;
143 }
144
145 // Table exists, so ensure we're at the right version, upgrading if
146 // necessary.
147 if (UpgradeVersion1To2IfNeeded())
148 return true;
149
150 // Upgrade failed, drop table and start fresh.
151 sql::Transaction recreation(db_.get());
152 if (recreation.Begin()) {
153 if (db_->Execute("DROP TABLE ItemTable")) {
154 if (CreateTable()) {
155 if (recreation.Commit())
156 return true;
157 }
158 }
159 }
160
161 // We were not able to verify that the database is at the correct version
162 // so close the connection.
163 // TODO(benm): Handle this better. Delete the file on disk and try again?
164 Close();
165 return false;
166 }
167
168 bool DomStorageDatabase::CreateTable() {
169 DCHECK(IsOpen());
170 // Current version is 2.
171 return CreateTableV2();
172 }
173
174 bool DomStorageDatabase::CreateTableV2() {
175 DCHECK(IsOpen());
176
177 return db_->Execute(
178 "CREATE TABLE IF NOT EXISTS ItemTable ("
179 "key TEXT UNIQUE ON CONFLICT REPLACE, "
180 "value BLOB NOT NULL ON CONFLICT FAIL)");
181 }
182
183 bool DomStorageDatabase::UpgradeVersion1To2IfNeeded() {
184 DCHECK(IsOpen());
185 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
186 "SELECT * FROM ItemTable"));
187 DCHECK(statement.is_valid());
188
189 // Quick check to see if we need to upgrade or not. The single
190 // effect of V1 -> V2 is to change the value column from type
191 // TEXT to type BLOB.
192 sql::ColType value_column_type = statement.DeclaredColumnType(1);
193 if (value_column_type == sql::COLUMN_TYPE_BLOB)
194 return true;
195
196 if (value_column_type != sql::COLUMN_TYPE_TEXT) {
197 // Something is messed up. This is not a V1 database.
198 return false;
199 }
200
201 // Need to migrate from TEXT value column to BLOB.
202 // Store the current database content so we can re-insert
203 // the data into the new V2 table.
204 ValuesMap values;
205 while (statement.Step()) {
206 string16 key = statement.ColumnString16(0);
207 NullableString16 value(statement.ColumnString16(1), false);
208 values[key] = value;
209 }
210
211 sql::Transaction migration(db_.get());
212 if (!migration.Begin())
213 return false;
214
215 if (db_->Execute("DROP TABLE ItemTable")) {
216 CreateTableV2();
217 if (CommitChanges(false, values))
218 return migration.Commit();
219 }
220 return false;
221 }
222
223 void DomStorageDatabase::Close() {
224 db_.reset(NULL);
225 }
226
227 } // namespace dom_storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698