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 +1 -0
- package/README.md +60 -146
- package/package.json +4 -5
- package/src/dbi.cpp +66 -0
- package/src/dbi.hpp +45 -0
- package/src/dbimou.cpp +184 -126
- package/src/dbimou.hpp +39 -24
- package/src/env_arg0.hpp +9 -0
- package/src/envmou.cpp +2 -2
- package/src/txnmou.cpp +10 -2
- package/src/txnmou.hpp +2 -0
package/CMakeLists.txt
CHANGED
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
|
|
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
|
|
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
|
-
**
|
|
200
|
+
**has(txn, key) → boolean**
|
|
201
201
|
```javascript
|
|
202
|
-
const
|
|
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
|
-
**
|
|
243
|
+
**drop(txn, [delete_db]) → void**
|
|
239
244
|
```javascript
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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,
|
|
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,
|
|
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
|
|
541
|
+
2. **Batch operations** - Use query API for bulk operations
|
|
628
542
|
3. **Reuse transactions** - Keep read transactions open for multiple operations
|
|
629
|
-
4. **
|
|
630
|
-
5. **
|
|
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
|
-
"
|
|
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.
|
|
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 <
|
|
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[
|
|
38
|
-
keymou::from(info[
|
|
39
|
-
auto val = valuemou::from(info[
|
|
40
|
-
|
|
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 (
|
|
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
|
-
|
|
77
|
+
std::uint64_t t;
|
|
61
78
|
auto key = (key_mode_.val & key_mode::ordinal) ?
|
|
62
|
-
keymou::from(info[
|
|
63
|
-
keymou::from(info[
|
|
64
|
-
|
|
65
|
-
|
|
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 (
|
|
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[
|
|
89
|
-
keymou::from(info[
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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 (
|
|
109
|
-
throw Napi::Error::New(env, "
|
|
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[
|
|
115
|
-
keymou::from(info[
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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 (
|
|
135
|
-
throw Napi::
|
|
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
|
-
//
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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[
|
|
245
|
+
// Определяем позицию функции: info[2] или info[3]
|
|
213
246
|
Napi::Function fn;
|
|
214
|
-
if (info.Length() ==
|
|
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
|
-
|
|
256
|
+
auto cursor = dbi::open_cursor(*txn);
|
|
230
257
|
|
|
231
258
|
// Парсим начальный ключ
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
keymou::from(info[
|
|
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() ==
|
|
242
|
-
cursor_mode = parse_cursor_mode(info[
|
|
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 (
|
|
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("
|
|
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(*
|
|
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
|
-
|
|
357
|
-
|
|
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
|
-
|
|
364
|
-
auto stat = get_stat(*
|
|
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 (
|
|
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
|
-
|
|
459
|
+
auto cursor = dbi::open_cursor(*txn);
|
|
418
460
|
|
|
419
|
-
// Парсим аргументы: from, limit, cursorMode
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
keymou::from(info[
|
|
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() >
|
|
426
|
-
count = info[
|
|
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() >
|
|
434
|
-
cursor_mode = parse_cursor_mode(info[
|
|
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 (
|
|
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
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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 "
|
|
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>
|
|
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(
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|