blob: ee3e3045b0c860c616e92af6d90142712c1b55e7 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/disk_cache/sql/sql_shared_cache_isolated_database.h"
#include <algorithm>
#include <limits>
#include <utility>
#include "base/check.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/ref_counted_memory.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/expected_macros.h"
#include "components/sqlite_vfs/client.h"
#include "components/sqlite_vfs/sqlite_sandboxed_vfs.h"
#include "components/sqlite_vfs/vfs_utils.h"
#include "net/base/features.h"
#include "net/disk_cache/sql/sql_backend_constants.h"
#include "net/disk_cache/sql/sql_read_cache_memory_monitor.h"
#include "net/disk_cache/sql/sql_shared_cache_isolated_database_queries.h"
#include "sql/database.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
#include "sql/streaming_blob_handle.h"
#include "sql/transaction.h"
using disk_cache_sql_queries::GetSharedCacheIsolatedDatabaseQuery;
using disk_cache_sql_queries::SharedCacheIsolatedDatabaseQuery;
namespace disk_cache {
namespace {
constexpr int kSharedCacheIsolatedDatabaseCurrentVersion = 1;
constexpr int kSharedCacheIsolatedDatabaseCompatibleVersion = 1;
} // namespace
// static
std::unique_ptr<SqlSharedCacheIsolatedDatabase::DatabaseAssets>
SqlSharedCacheIsolatedDatabase::DatabaseAssets::MaybeCreate(
const base::FilePath& directory,
SqlSharedCacheDbId shared_cache_db_id) {
ASSIGN_OR_RETURN(
auto pending_file_set,
sqlite_vfs::MakePendingFileSet(
sqlite_vfs::Client::kSharedCacheIsolated, directory,
base::FilePath::FromASCII(
base::StrCat({kSqlBackendSharedCacheIsolatedFileNamePrefix,
base::NumberToString(*shared_cache_db_id)})),
/*single_connection=*/false,
net::features::kRendererAccessibleHttpCacheWalMode.Get()),
[](auto) { return nullptr; });
ASSIGN_OR_RETURN(auto vfs_file_set,
sqlite_vfs::SqliteVfsFileSet::Bind(
sqlite_vfs::Client::kSharedCacheIsolated,
std::move(pending_file_set)),
[] { return nullptr; });
return std::make_unique<DatabaseAssets>(directory, shared_cache_db_id,
std::move(vfs_file_set),
base::PassKey<DatabaseAssets>());
}
SqlSharedCacheIsolatedDatabase::DatabaseAssets::DatabaseAssets(
const base::FilePath& directory,
SqlSharedCacheDbId shared_cache_db_id,
sqlite_vfs::SqliteVfsFileSet vfs_file_set,
base::PassKey<DatabaseAssets>)
: directory_(directory),
shared_cache_db_id_(shared_cache_db_id),
vfs_file_set_(std::move(vfs_file_set)),
unregister_runner_(sqlite_vfs::SqliteSandboxedVfsDelegate::GetInstance()
->RegisterSandboxedFiles(vfs_file_set_)),
db_(sql::DatabaseOptions()
.set_read_only(vfs_file_set_.read_only())
.set_exclusive_locking(vfs_file_set_.is_single_connection())
.set_wal_mode(vfs_file_set_.wal_mode())
.set_vfs_name_discouraged(
sqlite_vfs::SqliteSandboxedVfsDelegate::kSqliteVfsName)
.set_mmap_enabled(false),
sql::Database::Tag("SharedCacheIsolated")) {}
SqlSharedCacheIsolatedDatabase::DatabaseAssets::~DatabaseAssets() = default;
base::FilePath
SqlSharedCacheIsolatedDatabase::DatabaseAssets::GetDbVirtualFilePath() const {
return vfs_file_set_.GetDbVirtualFilePath();
}
base::expected<sqlite_vfs::PendingFileSet, sqlite_vfs::FileSetError>
SqlSharedCacheIsolatedDatabase::DatabaseAssets::ShareConnection() {
return sqlite_vfs::ShareConnection(
directory_,
base::FilePath::FromASCII(
base::StrCat({kSqlBackendSharedCacheIsolatedFileNamePrefix,
base::NumberToString(*shared_cache_db_id_)})),
vfs_file_set_, /*read_write=*/false);
}
void SqlSharedCacheIsolatedDatabase::DatabaseAssets::AbandonAndDeleteFiles() {
db_.Close();
vfs_file_set_.Abandon();
base::FilePath base_name = base::FilePath::FromASCII(
base::StrCat({kSqlBackendSharedCacheIsolatedFileNamePrefix,
base::NumberToString(shared_cache_db_id_.value())}));
sqlite_vfs::DeleteFiles(sqlite_vfs::Client::kSharedCacheIsolated, directory_,
base_name);
}
SqlSharedCacheIsolatedDatabase::SqlSharedCacheIsolatedDatabase(
std::string nik_string,
const base::FilePath& directory,
SqlSharedCacheDbId shared_cache_db_id,
scoped_refptr<base::SequencedTaskRunner> task_runner,
scoped_refptr<SqlReadCacheMemoryMonitor> read_cache_memory_monitor)
: nik_string_(std::move(nik_string)),
db_assets_(DatabaseAssets::MaybeCreate(directory, shared_cache_db_id)),
task_runner_(std::move(task_runner)),
read_cache_memory_monitor_(std::move(read_cache_memory_monitor)) {
CHECK(task_runner_);
}
SqlSharedCacheIsolatedDatabase::~SqlSharedCacheIsolatedDatabase() = default;
base::expected<std::vector<uint32_t>, SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::GetAllUrlHashes() {
if (ShouldSimulateFailure(OperationForTesting::kRead)) {
return base::unexpected(Error::kFailedForTesting);
}
if (!db_assets_) {
return base::unexpected(Error::kFailedToOpenVfsFileSet);
}
if (!db_assets_->db().is_open()) {
return base::unexpected(Error::kDatabaseNotOpen);
}
sql::Statement statement(db_assets_->db().GetCachedStatement(
SQL_FROM_HERE, GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kSelectHashes)));
std::vector<uint32_t> hashes;
while (statement.Step()) {
hashes.push_back(statement.ColumnInt(0));
}
if (!statement.Succeeded()) {
return base::unexpected(Error::kFailedToExecuteStatement);
}
return hashes;
}
base::expected<sqlite_vfs::PendingFileSet,
SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::GetSharedReadOnlyConnection() {
if (!db_assets_) {
return base::unexpected(Error::kFailedToOpenVfsFileSet);
}
auto pending_file_set = db_assets_->ShareConnection();
if (!pending_file_set.has_value()) {
return base::unexpected(Error::kFailedToShareConnection);
}
return std::move(*pending_file_set);
}
void SqlSharedCacheIsolatedDatabase::
SetSimulateDbFailureCallbackForTesting( // IN-TEST
SimFailedCallback callback) {
simulate_db_failure_callback_ = std::move(callback);
}
bool SqlSharedCacheIsolatedDatabase::ShouldSimulateFailure(
OperationForTesting op) const {
return simulate_db_failure_callback_ && simulate_db_failure_callback_.Run(op);
}
base::expected<void, SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::Init() {
if (ShouldSimulateFailure(OperationForTesting::kInit)) {
return base::unexpected(Error::kFailedForTesting);
}
if (!db_assets_) {
return base::unexpected(Error::kFailedToOpenVfsFileSet);
}
sql::Database& db = db_assets_->db();
if (db.is_open()) {
return base::ok();
}
if (!db.Open(db_assets_->GetDbVirtualFilePath())) {
return base::unexpected(Error::kFailedToOpenDatabase);
}
if (sql::MetaTable::RazeIfIncompatible(
&db, kSharedCacheIsolatedDatabaseCompatibleVersion,
kSharedCacheIsolatedDatabaseCurrentVersion) ==
sql::RazeIfIncompatibleResult::kFailed) {
return base::unexpected(Error::kFailedToRazeIncompatibleVersionDatabase);
}
sql::MetaTable meta_table;
if (sql::MetaTable::DoesTableExist(&db)) {
std::string saved_nik;
if (!meta_table.Init(&db, kSharedCacheIsolatedDatabaseCurrentVersion,
kSharedCacheIsolatedDatabaseCompatibleVersion) ||
!meta_table.GetValue(kSqlBackendSharedCacheIsolatedMetaTableKeyNik,
&saved_nik) ||
saved_nik != nik_string_) {
if (!db.Raze()) {
return base::unexpected(Error::kFailedToRazeIncompatibleNikDatabase);
}
meta_table.Reset();
} else {
return base::ok();
}
}
sql::Transaction transaction(&db);
if (!transaction.Begin()) {
return base::unexpected(Error::kFailedToStartTransaction);
}
if (!db.Execute(GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kCreateResourcesTable))) {
return base::unexpected(Error::kFailedToCreateResourcesTable);
}
if (!db.Execute(GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kCreateResourcesTableIndex))) {
return base::unexpected(Error::kFailedToCreateResourcesTableIndex);
}
if (!meta_table.Init(&db, kSharedCacheIsolatedDatabaseCurrentVersion,
kSharedCacheIsolatedDatabaseCompatibleVersion)) {
return base::unexpected(Error::kFailedToInitMetaTable);
}
if (!meta_table.SetValue(kSqlBackendSharedCacheIsolatedMetaTableKeyNik,
nik_string_)) {
return base::unexpected(Error::kFailedToSetNikInMetaTable);
}
if (!transaction.Commit()) {
return base::unexpected(Error::kFailedToCommitTransaction);
}
return base::ok();
}
base::expected<SqlSharedCacheRowId, SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::Insert(const CacheEntryKey& entry_key,
scoped_refptr<net::IOBuffer> headers,
uint32_t total_body_size,
scoped_refptr<net::IOBuffer> body) {
if (ShouldSimulateFailure(OperationForTesting::kInsert)) {
return base::unexpected(Error::kFailedForTesting);
}
if (!db_assets_ || !db_assets_->db().is_open()) {
return base::unexpected(Error::kDatabaseNotOpen);
}
if (total_body_size >
static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
return base::unexpected(Error::kBodyTooLarge);
}
sql::Transaction transaction(&db_assets_->db());
if (!transaction.Begin()) {
return base::unexpected(Error::kFailedToStartTransaction);
}
SqlSharedCacheRowId shared_cache_row_id;
const bool is_ready =
(total_body_size == 0) ||
(body && total_body_size == static_cast<uint32_t>(body->size()));
constexpr base::span<uint8_t, 0> kEmptySpan;
{
sql::Statement statement(db_assets_->db().GetCachedStatement(
SQL_FROM_HERE, GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kInsertResource)));
statement.BindInt64(0, entry_key.resource_url_hash().value());
statement.BindString(1, entry_key.resource_url());
// Wrapping the span with a RefCountedStaticMemory to avoid memory copy.
// SAFETY: The memory referenced by `headers->span()`/`kEmptySpan` must
// outlive `statement`.
statement.BindBlob(2, base::MakeRefCounted<base::RefCountedStaticMemory>(
headers ? headers->span() : kEmptySpan));
if (is_ready) {
// SAFETY: The memory referenced by `body->span()`/`kEmptySpan` must
// outlive `statement`.
statement.BindBlob(
3, base::MakeRefCounted<base::RefCountedStaticMemory>(
body ? body->span().first(total_body_size) : kEmptySpan));
} else {
statement.BindBlobForStreaming(3, total_body_size);
}
statement.BindBool(4, is_ready);
if (!statement.Run()) {
return base::unexpected(Error::kFailedToExecuteStatement);
}
shared_cache_row_id =
SqlSharedCacheRowId(db_assets_->db().GetLastInsertRowId());
}
if (body && !is_ready) {
RETURN_IF_ERROR(WriteBodyInternal(entry_key, shared_cache_row_id, 0, body,
/*set_ready=*/false,
/*in_transaction=*/true));
}
if (!transaction.Commit()) {
return base::unexpected(Error::kFailedToCommitTransaction);
}
return shared_cache_row_id;
}
base::expected<void, SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::WriteBody(
const CacheEntryKey& entry_key,
SqlSharedCacheRowId shared_cache_row_id,
int offset,
scoped_refptr<net::IOBuffer> buffer,
bool set_ready) {
return WriteBodyInternal(entry_key, shared_cache_row_id, offset, buffer,
set_ready, /*in_transaction=*/false);
}
base::expected<void, SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::WriteBodyInternal(
const CacheEntryKey& entry_key,
SqlSharedCacheRowId shared_cache_row_id,
int offset,
scoped_refptr<net::IOBuffer> buffer,
bool set_ready,
bool in_transaction) {
if (ShouldSimulateFailure(OperationForTesting::kWriteBody)) {
return base::unexpected(Error::kFailedForTesting);
}
if (!db_assets_ || !db_assets_->db().is_open()) {
return base::unexpected(Error::kDatabaseNotOpen);
}
const int size = buffer ? buffer->size() : 0;
CHECK_GE(size, 0);
if (offset < 0 || size > std::numeric_limits<int32_t>::max() - offset) {
return base::unexpected(Error::kInvalidWriteRange);
}
std::optional<sql::Transaction> transaction;
if (!in_transaction) {
transaction.emplace(&db_assets_->db());
if (!transaction->Begin()) {
return base::unexpected(Error::kFailedToStartTransaction);
}
}
if (size > 0) {
ASSIGN_OR_RETURN(auto blob_handle,
db_assets_->db().GetStreamingBlob(
"resources", "body", shared_cache_row_id.value(),
/*readonly=*/false),
[] { return Error::kFailedToGetBlob; });
if (!blob_handle.Write(offset, buffer->span())) {
return base::unexpected(Error::kFailedToWriteBlob);
}
}
if (set_ready) {
sql::Statement statement(db_assets_->db().GetCachedStatement(
SQL_FROM_HERE,
GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kSetResourceReady)));
statement.BindInt64(0, shared_cache_row_id.value());
if (!statement.Run()) {
return base::unexpected(Error::kFailedToSetReady);
}
}
if (transaction && !transaction->Commit()) {
return base::unexpected(Error::kFailedToCommitTransaction);
}
return base::ok();
}
SqlSharedCacheIsolatedDatabase::SqlSharedCacheBlobHandleImpl::
SqlSharedCacheBlobHandleImpl(
base::OnceClosure decrement_closure,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: decrement_closure_(std::move(decrement_closure)),
task_runner_(std::move(task_runner)) {}
SqlSharedCacheIsolatedDatabase::SqlSharedCacheBlobHandleImpl::
~SqlSharedCacheBlobHandleImpl() {
task_runner_->PostTask(FROM_HERE, std::move(decrement_closure_));
}
SqlSharedCacheIsolatedDatabase::BlobHandleHolder::BlobHandleHolder(
sql::StreamingBlobHandle blob_handle,
const CacheEntryKey& entry_key,
int body_size)
: blob_handle_(std::move(blob_handle)),
entry_key_(entry_key),
body_size_(body_size) {}
SqlSharedCacheIsolatedDatabase::BlobHandleHolder::~BlobHandleHolder() = default;
base::expected<scoped_refptr<SqlSharedCacheBlobHandle>,
SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::GetBlobHandle(
const CacheEntryKey& entry_key,
SqlSharedCacheRowId shared_cache_row_id,
int body_size) {
if (ShouldSimulateFailure(OperationForTesting::kRead)) {
return base::unexpected(Error::kFailedForTesting);
}
if (!db_assets_ || !db_assets_->db().is_open()) {
return base::unexpected(Error::kDatabaseNotOpen);
}
ASSIGN_OR_RETURN(
auto* blob_handle_holder,
GetCachedBlobHandleHolder(entry_key, shared_cache_row_id, body_size));
if (blob_handle_holder) {
blob_handle_holder->IncrementRefCount();
return base::MakeRefCounted<SqlSharedCacheBlobHandleImpl>(
base::BindOnce(
&SqlSharedCacheIsolatedDatabase::DecrementBlobHandleRefCount,
weak_factory_.GetWeakPtr(), shared_cache_row_id),
task_runner_);
}
ASSIGN_OR_RETURN(
auto streaming_blob_handle,
GetStreamingBlobHandle(entry_key, shared_cache_row_id, body_size));
blob_handle_holders_.try_emplace(
shared_cache_row_id,
std::make_unique<BlobHandleHolder>(std::move(streaming_blob_handle),
entry_key, body_size));
return base::MakeRefCounted<SqlSharedCacheBlobHandleImpl>(
base::BindOnce(
&SqlSharedCacheIsolatedDatabase::DecrementBlobHandleRefCount,
weak_factory_.GetWeakPtr(), shared_cache_row_id),
task_runner_);
}
base::expected<SqlSharedCacheIsolatedDatabase::BlobHandleHolder*,
SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::GetCachedBlobHandleHolder(
const CacheEntryKey& entry_key,
SqlSharedCacheRowId shared_cache_row_id,
int body_size) {
auto it = blob_handle_holders_.find(shared_cache_row_id);
if (it == blob_handle_holders_.end()) {
return nullptr;
}
if (it->second->resource_url() != entry_key.resource_url()) {
return base::unexpected(Error::kEntryNotFound);
}
if (it->second->body_size() != body_size) {
return base::unexpected(Error::kBodySizeMismatch);
}
return it->second.get();
}
base::expected<sql::StreamingBlobHandle, SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::GetStreamingBlobHandle(
const CacheEntryKey& entry_key,
SqlSharedCacheRowId shared_cache_row_id,
int body_size) {
if (!db_assets_ || !db_assets_->db().is_open()) {
return base::unexpected(Error::kDatabaseNotOpen);
}
{
sql::Statement statement(db_assets_->db().GetCachedStatement(
SQL_FROM_HERE,
GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kSelectUrlAndReadyByRowId)));
statement.BindInt64(0, shared_cache_row_id.value());
if (!statement.Step() || !statement.ColumnBool(1) ||
statement.ColumnString(0) != entry_key.resource_url()) {
return base::unexpected(Error::kEntryNotFound);
}
}
ASSIGN_OR_RETURN(auto blob_handle,
db_assets_->db().GetStreamingBlob(
"resources", "body", shared_cache_row_id.value(),
/*readonly=*/true),
[] { return Error::kFailedToGetBlob; });
if (blob_handle.GetSize() != body_size) {
return base::unexpected(Error::kBodySizeMismatch);
}
return blob_handle;
}
void SqlSharedCacheIsolatedDatabase::DecrementBlobHandleRefCount(
SqlSharedCacheRowId shared_cache_row_id) {
auto it = blob_handle_holders_.find(shared_cache_row_id);
if (it == blob_handle_holders_.end()) {
return;
}
CHECK(it->second);
it->second->DecrementRefCount();
if (it->second->ref_count() == 0) {
blob_handle_holders_.erase(it);
}
}
SqlSharedCacheIsolatedDatabase::ReadResultOrError
SqlSharedCacheIsolatedDatabase::Read(const CacheEntryKey& entry_key,
SqlSharedCacheRowId shared_cache_row_id,
int body_size,
int offset,
scoped_refptr<net::IOBuffer> buffer) {
if (ShouldSimulateFailure(OperationForTesting::kRead)) {
return base::unexpected(Error::kFailedForTesting);
}
if (!db_assets_ || !db_assets_->db().is_open()) {
return base::unexpected(Error::kDatabaseNotOpen);
}
CHECK(buffer);
const int buf_len = buffer->size();
CHECK_GE(buf_len, 0);
CHECK_GE(body_size, 0);
if (offset < 0 || buf_len > std::numeric_limits<int32_t>::max() - offset ||
offset + buf_len > body_size) {
return base::unexpected(Error::kInvalidReadRange);
}
std::optional<sql::StreamingBlobHandle> new_blob_handle;
sql::StreamingBlobHandle* blob_handle_ptr = nullptr;
ASSIGN_OR_RETURN(
auto* blob_handle_holder,
GetCachedBlobHandleHolder(entry_key, shared_cache_row_id, body_size));
if (blob_handle_holder) {
blob_handle_ptr = &blob_handle_holder->blob_handle();
}
if (!blob_handle_ptr) {
ASSIGN_OR_RETURN(
new_blob_handle,
GetStreamingBlobHandle(entry_key, shared_cache_row_id, body_size));
if (new_blob_handle.has_value()) {
blob_handle_ptr = &new_blob_handle.value();
}
}
if (!blob_handle_ptr) {
return base::unexpected(Error::kFailedToGetBlob);
}
if (!blob_handle_ptr->Read(offset, buffer->span())) {
return base::unexpected(Error::kFailedToReadBlob);
}
ReadResult read_result;
read_result.read_bytes = buf_len;
int64_t read_end = static_cast<int64_t>(offset) + buf_len;
if (read_end < static_cast<int64_t>(body_size) &&
read_cache_memory_monitor_) {
const int64_t remaining_bytes = static_cast<int64_t>(body_size) - read_end;
const int cache_size = base::saturated_cast<int>(remaining_bytes);
if (read_cache_memory_monitor_->Allocate(cache_size)) {
auto cache_buffer = base::MakeRefCounted<MonitoredVectorIOBuffer>(
cache_size, read_cache_memory_monitor_);
if (blob_handle_ptr->Read(read_end, cache_buffer->span())) {
read_result.cache_buffer = std::move(cache_buffer);
read_result.cache_buffer_offset = read_end;
}
}
}
return read_result;
}
void SqlSharedCacheIsolatedDatabase::DeleteEntry(
SqlSharedCacheRowId shared_cache_row_id) {
if (ShouldSimulateFailure(OperationForTesting::kDeleteEntry)) {
return;
}
if (!db_assets_ || !db_assets_->db().is_open()) {
return;
}
sql::Statement statement(db_assets_->db().GetCachedStatement(
SQL_FROM_HERE,
GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kDeleteResourceByRowId)));
statement.BindInt64(0, shared_cache_row_id.value());
statement.Run();
}
base::expected<void, SqlSharedCacheIsolatedDatabase::Error>
SqlSharedCacheIsolatedDatabase::DeleteEntries(
const std::vector<SqlSharedCacheRowId>& shared_cache_row_ids) {
CHECK(!shared_cache_row_ids.empty());
if (ShouldSimulateFailure(OperationForTesting::kDeleteEntries)) {
return base::unexpected(Error::kFailedForTesting);
}
if (!db_assets_ || !db_assets_->db().is_open()) {
return base::unexpected(Error::kDatabaseNotOpen);
}
sql::Database& db = db_assets_->db();
sql::Transaction transaction(&db);
if (!transaction.Begin()) {
return base::unexpected(Error::kFailedToStartTransaction);
}
for (auto row_id : shared_cache_row_ids) {
sql::Statement statement(db.GetCachedStatement(
SQL_FROM_HERE,
GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kDeleteResourceByRowId)));
statement.BindInt64(0, row_id.value());
statement.Run();
}
if (!transaction.Commit()) {
return base::unexpected(Error::kFailedToCommitTransaction);
}
// TODO(crbug.com/473666511): Autovacuum is disabled on Desktop, so we need
// to run VACUUM manually at some point. However, running it here might
// cause performance issues. Figure out a better timing/strategy to run it.
return base::ok();
}
void SqlSharedCacheIsolatedDatabase::Cleanup() {
if (db_assets_ && db_assets_->db().is_open()) {
bool has_entries = false;
{
sql::Statement count_statement(db_assets_->db().GetCachedStatement(
SQL_FROM_HERE,
GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kSelectRowidLimit1)));
has_entries = count_statement.Step();
}
if (!has_entries) {
db_assets_->AbandonAndDeleteFiles();
}
}
db_assets_.reset();
}
bool SqlSharedCacheIsolatedDatabase::HasRowForTesting( // IN-TEST
SqlSharedCacheRowId shared_cache_row_id) {
if (!db_assets_ || !db_assets_->db().is_open()) {
return false;
}
sql::Statement statement(db_assets_->db().GetCachedStatement(
SQL_FROM_HERE,
GetSharedCacheIsolatedDatabaseQuery(
SharedCacheIsolatedDatabaseQuery::kSelectUrlAndReadyByRowId)));
statement.BindInt64(0, shared_cache_row_id.value());
return statement.Step();
}
} // namespace disk_cache