Initial commit

without.crypto 0.1.0
Dan Burkert 10 years ago
commit 14de57067a
  1. 2
      .gitignore
  2. 15
      Cargo.toml
  3. 202
      LICENSE
  4. 13
      README.md
  5. 216
      src/environment.rs
  6. 101
      src/error.rs
  7. 180
      src/ffi.rs
  8. 194
      src/flags.rs
  9. 41
      src/lib.rs
  10. 256
      src/transaction.rs

2
.gitignore vendored

@ -0,0 +1,2 @@
/target
/Cargo.lock

@ -0,0 +1,15 @@
[package]
name = "lmdb"
version = "0.1.0"
authors = ["Dan Burkert <dan@danburkert.com>"]
license = "Apache-2.0"
description = "Safe Rust bindings for LMDB"
repository = "https://github.com/danburkert/lmdb-rs.git"
readme = "README.md"
keywords = ["LMDB", "database", "storage-engine", "key-value-store", "bindings"]
[lib]
name = "lmdb"

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014 Dan Burkert
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -0,0 +1,13 @@
# lmdbd-rs
Safe Rust bindings for the [Symas Lightning Memory-Mapped Database(LMDB)](http://symas.com/mdb/).
Provides the minimal amount of abstraction necessary to interact with LMDB safely in Rust. In
general, the API is very similar to the LMDB [C-API](http://symas.com/mdb/doc/).
## TODO
* Cursors.
* Zero-copy put API.
* Nested transactions.
* Database statistics.

@ -0,0 +1,216 @@
use libc::{c_uint, size_t, mode_t};
use std::io::FilePermission;
use std::ptr;
use error::{LmdbError, LmdbResult, lmdb_result};
use ffi;
use ffi::MDB_env;
use flags::EnvironmentFlags;
use transaction::Transaction;
/// An LMDB environment.
///
/// An environment supports multiple databases, all residing in the same shared-memory map.
pub struct Environment {
env: *mut MDB_env,
}
impl Environment {
/// Creates a new builder for specifying options for opening an LMDB environment.
pub fn new() -> EnvironmentBuilder {
EnvironmentBuilder {
flags: EnvironmentFlags::empty(),
max_readers: None,
max_dbs: None,
map_size: None
}
}
/// Returns a raw pointer to the underlying LMDB environment.
///
/// The caller **must** ensure that the pointer is not dereferenced after the lifetime of the
/// environment.
pub fn env(&self) -> *mut MDB_env {
self.env
}
/// Create a transaction for use with the environment.
///
/// `flags` must either be empty, or `MDB_RDONLY` in order to specify a read-only transaction.
pub fn begin_txn<'a>(&'a self, flags: EnvironmentFlags) -> LmdbResult<Transaction<'a>> {
Transaction::new(self, flags)
}
/// Flush data buffers to disk.
///
/// Data is always written to disk when `Transaction::commit()` is called, but the operating
/// system may keep it buffered. LMDB always flushes the OS buffers upon commit as well, unless
/// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`.
pub fn sync(&self, force: bool) -> LmdbResult<()> {
unsafe {
lmdb_result(ffi::mdb_env_sync(self.env(), if force { 1 } else { 0 }))
}
}
}
impl Drop for Environment {
fn drop(&mut self) {
unsafe { ffi::mdb_env_close(self.env) }
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//// Environment Builder
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Options for opening or creating an environment.
#[deriving(Show, PartialEq, Eq)]
pub struct EnvironmentBuilder {
flags: EnvironmentFlags,
max_readers: Option<c_uint>,
max_dbs: Option<c_uint>,
map_size: Option<size_t>,
}
impl EnvironmentBuilder {
/// Open an environment.
pub fn open(&self, path: &Path, mode: FilePermission) -> LmdbResult<Environment> {
let mut env: *mut MDB_env = ptr::null_mut();
unsafe {
lmdb_try!(ffi::mdb_env_create(&mut env));
if let Some(max_readers) = self.max_readers {
lmdb_try_with_cleanup!(ffi::mdb_env_set_maxreaders(env, max_readers),
ffi::mdb_env_close(env))
}
if let Some(max_dbs) = self.max_dbs {
lmdb_try_with_cleanup!(ffi::mdb_env_set_maxdbs(env, max_dbs),
ffi::mdb_env_close(env))
}
if let Some(map_size) = self.map_size {
lmdb_try_with_cleanup!(ffi::mdb_env_set_mapsize(env, map_size),
ffi::mdb_env_close(env))
}
lmdb_try_with_cleanup!(ffi::mdb_env_open(env,
path.to_c_str().as_ptr(),
self.flags.bits(),
mode.bits() as mode_t),
ffi::mdb_env_close(env));
}
Ok(Environment { env: env })
}
pub fn set_flags(&mut self, flags: EnvironmentFlags) -> &mut EnvironmentBuilder {
self.flags = flags;
self
}
/// Sets the maximum number of threads or reader slots for the environment.
///
/// This defines the number of slots in the lock table that is used to track readers in the
/// the environment. The default is 126. Starting a read-only transaction normally ties a lock
/// table slot to the current thread until the environment closes or the thread exits. If
/// `MDB_NOTLS` is in use, `Environment::open_txn` instead ties the slot to the `Transaction`
/// object until it or the `Environment` object is destroyed.
pub fn set_max_readers(&mut self, max_readers: c_uint) -> &mut EnvironmentBuilder {
self.max_readers = Some(max_readers);
self
}
/// Sets 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.
///
/// Currently a moderate number of slots are cheap but a huge number gets
/// expensive: 7-120 words per transaction, and every `Transaction::open_db`
/// does a linear search of the opened slots.
pub fn set_max_dbs(&mut self, max_readers: c_uint) -> &mut EnvironmentBuilder {
self.max_dbs = Some(max_readers);
self
}
/// Sets the size of the memory map to use for the 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. It may be increased at
/// later times.
///
/// 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.
pub fn set_map_size(&mut self, map_size: size_t) -> &mut EnvironmentBuilder {
self.map_size = Some(map_size);
self
}
}
#[cfg(test)]
mod test {
use std::io;
use flags;
use super::*;
#[test]
fn test_open() {
let dir = io::TempDir::new("test").unwrap();
// opening non-existent env with read-only should fail
assert!(Environment::new().set_flags(flags::MDB_RDONLY)
.open(dir.path(), io::USER_RWX)
.is_err());
// opening non-existent env should not fail
assert!(Environment::new().open(dir.path(), io::USER_RWX).is_ok());
// opening env with read-only should not fail
assert!(Environment::new().set_flags(flags::MDB_RDONLY)
.open(dir.path(), io::USER_RWX)
.is_ok());
}
#[test]
fn test_begin_txn() {
let dir = io::TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
{
// Mutable env, mutable txn
assert!(env.begin_txn(flags::EnvironmentFlags::empty()).is_ok());
} {
// Mutable env, read-only txn
assert!(env.begin_txn(flags::MDB_RDONLY).is_ok());
} {
// Read-only env, mutable txn
let env = Environment::new().set_flags(flags::MDB_RDONLY)
.open(dir.path(), io::USER_RWX)
.unwrap();
assert!(env.begin_txn(flags::EnvironmentFlags::empty()).is_err());
} {
// Read-only env, read-only txn
let env = Environment::new().set_flags(flags::MDB_RDONLY)
.open(dir.path(), io::USER_RWX)
.unwrap();
assert!(env.begin_txn(flags::MDB_RDONLY).is_ok());
}
}
#[test]
fn test_sync() {
let dir = io::TempDir::new("test").unwrap();
{
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
assert!(env.sync(true).is_ok());
} {
let env = Environment::new().set_flags(flags::MDB_RDONLY)
.open(dir.path(), io::USER_RWX)
.unwrap();
assert!(env.sync(true).is_ok());
}
}
}

@ -0,0 +1,101 @@
use libc::c_int;
use std::error::Error;
use std::io::IoError;
use std::str;
use ffi;
#[deriving(Show, Eq, PartialEq)]
pub enum LmdbError {
KeyExist,
NotFound,
PageNotFound,
Corrupted,
Panic,
VersionMismatch,
Invalid,
MapFull,
DbsFull,
ReadersFull,
TlsFull,
TxnFull,
CursorFull,
PageFull,
MapResized,
Incompatible,
BadRslot,
BadTxn,
BadValSize,
BadDbi,
Unknown(c_int),
Io(IoError),
}
impl Error for LmdbError {
fn description(&self) -> &str {
let err_code = match *self {
LmdbError::KeyExist => ffi::MDB_KEYEXIST,
LmdbError::NotFound => ffi::MDB_NOTFOUND,
LmdbError::PageNotFound => ffi::MDB_PAGE_NOTFOUND,
LmdbError::Corrupted => ffi::MDB_CORRUPTED,
LmdbError::Panic => ffi::MDB_PANIC,
LmdbError::VersionMismatch => ffi::MDB_VERSION_MISMATCH,
LmdbError::Invalid => ffi::MDB_INVALID,
LmdbError::MapFull => ffi::MDB_MAP_FULL,
LmdbError::DbsFull => ffi::MDB_DBS_FULL,
LmdbError::ReadersFull => ffi::MDB_READERS_FULL,
LmdbError::TlsFull => ffi::MDB_TLS_FULL,
LmdbError::TxnFull => ffi::MDB_TXN_FULL,
LmdbError::CursorFull => ffi::MDB_CURSOR_FULL,
LmdbError::PageFull => ffi::MDB_PAGE_FULL,
LmdbError::MapResized => ffi::MDB_MAP_RESIZED,
LmdbError::Incompatible => ffi::MDB_INCOMPATIBLE,
LmdbError::BadRslot => ffi::MDB_BAD_RSLOT,
LmdbError::BadTxn => ffi::MDB_BAD_TXN,
LmdbError::BadValSize => ffi::MDB_BAD_VALSIZE,
LmdbError::BadDbi => ffi::MDB_BAD_DBI,
LmdbError::Unknown(i) => i,
LmdbError::Io(ref io_error) => return io_error.description(),
};
unsafe { str::raw::c_str_to_static_slice(ffi::mdb_strerror(err_code) as *const _) }
}
}
impl LmdbError {
pub fn from_err_code(err_code: c_int) -> LmdbError {
match err_code {
i if i > 0 => LmdbError::Io(IoError::from_errno(err_code as uint, true)),
ffi::MDB_KEYEXIST => LmdbError::KeyExist,
ffi::MDB_NOTFOUND => LmdbError::NotFound,
ffi::MDB_PAGE_NOTFOUND => LmdbError::PageNotFound,
ffi::MDB_CORRUPTED => LmdbError::Corrupted,
ffi::MDB_PANIC => LmdbError::Panic,
ffi::MDB_VERSION_MISMATCH => LmdbError::VersionMismatch,
ffi::MDB_INVALID => LmdbError::Invalid,
ffi::MDB_MAP_FULL => LmdbError::MapFull,
ffi::MDB_DBS_FULL => LmdbError::DbsFull,
ffi::MDB_READERS_FULL => LmdbError::ReadersFull,
ffi::MDB_TLS_FULL => LmdbError::TlsFull,
ffi::MDB_TXN_FULL => LmdbError::TxnFull,
ffi::MDB_CURSOR_FULL => LmdbError::CursorFull,
ffi::MDB_PAGE_FULL => LmdbError::PageFull,
ffi::MDB_MAP_RESIZED => LmdbError::MapResized,
ffi::MDB_INCOMPATIBLE => LmdbError::Incompatible,
ffi::MDB_BAD_RSLOT => LmdbError::BadRslot,
ffi::MDB_BAD_TXN => LmdbError::BadTxn,
ffi::MDB_BAD_VALSIZE => LmdbError::BadValSize,
i => LmdbError::Unknown(i),
}
}
}
pub type LmdbResult<T> = Result<T, LmdbError>;
pub fn lmdb_result(err_code: c_int) -> LmdbResult<()> {
if err_code == ffi::MDB_SUCCESS {
Ok(())
} else {
Err(LmdbError::from_err_code(err_code))
}
}

@ -0,0 +1,180 @@
#![allow(non_camel_case_types, dead_code)]
extern crate libc;
use libc::{c_int, c_uint, c_void, c_char, size_t};
pub type mdb_mode_t = libc::mode_t;
pub type mdb_filehandle_t = libc::c_int;
pub type MDB_dbi = c_uint;
pub type MDB_rel_func = extern fn(*mut MDB_val, *mut c_void, *mut c_void, *mut c_void);
pub type MDB_msg_func = extern fn(*const c_char, *mut c_void) -> c_int;
pub type MDB_cmp_func = extern fn(*const MDB_val, *const MDB_val) -> c_int;
#[repr(C)]
pub struct MDB_val {
pub mv_size: size_t,
pub mv_data: *const c_void,
}
#[repr(C)]
pub struct MDB_env;
#[repr(C)]
pub struct MDB_txn;
#[repr(C)]
pub struct MDB_cursor;
#[repr(C)]
pub struct MDB_stat {
ms_psize: c_uint,
ms_depth: c_uint,
ms_branch_pages: size_t,
ms_leaf_pages: size_t,
ms_overflow_pages: size_t,
ms_entries: size_t
}
#[repr(C)]
pub struct MDB_envinfo {
me_mapaddr: *const c_void,
me_mapsize: size_t,
me_last_pgno: size_t,
me_last_txnid: size_t,
me_maxreaders: c_uint,
me_numreaders: c_uint
}
#[repr(C)]
pub enum MDB_cursor_op {
MDB_FIRST,
MDB_FIRST_DUP,
MDB_GET_BOTH,
MDB_GET_BOTH_RANGE,
MDB_GET_CURRENT,
MDB_GET_MULTIPLE,
MDB_LAST,
MDB_LAST_DUP,
MDB_NEXT,
MDB_NEXT_DUP,
MDB_NEXT_MULTIPLE,
MDB_NEXT_NODUP,
MDB_PREV,
MDB_PREV_DUP,
MDB_PREV_NODUP,
MDB_SET,
MDB_SET_KEY,
MDB_SET_RANGE
}
// Return codes
pub const MDB_SUCCESS: c_int = 0;
pub const MDB_KEYEXIST: c_int = -30799;
pub const MDB_NOTFOUND: c_int = -30798;
pub const MDB_PAGE_NOTFOUND: c_int = -30797;
pub const MDB_CORRUPTED: c_int = -30796;
pub const MDB_PANIC: c_int = -30795;
pub const MDB_VERSION_MISMATCH: c_int = -30794;
pub const MDB_INVALID: c_int = -30793;
pub const MDB_MAP_FULL: c_int = -30792;
pub const MDB_DBS_FULL: c_int = -30791;
pub const MDB_READERS_FULL: c_int = -30790;
pub const MDB_TLS_FULL: c_int = -30789;
pub const MDB_TXN_FULL: c_int = -30788;
pub const MDB_CURSOR_FULL: c_int = -30787;
pub const MDB_PAGE_FULL: c_int = -30786;
pub const MDB_MAP_RESIZED: c_int = -30785;
pub const MDB_INCOMPATIBLE: c_int = -30784;
pub const MDB_BAD_RSLOT: c_int = -30783;
pub const MDB_BAD_TXN: c_int = -30782;
pub const MDB_BAD_VALSIZE: c_int = -30781;
pub const MDB_BAD_DBI: c_int = -30780;
// Write flags
pub const MDB_NOOVERWRITE: c_uint = 0x10;
pub const MDB_NODUPDATA: c_uint = 0x20;
pub const MDB_CURRENT: c_uint = 0x40;
pub const MDB_RESERVE: c_uint = 0x10000;
pub const MDB_APPEND: c_uint = 0x20000;
pub const MDB_APPENDDUP: c_uint = 0x40000;
pub const MDB_MULTIPLE: c_uint = 0x80000;
// Database flags
pub const MDB_REVERSEKEY: c_uint = 0x02;
pub const MDB_DUPSORT: c_uint = 0x04;
pub const MDB_INTEGERKEY: c_uint = 0x08;
pub const MDB_DUPFIXED: c_uint = 0x10;
pub const MDB_INTEGERDUP: c_uint = 0x20;
pub const MDB_REVERSEDUP: c_uint = 0x40;
pub const MDB_CREATE: c_uint = 0x40000;
// Environment flags
pub const MDB_FIXEDMAP: c_uint = 0x01;
pub const MDB_NOSUBDIR: c_uint = 0x4000;
pub const MDB_NOSYNC: c_uint = 0x10000;
pub const MDB_RDONLY: c_uint = 0x20000;
pub const MDB_NOMETASYNC: c_uint = 0x40000;
pub const MDB_WRITEMAP: c_uint = 0x80000;
pub const MDB_MAPASYNC: c_uint = 0x100000;
pub const MDB_NOTLS: c_uint = 0x200000;
pub const MDB_NOLOCK: c_uint = 0x400000;
pub const MDB_NORDAHEAD: c_uint = 0x800000;
pub const MDB_NOMEMINIT: c_uint = 0x1000000;
#[link(name = "lmdb")]
extern {
pub fn mdb_version(major: *mut c_int, minor: *mut c_int, patch: *mut c_int) -> *mut c_char;
pub fn mdb_strerror(err: c_int) -> *mut c_char;
pub fn mdb_env_create(env: *mut *mut MDB_env) -> c_int;
pub fn mdb_env_open(env: *mut MDB_env, path: *const c_char, flags: c_uint, mode: mdb_mode_t) -> c_int;
pub fn mdb_env_copy(env: *mut MDB_env, path: *const c_char) -> c_int;
pub fn mdb_env_copyfd(env: *mut MDB_env, fd: mdb_filehandle_t) -> c_int;
pub fn mdb_env_copy2(env: *mut MDB_env, path: *const c_char, flags: c_uint) -> c_int;
pub fn mdb_env_copyfd2(env: *mut MDB_env, fd: mdb_filehandle_t, flags: c_uint) -> c_int;
pub fn mdb_env_stat(env: *mut MDB_env, stat: *mut MDB_stat) -> c_int;
pub fn mdb_env_info(env: *mut MDB_env, stat: *mut MDB_envinfo) -> c_int;
pub fn mdb_env_sync(env: *mut MDB_env, force: c_int) -> c_int;
pub fn mdb_env_close(env: *mut MDB_env);
pub fn mdb_env_set_flags(env: *mut MDB_env, flags: c_uint, onoff: c_int) -> c_int;
pub fn mdb_env_get_flags(env: *mut MDB_env, flags: *mut c_uint) -> c_int;
pub fn mdb_env_get_path(env: *mut MDB_env, path: *const *const c_char) -> c_int;
pub fn mdb_env_get_fd(env: *mut MDB_env, fd: *mut mdb_filehandle_t) -> c_int;
pub fn mdb_env_set_mapsize(env: *mut MDB_env, size: size_t) -> c_int;
pub fn mdb_env_set_maxreaders(env: *mut MDB_env, readers: c_uint) -> c_int;
pub fn mdb_env_get_maxreaders(env: *mut MDB_env, readers: *mut c_uint) -> c_int;
pub fn mdb_env_set_maxdbs(env: *mut MDB_env, dbs: MDB_dbi) -> c_int;
pub fn mdb_env_get_maxkeysize(env: *mut MDB_env) -> c_int;
pub fn mdb_txn_begin(env: *mut MDB_env, parent: *mut MDB_txn, flags: c_uint, txn: *mut *mut MDB_txn) -> c_int;
pub fn mdb_txn_env(txn: *mut MDB_txn) -> *mut MDB_env;
pub fn mdb_txn_commit(txn: *mut MDB_txn) -> c_int;
pub fn mdb_txn_abort(txn: *mut MDB_txn);
pub fn mdb_txn_reset(txn: *mut MDB_txn);
pub fn mdb_txn_renew(txn: *mut MDB_txn) -> c_int;
pub fn mdb_dbi_open(txn: *mut MDB_txn, name: *const c_char, flags: c_uint, dbi: *mut MDB_dbi) -> c_int;
pub fn mdb_stat(txn: *mut MDB_txn, dbi: MDB_dbi, stat: *mut MDB_stat) -> c_int;
pub fn mdb_dbi_flags(txn: *mut MDB_txn, dbi: MDB_dbi, flags: *mut c_uint) -> c_int;
pub fn mdb_dbi_close(txn: *mut MDB_txn, dbi: MDB_dbi);
pub fn mdb_drop(txn: *mut MDB_txn, dbi: MDB_dbi, del: c_int) -> c_int;
pub fn mdb_set_compare(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: *mut MDB_cmp_func) -> c_int;
pub fn mdb_set_dupsort(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: *mut MDB_cmp_func) -> c_int;
pub fn mdb_set_relfunc(txn: *mut MDB_txn, dbi: MDB_dbi, rel: *mut MDB_rel_func) -> c_int;
pub fn mdb_set_relctx(txn: *mut MDB_txn, dbi: MDB_dbi, ctx: *mut c_void) -> c_int;
pub fn mdb_get(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
pub fn mdb_put(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
pub fn mdb_del(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
pub fn mdb_cursor_open(txn: *mut MDB_txn, dbi: MDB_dbi, cursor: *mut *mut MDB_cursor) -> c_int;
pub fn mdb_cursor_close(cursor: *mut MDB_cursor);
pub fn mdb_cursor_renew(txn: *mut MDB_txn, cursor: *mut MDB_cursor) -> c_int;
pub fn mdb_cursor_txn(cursor: *mut MDB_cursor) -> *mut MDB_txn;
pub fn mdb_cursor_dbi(cursor: *mut MDB_cursor) -> MDB_dbi;
pub fn mdb_cursor_get(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, op: MDB_cursor_op) -> c_int;
pub fn mdb_cursor_put(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
pub fn mdb_cursor_del(cursor: *mut MDB_cursor, flags: c_uint) -> c_int;
pub fn mdb_cursor_count(cursor: *mut MDB_cursor, countp: *mut size_t) -> c_int;
pub fn mdb_cmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *const MDB_val, b: *const MDB_val) -> c_int;
pub fn mdb_dcmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *const MDB_val, b: *const MDB_val) -> c_int;
pub fn mdb_reader_list(env: *mut MDB_env, func: *mut MDB_msg_func, ctx: *mut c_void) -> c_int;
pub fn mdb_reader_check(env: *mut MDB_env, dead: *mut c_int) -> c_int;
}

@ -0,0 +1,194 @@
use libc::c_uint;
bitflags! {
#[doc="Environment Options"]
#[deriving(Show)]
flags EnvironmentFlags: c_uint {
#[doc="Use a fixed address for the mmap region. This flag must be specified"]
#[doc="when creating the environment, and is stored persistently in the environment."]
#[doc="If successful, the memory map will always reside at the same virtual address"]
#[doc="and pointers used to reference data items in the database will be constant"]
#[doc="across multiple invocations. This option may not always work, depending on"]
#[doc="how the operating system has allocated memory to shared libraries and other uses."]
#[doc="The feature is highly experimental."]
const MDB_FIXEDMAP = 0x01,
#[doc="By default, LMDB creates its environment in a directory whose"]
#[doc="pathname is given in *path*, and creates its data and lock files"]
#[doc="under that directory. With this option, *path* is used as-is for"]
#[doc="the database main data file. The database lock file is the *path*"]
#[doc="with `-lock` appended."]
const MDB_NOSUBDIR = 0x4000,
#[doc="Use a writeable memory map unless `MDB_RDONLY` is set. This is faster"]
#[doc="and uses fewer mallocs, but loses protection from application bugs"]
#[doc="like wild pointer writes and other bad updates into the database."]
#[doc="Incompatible with nested transactions."]
#[doc="Processes with and without `MDB_WRITEMAP` on the same environment do"]
#[doc="not cooperate well."]
const MDB_WRITEMAP = 0x80000,
#[doc="Open the environment or transaction in read-only mode. No write operations"]
#[doc="will be allowed. When opening an environment, LMDB will still modify the lock"]
#[doc="file - except on read-only filesystems, where LMDB does not use locks."]
const MDB_RDONLY = 0x20000,
#[doc="Flush system buffers to disk only once per transaction, omit the"]
#[doc="metadata flush. Defer that until the system flushes files to disk,"]
#[doc="or next non-`MDB_RDONLY` commit or #mdb_env_sync(). This optimization"]
#[doc="maintains database integrity, but a system crash may undo the last"]
#[doc="committed transaction. I.e. it preserves the ACI (atomicity,"]
#[doc="consistency, isolation) but not D (durability) database property."]
#[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."]
const MDB_NOMETASYNC = 0x40000,
#[doc="Don't flush system buffers to disk when committing a transaction."]
#[doc="This optimization means a system crash can corrupt the database or"]
#[doc="lose the last transactions if buffers are not yet flushed to disk."]
#[doc="The risk is governed by how often the system flushes dirty buffers"]
#[doc="to disk and how often #mdb_env_sync() is called. However, if the"]
#[doc="filesystem preserves write order and the `MDB_WRITEMAP` flag is not"]
#[doc="used, transactions exhibit ACI (atomicity, consistency, isolation)"]
#[doc="properties and only lose D (durability). I.e. database integrity"]
#[doc="is maintained, but a system crash may undo the final transactions."]
#[doc="Note that (`MDB_NOSYNC | MDB_WRITEMAP`) leaves the system with no"]
#[doc="hint for when to write transactions to disk, unless #mdb_env_sync()"]
#[doc="is called. (`MDB_MAPASYNC | MDB_WRITEMAP`) may be preferable."]
#[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."]
const MDB_NOSYNC = 0x10000,
#[doc="When using `MDB_WRITEMAP`, use asynchronous flushes to disk."]
#[doc="As with `MDB_NOSYNC`, a system crash can then corrupt the"]
#[doc="database or lose the last transactions. Calling #mdb_env_sync()"]
#[doc="ensures on-disk database integrity until next commit."]
#[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."]
const MDB_MAPASYNC = 0x100000,
#[doc="Don't use Thread-Local Storage. Tie reader locktable slots to"]
#[doc="`MDB_txn` objects instead of to threads. I.e. #mdb_txn_reset() keeps"]
#[doc="the slot reseved for the #MDB_txn object. A thread may use parallel"]
#[doc="read-only transactions. A read-only transaction may span threads if"]
#[doc="the user synchronizes its use. Applications that multiplex many"]
#[doc="user threads over individual OS threads need this option. Such an"]
#[doc="application must also serialize the write transactions in an OS"]
#[doc="thread, since LMDB's write locking is unaware of the user threads."]
const MDB_NOTLS = 0x200000,
#[doc="Don't do any locking. If concurrent access is anticipated, the"]
#[doc="caller must manage all concurrency itself. For proper operation"]
#[doc="the caller must enforce single-writer semantics, and must ensure"]
#[doc="that no readers are using old transactions while a writer is"]
#[doc="active. The simplest approach is to use an exclusive lock so that"]
#[doc="no readers may be active at all when a writer begins."]
const MDB_NOLOCK = 0x400000,
#[doc="Turn off readahead. Most operating systems perform readahead on"]
#[doc="read requests by default. This option turns it off if the OS"]
#[doc="supports it. Turning it off may help random read performance"]
#[doc="when the DB is larger than RAM and system RAM is full."]
#[doc="The option is not implemented on Windows."]
const MDB_NORDAHEAD = 0x800000,
#[doc="Don't initialize malloc'd memory before writing to unused spaces"]
#[doc="in the data file. By default, memory for pages written to the data"]
#[doc="file is obtained using malloc. While these pages may be reused in"]
#[doc="subsequent transactions, freshly malloc'd pages will be initialized"]
#[doc="to zeroes before use. This avoids persisting leftover data from other"]
#[doc="code (that used the heap and subsequently freed the memory) into the"]
#[doc="data file. Note that many other system libraries may allocate"]
#[doc="and free memory from the heap for arbitrary uses. E.g., stdio may"]
#[doc="use the heap for file I/O buffers. This initialization step has a"]
#[doc="modest performance cost so some applications may want to disable"]
#[doc="it using this flag. This option can be a problem for applications"]
#[doc="which handle sensitive data like passwords, and it makes memory"]
#[doc="checkers like Valgrind noisy. This flag is not needed with `MDB_WRITEMAP`,"]
#[doc="which writes directly to the mmap instead of using malloc for pages. The"]
#[doc="initialization is also skipped if `MDB_RESERVE` is used; the"]
#[doc="caller is expected to overwrite all of the memory that was"]
#[doc="reserved in that case."]
#[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."]
const MDB_NOMEMINIT = 0x1000000,
}
}
bitflags! {
#[doc="Database Options"]
#[deriving(Show)]
flags DatabaseFlags: c_uint {
#[doc="Keys are strings to be compared in reverse order, from the end"]
#[doc="of the strings to the beginning. By default, Keys are treated as strings and"]
#[doc="compared from beginning to end."]
const MDB_REVERSEKEY = 0x02,
#[doc="Duplicate keys may be used in the database. (Or, from another perspective,"]
#[doc="keys may have multiple data items, stored in sorted order.) By default"]
#[doc="keys must be unique and may have only a single data item."]
const MDB_DUPSORT = 0x04,
#[doc="Keys are binary integers in native byte order. Setting this option"]
#[doc="requires all keys to be the same size, typically sizeof(int)"]
#[doc="or sizeof(size_t)."]
const MDB_INTEGERKEY = 0x08,
#[doc="This flag may only be used in combination with `MDB_DUPSORT`. This option"]
#[doc="tells the library that the data items for this database are all the same"]
#[doc="size, which allows further optimizations in storage and retrieval. When"]
#[doc="all data items are the same size, the `MDB_GET_MULTIPLE` and `MDB_NEXT_MULTIPLE`"]
#[doc="cursor operations may be used to retrieve multiple items at once."]
const MDB_DUPFIXED = 0x10,
#[doc="This option specifies that duplicate data items are also integers, and"]
#[doc="should be sorted as such."]
const MDB_INTEGERDUP = 0x20,
#[doc="This option specifies that duplicate data items should be compared as"]
#[doc="strings in reverse order."]
const MDB_REVERSEDUP = 0x40,
#[doc="Create the named database if it doesn't exist. This option is not"]
#[doc="allowed in a read-only transaction or a read-only environment."]
const MDB_CREATE = 0x40000,
}
}
bitflags! {
#[doc="Write Options"]
#[deriving(Show)]
flags WriteFlags: c_uint {
#[doc="Enter the new key/data pair only if it does not"]
#[doc="already appear in the database. This flag may only be specified"]
#[doc="if the database was opened with `MDB_DUPSORT`. The function will"]
#[doc="return `MDB_KEYEXIST` if the key/data pair already appears in the"]
#[doc="database."]
const MDB_NODUPDATA = 0x20,
#[doc="Enter the new key/data pair only if the key"]
#[doc="does not already appear in the database. The function will return"]
#[doc="`KeyExist` if the key already appears in the database, even if"]
#[doc="the database supports duplicates (`MDB_DUPSORT`). The `data`"]
#[doc="parameter will be set to point to the existing item."]
const MDB_NOOVERWRITE = 0x10,
#[doc="Reserve space for data of the given size, but"]
#[doc="don't copy the given data. Instead, return a pointer to the"]
#[doc="reserved space, which the caller can fill in later - before"]
#[doc="the next update operation or the transaction ends. This saves"]
#[doc="an extra memcpy if the data is being generated later."]
#[doc="LMDB does nothing else with this memory, the caller is expected"]
#[doc="to modify all of the space requested."]
const MDB_RESERVE = 0x10000,
#[doc="Append the given key/data pair to the end of the"]
#[doc="database. No key comparisons are performed. This option allows"]
#[doc="fast bulk loading when keys are already known to be in the"]
#[doc="correct order. Loading unsorted keys with this flag will cause"]
#[doc="data corruption."]
const MDB_APPEND = 0x20000,
#[doc="Same as `MDB_APPEND`, but for sorted dup data."]
const MDB_APPENDDUP = 0x40000,
}
}

@ -0,0 +1,41 @@
//! Safe Rust bindings for the [Symas Lightning Memory-Mapped Database(LMDB)]
//! (http://symas.com/mdb/).
//!
//! Provides the minimal amount of abstraction necessary to interact with LMDB safely in Rust. In
//! general, the API is very similar to the LMDB [C-API](http://symas.com/mdb/doc/).
#![feature(phase, globs, macro_rules, unsafe_destructor, if_let)]
#[phase(plugin, link)] extern crate log;
extern crate libc;
extern crate sync;
pub use environment::{Environment, EnvironmentBuilder};
pub use error::{LmdbResult, LmdbError};
pub use transaction::{Database, Transaction};
macro_rules! lmdb_try {
($expr:expr) => ({
match $expr {
ffi::MDB_SUCCESS => (),
err_code => return Err(::std::error::FromError::from_error(LmdbError::from_err_code(err_code))),
}
})
}
macro_rules! lmdb_try_with_cleanup {
($expr:expr, $cleanup:expr) => ({
match $expr {
ffi::MDB_SUCCESS => (),
err_code => {
let _ = $cleanup;
return Err(::std::error::FromError::from_error(LmdbError::from_err_code(err_code)))
},
}
})
}
mod environment;
mod error;
mod transaction;
pub mod ffi;
pub mod flags;

@ -0,0 +1,256 @@
use libc::{c_uint, c_void, size_t};
use std::{mem, ptr, raw};
use std::kinds::marker;
use environment::Environment;
use error::{LmdbResult, lmdb_result};
use ffi;
use ffi::MDB_txn;
use flags::{DatabaseFlags, EnvironmentFlags, WriteFlags};
/// An LMDB transaction.
///
/// All database operations require a transaction.
pub struct Transaction<'a> {
txn: *mut MDB_txn,
_marker: marker::ContravariantLifetime<'a>,
}
#[unsafe_destructor]
impl <'a> Drop for Transaction<'a> {
fn drop(&mut self) {
unsafe { ffi::mdb_txn_abort(self.txn) }
}
}
impl <'a> Transaction<'a> {
/// Creates a new transaction in the given environment.
pub fn new(env: &'a Environment, flags: EnvironmentFlags) -> LmdbResult<Transaction<'a>> {
let mut txn: *mut MDB_txn = ptr::null_mut();
unsafe {
try!(lmdb_result(ffi::mdb_txn_begin(env.env(),
ptr::null_mut(),
flags.bits(),
&mut txn)));
Ok(Transaction {
txn: txn,
_marker: marker::ContravariantLifetime::<'a>,
})
}
}
/// Returns a raw pointer to the underlying LMDB transaction.
///
/// The caller **must** ensure that the pointer is not used after the lifetime of the
/// transaction.
pub fn txn(&self) -> *mut MDB_txn {
self.txn
}
/// Opens a handle to a database.
///
/// If `name` is `None`, then the returned handle will be for the default database.
///
/// If `name` is not `None`, then the returned handle will be for a named database. In this
/// case the envirnment must be configured to allow named databases through
/// `EnvironmentBuilder::set_max_dbs`.
///
/// The database handle will be private to the current transaction until the transaction is
/// successfully committed. If the transaction is aborted the database handle will be closed
/// automatically. After a successful commit the database handle will reside in the shared
/// environment, and may be used by other transactions.
///
/// A transaction that uses this function must finish (either commit or abort) before any other
/// transaction may use the function.
pub fn open_db(&self, name: Option<&str>, flags: DatabaseFlags) -> LmdbResult<Database<'a>> {
let c_name = name.map(|n| n.to_c_str());
let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() };
let mut dbi: ffi::MDB_dbi = 0;
unsafe {
try!(lmdb_result(ffi::mdb_dbi_open(self.txn, name_ptr, flags.bits(), &mut dbi)));
}
Ok(Database { dbi: dbi, _marker: marker::ContravariantLifetime::<'a> })
}
/// Gets the option flags for the given database in the transaction.
pub fn db_flags(&self, db: &Database) -> LmdbResult<DatabaseFlags> {
let mut flags: c_uint = 0;
unsafe {
try!(lmdb_result(ffi::mdb_dbi_flags(self.txn, db.dbi, &mut flags)));
}
Ok(DatabaseFlags::from_bits_truncate(flags))
}
/// Close a database handle. Normally unnecessary.
///
/// This call is not mutex protected. Handles should only be closed by a single thread, and only
/// if no other threads are going to reference the database handle or one of its cursors any
/// further. Do not close a handle if an existing transaction has modified its database. Doing
/// so can cause misbehavior from database corruption to errors like `MDB_BAD_VALSIZE` (since the
/// DB name is gone).
///
/// Closing a database handle is not necessary, but lets `Transaction::open_database` reuse the
/// handle value. Usually it's better to set a bigger `EnvironmentBuilder::set_max_dbs`, unless
/// that value would be large.
pub unsafe fn close_db(&self, db: Database) {
ffi::mdb_dbi_close(self.txn, db.dbi)
}
/// Commits the transaction.
///
/// Any pending operations will be saved.
pub fn commit(self) -> LmdbResult<()> {
unsafe { lmdb_result(ffi::mdb_txn_commit(self.txn())) }
}
/// Aborts the transaction.
///
/// Any pending operations will not be saved.
pub fn abort(self) {
unsafe { ffi::mdb_txn_abort(self.txn()) }
}
/// Gets an item from a database.
///
/// This function retrieves the data associated with the given key in the database. If the
/// database supports duplicate keys (`MDB_DUPSORT`) then the first data item for the key will
/// be returned. Retrieval of other items requires the use of `Transaction::cursor_get`.
pub fn get(&self, database: &Database, key: &[u8]) -> LmdbResult<&'a [u8]> {
let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t,
mv_data: key.as_ptr() as *const c_void };
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: 0,
mv_data: ptr::null() };
unsafe {
try!(lmdb_result(ffi::mdb_get(self.txn(),
database.dbi,
&mut key_val,
&mut data_val)));
let slice: &'a [u8] =
mem::transmute(raw::Slice {
data: data_val.mv_data as *const u8,
len: data_val.mv_size as uint
});
Ok(slice)
}
}
/// Stores an item 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 (`MDB_DUPSORT`).
pub fn put(&self,
database: &Database,
key: &[u8],
data: &[u8],
flags: WriteFlags)
-> LmdbResult<()> {
let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t,
mv_data: key.as_ptr() as *const c_void };
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: data.len() as size_t,
mv_data: data.as_ptr() as *const c_void };
unsafe {
lmdb_result(ffi::mdb_put(self.txn(),
database.dbi,
&mut key_val,
&mut data_val,
flags.bits()))
}
}
/// Deletes an item from a database.
///
/// This function removes key/data pairs from the database. If the database does not support
/// sorted duplicate data items (`MDB_DUPSORT`) the data parameter is ignored. If the database
/// supports sorted duplicates and the data parameter is `None`, all of the duplicate data items
/// for the key will be deleted. Otherwise, if the data parameter is `Some` only the matching
/// data item will be deleted. This function will return `MDB_NOTFOUND` if the specified key/data
/// pair is not in the database.
pub fn del(&self,
database: &Database,
key: &[u8],
data: Option<&[u8]>)
-> LmdbResult<()> {
let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t,
mv_data: key.as_ptr() as *const c_void };
let data_val: Option<ffi::MDB_val> =
data.map(|data| ffi::MDB_val { mv_size: data.len() as size_t,
mv_data: data.as_ptr() as *const c_void });
unsafe {
lmdb_result(ffi::mdb_del(self.txn(),
database.dbi,
&mut key_val,
data_val.map(|mut data_val| &mut data_val as *mut _)
.unwrap_or(ptr::null_mut())))
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//// Database
////////////////////////////////////////////////////////////////////////////////////////////////////
/// A handle to an individual database in an environment.
///
/// A database handle denotes the name and parameters of a database. The database may not
/// exist in the environment.
pub struct Database<'a> {
dbi: ffi::MDB_dbi,
_marker: marker::ContravariantLifetime<'a>,
}
#[cfg(test)]
mod test {
use std::io;
use environment::*;
use flags::*;
#[test]
fn test_open_db() {
let dir = io::TempDir::new("test").unwrap();
let env = Environment::new().set_max_dbs(10)
.open(dir.path(), io::USER_RWX)
.unwrap();
{
let txn = env.begin_txn(EnvironmentFlags::empty()).unwrap();
assert!(txn.open_db(None, DatabaseFlags::empty()).is_ok());
assert!(txn.commit().is_ok());
} {
let txn = env.begin_txn(EnvironmentFlags::empty()).unwrap();
assert!(txn.open_db(Some("testdb"), DatabaseFlags::empty()).is_err())
} {
let txn = env.begin_txn(EnvironmentFlags::empty()).unwrap();
txn.open_db(Some("testdb"), MDB_CREATE).unwrap();
assert!(txn.commit().is_ok());
} {
let txn = env.begin_txn(EnvironmentFlags::empty()).unwrap();
assert!(txn.open_db(Some("testdb"), DatabaseFlags::empty()).is_ok())
}
}
#[test]
fn test_put_get_del() {
let dir = io::TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let txn = env.begin_txn(EnvironmentFlags::empty()).unwrap();
let db = txn.open_db(None, DatabaseFlags::empty()).unwrap();
txn.put(&db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(&db, b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(&db, b"key3", b"val3", WriteFlags::empty()).unwrap();
txn.commit().unwrap();
let txn = env.begin_txn(EnvironmentFlags::empty()).unwrap();
assert_eq!(b"val1", txn.get(&db, b"key1").unwrap());
assert_eq!(b"val2", txn.get(&db, b"key2").unwrap());
assert_eq!(b"val3", txn.get(&db, b"key3").unwrap());
assert!(txn.get(&db, b"key").is_err());
txn.del(&db, b"key1", None).unwrap();
assert!(txn.get(&db, b"key1").is_err());
}
}
Loading…
Cancel
Save