mdbxmou 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +7 -0
- package/README.md +52 -0
- package/package.json +1 -1
- package/src/async/envmou_close.cpp +0 -3
- package/src/async/envmou_copy_to.cpp +0 -3
- package/src/async/envmou_keys.cpp +41 -17
- package/src/async/envmou_keys.hpp +3 -0
- package/src/async/envmou_open.cpp +0 -6
- package/src/async/envmou_query.cpp +2 -2
- package/src/cursormou.cpp +5 -5
- package/src/dbi.cpp +7 -2
- package/src/dbi.hpp +3 -0
- package/src/dbimou.cpp +65 -46
- package/src/querymou.cpp +2 -2
- package/src/txnmou.cpp +89 -22
- package/src/txnmou.hpp +4 -1
- package/src/typemou.hpp +30 -2
package/CMakeLists.txt
CHANGED
|
@@ -47,6 +47,13 @@ if(MSVC)
|
|
|
47
47
|
target_link_libraries(${PROJECT_NAME} ntdll.lib)
|
|
48
48
|
endif()
|
|
49
49
|
|
|
50
|
+
# Batch read limit for mdbx_cursor_get_batch (pairs count = limit / 2)
|
|
51
|
+
if(NOT DEFINED MDBXMOU_BATCH_LIMIT)
|
|
52
|
+
set(MDBXMOU_BATCH_LIMIT 512)
|
|
53
|
+
endif()
|
|
54
|
+
add_definitions(-DMDBXMOU_BATCH_LIMIT=${MDBXMOU_BATCH_LIMIT})
|
|
55
|
+
message(STATUS "MDBXMOU_BATCH_LIMIT: ${MDBXMOU_BATCH_LIMIT}")
|
|
56
|
+
|
|
50
57
|
# Include N-API wrappers
|
|
51
58
|
execute_process(COMMAND node -p "require('node-addon-api').include"
|
|
52
59
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
package/README.md
CHANGED
|
@@ -88,6 +88,58 @@ Options:
|
|
|
88
88
|
|
|
89
89
|
Note: When `keyFlag` or `valueFlag` are set at environment level, they become defaults for all subsequent operations unless explicitly overridden.
|
|
90
90
|
|
|
91
|
+
### Key Type Configuration
|
|
92
|
+
|
|
93
|
+
The library uses a two-level system for configuring key types:
|
|
94
|
+
|
|
95
|
+
#### Level 1: Environment (`keyFlag`)
|
|
96
|
+
|
|
97
|
+
When opening the environment, `keyFlag` controls how **string/binary keys** are returned:
|
|
98
|
+
|
|
99
|
+
| `keyFlag` value | String/Binary keys returned as |
|
|
100
|
+
|-----------------|-------------------------------|
|
|
101
|
+
| `0` (default) | `Buffer` |
|
|
102
|
+
| `keyFlag.string` (2) | `String` |
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
// Keys returned as Buffer (default)
|
|
106
|
+
await env.open({ path: './data' });
|
|
107
|
+
|
|
108
|
+
// Keys returned as String
|
|
109
|
+
await env.open({
|
|
110
|
+
path: './data',
|
|
111
|
+
keyFlag: MDBX_Param.keyFlag.string
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### Level 2: Database (`keyMode`)
|
|
116
|
+
|
|
117
|
+
When opening/creating a database, the **argument type** determines how **ordinal (integer) keys** are returned:
|
|
118
|
+
|
|
119
|
+
| Argument type | Ordinal keys returned as |
|
|
120
|
+
|---------------|-------------------------|
|
|
121
|
+
| `Number` | `Number` |
|
|
122
|
+
| `BigInt` | `BigInt` |
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
// Ordinal keys as Number
|
|
126
|
+
const dbi = txn.openMap(MDBX_Param.keyMode.ordinal); // keyMode.ordinal = 8
|
|
127
|
+
dbi.keys(txn); // [0, 1, 2, 3, ...]
|
|
128
|
+
|
|
129
|
+
// Ordinal keys as BigInt
|
|
130
|
+
const dbi = txn.openMap(BigInt(MDBX_Param.keyMode.ordinal)); // BigInt(8)
|
|
131
|
+
dbi.keys(txn); // [0n, 1n, 2n, 3n, ...]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Summary
|
|
135
|
+
|
|
136
|
+
| Key type | Configuration level | Option | Result type |
|
|
137
|
+
|----------|--------------------|--------------------|-------------|
|
|
138
|
+
| String/Binary | env.open() | `keyFlag: 0` | `Buffer` |
|
|
139
|
+
| String/Binary | env.open() | `keyFlag: keyFlag.string` | `String` |
|
|
140
|
+
| Ordinal | openMap/createMap | `keyMode.ordinal` (Number) | `Number` |
|
|
141
|
+
| Ordinal | openMap/createMap | `BigInt(keyMode.ordinal)` | `BigInt` |
|
|
142
|
+
|
|
91
143
|
**close() → Promise**
|
|
92
144
|
```javascript
|
|
93
145
|
await env.close();
|
package/package.json
CHANGED
|
@@ -5,9 +5,6 @@ namespace mdbxmou {
|
|
|
5
5
|
|
|
6
6
|
void async_copy::Execute()
|
|
7
7
|
{
|
|
8
|
-
// выдадим идентфикатор потока для лога (thread_id)
|
|
9
|
-
// fprintf(stderr, "TRACE: async_copy id=%d\n", gettid());
|
|
10
|
-
|
|
11
8
|
auto rc = mdbx_env_copy(that_, dest_.c_str(), flags_);
|
|
12
9
|
if (rc != MDBX_SUCCESS) {
|
|
13
10
|
SetError(mdbx_strerror(rc));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "envmou_keys.hpp"
|
|
2
2
|
#include "envmou.hpp"
|
|
3
3
|
#include "dbimou.hpp"
|
|
4
|
+
#include "../valuemou.hpp"
|
|
4
5
|
|
|
5
6
|
namespace mdbxmou {
|
|
6
7
|
|
|
@@ -29,7 +30,7 @@ static Napi::Value write_row(Napi::Env env, const keys_line& row)
|
|
|
29
30
|
for (std::uint32_t j = 0; j < param.size(); ++j) {
|
|
30
31
|
const auto& item = param[j];
|
|
31
32
|
Napi::Value key_value;
|
|
32
|
-
if (key_mode
|
|
33
|
+
if (mdbx::is_ordinal(key_mode)) {
|
|
33
34
|
if (key_flag.val & base_flag::number) {
|
|
34
35
|
key_value = Napi::Number::New(env, static_cast<double>(item.id_buf));
|
|
35
36
|
} else {
|
|
@@ -92,27 +93,50 @@ void async_keys::do_keys(txnmou_managed& txn,
|
|
|
92
93
|
auto stat = dbimou::get_stat(txn, dbi);
|
|
93
94
|
item.reserve(stat.ms_entries);
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
cursor.scan([&](const mdbx::pair& f) {
|
|
98
|
-
async_key rc{};
|
|
99
|
-
rc.id_buf = f.key.as_uint64();
|
|
100
|
-
item.push_back(std::move(rc));
|
|
101
|
-
return false;
|
|
102
|
-
});
|
|
103
|
-
} else {
|
|
104
|
-
cursor.scan([&](const mdbx::pair& f) {
|
|
105
|
-
async_key rc{};
|
|
106
|
-
rc.key_buf.assign(f.key.char_ptr(), f.key.end_char_ptr());
|
|
107
|
-
item.push_back(std::move(rc));
|
|
108
|
-
return false;
|
|
109
|
-
});
|
|
110
|
-
}
|
|
96
|
+
// Используем batch версию для лучшей производительности
|
|
97
|
+
do_keys_batch(txn, dbi, arg0);
|
|
111
98
|
} else {
|
|
112
99
|
do_keys_from(txn, dbi, arg0);
|
|
113
100
|
}
|
|
114
101
|
}
|
|
115
102
|
|
|
103
|
+
// Batch версия - читает ключи блоками через cursormou_managed::get_batch
|
|
104
|
+
void async_keys::do_keys_batch(txnmou_managed& txn,
|
|
105
|
+
mdbx::map_handle dbi, keys_line& arg0)
|
|
106
|
+
{
|
|
107
|
+
auto& item = arg0.item;
|
|
108
|
+
auto is_ordinal = mdbx::is_ordinal(arg0.key_mod);
|
|
109
|
+
|
|
110
|
+
auto cursor = dbi::open_cursor(txn, dbi);
|
|
111
|
+
|
|
112
|
+
// Буфер для batch - MDBXMOU_BATCH_LIMIT/2 пар (key, value)
|
|
113
|
+
#ifndef MDBXMOU_BATCH_LIMIT
|
|
114
|
+
#define MDBXMOU_BATCH_LIMIT 512
|
|
115
|
+
#endif
|
|
116
|
+
std::array<mdbx::slice, MDBXMOU_BATCH_LIMIT> pairs;
|
|
117
|
+
|
|
118
|
+
// Первый вызов с MDBX_FIRST
|
|
119
|
+
size_t count = cursor.get_batch(pairs, MDBX_FIRST);
|
|
120
|
+
|
|
121
|
+
while (count > 0) {
|
|
122
|
+
// pairs[0] = key1, pairs[1] = value1, pairs[2] = key2, ...
|
|
123
|
+
for (size_t i = 0; i < count; i += 2) {
|
|
124
|
+
keymou key{pairs[i]};
|
|
125
|
+
async_key key_item{};
|
|
126
|
+
if (is_ordinal) {
|
|
127
|
+
key_item.id_buf = key.as_uint64();
|
|
128
|
+
} else {
|
|
129
|
+
// Ключ - строка/буфер
|
|
130
|
+
key_item.key_buf.assign(key.char_ptr(), key.end_char_ptr());
|
|
131
|
+
}
|
|
132
|
+
item.push_back(std::move(key_item));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Следующий batch
|
|
136
|
+
count = cursor.get_batch(pairs, MDBX_NEXT);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
116
140
|
void async_keys::do_keys_from(txnmou_managed& txn,
|
|
117
141
|
mdbx::map_handle dbi, keys_line& arg0)
|
|
118
142
|
{
|
|
@@ -43,6 +43,9 @@ public:
|
|
|
43
43
|
void do_keys(txnmou_managed& txn,
|
|
44
44
|
mdbx::map_handle dbi, keys_line& arg0);
|
|
45
45
|
|
|
46
|
+
void do_keys_batch(txnmou_managed& txn,
|
|
47
|
+
mdbx::map_handle dbi, keys_line& arg0);
|
|
48
|
+
|
|
46
49
|
void do_keys_from(txnmou_managed& txn,
|
|
47
50
|
mdbx::map_handle dbi, keys_line& arg0);
|
|
48
51
|
};
|
|
@@ -5,9 +5,6 @@ namespace mdbxmou {
|
|
|
5
5
|
|
|
6
6
|
void async_open::Execute()
|
|
7
7
|
{
|
|
8
|
-
// выдадим идентфикатор потока для лога (thread_id)
|
|
9
|
-
// fprintf(stderr, "TRACE: async_open id=%d\n", gettid());
|
|
10
|
-
|
|
11
8
|
try {
|
|
12
9
|
auto e = envmou::create_and_open(arg0_);
|
|
13
10
|
that_.attach(e, arg0_);
|
|
@@ -18,9 +15,6 @@ void async_open::Execute()
|
|
|
18
15
|
|
|
19
16
|
void async_open::OnOK()
|
|
20
17
|
{
|
|
21
|
-
// выдадим идентфикатор потока для лога (thread_id)
|
|
22
|
-
// fprintf(stderr, "TRACE: async_open OnOK id=%d\n", gettid());
|
|
23
|
-
|
|
24
18
|
auto env = Env();
|
|
25
19
|
|
|
26
20
|
that_.unlock();
|
|
@@ -39,7 +39,7 @@ static Napi::Value write_row(Napi::Env env, const query_line& row)
|
|
|
39
39
|
const auto& item = param[j];
|
|
40
40
|
Napi::Value key_value;
|
|
41
41
|
Napi::Object js_item = Napi::Object::New(env);
|
|
42
|
-
if (key_mode
|
|
42
|
+
if (mdbx::is_ordinal(key_mode)) {
|
|
43
43
|
if (key_flag.val & base_flag::number) {
|
|
44
44
|
key_value = Napi::Number::New(env, static_cast<double>(item.id_buf));
|
|
45
45
|
} else {
|
|
@@ -141,7 +141,7 @@ void async_query::do_get(const txnmou_managed& txn,
|
|
|
141
141
|
{
|
|
142
142
|
auto key = mdbx::is_ordinal(key_mode) ?
|
|
143
143
|
keymou{q.id_buf} : keymou{q.key_buf};
|
|
144
|
-
mdbx::slice abs
|
|
144
|
+
mdbx::slice abs;
|
|
145
145
|
valuemou val{txn.get(dbi, key, abs)};
|
|
146
146
|
q.set(val);
|
|
147
147
|
}
|
package/src/cursormou.cpp
CHANGED
|
@@ -80,7 +80,7 @@ namespace mdbxmou
|
|
|
80
80
|
auto value_flag = dbi_->get_value_flag();
|
|
81
81
|
|
|
82
82
|
// Ключ
|
|
83
|
-
if (key_mode
|
|
83
|
+
if (mdbx::is_ordinal(key_mode)) {
|
|
84
84
|
if (key_flag.val & base_flag::bigint) {
|
|
85
85
|
result.Set("key", key.to_bigint(env));
|
|
86
86
|
} else {
|
|
@@ -210,7 +210,7 @@ namespace mdbxmou
|
|
|
210
210
|
|
|
211
211
|
auto key_mode = dbi_->get_key_mode();
|
|
212
212
|
|
|
213
|
-
keymou key = (key_mode
|
|
213
|
+
keymou key = (mdbx::is_ordinal(key_mode)) ?
|
|
214
214
|
keymou::from(info[0], env, key_num_) :
|
|
215
215
|
keymou::from(info[0], env, key_buf_);
|
|
216
216
|
|
|
@@ -231,7 +231,7 @@ namespace mdbxmou
|
|
|
231
231
|
auto value_flag = dbi_->get_value_flag();
|
|
232
232
|
|
|
233
233
|
// Ключ
|
|
234
|
-
if (key_mode
|
|
234
|
+
if (mdbx::is_ordinal(key_mode)) {
|
|
235
235
|
if (key_flag.val & base_flag::bigint) {
|
|
236
236
|
result.Set("key", key.to_bigint(env));
|
|
237
237
|
} else {
|
|
@@ -275,7 +275,7 @@ namespace mdbxmou
|
|
|
275
275
|
|
|
276
276
|
auto key_mode = dbi_->get_key_mode();
|
|
277
277
|
|
|
278
|
-
keymou key = (key_mode
|
|
278
|
+
keymou key = (mdbx::is_ordinal(key_mode)) ?
|
|
279
279
|
keymou::from(info[0], env, key_num_) :
|
|
280
280
|
keymou::from(info[0], env, key_buf_);
|
|
281
281
|
|
|
@@ -354,7 +354,7 @@ namespace mdbxmou
|
|
|
354
354
|
auto result = Napi::Object::New(env);
|
|
355
355
|
|
|
356
356
|
// Ключ
|
|
357
|
-
if (key_mode
|
|
357
|
+
if (mdbx::is_ordinal(key_mode)) {
|
|
358
358
|
if (key_flag.val & base_flag::bigint) {
|
|
359
359
|
result.Set("key", key.to_bigint(env));
|
|
360
360
|
} else {
|
package/src/dbi.cpp
CHANGED
|
@@ -46,11 +46,16 @@ bool dbi::del(MDBX_txn* txn, const keymou& key)
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
cursormou_managed dbi::open_cursor(MDBX_txn* txn) const
|
|
49
|
+
{
|
|
50
|
+
return open_cursor(txn, mdbx::map_handle{id_});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
cursormou_managed dbi::open_cursor(MDBX_txn *txn, mdbx::map_handle dbi)
|
|
49
54
|
{
|
|
50
55
|
MDBX_cursor* cursor_ptr;
|
|
51
|
-
auto rc = mdbx_cursor_open(txn,
|
|
56
|
+
auto rc = mdbx_cursor_open(txn, dbi.dbi, &cursor_ptr);
|
|
52
57
|
if (rc != MDBX_SUCCESS) {
|
|
53
|
-
|
|
58
|
+
mdbx::error::throw_exception(rc);
|
|
54
59
|
}
|
|
55
60
|
return cursormou_managed{ cursor_ptr };
|
|
56
61
|
}
|
package/src/dbi.hpp
CHANGED
|
@@ -37,6 +37,9 @@ public:
|
|
|
37
37
|
bool del(MDBX_txn* txn, const keymou& key);
|
|
38
38
|
|
|
39
39
|
cursormou_managed open_cursor(MDBX_txn* txn) const;
|
|
40
|
+
|
|
41
|
+
// Static version for map_handle
|
|
42
|
+
static cursormou_managed open_cursor(MDBX_txn *txn, mdbx::map_handle dbi);
|
|
40
43
|
|
|
41
44
|
// Drop database or delete it
|
|
42
45
|
void drop(MDBX_txn* txn, bool delete_db = false);
|
package/src/dbimou.cpp
CHANGED
|
@@ -48,7 +48,7 @@ Napi::Value dbimou::put(const Napi::CallbackInfo& info)
|
|
|
48
48
|
auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
|
|
49
49
|
try {
|
|
50
50
|
std::uint64_t t;
|
|
51
|
-
auto key = (key_mode_
|
|
51
|
+
auto key = mdbx::is_ordinal(key_mode_) ?
|
|
52
52
|
keymou::from(info[1], env, t) :
|
|
53
53
|
keymou::from(info[1], env, key_buf_);
|
|
54
54
|
|
|
@@ -61,7 +61,8 @@ Napi::Value dbimou::put(const Napi::CallbackInfo& info)
|
|
|
61
61
|
return env.Undefined();
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
Napi::Value dbimou::get(const Napi::CallbackInfo& info)
|
|
64
|
+
Napi::Value dbimou::get(const Napi::CallbackInfo& info)
|
|
65
|
+
{
|
|
65
66
|
Napi::Env env = info.Env();
|
|
66
67
|
auto arg_len = info.Length();
|
|
67
68
|
if (arg_len < 2) {
|
|
@@ -76,7 +77,7 @@ Napi::Value dbimou::get(const Napi::CallbackInfo& info) {
|
|
|
76
77
|
|
|
77
78
|
try {
|
|
78
79
|
std::uint64_t t;
|
|
79
|
-
auto key = (key_mode_
|
|
80
|
+
auto key = mdbx::is_ordinal(key_mode_) ?
|
|
80
81
|
keymou::from(info[1], env, t) :
|
|
81
82
|
keymou::from(info[1], env, key_buf_);
|
|
82
83
|
|
|
@@ -94,7 +95,8 @@ Napi::Value dbimou::get(const Napi::CallbackInfo& info) {
|
|
|
94
95
|
return env.Undefined();
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
Napi::Value dbimou::del(const Napi::CallbackInfo& info)
|
|
98
|
+
Napi::Value dbimou::del(const Napi::CallbackInfo& info)
|
|
99
|
+
{
|
|
98
100
|
Napi::Env env = info.Env();
|
|
99
101
|
auto arg_len = info.Length();
|
|
100
102
|
if (arg_len < 2) {
|
|
@@ -109,7 +111,7 @@ Napi::Value dbimou::del(const Napi::CallbackInfo& info) {
|
|
|
109
111
|
|
|
110
112
|
try {
|
|
111
113
|
std::uint64_t t;
|
|
112
|
-
auto key = (key_mode_
|
|
114
|
+
auto key = mdbx::is_ordinal(key_mode_) ?
|
|
113
115
|
keymou::from(info[1], env, t) :
|
|
114
116
|
keymou::from(info[1], env, key_buf_);
|
|
115
117
|
|
|
@@ -122,7 +124,8 @@ Napi::Value dbimou::del(const Napi::CallbackInfo& info) {
|
|
|
122
124
|
return env.Undefined();
|
|
123
125
|
}
|
|
124
126
|
|
|
125
|
-
Napi::Value dbimou::has(const Napi::CallbackInfo& info)
|
|
127
|
+
Napi::Value dbimou::has(const Napi::CallbackInfo& info)
|
|
128
|
+
{
|
|
126
129
|
Napi::Env env = info.Env();
|
|
127
130
|
auto arg_len = info.Length();
|
|
128
131
|
if (arg_len < 2) {
|
|
@@ -137,7 +140,7 @@ Napi::Value dbimou::has(const Napi::CallbackInfo& info) {
|
|
|
137
140
|
|
|
138
141
|
try {
|
|
139
142
|
std::uint64_t t;
|
|
140
|
-
auto key = (key_mode_
|
|
143
|
+
auto key = mdbx::is_ordinal(key_mode_) ?
|
|
141
144
|
keymou::from(info[1], env, t) :
|
|
142
145
|
keymou::from(info[1], env, key_buf_);
|
|
143
146
|
|
|
@@ -150,7 +153,8 @@ Napi::Value dbimou::has(const Napi::CallbackInfo& info) {
|
|
|
150
153
|
return env.Undefined();
|
|
151
154
|
}
|
|
152
155
|
|
|
153
|
-
Napi::Value dbimou::for_each(const Napi::CallbackInfo& info)
|
|
156
|
+
Napi::Value dbimou::for_each(const Napi::CallbackInfo& info)
|
|
157
|
+
{
|
|
154
158
|
Napi::Env env = info.Env();
|
|
155
159
|
auto arg_len = info.Length();
|
|
156
160
|
if (arg_len < 2) {
|
|
@@ -169,16 +173,16 @@ Napi::Value dbimou::for_each(const Napi::CallbackInfo& info) {
|
|
|
169
173
|
auto fn = info[1].As<Napi::Function>();
|
|
170
174
|
|
|
171
175
|
try {
|
|
172
|
-
auto
|
|
173
|
-
auto stat = dbi::get_stat(*txn);
|
|
176
|
+
auto stat = get_stat(*txn);
|
|
174
177
|
|
|
175
178
|
// Проверяем, есть ли записи в базе данных
|
|
176
179
|
if (stat.ms_entries == 0) {
|
|
177
180
|
return Napi::Number::New(env, 0);
|
|
178
181
|
}
|
|
179
|
-
|
|
182
|
+
|
|
183
|
+
auto cursor = open_cursor(*txn);
|
|
180
184
|
uint32_t index{};
|
|
181
|
-
if (key_mode_
|
|
185
|
+
if (mdbx::is_ordinal(key_mode_)) {
|
|
182
186
|
cursor.scan([&](const mdbx::pair& f) {
|
|
183
187
|
keymou key{f.key};
|
|
184
188
|
valuemou val{f.value};
|
|
@@ -188,7 +192,7 @@ Napi::Value dbimou::for_each(const Napi::CallbackInfo& info) {
|
|
|
188
192
|
// Конвертируем значение
|
|
189
193
|
Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
|
|
190
194
|
val.to_string(env) : val.to_buffer(env);
|
|
191
|
-
|
|
195
|
+
// Формируем результат
|
|
192
196
|
Napi::Value result = fn.Call({ rc_key, rc_val,
|
|
193
197
|
Napi::Number::New(env, static_cast<double>(index)) });
|
|
194
198
|
|
|
@@ -208,7 +212,7 @@ Napi::Value dbimou::for_each(const Napi::CallbackInfo& info) {
|
|
|
208
212
|
// Конвертируем значение
|
|
209
213
|
Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
|
|
210
214
|
val.to_string(env) : val.to_buffer(env);
|
|
211
|
-
|
|
215
|
+
// Формируем результат
|
|
212
216
|
Napi::Value result = fn.Call({ rc_key, rc_val,
|
|
213
217
|
Napi::Number::New(env, static_cast<double>(index)) });
|
|
214
218
|
|
|
@@ -234,7 +238,8 @@ Napi::Value dbimou::for_each(const Napi::CallbackInfo& info) {
|
|
|
234
238
|
}
|
|
235
239
|
}
|
|
236
240
|
|
|
237
|
-
Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info)
|
|
241
|
+
Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info)
|
|
242
|
+
{
|
|
238
243
|
Napi::Env env = info.Env();
|
|
239
244
|
auto arg_len = info.Length();
|
|
240
245
|
|
|
@@ -270,7 +275,7 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
|
|
|
270
275
|
|
|
271
276
|
// Парсим начальный ключ
|
|
272
277
|
std::uint64_t t;
|
|
273
|
-
keymou from_key = (key_mode_
|
|
278
|
+
keymou from_key = (mdbx::is_ordinal(key_mode_)) ?
|
|
274
279
|
keymou::from(info[1], env, t) :
|
|
275
280
|
keymou::from(info[1], env, key_buf_);
|
|
276
281
|
|
|
@@ -304,7 +309,7 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
|
|
|
304
309
|
std::size_t index{};
|
|
305
310
|
bool is_key_equal_mode = (cursor_mode == move_operation::key_equal ||
|
|
306
311
|
cursor_mode == move_operation::multi_exactkey_value_equal);
|
|
307
|
-
if (key_mode_
|
|
312
|
+
if (mdbx::is_ordinal(key_mode_)) {
|
|
308
313
|
cursor.scan_from([&](const mdbx::pair& f) {
|
|
309
314
|
keymou key{f.key};
|
|
310
315
|
valuemou val{f.value};
|
|
@@ -320,7 +325,7 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
|
|
|
320
325
|
// Конвертируем значение
|
|
321
326
|
Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
|
|
322
327
|
val.to_string(env) : val.to_buffer(env);
|
|
323
|
-
|
|
328
|
+
// Формируем результат
|
|
324
329
|
Napi::Value result = fn.Call({ rc_key, rc_val,
|
|
325
330
|
Napi::Number::New(env, static_cast<double>(index)) });
|
|
326
331
|
|
|
@@ -346,7 +351,7 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
|
|
|
346
351
|
// Конвертируем значение
|
|
347
352
|
Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
|
|
348
353
|
val.to_string(env) : val.to_buffer(env);
|
|
349
|
-
|
|
354
|
+
// Формируем результат
|
|
350
355
|
Napi::Value result = fn.Call({ rc_key, rc_val,
|
|
351
356
|
Napi::Number::New(env, static_cast<double>(index)) });
|
|
352
357
|
|
|
@@ -363,7 +368,8 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
|
|
|
363
368
|
}
|
|
364
369
|
}
|
|
365
370
|
|
|
366
|
-
Napi::Value dbimou::stat(const Napi::CallbackInfo& info)
|
|
371
|
+
Napi::Value dbimou::stat(const Napi::CallbackInfo& info)
|
|
372
|
+
{
|
|
367
373
|
Napi::Env env = info.Env();
|
|
368
374
|
auto arg_len = info.Length();
|
|
369
375
|
if (arg_len < 1) {
|
|
@@ -401,7 +407,8 @@ Napi::Value dbimou::stat(const Napi::CallbackInfo& info) {
|
|
|
401
407
|
return env.Undefined();
|
|
402
408
|
}
|
|
403
409
|
|
|
404
|
-
Napi::Value dbimou::keys(const Napi::CallbackInfo& info)
|
|
410
|
+
Napi::Value dbimou::keys(const Napi::CallbackInfo& info)
|
|
411
|
+
{
|
|
405
412
|
Napi::Env env = info.Env();
|
|
406
413
|
auto arg_len = info.Length();
|
|
407
414
|
if (arg_len < 1) {
|
|
@@ -415,8 +422,7 @@ Napi::Value dbimou::keys(const Napi::CallbackInfo& info) {
|
|
|
415
422
|
auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
|
|
416
423
|
|
|
417
424
|
try {
|
|
418
|
-
auto
|
|
419
|
-
auto stat = dbi::get_stat(*txn);
|
|
425
|
+
auto stat = get_stat(*txn);
|
|
420
426
|
|
|
421
427
|
// Создаем массив для ключей
|
|
422
428
|
Napi::Array keys = Napi::Array::New(env, stat.ms_entries);
|
|
@@ -424,27 +430,38 @@ Napi::Value dbimou::keys(const Napi::CallbackInfo& info) {
|
|
|
424
430
|
return keys;
|
|
425
431
|
}
|
|
426
432
|
|
|
433
|
+
// Используем batch версию для лучшей производительности
|
|
434
|
+
auto cursor = open_cursor(*txn);
|
|
435
|
+
|
|
436
|
+
// Буфер для batch - MDBXMOU_BATCH_LIMIT/2 пар (key, value)
|
|
437
|
+
#ifndef MDBXMOU_BATCH_LIMIT
|
|
438
|
+
#define MDBXMOU_BATCH_LIMIT 512
|
|
439
|
+
#endif // MDBXMOU_BATCH_LIMIT
|
|
440
|
+
std::array<mdbx::slice, MDBXMOU_BATCH_LIMIT> pairs;
|
|
441
|
+
|
|
427
442
|
uint32_t index{};
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
key.to_string(env) : key.to_buffer(env);
|
|
444
|
-
|
|
443
|
+
auto is_ordinal = mdbx::is_ordinal(key_mode_);
|
|
444
|
+
auto is_bigint = key_flag_.val & base_flag::bigint;
|
|
445
|
+
auto is_string = key_flag_.val & base_flag::string;
|
|
446
|
+
|
|
447
|
+
// Первый вызов с MDBX_FIRST
|
|
448
|
+
size_t count = cursor.get_batch(pairs, MDBX_FIRST);
|
|
449
|
+
|
|
450
|
+
while (count > 0) {
|
|
451
|
+
// pairs[0] = key1, pairs[1] = value1, pairs[2] = key2, ...
|
|
452
|
+
for (size_t i = 0; i < count; i += 2) {
|
|
453
|
+
keymou key{pairs[i]};
|
|
454
|
+
Napi::Value rc_key;
|
|
455
|
+
if (is_ordinal) {
|
|
456
|
+
rc_key = is_bigint ? key.to_bigint(env) : key.to_number(env);
|
|
457
|
+
} else {
|
|
458
|
+
rc_key = is_string ? key.to_string(env) : key.to_buffer(env);
|
|
459
|
+
}
|
|
445
460
|
keys.Set(index++, rc_key);
|
|
446
|
-
|
|
447
|
-
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Следующий batch
|
|
464
|
+
count = cursor.get_batch(pairs, MDBX_NEXT);
|
|
448
465
|
}
|
|
449
466
|
|
|
450
467
|
return keys;
|
|
@@ -455,7 +472,8 @@ Napi::Value dbimou::keys(const Napi::CallbackInfo& info) {
|
|
|
455
472
|
return env.Undefined();
|
|
456
473
|
}
|
|
457
474
|
|
|
458
|
-
Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info)
|
|
475
|
+
Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info)
|
|
476
|
+
{
|
|
459
477
|
Napi::Env env = info.Env();
|
|
460
478
|
auto arg_len = info.Length();
|
|
461
479
|
if (arg_len < 2) {
|
|
@@ -473,7 +491,7 @@ Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info) {
|
|
|
473
491
|
|
|
474
492
|
// Парсим аргументы: txn, from, limit, cursorMode
|
|
475
493
|
std::uint64_t t;
|
|
476
|
-
keymou from_key = (key_mode_
|
|
494
|
+
keymou from_key = (mdbx::is_ordinal(key_mode_)) ?
|
|
477
495
|
keymou::from(info[1], env, t) :
|
|
478
496
|
keymou::from(info[1], env, key_buf_);
|
|
479
497
|
|
|
@@ -514,7 +532,7 @@ Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info) {
|
|
|
514
532
|
bool is_key_equal_mode = (cursor_mode == move_operation::key_equal ||
|
|
515
533
|
cursor_mode == move_operation::multi_exactkey_value_equal);
|
|
516
534
|
|
|
517
|
-
if (key_mode_
|
|
535
|
+
if (mdbx::is_ordinal(key_mode_)) {
|
|
518
536
|
cursor.scan_from([&](const mdbx::pair& f) {
|
|
519
537
|
if (index >= count) {
|
|
520
538
|
return true; // останавливаем сканирование
|
|
@@ -566,7 +584,8 @@ Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info) {
|
|
|
566
584
|
return env.Undefined();
|
|
567
585
|
}
|
|
568
586
|
|
|
569
|
-
Napi::Value dbimou::drop(const Napi::CallbackInfo& info)
|
|
587
|
+
Napi::Value dbimou::drop(const Napi::CallbackInfo& info)
|
|
588
|
+
{
|
|
570
589
|
Napi::Env env = info.Env();
|
|
571
590
|
if (info.Length() < 1) {
|
|
572
591
|
throw Napi::TypeError::New(env, "First argument must be a transaction");
|
package/src/querymou.cpp
CHANGED
|
@@ -5,7 +5,7 @@ namespace mdbxmou {
|
|
|
5
5
|
|
|
6
6
|
dbimou* async_common::parse(const Napi::Object& arg0)
|
|
7
7
|
{
|
|
8
|
-
dbimou* dbi
|
|
8
|
+
dbimou* dbi{};
|
|
9
9
|
// если просто передали dbi
|
|
10
10
|
if (arg0.InstanceOf(dbimou::ctor.Value())) {
|
|
11
11
|
dbi = Napi::ObjectWrap<dbimou>::Unwrap(arg0);
|
|
@@ -27,7 +27,7 @@ void async_key::parse(const async_common& common, const Napi::Value& item)
|
|
|
27
27
|
auto key_flag = common.key_flag;
|
|
28
28
|
|
|
29
29
|
keymou key{};
|
|
30
|
-
if (common.key_mod
|
|
30
|
+
if (mdbx::is_ordinal(common.key_mod)) {
|
|
31
31
|
if (item.IsBigInt()) {
|
|
32
32
|
key = keymou{item.As<Napi::BigInt>(), id_buf};
|
|
33
33
|
} else if (item.IsNumber()) {
|
package/src/txnmou.cpp
CHANGED
|
@@ -70,21 +70,102 @@ void txnmou::dec_counter() noexcept
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
Napi::Value txnmou::get_dbi(const
|
|
73
|
+
Napi::Value txnmou::get_dbi(const char* name, base_flag key_flag,
|
|
74
|
+
base_flag value_flag, key_mode key_mode, value_mode value_mode,
|
|
75
|
+
db_mode db_mode)
|
|
74
76
|
{
|
|
75
|
-
Napi::Env env =
|
|
77
|
+
Napi::Env env = Env();
|
|
76
78
|
|
|
77
79
|
if ((mode_.val & txn_mode::ro) && (db_mode.val & db_mode::create)) {
|
|
78
80
|
throw Napi::Error::New(env, "dbi: cannot open DB in read-only transaction");
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
if (!txn_) {
|
|
84
|
+
throw Napi::Error::New(env, "txn already completed");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
MDBX_dbi dbi{};
|
|
88
|
+
auto flags = static_cast<MDBX_db_flags_t>(db_mode.val|key_mode.val|value_mode.val);
|
|
89
|
+
auto rc = mdbx_dbi_open(*this, (name && name[0]) ? name : nullptr, flags, &dbi);
|
|
90
|
+
if (rc != MDBX_SUCCESS) {
|
|
91
|
+
throw Napi::Error::New(env, std::string("mdbx_dbi_open: ") + mdbx_strerror(rc));
|
|
92
|
+
}
|
|
93
|
+
// создаем новый объект dbi
|
|
94
|
+
auto obj = dbimou::ctor.New({});
|
|
95
|
+
auto ptr = dbimou::Unwrap(obj);
|
|
96
|
+
ptr->attach(dbi, db_mode, key_mode,
|
|
97
|
+
value_mode, key_flag, value_flag);
|
|
98
|
+
return obj;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Napi::Value txnmou::get_dbi(const Napi::Object& arg0, db_mode db_mode)
|
|
102
|
+
{
|
|
103
|
+
auto env = arg0.Env();
|
|
104
|
+
auto conf = get_env_userctx(*env_);
|
|
105
|
+
auto key_flag = conf->key_flag;
|
|
106
|
+
auto value_flag = conf->value_flag;
|
|
81
107
|
key_mode key_mode{};
|
|
82
108
|
value_mode value_mode{};
|
|
83
109
|
std::string db_name{};
|
|
110
|
+
|
|
111
|
+
if (arg0.Has("name")) {
|
|
112
|
+
auto value = arg0.Get("name");
|
|
113
|
+
if (!value.IsUndefined() && !value.IsNull()) {
|
|
114
|
+
if (!value.IsString()) {
|
|
115
|
+
throw Napi::Error::New(env, "dbi: name must be string");
|
|
116
|
+
}
|
|
117
|
+
db_name = value.As<Napi::String>().Utf8Value();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (arg0.Has("keyFlag")) {
|
|
122
|
+
auto value = arg0.Get("keyFlag");
|
|
123
|
+
if (!value.IsUndefined() && !value.IsNull()) {
|
|
124
|
+
key_flag = base_flag::parse_key(value);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (arg0.Has("valueFlag")) {
|
|
129
|
+
auto value = arg0.Get("valueFlag");
|
|
130
|
+
if (!value.IsUndefined() && !value.IsNull()) {
|
|
131
|
+
value_flag = base_flag::parse_value(value);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (arg0.Has("keyMode")) {
|
|
136
|
+
auto value = arg0.Get("keyMode");
|
|
137
|
+
if (!value.IsUndefined() && !value.IsNull()) {
|
|
138
|
+
key_mode = parse_key_mode(env, value, key_flag);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (arg0.Has("valueMode")) {
|
|
143
|
+
auto value = arg0.Get("valueMode");
|
|
144
|
+
if (!value.IsUndefined() && !value.IsNull()) {
|
|
145
|
+
value_mode = value_mode::parse(value);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return get_dbi(db_name.empty() ? nullptr : db_name.c_str(),
|
|
150
|
+
key_flag, value_flag, key_mode, value_mode, db_mode);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
Napi::Value txnmou::get_dbi(const Napi::CallbackInfo& info, db_mode db_mode)
|
|
154
|
+
{
|
|
155
|
+
Napi::Env env = info.Env();
|
|
156
|
+
|
|
84
157
|
auto conf = get_env_userctx(*env_);
|
|
85
158
|
auto key_flag = conf->key_flag;
|
|
86
159
|
auto value_flag = conf->value_flag;
|
|
160
|
+
key_mode key_mode{};
|
|
161
|
+
value_mode value_mode{};
|
|
162
|
+
std::string db_name{};
|
|
87
163
|
auto arg_count = info.Length();
|
|
164
|
+
|
|
165
|
+
if (arg_count == 1 && info[0].IsObject()) {
|
|
166
|
+
return get_dbi(info[0].As<Napi::Object>(), db_mode);
|
|
167
|
+
}
|
|
168
|
+
|
|
88
169
|
if (arg_count == 3) {
|
|
89
170
|
auto arg0 = info[0]; // db_name
|
|
90
171
|
auto arg1 = info[1]; // key_mode
|
|
@@ -94,8 +175,8 @@ Napi::Value txnmou::get_dbi(const Napi::CallbackInfo& info, db_mode db_mode)
|
|
|
94
175
|
value_mode = value_mode::parse(arg2);
|
|
95
176
|
} else if (arg_count == 2) {
|
|
96
177
|
// db_name + key_mode || key_mode + value_mode
|
|
97
|
-
auto arg0 = info[0];
|
|
98
|
-
auto arg1 = info[1];
|
|
178
|
+
auto arg0 = info[0];
|
|
179
|
+
auto arg1 = info[1];
|
|
99
180
|
if (arg0.IsString()) {
|
|
100
181
|
db_name = arg0.As<Napi::String>().Utf8Value();
|
|
101
182
|
key_mode = parse_key_mode(env, arg1, key_flag);
|
|
@@ -116,24 +197,10 @@ Napi::Value txnmou::get_dbi(const Napi::CallbackInfo& info, db_mode db_mode)
|
|
|
116
197
|
throw Napi::Error::New(env, "Invalid argument type: expected string (db_name) or number (key_mode)");
|
|
117
198
|
}
|
|
118
199
|
}
|
|
119
|
-
// arg_count == 0:
|
|
120
|
-
|
|
121
|
-
if (!txn_) {
|
|
122
|
-
throw Napi::Error::New(env, "txn already completed");
|
|
123
|
-
}
|
|
200
|
+
// arg_count == 0: значения по умолчанию (key/value = buffer, если env не задавал флаги)
|
|
124
201
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
auto rc = mdbx_dbi_open(*this, db_name.empty() ? nullptr : db_name.c_str(), flags, &dbi);
|
|
128
|
-
if (rc != MDBX_SUCCESS) {
|
|
129
|
-
throw Napi::Error::New(env, std::string("mdbx_dbi_open: ") + mdbx_strerror(rc));
|
|
130
|
-
}
|
|
131
|
-
// создаем новый объект dbi
|
|
132
|
-
auto obj = dbimou::ctor.New({});
|
|
133
|
-
auto ptr = dbimou::Unwrap(obj);
|
|
134
|
-
ptr->attach(dbi, db_mode, key_mode,
|
|
135
|
-
value_mode, key_flag, value_flag);
|
|
136
|
-
return obj;
|
|
202
|
+
return get_dbi(db_name.empty() ? nullptr : db_name.c_str(),
|
|
203
|
+
key_flag, value_flag, key_mode, value_mode, db_mode);
|
|
137
204
|
}
|
|
138
205
|
|
|
139
206
|
Napi::Value txnmou::open_cursor(const Napi::CallbackInfo& info) {
|
|
@@ -178,4 +245,4 @@ void txnmou::attach(envmou& env, MDBX_txn* txn, txn_mode mode)
|
|
|
178
245
|
txn_.reset(txn);
|
|
179
246
|
}
|
|
180
247
|
|
|
181
|
-
} // namespace mdbxmou
|
|
248
|
+
} // namespace mdbxmou
|
package/src/txnmou.hpp
CHANGED
|
@@ -27,7 +27,10 @@ private:
|
|
|
27
27
|
// Уменьшает счетчик транзакций
|
|
28
28
|
void dec_counter() noexcept;
|
|
29
29
|
|
|
30
|
-
Napi::Value get_dbi(const
|
|
30
|
+
Napi::Value get_dbi(const char* name, base_flag key_flag, base_flag value_flag,
|
|
31
|
+
key_mode key_mode, value_mode value_mode, db_mode db_mode);
|
|
32
|
+
Napi::Value get_dbi(const Napi::Object& arg0, db_mode db_mode);
|
|
33
|
+
Napi::Value get_dbi(const Napi::CallbackInfo& info, db_mode db_mode);
|
|
31
34
|
|
|
32
35
|
public:
|
|
33
36
|
static Napi::FunctionReference ctor;
|
package/src/typemou.hpp
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include <mdbx.h++>
|
|
5
5
|
#include <cstdint>
|
|
6
6
|
#include <vector>
|
|
7
|
+
#include <array>
|
|
7
8
|
#include <utility>
|
|
8
9
|
|
|
9
10
|
namespace mdbxmou {
|
|
@@ -70,6 +71,33 @@ struct cursormou_managed final
|
|
|
70
71
|
::mdbx_cursor_close(cursor::handle_);
|
|
71
72
|
}
|
|
72
73
|
}
|
|
74
|
+
|
|
75
|
+
/// \brief Batch read using mdbx_cursor_get_batch
|
|
76
|
+
/// \param[out] pairs Array of mdbx::slice to fill (key0, val0, key1, val1, ...)
|
|
77
|
+
/// \param[in] limit Maximum number of slice elements in pairs array
|
|
78
|
+
/// \param[in] op Cursor operation (MDBX_FIRST or MDBX_NEXT only)
|
|
79
|
+
/// \return Number of items read (key+value count), 0 if end of data
|
|
80
|
+
/// \throws mdbx::exception on error
|
|
81
|
+
size_t get_batch(mdbx::slice* pairs, size_t limit, MDBX_cursor_op op)
|
|
82
|
+
{
|
|
83
|
+
size_t count{};
|
|
84
|
+
auto rc = ::mdbx_cursor_get_batch(cursor::handle_, &count, pairs, limit, op);
|
|
85
|
+
if (rc == MDBX_SUCCESS || rc == MDBX_RESULT_TRUE) {
|
|
86
|
+
return count;
|
|
87
|
+
}
|
|
88
|
+
if (rc == MDBX_NOTFOUND || rc == MDBX_ENODATA) {
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
mdbx::error::throw_exception(rc);
|
|
92
|
+
return 0; // unreachable
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// \brief Batch read with std::array
|
|
96
|
+
template<size_t N>
|
|
97
|
+
size_t get_batch(std::array<mdbx::slice, N>& pairs, MDBX_cursor_op op)
|
|
98
|
+
{
|
|
99
|
+
return get_batch(pairs.data(), N, op);
|
|
100
|
+
}
|
|
73
101
|
};
|
|
74
102
|
|
|
75
103
|
struct env_flag {
|
|
@@ -218,7 +246,7 @@ static inline key_mode parse_key_mode(Napi::Env env, const Napi::Value& arg0, ba
|
|
|
218
246
|
throw Napi::Error::New(env, "BigInt value lossless conversion failed");
|
|
219
247
|
}
|
|
220
248
|
mode.val = static_cast<int>(value);
|
|
221
|
-
if (mode
|
|
249
|
+
if (mdbx::is_ordinal(mode)) {
|
|
222
250
|
// только если цифровой key_flag не был задан
|
|
223
251
|
if (key_flag.val <= base_flag::string) {
|
|
224
252
|
key_flag.val = base_flag::bigint;
|
|
@@ -226,7 +254,7 @@ static inline key_mode parse_key_mode(Napi::Env env, const Napi::Value& arg0, ba
|
|
|
226
254
|
}
|
|
227
255
|
} else if (arg0.IsNumber()) {
|
|
228
256
|
mode = key_mode::parse(arg0);
|
|
229
|
-
if (mode
|
|
257
|
+
if (mdbx::is_ordinal(mode)) {
|
|
230
258
|
// только если цифровой key_flag не был задан
|
|
231
259
|
if (key_flag.val <= base_flag::string) {
|
|
232
260
|
key_flag.val = base_flag::number;
|