├── examples ├── testdata │ └── .gitignore ├── 7-largedb.js ├── 6-asyncio.js ├── 1-env.js ├── 9-unnamed-db.js ├── 11-createdbi-usertxn.js ├── 12-largedb-resize.js ├── 3-multiple-transactions.js ├── 2-datatypes.js ├── 4-cursors.js ├── 5-dupsort.js ├── 10-binkeycursors.js ├── 8-multiple-cursors-single-transaction.js └── advanced1-indexing.js ├── index.js ├── dependencies └── lmdb │ └── libraries │ └── liblmdb │ ├── .gitignore │ ├── tooltag │ ├── COPYRIGHT │ ├── mdb_copy.1 │ ├── sample-mdb.txt │ ├── mdb_stat.1 │ ├── mdb_copy.c │ ├── sample-bdb.txt │ ├── LICENSE │ ├── mdb_dump.1 │ ├── mdb_load.1 │ ├── Makefile │ ├── mtest2.c │ ├── mtest3.c │ ├── mtest5.c │ ├── mtest6.c │ ├── mtest4.c │ ├── mtest.c │ ├── midl.h │ ├── mdb_stat.c │ ├── mdb_dump.c │ ├── midl.c │ ├── intro.doc │ ├── CHANGES │ └── mdb_load.c ├── .gitignore ├── .jshintrc ├── LICENSE ├── package.json ├── binding.gyp ├── src ├── node-lmdb.cpp ├── dbi.cpp ├── misc.cpp ├── txn.cpp └── cursor.cpp ├── test └── cluster.js └── benchmark └── index.js /examples/testdata/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | * -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('bindings')('node-lmdb.node'); 4 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/.gitignore: -------------------------------------------------------------------------------- 1 | mtest 2 | mtest[23456] 3 | testdb 4 | mdb_copy 5 | mdb_stat 6 | mdb_dump 7 | mdb_load 8 | *.lo 9 | *.[ao] 10 | *.so 11 | *.exe 12 | *[~#] 13 | *.bak 14 | *.orig 15 | *.rej 16 | *.gcov 17 | *.gcda 18 | *.gcno 19 | core 20 | core.* 21 | valgrind.* 22 | man/ 23 | html/ 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Build and test data 3 | build 4 | prebuilds 5 | testdata 6 | test/testdata 7 | benchmark/benchdata 8 | 9 | # qmake autogenerated files 10 | moc_* 11 | qrc_* 12 | ui_* 13 | Makefile 14 | 15 | # Qt Creator's stuff 16 | *.pro.user 17 | qtc_packaging 18 | 19 | # Other generated files 20 | *.o 21 | *.slo 22 | *.lo 23 | *.core 24 | MANIFEST 25 | 26 | # gedit's temp files 27 | *~ 28 | .goutputstream* 29 | 30 | # Compiled Dynamic libraries 31 | *.so 32 | *.dylib 33 | 34 | # Compiled Static libraries 35 | *.lai 36 | *.la 37 | *.a 38 | 39 | # NPM dependencies 40 | node_modules/ 41 | 42 | # Visual Studio Code directory 43 | .vscode 44 | 45 | package-lock.json 46 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/tooltag: -------------------------------------------------------------------------------- 1 | 2 | 3 | mdb_copy_1 4 | mdb_copy - environment copy tool 5 | mdb_copy.1 6 | 7 | 8 | mdb_dump_1 9 | mdb_dump - environment export tool 10 | mdb_dump.1 11 | 12 | 13 | mdb_load_1 14 | mdb_load - environment import tool 15 | mdb_load.1 16 | 17 | 18 | mdb_stat_1 19 | mdb_stat - environment status tool 20 | mdb_stat.1 21 | 22 | 23 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright 2011-2019 Howard Chu, Symas Corp. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted only as authorized by the OpenLDAP 6 | Public License. 7 | 8 | A copy of this license is available in the file LICENSE in the 9 | top-level directory of the distribution or, alternatively, at 10 | . 11 | 12 | OpenLDAP is a registered trademark of the OpenLDAP Foundation. 13 | 14 | Individual files and/or contributed packages may be copyright by 15 | other parties and/or subject to additional restrictions. 16 | 17 | This work also contains materials derived from public sources. 18 | 19 | Additional information about OpenLDAP can be obtained at 20 | . 21 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": false, 3 | "browser": true, 4 | "camelcase": false, 5 | "curly": true, 6 | "devel": false, 7 | "eqeqeq": true, 8 | "esnext": true, 9 | "freeze": true, 10 | "immed": true, 11 | "indent": 2, 12 | "latedef": true, 13 | "newcap": false, 14 | "noarg": true, 15 | "node": true, 16 | "noempty": true, 17 | "nonew": true, 18 | "quotmark": "single", 19 | "regexp": true, 20 | "smarttabs": false, 21 | "strict": true, 22 | "trailing": true, 23 | "undef": true, 24 | "unused": true, 25 | "maxparams": 4, 26 | "maxstatements": 15, 27 | "maxcomplexity": 10, 28 | "maxdepth": 3, 29 | "maxlen": 120, 30 | "multistr": true, 31 | "predef": [ 32 | "after", 33 | "afterEach", 34 | "before", 35 | "beforeEach", 36 | "describe", 37 | "exports", 38 | "it", 39 | "module", 40 | "require" 41 | ] 42 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Timur Kristóf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /examples/7-largedb.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Example that shows how to use node-lmdb with LARGE databases 4 | */ 5 | 6 | // Set things up 7 | 8 | var lmdb = require('..'); 9 | var fs = require('fs'); 10 | 11 | var env = new lmdb.Env(); 12 | env.open({ path: './testdata', mapSize: 16 * 1024 * 1024 * 1024 }); 13 | var dbi = env.openDbi({ name: 'test', create: true }); 14 | 15 | // See how much we can squeeze into the db 16 | 17 | try { 18 | while (true) { 19 | for (var i = 0; i < 1000; i++) { 20 | var txn = env.beginTxn(); 21 | txn.putString(dbi, randomString(128), randomString(512)); 22 | txn.commit(); 23 | } 24 | console.log('database size', getDbSize(), 'MB'); 25 | } 26 | } 27 | catch (error) { 28 | console.log('database size', getDbSize(), 'MB'); 29 | console.log('error is', error); 30 | } 31 | 32 | // Utility functions 33 | 34 | function getDbSize () { 35 | return fs.statSync('./testdata/data.mdb').size / 1024 / 1024; 36 | } 37 | 38 | function randomString (length) { 39 | var result = ''; 40 | while (length-- > 0) { 41 | result += String.fromCharCode(97 + Math.floor(Math.random() * 26)); 42 | } 43 | return result; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /examples/6-asyncio.js: -------------------------------------------------------------------------------- 1 | 2 | // Require the module 3 | var lmdb = require('..'); 4 | 5 | // Create new LMDB environment 6 | var env = new lmdb.Env(); 7 | // Open the environment 8 | env.open({ 9 | path: "./testdata", 10 | maxDbs: 10, 11 | 12 | // These options prevent LMDB from automatically syncing on commit 13 | noMetaSync: true, 14 | noSync: true 15 | }); 16 | // Open database 17 | var dbi = env.openDbi({ 18 | name: "mydb1", 19 | create: true 20 | }); 21 | 22 | // Manipulate some data 23 | var txn = env.beginTxn(); 24 | var data = txn.getString(dbi, "hello"); 25 | console.log(data); 26 | if (data === null) { 27 | txn.putString(dbi, "hello", "Hello world!"); 28 | } 29 | else { 30 | txn.del(dbi, "hello"); 31 | } 32 | txn.commit(); 33 | 34 | // Manually sync the environment 35 | env.sync(function(err) { 36 | if (err) { 37 | // There was an error 38 | console.log("error", err); 39 | } 40 | else { 41 | console.log("successful sync"); 42 | } 43 | 44 | // Close the database 45 | dbi.close(); 46 | // Close the environment 47 | env.close(); 48 | }); 49 | 50 | console.log(""); 51 | console.log("Run this example again to see the alterations on the database!"); 52 | 53 | -------------------------------------------------------------------------------- /examples/1-env.js: -------------------------------------------------------------------------------- 1 | 2 | // Require the module 3 | var lmdb = require('..'); 4 | // Now you can use the module 5 | 6 | // Print the version 7 | console.log("Current lmdb version is", lmdb.version); 8 | // Create new LMDB environment 9 | var env = new lmdb.Env(); 10 | // Open the environment 11 | env.open({ 12 | // Path to the environment 13 | // IMPORTANT: you will get an error if the directory doesn't exist! 14 | path: "./testdata", 15 | // Maximum number of databases 16 | maxDbs: 10 17 | }); 18 | // Open database 19 | var dbi = env.openDbi({ 20 | name: "mydb1", 21 | create: true 22 | }); 23 | 24 | // Begin transaction 25 | var txn = env.beginTxn(); 26 | 27 | // Get data 28 | var data = txn.getString(dbi, "hello"); 29 | console.log(data); 30 | 31 | var stat = dbi.stat(txn); 32 | console.log("\ndatabase statistics:"); 33 | console.dir(stat); 34 | 35 | if (data === null) { 36 | // Put data 37 | txn.putString(dbi, "hello", "Hello world!"); 38 | } 39 | else { 40 | // Delete data 41 | txn.del(dbi, "hello"); 42 | } 43 | 44 | console.log(""); 45 | console.log("Run this example again to see the alterations on the database!"); 46 | 47 | // Commit transaction 48 | txn.commit(); 49 | 50 | // Close the database 51 | dbi.close(); 52 | // Close the environment 53 | env.close(); 54 | -------------------------------------------------------------------------------- /examples/9-unnamed-db.js: -------------------------------------------------------------------------------- 1 | 2 | // Require the module 3 | var lmdb = require('..'); 4 | // Now you can use the module 5 | 6 | // Print the version 7 | console.log("Current lmdb version is", lmdb.version); 8 | // Create new LMDB environment 9 | var env = new lmdb.Env(); 10 | // Open the environment 11 | env.open({ 12 | // Path to the environment 13 | // IMPORTANT: you will get an error if the directory doesn't exist! 14 | path: "./testdata", 15 | // Maximum number of databases 16 | maxDbs: 10 17 | }); 18 | // Open database 19 | var dbi = env.openDbi({ 20 | name: null, 21 | create: true 22 | }); 23 | 24 | // Begin transaction 25 | var txn = env.beginTxn(); 26 | 27 | // Get data 28 | var data = txn.getString(dbi, "hello"); 29 | console.log(data); 30 | 31 | var stat = dbi.stat(txn); 32 | console.log("\ndatabase statistics:"); 33 | console.dir(stat); 34 | 35 | if (data === null) { 36 | // Put data 37 | txn.putString(dbi, "hello", "Hello world!"); 38 | } 39 | else { 40 | // Delete data 41 | txn.del(dbi, "hello"); 42 | } 43 | 44 | console.log(""); 45 | console.log("Run this example again to see the alterations on the database!"); 46 | 47 | // Commit transaction 48 | txn.commit(); 49 | 50 | // Close the database 51 | dbi.close(); 52 | // Close the environment 53 | env.close(); 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-lmdb", 3 | "description": "Node binding for LMDB, the Lightning Memory-Mapped Database", 4 | "author": "Timur Kristóf ", 5 | "contributors": [ 6 | "Timur Kristóf", 7 | "Erich Ocean", 8 | "Braydon Fuller", 9 | "Andreas Holstenson (aholstenson)", 10 | "antoinevw", 11 | "b-ono", 12 | "da77a", 13 | "John Hewson (jahewson)", 14 | "Jeffrey Esquivel S. (jeffesquivels)", 15 | "Stefan Thomas (justmoon)", 16 | "Matt-Esch", 17 | "Oliver Zhou (oliverzy)", 18 | "Pascal Berrang (paberr)", 19 | "Raymond Neilson (rneilson)", 20 | "Kris Zyp (kriszyp)" 21 | ], 22 | "license": "MIT", 23 | "keywords": [ 24 | "lmdb", 25 | "database", 26 | "mdb", 27 | "lightning", 28 | "binding" 29 | ], 30 | "repository": "https://github.com/Venemo/node-lmdb", 31 | "version": "0.7.0", 32 | "main": "./index.js", 33 | "scripts": { 34 | "install": "prebuild-install || node-gyp rebuild", 35 | "prebuild": "prebuild --all --verbose", 36 | "test": "./node_modules/.bin/mocha test/**.test.js --recursive", 37 | "benchmark": "node ./benchmark/index.js" 38 | }, 39 | "gypfile": true, 40 | "dependencies": { 41 | "bindings": "^1.5.0", 42 | "prebuild-install": "^5.2.5", 43 | "nan": "^2.13.1" 44 | }, 45 | "devDependencies": { 46 | "benchmark": "^2.1.4", 47 | "chai": "^3.5.0", 48 | "mkdirp": "^0.5.1", 49 | "mocha": "^5.2.0", 50 | "node-gyp": "^3.6.0", 51 | "prebuild": "8.2.1", 52 | "rimraf": "^2.6.1", 53 | "jshint": "^2.9.4" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/11-createdbi-usertxn.js: -------------------------------------------------------------------------------- 1 | 2 | // Created as an example for https://github.com/Venemo/node-lmdb/issues/99 3 | // Demonstrates how to create a Dbi from a user-created write transaction. 4 | 5 | // Require the module 6 | var lmdb = require('..'); 7 | // Now you can use the module 8 | 9 | // Print the version 10 | console.log("Current lmdb version is", lmdb.version); 11 | // Create new LMDB environment 12 | var env = new lmdb.Env(); 13 | // Open the environment 14 | env.open({ 15 | // Path to the environment 16 | // IMPORTANT: you will get an error if the directory doesn't exist! 17 | path: "./testdata", 18 | // Maximum number of databases 19 | maxDbs: 10 20 | }); 21 | 22 | var txn1 = env.beginTxn(); 23 | var dbi = env.openDbi({ 24 | name: 'dbUsingUserSuppliedTxn', 25 | create: true, 26 | txn: txn1 27 | }); 28 | txn1.putString(dbi, 'hello', 'world'); 29 | txn1.commit(); 30 | 31 | var txn2 = env.beginTxn({ readOnly: true }); 32 | var str = txn2.getString(dbi, 'hello'); 33 | txn2.abort(); 34 | console.log(str); 35 | 36 | var txn3 = env.beginTxn(); 37 | dbi.drop({ txn: txn3 }); 38 | txn3.commit(); 39 | 40 | console.log("dbi dropped"); 41 | 42 | var txn4 = env.beginTxn({ readOnly: true }); 43 | try { 44 | dbi = env.openDbi({ 45 | name: 'dbUsingUserSuppliedTxn', 46 | create: false, 47 | txn: txn4 48 | }); 49 | } 50 | catch (err) { 51 | if (err.message.indexOf("MDB_NOTFOUND") >= 0) { 52 | console.log("dbi not found anymore, because we dropped it"); 53 | } 54 | else { 55 | console.log(err); 56 | } 57 | } 58 | txn4.abort(); 59 | 60 | // Close the environment 61 | env.close(); 62 | 63 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mdb_copy.1: -------------------------------------------------------------------------------- 1 | .TH MDB_COPY 1 "2014/07/01" "LMDB 0.9.14" 2 | .\" Copyright 2012-2018 Howard Chu, Symas Corp. All Rights Reserved. 3 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 4 | .SH NAME 5 | mdb_copy \- LMDB environment copy tool 6 | .SH SYNOPSIS 7 | .B mdb_copy 8 | [\c 9 | .BR \-V ] 10 | [\c 11 | .BR \-c ] 12 | [\c 13 | .BR \-n ] 14 | .B srcpath 15 | [\c 16 | .BR dstpath ] 17 | .SH DESCRIPTION 18 | The 19 | .B mdb_copy 20 | utility copies an LMDB environment. The environment can 21 | be copied regardless of whether it is currently in use. 22 | No lockfile is created, since it gets recreated at need. 23 | 24 | If 25 | .I dstpath 26 | is specified it must be the path of an empty directory 27 | for storing the backup. Otherwise, the backup will be 28 | written to stdout. 29 | 30 | .SH OPTIONS 31 | .TP 32 | .BR \-V 33 | Write the library version number to the standard output, and exit. 34 | .TP 35 | .BR \-c 36 | Compact while copying. Only current data pages will be copied; freed 37 | or unused pages will be omitted from the copy. This option will 38 | slow down the backup process as it is more CPU-intensive. 39 | Currently it fails if the environment has suffered a page leak. 40 | .TP 41 | .BR \-n 42 | Open LDMB environment(s) which do not use subdirectories. 43 | 44 | .SH DIAGNOSTICS 45 | Exit status is zero if no errors occur. 46 | Errors result in a non-zero exit status and 47 | a diagnostic message being written to standard error. 48 | .SH CAVEATS 49 | This utility can trigger significant file size growth if run 50 | in parallel with write transactions, because pages which they 51 | free during copying cannot be reused until the copy is done. 52 | .SH "SEE ALSO" 53 | .BR mdb_stat (1) 54 | .SH AUTHOR 55 | Howard Chu of Symas Corporation 56 | -------------------------------------------------------------------------------- /examples/12-largedb-resize.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Example that shows how to use node-lmdb with LARGE databases 4 | */ 5 | 6 | // Set things up 7 | 8 | var lmdb = require('..'); 9 | var fs = require('fs'); 10 | 11 | var env = new lmdb.Env(); 12 | env.open({ path: './testdata', mapSize: 16 * 1024 * 1024 }); 13 | 14 | // Ensure that the database is empty 15 | var dbi = env.openDbi({ 16 | name: "test12", 17 | create: true 18 | }); 19 | dbi.drop(); 20 | dbi = env.openDbi({ 21 | name: "test12", 22 | create: true 23 | }); 24 | 25 | // See how much we can squeeze into the db 26 | try { 27 | while (true) { 28 | for (var i = 0; i < 1000; i++) { 29 | var txn = env.beginTxn(); 30 | txn.putString(dbi, randomString(128), randomString(512)); 31 | txn.commit(); 32 | } 33 | console.log('database size', getDbSize(), 'MB'); 34 | } 35 | } 36 | catch (error) { 37 | console.log('database size', getDbSize(), 'MB'); 38 | console.log('needs resize'); 39 | } 40 | 41 | var info = env.info(); 42 | console.log('maximal database size before resize', (info.mapSize / 1024 / 1024), 'MB'); 43 | env.resize(info.mapSize * 2); 44 | info = env.info(); 45 | console.log('maximal database size after resize', (info.mapSize / 1024 / 1024), 'MB'); 46 | 47 | for (var i = 0; i < 1000; i++) { 48 | var txn = env.beginTxn(); 49 | txn.putString(dbi, randomString(128), randomString(512)); 50 | txn.commit(); 51 | } 52 | console.log('database size', getDbSize(), 'MB'); 53 | 54 | // Utility functions 55 | 56 | function getDbSize () { 57 | return fs.statSync('./testdata/data.mdb').size / 1024 / 1024; 58 | } 59 | 60 | function randomString (length) { 61 | var result = ''; 62 | while (length-- > 0) { 63 | result += String.fromCharCode(97 + Math.floor(Math.random() * 26)); 64 | } 65 | return result; 66 | } 67 | 68 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "os_linux_compiler%": "gcc", 4 | }, 5 | "targets": [ 6 | { 7 | "target_name": "node-lmdb", 8 | "win_delay_load_hook": "false", 9 | "sources": [ 10 | "dependencies/lmdb/libraries/liblmdb/mdb.c", 11 | "dependencies/lmdb/libraries/liblmdb/midl.c", 12 | "src/node-lmdb.cpp", 13 | "src/env.cpp", 14 | "src/misc.cpp", 15 | "src/txn.cpp", 16 | "src/dbi.cpp", 17 | "src/cursor.cpp" 18 | ], 19 | "include_dirs": [ 20 | "=7", { 30 | "cflags": [ 31 | "-Wimplicit-fallthrough=2", 32 | ], 33 | }], 34 | ], 35 | "ldflags": [ 36 | "-fPIC", 37 | "-fvisibility=hidden" 38 | ], 39 | "cflags": [ 40 | "-fPIC", 41 | "-fvisibility=hidden" 42 | ], 43 | "cflags_cc": [ 44 | "-fPIC", 45 | "-fvisibility=hidden", 46 | "-fvisibility-inlines-hidden", 47 | "-std=c++0x" 48 | ] 49 | }], 50 | ["OS=='mac'", { 51 | "xcode_settings": { 52 | "OTHER_CPLUSPLUSFLAGS" : ["-std=c++11"], 53 | "MACOSX_DEPLOYMENT_TARGET": "10.7", 54 | "OTHER_LDFLAGS": ["-std=c++11"], 55 | "CLANG_CXX_LIBRARY": "libc++" 56 | } 57 | }], 58 | ["OS=='win'", { 59 | "libraries": ["ntdll.lib"] 60 | }], 61 | ], 62 | } 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /examples/3-multiple-transactions.js: -------------------------------------------------------------------------------- 1 | 2 | var lmdb = require('..'); 3 | var env = new lmdb.Env(); 4 | env.open({ 5 | // Path to the environment 6 | path: "./testdata", 7 | // Maximum number of databases 8 | maxDbs: 10 9 | }); 10 | var dbi = env.openDbi({ 11 | name: "mydb3", 12 | create: true, 13 | keyIsUint32: true 14 | }); 15 | 16 | var data; 17 | 18 | // Write values 19 | 20 | var txn0 = env.beginTxn(); 21 | txn0.putString(dbi, 1, "Hello1"); 22 | txn0.putString(dbi, 2, "Hello2"); 23 | txn0.commit(); 24 | console.log("wrote initial values"); 25 | 26 | // Now mess around with transactions 27 | 28 | var txn1 = env.beginTxn({ readOnly: true }); 29 | console.log("txn1: started (read only)"); 30 | data = txn1.getString(dbi, 1); 31 | console.log("-----", "txn1", 1, data); 32 | 33 | var txn2 = env.beginTxn(); 34 | console.log("txn2: started"); 35 | txn2.putString(dbi, 1, "Ha ha ha"); 36 | console.log("txn2: put other value to key 1"); 37 | 38 | // txn2 sees the new value immediately 39 | data = txn2.getString(dbi, 1); 40 | console.log("-----", "txn2", 1, data); 41 | 42 | // txn1 still sees the old value 43 | data = txn1.getString(dbi, 1); 44 | console.log("-----", "txn1", 1, data); 45 | 46 | txn2.commit(); 47 | console.log("txn2: committed"); 48 | 49 | // txn1 still sees the old value! 50 | data = txn1.getString(dbi, 1); 51 | console.log("-----", "txn1", 1, data); 52 | 53 | txn1.reset(); 54 | txn1.renew(); 55 | console.log("rxn1: reset+renewed"); 56 | 57 | // now txn1 sees the new value 58 | data = txn1.getString(dbi, 1); 59 | console.log("-----", "txn1", 1, data); 60 | 61 | try { 62 | console.log("error expected here:"); 63 | // txn1 is readonly, this will throw an exception! 64 | txn1.putString(dbi, 2, "hööhh"); 65 | } 66 | catch (err) { 67 | console.log(err); 68 | } 69 | 70 | txn1.commit(); 71 | console.log("txn1: aborted"); 72 | 73 | dbi.close(); 74 | env.close(); 75 | 76 | -------------------------------------------------------------------------------- /src/node-lmdb.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of node-lmdb, the Node.js binding for lmdb 3 | // Copyright (c) 2013-2017 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #include "node-lmdb.h" 25 | 26 | using namespace v8; 27 | using namespace node; 28 | 29 | extern "C" { 30 | // Initializes the module 31 | void initializeModule(Local exports) { 32 | // Export Env as constructor for EnvWrap 33 | EnvWrap::setupExports(exports); 34 | 35 | // Export Cursor as constructor for CursorWrap 36 | CursorWrap::setupExports(exports); 37 | 38 | // Export misc things 39 | setupExportMisc(exports); 40 | } 41 | 42 | // The standard node macro 43 | NODE_MODULE(node_lmdb, initializeModule) 44 | } 45 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/sample-mdb.txt: -------------------------------------------------------------------------------- 1 | /* sample-mdb.txt - MDB toy/sample 2 | * 3 | * Do a line-by-line comparison of this and sample-bdb.txt 4 | */ 5 | /* 6 | * Copyright 2012-2018 Howard Chu, Symas Corp. 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted only as authorized by the OpenLDAP 11 | * Public License. 12 | * 13 | * A copy of this license is available in the file LICENSE in the 14 | * top-level directory of the distribution or, alternatively, at 15 | * . 16 | */ 17 | #include 18 | #include "lmdb.h" 19 | 20 | int main(int argc,char * argv[]) 21 | { 22 | int rc; 23 | MDB_env *env; 24 | MDB_dbi dbi; 25 | MDB_val key, data; 26 | MDB_txn *txn; 27 | MDB_cursor *cursor; 28 | char sval[32]; 29 | 30 | /* Note: Most error checking omitted for simplicity */ 31 | 32 | rc = mdb_env_create(&env); 33 | rc = mdb_env_open(env, "./testdb", 0, 0664); 34 | rc = mdb_txn_begin(env, NULL, 0, &txn); 35 | rc = mdb_dbi_open(txn, NULL, 0, &dbi); 36 | 37 | key.mv_size = sizeof(int); 38 | key.mv_data = sval; 39 | data.mv_size = sizeof(sval); 40 | data.mv_data = sval; 41 | 42 | sprintf(sval, "%03x %d foo bar", 32, 3141592); 43 | rc = mdb_put(txn, dbi, &key, &data, 0); 44 | rc = mdb_txn_commit(txn); 45 | if (rc) { 46 | fprintf(stderr, "mdb_txn_commit: (%d) %s\n", rc, mdb_strerror(rc)); 47 | goto leave; 48 | } 49 | rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); 50 | rc = mdb_cursor_open(txn, dbi, &cursor); 51 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 52 | printf("key: %p %.*s, data: %p %.*s\n", 53 | key.mv_data, (int) key.mv_size, (char *) key.mv_data, 54 | data.mv_data, (int) data.mv_size, (char *) data.mv_data); 55 | } 56 | mdb_cursor_close(cursor); 57 | mdb_txn_abort(txn); 58 | leave: 59 | mdb_dbi_close(env, dbi); 60 | mdb_env_close(env); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mdb_stat.1: -------------------------------------------------------------------------------- 1 | .TH MDB_STAT 1 "2015/09/30" "LMDB 0.9.17" 2 | .\" Copyright 2012-2018 Howard Chu, Symas Corp. All Rights Reserved. 3 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 4 | .SH NAME 5 | mdb_stat \- LMDB environment status tool 6 | .SH SYNOPSIS 7 | .B mdb_stat 8 | [\c 9 | .BR \-V ] 10 | [\c 11 | .BR \-e ] 12 | [\c 13 | .BR \-f [ f [ f ]]] 14 | [\c 15 | .BR \-n ] 16 | [\c 17 | .BR \-r [ r ]] 18 | [\c 19 | .BR \-a \ | 20 | .BI \-s \ subdb\fR] 21 | .BR \ envpath 22 | .SH DESCRIPTION 23 | The 24 | .B mdb_stat 25 | utility displays the status of an LMDB environment. 26 | .SH OPTIONS 27 | .TP 28 | .BR \-V 29 | Write the library version number to the standard output, and exit. 30 | .TP 31 | .BR \-e 32 | Display information about the database environment. 33 | .TP 34 | .BR \-f 35 | Display information about the environment freelist. 36 | If \fB\-ff\fP is given, summarize each freelist entry. 37 | If \fB\-fff\fP is given, display the full list of page IDs in the freelist. 38 | .TP 39 | .BR \-n 40 | Display the status of an LMDB database which does not use subdirectories. 41 | .TP 42 | .BR \-r 43 | Display information about the environment reader table. 44 | Shows the process ID, thread ID, and transaction ID for each active 45 | reader slot. The process ID and transaction ID are in decimal, the 46 | thread ID is in hexadecimal. The transaction ID is displayed as "-" 47 | if the reader does not currently have a read transaction open. 48 | If \fB\-rr\fP is given, check for stale entries in the reader 49 | table and clear them. The reader table will be printed again 50 | after the check is performed. 51 | .TP 52 | .BR \-a 53 | Display the status of all of the subdatabases in the environment. 54 | .TP 55 | .BR \-s \ subdb 56 | Display the status of a specific subdatabase. 57 | .SH DIAGNOSTICS 58 | Exit status is zero if no errors occur. 59 | Errors result in a non-zero exit status and 60 | a diagnostic message being written to standard error. 61 | .SH "SEE ALSO" 62 | .BR mdb_copy (1) 63 | .SH AUTHOR 64 | Howard Chu of Symas Corporation 65 | -------------------------------------------------------------------------------- /examples/2-datatypes.js: -------------------------------------------------------------------------------- 1 | 2 | var lmdb = require('..'); 3 | var env = new lmdb.Env(); 4 | env.open({ 5 | // Path to the environment 6 | path: "./testdata", 7 | // Maximum number of databases 8 | maxDbs: 10 9 | }); 10 | var dbi = env.openDbi({ 11 | name: "mydb2", 12 | create: true 13 | }); 14 | 15 | // Create transaction 16 | var txn = env.beginTxn(); 17 | 18 | // Example for getting/putting/deleting string data 19 | // ---------- 20 | var stringData = txn.getString(dbi, "key1"); 21 | // Print the string 22 | console.log("string data: ", stringData); 23 | // Toggle the value 24 | if (stringData === null) 25 | txn.putString(dbi, "key1", "Hello world!"); 26 | else 27 | txn.del(dbi, "key1"); 28 | 29 | // Example for getting/putting/deleting binary data 30 | // ---------- 31 | var binaryData = txn.getBinary(dbi, "key2"); 32 | // Print the string representation of the binary 33 | console.log("binary data: ", binaryData ? binaryData.toString() : null); 34 | // Toggle the value 35 | if (stringData === null) { 36 | var buffer = new Buffer("Hey my friend"); 37 | txn.putBinary(dbi, "key2", buffer); 38 | } 39 | else { 40 | txn.del(dbi, "key2"); 41 | } 42 | 43 | // Example for getting/putting/deleting number data 44 | // ---------- 45 | var numberData = txn.getNumber(dbi, "key3"); 46 | // Print the number 47 | console.log("number data: ", numberData); 48 | // Toggle the value 49 | if (numberData === null) 50 | txn.putNumber(dbi, "key3", 42); 51 | else 52 | txn.del(dbi, "key3"); 53 | 54 | // Example for getting/putting/deleting boolean data 55 | // ---------- 56 | var booleanData = txn.getBoolean(dbi, "key4"); 57 | // Print the boolean 58 | console.log("boolean data: ", booleanData); 59 | // Toggle the value 60 | if (booleanData === null) 61 | txn.putBoolean(dbi, "key4", true); 62 | else 63 | txn.del(dbi, "key4"); 64 | 65 | // Example for using integer key 66 | // ---------- 67 | var data = txn.getString(dbi, "key5"); 68 | console.log("integer key value: ", data); 69 | if (data === null) 70 | txn.putString(dbi, "key5", "Hello worllld!"); 71 | else 72 | txn.del(dbi, "key5"); 73 | 74 | console.log(""); 75 | console.log("Run this example again to see the alterations on the database!"); 76 | 77 | // Commit transaction 78 | txn.commit(); 79 | 80 | dbi.close(); 81 | env.close(); 82 | 83 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mdb_copy.c: -------------------------------------------------------------------------------- 1 | /* mdb_copy.c - memory-mapped database backup tool */ 2 | /* 3 | * Copyright 2012-2018 Howard Chu, Symas Corp. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | #ifdef _WIN32 15 | #include 16 | #define MDB_STDOUT GetStdHandle(STD_OUTPUT_HANDLE) 17 | #else 18 | #define MDB_STDOUT 1 19 | #endif 20 | #include 21 | #include 22 | #include 23 | #include "lmdb.h" 24 | 25 | static void 26 | sighandle(int sig) 27 | { 28 | } 29 | 30 | int main(int argc,char * argv[]) 31 | { 32 | int rc; 33 | MDB_env *env; 34 | const char *progname = argv[0], *act; 35 | unsigned flags = MDB_RDONLY; 36 | unsigned cpflags = 0; 37 | 38 | for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) { 39 | if (argv[1][1] == 'n' && argv[1][2] == '\0') 40 | flags |= MDB_NOSUBDIR; 41 | else if (argv[1][1] == 'c' && argv[1][2] == '\0') 42 | cpflags |= MDB_CP_COMPACT; 43 | else if (argv[1][1] == 'V' && argv[1][2] == '\0') { 44 | printf("%s\n", MDB_VERSION_STRING); 45 | exit(0); 46 | } else 47 | argc = 0; 48 | } 49 | 50 | if (argc<2 || argc>3) { 51 | fprintf(stderr, "usage: %s [-V] [-c] [-n] srcpath [dstpath]\n", progname); 52 | exit(EXIT_FAILURE); 53 | } 54 | 55 | #ifdef SIGPIPE 56 | signal(SIGPIPE, sighandle); 57 | #endif 58 | #ifdef SIGHUP 59 | signal(SIGHUP, sighandle); 60 | #endif 61 | signal(SIGINT, sighandle); 62 | signal(SIGTERM, sighandle); 63 | 64 | act = "opening environment"; 65 | rc = mdb_env_create(&env); 66 | if (rc == MDB_SUCCESS) { 67 | rc = mdb_env_open(env, argv[1], flags, 0600); 68 | } 69 | if (rc == MDB_SUCCESS) { 70 | act = "copying"; 71 | if (argc == 2) 72 | rc = mdb_env_copyfd2(env, MDB_STDOUT, cpflags); 73 | else 74 | rc = mdb_env_copy2(env, argv[2], cpflags); 75 | } 76 | if (rc) 77 | fprintf(stderr, "%s: %s failed, error %d (%s)\n", 78 | progname, act, rc, mdb_strerror(rc)); 79 | mdb_env_close(env); 80 | 81 | return rc ? EXIT_FAILURE : EXIT_SUCCESS; 82 | } 83 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/sample-bdb.txt: -------------------------------------------------------------------------------- 1 | /* sample-bdb.txt - BerkeleyDB toy/sample 2 | * 3 | * Do a line-by-line comparison of this and sample-mdb.txt 4 | */ 5 | /* 6 | * Copyright 2012-2018 Howard Chu, Symas Corp. 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted only as authorized by the OpenLDAP 11 | * Public License. 12 | * 13 | * A copy of this license is available in the file LICENSE in the 14 | * top-level directory of the distribution or, alternatively, at 15 | * . 16 | */ 17 | #include 18 | #include 19 | #include 20 | 21 | int main(int argc,char * argv[]) 22 | { 23 | int rc; 24 | DB_ENV *env; 25 | DB *dbi; 26 | DBT key, data; 27 | DB_TXN *txn; 28 | DBC *cursor; 29 | char sval[32], kval[32]; 30 | 31 | /* Note: Most error checking omitted for simplicity */ 32 | 33 | #define FLAGS (DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_INIT_MPOOL|DB_CREATE|DB_THREAD) 34 | rc = db_env_create(&env, 0); 35 | rc = env->open(env, "./testdb", FLAGS, 0664); 36 | rc = db_create(&dbi, env, 0); 37 | rc = env->txn_begin(env, NULL, &txn, 0); 38 | rc = dbi->open(dbi, txn, "test.bdb", NULL, DB_BTREE, DB_CREATE, 0664); 39 | 40 | memset(&key, 0, sizeof(DBT)); 41 | memset(&data, 0, sizeof(DBT)); 42 | key.size = sizeof(int); 43 | key.data = sval; 44 | data.size = sizeof(sval); 45 | data.data = sval; 46 | 47 | sprintf(sval, "%03x %d foo bar", 32, 3141592); 48 | rc = dbi->put(dbi, txn, &key, &data, 0); 49 | rc = txn->commit(txn, 0); 50 | if (rc) { 51 | fprintf(stderr, "txn->commit: (%d) %s\n", rc, db_strerror(rc)); 52 | goto leave; 53 | } 54 | rc = env->txn_begin(env, NULL, &txn, 0); 55 | rc = dbi->cursor(dbi, txn, &cursor, 0); 56 | key.flags = DB_DBT_USERMEM; 57 | key.data = kval; 58 | key.ulen = sizeof(kval); 59 | data.flags = DB_DBT_USERMEM; 60 | data.data = sval; 61 | data.ulen = sizeof(sval); 62 | while ((rc = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) { 63 | printf("key: %p %.*s, data: %p %.*s\n", 64 | key.data, (int) key.size, (char *) key.data, 65 | data.data, (int) data.size, (char *) data.data); 66 | } 67 | rc = cursor->c_close(cursor); 68 | rc = txn->abort(txn); 69 | leave: 70 | rc = dbi->close(dbi, 0); 71 | rc = env->close(env, 0); 72 | return rc; 73 | } 74 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/LICENSE: -------------------------------------------------------------------------------- 1 | The OpenLDAP Public License 2 | Version 2.8, 17 August 2003 3 | 4 | Redistribution and use of this software and associated documentation 5 | ("Software"), with or without modification, are permitted provided 6 | that the following conditions are met: 7 | 8 | 1. Redistributions in source form must retain copyright statements 9 | and notices, 10 | 11 | 2. Redistributions in binary form must reproduce applicable copyright 12 | statements and notices, this list of conditions, and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution, and 15 | 16 | 3. Redistributions must contain a verbatim copy of this document. 17 | 18 | The OpenLDAP Foundation may revise this license from time to time. 19 | Each revision is distinguished by a version number. You may use 20 | this Software under terms of this license revision or under the 21 | terms of any subsequent revision of the license. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS 24 | CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, 25 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 26 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 27 | SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) 28 | OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, 29 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 | POSSIBILITY OF SUCH DAMAGE. 36 | 37 | The names of the authors and copyright holders must not be used in 38 | advertising or otherwise to promote the sale, use or other dealing 39 | in this Software without specific, written prior permission. Title 40 | to copyright in this Software shall at all times remain with copyright 41 | holders. 42 | 43 | OpenLDAP is a registered trademark of the OpenLDAP Foundation. 44 | 45 | Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, 46 | California, USA. All Rights Reserved. Permission to copy and 47 | distribute verbatim copies of this document is granted. 48 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mdb_dump.1: -------------------------------------------------------------------------------- 1 | .TH MDB_DUMP 1 "2015/09/30" "LMDB 0.9.17" 2 | .\" Copyright 2014-2018 Howard Chu, Symas Corp. All Rights Reserved. 3 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 4 | .SH NAME 5 | mdb_dump \- LMDB environment export tool 6 | .SH SYNOPSIS 7 | .B mdb_dump 8 | [\c 9 | .BR \-V ] 10 | [\c 11 | .BI \-f \ file\fR] 12 | [\c 13 | .BR \-l ] 14 | [\c 15 | .BR \-n ] 16 | [\c 17 | .BR \-p ] 18 | [\c 19 | .BR \-a \ | 20 | .BI \-s \ subdb\fR] 21 | .BR \ envpath 22 | .SH DESCRIPTION 23 | The 24 | .B mdb_dump 25 | utility reads a database and writes its contents to the 26 | standard output using a portable flat-text format 27 | understood by the 28 | .BR mdb_load (1) 29 | utility. 30 | .SH OPTIONS 31 | .TP 32 | .BR \-V 33 | Write the library version number to the standard output, and exit. 34 | .TP 35 | .BR \-f \ file 36 | Write to the specified file instead of to the standard output. 37 | .TP 38 | .BR \-l 39 | List the databases stored in the environment. Just the 40 | names will be listed, no data will be output. 41 | .TP 42 | .BR \-n 43 | Dump an LMDB database which does not use subdirectories. 44 | .TP 45 | .BR \-p 46 | If characters in either the key or data items are printing characters (as 47 | defined by isprint(3)), output them directly. This option permits users to 48 | use standard text editors and tools to modify the contents of databases. 49 | 50 | Note: different systems may have different notions about what characters 51 | are considered printing characters, and databases dumped in this manner may 52 | be less portable to external systems. 53 | .TP 54 | .BR \-a 55 | Dump all of the subdatabases in the environment. 56 | .TP 57 | .BR \-s \ subdb 58 | Dump a specific subdatabase. If no database is specified, only the main database is dumped. 59 | .SH DIAGNOSTICS 60 | Exit status is zero if no errors occur. 61 | Errors result in a non-zero exit status and 62 | a diagnostic message being written to standard error. 63 | 64 | Dumping and reloading databases that use user-defined comparison functions 65 | will result in new databases that use the default comparison functions. 66 | \fBIn this case it is quite likely that the reloaded database will be 67 | damaged beyond repair permitting neither record storage nor retrieval.\fP 68 | 69 | The only available workaround is to modify the source for the 70 | .BR mdb_load (1) 71 | utility to load the database using the correct comparison functions. 72 | .SH "SEE ALSO" 73 | .BR mdb_load (1) 74 | .SH AUTHOR 75 | Howard Chu of Symas Corporation 76 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mdb_load.1: -------------------------------------------------------------------------------- 1 | .TH MDB_LOAD 1 "2015/09/30" "LMDB 0.9.17" 2 | .\" Copyright 2014-2018 Howard Chu, Symas Corp. All Rights Reserved. 3 | .\" Copying restrictions apply. See COPYRIGHT/LICENSE. 4 | .SH NAME 5 | mdb_load \- LMDB environment import tool 6 | .SH SYNOPSIS 7 | .B mdb_load 8 | [\c 9 | .BR \-V ] 10 | [\c 11 | .BI \-f \ file\fR] 12 | [\c 13 | .BR \-n ] 14 | [\c 15 | .BI \-s \ subdb\fR] 16 | [\c 17 | .BR \-N ] 18 | [\c 19 | .BR \-T ] 20 | .BR \ envpath 21 | .SH DESCRIPTION 22 | The 23 | .B mdb_load 24 | utility reads from the standard input and loads it into the 25 | LMDB environment 26 | .BR envpath . 27 | 28 | The input to 29 | .B mdb_load 30 | must be in the output format specified by the 31 | .BR mdb_dump (1) 32 | utility or as specified by the 33 | .B -T 34 | option below. 35 | .SH OPTIONS 36 | .TP 37 | .BR \-V 38 | Write the library version number to the standard output, and exit. 39 | .TP 40 | .BR \-f \ file 41 | Read from the specified file instead of from the standard input. 42 | .TP 43 | .BR \-n 44 | Load an LMDB database which does not use subdirectories. 45 | .TP 46 | .BR \-s \ subdb 47 | Load a specific subdatabase. If no database is specified, data is loaded into the main database. 48 | .TP 49 | .BR \-N 50 | Don't overwrite existing records when loading into an already existing database; just skip them. 51 | .TP 52 | .BR \-T 53 | Load data from simple text files. The input must be paired lines of text, where the first 54 | line of the pair is the key item, and the second line of the pair is its corresponding 55 | data item. 56 | 57 | A simple escape mechanism, where newline and backslash (\\) characters are special, is 58 | applied to the text input. Newline characters are interpreted as record separators. 59 | Backslash characters in the text will be interpreted in one of two ways: If the backslash 60 | character precedes another backslash character, the pair will be interpreted as a literal 61 | backslash. If the backslash character precedes any other character, the two characters 62 | following the backslash will be interpreted as a hexadecimal specification of a single 63 | character; for example, \\0a is a newline character in the ASCII character set. 64 | 65 | For this reason, any backslash or newline characters that naturally occur in the text 66 | input must be escaped to avoid misinterpretation by 67 | .BR mdb_load . 68 | 69 | .SH DIAGNOSTICS 70 | Exit status is zero if no errors occur. 71 | Errors result in a non-zero exit status and 72 | a diagnostic message being written to standard error. 73 | 74 | .SH "SEE ALSO" 75 | .BR mdb_dump (1) 76 | .SH AUTHOR 77 | Howard Chu of Symas Corporation 78 | -------------------------------------------------------------------------------- /test/cluster.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var cluster = require('cluster'); 5 | var path = require('path'); 6 | var numCPUs = require('os').cpus().length; 7 | 8 | var lmdb = require('..'); 9 | const MAX_DB_SIZE = 256 * 1024 * 1024; 10 | 11 | if (cluster.isMaster) { 12 | 13 | // The master process 14 | 15 | var env = new lmdb.Env(); 16 | env.open({ 17 | path: path.resolve(__dirname, './testdata'), 18 | maxDbs: 10, 19 | mapSize: MAX_DB_SIZE, 20 | maxReaders: 126 21 | }); 22 | 23 | var dbi = env.openDbi({ 24 | name: 'cluster', 25 | create: true 26 | }); 27 | 28 | var workerCount = Math.min(numCPUs * 2, 20); 29 | var value = Buffer.from('48656c6c6f2c20776f726c6421', 'hex'); 30 | 31 | // This will start as many workers as there are CPUs available. 32 | var workers = []; 33 | for (var i = 0; i < workerCount; i++) { 34 | var worker = cluster.fork(); 35 | workers.push(worker); 36 | } 37 | 38 | var messages = []; 39 | 40 | workers.forEach(function(worker) { 41 | worker.on('message', function(msg) { 42 | messages.push(msg); 43 | // Once every worker has replied with a response for the value 44 | // we can exit the test. 45 | if (messages.length === workerCount) { 46 | dbi.close(); 47 | env.close(); 48 | for (var i = 0; i < messages.length; i ++) { 49 | assert(messages[i] === value.toString('hex')); 50 | } 51 | process.exit(0); 52 | } 53 | }); 54 | }); 55 | 56 | var txn = env.beginTxn(); 57 | for (var i = 0; i < workers.length; i++) { 58 | txn.putBinary(dbi, 'key' + i, value); 59 | } 60 | 61 | txn.commit(); 62 | 63 | for (var i = 0; i < workers.length; i++) { 64 | var worker = workers[i]; 65 | worker.send({key: 'key' + i}); 66 | }; 67 | 68 | } else { 69 | 70 | // The worker process 71 | 72 | var env = new lmdb.Env(); 73 | env.open({ 74 | path: path.resolve(__dirname, './testdata'), 75 | maxDbs: 10, 76 | mapSize: MAX_DB_SIZE, 77 | maxReaders: 126, 78 | readOnly: true 79 | }); 80 | 81 | var dbi = env.openDbi({ 82 | name: 'cluster' 83 | }); 84 | 85 | process.on('message', function(msg) { 86 | if (msg.key) { 87 | var txn = env.beginTxn({readOnly: true}); 88 | var value = txn.getBinary(dbi, msg.key); 89 | 90 | if (value === null) { 91 | process.send(""); 92 | } else { 93 | process.send(value.toString('hex')); 94 | } 95 | 96 | txn.abort(); 97 | } 98 | }); 99 | 100 | } 101 | -------------------------------------------------------------------------------- /examples/4-cursors.js: -------------------------------------------------------------------------------- 1 | 2 | var lmdb = require('..'); 3 | var env = new lmdb.Env(); 4 | env.open({ 5 | // Path to the environment 6 | path: "./testdata", 7 | // Maximum number of databases 8 | maxDbs: 10 9 | }); 10 | 11 | // Ensure that the database is empty 12 | var dbi = env.openDbi({ 13 | name: "mydb4", 14 | create: true 15 | }); 16 | dbi.drop(); 17 | dbi = env.openDbi({ 18 | name: "mydb4", 19 | create: true 20 | }); 21 | 22 | // Write test values 23 | 24 | var txn0 = env.beginTxn(); 25 | txn0.putString(dbi, "a", "Helló1"); 26 | txn0.putString(dbi, "b", "Hello2"); 27 | txn0.putNumber(dbi, "c", 43); 28 | /* key 'd' is omitted intentionally */ 29 | txn0.putBinary(dbi, "e", new Buffer("öüóőúéáű")); 30 | txn0.putBoolean(dbi, "f", false); 31 | txn0.putString(dbi, "g", "Hello6"); 32 | txn0.commit(); 33 | console.log("wrote initial values"); 34 | 35 | var printFunc = function(key, data) { 36 | console.log("-----> key:", key); 37 | console.log("-----> data:", data); 38 | } 39 | 40 | // Begin transaction 41 | var txn = env.beginTxn(); 42 | 43 | // Create cursor 44 | var cursor = new lmdb.Cursor(txn, dbi); 45 | 46 | console.log("first (expected a)"); 47 | cursor.goToFirst(); 48 | cursor.getCurrentString(printFunc); 49 | 50 | console.log("next (expected b)"); 51 | cursor.goToNext(); 52 | cursor.getCurrentString(printFunc); 53 | 54 | console.log("next (expected c)"); 55 | cursor.goToNext(); 56 | cursor.getCurrentNumber(printFunc); 57 | 58 | 59 | console.log("next (expected e)"); 60 | cursor.goToNext(); 61 | cursor.getCurrentBinary(printFunc); 62 | 63 | 64 | console.log("prev (expected c)"); 65 | cursor.goToPrev(); 66 | cursor.getCurrentNumber(printFunc); 67 | 68 | 69 | console.log("last (expected g)"); 70 | cursor.goToLast(); 71 | cursor.getCurrentString(printFunc); 72 | 73 | 74 | console.log("prev (expected f)"); 75 | cursor.goToPrev(); 76 | cursor.getCurrentBoolean(printFunc); 77 | 78 | 79 | console.log("go to key 'b' (expected b)"); 80 | cursor.goToKey('b'); 81 | cursor.getCurrentString(printFunc); 82 | 83 | 84 | console.log("go to range 'd' (expected e)"); 85 | cursor.goToRange('d'); 86 | cursor.getCurrentBinary(printFunc); 87 | 88 | 89 | console.log("del (expected f)"); 90 | cursor.del(); 91 | cursor.getCurrentBoolean(printFunc); 92 | 93 | console.log(""); 94 | console.log("now iterating through all the keys"); 95 | 96 | for (var found = cursor.goToFirst(); found !== null; found = cursor.goToNext()) { 97 | console.log("-----> key:", found); 98 | } 99 | 100 | // Close cursor 101 | cursor.close(); 102 | 103 | // Commit transaction 104 | txn.commit(); 105 | 106 | dbi.close(); 107 | env.close(); 108 | 109 | -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var crypto = require('crypto'); 4 | var path = require('path'); 5 | var testDirPath = path.resolve(__dirname, './benchdata'); 6 | 7 | var rimraf = require('rimraf'); 8 | var mkdirp = require('mkdirp'); 9 | var benchmark = require('benchmark'); 10 | var suite = new benchmark.Suite(); 11 | 12 | var lmdb = require('..'); 13 | 14 | var env; 15 | var dbi; 16 | var keys = []; 17 | var total = 1000000; 18 | 19 | function cleanup(done) { 20 | // cleanup previous test directory 21 | rimraf(testDirPath, function(err) { 22 | if (err) { 23 | return done(err); 24 | } 25 | // setup clean directory 26 | mkdirp(testDirPath, function(err) { 27 | if (err) { 28 | return done(err); 29 | } 30 | done(); 31 | }); 32 | }); 33 | } 34 | 35 | function setup() { 36 | env = new lmdb.Env(); 37 | env.open({ 38 | path: testDirPath, 39 | maxDbs: 10, 40 | mapSize: 16 * 1024 * 1024 * 1024 41 | }); 42 | dbi = env.openDbi({ 43 | name: 'benchmarks', 44 | create: true 45 | }); 46 | 47 | var txn = env.beginTxn(); 48 | var c = 0; 49 | while(c < total) { 50 | var key = new Buffer(new Array(8)); 51 | key.writeDoubleBE(c); 52 | keys.push(key.toString('hex')); 53 | txn.putBinary(dbi, key.toString('hex'), crypto.randomBytes(32)); 54 | c++; 55 | } 56 | txn.commit(); 57 | } 58 | 59 | var txn; 60 | var c = 0; 61 | 62 | function getIndex() { 63 | if (c < total - 1) { 64 | c++; 65 | } else { 66 | c = 0; 67 | } 68 | return c; 69 | } 70 | 71 | function getBinary() { 72 | var data = txn.getBinary(dbi, keys[getIndex()]); 73 | } 74 | 75 | function getBinaryUnsafe() { 76 | var data = txn.getBinaryUnsafe(dbi, keys[getIndex()]); 77 | } 78 | 79 | function getString() { 80 | var data = txn.getString(dbi, keys[getIndex()]); 81 | } 82 | 83 | function getStringUnsafe() { 84 | var data = txn.getStringUnsafe(dbi, keys[getIndex()]); 85 | } 86 | 87 | cleanup(function(err) { 88 | if (err) { 89 | throw err; 90 | } 91 | 92 | setup(); 93 | 94 | suite.add('getBinary', getBinary); 95 | suite.add('getBinaryUnsafe', getBinaryUnsafe); 96 | suite.add('getString', getString); 97 | suite.add('getStringUnsafe', getStringUnsafe); 98 | 99 | suite.on('start', function() { 100 | txn = env.beginTxn(); 101 | }); 102 | 103 | suite.on('cycle', function(event) { 104 | txn.abort(); 105 | txn = env.beginTxn(); 106 | console.log(String(event.target)); 107 | }); 108 | 109 | suite.on('complete', function () { 110 | txn.abort(); 111 | dbi.close(); 112 | env.close(); 113 | console.log('Fastest is ' + this.filter('fastest').map('name')); 114 | }); 115 | 116 | suite.run(); 117 | 118 | }); 119 | -------------------------------------------------------------------------------- /examples/5-dupsort.js: -------------------------------------------------------------------------------- 1 | 2 | var lmdb, env, dbi; 3 | 4 | lmdb = require('..'); 5 | env = new lmdb.Env(); 6 | env.open({ 7 | // Path to the environment 8 | path: "./testdata", 9 | // Maximum number of databases 10 | maxDbs: 10 11 | }); 12 | 13 | try { 14 | // If the database exists, drop it 15 | dbi = env.openDbi({ 16 | name: "example5-dupsort", 17 | dupSort: true 18 | }); 19 | dbi.drop(); 20 | } 21 | catch (err) {} 22 | dbi = env.openDbi({ 23 | name: "example5-dupsort", 24 | create: true, 25 | dupSort: true, 26 | dupFixed: true, 27 | integerDup: true 28 | }); 29 | 30 | console.log("ensured database is empty"); 31 | 32 | var printFunc = function(key, data) { 33 | console.log("----------> key:", key); 34 | console.log("----------> data:", data); 35 | } 36 | 37 | var txn, cursor; 38 | 39 | txn = env.beginTxn(); 40 | txn.putNumber(dbi, "hello", 1); 41 | txn.putNumber(dbi, "apple", 2); 42 | txn.putNumber(dbi, "orange", 3); 43 | txn.putNumber(dbi, "apple", 4); 44 | txn.putNumber(dbi, "hello", 5); 45 | txn.putNumber(dbi, "appricot", 6); 46 | txn.putNumber(dbi, "hello", 7); 47 | txn.commit(); 48 | 49 | console.log("wrote test values"); 50 | 51 | txn = env.beginTxn({ readOnly: true }); 52 | cursor = new lmdb.Cursor(txn, dbi); 53 | 54 | console.log("goToRange 'banana'"); 55 | cursor.goToRange("banana"); 56 | cursor.getCurrentNumber(printFunc); 57 | 58 | console.log("goToNext"); 59 | cursor.goToNext(); 60 | cursor.getCurrentNumber(printFunc); 61 | 62 | console.log("goToNext"); 63 | cursor.goToNext(); 64 | cursor.getCurrentNumber(printFunc); 65 | 66 | console.log("goToNext"); 67 | cursor.goToNext(); 68 | cursor.getCurrentNumber(printFunc); 69 | 70 | console.log("goToDup 'apple', 4"); 71 | cursor.goToDup("apple", 4); 72 | cursor.getCurrentNumber(printFunc); 73 | 74 | console.log("goToDupRange 'hello', 0"); 75 | cursor.goToDup("hello", 0); 76 | cursor.getCurrentNumber(printFunc); 77 | 78 | console.log(""); 79 | console.log("iterating through a duplicate key: if-do-while"); 80 | 81 | var key = "hello"; 82 | 83 | if (cursor.goToRange(key) === key) { 84 | do { 85 | cursor.getCurrentNumber(function(key, data) { 86 | // do something with data 87 | console.log(key, data); 88 | }); 89 | } while (cursor.goToNextDup()); 90 | } 91 | 92 | console.log(""); 93 | console.log("iterating through a duplicate key: for"); 94 | 95 | var key = "apple"; 96 | 97 | for (var found = (cursor.goToRange(key) === key); found !== null; found = cursor.goToNextDup()) { 98 | cursor.getCurrentNumber(function(key, data) { 99 | // do something with data 100 | console.log(key, data); 101 | }); 102 | } 103 | 104 | cursor.close(); 105 | txn.abort(); 106 | dbi.close(); 107 | env.close(); 108 | 109 | -------------------------------------------------------------------------------- /examples/10-binkeycursors.js: -------------------------------------------------------------------------------- 1 | 2 | var lmdb = require('..'); 3 | var env = new lmdb.Env(); 4 | env.open({ 5 | // Path to the environment 6 | path: "./testdata", 7 | // Maximum number of databases 8 | maxDbs: 10 9 | }); 10 | 11 | // Ensure that the database is empty 12 | var dbi = env.openDbi({ 13 | name: "mydb10", 14 | create: true 15 | }); 16 | dbi.drop(); 17 | dbi = env.openDbi({ 18 | name: "mydb10", 19 | create: true 20 | }); 21 | 22 | // Write test values 23 | 24 | var txn0 = env.beginTxn(); 25 | txn0.putString(dbi, Buffer.from("a","utf8"), "Helló1"); 26 | txn0.putString(dbi, Buffer.from("b","utf8"), "Hello2"); 27 | txn0.putNumber(dbi, Buffer.from("c","utf8"), 43); 28 | /* key 'd' is omitted intentionally */ 29 | txn0.putBinary(dbi, "e", Buffer.from("öüóőúéáű")); 30 | txn0.putBoolean(dbi, "f", false); 31 | txn0.putString(dbi, "g", "Hello6"); 32 | txn0.commit(); 33 | console.log("wrote initial values"); 34 | 35 | var printFunc = function(key, data) { 36 | console.log("-----> key:", key.toString(), " : ", JSON.stringify(key)); 37 | console.log("-----> data:", data.toString(), " : ", JSON.stringify(data)); 38 | } 39 | 40 | // Begin transaction 41 | var txn = env.beginTxn(); 42 | 43 | // Create cursor 44 | var cursor = new lmdb.Cursor(txn, dbi, { keyIsBuffer: true }); 45 | 46 | console.log("first (expected a)"); 47 | cursor.goToFirst(); 48 | cursor.getCurrentString(printFunc); 49 | 50 | console.log("next (expected b)"); 51 | cursor.goToNext(); 52 | cursor.getCurrentString(printFunc); 53 | 54 | console.log("next (expected c)"); 55 | cursor.goToNext(); 56 | cursor.getCurrentNumber(printFunc); 57 | 58 | 59 | console.log("next (expected e)"); 60 | cursor.goToNext(); 61 | cursor.getCurrentBinary(printFunc); 62 | 63 | 64 | console.log("prev (expected c)"); 65 | cursor.goToPrev(); 66 | cursor.getCurrentNumber(printFunc); 67 | 68 | 69 | console.log("last (expected g)"); 70 | cursor.goToLast(); 71 | cursor.getCurrentString(printFunc); 72 | 73 | 74 | console.log("prev (expected f)"); 75 | cursor.goToPrev(); 76 | cursor.getCurrentBoolean(printFunc); 77 | 78 | 79 | console.log("go to key 'b' (expected b)"); 80 | cursor.goToKey(Buffer.from('b')); 81 | cursor.getCurrentString(printFunc); 82 | 83 | 84 | console.log("go to range 'd' (expected e)"); 85 | cursor.goToRange('d'); 86 | cursor.getCurrentBinary(printFunc); 87 | 88 | 89 | console.log("del (expected f)"); 90 | cursor.del(); 91 | cursor.getCurrentBoolean(printFunc); 92 | 93 | console.log(""); 94 | console.log("now iterating through all the keys"); 95 | 96 | for (var found = cursor.goToFirst(); found !== null; found = cursor.goToNext()) { 97 | console.log("-----> key:", found); 98 | } 99 | 100 | // Close cursor 101 | cursor.close(); 102 | 103 | // Commit transaction 104 | txn.commit(); 105 | 106 | dbi.close(); 107 | env.close(); 108 | 109 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for liblmdb (Lightning memory-mapped database library). 2 | 3 | ######################################################################## 4 | # Configuration. The compiler options must enable threaded compilation. 5 | # 6 | # Preprocessor macros (for CPPFLAGS) of interest... 7 | # Note that the defaults should already be correct for most 8 | # platforms; you should not need to change any of these. 9 | # Read their descriptions in mdb.c if you do: 10 | # 11 | # - MDB_USE_POSIX_SEM 12 | # - MDB_DSYNC 13 | # - MDB_FDATASYNC 14 | # - MDB_FDATASYNC_WORKS 15 | # - MDB_USE_PWRITEV 16 | # - MDB_USE_ROBUST 17 | # 18 | # There may be other macros in mdb.c of interest. You should 19 | # read mdb.c before changing any of them. 20 | # 21 | CC = gcc 22 | AR = ar 23 | W = -W -Wall -Wno-unused-parameter -Wbad-function-cast -Wuninitialized 24 | THREADS = -pthread 25 | OPT = -O2 -g 26 | CFLAGS = $(THREADS) $(OPT) $(W) $(XCFLAGS) 27 | LDLIBS = 28 | SOLIBS = 29 | SOEXT = .so 30 | prefix = /usr/local 31 | exec_prefix = $(prefix) 32 | bindir = $(exec_prefix)/bin 33 | libdir = $(exec_prefix)/lib 34 | includedir = $(prefix)/include 35 | datarootdir = $(prefix)/share 36 | mandir = $(datarootdir)/man 37 | 38 | ######################################################################## 39 | 40 | IHDRS = lmdb.h 41 | ILIBS = liblmdb.a liblmdb$(SOEXT) 42 | IPROGS = mdb_stat mdb_copy mdb_dump mdb_load 43 | IDOCS = mdb_stat.1 mdb_copy.1 mdb_dump.1 mdb_load.1 44 | PROGS = $(IPROGS) mtest mtest2 mtest3 mtest4 mtest5 45 | all: $(ILIBS) $(PROGS) 46 | 47 | install: $(ILIBS) $(IPROGS) $(IHDRS) 48 | mkdir -p $(DESTDIR)$(bindir) 49 | mkdir -p $(DESTDIR)$(libdir) 50 | mkdir -p $(DESTDIR)$(includedir) 51 | mkdir -p $(DESTDIR)$(mandir)/man1 52 | for f in $(IPROGS); do cp $$f $(DESTDIR)$(bindir); done 53 | for f in $(ILIBS); do cp $$f $(DESTDIR)$(libdir); done 54 | for f in $(IHDRS); do cp $$f $(DESTDIR)$(includedir); done 55 | for f in $(IDOCS); do cp $$f $(DESTDIR)$(mandir)/man1; done 56 | 57 | clean: 58 | rm -rf $(PROGS) *.[ao] *.[ls]o *~ testdb 59 | 60 | test: all 61 | rm -rf testdb && mkdir testdb 62 | ./mtest && ./mdb_stat testdb 63 | 64 | liblmdb.a: mdb.o midl.o 65 | $(AR) rs $@ mdb.o midl.o 66 | 67 | liblmdb$(SOEXT): mdb.lo midl.lo 68 | # $(CC) $(LDFLAGS) -pthread -shared -Wl,-Bsymbolic -o $@ mdb.o midl.o $(SOLIBS) 69 | $(CC) $(LDFLAGS) -pthread -shared -o $@ mdb.lo midl.lo $(SOLIBS) 70 | 71 | mdb_stat: mdb_stat.o liblmdb.a 72 | mdb_copy: mdb_copy.o liblmdb.a 73 | mdb_dump: mdb_dump.o liblmdb.a 74 | mdb_load: mdb_load.o liblmdb.a 75 | mtest: mtest.o liblmdb.a 76 | mtest2: mtest2.o liblmdb.a 77 | mtest3: mtest3.o liblmdb.a 78 | mtest4: mtest4.o liblmdb.a 79 | mtest5: mtest5.o liblmdb.a 80 | mtest6: mtest6.o liblmdb.a 81 | 82 | mdb.o: mdb.c lmdb.h midl.h 83 | $(CC) $(CFLAGS) $(CPPFLAGS) -c mdb.c 84 | 85 | midl.o: midl.c midl.h 86 | $(CC) $(CFLAGS) $(CPPFLAGS) -c midl.c 87 | 88 | mdb.lo: mdb.c lmdb.h midl.h 89 | $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c mdb.c -o $@ 90 | 91 | midl.lo: midl.c midl.h 92 | $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c midl.c -o $@ 93 | 94 | %: %.o 95 | $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ 96 | 97 | %.o: %.c lmdb.h 98 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< 99 | 100 | COV_FLAGS=-fprofile-arcs -ftest-coverage 101 | COV_OBJS=xmdb.o xmidl.o 102 | 103 | coverage: xmtest 104 | for i in mtest*.c [0-9]*.c; do j=`basename \$$i .c`; $(MAKE) $$j.o; \ 105 | gcc -o x$$j $$j.o $(COV_OBJS) -pthread $(COV_FLAGS); \ 106 | rm -rf testdb; mkdir testdb; ./x$$j; done 107 | gcov xmdb.c 108 | gcov xmidl.c 109 | 110 | xmtest: mtest.o xmdb.o xmidl.o 111 | gcc -o xmtest mtest.o xmdb.o xmidl.o -pthread $(COV_FLAGS) 112 | 113 | xmdb.o: mdb.c lmdb.h midl.h 114 | $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c mdb.c -o $@ 115 | 116 | xmidl.o: midl.c midl.h 117 | $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c midl.c -o $@ 118 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mtest2.c: -------------------------------------------------------------------------------- 1 | /* mtest2.c - memory-mapped database tester/toy */ 2 | /* 3 | * Copyright 2011-2018 Howard Chu, Symas Corp. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | /* Just like mtest.c, but using a subDB instead of the main DB */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include "lmdb.h" 21 | 22 | #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) 23 | #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) 24 | #define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ 25 | "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) 26 | 27 | int main(int argc,char * argv[]) 28 | { 29 | int i = 0, j = 0, rc; 30 | MDB_env *env; 31 | MDB_dbi dbi; 32 | MDB_val key, data; 33 | MDB_txn *txn; 34 | MDB_stat mst; 35 | MDB_cursor *cursor; 36 | int count; 37 | int *values; 38 | char sval[32] = ""; 39 | 40 | srand(time(NULL)); 41 | 42 | count = (rand()%384) + 64; 43 | values = (int *)malloc(count*sizeof(int)); 44 | 45 | for(i = 0;i -1; i-= (rand()%5)) { 87 | j++; 88 | txn=NULL; 89 | E(mdb_txn_begin(env, NULL, 0, &txn)); 90 | sprintf(sval, "%03x ", values[i]); 91 | if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, NULL))) { 92 | j--; 93 | mdb_txn_abort(txn); 94 | } else { 95 | E(mdb_txn_commit(txn)); 96 | } 97 | } 98 | free(values); 99 | printf("Deleted %d values\n", j); 100 | 101 | E(mdb_env_stat(env, &mst)); 102 | E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); 103 | E(mdb_cursor_open(txn, dbi, &cursor)); 104 | printf("Cursor next\n"); 105 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 106 | printf("key: %.*s, data: %.*s\n", 107 | (int) key.mv_size, (char *) key.mv_data, 108 | (int) data.mv_size, (char *) data.mv_data); 109 | } 110 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 111 | printf("Cursor prev\n"); 112 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { 113 | printf("key: %.*s, data: %.*s\n", 114 | (int) key.mv_size, (char *) key.mv_data, 115 | (int) data.mv_size, (char *) data.mv_data); 116 | } 117 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 118 | mdb_cursor_close(cursor); 119 | mdb_txn_abort(txn); 120 | 121 | mdb_dbi_close(env, dbi); 122 | mdb_env_close(env); 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /examples/8-multiple-cursors-single-transaction.js: -------------------------------------------------------------------------------- 1 | var lmdb = require('..'); 2 | var env = new lmdb.Env(); 3 | env.open({ 4 | // Path to the environment 5 | path: "./testdata", 6 | // Maximum number of databases 7 | maxDbs: 10 8 | }); 9 | 10 | function createTestDb(dbName) { 11 | // Ensure that the database is empty 12 | var dbi = env.openDbi({ 13 | name: dbName, 14 | create: true 15 | }); 16 | dbi.drop(); 17 | dbi = env.openDbi({ 18 | name: dbName, 19 | create: true 20 | }); 21 | 22 | // Write test values 23 | 24 | var txn0 = env.beginTxn(); 25 | txn0.putString(dbi, "a", "Helló1"); 26 | txn0.putString(dbi, "b", "Hello2"); 27 | txn0.putNumber(dbi, "c", 43); 28 | /* key 'd' is omitted intentionally */ 29 | txn0.putBinary(dbi, "e", new Buffer("öüóőúéáű")); 30 | txn0.putBoolean(dbi, "f", false); 31 | txn0.putString(dbi, "g", "Hello6"); 32 | txn0.commit(); 33 | console.log("wrote initial values"); 34 | dbi.close() 35 | } 36 | 37 | createTestDb("mydb8.1"); 38 | createTestDb("mydb8.2"); 39 | 40 | var printFunc = function (key, data) { 41 | console.log("-----> key:", key); 42 | console.log("-----> data:", data); 43 | } 44 | 45 | //Open DB1 46 | var dbi1 = env.openDbi({ 47 | name: "mydb8.1" 48 | }); 49 | 50 | // Begin shared readOnly transaction 51 | var txn = env.beginTxn({readOnly: true}); 52 | 53 | // Create cursor for DB1 54 | var cursor1 = new lmdb.Cursor(txn, dbi1); 55 | 56 | console.log("cursor1 - first (expected a)"); 57 | cursor1.goToFirst(); 58 | cursor1.getCurrentString(printFunc); 59 | 60 | console.log("cursor1 - next (expected b)"); 61 | cursor1.goToNext(); 62 | cursor1.getCurrentString(printFunc); 63 | 64 | 65 | //Open DB2 66 | var dbi2 = env.openDbi({ 67 | name: "mydb8.2" 68 | }); 69 | 70 | //txn does not know about dbi2 yet, opening a cursor to it will fail with "Error: Invalid argument" 71 | 72 | //Reset the transaction to make it aware of dbi2 73 | txn.reset() 74 | //Renew the transaction to keep cursor1 valid, without this calls on cursor1 will fail 75 | txn.renew() 76 | 77 | // Create cursor for DB2 78 | var cursor2 = new lmdb.Cursor(txn, dbi2); 79 | 80 | console.log("cursor2 - first (expected a)"); 81 | cursor2.goToFirst(); 82 | cursor2.getCurrentString(printFunc); 83 | 84 | console.log("cursor2 - next (expected b)"); 85 | cursor2.goToNext(); 86 | cursor2.getCurrentString(printFunc); 87 | 88 | 89 | //cursor1 still is at its old position and reads the expected values 90 | console.log("cursor1 - next (expected c)"); 91 | cursor1.goToNext(); 92 | cursor1.getCurrentNumber(printFunc); 93 | 94 | console.log("cursor1 - next (expected e)"); 95 | cursor1.goToNext(); 96 | cursor1.getCurrentBinary(printFunc); 97 | 98 | // Randomly reading on different cursors 99 | console.log("cursor2 - next (expected c)"); 100 | cursor2.goToNext(); 101 | cursor2.getCurrentNumber(printFunc); 102 | 103 | console.log("cursor2 - next (expected e)"); 104 | cursor2.goToNext(); 105 | cursor2.getCurrentBinary(printFunc); 106 | 107 | console.log("cursor1 - prev (expected c)"); 108 | cursor1.goToPrev(); 109 | cursor1.getCurrentNumber(printFunc); 110 | 111 | console.log("cursor1 - last (expected g)"); 112 | cursor1.goToLast(); 113 | cursor1.getCurrentString(printFunc); 114 | 115 | console.log("cursor2 - prev (expected c)"); 116 | cursor2.goToPrev(); 117 | cursor2.getCurrentNumber(printFunc); 118 | 119 | console.log("cursor2 - last (expected g)"); 120 | cursor2.goToLast(); 121 | cursor2.getCurrentString(printFunc); 122 | 123 | 124 | console.log(""); 125 | console.log("cursor1 - now iterating through all the keys"); 126 | 127 | for (var found = cursor1.goToFirst(); found !== null; found = cursor1.goToNext()) { 128 | console.log("-----> key:", found); 129 | } 130 | 131 | console.log(""); 132 | console.log("cursor2 - now iterating through all the keys"); 133 | 134 | for (var found = cursor2.goToFirst(); found !== null; found = cursor2.goToNext()) { 135 | console.log("-----> key:", found); 136 | } 137 | 138 | // Close cursors 139 | cursor1.close(); 140 | cursor2.close(); 141 | 142 | // Commit transaction 143 | txn.commit(); 144 | 145 | dbi1.close(); 146 | dbi2.close(); 147 | 148 | env.close(); 149 | 150 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mtest3.c: -------------------------------------------------------------------------------- 1 | /* mtest3.c - memory-mapped database tester/toy */ 2 | /* 3 | * Copyright 2011-2018 Howard Chu, Symas Corp. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | /* Tests for sorted duplicate DBs */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "lmdb.h" 21 | 22 | #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) 23 | #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) 24 | #define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ 25 | "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) 26 | 27 | int main(int argc,char * argv[]) 28 | { 29 | int i = 0, j = 0, rc; 30 | MDB_env *env; 31 | MDB_dbi dbi; 32 | MDB_val key, data; 33 | MDB_txn *txn; 34 | MDB_stat mst; 35 | MDB_cursor *cursor; 36 | int count; 37 | int *values; 38 | char sval[32]; 39 | char kval[sizeof(int)]; 40 | 41 | srand(time(NULL)); 42 | 43 | memset(sval, 0, sizeof(sval)); 44 | 45 | count = (rand()%384) + 64; 46 | values = (int *)malloc(count*sizeof(int)); 47 | 48 | for(i = 0;i -1; i-= (rand()%5)) { 91 | j++; 92 | txn=NULL; 93 | E(mdb_txn_begin(env, NULL, 0, &txn)); 94 | sprintf(kval, "%03x", values[i & ~0x0f]); 95 | sprintf(sval, "%03x %d foo bar", values[i], values[i]); 96 | key.mv_size = sizeof(int); 97 | key.mv_data = kval; 98 | data.mv_size = sizeof(sval); 99 | data.mv_data = sval; 100 | if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) { 101 | j--; 102 | mdb_txn_abort(txn); 103 | } else { 104 | E(mdb_txn_commit(txn)); 105 | } 106 | } 107 | free(values); 108 | printf("Deleted %d values\n", j); 109 | 110 | E(mdb_env_stat(env, &mst)); 111 | E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); 112 | E(mdb_cursor_open(txn, dbi, &cursor)); 113 | printf("Cursor next\n"); 114 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 115 | printf("key: %.*s, data: %.*s\n", 116 | (int) key.mv_size, (char *) key.mv_data, 117 | (int) data.mv_size, (char *) data.mv_data); 118 | } 119 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 120 | printf("Cursor prev\n"); 121 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { 122 | printf("key: %.*s, data: %.*s\n", 123 | (int) key.mv_size, (char *) key.mv_data, 124 | (int) data.mv_size, (char *) data.mv_data); 125 | } 126 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 127 | mdb_cursor_close(cursor); 128 | mdb_txn_abort(txn); 129 | 130 | mdb_dbi_close(env, dbi); 131 | mdb_env_close(env); 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mtest5.c: -------------------------------------------------------------------------------- 1 | /* mtest5.c - memory-mapped database tester/toy */ 2 | /* 3 | * Copyright 2011-2018 Howard Chu, Symas Corp. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | /* Tests for sorted duplicate DBs using cursor_put */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "lmdb.h" 21 | 22 | #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) 23 | #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) 24 | #define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ 25 | "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) 26 | 27 | int main(int argc,char * argv[]) 28 | { 29 | int i = 0, j = 0, rc; 30 | MDB_env *env; 31 | MDB_dbi dbi; 32 | MDB_val key, data; 33 | MDB_txn *txn; 34 | MDB_stat mst; 35 | MDB_cursor *cursor; 36 | int count; 37 | int *values; 38 | char sval[32]; 39 | char kval[sizeof(int)]; 40 | 41 | srand(time(NULL)); 42 | 43 | memset(sval, 0, sizeof(sval)); 44 | 45 | count = (rand()%384) + 64; 46 | values = (int *)malloc(count*sizeof(int)); 47 | 48 | for(i = 0;i -1; i-= (rand()%5)) { 93 | j++; 94 | txn=NULL; 95 | E(mdb_txn_begin(env, NULL, 0, &txn)); 96 | sprintf(kval, "%03x", values[i & ~0x0f]); 97 | sprintf(sval, "%03x %d foo bar", values[i], values[i]); 98 | key.mv_size = sizeof(int); 99 | key.mv_data = kval; 100 | data.mv_size = sizeof(sval); 101 | data.mv_data = sval; 102 | if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) { 103 | j--; 104 | mdb_txn_abort(txn); 105 | } else { 106 | E(mdb_txn_commit(txn)); 107 | } 108 | } 109 | free(values); 110 | printf("Deleted %d values\n", j); 111 | 112 | E(mdb_env_stat(env, &mst)); 113 | E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); 114 | E(mdb_cursor_open(txn, dbi, &cursor)); 115 | printf("Cursor next\n"); 116 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 117 | printf("key: %.*s, data: %.*s\n", 118 | (int) key.mv_size, (char *) key.mv_data, 119 | (int) data.mv_size, (char *) data.mv_data); 120 | } 121 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 122 | printf("Cursor prev\n"); 123 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { 124 | printf("key: %.*s, data: %.*s\n", 125 | (int) key.mv_size, (char *) key.mv_data, 126 | (int) data.mv_size, (char *) data.mv_data); 127 | } 128 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 129 | mdb_cursor_close(cursor); 130 | mdb_txn_abort(txn); 131 | 132 | mdb_dbi_close(env, dbi); 133 | mdb_env_close(env); 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mtest6.c: -------------------------------------------------------------------------------- 1 | /* mtest6.c - memory-mapped database tester/toy */ 2 | /* 3 | * Copyright 2011-2018 Howard Chu, Symas Corp. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | /* Tests for DB splits and merges */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "lmdb.h" 21 | 22 | #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) 23 | #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) 24 | #define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ 25 | "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) 26 | 27 | char dkbuf[1024]; 28 | 29 | int main(int argc,char * argv[]) 30 | { 31 | int i = 0, j = 0, rc; 32 | MDB_env *env; 33 | MDB_dbi dbi; 34 | MDB_val key, data, sdata; 35 | MDB_txn *txn; 36 | MDB_stat mst; 37 | MDB_cursor *cursor; 38 | int count; 39 | int *values; 40 | long kval; 41 | char *sval; 42 | 43 | srand(time(NULL)); 44 | 45 | E(mdb_env_create(&env)); 46 | E(mdb_env_set_mapsize(env, 10485760)); 47 | E(mdb_env_set_maxdbs(env, 4)); 48 | E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664)); 49 | 50 | E(mdb_txn_begin(env, NULL, 0, &txn)); 51 | E(mdb_dbi_open(txn, "id6", MDB_CREATE|MDB_INTEGERKEY, &dbi)); 52 | E(mdb_cursor_open(txn, dbi, &cursor)); 53 | E(mdb_stat(txn, dbi, &mst)); 54 | 55 | sval = calloc(1, mst.ms_psize / 4); 56 | key.mv_size = sizeof(long); 57 | key.mv_data = &kval; 58 | sdata.mv_size = mst.ms_psize / 4 - 30; 59 | sdata.mv_data = sval; 60 | 61 | printf("Adding 12 values, should yield 3 splits\n"); 62 | for (i=0;i<12;i++) { 63 | kval = i*5; 64 | sprintf(sval, "%08x", kval); 65 | data = sdata; 66 | (void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE)); 67 | } 68 | printf("Adding 12 more values, should yield 3 splits\n"); 69 | for (i=0;i<12;i++) { 70 | kval = i*5+4; 71 | sprintf(sval, "%08x", kval); 72 | data = sdata; 73 | (void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE)); 74 | } 75 | printf("Adding 12 more values, should yield 3 splits\n"); 76 | for (i=0;i<12;i++) { 77 | kval = i*5+1; 78 | sprintf(sval, "%08x", kval); 79 | data = sdata; 80 | (void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE)); 81 | } 82 | E(mdb_cursor_get(cursor, &key, &data, MDB_FIRST)); 83 | 84 | do { 85 | printf("key: %p %s, data: %p %.*s\n", 86 | key.mv_data, mdb_dkey(&key, dkbuf), 87 | data.mv_data, (int) data.mv_size, (char *) data.mv_data); 88 | } while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0); 89 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 90 | mdb_cursor_close(cursor); 91 | mdb_txn_commit(txn); 92 | 93 | #if 0 94 | j=0; 95 | 96 | for (i= count - 1; i > -1; i-= (rand()%5)) { 97 | j++; 98 | txn=NULL; 99 | E(mdb_txn_begin(env, NULL, 0, &txn)); 100 | sprintf(kval, "%03x", values[i & ~0x0f]); 101 | sprintf(sval, "%03x %d foo bar", values[i], values[i]); 102 | key.mv_size = sizeof(int); 103 | key.mv_data = kval; 104 | data.mv_size = sizeof(sval); 105 | data.mv_data = sval; 106 | if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) { 107 | j--; 108 | mdb_txn_abort(txn); 109 | } else { 110 | E(mdb_txn_commit(txn)); 111 | } 112 | } 113 | free(values); 114 | printf("Deleted %d values\n", j); 115 | 116 | E(mdb_env_stat(env, &mst)); 117 | E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); 118 | E(mdb_cursor_open(txn, dbi, &cursor)); 119 | printf("Cursor next\n"); 120 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 121 | printf("key: %.*s, data: %.*s\n", 122 | (int) key.mv_size, (char *) key.mv_data, 123 | (int) data.mv_size, (char *) data.mv_data); 124 | } 125 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 126 | printf("Cursor prev\n"); 127 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { 128 | printf("key: %.*s, data: %.*s\n", 129 | (int) key.mv_size, (char *) key.mv_data, 130 | (int) data.mv_size, (char *) data.mv_data); 131 | } 132 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 133 | mdb_cursor_close(cursor); 134 | mdb_txn_abort(txn); 135 | 136 | mdb_dbi_close(env, dbi); 137 | #endif 138 | mdb_env_close(env); 139 | 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /examples/advanced1-indexing.js: -------------------------------------------------------------------------------- 1 | 2 | // Indexing engine example 3 | // ---------- 4 | // 5 | // The purpose of this example is to show how to implement an indexing engine using node-lmdb. 6 | // It's not intended to be feature-full, just enough to give you an idea how to use the LMDB API. 7 | // 8 | // Limitations of this indexing engine: 9 | // * Doesn't support fields or advanced querying 10 | // * Tokenization is very simple, no stemming or stop words 11 | // * No phrase search, can only search for single words 12 | // 13 | // But hey, it's only ~100 LOC, so we're still cool :) 14 | 15 | 16 | // Indexing engine (implemented with the module pattern) 17 | var indexingEngine = (function() { 18 | var lmdb, env, dbi; 19 | 20 | // initializer function, call this before using the index 21 | var init = function() { 22 | lmdb = require('..'); 23 | env = new lmdb.Env(); 24 | env.open({ 25 | path: "./testdata", 26 | maxDbs: 10 27 | }); 28 | 29 | dbi = env.openDbi({ 30 | name: "example-advanced-indexing", 31 | create: true, 32 | dupSort: true 33 | }); 34 | }; 35 | 36 | // destroy function, call this when you no longer need the index 37 | var destroy = function() { 38 | dbi.close(); 39 | env.close(); 40 | }; 41 | 42 | // simple tokenizer 43 | var tokenize = function(document) { 44 | var tokens = []; 45 | for (var i in document) { 46 | if (document.hasOwnProperty(i) && typeof(document[i]) === "string") { 47 | var stripped = document[i].replace(/[\.!,?\[\]\\]/g, " "); 48 | var splitted = stripped.split(" "); 49 | 50 | for (var j = splitted.length; j--; ) { 51 | if (splitted[j] !== '' && tokens.indexOf(splitted[j]) === -1) { 52 | tokens.push(splitted[j].toLowerCase()); 53 | } 54 | } 55 | } 56 | } 57 | return tokens; 58 | }; 59 | 60 | // adds a document to the index 61 | var addDocument = function(document) { 62 | if (typeof(document.id) !== "number") { 63 | throw new Error("document must have an id property"); 64 | } 65 | 66 | var tokens = tokenize(document); 67 | var txn = env.beginTxn(); 68 | 69 | for (var i = tokens.length; i--; ) { 70 | //console.log(tokens[i], document.id); 71 | txn.putNumber(dbi, tokens[i], document.id); 72 | } 73 | 74 | txn.commit(); 75 | }; 76 | 77 | // adds multiple documents to the index 78 | var addDocuments = function(array) { 79 | if (!(array instanceof Array)) { 80 | throw new Error("This function expects an array."); 81 | } 82 | 83 | for (var i = array.length; i--; ) { 84 | addDocument(array[i]); 85 | } 86 | }; 87 | 88 | // performs a search in the index for the given word 89 | var searchForDocuments = function(str) { 90 | str = str.toLowerCase(); 91 | var txn = env.beginTxn({ readOnly: true }); 92 | var cursor = new lmdb.Cursor(txn, dbi); 93 | var results = []; 94 | 95 | // Go the the first occourence of `str` and iterate from there 96 | for (var found = cursor.goToRange(str); found !== null; found = cursor.goToNext()) { 97 | // Stop the loop if the current key is no longer what we're looking for 98 | if (found !== str) 99 | break; 100 | 101 | // Get current data item and push it to results 102 | cursor.getCurrentNumber(function(key, data) { 103 | results.push(data); 104 | }); 105 | } 106 | 107 | cursor.close(); 108 | txn.abort(); 109 | 110 | return results; 111 | }; 112 | 113 | // The object we return here is the public API of the indexing engine 114 | return Object.freeze({ 115 | init: init, 116 | destroy: destroy, 117 | addDocument: addDocument, 118 | addDocuments: addDocuments, 119 | searchForDocuments: searchForDocuments 120 | }); 121 | })(); 122 | 123 | indexingEngine.init(); 124 | 125 | var docs = []; 126 | docs.push({ 127 | id: 1, 128 | title: "Lord of the Rings", 129 | text: "Great book by J.R.R. Tolkien!" 130 | }); 131 | docs.push({ 132 | id: 2, 133 | title: "A Game of Thrones", 134 | text: "Fantasy book by George R.R. Martin, which also has a television adaptation." 135 | }); 136 | docs.push({ 137 | id: 3, 138 | title: "Caves of Steel", 139 | text: "Science fiction by the great writer Isaac Asimov" 140 | }); 141 | 142 | for (var i = docs.length; i--; ) { 143 | console.log("document details:", JSON.stringify(docs[i])); 144 | } 145 | 146 | indexingEngine.addDocuments(docs); 147 | console.log("successfully added documents to index"); 148 | 149 | var s; 150 | 151 | console.log("search:", s = "Great", indexingEngine.searchForDocuments(s)); 152 | console.log("search:", s = "Asimov", indexingEngine.searchForDocuments(s)); 153 | console.log("search:", s = "of", indexingEngine.searchForDocuments(s)); 154 | console.log("search:", s = "Lord", indexingEngine.searchForDocuments(s)); 155 | 156 | indexingEngine.destroy(); 157 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mtest4.c: -------------------------------------------------------------------------------- 1 | /* mtest4.c - memory-mapped database tester/toy */ 2 | /* 3 | * Copyright 2011-2018 Howard Chu, Symas Corp. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | 15 | /* Tests for sorted duplicate DBs with fixed-size keys */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "lmdb.h" 21 | 22 | #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) 23 | #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) 24 | #define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ 25 | "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) 26 | 27 | int main(int argc,char * argv[]) 28 | { 29 | int i = 0, j = 0, rc; 30 | MDB_env *env; 31 | MDB_dbi dbi; 32 | MDB_val key, data; 33 | MDB_txn *txn; 34 | MDB_stat mst; 35 | MDB_cursor *cursor; 36 | int count; 37 | int *values; 38 | char sval[8]; 39 | char kval[sizeof(int)]; 40 | 41 | memset(sval, 0, sizeof(sval)); 42 | 43 | count = 510; 44 | values = (int *)malloc(count*sizeof(int)); 45 | 46 | for(i = 0;i -1; i-= (rand()%3)) { 127 | j++; 128 | txn=NULL; 129 | E(mdb_txn_begin(env, NULL, 0, &txn)); 130 | sprintf(sval, "%07x", values[i]); 131 | key.mv_size = sizeof(int); 132 | key.mv_data = kval; 133 | data.mv_size = sizeof(sval); 134 | data.mv_data = sval; 135 | if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) { 136 | j--; 137 | mdb_txn_abort(txn); 138 | } else { 139 | E(mdb_txn_commit(txn)); 140 | } 141 | } 142 | free(values); 143 | printf("Deleted %d values\n", j); 144 | 145 | E(mdb_env_stat(env, &mst)); 146 | E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); 147 | E(mdb_cursor_open(txn, dbi, &cursor)); 148 | printf("Cursor next\n"); 149 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 150 | printf("key: %.*s, data: %.*s\n", 151 | (int) key.mv_size, (char *) key.mv_data, 152 | (int) data.mv_size, (char *) data.mv_data); 153 | } 154 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 155 | printf("Cursor prev\n"); 156 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { 157 | printf("key: %.*s, data: %.*s\n", 158 | (int) key.mv_size, (char *) key.mv_data, 159 | (int) data.mv_size, (char *) data.mv_data); 160 | } 161 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 162 | mdb_cursor_close(cursor); 163 | mdb_txn_abort(txn); 164 | 165 | mdb_dbi_close(env, dbi); 166 | mdb_env_close(env); 167 | return 0; 168 | } 169 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mtest.c: -------------------------------------------------------------------------------- 1 | /* mtest.c - memory-mapped database tester/toy */ 2 | /* 3 | * Copyright 2011-2018 Howard Chu, Symas Corp. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include "lmdb.h" 18 | 19 | #define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) 20 | #define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) 21 | #define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ 22 | "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) 23 | 24 | int main(int argc,char * argv[]) 25 | { 26 | int i = 0, j = 0, rc; 27 | MDB_env *env; 28 | MDB_dbi dbi; 29 | MDB_val key, data; 30 | MDB_txn *txn; 31 | MDB_stat mst; 32 | MDB_cursor *cursor, *cur2; 33 | MDB_cursor_op op; 34 | int count; 35 | int *values; 36 | char sval[32] = ""; 37 | 38 | srand(time(NULL)); 39 | 40 | count = (rand()%384) + 64; 41 | values = (int *)malloc(count*sizeof(int)); 42 | 43 | for(i = 0;i in each iteration, since MDB_NOOVERWRITE may modify it */ 62 | data.mv_size = sizeof(sval); 63 | data.mv_data = sval; 64 | if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE))) { 65 | j++; 66 | data.mv_size = sizeof(sval); 67 | data.mv_data = sval; 68 | } 69 | } 70 | if (j) printf("%d duplicates skipped\n", j); 71 | E(mdb_txn_commit(txn)); 72 | E(mdb_env_stat(env, &mst)); 73 | 74 | E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); 75 | E(mdb_cursor_open(txn, dbi, &cursor)); 76 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 77 | printf("key: %p %.*s, data: %p %.*s\n", 78 | key.mv_data, (int) key.mv_size, (char *) key.mv_data, 79 | data.mv_data, (int) data.mv_size, (char *) data.mv_data); 80 | } 81 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 82 | mdb_cursor_close(cursor); 83 | mdb_txn_abort(txn); 84 | 85 | j=0; 86 | key.mv_data = sval; 87 | for (i= count - 1; i > -1; i-= (rand()%5)) { 88 | j++; 89 | txn=NULL; 90 | E(mdb_txn_begin(env, NULL, 0, &txn)); 91 | sprintf(sval, "%03x ", values[i]); 92 | if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, NULL))) { 93 | j--; 94 | mdb_txn_abort(txn); 95 | } else { 96 | E(mdb_txn_commit(txn)); 97 | } 98 | } 99 | free(values); 100 | printf("Deleted %d values\n", j); 101 | 102 | E(mdb_env_stat(env, &mst)); 103 | E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); 104 | E(mdb_cursor_open(txn, dbi, &cursor)); 105 | printf("Cursor next\n"); 106 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 107 | printf("key: %.*s, data: %.*s\n", 108 | (int) key.mv_size, (char *) key.mv_data, 109 | (int) data.mv_size, (char *) data.mv_data); 110 | } 111 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 112 | printf("Cursor last\n"); 113 | E(mdb_cursor_get(cursor, &key, &data, MDB_LAST)); 114 | printf("key: %.*s, data: %.*s\n", 115 | (int) key.mv_size, (char *) key.mv_data, 116 | (int) data.mv_size, (char *) data.mv_data); 117 | printf("Cursor prev\n"); 118 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { 119 | printf("key: %.*s, data: %.*s\n", 120 | (int) key.mv_size, (char *) key.mv_data, 121 | (int) data.mv_size, (char *) data.mv_data); 122 | } 123 | CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); 124 | printf("Cursor last/prev\n"); 125 | E(mdb_cursor_get(cursor, &key, &data, MDB_LAST)); 126 | printf("key: %.*s, data: %.*s\n", 127 | (int) key.mv_size, (char *) key.mv_data, 128 | (int) data.mv_size, (char *) data.mv_data); 129 | E(mdb_cursor_get(cursor, &key, &data, MDB_PREV)); 130 | printf("key: %.*s, data: %.*s\n", 131 | (int) key.mv_size, (char *) key.mv_data, 132 | (int) data.mv_size, (char *) data.mv_data); 133 | 134 | mdb_cursor_close(cursor); 135 | mdb_txn_abort(txn); 136 | 137 | printf("Deleting with cursor\n"); 138 | E(mdb_txn_begin(env, NULL, 0, &txn)); 139 | E(mdb_cursor_open(txn, dbi, &cur2)); 140 | for (i=0; i<50; i++) { 141 | if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, MDB_NEXT))) 142 | break; 143 | printf("key: %p %.*s, data: %p %.*s\n", 144 | key.mv_data, (int) key.mv_size, (char *) key.mv_data, 145 | data.mv_data, (int) data.mv_size, (char *) data.mv_data); 146 | E(mdb_del(txn, dbi, &key, NULL)); 147 | } 148 | 149 | printf("Restarting cursor in txn\n"); 150 | for (op=MDB_FIRST, i=0; i<=32; op=MDB_NEXT, i++) { 151 | if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, op))) 152 | break; 153 | printf("key: %p %.*s, data: %p %.*s\n", 154 | key.mv_data, (int) key.mv_size, (char *) key.mv_data, 155 | data.mv_data, (int) data.mv_size, (char *) data.mv_data); 156 | } 157 | mdb_cursor_close(cur2); 158 | E(mdb_txn_commit(txn)); 159 | 160 | printf("Restarting cursor outside txn\n"); 161 | E(mdb_txn_begin(env, NULL, 0, &txn)); 162 | E(mdb_cursor_open(txn, dbi, &cursor)); 163 | for (op=MDB_FIRST, i=0; i<=32; op=MDB_NEXT, i++) { 164 | if (RES(MDB_NOTFOUND, mdb_cursor_get(cursor, &key, &data, op))) 165 | break; 166 | printf("key: %p %.*s, data: %p %.*s\n", 167 | key.mv_data, (int) key.mv_size, (char *) key.mv_data, 168 | data.mv_data, (int) data.mv_size, (char *) data.mv_data); 169 | } 170 | mdb_cursor_close(cursor); 171 | mdb_txn_abort(txn); 172 | 173 | mdb_dbi_close(env, dbi); 174 | mdb_env_close(env); 175 | 176 | return 0; 177 | } 178 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/midl.h: -------------------------------------------------------------------------------- 1 | /** @file midl.h 2 | * @brief LMDB ID List header file. 3 | * 4 | * This file was originally part of back-bdb but has been 5 | * modified for use in libmdb. Most of the macros defined 6 | * in this file are unused, just left over from the original. 7 | * 8 | * This file is only used internally in libmdb and its definitions 9 | * are not exposed publicly. 10 | */ 11 | /* $OpenLDAP$ */ 12 | /* This work is part of OpenLDAP Software . 13 | * 14 | * Copyright 2000-2019 The OpenLDAP Foundation. 15 | * Portions Copyright 2001-2018 Howard Chu, Symas Corp. 16 | * All rights reserved. 17 | * 18 | * Redistribution and use in source and binary forms, with or without 19 | * modification, are permitted only as authorized by the OpenLDAP 20 | * Public License. 21 | * 22 | * A copy of this license is available in the file LICENSE in the 23 | * top-level directory of the distribution or, alternatively, at 24 | * . 25 | */ 26 | 27 | #ifndef _MDB_MIDL_H_ 28 | #define _MDB_MIDL_H_ 29 | 30 | #include 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | /** @defgroup internal LMDB Internals 37 | * @{ 38 | */ 39 | 40 | /** @defgroup idls ID List Management 41 | * @{ 42 | */ 43 | /** A generic unsigned ID number. These were entryIDs in back-bdb. 44 | * Preferably it should have the same size as a pointer. 45 | */ 46 | typedef size_t MDB_ID; 47 | 48 | /** An IDL is an ID List, a sorted array of IDs. The first 49 | * element of the array is a counter for how many actual 50 | * IDs are in the list. In the original back-bdb code, IDLs are 51 | * sorted in ascending order. For libmdb IDLs are sorted in 52 | * descending order. 53 | */ 54 | typedef MDB_ID *MDB_IDL; 55 | 56 | /* IDL sizes - likely should be even bigger 57 | * limiting factors: sizeof(ID), thread stack size 58 | */ 59 | #define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */ 60 | #define MDB_IDL_DB_SIZE (1<. 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "lmdb.h" 19 | 20 | #ifdef _WIN32 21 | #define Z "I" 22 | #else 23 | #define Z "z" 24 | #endif 25 | 26 | static void prstat(MDB_stat *ms) 27 | { 28 | #if 0 29 | printf(" Page size: %u\n", ms->ms_psize); 30 | #endif 31 | printf(" Tree depth: %u\n", ms->ms_depth); 32 | printf(" Branch pages: %"Z"u\n", ms->ms_branch_pages); 33 | printf(" Leaf pages: %"Z"u\n", ms->ms_leaf_pages); 34 | printf(" Overflow pages: %"Z"u\n", ms->ms_overflow_pages); 35 | printf(" Entries: %"Z"u\n", ms->ms_entries); 36 | } 37 | 38 | static void usage(char *prog) 39 | { 40 | fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-a|-s subdb] dbpath\n", prog); 41 | exit(EXIT_FAILURE); 42 | } 43 | 44 | int main(int argc, char *argv[]) 45 | { 46 | int i, rc; 47 | MDB_env *env; 48 | MDB_txn *txn; 49 | MDB_dbi dbi; 50 | MDB_stat mst; 51 | MDB_envinfo mei; 52 | char *prog = argv[0]; 53 | char *envname; 54 | char *subname = NULL; 55 | int alldbs = 0, envinfo = 0, envflags = 0, freinfo = 0, rdrinfo = 0; 56 | 57 | if (argc < 2) { 58 | usage(prog); 59 | } 60 | 61 | /* -a: print stat of main DB and all subDBs 62 | * -s: print stat of only the named subDB 63 | * -e: print env info 64 | * -f: print freelist info 65 | * -r: print reader info 66 | * -n: use NOSUBDIR flag on env_open 67 | * -V: print version and exit 68 | * (default) print stat of only the main DB 69 | */ 70 | while ((i = getopt(argc, argv, "Vaefnrs:")) != EOF) { 71 | switch(i) { 72 | case 'V': 73 | printf("%s\n", MDB_VERSION_STRING); 74 | exit(0); 75 | break; 76 | case 'a': 77 | if (subname) 78 | usage(prog); 79 | alldbs++; 80 | break; 81 | case 'e': 82 | envinfo++; 83 | break; 84 | case 'f': 85 | freinfo++; 86 | break; 87 | case 'n': 88 | envflags |= MDB_NOSUBDIR; 89 | break; 90 | case 'r': 91 | rdrinfo++; 92 | break; 93 | case 's': 94 | if (alldbs) 95 | usage(prog); 96 | subname = optarg; 97 | break; 98 | default: 99 | usage(prog); 100 | } 101 | } 102 | 103 | if (optind != argc - 1) 104 | usage(prog); 105 | 106 | envname = argv[optind]; 107 | rc = mdb_env_create(&env); 108 | if (rc) { 109 | fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); 110 | return EXIT_FAILURE; 111 | } 112 | 113 | if (alldbs || subname) { 114 | mdb_env_set_maxdbs(env, 4); 115 | } 116 | 117 | rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664); 118 | if (rc) { 119 | fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); 120 | goto env_close; 121 | } 122 | 123 | if (envinfo) { 124 | (void)mdb_env_stat(env, &mst); 125 | (void)mdb_env_info(env, &mei); 126 | printf("Environment Info\n"); 127 | printf(" Map address: %p\n", mei.me_mapaddr); 128 | printf(" Map size: %"Z"u\n", mei.me_mapsize); 129 | printf(" Page size: %u\n", mst.ms_psize); 130 | printf(" Max pages: %"Z"u\n", mei.me_mapsize / mst.ms_psize); 131 | printf(" Number of pages used: %"Z"u\n", mei.me_last_pgno+1); 132 | printf(" Last transaction ID: %"Z"u\n", mei.me_last_txnid); 133 | printf(" Max readers: %u\n", mei.me_maxreaders); 134 | printf(" Number of readers used: %u\n", mei.me_numreaders); 135 | } 136 | 137 | if (rdrinfo) { 138 | printf("Reader Table Status\n"); 139 | rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout); 140 | if (rdrinfo > 1) { 141 | int dead; 142 | mdb_reader_check(env, &dead); 143 | printf(" %d stale readers cleared.\n", dead); 144 | rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout); 145 | } 146 | if (!(subname || alldbs || freinfo)) 147 | goto env_close; 148 | } 149 | 150 | rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); 151 | if (rc) { 152 | fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); 153 | goto env_close; 154 | } 155 | 156 | if (freinfo) { 157 | MDB_cursor *cursor; 158 | MDB_val key, data; 159 | size_t pages = 0, *iptr; 160 | 161 | printf("Freelist Status\n"); 162 | dbi = 0; 163 | rc = mdb_cursor_open(txn, dbi, &cursor); 164 | if (rc) { 165 | fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); 166 | goto txn_abort; 167 | } 168 | rc = mdb_stat(txn, dbi, &mst); 169 | if (rc) { 170 | fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); 171 | goto txn_abort; 172 | } 173 | prstat(&mst); 174 | while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { 175 | iptr = data.mv_data; 176 | pages += *iptr; 177 | if (freinfo > 1) { 178 | char *bad = ""; 179 | size_t pg, prev; 180 | ssize_t i, j, span = 0; 181 | j = *iptr++; 182 | for (i = j, prev = 1; --i >= 0; ) { 183 | pg = iptr[i]; 184 | if (pg <= prev) 185 | bad = " [bad sequence]"; 186 | prev = pg; 187 | pg += span; 188 | for (; i >= span && iptr[i-span] == pg; span++, pg++) ; 189 | } 190 | printf(" Transaction %"Z"u, %"Z"d pages, maxspan %"Z"d%s\n", 191 | *(size_t *)key.mv_data, j, span, bad); 192 | if (freinfo > 2) { 193 | for (--j; j >= 0; ) { 194 | pg = iptr[j]; 195 | for (span=1; --j >= 0 && iptr[j] == pg+span; span++) ; 196 | printf(span>1 ? " %9"Z"u[%"Z"d]\n" : " %9"Z"u\n", 197 | pg, span); 198 | } 199 | } 200 | } 201 | } 202 | mdb_cursor_close(cursor); 203 | printf(" Free pages: %"Z"u\n", pages); 204 | } 205 | 206 | rc = mdb_open(txn, subname, 0, &dbi); 207 | if (rc) { 208 | fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc)); 209 | goto txn_abort; 210 | } 211 | 212 | rc = mdb_stat(txn, dbi, &mst); 213 | if (rc) { 214 | fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); 215 | goto txn_abort; 216 | } 217 | printf("Status of %s\n", subname ? subname : "Main DB"); 218 | prstat(&mst); 219 | 220 | if (alldbs) { 221 | MDB_cursor *cursor; 222 | MDB_val key; 223 | 224 | rc = mdb_cursor_open(txn, dbi, &cursor); 225 | if (rc) { 226 | fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); 227 | goto txn_abort; 228 | } 229 | while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) { 230 | char *str; 231 | MDB_dbi db2; 232 | if (memchr(key.mv_data, '\0', key.mv_size)) 233 | continue; 234 | str = malloc(key.mv_size+1); 235 | memcpy(str, key.mv_data, key.mv_size); 236 | str[key.mv_size] = '\0'; 237 | rc = mdb_open(txn, str, 0, &db2); 238 | if (rc == MDB_SUCCESS) 239 | printf("Status of %s\n", str); 240 | free(str); 241 | if (rc) continue; 242 | rc = mdb_stat(txn, db2, &mst); 243 | if (rc) { 244 | fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); 245 | goto txn_abort; 246 | } 247 | prstat(&mst); 248 | mdb_close(env, db2); 249 | } 250 | mdb_cursor_close(cursor); 251 | } 252 | 253 | if (rc == MDB_NOTFOUND) 254 | rc = MDB_SUCCESS; 255 | 256 | mdb_close(env, dbi); 257 | txn_abort: 258 | mdb_txn_abort(txn); 259 | env_close: 260 | mdb_env_close(env); 261 | 262 | return rc ? EXIT_FAILURE : EXIT_SUCCESS; 263 | } 264 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/mdb_dump.c: -------------------------------------------------------------------------------- 1 | /* mdb_dump.c - memory-mapped database dump tool */ 2 | /* 3 | * Copyright 2011-2018 Howard Chu, Symas Corp. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted only as authorized by the OpenLDAP 8 | * Public License. 9 | * 10 | * A copy of this license is available in the file LICENSE in the 11 | * top-level directory of the distribution or, alternatively, at 12 | * . 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "lmdb.h" 22 | 23 | #ifdef _WIN32 24 | #define Z "I" 25 | #else 26 | #define Z "z" 27 | #endif 28 | 29 | #define PRINT 1 30 | static int mode; 31 | 32 | typedef struct flagbit { 33 | int bit; 34 | char *name; 35 | } flagbit; 36 | 37 | flagbit dbflags[] = { 38 | { MDB_REVERSEKEY, "reversekey" }, 39 | { MDB_DUPSORT, "dupsort" }, 40 | { MDB_INTEGERKEY, "integerkey" }, 41 | { MDB_DUPFIXED, "dupfixed" }, 42 | { MDB_INTEGERDUP, "integerdup" }, 43 | { MDB_REVERSEDUP, "reversedup" }, 44 | { 0, NULL } 45 | }; 46 | 47 | static volatile sig_atomic_t gotsig; 48 | 49 | static void dumpsig( int sig ) 50 | { 51 | gotsig=1; 52 | } 53 | 54 | static const char hexc[] = "0123456789abcdef"; 55 | 56 | static void hex(unsigned char c) 57 | { 58 | putchar(hexc[c >> 4]); 59 | putchar(hexc[c & 0xf]); 60 | } 61 | 62 | static void text(MDB_val *v) 63 | { 64 | unsigned char *c, *end; 65 | 66 | putchar(' '); 67 | c = v->mv_data; 68 | end = c + v->mv_size; 69 | while (c < end) { 70 | if (isprint(*c)) { 71 | putchar(*c); 72 | } else { 73 | putchar('\\'); 74 | hex(*c); 75 | } 76 | c++; 77 | } 78 | putchar('\n'); 79 | } 80 | 81 | static void byte(MDB_val *v) 82 | { 83 | unsigned char *c, *end; 84 | 85 | putchar(' '); 86 | c = v->mv_data; 87 | end = c + v->mv_size; 88 | while (c < end) { 89 | hex(*c++); 90 | } 91 | putchar('\n'); 92 | } 93 | 94 | /* Dump in BDB-compatible format */ 95 | static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name) 96 | { 97 | MDB_cursor *mc; 98 | MDB_stat ms; 99 | MDB_val key, data; 100 | MDB_envinfo info; 101 | unsigned int flags; 102 | int rc, i; 103 | 104 | rc = mdb_dbi_flags(txn, dbi, &flags); 105 | if (rc) return rc; 106 | 107 | rc = mdb_stat(txn, dbi, &ms); 108 | if (rc) return rc; 109 | 110 | rc = mdb_env_info(mdb_txn_env(txn), &info); 111 | if (rc) return rc; 112 | 113 | printf("VERSION=3\n"); 114 | printf("format=%s\n", mode & PRINT ? "print" : "bytevalue"); 115 | if (name) 116 | printf("database=%s\n", name); 117 | printf("type=btree\n"); 118 | printf("mapsize=%" Z "u\n", info.me_mapsize); 119 | if (info.me_mapaddr) 120 | printf("mapaddr=%p\n", info.me_mapaddr); 121 | printf("maxreaders=%u\n", info.me_maxreaders); 122 | 123 | if (flags & MDB_DUPSORT) 124 | printf("duplicates=1\n"); 125 | 126 | for (i=0; dbflags[i].bit; i++) 127 | if (flags & dbflags[i].bit) 128 | printf("%s=1\n", dbflags[i].name); 129 | 130 | printf("db_pagesize=%d\n", ms.ms_psize); 131 | printf("HEADER=END\n"); 132 | 133 | rc = mdb_cursor_open(txn, dbi, &mc); 134 | if (rc) return rc; 135 | 136 | while ((rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT) == MDB_SUCCESS)) { 137 | if (gotsig) { 138 | rc = EINTR; 139 | break; 140 | } 141 | if (mode & PRINT) { 142 | text(&key); 143 | text(&data); 144 | } else { 145 | byte(&key); 146 | byte(&data); 147 | } 148 | } 149 | printf("DATA=END\n"); 150 | if (rc == MDB_NOTFOUND) 151 | rc = MDB_SUCCESS; 152 | 153 | return rc; 154 | } 155 | 156 | static void usage(char *prog) 157 | { 158 | fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb] dbpath\n", prog); 159 | exit(EXIT_FAILURE); 160 | } 161 | 162 | int main(int argc, char *argv[]) 163 | { 164 | int i, rc; 165 | MDB_env *env; 166 | MDB_txn *txn; 167 | MDB_dbi dbi; 168 | char *prog = argv[0]; 169 | char *envname; 170 | char *subname = NULL; 171 | int alldbs = 0, envflags = 0, list = 0; 172 | 173 | if (argc < 2) { 174 | usage(prog); 175 | } 176 | 177 | /* -a: dump main DB and all subDBs 178 | * -s: dump only the named subDB 179 | * -n: use NOSUBDIR flag on env_open 180 | * -p: use printable characters 181 | * -f: write to file instead of stdout 182 | * -V: print version and exit 183 | * (default) dump only the main DB 184 | */ 185 | while ((i = getopt(argc, argv, "af:lnps:V")) != EOF) { 186 | switch(i) { 187 | case 'V': 188 | printf("%s\n", MDB_VERSION_STRING); 189 | exit(0); 190 | break; 191 | case 'l': 192 | list = 1; 193 | /*FALLTHROUGH*/; 194 | case 'a': 195 | if (subname) 196 | usage(prog); 197 | alldbs++; 198 | break; 199 | case 'f': 200 | if (freopen(optarg, "w", stdout) == NULL) { 201 | fprintf(stderr, "%s: %s: reopen: %s\n", 202 | prog, optarg, strerror(errno)); 203 | exit(EXIT_FAILURE); 204 | } 205 | break; 206 | case 'n': 207 | envflags |= MDB_NOSUBDIR; 208 | break; 209 | case 'p': 210 | mode |= PRINT; 211 | break; 212 | case 's': 213 | if (alldbs) 214 | usage(prog); 215 | subname = optarg; 216 | break; 217 | default: 218 | usage(prog); 219 | } 220 | } 221 | 222 | if (optind != argc - 1) 223 | usage(prog); 224 | 225 | #ifdef SIGPIPE 226 | signal(SIGPIPE, dumpsig); 227 | #endif 228 | #ifdef SIGHUP 229 | signal(SIGHUP, dumpsig); 230 | #endif 231 | signal(SIGINT, dumpsig); 232 | signal(SIGTERM, dumpsig); 233 | 234 | envname = argv[optind]; 235 | rc = mdb_env_create(&env); 236 | if (rc) { 237 | fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); 238 | return EXIT_FAILURE; 239 | } 240 | 241 | if (alldbs || subname) { 242 | mdb_env_set_maxdbs(env, 2); 243 | } 244 | 245 | rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664); 246 | if (rc) { 247 | fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); 248 | goto env_close; 249 | } 250 | 251 | rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); 252 | if (rc) { 253 | fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); 254 | goto env_close; 255 | } 256 | 257 | rc = mdb_open(txn, subname, 0, &dbi); 258 | if (rc) { 259 | fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc)); 260 | goto txn_abort; 261 | } 262 | 263 | if (alldbs) { 264 | MDB_cursor *cursor; 265 | MDB_val key; 266 | int count = 0; 267 | 268 | rc = mdb_cursor_open(txn, dbi, &cursor); 269 | if (rc) { 270 | fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); 271 | goto txn_abort; 272 | } 273 | while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) { 274 | char *str; 275 | MDB_dbi db2; 276 | if (memchr(key.mv_data, '\0', key.mv_size)) 277 | continue; 278 | count++; 279 | str = malloc(key.mv_size+1); 280 | memcpy(str, key.mv_data, key.mv_size); 281 | str[key.mv_size] = '\0'; 282 | rc = mdb_open(txn, str, 0, &db2); 283 | if (rc == MDB_SUCCESS) { 284 | if (list) { 285 | printf("%s\n", str); 286 | list++; 287 | } else { 288 | rc = dumpit(txn, db2, str); 289 | if (rc) 290 | break; 291 | } 292 | mdb_close(env, db2); 293 | } 294 | free(str); 295 | if (rc) continue; 296 | } 297 | mdb_cursor_close(cursor); 298 | if (!count) { 299 | fprintf(stderr, "%s: %s does not contain multiple databases\n", prog, envname); 300 | rc = MDB_NOTFOUND; 301 | } else if (rc == MDB_NOTFOUND) { 302 | rc = MDB_SUCCESS; 303 | } 304 | } else { 305 | rc = dumpit(txn, dbi, subname); 306 | } 307 | if (rc && rc != MDB_NOTFOUND) 308 | fprintf(stderr, "%s: %s: %s\n", prog, envname, mdb_strerror(rc)); 309 | 310 | mdb_close(env, dbi); 311 | txn_abort: 312 | mdb_txn_abort(txn); 313 | env_close: 314 | mdb_env_close(env); 315 | 316 | return rc ? EXIT_FAILURE : EXIT_SUCCESS; 317 | } 318 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/midl.c: -------------------------------------------------------------------------------- 1 | /** @file midl.c 2 | * @brief ldap bdb back-end ID List functions */ 3 | /* $OpenLDAP$ */ 4 | /* This work is part of OpenLDAP Software . 5 | * 6 | * Copyright 2000-2019 The OpenLDAP Foundation. 7 | * Portions Copyright 2001-2018 Howard Chu, Symas Corp. 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted only as authorized by the OpenLDAP 12 | * Public License. 13 | * 14 | * A copy of this license is available in the file LICENSE in the 15 | * top-level directory of the distribution or, alternatively, at 16 | * . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "midl.h" 25 | 26 | /** @defgroup internal LMDB Internals 27 | * @{ 28 | */ 29 | /** @defgroup idls ID List Management 30 | * @{ 31 | */ 32 | #define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) ) 33 | 34 | unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id ) 35 | { 36 | /* 37 | * binary search of id in ids 38 | * if found, returns position of id 39 | * if not found, returns first position greater than id 40 | */ 41 | unsigned base = 0; 42 | unsigned cursor = 1; 43 | int val = 0; 44 | unsigned n = ids[0]; 45 | 46 | while( 0 < n ) { 47 | unsigned pivot = n >> 1; 48 | cursor = base + pivot + 1; 49 | val = CMP( ids[cursor], id ); 50 | 51 | if( val < 0 ) { 52 | n = pivot; 53 | 54 | } else if ( val > 0 ) { 55 | base = cursor; 56 | n -= pivot + 1; 57 | 58 | } else { 59 | return cursor; 60 | } 61 | } 62 | 63 | if( val > 0 ) { 64 | ++cursor; 65 | } 66 | return cursor; 67 | } 68 | 69 | #if 0 /* superseded by append/sort */ 70 | int mdb_midl_insert( MDB_IDL ids, MDB_ID id ) 71 | { 72 | unsigned x, i; 73 | 74 | x = mdb_midl_search( ids, id ); 75 | assert( x > 0 ); 76 | 77 | if( x < 1 ) { 78 | /* internal error */ 79 | return -2; 80 | } 81 | 82 | if ( x <= ids[0] && ids[x] == id ) { 83 | /* duplicate */ 84 | assert(0); 85 | return -1; 86 | } 87 | 88 | if ( ++ids[0] >= MDB_IDL_DB_MAX ) { 89 | /* no room */ 90 | --ids[0]; 91 | return -2; 92 | 93 | } else { 94 | /* insert id */ 95 | for (i=ids[0]; i>x; i--) 96 | ids[i] = ids[i-1]; 97 | ids[x] = id; 98 | } 99 | 100 | return 0; 101 | } 102 | #endif 103 | 104 | MDB_IDL mdb_midl_alloc(int num) 105 | { 106 | MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID)); 107 | if (ids) { 108 | *ids++ = num; 109 | *ids = 0; 110 | } 111 | return ids; 112 | } 113 | 114 | void mdb_midl_free(MDB_IDL ids) 115 | { 116 | if (ids) 117 | free(ids-1); 118 | } 119 | 120 | void mdb_midl_shrink( MDB_IDL *idp ) 121 | { 122 | MDB_IDL ids = *idp; 123 | if (*(--ids) > MDB_IDL_UM_MAX && 124 | (ids = realloc(ids, (MDB_IDL_UM_MAX+2) * sizeof(MDB_ID)))) 125 | { 126 | *ids++ = MDB_IDL_UM_MAX; 127 | *idp = ids; 128 | } 129 | } 130 | 131 | static int mdb_midl_grow( MDB_IDL *idp, int num ) 132 | { 133 | MDB_IDL idn = *idp-1; 134 | /* grow it */ 135 | idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID)); 136 | if (!idn) 137 | return ENOMEM; 138 | *idn++ += num; 139 | *idp = idn; 140 | return 0; 141 | } 142 | 143 | int mdb_midl_need( MDB_IDL *idp, unsigned num ) 144 | { 145 | MDB_IDL ids = *idp; 146 | num += ids[0]; 147 | if (num > ids[-1]) { 148 | num = (num + num/4 + (256 + 2)) & -256; 149 | if (!(ids = realloc(ids-1, num * sizeof(MDB_ID)))) 150 | return ENOMEM; 151 | *ids++ = num - 2; 152 | *idp = ids; 153 | } 154 | return 0; 155 | } 156 | 157 | int mdb_midl_append( MDB_IDL *idp, MDB_ID id ) 158 | { 159 | MDB_IDL ids = *idp; 160 | /* Too big? */ 161 | if (ids[0] >= ids[-1]) { 162 | if (mdb_midl_grow(idp, MDB_IDL_UM_MAX)) 163 | return ENOMEM; 164 | ids = *idp; 165 | } 166 | ids[0]++; 167 | ids[ids[0]] = id; 168 | return 0; 169 | } 170 | 171 | int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app ) 172 | { 173 | MDB_IDL ids = *idp; 174 | /* Too big? */ 175 | if (ids[0] + app[0] >= ids[-1]) { 176 | if (mdb_midl_grow(idp, app[0])) 177 | return ENOMEM; 178 | ids = *idp; 179 | } 180 | memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID)); 181 | ids[0] += app[0]; 182 | return 0; 183 | } 184 | 185 | int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n ) 186 | { 187 | MDB_ID *ids = *idp, len = ids[0]; 188 | /* Too big? */ 189 | if (len + n > ids[-1]) { 190 | if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX)) 191 | return ENOMEM; 192 | ids = *idp; 193 | } 194 | ids[0] = len + n; 195 | ids += len; 196 | while (n) 197 | ids[n--] = id++; 198 | return 0; 199 | } 200 | 201 | void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge ) 202 | { 203 | MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k; 204 | idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */ 205 | old_id = idl[j]; 206 | while (i) { 207 | merge_id = merge[i--]; 208 | for (; old_id < merge_id; old_id = idl[--j]) 209 | idl[k--] = old_id; 210 | idl[k--] = merge_id; 211 | } 212 | idl[0] = total; 213 | } 214 | 215 | /* Quicksort + Insertion sort for small arrays */ 216 | 217 | #define SMALL 8 218 | #define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; } 219 | 220 | void 221 | mdb_midl_sort( MDB_IDL ids ) 222 | { 223 | /* Max possible depth of int-indexed tree * 2 items/level */ 224 | int istack[sizeof(int)*CHAR_BIT * 2]; 225 | int i,j,k,l,ir,jstack; 226 | MDB_ID a, itmp; 227 | 228 | ir = (int)ids[0]; 229 | l = 1; 230 | jstack = 0; 231 | for(;;) { 232 | if (ir - l < SMALL) { /* Insertion sort */ 233 | for (j=l+1;j<=ir;j++) { 234 | a = ids[j]; 235 | for (i=j-1;i>=1;i--) { 236 | if (ids[i] >= a) break; 237 | ids[i+1] = ids[i]; 238 | } 239 | ids[i+1] = a; 240 | } 241 | if (jstack == 0) break; 242 | ir = istack[jstack--]; 243 | l = istack[jstack--]; 244 | } else { 245 | k = (l + ir) >> 1; /* Choose median of left, center, right */ 246 | MIDL_SWAP(ids[k], ids[l+1]); 247 | if (ids[l] < ids[ir]) { 248 | MIDL_SWAP(ids[l], ids[ir]); 249 | } 250 | if (ids[l+1] < ids[ir]) { 251 | MIDL_SWAP(ids[l+1], ids[ir]); 252 | } 253 | if (ids[l] < ids[l+1]) { 254 | MIDL_SWAP(ids[l], ids[l+1]); 255 | } 256 | i = l+1; 257 | j = ir; 258 | a = ids[l+1]; 259 | for(;;) { 260 | do i++; while(ids[i] > a); 261 | do j--; while(ids[j] < a); 262 | if (j < i) break; 263 | MIDL_SWAP(ids[i],ids[j]); 264 | } 265 | ids[l+1] = ids[j]; 266 | ids[j] = a; 267 | jstack += 2; 268 | if (ir-i+1 >= j-l) { 269 | istack[jstack] = ir; 270 | istack[jstack-1] = i; 271 | ir = j-1; 272 | } else { 273 | istack[jstack] = j-1; 274 | istack[jstack-1] = l; 275 | l = i; 276 | } 277 | } 278 | } 279 | } 280 | 281 | unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id ) 282 | { 283 | /* 284 | * binary search of id in ids 285 | * if found, returns position of id 286 | * if not found, returns first position greater than id 287 | */ 288 | unsigned base = 0; 289 | unsigned cursor = 1; 290 | int val = 0; 291 | unsigned n = (unsigned)ids[0].mid; 292 | 293 | while( 0 < n ) { 294 | unsigned pivot = n >> 1; 295 | cursor = base + pivot + 1; 296 | val = CMP( id, ids[cursor].mid ); 297 | 298 | if( val < 0 ) { 299 | n = pivot; 300 | 301 | } else if ( val > 0 ) { 302 | base = cursor; 303 | n -= pivot + 1; 304 | 305 | } else { 306 | return cursor; 307 | } 308 | } 309 | 310 | if( val > 0 ) { 311 | ++cursor; 312 | } 313 | return cursor; 314 | } 315 | 316 | int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id ) 317 | { 318 | unsigned x, i; 319 | 320 | x = mdb_mid2l_search( ids, id->mid ); 321 | 322 | if( x < 1 ) { 323 | /* internal error */ 324 | return -2; 325 | } 326 | 327 | if ( x <= ids[0].mid && ids[x].mid == id->mid ) { 328 | /* duplicate */ 329 | return -1; 330 | } 331 | 332 | if ( ids[0].mid >= MDB_IDL_UM_MAX ) { 333 | /* too big */ 334 | return -2; 335 | 336 | } else { 337 | /* insert id */ 338 | ids[0].mid++; 339 | for (i=(unsigned)ids[0].mid; i>x; i--) 340 | ids[i] = ids[i-1]; 341 | ids[x] = *id; 342 | } 343 | 344 | return 0; 345 | } 346 | 347 | int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id ) 348 | { 349 | /* Too big? */ 350 | if (ids[0].mid >= MDB_IDL_UM_MAX) { 351 | return -2; 352 | } 353 | ids[0].mid++; 354 | ids[ids[0].mid] = *id; 355 | return 0; 356 | } 357 | 358 | /** @} */ 359 | /** @} */ 360 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/intro.doc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015-2018 Howard Chu, Symas Corp. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted only as authorized by the OpenLDAP 7 | * Public License. 8 | * 9 | * A copy of this license is available in the file LICENSE in the 10 | * top-level directory of the distribution or, alternatively, at 11 | * . 12 | */ 13 | /** @page starting Getting Started 14 | 15 | LMDB is compact, fast, powerful, and robust and implements a simplified 16 | variant of the BerkeleyDB (BDB) API. (BDB is also very powerful, and verbosely 17 | documented in its own right.) After reading this page, the main 18 | \ref mdb documentation should make sense. Thanks to Bert Hubert 19 | for creating the 20 | 21 | initial version of this writeup. 22 | 23 | Everything starts with an environment, created by #mdb_env_create(). 24 | Once created, this environment must also be opened with #mdb_env_open(). 25 | 26 | #mdb_env_open() gets passed a name which is interpreted as a directory 27 | path. Note that this directory must exist already, it is not created 28 | for you. Within that directory, a lock file and a storage file will be 29 | generated. If you don't want to use a directory, you can pass the 30 | #MDB_NOSUBDIR option, in which case the path you provided is used 31 | directly as the data file, and another file with a "-lock" suffix 32 | added will be used for the lock file. 33 | 34 | Once the environment is open, a transaction can be created within it 35 | using #mdb_txn_begin(). Transactions may be read-write or read-only, 36 | and read-write transactions may be nested. A transaction must only 37 | be used by one thread at a time. Transactions are always required, 38 | even for read-only access. The transaction provides a consistent 39 | view of the data. 40 | 41 | Once a transaction has been created, a database can be opened within it 42 | using #mdb_dbi_open(). If only one database will ever be used in the 43 | environment, a NULL can be passed as the database name. For named 44 | databases, the #MDB_CREATE flag must be used to create the database 45 | if it doesn't already exist. Also, #mdb_env_set_maxdbs() must be 46 | called after #mdb_env_create() and before #mdb_env_open() to set the 47 | maximum number of named databases you want to support. 48 | 49 | Note: a single transaction can open multiple databases. Generally 50 | databases should only be opened once, by the first transaction in 51 | the process. After the first transaction completes, the database 52 | handles can freely be used by all subsequent transactions. 53 | 54 | Within a transaction, #mdb_get() and #mdb_put() can store single 55 | key/value pairs if that is all you need to do (but see \ref Cursors 56 | below if you want to do more). 57 | 58 | A key/value pair is expressed as two #MDB_val structures. This struct 59 | has two fields, \c mv_size and \c mv_data. The data is a \c void pointer to 60 | an array of \c mv_size bytes. 61 | 62 | Because LMDB is very efficient (and usually zero-copy), the data returned 63 | in an #MDB_val structure may be memory-mapped straight from disk. In 64 | other words look but do not touch (or free() for that matter). 65 | Once a transaction is closed, the values can no longer be used, so 66 | make a copy if you need to keep them after that. 67 | 68 | @section Cursors Cursors 69 | 70 | To do more powerful things, we must use a cursor. 71 | 72 | Within the transaction, a cursor can be created with #mdb_cursor_open(). 73 | With this cursor we can store/retrieve/delete (multiple) values using 74 | #mdb_cursor_get(), #mdb_cursor_put(), and #mdb_cursor_del(). 75 | 76 | #mdb_cursor_get() positions itself depending on the cursor operation 77 | requested, and for some operations, on the supplied key. For example, 78 | to list all key/value pairs in a database, use operation #MDB_FIRST for 79 | the first call to #mdb_cursor_get(), and #MDB_NEXT on subsequent calls, 80 | until the end is hit. 81 | 82 | To retrieve all keys starting from a specified key value, use #MDB_SET. 83 | For more cursor operations, see the \ref mdb docs. 84 | 85 | When using #mdb_cursor_put(), either the function will position the 86 | cursor for you based on the \b key, or you can use operation 87 | #MDB_CURRENT to use the current position of the cursor. Note that 88 | \b key must then match the current position's key. 89 | 90 | @subsection summary Summarizing the Opening 91 | 92 | So we have a cursor in a transaction which opened a database in an 93 | environment which is opened from a filesystem after it was 94 | separately created. 95 | 96 | Or, we create an environment, open it from a filesystem, create a 97 | transaction within it, open a database within that transaction, 98 | and create a cursor within all of the above. 99 | 100 | Got it? 101 | 102 | @section thrproc Threads and Processes 103 | 104 | LMDB uses POSIX locks on files, and these locks have issues if one 105 | process opens a file multiple times. Because of this, do not 106 | #mdb_env_open() a file multiple times from a single process. Instead, 107 | share the LMDB environment that has opened the file across all threads. 108 | Otherwise, if a single process opens the same environment multiple times, 109 | closing it once will remove all the locks held on it, and the other 110 | instances will be vulnerable to corruption from other processes. 111 | 112 | Also note that a transaction is tied to one thread by default using 113 | Thread Local Storage. If you want to pass read-only transactions across 114 | threads, you can use the #MDB_NOTLS option on the environment. 115 | 116 | @section txns Transactions, Rollbacks, etc. 117 | 118 | To actually get anything done, a transaction must be committed using 119 | #mdb_txn_commit(). Alternatively, all of a transaction's operations 120 | can be discarded using #mdb_txn_abort(). In a read-only transaction, 121 | any cursors will \b not automatically be freed. In a read-write 122 | transaction, all cursors will be freed and must not be used again. 123 | 124 | For read-only transactions, obviously there is nothing to commit to 125 | storage. The transaction still must eventually be aborted to close 126 | any database handle(s) opened in it, or committed to keep the 127 | database handles around for reuse in new transactions. 128 | 129 | In addition, as long as a transaction is open, a consistent view of 130 | the database is kept alive, which requires storage. A read-only 131 | transaction that no longer requires this consistent view should 132 | be terminated (committed or aborted) when the view is no longer 133 | needed (but see below for an optimization). 134 | 135 | There can be multiple simultaneously active read-only transactions 136 | but only one that can write. Once a single read-write transaction 137 | is opened, all further attempts to begin one will block until the 138 | first one is committed or aborted. This has no effect on read-only 139 | transactions, however, and they may continue to be opened at any time. 140 | 141 | @section dupkeys Duplicate Keys 142 | 143 | #mdb_get() and #mdb_put() respectively have no and only some support 144 | for multiple key/value pairs with identical keys. If there are multiple 145 | values for a key, #mdb_get() will only return the first value. 146 | 147 | When multiple values for one key are required, pass the #MDB_DUPSORT 148 | flag to #mdb_dbi_open(). In an #MDB_DUPSORT database, by default 149 | #mdb_put() will not replace the value for a key if the key existed 150 | already. Instead it will add the new value to the key. In addition, 151 | #mdb_del() will pay attention to the value field too, allowing for 152 | specific values of a key to be deleted. 153 | 154 | Finally, additional cursor operations become available for 155 | traversing through and retrieving duplicate values. 156 | 157 | @section optim Some Optimization 158 | 159 | If you frequently begin and abort read-only transactions, as an 160 | optimization, it is possible to only reset and renew a transaction. 161 | 162 | #mdb_txn_reset() releases any old copies of data kept around for 163 | a read-only transaction. To reuse this reset transaction, call 164 | #mdb_txn_renew() on it. Any cursors in this transaction must also 165 | be renewed using #mdb_cursor_renew(). 166 | 167 | Note that #mdb_txn_reset() is similar to #mdb_txn_abort() and will 168 | close any databases you opened within the transaction. 169 | 170 | To permanently free a transaction, reset or not, use #mdb_txn_abort(). 171 | 172 | @section cleanup Cleaning Up 173 | 174 | For read-only transactions, any cursors created within it must 175 | be closed using #mdb_cursor_close(). 176 | 177 | It is very rarely necessary to close a database handle, and in 178 | general they should just be left open. 179 | 180 | @section onward The Full API 181 | 182 | The full \ref mdb documentation lists further details, like how to: 183 | 184 | \li size a database (the default limits are intentionally small) 185 | \li drop and clean a database 186 | \li detect and report errors 187 | \li optimize (bulk) loading speed 188 | \li (temporarily) reduce robustness to gain even more speed 189 | \li gather statistics about the database 190 | \li define custom sort orders 191 | 192 | */ 193 | -------------------------------------------------------------------------------- /dependencies/lmdb/libraries/liblmdb/CHANGES: -------------------------------------------------------------------------------- 1 | LMDB 0.9 Change Log 2 | 3 | LMDB 0.9.24 Engineering 4 | ITS#8969 Tweak mdb_page_split 5 | ITS#8975 WIN32 fix writemap set_mapsize crash 6 | ITS#9007 Fix loose pages in WRITEMAP 7 | 8 | LMDB 0.9.23 Release (2018/12/19) 9 | ITS#8756 Fix loose pages in dirty list 10 | ITS#8831 Fix mdb_load flag init 11 | ITS#8844 Fix mdb_env_close in forked process 12 | Documentation 13 | ITS#8857 mdb_cursor_del doesn't invalidate cursor 14 | ITS#8908 GET_MULTIPLE etc don't change passed in key 15 | 16 | LMDB 0.9.22 Release (2018/03/22) 17 | Fix MDB_DUPSORT alignment bug (ITS#8819) 18 | Fix regression with new db from 0.9.19 (ITS#8760) 19 | Fix liblmdb to build on Solaris (ITS#8612) 20 | Fix delete behavior with DUPSORT DB (ITS#8622) 21 | Fix mdb_cursor_get/mdb_cursor_del behavior (ITS#8722) 22 | 23 | LMDB 0.9.21 Release (2017/06/01) 24 | Fix xcursor after cursor_del (ITS#8622) 25 | 26 | LMDB 0.9.20 (Withdrawn) 27 | Fix mdb_load with escaped plaintext (ITS#8558) 28 | Fix mdb_cursor_last / mdb_put interaction (ITS#8557) 29 | 30 | LMDB 0.9.19 Release (2016/12/28) 31 | Fix mdb_env_cwalk cursor init (ITS#8424) 32 | Fix robust mutexes on Solaris 10/11 (ITS#8339) 33 | Tweak Win32 error message buffer 34 | Fix MDB_GET_BOTH on non-dup record (ITS#8393) 35 | Optimize mdb_drop 36 | Fix xcursors after mdb_cursor_del (ITS#8406) 37 | Fix MDB_NEXT_DUP after mdb_cursor_del (ITS#8412) 38 | Fix mdb_cursor_put resetting C_EOF (ITS#8489) 39 | Fix mdb_env_copyfd2 to return EPIPE on SIGPIPE (ITS#8504) 40 | Fix mdb_env_copy with empty DB (ITS#8209) 41 | Fix behaviors with fork (ITS#8505) 42 | Fix mdb_dbi_open with mainDB cursors (ITS#8542) 43 | Fix robust mutexes on kFreeBSD (ITS#8554) 44 | Fix utf8_to_utf16 error checks (ITS#7992) 45 | Fix F_NOCACHE on MacOS, error is non-fatal (ITS#7682) 46 | Build 47 | Make shared lib suffix overridable (ITS#8481) 48 | Documentation 49 | Cleanup doxygen nits 50 | Note reserved vs actual mem/disk usage 51 | 52 | 53 | LMDB 0.9.18 Release (2016/02/05) 54 | Fix robust mutex detection on glibc 2.10-11 (ITS#8330) 55 | Fix page_search_root assert on FreeDB (ITS#8336) 56 | Fix MDB_APPENDDUP vs. rewrite(single item) (ITS#8334) 57 | Fix mdb_copy of large files on Windows 58 | Fix subcursor move after delete (ITS#8355) 59 | Fix mdb_midl_shirnk off-by-one (ITS#8363) 60 | Check for utf8_to_utf16 failures (ITS#7992) 61 | Catch strdup failure in mdb_dbi_open 62 | Build 63 | Additional makefile var tweaks (ITS#8169) 64 | Documentation 65 | Add Getting Started page 66 | Update WRITEMAP description 67 | 68 | 69 | LMDB 0.9.17 Release (2015/11/30) 70 | Fix ITS#7377 catch calloc failure 71 | Fix ITS#8237 regression from ITS#7589 72 | Fix ITS#8238 page_split for DUPFIXED pages 73 | Fix ITS#8221 MDB_PAGE_FULL on delete/rebalance 74 | Fix ITS#8258 rebalance/split assert 75 | Fix ITS#8263 cursor_put cursor tracking 76 | Fix ITS#8264 cursor_del cursor tracking 77 | Fix ITS#8310 cursor_del cursor tracking 78 | Fix ITS#8299 mdb_del cursor tracking 79 | Fix ITS#8300 mdb_del cursor tracking 80 | Fix ITS#8304 mdb_del cursor tracking 81 | Fix ITS#7771 fakepage cursor tracking 82 | Fix ITS#7789 ensure mapsize >= pages in use 83 | Fix ITS#7971 mdb_txn_renew0() new reader slots 84 | Fix ITS#7969 use __sync_synchronize on non-x86 85 | Fix ITS#8311 page_split from update_key 86 | Fix ITS#8312 loose pages in nested txn 87 | Fix ITS#8313 mdb_rebalance dummy cursor 88 | Fix ITS#8315 dirty_room in nested txn 89 | Fix ITS#8323 dirty_list in nested txn 90 | Fix ITS#8316 page_merge cursor tracking 91 | Fix ITS#8321 cursor tracking 92 | Fix ITS#8319 mdb_load error messages 93 | Fix ITS#8320 mdb_load plaintext input 94 | Added mdb_txn_id() (ITS#7994) 95 | Added robust mutex support 96 | Miscellaneous cleanup/simplification 97 | Build 98 | Create install dirs if needed (ITS#8256) 99 | Fix ThreadProc decl on Win32/MSVC (ITS#8270) 100 | Added ssize_t typedef for MSVC (ITS#8067) 101 | Use ANSI apis on Windows (ITS#8069) 102 | Use O_SYNC if O_DSYNC,MDB_DSYNC are not defined (ITS#7209) 103 | Allow passing AR to make (ITS#8168) 104 | Allow passing mandir to make install (ITS#8169) 105 | 106 | LMDB 0.9.16 Release (2015/08/14) 107 | Fix cursor EOF bug (ITS#8190) 108 | Fix handling of subDB records (ITS#8181) 109 | Fix mdb_midl_shrink() usage (ITS#8200) 110 | 111 | LMDB 0.9.15 Release (2015/06/19) 112 | Fix txn init (ITS#7961,#7987) 113 | Fix MDB_PREV_DUP (ITS#7955,#7671) 114 | Fix compact of empty env (ITS#7956) 115 | Fix mdb_copy file mode 116 | Fix mdb_env_close() after failed mdb_env_open() 117 | Fix mdb_rebalance collapsing root (ITS#8062) 118 | Fix mdb_load with large values (ITS#8066) 119 | Fix to retry writes on EINTR (ITS#8106) 120 | Fix mdb_cursor_del on empty DB (ITS#8109) 121 | Fix MDB_INTEGERDUP key compare (ITS#8117) 122 | Fix error handling (ITS#7959,#8157,etc.) 123 | Fix race conditions (ITS#7969,7970) 124 | Added workaround for fdatasync bug in ext3fs 125 | Build 126 | Don't use -fPIC for static lib 127 | Update .gitignore (ITS#7952,#7953) 128 | Cleanup for "make test" (ITS#7841), "make clean", mtest*.c 129 | Misc. Android/Windows cleanup 130 | Documentation 131 | Fix MDB_APPEND doc 132 | Fix MDB_MAXKEYSIZE doc (ITS#8156) 133 | Fix mdb_cursor_put,mdb_cursor_del EACCES description 134 | Fix mdb_env_sync(MDB_RDONLY env) doc (ITS#8021) 135 | Clarify MDB_WRITEMAP doc (ITS#8021) 136 | Clarify mdb_env_open doc 137 | Clarify mdb_dbi_open doc 138 | 139 | LMDB 0.9.14 Release (2014/09/20) 140 | Fix to support 64K page size (ITS#7713) 141 | Fix to persist decreased as well as increased mapsizes (ITS#7789) 142 | Fix cursor bug when deleting last node of a DUPSORT key 143 | Fix mdb_env_info to return FIXEDMAP address 144 | Fix ambiguous error code from writing to closed DBI (ITS#7825) 145 | Fix mdb_copy copying past end of file (ITS#7886) 146 | Fix cursor bugs from page_merge/rebalance 147 | Fix to dirty fewer pages in deletes (mdb_page_loose()) 148 | Fix mdb_dbi_open creating subDBs (ITS#7917) 149 | Fix mdb_cursor_get(_DUP) with single value (ITS#7913) 150 | Fix Windows compat issues in mtests (ITS#7879) 151 | Add compacting variant of mdb_copy 152 | Add BigEndian integer key compare code 153 | Add mdb_dump/mdb_load utilities 154 | 155 | LMDB 0.9.13 Release (2014/06/18) 156 | Fix mdb_page_alloc unlimited overflow page search 157 | Documentation 158 | Re-fix MDB_CURRENT doc (ITS#7793) 159 | Fix MDB_GET_MULTIPLE/MDB_NEXT_MULTIPLE doc 160 | 161 | LMDB 0.9.12 Release (2014/06/13) 162 | Fix MDB_GET_BOTH regression (ITS#7875,#7681) 163 | Fix MDB_MULTIPLE writing multiple keys (ITS#7834) 164 | Fix mdb_rebalance (ITS#7829) 165 | Fix mdb_page_split (ITS#7815) 166 | Fix md_entries count (ITS#7861,#7828,#7793) 167 | Fix MDB_CURRENT (ITS#7793) 168 | Fix possible crash on Windows DLL detach 169 | Misc code cleanup 170 | Documentation 171 | mdb_cursor_put: cursor moves on error (ITS#7771) 172 | 173 | 174 | LMDB 0.9.11 Release (2014/01/15) 175 | Add mdb_env_set_assert() (ITS#7775) 176 | Fix: invalidate txn on page allocation errors (ITS#7377) 177 | Fix xcursor tracking in mdb_cursor_del0() (ITS#7771) 178 | Fix corruption from deletes (ITS#7756) 179 | Fix Windows/MSVC build issues 180 | Raise safe limit of max MDB_MAXKEYSIZE 181 | Misc code cleanup 182 | Documentation 183 | Remove spurious note about non-overlapping flags (ITS#7665) 184 | 185 | LMDB 0.9.10 Release (2013/11/12) 186 | Add MDB_NOMEMINIT option 187 | Fix mdb_page_split() again (ITS#7589) 188 | Fix MDB_NORDAHEAD definition (ITS#7734) 189 | Fix mdb_cursor_del() positioning (ITS#7733) 190 | Partial fix for larger page sizes (ITS#7713) 191 | Fix Windows64/MSVC build issues 192 | 193 | LMDB 0.9.9 Release (2013/10/24) 194 | Add mdb_env_get_fd() 195 | Add MDB_NORDAHEAD option 196 | Add MDB_NOLOCK option 197 | Avoid wasting space in mdb_page_split() (ITS#7589) 198 | Fix mdb_page_merge() cursor fixup (ITS#7722) 199 | Fix mdb_cursor_del() on last delete (ITS#7718) 200 | Fix adding WRITEMAP on existing env (ITS#7715) 201 | Fix nested txns (ITS#7515) 202 | Fix mdb_env_copy() O_DIRECT bug (ITS#7682) 203 | Fix mdb_cursor_set(SET_RANGE) return code (ITS#7681) 204 | Fix mdb_rebalance() cursor fixup (ITS#7701) 205 | Misc code cleanup 206 | Documentation 207 | Note that by default, readers need write access 208 | 209 | 210 | LMDB 0.9.8 Release (2013/09/09) 211 | Allow mdb_env_set_mapsize() on an open environment 212 | Fix mdb_dbi_flags() (ITS#7672) 213 | Fix mdb_page_unspill() in nested txns 214 | Fix mdb_cursor_get(CURRENT|NEXT) after a delete 215 | Fix mdb_cursor_get(DUP) to always return key (ITS#7671) 216 | Fix mdb_cursor_del() to always advance to next item (ITS#7670) 217 | Fix mdb_cursor_set(SET_RANGE) for tree with single page (ITS#7681) 218 | Fix mdb_env_copy() retry open if O_DIRECT fails (ITS#7682) 219 | Tweak mdb_page_spill() to be less aggressive 220 | Documentation 221 | Update caveats since mdb_reader_check() added in 0.9.7 222 | 223 | LMDB 0.9.7 Release (2013/08/17) 224 | Don't leave stale lockfile on failed RDONLY open (ITS#7664) 225 | Fix mdb_page_split() ref beyond cursor depth 226 | Fix read txn data race (ITS#7635) 227 | Fix mdb_rebalance (ITS#7536, #7538) 228 | Fix mdb_drop() (ITS#7561) 229 | Misc DEBUG macro fixes 230 | Add MDB_NOTLS envflag 231 | Add mdb_env_copyfd() 232 | Add mdb_txn_env() (ITS#7660) 233 | Add mdb_dbi_flags() (ITS#7661) 234 | Add mdb_env_get_maxkeysize() 235 | Add mdb_env_reader_list()/mdb_env_reader_check() 236 | Add mdb_page_spill/unspill, remove hard txn size limit 237 | Use shorter names for semaphores (ITS#7615) 238 | Build 239 | Fix install target (ITS#7656) 240 | Documentation 241 | Misc updates for cursors, DB handles, data lifetime 242 | 243 | LMDB 0.9.6 Release (2013/02/25) 244 | Many fixes/enhancements 245 | 246 | LMDB 0.9.5 Release (2012/11/30) 247 | Renamed from libmdb to liblmdb 248 | Many fixes/enhancements 249 | -------------------------------------------------------------------------------- /src/dbi.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of node-lmdb, the Node.js binding for lmdb 3 | // Copyright (c) 2013-2017 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #include "node-lmdb.h" 25 | #include 26 | 27 | using namespace v8; 28 | using namespace node; 29 | 30 | void setFlagFromValue(int *flags, int flag, const char *name, bool defaultValue, Local options); 31 | 32 | DbiWrap::DbiWrap(MDB_env *env, MDB_dbi dbi) { 33 | this->env = env; 34 | this->dbi = dbi; 35 | this->keyType = NodeLmdbKeyType::StringKey; 36 | this->isOpen = false; 37 | this->ew = nullptr; 38 | } 39 | 40 | DbiWrap::~DbiWrap() { 41 | // Imagine the following JS: 42 | // ------------------------ 43 | // var dbi1 = env.openDbi({ name: "hello" }); 44 | // var dbi2 = env.openDbi({ name: "hello" }); 45 | // dbi1.close(); 46 | // txn.putString(dbi2, "world"); 47 | // ----- 48 | // The above DbiWrap objects would both wrap the same MDB_dbi, and if closing the first one called mdb_dbi_close, 49 | // that'd also render the second DbiWrap instance unusable. 50 | // 51 | // For this reason, we will never call mdb_dbi_close 52 | // NOTE: according to LMDB authors, it is perfectly fine if mdb_dbi_close is never called on an MDB_dbi 53 | 54 | if (this->ew) { 55 | this->ew->Unref(); 56 | } 57 | } 58 | 59 | NAN_METHOD(DbiWrap::ctor) { 60 | Nan::HandleScope scope; 61 | 62 | MDB_dbi dbi; 63 | MDB_txn *txn; 64 | int rc; 65 | int flags = 0; 66 | int txnFlags = 0; 67 | Local name; 68 | bool nameIsNull = false; 69 | NodeLmdbKeyType keyType = NodeLmdbKeyType::StringKey; 70 | bool needsTransaction = true; 71 | bool isOpen = false; 72 | 73 | EnvWrap *ew = Nan::ObjectWrap::Unwrap(Local::Cast(info[0])); 74 | 75 | if (info[1]->IsObject()) { 76 | Local options = Local::Cast(info[1]); 77 | nameIsNull = options->Get(Nan::GetCurrentContext(), Nan::New("name").ToLocalChecked()).ToLocalChecked()->IsNull(); 78 | name = Local::Cast(options->Get(Nan::GetCurrentContext(), Nan::New("name").ToLocalChecked()).ToLocalChecked()); 79 | 80 | // Get flags from options 81 | 82 | // NOTE: mdb_set_relfunc is not exposed because MDB_FIXEDMAP is "highly experimental" 83 | // NOTE: mdb_set_relctx is not exposed because MDB_FIXEDMAP is "highly experimental" 84 | setFlagFromValue(&flags, MDB_REVERSEKEY, "reverseKey", false, options); 85 | setFlagFromValue(&flags, MDB_DUPSORT, "dupSort", false, options); 86 | setFlagFromValue(&flags, MDB_DUPFIXED, "dupFixed", false, options); 87 | setFlagFromValue(&flags, MDB_INTEGERDUP, "integerDup", false, options); 88 | setFlagFromValue(&flags, MDB_REVERSEDUP, "reverseDup", false, options); 89 | setFlagFromValue(&flags, MDB_CREATE, "create", false, options); 90 | 91 | // TODO: wrap mdb_set_compare 92 | // TODO: wrap mdb_set_dupsort 93 | 94 | keyType = keyTypeFromOptions(options); 95 | if (keyType == NodeLmdbKeyType::InvalidKey) { 96 | // NOTE: Error has already been thrown inside keyTypeFromOptions 97 | return; 98 | } 99 | 100 | if (keyType == NodeLmdbKeyType::Uint32Key) { 101 | flags |= MDB_INTEGERKEY; 102 | } 103 | 104 | // Set flags for txn used to open database 105 | Local create = options->Get(Nan::GetCurrentContext(), Nan::New("create").ToLocalChecked()).ToLocalChecked(); 106 | if (create->IsBoolean() ? !create->BooleanValue(Nan::GetCurrentContext()).ToChecked() : true) { 107 | txnFlags |= MDB_RDONLY; 108 | } 109 | 110 | auto txnObj = options->Get(Nan::GetCurrentContext(), Nan::New("txn").ToLocalChecked()).ToLocalChecked(); 111 | if (!txnObj->IsNull() && !txnObj->IsUndefined() && txnObj->IsObject()) { 112 | TxnWrap *tw = Nan::ObjectWrap::Unwrap(Local::Cast(txnObj)); 113 | needsTransaction = false; 114 | txn = tw->txn; 115 | } 116 | } 117 | else { 118 | return Nan::ThrowError("Invalid parameters."); 119 | } 120 | 121 | if (needsTransaction) { 122 | // Open transaction 123 | rc = mdb_txn_begin(ew->env, nullptr, txnFlags, &txn); 124 | if (rc != 0) { 125 | // No need to call mdb_txn_abort, because mdb_txn_begin already cleans up after itself 126 | return throwLmdbError(rc); 127 | } 128 | } 129 | 130 | // Open database 131 | // NOTE: nullptr in place of the name means using the unnamed database. 132 | rc = mdb_dbi_open(txn, nameIsNull ? nullptr : *String::Utf8Value(Isolate::GetCurrent(), name), flags, &dbi); 133 | if (rc != 0) { 134 | if (needsTransaction) { 135 | mdb_txn_abort(txn); 136 | } 137 | return throwLmdbError(rc); 138 | } 139 | else { 140 | isOpen = true; 141 | } 142 | 143 | if (needsTransaction) { 144 | // Commit transaction 145 | rc = mdb_txn_commit(txn); 146 | if (rc != 0) { 147 | return throwLmdbError(rc); 148 | } 149 | } 150 | 151 | // Create wrapper 152 | DbiWrap* dw = new DbiWrap(ew->env, dbi); 153 | if (isOpen) { 154 | dw->ew = ew; 155 | dw->ew->Ref(); 156 | } 157 | dw->keyType = keyType; 158 | dw->flags = flags; 159 | dw->isOpen = isOpen; 160 | dw->Wrap(info.This()); 161 | 162 | return info.GetReturnValue().Set(info.This()); 163 | } 164 | 165 | NAN_METHOD(DbiWrap::close) { 166 | Nan::HandleScope scope; 167 | 168 | DbiWrap *dw = Nan::ObjectWrap::Unwrap(info.This()); 169 | if (dw->isOpen) { 170 | mdb_dbi_close(dw->env, dw->dbi); 171 | dw->isOpen = false; 172 | dw->ew->Unref(); 173 | dw->ew = nullptr; 174 | } 175 | else { 176 | return Nan::ThrowError("The Dbi is not open, you can't close it."); 177 | } 178 | } 179 | 180 | NAN_METHOD(DbiWrap::drop) { 181 | Nan::HandleScope scope; 182 | 183 | DbiWrap *dw = Nan::ObjectWrap::Unwrap(info.This()); 184 | int del = 1; 185 | int rc; 186 | MDB_txn *txn; 187 | bool needsTransaction = true; 188 | 189 | if (!dw->isOpen) { 190 | return Nan::ThrowError("The Dbi is not open, you can't drop it."); 191 | } 192 | 193 | // Check if the database should be deleted 194 | if (info.Length() == 1 && info[0]->IsObject()) { 195 | Local options = Local::Cast(info[0]); 196 | 197 | // Just free pages 198 | Local opt = options->Get(Nan::GetCurrentContext(), Nan::New("justFreePages").ToLocalChecked()).ToLocalChecked(); 199 | del = opt->IsBoolean() ? !(opt->BooleanValue(Nan::GetCurrentContext()).ToChecked()) : 1; 200 | 201 | // User-supplied txn 202 | auto txnObj = options->Get(Nan::GetCurrentContext(), Nan::New("txn").ToLocalChecked()).ToLocalChecked(); 203 | if (!txnObj->IsNull() && !txnObj->IsUndefined() && txnObj->IsObject()) { 204 | TxnWrap *tw = Nan::ObjectWrap::Unwrap(Local::Cast(txnObj)); 205 | needsTransaction = false; 206 | txn = tw->txn; 207 | } 208 | } 209 | 210 | if (needsTransaction) { 211 | // Begin transaction 212 | rc = mdb_txn_begin(dw->env, nullptr, 0, &txn); 213 | if (rc != 0) { 214 | return throwLmdbError(rc); 215 | } 216 | } 217 | 218 | // Drop database 219 | rc = mdb_drop(txn, dw->dbi, del); 220 | if (rc != 0) { 221 | if (needsTransaction) { 222 | mdb_txn_abort(txn); 223 | } 224 | return throwLmdbError(rc); 225 | } 226 | 227 | if (needsTransaction) { 228 | // Commit transaction 229 | rc = mdb_txn_commit(txn); 230 | if (rc != 0) { 231 | return throwLmdbError(rc); 232 | } 233 | } 234 | 235 | // Only close database if del == 1 236 | if (del == 1) { 237 | dw->isOpen = false; 238 | dw->ew->Unref(); 239 | dw->ew = nullptr; 240 | } 241 | } 242 | 243 | NAN_METHOD(DbiWrap::stat) { 244 | Nan::HandleScope scope; 245 | 246 | DbiWrap *dw = Nan::ObjectWrap::Unwrap(info.This()); 247 | 248 | if (info.Length() != 1) { 249 | return Nan::ThrowError("dbi.stat should be called with a single argument which is a txn."); 250 | } 251 | 252 | TxnWrap *txn = Nan::ObjectWrap::Unwrap(Local::Cast(info[0])); 253 | 254 | MDB_stat stat; 255 | mdb_stat(txn->txn, dw->dbi, &stat); 256 | 257 | Local context = Nan::GetCurrentContext(); 258 | Local obj = Nan::New(); 259 | obj->Set(context, Nan::New("pageSize").ToLocalChecked(), Nan::New(stat.ms_psize)); 260 | obj->Set(context, Nan::New("treeDepth").ToLocalChecked(), Nan::New(stat.ms_depth)); 261 | obj->Set(context, Nan::New("treeBranchPageCount").ToLocalChecked(), Nan::New(stat.ms_branch_pages)); 262 | obj->Set(context, Nan::New("treeLeafPageCount").ToLocalChecked(), Nan::New(stat.ms_leaf_pages)); 263 | obj->Set(context, Nan::New("entryCount").ToLocalChecked(), Nan::New(stat.ms_entries)); 264 | obj->Set(context, Nan::New("overflowPages").ToLocalChecked(), Nan::New(stat.ms_overflow_pages)); 265 | 266 | info.GetReturnValue().Set(obj); 267 | } -------------------------------------------------------------------------------- /src/misc.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This file is part of node-lmdb, the Node.js binding for lmdb 3 | // Copyright (c) 2013-2017 Timur Kristóf 4 | // Licensed to you under the terms of the MIT license 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #include "node-lmdb.h" 25 | #include 26 | #include 27 | 28 | void setupExportMisc(Local exports) { 29 | Local versionObj = Nan::New(); 30 | 31 | int major, minor, patch; 32 | char *str = mdb_version(&major, &minor, &patch); 33 | Local context = Nan::GetCurrentContext(); 34 | versionObj->Set(context, Nan::New("versionString").ToLocalChecked(), Nan::New(str).ToLocalChecked()); 35 | versionObj->Set(context, Nan::New("major").ToLocalChecked(), Nan::New(major)); 36 | versionObj->Set(context, Nan::New("minor").ToLocalChecked(), Nan::New(minor)); 37 | versionObj->Set(context, Nan::New("patch").ToLocalChecked(), Nan::New(patch)); 38 | 39 | exports->Set(context, Nan::New("version").ToLocalChecked(), versionObj); 40 | } 41 | 42 | void setFlagFromValue(int *flags, int flag, const char *name, bool defaultValue, Local options) { 43 | Local context = Nan::GetCurrentContext(); 44 | Local opt = options->Get(context, Nan::New(name).ToLocalChecked()).ToLocalChecked(); 45 | if (opt->IsBoolean() ? opt->BooleanValue(context).ToChecked() : defaultValue) { 46 | *flags |= flag; 47 | } 48 | } 49 | 50 | NodeLmdbKeyType keyTypeFromOptions(const Local &val, NodeLmdbKeyType defaultKeyType) { 51 | if (val->IsNull() || val->IsUndefined()) { 52 | return defaultKeyType; 53 | } 54 | if (!val->IsObject()) { 55 | Nan::ThrowError("keyTypeFromOptions: Invalid argument passed to a node-lmdb function, must be an object."); 56 | return NodeLmdbKeyType::InvalidKey; 57 | } 58 | 59 | auto obj = Local::Cast(val); 60 | 61 | NodeLmdbKeyType keyType = defaultKeyType; 62 | int keyIsUint32 = 0; 63 | int keyIsBuffer = 0; 64 | int keyIsString = 0; 65 | 66 | setFlagFromValue(&keyIsUint32, 1, "keyIsUint32", false, obj); 67 | setFlagFromValue(&keyIsString, 1, "keyIsString", false, obj); 68 | setFlagFromValue(&keyIsBuffer, 1, "keyIsBuffer", false, obj); 69 | 70 | const char *keySpecificationErrorText = "You can't specify multiple key types at once. Either set keyIsUint32, or keyIsBuffer or keyIsString (default)."; 71 | 72 | if (keyIsUint32) { 73 | keyType = NodeLmdbKeyType::Uint32Key; 74 | if (keyIsBuffer || keyIsString) { 75 | Nan::ThrowError(keySpecificationErrorText); 76 | return NodeLmdbKeyType::InvalidKey; 77 | } 78 | } 79 | else if (keyIsBuffer) { 80 | keyType = NodeLmdbKeyType::BinaryKey; 81 | 82 | if (keyIsUint32 || keyIsString) { 83 | Nan::ThrowError(keySpecificationErrorText); 84 | return NodeLmdbKeyType::InvalidKey; 85 | } 86 | } 87 | else if (keyIsString) { 88 | keyType = NodeLmdbKeyType::StringKey; 89 | } 90 | 91 | return keyType; 92 | } 93 | 94 | NodeLmdbKeyType inferKeyType(const Local &val) { 95 | if (val->IsString()) { 96 | return NodeLmdbKeyType::StringKey; 97 | } 98 | if (val->IsUint32()) { 99 | return NodeLmdbKeyType::Uint32Key; 100 | } 101 | if (node::Buffer::HasInstance(val)) { 102 | return NodeLmdbKeyType::BinaryKey; 103 | } 104 | 105 | return NodeLmdbKeyType::InvalidKey; 106 | } 107 | 108 | NodeLmdbKeyType inferAndValidateKeyType(const Local &key, const Local &options, NodeLmdbKeyType dbiKeyType, bool &isValid) { 109 | auto keyType = keyTypeFromOptions(options, NodeLmdbKeyType::DefaultKey); 110 | auto inferredKeyType = inferKeyType(key); 111 | isValid = false; 112 | 113 | if (keyType != NodeLmdbKeyType::DefaultKey && inferredKeyType != keyType) { 114 | Nan::ThrowError("Specified key type doesn't match the key you gave."); 115 | return NodeLmdbKeyType::InvalidKey; 116 | } 117 | else { 118 | keyType = inferredKeyType; 119 | } 120 | if (dbiKeyType == NodeLmdbKeyType::Uint32Key && keyType != NodeLmdbKeyType::Uint32Key) { 121 | Nan::ThrowError("You specified keyIsUint32 on the Dbi, so you can't use other key types with it."); 122 | return NodeLmdbKeyType::InvalidKey; 123 | } 124 | 125 | isValid = true; 126 | return keyType; 127 | } 128 | 129 | argtokey_callback_t argToKey(const Local &val, MDB_val &key, NodeLmdbKeyType keyType, bool &isValid) { 130 | isValid = false; 131 | 132 | if (keyType == NodeLmdbKeyType::StringKey) { 133 | if (!val->IsString()) { 134 | Nan::ThrowError("Invalid key. Should be a string. (Specified with env.openDbi)"); 135 | return nullptr; 136 | } 137 | 138 | isValid = true; 139 | CustomExternalStringResource::writeTo(Local::Cast(val), &key); 140 | return ([](MDB_val &key) -> void { 141 | delete[] (uint16_t*)key.mv_data; 142 | }); 143 | } 144 | else if (keyType == NodeLmdbKeyType::Uint32Key) { 145 | if (!val->IsUint32()) { 146 | Nan::ThrowError("Invalid key. Should be an unsigned 32-bit integer. (Specified with env.openDbi)"); 147 | return nullptr; 148 | } 149 | 150 | isValid = true; 151 | uint32_t* uint32Key = new uint32_t; 152 | *uint32Key = val->Uint32Value(Nan::GetCurrentContext()).ToChecked(); 153 | key.mv_size = sizeof(uint32_t); 154 | key.mv_data = uint32Key; 155 | 156 | return ([](MDB_val &key) -> void { 157 | delete (uint32_t*)key.mv_data; 158 | }); 159 | } 160 | else if (keyType == NodeLmdbKeyType::BinaryKey) { 161 | if (!node::Buffer::HasInstance(val)) { 162 | Nan::ThrowError("Invalid key. Should be a Buffer. (Specified with env.openDbi)"); 163 | return nullptr; 164 | } 165 | 166 | isValid = true; 167 | key.mv_size = node::Buffer::Length(val); 168 | key.mv_data = node::Buffer::Data(val); 169 | 170 | return nullptr; 171 | } 172 | else if (keyType == NodeLmdbKeyType::InvalidKey) { 173 | Nan::ThrowError("Invalid key type. This might be a bug in node-lmdb."); 174 | } 175 | else { 176 | Nan::ThrowError("Unknown key type. This is a bug in node-lmdb."); 177 | } 178 | 179 | return nullptr; 180 | } 181 | 182 | Local keyToHandle(MDB_val &key, NodeLmdbKeyType keyType) { 183 | switch (keyType) { 184 | case NodeLmdbKeyType::Uint32Key: 185 | return Nan::New(*((uint32_t*)key.mv_data)); 186 | case NodeLmdbKeyType::BinaryKey: 187 | return valToBinary(key); 188 | case NodeLmdbKeyType::StringKey: 189 | return valToString(key); 190 | default: 191 | Nan::ThrowError("Unknown key type. This is a bug in node-lmdb."); 192 | return Nan::Undefined(); 193 | } 194 | } 195 | 196 | Local valToStringUnsafe(MDB_val &data) { 197 | auto resource = new CustomExternalStringResource(&data); 198 | auto str = Nan::New(resource); 199 | 200 | return str.ToLocalChecked(); 201 | } 202 | 203 | Local valToString(MDB_val &data) { 204 | // UTF-16 buffer 205 | const uint16_t *buffer = reinterpret_cast(data.mv_data); 206 | // Number of UTF-16 code points 207 | size_t n = data.mv_size / sizeof(uint16_t); 208 | 209 | // Check zero termination 210 | if (n < 1 || buffer[n - 1] != 0) { 211 | Nan::ThrowError("Invalid zero-terminated UTF-16 string"); 212 | return Nan::Undefined(); 213 | } 214 | 215 | size_t length = n - 1; 216 | auto str = Nan::New(buffer, length); 217 | 218 | return str.ToLocalChecked(); 219 | } 220 | 221 | Local valToBinary(MDB_val &data) { 222 | return Nan::CopyBuffer( 223 | (char*)data.mv_data, 224 | data.mv_size 225 | ).ToLocalChecked(); 226 | } 227 | 228 | Local valToBinaryUnsafe(MDB_val &data) { 229 | return Nan::NewBuffer( 230 | (char*)data.mv_data, 231 | data.mv_size, 232 | [](char *, void *) { 233 | // Data belongs to LMDB, we shouldn't free it here 234 | }, 235 | nullptr 236 | ).ToLocalChecked(); 237 | } 238 | 239 | Local valToNumber(MDB_val &data) { 240 | return Nan::New(*((double*)data.mv_data)); 241 | } 242 | 243 | Local valToBoolean(MDB_val &data) { 244 | return Nan::New(*((bool*)data.mv_data)); 245 | } 246 | 247 | void throwLmdbError(int rc) { 248 | auto err = Nan::Error(mdb_strerror(rc)); 249 | err.As()->Set(Nan::GetCurrentContext(), Nan::New("code").ToLocalChecked(), Nan::New(rc)); 250 | return Nan::ThrowError(err); 251 | } 252 | 253 | void consoleLog(const char *msg) { 254 | Local str = Nan::New("console.log('").ToLocalChecked(); 255 | //str = String::Concat(str, Nan::New(msg).ToLocalChecked()); 256 | //str = String::Concat(str, Nan::New("');").ToLocalChecked()); 257 | 258 | Local