database_options_unittest.cc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/callback_helpers.h"
  5. #include "base/files/file_path.h"
  6. #include "base/files/scoped_temp_dir.h"
  7. #include "sql/database.h"
  8. #include "sql/sqlite_result_code.h"
  9. #include "sql/statement.h"
  10. #include "sql/test/scoped_error_expecter.h"
  11. #include "sql/test/test_helpers.h"
  12. #include "sql/transaction.h"
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. #include "third_party/abseil-cpp/absl/types/optional.h"
  15. #include "third_party/sqlite/sqlite3.h"
  16. namespace sql {
  17. namespace {
  18. enum class OpenVariant {
  19. kInMemory = 1,
  20. kOnDiskExclusiveJournal = 2,
  21. kOnDiskNonExclusiveJournal = 3,
  22. kOnDiskExclusiveWal = 4,
  23. };
  24. // We use the parameter to run all tests with WAL mode on and off.
  25. class DatabaseOptionsTest : public testing::TestWithParam<OpenVariant> {
  26. public:
  27. DatabaseOptionsTest() = default;
  28. ~DatabaseOptionsTest() override = default;
  29. void SetUp() override {
  30. ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  31. db_path_ = temp_dir_.GetPath().AppendASCII("database_test.sqlite");
  32. }
  33. OpenVariant open_variant() const { return GetParam(); }
  34. // The options below interact with all other options. These tests ensure that
  35. // all combinations work.
  36. bool exclusive_locking() const {
  37. return GetParam() != OpenVariant::kOnDiskNonExclusiveJournal;
  38. }
  39. bool wal_mode() const {
  40. return GetParam() == OpenVariant::kOnDiskExclusiveWal;
  41. }
  42. void OpenDatabase(Database& db) {
  43. switch (open_variant()) {
  44. case OpenVariant::kOnDiskExclusiveJournal:
  45. ASSERT_TRUE(db.Open(db_path_));
  46. break;
  47. case OpenVariant::kOnDiskNonExclusiveJournal:
  48. ASSERT_TRUE(db.Open(db_path_));
  49. break;
  50. case OpenVariant::kOnDiskExclusiveWal:
  51. ASSERT_TRUE(db.Open(db_path_));
  52. break;
  53. case OpenVariant::kInMemory:
  54. ASSERT_TRUE(db.OpenInMemory());
  55. break;
  56. }
  57. }
  58. // Runs a rolled back transaction, followed by a committed transaction.
  59. void RunTransactions(Database& db) {
  60. {
  61. Transaction rolled_back(&db);
  62. ASSERT_TRUE(rolled_back.Begin());
  63. ASSERT_TRUE(db.Execute("CREATE TABLE rows(id PRIMARY KEY NOT NULL)"));
  64. rolled_back.Rollback();
  65. }
  66. {
  67. Transaction committed(&db);
  68. ASSERT_TRUE(committed.Begin());
  69. ASSERT_TRUE(db.Execute("CREATE TABLE rows(id PRIMARY KEY NOT NULL)"));
  70. ASSERT_TRUE(committed.Commit());
  71. }
  72. }
  73. protected:
  74. base::ScopedTempDir temp_dir_;
  75. base::FilePath db_path_;
  76. };
  77. TEST_P(DatabaseOptionsTest, FlushToDisk_FalseByDefault) {
  78. DatabaseOptions options = {
  79. .exclusive_locking = exclusive_locking(),
  80. .wal_mode = wal_mode(),
  81. };
  82. EXPECT_FALSE(options.flush_to_media) << "Invalid test assumption";
  83. Database db(options);
  84. OpenDatabase(db);
  85. EXPECT_EQ("0", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"));
  86. }
  87. TEST_P(DatabaseOptionsTest, FlushToDisk_True) {
  88. Database db(DatabaseOptions{
  89. .exclusive_locking = exclusive_locking(),
  90. .wal_mode = wal_mode(),
  91. .flush_to_media = true,
  92. });
  93. OpenDatabase(db);
  94. EXPECT_EQ("1", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"));
  95. }
  96. TEST_P(DatabaseOptionsTest, FlushToDisk_False_DoesNotCrash) {
  97. Database db(DatabaseOptions{
  98. .exclusive_locking = exclusive_locking(),
  99. .wal_mode = wal_mode(),
  100. .flush_to_media = false,
  101. });
  102. OpenDatabase(db);
  103. EXPECT_EQ("0", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"))
  104. << "Invalid test setup";
  105. RunTransactions(db);
  106. }
  107. TEST_P(DatabaseOptionsTest, FlushToDisk_True_DoesNotCrash) {
  108. Database db(DatabaseOptions{
  109. .exclusive_locking = exclusive_locking(),
  110. .wal_mode = wal_mode(),
  111. .flush_to_media = true,
  112. });
  113. OpenDatabase(db);
  114. EXPECT_EQ("1", sql::test::ExecuteWithResult(&db, "PRAGMA fullfsync"))
  115. << "Invalid test setup";
  116. RunTransactions(db);
  117. }
  118. TEST_P(DatabaseOptionsTest, PageSize_Default) {
  119. static_assert(DatabaseOptions::kDefaultPageSize == 4096,
  120. "The page size numbers in this test file need to change");
  121. Database db(DatabaseOptions{
  122. .exclusive_locking = exclusive_locking(),
  123. .wal_mode = wal_mode(),
  124. .page_size = 4096,
  125. });
  126. OpenDatabase(db);
  127. EXPECT_EQ("4096", sql::test::ExecuteWithResult(&db, "PRAGMA page_size"));
  128. RunTransactions(db);
  129. if (open_variant() != OpenVariant::kInMemory) {
  130. db.Close();
  131. EXPECT_EQ(4096, sql::test::ReadDatabasePageSize(db_path_).value_or(-1));
  132. }
  133. }
  134. TEST_P(DatabaseOptionsTest, PageSize_Large) {
  135. static_assert(DatabaseOptions::kDefaultPageSize < 16384,
  136. "The page size numbers in this test file need to change");
  137. Database db(DatabaseOptions{
  138. .exclusive_locking = exclusive_locking(),
  139. .wal_mode = wal_mode(),
  140. .page_size = 16384,
  141. });
  142. OpenDatabase(db);
  143. EXPECT_EQ("16384", sql::test::ExecuteWithResult(&db, "PRAGMA page_size"));
  144. RunTransactions(db);
  145. if (open_variant() != OpenVariant::kInMemory) {
  146. db.Close();
  147. EXPECT_EQ(16384, sql::test::ReadDatabasePageSize(db_path_).value_or(-1));
  148. }
  149. }
  150. TEST_P(DatabaseOptionsTest, PageSize_Small) {
  151. static_assert(DatabaseOptions::kDefaultPageSize > 1024,
  152. "The page size numbers in this test file need to change");
  153. Database db(DatabaseOptions{
  154. .exclusive_locking = exclusive_locking(),
  155. .wal_mode = wal_mode(),
  156. .page_size = 1024,
  157. });
  158. OpenDatabase(db);
  159. EXPECT_EQ("1024", sql::test::ExecuteWithResult(&db, "PRAGMA page_size"));
  160. RunTransactions(db);
  161. if (open_variant() != OpenVariant::kInMemory) {
  162. db.Close();
  163. EXPECT_EQ(1024, sql::test::ReadDatabasePageSize(db_path_).value_or(-1));
  164. }
  165. }
  166. TEST_P(DatabaseOptionsTest, CacheSize_Legacy) {
  167. Database db(DatabaseOptions{
  168. .exclusive_locking = exclusive_locking(),
  169. .wal_mode = wal_mode(),
  170. .cache_size = 0,
  171. });
  172. OpenDatabase(db);
  173. EXPECT_EQ("-2000", sql::test::ExecuteWithResult(&db, "PRAGMA cache_size"));
  174. }
  175. TEST_P(DatabaseOptionsTest, CacheSize_Small) {
  176. Database db(DatabaseOptions{
  177. .exclusive_locking = exclusive_locking(),
  178. .wal_mode = wal_mode(),
  179. .cache_size = 16,
  180. });
  181. OpenDatabase(db);
  182. EXPECT_EQ("16", sql::test::ExecuteWithResult(&db, "PRAGMA cache_size"));
  183. }
  184. TEST_P(DatabaseOptionsTest, CacheSize_Large) {
  185. Database db(DatabaseOptions{
  186. .exclusive_locking = exclusive_locking(),
  187. .wal_mode = wal_mode(),
  188. .cache_size = 1000,
  189. });
  190. OpenDatabase(db);
  191. EXPECT_EQ("1000", sql::test::ExecuteWithResult(&db, "PRAGMA cache_size"));
  192. }
  193. TEST_P(DatabaseOptionsTest, EnableViewsDiscouraged_FalseByDefault) {
  194. DatabaseOptions options = {
  195. .exclusive_locking = exclusive_locking(),
  196. .wal_mode = wal_mode(),
  197. };
  198. EXPECT_FALSE(options.enable_views_discouraged) << "Invalid test assumption";
  199. Database db(options);
  200. OpenDatabase(db);
  201. // sqlite3_db_config() currently only disables querying views. Schema
  202. // operations on views are still allowed.
  203. ASSERT_TRUE(db.Execute("CREATE VIEW view(id) AS SELECT 1"));
  204. {
  205. sql::test::ScopedErrorExpecter expecter;
  206. expecter.ExpectError(SQLITE_ERROR);
  207. Statement select_from_view(db.GetUniqueStatement("SELECT id FROM view"));
  208. EXPECT_FALSE(select_from_view.is_valid());
  209. EXPECT_TRUE(expecter.SawExpectedErrors());
  210. }
  211. // sqlite3_db_config() currently only disables querying views. Schema
  212. // operations on views are still allowed.
  213. EXPECT_TRUE(db.Execute("DROP VIEW IF EXISTS view"));
  214. }
  215. TEST_P(DatabaseOptionsTest, EnableViewsDiscouraged_True) {
  216. Database db(DatabaseOptions{
  217. .exclusive_locking = exclusive_locking(),
  218. .wal_mode = wal_mode(),
  219. .enable_views_discouraged = true,
  220. });
  221. OpenDatabase(db);
  222. ASSERT_TRUE(db.Execute("CREATE VIEW view(id) AS SELECT 1"));
  223. Statement select_from_view(db.GetUniqueStatement("SELECT id FROM view"));
  224. ASSERT_TRUE(select_from_view.is_valid());
  225. EXPECT_TRUE(select_from_view.Step());
  226. EXPECT_EQ(1, select_from_view.ColumnInt64(0));
  227. EXPECT_TRUE(db.Execute("DROP VIEW IF EXISTS view"));
  228. }
  229. TEST_P(DatabaseOptionsTest, EnableVirtualTablesDiscouraged_FalseByDefault) {
  230. DatabaseOptions options = {
  231. .exclusive_locking = exclusive_locking(),
  232. .wal_mode = wal_mode(),
  233. };
  234. EXPECT_FALSE(options.enable_virtual_tables_discouraged)
  235. << "Invalid test assumption";
  236. Database db(options);
  237. OpenDatabase(db);
  238. // sqlite3_prepare_v3() currently only disables accessing virtual tables.
  239. // Schema operations on virtual tables are still allowed.
  240. ASSERT_TRUE(db.Execute(
  241. "CREATE VIRTUAL TABLE fts_table USING fts3(data_table, content TEXT)"));
  242. {
  243. sql::test::ScopedErrorExpecter expecter;
  244. expecter.ExpectError(SQLITE_ERROR);
  245. Statement select_from_vtable(db.GetUniqueStatement(
  246. "SELECT content FROM fts_table WHERE content MATCH 'pattern'"));
  247. EXPECT_FALSE(select_from_vtable.is_valid());
  248. EXPECT_TRUE(expecter.SawExpectedErrors());
  249. }
  250. // sqlite3_prepare_v3() currently only disables accessing virtual tables.
  251. // Schema operations on virtual tables are still allowed.
  252. EXPECT_TRUE(db.Execute("DROP TABLE IF EXISTS fts_table"));
  253. }
  254. TEST_P(DatabaseOptionsTest, EnableVirtualTablesDiscouraged_True) {
  255. Database db(DatabaseOptions{
  256. .exclusive_locking = exclusive_locking(),
  257. .wal_mode = wal_mode(),
  258. .enable_virtual_tables_discouraged = true,
  259. });
  260. OpenDatabase(db);
  261. ASSERT_TRUE(db.Execute(
  262. "CREATE VIRTUAL TABLE fts_table USING fts3(data_table, content TEXT)"));
  263. Statement select_from_vtable(db.GetUniqueStatement(
  264. "SELECT content FROM fts_table WHERE content MATCH 'pattern'"));
  265. ASSERT_TRUE(select_from_vtable.is_valid());
  266. EXPECT_FALSE(select_from_vtable.Step());
  267. EXPECT_TRUE(db.Execute("DROP TABLE IF EXISTS fts_table"));
  268. }
  269. INSTANTIATE_TEST_SUITE_P(
  270. ,
  271. DatabaseOptionsTest,
  272. testing::Values(OpenVariant::kInMemory,
  273. OpenVariant::kOnDiskExclusiveJournal,
  274. OpenVariant::kOnDiskNonExclusiveJournal,
  275. OpenVariant::kOnDiskExclusiveWal));
  276. } // namespace
  277. } // namespace sql