Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(724)

Side by Side Diff: sql/connection_unittest.cc

Issue 17752002: [sql] Additional Raze() unit tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« sql/connection.cc ('K') | « sql/connection.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/bind.h"
5 #include "base/file_util.h" 6 #include "base/file_util.h"
6 #include "base/files/scoped_temp_dir.h" 7 #include "base/files/scoped_temp_dir.h"
7 #include "base/logging.h" 8 #include "base/logging.h"
8 #include "sql/connection.h" 9 #include "sql/connection.h"
9 #include "sql/meta_table.h" 10 #include "sql/meta_table.h"
10 #include "sql/statement.h" 11 #include "sql/statement.h"
11 #include "sql/test/scoped_error_ignorer.h" 12 #include "sql/test/scoped_error_ignorer.h"
12 #include "testing/gtest/include/gtest/gtest.h" 13 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/sqlite/sqlite3.h" 14 #include "third_party/sqlite/sqlite3.h"
14 15
16 namespace {
17
18 // Helper to return the count of items in sqlite_master. Return -1 in
19 // case of error.
20 int SqliteMasterCount(sql::Connection* db) {
21 const char* kMasterCount = "SELECT COUNT(*) FROM sqlite_master";
22 sql::Statement s(db->GetUniqueStatement(kMasterCount));
23 return s.Step() ? s.ColumnInt(0) : -1;
24 }
25
15 class SQLConnectionTest : public testing::Test { 26 class SQLConnectionTest : public testing::Test {
16 public: 27 public:
17 SQLConnectionTest() {} 28 SQLConnectionTest() {}
18 29
19 virtual void SetUp() { 30 virtual void SetUp() {
20 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 31 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
21 ASSERT_TRUE(db_.Open(db_path())); 32 ASSERT_TRUE(db_.Open(db_path()));
22 } 33 }
23 34
24 virtual void TearDown() { 35 virtual void TearDown() {
25 db_.Close(); 36 db_.Close();
26 } 37 }
27 38
28 sql::Connection& db() { return db_; } 39 sql::Connection& db() { return db_; }
29 40
30 base::FilePath db_path() { 41 base::FilePath db_path() {
31 return temp_dir_.path().AppendASCII("SQLConnectionTest.db"); 42 return temp_dir_.path().AppendASCII("SQLConnectionTest.db");
32 } 43 }
33 44
45 // Handle errors by blowing away the database.
46 void RazeErrorCallback(int expected_error, int error, sql::Statement* stmt) {
47 EXPECT_EQ(expected_error, error);
48 db_.RazeAndClose();
49 }
50
34 private: 51 private:
35 base::ScopedTempDir temp_dir_; 52 base::ScopedTempDir temp_dir_;
36 sql::Connection db_; 53 sql::Connection db_;
37 }; 54 };
38 55
39 TEST_F(SQLConnectionTest, Execute) { 56 TEST_F(SQLConnectionTest, Execute) {
40 // Valid statement should return true. 57 // Valid statement should return true.
41 ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); 58 ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
42 EXPECT_EQ(SQLITE_OK, db().GetErrorCode()); 59 EXPECT_EQ(SQLITE_OK, db().GetErrorCode());
43 60
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 } 203 }
187 204
188 ASSERT_TRUE(db().Raze()); 205 ASSERT_TRUE(db().Raze());
189 206
190 { 207 {
191 sql::Statement s(db().GetUniqueStatement("PRAGMA page_count")); 208 sql::Statement s(db().GetUniqueStatement("PRAGMA page_count"));
192 ASSERT_TRUE(s.Step()); 209 ASSERT_TRUE(s.Step());
193 EXPECT_EQ(1, s.ColumnInt(0)); 210 EXPECT_EQ(1, s.ColumnInt(0));
194 } 211 }
195 212
196 { 213 ASSERT_EQ(0, SqliteMasterCount(&db()));
197 sql::Statement s(db().GetUniqueStatement("SELECT * FROM sqlite_master"));
198 ASSERT_FALSE(s.Step());
199 }
200 214
201 { 215 {
202 sql::Statement s(db().GetUniqueStatement("PRAGMA auto_vacuum")); 216 sql::Statement s(db().GetUniqueStatement("PRAGMA auto_vacuum"));
203 ASSERT_TRUE(s.Step()); 217 ASSERT_TRUE(s.Step());
204 // The new database has the same auto_vacuum as a fresh database. 218 // The new database has the same auto_vacuum as a fresh database.
205 EXPECT_EQ(pragma_auto_vacuum, s.ColumnInt(0)); 219 EXPECT_EQ(pragma_auto_vacuum, s.ColumnInt(0));
206 } 220 }
207 } 221 }
208 222
209 // Test that Raze() maintains page_size. 223 // Test that Raze() maintains page_size.
(...skipping 28 matching lines...) Expand all
238 252
239 // Test that Raze() results are seen in other connections. 253 // Test that Raze() results are seen in other connections.
240 TEST_F(SQLConnectionTest, RazeMultiple) { 254 TEST_F(SQLConnectionTest, RazeMultiple) {
241 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; 255 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
242 ASSERT_TRUE(db().Execute(kCreateSql)); 256 ASSERT_TRUE(db().Execute(kCreateSql));
243 257
244 sql::Connection other_db; 258 sql::Connection other_db;
245 ASSERT_TRUE(other_db.Open(db_path())); 259 ASSERT_TRUE(other_db.Open(db_path()));
246 260
247 // Check that the second connection sees the table. 261 // Check that the second connection sees the table.
248 const char *kTablesQuery = "SELECT COUNT(*) FROM sqlite_master"; 262 ASSERT_EQ(1, SqliteMasterCount(&other_db));
249 sql::Statement s(other_db.GetUniqueStatement(kTablesQuery));
250 ASSERT_TRUE(s.Step());
251 ASSERT_EQ(1, s.ColumnInt(0));
252 ASSERT_FALSE(s.Step()); // Releases the shared lock.
253 263
254 ASSERT_TRUE(db().Raze()); 264 ASSERT_TRUE(db().Raze());
255 265
256 // The second connection sees the updated database. 266 // The second connection sees the updated database.
257 s.Reset(true); 267 ASSERT_EQ(0, SqliteMasterCount(&other_db));
258 ASSERT_TRUE(s.Step());
259 ASSERT_EQ(0, s.ColumnInt(0));
260 } 268 }
261 269
262 TEST_F(SQLConnectionTest, RazeLocked) { 270 TEST_F(SQLConnectionTest, RazeLocked) {
263 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; 271 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
264 ASSERT_TRUE(db().Execute(kCreateSql)); 272 ASSERT_TRUE(db().Execute(kCreateSql));
265 273
266 // Open a transaction and write some data in a second connection. 274 // Open a transaction and write some data in a second connection.
267 // This will acquire a PENDING or EXCLUSIVE transaction, which will 275 // This will acquire a PENDING or EXCLUSIVE transaction, which will
268 // cause the raze to fail. 276 // cause the raze to fail.
269 sql::Connection other_db; 277 sql::Connection other_db;
(...skipping 17 matching lines...) Expand all
287 const char *kQuery = "SELECT COUNT(*) FROM foo"; 295 const char *kQuery = "SELECT COUNT(*) FROM foo";
288 sql::Statement s(other_db.GetUniqueStatement(kQuery)); 296 sql::Statement s(other_db.GetUniqueStatement(kQuery));
289 ASSERT_TRUE(s.Step()); 297 ASSERT_TRUE(s.Step());
290 ASSERT_FALSE(db().Raze()); 298 ASSERT_FALSE(db().Raze());
291 299
292 // Complete the statement unlocks the database. 300 // Complete the statement unlocks the database.
293 ASSERT_FALSE(s.Step()); 301 ASSERT_FALSE(s.Step());
294 ASSERT_TRUE(db().Raze()); 302 ASSERT_TRUE(db().Raze());
295 } 303 }
296 304
305 // Verify that Raze() can handle an empty file. SQLite should treat
306 // this as an empty database.
307 TEST_F(SQLConnectionTest, RazeEmptyDB) {
308 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
309 ASSERT_TRUE(db().Execute(kCreateSql));
310 db().Close();
311
312 {
313 file_util::ScopedFILE file(file_util::OpenFile(db_path(), "r+"));
314 ASSERT_TRUE(file.get() != NULL);
315 ASSERT_EQ(0, fseek(file.get(), 0, SEEK_SET));
316 ASSERT_TRUE(file_util::TruncateFile(file.get()));
317 }
318
319 ASSERT_TRUE(db().Open(db_path()));
320 ASSERT_TRUE(db().Raze());
321 EXPECT_EQ(0, SqliteMasterCount(&db()));
322 }
323
324 // Verify that Raze() can handle a file of junk.
325 TEST_F(SQLConnectionTest, RazeNOTADB) {
326 db().Close();
327 sql::Connection::Delete(db_path());
328 ASSERT_FALSE(file_util::PathExists(db_path()));
329
330 {
331 file_util::ScopedFILE file(file_util::OpenFile(db_path(), "w"));
332 ASSERT_TRUE(file.get() != NULL);
333
334 const char* kJunk = "This is the hour of our discontent.";
335 fputs(kJunk, file.get());
336 }
337 ASSERT_TRUE(file_util::PathExists(db_path()));
338
339 // SQLite will successfully open the handle, but will fail with
340 // SQLITE_IOERR_SHORT_READ on pragma statemenets which read the
341 // header.
342 {
343 sql::ScopedErrorIgnorer ignore_errors;
344 ignore_errors.IgnoreError(SQLITE_IOERR_SHORT_READ);
345 EXPECT_TRUE(db().Open(db_path()));
346 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
347 }
348 EXPECT_TRUE(db().Raze());
349 db().Close();
350
351 // Now empty, the open should open an empty database.
352 EXPECT_TRUE(db().Open(db_path()));
353 EXPECT_EQ(0, SqliteMasterCount(&db()));
354 }
355
356 // Verify that Raze() can handle a database overwritten with garbage.
357 TEST_F(SQLConnectionTest, RazeNOTADB2) {
358 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
359 ASSERT_TRUE(db().Execute(kCreateSql));
360 ASSERT_EQ(1, SqliteMasterCount(&db()));
361 db().Close();
362
363 {
364 file_util::ScopedFILE file(file_util::OpenFile(db_path(), "r+"));
365 ASSERT_TRUE(file.get() != NULL);
366 ASSERT_EQ(0, fseek(file.get(), 0, SEEK_SET));
367
368 const char* kJunk = "This is the hour of our discontent.";
369 fputs(kJunk, file.get());
370 }
371
372 // SQLite will successfully open the handle, but will fail with
373 // SQLITE_NOTADB on pragma statemenets which attempt to read the
374 // corrupted header.
375 {
376 sql::ScopedErrorIgnorer ignore_errors;
377 ignore_errors.IgnoreError(SQLITE_NOTADB);
378 EXPECT_TRUE(db().Open(db_path()));
379 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
380 }
381 EXPECT_TRUE(db().Raze());
382 db().Close();
383
384 // Now empty, the open should succeed with an empty database.
385 EXPECT_TRUE(db().Open(db_path()));
386 EXPECT_EQ(0, SqliteMasterCount(&db()));
387 }
388
389 // Test that a callback from Open() can raze the database. This is
390 // essential for cases where the Open() can fail entirely, so the
391 // Raze() cannot happen later.
392 //
393 // Most corruptions seen in the wild seem to happen when two pages in
394 // the database were not written transactionally (the transaction
395 // changed both, but one wasn't successfully written for some reason).
396 // A special case of that is when the header indicates that the
397 // database contains more pages than are in the file. This breaks
398 // things at a very basic level, verify that Raze() can handle it.
399 TEST_F(SQLConnectionTest, RazeOpenCallback) {
400 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
401 ASSERT_TRUE(db().Execute(kCreateSql));
402 ASSERT_EQ(1, SqliteMasterCount(&db()));
403 db().Close();
404
405 // Trim a single page from the end of the file.
406 {
407 file_util::ScopedFILE file(file_util::OpenFile(db_path(), "r+"));
408 ASSERT_TRUE(file.get() != NULL);
409 ASSERT_EQ(0, fseek(file.get(), -1024, SEEK_END));
410 ASSERT_TRUE(file_util::TruncateFile(file.get()));
411 }
412
413 db().set_error_callback(base::Bind(&SQLConnectionTest::RazeErrorCallback,
414 base::Unretained(this),
415 SQLITE_CORRUPT));
416
417 // Open() will see SQLITE_CORRUPT due to size mismatch when
418 // attempting to run a pragma, and the callback will RazeAndClose().
419 // Later statements will fail, including the final secure_delete,
420 // which will fail the Open() itself.
421 ASSERT_FALSE(db().Open(db_path()));
422 db().Close();
423
424 // Now empty, the open should succeed with an empty database.
425 EXPECT_TRUE(db().Open(db_path()));
426 EXPECT_EQ(0, SqliteMasterCount(&db()));
427 }
428
297 // Basic test of RazeAndClose() operation. 429 // Basic test of RazeAndClose() operation.
298 TEST_F(SQLConnectionTest, RazeAndClose) { 430 TEST_F(SQLConnectionTest, RazeAndClose) {
299 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; 431 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
300 const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)"; 432 const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)";
301 433
302 // Test that RazeAndClose() closes the database, and that the 434 // Test that RazeAndClose() closes the database, and that the
303 // database is empty when re-opened. 435 // database is empty when re-opened.
304 ASSERT_TRUE(db().Execute(kCreateSql)); 436 ASSERT_TRUE(db().Execute(kCreateSql));
305 ASSERT_TRUE(db().Execute(kPopulateSql)); 437 ASSERT_TRUE(db().Execute(kPopulateSql));
306 ASSERT_TRUE(db().RazeAndClose()); 438 ASSERT_TRUE(db().RazeAndClose());
307 ASSERT_FALSE(db().is_open()); 439 ASSERT_FALSE(db().is_open());
308 db().Close(); 440 db().Close();
309 ASSERT_TRUE(db().Open(db_path())); 441 ASSERT_TRUE(db().Open(db_path()));
310 { 442 ASSERT_EQ(0, SqliteMasterCount(&db()));
311 sql::Statement s(db().GetUniqueStatement("SELECT * FROM sqlite_master"));
312 ASSERT_FALSE(s.Step());
313 }
314 443
315 // Test that RazeAndClose() can break transactions. 444 // Test that RazeAndClose() can break transactions.
316 ASSERT_TRUE(db().Execute(kCreateSql)); 445 ASSERT_TRUE(db().Execute(kCreateSql));
317 ASSERT_TRUE(db().Execute(kPopulateSql)); 446 ASSERT_TRUE(db().Execute(kPopulateSql));
318 ASSERT_TRUE(db().BeginTransaction()); 447 ASSERT_TRUE(db().BeginTransaction());
319 ASSERT_TRUE(db().RazeAndClose()); 448 ASSERT_TRUE(db().RazeAndClose());
320 ASSERT_FALSE(db().is_open()); 449 ASSERT_FALSE(db().is_open());
321 ASSERT_FALSE(db().CommitTransaction()); 450 ASSERT_FALSE(db().CommitTransaction());
322 db().Close(); 451 db().Close();
323 ASSERT_TRUE(db().Open(db_path())); 452 ASSERT_TRUE(db().Open(db_path()));
324 { 453 ASSERT_EQ(0, SqliteMasterCount(&db()));
325 sql::Statement s(db().GetUniqueStatement("SELECT * FROM sqlite_master"));
326 ASSERT_FALSE(s.Step());
327 }
328 } 454 }
329 455
330 // Test that various operations fail without crashing after 456 // Test that various operations fail without crashing after
331 // RazeAndClose(). 457 // RazeAndClose().
332 TEST_F(SQLConnectionTest, RazeAndCloseDiagnostics) { 458 TEST_F(SQLConnectionTest, RazeAndCloseDiagnostics) {
333 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; 459 const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
334 const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)"; 460 const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)";
335 const char* kSimpleSql = "SELECT 1"; 461 const char* kSimpleSql = "SELECT 1";
336 462
337 ASSERT_TRUE(db().Execute(kCreateSql)); 463 ASSERT_TRUE(db().Execute(kCreateSql));
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
419 // Should have both a main database file and a journal file because 545 // Should have both a main database file and a journal file because
420 // of journal_mode PERSIST. 546 // of journal_mode PERSIST.
421 base::FilePath journal(db_path().value() + FILE_PATH_LITERAL("-journal")); 547 base::FilePath journal(db_path().value() + FILE_PATH_LITERAL("-journal"));
422 ASSERT_TRUE(file_util::PathExists(db_path())); 548 ASSERT_TRUE(file_util::PathExists(db_path()));
423 ASSERT_TRUE(file_util::PathExists(journal)); 549 ASSERT_TRUE(file_util::PathExists(journal));
424 550
425 sql::Connection::Delete(db_path()); 551 sql::Connection::Delete(db_path());
426 EXPECT_FALSE(file_util::PathExists(db_path())); 552 EXPECT_FALSE(file_util::PathExists(db_path()));
427 EXPECT_FALSE(file_util::PathExists(journal)); 553 EXPECT_FALSE(file_util::PathExists(journal));
428 } 554 }
555
556 } // namespace
OLDNEW
« sql/connection.cc ('K') | « sql/connection.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698