Index: sql/connection.cc |
diff --git a/sql/connection.cc b/sql/connection.cc |
index e99b6bc53dacb8f0ab2569d5d6eb01245446be93..3e85245d6f237bf4e85437140f8361c2e8365de5 100644 |
--- a/sql/connection.cc |
+++ b/sql/connection.cc |
@@ -64,6 +64,31 @@ class ScopedWritableSchema { |
sqlite3* db_; |
}; |
+// Helper to wrap the sqlite3_backup_*() step of Raze(). Return |
+// SQLite error code from running the backup step. |
+int BackupDatabase(sqlite3* src, sqlite3* dst, const char* db_name) { |
Greg Billock
2013/06/27 21:01:47
Docs say src and dst must be different. Want to DC
Scott Hess - ex-Googler
2013/06/27 22:41:47
Done.
|
+ sqlite3_backup* backup = sqlite3_backup_init(dst, db_name, src, db_name); |
+ if (!backup) { |
+ // Since this call only sets things up, this indicates a gross |
+ // error in SQLite. |
+ DLOG(FATAL) << "Unable to start sqlite3_backup()."; |
Greg Billock
2013/06/27 21:01:47
Print out dst->errmsg/errcode as well here?
Scott Hess - ex-Googler
2013/06/27 22:41:47
OK. Not entirely convinced it will be useful due
|
+ return SQLITE_ABORT; |
+ } |
+ |
+ // -1 backs up the entire database. |
+ int rc = sqlite3_backup_step(backup, -1); |
+ int pages = sqlite3_backup_pagecount(backup); |
Greg Billock
2013/06/27 21:01:47
Useful if rc != SQLITE_DONE? Also, need to deal wi
Scott Hess - ex-Googler
2013/06/27 22:41:47
Honestly, I am not sure what to do in those cases.
|
+ sqlite3_backup_finish(backup); |
+ |
+ // If successful, exactly one page should have been backed up. If |
+ // this breaks, check this function to make sure assumptions aren't |
+ // being broken. |
+ if (rc == SQLITE_DONE) |
+ DCHECK_EQ(pages, 1); |
+ |
+ return rc; |
+} |
+ |
} // namespace |
namespace sql { |
@@ -313,33 +338,51 @@ bool Connection::Raze() { |
// page_size" can be used to query such a database. |
ScopedWritableSchema writable_schema(db_); |
- sqlite3_backup* backup = sqlite3_backup_init(db_, "main", |
- null_db.db_, "main"); |
- if (!backup) { |
- DLOG(FATAL) << "Unable to start sqlite3_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); |
+ const char* kMain = "main"; |
+ int rc = BackupDatabase(null_db.db_, db_, kMain); |
// The destination database was locked. |
if (rc == SQLITE_BUSY) { |
return false; |
} |
+ // SQLITE_NOTADB can happen if page 1 exists but is not formatted |
Greg Billock
2013/06/27 21:01:47
So this is page 1 of the destination?
Scott Hess - ex-Googler
2013/06/27 22:41:47
Comment amended.
|
+ // correctly. SQLITE_IOERR_SHORT_READ can happen if the database |
+ // isn't even big enough for one page. Either way, reach in and |
+ // truncate it before trying again. |
Greg Billock
2013/06/27 21:01:47
Above it sounds like PRAGMA writeable_schema will
Scott Hess - ex-Googler
2013/06/27 22:41:47
That case covers when the database in the filesyst
Greg Billock
2013/06/28 14:29:09
So we see both these on corruption?
On 2013/06/27
|
+ // TODO(shess): Maybe it would be worthwhile to just truncate from |
+ // the get-go? |
+ if (rc == SQLITE_NOTADB || rc == SQLITE_IOERR_SHORT_READ) { |
Greg Billock
2013/06/27 21:01:47
Should this stanza go in the backup method?
Scott Hess - ex-Googler
2013/06/27 22:41:47
That's what the TODO is about. First it tries to
Greg Billock
2013/06/28 14:29:09
I see. The histogram ought to provide good guidanc
|
+ sqlite3_file* file = NULL; |
+ rc = sqlite3_file_control(db_, "main", SQLITE_FCNTL_FILE_POINTER, &file); |
+ if (rc != SQLITE_OK) { |
+ DLOG(FATAL) << "Failure getting file handle."; |
+ return false; |
+ } else if (!file) { |
+ DLOG(FATAL) << "File handle is empty."; |
+ return false; |
+ } |
+ |
+ rc = file->pMethods->xTruncate(file, 0); |
+ if (rc != SQLITE_OK) { |
+ DLOG(FATAL) << "Failed to truncate file."; |
+ return false; |
+ } |
+ |
+ rc = BackupDatabase(null_db.db_, db_, kMain); |
+ |
+ if (rc != SQLITE_DONE) { |
+ DLOG(FATAL) << "Failed retrying Raze()."; |
+ } |
+ } |
+ |
// The entire database should have been backed up. |
if (rc != SQLITE_DONE) { |
+ // TODO(shess): Figure out which other cases can happen. |
DLOG(FATAL) << "Unable to copy entire null database."; |
return false; |
} |
- // Exactly one page should have been backed up. If this breaks, |
- // check this function to make sure assumptions aren't being broken. |
- DCHECK_EQ(pages, 1); |
- |
return true; |
} |
@@ -663,6 +706,7 @@ bool Connection::OpenInternal(const std::string& file_name) { |
// TODO(shess): Revise is_open() to consider poisoned_, and review |
// to see if any non-testing code even depends on it. |
DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open."; |
+ poisoned_ = false; |
int err = sqlite3_open(file_name.c_str(), &db_); |
if (err != SQLITE_OK) { |