1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/files/file_path.h"
6 #include "base/files/scoped_temp_dir.h"
7 #include "base/functional/callback_helpers.h"
8 #include "sql/database.h"
9 #include "sql/sqlite_result_code.h"
10 #include "sql/statement.h"
11 #include "sql/test/scoped_error_expecter.h"
12 #include "sql/test/test_helpers.h"
13 #include "sql/transaction.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/abseil-cpp/absl/types/optional.h"
16 #include "third_party/sqlite/sqlite3.h"
22 enum class OpenVariant {
24 kOnDiskExclusiveJournal = 2,
25 kOnDiskNonExclusiveJournal = 3,
26 kOnDiskExclusiveWal = 4,
29 // We use the parameter to run all tests with WAL mode on and off.
30 class DatabaseOptionsTest : public testing::TestWithParam<OpenVariant> {
32 DatabaseOptionsTest() = default;
33 ~DatabaseOptionsTest() override = default;
35 void SetUp() override {
36 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
37 db_path_ = temp_dir_.GetPath().AppendASCII("database_test.sqlite");
40 OpenVariant open_variant() const { return GetParam(); }
42 // The options below interact with all other options. These tests ensure that
43 // all combinations work.
44 bool exclusive_locking() const {
45 return GetParam() != OpenVariant::kOnDiskNonExclusiveJournal;
47 bool wal_mode() const {
48 return GetParam() == OpenVariant::kOnDiskExclusiveWal;
51 void OpenDatabase(Database& db) {
52 switch (open_variant()) {
53 case OpenVariant::kOnDiskExclusiveJournal:
54 ASSERT_TRUE(db.Open(db_path_));
57 case OpenVariant::kOnDiskNonExclusiveJournal:
58 ASSERT_TRUE(db.Open(db_path_));
61 case OpenVariant::kOnDiskExclusiveWal:
62 ASSERT_TRUE(db.Open(db_path_));
65 case OpenVariant::kInMemory:
66 ASSERT_TRUE(db.OpenInMemory());
71 // Runs a rolled back transaction, followed by a committed transaction.
72 void RunTransactions(Database& db) {
74 Transaction rolled_back(&db);
75 ASSERT_TRUE(rolled_back.Begin());
76 ASSERT_TRUE(db.Execute("CREATE TABLE rows(id PRIMARY KEY NOT NULL)"));
77 rolled_back.Rollback();
80 Transaction committed(&db);
81 ASSERT_TRUE(committed.Begin());
82 ASSERT_TRUE(db.Execute("CREATE TABLE rows(id PRIMARY KEY NOT NULL)"));
83 ASSERT_TRUE(committed.Commit());
88 base::ScopedTempDir temp_dir_;
89 base::FilePath db_path_;
92 TEST_P(DatabaseOptionsTest, FlushToDisk_FalseByDefault) {
93 DatabaseOptions options = {
94 .exclusive_locking = exclusive_locking(),
95 .wal_mode = wal_mode(),
97 EXPECT_FALSE(options.flush_to_media) << "Invalid test assumption";
102 EXPECT_EQ("0", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"));
105 TEST_P(DatabaseOptionsTest, FlushToDisk_True) {
106 Database db(DatabaseOptions{
107 .exclusive_locking = exclusive_locking(),
108 .wal_mode = wal_mode(),
109 .flush_to_media = true,
113 EXPECT_EQ("1", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"));
116 TEST_P(DatabaseOptionsTest, FlushToDisk_False_DoesNotCrash) {
117 Database db(DatabaseOptions{
118 .exclusive_locking = exclusive_locking(),
119 .wal_mode = wal_mode(),
120 .flush_to_media = false,
124 EXPECT_EQ("0", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"))
125 << "Invalid test setup";
129 TEST_P(DatabaseOptionsTest, FlushToDisk_True_DoesNotCrash) {
130 Database db(DatabaseOptions{
131 .exclusive_locking = exclusive_locking(),
132 .wal_mode = wal_mode(),
133 .flush_to_media = true,
137 EXPECT_EQ("1", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"))
138 << "Invalid test setup";
142 TEST_P(DatabaseOptionsTest, PageSize_Default) {
143 static_assert(DatabaseOptions::kDefaultPageSize == 4096,
144 "The page size numbers in this test file need to change");
145 Database db(DatabaseOptions{
146 .exclusive_locking = exclusive_locking(),
147 .wal_mode = wal_mode(),
152 EXPECT_EQ("4096", sql::test::ExecuteWithResult(&db, "PRAGMA page_size"));
155 if (open_variant() != OpenVariant::kInMemory) {
157 EXPECT_EQ(4096, sql::test::ReadDatabasePageSize(db_path_).value_or(-1));
161 TEST_P(DatabaseOptionsTest, PageSize_Large) {
162 static_assert(DatabaseOptions::kDefaultPageSize < 16384,
163 "The page size numbers in this test file need to change");
164 Database db(DatabaseOptions{
165 .exclusive_locking = exclusive_locking(),
166 .wal_mode = wal_mode(),
171 EXPECT_EQ("16384", sql::test::ExecuteWithResult(&db, "PRAGMA page_size"));
174 if (open_variant() != OpenVariant::kInMemory) {
176 EXPECT_EQ(16384, sql::test::ReadDatabasePageSize(db_path_).value_or(-1));
180 TEST_P(DatabaseOptionsTest, PageSize_Small) {
181 static_assert(DatabaseOptions::kDefaultPageSize > 1024,
182 "The page size numbers in this test file need to change");
183 Database db(DatabaseOptions{
184 .exclusive_locking = exclusive_locking(),
185 .wal_mode = wal_mode(),
190 EXPECT_EQ("1024", sql::test::ExecuteWithResult(&db, "PRAGMA page_size"));
193 if (open_variant() != OpenVariant::kInMemory) {
195 EXPECT_EQ(1024, sql::test::ReadDatabasePageSize(db_path_).value_or(-1));
199 TEST_P(DatabaseOptionsTest, CacheSize_Legacy) {
200 Database db(DatabaseOptions{
201 .exclusive_locking = exclusive_locking(),
202 .wal_mode = wal_mode(),
207 EXPECT_EQ("-2000", sql::test::ExecuteWithResult(&db, "PRAGMA cache_size"));
210 TEST_P(DatabaseOptionsTest, CacheSize_Small) {
211 Database db(DatabaseOptions{
212 .exclusive_locking = exclusive_locking(),
213 .wal_mode = wal_mode(),
217 EXPECT_EQ("16", sql::test::ExecuteWithResult(&db, "PRAGMA cache_size"));
220 TEST_P(DatabaseOptionsTest, CacheSize_Large) {
221 Database db(DatabaseOptions{
222 .exclusive_locking = exclusive_locking(),
223 .wal_mode = wal_mode(),
227 EXPECT_EQ("1000", sql::test::ExecuteWithResult(&db, "PRAGMA cache_size"));
230 TEST_P(DatabaseOptionsTest, EnableViewsDiscouraged_FalseByDefault) {
231 DatabaseOptions options = {
232 .exclusive_locking = exclusive_locking(),
233 .wal_mode = wal_mode(),
235 EXPECT_FALSE(options.enable_views_discouraged) << "Invalid test assumption";
237 Database db(options);
240 // sqlite3_db_config() currently only disables querying views. Schema
241 // operations on views are still allowed.
242 ASSERT_TRUE(db.Execute("CREATE VIEW view(id) AS SELECT 1"));
245 sql::test::ScopedErrorExpecter expecter;
246 expecter.ExpectError(SQLITE_ERROR);
247 Statement select_from_view(db.GetUniqueStatement("SELECT id FROM view"));
248 EXPECT_FALSE(select_from_view.is_valid());
249 EXPECT_TRUE(expecter.SawExpectedErrors());
252 // sqlite3_db_config() currently only disables querying views. Schema
253 // operations on views are still allowed.
254 EXPECT_TRUE(db.Execute("DROP VIEW IF EXISTS view"));
257 TEST_P(DatabaseOptionsTest, EnableViewsDiscouraged_True) {
258 Database db(DatabaseOptions{
259 .exclusive_locking = exclusive_locking(),
260 .wal_mode = wal_mode(),
261 .enable_views_discouraged = true,
265 ASSERT_TRUE(db.Execute("CREATE VIEW view(id) AS SELECT 1"));
267 Statement select_from_view(db.GetUniqueStatement("SELECT id FROM view"));
268 ASSERT_TRUE(select_from_view.is_valid());
269 EXPECT_TRUE(select_from_view.Step());
270 EXPECT_EQ(1, select_from_view.ColumnInt64(0));
272 EXPECT_TRUE(db.Execute("DROP VIEW IF EXISTS view"));
275 TEST_P(DatabaseOptionsTest, EnableVirtualTablesDiscouraged_FalseByDefault) {
276 DatabaseOptions options = {
277 .exclusive_locking = exclusive_locking(),
278 .wal_mode = wal_mode(),
280 EXPECT_FALSE(options.enable_virtual_tables_discouraged)
281 << "Invalid test assumption";
283 Database db(options);
286 // sqlite3_prepare_v3() currently only disables accessing virtual tables.
287 // Schema operations on virtual tables are still allowed.
288 ASSERT_TRUE(db.Execute(
289 "CREATE VIRTUAL TABLE fts_table USING fts3(data_table, content TEXT)"));
292 sql::test::ScopedErrorExpecter expecter;
293 expecter.ExpectError(SQLITE_ERROR);
294 Statement select_from_vtable(db.GetUniqueStatement(
295 "SELECT content FROM fts_table WHERE content MATCH 'pattern'"));
296 EXPECT_FALSE(select_from_vtable.is_valid());
297 EXPECT_TRUE(expecter.SawExpectedErrors());
300 // sqlite3_prepare_v3() currently only disables accessing virtual tables.
301 // Schema operations on virtual tables are still allowed.
302 EXPECT_TRUE(db.Execute("DROP TABLE IF EXISTS fts_table"));
305 TEST_P(DatabaseOptionsTest, EnableVirtualTablesDiscouraged_True) {
306 Database db(DatabaseOptions{
307 .exclusive_locking = exclusive_locking(),
308 .wal_mode = wal_mode(),
309 .enable_virtual_tables_discouraged = true,
313 ASSERT_TRUE(db.Execute(
314 "CREATE VIRTUAL TABLE fts_table USING fts3(data_table, content TEXT)"));
316 Statement select_from_vtable(db.GetUniqueStatement(
317 "SELECT content FROM fts_table WHERE content MATCH 'pattern'"));
318 ASSERT_TRUE(select_from_vtable.is_valid());
319 EXPECT_FALSE(select_from_vtable.Step());
321 EXPECT_TRUE(db.Execute("DROP TABLE IF EXISTS fts_table"));
324 INSTANTIATE_TEST_SUITE_P(
327 testing::Values(OpenVariant::kInMemory,
328 OpenVariant::kOnDiskExclusiveJournal,
329 OpenVariant::kOnDiskNonExclusiveJournal,
330 OpenVariant::kOnDiskExclusiveWal));