mdbxmou 0.3.2 → 0.3.4
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/lib/types.d.ts +30 -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 +40 -16
- package/src/async/envmou_keys.hpp +3 -0
- package/src/async/envmou_open.cpp +0 -6
- package/src/async/envmou_query.cpp +1 -1
- package/src/cursormou.cpp +344 -258
- package/src/cursormou.hpp +7 -0
- package/src/dbi.cpp +7 -2
- package/src/dbi.hpp +3 -0
- package/src/dbimou.cpp +43 -29
- package/src/envmou.cpp +0 -5
- package/src/querymou.cpp +1 -1
- package/src/txnmou.cpp +2 -5
- package/src/txnmou.hpp +12 -3
- package/src/typemou.hpp +28 -0
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/lib/types.d.ts
CHANGED
|
@@ -147,6 +147,36 @@ export interface MDBX_Cursor<K extends MDBXKey = MDBXKey, V extends MDBXValue =
|
|
|
147
147
|
*/
|
|
148
148
|
forEach(callback: (item: MDBXCursorResult<K, V>) => boolean | void, backward?: boolean): void;
|
|
149
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Check if cursor is at end-of-data (beyond first or last record).
|
|
152
|
+
* @returns true if cursor has no valid position
|
|
153
|
+
*/
|
|
154
|
+
eof(): boolean;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Check if cursor is on first record in database.
|
|
158
|
+
* @returns true if on first record
|
|
159
|
+
*/
|
|
160
|
+
onFirst(): boolean;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check if cursor is on last record in database.
|
|
164
|
+
* @returns true if on last record
|
|
165
|
+
*/
|
|
166
|
+
onLast(): boolean;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if cursor is on first value of current key (DUPSORT databases).
|
|
170
|
+
* @returns true if on first value for current key
|
|
171
|
+
*/
|
|
172
|
+
onFirstMultival(): boolean;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check if cursor is on last value of current key (DUPSORT databases).
|
|
176
|
+
* @returns true if on last value for current key
|
|
177
|
+
*/
|
|
178
|
+
onLastMultival(): boolean;
|
|
179
|
+
|
|
150
180
|
/**
|
|
151
181
|
* Close cursor. Must be called before transaction commit/abort.
|
|
152
182
|
* Safe to call multiple times.
|
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
|
|
|
@@ -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
|
+
const bool is_ordinal = mdbx::is_ordinal(arg0.key_mod);
|
|
109
|
+
|
|
110
|
+
auto cursor = dbi::get_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();
|
package/src/cursormou.cpp
CHANGED
|
@@ -2,272 +2,234 @@
|
|
|
2
2
|
#include "dbimou.hpp"
|
|
3
3
|
#include "txnmou.hpp"
|
|
4
4
|
|
|
5
|
-
namespace mdbxmou
|
|
6
|
-
|
|
7
|
-
Napi::FunctionReference cursormou::ctor{};
|
|
8
|
-
|
|
9
|
-
void cursormou::init(const char *class_name, Napi::Env env)
|
|
10
|
-
{
|
|
11
|
-
auto func = DefineClass(env, class_name, {
|
|
12
|
-
InstanceMethod("first", &cursormou::first),
|
|
13
|
-
InstanceMethod("last", &cursormou::last),
|
|
14
|
-
InstanceMethod("next", &cursormou::next),
|
|
15
|
-
InstanceMethod("prev", &cursormou::prev),
|
|
16
|
-
InstanceMethod("seek", &cursormou::seek),
|
|
17
|
-
InstanceMethod("seekGE", &cursormou::seek_ge),
|
|
18
|
-
InstanceMethod("current", &cursormou::current),
|
|
19
|
-
InstanceMethod("put", &cursormou::put),
|
|
20
|
-
InstanceMethod("del", &cursormou::del),
|
|
21
|
-
InstanceMethod("forEach", &cursormou::for_each),
|
|
22
|
-
InstanceMethod("close", &cursormou::close),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
ctor = Napi::Persistent(func);
|
|
26
|
-
ctor.SuppressDestruct();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
void cursormou::do_close() noexcept
|
|
5
|
+
namespace mdbxmou
|
|
30
6
|
{
|
|
31
|
-
if (cursor_) {
|
|
32
|
-
::mdbx_cursor_close(std::exchange(cursor_, nullptr));
|
|
33
|
-
if (txn_) {
|
|
34
|
-
--(*txn_);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
7
|
|
|
39
|
-
cursormou
|
|
40
|
-
{
|
|
41
|
-
do_close();
|
|
42
|
-
}
|
|
8
|
+
Napi::FunctionReference cursormou::ctor{};
|
|
43
9
|
|
|
44
|
-
void cursormou::
|
|
45
|
-
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
10
|
+
void cursormou::init(const char *class_name, Napi::Env env)
|
|
11
|
+
{
|
|
12
|
+
auto func = DefineClass(env, class_name, {
|
|
13
|
+
InstanceMethod("first", &cursormou::first),
|
|
14
|
+
InstanceMethod("last", &cursormou::last),
|
|
15
|
+
InstanceMethod("next", &cursormou::next),
|
|
16
|
+
InstanceMethod("prev", &cursormou::prev),
|
|
17
|
+
InstanceMethod("seek", &cursormou::seek),
|
|
18
|
+
InstanceMethod("seekGE", &cursormou::seek_ge),
|
|
19
|
+
InstanceMethod("current", &cursormou::current),
|
|
20
|
+
InstanceMethod("eof", &cursormou::eof),
|
|
21
|
+
InstanceMethod("onFirst", &cursormou::on_first),
|
|
22
|
+
InstanceMethod("onLast", &cursormou::on_last),
|
|
23
|
+
InstanceMethod("onFirstMultival", &cursormou::on_first_multival),
|
|
24
|
+
InstanceMethod("onLastMultival", &cursormou::on_last_multival),
|
|
25
|
+
InstanceMethod("put", &cursormou::put),
|
|
26
|
+
InstanceMethod("del", &cursormou::del),
|
|
27
|
+
InstanceMethod("forEach", &cursormou::for_each),
|
|
28
|
+
InstanceMethod("close", &cursormou::close),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
ctor = Napi::Persistent(func);
|
|
32
|
+
ctor.SuppressDestruct();
|
|
68
33
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// Ключ
|
|
78
|
-
if (key_mode.val & key_mode::ordinal) {
|
|
79
|
-
if (key_flag.val & base_flag::bigint) {
|
|
80
|
-
result.Set("key", key.to_bigint(env));
|
|
81
|
-
} else {
|
|
82
|
-
result.Set("key", key.to_number(env));
|
|
34
|
+
|
|
35
|
+
void cursormou::do_close() noexcept
|
|
36
|
+
{
|
|
37
|
+
if (cursor_) {
|
|
38
|
+
::mdbx_cursor_close(std::exchange(cursor_, nullptr));
|
|
39
|
+
if (txn_) {
|
|
40
|
+
--(*txn_);
|
|
41
|
+
}
|
|
83
42
|
}
|
|
84
|
-
} else {
|
|
85
|
-
result.Set("key", key.to_string(env));
|
|
86
43
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} else {
|
|
92
|
-
result.Set("value", val.to_buffer(env));
|
|
44
|
+
|
|
45
|
+
cursormou::~cursormou()
|
|
46
|
+
{
|
|
47
|
+
do_close();
|
|
93
48
|
}
|
|
94
|
-
|
|
95
|
-
return result;
|
|
96
|
-
}
|
|
97
49
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
50
|
+
void cursormou::attach(txnmou &txn, dbimou &dbi, MDBX_cursor *cursor)
|
|
51
|
+
{
|
|
52
|
+
txn_ = &(++txn);
|
|
53
|
+
dbi_ = &dbi;
|
|
54
|
+
cursor_ = cursor;
|
|
55
|
+
}
|
|
101
56
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
57
|
+
// Внутренний хелпер для навигации
|
|
58
|
+
Napi::Value cursormou::move(const Napi::Env &env, MDBX_cursor_op op)
|
|
59
|
+
{
|
|
60
|
+
if (!cursor_) {
|
|
61
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
62
|
+
}
|
|
105
63
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
64
|
+
keymou key{};
|
|
65
|
+
valuemou val{};
|
|
66
|
+
auto rc = mdbx_cursor_get(cursor_, key, val, op);
|
|
67
|
+
if (rc == MDBX_NOTFOUND) {
|
|
68
|
+
return env.Undefined();
|
|
69
|
+
}
|
|
109
70
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
71
|
+
if (rc != MDBX_SUCCESS) {
|
|
72
|
+
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
73
|
+
}
|
|
113
74
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
75
|
+
// Возвращаем {key, value}
|
|
76
|
+
auto result = Napi::Object::New(env);
|
|
117
77
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
Napi::Env env = info.Env();
|
|
122
|
-
|
|
123
|
-
if (!cursor_) {
|
|
124
|
-
throw Napi::Error::New(env, "cursor closed");
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (info.Length() < 1) {
|
|
128
|
-
throw Napi::Error::New(env, "key required");
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
auto key_mode = dbi_->get_key_mode();
|
|
132
|
-
|
|
133
|
-
keymou key = (key_mode.val & key_mode::ordinal) ?
|
|
134
|
-
keymou::from(info[0], env, key_num_) :
|
|
135
|
-
keymou::from(info[0], env, key_buf_);
|
|
136
|
-
|
|
137
|
-
valuemou val{};
|
|
138
|
-
auto rc = mdbx_cursor_get(cursor_, key, val, op);
|
|
139
|
-
if (MDBX_NOTFOUND == rc) {
|
|
140
|
-
return env.Undefined();
|
|
141
|
-
}
|
|
78
|
+
auto key_mode = dbi_->get_key_mode();
|
|
79
|
+
auto key_flag = dbi_->get_key_flag();
|
|
80
|
+
auto value_flag = dbi_->get_value_flag();
|
|
142
81
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
auto key_flag = dbi_->get_key_flag();
|
|
151
|
-
auto value_flag = dbi_->get_value_flag();
|
|
152
|
-
|
|
153
|
-
// Ключ
|
|
154
|
-
if (key_mode.val & key_mode::ordinal) {
|
|
155
|
-
if (key_flag.val & base_flag::bigint) {
|
|
156
|
-
result.Set("key", key.to_bigint(env));
|
|
82
|
+
// Ключ
|
|
83
|
+
if (key_mode.val & key_mode::ordinal) {
|
|
84
|
+
if (key_flag.val & base_flag::bigint) {
|
|
85
|
+
result.Set("key", key.to_bigint(env));
|
|
86
|
+
} else {
|
|
87
|
+
result.Set("key", key.to_number(env));
|
|
88
|
+
}
|
|
157
89
|
} else {
|
|
158
|
-
result.Set("key", key.
|
|
90
|
+
result.Set("key", key.to_string(env));
|
|
159
91
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
result
|
|
92
|
+
|
|
93
|
+
// Значение
|
|
94
|
+
if (value_flag.val & base_flag::string) {
|
|
95
|
+
result.Set("value", val.to_string(env));
|
|
96
|
+
} else {
|
|
97
|
+
result.Set("value", val.to_buffer(env));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result;
|
|
169
101
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
Napi::Value cursormou::seek(const Napi::CallbackInfo& info) {
|
|
175
|
-
return seek_impl(info, MDBX_SET_KEY);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
Napi::Value cursormou::seek_ge(const Napi::CallbackInfo& info) {
|
|
179
|
-
return seek_impl(info, MDBX_SET_RANGE);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
Napi::Value cursormou::put(const Napi::CallbackInfo& info) {
|
|
183
|
-
Napi::Env env = info.Env();
|
|
184
|
-
|
|
185
|
-
if (!cursor_) {
|
|
186
|
-
throw Napi::Error::New(env, "cursor closed");
|
|
102
|
+
|
|
103
|
+
Napi::Value cursormou::first(const Napi::CallbackInfo &info)
|
|
104
|
+
{
|
|
105
|
+
return move(info.Env(), MDBX_FIRST);
|
|
187
106
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
107
|
+
|
|
108
|
+
Napi::Value cursormou::last(const Napi::CallbackInfo &info)
|
|
109
|
+
{
|
|
110
|
+
return move(info.Env(), MDBX_LAST);
|
|
191
111
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
keymou::from(info[0], env, key_num_) :
|
|
197
|
-
keymou::from(info[0], env, key_buf_);
|
|
198
|
-
|
|
199
|
-
valuemou val = valuemou::from(info[1], env, val_buf_);
|
|
200
|
-
|
|
201
|
-
// Опциональный флаг put_mode (по умолчанию MDBX_UPSERT)
|
|
202
|
-
MDBX_put_flags_t flags = MDBX_UPSERT;
|
|
203
|
-
if (info.Length() > 2 && info[2].IsNumber()) {
|
|
204
|
-
flags = static_cast<MDBX_put_flags_t>(info[2].As<Napi::Number>().Int32Value());
|
|
112
|
+
|
|
113
|
+
Napi::Value cursormou::next(const Napi::CallbackInfo &info)
|
|
114
|
+
{
|
|
115
|
+
return move(info.Env(), MDBX_NEXT);
|
|
205
116
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
117
|
+
|
|
118
|
+
Napi::Value cursormou::prev(const Napi::CallbackInfo &info)
|
|
119
|
+
{
|
|
120
|
+
return move(info.Env(), MDBX_PREV);
|
|
210
121
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
Napi::Value cursormou::del(const Napi::CallbackInfo& info) {
|
|
216
|
-
Napi::Env env = info.Env();
|
|
217
|
-
|
|
218
|
-
if (!cursor_) {
|
|
219
|
-
throw Napi::Error::New(env, "cursor closed");
|
|
122
|
+
|
|
123
|
+
Napi::Value cursormou::current(const Napi::CallbackInfo &info)
|
|
124
|
+
{
|
|
125
|
+
return move(info.Env(), MDBX_GET_CURRENT);
|
|
220
126
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
127
|
+
|
|
128
|
+
Napi::Value cursormou::eof(const Napi::CallbackInfo &info)
|
|
129
|
+
{
|
|
130
|
+
Napi::Env env = info.Env();
|
|
131
|
+
if (!cursor_) {
|
|
132
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
133
|
+
}
|
|
134
|
+
auto rc = ::mdbx_cursor_eof(cursor_);
|
|
135
|
+
if (rc == MDBX_RESULT_TRUE)
|
|
136
|
+
return Napi::Boolean::New(env, true);
|
|
137
|
+
if (rc == MDBX_RESULT_FALSE)
|
|
138
|
+
return Napi::Boolean::New(env, false);
|
|
139
|
+
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
226
140
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
141
|
+
|
|
142
|
+
Napi::Value cursormou::on_first(const Napi::CallbackInfo &info)
|
|
143
|
+
{
|
|
144
|
+
Napi::Env env = info.Env();
|
|
145
|
+
if (!cursor_) {
|
|
146
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
147
|
+
}
|
|
148
|
+
auto rc = ::mdbx_cursor_on_first(cursor_);
|
|
149
|
+
if (rc == MDBX_RESULT_TRUE)
|
|
150
|
+
return Napi::Boolean::New(env, true);
|
|
151
|
+
if (rc == MDBX_RESULT_FALSE)
|
|
152
|
+
return Napi::Boolean::New(env, false);
|
|
153
|
+
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
231
154
|
}
|
|
232
|
-
|
|
233
|
-
|
|
155
|
+
|
|
156
|
+
Napi::Value cursormou::on_last(const Napi::CallbackInfo &info)
|
|
157
|
+
{
|
|
158
|
+
Napi::Env env = info.Env();
|
|
159
|
+
if (!cursor_) {
|
|
160
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
161
|
+
}
|
|
162
|
+
auto rc = ::mdbx_cursor_on_last(cursor_);
|
|
163
|
+
if (rc == MDBX_RESULT_TRUE)
|
|
164
|
+
return Napi::Boolean::New(env, true);
|
|
165
|
+
if (rc == MDBX_RESULT_FALSE)
|
|
166
|
+
return Napi::Boolean::New(env, false);
|
|
234
167
|
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
235
168
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
169
|
+
|
|
170
|
+
Napi::Value cursormou::on_first_multival(const Napi::CallbackInfo &info)
|
|
171
|
+
{
|
|
172
|
+
Napi::Env env = info.Env();
|
|
173
|
+
if (!cursor_) {
|
|
174
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
175
|
+
}
|
|
176
|
+
auto rc = ::mdbx_cursor_on_first_dup(cursor_);
|
|
177
|
+
if (rc == MDBX_RESULT_TRUE)
|
|
178
|
+
return Napi::Boolean::New(env, true);
|
|
179
|
+
if (rc == MDBX_RESULT_FALSE)
|
|
180
|
+
return Napi::Boolean::New(env, false);
|
|
181
|
+
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
247
182
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
183
|
+
|
|
184
|
+
Napi::Value cursormou::on_last_multival(const Napi::CallbackInfo &info)
|
|
185
|
+
{
|
|
186
|
+
Napi::Env env = info.Env();
|
|
187
|
+
if (!cursor_) {
|
|
188
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
189
|
+
}
|
|
190
|
+
auto rc = ::mdbx_cursor_on_last_dup(cursor_);
|
|
191
|
+
if (rc == MDBX_RESULT_TRUE)
|
|
192
|
+
return Napi::Boolean::New(env, true);
|
|
193
|
+
if (rc == MDBX_RESULT_FALSE)
|
|
194
|
+
return Napi::Boolean::New(env, false);
|
|
195
|
+
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
251
196
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
auto key_mode = dbi_->get_key_mode();
|
|
257
|
-
auto key_flag = dbi_->get_key_flag();
|
|
258
|
-
auto value_flag = dbi_->get_value_flag();
|
|
259
|
-
|
|
260
|
-
MDBX_cursor_op start_op = backward ? MDBX_LAST : MDBX_FIRST;
|
|
261
|
-
MDBX_cursor_op move_op = backward ? MDBX_PREV : MDBX_NEXT;
|
|
262
|
-
|
|
263
|
-
keymou key{};
|
|
264
|
-
valuemou val{};
|
|
265
|
-
// Первое позиционирование
|
|
266
|
-
auto rc = mdbx_cursor_get(cursor_, key, val, start_op);
|
|
267
|
-
while (MDBX_SUCCESS == rc)
|
|
197
|
+
|
|
198
|
+
// Хелпер для поиска
|
|
199
|
+
Napi::Value cursormou::seek_impl(const Napi::CallbackInfo &info, MDBX_cursor_op op)
|
|
268
200
|
{
|
|
201
|
+
Napi::Env env = info.Env();
|
|
202
|
+
|
|
203
|
+
if (!cursor_) {
|
|
204
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (info.Length() < 1) {
|
|
208
|
+
throw Napi::Error::New(env, "key required");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
auto key_mode = dbi_->get_key_mode();
|
|
212
|
+
|
|
213
|
+
keymou key = (key_mode.val & key_mode::ordinal) ?
|
|
214
|
+
keymou::from(info[0], env, key_num_) :
|
|
215
|
+
keymou::from(info[0], env, key_buf_);
|
|
216
|
+
|
|
217
|
+
valuemou val{};
|
|
218
|
+
auto rc = mdbx_cursor_get(cursor_, key, val, op);
|
|
219
|
+
if (MDBX_NOTFOUND == rc) {
|
|
220
|
+
return env.Undefined();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (MDBX_SUCCESS != rc) {
|
|
224
|
+
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Возвращаем {key, value}
|
|
269
228
|
auto result = Napi::Object::New(env);
|
|
270
|
-
|
|
229
|
+
|
|
230
|
+
auto key_flag = dbi_->get_key_flag();
|
|
231
|
+
auto value_flag = dbi_->get_value_flag();
|
|
232
|
+
|
|
271
233
|
// Ключ
|
|
272
234
|
if (key_mode.val & key_mode::ordinal) {
|
|
273
235
|
if (key_flag.val & base_flag::bigint) {
|
|
@@ -278,36 +240,160 @@ Napi::Value cursormou::for_each(const Napi::CallbackInfo& info) {
|
|
|
278
240
|
} else {
|
|
279
241
|
result.Set("key", key.to_string(env));
|
|
280
242
|
}
|
|
281
|
-
|
|
243
|
+
|
|
282
244
|
// Значение
|
|
283
245
|
if (value_flag.val & base_flag::string) {
|
|
284
246
|
result.Set("value", val.to_string(env));
|
|
285
247
|
} else {
|
|
286
248
|
result.Set("value", val.to_buffer(env));
|
|
287
249
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
250
|
+
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
Napi::Value cursormou::seek(const Napi::CallbackInfo &info)
|
|
255
|
+
{
|
|
256
|
+
return seek_impl(info, MDBX_SET_KEY);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
Napi::Value cursormou::seek_ge(const Napi::CallbackInfo &info)
|
|
260
|
+
{
|
|
261
|
+
return seek_impl(info, MDBX_SET_RANGE);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
Napi::Value cursormou::put(const Napi::CallbackInfo &info)
|
|
265
|
+
{
|
|
266
|
+
Napi::Env env = info.Env();
|
|
267
|
+
|
|
268
|
+
if (!cursor_) {
|
|
269
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (info.Length() < 2) {
|
|
273
|
+
throw Napi::Error::New(env, "key and value required");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
auto key_mode = dbi_->get_key_mode();
|
|
277
|
+
|
|
278
|
+
keymou key = (key_mode.val & key_mode::ordinal) ?
|
|
279
|
+
keymou::from(info[0], env, key_num_) :
|
|
280
|
+
keymou::from(info[0], env, key_buf_);
|
|
281
|
+
|
|
282
|
+
valuemou val = valuemou::from(info[1], env, val_buf_);
|
|
283
|
+
|
|
284
|
+
// Опциональный флаг put_mode (по умолчанию MDBX_UPSERT)
|
|
285
|
+
MDBX_put_flags_t flags = MDBX_UPSERT;
|
|
286
|
+
if (info.Length() > 2 && info[2].IsNumber()) {
|
|
287
|
+
flags = static_cast<MDBX_put_flags_t>(info[2].As<Napi::Number>().Int32Value());
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
auto rc = mdbx_cursor_put(cursor_, key, val, flags);
|
|
291
|
+
if (MDBX_SUCCESS != rc) {
|
|
292
|
+
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
295
293
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
rc = mdbx_cursor_get(cursor_, key, val, move_op);
|
|
294
|
+
|
|
295
|
+
return env.Undefined();
|
|
299
296
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
297
|
+
|
|
298
|
+
Napi::Value cursormou::del(const Napi::CallbackInfo &info)
|
|
299
|
+
{
|
|
300
|
+
Napi::Env env = info.Env();
|
|
301
|
+
|
|
302
|
+
if (!cursor_) {
|
|
303
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Опциональный флаг (MDBX_NODUPDATA для удаления только текущего значения в multi-value)
|
|
307
|
+
MDBX_put_flags_t flags = MDBX_CURRENT;
|
|
308
|
+
if (info.Length() > 0 && info[0].IsNumber()) {
|
|
309
|
+
flags = static_cast<MDBX_put_flags_t>(info[0].As<Napi::Number>().Int32Value());
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
auto rc = mdbx_cursor_del(cursor_, flags);
|
|
313
|
+
if (MDBX_NOTFOUND == rc) {
|
|
314
|
+
return Napi::Boolean::New(env, false);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (MDBX_SUCCESS != rc) {
|
|
318
|
+
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return Napi::Boolean::New(env, true);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// forEach(callback, backward = false)
|
|
325
|
+
// callback({key, value}) => true продолжить, false/undefined остановить
|
|
326
|
+
Napi::Value cursormou::for_each(const Napi::CallbackInfo &info)
|
|
327
|
+
{
|
|
328
|
+
Napi::Env env = info.Env();
|
|
329
|
+
|
|
330
|
+
if (!cursor_) {
|
|
331
|
+
throw Napi::Error::New(env, "cursor closed");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (info.Length() < 1 || !info[0].IsFunction()) {
|
|
335
|
+
throw Napi::TypeError::New(env, "forEach: callback function required");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
auto callback = info[0].As<Napi::Function>();
|
|
339
|
+
bool backward = info.Length() > 1 && info[1].ToBoolean().Value();
|
|
340
|
+
|
|
341
|
+
auto key_mode = dbi_->get_key_mode();
|
|
342
|
+
auto key_flag = dbi_->get_key_flag();
|
|
343
|
+
auto value_flag = dbi_->get_value_flag();
|
|
344
|
+
|
|
345
|
+
MDBX_cursor_op start_op = backward ? MDBX_LAST : MDBX_FIRST;
|
|
346
|
+
MDBX_cursor_op move_op = backward ? MDBX_PREV : MDBX_NEXT;
|
|
347
|
+
|
|
348
|
+
keymou key{};
|
|
349
|
+
valuemou val{};
|
|
350
|
+
// Первое позиционирование
|
|
351
|
+
auto rc = mdbx_cursor_get(cursor_, key, val, start_op);
|
|
352
|
+
while (MDBX_SUCCESS == rc)
|
|
353
|
+
{
|
|
354
|
+
auto result = Napi::Object::New(env);
|
|
355
|
+
|
|
356
|
+
// Ключ
|
|
357
|
+
if (key_mode.val & key_mode::ordinal) {
|
|
358
|
+
if (key_flag.val & base_flag::bigint) {
|
|
359
|
+
result.Set("key", key.to_bigint(env));
|
|
360
|
+
} else {
|
|
361
|
+
result.Set("key", key.to_number(env));
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
result.Set("key", key.to_string(env));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Значение
|
|
368
|
+
if (value_flag.val & base_flag::string) {
|
|
369
|
+
result.Set("value", val.to_string(env));
|
|
370
|
+
} else {
|
|
371
|
+
result.Set("value", val.to_buffer(env));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Вызов callback
|
|
375
|
+
auto ret = callback.Call({result});
|
|
376
|
+
|
|
377
|
+
// true stops the scan, false/undefined continues (same as dbi.forEach)
|
|
378
|
+
if (ret.IsBoolean() && ret.ToBoolean()) {
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Следующий элемент
|
|
383
|
+
rc = mdbx_cursor_get(cursor_, key, val, move_op);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (rc != MDBX_SUCCESS && rc != MDBX_NOTFOUND) {
|
|
387
|
+
throw Napi::Error::New(env, mdbx_strerror(rc));
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return env.Undefined();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
Napi::Value cursormou::close(const Napi::CallbackInfo &info)
|
|
394
|
+
{
|
|
395
|
+
do_close();
|
|
396
|
+
return info.Env().Undefined();
|
|
303
397
|
}
|
|
304
|
-
|
|
305
|
-
return env.Undefined();
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
Napi::Value cursormou::close(const Napi::CallbackInfo& info) {
|
|
309
|
-
do_close();
|
|
310
|
-
return info.Env().Undefined();
|
|
311
|
-
}
|
|
312
398
|
|
|
313
399
|
} // namespace mdbxmou
|
package/src/cursormou.hpp
CHANGED
|
@@ -53,6 +53,13 @@ public:
|
|
|
53
53
|
// Текущая позиция
|
|
54
54
|
Napi::Value current(const Napi::CallbackInfo&);
|
|
55
55
|
|
|
56
|
+
// Статус курсора
|
|
57
|
+
Napi::Value eof(const Napi::CallbackInfo&);
|
|
58
|
+
Napi::Value on_first(const Napi::CallbackInfo&);
|
|
59
|
+
Napi::Value on_last(const Napi::CallbackInfo&);
|
|
60
|
+
Napi::Value on_first_multival(const Napi::CallbackInfo&);
|
|
61
|
+
Napi::Value on_last_multival(const Napi::CallbackInfo&);
|
|
62
|
+
|
|
56
63
|
// Модификация
|
|
57
64
|
Napi::Value put(const Napi::CallbackInfo&);
|
|
58
65
|
Napi::Value del(const Napi::CallbackInfo&);
|
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 get_cursor(txn, mdbx::map_handle{ id_ });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
cursormou_managed dbi::get_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 get_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
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -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
|
|
|
@@ -320,7 +324,7 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
|
|
|
320
324
|
// Конвертируем значение
|
|
321
325
|
Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
|
|
322
326
|
val.to_string(env) : val.to_buffer(env);
|
|
323
|
-
|
|
327
|
+
// Формируем результат
|
|
324
328
|
Napi::Value result = fn.Call({ rc_key, rc_val,
|
|
325
329
|
Napi::Number::New(env, static_cast<double>(index)) });
|
|
326
330
|
|
|
@@ -346,7 +350,7 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
|
|
|
346
350
|
// Конвертируем значение
|
|
347
351
|
Napi::Value rc_val = (value_flag_.val & base_flag::string) ?
|
|
348
352
|
val.to_string(env) : val.to_buffer(env);
|
|
349
|
-
|
|
353
|
+
// Формируем результат
|
|
350
354
|
Napi::Value result = fn.Call({ rc_key, rc_val,
|
|
351
355
|
Napi::Number::New(env, static_cast<double>(index)) });
|
|
352
356
|
|
|
@@ -415,8 +419,7 @@ Napi::Value dbimou::keys(const Napi::CallbackInfo& info) {
|
|
|
415
419
|
auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
|
|
416
420
|
|
|
417
421
|
try {
|
|
418
|
-
auto
|
|
419
|
-
auto stat = dbi::get_stat(*txn);
|
|
422
|
+
auto stat = get_stat(*txn);
|
|
420
423
|
|
|
421
424
|
// Создаем массив для ключей
|
|
422
425
|
Napi::Array keys = Napi::Array::New(env, stat.ms_entries);
|
|
@@ -424,27 +427,38 @@ Napi::Value dbimou::keys(const Napi::CallbackInfo& info) {
|
|
|
424
427
|
return keys;
|
|
425
428
|
}
|
|
426
429
|
|
|
430
|
+
// Используем batch версию для лучшей производительности
|
|
431
|
+
auto cursor = open_cursor(*txn);
|
|
432
|
+
|
|
433
|
+
// Буфер для batch - MDBXMOU_BATCH_LIMIT/2 пар (key, value)
|
|
434
|
+
#ifndef MDBXMOU_BATCH_LIMIT
|
|
435
|
+
#define MDBXMOU_BATCH_LIMIT 512
|
|
436
|
+
#endif // MDBXMOU_BATCH_LIMIT
|
|
437
|
+
std::array<mdbx::slice, MDBXMOU_BATCH_LIMIT> pairs;
|
|
438
|
+
|
|
427
439
|
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
|
-
|
|
440
|
+
const bool is_ordinal = key_mode_.val & key_mode::ordinal;
|
|
441
|
+
const bool is_bigint = key_flag_.val & base_flag::bigint;
|
|
442
|
+
const bool is_string = key_flag_.val & base_flag::string;
|
|
443
|
+
|
|
444
|
+
// Первый вызов с MDBX_FIRST
|
|
445
|
+
size_t count = cursor.get_batch(pairs, MDBX_FIRST);
|
|
446
|
+
|
|
447
|
+
while (count > 0) {
|
|
448
|
+
// pairs[0] = key1, pairs[1] = value1, pairs[2] = key2, ...
|
|
449
|
+
for (size_t i = 0; i < count; i += 2) {
|
|
450
|
+
keymou key{pairs[i]};
|
|
451
|
+
Napi::Value rc_key;
|
|
452
|
+
if (is_ordinal) {
|
|
453
|
+
rc_key = is_bigint ? key.to_bigint(env) : key.to_number(env);
|
|
454
|
+
} else {
|
|
455
|
+
rc_key = is_string ? key.to_string(env) : key.to_buffer(env);
|
|
456
|
+
}
|
|
445
457
|
keys.Set(index++, rc_key);
|
|
446
|
-
|
|
447
|
-
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Следующий batch
|
|
461
|
+
count = cursor.get_batch(pairs, MDBX_NEXT);
|
|
448
462
|
}
|
|
449
463
|
|
|
450
464
|
return keys;
|
package/src/envmou.cpp
CHANGED
|
@@ -193,11 +193,6 @@ MDBX_env* envmou::create_and_open(const env_arg0& arg0)
|
|
|
193
193
|
throw std::runtime_error(mdbx_strerror(rc));
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
#ifdef _WIN32
|
|
197
|
-
auto id = static_cast<std::uint32_t>(GetCurrentThreadId());
|
|
198
|
-
#else
|
|
199
|
-
auto id = static_cast<std::uint32_t>(pthread_self());
|
|
200
|
-
#endif
|
|
201
196
|
// выдадим параметры mode, flag и id потока в котором открывается env
|
|
202
197
|
rc = mdbx_env_open(env, arg0.path.c_str(), arg0.flag, arg0.file_mode);
|
|
203
198
|
if (rc != MDBX_SUCCESS) {
|
package/src/querymou.cpp
CHANGED
package/src/txnmou.cpp
CHANGED
|
@@ -155,8 +155,7 @@ Napi::Value txnmou::open_cursor(const Napi::CallbackInfo& info) {
|
|
|
155
155
|
auto* dbi = dbimou::Unwrap(arg0);
|
|
156
156
|
|
|
157
157
|
MDBX_cursor* cursor{};
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
auto rc = mdbx_cursor_open(txn_.get(), dbi->get_id(), &cursor);
|
|
160
159
|
if (rc != MDBX_SUCCESS) {
|
|
161
160
|
throw Napi::Error::New(env, std::string("mdbx_cursor_open: ") + mdbx_strerror(rc));
|
|
162
161
|
}
|
|
@@ -174,10 +173,8 @@ Napi::Value txnmou::is_active(const Napi::CallbackInfo& info) {
|
|
|
174
173
|
|
|
175
174
|
void txnmou::attach(envmou& env, MDBX_txn* txn, txn_mode mode)
|
|
176
175
|
{
|
|
177
|
-
env_ = &env;
|
|
176
|
+
env_ = &(++env);
|
|
178
177
|
mode_ = mode;
|
|
179
|
-
|
|
180
|
-
++(*env_);
|
|
181
178
|
txn_.reset(txn);
|
|
182
179
|
}
|
|
183
180
|
|
package/src/txnmou.hpp
CHANGED
|
@@ -46,6 +46,7 @@ public:
|
|
|
46
46
|
|
|
47
47
|
Napi::Value commit(const Napi::CallbackInfo&);
|
|
48
48
|
Napi::Value abort(const Napi::CallbackInfo&);
|
|
49
|
+
|
|
49
50
|
Napi::Value open_map(const Napi::CallbackInfo& info) {
|
|
50
51
|
return get_dbi(info, db_mode{});
|
|
51
52
|
}
|
|
@@ -60,9 +61,17 @@ public:
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
// Cursor counting
|
|
63
|
-
txnmou& operator++() noexcept {
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
txnmou& operator++() noexcept {
|
|
65
|
+
++cursor_count_; return *this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
txnmou& operator--() noexcept {
|
|
69
|
+
--cursor_count_; return *this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
std::size_t cursor_count() const noexcept {
|
|
73
|
+
return cursor_count_;
|
|
74
|
+
}
|
|
66
75
|
|
|
67
76
|
Napi::Value is_active(const Napi::CallbackInfo&);
|
|
68
77
|
|
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 {
|