[sql] Scoped recovery framework.
sql::Recovery is intended to be used within a sql::Connection error
callback to either recover the database (*) or indicate that the
database is unrecoverable and should be razed. The intend is that the
code should either progress to a valid database which is composed of
data recovered from the original (likely corrupt) database, or a valid
database which is empty.
This is just the framework without the SQLite-level data-recovery
virtual table.
BUG=109482
Review URL: https://chromiumcodereview.appspot.com/19281002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@212607 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/sql/connection.cc b/sql/connection.cc
index e7b6fe1..4550b0e5 100644
--- a/sql/connection.cc
+++ b/sql/connection.cc
@@ -94,6 +94,21 @@
return rc;
}
+// Be very strict on attachment point. SQLite can handle a much wider
+// character set with appropriate quoting, but Chromium code should
+// just use clean names to start with.
+bool ValidAttachmentPoint(const char* attachment_point) {
+ for (size_t i = 0; attachment_point[i]; ++i) {
+ if (!((attachment_point[i] >= '0' && attachment_point[i] <= '9') ||
+ (attachment_point[i] >= 'a' && attachment_point[i] <= 'z') ||
+ (attachment_point[i] >= 'A' && attachment_point[i] <= 'Z') ||
+ attachment_point[i] == '_')) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace
namespace sql {
@@ -206,6 +221,10 @@
return OpenInternal(":memory:", NO_RETRY);
}
+bool Connection::OpenTemporary() {
+ return OpenInternal("", NO_RETRY);
+}
+
void Connection::CloseInternal(bool forced) {
// TODO(shess): Calling "PRAGMA journal_mode = DELETE" at this point
// will delete the -journal file. For ChromiumOS or other more
@@ -442,9 +461,7 @@
}
// Raze() cannot run in a transaction.
- while (transaction_nesting_) {
- RollbackTransaction();
- }
+ RollbackAllTransactions();
bool result = Raze();
@@ -458,6 +475,21 @@
return result;
}
+void Connection::Poison() {
+ if (!db_) {
+ DLOG_IF(FATAL, !poisoned_) << "Cannot poison null db";
+ return;
+ }
+
+ RollbackAllTransactions();
+ CloseInternal(true);
+
+ // Mark the database so that future API calls fail appropriately,
+ // but don't DCHECK (because after calling this function they are
+ // expected to fail).
+ poisoned_ = true;
+}
+
// TODO(shess): To the extent possible, figure out the optimal
// ordering for these deletes which will prevent other connections
// from seeing odd behavior. For instance, it may be necessary to
@@ -543,6 +575,35 @@
return commit.Run();
}
+void Connection::RollbackAllTransactions() {
+ if (transaction_nesting_ > 0) {
+ transaction_nesting_ = 0;
+ DoRollback();
+ }
+}
+
+bool Connection::AttachDatabase(const base::FilePath& other_db_path,
+ const char* attachment_point) {
+ DCHECK(ValidAttachmentPoint(attachment_point));
+
+ Statement s(GetUniqueStatement("ATTACH DATABASE ? AS ?"));
+#if OS_WIN
+ s.BindString16(0, other_db_path.value());
+#else
+ s.BindString(0, other_db_path.value());
+#endif
+ s.BindString(1, attachment_point);
+ return s.Run();
+}
+
+bool Connection::DetachDatabase(const char* attachment_point) {
+ DCHECK(ValidAttachmentPoint(attachment_point));
+
+ Statement s(GetUniqueStatement("DETACH DATABASE ?"));
+ s.BindString(0, attachment_point);
+ return s.Run();
+}
+
int Connection::ExecuteAndReturnErrorCode(const char* sql) {
AssertIOAllowed();
if (!db_) {