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

Side by Side Diff: sql/connection.cc

Issue 11369126: Make sql::Connection::Raze() more robust against corruptions. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Fix for android. Created 8 years, 1 month 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
« no previous file with comments | « sql/connection.h ('k') | sql/connection_unittest.cc » ('j') | 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 "sql/connection.h" 5 #include "sql/connection.h"
6 6
7 #include <string.h> 7 #include <string.h>
8 8
9 #include "base/file_path.h" 9 #include "base/file_path.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 22 matching lines...) Expand all
33 int SetTimeout(base::TimeDelta timeout) { 33 int SetTimeout(base::TimeDelta timeout) {
34 DCHECK_LT(timeout.InMilliseconds(), INT_MAX); 34 DCHECK_LT(timeout.InMilliseconds(), INT_MAX);
35 return sqlite3_busy_timeout(db_, 35 return sqlite3_busy_timeout(db_,
36 static_cast<int>(timeout.InMilliseconds())); 36 static_cast<int>(timeout.InMilliseconds()));
37 } 37 }
38 38
39 private: 39 private:
40 sqlite3* db_; 40 sqlite3* db_;
41 }; 41 };
42 42
43 // Helper to "safely" enable writable_schema. No error checking
44 // because it is reasonable to just forge ahead in case of an error.
45 // If turning it on fails, then most likely nothing will work, whereas
46 // if turning it off fails, it only matters if some code attempts to
47 // continue working with the database and tries to modify the
48 // sqlite_master table (none of our code does this).
49 class ScopedWritableSchema {
50 public:
51 explicit ScopedWritableSchema(sqlite3* db)
52 : db_(db) {
53 sqlite3_exec(db_, "PRAGMA writable_schema=1", NULL, NULL, NULL);
54 }
55 ~ScopedWritableSchema() {
56 sqlite3_exec(db_, "PRAGMA writable_schema=0", NULL, NULL, NULL);
57 }
58
59 private:
60 sqlite3* db_;
61 };
62
43 } // namespace 63 } // namespace
44 64
45 namespace sql { 65 namespace sql {
46 66
47 bool StatementID::operator<(const StatementID& other) const { 67 bool StatementID::operator<(const StatementID& other) const {
48 if (number_ != other.number_) 68 if (number_ != other.number_)
49 return number_ < other.number_; 69 return number_ < other.number_;
50 return strcmp(str_, other.str_) < 0; 70 return strcmp(str_, other.str_) < 0;
51 } 71 }
52 72
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 DLOG(FATAL) << "Cannot raze within a transaction"; 209 DLOG(FATAL) << "Cannot raze within a transaction";
190 return false; 210 return false;
191 } 211 }
192 212
193 sql::Connection null_db; 213 sql::Connection null_db;
194 if (!null_db.OpenInMemory()) { 214 if (!null_db.OpenInMemory()) {
195 DLOG(FATAL) << "Unable to open in-memory database."; 215 DLOG(FATAL) << "Unable to open in-memory database.";
196 return false; 216 return false;
197 } 217 }
198 218
199 // Get the page size from the current connection, then propagate it 219 if (page_size_) {
200 // to the null database. 220 // Enforce SQLite restrictions on |page_size_|.
201 { 221 DCHECK(!(page_size_ & (page_size_ - 1)))
202 Statement s(GetUniqueStatement("PRAGMA page_size")); 222 << " page_size_ " << page_size_ << " is not a power of two.";
203 if (!s.Step()) 223 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h
204 return false; 224 DCHECK_LE(page_size_, kSqliteMaxPageSize);
205 const std::string sql = StringPrintf("PRAGMA page_size=%d", 225 const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_);
206 s.ColumnInt(0));
207 if (!null_db.Execute(sql.c_str())) 226 if (!null_db.Execute(sql.c_str()))
208 return false; 227 return false;
209 } 228 }
210 229
211 // Get the value of auto_vacuum from the current connection, then propagate it 230 #if defined(OS_ANDROID)
212 // to the null database. 231 // Android compiles with SQLITE_DEFAULT_AUTOVACUUM. Unfortunately,
213 { 232 // in-memory databases do not respect this define.
214 Statement s(GetUniqueStatement("PRAGMA auto_vacuum")); 233 // TODO(shess): Figure out a way to set this without using platform
215 if (!s.Step()) 234 // specific code. AFAICT from sqlite3.c, the only way to do it
216 return false; 235 // would be to create an actual filesystem database, which is
217 const std::string sql = StringPrintf("PRAGMA auto_vacuum=%d", 236 // unfortunate.
218 s.ColumnInt(0)); 237 if (!null_db.Execute("PRAGMA auto_vacuum = 1"))
219 if (!null_db.Execute(sql.c_str())) 238 return false;
220 return false; 239 #endif
221 }
222 240
223 // The page size doesn't take effect until a database has pages, and 241 // The page size doesn't take effect until a database has pages, and
224 // at this point the null database has none. Changing the schema 242 // at this point the null database has none. Changing the schema
225 // version will create the first page. This will not affect the 243 // version will create the first page. This will not affect the
226 // schema version in the resulting database, as SQLite's backup 244 // schema version in the resulting database, as SQLite's backup
227 // implementation propagates the schema version from the original 245 // implementation propagates the schema version from the original
228 // connection to the new version of the database, incremented by one 246 // connection to the new version of the database, incremented by one
229 // so that other readers see the schema change and act accordingly. 247 // so that other readers see the schema change and act accordingly.
230 if (!null_db.Execute("PRAGMA schema_version = 1")) 248 if (!null_db.Execute("PRAGMA schema_version = 1"))
231 return false; 249 return false;
232 250
251 // SQLite tracks the expected number of database pages in the first
252 // page, and if it does not match the total retrieved from a
253 // filesystem call, treats the database as corrupt. This situation
254 // breaks almost all SQLite calls. "PRAGMA writable_schema" can be
255 // used to hint to SQLite to soldier on in that case, specifically
256 // for purposes of recovery. [See SQLITE_CORRUPT_BKPT case in
257 // sqlite3.c lockBtree().]
258 // TODO(shess): With this, "PRAGMA auto_vacuum" and "PRAGMA
259 // page_size" can be used to query such a database.
260 ScopedWritableSchema writable_schema(db_);
261
233 sqlite3_backup* backup = sqlite3_backup_init(db_, "main", 262 sqlite3_backup* backup = sqlite3_backup_init(db_, "main",
234 null_db.db_, "main"); 263 null_db.db_, "main");
235 if (!backup) { 264 if (!backup) {
236 DLOG(FATAL) << "Unable to start sqlite3_backup()."; 265 DLOG(FATAL) << "Unable to start sqlite3_backup().";
237 return false; 266 return false;
238 } 267 }
239 268
240 // -1 backs up the entire database. 269 // -1 backs up the entire database.
241 int rc = sqlite3_backup_step(backup, -1); 270 int rc = sqlite3_backup_step(backup, -1);
242 int pages = sqlite3_backup_pagecount(backup); 271 int pages = sqlite3_backup_pagecount(backup);
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 ignore_result(Execute("PRAGMA journal_mode = PERSIST")); 571 ignore_result(Execute("PRAGMA journal_mode = PERSIST"));
543 ignore_result(Execute("PRAGMA journal_size_limit = 16384")); 572 ignore_result(Execute("PRAGMA journal_size_limit = 16384"));
544 573
545 const base::TimeDelta kBusyTimeout = 574 const base::TimeDelta kBusyTimeout =
546 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds); 575 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds);
547 576
548 if (page_size_ != 0) { 577 if (page_size_ != 0) {
549 // Enforce SQLite restrictions on |page_size_|. 578 // Enforce SQLite restrictions on |page_size_|.
550 DCHECK(!(page_size_ & (page_size_ - 1))) 579 DCHECK(!(page_size_ & (page_size_ - 1)))
551 << " page_size_ " << page_size_ << " is not a power of two."; 580 << " page_size_ " << page_size_ << " is not a power of two.";
552 static const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h 581 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h
553 DCHECK_LE(page_size_, kSqliteMaxPageSize); 582 DCHECK_LE(page_size_, kSqliteMaxPageSize);
554 const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_); 583 const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_);
555 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) 584 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout))
556 DLOG(FATAL) << "Could not set page size: " << GetErrorMessage(); 585 DLOG(FATAL) << "Could not set page size: " << GetErrorMessage();
557 } 586 }
558 587
559 if (cache_size_ != 0) { 588 if (cache_size_ != 0) {
560 const std::string sql = StringPrintf("PRAGMA cache_size=%d", cache_size_); 589 const std::string sql = StringPrintf("PRAGMA cache_size=%d", cache_size_);
561 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) 590 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout))
562 DLOG(FATAL) << "Could not set cache size: " << GetErrorMessage(); 591 DLOG(FATAL) << "Could not set cache size: " << GetErrorMessage();
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
603 632
604 int Connection::OnSqliteError(int err, sql::Statement *stmt) { 633 int Connection::OnSqliteError(int err, sql::Statement *stmt) {
605 if (error_delegate_.get()) 634 if (error_delegate_.get())
606 return error_delegate_->OnError(err, this, stmt); 635 return error_delegate_->OnError(err, this, stmt);
607 // The default handling is to assert on debug and to ignore on release. 636 // The default handling is to assert on debug and to ignore on release.
608 DLOG(FATAL) << GetErrorMessage(); 637 DLOG(FATAL) << GetErrorMessage();
609 return err; 638 return err;
610 } 639 }
611 640
612 } // namespace sql 641 } // namespace sql
OLDNEW
« no previous file with comments | « sql/connection.h ('k') | sql/connection_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698