blob: 587db5289e57c7516703013a7af71a58fb57b0d7 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/user_annotations/user_annotations_database.h"
#include "base/files/file_path.h"
#include "base/metrics/histogram_functions.h"
#include "base/sequence_checker.h"
#include "build/build_config.h"
#include "components/user_annotations/user_annotations_features.h"
#include "sql/init_status.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
#include "sql/transaction.h"
namespace user_annotations {
inline constexpr base::FilePath::CharType kUserAnnotationsName[] =
FILE_PATH_LITERAL("UserAnnotations");
// These database versions should roll together unless we develop migrations.
constexpr int kLowestSupportedDatabaseVersion = 1;
constexpr int kCurrentDatabaseVersion = 1;
namespace {
[[nodiscard]] bool CreateTable(sql::Database& db) {
static constexpr char kSqlCreateTablePassages[] =
"CREATE TABLE IF NOT EXISTS entries("
// The ID of the entry.
"entry_id INTEGER PRIMARY KEY AUTOINCREMENT,"
// The key of the entry.
"key VARCHAR NOT NULL,"
"value VARCHAR NOT NULL);";
return db.Execute(kSqlCreateTablePassages);
}
} // namespace
UserAnnotationsDatabase::UserAnnotationsDatabase(
const base::FilePath& storage_dir) {
InitInternal(storage_dir);
// TODO(b:361696651): Record the DB init status.
}
UserAnnotationsDatabase::~UserAnnotationsDatabase() = default;
sql::InitStatus UserAnnotationsDatabase::InitInternal(
const base::FilePath& storage_dir) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
db_.set_histogram_tag("UserAnnotations");
base::FilePath db_file_path = storage_dir.Append(kUserAnnotationsName);
if (!db_.Open(db_file_path)) {
return sql::InitStatus::INIT_FAILURE;
}
// Raze old incompatible databases.
if (sql::MetaTable::RazeIfIncompatible(&db_, kLowestSupportedDatabaseVersion,
kCurrentDatabaseVersion) ==
sql::RazeIfIncompatibleResult::kFailed) {
return sql::InitStatus::INIT_FAILURE;
}
// Wrap initialization in a transaction to make it atomic.
sql::Transaction transaction(&db_);
if (!transaction.Begin()) {
return sql::InitStatus::INIT_FAILURE;
}
// Initialize the current version meta table. Safest to leave the compatible
// version equal to the current version - unless we know we're making a very
// safe backwards-compatible schema change.
sql::MetaTable meta_table;
if (!meta_table.Init(&db_, kCurrentDatabaseVersion,
/*compatible_version=*/kCurrentDatabaseVersion)) {
return sql::InitStatus::INIT_FAILURE;
}
if (meta_table.GetCompatibleVersionNumber() > kCurrentDatabaseVersion) {
return sql::INIT_TOO_NEW;
}
if (!CreateTable(db_)) {
return sql::INIT_FAILURE;
}
if (!transaction.Commit()) {
return sql::INIT_FAILURE;
}
return sql::InitStatus::INIT_OK;
}
bool UserAnnotationsDatabase::UpdateEntries(
const std::vector<optimization_guide::proto::UserAnnotationsEntry>&
entries) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sql::Transaction transaction(&db_);
if (!transaction.Begin()) {
return false;
}
if (ShouldReplaceAnnotationsAfterEachSubmission()) {
sql::Statement statement(
db_.GetCachedStatement(SQL_FROM_HERE, "DELETE FROM entries"));
if (!statement.Run()) {
return false;
}
}
for (const auto& entry : entries) {
static constexpr char kSqlInsertEntries[] =
"INSERT OR REPLACE INTO entries(key, value) VALUES(?,?)";
sql::Statement statement(
db_.GetCachedStatement(SQL_FROM_HERE, kSqlInsertEntries));
statement.BindString(0, entry.key());
statement.BindString(1, entry.value());
if (!statement.Run()) {
return false;
}
}
return transaction.Commit();
}
std::vector<optimization_guide::proto::UserAnnotationsEntry>
UserAnnotationsDatabase::RetrieveAllEntries() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<optimization_guide::proto::UserAnnotationsEntry> entries;
static constexpr char kSqlSelectAllEntries[] =
"SELECT entry_id, key, value FROM entries";
sql::Statement statement(
db_.GetCachedStatement(SQL_FROM_HERE, kSqlSelectAllEntries));
while (statement.Step()) {
optimization_guide::proto::UserAnnotationsEntry entry;
entry.set_entry_id(statement.ColumnInt64(0));
entry.set_key(statement.ColumnString(1));
entry.set_value(statement.ColumnString(2));
entries.push_back(std::move(entry));
}
return entries;
}
} // namespace user_annotations