mirror of
https://github.com/isar/libmdbx.git
synced 2025-04-01 02:32:57 +08:00
mdbx: запрещение unbind/close курсоров для вложенных транзакций.
This commit is contained in:
parent
4fcfb07b97
commit
b0665f7016
20
mdbx.h
20
mdbx.h
@ -5124,6 +5124,10 @@ MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API void *mdbx_cursor_get_userctx(const MDBX_
|
||||
* the same table handle as it was created with. This may be done whether the
|
||||
* previous transaction is live or dead.
|
||||
*
|
||||
* If the transaction is nested, then the cursor should not be used in its parent transaction.
|
||||
* Otherwise it is no way to restore state if this nested transaction will be aborted,
|
||||
* nor impossible to define the expected behavior.
|
||||
*
|
||||
* \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
|
||||
@ -5148,6 +5152,10 @@ LIBMDBX_API int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *cursor, MDBX_dbi db
|
||||
* the original DBI-handle internally. Thus it could be renewed with any running
|
||||
* transaction or closed.
|
||||
*
|
||||
* If the transaction is nested, then the cursor should not be used in its parent transaction.
|
||||
* Otherwise it is no way to restore state if this nested transaction will be aborted,
|
||||
* nor impossible to define the expected behavior.
|
||||
*
|
||||
* \see mdbx_cursor_renew()
|
||||
* \see mdbx_cursor_bind()
|
||||
* \see mdbx_cursor_close()
|
||||
@ -5232,6 +5240,10 @@ LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
|
||||
* Unbinds either closes all cursors associated (opened or renewed) with
|
||||
* a given transaction in a bulk with minimal overhead.
|
||||
*
|
||||
* A transaction should not be nested, since in this case no way to restore
|
||||
* state if this nested transaction will be aborted, nor impossible to define
|
||||
* the expected behavior.
|
||||
*
|
||||
* \see mdbx_cursor_unbind()
|
||||
* \see mdbx_cursor_close()
|
||||
*
|
||||
@ -5245,7 +5257,7 @@ LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
|
||||
* some possible errors are:
|
||||
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
|
||||
* by current thread.
|
||||
* \retval MDBX_BAD_TXN Given transaction is invalid or has
|
||||
* \retval MDBX_BAD_TXN Given transaction is invalid, nested or has
|
||||
* a child/nested transaction transaction. */
|
||||
LIBMDBX_API int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind, size_t *count);
|
||||
|
||||
@ -5255,6 +5267,10 @@ LIBMDBX_API int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind
|
||||
* Unbinds either closes all cursors associated (opened or renewed) with
|
||||
* a given transaction in a bulk with minimal overhead.
|
||||
*
|
||||
* A transaction should not be nested, since in this case no way to restore
|
||||
* state if this nested transaction will be aborted, nor impossible to define
|
||||
* the expected behavior.
|
||||
*
|
||||
* \see mdbx_cursor_unbind()
|
||||
* \see mdbx_cursor_close()
|
||||
*
|
||||
@ -5266,7 +5282,7 @@ LIBMDBX_API int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind
|
||||
* some possible errors are:
|
||||
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
|
||||
* by current thread.
|
||||
* \retval MDBX_BAD_TXN Given transaction is invalid or has
|
||||
* \retval MDBX_BAD_TXN Given transaction is invalid, nested or has
|
||||
* a child/nested transaction transaction. */
|
||||
LIBMDBX_INLINE_API(int, mdbx_txn_release_all_cursors, (const MDBX_txn *txn, bool unbind)) {
|
||||
return mdbx_txn_release_all_cursors_ex(txn, unbind, NULL);
|
||||
|
@ -44,8 +44,10 @@ int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
|
||||
if (unlikely(!mc))
|
||||
return LOG_IFERR(MDBX_EINVAL);
|
||||
|
||||
if (unlikely(mc->signature != cur_signature_ready4dispose && mc->signature != cur_signature_live))
|
||||
return LOG_IFERR(MDBX_EBADSIGN);
|
||||
if (unlikely(mc->signature != cur_signature_ready4dispose && mc->signature != cur_signature_live)) {
|
||||
int rc = (mc->signature == cur_signature_wait4eot) ? MDBX_EINVAL : MDBX_EBADSIGN;
|
||||
return LOG_IFERR(rc);
|
||||
}
|
||||
|
||||
int rc = check_txn(txn, MDBX_TXN_BLOCKED);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
@ -58,24 +60,12 @@ int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *mc, MDBX_dbi dbi) {
|
||||
if (unlikely(dbi == FREE_DBI && !(txn->flags & MDBX_TXN_RDONLY)))
|
||||
return LOG_IFERR(MDBX_EACCESS);
|
||||
|
||||
if (unlikely(mc->backup)) /* Cursor from parent transaction */ {
|
||||
cASSERT(mc, mc->signature == cur_signature_live);
|
||||
if (unlikely(cursor_dbi(mc) != dbi ||
|
||||
/* paranoia */ mc->signature != cur_signature_live || mc->txn != txn))
|
||||
return LOG_IFERR(MDBX_EINVAL);
|
||||
|
||||
cASSERT(mc, mc->tree == &txn->dbs[dbi]);
|
||||
cASSERT(mc, mc->clc == &txn->env->kvs[dbi].clc);
|
||||
cASSERT(mc, cursor_dbi(mc) == dbi);
|
||||
return likely(cursor_dbi(mc) == dbi &&
|
||||
/* paranoia */ mc->signature == cur_signature_live && mc->txn == txn)
|
||||
? MDBX_SUCCESS
|
||||
: LOG_IFERR(MDBX_EINVAL) /* Disallow change DBI in nested
|
||||
transactions */
|
||||
;
|
||||
}
|
||||
if (unlikely(mc->backup)) /* Cursor from parent transaction */
|
||||
LOG_IFERR(MDBX_EINVAL);
|
||||
|
||||
if (mc->signature == cur_signature_live) {
|
||||
if (mc->txn == txn && cursor_dbi(mc) == dbi)
|
||||
return MDBX_SUCCESS;
|
||||
rc = mdbx_cursor_unbind(mc);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return rc;
|
||||
@ -100,25 +90,22 @@ int mdbx_cursor_unbind(MDBX_cursor *mc) {
|
||||
return (mc->signature == cur_signature_ready4dispose) ? MDBX_SUCCESS : LOG_IFERR(MDBX_EBADSIGN);
|
||||
|
||||
if (unlikely(mc->backup)) /* Cursor from parent transaction */
|
||||
/* TODO: реализовать при переходе на двусвязный список курсоров */
|
||||
return LOG_IFERR(MDBX_EINVAL);
|
||||
|
||||
eASSERT(nullptr, mc->txn && mc->txn->signature == txn_signature);
|
||||
cASSERT(mc, mc->signature == cur_signature_live);
|
||||
cASSERT(mc, !mc->backup);
|
||||
if (unlikely(!mc->txn || mc->txn->signature != txn_signature)) {
|
||||
ERROR("Wrong cursor's transaction %p 0x%x", __Wpedantic_format_voidptr(mc->txn), mc->txn ? mc->txn->signature : 0);
|
||||
return LOG_IFERR(MDBX_PROBLEM);
|
||||
}
|
||||
|
||||
if (mc->next != mc) {
|
||||
const size_t dbi = (kvx_t *)mc->clc - mc->txn->env->kvs;
|
||||
cASSERT(mc, cursor_dbi(mc) == dbi);
|
||||
const size_t dbi = cursor_dbi(mc);
|
||||
cASSERT(mc, dbi < mc->txn->n_dbi);
|
||||
cASSERT(mc, &mc->txn->env->kvs[dbi].clc == mc->clc);
|
||||
if (dbi < mc->txn->n_dbi) {
|
||||
MDBX_cursor **prev = &mc->txn->cursors[dbi];
|
||||
while (*prev) {
|
||||
while (/* *prev && */ *prev != mc) {
|
||||
ENSURE(mc->txn->env, (*prev)->signature == cur_signature_live || (*prev)->signature == cur_signature_wait4eot);
|
||||
if (*prev == mc)
|
||||
break;
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
cASSERT(mc, *prev == mc);
|
||||
@ -219,30 +206,34 @@ again:
|
||||
|
||||
int mdbx_txn_release_all_cursors_ex(const MDBX_txn *txn, bool unbind, size_t *count) {
|
||||
int rc = check_txn(txn, MDBX_TXN_FINISHED | MDBX_TXN_HAS_CHILD);
|
||||
if (unlikely(rc != MDBX_SUCCESS))
|
||||
return LOG_IFERR(rc);
|
||||
if (unlikely(txn->parent)) {
|
||||
rc = MDBX_BAD_TXN;
|
||||
ERROR("%s, err %d", "must not unbind or close cursors for a nested txn", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
size_t n = 0;
|
||||
if (likely(rc == MDBX_SUCCESS)) {
|
||||
TXN_FOREACH_DBI_FROM(txn, i, MAIN_DBI) {
|
||||
while (txn->cursors[i]) {
|
||||
++n;
|
||||
MDBX_cursor *mc = txn->cursors[i];
|
||||
ENSURE(nullptr, mc->signature == cur_signature_live && (mc->next != mc) && !mc->backup);
|
||||
txn->cursors[i] = mc->next;
|
||||
mc->next = mc;
|
||||
if (unbind) {
|
||||
be_poor(mc);
|
||||
mc->signature = cur_signature_ready4dispose;
|
||||
} else {
|
||||
mc->signature = 0;
|
||||
osal_free(mc);
|
||||
}
|
||||
TXN_FOREACH_DBI_FROM(txn, i, MAIN_DBI) {
|
||||
while (txn->cursors[i]) {
|
||||
++n;
|
||||
MDBX_cursor *mc = txn->cursors[i];
|
||||
ENSURE(nullptr, mc->signature == cur_signature_live && (mc->next != mc) && !mc->backup);
|
||||
txn->cursors[i] = mc->next;
|
||||
mc->next = mc;
|
||||
if (unbind) {
|
||||
be_poor(mc);
|
||||
mc->signature = cur_signature_ready4dispose;
|
||||
} else {
|
||||
mc->signature = 0;
|
||||
osal_free(mc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_IFERR(rc);
|
||||
}
|
||||
if (count)
|
||||
*count = n;
|
||||
return rc;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
int mdbx_cursor_compare(const MDBX_cursor *l, const MDBX_cursor *r, bool ignore_multival) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user