From 526ed28de15c2bb92a693799b11d25c7d0ba0bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=D0=AE=D1=80=D1=8C?= =?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= Date: Mon, 16 Dec 2024 11:54:24 +0300 Subject: [PATCH] =?UTF-8?q?mdbx:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20`mdbx=5Fcursor=5Fcount=5Fex()`=20?= =?UTF-8?q?=D0=B2=20API.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mdbx.h | 28 +++++++++++++++++++++++++++- src/api-cursor.c | 43 ++++++++++++++++++++++++++++++++++++------- src/layout-dxb.h | 2 +- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/mdbx.h b/mdbx.h index 9854c6e5..16fa1066 100644 --- a/mdbx.h +++ b/mdbx.h @@ -5701,9 +5701,11 @@ LIBMDBX_API int mdbx_cursor_put(MDBX_cursor *cursor, const MDBX_val *key, MDBX_v * \retval MDBX_EINVAL An invalid parameter was specified. */ LIBMDBX_API int mdbx_cursor_del(MDBX_cursor *cursor, MDBX_put_flags_t flags); -/** \brief Return count of duplicates for current key. +/** \brief Return count values (aka duplicates) for current key. * \ingroup c_crud * + * \see mdbx_cursor_count_ex + * * This call is valid for all tables, but reasonable only for that support * sorted duplicate data items \ref MDBX_DUPSORT. * @@ -5718,6 +5720,30 @@ LIBMDBX_API int mdbx_cursor_del(MDBX_cursor *cursor, MDBX_put_flags_t flags); * was specified. */ LIBMDBX_API int mdbx_cursor_count(const MDBX_cursor *cursor, size_t *pcount); +/** \brief Return count values (aka duplicates) and nested b-tree statistics for current key. + * \ingroup c_crud + * + * \see mdbx_dbi_stat + * \see mdbx_dbi_dupsort_depthmask + * \see mdbx_cursor_count + * + * This call is valid for all tables, but reasonable only for that support + * sorted duplicate data items \ref MDBX_DUPSORT. + * + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). + * \param [out] pcount Address where the count will be stored. + * \param [out] stat The address of an \ref MDBX_stat structure where + * the statistics of a nested b-tree will be copied. + * \param [in] bytes The size of \ref MDBX_stat. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_THREAD_MISMATCH Given transaction is not owned + * by current thread. + * \retval MDBX_EINVAL Cursor is not initialized, or an invalid parameter + * was specified. */ +LIBMDBX_API int mdbx_cursor_count_ex(const MDBX_cursor *mc, size_t *count, MDBX_stat *stat, size_t bytes); + /** \brief Determines whether the cursor is pointed to a key-value pair or not, * i.e. was not positioned or points to the end of data. * \ingroup c_cursors diff --git a/src/api-cursor.c b/src/api-cursor.c index 24d21fce..e52f5817 100644 --- a/src/api-cursor.c +++ b/src/api-cursor.c @@ -308,8 +308,7 @@ int mdbx_cursor_compare(const MDBX_cursor *l, const MDBX_cursor *r, bool ignore_ return (l->flags & z_eof_hard) - (r->flags & z_eof_hard); } -/* Return the count of duplicate data items for the current key */ -int mdbx_cursor_count(const MDBX_cursor *mc, size_t *countp) { +int mdbx_cursor_count_ex(const MDBX_cursor *mc, size_t *count, MDBX_stat *ns, size_t bytes) { if (unlikely(mc == nullptr)) return LOG_IFERR(MDBX_EINVAL); @@ -320,21 +319,51 @@ int mdbx_cursor_count(const MDBX_cursor *mc, size_t *countp) { if (unlikely(rc != MDBX_SUCCESS)) return LOG_IFERR(rc); - if (unlikely(countp == nullptr)) - return LOG_IFERR(MDBX_EINVAL); + if (ns) { + const size_t size_before_modtxnid = offsetof(MDBX_stat, ms_mod_txnid); + if (unlikely(bytes != sizeof(MDBX_stat)) && bytes != size_before_modtxnid) + return LOG_IFERR(MDBX_EINVAL); + memset(ns, 0, sizeof(*ns)); + } - if ((*countp = is_filled(mc)) > 0) { + size_t nvals = 0; + if (is_filled(mc)) { + nvals = 1; if (!inner_hollow(mc)) { const page_t *mp = mc->pg[mc->top]; const node_t *node = page_node(mp, mc->ki[mc->top]); cASSERT(mc, node_flags(node) & N_DUP); - *countp = - unlikely(mc->subcur->nested_tree.items > PTRDIFF_MAX) ? PTRDIFF_MAX : (size_t)mc->subcur->nested_tree.items; + const tree_t *nt = &mc->subcur->nested_tree; + nvals = unlikely(nt->items > PTRDIFF_MAX) ? PTRDIFF_MAX : (size_t)nt->items; + if (ns) { + ns->ms_psize = (unsigned)node_ds(node); + if (node_flags(node) & N_TREE) { + ns->ms_psize = mc->txn->env->ps; + ns->ms_depth = nt->height; + ns->ms_branch_pages = nt->branch_pages; + } + cASSERT(mc, nt->large_pages == 0); + ns->ms_leaf_pages = nt->leaf_pages; + ns->ms_entries = nt->items; + if (likely(bytes >= offsetof(MDBX_stat, ms_mod_txnid) + sizeof(ns->ms_mod_txnid))) + ns->ms_mod_txnid = nt->mod_txnid; + } } } + + if (likely(count)) + *count = nvals; + return MDBX_SUCCESS; } +int mdbx_cursor_count(const MDBX_cursor *mc, size_t *count) { + if (unlikely(count == nullptr)) + return LOG_IFERR(MDBX_EINVAL); + + return mdbx_cursor_count_ex(mc, count, nullptr, 0); +} + int mdbx_cursor_on_first(const MDBX_cursor *mc) { if (unlikely(mc == nullptr)) return LOG_IFERR(MDBX_EINVAL); diff --git a/src/layout-dxb.h b/src/layout-dxb.h index ed2f261d..3d9514c8 100644 --- a/src/layout-dxb.h +++ b/src/layout-dxb.h @@ -62,7 +62,7 @@ typedef struct tree { uint16_t height; /* height of this tree */ uint32_t dupfix_size; /* key-size for MDBX_DUPFIXED (DUPFIX pages) */ pgno_t root; /* the root page of this tree */ - pgno_t branch_pages; /* number of internal pages */ + pgno_t branch_pages; /* number of branch pages */ pgno_t leaf_pages; /* number of leaf pages */ pgno_t large_pages; /* number of large pages */ uint64_t sequence; /* table sequence counter */