diff --git a/ChangeLog.md b/ChangeLog.md index 08c2aaf9..246d101d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -28,6 +28,7 @@ and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic выполнялось некорректное закрытие дескриптора, что могло приводить к созданию таблицы с пустым именем, утечки страниц БД и/или нарушению структуры b-tree (неверной ссылкой на корень таблицы). + Добавлен соответствующий тест `extra/early_close_dbi`. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5da6a391..559b143f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -98,6 +98,13 @@ if(UNIX AND NOT SUBPROJECT) set_target_properties(test_extra_dbi PROPERTIES CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON) endif() + add_executable(test_extra_early_close_dbi extra/early_close_dbi.c++) + target_include_directories(test_extra_early_close_dbi PRIVATE "${PROJECT_SOURCE_DIR}") + target_link_libraries(test_extra_early_close_dbi ${TOOL_MDBX_LIB}) + if(MDBX_CXX_STANDARD) + set_target_properties(test_extra_early_close_dbi PROPERTIES + CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON) + endif() endif() endif() diff --git a/test/extra/early_close_dbi.c++ b/test/extra/early_close_dbi.c++ new file mode 100644 index 00000000..a81df2e2 --- /dev/null +++ b/test/extra/early_close_dbi.c++ @@ -0,0 +1,110 @@ +#include "mdbx.h++" +#include + +static const char *const testkey = "testkey"; +static uint64_t testval = 11; + +int main(int argc, char *argv[]) { + (void)argc; + (void)argv; + + mdbx::path db_filename = "test-early_close_dbi"; + mdbx::env_managed::remove(db_filename); + + MDBX_env *environment; + MDBX_MAYBE_UNUSED int err = mdbx_env_create(&environment); + assert(err == MDBX_SUCCESS); + + err = mdbx_env_set_option(environment, MDBX_opt_max_db, 2); + assert(err == MDBX_SUCCESS); + err = mdbx_env_set_option(environment, MDBX_opt_max_readers, 2); + assert(err == MDBX_SUCCESS); + // status = mdbx_env_set_option(environment, MDBX_opt_prefault_write_enable, + // 1); assert(err == MDBX_SUCCESS); + + intptr_t lowerbound(0), size(0), upperbound(mdbx::env::geometry::GiB / 2); + intptr_t step(128 * mdbx::env::geometry::MiB), + shrink(256 * mdbx::env::geometry::MiB), pagesize(-1); + err = +#ifdef _WIN32 + mdbx_env_set_geometryW +#else + mdbx_env_set_geometry +#endif + (environment, lowerbound, size, upperbound, step, shrink, pagesize); + assert(err == MDBX_SUCCESS); + + MDBX_env_flags_t flags(MDBX_NOSUBDIR | MDBX_WRITEMAP | MDBX_LIFORECLAIM | + MDBX_NORDAHEAD); + err = mdbx_env_open(environment, db_filename.c_str(), flags, 0644); + assert(err == MDBX_SUCCESS); + + // --- + + MDBX_txn *transaction; + err = mdbx_txn_begin(environment, nullptr, MDBX_TXN_READWRITE, &transaction); + assert(err == MDBX_SUCCESS); + + MDBX_dbi textindex; + err = mdbx_dbi_open(transaction, "testdb", MDBX_DB_DEFAULTS, &textindex); + assert(err == MDBX_NOTFOUND); + err = mdbx_dbi_open(transaction, "testdb", MDBX_CREATE, &textindex); + assert(err == MDBX_SUCCESS); + + MDBX_val mdbxkey{(void *)testkey, std::strlen(testkey)}, mdbxval{}; + err = mdbx_get(transaction, textindex, &mdbxkey, &mdbxval); + assert(err == MDBX_NOTFOUND); + + unsigned dbi_flags, dbi_state; + err = mdbx_dbi_flags_ex(transaction, textindex, &dbi_flags, &dbi_state); + assert(err == MDBX_SUCCESS); + assert((dbi_state & (MDBX_DBI_CREAT | MDBX_DBI_DIRTY)) != 0); + err = mdbx_dbi_close(environment, textindex); + assert(err != MDBX_SUCCESS); + + err = mdbx_txn_commit(transaction); + assert(err == MDBX_SUCCESS); + + // --- + + err = mdbx_txn_begin(environment, nullptr, MDBX_TXN_READWRITE, &transaction); + assert(err == MDBX_SUCCESS); + + MDBX_val mdbxput{&testval, sizeof(uint64_t)}; + err = mdbx_put(transaction, textindex, &mdbxkey, &mdbxput, MDBX_NOOVERWRITE); + assert(err == MDBX_SUCCESS); + err = mdbx_get(transaction, textindex, &mdbxkey, &mdbxval); + assert(err == MDBX_SUCCESS); + assert(testval == *reinterpret_cast(mdbxval.iov_base)); + + err = mdbx_put(transaction, textindex, &mdbxkey, &mdbxput, MDBX_NOOVERWRITE); + assert(err == MDBX_KEYEXIST); + err = mdbx_get(transaction, textindex, &mdbxkey, &mdbxval); + assert(err == MDBX_SUCCESS); + assert(testval == *reinterpret_cast(mdbxval.iov_base)); + + err = mdbx_dbi_flags_ex(transaction, textindex, &dbi_flags, &dbi_state); + assert(err == MDBX_SUCCESS); + assert((dbi_state & MDBX_DBI_DIRTY) != 0); + err = mdbx_dbi_close(environment, textindex); + assert(err != MDBX_SUCCESS); + err = mdbx_txn_commit(transaction); + assert(err == MDBX_SUCCESS); + + // --- + + err = mdbx_txn_begin(environment, nullptr, MDBX_TXN_RDONLY, &transaction); + assert(err == MDBX_SUCCESS); + err = mdbx_get(transaction, textindex, &mdbxkey, &mdbxval); + assert(err == MDBX_SUCCESS); + assert(testval == *reinterpret_cast(mdbxval.iov_base)); + + err = mdbx_dbi_close(environment, textindex); + assert(err == MDBX_SUCCESS); + err = mdbx_txn_commit(transaction); + assert(err == MDBX_SUCCESS); + err = mdbx_env_close_ex(environment, true); + assert(err == MDBX_SUCCESS); + + return 0; +}