mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-30 22:47:16 +08:00
mdbx: добавление mdbx_cursor_unbind()
в API.
This commit is contained in:
parent
5f274eb4c6
commit
d28a397b2d
22
mdbx.h
22
mdbx.h
@ -4732,6 +4732,28 @@ mdbx_cursor_get_userctx(const MDBX_cursor *cursor);
|
||||
LIBMDBX_API int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *cursor,
|
||||
MDBX_dbi dbi);
|
||||
|
||||
/** \brief Unbind cursor from a transaction.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
* Unbinded cursor is disassociated with any transactions but still holds
|
||||
* the original DBI-handle internally. Thus it could be renewed with any running
|
||||
* transaction or closed.
|
||||
*
|
||||
* \see mdbx_cursor_renew()
|
||||
* \see mdbx_cursor_bind()
|
||||
* \see mdbx_cursor_close()
|
||||
*
|
||||
* \note In contrast to LMDB, the MDBX required that any opened cursors can be
|
||||
* reused and must be freed explicitly, regardless ones was opened in a
|
||||
* read-only or write transaction. The REASON for this is eliminates ambiguity
|
||||
* which helps to avoid errors such as: use-after-free, double-free, i.e.
|
||||
* memory corruption and segfaults.
|
||||
*
|
||||
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open().
|
||||
*
|
||||
* \returns A non-zero error value on failure and 0 on success. */
|
||||
LIBMDBX_API int mdbx_cursor_unbind(MDBX_cursor *cursor);
|
||||
|
||||
/** \brief Create a cursor handle for the specified transaction and DBI handle.
|
||||
* \ingroup c_cursors
|
||||
*
|
||||
|
7
mdbx.h++
7
mdbx.h++
@ -4223,6 +4223,9 @@ public:
|
||||
/// map handle.
|
||||
inline void bind(::mdbx::txn &txn, ::mdbx::map_handle map_handle);
|
||||
|
||||
/// \brief Unbind cursor from a transaction.
|
||||
inline void unbind();
|
||||
|
||||
/// \brief Returns the cursor's transaction.
|
||||
inline ::mdbx::txn txn() const;
|
||||
inline map_handle map() const;
|
||||
@ -6110,6 +6113,10 @@ inline void cursor::bind(::mdbx::txn &txn, ::mdbx::map_handle map_handle) {
|
||||
error::success_or_throw(::mdbx_cursor_bind(txn, handle_, map_handle.dbi));
|
||||
}
|
||||
|
||||
inline void cursor::unbind() {
|
||||
error::success_or_throw(::mdbx_cursor_unbind(handle_));
|
||||
}
|
||||
|
||||
inline txn cursor::txn() const {
|
||||
MDBX_txn *txn = ::mdbx_cursor_txn(handle_);
|
||||
error::throw_on_nullptr(txn, MDBX_EINVAL);
|
||||
|
64
src/core.c
64
src/core.c
@ -18846,6 +18846,38 @@ void *mdbx_cursor_get_userctx(const MDBX_cursor *mc) {
|
||||
return couple->mc_userctx;
|
||||
}
|
||||
|
||||
int mdbx_cursor_unbind(MDBX_cursor *mc) {
|
||||
if (unlikely(!mc))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
if (unlikely(mc->mc_signature != MDBX_MC_LIVE))
|
||||
return (mc->mc_signature == MDBX_MC_READY4CLOSE) ? MDBX_SUCCESS
|
||||
: MDBX_EBADSIGN;
|
||||
|
||||
if (unlikely(mc->mc_backup)) /* Cursor from parent transaction */
|
||||
return MDBX_EINVAL;
|
||||
|
||||
eASSERT(nullptr, mc->mc_txn && mc->mc_txn->mt_signature == MDBX_MT_SIGNATURE);
|
||||
cASSERT(mc, mc->mc_signature == MDBX_MC_LIVE);
|
||||
cASSERT(mc, !mc->mc_backup);
|
||||
if (unlikely(!mc->mc_txn || mc->mc_txn->mt_signature != MDBX_MT_SIGNATURE)) {
|
||||
ERROR("Wrong cursor's transaction %p 0x%x",
|
||||
__Wpedantic_format_voidptr(mc->mc_txn),
|
||||
mc->mc_txn ? mc->mc_txn->mt_signature : 0);
|
||||
return MDBX_PROBLEM;
|
||||
}
|
||||
if (mc->mc_flags & C_UNTRACK) {
|
||||
MDBX_cursor **prev = &mc->mc_txn->mt_cursors[mc->mc_dbi];
|
||||
while (*prev && *prev != mc)
|
||||
prev = &(*prev)->mc_next;
|
||||
cASSERT(mc, *prev == mc);
|
||||
*prev = mc->mc_next;
|
||||
}
|
||||
mc->mc_signature = MDBX_MC_READY4CLOSE;
|
||||
mc->mc_flags = 0;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
|
||||
if (unlikely(!mc))
|
||||
return MDBX_EINVAL;
|
||||
@ -18871,10 +18903,10 @@ int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
|
||||
mc->mc_txn != txn))
|
||||
return MDBX_EINVAL;
|
||||
|
||||
assert(mc->mc_db == &txn->mt_dbs[dbi]);
|
||||
assert(mc->mc_dbx == &txn->mt_dbxs[dbi]);
|
||||
assert(mc->mc_dbi == dbi);
|
||||
assert(mc->mc_dbistate == &txn->mt_dbistate[dbi]);
|
||||
cASSERT(mc, mc->mc_db == &txn->mt_dbs[dbi]);
|
||||
cASSERT(mc, mc->mc_dbx == &txn->mt_dbxs[dbi]);
|
||||
cASSERT(mc, mc->mc_dbi == dbi);
|
||||
cASSERT(mc, mc->mc_dbistate == &txn->mt_dbistate[dbi]);
|
||||
return likely(mc->mc_dbi == dbi &&
|
||||
/* paranoia */ mc->mc_signature == MDBX_MC_LIVE &&
|
||||
mc->mc_txn == txn)
|
||||
@ -18883,27 +18915,9 @@ int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
|
||||
}
|
||||
|
||||
if (mc->mc_signature == MDBX_MC_LIVE) {
|
||||
if (unlikely(!mc->mc_txn ||
|
||||
mc->mc_txn->mt_signature != MDBX_MT_SIGNATURE)) {
|
||||
ERROR("Wrong cursor's transaction %p 0x%x",
|
||||
__Wpedantic_format_voidptr(mc->mc_txn),
|
||||
mc->mc_txn ? mc->mc_txn->mt_signature : 0);
|
||||
return MDBX_PROBLEM;
|
||||
}
|
||||
if (mc->mc_flags & C_UNTRACK) {
|
||||
MDBX_cursor **prev = &mc->mc_txn->mt_cursors[mc->mc_dbi];
|
||||
while (*prev && *prev != mc)
|
||||
prev = &(*prev)->mc_next;
|
||||
cASSERT(mc, *prev == mc);
|
||||
*prev = mc->mc_next;
|
||||
}
|
||||
mc->mc_signature = MDBX_MC_READY4CLOSE;
|
||||
mc->mc_flags = 0;
|
||||
mc->mc_dbi = UINT_MAX;
|
||||
mc->mc_next = NULL;
|
||||
mc->mc_db = NULL;
|
||||
mc->mc_dbx = NULL;
|
||||
mc->mc_dbistate = NULL;
|
||||
rc = mdbx_cursor_unbind(mc);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
}
|
||||
cASSERT(mc, !(mc->mc_flags & C_UNTRACK));
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user