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

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

Generated by: LCOV version 2.0-1