0

Builder pattern in DatabaseOptions

DatabaseOptions is getting too large for the "explicit out of line
constructor for complex types" presubmit. Adding a constructor to it
prevents it from being an aggregate type, which is how most of the
callers currently use it.

This Cl makes DatabaseOptions members private and adds builder-type
setters for each member. It also updates all callers, and adds an out of
line constructor.

A future improvement could be to add a passkey to the setters for
discouraged options.

Bug: None
Change-Id: I63562f43c8b290247878d194039487b240e958c2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6216099
Reviewed-by: Gabriel Charette <gab@chromium.org>
Reviewed-by: Greg Thompson <grt@chromium.org>
Owners-Override: Gabriel Charette <gab@chromium.org>
Commit-Queue: Anthony Vallée-Dubois <anthonyvd@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1414974}
This commit is contained in:
Anthony Vallée-Dubois
2025-02-03 09:01:41 -08:00
committed by Chromium LUCI CQ
parent b198160771
commit 9adf0bbf4b
34 changed files with 315 additions and 293 deletions

@ -39,14 +39,13 @@ static const int kSizeThresholdForFlush = 200;
ActivityDatabase::ActivityDatabase(ActivityDatabase::Delegate* delegate)
: delegate_(delegate),
db_(
{
.page_size = 4096,
.cache_size = 32,
db_(sql::DatabaseOptions()
.set_page_size(4096)
.set_cache_size(32)
// TODO(pwnall): Add a meta table and remove this option.
.mmap_alt_status_discouraged = true,
.enable_views_discouraged = true, // Required by mmap_alt_status.
},
.set_mmap_alt_status_discouraged(true)
.set_enable_views_discouraged(
true), // Required by mmap_alt_status.
/*tag=*/"Activity"),
valid_db_(false),
batch_mode_(true),

@ -91,11 +91,11 @@ PredictorDatabaseInternal::PredictorDatabaseInternal(
scoped_refptr<base::SequencedTaskRunner> db_task_runner)
: db_path_(profile->GetPath().Append(kPredictorDatabaseName)),
db_(std::make_unique<sql::Database>(
sql::DatabaseOptions{
sql::DatabaseOptions()
// TODO(pwnall): Add a meta table and remove this option.
.mmap_alt_status_discouraged = true,
.enable_views_discouraged = true, // Required by mmap_alt_status.
},
.set_mmap_alt_status_discouraged(true)
.set_enable_views_discouraged(
true), // Required by mmap_alt_status.
sql::Database::Tag("Predictor"))),
db_task_runner_(db_task_runner),
autocomplete_table_(

@ -1070,12 +1070,9 @@ Database* Database::GetInstance() {
Database::Database() {
base::ScopedAllowBlockingForTesting allow_blocking;
db_ = std::make_unique<sql::Database>(
sql::DatabaseOptions{
.exclusive_locking =
false, // centipede may run several fuzzers at once
.page_size = sql::DatabaseOptions::kDefaultPageSize,
.cache_size = 0,
},
sql::DatabaseOptions()
// centipede may run several fuzzers at once
.set_exclusive_locking(false),
sql::test::kTestTag);
base::FilePath db_path;
CHECK(base::PathService::Get(base::DIR_TEMP, &db_path));

@ -382,7 +382,7 @@ void OptOutStoreSQL::LoadBlockList(
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (!db_) {
db_ = std::make_unique<sql::Database>(
sql::DatabaseOptions{
sql::DatabaseOptions()
// The entry size should be between 11 and 10 + x bytes, where x is
// the the length of the host name string in bytes. The total number
// of entries per host is bounded at 32, and the total number of
@ -394,8 +394,8 @@ void OptOutStoreSQL::LoadBlockList(
// in their top 20 hosts. It should be closer to 32 * 100 * 20 for
// most users, which is about 4096 * 15. The total size of the
// database will be capped at 3200 entries.
.page_size = 4096,
.cache_size = 250},
.set_page_size(4096)
.set_cache_size(250),
// TODO(crbug.com/40134470): Migrate to OptOutBlocklist and update any
// backend code that may depend on this tag.
sql::Database::Tag("OptOutBlacklist"));

@ -237,11 +237,11 @@ bool FaviconDatabase::IconMappingEnumerator::GetNextIconMapping(
}
FaviconDatabase::FaviconDatabase()
: db_(
{// Favicons db only stores favicons, so we don't need that big a page
// size or cache.
.page_size = 2048,
.cache_size = 32},
: db_(sql::DatabaseOptions()
// Favicons db only stores favicons, so we don't need that big a
// page size or cache.
.set_page_size(2048)
.set_cache_size(32),
/*tag=*/"Thumbnail") {}
FaviconDatabase::~FaviconDatabase() {

@ -83,22 +83,22 @@ HistoryDatabase::HistoryDatabase(
DownloadInterruptReason download_interrupt_reason_crash)
: DownloadDatabase(download_interrupt_reason_none,
download_interrupt_reason_crash),
db_(
{// Note that we don't set exclusive locking here. That's done by
// BeginExclusiveMode below which is called later (we have to be in
// shared mode to start out for the in-memory backend to read the
// data).
// TODO(crbug.com/40159106) Remove this dependency on normal locking
// mode.
.exclusive_locking = false,
// Set the database page size to something a little larger to give us
// better performance (we're typically seek rather than bandwidth
// limited). Must be a power of 2 and a max of 65536.
.page_size = 4096,
// Set the cache size. The page size, plus a little extra, times this
// value, tells us how much memory the cache will use maximum.
// 1000 * 4kB = 4MB
.cache_size = 1000},
db_(sql::DatabaseOptions()
// Note that we don't set exclusive locking here. That's done by
// BeginExclusiveMode below which is called later (we have to be
// in shared mode to start out for the in-memory backend to read
// the data).
// TODO(crbug.com/40159106) Remove this dependency on normal
// locking mode.
.set_exclusive_locking(false)
// Set the database page size to something a little larger to give
// us better performance (we're typically seek rather than
// bandwidth limited). Must be a power of 2 and a max of 65536.
.set_page_size(4096)
// Set the cache size. The page size, plus a little extra, times
// this value, tells us how much memory the cache will use
// maximum. 1000 * 4kB = 4MB
.set_cache_size(1000),
/*tag=*/"History"),
history_metadata_db_(&db_, &meta_table_) {}

@ -206,7 +206,7 @@ bool TopSitesDatabase::InitImpl(const base::FilePath& db_name) {
// Settings copied from FaviconDatabase.
db_ = std::make_unique<sql::Database>(
sql::DatabaseOptions{.page_size = 4096, .cache_size = 32},
sql::DatabaseOptions().set_page_size(4096).set_cache_size(32),
sql::Database::Tag("TopSites"));
db_->set_error_callback(
base::BindRepeating(&TopSitesDatabase::DatabaseErrorCallback,

@ -34,7 +34,7 @@ std::string CreateRandomSalt() {
MediaDeviceSaltDatabase::MediaDeviceSaltDatabase(const base::FilePath& db_path)
: db_path_(db_path),
db_(sql::DatabaseOptions{.page_size = 4096, .cache_size = 16},
db_(sql::DatabaseOptions().set_page_size(4096).set_cache_size(16),
/*tag=*/"MediaDeviceSalts") {}
std::optional<std::string> MediaDeviceSaltDatabase::GetOrInsertSalt(

@ -1109,7 +1109,8 @@ LoginDatabase::LoginDatabase(const base::FilePath& db_path,
: db_path_(db_path),
is_account_store_(is_account_store),
// Set options for a small, private database (based on WebDatabase).
db_({.page_size = 2048, .cache_size = 32}, /*tag=*/"Passwords"),
db_(sql::DatabaseOptions().set_page_size(2048).set_cache_size(32),
/*tag=*/"Passwords"),
is_deleting_undecryptable_logins_enabled_by_policy_(can_delete) {}
LoginDatabase::~LoginDatabase() = default;

@ -154,7 +154,7 @@ bool SqliteDatabaseTransaction::Commit() {
PowerBookmarkDatabaseImpl::PowerBookmarkDatabaseImpl(
const base::FilePath& database_dir)
: db_(sql::DatabaseOptions{.page_size = 4096, .cache_size = 128},
: db_(sql::DatabaseOptions().set_page_size(4096).set_cache_size(128),
/*tag=*/"PowerBookmarks"),
database_path_(database_dir.Append(kDatabaseName)) {
sync_db_ =

@ -114,8 +114,8 @@ UkmDatabaseBackend::UkmDatabaseBackend(
: database_path_(database_path),
in_memory_(in_memory),
callback_task_runner_(callback_task_runner),
db_(sql::DatabaseOptions{.wal_mode = base::FeatureList::IsEnabled(
kSqlWALModeOnSegmentationDatabase)},
db_(sql::DatabaseOptions().set_wal_mode(
base::FeatureList::IsEnabled(kSqlWALModeOnSegmentationDatabase)),
/*tag=*/"UKMMetrics"),
metrics_table_(&db_),
url_table_(&db_),

@ -209,12 +209,13 @@ SharedStorageDatabase::SharedStorageDatabase(
base::FilePath db_path,
scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
std::unique_ptr<SharedStorageDatabaseOptions> options)
: db_({.wal_mode = base::FeatureList::IsEnabled(
blink::features::kSharedStorageAPIEnableWALForDatabase),
// We DCHECK that the page size is valid in the constructor for
// `SharedStorageOptions`.
.page_size = options->max_page_size,
.cache_size = options->max_cache_size},
: db_(sql::DatabaseOptions()
.set_wal_mode(base::FeatureList::IsEnabled(
blink::features::kSharedStorageAPIEnableWALForDatabase))
// We DCHECK that the page size is valid in the constructor for
// `SharedStorageOptions`.
.set_page_size(options->max_page_size)
.set_cache_size(options->max_cache_size),
/*tag=*/"SharedStorage"),
db_path_(std::move(db_path)),
special_storage_policy_(std::move(special_storage_policy)),

@ -73,14 +73,16 @@ sql::InitStatus FailedMigrationTo(int version_num) {
} // namespace
WebDatabase::WebDatabase()
: db_({.wal_mode = base::FeatureList::IsEnabled(kSqlWALModeOnWebDatabase),
// We don't store that much data in the tables so use a small page
// size. This provides a large benefit for empty tables (which is
// very likely with the tables we create).
.page_size = 2048,
// We shouldn't have much data and what access we currently have is
// quite infrequent. So we go with a small cache size.
.cache_size = 32},
: db_(sql::DatabaseOptions()
.set_wal_mode(
base::FeatureList::IsEnabled(kSqlWALModeOnWebDatabase))
// We don't store that much data in the tables so use a small page
// size. This provides a large benefit for empty tables (which is
// very likely with the tables we create).
.set_page_size(2048)
// We shouldn't have much data and what access we currently have
// is quite infrequent. So we go with a small cache size.
.set_cache_size(32),
/*tag=*/"Web") {}
WebDatabase::~WebDatabase() {

@ -157,7 +157,7 @@ AggregationServiceStorageSql::AggregationServiceStorageSql(
clock_(*clock),
max_stored_requests_per_reporting_origin_(
max_stored_requests_per_reporting_origin),
db_(sql::DatabaseOptions{.page_size = 4096, .cache_size = 32},
db_(sql::DatabaseOptions().set_page_size(4096).set_cache_size(32),
/*tag=*/"AggregationService") {
DETACH_FROM_SEQUENCE(sequence_checker_);
CHECK(clock);

@ -557,7 +557,7 @@ AttributionStorageSql::AttributionStorageSql(
: path_to_database_(user_data_directory.empty()
? base::FilePath()
: DatabasePath(user_data_directory)),
db_(sql::DatabaseOptions{.page_size = 4096, .cache_size = 32},
db_(sql::DatabaseOptions().set_page_size(4096).set_cache_size(32),
/*tag=*/"Conversions"),
delegate_(delegate),
rate_limit_table_(delegate_),

@ -237,7 +237,7 @@ bool BrowsingTopicsSiteDataStorage::LazyInit() {
return db_init_status_ == InitStatus::kSuccess;
db_ = std::make_unique<sql::Database>(
sql::DatabaseOptions{.page_size = 4096, .cache_size = 32},
sql::DatabaseOptions().set_page_size(4096).set_cache_size(32),
sql::Database::Tag("BrowsingTopics"));
// base::Unretained is safe here because this BrowsingTopicsSiteDataStorage

@ -100,13 +100,13 @@ BtmDatabase::BtmDatabase(const std::optional<base::FilePath>& db_path)
: db_path_(db_path.value_or(base::FilePath())) {
DCHECK(base::FeatureList::IsEnabled(features::kBtm));
sql::DatabaseOptions db_options{
.wal_mode = base::FeatureList::IsEnabled(kSqlWALModeOnDipsDatabase),
.page_size = 4096,
.cache_size = 32};
if (base::FeatureList::IsEnabled(kDisableExclusiveLockingOnDipsDatabase)) {
db_options.exclusive_locking = false;
}
sql::DatabaseOptions db_options =
sql::DatabaseOptions()
.set_wal_mode(base::FeatureList::IsEnabled(kSqlWALModeOnDipsDatabase))
.set_page_size(4096)
.set_cache_size(32)
.set_exclusive_locking(!base::FeatureList::IsEnabled(
kDisableExclusiveLockingOnDipsDatabase));
db_ = std::make_unique<sql::Database>(db_options, sql::Database::Tag("DIPS"));

@ -750,7 +750,7 @@ bool FirstPartySetsDatabase::LazyInit() {
CHECK_EQ(db_.get(), nullptr);
db_ = std::make_unique<sql::Database>(
sql::DatabaseOptions{.page_size = 4096, .cache_size = 32},
sql::DatabaseOptions().set_page_size(4096).set_cache_size(32),
sql::Database::Tag("FirstPartySets"));
// base::Unretained is safe here because this FirstPartySetsDatabase owns
// the sql::Database instance that stores and uses the callback. So,

@ -5413,9 +5413,8 @@ base::FilePath DBPath(const base::FilePath& base) {
}
sql::DatabaseOptions GetDatabaseOptions() {
return sql::DatabaseOptions{
.wal_mode = base::FeatureList::IsEnabled(
features::kFledgeEnableWALForInterestGroupStorage)};
return sql::DatabaseOptions().set_wal_mode(base::FeatureList::IsEnabled(
features::kFledgeEnableWALForInterestGroupStorage));
}
void ReportCreateSchemaResult(

@ -53,7 +53,7 @@ CdmStorageDatabase::CdmStorageDatabase(const base::FilePath& path)
// bytes) and that we'll typically only be pulling one file at a time
// (playback), specify a large page size to allow inner nodes can pack
// many keys, to keep the index B-tree flat.
db_(sql::DatabaseOptions{.page_size = 32768, .cache_size = 8},
db_(sql::DatabaseOptions().set_page_size(32768).set_cache_size(8),
/*tag=*/"CdmStorage") {
// base::Unretained is safe because `db_` is owned by `this`
db_.set_error_callback(base::BindRepeating(

@ -96,7 +96,7 @@ class MockCdmStorageDatabaseV1 {
public:
// The database will be in-memory if `path` is empty.
explicit MockCdmStorageDatabaseV1()
: db_(sql::DatabaseOptions{.page_size = 32768, .cache_size = 8},
: db_(sql::DatabaseOptions().set_page_size(32768).set_cache_size(8),
sql::test::kTestTag) {}
~MockCdmStorageDatabaseV1() = default;

@ -113,7 +113,7 @@ PrivateAggregationBudgetStorage::PrivateAggregationBudgetStorage(
kFlushDelay),
db_task_runner_(std::move(db_task_runner)),
db_(std::make_unique<sql::Database>(
sql::DatabaseOptions{.page_size = 4096, .cache_size = 32},
sql::DatabaseOptions().set_page_size(4096).set_cache_size(32),
sql::Database::Tag("PrivateAggregation"))) {}
PrivateAggregationBudgetStorage::~PrivateAggregationBudgetStorage() {

@ -89,7 +89,7 @@ ClientTraceReport::ClientTraceReport() = default;
ClientTraceReport::~ClientTraceReport() = default;
TraceReportDatabase::TraceReportDatabase()
: database_(sql::DatabaseOptions{.page_size = 4096, .cache_size = 128},
: database_(sql::DatabaseOptions().set_page_size(4096).set_cache_size(128),
/*tag=*/"LocalTraces") {
DETACH_FROM_SEQUENCE(sequence_checker_);
}

@ -94,9 +94,9 @@ bool SQLitePersistentStoreBackendBase::InitializeDatabase() {
// TODO(crbug.com/40262972): Remove explicit_locking = false. This currently
// needs to be set to false because of several failing MigrationTests.
db_ = std::make_unique<sql::Database>(
sql::DatabaseOptions{
.exclusive_locking = false,
.exclusive_database_file_lock = enable_exclusive_access_},
sql::DatabaseOptions()
.set_exclusive_locking(false)
.set_exclusive_database_file_lock(enable_exclusive_access_),
histogram_tag_);
// base::Unretained is safe because |this| owns (and therefore outlives) the

@ -90,11 +90,11 @@ NOINLINE TrustTokenDatabaseOwner::TrustTokenDatabaseOwner(
db_task_runner)),
db_task_runner_(db_task_runner),
backing_database_(std::make_unique<sql::Database>(
sql::DatabaseOptions{
sql::DatabaseOptions()
// TODO(pwnall): Add a meta table and remove this option.
.mmap_alt_status_discouraged = true,
.enable_views_discouraged = true, // Required by mmap_alt_status.
},
.set_mmap_alt_status_discouraged(true)
.set_enable_views_discouraged(
true), // Required by mmap_alt_status.
sql::Database::Tag("TrustTokens"))),
issuer_table_(
std::make_unique<sqlite_proto::KeyValueTable<TrustTokenIssuerConfig>>(

@ -218,6 +218,9 @@ void RecordOpenDatabaseFailureReason(const std::string& histogram_tag,
} // namespace
DatabaseOptions::DatabaseOptions() = default;
DatabaseOptions::~DatabaseOptions() = default;
// static
Database::ScopedErrorExpecterCallback* Database::current_expecter_cb_ = nullptr;
@ -351,12 +354,12 @@ Database::Database(DatabaseOptions options, Database::Tag tag)
mmap_disabled_(!enable_mmap_by_default_),
histogram_tag_(tag.value),
tracing_track_name_(base::StrCat({"Database: ", histogram_tag_})) {
DCHECK_GE(options.page_size, 512);
DCHECK_LE(options.page_size, 65536);
DCHECK(!(options.page_size & (options.page_size - 1)))
DCHECK_GE(options.page_size_, 512);
DCHECK_LE(options.page_size_, 65536);
DCHECK(!(options.page_size_ & (options.page_size_ - 1)))
<< "page_size must be a power of two";
DCHECK(!options_.mmap_alt_status_discouraged ||
options_.enable_views_discouraged)
DCHECK(!options_.mmap_alt_status_discouraged_ ||
options_.enable_views_discouraged_)
<< "mmap_alt_status requires views";
// It's valid to construct a database on a sequence and then pass it to a
@ -513,7 +516,7 @@ void Database::Preload() {
return;
}
CHECK(!options_.exclusive_database_file_lock)
CHECK(!options_.exclusive_database_file_lock_)
<< "Cannot preload an exclusively locked database.";
std::optional<base::ScopedBlockingCall> scoped_blocking_call;
@ -882,7 +885,7 @@ size_t Database::ComputeMmapSizeForOpen() {
// sql::MetaTable, otherwise it is tracked in a special view.
// TODO(pwnall): Migrate all databases to using a meta table.
int64_t mmap_ofs = 0;
if (options_.mmap_alt_status_discouraged) {
if (options_.mmap_alt_status_discouraged_) {
if (!GetMmapAltStatus(&mmap_ofs)) {
return 0;
}
@ -973,7 +976,7 @@ size_t Database::ComputeMmapSizeForOpen() {
DCHECK(mmap_ofs > 0 || mmap_ofs == MetaTable::kMmapFailure);
}
if (options_.mmap_alt_status_discouraged) {
if (options_.mmap_alt_status_discouraged_) {
if (!SetMmapAltStatus(mmap_ofs)) {
return 0;
}
@ -1099,13 +1102,11 @@ bool Database::Raze() {
return false;
}
sql::Database null_db(
sql::DatabaseOptions{
.exclusive_locking = true,
.page_size = options_.page_size,
.cache_size = 0,
.enable_views_discouraged = options_.enable_views_discouraged,
},
Database null_db(
DatabaseOptions()
.set_exclusive_locking(true)
.set_page_size(options_.page_size_)
.set_enable_views_discouraged(options_.enable_views_discouraged_),
"RazeNullDB");
if (!null_db.OpenInMemory()) {
DLOG(FATAL) << "Unable to open in-memory database.";
@ -1189,7 +1190,7 @@ bool Database::Raze() {
// database connection open.
std::ignore = Execute("PRAGMA journal_mode=TRUNCATE;");
const std::string page_size_sql = base::StrCat(
{"PRAGMA page_size=", base::NumberToString(options_.page_size)});
{"PRAGMA page_size=", base::NumberToString(options_.page_size_)});
if (!Execute(page_size_sql)) {
return false;
}
@ -1989,7 +1990,7 @@ bool Database::OpenInternal(const std::string& db_file_path) {
int open_flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
SQLITE_OPEN_EXRESCODE | SQLITE_OPEN_PRIVATECACHE;
std::string uri_file_path = db_file_path;
if (options_.exclusive_database_file_lock) {
if (options_.exclusive_database_file_lock_) {
#if BUILDFLAG(IS_WIN)
const bool in_memory = db_file_path == kSqliteOpenInMemoryPath;
if (!in_memory) {
@ -2026,7 +2027,7 @@ bool Database::OpenInternal(const std::string& db_file_path) {
for (int i = 1; i <= kMaxOpenAttempts; ++i) {
sqlite_result_code = ToSqliteResultCode(
sqlite3_open_v2(uri_file_path.c_str(), &db, open_flags,
options_.vfs_name_discouraged));
options_.vfs_name_discouraged_));
if (sqlite_result_code != sql::SqliteResultCode::kBusy) {
// Record how many iterations were required to open the database. The
// histogram is not emitted if sqlite3_open_v2(...) fails.
@ -2088,7 +2089,7 @@ bool Database::OpenInternal(const std::string& db_file_path) {
static_assert(
SQLITE_DEFAULT_LOCKING_MODE == 1,
"Chrome assumes SQLite is configured to default to EXCLUSIVE locking");
if (!options_.exclusive_locking) {
if (!options_.exclusive_locking_) {
if (!Execute("PRAGMA locking_mode=NORMAL")) {
RecordOpenDatabaseFailureReason(
histogram_tag_, OpenDatabaseFailedReason::kLockingModeFailed);
@ -2132,7 +2133,7 @@ bool Database::OpenInternal(const std::string& db_file_path) {
// Needs to happen before entering WAL mode. Will only work if this the first
// time the database is being opened in WAL mode.
const std::string page_size_sql =
base::StringPrintf("PRAGMA page_size=%d", options_.page_size);
base::StringPrintf("PRAGMA page_size=%d", options_.page_size_);
if (!ExecuteWithTimeout(page_size_sql, kBusyTimeout)) {
RecordOpenDatabaseFailureReason(histogram_tag_,
OpenDatabaseFailedReason::kPageSizeFailed);
@ -2197,13 +2198,13 @@ bool Database::OpenInternal(const std::string& db_file_path) {
}
CHECK(db_);
if (options_.flush_to_media) {
if (options_.flush_to_media_) {
std::ignore = Execute("PRAGMA fullfsync=1");
}
if (options_.cache_size != 0) {
if (options_.cache_size_ != 0) {
const std::string cache_size_sql = base::StrCat(
{"PRAGMA cache_size=", base::NumberToString(options_.cache_size)});
{"PRAGMA cache_size=", base::NumberToString(options_.cache_size_)});
std::ignore = ExecuteWithTimeout(cache_size_sql, kBusyTimeout);
}
@ -2302,7 +2303,7 @@ void Database::ConfigureSqliteDatabaseObject() {
sqlite_result_code = ToSqliteResultCode(
sqlite3_db_config(db_, SQLITE_DBCONFIG_ENABLE_VIEW,
options_.enable_views_discouraged ? 1 : 0, nullptr));
options_.enable_views_discouraged_ ? 1 : 0, nullptr));
DCHECK_EQ(sqlite_result_code, SqliteResultCode::kOk)
<< "sqlite3_db_config() should not fail";
}
@ -2521,9 +2522,9 @@ bool Database::UseWALMode() const {
// locking, because this case does not require shared memory support.
// At the time this was implemented (May 2020), Fuchsia's shared
// memory support was insufficient for SQLite's needs.
return options_.wal_mode && options_.exclusive_locking;
return options_.wal_mode_ && options_.exclusive_locking_;
#else
return options_.wal_mode;
return options_.wal_mode_;
#endif // BUILDFLAG(IS_FUCHSIA)
}

@ -73,6 +73,9 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
// Guaranteed to match SQLITE_DEFAULT_PAGE_SIZE.
static constexpr int kDefaultPageSize = 4096;
DatabaseOptions();
~DatabaseOptions();
// If true, the database can only be opened by one process at a time.
//
// SQLite supports a locking protocol that allows multiple processes to safely
@ -93,7 +96,10 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
// Exclusive mode is strongly recommended. It reduces the I/O cost of setting
// up a transaction. It also removes the need of handling transaction failures
// due to lock contention.
bool exclusive_locking = true;
DatabaseOptions& set_exclusive_locking(bool exclusive_locking) {
exclusive_locking_ = exclusive_locking;
return *this;
}
// If true, enables exclusive=true vfs URI parameter on the database file.
// This is only supported on Windows.
@ -112,7 +118,11 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
// This option is experimental and will be merged into the `exclusive_locking`
// option above if proven to cause no OS compatibility issues.
// TODO(crbug.com/40262539): Merge into above option, if possible.
bool exclusive_database_file_lock = false;
DatabaseOptions& set_exclusive_database_file_lock(
bool exclusive_database_file_lock) {
exclusive_database_file_lock_ = exclusive_database_file_lock;
return *this;
}
// If true, enables SQLite's Write-Ahead Logging (WAL).
//
@ -127,8 +137,10 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
// 'PRAGMA page_size = <new-size>' will result in no-ops.
//
// More details at https://www.sqlite.org/wal.html
bool wal_mode =
base::FeatureList::IsEnabled(sql::features::kEnableWALModeByDefault);
DatabaseOptions& set_wal_mode(bool wal_mode) {
wal_mode_ = wal_mode;
return *this;
}
// If true, transaction commit waits for data to reach persistent media.
//
@ -148,7 +160,10 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
// until the data is written to the persistent media. This guarantees
// durability in the event of power loss, which is needed to guarantee the
// integrity of non-WAL databases.
bool flush_to_media = false;
DatabaseOptions& set_flush_to_media(bool flush_to_media) {
flush_to_media_ = flush_to_media;
return *this;
}
// Database page size.
//
@ -164,7 +179,10 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
// more I/O when making small changes to existing records.
//
// Must be a power of two between 512 and 65536 inclusive.
int page_size = kDefaultPageSize;
DatabaseOptions& set_page_size(int page_size) {
page_size_ = page_size;
return *this;
}
// The size of in-memory cache, in pages.
//
@ -173,7 +191,10 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
//
// 0 invokes SQLite's default, which is currently to size up the cache to use
// exactly 2,048,000 bytes of RAM.
int cache_size = 0;
DatabaseOptions& set_cache_size(int cache_size) {
cache_size_ = cache_size;
return *this;
}
// Stores mmap failures in the SQL schema, instead of the meta table.
//
@ -183,7 +204,11 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
// If this option is true, the mmap status is stored in the database schema.
// Like any other schema change, changing the mmap status invalidates all
// pre-compiled SQL statements.
bool mmap_alt_status_discouraged = false;
DatabaseOptions& set_mmap_alt_status_discouraged(
bool mmap_alt_status_discouraged) {
mmap_alt_status_discouraged_ = mmap_alt_status_discouraged;
return *this;
}
// If true, enables SQL views (a discouraged feature) for this database.
//
@ -192,13 +217,37 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
//
// If this option is false, CREATE VIEW and DROP VIEW succeed, but SELECT
// statements targeting views fail.
bool enable_views_discouraged = false;
DatabaseOptions& set_enable_views_discouraged(bool enable_views_discouraged) {
enable_views_discouraged_ = enable_views_discouraged;
return *this;
}
// If non-null, specifies the vfs implementation for the database to look for.
// Most use-cases do not require the use of a
// VFS(https://www.sqlite.org/vfs.html). This option should only be used when
// there is a clear need for it.
const char* vfs_name_discouraged = nullptr;
DatabaseOptions& set_vfs_name_discouraged(const char* vfs_name_discouraged) {
vfs_name_discouraged_ = vfs_name_discouraged;
return *this;
}
private:
friend class Database;
FRIEND_TEST_ALL_PREFIXES(DatabaseOptionsTest,
EnableViewsDiscouraged_FalseByDefault);
FRIEND_TEST_ALL_PREFIXES(DatabaseOptionsTest, FlushToDisk_FalseByDefault);
FRIEND_TEST_ALL_PREFIXES(SQLDatabaseTest, ReOpenWithDifferentJournalMode);
bool exclusive_locking_ = true;
bool exclusive_database_file_lock_ = false;
bool wal_mode_ =
base::FeatureList::IsEnabled(sql::features::kEnableWALModeByDefault);
bool flush_to_media_ = false;
int page_size_ = kDefaultPageSize;
int cache_size_ = 0;
bool mmap_alt_status_discouraged_ = false;
bool enable_views_discouraged_ = false;
const char* vfs_name_discouraged_ = nullptr;
};
// Holds database diagnostics in a structured format.
@ -324,7 +373,7 @@ class COMPONENT_EXPORT(SQL) Database {
// Pre-init configuration ----------------------------------------------------
// The page size that will be used when creating a new database.
int page_size() const { return options_.page_size; }
int page_size() const { return options_.page_size_; }
// Returns whether a database will be opened in WAL mode.
bool UseWALMode() const;

@ -25,6 +25,8 @@ enum class OpenVariant {
kOnDiskExclusiveWal = 4,
};
} // namespace
// We use the parameter to run all tests with WAL mode on and off.
class DatabaseOptionsTest : public testing::TestWithParam<OpenVariant> {
public:
@ -89,11 +91,10 @@ class DatabaseOptionsTest : public testing::TestWithParam<OpenVariant> {
};
TEST_P(DatabaseOptionsTest, FlushToDisk_FalseByDefault) {
DatabaseOptions options = {
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
};
EXPECT_FALSE(options.flush_to_media) << "Invalid test assumption";
DatabaseOptions options = DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode());
EXPECT_FALSE(options.flush_to_media_) << "Invalid test assumption";
Database db(options, test::kTestTag);
OpenDatabase(db);
@ -102,26 +103,22 @@ TEST_P(DatabaseOptionsTest, FlushToDisk_FalseByDefault) {
}
TEST_P(DatabaseOptionsTest, FlushToDisk_True) {
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.flush_to_media = true,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_flush_to_media(true),
test::kTestTag);
OpenDatabase(db);
EXPECT_EQ("1", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"));
}
TEST_P(DatabaseOptionsTest, FlushToDisk_False_DoesNotCrash) {
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.flush_to_media = false,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_flush_to_media(false),
test::kTestTag);
OpenDatabase(db);
EXPECT_EQ("0", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"))
@ -130,13 +127,11 @@ TEST_P(DatabaseOptionsTest, FlushToDisk_False_DoesNotCrash) {
}
TEST_P(DatabaseOptionsTest, FlushToDisk_True_DoesNotCrash) {
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.flush_to_media = true,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_flush_to_media(true),
test::kTestTag);
OpenDatabase(db);
EXPECT_EQ("1", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"))
@ -147,13 +142,11 @@ TEST_P(DatabaseOptionsTest, FlushToDisk_True_DoesNotCrash) {
TEST_P(DatabaseOptionsTest, PageSize_Default) {
static_assert(DatabaseOptions::kDefaultPageSize == 4096,
"The page size numbers in this test file need to change");
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.page_size = 4096,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_page_size(4096),
test::kTestTag);
OpenDatabase(db);
EXPECT_EQ("4096", sql::test::ExecuteWithResult(&db, "PRAGMA page_size"));
@ -168,13 +161,11 @@ TEST_P(DatabaseOptionsTest, PageSize_Default) {
TEST_P(DatabaseOptionsTest, PageSize_Large) {
static_assert(DatabaseOptions::kDefaultPageSize < 16384,
"The page size numbers in this test file need to change");
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.page_size = 16384,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_page_size(16384),
test::kTestTag);
OpenDatabase(db);
EXPECT_EQ("16384", sql::test::ExecuteWithResult(&db, "PRAGMA page_size"));
@ -189,13 +180,11 @@ TEST_P(DatabaseOptionsTest, PageSize_Large) {
TEST_P(DatabaseOptionsTest, PageSize_Small) {
static_assert(DatabaseOptions::kDefaultPageSize > 1024,
"The page size numbers in this test file need to change");
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.page_size = 1024,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_page_size(1024),
test::kTestTag);
OpenDatabase(db);
EXPECT_EQ("1024", sql::test::ExecuteWithResult(&db, "PRAGMA page_size"));
@ -208,48 +197,41 @@ TEST_P(DatabaseOptionsTest, PageSize_Small) {
}
TEST_P(DatabaseOptionsTest, CacheSize_Legacy) {
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.cache_size = 0,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_cache_size(0),
test::kTestTag);
OpenDatabase(db);
EXPECT_EQ("-2000", sql::test::ExecuteWithResult(&db, "PRAGMA cache_size"));
}
TEST_P(DatabaseOptionsTest, CacheSize_Small) {
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.cache_size = 16,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_cache_size(16),
test::kTestTag);
OpenDatabase(db);
EXPECT_EQ("16", sql::test::ExecuteWithResult(&db, "PRAGMA cache_size"));
}
TEST_P(DatabaseOptionsTest, CacheSize_Large) {
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.cache_size = 1000,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_cache_size(1000),
test::kTestTag);
OpenDatabase(db);
EXPECT_EQ("1000", sql::test::ExecuteWithResult(&db, "PRAGMA cache_size"));
}
TEST_P(DatabaseOptionsTest, EnableViewsDiscouraged_FalseByDefault) {
DatabaseOptions options = {
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
};
EXPECT_FALSE(options.enable_views_discouraged) << "Invalid test assumption";
DatabaseOptions options = DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode());
EXPECT_FALSE(options.enable_views_discouraged_) << "Invalid test assumption";
Database db(options, test::kTestTag);
OpenDatabase(db);
@ -272,13 +254,11 @@ TEST_P(DatabaseOptionsTest, EnableViewsDiscouraged_FalseByDefault) {
}
TEST_P(DatabaseOptionsTest, EnableViewsDiscouraged_True) {
Database db(
DatabaseOptions{
.exclusive_locking = exclusive_locking(),
.wal_mode = wal_mode(),
.enable_views_discouraged = true,
},
test::kTestTag);
Database db(DatabaseOptions()
.set_exclusive_locking(exclusive_locking())
.set_wal_mode(wal_mode())
.set_enable_views_discouraged(true),
test::kTestTag);
OpenDatabase(db);
ASSERT_TRUE(db.Execute("CREATE VIEW view(id) AS SELECT 1"));
@ -299,6 +279,4 @@ INSTANTIATE_TEST_SUITE_P(
OpenVariant::kOnDiskNonExclusiveJournal,
OpenVariant::kOnDiskExclusiveWal));
} // namespace
} // namespace sql

@ -147,18 +147,16 @@ class SQLDatabaseTest : public testing::Test,
}
DatabaseOptions GetDBOptions() {
DatabaseOptions options;
options.wal_mode = IsWALEnabled();
return DatabaseOptions()
.set_wal_mode(IsWALEnabled())
// TODO(crbug.com/40146017): Remove after switching to exclusive mode on by
// default.
options.exclusive_locking = false;
#if BUILDFLAG(IS_FUCHSIA) // Exclusive mode needs to be enabled to enter WAL
// mode on Fuchsia
if (IsWALEnabled()) {
options.exclusive_locking = true;
}
.set_exclusive_locking(IsWALEnabled())
#else
.set_exclusive_locking(false)
#endif // BUILDFLAG(IS_FUCHSIA)
return options;
;
}
bool IsWALEnabled() { return GetParam(); }
@ -706,8 +704,7 @@ TEST_P(SQLDatabaseTest, UseVfs) {
sqlite3_vfs_register(&vfs, /*makeDflt=*/false);
absl::Cleanup vfs_unregisterer = [&vfs]() { sqlite3_vfs_unregister(&vfs); };
DatabaseOptions options = GetDBOptions();
options.vfs_name_discouraged = kVFSName;
DatabaseOptions options = GetDBOptions().set_vfs_name_discouraged(kVFSName);
Database other_db(options, test::kTestTag);
// Since the vfs's Open function is not implemented `Open()` will fail.
@ -1050,7 +1047,8 @@ void TestPageSize(const base::FilePath& db_prefix,
const base::FilePath db_path = db_prefix.InsertBeforeExtensionASCII(
base::NumberToString(initial_page_size));
Database::Delete(db_path);
Database db({.page_size = initial_page_size}, test::kTestTag);
Database db(DatabaseOptions().set_page_size(initial_page_size),
test::kTestTag);
ASSERT_TRUE(db.Open(db_path));
ASSERT_TRUE(db.Execute(kCreateSql));
ASSERT_TRUE(db.Execute(kInsertSql1));
@ -1060,7 +1058,8 @@ void TestPageSize(const base::FilePath& db_prefix,
db.Close();
// Re-open the database while setting a new |options.page_size| in the object.
Database razed_db({.page_size = final_page_size}, test::kTestTag);
Database razed_db(DatabaseOptions().set_page_size(final_page_size),
test::kTestTag);
ASSERT_TRUE(razed_db.Open(db_path));
// Raze will use the page size set in the connection object, which may not
// match the file's page size.
@ -1938,9 +1937,9 @@ TEST_P(SQLDatabaseTest, MmapInitiallyEnabledAltStatus) {
db_->Close();
Database::Delete(db_path_);
DatabaseOptions options = GetDBOptions();
options.mmap_alt_status_discouraged = true;
options.enable_views_discouraged = true;
DatabaseOptions options = GetDBOptions()
.set_mmap_alt_status_discouraged(true)
.set_enable_views_discouraged(true);
db_ = std::make_unique<Database>(options, test::kTestTag);
ASSERT_TRUE(db_->Open(db_path_));
@ -2023,9 +2022,9 @@ TEST_P(SQLDatabaseTest, ComputeMmapSizeForOpenAltStatus) {
ASSERT_FALSE(db_->DoesViewExist("MmapStatus"));
// Using alt status, everything should be mapped, with state in the view.
DatabaseOptions options = GetDBOptions();
options.mmap_alt_status_discouraged = true;
options.enable_views_discouraged = true;
DatabaseOptions options = GetDBOptions()
.set_mmap_alt_status_discouraged(true)
.set_enable_views_discouraged(true);
db_ = std::make_unique<Database>(options, test::kTestTag);
ASSERT_TRUE(db_->Open(db_path_));
@ -2189,14 +2188,14 @@ TEST_P(SQLDatabaseTest, ReOpenWithDifferentJournalMode) {
}
// Re-open the database with a different mode (Rollback vs WAL).
DatabaseOptions options = GetDBOptions();
options.wal_mode = !is_wal;
DatabaseOptions options =
GetDBOptions()
.set_wal_mode(!is_wal)
#if BUILDFLAG(IS_FUCHSIA)
// Exclusive mode needs to be enabled to enter WAL mode on Fuchsia.
if (options.wal_mode) {
options.exclusive_locking = true;
}
// Exclusive mode needs to be enabled to enter WAL mode on Fuchsia.
.set_exclusive_locking(!is_wal)
#endif // BUILDFLAG(IS_FUCHSIA)
;
db_ = std::make_unique<Database>(options, test::kTestTag);
ASSERT_TRUE(db_->Open(db_path_));
@ -2210,8 +2209,8 @@ TEST_P(SQLDatabaseTest, ReOpenWithDifferentJournalMode) {
}
// Ensure appropriate journal file exists.
EXPECT_TRUE(IsOpenedInCorrectJournalMode(db_.get(), options.wal_mode));
EXPECT_EQ(base::PathExists(wal_path), options.wal_mode);
EXPECT_TRUE(IsOpenedInCorrectJournalMode(db_.get(), options.wal_mode_));
EXPECT_EQ(base::PathExists(wal_path), options.wal_mode_);
}
#if BUILDFLAG(IS_WIN)
@ -2230,11 +2229,10 @@ class SQLDatabaseTestExclusiveFileLockMode
}
DatabaseOptions GetDBOptions() {
DatabaseOptions options;
options.wal_mode = IsWALEnabled();
options.exclusive_locking = true;
options.exclusive_database_file_lock = IsExclusivelockEnabled();
return options;
return DatabaseOptions()
.set_wal_mode(IsWALEnabled())
.set_exclusive_locking(true)
.set_exclusive_database_file_lock(IsExclusivelockEnabled());
}
bool IsWALEnabled() { return std::get<0>(GetParam()); }
@ -2276,7 +2274,8 @@ TEST(SQLInvalidDatabaseFlagsDeathTest, ExclusiveDatabaseLock) {
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
auto db_path = temp_dir.GetPath().AppendASCII("database_test_locked.sqlite");
Database db({.exclusive_database_file_lock = true}, test::kTestTag);
Database db(DatabaseOptions().set_exclusive_database_file_lock(true),
test::kTestTag);
EXPECT_CHECK_DEATH_WITH(
{ std::ignore = db.Open(db_path); },
@ -2298,10 +2297,9 @@ class SQLDatabaseTestExclusiveMode : public testing::Test,
}
DatabaseOptions GetDBOptions() {
DatabaseOptions options;
options.wal_mode = IsWALEnabled();
options.exclusive_locking = true;
return options;
return DatabaseOptions()
.set_wal_mode(IsWALEnabled())
.set_exclusive_locking(true);
}
bool IsWALEnabled() { return GetParam(); }

@ -278,9 +278,9 @@ DEFINE_PROTO_FUZZER(const sql_fuzzers::RecoveryFuzzerTestCase& fuzzer_input) {
std::cout << test_case;
}
sql::DatabaseOptions database_options;
database_options.wal_mode = test_case.wal_mode();
sql::Database database(database_options, sql::test::kTestTag);
sql::Database database(
sql::DatabaseOptions().set_wal_mode(test_case.wal_mode()),
sql::test::kTestTag);
CHECK(database.Open(env.db_path()));
// Bootstrap the database with SQL queries derived from `fuzzer_input`.

@ -74,10 +74,7 @@ Recovery::Recovery(Database* database, Strategy strategy)
: strategy_(strategy),
db_(database),
recover_db_(
sql::DatabaseOptions{
.page_size = database ? database->page_size() : 0,
.cache_size = 0,
},
DatabaseOptions().set_page_size(database ? database->page_size() : 0),
"Recovery") {
CHECK(db_);
CHECK(db_->is_open());

@ -47,8 +47,8 @@ namespace sql {
namespace {
using sql::test::ExecuteWithResult;
using sql::test::ExecuteWithResults;
using test::ExecuteWithResult;
using test::ExecuteWithResults;
constexpr char kRecoveryResultHistogramName[] = "Sql.Recovery.Result";
constexpr char kRecoveryResultCodeHistogramName[] = "Sql.Recovery.ResultCode";
@ -68,7 +68,7 @@ class SqlRecoveryTest : public testing::Test,
public testing::WithParamInterface<bool> {
public:
SqlRecoveryTest()
: db_(DatabaseOptions{.wal_mode = ShouldEnableWal()}, test::kTestTag) {
: db_(DatabaseOptions().set_wal_mode(ShouldEnableWal()), test::kTestTag) {
scoped_feature_list_.InitWithFeatureStates(
{{features::kEnableWALModeByDefault, ShouldEnableWal()}});
}
@ -160,7 +160,7 @@ TEST_P(SqlRecoveryTest, RecoverCorruptIndex) {
ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(8, 8)"));
db_.Close();
ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index"));
ASSERT_TRUE(test::CorruptIndexRootPage(db_path_, "rows_index"));
ASSERT_TRUE(Reopen());
int error = SQLITE_OK;
@ -238,7 +238,7 @@ TEST_P(SqlRecoveryTest, RecoverCorruptTable) {
<< "Page overflow relies on specific size";
large_buffer.resize(kDbPageSize * 2);
std::ranges::fill(large_buffer, '8');
sql::Statement insert(db_.GetUniqueStatement(
Statement insert(db_.GetUniqueStatement(
"INSERT INTO rows(indexed,unindexed,filler) VALUES(8,8,?)"));
insert.BindBlob(0, large_buffer);
ASSERT_TRUE(insert.Run());
@ -259,7 +259,7 @@ TEST_P(SqlRecoveryTest, RecoverCorruptTable) {
}
{
sql::test::ScopedErrorExpecter expecter;
test::ScopedErrorExpecter expecter;
expecter.ExpectError(SQLITE_CORRUPT);
ASSERT_FALSE(Reopen());
EXPECT_TRUE(expecter.SawExpectedErrors());
@ -630,7 +630,8 @@ TEST_P(SqlRecoveryTest, RecoverIfPossibleWithPerDatabaseUma) {
TEST_P(SqlRecoveryTest, RecoverDatabaseWithView) {
db_.Close();
sql::Database db({.enable_views_discouraged = true}, test::kTestTag);
Database db(DatabaseOptions().set_enable_views_discouraged(true),
test::kTestTag);
ASSERT_TRUE(db.Open(db_path_));
ASSERT_TRUE(db.Execute(
@ -685,7 +686,7 @@ TEST_P(SqlRecoveryTest, RecoverDatabaseDelete) {
ASSERT_TRUE(OverwriteDatabaseHeader());
{
sql::test::ScopedErrorExpecter expecter;
test::ScopedErrorExpecter expecter;
expecter.ExpectError(SQLITE_NOTADB);
// Reopen() here because it will see SQLITE_NOTADB.
@ -727,13 +728,13 @@ TEST_P(SqlRecoveryTest, BeginRecoverDatabase) {
ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(8, 8)"));
db_.Close();
ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index"));
ASSERT_TRUE(test::CorruptIndexRootPage(db_path_, "rows_index"));
ASSERT_TRUE(Reopen());
static const char kIndexedCountSql[] =
"SELECT SUM(indexed) FROM rows INDEXED BY rows_index";
{
sql::test::ScopedErrorExpecter expecter;
test::ScopedErrorExpecter expecter;
expecter.ExpectError(SQLITE_CORRUPT);
EXPECT_EQ("", ExecuteWithResult(&db_, kIndexedCountSql))
<< "Index should still be corrupted after recovery rollback";
@ -760,7 +761,7 @@ TEST_P(SqlRecoveryTest, AttachFailure) {
ASSERT_TRUE(OverwriteDatabaseHeader());
{
sql::test::ScopedErrorExpecter expecter;
test::ScopedErrorExpecter expecter;
expecter.ExpectError(SQLITE_NOTADB);
// Reopen() here because it will see SQLITE_NOTADB.
@ -797,7 +798,8 @@ void TestPageSize(const base::FilePath& db_prefix,
const base::FilePath db_path = db_prefix.InsertBeforeExtensionASCII(
base::NumberToString(initial_page_size));
Database::Delete(db_path);
Database db({.page_size = initial_page_size}, test::kTestTag);
Database db{DatabaseOptions().set_page_size(initial_page_size),
test::kTestTag};
ASSERT_TRUE(db.Open(db_path));
ASSERT_TRUE(db.Execute(kCreateSql));
ASSERT_TRUE(db.Execute(kInsertSql1));
@ -807,7 +809,8 @@ void TestPageSize(const base::FilePath& db_prefix,
db.Close();
// Re-open the database while setting a new |options.page_size| in the object.
Database recover_db({.page_size = final_page_size}, test::kTestTag);
Database recover_db(DatabaseOptions().set_page_size(final_page_size),
test::kTestTag);
ASSERT_TRUE(recover_db.Open(db_path));
// Recovery will use the page size set in the database object, which may not
// match the file's page size.
@ -819,8 +822,7 @@ void TestPageSize(const base::FilePath& db_prefix,
recover_db.Close();
// Make sure the page size is read from the file.
Database recovered_db({.page_size = DatabaseOptions::kDefaultPageSize},
test::kTestTag);
Database recovered_db(test::kTestTag);
ASSERT_TRUE(recovered_db.Open(db_path));
ASSERT_EQ(expected_final_page_size,
ExecuteWithResult(&recovered_db, "PRAGMA page_size"));
@ -901,7 +903,7 @@ TEST_P(SqlRecoveryTest, PRE_RecoverFormerlyWalDbAfterCrash) {
temp_dir_.GetPath().AppendASCII("recovery_wal_test.sqlite");
// Open the DB in WAL mode to set journal_mode="wal".
Database wal_db{{.wal_mode = true}, test::kTestTag};
Database wal_db{DatabaseOptions().set_wal_mode(true), test::kTestTag};
ASSERT_TRUE(wal_db.Open(wal_db_path));
EXPECT_TRUE(wal_db.UseWALMode());
@ -916,7 +918,7 @@ TEST_P(SqlRecoveryTest, RecoverFormerlyWalDbAfterCrash) {
base::FilePath wal_db_path =
temp_dir_.GetPath().AppendASCII("recovery_wal_test.sqlite");
Database non_wal_db{{.wal_mode = false}, test::kTestTag};
Database non_wal_db{DatabaseOptions().set_wal_mode(false), test::kTestTag};
ASSERT_TRUE(non_wal_db.Open(wal_db_path));
auto run_recovery = base::BindLambdaForTesting([&]() {

@ -157,7 +157,7 @@ bool CorruptSizeInHeader(const base::FilePath& db_path) {
// This function doesn't reliably work if connections to the DB are still
// open. The database is opened in excusive mode. Open will fail if any
// other connection exists on the database.
sql::Database db({.wal_mode = true}, kTestTag);
Database db(DatabaseOptions().set_wal_mode(true), kTestTag);
if (!db.Open(db_path))
return false;
int wal_log_size = 0;
@ -196,7 +196,7 @@ bool CorruptSizeInHeader(const base::FilePath& db_path) {
bool CorruptSizeInHeaderWithLock(const base::FilePath& db_path) {
base::ScopedAllowBlockingForTesting allow_blocking;
sql::Database db(sql::test::kTestTag);
Database db(kTestTag);
if (!db.Open(db_path))
return false;
@ -214,7 +214,7 @@ bool CorruptIndexRootPage(const base::FilePath& db_path,
if (!page_size.has_value())
return false;
sql::Database db(sql::test::kTestTag);
Database db(kTestTag);
if (!db.Open(db_path))
return false;
@ -233,27 +233,27 @@ bool CorruptIndexRootPage(const base::FilePath& db_path,
return file.WriteAndCheck(page_offset, page_buffer);
}
size_t CountSQLTables(sql::Database* db) {
size_t CountSQLTables(Database* db) {
return CountSQLItemsOfType(db, "table");
}
size_t CountSQLIndices(sql::Database* db) {
size_t CountSQLIndices(Database* db) {
return CountSQLItemsOfType(db, "index");
}
size_t CountTableColumns(sql::Database* db, const char* table) {
size_t CountTableColumns(Database* db, const char* table) {
// TODO(shess): sql::Database::QuoteForSQL() would make sense.
std::string quoted_table;
{
static const char kQuoteSQL[] = "SELECT quote(?)";
sql::Statement s(db->GetUniqueStatement(kQuoteSQL));
Statement s(db->GetUniqueStatement(kQuoteSQL));
s.BindCString(0, table);
EXPECT_TRUE(s.Step());
quoted_table = s.ColumnString(0);
}
std::string sql = "PRAGMA table_info(" + quoted_table + ")";
sql::Statement s(db->GetUniqueStatement(sql));
Statement s(db->GetUniqueStatement(sql));
size_t rows = 0;
while (s.Step()) {
++rows;
@ -262,13 +262,13 @@ size_t CountTableColumns(sql::Database* db, const char* table) {
return rows;
}
bool CountTableRows(sql::Database* db, const char* table, size_t* count) {
bool CountTableRows(Database* db, const char* table, size_t* count) {
// TODO(shess): Table should probably be quoted with [] or "". See
// http://www.sqlite.org/lang_keywords.html . Meanwhile, odd names
// will throw an error.
std::string sql = "SELECT COUNT(*) FROM ";
sql += table;
sql::Statement s(db->GetUniqueStatement(sql));
Statement s(db->GetUniqueStatement(sql));
if (!s.Step())
return false;
@ -285,7 +285,7 @@ bool CreateDatabaseFromSQL(const base::FilePath& db_path,
if (!base::ReadFileToString(sql_path, &sql))
return false;
sql::Database db(sql::test::kTestTag);
Database db(kTestTag);
if (!db.Open(db_path))
return false;
@ -298,23 +298,23 @@ bool CreateDatabaseFromSQL(const base::FilePath& db_path,
return db.Execute(sql);
}
std::string IntegrityCheck(sql::Database& db) {
std::string IntegrityCheck(Database& db) {
std::vector<std::string> messages;
EXPECT_TRUE(db.FullIntegrityCheck(&messages));
return base::JoinString(messages, "\n");
}
std::string ExecuteWithResult(sql::Database* db, const base::cstring_view sql) {
sql::Statement s(db->GetUniqueStatement(sql));
std::string ExecuteWithResult(Database* db, const base::cstring_view sql) {
Statement s(db->GetUniqueStatement(sql));
return s.Step() ? s.ColumnString(0) : std::string();
}
std::string ExecuteWithResults(sql::Database* db,
std::string ExecuteWithResults(Database* db,
const base::cstring_view sql,
const base::cstring_view column_sep,
const base::cstring_view row_sep) {
sql::Statement s(db->GetUniqueStatement(sql));
Statement s(db->GetUniqueStatement(sql));
std::string ret;
while (s.Step()) {
if (!ret.empty())
@ -328,14 +328,14 @@ std::string ExecuteWithResults(sql::Database* db,
return ret;
}
int GetPageCount(sql::Database* db) {
sql::Statement statement(db->GetUniqueStatement("PRAGMA page_count"));
int GetPageCount(Database* db) {
Statement statement(db->GetUniqueStatement("PRAGMA page_count"));
CHECK(statement.Step());
return statement.ColumnInt(0);
}
// static
ColumnInfo ColumnInfo::Create(sql::Database* db,
ColumnInfo ColumnInfo::Create(Database* db,
const std::string& db_name,
const std::string& table_name,
const std::string& column_name) {

@ -1052,15 +1052,13 @@ QuotaError QuotaDatabase::EnsureOpened() {
return QuotaError::kDatabaseError;
}
sql::DatabaseOptions options{
// The quota database is a critical storage component. If it's corrupted,
// all client-side storage APIs fail, because they don't know where their
// data is stored.
.flush_to_media = true,
};
db_ = std::make_unique<sql::Database>(std::move(options),
sql::Database::Tag("Quota"));
db_ = std::make_unique<sql::Database>(
sql::DatabaseOptions()
// The quota database is a critical storage component. If it's
// corrupted, all client-side storage APIs fail, because they don't
// know where their data is stored.
.set_flush_to_media(true),
sql::Database::Tag("Quota"));
meta_table_ = std::make_unique<sql::MetaTable>();
db_->set_error_callback(base::BindRepeating(&QuotaDatabase::OnSqliteError,