Chromium Code Reviews| 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 |