diff options
author | George Hazan <ghazan@miranda.im> | 2021-08-24 18:45:25 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2021-08-24 18:45:25 +0300 |
commit | b13d2bacea4d260145eaa13541fcc1e39a4bfc9f (patch) | |
tree | 9a56211ba8d3603b9cf85aa7302830b05380f5a2 /libs/libmdbx/src/mdbx.h | |
parent | 4f51935548a140eb371cbb25126a82df8a41e413 (diff) |
libmdbx extracted back to the separate module, otherwise dbx_mdbx is marked as new on each rebuild
Diffstat (limited to 'libs/libmdbx/src/mdbx.h')
-rw-r--r-- | libs/libmdbx/src/mdbx.h | 5017 |
1 files changed, 5017 insertions, 0 deletions
diff --git a/libs/libmdbx/src/mdbx.h b/libs/libmdbx/src/mdbx.h new file mode 100644 index 0000000000..9d7dba99e9 --- /dev/null +++ b/libs/libmdbx/src/mdbx.h @@ -0,0 +1,5017 @@ +/** + +_libmdbx_ is an extremely fast, compact, powerful, embedded, +transactional [key-value +store](https://en.wikipedia.org/wiki/Key-value_database) database, with +[permissive license](./LICENSE). _MDBX_ has a specific set of properties and +capabilities, focused on creating unique lightweight solutions with +extraordinary performance. + +_libmdbx_ is superior to [LMDB](https://bit.ly/26ts7tL) in terms of features +and reliability, not inferior in performance. In comparison to LMDB, _libmdbx_ +makes many things just work perfectly, not silently and catastrophically +break down. _libmdbx_ supports Linux, Windows, MacOS, OSX, iOS, Android, +FreeBSD, DragonFly, Solaris, OpenSolaris, OpenIndiana, NetBSD, OpenBSD and other +systems compliant with POSIX.1-2008. + +_The Future will (be) [Positive](https://www.ptsecurity.com). Всё будет хорошо._ + + +\section copyright LICENSE & COPYRIGHT + +\authors Copyright (c) 2015-2021, Leonid Yuriev <leo@yuriev.ru> +and other _libmdbx_ authors: please see [AUTHORS](./AUTHORS) file. + +\copyright Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +<http://www.OpenLDAP.org/license.html>. + + --- + +This code is derived from "LMDB engine" written by +Howard Chu (Symas Corporation), which itself derived from btree.c +written by Martin Hedenfalk. + + --- + +Portions Copyright 2011-2015 Howard Chu, Symas Corp. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +<http://www.OpenLDAP.org/license.html>. + + --- + +Portions Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*******************************************************************************/ + +#pragma once +#ifndef LIBMDBX_H +#define LIBMDBX_H + +#ifdef _MSC_VER +#pragma warning(push, 1) +#pragma warning(disable : 4548) /* expression before comma has no effect; \ + expected expression with side - effect */ +#pragma warning(disable : 4530) /* C++ exception handler used, but unwind \ + * semantics are not enabled. Specify /EHsc */ +#pragma warning(disable : 4577) /* 'noexcept' used with no exception handling \ + * mode specified; termination on exception is \ + * not guaranteed. Specify /EHsc */ +#endif /* _MSC_VER (warnings) */ + +/* *INDENT-OFF* */ +/* clang-format off */ + +/** + \file mdbx.h + \brief The libmdbx C API header file + + \defgroup c_api C API + @{ + \defgroup c_err Error handling + \defgroup c_opening Opening & Closing + \defgroup c_transactions Transactions + \defgroup c_dbi Databases + \defgroup c_crud Create/Read/Update/Delete (see Quick Reference in details) + + \details + \anchor c_crud_hints +# Quick Reference for Insert/Update/Delete operations + +Historically, libmdbx inherits the API basis from LMDB, where it is often +difficult to select flags/options and functions for the desired operation. +So it is recommend using this hints. + +## Databases with UNIQUE keys + +In databases created without the \ref MDBX_DUPSORT option, keys are always +unique. Thus always a single value corresponds to the each key, and so there +are only a few cases of changing data. + +| Case | Flags to use | Result | +|---------------------------------------------|---------------------|------------------------| +| _INSERTING_||| +|Key is absent → Insertion |\ref MDBX_NOOVERWRITE|Insertion | +|Key exist → Error since key present |\ref MDBX_NOOVERWRITE|Error \ref MDBX_KEYEXIST and return Present value| +| _UPSERTING_||| +|Key is absent → Insertion |\ref MDBX_UPSERT |Insertion | +|Key exist → Update |\ref MDBX_UPSERT |Update | +| _UPDATING_||| +|Key is absent → Error since no such key |\ref MDBX_CURRENT |Error \ref MDBX_NOTFOUND| +|Key exist → Update |\ref MDBX_CURRENT |Update value | +| _DELETING_||| +|Key is absent → Error since no such key |\ref mdbx_del() or \ref mdbx_replace()|Error \ref MDBX_NOTFOUND| +|Key exist → Delete by key |\ref mdbx_del() with the parameter `data = NULL`|Deletion| +|Key exist → Delete by key with with data matching check|\ref mdbx_del() with the parameter `data` filled with the value which should be match for deletion|Deletion or \ref MDBX_NOTFOUND if the value does not match| +|Delete at the current cursor position |\ref mdbx_cursor_del() with \ref MDBX_CURRENT flag|Deletion| +|Extract (read & delete) value by the key |\ref mdbx_replace() with zero flag and parameter `new_data = NULL`|Returning a deleted value| + + +## Databases with NON-UNIQUE keys + +In databases created with the \ref MDBX_DUPSORT (Sorted Duplicates) option, keys +may be non unique. Such non-unique keys in a key-value database may be treated +as a duplicates or as like a multiple values corresponds to keys. + + +| Case | Flags to use | Result | +|---------------------------------------------|---------------------|------------------------| +| _INSERTING_||| +|Key is absent → Insertion |\ref MDBX_NOOVERWRITE|Insertion| +|Key exist → Needn't to add new values |\ref MDBX_NOOVERWRITE|Error \ref MDBX_KEYEXIST with returning the first value from those already present| +| _UPSERTING_||| +|Key is absent → Insertion |\ref MDBX_UPSERT |Insertion| +|Key exist → Wanna to add new values |\ref MDBX_UPSERT |Add one more value to the key| +|Key exist → Replace all values with a new one|\ref MDBX_UPSERT + \ref MDBX_ALLDUPS|Overwrite by single new value| +| _UPDATING_||| +|Key is absent → Error since no such key |\ref MDBX_CURRENT |Error \ref MDBX_NOTFOUND| +|Key exist, Single value → Update |\ref MDBX_CURRENT |Update single value | +|Key exist, Multiple values → Replace all values with a new one|\ref MDBX_CURRENT + \ref MDBX_ALLDUPS|Overwrite by single new value| +|Key exist, Multiple values → Error since it is unclear which of the values should be updated|\ref mdbx_put() with \ref MDBX_CURRENT|Error \ref MDBX_EMULTIVAL| +|Key exist, Multiple values → Update particular entry of multi-value|\ref mdbx_replace() with \ref MDBX_CURRENT + \ref MDBX_NOOVERWRITE and the parameter `old_value` filled with the value that wanna to update|Update one multi-value entry| +|Key exist, Multiple values → Update the current entry of multi-value|\ref mdbx_cursor_put() with \ref MDBX_CURRENT|Update one multi-value entry| +| _DELETING_||| +|Key is absent → Error since no such key |\ref mdbx_del() or \ref mdbx_replace()|Error \ref MDBX_NOTFOUND| +|Key exist → Delete all values corresponds given key|\ref mdbx_del() with the parameter `data = NULL`|Deletion| +|Key exist → Delete particular value corresponds given key|\ref mdbx_del() with the parameter `data` filled with the value that wanna to delete, or \ref mdbx_replace() with \ref MDBX_CURRENT + \ref MDBX_NOOVERWRITE and the `old_value` parameter filled with the value that wanna to delete and `new_data = NULL`| Deletion or \ref MDBX_NOTFOUND if no such key-value pair| +|Delete one value at the current cursor position|\ref mdbx_cursor_del() with \ref MDBX_CURRENT flag|Deletion only the current entry| +|Delete all values of key at the current cursor position|\ref mdbx_cursor_del() with with \ref MDBX_ALLDUPS flag|Deletion all duplicates of key (all multi-values) at the current cursor position| + + \defgroup c_cursors Cursors + \defgroup c_statinfo Statistics & Information + \defgroup c_settings Settings + \defgroup c_debug Logging and runtime debug + \defgroup c_rqest Range query estimation + \defgroup c_extra Extra operations +*/ + +/* *INDENT-ON* */ +/* clang-format on */ + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> + +#if defined(_WIN32) || defined(_WIN64) +#include <windows.h> +#include <winnt.h> +#ifndef __mode_t_defined +typedef unsigned short mdbx_mode_t; +#else +typedef mode_t mdbx_mode_t; +#endif /* __mode_t_defined */ +typedef HANDLE mdbx_filehandle_t; +typedef DWORD mdbx_pid_t; +typedef DWORD mdbx_tid_t; +#else /* Windows */ +#include <errno.h> /* for error codes */ +#include <pthread.h> /* for pthread_t */ +#include <sys/types.h> /* for pid_t */ +#include <sys/uio.h> /* for struct iovec */ +#define HAVE_STRUCT_IOVEC 1 +typedef int mdbx_filehandle_t; +typedef pid_t mdbx_pid_t; +typedef pthread_t mdbx_tid_t; +typedef mode_t mdbx_mode_t; +#endif /* !Windows */ + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +/** @} close c_api + * \defgroup api_macros Common Macros + * @{ */ + +/*----------------------------------------------------------------------------*/ + +#ifndef __has_attribute +#define __has_attribute(x) (0) +#endif /* __has_attribute */ + +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(x) 0 +#endif /* __has_cpp_attribute */ + +#ifndef __has_feature +#define __has_feature(x) (0) +#endif /* __has_feature */ + +#ifndef __has_extension +#define __has_extension(x) (0) +#endif /* __has_extension */ + +#ifndef __has_builtin +#define __has_builtin(x) (0) +#endif /* __has_builtin */ + +/** Many functions have no effects except the return value and their + * return value depends only on the parameters and/or global variables. + * Such a function can be subject to common subexpression elimination + * and loop optimization just as an arithmetic operator would be. + * These functions should be declared with the attribute pure. */ +#if (defined(__GNUC__) || __has_attribute(__pure__)) && \ + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ + || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) +#define MDBX_PURE_FUNCTION __attribute__((__pure__)) +#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 +#define MDBX_PURE_FUNCTION +#elif defined(__cplusplus) && __has_cpp_attribute(gnu::pure) && \ + (!defined(__clang__) || !__has_feature(cxx_exceptions)) +#define MDBX_PURE_FUNCTION [[gnu::pure]] +#else +#define MDBX_PURE_FUNCTION +#endif /* MDBX_PURE_FUNCTION */ + +/** Like \ref MDBX_PURE_FUNCTION with addition `noexcept` restriction + * that is compatible to CLANG and proposed [[pure]]. */ +#if defined(__GNUC__) || \ + (__has_attribute(__pure__) && __has_attribute(__nothrow__)) +#define MDBX_NOTHROW_PURE_FUNCTION __attribute__((__pure__, __nothrow__)) +#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 +#if __has_cpp_attribute(pure) +#define MDBX_NOTHROW_PURE_FUNCTION [[pure]] +#else +#define MDBX_NOTHROW_PURE_FUNCTION +#endif +#elif defined(__cplusplus) && __has_cpp_attribute(gnu::pure) +#if __has_cpp_attribute(gnu::nothrow) +#define MDBX_NOTHROW_PURE_FUNCTION [[gnu::pure, gnu::nothrow]] +#else +#define MDBX_NOTHROW_PURE_FUNCTION [[gnu::pure]] +#endif +#elif defined(__cplusplus) && __has_cpp_attribute(pure) +#define MDBX_NOTHROW_PURE_FUNCTION [[pure]] +#else +#define MDBX_NOTHROW_PURE_FUNCTION +#endif /* MDBX_NOTHROW_PURE_FUNCTION */ + +/** Many functions do not examine any values except their arguments, + * and have no effects except the return value. Basically this is just + * slightly more strict class than the PURE attribute, since function + * is not allowed to read global memory. + * + * Note that a function that has pointer arguments and examines the + * data pointed to must not be declared const. Likewise, a function + * that calls a non-const function usually must not be const. + * It does not make sense for a const function to return void. */ +#if (defined(__GNUC__) || __has_attribute(__pure__)) && \ + (!defined(__clang__) /* https://bugs.llvm.org/show_bug.cgi?id=43275 */ \ + || !defined(__cplusplus) || !__has_feature(cxx_exceptions)) +#define MDBX_CONST_FUNCTION __attribute__((__const__)) +#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 +#define MDBX_CONST_FUNCTION MDBX_PURE_FUNCTION +#elif defined(__cplusplus) && __has_cpp_attribute(gnu::const) && \ + (!defined(__clang__) || !__has_feature(cxx_exceptions)) +#define MDBX_CONST_FUNCTION [[gnu::const]] +#else +#define MDBX_CONST_FUNCTION MDBX_PURE_FUNCTION +#endif /* MDBX_CONST_FUNCTION */ + +/** Like \ref MDBX_CONST_FUNCTION with addition `noexcept` restriction + * that is compatible to CLANG and future [[const]]. */ +#if defined(__GNUC__) || \ + (__has_attribute(__const__) && __has_attribute(__nothrow__)) +#define MDBX_NOTHROW_CONST_FUNCTION __attribute__((__const__, __nothrow__)) +#elif defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1920 +#define MDBX_NOTHROW_CONST_FUNCTION MDBX_NOTHROW_PURE_FUNCTION +#elif defined(__cplusplus) && __has_cpp_attribute(gnu::const) +#if __has_cpp_attribute(gnu::nothrow) +#define MDBX_NOTHROW_PURE_FUNCTION [[gnu::const, gnu::nothrow]] +#else +#define MDBX_NOTHROW_PURE_FUNCTION [[gnu::const]] +#endif +#elif defined(__cplusplus) && __has_cpp_attribute(const) +#define MDBX_NOTHROW_CONST_FUNCTION [[const]] +#else +#define MDBX_NOTHROW_CONST_FUNCTION MDBX_NOTHROW_PURE_FUNCTION +#endif /* MDBX_NOTHROW_CONST_FUNCTION */ + +#ifndef MDBX_DEPRECATED /* may be predefined to avoid warnings "deprecated" */ +#ifdef __deprecated +#define MDBX_DEPRECATED __deprecated +#elif defined(__GNUC__) || __has_attribute(__deprecated__) +#define MDBX_DEPRECATED __attribute__((__deprecated__)) +#elif defined(_MSC_VER) +#define MDBX_DEPRECATED __declspec(deprecated) +#else +#define MDBX_DEPRECATED +#endif +#endif /* MDBX_DEPRECATED */ + +#ifndef __dll_export +#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(__GNUC__) || __has_attribute(__dllexport__) +#define __dll_export __attribute__((__dllexport__)) +#elif defined(_MSC_VER) +#define __dll_export __declspec(dllexport) +#else +#define __dll_export +#endif +#elif defined(__GNUC__) || __has_attribute(__visibility__) +#define __dll_export __attribute__((__visibility__("default"))) +#else +#define __dll_export +#endif +#endif /* __dll_export */ + +#ifndef __dll_import +#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(__GNUC__) || __has_attribute(__dllimport__) +#define __dll_import __attribute__((__dllimport__)) +#elif defined(_MSC_VER) +#define __dll_import __declspec(dllimport) +#else +#define __dll_import +#endif +#else +#define __dll_import +#endif +#endif /* __dll_import */ + +/** \brief Auxiliary macro for robustly define the both inline version of API + * function and non-inline fallback dll-exported version for applications linked + * with old version of libmdbx, with a strictly ODR-common implementation. */ +#if !defined(LIBMDBX_INTERNALS) +#define LIBMDBX_INLINE_API(TYPE, NAME, ARGS) static __inline TYPE NAME ARGS +#else +#define LIBMDBX_INLINE_API(TYPE, NAME, ARGS) \ + /* proto of exported which uses common impl */ LIBMDBX_API TYPE NAME ARGS; \ + /* definition of common impl */ static __inline TYPE __inline_##NAME ARGS +#endif /* LIBMDBX_INLINE_API */ + +/*----------------------------------------------------------------------------*/ + +#ifndef __cplusplus +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true (1) +#endif +#ifndef false +#define false (0) +#endif +#endif /* bool without __cplusplus */ + +#if !defined(DOXYGEN) && (!defined(__cpp_noexcept_function_type) || \ + __cpp_noexcept_function_type < 201510L) +#define MDBX_CXX17_NOEXCEPT +#else +#define MDBX_CXX17_NOEXCEPT noexcept +#endif /* MDBX_CXX17_NOEXCEPT */ + +/* Workaround for old compilers without properly support for constexpr. */ +#if !defined(__cplusplus) +#define MDBX_CXX01_CONSTEXPR __inline +#define MDBX_CXX01_CONSTEXPR_VAR const +#elif !defined(DOXYGEN) && \ + (!defined(__cpp_constexpr) || __cpp_constexpr < 200704L || \ + (defined(__LCC__) && __LCC__ < 124) || \ + (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 407) && \ + !defined(__clang__) && !defined(__LCC__)) || \ + (defined(_MSC_VER) && _MSC_VER < 1910) || \ + (defined(__clang__) && __clang_major__ < 4)) +#define MDBX_CXX01_CONSTEXPR inline +#define MDBX_CXX01_CONSTEXPR_VAR const +#else +#define MDBX_CXX01_CONSTEXPR constexpr +#define MDBX_CXX01_CONSTEXPR_VAR constexpr +#endif /* MDBX_CXX01_CONSTEXPR */ + +#if !defined(__cplusplus) +#define MDBX_CXX11_CONSTEXPR __inline +#define MDBX_CXX11_CONSTEXPR_VAR const +#elif !defined(DOXYGEN) && \ + (!defined(__cpp_constexpr) || __cpp_constexpr < 201304 || \ + (defined(__LCC__) && __LCC__ < 124) || \ + (defined(__GNUC__) && __GNUC__ < 6 && !defined(__clang__) && \ + !defined(__LCC__)) || \ + (defined(_MSC_VER) && _MSC_VER < 1910) || \ + (defined(__clang__) && __clang_major__ < 5)) +#define MDBX_CXX11_CONSTEXPR inline +#define MDBX_CXX11_CONSTEXPR_VAR const +#else +#define MDBX_CXX11_CONSTEXPR constexpr +#define MDBX_CXX11_CONSTEXPR_VAR constexpr +#endif /* MDBX_CXX11_CONSTEXPR */ + +#if !defined(__cplusplus) +#define MDBX_CXX14_CONSTEXPR __inline +#define MDBX_CXX14_CONSTEXPR_VAR const +#elif defined(DOXYGEN) || \ + defined(__cpp_constexpr) && __cpp_constexpr >= 201304L && \ + ((defined(_MSC_VER) && _MSC_VER >= 1910) || \ + (defined(__clang__) && __clang_major__ > 4) || \ + (defined(__GNUC__) && __GNUC__ > 6) || \ + (!defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER))) +#define MDBX_CXX14_CONSTEXPR constexpr +#define MDBX_CXX14_CONSTEXPR_VAR constexpr +#else +#define MDBX_CXX14_CONSTEXPR inline +#define MDBX_CXX14_CONSTEXPR_VAR const +#endif /* MDBX_CXX14_CONSTEXPR */ + +#if defined(__noreturn) +#define MDBX_NORETURN __noreturn +#elif defined(_Noreturn) +#define MDBX_NORETURN _Noreturn +#elif defined(__GNUC__) || __has_attribute(__noreturn__) +#define MDBX_NORETURN __attribute__((__noreturn__)) +#elif defined(_MSC_VER) && !defined(__clang__) +#define MDBX_NORETURN __declspec(noreturn) +#else +#define MDBX_NORETURN +#endif /* MDBX_NORETURN */ + +#ifndef MDBX_PRINTF_ARGS +#if defined(__GNUC__) || __has_attribute(__format__) +#define MDBX_PRINTF_ARGS(format_index, first_arg) \ + __attribute__((__format__(__printf__, format_index, first_arg))) +#else +#define MDBX_PRINTF_ARGS(format_index, first_arg) +#endif +#endif /* MDBX_PRINTF_ARGS */ + +/* Oh, below are some songs and dances since: + * - C++ requires explicit definition of the necessary operators. + * - the proper implementation of DEFINE_ENUM_FLAG_OPERATORS for C++ required + * the constexpr feature which is broken in most old compilers; + * - DEFINE_ENUM_FLAG_OPERATORS may be defined broken as in the Windows SDK. */ +#ifndef DEFINE_ENUM_FLAG_OPERATORS + +#ifdef __cplusplus +#if !defined(__cpp_constexpr) || __cpp_constexpr < 200704L || \ + (defined(__LCC__) && __LCC__ < 124) || \ + (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 407) && \ + !defined(__clang__) && !defined(__LCC__)) || \ + (defined(_MSC_VER) && _MSC_VER < 1910) || \ + (defined(__clang__) && __clang_major__ < 4) +/* The constexpr feature is not available or (may be) broken */ +#define CONSTEXPR_ENUM_FLAGS_OPERATIONS 0 +#else +/* C always allows these operators for enums */ +#define CONSTEXPR_ENUM_FLAGS_OPERATIONS 1 +#endif /* __cpp_constexpr */ + +/// Define operator overloads to enable bit operations on enum values that are +/// used to define flags (based on Microsoft's DEFINE_ENUM_FLAG_OPERATORS). +#define DEFINE_ENUM_FLAG_OPERATORS(ENUM) \ + extern "C++" { \ + MDBX_CXX01_CONSTEXPR ENUM operator|(ENUM a, ENUM b) { \ + return ENUM(unsigned(a) | unsigned(b)); \ + } \ + MDBX_CXX14_CONSTEXPR ENUM &operator|=(ENUM &a, ENUM b) { return a = a | b; } \ + MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, ENUM b) { \ + return ENUM(unsigned(a) & unsigned(b)); \ + } \ + MDBX_CXX01_CONSTEXPR ENUM operator&(ENUM a, unsigned b) { \ + return ENUM(unsigned(a) & b); \ + } \ + MDBX_CXX01_CONSTEXPR ENUM operator&(unsigned a, ENUM b) { \ + return ENUM(a & unsigned(b)); \ + } \ + MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, ENUM b) { return a = a & b; } \ + MDBX_CXX14_CONSTEXPR ENUM &operator&=(ENUM &a, unsigned b) { \ + return a = a & b; \ + } \ + MDBX_CXX01_CONSTEXPR unsigned operator~(ENUM a) { return ~unsigned(a); } \ + MDBX_CXX01_CONSTEXPR ENUM operator^(ENUM a, ENUM b) { \ + return ENUM(unsigned(a) ^ unsigned(b)); \ + } \ + MDBX_CXX14_CONSTEXPR ENUM &operator^=(ENUM &a, ENUM b) { return a = a ^ b; } \ + } +#else /* __cplusplus */ +/* nope for C since it always allows these operators for enums */ +#define DEFINE_ENUM_FLAG_OPERATORS(ENUM) +#define CONSTEXPR_ENUM_FLAGS_OPERATIONS 1 +#endif /* !__cplusplus */ + +#elif !defined(CONSTEXPR_ENUM_FLAGS_OPERATIONS) + +#ifdef __cplusplus +/* DEFINE_ENUM_FLAG_OPERATORS may be defined broken as in the Windows SDK */ +#define CONSTEXPR_ENUM_FLAGS_OPERATIONS 0 +#else +/* C always allows these operators for enums */ +#define CONSTEXPR_ENUM_FLAGS_OPERATIONS 1 +#endif + +#endif /* DEFINE_ENUM_FLAG_OPERATORS */ + +/** @} end of Common Macros */ + +/*----------------------------------------------------------------------------*/ + +/** \addtogroup c_api + * @{ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* MDBX version 0.9.x */ +#define MDBX_VERSION_MAJOR 0 +#define MDBX_VERSION_MINOR 9 + +#ifndef LIBMDBX_API +#if defined(LIBMDBX_EXPORTS) +#define LIBMDBX_API __dll_export +#elif defined(LIBMDBX_IMPORTS) +#define LIBMDBX_API __dll_import +#else +#define LIBMDBX_API +#endif +#endif /* LIBMDBX_API */ + +#ifdef __cplusplus +#if defined(__clang__) || __has_attribute(type_visibility) +#define LIBMDBX_API_TYPE LIBMDBX_API __attribute__((type_visibility("default"))) +#else +#define LIBMDBX_API_TYPE LIBMDBX_API +#endif +#else +#define LIBMDBX_API_TYPE +#endif /* LIBMDBX_API_TYPE */ + +#if defined(LIBMDBX_IMPORTS) +#define LIBMDBX_VERINFO_API __dll_import +#else +#define LIBMDBX_VERINFO_API __dll_export +#endif /* LIBMDBX_VERINFO_API */ + +/** \brief libmdbx version information */ +extern LIBMDBX_VERINFO_API const struct MDBX_version_info { + uint8_t major; /**< Major version number */ + uint8_t minor; /**< Minor version number */ + uint16_t release; /**< Release number of Major.Minor */ + uint32_t revision; /**< Revision number of Release */ + struct { + const char *datetime; /**< committer date, strict ISO-8601 format */ + const char *tree; /**< commit hash (hexadecimal digits) */ + const char *commit; /**< tree hash, i.e. digest of the source code */ + const char *describe; /**< git-describe string */ + } git; /**< source information from git */ + const char *sourcery; /**< sourcery anchor for pinning */ +} /** \brief libmdbx version information */ mdbx_version; + +/** \brief libmdbx build information + * \attention Some strings could be NULL in case no corresponding information + * was provided at build time (i.e. flags). */ +extern LIBMDBX_VERINFO_API const struct MDBX_build_info { + const char *datetime; /**< build timestamp (ISO-8601 or __DATE__ __TIME__) */ + const char *target; /**< cpu/arch-system-config triplet */ + const char *options; /**< mdbx-related options */ + const char *compiler; /**< compiler */ + const char *flags; /**< CFLAGS and CXXFLAGS */ +} /** \brief libmdbx build information */ mdbx_build; + +#if defined(_WIN32) || defined(_WIN64) +#if !MDBX_BUILD_SHARED_LIBRARY + +/* MDBX internally uses global and thread local storage destructors to + * automatically (de)initialization, releasing reader lock table slots + * and so on. + * + * If MDBX builded as a DLL this is done out-of-the-box by DllEntry() function, + * which called automatically by Windows core with passing corresponding reason + * argument. + * + * Otherwise, if MDBX was builded not as a DLL, some black magic + * may be required depending of Windows version: + * - Modern Windows versions, including Windows Vista and later, provides + * support for "TLS Directory" (e.g .CRT$XL[A-Z] sections in executable + * or dll file). In this case, MDBX capable of doing all automatically, + * and you do not need to call mdbx_dll_handler(). + * - Obsolete versions of Windows, prior to Windows Vista, REQUIRES calling + * mdbx_dll_handler() manually from corresponding DllMain() or WinMain() + * of your DLL or application. + * - This behavior is under control of the MODX_CONFIG_MANUAL_TLS_CALLBACK + * option, which is determined by default according to the target version + * of Windows at build time. + * But you may override MODX_CONFIG_MANUAL_TLS_CALLBACK in special cases. + * + * Therefore, building MDBX as a DLL is recommended for all version of Windows. + * So, if you doubt, just build MDBX as the separate DLL and don't worry. */ + +#ifndef MDBX_CONFIG_MANUAL_TLS_CALLBACK +#if defined(_WIN32_WINNT_VISTA) && WINVER >= _WIN32_WINNT_VISTA +/* As described above mdbx_dll_handler() is NOT needed forWindows Vista + * and later. */ +#define MDBX_CONFIG_MANUAL_TLS_CALLBACK 0 +#else +/* As described above mdbx_dll_handler() IS REQUIRED for Windows versions + * prior to Windows Vista. */ +#define MDBX_CONFIG_MANUAL_TLS_CALLBACK 1 +#endif +#endif /* MDBX_CONFIG_MANUAL_TLS_CALLBACK */ + +#if MDBX_CONFIG_MANUAL_TLS_CALLBACK +void LIBMDBX_API NTAPI mdbx_dll_handler(PVOID module, DWORD reason, + PVOID reserved); +#endif /* MDBX_CONFIG_MANUAL_TLS_CALLBACK */ +#endif /* !MDBX_BUILD_SHARED_LIBRARY */ +#endif /* Windows */ + +/* OPACITY STRUCTURES *********************************************************/ + +/** \brief Opaque structure for a database environment. + * \details An environment supports multiple key-value sub-databases (aka + * key-value spaces or tables), all residing in the same shared-memory map. + * \see mdbx_env_create() \see mdbx_env_close() */ +#ifndef __cplusplus +typedef struct MDBX_env MDBX_env; +#else +struct MDBX_env; +#endif + +/** \brief Opaque structure for a transaction handle. + * \ingroup c_transactions + * \details All database operations require a transaction handle. Transactions + * may be read-only or read-write. + * \see mdbx_txn_begin() \see mdbx_txn_commit() \see mdbx_txn_abort() */ +#ifndef __cplusplus +typedef struct MDBX_txn MDBX_txn; +#else +struct MDBX_txn; +#endif + +/** \brief A handle for an individual database (key-value spaces) in the + * environment. \ingroup c_dbi \details Zero handle is used internally (hidden + * Garbage Collection DB). So, any valid DBI-handle great than 0 and less than + * or equal \ref MDBX_MAX_DBI. \see mdbx_dbi_open() \see mdbx_dbi_close() */ +typedef uint32_t MDBX_dbi; + +/** \brief Opaque structure for navigating through a database + * \ingroup c_cursors + * \see mdbx_cursor_create() \see mdbx_cursor_bind() \see mdbx_cursor_close() + */ +#ifndef __cplusplus +typedef struct MDBX_cursor MDBX_cursor; +#else +struct MDBX_cursor; +#endif + +/** \brief Generic structure used for passing keys and data in and out of the + * database. + * \anchor MDBX_val \see mdbx::slice \see mdbx::buffer + * + * \details Values returned from the database are valid only until a subsequent + * update operation, or the end of the transaction. Do not modify or + * free them, they commonly point into the database itself. + * + * Key sizes must be between 0 and \ref mdbx_env_get_maxkeysize() inclusive. + * The same applies to data sizes in databases with the \ref MDBX_DUPSORT flag. + * Other data items can in theory be from 0 to \ref MDBX_MAXDATASIZE bytes long. + * + * \note The notable difference between MDBX and LMDB is that MDBX support zero + * length keys. */ +#ifndef HAVE_STRUCT_IOVEC +struct iovec { + void *iov_base; /**< pointer to some data */ + size_t iov_len; /**< the length of data in bytes */ +}; +#define HAVE_STRUCT_IOVEC +#endif /* HAVE_STRUCT_IOVEC */ + +#if defined(__sun) || defined(__SVR4) || defined(__svr4__) +/* The `iov_len` is signed on Sun/Solaris. + * So define custom MDBX_val to avoid a lot of warnings. */ +struct MDBX_val { + void *iov_base; /**< pointer to some data */ + size_t iov_len; /**< the length of data in bytes */ +}; +#ifndef __cplusplus +typedef struct MDBX_val MDBX_val; +#endif +#else /* SunOS */ +typedef struct iovec MDBX_val; +#endif /* ! SunOS */ + +enum MDBX_constants { + /** The hard limit for DBI handles */ + MDBX_MAX_DBI = UINT32_C(32765), + + /** The maximum size of a data item. */ + MDBX_MAXDATASIZE = UINT32_C(0x7fff0000), + + /** The minimal database page size in bytes. */ + MDBX_MIN_PAGESIZE = 256, + + /** The maximal database page size in bytes. */ + MDBX_MAX_PAGESIZE = 65536, +}; + +/* THE FILES ******************************************************************* + * At the file system level, the environment corresponds to a pair of files. */ + +/** \brief The name of the lock file in the environment */ +#define MDBX_LOCKNAME "/mdbx.lck" +/** \brief The name of the data file in the environment */ +#define MDBX_DATANAME "/mdbx.dat" + +/** \brief The suffix of the lock file when \ref MDBX_NOSUBDIR is used */ +#define MDBX_LOCK_SUFFIX "-lck" + +/* DEBUG & LOGGING ************************************************************/ + +/** \addtogroup c_debug + * \note Most of debug feature enabled only when libmdbx builded with + * \ref MDBX_DEBUG build option. @{ */ + +/** Log level (requires build libmdbx with \ref MDBX_DEBUG option) */ +enum MDBX_log_level_t { + /** Critical conditions, i.e. assertion failures */ + MDBX_LOG_FATAL = 0, + + /** Enables logging for error conditions and \ref MDBX_LOG_FATAL */ + MDBX_LOG_ERROR = 1, + + /** Enables logging for warning conditions and \ref MDBX_LOG_ERROR ... + \ref MDBX_LOG_FATAL */ + MDBX_LOG_WARN = 2, + + /** Enables logging for normal but significant condition and + \ref MDBX_LOG_WARN ... \ref MDBX_LOG_FATAL */ + MDBX_LOG_NOTICE = 3, + + /** Enables logging for verbose informational and \ref MDBX_LOG_NOTICE ... + \ref MDBX_LOG_FATAL */ + MDBX_LOG_VERBOSE = 4, + + /** Enables logging for debug-level messages and \ref MDBX_LOG_VERBOSE ... + \ref MDBX_LOG_FATAL */ + MDBX_LOG_DEBUG = 5, + + /** Enables logging for trace debug-level messages and \ref MDBX_LOG_DEBUG ... + \ref MDBX_LOG_FATAL */ + MDBX_LOG_TRACE = 6, + + /** Enables extra debug-level messages (dump pgno lists) + and all other log-messages */ + MDBX_LOG_EXTRA = 7, + + /** for \ref mdbx_setup_debug() only: Don't change current settings */ + MDBX_LOG_DONTCHANGE = -1 +}; +#ifndef __cplusplus +typedef enum MDBX_log_level_t MDBX_log_level_t; +#endif + +/** \brief Runtime debug flags + * + * \details `MDBX_DBG_DUMP` and `MDBX_DBG_LEGACY_MULTIOPEN` always have an + * effect, but `MDBX_DBG_ASSERT`, `MDBX_DBG_AUDIT` and `MDBX_DBG_JITTER` only if + * libmdbx builded with \ref MDBX_DEBUG. */ +enum MDBX_debug_flags_t { + /** Enable assertion checks. + * Requires build with \ref MDBX_DEBUG > 0 */ + MDBX_DBG_ASSERT = 1, + + /** Enable pages usage audit at commit transactions. + * Requires build with \ref MDBX_DEBUG > 0 */ + MDBX_DBG_AUDIT = 2, + + /** Enable small random delays in critical points. + * Requires build with \ref MDBX_DEBUG > 0 */ + MDBX_DBG_JITTER = 4, + + /** Include or not meta-pages in coredump files. + * May affect performance in \ref MDBX_WRITEMAP mode */ + MDBX_DBG_DUMP = 8, + + /** Allow multi-opening environment(s) */ + MDBX_DBG_LEGACY_MULTIOPEN = 16, + + /** Allow read and write transactions overlapping for the same thread */ + MDBX_DBG_LEGACY_OVERLAP = 32, + + /** for mdbx_setup_debug() only: Don't change current settings */ + MDBX_DBG_DONTCHANGE = -1 +}; +#ifndef __cplusplus +typedef enum MDBX_debug_flags_t MDBX_debug_flags_t; +#else +DEFINE_ENUM_FLAG_OPERATORS(MDBX_debug_flags_t) +#endif + +/** \brief A debug-logger callback function, + * called before printing the message and aborting. + * \see mdbx_setup_debug() + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [in] msg The assertion message, not including newline. */ +typedef void MDBX_debug_func(MDBX_log_level_t loglevel, const char *function, + int line, const char *fmt, + va_list args) MDBX_CXX17_NOEXCEPT; + +/** \brief The "don't change `logger`" value for mdbx_setup_debug() */ +#define MDBX_LOGGER_DONTCHANGE ((MDBX_debug_func *)(intptr_t)-1) + +/** \brief Setup global log-level, debug options and debug logger. + * \returns The previously `debug_flags` in the 0-15 bits + * and `log_level` in the 16-31 bits. */ +LIBMDBX_API int mdbx_setup_debug(MDBX_log_level_t log_level, + MDBX_debug_flags_t debug_flags, + MDBX_debug_func *logger); + +/** \brief A callback function for most MDBX assert() failures, + * called before printing the message and aborting. + * \see mdbx_env_set_assert() + * + * \param [in] env An environment handle returned by mdbx_env_create(). + * \param [in] msg The assertion message, not including newline. */ +typedef void MDBX_assert_func(const MDBX_env *env, const char *msg, + const char *function, + unsigned line) MDBX_CXX17_NOEXCEPT; + +/** \brief Set or reset the assert() callback of the environment. + * + * Does nothing if libmdbx was built with MDBX_DEBUG=0 or with NDEBUG, + * and will return `MDBX_ENOSYS` in such case. + * + * \param [in] env An environment handle returned by mdbx_env_create(). + * \param [in] func An MDBX_assert_func function, or 0. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_env_set_assert(MDBX_env *env, MDBX_assert_func *func); + +/** \brief Dump given MDBX_val to the buffer + * + * Dumps it as string if value is printable (all bytes in the range 0x20..0x7E), + * otherwise made hexadecimal dump. Requires at least 4 byte length buffer. + * + * \returns One of: + * - NULL if given buffer size less than 4 bytes; + * - pointer to constant string if given value NULL or empty; + * - otherwise pointer to given buffer. */ +LIBMDBX_API const char *mdbx_dump_val(const MDBX_val *key, char *const buf, + const size_t bufsize); + +/** \brief Panics with message and causes abnormal process termination. */ +LIBMDBX_API void mdbx_panic(const char *fmt, ...) MDBX_PRINTF_ARGS(1, 2); + +/** @} end of logging & debug */ + +/** \brief Environment flags + * \ingroup c_opening + * \anchor env_flags + * \see mdbx_env_open() \see mdbx_env_set_flags() */ +enum MDBX_env_flags_t { + MDBX_ENV_DEFAULTS = 0, + + /** No environment directory. + * + * By default, MDBX creates its environment in a directory whose pathname is + * given in path, and creates its data and lock files under that directory. + * With this option, path is used as-is for the database main data file. + * The database lock file is the path with "-lck" appended. + * + * - with `MDBX_NOSUBDIR` = in a filesystem we have the pair of MDBX-files + * which names derived from given pathname by appending predefined suffixes. + * + * - without `MDBX_NOSUBDIR` = in a filesystem we have the MDBX-directory with + * given pathname, within that a pair of MDBX-files with predefined names. + * + * This flag affects only at new environment creating by \ref mdbx_env_open(), + * otherwise at opening an existing environment libmdbx will choice this + * automatically. */ + MDBX_NOSUBDIR = UINT32_C(0x4000), + + /** Read only mode. + * + * Open the environment in read-only mode. No write operations will be + * allowed. MDBX will still modify the lock file - except on read-only + * filesystems, where MDBX does not use locks. + * + * - with `MDBX_RDONLY` = open environment in read-only mode. + * MDBX supports pure read-only mode (i.e. without opening LCK-file) only + * when environment directory and/or both files are not writable (and the + * LCK-file may be missing). In such case allowing file(s) to be placed + * on a network read-only share. + * + * - without `MDBX_RDONLY` = open environment in read-write mode. + * + * This flag affects only at environment opening but can't be changed after. + */ + MDBX_RDONLY = UINT32_C(0x20000), + + /** Open environment in exclusive/monopolistic mode. + * + * `MDBX_EXCLUSIVE` flag can be used as a replacement for `MDB_NOLOCK`, + * which don't supported by MDBX. + * In this way, you can get the minimal overhead, but with the correct + * multi-process and multi-thread locking. + * + * - with `MDBX_EXCLUSIVE` = open environment in exclusive/monopolistic mode + * or return \ref MDBX_BUSY if environment already used by other process. + * The main feature of the exclusive mode is the ability to open the + * environment placed on a network share. + * + * - without `MDBX_EXCLUSIVE` = open environment in cooperative mode, + * i.e. for multi-process access/interaction/cooperation. + * The main requirements of the cooperative mode are: + * + * 1. data files MUST be placed in the LOCAL file system, + * but NOT on a network share. + * 2. environment MUST be opened only by LOCAL processes, + * but NOT over a network. + * 3. OS kernel (i.e. file system and memory mapping implementation) and + * all processes that open the given environment MUST be running + * in the physically single RAM with cache-coherency. The only + * exception for cache-consistency requirement is Linux on MIPS + * architecture, but this case has not been tested for a long time). + * + * This flag affects only at environment opening but can't be changed after. + */ + MDBX_EXCLUSIVE = UINT32_C(0x400000), + + /** Using database/environment which already opened by another process(es). + * + * The `MDBX_ACCEDE` flag is useful to avoid \ref MDBX_INCOMPATIBLE error + * while opening the database/environment which is already used by another + * process(es) with unknown mode/flags. In such cases, if there is a + * difference in the specified flags (\ref MDBX_NOMETASYNC, + * \ref MDBX_SAFE_NOSYNC, \ref MDBX_UTTERLY_NOSYNC, \ref MDBX_LIFORECLAIM, + * \ref MDBX_COALESCE and \ref MDBX_NORDAHEAD), instead of returning an error, + * the database will be opened in a compatibility with the already used mode. + * + * `MDBX_ACCEDE` has no effect if the current process is the only one either + * opening the DB in read-only mode or other process(es) uses the DB in + * read-only mode. */ + MDBX_ACCEDE = UINT32_C(0x40000000), + + /** Map data into memory with write permission. + * + * Use a writeable memory map unless \ref MDBX_RDONLY is set. This uses fewer + * mallocs and requires much less work for tracking database pages, but + * loses protection from application bugs like wild pointer writes and other + * bad updates into the database. This may be slightly faster for DBs that + * fit entirely in RAM, but is slower for DBs larger than RAM. Also adds the + * possibility for stray application writes thru pointers to silently + * corrupt the database. + * + * - with `MDBX_WRITEMAP` = all data will be mapped into memory in the + * read-write mode. This offers a significant performance benefit, since the + * data will be modified directly in mapped memory and then flushed to disk + * by single system call, without any memory management nor copying. + * + * - without `MDBX_WRITEMAP` = data will be mapped into memory in the + * read-only mode. This requires stocking all modified database pages in + * memory and then writing them to disk through file operations. + * + * \warning On the other hand, `MDBX_WRITEMAP` adds the possibility for stray + * application writes thru pointers to silently corrupt the database. + * + * \note The `MDBX_WRITEMAP` mode is incompatible with nested transactions, + * since this is unreasonable. I.e. nested transactions requires mallocation + * of database pages and more work for tracking ones, which neuters a + * performance boost caused by the `MDBX_WRITEMAP` mode. + * + * This flag affects only at environment opening but can't be changed after. + */ + MDBX_WRITEMAP = UINT32_C(0x80000), + + /** Tie reader locktable slots to read-only transactions + * instead of to threads. + * + * Don't use Thread-Local Storage, instead tie reader locktable slots to + * \ref MDBX_txn objects instead of to threads. So, \ref mdbx_txn_reset() + * keeps the slot reserved for the \ref MDBX_txn object. A thread may use + * parallel read-only transactions. And a read-only transaction may span + * threads if you synchronizes its use. + * + * Applications that multiplex many user threads over individual OS threads + * need this option. Such an application must also serialize the write + * transactions in an OS thread, since MDBX's write locking is unaware of + * the user threads. + * + * \note Regardless to `MDBX_NOTLS` flag a write transaction entirely should + * always be used in one thread from start to finish. MDBX checks this in a + * reasonable manner and return the \ref MDBX_THREAD_MISMATCH error in rules + * violation. + * + * This flag affects only at environment opening but can't be changed after. + */ + MDBX_NOTLS = UINT32_C(0x200000), + + /** Don't do readahead. + * + * Turn off readahead. Most operating systems perform readahead on read + * requests by default. This option turns it off if the OS supports it. + * Turning it off may help random read performance when the DB is larger + * than RAM and system RAM is full. + * + * By default libmdbx dynamically enables/disables readahead depending on + * the actual database size and currently available memory. On the other + * hand, such automation has some limitation, i.e. could be performed only + * when DB size changing but can't tracks and reacts changing a free RAM + * availability, since it changes independently and asynchronously. + * + * \note The mdbx_is_readahead_reasonable() function allows to quickly find + * out whether to use readahead or not based on the size of the data and the + * amount of available memory. + * + * This flag affects only at environment opening and can't be changed after. + */ + MDBX_NORDAHEAD = UINT32_C(0x800000), + + /** Don't initialize malloc'd memory before writing to datafile. + * + * Don't initialize malloc'd memory before writing to unused spaces in the + * data file. By default, memory for pages written to the data file is + * obtained using malloc. While these pages may be reused in subsequent + * transactions, freshly malloc'd pages will be initialized to zeroes before + * use. This avoids persisting leftover data from other code (that used the + * heap and subsequently freed the memory) into the data file. + * + * Note that many other system libraries may allocate and free memory from + * the heap for arbitrary uses. E.g., stdio may use the heap for file I/O + * buffers. This initialization step has a modest performance cost so some + * applications may want to disable it using this flag. This option can be a + * problem for applications which handle sensitive data like passwords, and + * it makes memory checkers like Valgrind noisy. This flag is not needed + * with \ref MDBX_WRITEMAP, which writes directly to the mmap instead of using + * malloc for pages. The initialization is also skipped if \ref MDBX_RESERVE + * is used; the caller is expected to overwrite all of the memory that was + * reserved in that case. + * + * This flag may be changed at any time using `mdbx_env_set_flags()`. */ + MDBX_NOMEMINIT = UINT32_C(0x1000000), + + /** Aims to coalesce a Garbage Collection items. + * + * With `MDBX_COALESCE` flag MDBX will aims to coalesce items while recycling + * a Garbage Collection. Technically, when possible short lists of pages + * will be combined into longer ones, but to fit on one database page. As a + * result, there will be fewer items in Garbage Collection and a page lists + * are longer, which slightly increases the likelihood of returning pages to + * Unallocated space and reducing the database file. + * + * This flag may be changed at any time using mdbx_env_set_flags(). */ + MDBX_COALESCE = UINT32_C(0x2000000), + + /** LIFO policy for recycling a Garbage Collection items. + * + * `MDBX_LIFORECLAIM` flag turns on LIFO policy for recycling a Garbage + * Collection items, instead of FIFO by default. On systems with a disk + * write-back cache, this can significantly increase write performance, up + * to several times in a best case scenario. + * + * LIFO recycling policy means that for reuse pages will be taken which became + * unused the lastest (i.e. just now or most recently). Therefore the loop of + * database pages circulation becomes as short as possible. In other words, + * the number of pages, that are overwritten in memory and on disk during a + * series of write transactions, will be as small as possible. Thus creates + * ideal conditions for the efficient operation of the disk write-back cache. + * + * \ref MDBX_LIFORECLAIM is compatible with all no-sync flags, but gives NO + * noticeable impact in combination with \ref MDBX_SAFE_NOSYNC or + * \ref MDBX_UTTERLY_NOSYNC. Because MDBX will reused pages only before the + * last "steady" MVCC-snapshot, i.e. the loop length of database pages + * circulation will be mostly defined by frequency of calling + * \ref mdbx_env_sync() rather than LIFO and FIFO difference. + * + * This flag may be changed at any time using mdbx_env_set_flags(). */ + MDBX_LIFORECLAIM = UINT32_C(0x4000000), + + /** Debugging option, fill/perturb released pages. */ + MDBX_PAGEPERTURB = UINT32_C(0x8000000), + + /* SYNC MODES****************************************************************/ + /** \defgroup sync_modes SYNC MODES + * + * \attention Using any combination of \ref MDBX_SAFE_NOSYNC, \ref + * MDBX_NOMETASYNC and especially \ref MDBX_UTTERLY_NOSYNC is always a deal to + * reduce durability for gain write performance. You must know exactly what + * you are doing and what risks you are taking! + * + * \note for LMDB users: \ref MDBX_SAFE_NOSYNC is NOT similar to LMDB_NOSYNC, + * but \ref MDBX_UTTERLY_NOSYNC is exactly match LMDB_NOSYNC. See details + * below. + * + * THE SCENE: + * - The DAT-file contains several MVCC-snapshots of B-tree at same time, + * each of those B-tree has its own root page. + * - Each of meta pages at the beginning of the DAT file contains a + * pointer to the root page of B-tree which is the result of the particular + * transaction, and a number of this transaction. + * - For data durability, MDBX must first write all MVCC-snapshot data + * pages and ensure that are written to the disk, then update a meta page + * with the new transaction number and a pointer to the corresponding new + * root page, and flush any buffers yet again. + * - Thus during commit a I/O buffers should be flushed to the disk twice; + * i.e. fdatasync(), FlushFileBuffers() or similar syscall should be + * called twice for each commit. This is very expensive for performance, + * but guaranteed durability even on unexpected system failure or power + * outage. Of course, provided that the operating system and the + * underlying hardware (e.g. disk) work correctly. + * + * TRADE-OFF: + * By skipping some stages described above, you can significantly benefit in + * speed, while partially or completely losing in the guarantee of data + * durability and/or consistency in the event of system or power failure. + * Moreover, if for any reason disk write order is not preserved, then at + * moment of a system crash, a meta-page with a pointer to the new B-tree may + * be written to disk, while the itself B-tree not yet. In that case, the + * database will be corrupted! + * + * \see MDBX_SYNC_DURABLE \see MDBX_NOMETASYNC \see MDBX_SAFE_NOSYNC + * \see MDBX_UTTERLY_NOSYNC + * + * @{ */ + + /** Default robust and durable sync mode. + * + * Metadata is written and flushed to disk after a data is written and + * flushed, which guarantees the integrity of the database in the event + * of a crash at any time. + * + * \attention Please do not use other modes until you have studied all the + * details and are sure. Otherwise, you may lose your users' data, as happens + * in [Miranda NG](https://www.miranda-ng.org/) messenger. */ + MDBX_SYNC_DURABLE = 0, + + /** Don't sync the meta-page after commit. + * + * Flush system buffers to disk only once per transaction commit, omit the + * metadata flush. Defer that until the system flushes files to disk, + * or next non-\ref MDBX_RDONLY commit or \ref mdbx_env_sync(). Depending on + * the platform and hardware, with \ref MDBX_NOMETASYNC you may get a doubling + * of write performance. + * + * This trade-off maintains database integrity, but a system crash may + * undo the last committed transaction. I.e. it preserves the ACI + * (atomicity, consistency, isolation) but not D (durability) database + * property. + * + * `MDBX_NOMETASYNC` flag may be changed at any time using + * \ref mdbx_env_set_flags() or by passing to \ref mdbx_txn_begin() for + * particular write transaction. \see sync_modes */ + MDBX_NOMETASYNC = UINT32_C(0x40000), + + /** Don't sync anything but keep previous steady commits. + * + * Like \ref MDBX_UTTERLY_NOSYNC the `MDBX_SAFE_NOSYNC` flag disable similarly + * flush system buffers to disk when committing a transaction. But there is a + * huge difference in how are recycled the MVCC snapshots corresponding to + * previous "steady" transactions (see below). + * + * With \ref MDBX_WRITEMAP the `MDBX_SAFE_NOSYNC` instructs MDBX to use + * asynchronous mmap-flushes to disk. Asynchronous mmap-flushes means that + * actually all writes will scheduled and performed by operation system on it + * own manner, i.e. unordered. MDBX itself just notify operating system that + * it would be nice to write data to disk, but no more. + * + * Depending on the platform and hardware, with `MDBX_SAFE_NOSYNC` you may get + * a multiple increase of write performance, even 10 times or more. + * + * In contrast to \ref MDBX_UTTERLY_NOSYNC mode, with `MDBX_SAFE_NOSYNC` flag + * MDBX will keeps untouched pages within B-tree of the last transaction + * "steady" which was synced to disk completely. This has big implications for + * both data durability and (unfortunately) performance: + * - a system crash can't corrupt the database, but you will lose the last + * transactions; because MDBX will rollback to last steady commit since it + * kept explicitly. + * - the last steady transaction makes an effect similar to "long-lived" read + * transaction (see above in the \ref restrictions section) since prevents + * reuse of pages freed by newer write transactions, thus the any data + * changes will be placed in newly allocated pages. + * - to avoid rapid database growth, the system will sync data and issue + * a steady commit-point to resume reuse pages, each time there is + * insufficient space and before increasing the size of the file on disk. + * + * In other words, with `MDBX_SAFE_NOSYNC` flag MDBX insures you from the + * whole database corruption, at the cost increasing database size and/or + * number of disk IOPs. So, `MDBX_SAFE_NOSYNC` flag could be used with + * \ref mdbx_env_sync() as alternatively for batch committing or nested + * transaction (in some cases). As well, auto-sync feature exposed by + * \ref mdbx_env_set_syncbytes() and \ref mdbx_env_set_syncperiod() functions + * could be very useful with `MDBX_SAFE_NOSYNC` flag. + * + * The number and volume of of disk IOPs with MDBX_SAFE_NOSYNC flag will + * exactly the as without any no-sync flags. However, you should expect a + * larger process's [work set](https://bit.ly/2kA2tFX) and significantly worse + * a [locality of reference](https://bit.ly/2mbYq2J), due to the more + * intensive allocation of previously unused pages and increase the size of + * the database. + * + * `MDBX_SAFE_NOSYNC` flag may be changed at any time using + * \ref mdbx_env_set_flags() or by passing to \ref mdbx_txn_begin() for + * particular write transaction. */ + MDBX_SAFE_NOSYNC = UINT32_C(0x10000), + + /** \deprecated Please use \ref MDBX_SAFE_NOSYNC instead of `MDBX_MAPASYNC`. + * + * Since version 0.9.x the `MDBX_MAPASYNC` is deprecated and has the same + * effect as \ref MDBX_SAFE_NOSYNC with \ref MDBX_WRITEMAP. This just API + * simplification is for convenience and clarity. */ + MDBX_MAPASYNC = MDBX_SAFE_NOSYNC, + + /** Don't sync anything and wipe previous steady commits. + * + * Don't flush system buffers to disk when committing a transaction. This + * optimization means a system crash can corrupt the database, if buffers are + * not yet flushed to disk. Depending on the platform and hardware, with + * `MDBX_UTTERLY_NOSYNC` you may get a multiple increase of write performance, + * even 100 times or more. + * + * If the filesystem preserves write order (which is rare and never provided + * unless explicitly noted) and the \ref MDBX_WRITEMAP and \ref + * MDBX_LIFORECLAIM flags are not used, then a system crash can't corrupt the + * database, but you can lose the last transactions, if at least one buffer is + * not yet flushed to disk. The risk is governed by how often the system + * flushes dirty buffers to disk and how often \ref mdbx_env_sync() is called. + * So, transactions exhibit ACI (atomicity, consistency, isolation) properties + * and only lose `D` (durability). I.e. database integrity is maintained, but + * a system crash may undo the final transactions. + * + * Otherwise, if the filesystem not preserves write order (which is + * typically) or \ref MDBX_WRITEMAP or \ref MDBX_LIFORECLAIM flags are used, + * you should expect the corrupted database after a system crash. + * + * So, most important thing about `MDBX_UTTERLY_NOSYNC`: + * - a system crash immediately after commit the write transaction + * high likely lead to database corruption. + * - successful completion of mdbx_env_sync(force = true) after one or + * more committed transactions guarantees consistency and durability. + * - BUT by committing two or more transactions you back database into + * a weak state, in which a system crash may lead to database corruption! + * In case single transaction after mdbx_env_sync, you may lose transaction + * itself, but not a whole database. + * + * Nevertheless, `MDBX_UTTERLY_NOSYNC` provides "weak" durability in case + * of an application crash (but no durability on system failure), and + * therefore may be very useful in scenarios where data durability is + * not required over a system failure (e.g for short-lived data), or if you + * can take such risk. + * + * `MDBX_UTTERLY_NOSYNC` flag may be changed at any time using + * \ref mdbx_env_set_flags(), but don't has effect if passed to + * \ref mdbx_txn_begin() for particular write transaction. \see sync_modes */ + MDBX_UTTERLY_NOSYNC = MDBX_SAFE_NOSYNC | UINT32_C(0x100000), + + /** @} end of SYNC MODES */ +}; +#ifndef __cplusplus +/** \ingroup c_opening */ +typedef enum MDBX_env_flags_t MDBX_env_flags_t; +#else +DEFINE_ENUM_FLAG_OPERATORS(MDBX_env_flags_t) +#endif + +/** Transaction flags + * \ingroup c_transactions + * \anchor txn_flags + * \see mdbx_txn_begin() \see mdbx_txn_flags() */ +enum MDBX_txn_flags_t { + /** Start read-write transaction. + * + * Only one write transaction may be active at a time. Writes are fully + * serialized, which guarantees that writers can never deadlock. */ + MDBX_TXN_READWRITE = 0, + + /** Start read-only transaction. + * + * There can be multiple read-only transactions simultaneously that do not + * block each other and a write transactions. */ + MDBX_TXN_RDONLY = MDBX_RDONLY, + +/** Prepare but not start read-only transaction. + * + * Transaction will not be started immediately, but created transaction handle + * will be ready for use with \ref mdbx_txn_renew(). This flag allows to + * preallocate memory and assign a reader slot, thus avoiding these operations + * at the next start of the transaction. */ +#if CONSTEXPR_ENUM_FLAGS_OPERATIONS || defined(DOXYGEN) + MDBX_TXN_RDONLY_PREPARE = MDBX_RDONLY | MDBX_NOMEMINIT, +#else + MDBX_TXN_RDONLY_PREPARE = uint32_t(MDBX_RDONLY) | uint32_t(MDBX_NOMEMINIT), +#endif + + /** Do not block when starting a write transaction. */ + MDBX_TXN_TRY = UINT32_C(0x10000000), + + /** Exactly the same as \ref MDBX_NOMETASYNC, + * but for this transaction only */ + MDBX_TXN_NOMETASYNC = MDBX_NOMETASYNC, + + /** Exactly the same as \ref MDBX_SAFE_NOSYNC, + * but for this transaction only */ + MDBX_TXN_NOSYNC = MDBX_SAFE_NOSYNC +}; +#ifndef __cplusplus +typedef enum MDBX_txn_flags_t MDBX_txn_flags_t; +#else +DEFINE_ENUM_FLAG_OPERATORS(MDBX_txn_flags_t) +#endif + +/** \brief Database flags + * \ingroup c_dbi + * \anchor db_flags + * \see mdbx_dbi_open() */ +enum MDBX_db_flags_t { + MDBX_DB_DEFAULTS = 0, + + /** Use reverse string keys */ + MDBX_REVERSEKEY = UINT32_C(0x02), + + /** Use sorted duplicates, i.e. allow multi-values */ + MDBX_DUPSORT = UINT32_C(0x04), + + /** Numeric keys in native byte order either uint32_t or uint64_t. The keys + * must all be of the same size and must be aligned while passing as + * arguments. */ + MDBX_INTEGERKEY = UINT32_C(0x08), + + /** With \ref MDBX_DUPSORT; sorted dup items have fixed size */ + MDBX_DUPFIXED = UINT32_C(0x10), + + /** With \ref MDBX_DUPSORT and with \ref MDBX_DUPFIXED; dups are fixed size + * \ref MDBX_INTEGERKEY -style integers. The data values must all be of the + * same size and must be aligned while passing as arguments. */ + MDBX_INTEGERDUP = UINT32_C(0x20), + + /** With \ref MDBX_DUPSORT; use reverse string comparison */ + MDBX_REVERSEDUP = UINT32_C(0x40), + + /** Create DB if not already existing */ + MDBX_CREATE = UINT32_C(0x40000), + + /** Opens an existing sub-database created with unknown flags. + * + * The `MDBX_DB_ACCEDE` flag is intend to open a existing sub-database which + * was created with unknown flags (\ref MDBX_REVERSEKEY, \ref MDBX_DUPSORT, + * \ref MDBX_INTEGERKEY, \ref MDBX_DUPFIXED, \ref MDBX_INTEGERDUP and + * \ref MDBX_REVERSEDUP). + * + * In such cases, instead of returning the \ref MDBX_INCOMPATIBLE error, the + * sub-database will be opened with flags which it was created, and then an + * application could determine the actual flags by \ref mdbx_dbi_flags(). */ + MDBX_DB_ACCEDE = MDBX_ACCEDE +}; +#ifndef __cplusplus +/** \ingroup c_dbi */ +typedef enum MDBX_db_flags_t MDBX_db_flags_t; +#else +DEFINE_ENUM_FLAG_OPERATORS(MDBX_db_flags_t) +#endif + +/** \brief Data changing flags + * \ingroup c_crud + * \see \ref c_crud_hints "Quick reference for Insert/Update/Delete operations" + * \see mdbx_put() \see mdbx_cursor_put() \see mdbx_replace() */ +enum MDBX_put_flags_t { + /** Upsertion by default (without any other flags) */ + MDBX_UPSERT = 0, + + /** For insertion: Don't write if the key already exists. */ + MDBX_NOOVERWRITE = UINT32_C(0x10), + + /** Has effect only for \ref MDBX_DUPSORT databases. + * For upsertion: don't write if the key-value pair already exist. + * For deletion: remove all values for key. */ + MDBX_NODUPDATA = UINT32_C(0x20), + + /** For upsertion: overwrite the current key/data pair. + * MDBX allows this flag for \ref mdbx_put() for explicit overwrite/update + * without insertion. + * For deletion: remove only single entry at the current cursor position. */ + 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. + * Return a pointer to the reserved space. */ + MDBX_RESERVE = UINT32_C(0x10000), + + /** Data is being appended. + * Don't split full pages, continue on a new instead. */ + MDBX_APPEND = UINT32_C(0x20000), + + /** Has effect only for \ref MDBX_DUPSORT databases. + * Duplicate data is being appended. + * Don't split full pages, continue on a new instead. */ + MDBX_APPENDDUP = UINT32_C(0x40000), + + /** Only for \ref MDBX_DUPFIXED. + * Store multiple data items in one call. */ + MDBX_MULTIPLE = UINT32_C(0x80000) +}; +#ifndef __cplusplus +/** \ingroup c_crud */ +typedef enum MDBX_put_flags_t MDBX_put_flags_t; +#else +DEFINE_ENUM_FLAG_OPERATORS(MDBX_put_flags_t) +#endif + +/** \brief Environment copy flags + * \ingroup c_extra + * \see mdbx_env_copy() \see mdbx_env_copy2fd() */ +enum MDBX_copy_flags_t { + MDBX_CP_DEFAULTS = 0, + + /** Copy with compactification: Omit free space from copy and renumber all + * pages sequentially */ + MDBX_CP_COMPACT = 1u, + + /** Force to make resizeable copy, i.e. dynamic size instead of fixed */ + MDBX_CP_FORCE_DYNAMIC_SIZE = 2u +}; +#ifndef __cplusplus +/** \ingroup c_extra */ +typedef enum MDBX_copy_flags_t MDBX_copy_flags_t; +#else +DEFINE_ENUM_FLAG_OPERATORS(MDBX_copy_flags_t) +#endif + +/** \brief Cursor operations + * \ingroup c_cursors + * This is the set of all operations for retrieving data using a cursor. + * \see mdbx_cursor_set() */ +enum MDBX_cursor_op { + /** Position at first key/data item */ + MDBX_FIRST, + + /** \ref MDBX_DUPSORT -only: Position at first data item of current key. */ + MDBX_FIRST_DUP, + + /** \ref MDBX_DUPSORT -only: Position at key/data pair. */ + MDBX_GET_BOTH, + + /** \ref MDBX_DUPSORT -only: Position at given key and at first data greater + * than or equal to specified data. */ + MDBX_GET_BOTH_RANGE, + + /** Return key/data at current cursor position */ + MDBX_GET_CURRENT, + + /** \ref MDBX_DUPFIXED -only: Return up to a page of duplicate data items + * from current cursor position. Move cursor to prepare + * for \ref MDBX_NEXT_MULTIPLE. */ + MDBX_GET_MULTIPLE, + + /** Position at last key/data item */ + MDBX_LAST, + + /** \ref MDBX_DUPSORT -only: Position at last data item of current key. */ + MDBX_LAST_DUP, + + /** Position at next data item */ + MDBX_NEXT, + + /** \ref MDBX_DUPSORT -only: Position at next data item of current key. */ + MDBX_NEXT_DUP, + + /** \ref MDBX_DUPFIXED -only: Return up to a page of duplicate data items + * from next cursor position. Move cursor to prepare + * for `MDBX_NEXT_MULTIPLE`. */ + MDBX_NEXT_MULTIPLE, + + /** Position at first data item of next key */ + MDBX_NEXT_NODUP, + + /** Position at previous data item */ + MDBX_PREV, + + /** \ref MDBX_DUPSORT -only: Position at previous data item of current key. */ + MDBX_PREV_DUP, + + /** Position at last data item of previous key */ + MDBX_PREV_NODUP, + + /** Position at specified key */ + MDBX_SET, + + /** Position at specified key, return both key and data */ + MDBX_SET_KEY, + + /** Position at first key greater than or equal to specified key. */ + MDBX_SET_RANGE, + + /** \ref MDBX_DUPFIXED -only: Position at previous page and return up to + * a page of duplicate data items. */ + MDBX_PREV_MULTIPLE, + + /** Position at first key-value pair greater than or equal to specified, + * return both key and data, and the return code depends on a exact match. + * + * For non DUPSORT-ed collections this work the same to \ref MDBX_SET_RANGE, + * but returns \ref MDBX_SUCCESS if key found exactly and + * \ref MDBX_RESULT_TRUE if greater key was found. + * + * For DUPSORT-ed a data value is taken into account for duplicates, + * i.e. for a pairs/tuples of a key and an each data value of duplicates. + * Returns \ref MDBX_SUCCESS if key-value pair found exactly and + * \ref MDBX_RESULT_TRUE if the next pair was returned. */ + MDBX_SET_LOWERBOUND +}; +#ifndef __cplusplus +/** \ingroup c_cursors */ +typedef enum MDBX_cursor_op MDBX_cursor_op; +#endif + +/** \brief Errors and return codes + * \ingroup c_err + * + * BerkeleyDB uses -30800 to -30999, we'll go under them + * \see mdbx_strerror() \see mdbx_strerror_r() \see mdbx_liberr2str() */ +enum MDBX_error_t { + /** Successful result */ + MDBX_SUCCESS = 0, + + /** Alias for \ref MDBX_SUCCESS */ + MDBX_RESULT_FALSE = MDBX_SUCCESS, + + /** Successful result with special meaning or a flag */ + MDBX_RESULT_TRUE = -1, + + /** key/data pair already exists */ + MDBX_KEYEXIST = -30799, + + /** The first LMDB-compatible defined error code */ + MDBX_FIRST_LMDB_ERRCODE = MDBX_KEYEXIST, + + /** key/data pair not found (EOF) */ + MDBX_NOTFOUND = -30798, + + /** Requested page not found - this usually indicates corruption */ + MDBX_PAGE_NOTFOUND = -30797, + + /** Database is corrupted (page was wrong type and so on) */ + MDBX_CORRUPTED = -30796, + + /** Environment had fatal error, + * i.e. update of meta page failed and so on. */ + MDBX_PANIC = -30795, + + /** DB file version mismatch with libmdbx */ + MDBX_VERSION_MISMATCH = -30794, + + /** File is not a valid MDBX file */ + MDBX_INVALID = -30793, + + /** Environment mapsize reached */ + MDBX_MAP_FULL = -30792, + + /** Environment maxdbs reached */ + MDBX_DBS_FULL = -30791, + + /** Environment maxreaders reached */ + MDBX_READERS_FULL = -30790, + + /** Transaction has too many dirty pages, i.e transaction too big */ + MDBX_TXN_FULL = -30788, + + /** Cursor stack too deep - this usually indicates corruption, + * i.e branch-pages loop */ + MDBX_CURSOR_FULL = -30787, + + /** Page has not enough space - internal error */ + MDBX_PAGE_FULL = -30786, + + /** Database engine was unable to extend mapping, e.g. since address space + * is unavailable or busy. This can mean: + * - Database size extended by other process beyond to environment mapsize + * and engine was unable to extend mapping while starting read + * transaction. Environment should be reopened to continue. + * - Engine was unable to extend mapping during write transaction + * or explicit call of \ref mdbx_env_set_geometry(). */ + MDBX_UNABLE_EXTEND_MAPSIZE = -30785, + + /** Environment or database is not compatible with the requested operation + * or the specified flags. This can mean: + * - The operation expects an \ref MDBX_DUPSORT / \ref MDBX_DUPFIXED + * database. + * - Opening a named DB when the unnamed DB has \ref MDBX_DUPSORT / + * \ref MDBX_INTEGERKEY. + * - Accessing a data record as a database, or vice versa. + * - The database was dropped and recreated with different flags. */ + MDBX_INCOMPATIBLE = -30784, + + /** Invalid reuse of reader locktable slot, + * e.g. read-transaction already run for current thread */ + MDBX_BAD_RSLOT = -30783, + + /** Transaction is not valid for requested operation, + * e.g. had errored and be must aborted, has a child, or is invalid */ + MDBX_BAD_TXN = -30782, + + /** Invalid size or alignment of key or data for target database, + * either invalid subDB name */ + MDBX_BAD_VALSIZE = -30781, + + /** The specified DBI-handle is invalid + * or changed by another thread/transaction */ + MDBX_BAD_DBI = -30780, + + /** Unexpected internal error, transaction should be aborted */ + MDBX_PROBLEM = -30779, + + /** The last LMDB-compatible defined error code */ + MDBX_LAST_LMDB_ERRCODE = MDBX_PROBLEM, + + /** Another write transaction is running or environment is already used while + * opening with \ref MDBX_EXCLUSIVE flag */ + MDBX_BUSY = -30778, + + /** The first of MDBX-added error codes */ + MDBX_FIRST_ADDED_ERRCODE = MDBX_BUSY, + + /** The specified key has more than one associated value */ + MDBX_EMULTIVAL = -30421, + + /** Bad signature of a runtime object(s), this can mean: + * - memory corruption or double-free; + * - ABI version mismatch (rare case); */ + MDBX_EBADSIGN = -30420, + + /** Database should be recovered, but this could NOT be done for now + * since it opened in read-only mode */ + MDBX_WANNA_RECOVERY = -30419, + + /** The given key value is mismatched to the current cursor position */ + MDBX_EKEYMISMATCH = -30418, + + /** Database is too large for current system, + * e.g. could NOT be mapped into RAM. */ + MDBX_TOO_LARGE = -30417, + + /** A thread has attempted to use a not owned object, + * e.g. a transaction that started by another thread. */ + MDBX_THREAD_MISMATCH = -30416, + + /** Overlapping read and write transactions for the current thread */ + MDBX_TXN_OVERLAPPING = -30415, + + /* The last of MDBX-added error codes */ + MDBX_LAST_ADDED_ERRCODE = MDBX_TXN_OVERLAPPING, + +#if defined(_WIN32) || defined(_WIN64) + MDBX_ENODATA = ERROR_HANDLE_EOF, + MDBX_EINVAL = ERROR_INVALID_PARAMETER, + MDBX_EACCESS = ERROR_ACCESS_DENIED, + MDBX_ENOMEM = ERROR_OUTOFMEMORY, + MDBX_EROFS = ERROR_FILE_READ_ONLY, + MDBX_ENOSYS = ERROR_NOT_SUPPORTED, + MDBX_EIO = ERROR_WRITE_FAULT, + MDBX_EPERM = ERROR_INVALID_FUNCTION, + MDBX_EINTR = ERROR_CANCELLED, + MDBX_ENOFILE = ERROR_FILE_NOT_FOUND, + MDBX_EREMOTE = ERROR_REMOTE_STORAGE_MEDIA_ERROR +#else /* Windows */ +#ifdef ENODATA + MDBX_ENODATA = ENODATA, +#else + MDBX_ENODATA = -1, +#endif /* ENODATA */ + MDBX_EINVAL = EINVAL, + MDBX_EACCESS = EACCES, + MDBX_ENOMEM = ENOMEM, + MDBX_EROFS = EROFS, + MDBX_ENOSYS = ENOSYS, + MDBX_EIO = EIO, + MDBX_EPERM = EPERM, + MDBX_EINTR = EINTR, + MDBX_ENOFILE = ENOENT, + MDBX_EREMOTE = ENOTBLK +#endif /* !Windows */ +}; +#ifndef __cplusplus +/** \ingroup c_err */ +typedef enum MDBX_error_t MDBX_error_t; +#endif + +/** MDBX_MAP_RESIZED + * \ingroup c_err + * \deprecated Please review your code to use MDBX_UNABLE_EXTEND_MAPSIZE + * instead. */ +MDBX_DEPRECATED static __inline int MDBX_MAP_RESIZED_is_deprecated() { + return MDBX_UNABLE_EXTEND_MAPSIZE; +} +#define MDBX_MAP_RESIZED MDBX_MAP_RESIZED_is_deprecated() + +/** \brief Return a string describing a given error code. + * \ingroup c_err + * + * This function is a superset of the ANSI C X3.159-1989 (ANSI C) `strerror()` + * function. If the error code is greater than or equal to 0, then the string + * returned by the system function `strerror()` is returned. If the error code + * is less than 0, an error string corresponding to the MDBX library error is + * returned. See errors for a list of MDBX-specific error codes. + * + * `mdbx_strerror()` is NOT thread-safe because may share common internal buffer + * for system messages. The returned string must NOT be modified by the + * application, but MAY be modified by a subsequent call to + * \ref mdbx_strerror(), `strerror()` and other related functions. + * \see mdbx_strerror_r() + * + * \param [in] errnum The error code. + * + * \returns "error message" The description of the error. */ +LIBMDBX_API const char *mdbx_strerror(int errnum); + +/** \brief Return a string describing a given error code. + * \ingroup c_err + * + * This function is a superset of the ANSI C X3.159-1989 (ANSI C) `strerror()` + * function. If the error code is greater than or equal to 0, then the string + * returned by the system function `strerror()` is returned. If the error code + * is less than 0, an error string corresponding to the MDBX library error is + * returned. See errors for a list of MDBX-specific error codes. + * + * `mdbx_strerror_r()` is thread-safe since uses user-supplied buffer where + * appropriate. The returned string must NOT be modified by the application, + * since it may be pointer to internal constant string. However, there is no + * restriction if the returned string points to the supplied buffer. + * \see mdbx_strerror() + * + * mdbx_liberr2str() returns string describing only MDBX error numbers but NULL + * for non-MDBX error codes. This function is thread-safe since return pointer + * to constant non-localized strings. + * + * \param [in] errnum The error code. + * \param [in,out] buf Buffer to store the error message. + * \param [in] buflen The size of buffer to store the message. + * + * \returns "error message" The description of the error. */ +LIBMDBX_API const char *mdbx_strerror_r(int errnum, char *buf, size_t buflen); +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API const char *mdbx_liberr2str(int errnum); + +#if defined(_WIN32) || defined(_WIN64) || defined(DOXYGEN) +/** Bit of Windows' madness. The similar to \ref mdbx_strerror() but returns + * Windows error-messages in the OEM-encoding for console utilities. + * \ingroup c_err + * \see mdbx_strerror_r_ANSI2OEM() */ +LIBMDBX_API const char *mdbx_strerror_ANSI2OEM(int errnum); + +/** Bit of Windows' madness. The similar to \ref mdbx_strerror_r() but returns + * Windows error-messages in the OEM-encoding for console utilities. + * \ingroup c_err + * \see mdbx_strerror_ANSI2OEM() */ +LIBMDBX_API const char *mdbx_strerror_r_ANSI2OEM(int errnum, char *buf, + size_t buflen); +#endif /* Bit of Windows' madness */ + +/** \brief Create an MDBX environment instance. + * \ingroup c_opening + * + * This function allocates memory for a \ref MDBX_env structure. To release + * the allocated memory and discard the handle, call \ref mdbx_env_close(). + * Before the handle may be used, it must be opened using \ref mdbx_env_open(). + * + * Various other options may also need to be set before opening the handle, + * e.g. \ref mdbx_env_set_geometry(), \ref mdbx_env_set_maxreaders(), + * \ref mdbx_env_set_maxdbs(), depending on usage requirements. + * + * \param [out] penv The address where the new handle will be stored. + * + * \returns a non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_env_create(MDBX_env **penv); + +/** \brief MDBX environment options. */ +enum MDBX_option_t { + /** \brief Controls the maximum number of named databases for the environment. + * + * \details By default only unnamed key-value database could used and + * appropriate value should set by `MDBX_opt_max_db` to using any more named + * subDB(s). To reduce overhead, use the minimum sufficient value. This option + * may only set after \ref mdbx_env_create() and before \ref mdbx_env_open(). + * + * \see mdbx_env_set_maxdbs() \see mdbx_env_get_maxdbs() */ + MDBX_opt_max_db, + + /** \brief Defines the maximum number of threads/reader slots + * for all processes interacting with the database. + * + * \details This defines the number of slots in the lock table that is used to + * track readers in the the environment. The default is about 100 for 4K + * system page size. Starting a read-only transaction normally ties a lock + * table slot to the current thread until the environment closes or the thread + * exits. If \ref MDBX_NOTLS is in use, \ref mdbx_txn_begin() instead ties the + * slot to the \ref MDBX_txn object until it or the \ref MDBX_env object is + * destroyed. This option may only set after \ref mdbx_env_create() and before + * \ref mdbx_env_open(), and has an effect only when the database is opened by + * the first process interacts with the database. + * + * \see mdbx_env_set_maxreaders() \see mdbx_env_get_maxreaders() */ + MDBX_opt_max_readers, + + /** \brief Controls interprocess/shared threshold to force flush the data + * buffers to disk, if \ref MDBX_SAFE_NOSYNC is used. + * + * \see mdbx_env_set_syncbytes() \see mdbx_env_get_syncbytes() */ + MDBX_opt_sync_bytes, + + /** \brief Controls interprocess/shared relative period since the last + * unsteady commit to force flush the data buffers to disk, + * if \ref MDBX_SAFE_NOSYNC is used. + * \see mdbx_env_set_syncperiod() \see mdbx_env_get_syncperiod() */ + MDBX_opt_sync_period, + + /** \brief Controls the in-process limit to grow a list of reclaimed/recycled + * page's numbers for finding a sequence of contiguous pages for large data + * items. + * + * \details A long values requires allocation of contiguous database pages. + * To find such sequences, it may be necessary to accumulate very large lists, + * especially when placing very long values (more than a megabyte) in a large + * databases (several tens of gigabytes), which is much expensive in extreme + * cases. This threshold allows you to avoid such costs by allocating new + * pages at the end of the database (with its possible growth on disk), + * instead of further accumulating/reclaiming Garbage Collection records. + * + * On the other hand, too small threshold will lead to unreasonable database + * growth, or/and to the inability of put long values. + * + * The `MDBX_opt_rp_augment_limit` controls described limit for the current + * process. Default is 262144, it is usually enough for most cases. */ + MDBX_opt_rp_augment_limit, + + /** \brief Controls the in-process limit to grow a cache of dirty + * pages for reuse in the current transaction. + * + * \details A 'dirty page' refers to a page that has been updated in memory + * only, the changes to a dirty page are not yet stored on disk. + * To reduce overhead, it is reasonable to release not all such pages + * immediately, but to leave some ones in cache for reuse in the current + * transaction. + * + * The `MDBX_opt_loose_limit` allows you to set a limit for such cache inside + * the current process. Should be in the range 0..255, default is 64. */ + MDBX_opt_loose_limit, + + /** \brief Controls the in-process limit of a pre-allocated memory items + * for dirty pages. + * + * \details A 'dirty page' refers to a page that has been updated in memory + * only, the changes to a dirty page are not yet stored on disk. + * Without \ref MDBX_WRITEMAP dirty pages are allocated from memory and + * released when a transaction is committed. To reduce overhead, it is + * reasonable to release not all ones, but to leave some allocations in + * reserve for reuse in the next transaction(s). + * + * The `MDBX_opt_dp_reserve_limit` allows you to set a limit for such reserve + * inside the current process. Default is 1024. */ + MDBX_opt_dp_reserve_limit, + + /** \brief Controls the in-process limit of dirty pages + * for a write transaction. + * + * \details A 'dirty page' refers to a page that has been updated in memory + * only, the changes to a dirty page are not yet stored on disk. + * Without \ref MDBX_WRITEMAP dirty pages are allocated from memory and will + * be busy until are written to disk. Therefore for a large transactions is + * reasonable to limit dirty pages collecting above an some threshold but + * spill to disk instead. + * + * The `MDBX_opt_txn_dp_limit` controls described threshold for the current + * process. Default is 65536, it is usually enough for most cases. */ + MDBX_opt_txn_dp_limit, + + /** \brief Controls the in-process initial allocation size for dirty pages + * list of a write transaction. Default is 1024. */ + MDBX_opt_txn_dp_initial, + + /** \brief Controls the in-process how maximal part of the dirty pages may be + * spilled when necessary. + * + * \details The `MDBX_opt_spill_max_denominator` defines the denominator for + * limiting from the top for part of the current dirty pages may be spilled + * when the free room for a new dirty pages (i.e. distance to the + * `MDBX_opt_txn_dp_limit` threshold) is not enough to perform requested + * operation. + * Exactly `max_pages_to_spill = dirty_pages - dirty_pages / N`, + * where `N` is the value set by `MDBX_opt_spill_max_denominator`. + * + * Should be in the range 0..255, where zero means no limit, i.e. all dirty + * pages could be spilled. Default is 8, i.e. no more than 7/8 of the current + * dirty pages may be spilled when reached the condition described above. */ + MDBX_opt_spill_max_denominator, + + /** \brief Controls the in-process how minimal part of the dirty pages should + * be spilled when necessary. + * + * \details The `MDBX_opt_spill_min_denominator` defines the denominator for + * limiting from the bottom for part of the current dirty pages should be + * spilled when the free room for a new dirty pages (i.e. distance to the + * `MDBX_opt_txn_dp_limit` threshold) is not enough to perform requested + * operation. + * Exactly `min_pages_to_spill = dirty_pages / N`, + * where `N` is the value set by `MDBX_opt_spill_min_denominator`. + * + * Should be in the range 0..255, where zero means no restriction at the + * bottom. Default is 8, i.e. at least the 1/8 of the current dirty pages + * should be spilled when reached the condition described above. */ + MDBX_opt_spill_min_denominator, + + /** \brief Controls the in-process how much of the parent transaction dirty + * pages will be spilled while start each child transaction. + * + * \details The `MDBX_opt_spill_parent4child_denominator` defines the + * denominator to determine how much of parent transaction dirty pages will be + * spilled explicitly while start each child transaction. + * Exactly `pages_to_spill = dirty_pages / N`, + * where `N` is the value set by `MDBX_opt_spill_parent4child_denominator`. + * + * For a stack of nested transactions each dirty page could be spilled only + * once, and parent's dirty pages couldn't be spilled while child + * transaction(s) are running. Therefore a child transaction could reach + * \ref MDBX_TXN_FULL when parent(s) transaction has spilled too less (and + * child reach the limit of dirty pages), either when parent(s) has spilled + * too more (since child can't spill already spilled pages). So there is no + * universal golden ratio. + * + * Should be in the range 0..255, where zero means no explicit spilling will + * be performed during starting nested transactions. + * Default is 0, i.e. by default no spilling performed during starting nested + * transactions, that correspond historically behaviour. */ + MDBX_opt_spill_parent4child_denominator, +}; +#ifndef __cplusplus +/** \ingroup c_settings */ +typedef enum MDBX_option_t MDBX_option_t; +#endif + +LIBMDBX_API int mdbx_env_set_option(MDBX_env *env, const MDBX_option_t option, + const uint64_t value); +LIBMDBX_API int mdbx_env_get_option(const MDBX_env *env, + const MDBX_option_t option, + uint64_t *value); + +/** \brief Open an environment instance. + * \ingroup c_opening + * + * Indifferently this function will fails or not, the \ref mdbx_env_close() must + * be called later to discard the \ref MDBX_env handle and release associated + * resources. + * + * \param [in] env An environment handle returned + * by \ref mdbx_env_create() + * + * \param [in] pathname The pathname for the database or the directory in which + * the database files reside. In the case of directory it + * must already exist and be writable. + * + * \param [in] flags Special options for this environment. This parameter + * must be set to 0 or by bitwise OR'ing together one + * or more of the values described above in the + * \ref env_flags and \ref sync_modes sections. + * + * Flags set by mdbx_env_set_flags() are also used: + * - \ref MDBX_NOSUBDIR, \ref MDBX_RDONLY, \ref MDBX_EXCLUSIVE, + * \ref MDBX_WRITEMAP, \ref MDBX_NOTLS, \ref MDBX_NORDAHEAD, + * \ref MDBX_NOMEMINIT, \ref MDBX_COALESCE, \ref MDBX_LIFORECLAIM. + * See \ref env_flags section. + * + * - \ref MDBX_NOMETASYNC, \ref MDBX_SAFE_NOSYNC, \ref MDBX_UTTERLY_NOSYNC. + * See \ref sync_modes section. + * + * \note `MDB_NOLOCK` flag don't supported by MDBX, + * try use \ref MDBX_EXCLUSIVE as a replacement. + * + * \note MDBX don't allow to mix processes with different \ref MDBX_SAFE_NOSYNC + * flags on the same environment. + * In such case \ref MDBX_INCOMPATIBLE will be returned. + * + * If the database is already exist and parameters specified early by + * \ref mdbx_env_set_geometry() are incompatible (i.e. for instance, different + * page size) then \ref mdbx_env_open() will return \ref MDBX_INCOMPATIBLE + * error. + * + * \param [in] mode The UNIX permissions to set on created files. + * Zero value means to open existing, but do not create. + * + * \return A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_VERSION_MISMATCH The version of the MDBX library doesn't match + * the version that created the database environment. + * \retval MDBX_INVALID The environment file headers are corrupted. + * \retval MDBX_ENOENT The directory specified by the path parameter + * doesn't exist. + * \retval MDBX_EACCES The user didn't have permission to access + * the environment files. + * \retval MDBX_EAGAIN The environment was locked by another process. + * \retval MDBX_BUSY The \ref MDBX_EXCLUSIVE flag was specified and the + * environment is in use by another process, + * or the current process tries to open environment + * more than once. + * \retval MDBX_INCOMPATIBLE Environment is already opened by another process, + * but with different set of \ref MDBX_SAFE_NOSYNC, + * \ref MDBX_UTTERLY_NOSYNC flags. + * Or if the database is already exist and parameters + * specified early by \ref mdbx_env_set_geometry() + * are incompatible (i.e. different pagesize, etc). + * + * \retval MDBX_WANNA_RECOVERY The \ref MDBX_RDONLY flag was specified but + * read-write access is required to rollback + * inconsistent state after a system crash. + * + * \retval MDBX_TOO_LARGE Database is too large for this process, + * i.e. 32-bit process tries to open >4Gb database. + */ +LIBMDBX_API int mdbx_env_open(MDBX_env *env, const char *pathname, + MDBX_env_flags_t flags, mdbx_mode_t mode); + +/** \brief Deletion modes for \ref mdbx_env_delete(). + * \ingroup c_extra + * \see mdbx_env_delete() */ +enum MDBX_env_delete_mode_t { + /** \brief Just delete the environment's files and directory if any. + * \note On POSIX systems, processes already working with the database will + * continue to work without interference until it close the environment. + * \note On Windows, the behavior of `MDB_ENV_JUST_DELETE` is different + * because the system does not support deleting files that are currently + * memory mapped. */ + MDBX_ENV_JUST_DELETE = 0, + /** \brief Make sure that the environment is not being used by other + * processes, or return an error otherwise. */ + MDBX_ENV_ENSURE_UNUSED = 1, + /** \brief Wait until other processes closes the environment before deletion. + */ + MDBX_ENV_WAIT_FOR_UNUSED = 2, +}; +#ifndef __cplusplus +/** \ingroup c_extra */ +typedef enum MDBX_env_delete_mode_t MDBX_env_delete_mode_t; +#endif + +/** \brief Delete the environment's files in a proper and multiprocess-safe way. + * \ingroup c_extra + * + * \param [in] pathname The pathname for the database or the directory in which + * the database files reside. + * + * \param [in] mode Special deletion mode for the environment. This + * parameter must be set to one of the values described + * above in the \ref MDBX_env_delete_mode_t section. + * + * \note The \ref MDBX_ENV_JUST_DELETE don't supported on Windows since system + * unable to delete a memory-mapped files. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_RESULT_TRUE No corresponding files or directories were found, + * so no deletion was performed. */ +LIBMDBX_API int mdbx_env_delete(const char *pathname, + MDBX_env_delete_mode_t mode); + +/** \brief Copy an MDBX environment to the specified path, with options. + * \ingroup c_extra + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * \note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under \ref restrictions section. + * + * \param [in] env An environment handle returned by mdbx_env_create(). + * It must have already been opened successfully. + * \param [in] dest The pathname of a file in which the copy will reside. + * This file must not be already exist, but parent directory + * must be writable. + * \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: + * + * - \ref MDBX_CP_COMPACT + * Perform compaction while copying: omit free pages and sequentially + * renumber all pages in output. This option consumes little bit more + * CPU for processing, but may running quickly than the default, on + * account skipping free pages. + * + * - \ref MDBX_CP_FORCE_DYNAMIC_SIZE + * Force to make resizeable copy, i.e. dynamic size instead of fixed. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_env_copy(MDBX_env *env, const char *dest, + MDBX_copy_flags_t flags); + +/** \brief Copy an environment to the specified file descriptor, with + * options. \ingroup c_extra + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * \see mdbx_env_copy() + * + * \note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under \ref restrictions + * section. + * + * \note Fails if the environment has suffered a page leak and the destination + * file descriptor is associated with a pipe, socket, or FIFO. + * + * \param [in] env An environment handle returned by mdbx_env_create(). + * It must have already been opened successfully. + * \param [in] fd The file descriptor to write the copy to. It must have + * already been opened for Write access. + * \param [in] flags Special options for this operation. \see mdbx_env_copy() + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_env_copy2fd(MDBX_env *env, mdbx_filehandle_t fd, + MDBX_copy_flags_t flags); + +/** \brief Statistics for a database in the environment + * \ingroup c_statinfo + * \see mdbx_env_stat_ex() \see mdbx_dbi_stat() */ +struct MDBX_stat { + uint32_t ms_psize; /**< Size of a database page. This is the same for all + databases. */ + uint32_t ms_depth; /**< Depth (height) of the B-tree */ + uint64_t ms_branch_pages; /**< Number of internal (non-leaf) pages */ + uint64_t ms_leaf_pages; /**< Number of leaf pages */ + uint64_t ms_overflow_pages; /**< Number of overflow pages */ + uint64_t ms_entries; /**< Number of data items */ + uint64_t ms_mod_txnid; /**< Transaction ID of committed last modification */ +}; +#ifndef __cplusplus +/** \ingroup c_statinfo */ +typedef struct MDBX_stat MDBX_stat; +#endif + +/** \brief Return statistics about the MDBX environment. + * \ingroup c_statinfo + * + * At least one of env or txn argument must be non-null. If txn is passed + * non-null then stat will be filled accordingly to the given transaction. + * Otherwise, if txn is null, then stat will be populated by a snapshot from + * the last committed write transaction, and at next time, other information + * can be returned. + * + * Legacy mdbx_env_stat() correspond to calling \ref mdbx_env_stat_ex() with the + * null `txn` argument. + * + * \param [in] env An environment handle returned by \ref mdbx_env_create() + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin() + * \param [out] stat The address of an \ref MDBX_stat structure where + * the statistics will be copied + * \param [in] bytes The size of \ref MDBX_stat. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_env_stat_ex(const MDBX_env *env, const MDBX_txn *txn, + MDBX_stat *stat, size_t bytes); + +/** \brief Return statistics about the MDBX environment. + * \ingroup c_statinfo + * \deprecated Please use mdbx_env_stat_ex() instead. */ +MDBX_DEPRECATED LIBMDBX_INLINE_API(int, mdbx_env_stat, + (const MDBX_env *env, MDBX_stat *stat, + size_t bytes)) { + return mdbx_env_stat_ex(env, NULL, stat, bytes); +} + +/** \brief Information about the environment + * \ingroup c_statinfo + * \see mdbx_env_info_ex() */ +struct MDBX_envinfo { + struct { + uint64_t lower; /**< Lower limit for datafile size */ + uint64_t upper; /**< Upper limit for datafile size */ + uint64_t current; /**< Current datafile size */ + uint64_t shrink; /**< Shrink threshold for datafile */ + uint64_t grow; /**< Growth step for datafile */ + } mi_geo; + uint64_t mi_mapsize; /**< Size of the data memory map */ + uint64_t mi_last_pgno; /**< Number of the last used page */ + uint64_t mi_recent_txnid; /**< ID of the last committed transaction */ + uint64_t mi_latter_reader_txnid; /**< ID of the last reader transaction */ + uint64_t mi_self_latter_reader_txnid; /**< ID of the last reader transaction + of caller process */ + uint64_t mi_meta0_txnid, mi_meta0_sign; + uint64_t mi_meta1_txnid, mi_meta1_sign; + uint64_t mi_meta2_txnid, mi_meta2_sign; + uint32_t mi_maxreaders; /**< Total reader slots in the environment */ + uint32_t mi_numreaders; /**< Max reader slots used in the environment */ + uint32_t mi_dxb_pagesize; /**< Database pagesize */ + uint32_t mi_sys_pagesize; /**< System pagesize */ + + /** \brief A mostly unique ID that is regenerated on each boot. + + As such it can be used to identify the local machine's current boot. MDBX + uses such when open the database to determine whether rollback required to + the last steady sync point or not. I.e. if current bootid is differ from the + value within a database then the system was rebooted and all changes since + last steady sync must be reverted for data integrity. Zeros mean that no + relevant information is available from the system. */ + struct { + struct { + uint64_t x, y; + } current, meta0, meta1, meta2; + } mi_bootid; + + /** Bytes not explicitly synchronized to disk */ + uint64_t mi_unsync_volume; + /** Current auto-sync threshold, see \ref mdbx_env_set_syncbytes(). */ + uint64_t mi_autosync_threshold; + /** Time since the last steady sync in 1/65536 of second */ + uint32_t mi_since_sync_seconds16dot16; + /** Current auto-sync period in 1/65536 of second, + * see \ref mdbx_env_set_syncperiod(). */ + uint32_t mi_autosync_period_seconds16dot16; + /** Time since the last readers check in 1/65536 of second, + * see \ref mdbx_reader_check(). */ + uint32_t mi_since_reader_check_seconds16dot16; + /** Current environment mode. + * The same as \ref mdbx_env_get_flags() returns. */ + uint32_t mi_mode; +}; +#ifndef __cplusplus +/** \ingroup c_statinfo */ +typedef struct MDBX_envinfo MDBX_envinfo; +#endif + +/** \brief Return information about the MDBX environment. + * \ingroup c_statinfo + * + * At least one of env or txn argument must be non-null. If txn is passed + * non-null then stat will be filled accordingly to the given transaction. + * Otherwise, if txn is null, then stat will be populated by a snapshot from + * the last committed write transaction, and at next time, other information + * can be returned. + * + * Legacy \ref mdbx_env_info() correspond to calling \ref mdbx_env_info_ex() + * with the null `txn` argument. + * + * \param [in] env An environment handle returned by \ref mdbx_env_create() + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin() + * \param [out] info The address of an \ref MDBX_envinfo structure + * where the information will be copied + * \param [in] bytes The size of \ref MDBX_envinfo. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_env_info_ex(const MDBX_env *env, const MDBX_txn *txn, + MDBX_envinfo *info, size_t bytes); +/** \brief Return information about the MDBX environment. + * \ingroup c_statinfo + * \deprecated Please use mdbx_env_info_ex() instead. */ +MDBX_DEPRECATED LIBMDBX_INLINE_API(int, mdbx_env_info, + (const MDBX_env *env, MDBX_envinfo *info, + size_t bytes)) { + return mdbx_env_info_ex(env, NULL, info, bytes); +} + +/** \brief Flush the environment data buffers to disk. + * \ingroup c_extra + * + * Unless the environment was opened with no-sync flags (\ref MDBX_NOMETASYNC, + * \ref MDBX_SAFE_NOSYNC and \ref MDBX_UTTERLY_NOSYNC), then + * data is always written an flushed to disk when \ref mdbx_txn_commit() is + * called. Otherwise \ref mdbx_env_sync() may be called to manually write and + * flush unsynced data to disk. + * + * Besides, \ref mdbx_env_sync_ex() with argument `force=false` may be used to + * provide polling mode for lazy/asynchronous sync in conjunction with + * \ref mdbx_env_set_syncbytes() and/or \ref mdbx_env_set_syncperiod(). + * + * \note This call is not valid if the environment was opened with MDBX_RDONLY. + * + * \param [in] env An environment handle returned by \ref mdbx_env_create() + * \param [in] force If non-zero, force a flush. Otherwise, If force is + * zero, then will run in polling mode, + * i.e. it will check the thresholds that were + * set \ref mdbx_env_set_syncbytes() + * and/or \ref mdbx_env_set_syncperiod() and perform flush + * if at least one of the thresholds is reached. + * + * \param [in] nonblock Don't wait if write transaction + * is running by other thread. + * + * \returns A non-zero error value on failure and \ref MDBX_RESULT_TRUE or 0 on + * success. The \ref MDBX_RESULT_TRUE means no data pending for flush + * to disk, and 0 otherwise. Some possible errors are: + * + * \retval MDBX_EACCES the environment is read-only. + * \retval MDBX_BUSY the environment is used by other thread + * and `nonblock=true`. + * \retval MDBX_EINVAL an invalid parameter was specified. + * \retval MDBX_EIO an error occurred during synchronization. */ +LIBMDBX_API int mdbx_env_sync_ex(MDBX_env *env, bool force, bool nonblock); + +/** \brief The shortcut to calling \ref mdbx_env_sync_ex() with + * the `force=true` and `nonblock=false` arguments. + * \ingroup c_extra */ +LIBMDBX_INLINE_API(int, mdbx_env_sync, (MDBX_env * env)) { + return mdbx_env_sync_ex(env, true, false); +} + +/** \brief The shortcut to calling \ref mdbx_env_sync_ex() with + * the `force=false` and `nonblock=true` arguments. + * \ingroup c_extra */ +LIBMDBX_INLINE_API(int, mdbx_env_sync_poll, (MDBX_env * env)) { + return mdbx_env_sync_ex(env, false, true); +} + +/** \brief Sets threshold to force flush the data buffers to disk, even any of + * \ref MDBX_SAFE_NOSYNC flag in the environment. + * \ingroup c_settings + * + * The threshold value affects all processes which operates with given + * environment until the last process close environment or a new value will be + * settled. + * + * Data is always written to disk when \ref mdbx_txn_commit() is called, but + * the operating system may keep it buffered. MDBX always flushes the OS buffers + * upon commit as well, unless the environment was opened with + * \ref MDBX_SAFE_NOSYNC, \ref MDBX_UTTERLY_NOSYNC + * or in part \ref MDBX_NOMETASYNC. + * + * The default is 0, than mean no any threshold checked, and no additional + * flush will be made. + * + * \param [in] env An environment handle returned by mdbx_env_create(). + * \param [in] threshold The size in bytes of summary changes when + * a synchronous flush would be made. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_INLINE_API(int, mdbx_env_set_syncbytes, + (MDBX_env * env, size_t threshold)) { + return mdbx_env_set_option(env, MDBX_opt_sync_bytes, threshold); +} + +/** \brief Sets relative period since the last unsteady commit to force flush + * the data buffers to disk, even of \ref MDBX_SAFE_NOSYNC flag in the + * environment. + * + * \ingroup c_settings + * + * The relative period value affects all processes which operates with given + * environment until the last process close environment or a new value will be + * settled. + * + * Data is always written to disk when \ref mdbx_txn_commit() is called, but the + * operating system may keep it buffered. MDBX always flushes the OS buffers + * upon commit as well, unless the environment was opened with + * \ref MDBX_SAFE_NOSYNC or in part \ref MDBX_NOMETASYNC. + * + * Settled period don't checked asynchronously, but only by the + * \ref mdbx_txn_commit() and \ref mdbx_env_sync() functions. Therefore, in + * cases where transactions are committed infrequently and/or irregularly, + * polling by \ref mdbx_env_sync() may be a reasonable solution to timeout + * enforcement. + * + * The default is 0, than mean no any timeout checked, and no additional + * flush will be made. + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [in] seconds_16dot16 The period in 1/65536 of second when + * a synchronous flush would be made since + * the last unsteady commit. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_INLINE_API(int, mdbx_env_set_syncperiod, + (MDBX_env * env, unsigned seconds_16dot16)) { + return mdbx_env_set_option(env, MDBX_opt_sync_period, seconds_16dot16); +} + +/** \brief Close the environment and release the memory map. + * \ingroup c_opening + * + * Only a single thread may call this function. All transactions, databases, + * and cursors must already be closed before calling this function. Attempts + * to use any such handles after calling this function will cause a `SIGSEGV`. + * The environment handle will be freed and must not be used again after this + * call. + * + * \param [in] env An environment handle returned by + * \ref mdbx_env_create(). + * + * \param [in] dont_sync A dont'sync flag, if non-zero the last checkpoint + * will be kept "as is" and may be still "weak" in the + * \ref MDBX_SAFE_NOSYNC or \ref MDBX_UTTERLY_NOSYNC + * modes. Such "weak" checkpoint will be ignored on + * opening next time, and transactions since the last + * non-weak checkpoint (meta-page update) will rolledback + * for consistency guarantee. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_BUSY The write transaction is running by other thread, + * in such case \ref MDBX_env instance has NOT be destroyed + * not released! + * \note If any OTHER error code was returned then + * given MDBX_env instance has been destroyed and released. + * + * \retval MDBX_EBADSIGN Environment handle already closed or not valid, + * i.e. \ref mdbx_env_close() was already called for the + * `env` or was not created by \ref mdbx_env_create(). + * + * \retval MDBX_PANIC If \ref mdbx_env_close_ex() was called in the child + * process after `fork()`. In this case \ref MDBX_PANIC + * is expected, i.e. \ref MDBX_env instance was freed in + * proper manner. + * + * \retval MDBX_EIO An error occurred during synchronization. */ +LIBMDBX_API int mdbx_env_close_ex(MDBX_env *env, bool dont_sync); + +/** \brief The shortcut to calling \ref mdbx_env_close_ex() with + * the `dont_sync=false` argument. + * \ingroup c_opening */ +LIBMDBX_INLINE_API(int, mdbx_env_close, (MDBX_env * env)) { + return mdbx_env_close_ex(env, false); +} + +/** \brief Set environment flags. + * \ingroup c_settings + * + * This may be used to set some flags in addition to those from + * mdbx_env_open(), or to unset these flags. + * \see mdbx_env_get_flags() + * + * \note In contrast to LMDB, the MDBX serialize threads via mutex while + * changing the flags. Therefore this function will be blocked while a write + * transaction running by other thread, or \ref MDBX_BUSY will be returned if + * function called within a write transaction. + * + * \param [in] env An environment handle returned + * by \ref mdbx_env_create(). + * \param [in] flags The \ref env_flags to change, bitwise OR'ed together. + * \param [in] onoff A non-zero value sets the flags, zero clears them. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_env_set_flags(MDBX_env *env, MDBX_env_flags_t flags, + bool onoff); + +/** \brief Get environment flags. + * \ingroup c_statinfo + * \see mdbx_env_set_flags() + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [out] flags The address of an integer to store the flags. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_env_get_flags(const MDBX_env *env, unsigned *flags); + +/** \brief Return the path that was used in mdbx_env_open(). + * \ingroup c_statinfo + * + * \param [in] env An environment handle returned by \ref mdbx_env_create() + * \param [out] dest Address of a string pointer to contain the path. + * This is the actual string in the environment, not a + * copy. It should not be altered in any way. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_env_get_path(const MDBX_env *env, const char **dest); + +/** \brief Return the file descriptor for the given environment. + * \ingroup c_statinfo + * + * \note All MDBX file descriptors have `FD_CLOEXEC` and + * couldn't be used after exec() and or `fork()`. + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [out] fd Address of a int to contain the descriptor. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_env_get_fd(const MDBX_env *env, mdbx_filehandle_t *fd); + +/** \brief Set all size-related parameters of environment, including page size + * and the min/max size of the memory map. \ingroup c_settings + * + * In contrast to LMDB, the MDBX provide automatic size management of an + * database according the given parameters, including shrinking and resizing + * on the fly. From user point of view all of these just working. Nevertheless, + * it is reasonable to know some details in order to make optimal decisions + * when choosing parameters. + * + * Both \ref mdbx_env_info_ex() and legacy \ref mdbx_env_info() are inapplicable + * to read-only opened environment. + * + * Both \ref mdbx_env_info_ex() and legacy \ref mdbx_env_info() could be called + * either before or after \ref mdbx_env_open(), either within the write + * transaction running by current thread or not: + * + * - In case \ref mdbx_env_info_ex() or legacy \ref mdbx_env_info() was called + * BEFORE \ref mdbx_env_open(), i.e. for closed environment, then the + * specified parameters will be used for new database creation, or will be + * applied during opening if database exists and no other process using it. + * + * If the database is already exist, opened with \ref MDBX_EXCLUSIVE or not + * used by any other process, and parameters specified by + * \ref mdbx_env_set_geometry() are incompatible (i.e. for instance, + * different page size) then \ref mdbx_env_open() will return + * \ref MDBX_INCOMPATIBLE error. + * + * In another way, if database will opened read-only or will used by other + * process during calling \ref mdbx_env_open() that specified parameters will + * silently discarded (open the database with \ref MDBX_EXCLUSIVE flag + * to avoid this). + * + * - In case \ref mdbx_env_info_ex() or legacy \ref mdbx_env_info() was called + * after \ref mdbx_env_open() WITHIN the write transaction running by current + * thread, then specified parameters will be applied as a part of write + * transaction, i.e. will not be visible to any others processes until the + * current write transaction has been committed by the current process. + * However, if transaction will be aborted, then the database file will be + * reverted to the previous size not immediately, but when a next transaction + * will be committed or when the database will be opened next time. + * + * - In case \ref mdbx_env_info_ex() or legacy \ref mdbx_env_info() was called + * after \ref mdbx_env_open() but OUTSIDE a write transaction, then MDBX will + * execute internal pseudo-transaction to apply new parameters (but only if + * anything has been changed), and changes be visible to any others processes + * immediately after succesful completion of function. + * + * Essentially a concept of "automatic size management" is simple and useful: + * - There are the lower and upper bound of the database file size; + * - There is the growth step by which the database file will be increased, + * in case of lack of space. + * - There is the threshold for unused space, beyond which the database file + * will be shrunk. + * - The size of the memory map is also the maximum size of the database. + * - MDBX will automatically manage both the size of the database and the size + * of memory map, according to the given parameters. + * + * So, there some considerations about choosing these parameters: + * - The lower bound allows you to prevent database shrinking below some + * rational size to avoid unnecessary resizing costs. + * - The upper bound allows you to prevent database growth above some rational + * size. Besides, the upper bound defines the linear address space + * reservation in each process that opens the database. Therefore changing + * the upper bound is costly and may be required reopening environment in + * case of \ref MDBX_UNABLE_EXTEND_MAPSIZE errors, and so on. Therefore, this + * value should be chosen reasonable as large as possible, to accommodate + * future growth of the database. + * - The growth step must be greater than zero to allow the database to grow, + * but also reasonable not too small, since increasing the size by little + * steps will result a large overhead. + * - The shrink threshold must be greater than zero to allow the database + * to shrink but also reasonable not too small (to avoid extra overhead) and + * not less than growth step to avoid up-and-down flouncing. + * - The current size (i.e. size_now argument) is an auxiliary parameter for + * simulation legacy \ref mdbx_env_set_mapsize() and as workaround Windows + * issues (see below). + * + * Unfortunately, Windows has is a several issues + * with resizing of memory-mapped file: + * - Windows unable shrinking a memory-mapped file (i.e memory-mapped section) + * in any way except unmapping file entirely and then map again. Moreover, + * it is impossible in any way if a memory-mapped file is used more than + * one process. + * - Windows does not provide the usual API to augment a memory-mapped file + * (that is, a memory-mapped partition), but only by using "Native API" + * in an undocumented way. + * + * MDBX bypasses all Windows issues, but at a cost: + * - Ability to resize database on the fly requires an additional lock + * and release `SlimReadWriteLock during` each read-only transaction. + * - During resize all in-process threads should be paused and then resumed. + * - Shrinking of database file is performed only when it used by single + * process, i.e. when a database closes by the last process or opened + * by the first. + * = Therefore, the size_now argument may be useful to set database size + * by the first process which open a database, and thus avoid expensive + * remapping further. + * + * For create a new database with particular parameters, including the page + * size, \ref mdbx_env_set_geometry() should be called after + * \ref mdbx_env_create() and before mdbx_env_open(). Once the database is + * created, the page size cannot be changed. If you do not specify all or some + * of the parameters, the corresponding default values will be used. For + * instance, the default for database size is 10485760 bytes. + * + * If the mapsize is increased by another process, MDBX silently and + * transparently adopt these changes at next transaction start. However, + * \ref mdbx_txn_begin() will return \ref MDBX_UNABLE_EXTEND_MAPSIZE if new + * mapping size could not be applied for current process (for instance if + * address space is busy). Therefore, in the case of + * \ref MDBX_UNABLE_EXTEND_MAPSIZE error you need close and reopen the + * environment to resolve error. + * + * \note Actual values may be different than your have specified because of + * rounding to specified database page size, the system page size and/or the + * size of the system virtual memory management unit. You can get actual values + * by \ref mdbx_env_sync_ex() or see by using the tool `mdbx_chk` with the `-v` + * option. + * + * Legacy \ref mdbx_env_set_mapsize() correspond to calling + * \ref mdbx_env_set_geometry() with the arguments `size_lower`, `size_now`, + * `size_upper` equal to the `size` and `-1` (i.e. default) for all other + * parameters. + * + * \param [in] env An environment handle returned + * by \ref mdbx_env_create() + * + * \param [in] size_lower The lower bound of database size in bytes. + * Zero value means "minimal acceptable", + * and negative means "keep current or use default". + * + * \param [in] size_now The size in bytes to setup the database size for + * now. Zero value means "minimal acceptable", and + * negative means "keep current or use default". So, + * it is recommended always pass -1 in this argument + * except some special cases. + * + * \param [in] size_upper The upper bound of database size in bytes. + * Zero value means "minimal acceptable", + * and negative means "keep current or use default". + * It is recommended to avoid change upper bound while + * database is used by other processes or threaded + * (i.e. just pass -1 in this argument except absolutely + * necessary). Otherwise you must be ready for + * \ref MDBX_UNABLE_EXTEND_MAPSIZE error(s), unexpected + * pauses during remapping and/or system errors like + * "address busy", and so on. In other words, there + * is no way to handle a growth of the upper bound + * robustly because there may be a lack of appropriate + * system resources (which are extremely volatile in + * a multi-process multi-threaded environment). + * + * \param [in] growth_step The growth step in bytes, must be greater than + * zero to allow the database to grow. Negative value + * means "keep current or use default". + * + * \param [in] shrink_threshold The shrink threshold in bytes, must be greater + * than zero to allow the database to shrink and + * greater than growth_step to avoid shrinking + * right after grow. + * Negative value means "keep current + * or use default". Default is 2*growth_step. + * + * \param [in] pagesize The database page size for new database + * creation or -1 otherwise. Must be power of 2 + * in the range between \ref MDBX_MIN_PAGESIZE and + * \ref MDBX_MAX_PAGESIZE. Zero value means + * "minimal acceptable", and negative means + * "keep current or use default". + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EINVAL An invalid parameter was specified, + * or the environment has an active write transaction. + * \retval MDBX_EPERM Specific for Windows: Shrinking was disabled before + * and now it wanna be enabled, but there are reading + * threads that don't use the additional `SRWL` (that + * is required to avoid Windows issues). + * \retval MDBX_EACCESS The environment opened in read-only. + * \retval MDBX_MAP_FULL Specified size smaller than the space already + * consumed by the environment. + * \retval MDBX_TOO_LARGE Specified size is too large, i.e. too many pages for + * given size, or a 32-bit process requests too much + * bytes for the 32-bit address space. */ +LIBMDBX_API int mdbx_env_set_geometry(MDBX_env *env, intptr_t size_lower, + intptr_t size_now, intptr_t size_upper, + intptr_t growth_step, + intptr_t shrink_threshold, + intptr_t pagesize); + +/** \deprecated Please use \ref mdbx_env_set_geometry() instead. + * \ingroup c_settings */ +MDBX_DEPRECATED LIBMDBX_INLINE_API(int, mdbx_env_set_mapsize, + (MDBX_env * env, size_t size)) { + return mdbx_env_set_geometry(env, size, size, size, -1, -1, -1); +} + +/** \brief Find out whether to use readahead or not, based on the given database + * size and the amount of available memory. \ingroup c_extra + * + * \param [in] volume The expected database size in bytes. + * \param [in] redundancy Additional reserve or overload in case of negative + * value. + * + * \returns A \ref MDBX_RESULT_TRUE or \ref MDBX_RESULT_FALSE value, + * otherwise the error code: + * \retval MDBX_RESULT_TRUE Readahead is reasonable. + * \retval MDBX_RESULT_FALSE Readahead is NOT reasonable, + * i.e. \ref MDBX_NORDAHEAD is useful to + * open environment by \ref mdbx_env_open(). + * \retval Otherwise the error code. */ +LIBMDBX_API int mdbx_is_readahead_reasonable(size_t volume, + intptr_t redundancy); + +/** \brief Returns the minimal database page size in bytes. + * \ingroup c_statinfo */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_INLINE_API(intptr_t, mdbx_limits_pgsize_min, + (void)) { + return MDBX_MIN_PAGESIZE; +} + +/** \brief Returns the maximal database page size in bytes. + * \ingroup c_statinfo */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_INLINE_API(intptr_t, mdbx_limits_pgsize_max, + (void)) { + return MDBX_MAX_PAGESIZE; +} + +/** \brief Returns minimal database size in bytes for given page size, + * or -1 if pagesize is invalid. + * \ingroup c_statinfo */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t +mdbx_limits_dbsize_min(intptr_t pagesize); + +/** \brief Returns maximal database size in bytes for given page size, + * or -1 if pagesize is invalid. + * \ingroup c_statinfo */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t +mdbx_limits_dbsize_max(intptr_t pagesize); + +/** \brief Returns maximal key size in bytes for given page size + * and database flags, or -1 if pagesize is invalid. + * \ingroup c_statinfo + * \see db_flags */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t +mdbx_limits_keysize_max(intptr_t pagesize, MDBX_db_flags_t flags); + +/** \brief Returns maximal data size in bytes for given page size + * and database flags, or -1 if pagesize is invalid. + * \ingroup c_statinfo + * \see db_flags */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t +mdbx_limits_valsize_max(intptr_t pagesize, MDBX_db_flags_t flags); + +/** \brief Returns maximal write transaction size (i.e. limit for summary volume + * of dirty pages) in bytes for given page size, or -1 if pagesize is invalid. + * \ingroup c_statinfo */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API intptr_t +mdbx_limits_txnsize_max(intptr_t pagesize); + +/** \brief Set the maximum number of threads/reader slots for for all processes + * interacts with the database. \ingroup c_settings + * + * \details This defines the number of slots in the lock table that is used to + * track readers in the the environment. The default is about 100 for 4K system + * page size. Starting a read-only transaction normally ties a lock table slot + * to the current thread until the environment closes or the thread exits. If + * \ref MDBX_NOTLS is in use, \ref mdbx_txn_begin() instead ties the slot to the + * \ref MDBX_txn object until it or the \ref MDBX_env object is destroyed. + * This function may only be called after \ref mdbx_env_create() and before + * \ref mdbx_env_open(), and has an effect only when the database is opened by + * the first process interacts with the database. + * \see mdbx_env_get_maxreaders() + * + * \param [in] env An environment handle returned + * by \ref mdbx_env_create(). + * \param [in] readers The maximum number of reader lock table slots. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EINVAL An invalid parameter was specified. + * \retval MDBX_EPERM The environment is already open. */ +LIBMDBX_INLINE_API(int, mdbx_env_set_maxreaders, + (MDBX_env * env, unsigned readers)) { + return mdbx_env_set_option(env, MDBX_opt_max_readers, readers); +} + +/** \brief Get the maximum number of threads/reader slots for the environment. + * \ingroup c_statinfo + * \see mdbx_env_set_maxreaders() + * + * \param [in] env An environment handle returned + * by \ref mdbx_env_create(). + * \param [out] readers Address of an integer to store the number of readers. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_INLINE_API(int, mdbx_env_get_maxreaders, + (const MDBX_env *env, unsigned *readers)) { + int rc = MDBX_EINVAL; + if (readers) { + uint64_t proxy = 0; + rc = mdbx_env_get_option(env, MDBX_opt_max_readers, &proxy); + *readers = (unsigned)proxy; + } + return rc; +} + +/** \brief Set the maximum number of named databases for the environment. + * \ingroup c_settings + * + * This function is only needed if multiple databases will be used in the + * environment. Simpler applications that use the environment as a single + * unnamed database can ignore this option. + * This function may only be called after \ref mdbx_env_create() and before + * \ref mdbx_env_open(). + * + * Currently a moderate number of slots are cheap but a huge number gets + * expensive: 7-120 words per transaction, and every \ref mdbx_dbi_open() + * does a linear search of the opened slots. + * \see mdbx_env_get_maxdbs() + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [in] dbs The maximum number of databases. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EINVAL An invalid parameter was specified. + * \retval MDBX_EPERM The environment is already open. */ +LIBMDBX_INLINE_API(int, mdbx_env_set_maxdbs, (MDBX_env * env, MDBX_dbi dbs)) { + return mdbx_env_set_option(env, MDBX_opt_max_db, dbs); +} + +/** \brief Get the maximum number of named databases for the environment. + * \ingroup c_statinfo + * \see mdbx_env_set_maxdbs() + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [out] dbs Address to store the maximum number of databases. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_INLINE_API(int, mdbx_env_get_maxdbs, + (const MDBX_env *env, MDBX_dbi *dbs)) { + int rc = MDBX_EINVAL; + if (dbs) { + uint64_t proxy = 0; + rc = mdbx_env_get_option(env, MDBX_opt_max_db, &proxy); + *dbs = (MDBX_dbi)proxy; + } + return rc; +} + +/** \brief Returns the default size of database page for the current system. + * \ingroup c_statinfo + * \details Default size of database page depends on the size of the system + * page and usually exactly match it. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API size_t mdbx_default_pagesize(void); + +/** \brief Get the maximum size of keys can write. + * \ingroup c_statinfo + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [in] flags Database options (\ref MDBX_DUPSORT, \ref MDBX_INTEGERKEY + * and so on). \see db_flags + * + * \returns The maximum size of a key can write, + * or -1 if something is wrong. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int +mdbx_env_get_maxkeysize_ex(const MDBX_env *env, MDBX_db_flags_t flags); + +/** \brief Get the maximum size of data we can write. + * \ingroup c_statinfo + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [in] flags Database options (\ref MDBX_DUPSORT, \ref MDBX_INTEGERKEY + * and so on). \see db_flags + * + * \returns The maximum size of a data can write, + * or -1 if something is wrong. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int +mdbx_env_get_maxvalsize_ex(const MDBX_env *env, MDBX_db_flags_t flags); + +/** \deprecated Please use \ref mdbx_env_get_maxkeysize_ex() + * and/or \ref mdbx_env_get_maxvalsize_ex() + * \ingroup c_statinfo */ +MDBX_NOTHROW_PURE_FUNCTION MDBX_DEPRECATED LIBMDBX_API int +mdbx_env_get_maxkeysize(const MDBX_env *env); + +/** \brief Set application information associated with the \ref MDBX_env. + * \ingroup c_settings + * \see mdbx_env_get_userctx() + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [in] ctx An arbitrary pointer for whatever the application needs. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_env_set_userctx(MDBX_env *env, void *ctx); + +/** \brief Get the application information associated with the MDBX_env. + * \ingroup c_statinfo + * \see mdbx_env_set_userctx() + * + * \param [in] env An environment handle returned by \ref mdbx_env_create() + * \returns The pointer set by \ref mdbx_env_set_userctx() + * or `NULL` if something wrong. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API void * +mdbx_env_get_userctx(const MDBX_env *env); + +/** \brief Create a transaction with a user provided context pointer + * for use with the environment. + * \ingroup c_transactions + * + * The transaction handle may be discarded using \ref mdbx_txn_abort() + * or \ref mdbx_txn_commit(). + * \see mdbx_txn_begin() + * + * \note A transaction and its cursors must only be used by a single thread, + * and a thread may only have a single transaction at a time. If \ref MDBX_NOTLS + * is in use, this does not apply to read-only transactions. + * + * \note Cursors may not span transactions. + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * + * \param [in] parent If this parameter is non-NULL, the new transaction will + * be a nested transaction, with the transaction indicated + * by parent as its parent. Transactions may be nested + * to any level. A parent transaction and its cursors may + * not issue any other operations than mdbx_txn_commit and + * \ref mdbx_txn_abort() while it has active child + * transactions. + * + * \param [in] flags Special options for this transaction. This parameter + * must be set to 0 or by bitwise OR'ing together one + * or more of the values described here: + * - \ref MDBX_RDONLY This transaction will not perform + * any write operations. + * + * - \ref MDBX_TXN_TRY Do not block when starting + * a write transaction. + * + * - \ref MDBX_SAFE_NOSYNC, \ref MDBX_NOMETASYNC. + * Do not sync data to disk corresponding + * to \ref MDBX_NOMETASYNC or \ref MDBX_SAFE_NOSYNC + * description. \see sync_modes + * + * \param [out] txn Address where the new MDBX_txn handle will be stored. + * + * \param [in] context A pointer to application context to be associated with + * created transaction and could be retrieved by + * \ref mdbx_txn_get_userctx() until transaction finished. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_PANIC A fatal error occurred earlier and the + * environment must be shut down. + * \retval MDBX_UNABLE_EXTEND_MAPSIZE Another process wrote data beyond + * this MDBX_env's mapsize and this + * environment map must be resized as well. + * See \ref mdbx_env_set_mapsize(). + * \retval MDBX_READERS_FULL A read-only transaction was requested and + * the reader lock table is full. + * See \ref mdbx_env_set_maxreaders(). + * \retval MDBX_ENOMEM Out of memory. + * \retval MDBX_BUSY The write transaction is already started by the + * current thread. */ +LIBMDBX_API int mdbx_txn_begin_ex(MDBX_env *env, MDBX_txn *parent, + MDBX_txn_flags_t flags, MDBX_txn **txn, + void *context); + +/** \brief Create a transaction for use with the environment. + * \ingroup c_transactions + * + * The transaction handle may be discarded using \ref mdbx_txn_abort() + * or \ref mdbx_txn_commit(). + * \see mdbx_txn_begin_ex() + * + * \note A transaction and its cursors must only be used by a single thread, + * and a thread may only have a single transaction at a time. If \ref MDBX_NOTLS + * is in use, this does not apply to read-only transactions. + * + * \note Cursors may not span transactions. + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * + * \param [in] parent If this parameter is non-NULL, the new transaction will + * be a nested transaction, with the transaction indicated + * by parent as its parent. Transactions may be nested + * to any level. A parent transaction and its cursors may + * not issue any other operations than mdbx_txn_commit and + * \ref mdbx_txn_abort() while it has active child + * transactions. + * + * \param [in] flags Special options for this transaction. This parameter + * must be set to 0 or by bitwise OR'ing together one + * or more of the values described here: + * - \ref MDBX_RDONLY This transaction will not perform + * any write operations. + * + * - \ref MDBX_TXN_TRY Do not block when starting + * a write transaction. + * + * - \ref MDBX_SAFE_NOSYNC, \ref MDBX_NOMETASYNC. + * Do not sync data to disk corresponding + * to \ref MDBX_NOMETASYNC or \ref MDBX_SAFE_NOSYNC + * description. \see sync_modes + * + * \param [out] txn Address where the new MDBX_txn handle will be stored. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_PANIC A fatal error occurred earlier and the + * environment must be shut down. + * \retval MDBX_UNABLE_EXTEND_MAPSIZE Another process wrote data beyond + * this MDBX_env's mapsize and this + * environment map must be resized as well. + * See \ref mdbx_env_set_mapsize(). + * \retval MDBX_READERS_FULL A read-only transaction was requested and + * the reader lock table is full. + * See \ref mdbx_env_set_maxreaders(). + * \retval MDBX_ENOMEM Out of memory. + * \retval MDBX_BUSY The write transaction is already started by the + * current thread. */ +LIBMDBX_INLINE_API(int, mdbx_txn_begin, + (MDBX_env * env, MDBX_txn *parent, MDBX_txn_flags_t flags, + MDBX_txn **txn)) { + return mdbx_txn_begin_ex(env, parent, flags, txn, NULL); +} + +/** \brief Set application information associated with the \ref MDBX_txn. + * \ingroup c_transactions + * \see mdbx_txn_get_userctx() + * + * \param [in] txn An transaction handle returned by \ref mdbx_txn_begin_ex() + * or \ref mdbx_txn_begin(). + * \param [in] ctx An arbitrary pointer for whatever the application needs. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_txn_set_userctx(MDBX_txn *txn, void *ctx); + +/** \brief Get the application information associated with the MDBX_txn. + * \ingroup c_transactions + * \see mdbx_txn_set_userctx() + * + * \param [in] txn An transaction handle returned by \ref mdbx_txn_begin_ex() + * or \ref mdbx_txn_begin(). + * \returns The pointer which was passed via the `context` parameter + * of `mdbx_txn_begin_ex()` or set by \ref mdbx_txn_set_userctx(), + * or `NULL` if something wrong. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API void * +mdbx_txn_get_userctx(const MDBX_txn *txn); + +/** \brief Information about the transaction + * \ingroup c_statinfo + * \see mdbx_txn_info */ +struct MDBX_txn_info { + /** The ID of the transaction. For a READ-ONLY transaction, this corresponds + to the snapshot being read. */ + uint64_t txn_id; + + /** For READ-ONLY transaction: the lag from a recent MVCC-snapshot, i.e. the + number of committed transaction since read transaction started. + For WRITE transaction (provided if `scan_rlt=true`): the lag of the oldest + reader from current transaction (i.e. at least 1 if any reader running). */ + uint64_t txn_reader_lag; + + /** Used space by this transaction, i.e. corresponding to the last used + * database page. */ + uint64_t txn_space_used; + + /** Current size of database file. */ + uint64_t txn_space_limit_soft; + + /** Upper bound for size the database file, i.e. the value `size_upper` + argument of the appropriate call of \ref mdbx_env_set_geometry(). */ + uint64_t txn_space_limit_hard; + + /** For READ-ONLY transaction: The total size of the database pages that were + retired by committed write transactions after the reader's MVCC-snapshot, + i.e. the space which would be freed after the Reader releases the + MVCC-snapshot for reuse by completion read transaction. + For WRITE transaction: The summarized size of the database pages that were + retired for now due Copy-On-Write during this transaction. */ + uint64_t txn_space_retired; + + /** For READ-ONLY transaction: the space available for writer(s) and that + must be exhausted for reason to call the Handle-Slow-Readers callback for + this read transaction. + For WRITE transaction: the space inside transaction + that left to `MDBX_TXN_FULL` error. */ + uint64_t txn_space_leftover; + + /** For READ-ONLY transaction (provided if `scan_rlt=true`): The space that + actually become available for reuse when only this transaction will be + finished. + For WRITE transaction: The summarized size of the dirty database + pages that generated during this transaction. */ + uint64_t txn_space_dirty; +}; +#ifndef __cplusplus +/** \ingroup c_statinfo */ +typedef struct MDBX_txn_info MDBX_txn_info; +#endif + +/** \brief Return information about the MDBX transaction. + * \ingroup c_statinfo + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin() + * \param [out] info The address of an \ref MDBX_txn_info structure + * where the information will be copied. + * \param [in] scan_rlt The boolean flag controls the scan of the read lock + * table to provide complete information. Such scan + * is relatively expensive and you can avoid it + * if corresponding fields are not needed. + * See description of \ref MDBX_txn_info. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_txn_info(const MDBX_txn *txn, MDBX_txn_info *info, + bool scan_rlt); + +/** \brief Returns the transaction's MDBX_env. + * \ingroup c_transactions + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin() */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API MDBX_env * +mdbx_txn_env(const MDBX_txn *txn); + +/** \brief Return the transaction's flags. + * \ingroup c_transactions + * + * This returns the flags associated with this transaction. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * + * \returns A transaction flags, valid if input is an valid transaction, + * otherwise -1. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int mdbx_txn_flags(const MDBX_txn *txn); + +/** \brief Return the transaction's ID. + * \ingroup c_statinfo + * + * This returns the identifier associated with this transaction. For a + * read-only transaction, this corresponds to the snapshot being read; + * concurrent readers will frequently have the same transaction ID. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * + * \returns A transaction ID, valid if input is an active transaction, + * otherwise 0. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API uint64_t +mdbx_txn_id(const MDBX_txn *txn); + +/** \brief Latency of commit stages in 1/65536 of seconds units. + * \warning This structure may be changed in future releases. + * \see mdbx_txn_commit_ex() */ +struct MDBX_commit_latency { + /** \brief Duration of preparation (commit child transactions, update + * sub-databases records and cursors destroying). */ + uint32_t preparation; + /** \brief Duration of GC/freeDB handling & updation. */ + uint32_t gc; + /** \brief Duration of internal audit if enabled. */ + uint32_t audit; + /** \brief Duration of writing dirty/modified data pages. */ + uint32_t write; + /** \brief Duration of syncing written data to the dist/storage. */ + uint32_t sync; + /** \brief Duration of transaction ending (releasing resources). */ + uint32_t ending; + /** \brief The total duration of a commit. */ + uint32_t whole; +}; +#ifndef __cplusplus +/** \ingroup c_statinfo */ +typedef struct MDBX_commit_latency MDBX_commit_latency; +#endif + +/** \brief Commit all the operations of a transaction into the database and + * collect latency information. + * \see mdbx_txn_commit() + * \ingroup c_statinfo + * \warning This function may be changed in future releases. */ +LIBMDBX_API int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency); + +/** \brief Commit all the operations of a transaction into the database. + * \ingroup c_transactions + * + * If the current thread is not eligible to manage the transaction then + * the \ref MDBX_THREAD_MISMATCH error will returned. Otherwise the transaction + * will be committed and its handle is freed. If the transaction cannot + * be committed, it will be aborted with the corresponding error returned. + * + * Thus, a result other than \ref MDBX_THREAD_MISMATCH means that the + * transaction is terminated: + * - Resources are released; + * - Transaction handle is invalid; + * - Cursor(s) associated with transaction must not be used, except with + * mdbx_cursor_renew() and \ref mdbx_cursor_close(). + * Such cursor(s) must be closed explicitly by \ref mdbx_cursor_close() + * before or after transaction commit, either can be reused with + * \ref mdbx_cursor_renew() until it will be explicitly closed by + * \ref mdbx_cursor_close(). + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_RESULT_TRUE Transaction was aborted since it should + * be aborted due to previous errors. + * \retval MDBX_PANIC A fatal error occurred earlier + * and the environment must be shut down. + * \retval MDBX_BAD_TXN Transaction is already finished or never began. + * \retval MDBX_EBADSIGN Transaction object has invalid signature, + * e.g. transaction was already terminated + * or memory was corrupted. + * \retval MDBX_THREAD_MISMATCH Given transaction is not owned + * by current thread. + * \retval MDBX_EINVAL Transaction handle is NULL. + * \retval MDBX_ENOSPC No more disk space. + * \retval MDBX_EIO A system-level I/O error occurred. + * \retval MDBX_ENOMEM Out of memory. */ +LIBMDBX_INLINE_API(int, mdbx_txn_commit, (MDBX_txn * txn)) { + return mdbx_txn_commit_ex(txn, NULL); +} + +/** \brief Abandon all the operations of the transaction instead of saving them. + * \ingroup c_transactions + * + * The transaction handle is freed. It and its cursors must not be used again + * after this call, except with \ref mdbx_cursor_renew() and + * \ref mdbx_cursor_close(). + * + * If the current thread is not eligible to manage the transaction then + * the \ref MDBX_THREAD_MISMATCH error will returned. Otherwise the transaction + * will be aborted and its handle is freed. Thus, a result other than + * \ref MDBX_THREAD_MISMATCH means that the transaction is terminated: + * - Resources are released; + * - Transaction handle is invalid; + * - Cursor(s) associated with transaction must not be used, except with + * \ref mdbx_cursor_renew() and \ref mdbx_cursor_close(). + * Such cursor(s) must be closed explicitly by \ref mdbx_cursor_close() + * before or after transaction abort, either can be reused with + * \ref mdbx_cursor_renew() until it will be explicitly closed by + * \ref mdbx_cursor_close(). + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_PANIC A fatal error occurred earlier and + * the environment must be shut down. + * \retval MDBX_BAD_TXN Transaction is already finished or never began. + * \retval MDBX_EBADSIGN Transaction object has invalid signature, + * e.g. transaction was already terminated + * or memory was corrupted. + * \retval MDBX_THREAD_MISMATCH Given transaction is not owned + * by current thread. + * \retval MDBX_EINVAL Transaction handle is NULL. */ +LIBMDBX_API int mdbx_txn_abort(MDBX_txn *txn); + +/** \brief Marks transaction as broken. + * \ingroup c_transactions + * + * Function keeps the transaction handle and corresponding locks, but it + * is not possible to perform any operations in a broken transaction. + * Broken transaction must then be aborted explicitly later. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * + * \see mdbx_txn_abort() \see mdbx_txn_reset() \see mdbx_txn_commit() + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_txn_break(MDBX_txn *txn); + +/** \brief Reset a read-only transaction. + * \ingroup c_transactions + * + * Abort the read-only transaction like \ref mdbx_txn_abort(), but keep the + * transaction handle. Therefore \ref mdbx_txn_renew() may reuse the handle. + * This saves allocation overhead if the process will start a new read-only + * transaction soon, and also locking overhead if \ref MDBX_NOTLS is in use. The + * reader table lock is released, but the table slot stays tied to its thread + * or \ref MDBX_txn. Use \ref mdbx_txn_abort() to discard a reset handle, and to + * free its lock table slot if \ref MDBX_NOTLS is in use. + * + * Cursors opened within the transaction must not be used again after this + * call, except with \ref mdbx_cursor_renew() and \ref mdbx_cursor_close(). + * + * Reader locks generally don't interfere with writers, but they keep old + * versions of database pages allocated. Thus they prevent the old pages from + * being reused when writers commit new data, and so under heavy load the + * database size may grow much more rapidly than otherwise. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_PANIC A fatal error occurred earlier and + * the environment must be shut down. + * \retval MDBX_BAD_TXN Transaction is already finished or never began. + * \retval MDBX_EBADSIGN Transaction object has invalid signature, + * e.g. transaction was already terminated + * or memory was corrupted. + * \retval MDBX_THREAD_MISMATCH Given transaction is not owned + * by current thread. + * \retval MDBX_EINVAL Transaction handle is NULL. */ +LIBMDBX_API int mdbx_txn_reset(MDBX_txn *txn); + +/** \brief Renew a read-only transaction. + * \ingroup c_transactions + * + * This acquires a new reader lock for a transaction handle that had been + * released by \ref mdbx_txn_reset(). It must be called before a reset + * transaction may be used again. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_PANIC A fatal error occurred earlier and + * the environment must be shut down. + * \retval MDBX_BAD_TXN Transaction is already finished or never began. + * \retval MDBX_EBADSIGN Transaction object has invalid signature, + * e.g. transaction was already terminated + * or memory was corrupted. + * \retval MDBX_THREAD_MISMATCH Given transaction is not owned + * by current thread. + * \retval MDBX_EINVAL Transaction handle is NULL. */ +LIBMDBX_API int mdbx_txn_renew(MDBX_txn *txn); + +/** \brief The fours integers markers (aka "canary") associated with the + * environment. \ingroup c_crud \see mdbx_canary_set() \see mdbx_canary_get() + * + * The `x`, `y` and `z` values could be set by \ref mdbx_canary_put(), while the + * 'v' will be always set to the transaction number. Updated values becomes + * visible outside the current transaction only after it was committed. Current + * values could be retrieved by \ref mdbx_canary_get(). */ +struct MDBX_canary { + uint64_t x, y, z, v; +}; +#ifndef __cplusplus +/** \ingroup c_crud */ +typedef struct MDBX_canary MDBX_canary; +#endif + +/** \brief Set integers markers (aka "canary") associated with the environment. + * \ingroup c_crud + * \see mdbx_canary_get() + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin() + * \param [in] canary A optional pointer to \ref MDBX_canary structure for `x`, + * `y` and `z` values from. + * - If canary is NOT NULL then the `x`, `y` and `z` values will be + * updated from given canary argument, but the 'v' be always set + * to the current transaction number if at least one `x`, `y` or + * `z` values have changed (i.e. if `x`, `y` and `z` have the same + * values as currently present then nothing will be changes or + * updated). + * - if canary is NULL then the `v` value will be explicitly update + * to the current transaction number without changes `x`, `y` nor + * `z`. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_canary_put(MDBX_txn *txn, const MDBX_canary *canary); + +/** \brief Returns fours integers markers (aka "canary") associated with the + * environment. + * \ingroup c_crud + * \see mdbx_canary_set() + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] canary The address of an MDBX_canary structure where the + * information will be copied. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary); + +/** \brief A callback function used to compare two keys in a database + * \ingroup c_crud + * \see mdbx_cmp() \see mdbx_get_keycmp() + * \see mdbx_get_datacmp \see mdbx_dcmp() */ +typedef int(MDBX_cmp_func)(const MDBX_val *a, + const MDBX_val *b) MDBX_CXX17_NOEXCEPT; + +/** \brief Open or Create a database in the environment. + * \ingroup c_dbi + * + * A database handle denotes the name and parameters of a database, + * independently of whether such a database exists. The database handle may be + * discarded by calling \ref mdbx_dbi_close(). The old database handle is + * returned if the database was already open. The handle may only be closed + * once. + * + * \note A notable difference between MDBX and LMDB is that MDBX make handles + * opened for existing databases immediately available for other transactions, + * regardless this transaction will be aborted or reset. The REASON for this is + * to avoiding the requirement for multiple opening a same handles in + * concurrent read transactions, and tracking of such open but hidden handles + * until the completion of read transactions which opened them. + * + * Nevertheless, the handle for the NEWLY CREATED database will be invisible + * for other transactions until the this write transaction is successfully + * committed. If the write transaction is aborted the handle will be closed + * automatically. After a successful commit the such handle will reside in the + * shared environment, and may be used by other transactions. + * + * In contrast to LMDB, the MDBX allow this function to be called from multiple + * concurrent transactions or threads in the same process. + * + * To use named database (with name != NULL), \ref mdbx_env_set_maxdbs() + * must be called before opening the environment. Table names are + * keys in the internal unnamed database, and may be read but not written. + * + * \param [in] txn transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] name The name of the database to open. If only a single + * database is needed in the environment, + * this value may be NULL. + * \param [in] flags Special options for this database. This parameter must + * be set to 0 or by bitwise OR'ing together one or more + * of the values described here: + * - \ref MDBX_REVERSEKEY + * Keys are strings to be compared in reverse order, from the end + * of the strings to the beginning. By default, Keys are treated as + * strings and compared from beginning to end. + * - \ref MDBX_INTEGERKEY + * Keys are binary integers in native byte order, either uint32_t or + * uint64_t, and will be sorted as such. The keys must all be of the + * same size and must be aligned while passing as arguments. + * - \ref MDBX_DUPSORT + * Duplicate keys may be used in the database. Or, from another point of + * view, keys may have multiple data items, stored in sorted order. By + * default keys must be unique and may have only a single data item. + * - \ref MDBX_DUPFIXED + * This flag may only be used in combination with \ref MDBX_DUPSORT. This + * option tells the library that the data items for this database are + * all the same size, which allows further optimizations in storage and + * retrieval. When all data items are the same size, the + * \ref MDBX_GET_MULTIPLE, \ref MDBX_NEXT_MULTIPLE and + * \ref MDBX_PREV_MULTIPLE cursor operations may be used to retrieve + * multiple items at once. + * - \ref MDBX_INTEGERDUP + * This option specifies that duplicate data items are binary integers, + * similar to \ref MDBX_INTEGERKEY keys. The data values must all be of the + * same size and must be aligned while passing as arguments. + * - \ref MDBX_REVERSEDUP + * This option specifies that duplicate data items should be compared as + * strings in reverse order (the comparison is performed in the direction + * from the last byte to the first). + * - \ref MDBX_CREATE + * Create the named database if it doesn't exist. This option is not + * allowed in a read-only transaction or a read-only environment. + * + * \param [out] dbi Address where the new \ref MDBX_dbi handle + * will be stored. + * + * For \ref mdbx_dbi_open_ex() additional arguments allow you to set custom + * comparison functions for keys and values (for multimaps). + * However, I recommend not using custom comparison functions, but instead + * converting the keys to one of the forms that are suitable for built-in + * comparators (for instance take look to the \ref value2key). + * The reasons to not using custom comparators are: + * - The order of records could not be validated without your code. + * So `mdbx_chk` utility will reports "wrong order" errors + * and the `-i` option is required to ignore ones. + * - A records could not be ordered or sorted without your code. + * So mdbx_load utility should be used with `-a` option to preserve + * input data order. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_NOTFOUND The specified database doesn't exist in the + * environment and \ref MDBX_CREATE was not specified. + * \retval MDBX_DBS_FULL Too many databases have been opened. + * \see mdbx_env_set_maxdbs() + * \retval MDBX_INCOMPATIBLE Database is incompatible with given flags, + * i.e. the passed flags is different with which the + * database was created, or the database was already + * opened with a different comparison function(s). + * \retval MDBX_THREAD_MISMATCH Given transaction is not owned + * by current thread. */ +LIBMDBX_API int mdbx_dbi_open(MDBX_txn *txn, const char *name, + MDBX_db_flags_t flags, MDBX_dbi *dbi); + +/** \deprecated Please avoid using custom comparators + * and use mdbx_dbi_open() instead. + * \ingroup c_dbi + * + * \param [in] txn transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] name The name of the database to open. If only a single + * database is needed in the environment, + * this value may be NULL. + * \param [in] flags Special options for this database. + * \param [in] keycmp Optional custom key comparison function for a database. + * \param [in] datacmp Optional custom data comparison function for a database. + * \param [out] dbi Address where the new MDBX_dbi handle will be stored. + * \returns A non-zero error value on failure and 0 on success. */ +MDBX_DEPRECATED LIBMDBX_API int +mdbx_dbi_open_ex(MDBX_txn *txn, const char *name, MDBX_db_flags_t flags, + MDBX_dbi *dbi, MDBX_cmp_func *keycmp, MDBX_cmp_func *datacmp); + +/** \defgroup value2key Value-to-Key functions to avoid custom comparators + * \see key2value + * @{ + * + * The \ref mdbx_key_from_jsonInteger() build a keys which are comparable with + * keys created by \ref mdbx_key_from_double(). So this allows mixing `int64_t` + * and IEEE754 double values in one index for JSON-numbers with restriction for + * integer numbers range corresponding to RFC-7159, i.e. \f$[-2^{53}+1, + * 2^{53}-1]\f$. See bottom of page 6 at https://tools.ietf.org/html/rfc7159 */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API uint64_t +mdbx_key_from_jsonInteger(const int64_t json_integer); + +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API uint64_t +mdbx_key_from_double(const double ieee754_64bit); + +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API uint64_t +mdbx_key_from_ptrdouble(const double *const ieee754_64bit); + +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API uint32_t +mdbx_key_from_float(const float ieee754_32bit); + +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API uint32_t +mdbx_key_from_ptrfloat(const float *const ieee754_32bit); + +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_INLINE_API(uint64_t, mdbx_key_from_int64, + (const int64_t i64)) { + return UINT64_C(0x8000000000000000) + i64; +} + +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_INLINE_API(uint32_t, mdbx_key_from_int32, + (const int32_t i32)) { + return UINT32_C(0x80000000) + i32; +} +/** @} */ + +/** \defgroup key2value Key-to-Value functions to avoid custom comparators + * \see value2key + * @{ */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int64_t +mdbx_jsonInteger_from_key(const MDBX_val); + +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API double +mdbx_double_from_key(const MDBX_val); + +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API float +mdbx_float_from_key(const MDBX_val); + +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int32_t +mdbx_int32_from_key(const MDBX_val); + +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int64_t +mdbx_int64_from_key(const MDBX_val); +/** @} */ + +/** \brief Retrieve statistics for a database. + * \ingroup c_statinfo + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [out] stat The address of an \ref MDBX_stat structure where + * the statistics 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 An invalid parameter was specified. */ +LIBMDBX_API int mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *stat, + size_t bytes); + +/** \brief Retrieve depth (bitmask) information of nested dupsort (multi-value) + * B+trees for given database. + * \ingroup c_statinfo + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [out] mask The address of an uint32_t value where the bitmask + * will be stored. + * + * \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 An invalid parameter was specified. + * \retval MDBX_RESULT_TRUE The dbi isn't a dupsort (multi-value) database. */ +LIBMDBX_API int mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi, + uint32_t *mask); + +/** \brief DBI state bits returted by \ref mdbx_dbi_flags_ex() + * \ingroup c_statinfo + * \see mdbx_dbi_flags_ex() */ +enum MDBX_dbi_state_t { + /** DB was written in this txn */ + MDBX_DBI_DIRTY = 0x01, + /** Named-DB record is older than txnID */ + MDBX_DBI_STALE = 0x02, + /** Named-DB handle opened in this txn */ + MDBX_DBI_FRESH = 0x04, + /** Named-DB handle created in this txn */ + MDBX_DBI_CREAT = 0x08, +}; +#ifndef __cplusplus +/** \ingroup c_statinfo */ +typedef enum MDBX_dbi_state_t MDBX_dbi_state_t; +#else +DEFINE_ENUM_FLAG_OPERATORS(MDBX_dbi_state_t) +#endif + +/** \brief Retrieve the DB flags and status for a database handle. + * \ingroup c_statinfo + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [out] flags Address where the flags will be returned. + * \param [out] state Address where the state will be returned. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_dbi_flags_ex(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags, + unsigned *state); +/** \brief The shortcut to calling \ref mdbx_dbi_flags_ex() with `state=NULL` + * for discarding it result. \ingroup c_statinfo */ +LIBMDBX_INLINE_API(int, mdbx_dbi_flags, + (MDBX_txn * txn, MDBX_dbi dbi, unsigned *flags)) { + unsigned state; + return mdbx_dbi_flags_ex(txn, dbi, flags, &state); +} + +/** \brief Close a database handle. Normally unnecessary. + * \ingroup c_dbi + * + * Closing a database handle is not necessary, but lets \ref mdbx_dbi_open() + * reuse the handle value. Usually it's better to set a bigger + * \ref mdbx_env_set_maxdbs(), unless that value would be large. + * + * \note Use with care. + * This call is synchronized via mutex with \ref mdbx_dbi_close(), but NOT with + * other transactions running by other threads. The "next" version of libmdbx + * (\ref MithrilDB) will solve this issue. + * + * Handles should only be closed if no other threads are going to reference + * the database handle or one of its cursors any further. Do not close a handle + * if an existing transaction has modified its database. Doing so can cause + * misbehavior from database corruption to errors like \ref MDBX_BAD_DBI + * (since the DB name is gone). + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_dbi_close(MDBX_env *env, MDBX_dbi dbi); + +/** \brief Empty or delete and close a database. + * \ingroup c_crud + * + * \see mdbx_dbi_close() \see mdbx_dbi_open() + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in] del `false` to empty the DB, `true` to delete it + * from the environment and close the DB handle. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, bool del); + +/** \brief Get items from a database. + * \ingroup c_crud + * + * This function retrieves key/data pairs from the database. The address + * and length of the data associated with the specified key are returned + * in the structure to which data refers. + * If the database supports duplicate keys (\ref MDBX_DUPSORT) then the + * first data item for the key will be returned. Retrieval of other + * items requires the use of \ref mdbx_cursor_get(). + * + * \note The memory pointed to by the returned values is owned by the + * database. The caller need not dispose of the memory, and may not + * modify it in any way. For values returned in a read-only transaction + * any modification attempts will cause a `SIGSEGV`. + * + * \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 \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in] key The key to search for in the database. + * \param [in,out] data The data corresponding to the key. + * + * \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_NOTFOUND The key was not in the database. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, + MDBX_val *data); + +/** \brief Get items from a database + * and optionally number of data items for a given key. + * + * \ingroup c_crud + * + * Briefly this function does the same as \ref mdbx_get() with a few + * differences: + * 1. If values_count is NOT NULL, then returns the count + * of multi-values/duplicates for a given key. + * 2. Updates BOTH the key and the data for pointing to the actual key-value + * pair inside the database. + * + * \param [in] txn A transaction handle returned + * by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in,out] key The key to search for in the database. + * \param [in,out] data The data corresponding to the key. + * \param [out] values_count The optional address to return number of values + * associated with given key: + * = 0 - in case \ref MDBX_NOTFOUND error; + * = 1 - exactly for databases + * WITHOUT \ref MDBX_DUPSORT; + * >= 1 for databases WITH \ref MDBX_DUPSORT. + * + * \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_NOTFOUND The key was not in the database. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, + MDBX_val *data, size_t *values_count); + +/** \brief Get equal or great item from a database. + * \ingroup c_crud + * + * Briefly this function does the same as \ref mdbx_get() with a few + * differences: + * 1. Return equal or great (due comparison function) key-value + * pair, but not only exactly matching with the key. + * 2. On success return \ref MDBX_SUCCESS if key found exactly, + * and \ref MDBX_RESULT_TRUE otherwise. Moreover, for databases with + * \ref MDBX_DUPSORT flag the data argument also will be used to match over + * multi-value/duplicates, and \ref MDBX_SUCCESS will be returned only when + * BOTH the key and the data match exactly. + * 3. Updates BOTH the key and the data for pointing to the actual key-value + * pair inside the database. + * + * \param [in] txn A transaction handle returned + * by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in,out] key The key to search for in the database. + * \param [in,out] data The data corresponding to the key. + * + * \returns A non-zero error value on failure and \ref MDBX_RESULT_FALSE + * or \ref MDBX_RESULT_TRUE on success (as described above). + * Some possible errors are: + * \retval MDBX_THREAD_MISMATCH Given transaction is not owned + * by current thread. + * \retval MDBX_NOTFOUND The key was not in the database. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi, + MDBX_val *key, MDBX_val *data); + +/** \brief Store items into a database. + * \ingroup c_crud + * + * 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, or adding a duplicate data item if + * duplicates are allowed (see \ref MDBX_DUPSORT). + * + * \param [in] txn A transaction handle returned + * by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in] key The key 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: + * - \ref MDBX_NODUPDATA + * 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 with \ref MDBX_DUPSORT. The function will return + * \ref MDBX_KEYEXIST if the key/data pair already appears in the database. + * + * - \ref MDBX_NOOVERWRITE + * Enter the new key/data pair only if the key does not already appear + * in the database. The function will return \ref MDBX_KEYEXIST if the key + * already appears in the database, even if the database supports + * duplicates (see \ref MDBX_DUPSORT). The data parameter will be set + * to point to the existing item. + * + * - \ref MDBX_CURRENT + * 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. + * 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 + * Reserve space for data of the given size, but don't copy the given + * data. Instead, return a pointer to the reserved space, which the + * caller can fill in later - before the next update operation or the + * transaction ends. This saves an extra memcpy if the data is being + * generated later. MDBX does nothing else with this memory, the caller + * is expected to modify all of the space requested. This flag must not + * be specified if the database was opened with \ref MDBX_DUPSORT. + * + * - \ref MDBX_APPEND + * Append the given key/data pair to the end of the database. This option + * allows fast bulk loading when keys are already known to be in the + * correct order. Loading unsorted keys with this flag will cause + * a \ref MDBX_EKEYMISMATCH error. + * + * - \ref MDBX_APPENDDUP + * 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. + * + * \see \ref c_crud_hints "Quick reference for Insert/Update/Delete operations" + * + * \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_KEYEXIST The key/value pair already exists in the database. + * \retval MDBX_MAP_FULL The database is full, see \ref mdbx_env_set_mapsize(). + * \retval MDBX_TXN_FULL The transaction has too many dirty pages. + * \retval MDBX_EACCES An attempt was made to write + * in a read-only transaction. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_put(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, + MDBX_val *data, MDBX_put_flags_t flags); + +/** \brief Replace items in a database. + * \ingroup c_crud + * + * This function allows to update or delete an existing value at the same time + * as the previous value is retrieved. If the argument new_data equal is NULL + * zero, the removal is performed, otherwise the update/insert. + * + * The current value may be in an already changed (aka dirty) page. In this + * case, the page will be overwritten during the update, and the old value will + * be lost. Therefore, an additional buffer must be passed via old_data + * argument initially to copy the old value. If the buffer passed in is too + * small, the function will return \ref MDBX_RESULT_TRUE by setting iov_len + * field pointed by old_data argument to the appropriate value, without + * performing any changes. + * + * For databases with non-unique keys (i.e. with \ref MDBX_DUPSORT flag), + * another use case is also possible, when by old_data argument selects a + * specific item from multi-value/duplicates with the same key for deletion or + * update. To select this scenario in flags should simultaneously specify + * \ref MDBX_CURRENT and \ref MDBX_NOOVERWRITE. This combination is chosen + * because it makes no sense, and thus allows you to identify the request of + * such a scenario. + * + * \param [in] txn A transaction handle returned + * by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in] key The key to store in the database. + * \param [in] new_data The data to store, if NULL then deletion will + * be performed. + * \param [in,out] old_data The buffer for retrieve previous value as describe + * above. + * \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 in \ref mdbx_put() description above, + * and additionally + * (\ref MDBX_CURRENT | \ref MDBX_NOOVERWRITE) + * combination for selection particular item from + * multi-value/duplicates. + * + * \see \ref c_crud_hints "Quick reference for Insert/Update/Delete operations" + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_replace(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, + MDBX_val *new_data, MDBX_val *old_data, + MDBX_put_flags_t flags); + +typedef int (*MDBX_preserve_func)(void *context, MDBX_val *target, + const void *src, size_t bytes); +LIBMDBX_API int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi, + const MDBX_val *key, MDBX_val *new_data, + MDBX_val *old_data, MDBX_put_flags_t flags, + MDBX_preserve_func preserver, + void *preserver_context); + +/** \brief Delete items from a database. + * \ingroup c_crud + * + * This function removes key/data pairs from the database. + * + * \note The data parameter is NOT ignored regardless the database does + * support sorted duplicate data items or not. If the data parameter + * is non-NULL only the matching data item will be deleted. Otherwise, if data + * parameter is NULL, any/all value(s) for specified key will be deleted. + * + * This function will return \ref MDBX_NOTFOUND if the specified key/data + * pair is not in the database. + * + * \see \ref c_crud_hints "Quick reference for Insert/Update/Delete operations" + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in] key The key to delete from the database. + * \param [in] data The data to delete. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EACCES An attempt was made to write + * in a read-only transaction. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key, + const MDBX_val *data); + +/** \brief Create a cursor handle but not bind it to transaction nor DBI handle. + * \ingroup c_cursors + * + * An capable of operation cursor is associated with a specific transaction and + * database. A cursor cannot be used when its database handle is closed. Nor + * when its transaction has ended, except with \ref mdbx_cursor_bind() and + * \ref mdbx_cursor_renew(). + * Also it can be discarded with \ref mdbx_cursor_close(). + * + * A cursor must be closed explicitly always, before or after its transaction + * ends. It can be reused with \ref mdbx_cursor_bind() + * or \ref mdbx_cursor_renew() before finally closing it. + * + * \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] context A pointer to application context to be associated with + * created cursor and could be retrieved by + * \ref mdbx_cursor_get_userctx() until cursor closed. + * + * \returns Created cursor handle or NULL in case out of memory. */ +LIBMDBX_API MDBX_cursor *mdbx_cursor_create(void *context); + +/** \brief Set application information associated with the \ref MDBX_cursor. + * \ingroup c_cursors + * \see mdbx_cursor_get_userctx() + * + * \param [in] cursor An cursor handle returned by \ref mdbx_cursor_create() + * or \ref mdbx_cursor_open(). + * \param [in] ctx An arbitrary pointer for whatever the application needs. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_cursor_set_userctx(MDBX_cursor *cursor, void *ctx); + +/** \brief Get the application information associated with the MDBX_cursor. + * \ingroup c_cursors + * \see mdbx_cursor_set_userctx() + * + * \param [in] cursor An cursor handle returned by \ref mdbx_cursor_create() + * or \ref mdbx_cursor_open(). + * \returns The pointer which was passed via the `context` parameter + * of `mdbx_cursor_create()` or set by \ref mdbx_cursor_set_userctx(), + * or `NULL` if something wrong. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API void * +mdbx_cursor_get_userctx(const MDBX_cursor *cursor); + +/** \brief Bind cursor to specified transaction and DBI handle. + * \ingroup c_cursors + * + * Using of the `mdbx_cursor_bind()` is equivalent to calling + * \ref mdbx_cursor_renew() but with specifying an arbitrary dbi handle. + * + * An capable of operation cursor is associated with a specific transaction and + * database. The cursor may be associated with a new transaction, + * and referencing a new or the same database handle as it was created with. + * This may be done whether the previous transaction is live or dead. + * + * \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] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [out] cursor A cursor handle returned by \ref mdbx_cursor_create(). + * + * \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 An invalid parameter was specified. */ +LIBMDBX_API int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *cursor, + MDBX_dbi dbi); + +/** \brief Create a cursor handle for the specified transaction and DBI handle. + * \ingroup c_cursors + * + * Using of the `mdbx_cursor_open()` is equivalent to calling + * \ref mdbx_cursor_create() and then \ref mdbx_cursor_bind() functions. + * + * An capable of operation cursor is associated with a specific transaction and + * database. A cursor cannot be used when its database handle is closed. Nor + * when its transaction has ended, except with \ref mdbx_cursor_bind() and + * \ref mdbx_cursor_renew(). + * Also it can be discarded with \ref mdbx_cursor_close(). + * + * A cursor must be closed explicitly always, before or after its transaction + * ends. It can be reused with \ref mdbx_cursor_bind() + * or \ref mdbx_cursor_renew() before finally closing it. + * + * \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] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [out] cursor Address where the new \ref MDBX_cursor handle will be + * stored. + * + * \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 An invalid parameter was specified. */ +LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi, + MDBX_cursor **cursor); + +/** \brief Close a cursor handle. + * \ingroup c_cursors + * + * The cursor handle will be freed and must not be used again after this call, + * but its transaction may still be live. + * + * \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() + * or \ref mdbx_cursor_create(). */ +LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor); + +/** \brief Renew a cursor handle. + * \ingroup c_cursors + * + * An capable of operation cursor is associated with a specific transaction and + * database. The cursor may be associated with a new transaction, + * and referencing a new or the same database handle as it was created with. + * This may be done whether the previous transaction is live or dead. + * + * Using of the `mdbx_cursor_renew()` is equivalent to calling + * \ref mdbx_cursor_bind() with the DBI handle that previously + * the cursor was used with. + * + * \note In contrast to LMDB, the MDBX allow any cursor to be re-used by using + * \ref mdbx_cursor_renew(), to avoid unnecessary malloc/free overhead until it + * freed by \ref mdbx_cursor_close(). + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). + * + * \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 An invalid parameter was specified. */ +LIBMDBX_API int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *cursor); + +/** \brief Return the cursor's transaction handle. + * \ingroup c_cursors + * + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API MDBX_txn * +mdbx_cursor_txn(const MDBX_cursor *cursor); + +/** \brief Return the cursor's database handle. + * \ingroup c_cursors + * + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). */ +LIBMDBX_API MDBX_dbi mdbx_cursor_dbi(const MDBX_cursor *cursor); + +/** \brief Copy cursor position and state. + * \ingroup c_cursors + * + * \param [in] src A source cursor handle returned + * by \ref mdbx_cursor_create() or \ref mdbx_cursor_open(). + * + * \param [in,out] dest A destination cursor handle returned + * by \ref mdbx_cursor_create() or \ref mdbx_cursor_open(). + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest); + +/** \brief Retrieve by cursor. + * \ingroup c_cursors c_crud + * + * This function retrieves key/data pairs from the database. The address and + * length of the key are returned in the object to which key refers (except + * for the case of the \ref MDBX_SET option, in which the key object is + * unchanged), and the address and length of the data are returned in the object + * to which data refers. + * \see mdbx_get() + * + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). + * \param [in,out] key The key for a retrieved item. + * \param [in,out] data The data of a retrieved item. + * \param [in] op A cursor operation \ref MDBX_cursor_op. + * + * \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_NOTFOUND No matching key found. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key, + MDBX_val *data, MDBX_cursor_op op); + +/** \brief Store by cursor. + * \ingroup c_cursors c_crud + * + * This function stores key/data pairs into the database. The cursor is + * positioned at the new item, or on failure usually near it. + * + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). + * \param [in] key The key operated on. + * \param [in,out] data The data operated on. + * \param [in] flags 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: + * - \ref MDBX_CURRENT + * Replace the item at the current cursor position. The key parameter + * must still be provided, and must match it, otherwise the function + * 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 + * automatically handles reordering for sorted duplicates + * (see \ref MDBX_DUPSORT). + * + * - \ref MDBX_NODUPDATA + * 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 + * with \ref MDBX_DUPSORT. The function will return \ref MDBX_KEYEXIST + * if the key/data pair already appears in the database. + * + * - \ref MDBX_NOOVERWRITE + * Enter the new key/data pair only if the key does not already appear + * in the database. The function will return \ref MDBX_KEYEXIST if the key + * already appears in the database, even if the database supports + * duplicates (\ref MDBX_DUPSORT). + * + * - \ref MDBX_RESERVE + * Reserve space for data of the given size, but don't copy the given + * data. Instead, return a pointer to the reserved space, which the + * caller can fill in later - before the next update operation or the + * transaction ends. This saves an extra memcpy if the data is being + * generated later. This flag must not be specified if the database + * was opened with \ref MDBX_DUPSORT. + * + * - \ref MDBX_APPEND + * Append the given key/data pair to the end of the database. No key + * comparisons are performed. This option allows fast bulk loading when + * keys are already known to be in the correct order. Loading unsorted + * keys with this flag will cause a \ref MDBX_KEYEXIST error. + * + * - \ref MDBX_APPENDDUP + * 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. + * + * \see \ref c_crud_hints "Quick reference for Insert/Update/Delete operations" + * + * \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_EKEYMISMATCH The given key value is mismatched to the current + * cursor position + * \retval MDBX_MAP_FULL The database is full, + * see \ref mdbx_env_set_mapsize(). + * \retval MDBX_TXN_FULL The transaction has too many dirty pages. + * \retval MDBX_EACCES An attempt was made to write in a read-only + * transaction. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_cursor_put(MDBX_cursor *cursor, const MDBX_val *key, + MDBX_val *data, MDBX_put_flags_t flags); + +/** \brief Delete current key/data pair. + * \ingroup c_cursors c_crud + * + * This function deletes the key/data pair to which the cursor refers. This + * does not invalidate the cursor, so operations such as \ref MDBX_NEXT can + * still be used on it. Both \ref MDBX_NEXT and \ref MDBX_GET_CURRENT will + * return the same record after this operation. + * + * \param [in] cursor A cursor handle returned by mdbx_cursor_open(). + * \param [in] flags Options for this operation. This parameter must be set + * to one of the values described here. + * + * - \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 + * only for database(s) was created with \ref MDBX_DUPSORT. + * + * \see \ref c_crud_hints "Quick reference for Insert/Update/Delete operations" + * + * \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_MAP_FULL The database is full, + * see \ref mdbx_env_set_mapsize(). + * \retval MDBX_TXN_FULL The transaction has too many dirty pages. + * \retval MDBX_EACCES An attempt was made to write in a read-only + * transaction. + * \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. + * \ingroup c_cursors c_crud + * + * This call is valid for all databases, 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. + * + * \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(const MDBX_cursor *cursor, size_t *pcount); + +/** \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 + * + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). + * + * \returns A \ref MDBX_RESULT_TRUE or \ref MDBX_RESULT_FALSE value, + * otherwise the error code: + * \retval MDBX_RESULT_TRUE No more data available or cursor not + * positioned + * \retval MDBX_RESULT_FALSE A data is available + * \retval Otherwise the error code */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int +mdbx_cursor_eof(const MDBX_cursor *cursor); + +/** \brief Determines whether the cursor is pointed to the first key-value pair + * or not. \ingroup c_cursors + * + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). + * + * \returns A MDBX_RESULT_TRUE or MDBX_RESULT_FALSE value, + * otherwise the error code: + * \retval MDBX_RESULT_TRUE Cursor positioned to the first key-value pair + * \retval MDBX_RESULT_FALSE Cursor NOT positioned to the first key-value + * pair \retval Otherwise the error code */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int +mdbx_cursor_on_first(const MDBX_cursor *cursor); + +/** \brief Determines whether the cursor is pointed to the last key-value pair + * or not. \ingroup c_cursors + * + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). + * + * \returns A \ref MDBX_RESULT_TRUE or \ref MDBX_RESULT_FALSE value, + * otherwise the error code: + * \retval MDBX_RESULT_TRUE Cursor positioned to the last key-value pair + * \retval MDBX_RESULT_FALSE Cursor NOT positioned to the last key-value pair + * \retval Otherwise the error code */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int +mdbx_cursor_on_last(const MDBX_cursor *cursor); + +/** \addtogroup c_rqest + * \details \note The estimation result varies greatly depending on the filling + * of specific pages and the overall balance of the b-tree: + * + * 1. The number of items is estimated by analyzing the height and fullness of + * the b-tree. The accuracy of the result directly depends on the balance of + * the b-tree, which in turn is determined by the history of previous + * insert/delete operations and the nature of the data (i.e. variability of + * keys length and so on). Therefore, the accuracy of the estimation can vary + * greatly in a particular situation. + * + * 2. To understand the potential spread of results, you should consider a + * possible situations basing on the general criteria for splitting and merging + * b-tree pages: + * - the page is split into two when there is no space for added data; + * - two pages merge if the result fits in half a page; + * - thus, the b-tree can consist of an arbitrary combination of pages filled + * both completely and only 1/4. Therefore, in the worst case, the result + * can diverge 4 times for each level of the b-tree excepting the first and + * the last. + * + * 3. In practice, the probability of extreme cases of the above situation is + * close to zero and in most cases the error does not exceed a few percent. On + * the other hand, it's just a chance you shouldn't overestimate. */ + +/** \brief Estimates the distance between cursors as a number of elements. + * \ingroup c_rqest + * + * This function performs a rough estimate based only on b-tree pages that are + * common for the both cursor's stacks. The results of such estimation can be + * used to build and/or optimize query execution plans. + * + * Please see notes on accuracy of the result in the details + * of \ref c_rqest section. + * + * Both cursors must be initialized for the same database and the same + * transaction. + * + * \param [in] first The first cursor for estimation. + * \param [in] last The second cursor for estimation. + * \param [out] distance_items The pointer to store estimated distance value, + * i.e. `*distance_items = distance(first, last)`. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_estimate_distance(const MDBX_cursor *first, + const MDBX_cursor *last, + ptrdiff_t *distance_items); + +/** \brief Estimates the move distance. + * \ingroup c_rqest + * + * This function performs a rough estimate distance between the current + * cursor position and next position after the specified move-operation with + * given key and data. The results of such estimation can be used to build + * and/or optimize query execution plans. Current cursor position and state are + * preserved. + * + * Please see notes on accuracy of the result in the details + * of \ref c_rqest section. + * + * \param [in] cursor Cursor for estimation. + * \param [in,out] key The key for a retrieved item. + * \param [in,out] data The data of a retrieved item. + * \param [in] move_op A cursor operation \ref MDBX_cursor_op. + * \param [out] distance_items A pointer to store estimated move distance + * as the number of elements. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key, + MDBX_val *data, MDBX_cursor_op move_op, + ptrdiff_t *distance_items); + +/** \brief Estimates the size of a range as a number of elements. + * \ingroup c_rqest + * + * The results of such estimation can be used to build and/or optimize query + * execution plans. + * + * Please see notes on accuracy of the result in the details + * of \ref c_rqest section. + * + * + * \param [in] txn A transaction handle returned + * by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in] begin_key The key of range beginning or NULL for explicit FIRST. + * \param [in] begin_data Optional additional data to seeking among sorted + * duplicates. + * Only for \ref MDBX_DUPSORT, NULL otherwise. + * \param [in] end_key The key of range ending or NULL for explicit LAST. + * \param [in] end_data Optional additional data to seeking among sorted + * duplicates. + * Only for \ref MDBX_DUPSORT, NULL otherwise. + * \param [out] distance_items A pointer to store range estimation result. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi, + MDBX_val *begin_key, MDBX_val *begin_data, + MDBX_val *end_key, MDBX_val *end_data, + ptrdiff_t *distance_items); + +/** \brief The EPSILON value for mdbx_estimate_range() + * \ingroup c_rqest */ +#define MDBX_EPSILON ((MDBX_val *)((ptrdiff_t)-1)) + +/** \brief Determines whether the given address is on a dirty database page of + * the transaction or not. \ingroup c_statinfo + * + * Ultimately, this allows to avoid copy data from non-dirty pages. + * + * "Dirty" pages are those that have already been changed during a write + * transaction. Accordingly, any further changes may result in such pages being + * overwritten. Therefore, all functions libmdbx performing changes inside the + * database as arguments should NOT get pointers to data in those pages. In + * turn, "not dirty" pages before modification will be copied. + * + * In other words, data from dirty pages must either be copied before being + * passed as arguments for further processing or rejected at the argument + * validation stage. Thus, `mdbx_is_dirty()` allows you to get rid of + * unnecessary copying, and perform a more complete check of the arguments. + * + * \note The address passed must point to the beginning of the data. This is + * the only way to ensure that the actual page header is physically located in + * the same memory page, including for multi-pages with long data. + * + * \note In rare cases the function may return a false positive answer + * (\ref MDBX_RESULT_TRUE when data is NOT on a dirty page), but never a false + * negative if the arguments are correct. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] ptr The address of data to check. + * + * \returns A MDBX_RESULT_TRUE or MDBX_RESULT_FALSE value, + * otherwise the error code: + * \retval MDBX_RESULT_TRUE Given address is on the dirty page. + * \retval MDBX_RESULT_FALSE Given address is NOT on the dirty page. + * \retval Otherwise the error code. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int mdbx_is_dirty(const MDBX_txn *txn, + const void *ptr); + +/** \brief Sequence generation for a database. + * \ingroup c_crud + * + * The function allows to create a linear sequence of unique positive integers + * for each database. The function can be called for a read transaction to + * retrieve the current sequence value, and the increment must be zero. + * Sequence changes become visible outside the current write transaction after + * it is committed, and discarded on abort. + * + * \param [in] txn A transaction handle returned + * by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [out] result The optional address where the value of sequence + * before the change will be stored. + * \param [in] increment Value to increase the sequence, + * must be 0 for read-only transactions. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_RESULT_TRUE Increasing the sequence has resulted in an + * overflow and therefore cannot be executed. */ +LIBMDBX_API int mdbx_dbi_sequence(MDBX_txn *txn, MDBX_dbi dbi, uint64_t *result, + uint64_t increment); + +/** \brief Compare two keys according to a particular database. + * \ingroup c_crud + * + * This returns a comparison as if the two data items were keys in the + * specified database. + * + * \warning There ss a Undefined behavior if one of arguments is invalid. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in] a The first item to compare. + * \param [in] b The second item to compare. + * + * \returns < 0 if a < b, 0 if a == b, > 0 if a > b */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int mdbx_cmp(const MDBX_txn *txn, + MDBX_dbi dbi, + const MDBX_val *a, + const MDBX_val *b); + +/** \brief Returns default internal key's comparator for given database flags. + * \ingroup c_extra */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API MDBX_cmp_func * +mdbx_get_keycmp(MDBX_db_flags_t flags); + +/** \brief Compare two data items according to a particular database. + * \ingroup c_crud + * + * This returns a comparison as if the two items were data items of the + * specified database. + * + * \warning There ss a Undefined behavior if one of arguments is invalid. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in] a The first item to compare. + * \param [in] b The second item to compare. + * + * \returns < 0 if a < b, 0 if a == b, > 0 if a > b */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API int mdbx_dcmp(const MDBX_txn *txn, + MDBX_dbi dbi, + const MDBX_val *a, + const MDBX_val *b); + +/** \brief Returns default internal data's comparator for given database flags + * \ingroup c_extra */ +MDBX_NOTHROW_CONST_FUNCTION LIBMDBX_API MDBX_cmp_func * +mdbx_get_datacmp(MDBX_db_flags_t flags); + +/** \brief A callback function used to enumerate the reader lock table. + * \ingroup c_statinfo + * + * \param [in] ctx An arbitrary context pointer for the callback. + * \param [in] num The serial number during enumeration, + * starting from 1. + * \param [in] slot The reader lock table slot number. + * \param [in] txnid The ID of the transaction being read, + * i.e. the MVCC-snapshot number. + * \param [in] lag The lag from a recent MVCC-snapshot, + * i.e. the number of committed write transactions + * since the current read transaction started. + * \param [in] pid The reader process ID. + * \param [in] thread The reader thread ID. + * \param [in] bytes_used The number of last used page in the MVCC-snapshot + * which being read, + * i.e. database file can't shrinked beyond this. + * \param [in] bytes_retired The total size of the database pages that were + * retired by committed write transactions after + * the reader's MVCC-snapshot, + * i.e. the space which would be freed after + * the Reader releases the MVCC-snapshot + * for reuse by completion read transaction. + * + * \returns < 0 on failure, >= 0 on success. \see mdbx_reader_list() */ +typedef int(MDBX_reader_list_func)(void *ctx, int num, int slot, mdbx_pid_t pid, + mdbx_tid_t thread, uint64_t txnid, + uint64_t lag, size_t bytes_used, + size_t bytes_retained) MDBX_CXX17_NOEXCEPT; + +/** \brief Enumerate the entries in the reader lock table. + * + * \ingroup c_statinfo + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [in] func A \ref MDBX_reader_list_func function. + * \param [in] ctx An arbitrary context pointer for the enumeration + * function. + * + * \returns A non-zero error value on failure and 0 on success, + * or \ref MDBX_RESULT_TRUE if the reader lock table is empty. */ +LIBMDBX_API int mdbx_reader_list(const MDBX_env *env, + MDBX_reader_list_func *func, void *ctx); + +/** \brief Check for stale entries in the reader lock table. + * \ingroup c_extra + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [out] dead Number of stale slots that were cleared. + * + * \returns A non-zero error value on failure and 0 on success, + * or \ref MDBX_RESULT_TRUE if a dead reader(s) found or mutex was recovered. */ +LIBMDBX_API int mdbx_reader_check(MDBX_env *env, int *dead); + +/** \brief Returns a lag of the reading for the given transaction. + * \ingroup c_statinfo + * + * Returns an information for estimate how much given read-only + * transaction is lagging relative the to actual head. + * \deprecated Please use \ref mdbx_txn_info() instead. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [out] percent Percentage of page allocation in the database. + * + * \returns Number of transactions committed after the given was started for + * read, or negative value on failure. */ +MDBX_DEPRECATED LIBMDBX_API int mdbx_txn_straggler(const MDBX_txn *txn, + int *percent); + +/** \brief Registers the current thread as a reader for the environment. + * \ingroup c_extra + * + * To perform read operations without blocking, a reader slot must be assigned + * for each thread. However, this assignment requires a short-term lock + * acquisition which is performed automatically. This function allows you to + * assign the reader slot in advance and thus avoid capturing the blocker when + * the read transaction starts firstly from current thread. + * \see mdbx_thread_unregister() + * + * \note Threads are registered automatically the first time a read transaction + * starts. Therefore, there is no need to use this function, except in + * special cases. + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * + * \returns A non-zero error value on failure and 0 on success, + * or \ref MDBX_RESULT_TRUE if thread is already registered. */ +LIBMDBX_API int mdbx_thread_register(const MDBX_env *env); + +/** \brief Unregisters the current thread as a reader for the environment. + * \ingroup c_extra + * + * To perform read operations without blocking, a reader slot must be assigned + * for each thread. However, the assigned reader slot will remain occupied until + * the thread ends or the environment closes. This function allows you to + * explicitly release the assigned reader slot. + * \see mdbx_thread_register() + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * + * \returns A non-zero error value on failure and 0 on success, or + * \ref MDBX_RESULT_TRUE if thread is not registered or already unregistered. */ +LIBMDBX_API int mdbx_thread_unregister(const MDBX_env *env); + +/** \brief A Handle-Slow-Readers callback function to resolve database + * full/overflow issue due to a reader(s) which prevents the old data from being + * recycled. + * \ingroup c_err + * + * Read transactions prevent reuse of pages freed by newer write transactions, + * thus the database can grow quickly. This callback will be called when there + * is not enough space in the database (i.e. before increasing the database size + * or before \ref MDBX_MAP_FULL error) and thus can be used to resolve issues + * with a "long-lived" read transactions. + * \see long-lived-read + * + * Using this callback you can choose how to resolve the situation: + * - abort the write transaction with an error; + * - wait for the read transaction(s) to complete; + * - notify a thread performing a long-lived read transaction + * and wait for an effect; + * - kill the thread or whole process that performs the long-lived read + * transaction; + * + * Depending on the arguments and needs, your implementation may wait, + * terminate a process or thread that is performing a long read, or perform + * some other action. In doing so it is important that the returned code always + * corresponds to the performed action. + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * \param [in] txn The current write transaction which internally at + * the \ref MDBX_MAP_FULL condition. + * \param [in] pid A pid of the reader process. + * \param [in] tid A thread_id of the reader thread. + * \param [in] laggard An oldest read transaction number on which stalled. + * \param [in] gap A lag from the last committed txn. + * \param [in] space A space that actually become available for reuse after + * this reader finished. The callback function can take + * this value into account to evaluate the impact that + * a long-running transaction has. + * \param [in] retry A retry number starting from 0. + * If callback has returned 0 at least once, then at end + * of current handling loop the callback function will be + * called additionally with negative value to notify about + * the end of loop. The callback function can use this value + * to implement timeout logic while waiting for readers. + * + * \returns The RETURN CODE determines the further actions libmdbx and must + * match the action which was executed by the callback: + * + * \retval -2 or less An error condition and the reader was not killed. + * + * \retval -1 The callback was unable to solve the problem and + * agreed on \ref MDBX_MAP_FULL error; + * libmdbx should increase the database size or + * return \ref MDBX_MAP_FULL error. + * + * \retval 0 (zero) The callback solved the problem or just waited for + * a while, libmdbx should rescan the reader lock table and + * retry. This also includes a situation when corresponding + * transaction terminated in normal way by + * \ref mdbx_txn_abort() or \ref mdbx_txn_reset(), + * and my be restarted. I.e. reader slot don't needed + * to be cleaned from transaction. + * + * \retval 1 Transaction aborted asynchronous and reader slot + * should be cleared immediately, i.e. read transaction + * will not continue but \ref mdbx_txn_abort() + * or \ref mdbx_txn_reset() will be called later. + * + * \retval 2 or great The reader process was terminated or killed, + * and libmdbx should entirely reset reader registration. + * + * \see mdbx_env_set_hsr() \see mdbx_env_get_hsr() + */ +typedef int(MDBX_hsr_func)(const MDBX_env *env, const MDBX_txn *txn, + mdbx_pid_t pid, mdbx_tid_t tid, uint64_t laggard, + unsigned gap, size_t space, + int retry) MDBX_CXX17_NOEXCEPT; + +/** \brief Sets a Handle-Slow-Readers callback to resolve database full/overflow + * issue due to a reader(s) which prevents the old data from being recycled. + * \ingroup c_err + * + * The callback will only be triggered when the database is full due to a + * reader(s) prevents the old data from being recycled. + * + * \see mdbx_env_get_hsr() + * \see long-lived-read + * + * \param [in] env An environment handle returned + * by \ref mdbx_env_create(). + * \param [in] hsr_callback A \ref MDBX_hsr_func function + * or NULL to disable. + * + * \returns A non-zero error value on failure and 0 on success. */ +LIBMDBX_API int mdbx_env_set_hsr(MDBX_env *env, MDBX_hsr_func *hsr_callback); + +/** \brief Gets current Handle-Slow-Readers callback used to resolve database + * full/overflow issue due to a reader(s) which prevents the old data from being + * recycled. + * \see mdbx_env_set_hsr() + * + * \param [in] env An environment handle returned by \ref mdbx_env_create(). + * + * \returns A MDBX_hsr_func function or NULL if disabled + * or something wrong. */ +MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API MDBX_hsr_func * +mdbx_env_get_hsr(const MDBX_env *env); + +/** \defgroup btree_traversal B-tree Traversal + * This is internal API for mdbx_chk tool. You should avoid to use it, except + * some extremal special cases. + * \ingroup c_extra + * @{ */ + +/** \brief Page types for traverse the b-tree. + * \see mdbx_env_pgwalk() \see MDBX_pgvisitor_func */ +enum MDBX_page_type_t { + MDBX_page_broken, + MDBX_page_meta, + MDBX_page_large, + MDBX_page_branch, + MDBX_page_leaf, + MDBX_page_dupfixed_leaf, + MDBX_subpage_leaf, + MDBX_subpage_dupfixed_leaf, + MDBX_subpage_broken, +}; +#ifndef __cplusplus +typedef enum MDBX_page_type_t MDBX_page_type_t; +#endif + +/** \brief Pseudo-name for MainDB */ +#define MDBX_PGWALK_MAIN ((const char *)((ptrdiff_t)0)) +/** \brief Pseudo-name for GarbageCollectorDB */ +#define MDBX_PGWALK_GC ((const char *)((ptrdiff_t)-1)) +/** \brief Pseudo-name for MetaPages */ +#define MDBX_PGWALK_META ((const char *)((ptrdiff_t)-2)) + +/** \brief Callback function for traverse the b-tree. \see mdbx_env_pgwalk() */ +typedef int MDBX_pgvisitor_func( + const uint64_t pgno, const unsigned number, void *const ctx, const int deep, + const char *const dbi, const size_t page_size, const MDBX_page_type_t type, + const MDBX_error_t err, const size_t nentries, const size_t payload_bytes, + const size_t header_bytes, const size_t unused_bytes) MDBX_CXX17_NOEXCEPT; + +/** \brief B-tree traversal function. */ +LIBMDBX_API int mdbx_env_pgwalk(MDBX_txn *txn, MDBX_pgvisitor_func *visitor, + void *ctx, bool dont_check_keys_ordering); + +/** \brief Open an environment instance using specific meta-page + * for checking and recovery. + * + * This function mostly of internal API for `mdbx_chk` utility and subject to + * change at any time. Do not use this function to avoid shooting your own + * leg(s). */ +LIBMDBX_API int mdbx_env_open_for_recovery(MDBX_env *env, const char *pathname, + unsigned target_meta, + bool writeable); + +/** \brief Turn database to the specified meta-page. + * + * This function mostly of internal API for `mdbx_chk` utility and subject to + * change at any time. Do not use this function to avoid shooting your own + * leg(s). */ +LIBMDBX_API int mdbx_env_turn_for_recovery(MDBX_env *env, unsigned target_meta); + +/** @} B-tree Traversal */ + +/**** Attribute support functions for Nexenta + * *******************************************/ +#if defined(MDBX_NEXENTA_ATTRS) || defined(DOXYGEN) +/** \defgroup nexenta Attribute support functions for Nexenta + * \ingroup c_crud + * @{ */ +typedef uint_fast64_t mdbx_attr_t; + +/** 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 \ref MDBX_RESERVE feature, + * therefore doesn't support \ref MDBX_DUPSORT. + * + * \param [in] cursor A cursor handle returned by \ref mdbx_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: + * - \ref MDBX_CURRENT + * Replace the item at the current cursor position. The key parameter + * must still be provided, and must match it, otherwise the function + * return \ref MDBX_EKEYMISMATCH. + * + * - \ref MDBX_APPEND + * Append the given key/data pair to the end of the database. No key + * comparisons are performed. This option allows fast bulk loading when + * keys are already known to be in the correct order. Loading unsorted + * keys with this flag will cause a \ref MDBX_KEYEXIST error. + * + * \see \ref c_crud_hints "Quick reference for Insert/Update/Delete operations" + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_EKEYMISMATCH + * \retval MDBX_MAP_FULL The database is full, see \ref mdbx_env_set_mapsize(). + * \retval MDBX_TXN_FULL The transaction has too many dirty pages. + * \retval MDBX_EACCES An attempt was made to write in a read-only + * transaction. + * \retval MDBX_EINVAL an invalid parameter was specified. */ +LIBMDBX_API int mdbx_cursor_put_attr(MDBX_cursor *cursor, MDBX_val *key, + MDBX_val *data, mdbx_attr_t attr, + MDBX_put_flags_t flags); + +/** 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 \ref MDBX_RESERVE feature, + * therefore doesn't support \ref MDBX_DUPSORT. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_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: + * - \ref MDBX_NOOVERWRITE + * Enter the new key/data pair only if the key does not already appear + * in the database. The function will return \ref MDBX_KEYEXIST if the key + * already appears in the database. The data parameter will be set to + * point to the existing item. + * + * - \ref MDBX_CURRENT + * 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. Or the \ref MDBX_EMULTIVAL in case duplicates for the given + * key. + * + * - \ref MDBX_APPEND + * Append the given key/data pair to the end of the database. This option + * allows fast bulk loading when keys are already known to be in the + * correct order. Loading unsorted keys with this flag will cause + * a \ref MDBX_EKEYMISMATCH error. + * + * \see \ref c_crud_hints "Quick reference for Insert/Update/Delete operations" + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_KEYEXIST + * \retval MDBX_MAP_FULL The database is full, see \ref mdbx_env_set_mapsize(). + * \retval MDBX_TXN_FULL The transaction has too many dirty pages. + * \retval MDBX_EACCES An attempt was made to write + * in a read-only transaction. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_put_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, + MDBX_val *data, mdbx_attr_t attr, + MDBX_put_flags_t flags); + +/** Set items attribute from a database. + * + * This function stores key/data pairs attribute to the database. + * + * \note Internally based on \ref MDBX_RESERVE feature, + * therefore doesn't support \ref MDBX_DUPSORT. + * + * \param [in] txn A transaction handle returned by \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_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. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_NOTFOUND The key-value pair was not in the database. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_set_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, + MDBX_val *data, mdbx_attr_t attr); + +/** Get items attribute from a database cursor. + * + * This function retrieves key/data pairs from the database. The address and + * length of the key are returned in the object to which key refers (except + * for the case of the \ref MDBX_SET option, in which the key object is + * unchanged), and the address and length of the data are returned in the object + * to which data refers. + * \see mdbx_get() + * + * \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open(). + * \param [in,out] key The key for a retrieved item. + * \param [in,out] data The data of a retrieved item. + * \param [out] pattr The pointer to retrieve attribute. + * \param [in] op A cursor operation MDBX_cursor_op. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_NOTFOUND No matching key found. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_cursor_get_attr(MDBX_cursor *cursor, MDBX_val *key, + MDBX_val *data, mdbx_attr_t *pattr, + MDBX_cursor_op op); + +/** Get items attribute from a database. + * + * This function retrieves key/data pairs from the database. The address + * and length of the data associated with the specified key are returned + * in the structure to which data refers. + * If the database supports duplicate keys (see \ref MDBX_DUPSORT) then the + * first data item for the key will be returned. Retrieval of other + * items requires the use of \ref mdbx_cursor_get(). + * + * \note The memory pointed to by the returned values is owned by the + * database. The caller need not dispose of the memory, and may not + * modify it in any way. For values returned in a read-only transaction + * any modification attempts will cause a `SIGSEGV`. + * + * \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 \ref mdbx_txn_begin(). + * \param [in] dbi A database handle returned by \ref mdbx_dbi_open(). + * \param [in] key The key to search for in the database. + * \param [in,out] data The data corresponding to the key. + * \param [out] pattr The pointer to retrieve attribute. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_NOTFOUND The key was not in the database. + * \retval MDBX_EINVAL An invalid parameter was specified. */ +LIBMDBX_API int mdbx_get_attr(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, + MDBX_val *data, mdbx_attr_t *pattr); +/** @} end of Attribute support functions for Nexenta */ +#endif /* MDBX_NEXENTA_ATTRS */ + +/** @} end of C API */ + +/******************************************************************************* + * Workaround for mmaped-lookahead-cross-page-boundary bug + * in an obsolete versions of Elbrus's libc and kernels. */ +#if defined(__e2k__) && defined(MDBX_E2K_MLHCPB_WORKAROUND) && \ + MDBX_E2K_MLHCPB_WORKAROUND +LIBMDBX_API int mdbx_e2k_memcmp_bug_workaround(const void *s1, const void *s2, + size_t n); +LIBMDBX_API int mdbx_e2k_strcmp_bug_workaround(const char *s1, const char *s2); +LIBMDBX_API int mdbx_e2k_strncmp_bug_workaround(const char *s1, const char *s2, + size_t n); +LIBMDBX_API size_t mdbx_e2k_strlen_bug_workaround(const char *s); +LIBMDBX_API size_t mdbx_e2k_strnlen_bug_workaround(const char *s, + size_t maxlen); +#ifdef __cplusplus +namespace std { +inline int mdbx_e2k_memcmp_bug_workaround(const void *s1, const void *s2, + size_t n) { + return ::mdbx_e2k_memcmp_bug_workaround(s1, s2, n); +} +inline int mdbx_e2k_strcmp_bug_workaround(const char *s1, const char *s2) { + return ::mdbx_e2k_strcmp_bug_workaround(s1, s2); +} +inline int mdbx_e2k_strncmp_bug_workaround(const char *s1, const char *s2, + size_t n) { + return ::mdbx_e2k_strncmp_bug_workaround(s1, s2, n); +} +inline size_t mdbx_e2k_strlen_bug_workaround(const char *s) { + return ::mdbx_e2k_strlen_bug_workaround(s); +} +inline size_t mdbx_e2k_strnlen_bug_workaround(const char *s, size_t maxlen) { + return ::mdbx_e2k_strnlen_bug_workaround(s, maxlen); +} +} // namespace std +#endif /* __cplusplus */ + +#include <string.h> +#include <strings.h> +#undef memcmp +#define memcmp mdbx_e2k_memcmp_bug_workaround +#undef bcmp +#define bcmp mdbx_e2k_memcmp_bug_workaround +#undef strcmp +#define strcmp mdbx_e2k_strcmp_bug_workaround +#undef strncmp +#define strncmp mdbx_e2k_strncmp_bug_workaround +#undef strlen +#define strlen mdbx_e2k_strlen_bug_workaround +#undef strnlen +#define strnlen mdbx_e2k_strnlen_bug_workaround +#endif /* MDBX_E2K_MLHCPB_WORKAROUND */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LIBMDBX_H */ |