blob: 1cd6fb9aa9c32661b7b209d82a0dc6bb33a716d6 [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 "components/persistent_cache/sqlite/sqlite_backend_impl.h"
#include <memory>
#include <utility>
#include "base/check_op.h"
#include "base/containers/span.h"
#include "components/persistent_cache/sqlite/sqlite_entry_impl.h"
#include "components/persistent_cache/sqlite/vfs/sandboxed_file.h"
#include "components/persistent_cache/sqlite/vfs/sqlite_sandboxed_vfs.h"
#include "sql/database.h"
#include "sql/statement.h"
namespace {
constexpr const char kSqliteHistogramTag[] = "PersistentCache";
} // namespace
namespace persistent_cache {
// static
SqliteVfsFileSet SqliteBackendImpl::GetVfsFileSetFromParams(
BackendParams backend_params) {
CHECK_EQ(backend_params.type, BackendType::kSqlite);
using AccessRights = SandboxedFile::AccessRights;
SandboxedFile db_file = SandboxedFile(std::move(backend_params.db_file),
backend_params.db_file_is_writable
? AccessRights::kReadWrite
: AccessRights::kReadOnly);
SandboxedFile journal_file = SandboxedFile(
std::move(backend_params.journal_file),
backend_params.journal_file_is_writable ? AccessRights::kReadWrite
: AccessRights::kReadOnly);
return SqliteVfsFileSet(std::move(db_file), std::move(journal_file));
}
SqliteBackendImpl::SqliteBackendImpl(BackendParams backend_params)
: SqliteBackendImpl(GetVfsFileSetFromParams(std::move(backend_params))) {}
SqliteBackendImpl::SqliteBackendImpl(SqliteVfsFileSet vfs_file_set)
: database_path_(vfs_file_set.GetDbVirtualFilePath()),
db_(sql::DatabaseOptions()
.set_vfs_name_discouraged(
SqliteSandboxedVfsDelegate::kSqliteVfsName)
// Prevent SQLite from trying to use mmap, as SandboxedVfs does
// not currently support this.
.set_mmap_enabled(false),
kSqliteHistogramTag),
unregister_runner_(
SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles(
std::move(vfs_file_set))) {}
SqliteBackendImpl::~SqliteBackendImpl() = default;
bool SqliteBackendImpl::Initialize() {
CHECK(!initialized_);
TRACE_EVENT0("persistent_cache", "initialize");
if (!db_.Open(database_path_)) {
TRACE_EVENT_INSTANT1("persistent_cache", "open_failed",
TRACE_EVENT_SCOPE_THREAD, "error_code",
db_.GetErrorCode());
return false;
}
if (!db_.Execute(
"CREATE TABLE IF NOT EXISTS entries(key TEXT PRIMARY KEY UNIQUE NOT "
"NULL, content BLOB NOT NULL)")) {
TRACE_EVENT_INSTANT1("persistent_cache", "create_failed",
TRACE_EVENT_SCOPE_THREAD, "error_code",
db_.GetErrorCode());
return false;
}
initialized_ = true;
return true;
}
std::unique_ptr<Entry> SqliteBackendImpl::Find(std::string_view key) {
CHECK(initialized_);
CHECK_GT(key.length(), 0ull);
TRACE_EVENT0("persistent_cache", "Find");
sql::Statement stm = sql::Statement(db_.GetCachedStatement(
SQL_FROM_HERE, "SELECT content FROM entries WHERE key = ?"));
stm.BindString(0, key);
DCHECK(stm.is_valid());
if (!stm.Step()) {
const int error_code = db_.GetErrorCode();
// If the last error code is SQLITE_DONE then `Step()` failed because the
// row was not found which is not a reportable error.
if (error_code != SQLITE_DONE) {
TRACE_EVENT_INSTANT1("persistent_cache", "find_failed",
TRACE_EVENT_SCOPE_THREAD, "error_code", error_code);
}
return nullptr;
}
return SqliteEntryImpl::MakeUnique(Passkey(), stm.ColumnString(0));
}
void SqliteBackendImpl::Insert(std::string_view key,
base::span<const uint8_t> content) {
CHECK(initialized_);
CHECK_GT(key.length(), 0ull);
TRACE_EVENT0("persistent_cache", "insert");
sql::Statement stm(db_.GetCachedStatement(
SQL_FROM_HERE, "REPLACE INTO entries (key, content) VALUES (?, ?)"));
stm.BindString(0, key);
stm.BindString(1, base::as_string_view(content));
DCHECK(stm.is_valid());
if (!stm.Run()) {
TRACE_EVENT_INSTANT1("persistent_cache", "insert_failed",
TRACE_EVENT_SCOPE_THREAD, "error_code",
db_.GetErrorCode());
}
}
} // namespace persistent_cache