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

Unified Diff: sql/recovery.cc

Issue 18180013: Scoped recovery module for sql/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Oops, add recover_unittest.cc 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 side-by-side diff with in-line comments
Download patch
« sql/recovery.h ('K') | « sql/recovery.h ('k') | sql/scoped_attach.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sql/recovery.cc
diff --git a/sql/recovery.cc b/sql/recovery.cc
new file mode 100644
index 0000000000000000000000000000000000000000..32c49b7dda915c0adeab178b629cb3612097bd8b
--- /dev/null
+++ b/sql/recovery.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sql/recovery.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "sql/connection.h"
+#include "third_party/sqlite/sqlite3.h"
+
+namespace sql {
+
+Recovery::Recovery(Connection* connection)
+ : db_(connection),
+ recover_db_(),
+ attach_(&recover_db_) {
+ // TODO(shess): Consider only applying this for small databases.
+
+ // Result should keep the page size specified earlier.
+ if (db_->page_size_)
+ recover_db_.set_page_size(db_->page_size_);
+}
+
+Recovery::~Recovery() {
+ if (db_)
+ Unrecoverable();
+}
+
+bool Recovery::Open(const base::FilePath& db_path) {
+ // Prevent re-entrancy if some call to db_ happens to fail.
+ // TODO(shess): Should this be scoped? Should the caller manage it?
+ db_->reset_error_callback();
+
+ if (!recover_db_.OpenTemporary())
+ return false;
+
+ // Turn on |SQLITE_RecoveryMode|, which allows reading certain
+ // broken databases.
+ // TODO(shess): Double-check that this works for attached dbs.
+ if (!recover_db_.Execute("PRAGMA writable_schema=1")) {
+ recover_db_.Close();
+ return false;
+ }
+
+ // Break any outstanding transactions on original database to
+ // prevent deadlocks reading through the attached version.
+ // TODO(shess): A client may legitimately wish to recover from
+ // within the transaction context, because it would potentially
+ // preserve the in-process changes. Unfortunately, any attach-based
+ // system could not handle that. A system which manually queried
+ // one database and stored to the other could.
+ db_->RollbackAllTransactions();
+
+ if (!attach_.Attach(db_path, "corrupt")) {
+ recover_db_.Close();
+ return false;
+ }
+
+ return true;
+}
+
+void Recovery::Unrecoverable() {
+ CHECK(db_);
+ attach_.Detach();
+ recover_db_.Close();
+ db_->RazeAndClose();
+ db_ = NULL;
+}
+
+bool Recovery::Recovered() {
+ CHECK(db_);
+ CHECK(recover_db_.is_open());
+
+ sqlite3_backup* backup = sqlite3_backup_init(db_->db_, "main",
+ recover_db_.db_, "main");
+ if (!backup) {
+ LOG(ERROR) << "Failed to make backup?";
+ return false;
+ }
+
+ // -1 backs up the entire database.
+ int rc = sqlite3_backup_step(backup, -1);
+ int pages = sqlite3_backup_pagecount(backup);
+ sqlite3_backup_finish(backup);
+ DCHECK_GT(pages, 0);
+
+ // The destination database was locked.
+ if (rc == SQLITE_BUSY) {
+ // TODO(shess): Ignore it? What if it's ALWAYS locked? If it's
+ // locked, it probably can't be deleted, either, and Raze() will
+ // fail the same way.
+ attach_.Detach();
+ recover_db_.Close();
+ db_->CloseAndPoison();
+ db_ = NULL;
+ return false;
+ }
+
+ // It is unclear what errors are possible at this point, and how to
+ // handle them. Some could perhaps be resolved with a future retry,
+ // but it is unclear how that could possibly work.
+ // TODO(shess): I wonder about try/sleep/try :-).
+ if (rc != SQLITE_DONE) {
+ attach_.Detach();
+ recover_db_.Close();
+ db_->RazeAndClose();
+ db_ = NULL;
+ return false;
+ }
+
+ // Clean up the recovery db, and terminate the main database
+ // connection.
+ attach_.Detach();
+ recover_db_.Close();
+ db_->CloseAndPoison();
+ db_ = NULL;
+ return true;
+}
+
+} // namespace sql
« sql/recovery.h ('K') | « sql/recovery.h ('k') | sql/scoped_attach.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698