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 |