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

Unified 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: DomStorageDatabase for backing local storage to disk. Created 8 years, 11 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 side-by-side diff with in-line comments
Download patch
Index: webkit/dom_storage/dom_storage_database.cc
diff --git a/webkit/dom_storage/dom_storage_database.cc b/webkit/dom_storage/dom_storage_database.cc
new file mode 100644
index 0000000000000000000000000000000000000000..cba58f9343e4c77de1746d9ea5e5a54ecae1f811
--- /dev/null
+++ b/webkit/dom_storage/dom_storage_database.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/dom_storage/dom_storage_database.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "sql/diagnostic_error_delegate.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+namespace {
+
+class HistogramUniquifier {
+ public:
+ static const char* name() { return "Sqlite.DomStorageDatabase.Error"; }
+};
+
+sql::ErrorDelegate* GetErrorHandlerForDomStorageDatabase() {
+ return new sql::DiagnosticErrorDelegate<HistogramUniquifier>();
+}
+
+} // anon namespace
+
+namespace dom_storage {
+
+DomStorageDatabase::DomStorageDatabase(const FilePath& file_path)
+ : file_path_(file_path),
+ db_(NULL) {
+ DCHECK(!file_path_.empty());
+}
+
+DomStorageDatabase::~DomStorageDatabase() {
+ if (IsOpen())
+ Close();
michaeln 2012/01/31 22:45:32 Since sql::Connection dtor will close automaticall
benm (inactive) 2012/02/01 15:42:09 Close is private and useful in the unit tests. I c
+}
+
+bool DomStorageDatabase::LazyOpen() {
michaeln 2012/01/31 22:45:32 can you reorder the methods in the .cc file to mat
benm (inactive) 2012/02/01 15:42:09 Done.
+ if (IsOpen())
+ return true;
+
+ db_.reset(new sql::Connection());
+ bool is_open = db_->Open(file_path_);
+ db_->set_error_delegate(GetErrorHandlerForDomStorageDatabase());
michaeln 2012/01/31 22:45:32 If the db->Open() method can invoke the error dele
benm (inactive) 2012/02/01 15:42:09 The docs in sql::Connection actually say that it s
+
+ if (!is_open) {
+ LOG(WARNING) << "Unable to open Database at " << file_path_.value();
+ return false;
+ }
+
+ // sql::Connection uses UTF-8 encoding, but WebCore style databases use
+ // UTF-16, so ensure we match.
+ ignore_result(db_->Execute("PRAGMA encoding=\"UTF-16\""));
+
+ // Ensure we're at the correct version.
+ if (db_->DoesTableExist("ItemTable")) {
+ if (UpgradeVersion1To2IfNeeded())
+ return true;
+ else {
+ // Upgrade failed, drop table and start fresh.
+ if (db_->Execute("DROP TABLE ItemTable"))
+ return CreateTable();
+ else
+ return false;
+ }
+ } else
+ return CreateTable();
michaeln 2012/01/31 22:45:32 style nit: since the if { block } has brackets, th
benm (inactive) 2012/02/01 15:42:09 gah, webkit style on the brain :)
+}
+
+void DomStorageDatabase::Close() {
+ if (!LazyOpen())
michaeln 2012/01/31 22:45:32 at this point, there's no reason to open if not al
benm (inactive) 2012/02/01 15:42:09 Good spot, think I was a little too generous with
+ return;
+
+ db_->Close();
+ db_.reset(NULL);
+}
+
+bool DomStorageDatabase::CreateTable() {
+ // Current version is 2.
+ return CreateTableV2();
+}
+
+bool DomStorageDatabase::CreateTableV2() {
+ if (!LazyOpen())
+ return false;
+
+ return db_->Execute(
+ "CREATE TABLE IF NOT EXISTS ItemTable ("
+ "key TEXT UNIQUE ON CONFLICT REPLACE, "
+ "value BLOB NOT NULL ON CONFLICT FAIL)");
+}
+
+void DomStorageDatabase::ReadAllValues(
+ DomStorageDatabase::ValuesMap* result) {
+ if (!LazyOpen())
+ return;
+
+ sql::Statement stmt(db_->GetCachedStatement(SQL_FROM_HERE,
+ "SELECT * from ItemTable"));
+ DCHECK(stmt.is_valid());
+ if (!stmt)
+ return;
+
+ while (stmt.Step()) {
+ string16 key = stmt.ColumnString16(0);
+ string16 value;
+ stmt.ColumnBlobAsString16(1, &value);
+ (*result)[key] = value;
+ }
+}
+
+bool DomStorageDatabase::WriteValues(const ValuesMap& values) {
+ if (!LazyOpen())
+ return false;
+
+ ValuesMap::const_iterator it = values.begin();
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+
+ for(; it != values.end(); ++it) {
+ sql::Statement stmt(db_->GetCachedStatement(SQL_FROM_HERE,
+ "INSERT INTO ItemTable VALUES (?,?)"));
+ string16 key = it->first;
+ string16 value = it->second;
+ stmt.BindString16(0, key);
+ stmt.BindBlob(1, value.data(), value.length() * sizeof(char16));
+ DCHECK(stmt.is_valid());
+ stmt.Run();
+ }
+ transaction.Commit();
+ return true;
michaeln 2012/01/31 22:45:32 return transaction.Commit();
benm (inactive) 2012/02/01 15:42:09 Done.
+}
+
+bool DomStorageDatabase::RemoveValues(const ValuesSet& keys) {
+ if (!LazyOpen())
+ return false;
+
+ ValuesSet::const_iterator it = keys.begin();
+
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+
+ for ( ; it != keys.end(); ++it) {
+ sql::Statement stmt(db_->GetCachedStatement(SQL_FROM_HERE,
+ "DELETE FROM ItemTable WHERE key=?"));
+ stmt.BindString16(0, *it);
+ DCHECK(stmt.is_valid());
+ stmt.Run();
+ }
+ transaction.Commit();
+ return true;
+}
+
+bool DomStorageDatabase::UpgradeVersion1To2IfNeeded() {
+ sql::Statement stmt(db_->GetCachedStatement(SQL_FROM_HERE,
+ "SELECT * FROM ItemTable"));
+ DCHECK(stmt.is_valid());
+ if (!stmt)
+ return false;
+
+ // Quick check to see if we need to upgrade or not. The single
+ // effect of V1 -> V2 is to change the value column from type
+ // TEXT to type BLOB.
+ sql::ColType valueColumnType = stmt.DeclaredColumnType(1);
+ if (valueColumnType == sql::COLUMN_TYPE_BLOB)
+ return true;
+
+ if (valueColumnType != sql::COLUMN_TYPE_TEXT) {
+ // Something is messed up. This is not a V1 database.
+ return false;
+ }
+
+ // Need to migrate from TEXT value column to BLOB.
+ ValuesMap values;
+ while (stmt.Step()) {
+ string16 key = stmt.ColumnString16(0);
+ string16 value = stmt.ColumnString16(1);
+ values[key] = value;
+ }
+
+ {
michaeln 2012/01/31 22:45:32 why the extra scope
benm (inactive) 2012/02/01 15:42:09 hmm, I think I used to have some extra stuff after
+ sql::Transaction migration(db_.get());
+ if (migration.Begin()) {
+ if (db_->Execute("DROP TABLE ItemTable")) {
+ CreateTableV2();
+ if (WriteValues(values)) {
+ migration.Commit();
michaeln 2012/01/31 22:45:32 return migration.Commit()?
benm (inactive) 2012/02/01 15:42:09 Done.
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+} // namespace dom_storage

Powered by Google App Engine
This is Rietveld 408576698