Chromium Code Reviews
[email protected] (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(703)

Side by Side Diff: sql/connection.cc

Issue 16664005: [sql] Framework for allowing tests to handle errors. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Call ShouldIgnoreError() in all modes. Created 7 years, 6 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
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 "sql/connection.h" 5 #include "sql/connection.h"
6 6
7 #include <string.h> 7 #include <string.h>
8 8
9 #include "base/files/file_path.h" 9 #include "base/files/file_path.h"
10 #include "base/file_util.h" 10 #include "base/file_util.h"
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 sqlite3_exec(db_, "PRAGMA writable_schema=1", NULL, NULL, NULL); 57 sqlite3_exec(db_, "PRAGMA writable_schema=1", NULL, NULL, NULL);
58 } 58 }
59 ~ScopedWritableSchema() { 59 ~ScopedWritableSchema() {
60 sqlite3_exec(db_, "PRAGMA writable_schema=0", NULL, NULL, NULL); 60 sqlite3_exec(db_, "PRAGMA writable_schema=0", NULL, NULL, NULL);
61 } 61 }
62 62
63 private: 63 private:
64 sqlite3* db_; 64 sqlite3* db_;
65 }; 65 };
66 66
67 // Helper to wrap the sqlite3_backup_*() step of Raze(). Return
68 // SQLite error code from running the backup step.
69 int BackupDatabase(sqlite3* src, sqlite3* dst, const char* db_name) {
70 sqlite3_backup* backup = sqlite3_backup_init(dst, db_name, src, db_name);
71 if (!backup) {
72 // Since this call only sets things up, this indicates a gross
73 // error in SQLite.
74 DLOG(FATAL) << "Unable to start sqlite3_backup().";
75 return SQLITE_ABORT;
76 }
77
78 // -1 backs up the entire database.
79 int rc = sqlite3_backup_step(backup, -1);
80 int pages = sqlite3_backup_pagecount(backup);
81 sqlite3_backup_finish(backup);
82
83 // Exactly one page should have been backed up. If this breaks,
84 // check this function to make sure assumptions aren't being broken.
85 if (rc == SQLITE_DONE)
86 DCHECK_EQ(pages, 1);
87
88 return rc;
89 }
90
91 // Hooks for ScopedErrorIgnorer.
92 sql::ScopedErrorIgnorer* g_current_ignorer = NULL;
93 bool ShouldIgnoreError(int error) {
94 if (g_current_ignorer)
95 return g_current_ignorer->ShouldIgnoreError(error);
96 return false;
97 }
98
67 } // namespace 99 } // namespace
68 100
69 namespace sql { 101 namespace sql {
70 102
71 bool StatementID::operator<(const StatementID& other) const { 103 bool StatementID::operator<(const StatementID& other) const {
72 if (number_ != other.number_) 104 if (number_ != other.number_)
73 return number_ < other.number_; 105 return number_ < other.number_;
74 return strcmp(str_, other.str_) < 0; 106 return strcmp(str_, other.str_) < 0;
75 } 107 }
76 108
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 // page, and if it does not match the total retrieved from a 320 // page, and if it does not match the total retrieved from a
289 // filesystem call, treats the database as corrupt. This situation 321 // filesystem call, treats the database as corrupt. This situation
290 // breaks almost all SQLite calls. "PRAGMA writable_schema" can be 322 // breaks almost all SQLite calls. "PRAGMA writable_schema" can be
291 // used to hint to SQLite to soldier on in that case, specifically 323 // used to hint to SQLite to soldier on in that case, specifically
292 // for purposes of recovery. [See SQLITE_CORRUPT_BKPT case in 324 // for purposes of recovery. [See SQLITE_CORRUPT_BKPT case in
293 // sqlite3.c lockBtree().] 325 // sqlite3.c lockBtree().]
294 // TODO(shess): With this, "PRAGMA auto_vacuum" and "PRAGMA 326 // TODO(shess): With this, "PRAGMA auto_vacuum" and "PRAGMA
295 // page_size" can be used to query such a database. 327 // page_size" can be used to query such a database.
296 ScopedWritableSchema writable_schema(db_); 328 ScopedWritableSchema writable_schema(db_);
297 329
298 sqlite3_backup* backup = sqlite3_backup_init(db_, "main", 330 const char* kMain = "main";
299 null_db.db_, "main"); 331 int rc = BackupDatabase(null_db.db_, db_, kMain);
300 if (!backup) {
301 DLOG(FATAL) << "Unable to start sqlite3_backup().";
302 return false;
303 }
304
305 // -1 backs up the entire database.
306 int rc = sqlite3_backup_step(backup, -1);
307 int pages = sqlite3_backup_pagecount(backup);
308 sqlite3_backup_finish(backup);
309 332
310 // The destination database was locked. 333 // The destination database was locked.
311 if (rc == SQLITE_BUSY) { 334 if (rc == SQLITE_BUSY) {
312 return false; 335 return false;
313 } 336 }
314 337
338 // SQLITE_NOTADB can happen if page 1 exists but is not formatted
erikwright (departed) 2013/06/11 18:54:19 Can/should this block be a separate CL?
Scott Hess - ex-Googler 2013/06/12 22:42:46 It's in here so that the example tests can pass.
339 // correctly. SQLITE_IOERR_SHORT_READ can happen if the database
340 // isn't even big enough for one page. Either way, reach in and
341 // truncate it before trying again.
342 // TODO(shess): Maybe it would be worthwhile to just truncate from
343 // the get-go?
344 if (rc == SQLITE_NOTADB || rc == SQLITE_IOERR_SHORT_READ) {
345 sqlite3_file* file = NULL;
346 rc = sqlite3_file_control(db_, "main", SQLITE_FCNTL_FILE_POINTER, &file);
347 if (rc != SQLITE_OK) {
348 DLOG(FATAL) << "Failure getting file handle.";
349 return false;
350 } else if (!file) {
351 DLOG(FATAL) << "File handle is empty.";
352 return false;
353 }
354
355 rc = file->pMethods->xTruncate(file, 0);
356 if (rc != SQLITE_OK) {
357 DLOG(FATAL) << "Failed to truncate file.";
358 return false;
359 }
360
361 rc = BackupDatabase(null_db.db_, db_, kMain);
362
363 if (rc != SQLITE_DONE) {
364 DLOG(FATAL) << "Failed retrying Raze().";
365 }
366 }
367
315 // The entire database should have been backed up. 368 // The entire database should have been backed up.
316 if (rc != SQLITE_DONE) { 369 if (rc != SQLITE_DONE) {
370 // TODO(shess): Figure out which other cases can happen.
317 DLOG(FATAL) << "Unable to copy entire null database."; 371 DLOG(FATAL) << "Unable to copy entire null database.";
318 return false; 372 return false;
319 } 373 }
320 374
321 // Exactly one page should have been backed up. If this breaks,
322 // check this function to make sure assumptions aren't being broken.
323 DCHECK_EQ(pages, 1);
324
325 return true; 375 return true;
326 } 376 }
327 377
328 bool Connection::RazeWithTimout(base::TimeDelta timeout) { 378 bool Connection::RazeWithTimout(base::TimeDelta timeout) {
329 if (!db_) { 379 if (!db_) {
330 DLOG_IF(FATAL, !poisoned_) << "Cannot raze null db"; 380 DLOG_IF(FATAL, !poisoned_) << "Cannot raze null db";
331 return false; 381 return false;
332 } 382 }
333 383
334 ScopedBusyTimeout busy_timeout(db_); 384 ScopedBusyTimeout busy_timeout(db_);
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
432 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; 482 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
433 return false; 483 return false;
434 } 484 }
435 485
436 int error = ExecuteAndReturnErrorCode(sql); 486 int error = ExecuteAndReturnErrorCode(sql);
437 if (error != SQLITE_OK) 487 if (error != SQLITE_OK)
438 error = OnSqliteError(error, NULL); 488 error = OnSqliteError(error, NULL);
439 489
440 // This needs to be a FATAL log because the error case of arriving here is 490 // This needs to be a FATAL log because the error case of arriving here is
441 // that there's a malformed SQL statement. This can arise in development if 491 // that there's a malformed SQL statement. This can arise in development if
442 // a change alters the schema but not all queries adjust. 492 // a change alters the schema but not all queries adjust. This can happen
493 // in production if the schema is corrupted.
443 if (error == SQLITE_ERROR) 494 if (error == SQLITE_ERROR)
444 DLOG(FATAL) << "SQL Error in " << sql << ", " << GetErrorMessage(); 495 DLOG(FATAL) << "SQL Error in " << sql << ", " << GetErrorMessage();
445 return error == SQLITE_OK; 496 return error == SQLITE_OK;
446 } 497 }
447 498
448 bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) { 499 bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) {
449 if (!db_) { 500 if (!db_) {
450 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; 501 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
451 return false; 502 return false;
452 } 503 }
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after
650 DCHECK_EQ(err, SQLITE_OK) << "Could not enable extended result codes"; 701 DCHECK_EQ(err, SQLITE_OK) << "Could not enable extended result codes";
651 702
652 // If indicated, lock up the database before doing anything else, so 703 // If indicated, lock up the database before doing anything else, so
653 // that the following code doesn't have to deal with locking. 704 // that the following code doesn't have to deal with locking.
654 // TODO(shess): This code is brittle. Find the cases where code 705 // TODO(shess): This code is brittle. Find the cases where code
655 // doesn't request |exclusive_locking_| and audit that it does the 706 // doesn't request |exclusive_locking_| and audit that it does the
656 // right thing with SQLITE_BUSY, and that it doesn't make 707 // right thing with SQLITE_BUSY, and that it doesn't make
657 // assumptions about who might change things in the database. 708 // assumptions about who might change things in the database.
658 // http://crbug.com/56559 709 // http://crbug.com/56559
659 if (exclusive_locking_) { 710 if (exclusive_locking_) {
660 // TODO(shess): This should probably be a full CHECK(). Code 711 // TODO(shess): This should probably be a failure. Code which
661 // which requests exclusive locking but doesn't get it is almost 712 // requests exclusive locking but doesn't get it is almost certain
662 // certain to be ill-tested. 713 // to be ill-tested.
663 if (!Execute("PRAGMA locking_mode=EXCLUSIVE")) 714 ignore_result(Execute("PRAGMA locking_mode=EXCLUSIVE"));
664 DLOG(FATAL) << "Could not set locking mode: " << GetErrorMessage();
665 } 715 }
666 716
667 // http://www.sqlite.org/pragma.html#pragma_journal_mode 717 // http://www.sqlite.org/pragma.html#pragma_journal_mode
668 // DELETE (default) - delete -journal file to commit. 718 // DELETE (default) - delete -journal file to commit.
669 // TRUNCATE - truncate -journal file to commit. 719 // TRUNCATE - truncate -journal file to commit.
670 // PERSIST - zero out header of -journal file to commit. 720 // PERSIST - zero out header of -journal file to commit.
671 // journal_size_limit provides size to trim to in PERSIST. 721 // journal_size_limit provides size to trim to in PERSIST.
672 // TODO(shess): Figure out if PERSIST and journal_size_limit really 722 // TODO(shess): Figure out if PERSIST and journal_size_limit really
673 // matter. In theory, it keeps pages pre-allocated, so if 723 // matter. In theory, it keeps pages pre-allocated, so if
674 // transactions usually fit, it should be faster. 724 // transactions usually fit, it should be faster.
675 ignore_result(Execute("PRAGMA journal_mode = PERSIST")); 725 ignore_result(Execute("PRAGMA journal_mode = PERSIST"));
676 ignore_result(Execute("PRAGMA journal_size_limit = 16384")); 726 ignore_result(Execute("PRAGMA journal_size_limit = 16384"));
677 727
678 const base::TimeDelta kBusyTimeout = 728 const base::TimeDelta kBusyTimeout =
679 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds); 729 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds);
680 730
681 if (page_size_ != 0) { 731 if (page_size_ != 0) {
682 // Enforce SQLite restrictions on |page_size_|. 732 // Enforce SQLite restrictions on |page_size_|.
683 DCHECK(!(page_size_ & (page_size_ - 1))) 733 DCHECK(!(page_size_ & (page_size_ - 1)))
684 << " page_size_ " << page_size_ << " is not a power of two."; 734 << " page_size_ " << page_size_ << " is not a power of two.";
685 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h 735 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h
686 DCHECK_LE(page_size_, kSqliteMaxPageSize); 736 DCHECK_LE(page_size_, kSqliteMaxPageSize);
687 const std::string sql = 737 const std::string sql =
688 base::StringPrintf("PRAGMA page_size=%d", page_size_); 738 base::StringPrintf("PRAGMA page_size=%d", page_size_);
689 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) 739 ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout));
690 DLOG(FATAL) << "Could not set page size: " << GetErrorMessage();
691 } 740 }
692 741
693 if (cache_size_ != 0) { 742 if (cache_size_ != 0) {
694 const std::string sql = 743 const std::string sql =
695 base::StringPrintf("PRAGMA cache_size=%d", cache_size_); 744 base::StringPrintf("PRAGMA cache_size=%d", cache_size_);
696 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) 745 ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout));
697 DLOG(FATAL) << "Could not set cache size: " << GetErrorMessage();
698 } 746 }
699 747
700 if (!ExecuteWithTimeout("PRAGMA secure_delete=ON", kBusyTimeout)) { 748 if (!ExecuteWithTimeout("PRAGMA secure_delete=ON", kBusyTimeout)) {
701 DLOG(FATAL) << "Could not enable secure_delete: " << GetErrorMessage();
702 Close(); 749 Close();
703 return false; 750 return false;
704 } 751 }
705 752
706 return true; 753 return true;
707 } 754 }
708 755
709 void Connection::DoRollback() { 756 void Connection::DoRollback() {
710 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); 757 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK"));
711 rollback.Run(); 758 rollback.Run();
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
756 error_callback_.Run(err, stmt); 803 error_callback_.Run(err, stmt);
757 return err; 804 return err;
758 } 805 }
759 806
760 // TODO(shess): Remove |error_delegate_| once everything is 807 // TODO(shess): Remove |error_delegate_| once everything is
761 // converted to |error_callback_|. 808 // converted to |error_callback_|.
762 if (error_delegate_.get()) 809 if (error_delegate_.get())
763 return error_delegate_->OnError(err, this, stmt); 810 return error_delegate_->OnError(err, this, stmt);
764 811
765 // The default handling is to assert on debug and to ignore on release. 812 // The default handling is to assert on debug and to ignore on release.
766 DLOG(FATAL) << GetErrorMessage(); 813 if (!ShouldIgnoreError(err))
814 DLOG(FATAL) << GetErrorMessage();
767 return err; 815 return err;
768 } 816 }
769 817
770 // TODO(shess): Allow specifying integrity_check versus quick_check. 818 // TODO(shess): Allow specifying integrity_check versus quick_check.
771 // TODO(shess): Allow specifying maximum results (default 100 lines). 819 // TODO(shess): Allow specifying maximum results (default 100 lines).
772 bool Connection::IntegrityCheck(std::vector<std::string>* messages) { 820 bool Connection::IntegrityCheck(std::vector<std::string>* messages) {
773 const char kSql[] = "PRAGMA integrity_check"; 821 const char kSql[] = "PRAGMA integrity_check";
774 sql::Statement stmt(GetUniqueStatement(kSql)); 822 sql::Statement stmt(GetUniqueStatement(kSql));
775 823
776 messages->clear(); 824 messages->clear();
777 825
778 // The pragma appears to return all results (up to 100 by default) 826 // The pragma appears to return all results (up to 100 by default)
779 // as a single string. This doesn't appear to be an API contract, 827 // as a single string. This doesn't appear to be an API contract,
780 // it could return separate lines, so loop _and_ split. 828 // it could return separate lines, so loop _and_ split.
781 while (stmt.Step()) { 829 while (stmt.Step()) {
782 std::string result(stmt.ColumnString(0)); 830 std::string result(stmt.ColumnString(0));
783 base::SplitString(result, '\n', messages); 831 base::SplitString(result, '\n', messages);
784 } 832 }
785 return stmt.Succeeded(); 833 return stmt.Succeeded();
786 } 834 }
787 835
836 ScopedErrorIgnorer::ScopedErrorIgnorer()
837 : checked_(false),
838 next_(g_current_ignorer) {
839 g_current_ignorer = this;
840 }
841
842 ScopedErrorIgnorer::~ScopedErrorIgnorer() {
843 CHECK(checked_);
erikwright (departed) 2013/06/11 18:54:19 This should probably be ADD_FAILURE instead of CHE
Scott Hess - ex-Googler 2013/06/12 22:42:46 Basically I hate all this :-). OK, so would it ma
844 DCHECK_EQ(g_current_ignorer, this);
845 g_current_ignorer = next_;
846 }
847
848 void ScopedErrorIgnorer::IgnoreError(int err) {
849 DCHECK_EQ(0u, ignore_errors_.count(err));
850 ignore_errors_.insert(err);
851 }
852
853 bool ScopedErrorIgnorer::CheckIgnoredErrors() {
854 checked_ = true;
855 return errors_ignored_ == ignore_errors_;
856 }
857
858 // static
859 bool ScopedErrorIgnorer::ShouldIgnoreError(int err) {
860 // Do not ignore if no scoped ignorer.
861 if (!g_current_ignorer)
862 return false;
863
864 // Check extended code.
865 if (g_current_ignorer->ignore_errors_.count(err) > 0) {
866 // Record that the error was seen and ignore it.
867 g_current_ignorer->errors_ignored_.insert(err);
868 return true;
869 }
870
871 // Trim extended codes.
872 err &= 0xff;
873
874 // Check extended code.
erikwright (departed) 2013/06/11 18:54:19 It seems like either line 874 or 864 is wrong?
Scott Hess - ex-Googler 2013/06/12 22:42:46 Doh, this one is checking the non-extended code.
875 if (g_current_ignorer->ignore_errors_.count(err) > 0) {
876 // Record that the error was seen and ignore it.
877 g_current_ignorer->errors_ignored_.insert(err);
878 return true;
879 }
880
881 // Unexpected error, do not ignore.
882 return false;
883 }
884
788 } // namespace sql 885 } // namespace sql
OLDNEW
« sql/connection.h ('K') | « sql/connection.h ('k') | sql/connection_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698