mdbxmou 0.1.35 → 0.2.0

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 CHANGED
@@ -23,6 +23,7 @@ add_library(${PROJECT_NAME} SHARED
23
23
  "src/querymou.cpp"
24
24
  "src/envmou.cpp"
25
25
  "src/txnmou.cpp"
26
+ "src/dbi.cpp"
26
27
  "src/dbimou.cpp")
27
28
 
28
29
  # Gives our library file a .node extension without any "lib" prefix
package/README.md CHANGED
@@ -5,7 +5,7 @@ High-performance Node.js binding for libmdbx — a fast, lightweight, embedded k
5
5
  ## Features
6
6
 
7
7
  - **Synchronous API** — Direct MDBX operations in main thread
8
- - **Asynchronous API** — Background operations via single Worker Thread
8
+ - **Asynchronous API** — Background operations with async/await
9
9
  - **Transactions** — ACID transactions with read/write modes
10
10
  - **Multiple key/value types** — String, binary, ordinal (integer) keys
11
11
  - **Batch operations** — Efficient multi-key read/write
@@ -33,14 +33,14 @@ await env.open({
33
33
  // Write data
34
34
  const txn = env.startWrite();
35
35
  const dbi = txn.createMap(MDBX_Param.keyMode.ordinal);
36
- dbi.put(1, "hello");
37
- dbi.put(2, "world");
36
+ dbi.put(txn, 1, "hello");
37
+ dbi.put(txn, 2, "world");
38
38
  txn.commit();
39
39
 
40
40
  // Read data
41
41
  const readTxn = env.startRead();
42
42
  const readDbi = readTxn.openMap(BigInt(MDBX_Param.keyMode.ordinal));
43
- const value = readDbi.get(1);
43
+ const value = readDbi.get(readTxn, 1);
44
44
  console.log(value); // "hello"
45
45
  readTxn.commit();
46
46
 
@@ -180,32 +180,37 @@ txn.abort();
180
180
 
181
181
  #### Methods
182
182
 
183
- **put(key, value, [flags])**
183
+ **put(txn, key, value)**
184
184
  ```javascript
185
- dbi.put(123, "value");
186
- dbi.put("key", Buffer.from("binary data"));
185
+ dbi.put(txn, 123, "value");
186
+ dbi.put(txn, "key", Buffer.from("binary data"));
187
187
  ```
188
188
 
189
- **get(key) → value**
189
+ **get(txn, key) → value**
190
190
  ```javascript
191
- const value = dbi.get(123);
192
- const binary = dbi.get("key");
191
+ const value = dbi.get(txn, 123);
192
+ const binary = dbi.get(txn, "key");
193
193
  ```
194
194
 
195
- **del(key) → boolean**
195
+ **del(txn, key) → boolean**
196
196
  ```javascript
197
- const deleted = dbi.del(123);
197
+ const deleted = dbi.del(txn, 123);
198
198
  ```
199
199
 
200
- **stat() → Object**
200
+ **has(txn, key) → boolean**
201
201
  ```javascript
202
- const stats = dbi.stat();
202
+ const exists = dbi.has(txn, 123);
203
+ ```
204
+
205
+ **stat(txn) → Object**
206
+ ```javascript
207
+ const stats = dbi.stat(txn);
203
208
  // { pageSize: 4096, depth: 1, entries: 10, ... }
204
209
  ```
205
210
 
206
- **forEach(callback)**
211
+ **forEach(txn, callback)**
207
212
  ```javascript
208
- dbi.forEach((key, value, index) => {
213
+ dbi.forEach(txn, (key, value, index) => {
209
214
  console.log(`${key}: ${value}`);
210
215
  return false; // continue iteration (or undefined)
211
216
  // return true; // stop iteration
@@ -214,34 +219,37 @@ dbi.forEach((key, value, index) => {
214
219
 
215
220
  > **Note**: forEach continues scanning while callback returns `undefined` or `false`, and stops when callback returns `true`.
216
221
 
217
- **keys() → Array**
222
+ **keys(txn) → Array**
218
223
  ```javascript
219
224
  // Get all keys
220
- const allKeys = dbi.keys();
225
+ const allKeys = dbi.keys(txn);
221
226
  ```
222
227
 
223
- **keysFrom(startKey, [limit], [cursorMode]) → Array**
228
+ **keysFrom(txn, startKey, [limit], [cursorMode]) → Array**
224
229
  ```javascript
225
230
  // Get keys starting from specific key
226
- const keys = dbi.keysFrom(42, 50); // 50 keys starting from 42
231
+ const keys = dbi.keysFrom(txn, 42, 50); // 50 keys starting from 42
227
232
 
228
233
  // With cursor mode
229
- const keys = dbi.keysFrom(42, 50, 'keyGreaterOrEqual');
234
+ const keys = dbi.keysFrom(txn, 42, 50, 'keyGreaterOrEqual');
230
235
 
231
236
  // BigInt keys
232
- const bigIntKeys = dbi.keysFrom(42n, 50);
237
+ const bigIntKeys = dbi.keysFrom(txn, 42n, 50);
233
238
 
234
239
  // Key equal mode (for multi-value databases)
235
- const equalKeys = dbi.keysFrom(5, 10, 'keyEqual');
240
+ const equalKeys = dbi.keysFrom(txn, 5, 10, 'keyEqual');
236
241
  ```
237
242
 
238
- **query(requests) → Promise<Array>** (Async batch operations)
243
+ **drop(txn, [delete_db]) → void**
239
244
  ```javascript
240
- dbi.forEach((key, value, index) => {
241
- console.log(`${key}: ${value}`);
242
- // return false; // continue iteration (or undefined)
243
- // return true; // stop iteration
244
- });
245
+ // Clear database contents (keep structure)
246
+ dbi.drop(txn, false);
247
+
248
+ // Delete database completely
249
+ dbi.drop(txn, true);
250
+
251
+ // Default behavior (clear contents)
252
+ dbi.drop(txn);
245
253
  ```
246
254
 
247
255
  ## Key and Value Types
@@ -280,7 +288,7 @@ function syncExample() {
280
288
  const dbi = writeTxn.createMap(MDBX_Param.keyMode.ordinal);
281
289
 
282
290
  for (let i = 0; i < 1000; i++) {
283
- dbi.put(i, `value_${i}`);
291
+ dbi.put(writeTxn, i, `value_${i}`);
284
292
  }
285
293
  writeTxn.commit();
286
294
 
@@ -288,17 +296,17 @@ function syncExample() {
288
296
  const readTxn = env.startRead();
289
297
  const readDbi = readTxn.openMap(MDBX_Param.keyMode.ordinal); // keys as numbers
290
298
 
291
- const value = readDbi.get(42);
299
+ const value = readDbi.get(readTxn, 42);
292
300
  console.log(value); // "value_42"
293
301
 
294
302
  // Iterate with cursor
295
- readDbi.forEach((key, value, index) => {
303
+ readDbi.forEach(readTxn, (key, value, index) => {
296
304
  console.log(`Key ${key} (type: ${typeof key}): ${value}`); // key is number
297
305
  return index >= 9; // stop after 10 items (indices 0-9)
298
306
  });
299
307
 
300
308
  // Get specific keys
301
- const someKeys = readDbi.keysFrom(100, { limit: 50 });
309
+ const someKeys = readDbi.keysFrom(readTxn, 100, 50);
302
310
  console.log(`Keys 100-149:`, someKeys); // array of numbers
303
311
 
304
312
  readTxn.commit();
@@ -316,7 +324,7 @@ async function asyncExample() {
316
324
  const dbi = writeTxn.createMap(MDBX_Param.keyMode.ordinal);
317
325
 
318
326
  for (let i = 0; i < 1000; i++) {
319
- dbi.put(i, `value_${i}`);
327
+ dbi.put(writeTxn, i, `value_${i}`);
320
328
  }
321
329
  writeTxn.commit();
322
330
 
@@ -324,17 +332,17 @@ async function asyncExample() {
324
332
  const readTxn = env.startRead();
325
333
  const readDbi = readTxn.openMap(BigInt(MDBX_Param.keyMode.ordinal)); // keys as BigInts
326
334
 
327
- const value = readDbi.get(42);
335
+ const value = readDbi.get(readTxn, 42);
328
336
  console.log(value); // "value_42"
329
337
 
330
338
  // Iterate with BigInt keys
331
- readDbi.forEach((key, value, index) => {
339
+ readDbi.forEach(readTxn, (key, value, index) => {
332
340
  console.log(`Key ${key} (type: ${typeof key}): ${value}`); // key is bigint
333
341
  return index >= 9; // stop after 10 items (indices 0-9)
334
342
  });
335
343
 
336
344
  // Get BigInt keys
337
- const bigIntKeys = readDbi.keysFrom(100n, { limit: 50 });
345
+ const bigIntKeys = readDbi.keysFrom(readTxn, 100n, 50);
338
346
  console.log(`Keys 100n-149n:`, bigIntKeys); // array of BigInts
339
347
 
340
348
  readTxn.commit();
@@ -355,16 +363,16 @@ function keyTypesExample() {
355
363
  const dbi = txn.createMap(MDBX_Param.keyMode.ordinal);
356
364
 
357
365
  // Store some data
358
- dbi.put(1, "one");
359
- dbi.put(2, "two");
360
- dbi.put(3, "three");
366
+ dbi.put(txn, 1, "one");
367
+ dbi.put(txn, 2, "two");
368
+ dbi.put(txn, 3, "three");
361
369
  txn.commit();
362
370
 
363
371
  // Read with number keyMode
364
372
  const readTxn1 = env.startRead();
365
373
  const numberDbi = readTxn1.openMap(MDBX_Param.keyMode.ordinal);
366
374
 
367
- numberDbi.forEach((key, value) => {
375
+ numberDbi.forEach(readTxn1, (key, value) => {
368
376
  console.log(`Number key: ${key} (${typeof key})`); // number
369
377
  // return undefined; // continue iteration (default)
370
378
  });
@@ -374,7 +382,7 @@ function keyTypesExample() {
374
382
  const readTxn2 = env.startRead();
375
383
  const bigintDbi = readTxn2.openMap(BigInt(MDBX_Param.keyMode.ordinal));
376
384
 
377
- bigintDbi.forEach((key, value) => {
385
+ bigintDbi.forEach(readTxn2, (key, value) => {
378
386
  console.log(`BigInt key: ${key} (${typeof key})`); // bigint
379
387
  // return false; // continue iteration
380
388
  });
@@ -398,7 +406,7 @@ function cursorExample() {
398
406
 
399
407
  // Store test data
400
408
  for (let i = 0; i < 100; i++) {
401
- dbi.put(i, `value_${i}`);
409
+ dbi.put(txn, i, `value_${i}`);
402
410
  }
403
411
  txn.commit();
404
412
 
@@ -406,25 +414,25 @@ function cursorExample() {
406
414
  const readDbi = readTxn.openMap(MDBX_Param.keyMode.ordinal);
407
415
 
408
416
  // Get all keys
409
- const allKeys = readDbi.keys();
417
+ const allKeys = readDbi.keys(readTxn);
410
418
  console.log(`Total keys: ${allKeys.length}`);
411
419
 
412
420
  // Get limited keys - use keysFrom with limit
413
- const firstTen = readDbi.keysFrom(0, 10);
421
+ const firstTen = readDbi.keysFrom(readTxn, 0, 10);
414
422
  console.log(`First 10 keys:`, firstTen);
415
423
 
416
424
  // Get keys from specific position
417
- const fromFifty = readDbi.keysFrom(50, 20);
425
+ const fromFifty = readDbi.keysFrom(readTxn, 50, 20);
418
426
  console.log(`Keys 50-69:`, fromFifty);
419
427
 
420
428
  // Reverse iteration - need manual logic or forEach
421
- const allKeysForReverse = readDbi.keys();
429
+ const allKeysForReverse = readDbi.keys(readTxn);
422
430
  const lastTen = allKeysForReverse.slice(-10).reverse();
423
431
  console.log(`Last 10 keys:`, lastTen);
424
432
 
425
433
  // Manual iteration with forEach
426
434
  let count = 0;
427
- readDbi.forEach((key, value, index) => {
435
+ readDbi.forEach(readTxn, (key, value, index) => {
428
436
  if (key >= 80) {
429
437
  console.log(`Key ${key}: ${value}`);
430
438
  count++;
@@ -437,52 +445,6 @@ function cursorExample() {
437
445
  }
438
446
  ```
439
447
 
440
- ### Batch Operations (Asynchronous)
441
-
442
- ```javascript
443
- const { MDBX_Async_Env } = require('mdbxmou/lib/mdbx_evn_async');
444
- const { MDBX_Param } = require('mdbxmou');
445
-
446
- async function batchExample() {
447
- const env = new MDBX_Async_Env();
448
- await env.open({ path: './async-data' });
449
-
450
- // Write transaction in worker thread
451
- const writeTxn = await env.startWrite();
452
- const dbi = await writeTxn.openMap({
453
- keyMode: MDBX_Param.keyMode.ordinal,
454
- create: true
455
- });
456
-
457
- // Batch write
458
- await dbi.putBatch([
459
- { key: 1n, value: "one" },
460
- { key: 2n, value: "two" },
461
- { key: 3n, value: "three" }
462
- ]);
463
-
464
- await writeTxn.commit();
465
-
466
- // Read transaction in worker thread
467
- const readTxn = await env.startRead();
468
- const readDbi = await readTxn.openMap({
469
- keyMode: MDBX_Param.keyMode.ordinal
470
- });
471
-
472
- // Batch read
473
- const results = await readDbi.getBatch([1n, 2n, 3n]);
474
- results.forEach((result, i) => {
475
- if (result.found) {
476
- console.log(`Key ${result.key}: ${result.value}`);
477
- }
478
- });
479
-
480
- await readTxn.commit();
481
- await env.close();
482
- await env.terminate();
483
- }
484
- ```
485
-
486
448
  ### Query API (Advanced Async)
487
449
 
488
450
  ```javascript
@@ -519,54 +481,6 @@ async function queryExample() {
519
481
  }
520
482
  ```
521
483
 
522
- ### Worker Thread Example
523
-
524
- ```javascript
525
- const { MDBX_Async_Env } = require('mdbxmou/lib/mdbx_evn_async');
526
-
527
- async function workerExample() {
528
- // Single worker thread for all async operations
529
- const env = new MDBX_Async_Env();
530
- await env.open({ path: './worker-data' });
531
-
532
- // Each transaction runs in the same worker thread
533
- const txn1 = await env.startWrite();
534
- const dbi1 = await txn1.openMap({
535
- keyMode: MDBX_Param.keyMode.ordinal,
536
- create: true
537
- });
538
- await dbi1.put(1n, "worker-value-1");
539
- await txn1.commit();
540
-
541
- // Another transaction in the same worker
542
- const txn2 = await env.startWrite();
543
- const dbi2 = await txn2.openMap({
544
- keyMode: MDBX_Param.keyMode.ordinal,
545
- create: true
546
- });
547
- await dbi2.put(2n, "worker-value-2");
548
- await txn2.commit();
549
-
550
- // Read transaction
551
- const readTxn = await env.startRead();
552
- const readDbi = await readTxn.openMap({
553
- keyMode: MDBX_Param.keyMode.ordinal
554
- });
555
-
556
- const results = await readDbi.getBatch([1n, 2n]);
557
- results.forEach(r => {
558
- if (r.found) {
559
- console.log(`Key ${r.key}: ${r.value}`);
560
- }
561
- });
562
-
563
- await readTxn.commit();
564
-
565
- await env.close();
566
- await env.terminate(); // Terminate the worker thread
567
- }
568
- ```
569
-
570
484
  ## Error Handling
571
485
 
572
486
  ```javascript
@@ -578,7 +492,7 @@ try {
578
492
  const dbi = txn.createMap(MDBX_Param.keyMode.ordinal);
579
493
 
580
494
  // This might throw if key already exists with MDBX_NOOVERWRITE
581
- dbi.put(123, "value", MDBX_Param.putFlag.nooverwrite);
495
+ dbi.put(txn, 123, "value", MDBX_Param.putFlag.nooverwrite);
582
496
 
583
497
  txn.commit();
584
498
  } catch (error) {
@@ -624,10 +538,10 @@ Note: For ordinal (integer) keys, use keyFlag.number or keyFlag.bigint to specif
624
538
  ## Performance Tips
625
539
 
626
540
  1. **Use ordinal keys** for integer data - much faster than string keys
627
- 2. **Batch operations** - Use query API or async worker thread for bulk operations
541
+ 2. **Batch operations** - Use query API for bulk operations
628
542
  3. **Reuse transactions** - Keep read transactions open for multiple operations
629
- 4. **Worker thread** - Use async API for CPU-intensive or I/O operations
630
- 5. **Memory mapping** - MDBX uses memory-mapped files for zero-copy access
543
+ 4. **Memory mapping** - MDBX uses memory-mapped files for zero-copy access
544
+ 5. **Transaction scope** - Always pass transaction object to DBI methods
631
545
 
632
546
  ## License
633
547
 
package/package.json CHANGED
@@ -16,17 +16,16 @@
16
16
  },
17
17
  "scripts": {
18
18
  "e3": "node ./test/e3.js",
19
+ "e33": "node ./test/e33.js",
19
20
  "e4": "node ./test/e4.js",
20
21
  "e5": "node ./test/e5.js",
21
- "test": "node ./test/e3.js && node ./test/e4.js && node ./test/e5.js",
22
22
  "build": "node build.js",
23
- "install-1": "git submodule update --init --recursive 2>/dev/null || true && node build.js",
24
- "install": "node build.js",
25
- "!prepublishOnly": "./prepare-npm.sh"
23
+ "build-dev": "node build-dev.js",
24
+ "install": "node build.js"
26
25
  },
27
26
  "gypfile": true,
28
27
  "name": "mdbxmou",
29
- "version": "0.1.35",
28
+ "version": "0.2.0",
30
29
  "description": "Node bindings for mdbx",
31
30
  "repository": {
32
31
  "type": "git",
package/src/dbi.cpp ADDED
@@ -0,0 +1,66 @@
1
+ #include "dbi.hpp"
2
+
3
+ namespace mdbxmou {
4
+
5
+ MDBX_stat dbi::get_stat(const MDBX_txn* txn, MDBX_dbi dbi)
6
+ {
7
+ MDBX_stat stat;
8
+ auto rc = mdbx_dbi_stat(txn, dbi, &stat, sizeof(stat));
9
+ if (rc != MDBX_SUCCESS) {
10
+ throw std::runtime_error(mdbx_strerror(rc));
11
+ }
12
+ return stat;
13
+ }
14
+
15
+ valuemou dbi::get(const MDBX_txn* txn, const keymou& key) const
16
+ {
17
+ valuemou val{};
18
+ auto rc = mdbx_get(txn, id_, key, val);
19
+ if (rc == MDBX_NOTFOUND)
20
+ return {};
21
+ if (rc != MDBX_SUCCESS) {
22
+ throw std::runtime_error(mdbx_strerror(rc));
23
+ }
24
+ return val;
25
+ }
26
+
27
+ void dbi::put(MDBX_txn* txn, const keymou& key, valuemou& value, MDBX_put_flags_t flags)
28
+ {
29
+ auto rc = mdbx_put(txn, id_, key, value, flags);
30
+ if (rc != MDBX_SUCCESS) {
31
+ throw std::runtime_error(mdbx_strerror(rc));
32
+ }
33
+ }
34
+
35
+ bool dbi::del(MDBX_txn* txn, const keymou& key)
36
+ {
37
+ auto rc = mdbx_del(txn, id_, key, nullptr);
38
+ if (rc == MDBX_NOTFOUND) {
39
+ return false;
40
+ }
41
+ if (rc != MDBX_SUCCESS) {
42
+ throw std::runtime_error(mdbx_strerror(rc));
43
+ }
44
+ return true;
45
+ }
46
+
47
+
48
+ cursormou_managed dbi::open_cursor(MDBX_txn* txn) const
49
+ {
50
+ MDBX_cursor* cursor_ptr;
51
+ auto rc = mdbx_cursor_open(txn, id_, &cursor_ptr);
52
+ if (rc != MDBX_SUCCESS) {
53
+ throw std::runtime_error(mdbx_strerror(rc));
54
+ }
55
+ return cursormou_managed{ cursor_ptr };
56
+ }
57
+
58
+ void dbi::drop(MDBX_txn* txn, bool delete_db)
59
+ {
60
+ auto rc = mdbx_drop(txn, id_, delete_db);
61
+ if (rc != MDBX_SUCCESS) {
62
+ throw std::runtime_error(mdbx_strerror(rc));
63
+ }
64
+ }
65
+
66
+ } // namespace mdbxmou
package/src/dbi.hpp ADDED
@@ -0,0 +1,45 @@
1
+ #pragma once
2
+
3
+ #include "valuemou.hpp"
4
+ #include <memory>
5
+
6
+ namespace mdbxmou {
7
+
8
+ class dbi
9
+ {
10
+ protected:
11
+ MDBX_dbi id_{};
12
+
13
+ public:
14
+ dbi() = default;
15
+
16
+ void attach(MDBX_dbi id) noexcept {
17
+ id_ = id;
18
+ }
19
+
20
+ static MDBX_stat get_stat(const MDBX_txn* txn, MDBX_dbi dbi);
21
+
22
+ MDBX_stat get_stat(const MDBX_txn* txn) const
23
+ {
24
+ return get_stat(txn, id_);
25
+ }
26
+
27
+ valuemou get(const MDBX_txn* txn, const keymou& key) const;
28
+
29
+ bool has(const MDBX_txn* txn, const keymou& key) const
30
+ {
31
+ return !get(txn, key).is_null();
32
+ }
33
+
34
+ void put(MDBX_txn* txn, const keymou& key, valuemou& value,
35
+ MDBX_put_flags_t flags = MDBX_UPSERT);
36
+
37
+ bool del(MDBX_txn* txn, const keymou& key);
38
+
39
+ cursormou_managed open_cursor(MDBX_txn* txn) const;
40
+
41
+ // Drop database or delete it
42
+ void drop(MDBX_txn* txn, bool delete_db = false);
43
+ };
44
+
45
+ } // namespace mdbxmou
package/src/dbimou.cpp CHANGED
@@ -18,30 +18,41 @@ void dbimou::init(const char *class_name, Napi::Env env)
18
18
  InstanceMethod("stat", &dbimou::stat),
19
19
  InstanceMethod("keys", &dbimou::keys),
20
20
  InstanceMethod("keysFrom", &dbimou::keys_from),
21
+ InstanceMethod("drop", &dbimou::drop),
22
+
23
+ // Свойства только для чтения
24
+ InstanceAccessor("id", &dbimou::get_id, nullptr),
25
+ InstanceAccessor("dbMode", &dbimou::get_mode, nullptr),
26
+ InstanceAccessor("keyMode", &dbimou::get_key_mode, nullptr),
27
+ InstanceAccessor("valueMode", &dbimou::get_value_mode, nullptr),
28
+ InstanceAccessor("keyFlag", &dbimou::get_key_flag, nullptr),
29
+ InstanceAccessor("valueFlag", &dbimou::get_value_flag, nullptr),
21
30
  });
22
31
 
23
32
  ctor = Napi::Persistent(func);
24
33
  ctor.SuppressDestruct();
25
34
  }
26
35
 
27
- Napi::Value dbimou::put(const Napi::CallbackInfo& info) {
36
+ Napi::Value dbimou::put(const Napi::CallbackInfo& info)
37
+ {
28
38
  Napi::Env env = info.Env();
29
-
30
39
  auto arg_len = info.Length();
31
- if (arg_len < 2) {
32
- throw Napi::Error::New(env, "put: key and value required");
40
+ if (arg_len < 3) {
41
+ throw Napi::Error::New(env, "put: txnmou, key and value required");
33
42
  }
34
-
43
+ auto arg0 = info[0].As<Napi::Object>();
44
+ // Дополнительная проверка - есть ли у объекта нужный constructor
45
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
46
+ throw Napi::TypeError::New(env, "put: first argument must be MDBX_Txn instance");
47
+ }
48
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
35
49
  try {
50
+ std::uint64_t t;
36
51
  auto key = (key_mode_.val & key_mode::ordinal) ?
37
- keymou::from(info[0], env, id_buf_) :
38
- keymou::from(info[0], env, key_buf_);
39
- auto val = valuemou::from(info[1], env, val_buf_);
40
- auto flag = static_cast<MDBX_put_flags_t>(key_mode_.val & value_mode_.val);
41
- auto rc = mdbx_put(*txn_, dbi_, key, val, flag);
42
- if (rc != MDBX_SUCCESS) {
43
- throw Napi::Error::New(env, mdbx_strerror(rc));
44
- }
52
+ keymou::from(info[1], env, t) :
53
+ keymou::from(info[1], env, key_buf_);
54
+ auto val = valuemou::from(info[2], env, val_buf_);
55
+ dbi::put(*txn, key, val, *this);
45
56
  } catch (const std::exception& e) {
46
57
  throw Napi::Error::New(env, std::string("put: ") + e.what());
47
58
  }
@@ -51,22 +62,28 @@ Napi::Value dbimou::put(const Napi::CallbackInfo& info) {
51
62
 
52
63
  Napi::Value dbimou::get(const Napi::CallbackInfo& info) {
53
64
  Napi::Env env = info.Env();
54
-
55
- if (info.Length() < 1) {
56
- throw Napi::Error::New(env, "get: key required");
65
+ auto arg_len = info.Length();
66
+ if (arg_len < 2) {
67
+ throw Napi::Error::New(env, "get: txnmou and key required");
57
68
  }
69
+ auto arg0 = info[0].As<Napi::Object>();
70
+ // Дополнительная проверка - есть ли у объекта нужный constructor
71
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
72
+ throw Napi::TypeError::New(env, "get: first argument must be MDBX_Txn instance");
73
+ }
74
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
58
75
 
59
76
  try {
60
- valuemou val{};
77
+ std::uint64_t t;
61
78
  auto key = (key_mode_.val & key_mode::ordinal) ?
62
- keymou::from(info[0], env, id_buf_) :
63
- keymou::from(info[0], env, key_buf_);
64
- auto rc = mdbx_get(*txn_, dbi_, key, val);
65
- if (rc == MDBX_NOTFOUND)
79
+ keymou::from(info[1], env, t) :
80
+ keymou::from(info[1], env, key_buf_);
81
+
82
+ auto val = dbi::get(*txn, key);
83
+ if (val.is_null()) {
66
84
  return env.Undefined();
67
- if (rc != MDBX_SUCCESS) {
68
- throw Napi::Error::New(env, mdbx_strerror(rc));
69
85
  }
86
+
70
87
  return (value_flag_.val & base_flag::string) ?
71
88
  val.to_string(env) : val.to_buffer(env);
72
89
  } catch (const std::exception& e) {
@@ -78,23 +95,25 @@ Napi::Value dbimou::get(const Napi::CallbackInfo& info) {
78
95
 
79
96
  Napi::Value dbimou::del(const Napi::CallbackInfo& info) {
80
97
  Napi::Env env = info.Env();
81
-
82
- if (info.Length() < 1) {
83
- throw Napi::Error::New(env, "del: key required");
98
+ auto arg_len = info.Length();
99
+ if (arg_len < 2) {
100
+ throw Napi::Error::New(env, "del: txnmou and key required");
84
101
  }
102
+ auto arg0 = info[0].As<Napi::Object>();
103
+ // Дополнительная проверка - есть ли у объекта нужный constructor
104
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
105
+ throw Napi::TypeError::New(env, "del: first argument must be MDBX_Txn instance");
106
+ }
107
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
85
108
 
86
109
  try {
110
+ std::uint64_t t;
87
111
  auto key = (key_mode_.val & key_mode::ordinal) ?
88
- keymou::from(info[0], env, id_buf_) :
89
- keymou::from(info[0], env, key_buf_);
90
- auto rc = mdbx_del(*txn_, dbi_, key, nullptr);
91
- if (rc == MDBX_NOTFOUND) {
92
- return Napi::Value::From(env, false);
93
- }
94
- if (rc != MDBX_SUCCESS) {
95
- throw Napi::Error::New(env, mdbx_strerror(rc));
96
- }
97
- return Napi::Value::From(env, true);
112
+ keymou::from(info[1], env, t) :
113
+ keymou::from(info[1], env, key_buf_);
114
+
115
+ bool result = dbi::del(*txn, key);
116
+ return Napi::Value::From(env, result);
98
117
  } catch (const std::exception& e) {
99
118
  throw Napi::Error::New(env, std::string("del: ") + e.what());
100
119
  }
@@ -104,23 +123,25 @@ Napi::Value dbimou::del(const Napi::CallbackInfo& info) {
104
123
 
105
124
  Napi::Value dbimou::has(const Napi::CallbackInfo& info) {
106
125
  Napi::Env env = info.Env();
107
-
108
- if (info.Length() < 1) {
109
- throw Napi::Error::New(env, "Key is required");
126
+ auto arg_len = info.Length();
127
+ if (arg_len < 2) {
128
+ throw Napi::Error::New(env, "has: txnmou and key required");
110
129
  }
130
+ auto arg0 = info[0].As<Napi::Object>();
131
+ // Дополнительная проверка - есть ли у объекта нужный constructor
132
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
133
+ throw Napi::TypeError::New(env, "has: first argument must be MDBX_Txn instance");
134
+ }
135
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
111
136
 
112
137
  try {
138
+ std::uint64_t t;
113
139
  auto key = (key_mode_.val & key_mode::ordinal) ?
114
- keymou::from(info[0], env, id_buf_) :
115
- keymou::from(info[0], env, key_buf_);
116
- auto rc = mdbx_get(*txn_, dbi_, key, nullptr);
117
- if (rc == MDBX_NOTFOUND) {
118
- return Napi::Value::From(env, false);
119
- }
120
- if (rc != MDBX_SUCCESS) {
121
- throw Napi::Error::New(env, mdbx_strerror(rc));
122
- }
123
- return Napi::Value::From(env, true);
140
+ keymou::from(info[1], env, t) :
141
+ keymou::from(info[1], env, key_buf_);
142
+
143
+ bool result = dbi::has(*txn, key);
144
+ return Napi::Value::From(env, result);
124
145
  } catch (const std::exception& e) {
125
146
  throw Napi::Error::New(env, std::string("has: ") + e.what());
126
147
  }
@@ -130,26 +151,26 @@ Napi::Value dbimou::has(const Napi::CallbackInfo& info) {
130
151
 
131
152
  Napi::Value dbimou::for_each(const Napi::CallbackInfo& info) {
132
153
  Napi::Env env = info.Env();
133
-
134
- if (info.Length() < 1) {
135
- throw Napi::TypeError::New(env, "Expected at least one argument");
154
+ auto arg_len = info.Length();
155
+ if (arg_len < 2) {
156
+ throw Napi::Error::New(env, "for_each: txnmou and function required");
136
157
  }
137
-
138
- // Проверяем тип вызова: forEach(fn), forEach(fromKey, fn) или forEach(fromKey, cursorMode, fn)
139
- if (info.Length() == 1 && info[0].IsFunction()) {
140
- // Обычный forEach(fn)
141
- auto fn = info[0].As<Napi::Function>();
142
-
143
- MDBX_cursor* cursor_ptr;
144
- auto rc = mdbx_cursor_open(*txn_, dbi_, &cursor_ptr);
145
- if (rc != MDBX_SUCCESS) {
146
- throw Napi::Error::New(env, mdbx_strerror(rc));
147
- }
158
+ auto arg0 = info[0].As<Napi::Object>();
159
+ // Дополнительная проверка - есть ли у объекта нужный constructor
160
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
161
+ throw Napi::TypeError::New(env, "for_each: first argument must be MDBX_Txn instance");
162
+ }
163
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
164
+
165
+ // Проверяем тип вызова: forEach(txn, fn), forEach(txn, fromKey, fn) или forEach(txn, fromKey, cursorMode, fn)
166
+ if (info.Length() == 2 && info[1].IsFunction()) {
167
+ // Обычный forEach(txn, fn)
168
+ auto fn = info[1].As<Napi::Function>();
148
169
 
149
- uint32_t index{};
150
-
151
170
  try {
152
- cursormou_managed cursor{ cursor_ptr };
171
+ auto cursor = dbi::open_cursor(*txn);
172
+ uint32_t index{};
173
+
153
174
  if (key_mode_.val & key_mode::ordinal) {
154
175
  cursor.scan([&](const mdbx::pair& f) {
155
176
  keymou key{f.key};
@@ -190,56 +211,63 @@ Napi::Value dbimou::for_each(const Napi::CallbackInfo& info) {
190
211
  return result.IsBoolean() ? result.ToBoolean() : false;
191
212
  });
192
213
  }
214
+
215
+ return Napi::Number::New(env, static_cast<double>(index));
193
216
  } catch (const std::exception& e) {
194
217
  throw Napi::Error::New(env, std::string("forEach: ") + e.what());
195
218
  }
196
219
 
197
- return Napi::Number::New(env, static_cast<double>(index));
198
-
199
- } else if ((info.Length() == 2 && info[1].IsFunction()) ||
200
- (info.Length() == 3 && info[2].IsFunction())) {
201
- // forEach(fromKey, fn) или forEach(fromKey, cursorMode, fn) - делегируем внутреннему методу
220
+ } else if ((info.Length() == 3 && info[2].IsFunction()) ||
221
+ (info.Length() == 4 && info[3].IsFunction())) {
222
+ // forEach(txn, fromKey, fn) или forEach(txn, fromKey, cursorMode, fn) - делегируем внутреннему методу
202
223
  return for_each_from(info);
203
224
 
204
225
  } else {
205
- throw Napi::TypeError::New(env, "Expected function, (key, function) or (key, cursorMode, function)");
226
+ throw Napi::TypeError::New(env, "Expected (txn, function), (txn, key, function) or (txn, key, cursorMode, function)");
206
227
  }
207
228
  }
208
229
 
209
230
  Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
210
231
  Napi::Env env = info.Env();
232
+ auto arg_len = info.Length();
233
+
234
+ if (arg_len < 3) {
235
+ throw Napi::Error::New(env, "for_each_from: txnmou, fromKey and function required");
236
+ }
237
+
238
+ auto arg0 = info[0].As<Napi::Object>();
239
+ // Дополнительная проверка - есть ли у объекта нужный constructor
240
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
241
+ throw Napi::TypeError::New(env, "for_each_from: first argument must be MDBX_Txn instance");
242
+ }
243
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
211
244
 
212
- // Определяем позицию функции: info[1] или info[2]
245
+ // Определяем позицию функции: info[2] или info[3]
213
246
  Napi::Function fn;
214
- if (info.Length() == 2 && info[1].IsFunction()) {
215
- fn = info[1].As<Napi::Function>();
216
- } else if (info.Length() == 3 && info[2].IsFunction()) {
247
+ if (info.Length() == 3 && info[2].IsFunction()) {
217
248
  fn = info[2].As<Napi::Function>();
249
+ } else if (info.Length() == 4 && info[3].IsFunction()) {
250
+ fn = info[3].As<Napi::Function>();
218
251
  } else {
219
252
  throw Napi::TypeError::New(env, "Function argument required");
220
253
  }
221
254
 
222
- MDBX_cursor* cursor_ptr;
223
- auto rc = mdbx_cursor_open(*txn_, dbi_, &cursor_ptr);
224
- if (rc != MDBX_SUCCESS) {
225
- throw Napi::Error::New(env, mdbx_strerror(rc));
226
- }
227
-
228
255
  try {
229
- cursormou_managed cursor{ cursor_ptr };
256
+ auto cursor = dbi::open_cursor(*txn);
230
257
 
231
258
  // Парсим начальный ключ
232
- keymou from_key = mdbx::is_ordinal(key_mode_) ?
233
- keymou::from(info[0], env, id_buf_) :
234
- keymou::from(info[0], env, key_buf_);
259
+ std::uint64_t t;
260
+ keymou from_key = (key_mode_.val & key_mode::ordinal) ?
261
+ keymou::from(info[1], env, t) :
262
+ keymou::from(info[1], env, key_buf_);
235
263
 
236
264
  // Парсим cursor mode (если передан)
237
265
  using move_operation = mdbx::cursor::move_operation;
238
266
  auto cursor_mode = move_operation::key_greater_or_equal;
239
267
  auto turn_mode = move_operation::next;
240
268
 
241
- if (info.Length() == 3 && !info[1].IsUndefined()) {
242
- cursor_mode = parse_cursor_mode(info[1]);
269
+ if (info.Length() == 4 && !info[2].IsUndefined()) {
270
+ cursor_mode = parse_cursor_mode(info[2]);
243
271
 
244
272
  // Определяем направление сканирования на основе операции
245
273
  switch (cursor_mode) {
@@ -268,7 +296,7 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
268
296
  keymou key{f.key};
269
297
  valuemou val{f.value};
270
298
  if (is_key_equal_mode) {
271
- if (id_buf_ != key.as_int64()) {
299
+ if (t != key.as_int64()) {
272
300
  return true; // останавливаем сканирование
273
301
  }
274
302
  }
@@ -318,15 +346,25 @@ Napi::Value dbimou::for_each_from(const Napi::CallbackInfo& info) {
318
346
 
319
347
  return Napi::Number::New(env, static_cast<double>(index));
320
348
  } catch (const std::exception& e) {
321
- throw Napi::Error::New(env, std::string("forEach: ") + e.what());
349
+ throw Napi::Error::New(env, std::string("forEach_from: ") + e.what());
322
350
  }
323
351
  }
324
352
 
325
353
  Napi::Value dbimou::stat(const Napi::CallbackInfo& info) {
326
354
  Napi::Env env = info.Env();
355
+ auto arg_len = info.Length();
356
+ if (arg_len < 1) {
357
+ throw Napi::Error::New(env, "stat: txnmou required");
358
+ }
359
+ auto arg0 = info[0].As<Napi::Object>();
360
+ // Дополнительная проверка - есть ли у объекта нужный constructor
361
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
362
+ throw Napi::TypeError::New(env, "stat: first argument must be MDBX_Txn instance");
363
+ }
364
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
327
365
 
328
366
  try {
329
- auto stat = get_stat(*txn_, dbi_);
367
+ auto stat = dbi::get_stat(*txn);
330
368
 
331
369
  Napi::Object result = Napi::Object::New(env);
332
370
 
@@ -352,16 +390,20 @@ Napi::Value dbimou::stat(const Napi::CallbackInfo& info) {
352
390
 
353
391
  Napi::Value dbimou::keys(const Napi::CallbackInfo& info) {
354
392
  Napi::Env env = info.Env();
355
-
356
- MDBX_cursor* cursor_ptr;
357
- auto rc = mdbx_cursor_open(*txn_, dbi_, &cursor_ptr);
358
- if (rc != MDBX_SUCCESS) {
359
- throw Napi::Error::New(env, mdbx_strerror(rc));
393
+ auto arg_len = info.Length();
394
+ if (arg_len < 1) {
395
+ throw Napi::Error::New(env, "keys: txnmou required");
360
396
  }
361
-
397
+ auto arg0 = info[0].As<Napi::Object>();
398
+ // Дополнительная проверка - есть ли у объекта нужный constructor
399
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
400
+ throw Napi::TypeError::New(env, "keys: first argument must be MDBX_Txn instance");
401
+ }
402
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
403
+
362
404
  try {
363
- cursormou_managed cursor{ cursor_ptr };
364
- auto stat = get_stat(*txn_, dbi_);
405
+ auto cursor = dbi::open_cursor(*txn);
406
+ auto stat = dbi::get_stat(*txn);
365
407
 
366
408
  // Создаем массив для ключей
367
409
  Napi::Array keys = Napi::Array::New(env, stat.ms_entries);
@@ -402,36 +444,37 @@ Napi::Value dbimou::keys(const Napi::CallbackInfo& info) {
402
444
 
403
445
  Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info) {
404
446
  Napi::Env env = info.Env();
405
-
406
- if (info.Length() < 1) {
407
- throw Napi::Error::New(env, "keysFrom: 'from' key required");
408
- }
409
-
410
- MDBX_cursor* cursor_ptr;
411
- auto rc = mdbx_cursor_open(*txn_, dbi_, &cursor_ptr);
412
- if (rc != MDBX_SUCCESS) {
413
- throw Napi::Error::New(env, mdbx_strerror(rc));
447
+ auto arg_len = info.Length();
448
+ if (arg_len < 2) {
449
+ throw Napi::Error::New(env, "keysFrom: txnmou and 'from' key required");
414
450
  }
451
+ auto arg0 = info[0].As<Napi::Object>();
452
+ // Дополнительная проверка - есть ли у объекта нужный constructor
453
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
454
+ throw Napi::TypeError::New(env, "keysFrom: first argument must be MDBX_Txn instance");
455
+ }
456
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
415
457
 
416
458
  try {
417
- cursormou_managed cursor{ cursor_ptr };
459
+ auto cursor = dbi::open_cursor(*txn);
418
460
 
419
- // Парсим аргументы: from, limit, cursorMode
420
- keymou from_key = mdbx::is_ordinal(key_mode_) ?
421
- keymou::from(info[0], env, id_buf_) :
422
- keymou::from(info[0], env, key_buf_);
461
+ // Парсим аргументы: txn, from, limit, cursorMode
462
+ std::uint64_t t;
463
+ keymou from_key = (key_mode_.val & key_mode::ordinal) ?
464
+ keymou::from(info[1], env, t) :
465
+ keymou::from(info[1], env, key_buf_);
423
466
 
424
467
  std::size_t count = SIZE_MAX;
425
- if (info.Length() > 1 && !info[1].IsUndefined()) {
426
- count = info[1].As<Napi::Number>().Uint32Value();
468
+ if (info.Length() > 2 && !info[2].IsUndefined()) {
469
+ count = info[2].As<Napi::Number>().Uint32Value();
427
470
  }
428
471
 
429
472
  using move_operation = mdbx::cursor::move_operation;
430
473
  auto cursor_mode = move_operation::key_greater_or_equal;
431
474
  auto turn_mode = move_operation::next;
432
475
 
433
- if (info.Length() > 2 && !info[2].IsUndefined()) {
434
- cursor_mode = parse_cursor_mode(info[2]);
476
+ if (info.Length() > 3 && !info[3].IsUndefined()) {
477
+ cursor_mode = parse_cursor_mode(info[3]);
435
478
 
436
479
  // Определяем направление сканирования на основе операции
437
480
  switch (cursor_mode) {
@@ -466,7 +509,7 @@ Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info) {
466
509
 
467
510
  keymou key{f.key};
468
511
  if (is_key_equal_mode) {
469
- if (id_buf_ != key.as_int64()) {
512
+ if (t != key.as_int64()) {
470
513
  return true; // останавливаем сканирование
471
514
  }
472
515
  }
@@ -510,13 +553,28 @@ Napi::Value dbimou::keys_from(const Napi::CallbackInfo& info) {
510
553
  return env.Undefined();
511
554
  }
512
555
 
513
- const env_arg0* dbimou::get_env_userctx(MDBX_env* e)
514
- {
515
- assert(e);
516
- auto rc = static_cast<env_arg0*>(mdbx_env_get_userctx(e));
517
- if (!rc)
518
- throw std::runtime_error("env: userctx not set");
519
- return rc;
520
- }
556
+ Napi::Value dbimou::drop(const Napi::CallbackInfo& info) {
557
+ Napi::Env env = info.Env();
558
+ if (info.Length() < 1) {
559
+ Napi::TypeError::New(env, "First argument must be a transaction").ThrowAsJavaScriptException();
560
+ return env.Undefined();
561
+ }
562
+ auto arg0 = info[0].As<Napi::Object>();
563
+ if (!arg0.InstanceOf(txnmou::ctor.Value())) {
564
+ Napi::TypeError::New(env, "First argument must be a txnmou instance").ThrowAsJavaScriptException();
565
+ return env.Undefined();
566
+ }
567
+ auto txn = Napi::ObjectWrap<txnmou>::Unwrap(arg0);
568
+ bool delete_db = false;
569
+ if (info.Length() > 1 && info[1].IsBoolean()) {
570
+ delete_db = info[1].As<Napi::Boolean>().Value();
571
+ }
572
+ try {
573
+ dbi::drop(*txn, delete_db);
574
+ } catch (const std::exception& e) {
575
+ throw Napi::Error::New(env, std::string("drop: ") + e.what());
576
+ }
577
+ return env.Undefined();
578
+ }
521
579
 
522
580
  } // namespace mdbxmou
package/src/dbimou.hpp CHANGED
@@ -1,6 +1,6 @@
1
1
  #pragma once
2
2
 
3
- #include "valuemou.hpp"
3
+ #include "dbi.hpp"
4
4
  #include "querymou.hpp"
5
5
  #include "env_arg0.hpp"
6
6
  #include <memory>
@@ -13,36 +13,50 @@ struct env_arg0;
13
13
 
14
14
  class dbimou final
15
15
  : public Napi::ObjectWrap<dbimou>
16
+ , public dbi
16
17
  {
17
- // мы должны отслеживать время жизни evn
18
- envmou* env_{nullptr};
19
- txnmou* txn_{nullptr};
20
- MDBX_dbi dbi_{};
21
18
  db_mode mode_{};
22
19
  key_mode key_mode_{};
23
20
  value_mode value_mode_{};
21
+
24
22
  base_flag key_flag_{};
25
23
  base_flag value_flag_{};
24
+
26
25
  buffer_type key_buf_{};
27
26
  buffer_type val_buf_{};
28
27
  std::uint64_t id_buf_{};
29
28
 
30
- public:
31
- static inline MDBX_stat get_stat(MDBX_txn* txn, MDBX_dbi dbi)
32
- {
33
- MDBX_stat stat;
34
- auto rc = mdbx_dbi_stat(txn, dbi, &stat, sizeof(stat));
35
- if (rc != MDBX_SUCCESS) {
36
- throw std::runtime_error(mdbx_strerror(rc));
37
- }
38
- return stat;
39
- }
40
-
29
+ public:
41
30
  static Napi::FunctionReference ctor;
42
31
 
43
32
  dbimou(const Napi::CallbackInfo& info)
44
- : Napi::ObjectWrap<dbimou>(info)
45
- { }
33
+ : Napi::ObjectWrap<dbimou>{info}
34
+ , dbi{}
35
+ { }
36
+
37
+ Napi::Value get_id(const Napi::CallbackInfo& info) {
38
+ return Napi::BigInt::New(info.Env(), static_cast<int64_t>(id_));
39
+ }
40
+
41
+ Napi::Value get_mode(const Napi::CallbackInfo& info) {
42
+ return Napi::Number::New(info.Env(), static_cast<double>(mode_.val));
43
+ }
44
+
45
+ Napi::Value get_key_mode(const Napi::CallbackInfo& info) {
46
+ return Napi::Number::New(info.Env(), static_cast<double>(key_mode_.val));
47
+ }
48
+
49
+ Napi::Value get_value_mode(const Napi::CallbackInfo& info) {
50
+ return Napi::Number::New(info.Env(), static_cast<double>(value_mode_.val));
51
+ }
52
+
53
+ Napi::Value get_key_flag(const Napi::CallbackInfo& info) {
54
+ return Napi::Number::New(info.Env(), static_cast<double>(key_flag_.val));
55
+ }
56
+
57
+ Napi::Value get_value_flag(const Napi::CallbackInfo& info) {
58
+ return Napi::Number::New(info.Env(), static_cast<double>(value_flag_.val));
59
+ }
46
60
 
47
61
  static void init(const char *class_name, Napi::Env env);
48
62
 
@@ -55,6 +69,7 @@ public:
55
69
  Napi::Value stat(const Napi::CallbackInfo&);
56
70
  Napi::Value keys(const Napi::CallbackInfo&);
57
71
  Napi::Value keys_from(const Napi::CallbackInfo&);
72
+ Napi::Value drop(const Napi::CallbackInfo& info);
58
73
 
59
74
  private:
60
75
  // Внутренний метод для forEach с начальным ключом
@@ -62,13 +77,11 @@ private:
62
77
 
63
78
  public:
64
79
 
65
- void attach(envmou* env, txnmou* txn, MDBX_dbi dbi,
66
- db_mode mode, key_mode key_mode, value_mode value_mode,
80
+ void attach(MDBX_dbi id, db_mode mode,
81
+ key_mode key_mode, value_mode value_mode,
67
82
  base_flag key_flag, base_flag value_flag)
68
83
  {
69
- env_ = env;
70
- txn_ = txn;
71
- dbi_ = dbi;
84
+ dbi::attach(id);
72
85
  mode_ = mode;
73
86
  key_mode_ = key_mode;
74
87
  value_mode_ = value_mode;
@@ -76,7 +89,9 @@ public:
76
89
  value_flag_ = value_flag;
77
90
  }
78
91
 
79
- static const env_arg0* get_env_userctx(MDBX_env* env_ptr);
92
+ operator MDBX_put_flags_t() const noexcept {
93
+ return static_cast<MDBX_put_flags_t>(key_mode_.val & value_mode_.val);
94
+ }
80
95
  };
81
96
 
82
97
  } // namespace mdbxmou
package/src/env_arg0.hpp CHANGED
@@ -18,4 +18,13 @@ struct env_arg0 {
18
18
  base_flag value_flag{};
19
19
  };
20
20
 
21
+ static inline const env_arg0* get_env_userctx(MDBX_env* env_ptr)
22
+ {
23
+ assert(env_ptr);
24
+ auto rc = static_cast<env_arg0*>(mdbx_env_get_userctx(env_ptr));
25
+ if (!rc)
26
+ throw std::runtime_error("env: userctx not set");
27
+ return rc;
28
+ }
29
+
21
30
  } // namespace mdbxmou
package/src/envmou.cpp CHANGED
@@ -386,7 +386,7 @@ Napi::Value envmou::query(const Napi::CallbackInfo& info)
386
386
 
387
387
  check();
388
388
 
389
- auto conf = dbimou::get_env_userctx(*this);
389
+ auto conf = get_env_userctx(*this);
390
390
 
391
391
  auto arg0 = info[0];
392
392
  query_request query = parse_query(mode,
@@ -429,7 +429,7 @@ Napi::Value envmou::keys(const Napi::CallbackInfo& info)
429
429
 
430
430
  check();
431
431
 
432
- auto conf = dbimou::get_env_userctx(*this);
432
+ auto conf = get_env_userctx(*this);
433
433
 
434
434
  auto arg0 = info[0];
435
435
  keys_request query = parse_keys(mode,
package/src/txnmou.cpp CHANGED
@@ -97,7 +97,7 @@ Napi::Value txnmou::get_dbi(const Napi::CallbackInfo& info, db_mode db_mode)
97
97
  key_mode key_mode{};
98
98
  value_mode value_mode{};
99
99
  std::string db_name{};
100
- auto conf = dbimou::get_env_userctx(*env_);
100
+ auto conf = get_env_userctx(*env_);
101
101
  auto key_flag = conf->key_flag;
102
102
  auto value_flag = conf->value_flag;
103
103
  auto arg_count = info.Length();
@@ -145,7 +145,7 @@ Napi::Value txnmou::get_dbi(const Napi::CallbackInfo& info, db_mode db_mode)
145
145
  if (rc != MDBX_SUCCESS) {
146
146
  throw std::runtime_error(std::string("mdbx_dbi_open ") + mdbx_strerror(rc));
147
147
  }
148
- ptr->attach(env_, this, dbi, db_mode, key_mode,
148
+ ptr->attach(dbi, db_mode, key_mode,
149
149
  value_mode, key_flag, value_flag);
150
150
  return obj;
151
151
  }
@@ -251,4 +251,12 @@ void txnmou::free_txn::operator()(MDBX_txn* txn) const noexcept {
251
251
  }
252
252
  }
253
253
 
254
+ void txnmou::drop(MDBX_dbi id, bool del)
255
+ {
256
+ auto rc = mdbx_drop(*this, id, del);
257
+ if (rc != MDBX_SUCCESS) {
258
+ throw std::runtime_error(mdbx_strerror(rc));
259
+ }
260
+ }
261
+
254
262
  } // namespace mdbxmou
package/src/txnmou.hpp CHANGED
@@ -117,6 +117,8 @@ public:
117
117
 
118
118
  void attach(envmou& env, MDBX_txn* txn,
119
119
  txn_mode mode, txnmou* parent = nullptr);
120
+
121
+ void drop(MDBX_dbi id, bool del);
120
122
  };
121
123
 
122
124
  } // namespace mdbxmou