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

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: Disable test that uses the webcore localstorage file pending the test data getting checked in. 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..26a525e2c58249f9c037ab17de3510d9ce6e016d
--- /dev/null
+++ b/webkit/dom_storage/dom_storage_database.cc
@@ -0,0 +1,203 @@
+// 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 "base/file_util.h"
+#include "base/logging.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() {
+}
+
+void DomStorageDatabase::ReadAllValues(
+ DomStorageDatabase::ValuesMap* result) {
+ if (!LazyOpen(false))
+ return;
+
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
+ "SELECT * from ItemTable"));
+ DCHECK(statement.is_valid());
+
+ while (statement.Step()) {
+ string16 key = statement.ColumnString16(0);
+ string16 value;
+ statement.ColumnBlobAsString16(1, &value);
+ (*result)[key] = NullableString16(value, false);
+ }
+}
+
+bool DomStorageDatabase::CommitChanges(bool clear_all_first,
+ const ValuesMap& changes) {
+ if (!LazyOpen(!changes.empty()))
+ return false;
+
+ sql::Transaction transaction(db_.get());
+ if (!transaction.Begin())
+ return false;
+
+ if (clear_all_first) {
+ if (!db_->Execute("DELETE FROM ItemTable"))
+ return false;
+ }
+
+ ValuesMap::const_iterator it = changes.begin();
+ for(; it != changes.end(); ++it) {
+ sql::Statement statement;
+ string16 key = it->first;
+ NullableString16 value = it->second;
+ if (value.is_null()) {
+ statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
+ "DELETE FROM ItemTable WHERE key=?"));
+ statement.BindString16(0, key);
+ } else {
+ statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
+ "INSERT INTO ItemTable VALUES (?,?)"));
michaeln 2012/02/03 04:45:15 style nit: indent two more spaces
benm (inactive) 2012/02/03 11:46:40 Done.
+ statement.BindString16(0, key);
+ statement.BindBlob(1, value.string().data(),
+ value.string().length() * sizeof(char16));
+ }
+ DCHECK(statement.is_valid());
+ statement.Run();
+ }
+ return transaction.Commit();
+}
+
+bool DomStorageDatabase::LazyOpen(bool create_if_needed) {
+ if (IsOpen())
+ return true;
+
+ bool database_exists = file_util::PathExists(file_path_);
+
+ if (!database_exists && !create_if_needed) {
+ // If the file doesn't exist already and we haven't been asked to create
+ // a file on disk, then we don't bother opening the database. This means
+ // we wait until we absolutely need to put something onto disk before we
+ // do so.
+ return false;
+ }
+
+ db_.reset(new sql::Connection());
+ db_->set_error_delegate(GetErrorHandlerForDomStorageDatabase());
+ if (!db_->Open(file_path_)) {
michaeln 2012/02/03 04:45:15 please put a todo here about dealing with this sor
benm (inactive) 2012/02/03 11:46:40 Done.
+ LOG(WARNING) << "Unable to open DOM storage 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\""));
+
+ // If the table doesn't exist, try to create it at the current version.
+ if (!db_->DoesTableExist("ItemTable")) {
+ if (CreateTable())
+ return true;
+ // Couldn't create the table.
+ Close();
+ return false;
+ }
+
+ // Table exists, so ensure we're at the right version, upgrading if
+ // necessary.
+ if (UpgradeVersion1To2IfNeeded())
+ return true;
+
+ // Upgrade failed, drop table and start fresh.
+ if (db_->Execute("DROP TABLE ItemTable")) {
michaeln 2012/02/03 04:45:15 The DROP TABLE statement and the following CREATE
benm (inactive) 2012/02/03 11:46:40 Done.
+ if (CreateTable())
+ return true;
+ }
+
+ Close();
+ return false;
+}
+
+bool DomStorageDatabase::CreateTable() {
+ DCHECK(IsOpen());
+ // Current version is 2.
+ return CreateTableV2();
+}
+
+bool DomStorageDatabase::CreateTableV2() {
+ DCHECK(IsOpen());
+
+ return db_->Execute(
+ "CREATE TABLE IF NOT EXISTS ItemTable ("
+ "key TEXT UNIQUE ON CONFLICT REPLACE, "
+ "value BLOB NOT NULL ON CONFLICT FAIL)");
+}
+
+bool DomStorageDatabase::UpgradeVersion1To2IfNeeded() {
+ DCHECK(IsOpen());
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
+ "SELECT * FROM ItemTable"));
+ DCHECK(statement.is_valid());
+
+ // 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 value_column_type = statement.DeclaredColumnType(1);
+ if (value_column_type == sql::COLUMN_TYPE_BLOB)
+ return true;
+
+ if (value_column_type != 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.
+ // Store the current database content so we can re-insert
+ // the data into the new V2 table.
+ ValuesMap values;
+ while (statement.Step()) {
+ string16 key = statement.ColumnString16(0);
+ NullableString16 value(statement.ColumnString16(1), false);
+ values[key] = value;
+ }
+
+ sql::Transaction migration(db_.get());
+ if (!migration.Begin())
+ return false;
+
+ if (db_->Execute("DROP TABLE ItemTable")) {
+ CreateTableV2();
+ if (CommitChanges(false, values))
+ return migration.Commit();
+ }
+ return false;
+}
+
+void DomStorageDatabase::Close() {
+ if (!IsOpen())
+ return;
+
+ db_->Close();
+ db_.reset(NULL);
michaeln 2012/02/03 04:45:15 could this be simplified to just db_.reset(NULL) a
benm (inactive) 2012/02/03 11:46:40 lgtm! :)
+}
+
+} // namespace dom_storage

Powered by Google App Engine
This is Rietveld 408576698