Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/cpp_lmdb/iterators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ class ro_iterator
base::navigate_cursor(MDB_FIRST, nullptr);
}

explicit ro_iterator(
LmdbApi const &api, MDB_cursor &cursor, byte_span const &key) noexcept
: base{api, cursor}
{
auto db_key = details::to_mdb_val(key);
base::navigate_cursor(MDB_SET_RANGE, &db_key);
}

ro_iterator(ro_iterator &&) = default;
auto operator=(ro_iterator &&) -> ro_iterator & = default;

Expand Down
25 changes: 20 additions & 5 deletions include/cpp_lmdb/transactions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#include "cpp_lmdb/concepts.hpp"
#include "cpp_lmdb/db_item.hpp"
#include "cpp_lmdb/iterators.hpp"
#include "cpp_lmdb/views.hpp"
#include "cpp_lmdb/types.hpp"
#include "cpp_lmdb/views.hpp"

// details
#include "cpp_lmdb/details/details.hpp"
Expand Down Expand Up @@ -38,13 +38,17 @@ class transaction {

using ro_view
= db_view<ro_iterator<key_trait, value_trait, LmdbApi>, LmdbApi>;
using ro_dup_view = db_dup_view<
using ro_view_from_key = db_view_from_key<
ro_iterator<key_trait, value_trait, LmdbApi>,
LmdbApi>;
using ro_view_by_key = db_view_from_key<
ro_dup_iterator<key_trait, value_trait, LmdbApi>,
LmdbApi>;

public:
transaction(
MDB_dbi const db_index, details::txn_unique_ptr_t<LmdbApi> &&txn) noexcept
MDB_dbi const db_index,
details::txn_unique_ptr_t<LmdbApi> &&txn) noexcept
: _db_index{db_index}
, _txn{std::move(txn)}
, _api{_txn.get_deleter().api}
Expand Down Expand Up @@ -90,6 +94,17 @@ class transaction {
return {};
}

auto lower_bound(key_type const &key) const noexcept
-> std::expected<ro_view_from_key, error_t>
{
auto cursor = details::make_cursor(_api, _txn.get(), _db_index);
if (!cursor)
return std::unexpected{error_t{cursor.error()}};

auto const key_bytes = key_trait::to_bytes(key);
return ro_view_from_key{std::move(*cursor), key_bytes};
}

auto get(key_type const &key) const noexcept
-> std::expected<value_type, error_t>
requires(!details::key_value_trait_helper<
Expand Down Expand Up @@ -120,7 +135,7 @@ class transaction {
}

auto iterate_by_key(key_type const &key) const noexcept
-> std::expected<ro_dup_view, error_t>
-> std::expected<ro_view_by_key, error_t>
requires(
details::key_value_trait_helper<KeyValueTrait>::duplicates_enabled)
{
Expand All @@ -129,7 +144,7 @@ class transaction {
return std::unexpected{error_t{cursor.error()}};

auto const key_bytes = key_trait::to_bytes(key);
return ro_dup_view{std::move(*cursor), key_bytes};
return ro_view_by_key{std::move(*cursor), key_bytes};
}

private:
Expand Down
10 changes: 5 additions & 5 deletions include/cpp_lmdb/views.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ class db_view
};

template <std::input_iterator Iterator, lmdb_api_like LmdbApi>
class db_dup_view
: public std::ranges::view_interface<db_dup_view<Iterator, LmdbApi>> {
class db_view_from_key
: public std::ranges::view_interface<db_view_from_key<Iterator, LmdbApi>> {
public:
explicit db_dup_view(
explicit db_view_from_key(
details::cursor_unique_ptr_t<LmdbApi> &&cursor, byte_span const &key)
: _cursor{std::move(cursor)}, _key{key.begin(), key.end()}
{}

db_dup_view(db_dup_view &&) = default;
auto operator=(db_dup_view &&) -> db_dup_view & = default;
db_view_from_key(db_view_from_key &&) = default;
auto operator=(db_view_from_key &&) -> db_view_from_key & = default;

auto begin() const
{
Expand Down
1 change: 1 addition & 0 deletions test/integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ add_executable(
integrations_tests
test_db_int_keys_and_values.cpp
test_db_string_keys_and_values.cpp
test_db_dup_int_keys_values.cpp
)

enable_testing()
Expand Down
59 changes: 59 additions & 0 deletions test/integration/test_db_dup_int_keys_values.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include "cpp_lmdb/cpp_lmdb.hpp"
#include "test_utils.hpp"

// gtest
#include "gmock/gmock.h"
#include "gtest/gtest.h"

// std
#include <cstring>
#include <filesystem>

using namespace ::testing; // NOLINT(google-build-using-namespace)

namespace cpp_lmdb_tests
{

TEST(
integration_test,
db_int_duplicate_keys_and_values_iterate_by_key_lower_bound)
{
using test_trait = lmdb::duplicate_key<
lmdb::trivial_trait<uint8_t>,
lmdb::trivial_trait<uint32_t>>;

constexpr auto test_env = "./test_env";

if (std::filesystem::exists(test_env))
std::filesystem::remove_all(test_env);
std::filesystem::create_directory(test_env);

auto environment = lmdb::make_environment<lmdb::env_flags_t::none, 1>(
test_env, lmdb::default_file_mode);

ASSERT_TRUE(environment);
auto rw_db = environment->open_rw_db<test_trait>(
"test_db", lmdb::create_if_not_exists::yes);

ASSERT_TRUE(rw_db);

auto transaction = rw_db->begin_rw_transaction();
ASSERT_TRUE(transaction);

EXPECT_TRUE(transaction->insert(0xAA, 2000));
EXPECT_TRUE(transaction->insert(0xBB, 5001));
EXPECT_TRUE(transaction->insert(0xBB, 5002));
EXPECT_TRUE(transaction->insert(0xCC, 10000));

ASSERT_TRUE(rw_db->commit_transaction(std::move(*transaction)));
{
auto const ro_tx = rw_db->begin_ro_transaction();
ASSERT_TRUE(ro_tx);

EXPECT_THAT(
cpp_lmdb_tests::get_all_values(ro_tx->lower_bound(0xBB).value()),
ElementsAre(5001, 5002, 10000));
}
}

} // namespace cpp_lmdb_tests
22 changes: 18 additions & 4 deletions test/integration/test_db_int_keys_and_values.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include "cpp_lmdb/cpp_lmdb.hpp"

#include "test_utils.hpp"

// gtest
Expand Down Expand Up @@ -43,6 +42,8 @@ TEST(integration_test, db_int_keys_and_values_unique_key)

EXPECT_TRUE(transaction->insert(0xAA, 2000));
EXPECT_TRUE(transaction->insert(0xBB, 5000));
EXPECT_TRUE(transaction->insert(0xCC, 8000));

{
auto const result = transaction->try_insert(0xAA, 3000);
ASSERT_FALSE(result);
Expand All @@ -58,15 +59,19 @@ TEST(integration_test, db_int_keys_and_values_unique_key)
ASSERT_TRUE(result);
EXPECT_EQ(*result, 5000);
}

{
const auto result = transaction->get(0xCC);
ASSERT_TRUE(result);
EXPECT_EQ(*result, 8000);
}
{
ASSERT_TRUE(transaction->insert(0xBB, 10000));
const auto result = transaction->get(0xBB);
ASSERT_TRUE(result);
EXPECT_EQ(*result, 10000);
}

rw_db->commit_transaction(std::move(*transaction));
ASSERT_TRUE(rw_db->commit_transaction(std::move(*transaction)));
{
auto ro_tx = rw_db->begin_ro_transaction();
ASSERT_TRUE(ro_tx);
Expand All @@ -80,14 +85,23 @@ TEST(integration_test, db_int_keys_and_values_unique_key)
ASSERT_TRUE(result);
EXPECT_EQ(*result, 10000);
}
{
const auto result = ro_tx->get(0xCC);
ASSERT_TRUE(result);
EXPECT_EQ(*result, 8000);
}
}
{
auto ro_tx = rw_db->begin_ro_transaction();
ASSERT_TRUE(ro_tx);

EXPECT_THAT(
cpp_lmdb_tests::get_all_values(ro_tx->iterate().value()),
ElementsAre(2000, 10000));
ElementsAre(2000, 10000, 8000));

EXPECT_THAT(
cpp_lmdb_tests::get_all_values(ro_tx->lower_bound(0xBB).value()),
UnorderedElementsAre(10000, 8000));
}
}

Expand Down
44 changes: 44 additions & 0 deletions test/unit/test_transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,48 @@ TEST_F(test_transaction, trivial_types_dup_iterate_by_key)
EXPECT_EQ(db_item.value(), 0x20000030);
}

TEST_F(test_transaction, trivial_types_transaction_lower_bound)
{
std::array<uint8_t, 4> test_value{0x30, 0x0, 0x0, 0x20};

lmdb::
transaction<test_trait, lmdb::read_only_t::no, StrictMock<mocks::api>>
transaction{test_dbi, std::move(txn)};

auto *cursor{reinterpret_cast<MDB_cursor *>(0x84)};

{
InSequence const seq;
Comment thread
vvish marked this conversation as resolved.

EXPECT_CALL(api, mdb_cursor_open(test_txn, test_dbi, _))
.WillOnce(DoAll(SetArgPointee<2>(cursor), Return(MDB_SUCCESS)));

EXPECT_CALL(
api,
mdb_cursor_get(
cursor,
Pointee(MdbValBytesAre{0x78, 0x56, 0x34, 0x12}),
_,
MDB_SET_RANGE))
.WillOnce(DoAll(
SetArgPointee<2>(
MDB_val{test_value.size(), test_value.data()}),
Return(MDB_SUCCESS)));

EXPECT_CALL(api, mdb_cursor_close(cursor));
EXPECT_CALL(api, mdb_txn_abort(test_txn));
}

const auto result = transaction.lower_bound(0x12345678);
ASSERT_TRUE(result);

auto const &db_view = *result;
auto const it = db_view.begin();
ASSERT_NE(it, db_view.end());

auto const &key_value = *it;
EXPECT_EQ(key_value.key(), 0x12345678);
EXPECT_EQ(key_value.value(), 0x20000030);
}

} // namespace cpp_lmdb_tests
Loading