1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "sql/recovery.h" 6 7 #include "base/files/file_path.h" 8 #include "base/format_macros.h" 9 #include "base/logging.h" 10 #include "base/metrics/histogram.h" 11 #include "base/metrics/sparse_histogram.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/stringprintf.h" 14 #include "sql/connection.h" 15 #include "sql/statement.h" 16 #include "third_party/sqlite/sqlite3.h" 17 18 namespace sql { 19 20 namespace { 21 22 enum RecoveryEventType { 23 // Init() completed successfully. 24 RECOVERY_SUCCESS_INIT = 0, 25 26 // Failed to open temporary database to recover into. 27 RECOVERY_FAILED_OPEN_TEMPORARY, 28 29 // Failed to initialize recover vtable system. 30 RECOVERY_FAILED_VIRTUAL_TABLE_INIT, 31 32 // System SQLite doesn't support vtable. 33 RECOVERY_FAILED_VIRTUAL_TABLE_SYSTEM_SQLITE, 34 35 // Failed attempting to enable writable_schema. 36 RECOVERY_FAILED_WRITABLE_SCHEMA, 37 38 // Failed to attach the corrupt database to the temporary database. 39 RECOVERY_FAILED_ATTACH, 40 41 // Backup() successfully completed. 42 RECOVERY_SUCCESS_BACKUP, 43 44 // Failed sqlite3_backup_init(). Error code in Sqlite.RecoveryHandle. 45 RECOVERY_FAILED_BACKUP_INIT, 46 47 // Failed sqlite3_backup_step(). Error code in Sqlite.RecoveryStep. 48 RECOVERY_FAILED_BACKUP_STEP, 49 50 // AutoRecoverTable() successfully completed. 51 RECOVERY_SUCCESS_AUTORECOVER, 52 53 // The target table contained a type which the code is not equipped 54 // to handle. This should only happen if things are fubar. 55 RECOVERY_FAILED_AUTORECOVER_UNRECOGNIZED_TYPE, 56 57 // The target table does not exist. 58 RECOVERY_FAILED_AUTORECOVER_MISSING_TABLE, 59 60 // The recovery virtual table creation failed. 61 RECOVERY_FAILED_AUTORECOVER_CREATE, 62 63 // Copying data from the recovery table to the target table failed. 64 RECOVERY_FAILED_AUTORECOVER_INSERT, 65 66 // Dropping the recovery virtual table failed. 67 RECOVERY_FAILED_AUTORECOVER_DROP, 68 69 // SetupMeta() successfully completed. 70 RECOVERY_SUCCESS_SETUP_META, 71 72 // Failure creating recovery meta table. 73 RECOVERY_FAILED_META_CREATE, 74 75 // GetMetaVersionNumber() successfully completed. 76 RECOVERY_SUCCESS_META_VERSION, 77 78 // Failed in querying recovery meta table. 79 RECOVERY_FAILED_META_QUERY, 80 81 // No version key in recovery meta table. 82 RECOVERY_FAILED_META_NO_VERSION, 83 84 // Always keep this at the end. 85 RECOVERY_EVENT_MAX, 86 }; 87 88 void RecordRecoveryEvent(RecoveryEventType recovery_event) { 89 UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents", 90 recovery_event, RECOVERY_EVENT_MAX); 91 } 92 93 } // namespace 94 95 // static 96 bool Recovery::FullRecoverySupported() { 97 // TODO(shess): See comment in Init(). 98 #if defined(USE_SYSTEM_SQLITE) 99 return false; 100 #else 101 return true; 102 #endif 103 } 104 105 // static 106 scoped_ptr<Recovery> Recovery::Begin( 107 Connection* connection, 108 const base::FilePath& db_path) { 109 scoped_ptr<Recovery> r(new Recovery(connection)); 110 if (!r->Init(db_path)) { 111 // TODO(shess): Should Init() failure result in Raze()? 112 r->Shutdown(POISON); 113 return scoped_ptr<Recovery>(); 114 } 115 116 return r.Pass(); 117 } 118 119 // static 120 bool Recovery::Recovered(scoped_ptr<Recovery> r) { 121 return r->Backup(); 122 } 123 124 // static 125 void Recovery::Unrecoverable(scoped_ptr<Recovery> r) { 126 CHECK(r->db_); 127 // ~Recovery() will RAZE_AND_POISON. 128 } 129 130 // static 131 void Recovery::Rollback(scoped_ptr<Recovery> r) { 132 // TODO(shess): HISTOGRAM to track? Or just have people crash out? 133 // Crash and dump? 134 r->Shutdown(POISON); 135 } 136 137 Recovery::Recovery(Connection* connection) 138 : db_(connection), 139 recover_db_() { 140 // Result should keep the page size specified earlier. 141 if (db_->page_size_) 142 recover_db_.set_page_size(db_->page_size_); 143 144 // TODO(shess): This may not handle cases where the default page 145 // size is used, but the default has changed. I do not think this 146 // has ever happened. This could be handled by using "PRAGMA 147 // page_size", at the cost of potential additional failure cases. 148 } 149 150 Recovery::~Recovery() { 151 Shutdown(RAZE_AND_POISON); 152 } 153 154 bool Recovery::Init(const base::FilePath& db_path) { 155 // Prevent the possibility of re-entering this code due to errors 156 // which happen while executing this code. 157 DCHECK(!db_->has_error_callback()); 158 159 // Break any outstanding transactions on the original database to 160 // prevent deadlocks reading through the attached version. 161 // TODO(shess): A client may legitimately wish to recover from 162 // within the transaction context, because it would potentially 163 // preserve any in-flight changes. Unfortunately, any attach-based 164 // system could not handle that. A system which manually queried 165 // one database and stored to the other possibly could, but would be 166 // more complicated. 167 db_->RollbackAllTransactions(); 168 169 // Disable exclusive locking mode so that the attached database can 170 // access things. The locking_mode change is not active until the 171 // next database access, so immediately force an access. Enabling 172 // writable_schema allows processing through certain kinds of 173 // corruption. 174 // TODO(shess): It would be better to just close the handle, but it 175 // is necessary for the final backup which rewrites things. It 176 // might be reasonable to close then re-open the handle. 177 ignore_result(db_->Execute("PRAGMA writable_schema=1")); 178 ignore_result(db_->Execute("PRAGMA locking_mode=NORMAL")); 179 ignore_result(db_->Execute("SELECT COUNT(*) FROM sqlite_master")); 180 181 // TODO(shess): If this is a common failure case, it might be 182 // possible to fall back to a memory database. But it probably 183 // implies that the SQLite tmpdir logic is busted, which could cause 184 // a variety of other random issues in our code. 185 if (!recover_db_.OpenTemporary()) { 186 RecordRecoveryEvent(RECOVERY_FAILED_OPEN_TEMPORARY); 187 return false; 188 } 189 190 // TODO(shess): Figure out a story for USE_SYSTEM_SQLITE. The 191 // virtual table implementation relies on SQLite internals for some 192 // types and functions, which could be copied inline to make it 193 // standalone. Or an alternate implementation could try to read 194 // through errors entirely at the SQLite level. 195 // 196 // For now, defer to the caller. The setup will succeed, but the 197 // later CREATE VIRTUAL TABLE call will fail, at which point the 198 // caller can fire Unrecoverable(). 199 #if !defined(USE_SYSTEM_SQLITE) 200 int rc = recoverVtableInit(recover_db_.db_); 201 if (rc != SQLITE_OK) { 202 RecordRecoveryEvent(RECOVERY_FAILED_VIRTUAL_TABLE_INIT); 203 LOG(ERROR) << "Failed to initialize recover module: " 204 << recover_db_.GetErrorMessage(); 205 return false; 206 } 207 #else 208 // If this is infrequent enough, just wire it to Raze(). 209 RecordRecoveryEvent(RECOVERY_FAILED_VIRTUAL_TABLE_SYSTEM_SQLITE); 210 #endif 211 212 // Turn on |SQLITE_RecoveryMode| for the handle, which allows 213 // reading certain broken databases. 214 if (!recover_db_.Execute("PRAGMA writable_schema=1")) { 215 RecordRecoveryEvent(RECOVERY_FAILED_WRITABLE_SCHEMA); 216 return false; 217 } 218 219 if (!recover_db_.AttachDatabase(db_path, "corrupt")) { 220 RecordRecoveryEvent(RECOVERY_FAILED_ATTACH); 221 return false; 222 } 223 224 RecordRecoveryEvent(RECOVERY_SUCCESS_INIT); 225 return true; 226 } 227 228 bool Recovery::Backup() { 229 CHECK(db_); 230 CHECK(recover_db_.is_open()); 231 232 // TODO(shess): Some of the failure cases here may need further 233 // exploration. Just as elsewhere, persistent problems probably 234 // need to be razed, while anything which might succeed on a future 235 // run probably should be allowed to try. But since Raze() uses the 236 // same approach, even that wouldn't work when this code fails. 237 // 238 // The documentation for the backup system indicate a relatively 239 // small number of errors are expected: 240 // SQLITE_BUSY - cannot lock the destination database. This should 241 // only happen if someone has another handle to the 242 // database, Chromium generally doesn't do that. 243 // SQLITE_LOCKED - someone locked the source database. Should be 244 // impossible (perhaps anti-virus could?). 245 // SQLITE_READONLY - destination is read-only. 246 // SQLITE_IOERR - since source database is temporary, probably 247 // indicates that the destination contains blocks 248 // throwing errors, or gross filesystem errors. 249 // SQLITE_NOMEM - out of memory, should be transient. 250 // 251 // AFAICT, SQLITE_BUSY and SQLITE_NOMEM could perhaps be considered 252 // transient, with SQLITE_LOCKED being unclear. 253 // 254 // SQLITE_READONLY and SQLITE_IOERR are probably persistent, with a 255 // strong chance that Raze() would not resolve them. If Delete() 256 // deletes the database file, the code could then re-open the file 257 // and attempt the backup again. 258 // 259 // For now, this code attempts a best effort and records histograms 260 // to inform future development. 261 262 // Backup the original db from the recovered db. 263 const char* kMain = "main"; 264 sqlite3_backup* backup = sqlite3_backup_init(db_->db_, kMain, 265 recover_db_.db_, kMain); 266 if (!backup) { 267 RecordRecoveryEvent(RECOVERY_FAILED_BACKUP_INIT); 268 269 // Error code is in the destination database handle. 270 int err = sqlite3_extended_errcode(db_->db_); 271 UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.RecoveryHandle", err); 272 LOG(ERROR) << "sqlite3_backup_init() failed: " 273 << sqlite3_errmsg(db_->db_); 274 275 return false; 276 } 277 278 // -1 backs up the entire database. 279 int rc = sqlite3_backup_step(backup, -1); 280 int pages = sqlite3_backup_pagecount(backup); 281 // TODO(shess): sqlite3_backup_finish() appears to allow returning a 282 // different value from sqlite3_backup_step(). Circle back and 283 // figure out if that can usefully inform the decision of whether to 284 // retry or not. 285 sqlite3_backup_finish(backup); 286 DCHECK_GT(pages, 0); 287 288 if (rc != SQLITE_DONE) { 289 RecordRecoveryEvent(RECOVERY_FAILED_BACKUP_STEP); 290 UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.RecoveryStep", rc); 291 LOG(ERROR) << "sqlite3_backup_step() failed: " 292 << sqlite3_errmsg(db_->db_); 293 } 294 295 // The destination database was locked. Give up, but leave the data 296 // in place. Maybe it won't be locked next time. 297 if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) { 298 Shutdown(POISON); 299 return false; 300 } 301 302 // Running out of memory should be transient, retry later. 303 if (rc == SQLITE_NOMEM) { 304 Shutdown(POISON); 305 return false; 306 } 307 308 // TODO(shess): For now, leave the original database alone, pending 309 // results from Sqlite.RecoveryStep. Some errors should probably 310 // route to RAZE_AND_POISON. 311 if (rc != SQLITE_DONE) { 312 Shutdown(POISON); 313 return false; 314 } 315 316 // Clean up the recovery db, and terminate the main database 317 // connection. 318 RecordRecoveryEvent(RECOVERY_SUCCESS_BACKUP); 319 Shutdown(POISON); 320 return true; 321 } 322 323 void Recovery::Shutdown(Recovery::Disposition raze) { 324 if (!db_) 325 return; 326 327 recover_db_.Close(); 328 if (raze == RAZE_AND_POISON) { 329 db_->RazeAndClose(); 330 } else if (raze == POISON) { 331 db_->Poison(); 332 } 333 db_ = NULL; 334 } 335 336 bool Recovery::AutoRecoverTable(const char* table_name, 337 size_t extend_columns, 338 size_t* rows_recovered) { 339 // Query the info for the recovered table in database [main]. 340 std::string query( 341 base::StringPrintf("PRAGMA main.table_info(%s)", table_name)); 342 Statement s(db()->GetUniqueStatement(query.c_str())); 343 344 // The columns of the recover virtual table. 345 std::vector<std::string> create_column_decls; 346 347 // The columns to select from the recover virtual table when copying 348 // to the recovered table. 349 std::vector<std::string> insert_columns; 350 351 // If PRIMARY KEY is a single INTEGER column, then it is an alias 352 // for ROWID. The primary key can be compound, so this can only be 353 // determined after processing all column data and tracking what is 354 // seen. |pk_column_count| counts the columns in the primary key. 355 // |rowid_decl| stores the ROWID version of the last INTEGER column 356 // seen, which is at |rowid_ofs| in |create_column_decls|. 357 size_t pk_column_count = 0; 358 size_t rowid_ofs = 0; // Only valid if rowid_decl is set. 359 std::string rowid_decl; // ROWID version of column |rowid_ofs|. 360 361 while (s.Step()) { 362 const std::string column_name(s.ColumnString(1)); 363 const std::string column_type(s.ColumnString(2)); 364 const bool not_null = s.ColumnBool(3); 365 const int default_type = s.ColumnType(4); 366 const bool default_is_null = (default_type == COLUMN_TYPE_NULL); 367 const int pk_column = s.ColumnInt(5); 368 369 if (pk_column > 0) { 370 // TODO(shess): http://www.sqlite.org/pragma.html#pragma_table_info 371 // documents column 5 as the index of the column in the primary key 372 // (zero for not in primary key). I find that it is always 1 for 373 // columns in the primary key. Since this code is very dependent on 374 // that pragma, review if the implementation changes. 375 DCHECK_EQ(1, pk_column); 376 ++pk_column_count; 377 } 378 379 // Construct column declaration as "name type [optional constraint]". 380 std::string column_decl = column_name; 381 382 // SQLite's affinity detection is documented at: 383 // http://www.sqlite.org/datatype3.html#affname 384 // The gist of it is that CHAR, TEXT, and INT use substring matches. 385 // TODO(shess): It would be nice to unit test the type handling, 386 // but it is not obvious to me how to write a test which would 387 // fail appropriately when something was broken. It would have to 388 // somehow use data which would allow detecting the various type 389 // coercions which happen. If STRICT could be enabled, type 390 // mismatches could be detected by which rows are filtered. 391 if (column_type.find("INT") != std::string::npos) { 392 if (pk_column == 1) { 393 rowid_ofs = create_column_decls.size(); 394 rowid_decl = column_name + " ROWID"; 395 } 396 column_decl += " INTEGER"; 397 } else if (column_type.find("CHAR") != std::string::npos || 398 column_type.find("TEXT") != std::string::npos) { 399 column_decl += " TEXT"; 400 } else if (column_type == "BLOB") { 401 column_decl += " BLOB"; 402 } else if (column_type.find("DOUB") != std::string::npos) { 403 column_decl += " FLOAT"; 404 } else { 405 // TODO(shess): AFAICT, there remain: 406 // - contains("CLOB") -> TEXT 407 // - contains("REAL") -> FLOAT 408 // - contains("FLOA") -> FLOAT 409 // - other -> "NUMERIC" 410 // Just code those in as they come up. 411 NOTREACHED() << " Unsupported type " << column_type; 412 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_UNRECOGNIZED_TYPE); 413 return false; 414 } 415 416 // If column has constraint "NOT NULL", then inserting NULL into 417 // that column will fail. If the column has a non-NULL DEFAULT 418 // specified, the INSERT will handle it (see below). If the 419 // DEFAULT is also NULL, the row must be filtered out. 420 // TODO(shess): The above scenario applies to INSERT OR REPLACE, 421 // whereas INSERT OR IGNORE drops such rows. 422 // http://www.sqlite.org/lang_conflict.html 423 if (not_null && default_is_null) 424 column_decl += " NOT NULL"; 425 426 create_column_decls.push_back(column_decl); 427 428 // Per the NOTE in the header file, convert NULL values to the 429 // DEFAULT. All columns could be IFNULL(column_name,default), but 430 // the NULL case would require special handling either way. 431 if (default_is_null) { 432 insert_columns.push_back(column_name); 433 } else { 434 // The default value appears to be pre-quoted, as if it is 435 // literally from the sqlite_master CREATE statement. 436 std::string default_value = s.ColumnString(4); 437 insert_columns.push_back(base::StringPrintf( 438 "IFNULL(%s,%s)", column_name.c_str(), default_value.c_str())); 439 } 440 } 441 442 // Receiving no column information implies that the table doesn't exist. 443 if (create_column_decls.empty()) { 444 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_MISSING_TABLE); 445 return false; 446 } 447 448 // If the PRIMARY KEY was a single INTEGER column, convert it to ROWID. 449 if (pk_column_count == 1 && !rowid_decl.empty()) 450 create_column_decls[rowid_ofs] = rowid_decl; 451 452 // Additional columns accept anything. 453 // TODO(shess): ignoreN isn't well namespaced. But it will fail to 454 // execute in case of conflicts. 455 for (size_t i = 0; i < extend_columns; ++i) { 456 create_column_decls.push_back( 457 base::StringPrintf("ignore%" PRIuS " ANY", i)); 458 } 459 460 std::string recover_create(base::StringPrintf( 461 "CREATE VIRTUAL TABLE temp.recover_%s USING recover(corrupt.%s, %s)", 462 table_name, 463 table_name, 464 JoinString(create_column_decls, ',').c_str())); 465 466 std::string recover_insert(base::StringPrintf( 467 "INSERT OR REPLACE INTO main.%s SELECT %s FROM temp.recover_%s", 468 table_name, 469 JoinString(insert_columns, ',').c_str(), 470 table_name)); 471 472 std::string recover_drop(base::StringPrintf( 473 "DROP TABLE temp.recover_%s", table_name)); 474 475 if (!db()->Execute(recover_create.c_str())) { 476 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_CREATE); 477 return false; 478 } 479 480 if (!db()->Execute(recover_insert.c_str())) { 481 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_INSERT); 482 ignore_result(db()->Execute(recover_drop.c_str())); 483 return false; 484 } 485 486 *rows_recovered = db()->GetLastChangeCount(); 487 488 // TODO(shess): Is leaving the recover table around a breaker? 489 if (!db()->Execute(recover_drop.c_str())) { 490 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_DROP); 491 return false; 492 } 493 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVER); 494 return true; 495 } 496 497 bool Recovery::SetupMeta() { 498 const char kCreateSql[] = 499 "CREATE VIRTUAL TABLE temp.recover_meta USING recover" 500 "(" 501 "corrupt.meta," 502 "key TEXT NOT NULL," 503 "value ANY" // Whatever is stored. 504 ")"; 505 if (!db()->Execute(kCreateSql)) { 506 RecordRecoveryEvent(RECOVERY_FAILED_META_CREATE); 507 return false; 508 } 509 RecordRecoveryEvent(RECOVERY_SUCCESS_SETUP_META); 510 return true; 511 } 512 513 bool Recovery::GetMetaVersionNumber(int* version) { 514 DCHECK(version); 515 // TODO(shess): DCHECK(db()->DoesTableExist("temp.recover_meta")); 516 // Unfortunately, DoesTableExist() queries sqlite_master, not 517 // sqlite_temp_master. 518 519 const char kVersionSql[] = 520 "SELECT value FROM temp.recover_meta WHERE key = 'version'"; 521 sql::Statement recovery_version(db()->GetUniqueStatement(kVersionSql)); 522 if (!recovery_version.Step()) { 523 if (!recovery_version.Succeeded()) { 524 RecordRecoveryEvent(RECOVERY_FAILED_META_QUERY); 525 } else { 526 RecordRecoveryEvent(RECOVERY_FAILED_META_NO_VERSION); 527 } 528 return false; 529 } 530 531 RecordRecoveryEvent(RECOVERY_SUCCESS_META_VERSION); 532 *version = recovery_version.ColumnInt(0); 533 return true; 534 } 535 536 } // namespace sql 537