| // 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 "base/strings/string_view_util.h" |
| #include "base/trace_event/trace_event.h" |
| #include "components/persistent_cache/backend_params.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; |
| std::unique_ptr<SandboxedFile> db_file = std::make_unique<SandboxedFile>( |
| std::move(backend_params.db_file), backend_params.db_file_is_writable |
| ? AccessRights::kReadWrite |
| : AccessRights::kReadOnly); |
| std::unique_ptr<SandboxedFile> journal_file = std::make_unique<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()), |
| vfs_file_set_(std::move(vfs_file_set)), |
| unregister_runner_( |
| SqliteSandboxedVfsDelegate::GetInstance()->RegisterSandboxedFiles( |
| vfs_file_set_)), |
| 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) {} |
| |
| 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, input_signature INTEGER, " |
| "write_timestamp INTEGER)")) { |
| 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, input_signature, write_timestamp " |
| "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; |
| } |
| |
| EntryMetadata metadata; |
| metadata.input_signature = stm.ColumnInt64(1); |
| metadata.write_timestamp = stm.ColumnInt64(2); |
| |
| return SqliteEntryImpl::MakeUnique(Passkey(), stm.ColumnString(0), metadata); |
| } |
| |
| void SqliteBackendImpl::Insert(std::string_view key, |
| base::span<const uint8_t> content, |
| EntryMetadata metadata) { |
| CHECK(initialized_); |
| CHECK_GT(key.length(), 0ull); |
| TRACE_EVENT0("persistent_cache", "insert"); |
| |
| CHECK_EQ(metadata.write_timestamp, 0) |
| << "Write timestamp is generated by SQLite so it should not be specified " |
| "manually"; |
| |
| sql::Statement stm(db_.GetCachedStatement( |
| SQL_FROM_HERE, |
| "REPLACE INTO entries (key, content, input_signature, write_timestamp) " |
| "VALUES (?, ?, ?, strftime(\'%s\', \'now\'))")); |
| |
| stm.BindString(0, key); |
| stm.BindBlob(1, content); |
| stm.BindInt64(2, metadata.input_signature); |
| |
| DCHECK(stm.is_valid()); |
| if (!stm.Run()) { |
| TRACE_EVENT_INSTANT1("persistent_cache", "insert_failed", |
| TRACE_EVENT_SCOPE_THREAD, "error_code", |
| db_.GetErrorCode()); |
| } |
| } |
| |
| BackendType SqliteBackendImpl::GetType() const { |
| return BackendType::kSqlite; |
| } |
| |
| bool SqliteBackendImpl::IsReadOnly() const { |
| return vfs_file_set_.read_only(); |
| } |
| |
| } // namespace persistent_cache |