blob: e006553969b1344e4de11d53f25ad3d45f6ff07f [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/tab/tab_state_storage_database.h"
#include "base/check.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "sql/database.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
#include "sql/transaction.h"
namespace tabs {
namespace {
const int kCurrentVersionNumber = 1;
const int kCompatibleVersionNumber = 1;
constexpr char kTabsTableName[] = "nodes";
bool CreateTable(sql::Database* db, base::cstring_view table_creation_script) {
DCHECK(db->IsSQLValid(table_creation_script));
return db->Execute(table_creation_script);
}
bool CreateSchema(sql::Database* db, sql::MetaTable* meta_table) {
DCHECK(db->HasActiveTransactions());
static constexpr char kCreateTabSchemaSql[] =
"CREATE TABLE IF NOT EXISTS nodes("
"id INTEGER PRIMARY KEY NOT NULL,"
"type INTEGER NOT NULL,"
"children BLOB,"
"payload BLOB)";
return CreateTable(db, kCreateTabSchemaSql);
}
bool InitSchema(sql::Database* db, sql::MetaTable* meta_table) {
bool has_metatable = meta_table->DoesTableExist(db);
bool has_schema = db->DoesTableExist(kTabsTableName);
if (!has_metatable && has_schema) {
db->Raze();
}
sql::Transaction transaction(db);
if (!transaction.Begin()) {
DLOG(ERROR) << "Transaction could not be started.";
return false;
}
if (!meta_table->Init(db, kCurrentVersionNumber, kCompatibleVersionNumber)) {
return false;
}
if (meta_table->GetCompatibleVersionNumber() > kCurrentVersionNumber) {
return false;
}
if (!has_schema && !CreateSchema(db, meta_table)) {
return false;
}
return meta_table->SetVersionNumber(kCurrentVersionNumber) &&
meta_table->SetCompatibleVersionNumber(kCompatibleVersionNumber) &&
transaction.Commit();
}
} // namespace
TabStateStorageDatabase::TabStateStorageDatabase(
const base::FilePath& profile_path)
: profile_path_(profile_path),
db_(std::make_unique<sql::Database>(
sql::Database::Tag("TabStateStorage"))),
meta_table_(std::make_unique<sql::MetaTable>()) {}
TabStateStorageDatabase::~TabStateStorageDatabase() = default;
bool TabStateStorageDatabase::Initialize() {
CHECK(db_);
CHECK(meta_table_);
base::FilePath db_dir = profile_path_.Append(FILE_PATH_LITERAL("Tabs"));
if (!base::CreateDirectory(db_dir)) {
LOG(ERROR) << "Failed to create directory for tab state storage database: "
<< db_dir;
return false;
}
const base::FilePath db_path = db_dir.Append(FILE_PATH_LITERAL("TabDB"));
if (!db_->Open(db_path)) {
LOG(ERROR) << "Failed to open tab state storage database: "
<< db_->GetErrorMessage();
return false;
}
if (!InitSchema(db_.get(), meta_table_.get())) {
DLOG(ERROR) << "Failed to create schema for tab state storage database: "
<< db_->GetErrorMessage();
db_->Close();
return false;
}
return true;
}
bool TabStateStorageDatabase::SaveNode(int id,
int type,
std::string payload,
std::string children) {
CHECK(db_);
sql::Transaction transaction(db_.get());
if (!transaction.Begin()) {
return false;
}
static constexpr char kInsertTabSql[] =
"INSERT OR REPLACE INTO nodes"
"(id, type, payload, children)"
"VALUES (?,?,?,?)";
DCHECK(db_->IsSQLValid(kInsertTabSql));
sql::Statement write_statement(
db_->GetCachedStatement(SQL_FROM_HERE, kInsertTabSql));
write_statement.BindInt(0, id);
write_statement.BindInt(1, type);
write_statement.BindBlob(2, std::move(payload));
write_statement.BindBlob(3, std::move(children));
if (!write_statement.Run()) {
DLOG(ERROR) << "Could not write to tabs table.";
return false;
}
return transaction.Commit();
}
std::vector<NodeState> TabStateStorageDatabase::LoadAllNodes() {
std::vector<NodeState> entries;
static constexpr char kSelectAllTabsSql[] =
"SELECT id, type, payload, children FROM nodes";
sql::Statement select_statement(
db_->GetCachedStatement(SQL_FROM_HERE, kSelectAllTabsSql));
while (select_statement.Step()) {
NodeState entry;
entry.id = select_statement.ColumnInt(0);
entry.type = select_statement.ColumnInt(1);
select_statement.ColumnBlobAsString(2, &entry.payload);
select_statement.ColumnBlobAsString(3, &entry.children);
entries.emplace_back(std::move(entry));
}
return entries;
}
} // namespace tabs