Avi Drissman | 69b874f | 2022-09-15 19:11:14 | [diff] [blame] | 1 | // Copyright 2013 The Chromium Authors |
shess@chromium.org | 8d40941 | 2013-07-19 18:25:30 | [diff] [blame] | 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 | |
avi | 0b51920 | 2015-12-21 07:25:19 | [diff] [blame] | 7 | #include<stddef.h> |
| 8 | |
Victor Costan | 90dae26 | 2021-06-01 21:01:08 | [diff] [blame] | 9 | #include<string> |
Avi Drissman | 7012747 | 2022-01-08 23:20:23 | [diff] [blame] | 10 | #include<tuple> |
Victor Costan | 90dae26 | 2021-06-01 21:01:08 | [diff] [blame] | 11 | |
Takuto Ikuta | 2eb6134 | 2024-05-10 09:05:35 | [diff] [blame] | 12 | #include"base/check.h" |
Victor Costan | 90dae26 | 2021-06-01 21:01:08 | [diff] [blame] | 13 | #include"base/check_op.h" |
shess@chromium.org | 8d40941 | 2013-07-19 18:25:30 | [diff] [blame] | 14 | #include"base/logging.h" |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 15 | #include"base/metrics/histogram_functions.h" |
Victor Costan | 90dae26 | 2021-06-01 21:01:08 | [diff] [blame] | 16 | #include"base/notreached.h" |
Austin Sullivan | 7b54340 | 2023-10-07 00:55:09 | [diff] [blame] | 17 | #include"base/strings/strcat.h" |
Austin Sullivan | 265c201b | 2023-05-31 02:04:26 | [diff] [blame] | 18 | #include"build/build_config.h" |
Victor Costan | cfbfa60 | 2018-08-01 23:24:46 | [diff] [blame] | 19 | #include"sql/database.h" |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 20 | #include"sql/error_delegate_util.h" |
| 21 | #include"sql/internal_api_token.h" |
| 22 | #include"sql/meta_table.h" |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 23 | #include"sql/sqlite_result_code.h" |
shess@chromium.org | 8d40941 | 2013-07-19 18:25:30 | [diff] [blame] | 24 | #include"third_party/sqlite/sqlite3.h" |
Victor Costan | 538c1431 | 2019-03-25 19:13:17 | [diff] [blame] | 25 | |
shess@chromium.org | 8d40941 | 2013-07-19 18:25:30 | [diff] [blame] | 26 | namespace sql{ |
| 27 | |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 28 | namespace{ |
| 29 | |
| 30 | constexprchar kMainDatabaseName[]="main"; |
| 31 | |
| 32 | }// namespace |
| 33 | |
| 34 | // static |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 35 | boolRecovery::ShouldAttemptRecovery(Database* database,int extended_error){ |
Evan Stade | b3be637 | 2024-02-27 21:11:09 | [diff] [blame] | 36 | return database&& database->is_open()&& |
Austin Sullivan | 18fdb6c9 | 2023-05-18 20:55:49 | [diff] [blame] | 37 | !database->DbPath(InternalApiToken()).empty()&& |
Evan Stade | 2f6256b70d | 2024-01-26 14:58:55 | [diff] [blame] | 38 | #if BUILDFLAG(IS_FUCHSIA) |
| 39 | // Recovering WAL databases is not supported on Fuchsia. |
| 40 | !database->UseWALMode()&& |
| 41 | #endif// BUILDFLAG(IS_FUCHSIA) |
Austin Sullivan | 18fdb6c9 | 2023-05-18 20:55:49 | [diff] [blame] | 42 | IsErrorCatastrophic(extended_error); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 43 | } |
| 44 | |
| 45 | // static |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 46 | SqliteResultCodeRecovery::RecoverDatabase(Database* database, |
| 47 | Strategy strategy){ |
| 48 | auto recovery=Recovery(database, strategy); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 49 | return recovery.RecoverAndReplaceDatabase(); |
| 50 | } |
| 51 | |
Austin Sullivan | 265c201b | 2023-05-31 02:04:26 | [diff] [blame] | 52 | // static |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 53 | boolRecovery::RecoverIfPossible(Database* database, |
| 54 | int extended_error, |
| 55 | Strategy strategy){ |
Evan Stade | b3be637 | 2024-02-27 21:11:09 | [diff] [blame] | 56 | if(!ShouldAttemptRecovery(database, extended_error)){ |
Austin Sullivan | 265c201b | 2023-05-31 02:04:26 | [diff] [blame] | 57 | returnfalse; |
| 58 | } |
| 59 | |
| 60 | // Recovery should be attempted. Since recovery must only be attempted from |
| 61 | // within a database error callback, reset the error callback to prevent |
| 62 | // re-entry. |
| 63 | database->reset_error_callback(); |
| 64 | |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 65 | auto result=Recovery::RecoverDatabase(database, strategy); |
Evan Stade | b3be637 | 2024-02-27 21:11:09 | [diff] [blame] | 66 | if(!IsSqliteSuccessCode(result)){ |
| 67 | DLOG(ERROR)<<"Database recovery failed with result code: "<< result; |
Austin Sullivan | 265c201b | 2023-05-31 02:04:26 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | returntrue; |
| 71 | } |
| 72 | |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 73 | Recovery::Recovery(Database* database,Strategy strategy) |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 74 | : strategy_(strategy), |
| 75 | db_(database), |
Anthony Vallée-Dubois | e3c9491 | 2024-12-12 16:47:47 | [diff] [blame] | 76 | recover_db_( |
Anthony Vallée-Dubois | 9adf0bb | 2025-02-03 17:01:41 | [diff] [blame] | 77 | DatabaseOptions().set_page_size(database? database->page_size():0), |
Anthony Vallée-Dubois | e3c9491 | 2024-12-12 16:47:47 | [diff] [blame] | 78 | "Recovery"){ |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 79 | CHECK(db_); |
| 80 | CHECK(db_->is_open()); |
Austin Sullivan | 2fbe496e | 2023-05-18 19:48:52 | [diff] [blame] | 81 | // Recovery is likely to be used in error handling. To prevent re-entry due to |
| 82 | // errors while attempting to recover the database, the error callback must |
| 83 | // not be set. |
| 84 | CHECK(!db_->has_error_callback()); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 85 | |
| 86 | auto db_path= db_->DbPath(InternalApiToken()); |
| 87 | |
| 88 | // Corruption recovery for in-memory databases is not supported. |
| 89 | CHECK(!db_path.empty()); |
| 90 | |
Austin Sullivan | db911aa | 2023-12-21 13:44:29 | [diff] [blame] | 91 | // Cache the database's histogram tag while the database is open. |
| 92 | database_uma_name_= db_->histogram_tag(); |
| 93 | |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 94 | recovery_database_path_= db_path.AddExtensionASCII(".recovery"); |
| 95 | |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 96 | // Break any outstanding transactions on the original database, since the |
| 97 | // recovery module opens a transaction on the database while recovery is in |
| 98 | // progress. |
| 99 | db_->RollbackAllTransactions(); |
| 100 | } |
| 101 | |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 102 | Recovery::~Recovery(){ |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 103 | // Recovery result must be set before we reach this point. |
| 104 | CHECK_NE(result_,Result::kUnknown); |
| 105 | |
| 106 | base::UmaHistogramEnumeration("Sql.Recovery.Result", result_); |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 107 | UmaHistogramSqliteResult("Sql.Recovery.ResultCode", |
| 108 | static_cast<int>(sqlite_result_code_)); |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 109 | |
Austin Sullivan | 7b54340 | 2023-10-07 00:55:09 | [diff] [blame] | 110 | if(!database_uma_name_.empty()){ |
| 111 | base::UmaHistogramEnumeration( |
| 112 | base::StrCat({"Sql.Recovery.Result.", database_uma_name_}), result_); |
| 113 | UmaHistogramSqliteResult( |
| 114 | base::StrCat({"Sql.Recovery.ResultCode.", database_uma_name_}), |
| 115 | static_cast<int>(sqlite_result_code_)); |
| 116 | } |
| 117 | |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 118 | if(db_){ |
| 119 | if(result_==Result::kSuccess){ |
| 120 | // Poison the original handle, but don't raze the database. |
| 121 | db_->Poison(); |
| 122 | }else{ |
| 123 | db_->RazeAndPoison(); |
| 124 | } |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 125 | } |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 126 | |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 127 | db_=nullptr; |
| 128 | |
| 129 | if(recover_db_.is_open()){ |
| 130 | recover_db_.Close(); |
| 131 | } |
Alison Gale | 71bd8f15 | 2024-04-26 22:38:20 | [diff] [blame] | 132 | // TODO(crbug.com/40061775): Don't always delete the recovery db if we |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 133 | // are ever to keep around successfully-recovered, but unsuccessfully-restored |
| 134 | // databases. |
| 135 | sql::Database::Delete(recovery_database_path_); |
| 136 | } |
| 137 | |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 138 | voidRecovery::SetRecoverySucceeded(){ |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 139 | // Recovery result must only be set once. |
| 140 | CHECK_EQ(result_,Result::kUnknown); |
| 141 | |
| 142 | result_=Result::kSuccess; |
| 143 | } |
| 144 | |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 145 | voidRecovery::SetRecoveryFailed(Result failure_result, |
| 146 | SqliteResultCode result_code){ |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 147 | // Recovery result must only be set once. |
| 148 | CHECK_EQ(result_,Result::kUnknown); |
| 149 | |
| 150 | switch(failure_result){ |
| 151 | caseResult::kUnknown: |
| 152 | caseResult::kSuccess: |
Peter Boström | 89c82708 | 2024-09-20 10:54:38 | [diff] [blame] | 153 | NOTREACHED(); |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 154 | caseResult::kFailedRecoveryInit: |
| 155 | caseResult::kFailedRecoveryRun: |
| 156 | caseResult::kFailedToOpenRecoveredDatabase: |
| 157 | caseResult::kFailedMetaTableDoesNotExist: |
| 158 | caseResult::kFailedMetaTableInit: |
| 159 | caseResult::kFailedMetaTableVersionWasInvalid: |
| 160 | caseResult::kFailedBackupInit: |
| 161 | caseResult::kFailedBackupRun: |
| 162 | break; |
| 163 | } |
| 164 | |
| 165 | result_= failure_result; |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 166 | sqlite_result_code_= result_code; |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 167 | } |
| 168 | |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 169 | SqliteResultCodeRecovery::RecoverAndReplaceDatabase(){ |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 170 | auto sqlite_result_code=AttemptToRecoverDatabaseToBackup(); |
| 171 | if(sqlite_result_code!=SqliteResultCode::kOk){ |
| 172 | return sqlite_result_code; |
| 173 | } |
| 174 | |
| 175 | // Open a connection to the newly-created recovery database. |
| 176 | if(!recover_db_.Open(recovery_database_path_)){ |
| 177 | DVLOG(1)<<"Unable to open recovery database."; |
| 178 | |
Alison Gale | 71bd8f15 | 2024-04-26 22:38:20 | [diff] [blame] | 179 | // TODO(crbug.com/40061775): It's unfortunate to give up now, after |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 180 | // we've successfully recovered the database to a backup. Consider falling |
| 181 | // back to base::Move(). |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 182 | SetRecoveryFailed(Result::kFailedToOpenRecoveredDatabase, |
| 183 | ToSqliteResultCode(recover_db_.GetErrorCode())); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 184 | returnSqliteResultCode::kError; |
| 185 | } |
| 186 | |
| 187 | if(strategy_==Strategy::kRecoverWithMetaVersionOrRaze&& |
| 188 | !RecoveredDbHasValidMetaTable()){ |
| 189 | DVLOG(1)<<"Could not read valid version number from recovery database."; |
| 190 | returnSqliteResultCode::kError; |
| 191 | } |
| 192 | |
| 193 | returnReplaceOriginalWithRecoveredDb(); |
| 194 | } |
| 195 | |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 196 | SqliteResultCodeRecovery::AttemptToRecoverDatabaseToBackup(){ |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 197 | CHECK(db_->is_open()); |
| 198 | CHECK(!recover_db_.is_open()); |
| 199 | |
| 200 | // See full documentation for the corruption recovery module in |
| 201 | // https://sqlite.org/src/file/ext/recover/sqlite3recover.h |
| 202 | |
| 203 | // sqlite3_recover_init() create a new sqlite3_recover handle, with data being |
| 204 | // recovered into a new database. This should very rarely fail - e.g. if |
| 205 | // memory for the recovery object itself could not be allocated. If it does |
| 206 | // fail, `recover` will be nullptr and an error code will surface when |
| 207 | // attempting to configure the recovery object below. |
| 208 | sqlite3_recover* recover= |
| 209 | sqlite3_recover_init(db_->db(InternalApiToken()), kMainDatabaseName, |
| 210 | recovery_database_path_.AsUTF8Unsafe().c_str()); |
| 211 | |
| 212 | // sqlite3_recover_config() configures the sqlite3_recover object. |
| 213 | // |
| 214 | // These functions should only fail if the above initialization failed, or if |
| 215 | // invalid parameters are passed. |
| 216 | |
| 217 | // Don't bother creating a lost-and-found table. |
| 218 | sqlite3_recover_config(recover, SQLITE_RECOVER_LOST_AND_FOUND,nullptr); |
| 219 | // Do not attempt to recover records from pages that appear to be linked to |
| 220 | // the freelist, to avoid "recovering" deleted records. |
| 221 | int kRecoverFreelist=0; |
| 222 | sqlite3_recover_config(recover, SQLITE_RECOVER_FREELIST_CORRUPT, |
| 223 | static_cast<void*>(&kRecoverFreelist)); |
| 224 | // Attempt to recover ROWID values that are not INTEGER PRIMARY KEY. |
| 225 | int kRecoverRowIds=1; |
| 226 | sqlite3_recover_config(recover, SQLITE_RECOVER_ROWIDS, |
| 227 | static_cast<void*>(&kRecoverRowIds)); |
| 228 | |
| 229 | auto sqlite_result_code= |
| 230 | ToSqliteResultCode(sqlite3_recover_errcode(recover)); |
| 231 | if(sqlite_result_code!=SqliteResultCode::kOk){ |
| 232 | CHECK_NE(sqlite_result_code,SqliteResultCode::kApiMisuse); |
| 233 | |
| 234 | // The recovery could not be configured. |
Alison Gale | 71bd8f15 | 2024-04-26 22:38:20 | [diff] [blame] | 235 | // TODO(crbug.com/40061775): This is likely a transient issue, so we |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 236 | // could consider keeping the database intact in case the caller wants to |
| 237 | // try again later. For now, we'll always raze. |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 238 | SetRecoveryFailed(Result::kFailedRecoveryInit, sqlite_result_code); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 239 | |
| 240 | DVLOG(1)<<"recovery config error: "<< sqlite_result_code |
| 241 | << sqlite3_recover_errcode(recover); |
| 242 | |
| 243 | // Clean up the recovery object. |
| 244 | sqlite3_recover_finish(recover); |
| 245 | return sqlite_result_code; |
| 246 | } |
| 247 | |
| 248 | // sqlite3_recover_run() attempts to construct an copy of the database with |
| 249 | // data corruption handled. It returns SQLITE_OK if recovery was successful. |
| 250 | sqlite_result_code=ToSqliteResultCode(sqlite3_recover_run(recover)); |
| 251 | |
| 252 | // sqlite3_recover_finish() cleans up the recovery object. It should return |
| 253 | // the same error code as from sqlite3_recover_run(). |
| 254 | auto finish_result_code=ToSqliteResultCode(sqlite3_recover_finish(recover)); |
| 255 | CHECK_EQ(finish_result_code, sqlite_result_code); |
| 256 | |
| 257 | if(sqlite_result_code!=SqliteResultCode::kOk){ |
| 258 | // Could not recover the database. |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 259 | SetRecoveryFailed(Result::kFailedRecoveryRun, sqlite_result_code); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 260 | |
| 261 | DVLOG(1)<<"recovery error: "<< sqlite_result_code |
| 262 | << sqlite3_recover_errmsg(recover); |
| 263 | } |
| 264 | |
| 265 | return sqlite_result_code; |
| 266 | } |
| 267 | |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 268 | SqliteResultCodeRecovery::ReplaceOriginalWithRecoveredDb(){ |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 269 | CHECK(db_->is_open()); |
| 270 | CHECK(recover_db_.is_open()); |
| 271 | |
| 272 | // sqlite3_backup_init() fails if a transaction is ongoing. This should be |
| 273 | // rare, since we rolled back all transactions in this object's constructor. |
| 274 | sqlite3_backup* backup= sqlite3_backup_init( |
| 275 | db_->db(InternalApiToken()), kMainDatabaseName, |
| 276 | recover_db_.db(InternalApiToken()), kMainDatabaseName); |
| 277 | if(!backup){ |
| 278 | // Error code is in the destination database handle. |
| 279 | DVLOG(1)<<"sqlite3_backup_init() failed: " |
| 280 | << sqlite3_errmsg(db_->db(InternalApiToken())); |
| 281 | |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 282 | auto result_code= |
| 283 | ToSqliteResultCode(sqlite3_errcode(db_->db(InternalApiToken()))); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 284 | |
Alison Gale | 71bd8f15 | 2024-04-26 22:38:20 | [diff] [blame] | 285 | // TODO(crbug.com/40061775): It's unfortunate to give up now, after |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 286 | // we've successfully recovered the database. Consider falling back to |
| 287 | // base::Move(). |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 288 | SetRecoveryFailed(Result::kFailedBackupInit, result_code); |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 289 | return result_code; |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 290 | } |
| 291 | |
| 292 | // sqlite3_backup_step() copies pages from the source to the destination |
| 293 | // database. It returns SQLITE_DONE if copying successfully completed, or some |
| 294 | // other error on failure. |
Alison Gale | 71bd8f15 | 2024-04-26 22:38:20 | [diff] [blame] | 295 | // TODO(crbug.com/40061775): Some of these errors are transient and the |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 296 | // operation could feasibly succeed at a later time. Consider keeping around |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 297 | // successfully-recovered, but unsuccessfully-restored databases or falling |
| 298 | // back to base::Move(). |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 299 | constexprint kUnlimitedPageCount=-1;// Back up entire database. |
| 300 | auto sqlite_result_code= |
| 301 | ToSqliteResultCode(sqlite3_backup_step(backup, kUnlimitedPageCount)); |
| 302 | |
| 303 | // sqlite3_backup_remaining() returns the number of pages still to be backed |
| 304 | // up, which should be zero if sqlite3_backup_step() completed successfully. |
| 305 | int pages_remaining= sqlite3_backup_remaining(backup); |
| 306 | |
| 307 | // sqlite3_backup_finish() releases the sqlite3_backup object. |
| 308 | // |
| 309 | // It returns an error code only if the backup encountered a permanent error. |
| 310 | // We use the the sqlite3_backup_step() result instead, because it also tells |
| 311 | // us about temporary errors, like SQLITE_BUSY. |
| 312 | // |
| 313 | // We pass the sqlite3_backup_finish() result code through |
| 314 | // ToSqliteResultCode() to catch codes that should never occur, like |
| 315 | // SQLITE_MISUSE. |
| 316 | std::ignore=ToSqliteResultCode(sqlite3_backup_finish(backup)); |
| 317 | |
| 318 | if(sqlite_result_code!=SqliteResultCode::kDone){ |
| 319 | CHECK_NE(sqlite_result_code,SqliteResultCode::kOk) |
| 320 | <<"sqlite3_backup_step() returned SQLITE_OK (instead of SQLITE_DONE) " |
| 321 | <<"when asked to back up the entire database"; |
| 322 | |
| 323 | DVLOG(1)<<"sqlite3_backup_step() failed: " |
| 324 | << sqlite3_errmsg(db_->db(InternalApiToken())); |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 325 | SetRecoveryFailed(Result::kFailedBackupRun, sqlite_result_code); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 326 | return sqlite_result_code; |
| 327 | } |
| 328 | |
| 329 | // The original database was successfully recovered and replaced. Hooray! |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 330 | SetRecoverySucceeded(); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 331 | |
| 332 | CHECK_EQ(pages_remaining,0); |
| 333 | returnSqliteResultCode::kOk; |
| 334 | } |
| 335 | |
Evan Stade | 4c7d6b3 | 2024-03-12 16:50:27 | [diff] [blame] | 336 | boolRecovery::RecoveredDbHasValidMetaTable(){ |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 337 | CHECK(recover_db_.is_open()); |
| 338 | |
| 339 | if(!MetaTable::DoesTableExist(&recover_db_)){ |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 340 | DVLOG(1)<<"Meta table does not exist in recovery database."; |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 341 | SetRecoveryFailed(Result::kFailedMetaTableDoesNotExist, |
| 342 | ToSqliteResultCode(recover_db_.GetErrorCode())); |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 343 | returnfalse; |
| 344 | } |
| 345 | |
| 346 | // MetaTable::Init will not create a meta table if one already exists. |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 347 | sql::MetaTable meta_table; |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 348 | if(!meta_table.Init(&recover_db_,/*version=*/1, |
| 349 | /*compatible_version=*/1)){ |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 350 | SetRecoveryFailed(Result::kFailedMetaTableInit, |
| 351 | ToSqliteResultCode(recover_db_.GetErrorCode())); |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 352 | returnfalse; |
| 353 | } |
| 354 | |
| 355 | // Confirm that we can read a valid version number from the recovered table. |
| 356 | if(meta_table.GetVersionNumber()<=0){ |
Austin Sullivan | 5493084 | 2023-06-06 21:09:01 | [diff] [blame] | 357 | SetRecoveryFailed(Result::kFailedMetaTableVersionWasInvalid, |
| 358 | ToSqliteResultCode(recover_db_.GetErrorCode())); |
Austin Sullivan | c99e930 | 2023-05-19 02:41:45 | [diff] [blame] | 359 | returnfalse; |
| 360 | } |
| 361 | |
| 362 | returntrue; |
Austin Sullivan | 0593ef9 | 2023-05-17 20:46:30 | [diff] [blame] | 363 | } |
| 364 | |
shess@chromium.org | 8d40941 | 2013-07-19 18:25:30 | [diff] [blame] | 365 | }// namespace sql |