Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 // prevent optimization. | 125 // prevent optimization. |
| 126 CHECK_LT(nTouched, 1000*1000*1000U); | 126 CHECK_LT(nTouched, 1000*1000*1000U); |
| 127 #endif | 127 #endif |
| 128 sqlite3_close(db_); | 128 sqlite3_close(db_); |
| 129 db_ = NULL; | 129 db_ = NULL; |
| 130 } | 130 } |
| 131 } | 131 } |
| 132 | 132 |
| 133 void Connection::Preload() { | 133 void Connection::Preload() { |
| 134 if (!db_) { | 134 if (!db_) { |
| 135 NOTREACHED(); | 135 DLOG(FATAL) << "Cannot preload null db"; |
| 136 return; | 136 return; |
| 137 } | 137 } |
| 138 | 138 |
| 139 // A statement must be open for the preload command to work. If the meta | 139 // A statement must be open for the preload command to work. If the meta |
| 140 // table doesn't exist, it probably means this is a new database and there | 140 // table doesn't exist, it probably means this is a new database and there |
| 141 // is nothing to preload (so it's OK we do nothing). | 141 // is nothing to preload (so it's OK we do nothing). |
| 142 if (!DoesTableExist("meta")) | 142 if (!DoesTableExist("meta")) |
| 143 return; | 143 return; |
| 144 Statement dummy(GetUniqueStatement("SELECT * FROM meta")); | 144 Statement dummy(GetUniqueStatement("SELECT * FROM meta")); |
| 145 if (!dummy || !dummy.Step()) | 145 if (!dummy.Step()) |
| 146 return; | 146 return; |
| 147 | 147 |
| 148 #if !defined(USE_SYSTEM_SQLITE) | 148 #if !defined(USE_SYSTEM_SQLITE) |
| 149 // This function is only defined in Chromium's version of sqlite. | 149 // This function is only defined in Chromium's version of sqlite. |
| 150 // Do not call it when using system sqlite. | 150 // Do not call it when using system sqlite. |
| 151 sqlite3_preload(db_); | 151 sqlite3_preload(db_); |
| 152 #endif | 152 #endif |
| 153 } | 153 } |
| 154 | 154 |
| 155 bool Connection::BeginTransaction() { | 155 bool Connection::BeginTransaction() { |
| 156 if (needs_rollback_) { | 156 if (needs_rollback_) { |
| 157 DCHECK_GT(transaction_nesting_, 0); | 157 DCHECK_GT(transaction_nesting_, 0); |
| 158 | 158 |
| 159 // When we're going to rollback, fail on this begin and don't actually | 159 // When we're going to rollback, fail on this begin and don't actually |
| 160 // mark us as entering the nested transaction. | 160 // mark us as entering the nested transaction. |
| 161 return false; | 161 return false; |
| 162 } | 162 } |
| 163 | 163 |
| 164 bool success = true; | 164 bool success = true; |
| 165 if (!transaction_nesting_) { | 165 if (!transaction_nesting_) { |
| 166 needs_rollback_ = false; | 166 needs_rollback_ = false; |
| 167 | 167 |
| 168 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION")); | 168 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION")); |
| 169 if (!begin || !begin.Run()) | 169 if (!begin.Run()) |
| 170 return false; | 170 return false; |
| 171 } | 171 } |
| 172 transaction_nesting_++; | 172 transaction_nesting_++; |
| 173 return success; | 173 return success; |
| 174 } | 174 } |
| 175 | 175 |
| 176 void Connection::RollbackTransaction() { | 176 void Connection::RollbackTransaction() { |
| 177 if (!transaction_nesting_) { | 177 if (!transaction_nesting_) { |
| 178 NOTREACHED() << "Rolling back a nonexistent transaction"; | 178 DLOG(FATAL) << "Rolling back a nonexistent transaction"; |
| 179 return; | 179 return; |
| 180 } | 180 } |
| 181 | 181 |
| 182 transaction_nesting_--; | 182 transaction_nesting_--; |
| 183 | 183 |
| 184 if (transaction_nesting_ > 0) { | 184 if (transaction_nesting_ > 0) { |
| 185 // Mark the outermost transaction as needing rollback. | 185 // Mark the outermost transaction as needing rollback. |
| 186 needs_rollback_ = true; | 186 needs_rollback_ = true; |
| 187 return; | 187 return; |
| 188 } | 188 } |
| 189 | 189 |
| 190 DoRollback(); | 190 DoRollback(); |
| 191 } | 191 } |
| 192 | 192 |
| 193 bool Connection::CommitTransaction() { | 193 bool Connection::CommitTransaction() { |
| 194 if (!transaction_nesting_) { | 194 if (!transaction_nesting_) { |
| 195 NOTREACHED() << "Rolling back a nonexistent transaction"; | 195 DLOG(FATAL) << "Rolling back a nonexistent transaction"; |
| 196 return false; | 196 return false; |
| 197 } | 197 } |
| 198 transaction_nesting_--; | 198 transaction_nesting_--; |
| 199 | 199 |
| 200 if (transaction_nesting_ > 0) { | 200 if (transaction_nesting_ > 0) { |
| 201 // Mark any nested transactions as failing after we've already got one. | 201 // Mark any nested transactions as failing after we've already got one. |
| 202 return !needs_rollback_; | 202 return !needs_rollback_; |
| 203 } | 203 } |
| 204 | 204 |
| 205 if (needs_rollback_) { | 205 if (needs_rollback_) { |
| 206 DoRollback(); | 206 DoRollback(); |
| 207 return false; | 207 return false; |
| 208 } | 208 } |
| 209 | 209 |
| 210 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT")); | 210 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT")); |
| 211 if (!commit) | |
| 212 return false; | |
| 213 return commit.Run(); | 211 return commit.Run(); |
| 214 } | 212 } |
| 215 | 213 |
| 216 bool Connection::Execute(const char* sql) { | 214 int Connection::ExecuteAndReturnErrorCode(const char* sql) { |
| 217 if (!db_) | 215 if (!db_) |
| 218 return false; | 216 return false; |
| 219 return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK; | 217 return sqlite3_exec(db_, sql, NULL, NULL, NULL); |
| 218 } | |
| 219 | |
| 220 bool Connection::Execute(const char* sql) { | |
| 221 int error = ExecuteAndReturnErrorCode(sql); | |
| 222 if (error == SQLITE_ERROR) | |
| 223 DLOG(FATAL) << "SQL Error in " << sql; | |
|
Scott Hess - ex-Googler
2011/12/09 22:54:40
In my final review, I went over the OnSqliteError(
| |
| 224 return error == SQLITE_OK; | |
| 220 } | 225 } |
| 221 | 226 |
| 222 bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) { | 227 bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) { |
| 223 if (!db_) | 228 if (!db_) |
| 224 return false; | 229 return false; |
| 225 | 230 |
| 226 ScopedBusyTimeout busy_timeout(db_); | 231 ScopedBusyTimeout busy_timeout(db_); |
| 227 busy_timeout.SetTimeout(timeout); | 232 busy_timeout.SetTimeout(timeout); |
| 228 return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK; | 233 return Execute(sql); |
| 229 } | 234 } |
| 230 | 235 |
| 231 bool Connection::HasCachedStatement(const StatementID& id) const { | 236 bool Connection::HasCachedStatement(const StatementID& id) const { |
| 232 return statement_cache_.find(id) != statement_cache_.end(); | 237 return statement_cache_.find(id) != statement_cache_.end(); |
| 233 } | 238 } |
| 234 | 239 |
| 235 scoped_refptr<Connection::StatementRef> Connection::GetCachedStatement( | 240 scoped_refptr<Connection::StatementRef> Connection::GetCachedStatement( |
| 236 const StatementID& id, | 241 const StatementID& id, |
| 237 const char* sql) { | 242 const char* sql) { |
| 238 CachedStatementMap::iterator i = statement_cache_.find(id); | 243 CachedStatementMap::iterator i = statement_cache_.find(id); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 252 return statement; | 257 return statement; |
| 253 } | 258 } |
| 254 | 259 |
| 255 scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement( | 260 scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement( |
| 256 const char* sql) { | 261 const char* sql) { |
| 257 if (!db_) | 262 if (!db_) |
| 258 return new StatementRef(this, NULL); // Return inactive statement. | 263 return new StatementRef(this, NULL); // Return inactive statement. |
| 259 | 264 |
| 260 sqlite3_stmt* stmt = NULL; | 265 sqlite3_stmt* stmt = NULL; |
| 261 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) { | 266 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 262 // Treat this as non-fatal, it can occur in a number of valid cases, and | 267 // This is evidence of a syntax error in the incoming SQL. |
| 263 // callers should be doing their own error handling. | 268 DLOG(FATAL) << "SQL compile error " << GetErrorMessage(); |
| 264 DLOG(WARNING) << "SQL compile error " << GetErrorMessage(); | |
| 265 return new StatementRef(this, NULL); | 269 return new StatementRef(this, NULL); |
| 266 } | 270 } |
| 267 return new StatementRef(this, stmt); | 271 return new StatementRef(this, stmt); |
| 268 } | 272 } |
| 269 | 273 |
| 274 bool Connection::IsSQLValid(const char* sql) { | |
| 275 sqlite3_stmt* stmt = NULL; | |
| 276 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) | |
| 277 return false; | |
| 278 | |
| 279 sqlite3_finalize(stmt); | |
| 280 return true; | |
| 281 } | |
| 282 | |
| 270 bool Connection::DoesTableExist(const char* table_name) const { | 283 bool Connection::DoesTableExist(const char* table_name) const { |
| 271 // GetUniqueStatement can't be const since statements may modify the | 284 // GetUniqueStatement can't be const since statements may modify the |
| 272 // database, but we know ours doesn't modify it, so the cast is safe. | 285 // database, but we know ours doesn't modify it, so the cast is safe. |
| 273 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement( | 286 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement( |
| 274 "SELECT name FROM sqlite_master " | 287 "SELECT name FROM sqlite_master " |
| 275 "WHERE type='table' AND name=?")); | 288 "WHERE type='table' AND name=?")); |
| 276 if (!statement) | |
| 277 return false; | |
| 278 statement.BindString(0, table_name); | 289 statement.BindString(0, table_name); |
| 279 return statement.Step(); // Table exists if any row was returned. | 290 return statement.Step(); // Table exists if any row was returned. |
| 280 } | 291 } |
| 281 | 292 |
| 282 bool Connection::DoesColumnExist(const char* table_name, | 293 bool Connection::DoesColumnExist(const char* table_name, |
| 283 const char* column_name) const { | 294 const char* column_name) const { |
| 284 std::string sql("PRAGMA TABLE_INFO("); | 295 std::string sql("PRAGMA TABLE_INFO("); |
| 285 sql.append(table_name); | 296 sql.append(table_name); |
| 286 sql.append(")"); | 297 sql.append(")"); |
| 287 | 298 |
| 288 // Our SQL is non-mutating, so this cast is OK. | 299 // Our SQL is non-mutating, so this cast is OK. |
| 289 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement( | 300 Statement statement(const_cast<Connection*>(this)->GetUniqueStatement( |
| 290 sql.c_str())); | 301 sql.c_str())); |
| 291 if (!statement) | |
| 292 return false; | |
| 293 | 302 |
| 294 while (statement.Step()) { | 303 while (statement.Step()) { |
| 295 if (!statement.ColumnString(1).compare(column_name)) | 304 if (!statement.ColumnString(1).compare(column_name)) |
| 296 return true; | 305 return true; |
| 297 } | 306 } |
| 298 return false; | 307 return false; |
| 299 } | 308 } |
| 300 | 309 |
| 301 int64 Connection::GetLastInsertRowId() const { | 310 int64 Connection::GetLastInsertRowId() const { |
| 302 if (!db_) { | 311 if (!db_) { |
| 303 NOTREACHED(); | 312 DLOG(FATAL) << "Illegal use of connection without a db"; |
| 304 return 0; | 313 return 0; |
| 305 } | 314 } |
| 306 return sqlite3_last_insert_rowid(db_); | 315 return sqlite3_last_insert_rowid(db_); |
| 307 } | 316 } |
| 308 | 317 |
| 309 int Connection::GetLastChangeCount() const { | 318 int Connection::GetLastChangeCount() const { |
| 310 if (!db_) { | 319 if (!db_) { |
| 311 NOTREACHED(); | 320 DLOG(FATAL) << "Illegal use of connection without a db"; |
| 312 return 0; | 321 return 0; |
| 313 } | 322 } |
| 314 return sqlite3_changes(db_); | 323 return sqlite3_changes(db_); |
| 315 } | 324 } |
| 316 | 325 |
| 317 int Connection::GetErrorCode() const { | 326 int Connection::GetErrorCode() const { |
| 318 if (!db_) | 327 if (!db_) |
| 319 return SQLITE_ERROR; | 328 return SQLITE_ERROR; |
| 320 return sqlite3_errcode(db_); | 329 return sqlite3_errcode(db_); |
| 321 } | 330 } |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 332 } | 341 } |
| 333 | 342 |
| 334 const char* Connection::GetErrorMessage() const { | 343 const char* Connection::GetErrorMessage() const { |
| 335 if (!db_) | 344 if (!db_) |
| 336 return "sql::Connection has no connection."; | 345 return "sql::Connection has no connection."; |
| 337 return sqlite3_errmsg(db_); | 346 return sqlite3_errmsg(db_); |
| 338 } | 347 } |
| 339 | 348 |
| 340 bool Connection::OpenInternal(const std::string& file_name) { | 349 bool Connection::OpenInternal(const std::string& file_name) { |
| 341 if (db_) { | 350 if (db_) { |
| 342 NOTREACHED() << "sql::Connection is already open."; | 351 DLOG(FATAL) << "sql::Connection is already open."; |
| 343 return false; | 352 return false; |
| 344 } | 353 } |
| 345 | 354 |
| 346 int err = sqlite3_open(file_name.c_str(), &db_); | 355 int err = sqlite3_open(file_name.c_str(), &db_); |
| 347 if (err != SQLITE_OK) { | 356 if (err != SQLITE_OK) { |
| 348 OnSqliteError(err, NULL); | 357 OnSqliteError(err, NULL); |
| 349 db_ = NULL; | 358 db_ = NULL; |
| 350 return false; | 359 return false; |
| 351 } | 360 } |
| 352 | 361 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 363 // TODO(shess): This code is brittle. Find the cases where code | 372 // TODO(shess): This code is brittle. Find the cases where code |
| 364 // doesn't request |exclusive_locking_| and audit that it does the | 373 // doesn't request |exclusive_locking_| and audit that it does the |
| 365 // right thing with SQLITE_BUSY, and that it doesn't make | 374 // right thing with SQLITE_BUSY, and that it doesn't make |
| 366 // assumptions about who might change things in the database. | 375 // assumptions about who might change things in the database. |
| 367 // http://crbug.com/56559 | 376 // http://crbug.com/56559 |
| 368 if (exclusive_locking_) { | 377 if (exclusive_locking_) { |
| 369 // TODO(shess): This should probably be a full CHECK(). Code | 378 // TODO(shess): This should probably be a full CHECK(). Code |
| 370 // which requests exclusive locking but doesn't get it is almost | 379 // which requests exclusive locking but doesn't get it is almost |
| 371 // certain to be ill-tested. | 380 // certain to be ill-tested. |
| 372 if (!Execute("PRAGMA locking_mode=EXCLUSIVE")) | 381 if (!Execute("PRAGMA locking_mode=EXCLUSIVE")) |
| 373 NOTREACHED() << "Could not set locking mode: " << GetErrorMessage(); | 382 DLOG(FATAL) << "Could not set locking mode: " << GetErrorMessage(); |
| 374 } | 383 } |
| 375 | 384 |
| 376 const base::TimeDelta kBusyTimeout = | 385 const base::TimeDelta kBusyTimeout = |
| 377 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds); | 386 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds); |
| 378 | 387 |
| 379 if (page_size_ != 0) { | 388 if (page_size_ != 0) { |
| 380 // Enforce SQLite restrictions on |page_size_|. | 389 // Enforce SQLite restrictions on |page_size_|. |
| 381 DCHECK(!(page_size_ & (page_size_ - 1))) | 390 DCHECK(!(page_size_ & (page_size_ - 1))) |
| 382 << " page_size_ " << page_size_ << " is not a power of two."; | 391 << " page_size_ " << page_size_ << " is not a power of two."; |
| 383 static const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h | 392 static const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h |
| 384 DCHECK_LE(page_size_, kSqliteMaxPageSize); | 393 DCHECK_LE(page_size_, kSqliteMaxPageSize); |
| 385 const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_); | 394 const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_); |
| 386 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) | 395 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) |
| 387 NOTREACHED() << "Could not set page size: " << GetErrorMessage(); | 396 DLOG(FATAL) << "Could not set page size: " << GetErrorMessage(); |
| 388 } | 397 } |
| 389 | 398 |
| 390 if (cache_size_ != 0) { | 399 if (cache_size_ != 0) { |
| 391 const std::string sql = StringPrintf("PRAGMA cache_size=%d", cache_size_); | 400 const std::string sql = StringPrintf("PRAGMA cache_size=%d", cache_size_); |
| 392 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) | 401 if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout)) |
| 393 NOTREACHED() << "Could not set cache size: " << GetErrorMessage(); | 402 DLOG(FATAL) << "Could not set cache size: " << GetErrorMessage(); |
| 394 } | 403 } |
| 395 | 404 |
| 396 if (!ExecuteWithTimeout("PRAGMA secure_delete=ON", kBusyTimeout)) { | 405 if (!ExecuteWithTimeout("PRAGMA secure_delete=ON", kBusyTimeout)) { |
| 397 NOTREACHED() << "Could not enable secure_delete: " << GetErrorMessage(); | 406 DLOG(FATAL) << "Could not enable secure_delete: " << GetErrorMessage(); |
| 398 Close(); | 407 Close(); |
| 399 return false; | 408 return false; |
| 400 } | 409 } |
| 401 | 410 |
| 402 return true; | 411 return true; |
| 403 } | 412 } |
| 404 | 413 |
| 405 void Connection::DoRollback() { | 414 void Connection::DoRollback() { |
| 406 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); | 415 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); |
| 407 if (rollback) | 416 rollback.Run(); |
| 408 rollback.Run(); | |
| 409 } | 417 } |
| 410 | 418 |
| 411 void Connection::StatementRefCreated(StatementRef* ref) { | 419 void Connection::StatementRefCreated(StatementRef* ref) { |
| 412 DCHECK(open_statements_.find(ref) == open_statements_.end()); | 420 DCHECK(open_statements_.find(ref) == open_statements_.end()); |
| 413 open_statements_.insert(ref); | 421 open_statements_.insert(ref); |
| 414 } | 422 } |
| 415 | 423 |
| 416 void Connection::StatementRefDeleted(StatementRef* ref) { | 424 void Connection::StatementRefDeleted(StatementRef* ref) { |
| 417 StatementRefSet::iterator i = open_statements_.find(ref); | 425 StatementRefSet::iterator i = open_statements_.find(ref); |
| 418 if (i == open_statements_.end()) | 426 if (i == open_statements_.end()) |
| 419 NOTREACHED(); | 427 DLOG(FATAL) << "Could not find statement"; |
| 420 else | 428 else |
| 421 open_statements_.erase(i); | 429 open_statements_.erase(i); |
| 422 } | 430 } |
| 423 | 431 |
| 424 void Connection::ClearCache() { | 432 void Connection::ClearCache() { |
| 425 statement_cache_.clear(); | 433 statement_cache_.clear(); |
| 426 | 434 |
| 427 // The cache clear will get most statements. There may be still be references | 435 // The cache clear will get most statements. There may be still be references |
| 428 // to some statements that are held by others (including one-shot statements). | 436 // to some statements that are held by others (including one-shot statements). |
| 429 // This will deactivate them so they can't be used again. | 437 // This will deactivate them so they can't be used again. |
| 430 for (StatementRefSet::iterator i = open_statements_.begin(); | 438 for (StatementRefSet::iterator i = open_statements_.begin(); |
| 431 i != open_statements_.end(); ++i) | 439 i != open_statements_.end(); ++i) |
| 432 (*i)->Close(); | 440 (*i)->Close(); |
| 433 } | 441 } |
| 434 | 442 |
| 435 int Connection::OnSqliteError(int err, sql::Statement *stmt) { | 443 int Connection::OnSqliteError(int err, sql::Statement *stmt) { |
| 436 if (error_delegate_.get()) | 444 if (error_delegate_.get()) |
| 437 return error_delegate_->OnError(err, this, stmt); | 445 return error_delegate_->OnError(err, this, stmt); |
| 438 // The default handling is to assert on debug and to ignore on release. | 446 // The default handling is to assert on debug and to ignore on release. |
| 439 NOTREACHED() << GetErrorMessage(); | 447 DLOG(FATAL) << GetErrorMessage(); |
| 440 return err; | 448 return err; |
| 441 } | 449 } |
| 442 | 450 |
| 443 } // namespace sql | 451 } // namespace sql |
| OLD | NEW |