LCOV - code coverage report
Current view: top level - src/wallet/test - db_tests.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 94.2 % 171 161
Test Date: 2025-04-01 05:52:06 Functions: 94.7 % 19 18
Branches: 50.3 % 1010 508

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2018-present The Bitcoin Core developers
       2                 :             : // Distributed under the MIT software license, see the accompanying
       3                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :             : 
       5                 :             : #include <bitcoin-build-config.h> // IWYU pragma: keep
       6                 :             : 
       7                 :             : #include <boost/test/unit_test.hpp>
       8                 :             : 
       9                 :             : #include <test/util/setup_common.h>
      10                 :             : #include <util/check.h>
      11                 :             : #include <util/fs.h>
      12                 :             : #include <util/translation.h>
      13                 :             : #ifdef USE_BDB
      14                 :             : #include <wallet/bdb.h>
      15                 :             : #endif
      16                 :             : #include <wallet/sqlite.h>
      17                 :             : #include <wallet/migrate.h>
      18                 :             : #include <wallet/test/util.h>
      19                 :             : #include <wallet/walletutil.h> // for WALLET_FLAG_DESCRIPTORS
      20                 :             : 
      21                 :             : #include <fstream>
      22                 :             : #include <memory>
      23                 :             : #include <string>
      24                 :             : 
      25                 :           0 : inline std::ostream& operator<<(std::ostream& os, const std::pair<const SerializeData, SerializeData>& kv)
      26                 :             : {
      27                 :           0 :     std::span key{kv.first}, value{kv.second};
      28                 :           0 :     os << "(\"" << std::string_view{reinterpret_cast<const char*>(key.data()), key.size()} << "\", \""
      29                 :           0 :        << std::string_view{reinterpret_cast<const char*>(value.data()), value.size()} << "\")";
      30                 :           0 :     return os;
      31                 :             : }
      32                 :             : 
      33                 :             : namespace wallet {
      34                 :             : 
      35                 :          22 : inline std::span<const std::byte> StringBytes(std::string_view str)
      36                 :             : {
      37                 :          22 :     return std::as_bytes(std::span{str});
      38                 :             : }
      39                 :             : 
      40                 :          14 : static SerializeData StringData(std::string_view str)
      41                 :             : {
      42                 :          14 :     auto bytes = StringBytes(str);
      43                 :          14 :     return SerializeData{bytes.begin(), bytes.end()};
      44                 :             : }
      45                 :             : 
      46                 :           8 : static void CheckPrefix(DatabaseBatch& batch, std::span<const std::byte> prefix, MockableData expected)
      47                 :             : {
      48                 :           8 :     std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
      49                 :           8 :     MockableData actual;
      50                 :          68 :     while (true) {
      51                 :          38 :         DataStream key, value;
      52         [ +  - ]:          38 :         DatabaseCursor::Status status = cursor->Next(key, value);
      53         [ +  + ]:          38 :         if (status == DatabaseCursor::Status::DONE) break;
      54   [ +  -  +  -  :          60 :         BOOST_CHECK(status == DatabaseCursor::Status::MORE);
                   +  - ]
      55   [ +  -  +  -  :          60 :         BOOST_CHECK(
          +  -  +  -  +  
                      - ]
      56                 :             :             actual.emplace(SerializeData(key.begin(), key.end()), SerializeData(value.begin(), value.end())).second);
      57                 :          38 :     }
      58   [ +  -  +  -  :          16 :     BOOST_CHECK_EQUAL_COLLECTIONS(actual.begin(), actual.end(), expected.begin(), expected.end());
                   +  - ]
      59                 :           8 : }
      60                 :             : 
      61                 :             : BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup)
      62                 :             : 
      63                 :             : #ifdef USE_BDB
      64                 :             : static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, fs::path& database_filename)
      65                 :             : {
      66                 :             :     fs::path data_file = BDBDataFile(path);
      67                 :             :     database_filename = data_file.filename();
      68                 :             :     return GetBerkeleyEnv(data_file.parent_path(), false);
      69                 :             : }
      70                 :             : 
      71                 :             : BOOST_AUTO_TEST_CASE(getwalletenv_file)
      72                 :             : {
      73                 :             :     fs::path test_name = "test_name.dat";
      74                 :             :     const fs::path datadir = m_args.GetDataDirNet();
      75                 :             :     fs::path file_path = datadir / test_name;
      76                 :             :     std::ofstream f{file_path};
      77                 :             :     f.close();
      78                 :             : 
      79                 :             :     fs::path filename;
      80                 :             :     std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
      81                 :             :     BOOST_CHECK_EQUAL(filename, test_name);
      82                 :             :     BOOST_CHECK_EQUAL(env->Directory(), datadir);
      83                 :             : }
      84                 :             : 
      85                 :             : BOOST_AUTO_TEST_CASE(getwalletenv_directory)
      86                 :             : {
      87                 :             :     fs::path expected_name = "wallet.dat";
      88                 :             :     const fs::path datadir = m_args.GetDataDirNet();
      89                 :             : 
      90                 :             :     fs::path filename;
      91                 :             :     std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
      92                 :             :     BOOST_CHECK_EQUAL(filename, expected_name);
      93                 :             :     BOOST_CHECK_EQUAL(env->Directory(), datadir);
      94                 :             : }
      95                 :             : 
      96                 :             : BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
      97                 :             : {
      98                 :             :     fs::path datadir = m_args.GetDataDirNet() / "1";
      99                 :             :     fs::path datadir_2 = m_args.GetDataDirNet() / "2";
     100                 :             :     fs::path filename;
     101                 :             : 
     102                 :             :     std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);
     103                 :             :     std::shared_ptr<BerkeleyEnvironment> env_2 = GetWalletEnv(datadir, filename);
     104                 :             :     std::shared_ptr<BerkeleyEnvironment> env_3 = GetWalletEnv(datadir_2, filename);
     105                 :             : 
     106                 :             :     BOOST_CHECK(env_1 == env_2);
     107                 :             :     BOOST_CHECK(env_2 != env_3);
     108                 :             : }
     109                 :             : 
     110                 :             : BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance)
     111                 :             : {
     112                 :             :     fs::path datadir = gArgs.GetDataDirNet() / "1";
     113                 :             :     fs::path datadir_2 = gArgs.GetDataDirNet() / "2";
     114                 :             :     fs::path filename;
     115                 :             : 
     116                 :             :     std::shared_ptr <BerkeleyEnvironment> env_1_a = GetWalletEnv(datadir, filename);
     117                 :             :     std::shared_ptr <BerkeleyEnvironment> env_2_a = GetWalletEnv(datadir_2, filename);
     118                 :             :     env_1_a.reset();
     119                 :             : 
     120                 :             :     std::shared_ptr<BerkeleyEnvironment> env_1_b = GetWalletEnv(datadir, filename);
     121                 :             :     std::shared_ptr<BerkeleyEnvironment> env_2_b = GetWalletEnv(datadir_2, filename);
     122                 :             : 
     123                 :             :     BOOST_CHECK(env_1_a != env_1_b);
     124                 :             :     BOOST_CHECK(env_2_a == env_2_b);
     125                 :             : }
     126                 :             : #endif
     127                 :             : 
     128                 :           4 : static std::vector<std::unique_ptr<WalletDatabase>> TestDatabases(const fs::path& path_root)
     129                 :             : {
     130                 :           4 :     std::vector<std::unique_ptr<WalletDatabase>> dbs;
     131         [ +  - ]:           4 :     DatabaseOptions options;
     132                 :           4 :     DatabaseStatus status;
     133         [ +  - ]:           4 :     bilingual_str error;
     134                 :             : #ifdef USE_BDB
     135                 :             :     dbs.emplace_back(MakeBerkeleyDatabase(path_root / "bdb", options, status, error));
     136                 :             :     // Needs BDB to make the DB to read
     137                 :             :     dbs.emplace_back(std::make_unique<BerkeleyRODatabase>(BDBDataFile(path_root / "bdb"), /*open=*/false));
     138                 :             : #endif
     139   [ +  -  +  -  :          12 :     dbs.emplace_back(MakeSQLiteDatabase(path_root / "sqlite", options, status, error));
             +  -  +  - ]
     140   [ +  -  +  - ]:           4 :     dbs.emplace_back(CreateMockableWalletDatabase());
     141                 :           4 :     return dbs;
     142                 :           4 : }
     143                 :             : 
     144   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(db_cursor_prefix_range_test)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     145                 :             : {
     146                 :             :     // Test each supported db
     147         [ +  + ]:           3 :     for (const auto& database : TestDatabases(m_path_root)) {
     148         [ +  - ]:           2 :         std::vector<std::string> prefixes = {"", "FIRST", "SECOND", "P\xfe\xff", "P\xff\x01", "\xff\xff"};
     149                 :             : 
     150   [ +  -  +  - ]:           2 :         std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
     151   [ +  -  -  + ]:           2 :         if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
     152                 :             :             // For BerkeleyRO, open the file now. This must happen after BDB has written to the file
     153         [ #  # ]:           0 :             database->Open();
     154                 :             :         } else {
     155                 :             :             // Write elements to it if not berkeleyro
     156         [ +  + ]:          22 :             for (unsigned int i = 0; i < 10; i++) {
     157         [ +  + ]:         140 :                 for (const auto& prefix : prefixes) {
     158   [ +  -  +  -  :         240 :                     BOOST_CHECK(handler->Write(std::make_pair(prefix, i), i));
             +  -  +  - ]
     159                 :             :                 }
     160                 :             :             }
     161                 :             :         }
     162                 :             : 
     163                 :             :         // Now read all the items by prefix and verify that each element gets parsed correctly
     164         [ +  + ]:          14 :         for (const auto& prefix : prefixes) {
     165                 :          12 :             DataStream s_prefix;
     166         [ +  - ]:          12 :             s_prefix << prefix;
     167         [ +  - ]:          12 :             std::unique_ptr<DatabaseCursor> cursor = handler->GetNewPrefixCursor(s_prefix);
     168                 :          12 :             DataStream key;
     169                 :          12 :             DataStream value;
     170         [ +  + ]:         132 :             for (int i = 0; i < 10; i++) {
     171         [ +  - ]:         120 :                 DatabaseCursor::Status status = cursor->Next(key, value);
     172   [ +  -  +  - ]:         120 :                 BOOST_CHECK_EQUAL(status, DatabaseCursor::Status::MORE);
     173                 :             : 
     174         [ +  - ]:         120 :                 std::string key_back;
     175                 :         120 :                 unsigned int i_back;
     176   [ +  -  +  - ]:         120 :                 key >> key_back >> i_back;
     177   [ +  -  +  - ]:         120 :                 BOOST_CHECK_EQUAL(key_back, prefix);
     178                 :             : 
     179                 :         120 :                 unsigned int value_back;
     180         [ +  - ]:         120 :                 value >> value_back;
     181   [ +  -  +  - ]:         120 :                 BOOST_CHECK_EQUAL(value_back, i_back);
     182                 :             :             }
     183                 :             : 
     184                 :             :             // Let's now read it once more, it should return DONE
     185   [ +  -  +  -  :          24 :             BOOST_CHECK(cursor->Next(key, value) == DatabaseCursor::Status::DONE);
                   +  - ]
     186                 :          12 :         }
     187         [ +  - ]:           2 :         handler.reset();
     188         [ +  - ]:           2 :         database->Close();
     189                 :           3 :     }
     190                 :           1 : }
     191                 :             : 
     192                 :             : // Lower level DatabaseBase::GetNewPrefixCursor test, to cover cases that aren't
     193                 :             : // covered in the higher level test above. The higher level test uses
     194                 :             : // serialized strings which are prefixed with string length, so it doesn't test
     195                 :             : // truly empty prefixes or prefixes that begin with \xff
     196   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     197                 :             : {
     198                 :           1 :     const MockableData::value_type
     199   [ +  -  +  - ]:           1 :         e{StringData(""), StringData("e")},
     200   [ +  -  +  -  :           1 :         p{StringData("prefix"), StringData("p")},
                   +  - ]
     201   [ +  -  +  -  :           1 :         ps{StringData("prefixsuffix"), StringData("ps")},
                   +  - ]
     202   [ +  -  +  -  :           1 :         f{StringData("\xff"), StringData("f")},
                   +  - ]
     203   [ +  -  +  -  :           1 :         fs{StringData("\xffsuffix"), StringData("fs")},
                   +  - ]
     204   [ +  -  +  -  :           1 :         ff{StringData("\xff\xff"), StringData("ff")},
                   +  - ]
     205   [ +  -  +  - ]:           1 :         ffs{StringData("\xff\xffsuffix"), StringData("ffs")};
     206   [ +  -  +  + ]:           3 :     for (const auto& database : TestDatabases(m_path_root)) {
     207         [ +  - ]:           2 :         std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
     208                 :             : 
     209   [ +  -  -  + ]:           2 :         if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
     210                 :             :             // For BerkeleyRO, open the file now. This must happen after BDB has written to the file
     211         [ #  # ]:           0 :             database->Open();
     212                 :             :         } else {
     213                 :             :             // Write elements to it if not berkeleyro
     214   [ +  -  +  -  :          44 :             for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) {
          +  -  +  -  +  
          -  +  -  +  -  
          -  +  +  +  -  
                      - ]
     215         [ -  + ]:          14 :                 batch->Write(std::span{k}, std::span{v});
     216   [ +  +  -  - ]:          16 :             }
     217                 :             :         }
     218                 :             : 
     219   [ +  -  +  -  :          18 :         CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs});
             +  +  -  - ]
     220   [ +  -  +  -  :           8 :         CheckPrefix(*batch, StringBytes("prefix"), {p, ps});
             +  +  -  - ]
     221   [ +  -  +  -  :          12 :         CheckPrefix(*batch, StringBytes("\xff"), {f, fs, ff, ffs});
             +  +  -  - ]
     222   [ +  -  +  -  :           8 :         CheckPrefix(*batch, StringBytes("\xff\xff"), {ff, ffs});
             +  +  -  - ]
     223         [ +  - ]:           2 :         batch.reset();
     224         [ +  - ]:           2 :         database->Close();
     225                 :           3 :     }
     226   [ +  -  +  -  :          15 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  -  -  -  -  
             -  -  -  - ]
     227                 :             : 
     228   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(db_availability_after_write_error)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     229                 :             : {
     230                 :             :     // Ensures the database remains accessible without deadlocking after a write error.
     231                 :             :     // To simulate the behavior, record overwrites are disallowed, and the test verifies
     232                 :             :     // that the database remains active after failing to store an existing record.
     233         [ +  + ]:           3 :     for (const auto& database : TestDatabases(m_path_root)) {
     234   [ +  -  -  + ]:           2 :         if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
     235                 :             :             // Skip this test if BerkeleyRO
     236                 :           0 :             continue;
     237                 :             :         }
     238                 :             :         // Write original record
     239         [ +  - ]:           2 :         std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
     240         [ +  - ]:           2 :         std::string key = "key";
     241         [ +  - ]:           2 :         std::string value = "value";
     242         [ +  - ]:           2 :         std::string value2 = "value_2";
     243   [ +  -  +  -  :           4 :         BOOST_CHECK(batch->Write(key, value));
             +  -  +  - ]
     244                 :             :         // Attempt to overwrite the record (expect failure)
     245   [ +  -  +  -  :           4 :         BOOST_CHECK(!batch->Write(key, value2, /*fOverwrite=*/false));
             +  -  +  - ]
     246                 :             :         // Successfully overwrite the record
     247   [ +  -  +  -  :           4 :         BOOST_CHECK(batch->Write(key, value2, /*fOverwrite=*/true));
             +  -  +  - ]
     248                 :             :         // Sanity-check; read and verify the overwritten value
     249         [ +  - ]:           2 :         std::string read_value;
     250   [ +  -  +  -  :           4 :         BOOST_CHECK(batch->Read(key, read_value));
             +  -  +  - ]
     251   [ +  -  +  - ]:           2 :         BOOST_CHECK_EQUAL(read_value, value2);
     252                 :           3 :     }
     253                 :           1 : }
     254                 :             : 
     255                 :             : // Verify 'ErasePrefix' functionality using db keys similar to the ones used by the wallet.
     256                 :             : // Keys are in the form of std::pair<TYPE, ENTRY_ID>
     257   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(erase_prefix)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     258                 :             : {
     259                 :           1 :     const std::string key = "key";
     260         [ +  - ]:           1 :     const std::string key2 = "key2";
     261         [ +  - ]:           1 :     const std::string value = "value";
     262         [ +  - ]:           1 :     const std::string value2 = "value_2";
     263   [ +  -  +  -  :          16 :     auto make_key = [](std::string type, std::string id) { return std::make_pair(type, id); };
          +  -  +  -  +  
          -  +  -  +  -  
                   +  - ]
     264                 :             : 
     265   [ +  -  +  + ]:           3 :     for (const auto& database : TestDatabases(m_path_root)) {
     266   [ +  -  -  + ]:           2 :         if (dynamic_cast<BerkeleyRODatabase*>(database.get())) {
     267                 :             :             // Skip this test if BerkeleyRO
     268                 :           0 :             continue;
     269                 :             :         }
     270         [ +  - ]:           2 :         std::unique_ptr<DatabaseBatch> batch = database->MakeBatch();
     271                 :             : 
     272                 :             :         // Write two entries with the same key type prefix, a third one with a different prefix
     273                 :             :         // and a fourth one with the type-id values inverted
     274   [ +  -  +  -  :           8 :         BOOST_CHECK(batch->Write(make_key(key, value), value));
          +  -  +  -  +  
                -  +  - ]
     275   [ +  -  +  -  :           8 :         BOOST_CHECK(batch->Write(make_key(key, value2), value2));
          +  -  +  -  +  
                -  +  - ]
     276   [ +  -  +  -  :           8 :         BOOST_CHECK(batch->Write(make_key(key2, value), value));
          +  -  +  -  +  
                -  +  - ]
     277   [ +  -  +  -  :           8 :         BOOST_CHECK(batch->Write(make_key(value, key), value));
          +  -  +  -  +  
                -  +  - ]
     278                 :             : 
     279                 :             :         // Erase the ones with the same prefix and verify result
     280   [ +  -  +  -  :           4 :         BOOST_CHECK(batch->TxnBegin());
             +  -  +  - ]
     281   [ +  -  +  -  :           4 :         BOOST_CHECK(batch->ErasePrefix(DataStream() << key));
          +  -  +  -  +  
                      - ]
     282   [ +  -  +  -  :           4 :         BOOST_CHECK(batch->TxnCommit());
             +  -  +  - ]
     283                 :             : 
     284   [ +  -  +  -  :          10 :         BOOST_CHECK(!batch->Exists(make_key(key, value)));
          +  -  +  -  +  
                -  +  - ]
     285   [ +  -  +  -  :           8 :         BOOST_CHECK(!batch->Exists(make_key(key, value2)));
          +  -  +  -  +  
                -  +  - ]
     286                 :             :         // Also verify that entries with a different prefix were not erased
     287   [ +  -  +  -  :           8 :         BOOST_CHECK(batch->Exists(make_key(key2, value)));
          +  -  +  -  +  
                -  +  - ]
     288   [ +  -  +  -  :           8 :         BOOST_CHECK(batch->Exists(make_key(value, key)));
          +  -  +  -  +  
                      - ]
     289                 :           3 :     }
     290                 :           1 : }
     291                 :             : 
     292                 :             : // Test-only statement execution error
     293                 :             : constexpr int TEST_SQLITE_ERROR = -999;
     294                 :             : 
     295                 :             : class DbExecBlocker : public SQliteExecHandler
     296                 :             : {
     297                 :             : private:
     298                 :             :     SQliteExecHandler m_base_exec;
     299                 :             :     std::set<std::string> m_blocked_statements;
     300                 :             : public:
     301         [ +  - ]:           1 :     DbExecBlocker(std::set<std::string> blocked_statements) : m_blocked_statements(blocked_statements) {}
     302                 :           1 :     int Exec(SQLiteDatabase& database, const std::string& statement) override {
     303         [ -  + ]:           1 :         if (m_blocked_statements.contains(statement)) return TEST_SQLITE_ERROR;
     304                 :           0 :         return m_base_exec.Exec(database, statement);
     305                 :             :     }
     306                 :             : };
     307                 :             : 
     308   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(txn_close_failure_dangling_txn)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     309                 :             : {
     310                 :             :     // Verifies that there is no active dangling, to-be-reversed db txn
     311                 :             :     // after the batch object that initiated it is destroyed.
     312         [ +  - ]:           1 :     DatabaseOptions options;
     313                 :           1 :     DatabaseStatus status;
     314         [ +  - ]:           1 :     bilingual_str error;
     315   [ +  -  +  -  :           3 :     std::unique_ptr<SQLiteDatabase> database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
                   +  - ]
     316                 :             : 
     317         [ +  - ]:           1 :     std::string key = "key";
     318         [ +  - ]:           1 :     std::string value = "value";
     319                 :             : 
     320         [ +  - ]:           1 :     std::unique_ptr<SQLiteBatch> batch = std::make_unique<SQLiteBatch>(*database);
     321   [ +  -  +  -  :           2 :     BOOST_CHECK(batch->TxnBegin());
             +  -  +  - ]
     322   [ +  -  +  -  :           2 :     BOOST_CHECK(batch->Write(key, value));
             +  -  +  - ]
     323                 :             :     // Set a handler to prevent txn abortion during destruction.
     324                 :             :     // Mimicking a db statement execution failure.
     325   [ +  -  +  -  :           2 :     batch->SetExecHandler(std::make_unique<DbExecBlocker>(std::set<std::string>{"ROLLBACK TRANSACTION"}));
          +  -  +  +  -  
                      - ]
     326                 :             :     // Destroy batch
     327         [ +  - ]:           1 :     batch.reset();
     328                 :             : 
     329                 :             :     // Ensure there is no dangling, to-be-reversed db txn
     330   [ +  -  +  -  :           2 :     BOOST_CHECK(!database->HasActiveTxn());
             +  -  +  - ]
     331                 :             : 
     332                 :             :     // And, just as a sanity check; verify that new batchs only write what they suppose to write
     333                 :             :     // and nothing else.
     334         [ +  - ]:           1 :     std::string key2 = "key2";
     335         [ +  - ]:           1 :     std::unique_ptr<SQLiteBatch> batch2 = std::make_unique<SQLiteBatch>(*database);
     336   [ +  -  +  -  :           2 :     BOOST_CHECK(batch2->Write(key2, value));
             +  -  +  - ]
     337                 :             :     // The first key must not exist
     338   [ +  -  +  -  :           2 :     BOOST_CHECK(!batch2->Exists(key));
                   +  - ]
     339         [ +  - ]:           3 : }
     340                 :             : 
     341   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(concurrent_txn_dont_interfere)
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
     342                 :             : {
     343                 :           1 :     std::string key = "key";
     344         [ +  - ]:           1 :     std::string value = "value";
     345         [ +  - ]:           1 :     std::string value2 = "value_2";
     346                 :             : 
     347         [ +  - ]:           1 :     DatabaseOptions options;
     348                 :           1 :     DatabaseStatus status;
     349         [ +  - ]:           1 :     bilingual_str error;
     350   [ +  -  +  -  :           3 :     const auto& database = MakeSQLiteDatabase(m_path_root / "sqlite", options, status, error);
                   +  - ]
     351                 :             : 
     352   [ +  -  +  - ]:           1 :     std::unique_ptr<DatabaseBatch> handler = Assert(database)->MakeBatch();
     353                 :             : 
     354                 :             :     // Verify concurrent db transactions does not interfere between each other.
     355                 :             :     // Start db txn, write key and check the key does exist within the db txn.
     356   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->TxnBegin());
             +  -  +  - ]
     357   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->Write(key, value));
             +  -  +  - ]
     358   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->Exists(key));
             +  -  +  - ]
     359                 :             : 
     360                 :             :     // But, the same key, does not exist in another handler
     361   [ +  -  +  - ]:           1 :     std::unique_ptr<DatabaseBatch> handler2 = Assert(database)->MakeBatch();
     362   [ +  -  +  -  :           2 :     BOOST_CHECK(handler2->Exists(key));
             +  -  +  - ]
     363                 :             : 
     364                 :             :     // Attempt to commit the handler txn calling the handler2 methods.
     365                 :             :     // Which, must not be possible.
     366   [ +  -  +  -  :           2 :     BOOST_CHECK(!handler2->TxnCommit());
             +  -  +  - ]
     367   [ +  -  +  -  :           2 :     BOOST_CHECK(!handler2->TxnAbort());
             +  -  +  - ]
     368                 :             : 
     369                 :             :     // Only the first handler can commit the changes.
     370   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->TxnCommit());
             +  -  +  - ]
     371                 :             :     // And, once commit is completed, handler2 can read the record
     372         [ +  - ]:           1 :     std::string read_value;
     373   [ +  -  +  -  :           2 :     BOOST_CHECK(handler2->Read(key, read_value));
             +  -  +  - ]
     374   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(read_value, value);
     375                 :             : 
     376                 :             :     // Also, once txn is committed, single write statements are re-enabled.
     377                 :             :     // Which means that handler2 can read the record changes directly.
     378   [ +  -  +  -  :           2 :     BOOST_CHECK(handler->Write(key, value2, /*fOverwrite=*/true));
             +  -  +  - ]
     379   [ +  -  +  -  :           2 :     BOOST_CHECK(handler2->Read(key, read_value));
             +  -  +  - ]
     380   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(read_value, value2);
     381                 :           2 : }
     382                 :             : 
     383                 :             : BOOST_AUTO_TEST_SUITE_END()
     384                 :             : } // namespace wallet
        

Generated by: LCOV version 2.0-1