aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Chow <github@achow101.com>2022-08-26 13:18:04 -0400
committerAndrew Chow <github@achow101.com>2022-08-29 17:30:38 -0400
commite7b16f925ae5b117e8b74ce814b63e19b19b50f4 (patch)
tree49d42195c832c044a4a714c4776a147be362a439
parent5b62f095e790a0d4e2a70ece89465b64fc68358a (diff)
Implement MigrateToSQLite
-rw-r--r--src/wallet/wallet.cpp72
-rw-r--r--src/wallet/wallet.h9
2 files changed, 80 insertions, 1 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 620c6b61d0..3d2d59cf96 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -3654,4 +3654,76 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat
return spk_man;
}
+
+bool CWallet::MigrateToSQLite(bilingual_str& error)
+{
+ AssertLockHeld(cs_wallet);
+
+ WalletLogPrintf("Migrating wallet storage database from BerkeleyDB to SQLite.\n");
+
+ if (m_database->Format() == "sqlite") {
+ error = _("Error: This wallet already uses SQLite");
+ return false;
+ }
+
+ // Get all of the records for DB type migration
+ std::unique_ptr<DatabaseBatch> batch = m_database->MakeBatch();
+ std::vector<std::pair<SerializeData, SerializeData>> records;
+ if (!batch->StartCursor()) {
+ error = _("Error: Unable to begin reading all records in the database");
+ return false;
+ }
+ bool complete = false;
+ while (true) {
+ CDataStream ss_key(SER_DISK, CLIENT_VERSION);
+ CDataStream ss_value(SER_DISK, CLIENT_VERSION);
+ bool ret = batch->ReadAtCursor(ss_key, ss_value, complete);
+ if (!ret) {
+ break;
+ }
+ SerializeData key(ss_key.begin(), ss_key.end());
+ SerializeData value(ss_value.begin(), ss_value.end());
+ records.emplace_back(key, value);
+ }
+ batch->CloseCursor();
+ batch.reset();
+ if (!complete) {
+ error = _("Error: Unable to read all records in the database");
+ return false;
+ }
+
+ // Close this database and delete the file
+ fs::path db_path = fs::PathFromString(m_database->Filename());
+ fs::path db_dir = db_path.parent_path();
+ m_database->Close();
+ fs::remove(db_path);
+
+ // Make new DB
+ DatabaseOptions opts;
+ opts.require_create = true;
+ opts.require_format = DatabaseFormat::SQLITE;
+ DatabaseStatus db_status;
+ std::unique_ptr<WalletDatabase> new_db = MakeDatabase(db_dir, opts, db_status, error);
+ assert(new_db); // This is to prevent doing anything further with this wallet. The original file was deleted, but a backup exists.
+ m_database.reset();
+ m_database = std::move(new_db);
+
+ // Write existing records into the new DB
+ batch = m_database->MakeBatch();
+ bool began = batch->TxnBegin();
+ assert(began); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution.
+ for (const auto& [key, value] : records) {
+ CDataStream ss_key(key, SER_DISK, CLIENT_VERSION);
+ CDataStream ss_value(value, SER_DISK, CLIENT_VERSION);
+ if (!batch->Write(ss_key, ss_value)) {
+ batch->TxnAbort();
+ m_database->Close();
+ fs::remove(m_database->Filename());
+ assert(false); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution.
+ }
+ }
+ bool committed = batch->TxnCommit();
+ assert(committed); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution.
+ return true;
+}
} // namespace wallet
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index d1876dda70..45391c20d7 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -316,7 +316,7 @@ private:
std::string m_name;
/** Internal database handle. */
- std::unique_ptr<WalletDatabase> const m_database;
+ std::unique_ptr<WalletDatabase> m_database;
/**
* The following is used to keep track of how far behind the wallet is
@@ -920,6 +920,13 @@ public:
//! Add a descriptor to the wallet, return a ScriptPubKeyMan & associated output type
ScriptPubKeyMan* AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
+ /** Move all records from the BDB database to a new SQLite database for storage.
+ * The original BDB file will be deleted and replaced with a new SQLite file.
+ * A backup is not created.
+ * May crash if something unexpected happens in the filesystem.
+ */
+ bool MigrateToSQLite(bilingual_str& error) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
};
/**