Lindenii Project Forge
Move the raw bindings to lmdb::ffi and remove docs
use fmt; use io;
use lmdb;
use lmdb_ffi = lmdb::ffi;
use strings; use types; use types::c; use os; export fn main() void = { if (len(os::args) != 3) { abort("need two arguments (db dir, key)"); };
const key = lmdb::val{
const key = lmdb_ffi::val{
mv_size = len(strings::toutf8(os::args[2])), mv_data = c::unterminatedstr(os::args[2]): *opaque, };
let val: lmdb::val = lmdb::val{
let val: lmdb_ffi::val = lmdb_ffi::val{
mv_size = 0, mv_data = null, };
let env: nullable *lmdb::env = null; let rc: int = lmdb::env_create(&env);
let env: nullable *lmdb_ffi::env = null; let rc: int = lmdb_ffi::env_create(&env);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to create env"); };
let env: *lmdb::env = env as *lmdb::env;
let env: *lmdb_ffi::env = env as *lmdb_ffi::env;
let path = c::fromstr(os::args[1]); defer free(path);
rc = lmdb::env_open(env, path, 0, 0o644);
rc = lmdb_ffi::env_open(env, path, 0, 0o644);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to open env"); };
let txn: nullable *lmdb::txn = null; rc = lmdb::txn_begin(env, null, 0, &txn);
let txn: nullable *lmdb_ffi::txn = null; rc = lmdb_ffi::txn_begin(env, null, 0, &txn);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to create txn"); };
let txn: *lmdb::txn = txn as *lmdb::txn;
let txn: *lmdb_ffi::txn = txn as *lmdb_ffi::txn;
let dbi: lmdb::dbi = 0; rc = lmdb::dbi_open(txn, null, lmdb::CREATE, &dbi);
let dbi: lmdb_ffi::dbi = 0; rc = lmdb_ffi::dbi_open(txn, null, lmdb_ffi::CREATE, &dbi);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to create dbi"); };
rc = lmdb::get(txn, dbi, &key, &val);
rc = lmdb_ffi::get(txn, dbi, &key, &val);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to get"); };
rc = lmdb::txn_abort(txn);
rc = lmdb_ffi::txn_abort(txn);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to abort"); }; let data: []u8 = *(&(types::slice { data = val.mv_data, length = val.mv_size, capacity = val.mv_size, }): *[]u8); io::write(os::stdout, data)!; };
use fmt; use io;
use lmdb;
use lmdb_ffi = lmdb::ffi;
use strings; use types; use types::c; use os; export fn main() void = { if (len(os::args) != 3) { abort("need two arguments (db dir, key)"); };
const key = lmdb::val{
const key = lmdb_ffi::val{
mv_size = len(strings::toutf8(os::args[2])), mv_data = c::unterminatedstr(os::args[2]): *opaque, }; let v = io::drain(os::stdin)!; defer free(v);
const val = lmdb::val{
const val = lmdb_ffi::val{
mv_size = len(v), mv_data = (*(&v: *types::slice)).data: *opaque, };
let env: nullable *lmdb::env = null; let rc: int = lmdb::env_create(&env);
let env: nullable *lmdb_ffi::env = null; let rc: int = lmdb_ffi::env_create(&env);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to create env"); };
let env: *lmdb::env = env as *lmdb::env;
let env: *lmdb_ffi::env = env as *lmdb_ffi::env;
let path = c::fromstr(os::args[1]); defer free(path);
rc = lmdb::env_open(env, path, 0, 0o644);
rc = lmdb_ffi::env_open(env, path, 0, 0o644);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to open env"); };
let txn: nullable *lmdb::txn = null; rc = lmdb::txn_begin(env, null, 0, &txn);
let txn: nullable *lmdb_ffi::txn = null; rc = lmdb_ffi::txn_begin(env, null, 0, &txn);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to create txn"); };
let txn: *lmdb::txn = txn as *lmdb::txn;
let txn: *lmdb_ffi::txn = txn as *lmdb_ffi::txn;
let dbi: lmdb::dbi = 0; rc = lmdb::dbi_open(txn, null, lmdb::CREATE, &dbi);
let dbi: lmdb_ffi::dbi = 0; rc = lmdb_ffi::dbi_open(txn, null, lmdb_ffi::CREATE, &dbi);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to create dbi"); };
rc = lmdb::put(txn, dbi, &key, &val, 0);
rc = lmdb_ffi::put(txn, dbi, &key, &val, 0);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to put"); };
rc = lmdb::txn_commit(txn);
rc = lmdb_ffi::txn_commit(txn);
if (rc != 0) { fmt::printf("error: {}\n", rc)!; abort("failed to commit"); }; };
Lightning Memory-Mapped Database Manager (LMDB) Introduction LMDB is a Btree-based database management library modeled loosely on the BerkeleyDB API, but much simplified. The entire database is exposed in a memory map, and all data fetches return data directly from the mapped memory, so no malloc's or memcpy's occur during data fetches. As such, the library is extremely simple because it requires no page caching layer of its own, and it is extremely high performance and memory-efficient. It is also fully transactional with full ACID semantics, and when the memory map is read-only, the database integrity cannot be corrupted by stray pointer writes from application code. The library is fully thread-aware and supports concurrent read/write access from multiple processes and threads. Data pages use a copy-on- write strategy so no active data pages are ever overwritten, which also provides resistance to corruption and eliminates the need of any special recovery procedures after a system crash. Writes are fully serialized; only one write transaction may be active at a time, which guarantees that writers can never deadlock. The database structure is multi-versioned so readers run with no locks; writers cannot block readers, and readers don't block writers. Unlike other well-known database mechanisms which use either write-ahead transaction logs or append-only data writes, LMDB requires no maintenance during operation. Both write-ahead loggers and append-only databases require periodic checkpointing and/or compaction of their log or database files otherwise they grow without bound. LMDB tracks free pages within the database and re-uses them for new write operations, so the database size does not grow without bound in normal use. The memory map can be used as a read-only or read-write map. It is read-only by default as this provides total immunity to corruption. Using read-write mode offers much higher write performance, but adds the possibility for stray application writes thru pointers to silently corrupt the database. Of course if your application code is known to be bug-free (...) then this is not an issue. Caveats Troubleshooting the lock file, plus semaphores on BSD systems: - A broken lockfile can cause sync issues. Stale reader transactions left behind by an aborted program cause further writes to grow the database quickly, and stale locks can block further operation. Fix: Check for stale readers periodically, using the [[reader_check]] function or the "mdb_stat" tool. Stale writers will be cleared automatically on some systems: - Windows - automatic - Linux, systems using POSIX mutexes with Robust option - automatic - not on BSD, systems using POSIX semaphores. Otherwise just make all programs using the database close it; the lockfile is always reset on first open of the environment. - On BSD systems or others configured with [[USE_POSIX_SEM]], startup can fail due to semaphores owned by another userid. Fix: Open and close the database as the user which owns the semaphores (likely last user) or as root, while no other process is using the database. Restrictions/caveats (in addition to those listed for some functions): - Only the database owner should normally use the database on BSD systems or when otherwise configured with [[USE_POSIX_SEM]]. Multiple users can cause startup to fail later, as noted above. - There is normally no pure read-only mode, since readers need write access to locks and lock file. Exceptions: On read-only filesystems or with the [[NOLOCK]] flag described under [[env_open]]. - An LMDB configuration will often reserve considerable unused memory address space and maybe file size for future growth. This does not use actual memory or disk space, but users may need to understand the difference so they won't be scared off. - By default, in versions before 0.9.10, unused portions of the data file might receive garbage data from memory freed by other code. (This does not happen when using the [[WRITEMAP]] flag.) As of 0.9.10 the default behavior is to initialize such memory before writing to the data file. Since there may be a slight performance cost due to this initialization, applications may disable it using the [[NOMEMINIT]] flag. Applications handling sensitive data which must not be written should not use this flag. This flag is irrelevant when using [[WRITEMAP]]. - A thread can only use one transaction at a time, plus any child transactions. Each transaction belongs to one thread. See below. The [[NOTLS]] flag changes this for read-only transactions. - Use an [[env]] in the process which opened it, not after [[rt::fork]]. - Do not have open an LMDB database twice in the same process at the same time. Not even from a plain [[rt::open]] call - [[rt::close]]ing it breaks [[rt::fcntl]] advisory locking. (It is OK to reopen it after [[rt::fork]] - [[rt::exec]], since the lockfile has [[rt::FD_CLOEXEC]] set.) - Avoid long-lived transactions. Read transactions prevent reuse of pages freed by newer write transactions, thus the database can grow quickly. Write transactions prevent other write transactions, since writes are serialized. - Avoid suspending a process with active transactions. These would then be "long-lived" as above. Also read transactions suspended when writers commit could sometimes see wrong data. ...when several processes can use a database concurrently: - Avoid aborting a process with an active transaction. The transaction becomes "long-lived" as above until a check for stale readers is performed or the lockfile is reset, since the process may not remove it from the lockfile. This does not apply to write transactions if the system clears stale writers, see above. - If you do that anyway, do a periodic check for stale readers. Or close the environment once in a while, so the lockfile can get reset. - Do not use LMDB databases on remote filesystems, even between processes on the same host. This breaks [[rt::flock]] on some OSes, possibly memory map sync, and certainly sync between programs on different hosts. - Opening a database can fail if another process is opening or closing it at exactly the same time.
// Create a cursor handle. // // A cursor is associated with a specific transaction and database. // A cursor cannot be used when its database handle is closed. Nor // when its transaction has ended, except with [[cursor_renew]]. // // It can be discarded with [[cursor_close]]. // // A cursor in a write-transaction can be closed before its transaction // ends, and will otherwise be closed when its transaction ends. // // A cursor in a read-only transaction must be closed explicitly, before // or after its transaction ends. It can be reused with // [[cursor_renew]] before finally closing it. // // - txn: A transaction handle returned by [[txn_begin]] // - dbi: A database handle returned by [[dbi_open]] // - cursor: Address where the new [[cursor]] handle will be stored // - return: EINVAL on failure and 0 on success. export @symbol("mdb_cursor_open") fn cursor_open(txn: *txn, dbi: *dbi, cursor: *nullable *cursor) int; // Close a cursor handle. // // The cursor handle will be freed and must not be used again after this call. // Its transaction must still be live if it is a write-transaction. // // - cursor: A cursor handle returned by [[cursor_open]] export @symbol("mdb_cursor_close") fn cursor_close(cursor: *cursor) void; // Retrieve by cursor. // // This function retrieves key/data pairs from the database. The address and length // of the key are returned in the object to which key refers (except for the // case of the [[SET]] option, in which the key object is unchanged), and // the address and length of the data are returned in the object to which data // refers. // // See [[get]] for restrictions on using the output values. // // Parameters: // - cursor: A cursor handle returned by [[cursor_open]] // - key: The key for a retrieved item // - data: The data of a retrieved item // - op: A cursor operation [[cursor_op]] // // Return value: A non-zero error value on failure and 0 on success. // Some possible errors are: // - [[NOTFOUND]] - no matching key found; // - [[EINVAL]] - an invalid parameter was specified. export @symbol("mdb_cursor_get") fn cursor_get(cursor: *cursor, key: *val, data: *val, op: cursor_op) int; // Delete current key/data pair // // This function deletes the key/data pair to which the cursor refers. // This does not invalidate the cursor, so operations such as MDB_NEXT // can still be used on it. // // Both MDB_NEXT and MDB_GET_CURRENT will return the same record after // this operation. // // Parameters: // - cursor: A cursor handle returned by [[cursor_open]] // - flags: Options for this operation. This parameter // must be set to 0 or one of the values described here. // // Flags: // - [[NODUPDATA]] - delete all of the data items for the current key. This flag // may only be specified if the database was opened with [[DUPSORT]]. // // Return value: A non-zero error value on failure and 0 on success. Some // possible errors are: // - EACCES - an attempt was made to write in a read-only transaction. // - EINVAL - an invalid parameter was specified. // export @symbol("mdb_cursor_del") fn cursor_del(cursor: *cursor, flags: uint) int;
// This is the set of all operations for retrieving data // using a cursor. export type cursor_op = enum { FIRST, // Position at first key/data item FIRST_DUP, // Position at first data item of current key. Only for [[DUPSORT]] GET_BOTH, // Position at key/data pair. Only for [[DUPSORT]] GET_BOTH_RANGE, // position at key, nearest data. Only for [[DUPSORT]] GET_CURRENT, // Return key/data at current cursor position GET_MULTIPLE, // Return up to a page of duplicate data items from current cursor position. Move cursor to prepare for [[NEXT_MULTIPLE]]. Only for [[DUPFIXED]] LAST, // Position at last key/data item LAST_DUP, // Position at last data item of current key. Only for [[DUPSORT]] NEXT, // Position at next data item NEXT_DUP, // Position at next data item of current key. Only for [[DUPSORT]] NEXT_MULTIPLE, // Return up to a page of duplicate data items from next cursor position. Move cursor to prepare for [[NEXT_MULTIPLE]]. Only for [[DUPFIXED]] NEXT_NODUP, // Position at first data item of next key PREV, // Position at previous data item PREV_DUP, // Position at previous data item of current key. Only for [[DUPSORT]] PREV_NODUP, // Position at last data item of previous key SET, // Position at specified key SET_KEY, // Position at specified key, return key + data SET_RANGE, // Position at first key greater than or equal to specified key. PREV_MULTIPLE, // Position at previous page and return up to a page of duplicate data items. Only for [[DUPFIXED]] };
use types::c; // Open a database in the environment. // // A database handle denotes the name and parameters of a database, // independently of whether such a database actually exists. The database // handle may be discarded by calling [[dbi_close]]. If the database was // already open, this function returns the old handle. The handle may only // be closed once. // // The database handle will be private to the current transaction until the // transaction is successfully committed. If the transaction is aborted, the // handle will be closed automatically. After a successful commit, the handle // will reside in the shared environment and may be used by other transactions. // // This function must not be called from multiple concurrent transactions in // the same process. A transaction that uses this function must finish (either // commit or abort) before any other transaction in the process may use this // function. // // To use named databases (with name != NULL), [[env_set_maxdbs]] must be // called before opening the environment. Database names are keys in the unnamed // database, and may be read but not written. // // Parameters: // - txn: A transaction handle returned by [[txn_begin]] // - name: The name of the database to open. If only a single database is // needed in the environment, this value may be NULL. // - flags: Special options for this database. Must be set to 0 or by // bitwise OR'ing together one or more of the defined flags. // - dbi: Address where the new [[dbi]] handle will be stored. // // Flags: // - [[REVERSEKEY]]: Keys are compared in reverse order (from end to start) // - [[DUPSORT]]: Duplicate keys may be used in the database // - [[INTEGERKEY]]: Keys are binary integers in native byte order // - [[DUPFIXED]]: Requires [[DUPSORT]]. Data items are all the same size // - [[INTEGERDUP]]: Duplicate data items are binary integers // - [[REVERSEDUP]]: Duplicate data items are compared in reverse order // - [[CREATE]]: Create the named database if it does not exist // // Return value: A non-zero error value on failure and 0 on success. Some // possible errors are: // - [[NOTFOUND]]: The specified database doesn’t exist and [[CREATE]] was not specified // - [[DBS_FULL]]: Too many databases have been opened (see [[env_set_maxdbs]]) export @symbol("mdb_dbi_open") fn dbi_open(txn: *txn, name: const nullable *c::char, flags: uint, dbi: *dbi) int;
use types::c; // Create an LMDB environment handle. // // This function allocates memory for a [[env]] structure. To release // the allocated memory and discard the handle, call [[env_close]]. // Before the handle may be used, it must be opened using [[env_open]]. // Various other options may also need to be set before opening the handle, // e.g. [[env_set_mapsize]], [[env_set_maxreaders]], [[env_set_maxdbs]], // depending on usage requirements. // // Parameters: // - env: The address where the new handle will be stored // // Return value: A non-zero error value on failure and 0 on success. export @symbol("mdb_env_create") fn env_create(env: *nullable *env) int; // Open an environment handle. // // If this function fails, [[env_close]] must be called to discard the [[env]] handle. // // Parameters: // - env: An environment handle returned by [[env_create]] // - path: The directory in which the database files reside. This // directory must already exist and be writable. // - flags: Special options for this environment. This parameter // must be set to 0 or by bitwise OR'ing together one or more of the // values described here. Flags set by [[env_set_flags]] are also used. // - mode: The UNIX permissions to set on created files and semaphores. This // parameter is ignored on Windows. // // - [[FIXEDMAP]]: // use a fixed address for the mmap region. This flag must be specified // when creating the environment, and is stored persistently in the environment. // If successful, the memory map will always reside at the same virtual address // and pointers used to reference data items in the database will be constant // across multiple invocations. This option may not always work, depending on // how the operating system has allocated memory to shared libraries and other uses. // The feature is highly experimental. // - [[NOSUBDIR]]: // By default, LMDB creates its environment in a directory whose // pathname is given in path, and creates its data and lock files // under that directory. With this option, path is used as-is for // the database main data file. The database lock file is the path // with "-lock" appended. // - [[RDONLY]]: // Open the environment in read-only mode. No write operations will be // allowed. LMDB will still modify the lock file - except on read-only // filesystems, where LMDB does not use locks. // - [[WRITEMAP]]: // Use a writeable memory map unless MDB_RDONLY is set. This uses // fewer mallocs but loses protection from application bugs // like wild pointer writes and other bad updates into the database. // This may be slightly faster for DBs that fit entirely in RAM, but // is slower for DBs larger than RAM. // Incompatible with nested transactions. // Do not mix processes with and without MDB_WRITEMAP on the same // environment. This can defeat durability (#mdb_env_sync etc). // - [[NOMETASYNC]]: // Flush system buffers to disk only once per transaction, omit the // metadata flush. Defer that until the system flushes files to disk, // or next non-MDB_RDONLY commit or [[env_sync]]. This optimization // maintains database integrity, but a system crash may undo the last // committed transaction. I.e. it preserves the ACI (atomicity, // consistency, isolation) but not D (durability) database property. // This flag may be changed at any time using [[env_set_flags]]. // - [[NOSYNC]]: // Don't flush system buffers to disk when committing a transaction. // This optimization means a system crash can corrupt the database or // lose the last transactions if buffers are not yet flushed to disk. // The risk is governed by how often the system flushes dirty buffers // to disk and how often [[env_sync]] is called. However, if the // filesystem preserves write order and the [[WRITEMAP]] flag is not // used, transactions exhibit ACI (atomicity, consistency, isolation) // properties and only lose D (durability). I.e. database integrity // is maintained, but a system crash may undo the final transactions. // Note that ([[NOSYNC]] | [[WRITEMAP)]] leaves the system with no // hint for when to write transactions to disk, unless [[env_sync]] // is called. ([[MAPASYNC]] | [[WRITEMAP)]] may be preferable. // This flag may be changed at any time using [[env_set_flags]]. // - [[MAPASYNC]]: // When using [[WRITEMAP]], use asynchronous flushes to disk. // As with [[NOSYNC]], a system crash can then corrupt the // database or lose the last transactions. Calling [[env_sync]] // ensures on-disk database integrity until next commit. // This flag may be changed at any time using [[env_set_flags]]. // - [[NOTLS]]: // Don't use Thread-Local Storage. Tie reader locktable slots to // [[txn]] objects instead of to threads. I.e. [[txn_reset]] keeps // the slot reserved for the [[txn]] object. A thread may use parallel // read-only transactions. A read-only transaction may span threads if // the user synchronizes its use. Applications that multiplex many // user threads over individual OS threads need this option. Such an // application must also serialize the write transactions in an OS // thread, since LMDB's write locking is unaware of the user threads. // - [[NOLOCK]]: // Don't do any locking. If concurrent access is anticipated, the // caller must manage all concurrency itself. For proper operation // the caller must enforce single-writer semantics, and must ensure // that no readers are using old transactions while a writer is // active. The simplest approach is to use an exclusive lock so that // no readers may be active at all when a writer begins. // - [[NORDAHEAD]]: // Turn off readahead. Most operating systems perform readahead on // read requests by default. This option turns it off if the OS // supports it. Turning it off may help random read performance // when the DB is larger than RAM and system RAM is full. // The option is not implemented on Windows. // - [[NOMEMINIT]]: // Don't initialize malloc'd memory before writing to unused spaces // in the data file. By default, memory for pages written to the data // file is obtained using malloc. While these pages may be reused in // subsequent transactions, freshly malloc'd pages will be initialized // to zeroes before use. This avoids persisting leftover data from other // code (that used the heap and subsequently freed the memory) into the // data file. Note that many other system libraries may allocate // and free memory from the heap for arbitrary uses. E.g., stdio may // use the heap for file I/O buffers. This initialization step has a // modest performance cost so some applications may want to disable // it using this flag. This option can be a problem for applications // which handle sensitive data like passwords, and it makes memory // checkers like Valgrind noisy. This flag is not needed with [[WRITEMAP]], // which writes directly to the mmap instead of using malloc for pages. The // initialization is also skipped if [[RESERVE]] is used; the // caller is expected to overwrite all of the memory that was // reserved in that case. // This flag may be changed at any time using [[env_set_flags]]. // // Return value: A non-zero error value on failure and 0 on success. Some possible // errors are: // - [[VERSION_MISMATCH]] - the version of the LMDB library doesn't match the // version that created the database environment. // - [[INVALID]] - the environment file headers are corrupted. // - ENOENT - the directory specified by the path parameter doesn't exist. // - EACCES - the user didn't have permission to access the environment files. // - EAGAIN - the environment was locked by another process. export @symbol("mdb_env_open") fn env_open(env: *env, path: const *c::char, flag: uint, mode: mode_t) int; // Close the environment and release the memory map. // // Only a single thread may call this function. All transactions, databases, // and cursors must already be closed before calling this function. Attempts to // use any such handles after calling this function will cause a SIGSEGV. // The environment handle will be freed and must not be used again after this call. // // Parameters: // - env: An environment handle returned by [[env_create]] export @symbol("mdb_env_close") fn env_close(env: *env) void; // Set the size of the memory map to use for this environment. // // The size should be a multiple of the OS page size. The default is // 10485760 bytes. The size of the memory map is also the maximum size // of the database. The value should be chosen as large as possible, // to accommodate future growth of the database. // // This function should be called after [[env_create]] and before [[env_open]]. // It may be called at later times if no transactions are active in // this process. Note that the library does not check for this condition, // the caller must ensure it explicitly. // // The new size takes effect immediately for the current process but // will not be persisted to any others until a write transaction has been // committed by the current process. Also, only mapsize increases are // persisted into the environment. // // If the mapsize is increased by another process, and data has grown // beyond the range of the current mapsize, [[txn_begin]] will // return [[MAP_RESIZED]]. This function may be called with a size // of zero to adopt the new size. // // Any attempt to set a size smaller than the space already consumed // by the environment will be silently changed to the current size of the used space. // // Parameters // - env: An environment handle returned by [[env_create]] // - size: The size in bytes // // Return value: A non-zero error value on failure and 0 on success. Some possible // errors are: // - EINVAL - an invalid parameter was specified, or the environment has // an active write transaction. export @symbol("mdb_env_set_mapsize") fn env_set_mapsize(env: *env, size_: size) int; // Set the maximum number of named databases for the environment. // // This function is only needed if multiple databases will be used in the // environment. Simpler applications that use the environment as a single // unnamed database can ignore this option. // // This function may only be called after [[env_create]] and before [[env_open]]. // // Currently a moderate number of slots are cheap but a huge number gets // expensive: 7-120 words per transaction, and every [[dbi_open]] // does a linear search of the opened slots. // // Parameters // - env: An environment handle returned by [[env_create]] // - dbs: The maximum number of databases // // Return value: A non-zero error value on failure and 0 on success. Some possible // errors are: // - EINVAL - an invalid parameter was specified, or the environment is already open. export @symbol("mdb_env_set_maxdbs") fn env_set_maxdbs(env: *env, dbs: dbi) int;
These are raw unencumbered bindings for LMDB, except that mdb_/MDB_ prefixes have been removed, and nullable is added where necessary. Please refer to the official LMDB documentation at http://www.lmdb.tech/doc/
export @symbol("mdb_cursor_open") fn cursor_open(txn: *txn, dbi: *dbi, cursor: *nullable *cursor) int; export @symbol("mdb_cursor_close") fn cursor_close(cursor: *cursor) void; export @symbol("mdb_cursor_get") fn cursor_get(cursor: *cursor, key: *val, data: *val, op: cursor_op) int; export @symbol("mdb_cursor_del") fn cursor_del(cursor: *cursor, flags: uint) int;
export type cursor_op = enum { FIRST, FIRST_DUP, GET_BOTH, GET_BOTH_RANGE, GET_CURRENT, GET_MULTIPLE, LAST, LAST_DUP, NEXT, NEXT_DUP, NEXT_MULTIPLE, NEXT_NODUP, PREV, PREV_DUP, PREV_NODUP, SET, SET_KEY, SET_RANGE, PREV_MULTIPLE, };
use types::c; export @symbol("mdb_dbi_open") fn dbi_open(txn: *txn, name: const nullable *c::char, flags: uint, dbi: *dbi) int;
use types::c; export @symbol("mdb_env_create") fn env_create(env: *nullable *env) int; export @symbol("mdb_env_open") fn env_open(env: *env, path: const *c::char, flag: uint, mode: mode_t) int; export @symbol("mdb_env_close") fn env_close(env: *env) void; export @symbol("mdb_env_set_mapsize") fn env_set_mapsize(env: *env, size_: size) int; export @symbol("mdb_env_set_maxdbs") fn env_set_maxdbs(env: *env, dbs: dbi) int;
export def CP_COMPACT = 0x01;
export def REVERSEKEY = 0x02; export def DUPSORT = 0x04; export def INTEGERKEY = 0x08; export def DUPFIXED = 0x10; export def INTEGERDUP = 0x20; export def REVERSEDUP = 0x40; export def CREATE = 0x40000;
export def FIXEDMAP = 0x01; export def NOSUBDIR = 0x4000; export def NOSYNC = 0x10000; export def RDONLY = 0x20000; export def NOMETASYNC = 0x40000; export def WRITEMAP = 0x80000; export def MAPASYNC = 0x100000; export def NOTLS = 0x200000; export def NOLOCK = 0x400000; export def NORDAHEAD = 0x800000; export def NOMEMINIT = 0x1000000;
export def NOOVERWRITE = 0x10; export def NODUPDATA = 0x20; export def CURRENT = 0x40; export def RESERVE = 0x10000; export def APPEND = 0x20000; export def APPENDDUP = 0x40000; export def MULTIPLE = 0x80000;
export @symbol("mdb_del") fn del(txn: *txn, dbi: dbi, key: *val, data: *val) int; export @symbol("mdb_get") fn get(txn: *txn, dbi: dbi, key: *val, data: *val) int; export @symbol("mdb_put") fn put(txn: *txn, dbi: dbi, key: *val, data: *val, flags: uint) int;
export @symbol("mdb_reader_check") fn reader_check(env: *env, dead: int) int;
export def SUCCESS = 0; export def KEYEXIST = (-30799); export def NOTFOUND = (-30798); export def PAGE_NOTFOUND = (-30797); export def CORRUPTED = (-30796); export def PANIC = (-30795); export def VERSION_MISMATCH = (-30794); export def INVALID = (-30793); export def MAP_FULL = (-30792); export def DBS_FULL = (-30791); export def READERS_FULL = (-30790); export def TLS_FULL = (-30789); export def TXN_FULL = (-30788); export def CURSOR_FULL = (-30787); export def PAGE_FULL = (-30786); export def MAP_RESIZED = (-30785); export def INCOMPATIBLE = (-30784); export def BAD_RSLOT = (-30783); export def BAD_TXN = (-30782); export def BAD_VALSIZE = (-30781); export def BAD_DBI = (-30780); export def LAST_ERRCODE = BAD_DBI;
export @symbol("mdb_txn_begin") fn txn_begin(env: *env, parent: nullable *txn, flags: uint, txn: *nullable *txn) int; export @symbol("mdb_txn_commit") fn txn_commit(txn: *txn) int; export @symbol("mdb_txn_abort") fn txn_abort(txn: *txn) int;
export type cursor = opaque; export type dbi = uint; export type env = opaque; export type txn = opaque; export type val = struct { mv_size: size, mv_data: nullable *opaque, }; export type mode_t = u32;
// Compacting copy: Omit free space from copy, and renumber all pages sequentially. export def CP_COMPACT = 0x01;
// use reverse string keys export def REVERSEKEY = 0x02; // use sorted duplicates export def DUPSORT = 0x04; // numeric keys in native byte order: either unsigned int or size_t. The keys must all be of the same size. export def INTEGERKEY = 0x08; // with [[DUPSORT]], sorted dup items have fixed size export def DUPFIXED = 0x10; // with [[DUPSORT]], dups are [[INTEGERKEY]]-style integers export def INTEGERDUP = 0x20; // with [[DUPSORT]], use reverse string dups export def REVERSEDUP = 0x40; // create DB if not already existing export def CREATE = 0x40000;
// mmap at a fixed address (experimental) export def FIXEDMAP = 0x01; // no environment directory export def NOSUBDIR = 0x4000; // don't fsync after commit export def NOSYNC = 0x10000; // read only export def RDONLY = 0x20000; // don't fsync metapage after commit export def NOMETASYNC = 0x40000; // use writable mmap export def WRITEMAP = 0x80000; // use asynchronous msync when [[WRITEMAP]] is used export def MAPASYNC = 0x100000; // tie reader locktable slots to [[txn]] objects instead of to threads export def NOTLS = 0x200000; // don't do any locking, caller must manage their own locks export def NOLOCK = 0x400000; // don't do readahead (no effect on Windows) export def NORDAHEAD = 0x800000; // don't initialize malloc'd memory before writing to datafile export def NOMEMINIT = 0x1000000;
// For put: Don't write if the key already exists. export def NOOVERWRITE = 0x10; // Only for [[DUPSORT]]: For put: don't write if the key and data pair already exist. For mdb_cursor_del: remove all duplicate data items. export def NODUPDATA = 0x20; // For mdb_cursor_put: overwrite the current key/data pair export def CURRENT = 0x40; // For put: Just reserve space for data, don't copy it. Return a pointer to the reserved space. export def RESERVE = 0x10000; // Data is being appended, don't split full pages. export def APPEND = 0x20000; // Duplicate data is being appended, don't split full pages. export def APPENDDUP = 0x40000; // Store multiple data items in one call. Only for [[DUPFIXED]]. export def MULTIPLE = 0x80000;
// Delete items from a database. // // This function removes key/data pairs from the database. If the database does not // support sorted duplicate data items ([[DUPSORT]]), the data parameter is ignored. // If the database supports sorted duplicates and the data parameter is NULL, all // of the duplicate data items for the key will be deleted. Otherwise, if the data // parameter is non-NULL, only the matching data item will be deleted. This function // will return [[NOTFOUND]] if the specified key/data pair is not in the database. // // Parameters: // - txn: A transaction handle returned by [[txn_begin]] // - dbi: A database handle returned by [[dbi_open]] // - key: The key to delete from the database // - data: The data to delete // // Return value: A non-zero error value on failure and 0 on success. Some possible errors are: // - EACCES: an attempt was made to write in a read-only transaction. // - EINVAL: an invalid parameter was specified. export @symbol("mdb_del") fn del(txn: *txn, dbi: dbi, key: *val, data: *val) int; // Get items from a database. // // This function retrieves key/data pairs from the database. The address and length // of the data associated with the specified key are returned in the structure to // which data refers. If the database supports duplicate keys ([[DUPSORT]]), then // the first data item for the key will be returned. Retrieval of other items // requires the use of [[cursor_get]]. // // The memory pointed to by the returned values is owned by the database. The caller // need not dispose of the memory, and may not modify it in any way. For values // returned in a read-only transaction, any modification attempts cause a SIGSEGV. // Values returned from the database are valid only until a subsequent update // operation, or the end of the transaction. // // Parameters: // - txn: A transaction handle returned by [[txn_begin]] // - dbi: A database handle returned by [[dbi_open]] // - key: The key to search for in the database // - data: The data corresponding to the key // // Return value: A non-zero error value on failure and 0 on success. Some possible errors are: // - [[NOTFOUND]]: the key was not in the database // - EINVAL: an invalid parameter was specified export @symbol("mdb_get") fn get(txn: *txn, dbi: dbi, key: *val, data: *val) int; // Store items into a database. // // This function stores key/data pairs in the database. The default behavior // is to enter the new key/data pair, replacing any previously existing key // if duplicates are disallowed, or adding a duplicate data item if duplicates // are allowed ([[DUPSORT]]). // // Parameters: // - txn: A transaction handle returned by [[txn_begin]] // - dbi: A database handle returned by [[dbi_open]] // - key: The key to store in the database // - data: The data to store // - flags: Special options for this operation. Must be set to 0 or by bitwise // OR'ing together one or more of the values described below. // // Flags: // - [[NODUPDATA]]: enter the new key/data pair only if it does not already appear // in the database. Only valid if the database was opened with [[DUPSORT]]. // Returns [[KEYEXIST]] if the key/data pair already appears. // - [[NOOVERWRITE]]: enter the new key/data pair only if the key does not already // appear in the database. Returns [[KEYEXIST]] if the key is present, // even if the database supports duplicates. The data parameter will be set // to the existing item. // - [[RESERVE]]: reserve space for data of the given size, but don't copy the // given data. Instead, return a pointer to the reserved space, which the caller // can fill later (before the next update or transaction end). This must not // be specified if the database was opened with [[DUPSORT]]. // - [[APPEND]]: append the given key/data pair to the end of the database for // fast bulk loading when keys are already in correct order. Loading unsorted // keys with this flag will cause a [[KEYEXIST]] error. // - [[APPENDDUP]]: same as [[APPEND]], but for sorted duplicate data. // // Return value: A non-zero error value on failure and 0 on success. Some possible errors are: // - [[MAP_FULL]]: the database is full (see [[env_set_mapsize]]) // - [[TXN_FULL]]: the transaction has too many dirty pages // - EACCES: an attempt was made to write in a read-only transaction // - EINVAL: an invalid parameter was specified export @symbol("mdb_put") fn put(txn: *txn, dbi: dbi, key: *val, data: *val, flags: uint) int;
// Check for stale entries in the reader lock table. // // Parameters // - env: An environment handle returned by [[env_create]] // - dead: Number of stale slots that were cleared // // Return value: 0 on success, non-zero on failure. export @symbol("mdb_reader_check") fn reader_check(env: *env, dead: int) int;
// BerkeleyDB uses -30800 to -30999, we'll go under them // Successful result export def SUCCESS = 0; // key/data pair already exists export def KEYEXIST = (-30799); // key/data pair not found (EOF) export def NOTFOUND = (-30798); // Requested page not found - this usually indicates corruption export def PAGE_NOTFOUND = (-30797); // Located page was wrong type export def CORRUPTED = (-30796); // Update of meta page failed or environment had fatal error export def PANIC = (-30795); // Environment version mismatch export def VERSION_MISMATCH = (-30794); // File is not a valid LMDB file export def INVALID = (-30793); // Environment mapsize reached export def MAP_FULL = (-30792); // Environment maxdbs reached export def DBS_FULL = (-30791); // Environment maxreaders reached export def READERS_FULL = (-30790); // Too many TLS keys in use - Windows only export def TLS_FULL = (-30789); // Txn has too many dirty pages export def TXN_FULL = (-30788); // Cursor stack too deep - internal error export def CURSOR_FULL = (-30787); // Page has not enough space - internal error export def PAGE_FULL = (-30786); // Database contents grew beyond environment mapsize export def MAP_RESIZED = (-30785); // Operation and DB incompatible, or DB type changed. This can mean: // (1) The operation expects an [[DUPSORT]] / [[DUPFIXED]] database. // (2) Opening a named DB when the unnamed DB has [[DUPSORT]] / [[INTEGERKEY]]. // (3) Accessing a data record as a database, or vice versa. // (4) The database was dropped and recreated with different flags. export def INCOMPATIBLE = (-30784); // Invalid reuse of reader locktable slot export def BAD_RSLOT = (-30783); // Transaction must abort, has a child, or is invalid export def BAD_TXN = (-30782); // Unsupported size of key/DB name/data, or wrong DUPFIXED size export def BAD_VALSIZE = (-30781); // The specified DBI was changed unexpectedly export def BAD_DBI = (-30780); // The last defined error code export def LAST_ERRCODE = BAD_DBI;
// Create a transaction for use with the environment. // // The transaction handle may be discarded using [[txn_abort]] or [[txn_commit]]. // // A transaction and its cursors must only be used by a single // thread, and a thread may only have a single transaction at a time. // If [[NOTLS]] is in use, this does not apply to read-only transactions. // // Cursors may not span transactions. // // Parameters // - env: An environment handle returned by [[env_create]] // - parent: If this parameter is non-NULL, the new transaction // will be a nested transaction, with the transaction indicated by parent // as its parent. Transactions may be nested to any level. A parent // transaction and its cursors may not issue any other operations than // mdb_txn_commit and mdb_txn_abort while it has active child transactions. // - flags: Special options for this transaction. This parameter // must be set to 0 or by bitwise OR'ing together one or more of the // values described here. // - txn: Address where the new [[txn]] handle will be stored // // Flags // - [[MDB_RDONLY]] // This transaction will not perform any write operations. // // Return value: A non-zero error value on failure and 0 on success. Some possible // errors are: // - [[PANIC]] - a fatal error occurred earlier and the environment // - must be shut down. // - [[MAP_RESIZED]] - another process wrote data beyond this MDB_env's // - mapsize and this environment's map must be resized as well. // - See [[env_set_mapsize]]. // - [[READERS_FULL]] - a read-only transaction was requested and // - the reader lock table is full. See [[env_set_maxreaders]]. // - ENOMEM - out of memory. export @symbol("mdb_txn_begin") fn txn_begin(env: *env, parent: nullable *txn, flags: uint, txn: *nullable *txn) int; // Commit all the operations of a transaction into the database. // // The transaction handle is freed. It and its cursors must not be used // again after this call, except with [[cursor_renew]]. // // Parameters // - txn: A transaction handle returned by [[txn_begin]] // // Return value: A non-zero error value on failure and 0 on success. Some possible // errors are: // - EINVAL - an invalid parameter was specified. // - ENOSPC - no more disk space. // - EIO - a low-level I/O error occurred while writing. // - ENOMEM - out of memory. export @symbol("mdb_txn_commit") fn txn_commit(txn: *txn) int; // Abandon all the operations of the transaction instead of saving them. // // The transaction handle is freed. It and its cursors must not be used // again after this call, except with [[cursor_renew]]. // // Parameters // - txn: A transaction handle returned by [[txn_begin]] export @symbol("mdb_txn_abort") fn txn_abort(txn: *txn) int;
// Opaque structure for navigating through a database export type cursor = opaque; // A handle for an individual database in the DB environment. export type dbi = uint; // Opaque structure for a database environment. // // A DB environment supports multiple databases, all residing in the same // shared-memory map. export type env = opaque; // Opaque structure for a transaction handle. // // All database operations require a transaction handle. Transactions may be // read-only or read-write. export type txn = opaque; // Generic structure used for passing keys and data in and out // of the database. // // Values returned from the database are valid only until a subsequent // update operation, or the end of the transaction. Do not modify or // free them, they commonly point into the database itself. // // Key sizes must be between 1 and #mdb_env_get_maxkeysize() inclusive. // The same applies to data sizes in databases with the [[DUPSORT]] flag. // Other data items can in theory be from 0 to 0xffffffff bytes long. export type val = struct { mv_size: size, mv_data: nullable *opaque, }; // Unix permissions for creating files, or dummy definition for Windows export type mode_t = u32;