diff --git a/mdbx.h b/mdbx.h index d01bd4f3..867ec5e5 100644 --- a/mdbx.h +++ b/mdbx.h @@ -1631,6 +1631,167 @@ LIBMDBX_API int mdbx_is_dirty(const MDBX_txn *txn, const void *ptr); LIBMDBX_API int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, uint64_t increment); +/*----------------------------------------------------------------------------*/ +/* attribute support functions for Nexenta */ +typedef uint64_t mdbx_attr_t; + + /** @brief Store by cursor with attribute. + * + * This function stores key/data pairs into the database. + * The cursor is positioned at the new item, or on failure usually near it. + * @note Internally based on #MDBX_RESERVE feature, therefore doesn't support #MDBX_DUPSORT. + * @note Earlier documentation incorrectly said errors would leave the + * state of the cursor unchanged. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in] key The key operated on. + * @param[in] data The data operated on. + * @param[in] attr The attribute. + * @param[in] flags Options for this operation. This parameter + * must be set to 0 or one of the values described here. + * + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * + */ +int mdbx_cursor_put_attr(MDBX_cursor *cursor, MDBX_val *key, MDBX_val *data, + mdbx_attr_t attr, unsigned flags); + + /** @brief Store items and attributes into a database. + * + * This function stores key/data pairs in the database. The default behavior + * is to enter the new key/data pair, replacing any previously existing key + * if duplicates are disallowed. + * @note Internally based on #MDBX_RESERVE feature, therefore doesn't support #MDBX_DUPSORT. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to store in the database + * @param[in] attr The attribute to store in the database + * @param[in,out] data The data to store + * @param[in] flags Special options for this operation. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * + */ +int mdbx_put_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, + mdbx_attr_t attr, unsigned flags); + + /** @brief Set items attribute from a database. + * + * This function stores key/data pairs attribute to the database. + * @note Internally based on #MDBX_RESERVE feature, therefore doesn't support #MDBX_DUPSORT. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to search for in the database + * @param[in] data The data to be stored or NULL to save previous value. + * @param[in] attr The attribute to be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * + */ +int mdbx_set_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, + mdbx_attr_t attr); + + /** @brief Get items attribute from a database cursor. + * + * This function retrieves key/data pairs attribute from the database. + * The attribute of the specified key-value pair is returned in + * uint64_t to which \b attrptr refers. + * If the database supports duplicate keys (#MDBX_DUPSORT) then both + * key and data parameters are required, otherwise data could be NULL. + * + * @note Values returned from the database are valid only until a + * subsequent update operation, or the end of the transaction. + * @param[in] mc A database cursor pointing at the node + * @param[in] key The key to search for in the database + * @param[in,out] data The data for #MDBX_DUPSORT databases + * @param[out] attrptr The pointer to the result + * @param[in] op A cursor operation #MDBX_cursor_op + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * + */ +int mdbx_cursor_get_attr(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, + mdbx_attr_t *attrptr, MDBX_cursor_op op); + + /** @brief Get items attribute from a database. + * + * This function retrieves key/data pairs attribute from the database. + * The attribute of the specified key-value pair is returned in + * uint64_t to which \b attrptr refers. + * If the database supports duplicate keys (#MDBX_DUPSORT) then both + * key and data parameters are required, otherwise data is ignored. + * + * @note Values returned from the database are valid only until a + * subsequent update operation, or the end of the transaction. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to search for in the database + * @param[in] data The data for #MDBX_DUPSORT databases + * @param[out] attrptr The pointer to the result + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * + */ +int mdbx_get_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *data, + mdbx_attr_t *attrptr); + #ifdef __cplusplus } #endif diff --git a/src/mdbx.c b/src/mdbx.c index ae1a71ff..57ebe42d 100644 --- a/src/mdbx.c +++ b/src/mdbx.c @@ -11097,3 +11097,141 @@ int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, return MDBX_SUCCESS; } + +/*----------------------------------------------------------------------------*/ +/* attribute support functions for Nexenta */ + +static __inline int +mdbx_attr_peek(MDBX_val *data, mdbx_attr_t *attrptr) +{ + if (unlikely(data->iov_len < sizeof(mdbx_attr_t))) + return MDBX_INCOMPATIBLE; + + if (likely(attrptr != NULL)) + *attrptr = *(mdbx_attr_t*) data->iov_base; + data->iov_len -= sizeof(mdbx_attr_t); + data->iov_base = likely(data->iov_len > 0) + ? ((mdbx_attr_t*) data->iov_base) + 1 : NULL; + + return MDBX_SUCCESS; +} + +static __inline int +mdbx_attr_poke(MDBX_val *reserved, MDBX_val *data, mdbx_attr_t attr, unsigned flags) +{ + mdbx_attr_t *space = reserved->iov_base; + if (flags & MDBX_RESERVE) { + if (likely(data != NULL)) { + data->iov_base = data->iov_len ? space + 1 : NULL; + } + } else { + *space = attr; + if (likely(data != NULL)) { + memcpy(space + 1, data->iov_base, data->iov_len ); + } + } + + return MDBX_SUCCESS; +} + +int +mdbx_cursor_get_attr(MDBX_cursor *mc, MDBX_val *key, MDBX_val *data, + mdbx_attr_t *attrptr, MDBX_cursor_op op) +{ + int rc = mdbx_cursor_get(mc, key, data, op); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + + return mdbx_attr_peek(data, attrptr); +} + +int +mdbx_get_attr(MDBX_txn *txn, MDBX_dbi dbi, + MDBX_val *key, MDBX_val *data, uint64_t *attrptr) +{ + int rc = mdbx_get(txn, dbi, key, data); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + + return mdbx_attr_peek(data, attrptr); +} + +int +mdbx_put_attr(MDBX_txn *txn, MDBX_dbi dbi, + MDBX_val *key, MDBX_val *data, mdbx_attr_t attr, unsigned flags) +{ + MDBX_val reserve = { + .iov_base = NULL, + .iov_len = (data ? data->iov_len : 0) + sizeof(mdbx_attr_t) + }; + + int rc = mdbx_put(txn, dbi, key, &reserve, flags | MDBX_RESERVE); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + + return mdbx_attr_poke(&reserve, data, attr, flags); +} + +int mdbx_cursor_put_attr(MDBX_cursor *cursor, MDBX_val *key, MDBX_val *data, + mdbx_attr_t attr, unsigned flags) +{ + MDBX_val reserve = { + .iov_base = NULL, + .iov_len = (data ? data->iov_len : 0) + sizeof(mdbx_attr_t) + }; + + int rc = mdbx_cursor_put(cursor, key, &reserve, flags | MDBX_RESERVE); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + + return mdbx_attr_poke(&reserve, data, attr, flags); +} + +int mdbx_set_attr(MDBX_txn *txn, MDBX_dbi dbi, + MDBX_val *key, MDBX_val *data, mdbx_attr_t attr) +{ + MDBX_cursor mc; + MDBX_xcursor mx; + MDBX_val old_data; + mdbx_attr_t old_attr; + int rc; + + if (unlikely(!key || !txn)) + return EINVAL; + + if (unlikely(txn->mt_signature != MDBX_MT_SIGNATURE)) + return MDBX_VERSION_MISMATCH; + + if (unlikely(!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))) + return EINVAL; + + if (unlikely(txn->mt_flags & (MDBX_TXN_RDONLY|MDBX_TXN_BLOCKED))) + return (txn->mt_flags & MDBX_TXN_RDONLY) ? EACCES : MDBX_BAD_TXN; + + mdbx_cursor_init(&mc, txn, dbi, &mx); + rc = mdbx_cursor_set(&mc, key, &old_data, MDBX_SET, NULL); + if (unlikely(rc != MDBX_SUCCESS)) { + if (rc == MDBX_NOTFOUND && data) { + mc.mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = &mc; + rc = mdbx_cursor_put_attr(&mc, key, data, attr, 0); + txn->mt_cursors[dbi] = mc.mc_next; + } + return rc; + } + + rc = mdbx_attr_peek(&old_data, &old_attr); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; + + if (old_attr == attr && (!data || + (data->iov_len == old_data.iov_len + && memcpy(data->iov_base, old_data.iov_base, old_data.iov_len) == 0))) + return MDBX_SUCCESS; + + mc.mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = &mc; + rc = mdbx_cursor_put_attr(&mc, key, data ? data : &old_data, attr, MDBX_CURRENT); + txn->mt_cursors[dbi] = mc.mc_next; + return rc; +}