Skip to content
Open
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
13 changes: 13 additions & 0 deletions include/cpp_lmdb/transactions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class transaction {

using ro_view
= db_view<ro_iterator<key_trait, value_trait, LmdbApi>, LmdbApi>;
using ro_lo_view
= db_dup_view<ro_iterator<key_trait, value_trait, LmdbApi>, LmdbApi>;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to rename db_dup_view into something like db_view_with_key as it is now used not only in key duplicate scenarios

using ro_dup_view = db_dup_view<
ro_dup_iterator<key_trait, value_trait, LmdbApi>,
LmdbApi>;
Expand Down Expand Up @@ -90,6 +92,17 @@ class transaction {
return {};
}

auto lower_bound(key_type const &key) const noexcept
-> std::expected<ro_lo_view, 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_lo_view{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
38 changes: 38 additions & 0 deletions test/unit/test_transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,42 @@ TEST_F(test_transaction, trivial_types_dup_iterate_by_key)
ASSERT_TRUE(result);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a small bug in trivial_types_dup_iterate_by_key, iterate_by_key call should be used instead. I will look into that as a different PR.

}

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;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great to have basic integration test as well.


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();
EXPECT_NE(it, db_view.end());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use ASSERT_NE before iterator will be dereferenced.


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

} // namespace cpp_lmdb_tests