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

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: Remove some unused includes. 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..90ad5695af216a4f3aa076e8fde3b15a8578f1ac
--- /dev/null
+++ b/webkit/dom_storage/dom_storage_database.cc
@@ -0,0 +1,192 @@
+// 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());
+}
+
+void DomStorageDatabase::ReadAllValues(
+ DomStorageDatabase::ValuesMap* result) {
+ if (!LazyOpen(false))
+ return;
+
+ sql::Statement stmt(db_->GetCachedStatement(SQL_FROM_HERE,
+ "SELECT * from ItemTable"));
+ DCHECK(stmt.is_valid());
+ 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
+ return;
+
+ while (stmt.Step()) {
+ string16 key = stmt.ColumnString16(0);
+ string16 value;
+ stmt.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();
+ 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
+ for(; it != changes.end(); ++it) {
+ string16 key = it->first;
+ NullableString16 value = it->second;
+ if (value.is_null()) {
+ stmt.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
+ "DELETE FROM ItemTable WHERE key=?"));
+ stmt.BindString16(0, key);
+ } else {
+ stmt.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
+ "INSERT INTO ItemTable VALUES (?,?)"));
+ stmt.BindString16(0, key);
+ 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.
+ stmt.BindBlob(1, value_str.data(), value_str.length() * sizeof(char16));
+ }
+ DCHECK(stmt.is_valid());
+ stmt.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_)) {
+ 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
+ << 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 (!db_->DoesTableExist("ItemTable"))
+ return CreateTable();
+
+ // 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"))
+ return CreateTable();
+
+ 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
+}
+
+bool DomStorageDatabase::CreateTable() {
+ // Current version is 2.
+ 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
+}
+
+bool DomStorageDatabase::CreateTableV2() {
+ 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
+ return false;
+
+ 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() {
+ 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);
michaeln 2012/02/01 21:03:11 nit: nix camelCase variable naming
benm (inactive) 2012/02/02 12:14:49 Done.
+ if (valueColumnType == sql::COLUMN_TYPE_BLOB)
+ return true;
+
+ if (valueColumnType != sql::COLUMN_TYPE_TEXT) {
+ // 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
+ 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] = NullableString16(value, false);
+ }
+
+ sql::Transaction migration(db_.get());
+ 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.
+ 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);
+}
+
+} // namespace dom_storage

Powered by Google App Engine
This is Rietveld 408576698