mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-06 18:54:14 +08:00
mdbx: add MDBX_ALLDUPS & MDBX_UPSERT, rework handling of others.
Change-Id: I27d437540d883935d78242e4fc7e28951ab9f496
This commit is contained in:
parent
0671114bbb
commit
b095ad872c
67
mdbx.h
67
mdbx.h
@ -1233,20 +1233,28 @@ DEFINE_ENUM_FLAG_OPERATORS(MDBX_db_flags_t)
|
|||||||
* \ingroup c_crud
|
* \ingroup c_crud
|
||||||
* \see mdbx_put() \see mdbx_cursor_put() \see mdbx_replace() */
|
* \see mdbx_put() \see mdbx_cursor_put() \see mdbx_replace() */
|
||||||
enum MDBX_put_flags_t {
|
enum MDBX_put_flags_t {
|
||||||
MDBX_PUT_DEFAULTS = 0,
|
/** Upsertion by default (without any other flags) */
|
||||||
|
MDBX_UPSERT = 0,
|
||||||
|
|
||||||
/** For upsertion: Don't write if the key already exists. */
|
/** For insertion: Don't write if the key already exists. */
|
||||||
MDBX_NOOVERWRITE = UINT32_C(0x10),
|
MDBX_NOOVERWRITE = UINT32_C(0x10),
|
||||||
|
|
||||||
/** Only for \ref MDBX_DUPSORT. For upsertion: don't write if the key/data
|
/** Has effect only for \ref MDBX_DUPSORT databases.
|
||||||
* pair already exist. For deletion: remove all duplicate data items. */
|
* For upsertion: don't write if the key-value pair already exist.
|
||||||
|
* For deletion: remove all values for key. */
|
||||||
MDBX_NODUPDATA = UINT32_C(0x20),
|
MDBX_NODUPDATA = UINT32_C(0x20),
|
||||||
|
|
||||||
/** For upsertion: overwrite the current key/data pair.
|
/** For upsertion: overwrite the current key/data pair.
|
||||||
* MDBX allows this flag for \ref mdbx_put() for explicit overwrite/update
|
* MDBX allows this flag for \ref mdbx_put() for explicit overwrite/update
|
||||||
* without insertion. */
|
* without insertion.
|
||||||
|
* For deletion: remove only single entry at the current cursor position. */
|
||||||
MDBX_CURRENT = UINT32_C(0x40),
|
MDBX_CURRENT = UINT32_C(0x40),
|
||||||
|
|
||||||
|
/** Has effect only for \ref MDBX_DUPSORT databases.
|
||||||
|
* For deletion: remove all multi-values (aka duplicates) for given key.
|
||||||
|
* For upsertion: replace all multi-values for given key with a new one. */
|
||||||
|
MDBX_ALLDUPS = UINT32_C(0x80),
|
||||||
|
|
||||||
/** For upsertion: Just reserve space for data, don't copy it.
|
/** For upsertion: Just reserve space for data, don't copy it.
|
||||||
* Return a pointer to the reserved space. */
|
* Return a pointer to the reserved space. */
|
||||||
MDBX_RESERVE = UINT32_C(0x10000),
|
MDBX_RESERVE = UINT32_C(0x10000),
|
||||||
@ -1255,11 +1263,13 @@ enum MDBX_put_flags_t {
|
|||||||
* Don't split full pages, continue on a new instead. */
|
* Don't split full pages, continue on a new instead. */
|
||||||
MDBX_APPEND = UINT32_C(0x20000),
|
MDBX_APPEND = UINT32_C(0x20000),
|
||||||
|
|
||||||
/** Duplicate data is being appended.
|
/** Has effect only for \ref MDBX_DUPSORT databases.
|
||||||
|
* Duplicate data is being appended.
|
||||||
* Don't split full pages, continue on a new instead. */
|
* Don't split full pages, continue on a new instead. */
|
||||||
MDBX_APPENDDUP = UINT32_C(0x40000),
|
MDBX_APPENDDUP = UINT32_C(0x40000),
|
||||||
|
|
||||||
/** Store multiple data items in one call. Only for \ref MDBX_DUPFIXED. */
|
/** Only for \ref MDBX_DUPFIXED.
|
||||||
|
* Store multiple data items in one call. */
|
||||||
MDBX_MULTIPLE = UINT32_C(0x80000)
|
MDBX_MULTIPLE = UINT32_C(0x80000)
|
||||||
};
|
};
|
||||||
#ifndef __cplusplus
|
#ifndef __cplusplus
|
||||||
@ -3186,7 +3196,7 @@ LIBMDBX_API int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi,
|
|||||||
* This parameter must be set to 0 or by bitwise OR'ing
|
* This parameter must be set to 0 or by bitwise OR'ing
|
||||||
* together one or more of the values described here:
|
* together one or more of the values described here:
|
||||||
* - \ref MDBX_NODUPDATA
|
* - \ref MDBX_NODUPDATA
|
||||||
* Enter the new key/data pair only if it does not already appear
|
* Enter the new key-value pair only if it does not already appear
|
||||||
* in the database. This flag may only be specified if the database
|
* in the database. This flag may only be specified if the database
|
||||||
* was opened with \ref MDBX_DUPSORT. The function will return
|
* was opened with \ref MDBX_DUPSORT. The function will return
|
||||||
* \ref MDBX_KEYEXIST if the key/data pair already appears in the database.
|
* \ref MDBX_KEYEXIST if the key/data pair already appears in the database.
|
||||||
@ -3201,7 +3211,9 @@ LIBMDBX_API int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi,
|
|||||||
* - \ref MDBX_CURRENT
|
* - \ref MDBX_CURRENT
|
||||||
* Update an single existing entry, but not add new ones. The function will
|
* Update an single existing entry, but not add new ones. The function will
|
||||||
* return \ref MDBX_NOTFOUND if the given key not exist in the database.
|
* return \ref MDBX_NOTFOUND if the given key not exist in the database.
|
||||||
* Or the \ref MDBX_EMULTIVAL in case duplicates for the given key.
|
* In case multi-values for the given key, with combination of
|
||||||
|
* the \ref MDBX_ALLDUPS will replace all multi-values,
|
||||||
|
* otherwise return the \ref MDBX_EMULTIVAL.
|
||||||
*
|
*
|
||||||
* - \ref MDBX_RESERVE
|
* - \ref MDBX_RESERVE
|
||||||
* Reserve space for data of the given size, but don't copy the given
|
* Reserve space for data of the given size, but don't copy the given
|
||||||
@ -3221,6 +3233,21 @@ LIBMDBX_API int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi,
|
|||||||
* - \ref MDBX_APPENDDUP
|
* - \ref MDBX_APPENDDUP
|
||||||
* As above, but for sorted dup data.
|
* As above, but for sorted dup data.
|
||||||
*
|
*
|
||||||
|
* - \ref MDBX_MULTIPLE
|
||||||
|
* Store multiple contiguous data elements in a single request. This flag
|
||||||
|
* may only be specified if the database was opened with
|
||||||
|
* \ref MDBX_DUPFIXED. With combination the \ref MDBX_ALLDUPS
|
||||||
|
* will replace all multi-values.
|
||||||
|
* The data argument must be an array of two \ref MDBX_val. The `iov_len`
|
||||||
|
* of the first \ref MDBX_val must be the size of a single data element.
|
||||||
|
* The `iov_base` of the first \ref MDBX_val must point to the beginning
|
||||||
|
* of the array of contiguous data elements which must be properly aligned
|
||||||
|
* in case of database with \ref MDBX_INTEGERDUP flag.
|
||||||
|
* The `iov_len` of the second \ref MDBX_val must be the count of the
|
||||||
|
* number of data elements to store. On return this field will be set to
|
||||||
|
* the count of the number of elements actually written. The `iov_base` of
|
||||||
|
* the second \ref MDBX_val is unused.
|
||||||
|
*
|
||||||
* \returns A non-zero error value on failure and 0 on success,
|
* \returns A non-zero error value on failure and 0 on success,
|
||||||
* some possible errors are:
|
* some possible errors are:
|
||||||
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
|
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
|
||||||
@ -3433,14 +3460,15 @@ LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key,
|
|||||||
* - \ref MDBX_CURRENT
|
* - \ref MDBX_CURRENT
|
||||||
* Replace the item at the current cursor position. The key parameter
|
* Replace the item at the current cursor position. The key parameter
|
||||||
* must still be provided, and must match it, otherwise the function
|
* must still be provided, and must match it, otherwise the function
|
||||||
* return \ref MDBX_EKEYMISMATCH.
|
* return \ref MDBX_EKEYMISMATCH. With combination the
|
||||||
|
* \ref MDBX_ALLDUPS will replace all multi-values.
|
||||||
*
|
*
|
||||||
* \note MDBX allows (unlike LMDB) you to change the size of the data and
|
* \note MDBX allows (unlike LMDB) you to change the size of the data and
|
||||||
* automatically handles reordering for sorted duplicates
|
* automatically handles reordering for sorted duplicates
|
||||||
* (see \ref MDBX_DUPSORT).
|
* (see \ref MDBX_DUPSORT).
|
||||||
*
|
*
|
||||||
* - \ref MDBX_NODUPDATA
|
* - \ref MDBX_NODUPDATA
|
||||||
* Enter the new key/data pair only if it does not already appear in the
|
* Enter the new key-value pair only if it does not already appear in the
|
||||||
* database. This flag may only be specified if the database was opened
|
* database. This flag may only be specified if the database was opened
|
||||||
* with \ref MDBX_DUPSORT. The function will return \ref MDBX_KEYEXIST
|
* with \ref MDBX_DUPSORT. The function will return \ref MDBX_KEYEXIST
|
||||||
* if the key/data pair already appears in the database.
|
* if the key/data pair already appears in the database.
|
||||||
@ -3471,10 +3499,13 @@ LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key,
|
|||||||
* - \ref MDBX_MULTIPLE
|
* - \ref MDBX_MULTIPLE
|
||||||
* Store multiple contiguous data elements in a single request. This flag
|
* Store multiple contiguous data elements in a single request. This flag
|
||||||
* may only be specified if the database was opened with
|
* may only be specified if the database was opened with
|
||||||
* \ref MDBX_DUPFIXED. The data argument must be an array of two
|
* \ref MDBX_DUPFIXED. With combination the \ref MDBX_ALLDUPS
|
||||||
* \ref MDBX_val. The `iov_len` of the first \ref MDBX_val must be the size
|
* will replace all multi-values.
|
||||||
* of a single data element. The `iov_base` of the first \ref MDBX_val must
|
* The data argument must be an array of two \ref MDBX_val. The `iov_len`
|
||||||
* point to the beginning of the array of contiguous data elements.
|
* of the first \ref MDBX_val must be the size of a single data element.
|
||||||
|
* The `iov_base` of the first \ref MDBX_val must point to the beginning
|
||||||
|
* of the array of contiguous data elements which must be properly aligned
|
||||||
|
* in case of database with \ref MDBX_INTEGERDUP flag.
|
||||||
* The `iov_len` of the second \ref MDBX_val must be the count of the
|
* The `iov_len` of the second \ref MDBX_val must be the count of the
|
||||||
* number of data elements to store. On return this field will be set to
|
* number of data elements to store. On return this field will be set to
|
||||||
* the count of the number of elements actually written. The `iov_base` of
|
* the count of the number of elements actually written. The `iov_base` of
|
||||||
@ -3505,9 +3536,11 @@ LIBMDBX_API int mdbx_cursor_put(MDBX_cursor *cursor, const MDBX_val *key,
|
|||||||
*
|
*
|
||||||
* \param [in] cursor A cursor handle returned by mdbx_cursor_open().
|
* \param [in] cursor A cursor handle returned by mdbx_cursor_open().
|
||||||
* \param [in] flags Options for this operation. This parameter must be set
|
* \param [in] flags Options for this operation. This parameter must be set
|
||||||
* to 0 or one of the values described here.
|
* to one of the values described here.
|
||||||
*
|
*
|
||||||
* - \ref MDBX_NODUPDATA
|
* - \ref MDBX_CURRENT Delete only single entry at current cursor position.
|
||||||
|
* - \ref MDBX_ALLDUPS
|
||||||
|
* or \ref MDBX_NODUPDATA (supported for compatibility)
|
||||||
* Delete all of the data items for the current key. This flag has effect
|
* Delete all of the data items for the current key. This flag has effect
|
||||||
* only for database(s) was created with \ref MDBX_DUPSORT.
|
* only for database(s) was created with \ref MDBX_DUPSORT.
|
||||||
*
|
*
|
||||||
|
15
mdbx.h++
15
mdbx.h++
@ -1172,13 +1172,9 @@ struct LIBMDBX_API_TYPE map_handle {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum put_mode {
|
enum put_mode {
|
||||||
insert = MDBX_NOOVERWRITE | MDBX_NODUPDATA,
|
insert = MDBX_NOOVERWRITE,
|
||||||
#if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER < 1910
|
upsert = MDBX_UPSERT,
|
||||||
update = uint32_t(MDBX_CURRENT) | uint32_t(MDBX_NODUPDATA),
|
update = MDBX_CURRENT,
|
||||||
#else
|
|
||||||
update = MDBX_CURRENT | MDBX_NODUPDATA,
|
|
||||||
#endif
|
|
||||||
upsert = MDBX_NODUPDATA
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LIBMDBX_API_TYPE env_ref {
|
class LIBMDBX_API_TYPE env_ref {
|
||||||
@ -2912,9 +2908,8 @@ inline void txn_ref::update(map_handle map, const slice &key,
|
|||||||
|
|
||||||
inline bool txn_ref::try_update(map_handle map, const slice &key,
|
inline bool txn_ref::try_update(map_handle map, const slice &key,
|
||||||
const slice &value) {
|
const slice &value) {
|
||||||
const int err =
|
const int err = ::mdbx_put(handle_, map.dbi, &key,
|
||||||
::mdbx_put(handle_, map.dbi, &key, const_cast<slice *>(&value),
|
const_cast<slice *>(&value), MDBX_CURRENT);
|
||||||
MDBX_CURRENT | MDBX_NODUPDATA);
|
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case MDBX_SUCCESS:
|
case MDBX_SUCCESS:
|
||||||
return true;
|
return true;
|
||||||
|
196
src/core.c
196
src/core.c
@ -10689,6 +10689,12 @@ static int __hot cmp_lenfast(const MDBX_val *a, const MDBX_val *b) {
|
|||||||
return likely(diff) ? diff : memcmp(a->iov_base, b->iov_base, a->iov_len);
|
return likely(diff) ? diff : memcmp(a->iov_base, b->iov_base, a->iov_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool unsure_equal(MDBX_cmp_func cmp, const MDBX_val *a,
|
||||||
|
const MDBX_val *b) {
|
||||||
|
return cmp == cmp_lenfast || cmp == cmp_lexical || cmp == cmp_reverse ||
|
||||||
|
cmp == cmp_int_unaligned || cmp_lenfast(a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Search for key within a page, using binary search.
|
/* Search for key within a page, using binary search.
|
||||||
* Returns the smallest entry larger or equal to the key.
|
* Returns the smallest entry larger or equal to the key.
|
||||||
* If exactp is non-null, stores whether the found entry was an exact match
|
* If exactp is non-null, stores whether the found entry was an exact match
|
||||||
@ -12078,7 +12084,9 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
|
|||||||
|
|
||||||
/* Check this first so counter will always be zero on any early failures. */
|
/* Check this first so counter will always be zero on any early failures. */
|
||||||
size_t mcount = 0, dcount = 0;
|
size_t mcount = 0, dcount = 0;
|
||||||
if (flags & MDBX_MULTIPLE) {
|
if (unlikely(flags & MDBX_MULTIPLE)) {
|
||||||
|
if (unlikely(flags & MDBX_RESERVE))
|
||||||
|
return MDBX_EINVAL;
|
||||||
if (unlikely(!F_ISSET(mc->mc_db->md_flags, MDBX_DUPFIXED)))
|
if (unlikely(!F_ISSET(mc->mc_db->md_flags, MDBX_DUPFIXED)))
|
||||||
return MDBX_INCOMPATIBLE;
|
return MDBX_INCOMPATIBLE;
|
||||||
dcount = data[1].iov_len;
|
dcount = data[1].iov_len;
|
||||||
@ -12182,6 +12190,8 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
|
|||||||
|
|
||||||
int dupdata_flag = 0;
|
int dupdata_flag = 0;
|
||||||
if ((flags & MDBX_CURRENT) != 0 && (mc->mc_flags & C_SUB) == 0) {
|
if ((flags & MDBX_CURRENT) != 0 && (mc->mc_flags & C_SUB) == 0) {
|
||||||
|
if (unlikely(flags & (MDBX_APPEND | MDBX_NOOVERWRITE)))
|
||||||
|
return MDBX_EINVAL;
|
||||||
/* Опция MDBX_CURRENT означает, что запрошено обновление текущей записи,
|
/* Опция MDBX_CURRENT означает, что запрошено обновление текущей записи,
|
||||||
* на которой сейчас стоит курсор. Проверяем что переданный ключ совпадает
|
* на которой сейчас стоит курсор. Проверяем что переданный ключ совпадает
|
||||||
* со значением в текущей позиции курсора.
|
* со значением в текущей позиции курсора.
|
||||||
@ -12194,6 +12204,9 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
|
|||||||
if (mc->mc_dbx->md_cmp(key, ¤t_key) != 0)
|
if (mc->mc_dbx->md_cmp(key, ¤t_key) != 0)
|
||||||
return MDBX_EKEYMISMATCH;
|
return MDBX_EKEYMISMATCH;
|
||||||
|
|
||||||
|
if (unlikely((flags & MDBX_MULTIPLE)))
|
||||||
|
goto drop_current;
|
||||||
|
|
||||||
if (F_ISSET(mc->mc_db->md_flags, MDBX_DUPSORT)) {
|
if (F_ISSET(mc->mc_db->md_flags, MDBX_DUPSORT)) {
|
||||||
MDBX_node *node = page_node(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
|
MDBX_node *node = page_node(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
|
||||||
if (F_ISSET(node_flags(node), F_DUPDATA)) {
|
if (F_ISSET(node_flags(node), F_DUPDATA)) {
|
||||||
@ -12201,24 +12214,31 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
|
|||||||
mc->mc_xcursor != NULL &&
|
mc->mc_xcursor != NULL &&
|
||||||
(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED));
|
(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED));
|
||||||
/* Если за ключом более одного значения, либо если размер данных
|
/* Если за ключом более одного значения, либо если размер данных
|
||||||
* отличается, то вместо inplace обновления требуется удаление и
|
* отличается, то вместо обновления требуется удаление и
|
||||||
* последующая вставка. */
|
* последующая вставка. */
|
||||||
if (mc->mc_xcursor->mx_db.md_entries > 1 ||
|
if (mc->mc_xcursor->mx_db.md_entries > 1 ||
|
||||||
current_data.iov_len != data->iov_len) {
|
current_data.iov_len != data->iov_len) {
|
||||||
rc = mdbx_cursor_del(mc, 0);
|
drop_current:
|
||||||
if (rc != MDBX_SUCCESS)
|
rc = mdbx_cursor_del(mc, flags & MDBX_ALLDUPS);
|
||||||
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
return rc;
|
return rc;
|
||||||
flags -= MDBX_CURRENT;
|
flags -= MDBX_CURRENT;
|
||||||
|
goto skip_check_samedata;
|
||||||
}
|
}
|
||||||
} else if (unlikely(node_size(key, data) >
|
} else if (unlikely(node_size(key, data) >
|
||||||
/* See note inside leaf_size() */
|
/* See note inside leaf_size() */
|
||||||
env->me_branch_nodemax)) {
|
env->me_branch_nodemax)) {
|
||||||
rc = mdbx_cursor_del(mc, 0);
|
rc = mdbx_cursor_del(mc, 0);
|
||||||
if (rc != MDBX_SUCCESS)
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
return rc;
|
return rc;
|
||||||
flags -= MDBX_CURRENT;
|
flags -= MDBX_CURRENT;
|
||||||
|
goto skip_check_samedata;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!(flags & MDBX_RESERVE) &&
|
||||||
|
unlikely(cmp_lenfast(¤t_data, data) == 0))
|
||||||
|
return MDBX_SUCCESS /* the same data, nothing to update */;
|
||||||
|
skip_check_samedata:;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mc->mc_db->md_root == P_INVALID) {
|
if (mc->mc_db->md_root == P_INVALID) {
|
||||||
@ -12228,38 +12248,65 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
|
|||||||
mc->mc_flags &= ~C_INITIALIZED;
|
mc->mc_flags &= ~C_INITIALIZED;
|
||||||
rc = MDBX_NO_ROOT;
|
rc = MDBX_NO_ROOT;
|
||||||
} else if ((flags & MDBX_CURRENT) == 0) {
|
} else if ((flags & MDBX_CURRENT) == 0) {
|
||||||
int exact = 0;
|
int exact = false;
|
||||||
if ((flags & MDBX_APPEND) != 0 && mc->mc_db->md_entries > 0) {
|
if ((flags & MDBX_APPEND) && mc->mc_db->md_entries > 0) {
|
||||||
rc = mdbx_cursor_last(mc, &dkey, &olddata);
|
rc = mdbx_cursor_last(mc, &dkey, &olddata);
|
||||||
if (rc == 0) {
|
if (likely(rc == MDBX_SUCCESS)) {
|
||||||
rc = mc->mc_dbx->md_cmp(key, &dkey);
|
rc = mc->mc_dbx->md_cmp(key, &dkey);
|
||||||
if (rc > 0) {
|
if (likely(rc > 0)) {
|
||||||
|
mc->mc_ki[mc->mc_top]++; /* step forward for appending */
|
||||||
rc = MDBX_NOTFOUND;
|
rc = MDBX_NOTFOUND;
|
||||||
mc->mc_ki[mc->mc_top]++;
|
} else {
|
||||||
} else if (unlikely(rc < 0 || (flags & MDBX_APPENDDUP) == 0)) {
|
if (unlikely(rc != 0 || !(flags & MDBX_APPENDDUP)))
|
||||||
/* new key is <= last key */
|
/* new-key < last-key
|
||||||
rc = MDBX_EKEYMISMATCH;
|
* or new-key == last-key without MDBX_APPENDDUP */
|
||||||
|
return MDBX_EKEYMISMATCH;
|
||||||
|
exact = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rc = mdbx_cursor_set(mc, (MDBX_val *)key, &olddata, MDBX_SET, &exact);
|
rc = mdbx_cursor_set(mc, (MDBX_val *)key, &olddata, MDBX_SET, &exact);
|
||||||
}
|
}
|
||||||
if ((flags & MDBX_NOOVERWRITE) &&
|
if (likely(rc == MDBX_SUCCESS)) {
|
||||||
(rc == MDBX_SUCCESS || rc == MDBX_EKEYMISMATCH)) {
|
if (exact) {
|
||||||
|
if (unlikely(flags & MDBX_NOOVERWRITE)) {
|
||||||
mdbx_debug("duplicate key [%s]", DKEY(key));
|
mdbx_debug("duplicate key [%s]", DKEY(key));
|
||||||
*data = olddata;
|
*data = olddata;
|
||||||
return MDBX_KEYEXIST;
|
return MDBX_KEYEXIST;
|
||||||
}
|
}
|
||||||
if (likely(rc == MDBX_SUCCESS)) {
|
if (unlikely(mc->mc_flags & C_SUB)) {
|
||||||
if (exact) {
|
/* nested subtree of DUPSORT-database with the same key,
|
||||||
if (mc->mc_flags & C_SUB) {
|
* nothing to update */
|
||||||
mdbx_assert(env, data->iov_len == 0);
|
mdbx_assert(env, data->iov_len == 0 && olddata.iov_len == 0);
|
||||||
return (flags & MDBX_NODUPDATA) ? MDBX_KEYEXIST : MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
|
}
|
||||||
|
if (unlikely(flags & MDBX_ALLDUPS) && mc->mc_xcursor &&
|
||||||
|
(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) {
|
||||||
|
rc = mdbx_cursor_del(mc, MDBX_ALLDUPS);
|
||||||
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
|
return rc;
|
||||||
|
flags -= MDBX_ALLDUPS;
|
||||||
|
rc = MDBX_NOTFOUND;
|
||||||
|
exact = false;
|
||||||
|
} else /* checking for early exit without dirtying pages */
|
||||||
|
if (!(flags & (MDBX_RESERVE | MDBX_MULTIPLE)) &&
|
||||||
|
unlikely(mc->mc_dbx->md_dcmp(data, &olddata) == 0)) {
|
||||||
|
if (!mc->mc_xcursor)
|
||||||
|
/* the same data, nothing to update */
|
||||||
|
return MDBX_SUCCESS;
|
||||||
|
if (flags & MDBX_NODUPDATA)
|
||||||
|
return MDBX_KEYEXIST;
|
||||||
|
if (flags & MDBX_APPENDDUP)
|
||||||
|
return MDBX_EKEYMISMATCH;
|
||||||
|
if (likely(unsure_equal(mc->mc_dbx->md_dcmp, data, &olddata)))
|
||||||
|
/* data is match exactly byte-to-byte, nothing to update */
|
||||||
|
return MDBX_SUCCESS;
|
||||||
|
else {
|
||||||
|
/* The data has differences, but the user-provided comparator
|
||||||
|
* considers them equal. So continue update since called without.
|
||||||
|
* Continue to update since was called without MDBX_NODUPDATA. */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!(flags & MDBX_RESERVE) &&
|
|
||||||
unlikely(mc->mc_dbx->md_dcmp(data, &olddata) == 0))
|
|
||||||
return ((flags & MDBX_NODUPDATA) && mc->mc_xcursor) ? MDBX_KEYEXIST
|
|
||||||
: MDBX_SUCCESS;
|
|
||||||
}
|
}
|
||||||
} else if (unlikely(rc != MDBX_NOTFOUND))
|
} else if (unlikely(rc != MDBX_NOTFOUND))
|
||||||
return rc;
|
return rc;
|
||||||
@ -12269,7 +12316,7 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
|
|||||||
|
|
||||||
/* Cursor is positioned, check for room in the dirty list */
|
/* Cursor is positioned, check for room in the dirty list */
|
||||||
if (!nospill) {
|
if (!nospill) {
|
||||||
if (flags & MDBX_MULTIPLE) {
|
if (unlikely(flags & MDBX_MULTIPLE)) {
|
||||||
rdata = &xdata;
|
rdata = &xdata;
|
||||||
xdata.iov_len = data->iov_len * dcount;
|
xdata.iov_len = data->iov_len * dcount;
|
||||||
} else {
|
} else {
|
||||||
@ -12477,8 +12524,25 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
|
|||||||
/* Was a single item before, must convert now */
|
/* Was a single item before, must convert now */
|
||||||
if (!F_ISSET(node_flags(node), F_DUPDATA)) {
|
if (!F_ISSET(node_flags(node), F_DUPDATA)) {
|
||||||
|
|
||||||
/* Just overwrite the current item */
|
/* does data match? */
|
||||||
if (flags & MDBX_CURRENT) {
|
const int cmp = mc->mc_dbx->md_dcmp(data, &olddata);
|
||||||
|
if ((flags & MDBX_APPENDDUP) && unlikely(cmp <= 0))
|
||||||
|
return MDBX_EKEYMISMATCH;
|
||||||
|
if (cmp == 0) {
|
||||||
|
if (flags & MDBX_NODUPDATA)
|
||||||
|
return MDBX_KEYEXIST;
|
||||||
|
if (likely(unsure_equal(mc->mc_dbx->md_dcmp, data, &olddata))) {
|
||||||
|
/* data is match exactly byte-to-byte, nothing to update */
|
||||||
|
if (unlikely(flags & MDBX_MULTIPLE)) {
|
||||||
|
rc = MDBX_SUCCESS;
|
||||||
|
goto continue_multiple;
|
||||||
|
}
|
||||||
|
return MDBX_SUCCESS;
|
||||||
|
} else {
|
||||||
|
/* The data has differences, but the user-provided comparator
|
||||||
|
* considers them equal. So continue update since called without.
|
||||||
|
* Continue to update since was called without MDBX_NODUPDATA. */
|
||||||
|
}
|
||||||
mdbx_cassert(
|
mdbx_cassert(
|
||||||
mc,
|
mc,
|
||||||
node_size(key, data) <=
|
node_size(key, data) <=
|
||||||
@ -12486,11 +12550,8 @@ int mdbx_cursor_put(MDBX_cursor *mc, const MDBX_val *key, MDBX_val *data,
|
|||||||
goto current;
|
goto current;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* does data match? */
|
/* Just overwrite the current item */
|
||||||
if (!mc->mc_dbx->md_dcmp(data, &olddata)) {
|
if (flags & MDBX_CURRENT) {
|
||||||
if (unlikely(flags & (MDBX_NODUPDATA | MDBX_APPENDDUP)))
|
|
||||||
return MDBX_KEYEXIST;
|
|
||||||
/* overwrite it */
|
|
||||||
mdbx_cassert(
|
mdbx_cassert(
|
||||||
mc,
|
mc,
|
||||||
node_size(key, data) <=
|
node_size(key, data) <=
|
||||||
@ -12703,16 +12764,18 @@ new_sub:;
|
|||||||
xdata.iov_len = 0;
|
xdata.iov_len = 0;
|
||||||
xdata.iov_base = nullptr;
|
xdata.iov_base = nullptr;
|
||||||
MDBX_node *node = page_node(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
|
MDBX_node *node = page_node(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
|
||||||
if (flags & MDBX_CURRENT) {
|
#define SHIFT_MDBX_NODUPDATA_TO_MDBX_NOOVERWRITE 1
|
||||||
xflags = (flags & MDBX_NODUPDATA)
|
STATIC_ASSERT(
|
||||||
? MDBX_CURRENT | MDBX_NOOVERWRITE | MDBX_NOSPILL
|
(MDBX_NODUPDATA >> SHIFT_MDBX_NODUPDATA_TO_MDBX_NOOVERWRITE) ==
|
||||||
: MDBX_CURRENT | MDBX_NOSPILL;
|
MDBX_NOOVERWRITE);
|
||||||
} else {
|
xflags = MDBX_CURRENT | MDBX_NOSPILL |
|
||||||
|
((flags & MDBX_NODUPDATA) >>
|
||||||
|
SHIFT_MDBX_NODUPDATA_TO_MDBX_NOOVERWRITE);
|
||||||
|
if ((flags & MDBX_CURRENT) == 0) {
|
||||||
|
xflags -= MDBX_CURRENT;
|
||||||
rc2 = mdbx_xcursor_init1(mc, node);
|
rc2 = mdbx_xcursor_init1(mc, node);
|
||||||
if (unlikely(rc2 != MDBX_SUCCESS))
|
if (unlikely(rc2 != MDBX_SUCCESS))
|
||||||
return rc2;
|
return rc2;
|
||||||
xflags = (flags & MDBX_NODUPDATA) ? MDBX_NOOVERWRITE | MDBX_NOSPILL
|
|
||||||
: MDBX_NOSPILL;
|
|
||||||
}
|
}
|
||||||
if (sub_root)
|
if (sub_root)
|
||||||
mc->mc_xcursor->mx_cursor.mc_pg[0] = sub_root;
|
mc->mc_xcursor->mx_cursor.mc_pg[0] = sub_root;
|
||||||
@ -12774,6 +12837,7 @@ new_sub:;
|
|||||||
}
|
}
|
||||||
if (flags & MDBX_MULTIPLE) {
|
if (flags & MDBX_MULTIPLE) {
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
|
continue_multiple:
|
||||||
mcount++;
|
mcount++;
|
||||||
/* let caller know how many succeeded, if any */
|
/* let caller know how many succeeded, if any */
|
||||||
data[1].iov_len = mcount;
|
data[1].iov_len = mcount;
|
||||||
@ -12829,7 +12893,7 @@ int mdbx_cursor_del(MDBX_cursor *mc, MDBX_put_flags_t flags) {
|
|||||||
|
|
||||||
MDBX_node *node = page_node(mp, mc->mc_ki[mc->mc_top]);
|
MDBX_node *node = page_node(mp, mc->mc_ki[mc->mc_top]);
|
||||||
if (F_ISSET(node_flags(node), F_DUPDATA)) {
|
if (F_ISSET(node_flags(node), F_DUPDATA)) {
|
||||||
if (flags & MDBX_NODUPDATA) {
|
if (flags & (MDBX_ALLDUPS | /* for compatibility */ MDBX_NODUPDATA)) {
|
||||||
/* mdbx_cursor_del0() will subtract the final entry */
|
/* mdbx_cursor_del0() will subtract the final entry */
|
||||||
mc->mc_db->md_entries -= mc->mc_xcursor->mx_db.md_entries - 1;
|
mc->mc_db->md_entries -= mc->mc_xcursor->mx_db.md_entries - 1;
|
||||||
mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED;
|
mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED;
|
||||||
@ -14965,7 +15029,7 @@ static int mdbx_del0(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
|
|||||||
data = &rdata;
|
data = &rdata;
|
||||||
} else {
|
} else {
|
||||||
op = MDBX_SET;
|
op = MDBX_SET;
|
||||||
flags |= MDBX_NODUPDATA;
|
flags |= MDBX_ALLDUPS;
|
||||||
}
|
}
|
||||||
rc =
|
rc =
|
||||||
mdbx_cursor_set(&cx.outer, (MDBX_val *)key, (MDBX_val *)data, op, &exact);
|
mdbx_cursor_set(&cx.outer, (MDBX_val *)key, (MDBX_val *)data, op, &exact);
|
||||||
@ -15480,8 +15544,9 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
|
|||||||
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
|
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
|
||||||
return MDBX_BAD_DBI;
|
return MDBX_BAD_DBI;
|
||||||
|
|
||||||
if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_RESERVE |
|
if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_ALLDUPS |
|
||||||
MDBX_APPEND | MDBX_APPENDDUP | MDBX_CURRENT)))
|
MDBX_ALLDUPS | MDBX_RESERVE | MDBX_APPEND |
|
||||||
|
MDBX_APPENDDUP | MDBX_CURRENT | MDBX_MULTIPLE)))
|
||||||
return MDBX_EINVAL;
|
return MDBX_EINVAL;
|
||||||
|
|
||||||
if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
|
if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY | MDBX_TXN_BLOCKED)))
|
||||||
@ -15498,7 +15563,8 @@ int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, MDBX_val *data,
|
|||||||
if (flags & MDBX_CURRENT) {
|
if (flags & MDBX_CURRENT) {
|
||||||
rc = mdbx_cursor_get(&cx.outer, (MDBX_val *)key, NULL, MDBX_SET);
|
rc = mdbx_cursor_get(&cx.outer, (MDBX_val *)key, NULL, MDBX_SET);
|
||||||
if (likely(rc == MDBX_SUCCESS) &&
|
if (likely(rc == MDBX_SUCCESS) &&
|
||||||
(txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT)) {
|
(txn->mt_dbs[dbi].md_flags & MDBX_DUPSORT) &&
|
||||||
|
(flags & MDBX_ALLDUPS) == 0) {
|
||||||
/* LY: allows update (explicit overwrite) only for unique keys */
|
/* LY: allows update (explicit overwrite) only for unique keys */
|
||||||
MDBX_node *node = page_node(cx.outer.mc_pg[cx.outer.mc_top],
|
MDBX_node *node = page_node(cx.outer.mc_pg[cx.outer.mc_top],
|
||||||
cx.outer.mc_ki[cx.outer.mc_top]);
|
cx.outer.mc_ki[cx.outer.mc_top]);
|
||||||
@ -18277,8 +18343,9 @@ int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
|
|||||||
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
|
if (unlikely(!mdbx_txn_dbi_exists(txn, dbi, DBI_USRVALID)))
|
||||||
return MDBX_BAD_DBI;
|
return MDBX_BAD_DBI;
|
||||||
|
|
||||||
if (unlikely(flags & ~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_RESERVE |
|
if (unlikely(flags &
|
||||||
MDBX_APPEND | MDBX_APPENDDUP | MDBX_CURRENT)))
|
~(MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_ALLDUPS |
|
||||||
|
MDBX_RESERVE | MDBX_APPEND | MDBX_APPENDDUP | MDBX_CURRENT)))
|
||||||
return MDBX_EINVAL;
|
return MDBX_EINVAL;
|
||||||
|
|
||||||
MDBX_cursor_couple cx;
|
MDBX_cursor_couple cx;
|
||||||
@ -18302,16 +18369,6 @@ int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
|
|||||||
rc = mdbx_cursor_get(&cx.outer, &present_key, old_data, MDBX_GET_BOTH);
|
rc = mdbx_cursor_get(&cx.outer, &present_key, old_data, MDBX_GET_BOTH);
|
||||||
if (rc != MDBX_SUCCESS)
|
if (rc != MDBX_SUCCESS)
|
||||||
goto bailout;
|
goto bailout;
|
||||||
|
|
||||||
if (new_data) {
|
|
||||||
/* обновление конкретного дубликата */
|
|
||||||
/* (!!!) We can skip update only when a data exactly the same, but user's
|
|
||||||
* md_dcmp() may returns zero even data is NOT matches byte-to-byte.
|
|
||||||
* So to skip update the cmp_len fast() should be used. */
|
|
||||||
if (cmp_lenfast(old_data, new_data) == 0)
|
|
||||||
/* если данные совпадают, то ничего делать не надо */
|
|
||||||
goto bailout;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/* в old_data буфер для сохранения предыдущего значения */
|
/* в old_data буфер для сохранения предыдущего значения */
|
||||||
if (unlikely(new_data && old_data->iov_base == new_data->iov_base))
|
if (unlikely(new_data && old_data->iov_base == new_data->iov_base))
|
||||||
@ -18341,33 +18398,19 @@ int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
|
|||||||
goto bailout;
|
goto bailout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* если данные совпадают, то ничего делать не надо */
|
|
||||||
if (new_data && /* the cmp_lenfast() must be used, see above */
|
|
||||||
cmp_lenfast(&present_data, new_data) == 0) {
|
|
||||||
*old_data = *new_data;
|
|
||||||
goto bailout;
|
|
||||||
}
|
|
||||||
/* В оригинальной LMDB флажок MDBX_CURRENT здесь приведет
|
/* В оригинальной LMDB флажок MDBX_CURRENT здесь приведет
|
||||||
* к замене данных без учета MDBX_DUPSORT сортировки,
|
* к замене данных без учета MDBX_DUPSORT сортировки,
|
||||||
* но здесь это в любом случае допустимо, так как мы
|
* но здесь это в любом случае допустимо, так как мы
|
||||||
* проверили что для ключа есть только одно значение. */
|
* проверили что для ключа есть только одно значение. */
|
||||||
} else if ((flags & MDBX_NODUPDATA) &&
|
|
||||||
cx.outer.mc_dbx->md_dcmp(&present_data, new_data) == 0) {
|
|
||||||
/* если данные совпадают и установлен MDBX_NODUPDATA */
|
|
||||||
rc = MDBX_KEYEXIST;
|
|
||||||
goto bailout;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
/* если данные совпадают, то ничего делать не надо */
|
|
||||||
if (new_data && /* the cmp_lenfast() must be used, see comment above */
|
|
||||||
cmp_lenfast(&present_data, new_data) == 0) {
|
|
||||||
*old_data = *new_data;
|
|
||||||
goto bailout;
|
|
||||||
}
|
|
||||||
flags |= MDBX_CURRENT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_DIRTY(page)) {
|
if (IS_DIRTY(page)) {
|
||||||
|
if (new_data && cmp_lenfast(&present_data, new_data) == 0) {
|
||||||
|
/* если данные совпадают, то ничего делать не надо */
|
||||||
|
*old_data = *new_data;
|
||||||
|
goto bailout;
|
||||||
|
}
|
||||||
rc = preserver ? preserver(preserver_context, old_data,
|
rc = preserver ? preserver(preserver_context, old_data,
|
||||||
present_data.iov_base, present_data.iov_len)
|
present_data.iov_base, present_data.iov_len)
|
||||||
: MDBX_SUCCESS;
|
: MDBX_SUCCESS;
|
||||||
@ -18376,13 +18419,14 @@ int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
|
|||||||
} else {
|
} else {
|
||||||
*old_data = present_data;
|
*old_data = present_data;
|
||||||
}
|
}
|
||||||
|
flags |= MDBX_CURRENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(new_data))
|
if (likely(new_data))
|
||||||
rc = mdbx_cursor_put(&cx.outer, key, new_data, flags);
|
rc = mdbx_cursor_put(&cx.outer, key, new_data, flags);
|
||||||
else
|
else
|
||||||
rc = mdbx_cursor_del(&cx.outer, 0);
|
rc = mdbx_cursor_del(&cx.outer, flags & MDBX_ALLDUPS);
|
||||||
|
|
||||||
bailout:
|
bailout:
|
||||||
txn->mt_cursors[dbi] = cx.outer.mc_next;
|
txn->mt_cursors[dbi] = cx.outer.mc_next;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user