This source file includes following definitions.
- GetJournalFilePath
- Init
- ReadAllValues
- CommitChanges
- LazyOpen
- DetectSchemaVersion
- CreateTableV2
- DeleteFileAndRecreate
- UpgradeVersion1To2
- Close
#include "content/browser/dom_storage/dom_storage_database.h"
#include "base/bind.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "sql/statement.h"
#include "sql/transaction.h"
#include "third_party/sqlite/sqlite3.h"
namespace {
const base::FilePath::CharType kJournal[] = FILE_PATH_LITERAL("-journal");
}
namespace content {
base::FilePath DOMStorageDatabase::GetJournalFilePath(
const base::FilePath& database_path) {
base::FilePath::StringType journal_file_name =
database_path.BaseName().value() + kJournal;
return database_path.DirName().Append(journal_file_name);
}
DOMStorageDatabase::DOMStorageDatabase(const base::FilePath& file_path)
: file_path_(file_path) {
Init();
}
DOMStorageDatabase::DOMStorageDatabase() {
Init();
}
void DOMStorageDatabase::Init() {
failed_to_open_ = false;
tried_to_recreate_ = false;
known_to_be_empty_ = false;
}
DOMStorageDatabase::~DOMStorageDatabase() {
if (known_to_be_empty_ && !file_path_.empty()) {
Close();
sql::Connection::Delete(file_path_);
}
}
void DOMStorageDatabase::ReadAllValues(DOMStorageValuesMap* result) {
if (!LazyOpen(false))
return;
sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
"SELECT * from ItemTable"));
DCHECK(statement.is_valid());
while (statement.Step()) {
base::string16 key = statement.ColumnString16(0);
base::string16 value;
statement.ColumnBlobAsString16(1, &value);
(*result)[key] = base::NullableString16(value, false);
}
known_to_be_empty_ = result->empty();
}
bool DOMStorageDatabase::CommitChanges(bool clear_all_first,
const DOMStorageValuesMap& changes) {
if (!LazyOpen(!changes.empty())) {
return clear_all_first && changes.empty() &&
!base::PathExists(file_path_);
}
bool old_known_to_be_empty = known_to_be_empty_;
sql::Transaction transaction(db_.get());
if (!transaction.Begin())
return false;
if (clear_all_first) {
if (!db_->Execute("DELETE FROM ItemTable"))
return false;
known_to_be_empty_ = true;
}
bool did_delete = false;
bool did_insert = false;
DOMStorageValuesMap::const_iterator it = changes.begin();
for(; it != changes.end(); ++it) {
sql::Statement statement;
base::string16 key = it->first;
base::NullableString16 value = it->second;
if (value.is_null()) {
statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM ItemTable WHERE key=?"));
statement.BindString16(0, key);
did_delete = true;
} else {
statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE,
"INSERT INTO ItemTable VALUES (?,?)"));
statement.BindString16(0, key);
statement.BindBlob(1, value.string().data(),
value.string().length() * sizeof(base::char16));
known_to_be_empty_ = false;
did_insert = true;
}
DCHECK(statement.is_valid());
statement.Run();
}
if (!known_to_be_empty_ && did_delete && !did_insert) {
sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
"SELECT count(key) from ItemTable"));
if (statement.Step())
known_to_be_empty_ = statement.ColumnInt(0) == 0;
}
bool success = transaction.Commit();
if (!success)
known_to_be_empty_ = old_known_to_be_empty;
return success;
}
bool DOMStorageDatabase::LazyOpen(bool create_if_needed) {
if (failed_to_open_) {
return false;
}
if (IsOpen())
return true;
bool database_exists = base::PathExists(file_path_);
if (!database_exists && !create_if_needed) {
return false;
}
db_.reset(new sql::Connection());
db_->set_histogram_tag("DOMStorageDatabase");
if (file_path_.empty()) {
if (!db_->OpenInMemory()) {
NOTREACHED() << "Unable to open DOM storage database in memory.";
failed_to_open_ = true;
return false;
}
} else {
if (!db_->Open(file_path_)) {
LOG(ERROR) << "Unable to open DOM storage database at "
<< file_path_.value()
<< " error: " << db_->GetErrorMessage();
if (database_exists && !tried_to_recreate_)
return DeleteFileAndRecreate();
failed_to_open_ = true;
return false;
}
}
ignore_result(db_->Execute("PRAGMA encoding=\"UTF-16\""));
if (!database_exists) {
if (CreateTableV2())
return true;
} else {
SchemaVersion current_version = DetectSchemaVersion();
if (current_version == V2) {
return true;
} else if (current_version == V1) {
if (UpgradeVersion1To2())
return true;
}
}
Close();
return DeleteFileAndRecreate();
}
DOMStorageDatabase::SchemaVersion DOMStorageDatabase::DetectSchemaVersion() {
DCHECK(IsOpen());
if (db_->ExecuteAndReturnErrorCode("PRAGMA auto_vacuum") != SQLITE_OK)
return INVALID;
if (!db_->DoesTableExist("ItemTable") ||
!db_->DoesColumnExist("ItemTable", "key") ||
!db_->DoesColumnExist("ItemTable", "value"))
return INVALID;
sql::Statement statement(
db_->GetUniqueStatement("SELECT key,value from ItemTable LIMIT 1"));
if (statement.DeclaredColumnType(0) != sql::COLUMN_TYPE_TEXT)
return INVALID;
switch (statement.DeclaredColumnType(1)) {
case sql::COLUMN_TYPE_BLOB:
return V2;
case sql::COLUMN_TYPE_TEXT:
return V1;
default:
return INVALID;
}
}
bool DOMStorageDatabase::CreateTableV2() {
DCHECK(IsOpen());
return db_->Execute(
"CREATE TABLE ItemTable ("
"key TEXT UNIQUE ON CONFLICT REPLACE, "
"value BLOB NOT NULL ON CONFLICT FAIL)");
}
bool DOMStorageDatabase::DeleteFileAndRecreate() {
DCHECK(!IsOpen());
DCHECK(base::PathExists(file_path_));
if (tried_to_recreate_)
return false;
tried_to_recreate_ = true;
if (!base::DirectoryExists(file_path_) &&
sql::Connection::Delete(file_path_)) {
return LazyOpen(true);
}
failed_to_open_ = true;
return false;
}
bool DOMStorageDatabase::UpgradeVersion1To2() {
DCHECK(IsOpen());
DCHECK(DetectSchemaVersion() == V1);
sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
"SELECT * FROM ItemTable"));
DCHECK(statement.is_valid());
DOMStorageValuesMap values;
while (statement.Step()) {
base::string16 key = statement.ColumnString16(0);
base::NullableString16 value(statement.ColumnString16(1), false);
values[key] = value;
}
sql::Transaction migration(db_.get());
return migration.Begin() &&
db_->Execute("DROP TABLE ItemTable") &&
CreateTableV2() &&
CommitChanges(false, values) &&
migration.Commit();
}
void DOMStorageDatabase::Close() {
db_.reset(NULL);
}
}