[sql] Verify control of close-time WAL checkpoint.
SQLite WAL mode by default checkpoints the WAL to the main database at
close time. Chromium closes databases at shutdown, which causes
shutdown slowdowns due to buffered data being flushed. WAL
checkpointing would effectively increase the amount of buffered data
being flushed.
[WAL mode is currently not in use by Chromium.]
BUG=675264
Review-Url: https://codereview.chromium.org/2827673006
Cr-Commit-Position: refs/heads/master@{#465772}
diff --git a/sql/sqlite_features_unittest.cc b/sql/sqlite_features_unittest.cc
index 0f124af..88f7802f 100644
--- a/sql/sqlite_features_unittest.cc
+++ b/sql/sqlite_features_unittest.cc
@@ -32,8 +32,12 @@
// Test that certain features are/are-not enabled in our SQLite.
+namespace sql {
namespace {
+using sql::test::ExecuteWithResult;
+using sql::test::ExecuteWithResults;
+
void CaptureErrorCallback(int* error_pointer, std::string* sql_text,
int error, sql::Statement* stmt) {
*error_pointer = error;
@@ -41,6 +45,8 @@
*sql_text = text ? text : "no statement available";
}
+} // namespace
+
class SQLiteFeaturesTest : public sql::SQLTestBase {
public:
SQLiteFeaturesTest() : error_(SQLITE_OK) {}
@@ -102,10 +108,8 @@
ASSERT_TRUE(db().Execute("INSERT INTO foo (x) VALUES ('test')"));
- sql::Statement s(db().GetUniqueStatement(
- "SELECT x FROM foo WHERE x MATCH 'te*'"));
- ASSERT_TRUE(s.Step());
- EXPECT_EQ("test", s.ColumnString(0));
+ EXPECT_EQ("test",
+ ExecuteWithResult(&db(), "SELECT x FROM foo WHERE x MATCH 'te*'"));
}
#endif
@@ -118,10 +122,9 @@
sqlite3_sleep(1);
base::TimeDelta delta = base::TimeTicks::Now() - before;
- // It is not impossible for this to be over 1000 if things are compiled the
- // right way. But it is very unlikely, most platforms seem to be around
- // <TBD>.
- LOG(ERROR) << "Milliseconds: " << delta.InMilliseconds();
+ // It is not impossible for this to be over 1000 if things are compiled
+ // correctly, but that is very unlikely. Most platforms seem to be exactly
+ // 1ms, with the rest at 2ms, and the worst observed cases was ASAN at 7ms.
EXPECT_LT(delta.InMilliseconds(), 1000);
}
#endif
@@ -135,28 +138,29 @@
"CREATE TABLE children ("
" id INTEGER PRIMARY KEY,"
" pid INTEGER NOT NULL REFERENCES parents(id) ON DELETE CASCADE)"));
+ const char kSelectParents[] = "SELECT * FROM parents ORDER BY id";
+ const char kSelectChildren[] = "SELECT * FROM children ORDER BY id";
// Inserting without a matching parent should fail with constraint violation.
// Mask off any extended error codes for USE_SYSTEM_SQLITE.
- int insertErr = db().ExecuteAndReturnErrorCode(
- "INSERT INTO children VALUES (10, 1)");
- EXPECT_EQ(SQLITE_CONSTRAINT, (insertErr&0xff));
-
- size_t rows;
- EXPECT_TRUE(sql::test::CountTableRows(&db(), "children", &rows));
- EXPECT_EQ(0u, rows);
+ EXPECT_EQ("", ExecuteWithResult(&db(), kSelectParents));
+ const int insert_error =
+ db().ExecuteAndReturnErrorCode("INSERT INTO children VALUES (10, 1)");
+ EXPECT_EQ(SQLITE_CONSTRAINT, (insert_error & 0xff));
+ EXPECT_EQ("", ExecuteWithResult(&db(), kSelectChildren));
// Inserting with a matching parent should work.
ASSERT_TRUE(db().Execute("INSERT INTO parents VALUES (1)"));
+ EXPECT_EQ("1", ExecuteWithResults(&db(), kSelectParents, "|", "\n"));
EXPECT_TRUE(db().Execute("INSERT INTO children VALUES (11, 1)"));
EXPECT_TRUE(db().Execute("INSERT INTO children VALUES (12, 1)"));
- EXPECT_TRUE(sql::test::CountTableRows(&db(), "children", &rows));
- EXPECT_EQ(2u, rows);
+ EXPECT_EQ("11|1\n12|1",
+ ExecuteWithResults(&db(), kSelectChildren, "|", "\n"));
- // Deleting the parent should cascade, i.e., delete the children as well.
+ // Deleting the parent should cascade, deleting the children as well.
ASSERT_TRUE(db().Execute("DELETE FROM parents"));
- EXPECT_TRUE(sql::test::CountTableRows(&db(), "children", &rows));
- EXPECT_EQ(0u, rows);
+ EXPECT_EQ("", ExecuteWithResult(&db(), kSelectParents));
+ EXPECT_EQ("", ExecuteWithResult(&db(), kSelectChildren));
}
#if defined(MOJO_APPTEST_IMPL) || defined(OS_IOS)
@@ -453,4 +457,36 @@
}
#endif // !defined(USE_SYSTEM_SQLITE)
-} // namespace
+#if !defined(USE_SYSTEM_SQLITE)
+// SQLite WAL mode defaults to checkpointing the WAL on close. This would push
+// additional work into Chromium shutdown. Verify that SQLite supports a config
+// option to not checkpoint on close.
+TEST_F(SQLiteFeaturesTest, WALNoClose) {
+ base::FilePath wal_path(db_path().value() + FILE_PATH_LITERAL("-wal"));
+
+ // Turn on WAL mode, then verify that the mode changed (WAL is supported).
+ ASSERT_TRUE(db().Execute("PRAGMA journal_mode = WAL"));
+ ASSERT_EQ("wal", ExecuteWithResult(&db(), "PRAGMA journal_mode"));
+
+ // The WAL file is created lazily on first change.
+ ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
+
+ // By default, the WAL is checkpointed then deleted on close.
+ ASSERT_TRUE(GetPathExists(wal_path));
+ db().Close();
+ ASSERT_FALSE(GetPathExists(wal_path));
+
+ // Reopen and configure the database to not checkpoint WAL on close.
+ ASSERT_TRUE(Reopen());
+ ASSERT_TRUE(db().Execute("PRAGMA journal_mode = WAL"));
+ ASSERT_TRUE(db().Execute("ALTER TABLE foo ADD COLUMN c"));
+ ASSERT_EQ(
+ SQLITE_OK,
+ sqlite3_db_config(db().db_, SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, 1, NULL));
+ ASSERT_TRUE(GetPathExists(wal_path));
+ db().Close();
+ ASSERT_TRUE(GetPathExists(wal_path));
+}
+#endif
+
+} // namespace sql