OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |