blob: 844f58406468577b527def40cf69e822db546937 [file] [log] [blame]
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/dom/local_storage_database.h"
#include "base/debug/trace_event.h"
#include "cobalt/dom/storage_area.h"
#include "cobalt/storage/storage_manager.h"
#include "nb/memory_scope.h"
#include "sql/statement.h"
namespace cobalt {
namespace dom {
namespace {
const int kOriginalLocalStorageSchemaVersion = 1;
const int kLatestLocalStorageSchemaVersion = 1;
void SqlInit(storage::SqlContext* sql_context) {
TRACK_MEMORY_SCOPE("Storage");
TRACE_EVENT0("cobalt::storage", "LocalStorage::SqlInit()");
sql::Connection* conn = sql_context->sql_connection();
int schema_version;
bool table_exists =
sql_context->GetSchemaVersion("LocalStorageTable", &schema_version);
if (table_exists) {
if (schema_version == storage::StorageManager::kSchemaTableIsNew) {
// This savegame predates the existence of the schema table.
// Since the local-storage table did not change between the initial
// release of the app and the introduction of the schema table, assume
// that this existing local-storage table is schema version 1. This
// avoids a loss of data on upgrade.
DLOG(INFO) << "Updating LocalStorageTable schema version to "
<< kOriginalLocalStorageSchemaVersion;
sql_context->UpdateSchemaVersion("LocalStorageTable",
kOriginalLocalStorageSchemaVersion);
} else if (schema_version == storage::StorageManager::kSchemaVersionLost) {
// Since there has only been one schema so far, treat this the same as
// kSchemaTableIsNew. When there are multiple schemas in the wild,
// we may want to drop the table instead.
sql_context->UpdateSchemaVersion("LocalStorageTable",
kOriginalLocalStorageSchemaVersion);
}
} else {
// The table does not exist, so create it in its latest form.
sql::Statement create_table(conn->GetUniqueStatement(
"CREATE TABLE LocalStorageTable ("
" site_identifier TEXT, "
" key TEXT, "
" value TEXT NOT NULL ON CONFLICT FAIL, "
" UNIQUE(site_identifier, key) ON CONFLICT REPLACE"
")"));
bool ok = create_table.Run();
DCHECK(ok);
sql_context->UpdateSchemaVersion("LocalStorageTable",
kLatestLocalStorageSchemaVersion);
}
}
void SqlReadValues(const std::string& id,
const LocalStorageDatabase::ReadCompletionCallback& callback,
storage::SqlContext* sql_context) {
TRACK_MEMORY_SCOPE("Storage");
scoped_ptr<StorageArea::StorageMap> values(new StorageArea::StorageMap);
sql::Connection* conn = sql_context->sql_connection();
sql::Statement get_values(conn->GetCachedStatement(
SQL_FROM_HERE,
"SELECT key, value FROM LocalStorageTable WHERE site_identifier = ?"));
get_values.BindString(0, id);
while (get_values.Step()) {
// TODO: In Steel, these were string16.
std::string key(get_values.ColumnString(0));
std::string value(get_values.ColumnString(1));
values->insert(std::make_pair(key, value));
}
callback.Run(values.Pass());
}
void SqlWrite(const std::string& id, const std::string& key,
const std::string& value, storage::SqlContext* sql_context) {
TRACK_MEMORY_SCOPE("Storage");
sql::Connection* conn = sql_context->sql_connection();
sql::Statement write_statement(conn->GetCachedStatement(
SQL_FROM_HERE,
"INSERT INTO LocalStorageTable (site_identifier, key, value) "
"VALUES (?, ?, ?)"));
write_statement.BindString(0, id);
write_statement.BindString(1, key);
write_statement.BindString(2, value);
bool ok = write_statement.Run();
DCHECK(ok);
}
void SqlDelete(const std::string& id, const std::string& key,
storage::SqlContext* sql_context) {
TRACK_MEMORY_SCOPE("Storage");
sql::Connection* conn = sql_context->sql_connection();
sql::Statement delete_statement(
conn->GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM LocalStorageTable "
"WHERE site_identifier = ? AND key = ?"));
delete_statement.BindString(0, id);
delete_statement.BindString(1, key);
bool ok = delete_statement.Run();
DCHECK(ok);
}
void SqlClear(const std::string& id, storage::SqlContext* sql_context) {
TRACK_MEMORY_SCOPE("Storage");
sql::Connection* conn = sql_context->sql_connection();
sql::Statement clear_statement(
conn->GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM LocalStorageTable "
"WHERE site_identifier = ?"));
clear_statement.BindString(0, id);
bool ok = clear_statement.Run();
DCHECK(ok);
}
} // namespace
LocalStorageDatabase::LocalStorageDatabase(storage::StorageManager* storage)
: storage_(storage), initialized_(false) {}
// Init is done lazily only once the first operation occurs. This is to avoid
// a potential wait while the storage manager loads from disk.
void LocalStorageDatabase::Init() {
if (!initialized_) {
storage_->GetSqlContext(base::Bind(&SqlInit));
initialized_ = true;
}
}
void LocalStorageDatabase::ReadAll(const std::string& id,
const ReadCompletionCallback& callback) {
TRACK_MEMORY_SCOPE("Storage");
Init();
storage_->GetSqlContext(base::Bind(&SqlReadValues, id, callback));
}
void LocalStorageDatabase::Write(const std::string& id, const std::string& key,
const std::string& value) {
TRACK_MEMORY_SCOPE("Storage");
Init();
storage_->GetSqlContext(base::Bind(&SqlWrite, id, key, value));
storage_->FlushOnChange();
}
void LocalStorageDatabase::Delete(const std::string& id,
const std::string& key) {
Init();
storage_->GetSqlContext(base::Bind(&SqlDelete, id, key));
storage_->FlushOnChange();
}
void LocalStorageDatabase::Clear(const std::string& id) {
Init();
storage_->GetSqlContext(base::Bind(&SqlClear, id));
storage_->FlushOnChange();
}
void LocalStorageDatabase::Flush(const base::Closure& callback) {
storage_->FlushNow(callback);
}
} // namespace dom
} // namespace cobalt