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

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: Remove some unused includes. 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
13 namespace {
14
15 class HistogramUniquifier {
16 public:
17 static const char* name() { return "Sqlite.DomStorageDatabase.Error"; }
18 };
19
20 sql::ErrorDelegate* GetErrorHandlerForDomStorageDatabase() {
21 return new sql::DiagnosticErrorDelegate<HistogramUniquifier>();
22 }
23
24 } // anon namespace
25
26 namespace dom_storage {
27
28 DomStorageDatabase::DomStorageDatabase(const FilePath& file_path)
29 : file_path_(file_path),
30 db_(NULL) {
31 DCHECK(!file_path_.empty());
32 }
33
34 void DomStorageDatabase::ReadAllValues(
35 DomStorageDatabase::ValuesMap* result) {
36 if (!LazyOpen(false))
37 return;
38
39 sql::Statement stmt(db_->GetCachedStatement(SQL_FROM_HERE,
40 "SELECT * from ItemTable"));
41 DCHECK(stmt.is_valid());
42 if (!stmt)
michaeln 2012/02/01 21:03:11 i'm looking at http://codereview.chromium.org/9249
benm (inactive) 2012/02/02 12:14:49 Removed the early returns but kept DCHECK, as I th
43 return;
44
45 while (stmt.Step()) {
46 string16 key = stmt.ColumnString16(0);
47 string16 value;
48 stmt.ColumnBlobAsString16(1, &value);
49 (*result)[key] = NullableString16(value, false);
50 }
51 }
52
53 bool DomStorageDatabase::CommitChanges(bool clear_all_first,
54 const ValuesMap& changes) {
55 if (!LazyOpen(!changes.empty()))
56 return false;
57
58 sql::Transaction transaction(db_.get());
59 if (!transaction.Begin())
60 return false;
61
62 if (clear_all_first) {
63 if (!db_->Execute("DELETE FROM ItemTable"))
64 return false;
65 }
66
67 ValuesMap::const_iterator it = changes.begin();
68 sql::Statement stmt;
michaeln 2012/02/01 21:03:11 can this be moved into the body of the for loop? a
benm (inactive) 2012/02/02 12:14:49 Done, updated stmt everywhere
69 for(; it != changes.end(); ++it) {
70 string16 key = it->first;
71 NullableString16 value = it->second;
72 if (value.is_null()) {
73 stmt.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
74 "DELETE FROM ItemTable WHERE key=?"));
75 stmt.BindString16(0, key);
76 } else {
77 stmt.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
78 "INSERT INTO ItemTable VALUES (?,?)"));
79 stmt.BindString16(0, key);
80 string16 value_str = value.string();
michaeln 2012/02/01 21:03:11 can the local be removed to avoid making a copy
benm (inactive) 2012/02/02 12:14:49 Done.
81 stmt.BindBlob(1, value_str.data(), value_str.length() * sizeof(char16));
82 }
83 DCHECK(stmt.is_valid());
84 stmt.Run();
85 }
86 return transaction.Commit();
87 }
88
89 bool DomStorageDatabase::LazyOpen(bool create_if_needed) {
90 if (IsOpen())
91 return true;
92
93 bool database_exists = file_util::PathExists(file_path_);
94
95 if (!database_exists && !create_if_needed) {
96 // If the file doesn't exist already and we haven't been asked to create
97 // a file on disk, then we don't bother opening the database. This means
98 // we wait until we absolutely need to put something onto disk before we
99 // do so.
100 return false;
101 }
102
103 db_.reset(new sql::Connection());
104 db_->set_error_delegate(GetErrorHandlerForDomStorageDatabase());
105 if (!db_->Open(file_path_)) {
106 LOG(WARNING) << "Unable to open DOM storage database at "
michaeln 2012/02/01 21:03:11 what should we do at this point?
benm (inactive) 2012/02/02 12:14:49 Good question :) My thinking was that it's ok to r
michaeln 2012/02/03 04:45:15 The caller is every method that may want to read o
benm (inactive) 2012/02/03 11:46:40 Good point, added a TODO to handle this failure to
107 << file_path_.value();
108 return false;
109 }
110
111 // sql::Connection uses UTF-8 encoding, but WebCore style databases use
112 // UTF-16, so ensure we match.
113 ignore_result(db_->Execute("PRAGMA encoding=\"UTF-16\""));
114
115 if (!db_->DoesTableExist("ItemTable"))
116 return CreateTable();
117
118 // Table exists, so ensure we're at the right version, upgrading if
119 // necessary.
120 if (UpgradeVersion1To2IfNeeded())
121 return true;
122
123 // Upgrade failed, drop table and start fresh.
124 if (db_->Execute("DROP TABLE ItemTable"))
125 return CreateTable();
126
127 return false;
michaeln 2012/02/01 21:03:11 upon return, will IsOpen() report 'true' since the
benm (inactive) 2012/02/02 12:14:49 good call
128 }
129
130 bool DomStorageDatabase::CreateTable() {
131 // Current version is 2.
132 return CreateTableV2();
michaeln 2012/02/01 21:03:11 doesn't look like we really need two separate meth
benm (inactive) 2012/02/02 12:14:49 My thinking was that it's useful to have a specifi
michaeln 2012/02/03 04:45:15 Still don't see what value the indirection adds. T
benm (inactive) 2012/02/03 11:46:40 My thinking is that if we introduce a V3 database
133 }
134
135 bool DomStorageDatabase::CreateTableV2() {
136 if (!LazyOpen(false))
michaeln 2012/02/01 21:03:11 should this method DCHECK that the db_ connection
benm (inactive) 2012/02/02 12:14:49 sgtm
137 return false;
138
139 return db_->Execute(
140 "CREATE TABLE IF NOT EXISTS ItemTable ("
141 "key TEXT UNIQUE ON CONFLICT REPLACE, "
142 "value BLOB NOT NULL ON CONFLICT FAIL)");
143 }
144
145 bool DomStorageDatabase::UpgradeVersion1To2IfNeeded() {
146 sql::Statement stmt(db_->GetCachedStatement(SQL_FROM_HERE,
147 "SELECT * FROM ItemTable"));
148 DCHECK(stmt.is_valid());
149 if (!stmt)
150 return false;
151
152 // Quick check to see if we need to upgrade or not. The single
153 // effect of V1 -> V2 is to change the value column from type
154 // TEXT to type BLOB.
155 sql::ColType valueColumnType = stmt.DeclaredColumnType(1);
michaeln 2012/02/01 21:03:11 nit: nix camelCase variable naming
benm (inactive) 2012/02/02 12:14:49 Done.
156 if (valueColumnType == sql::COLUMN_TYPE_BLOB)
157 return true;
158
159 if (valueColumnType != sql::COLUMN_TYPE_TEXT) {
160 // Something is messed up. This is not a V1 database.
michaeln 2012/02/01 21:03:11 what should we do at this point? delete it and sta
benm (inactive) 2012/02/02 12:14:49 Yeah, that is taken care of by the LazyOpen functi
michaeln 2012/02/03 04:45:15 LazyOpen continues to try to work with the existin
benm (inactive) 2012/02/03 11:46:40 Good point. However I don't think that we can get
161 return false;
162 }
163
164 // Need to migrate from TEXT value column to BLOB.
165 ValuesMap values;
166 while (stmt.Step()) {
167 string16 key = stmt.ColumnString16(0);
168 string16 value = stmt.ColumnString16(1);
169 values[key] = NullableString16(value, false);
170 }
171
172 sql::Transaction migration(db_.get());
173 if (migration.Begin()) {
michaeln 2012/02/01 21:03:11 prefer early returns if (!begin()) return false
benm (inactive) 2012/02/02 12:14:49 Done.
174 if (db_->Execute("DROP TABLE ItemTable")) {
175 CreateTableV2();
176 if (CommitChanges(false, values)) {
177 return migration.Commit();
178 }
179 }
180 }
181 return false;
182 }
183
184 void DomStorageDatabase::Close() {
185 if (!IsOpen())
186 return;
187
188 db_->Close();
189 db_.reset(NULL);
190 }
191
192 } // namespace dom_storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698