mdbxmou 0.3.1 → 0.3.3
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/lib/types.d.ts +116 -0
- package/package.json +1 -1
- package/src/cursormou.cpp +343 -260
- package/src/cursormou.hpp +7 -0
- package/src/envmou.cpp +0 -5
- package/src/txnmou.cpp +2 -5
- package/src/txnmou.hpp +12 -3
package/lib/types.d.ts
CHANGED
|
@@ -73,6 +73,117 @@ export interface MDBXDbiStat {
|
|
|
73
73
|
modTxnId: number;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** Result of cursor navigation/search operations */
|
|
77
|
+
export interface MDBXCursorResult<K extends MDBXKey = MDBXKey, V extends MDBXValue = MDBXValue> {
|
|
78
|
+
key: K;
|
|
79
|
+
value: V;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Database cursor for sequential access and range queries.
|
|
84
|
+
* Must be closed before transaction commit/abort.
|
|
85
|
+
* @example
|
|
86
|
+
* ```js
|
|
87
|
+
* const cursor = txn.openCursor(dbi);
|
|
88
|
+
* for (let item = cursor.first(); item; item = cursor.next()) {
|
|
89
|
+
* console.log(item.key, item.value);
|
|
90
|
+
* }
|
|
91
|
+
* cursor.close();
|
|
92
|
+
* txn.commit();
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export interface MDBX_Cursor<K extends MDBXKey = MDBXKey, V extends MDBXValue = MDBXValue> {
|
|
96
|
+
/** Move to first record. Returns undefined if database is empty. */
|
|
97
|
+
first(): MDBXCursorResult<K, V> | undefined;
|
|
98
|
+
/** Move to last record. Returns undefined if database is empty. */
|
|
99
|
+
last(): MDBXCursorResult<K, V> | undefined;
|
|
100
|
+
/** Move to next record. Returns undefined if at end. */
|
|
101
|
+
next(): MDBXCursorResult<K, V> | undefined;
|
|
102
|
+
/** Move to previous record. Returns undefined if at beginning. */
|
|
103
|
+
prev(): MDBXCursorResult<K, V> | undefined;
|
|
104
|
+
/** Get current record without moving cursor. */
|
|
105
|
+
current(): MDBXCursorResult<K, V> | undefined;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Seek to exact key match.
|
|
109
|
+
* @param key - The key to find
|
|
110
|
+
* @returns Record if found, undefined otherwise
|
|
111
|
+
*/
|
|
112
|
+
seek(key: K): MDBXCursorResult<K, V> | undefined;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Seek to key >= given key (lower_bound semantics).
|
|
116
|
+
* @param key - The key to search from
|
|
117
|
+
* @returns First record with key >= given key, undefined if none
|
|
118
|
+
*/
|
|
119
|
+
seekGE(key: K): MDBXCursorResult<K, V> | undefined;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Insert or update a key-value pair.
|
|
123
|
+
* @param key - The key
|
|
124
|
+
* @param value - The value (Buffer or string)
|
|
125
|
+
* @param flags - Optional MDBX put flags
|
|
126
|
+
*/
|
|
127
|
+
put(key: K, value: MDBXValue, flags?: number): void;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Delete record at current cursor position.
|
|
131
|
+
* @param flags - Optional MDBX delete flags
|
|
132
|
+
* @returns true if deleted, false if not found
|
|
133
|
+
*/
|
|
134
|
+
del(flags?: number): boolean;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Iterate over all records.
|
|
138
|
+
* @param callback - Called for each record. Return true to stop iteration.
|
|
139
|
+
* @param backward - If true, iterate from last to first
|
|
140
|
+
* @example
|
|
141
|
+
* ```js
|
|
142
|
+
* cursor.forEach(({key, value}) => {
|
|
143
|
+
* console.log(key, value);
|
|
144
|
+
* if (key === 'stop') return true; // stop iteration
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
forEach(callback: (item: MDBXCursorResult<K, V>) => boolean | void, backward?: boolean): void;
|
|
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
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Close cursor. Must be called before transaction commit/abort.
|
|
182
|
+
* Safe to call multiple times.
|
|
183
|
+
*/
|
|
184
|
+
close(): void;
|
|
185
|
+
}
|
|
186
|
+
|
|
76
187
|
export interface MDBX_Dbi<K extends MDBXKey = MDBXKey, V extends MDBXValue = MDBXValue> {
|
|
77
188
|
readonly id: bigint;
|
|
78
189
|
readonly dbMode: number;
|
|
@@ -119,6 +230,11 @@ export interface MDBX_Txn {
|
|
|
119
230
|
createMap(name: string, keyMode: number | bigint): MDBX_Dbi;
|
|
120
231
|
createMap(name: string, keyMode: number | bigint, valueMode: number): MDBX_Dbi;
|
|
121
232
|
|
|
233
|
+
/** Open a cursor for the given dbi */
|
|
234
|
+
openCursor<K extends MDBXKey = MDBXKey, V extends MDBXValue = MDBXValue>(
|
|
235
|
+
dbi: MDBX_Dbi<K, V>
|
|
236
|
+
): MDBX_Cursor<K, V>;
|
|
237
|
+
|
|
122
238
|
isActive(): boolean;
|
|
123
239
|
isTopLevel(): boolean;
|
|
124
240
|
|
package/package.json
CHANGED
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,39 +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());
|
|
295
288
|
}
|
|
296
|
-
|
|
297
|
-
|
|
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));
|
|
298
293
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
rc = mdbx_cursor_get(cursor_, key, val, move_op);
|
|
294
|
+
|
|
295
|
+
return env.Undefined();
|
|
302
296
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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();
|
|
306
397
|
}
|
|
307
|
-
|
|
308
|
-
return env.Undefined();
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
Napi::Value cursormou::close(const Napi::CallbackInfo& info) {
|
|
312
|
-
do_close();
|
|
313
|
-
return info.Env().Undefined();
|
|
314
|
-
}
|
|
315
398
|
|
|
316
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/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/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
|
|