| // 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 |