├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── docker ├── Dockerfile-lua5.1 ├── Dockerfile-lua5.2 ├── Dockerfile-lua5.3 ├── build_container.sh └── run_tests_in_container.sh ├── lightningmdb-0.9.22.1-1.rockspec ├── lightningmdb-scm-1.rockspec ├── lightningmdb.c ├── lpack.c ├── mdb_test.lua ├── test.lua └── test_common.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.o 3 | *.src.rock 4 | temp/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Shmulik Regev 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. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # makefile for lightning library for Lua 2 | # based on lpack's 3 | 4 | # change these to reflect your Lua installation 5 | BASE_DIR ?= /usr/local 6 | LUAINC ?= $(BASE_DIR)/include 7 | LUALIB ?= $(BASE_DIR)/lib 8 | LUABIN ?= $(BASE_DIR)/bin/lua 9 | 10 | LMDB_INCDIR= /usr/local/include 11 | LMDB_LIBDIR= /usr/local/lib 12 | 13 | # probably no need to change anything below here 14 | platform=$(shell uname) 15 | 16 | ifeq ($(platform),Linux) 17 | PLATFORM_CFLAGS= -fPIC 18 | else 19 | PLATFORM_CFLAGS= 20 | endif 21 | 22 | ifeq ($(platform),Darwin) 23 | PLATFORM_LDFLAGS= -undefined dynamic_lookup 24 | else 25 | PLATFORM_LDFLAGS= 26 | endif 27 | 28 | WARN= -pedantic -Wall 29 | CFLAGS= $(INCS) $(WARN) $G -g -O2 $(PLATFORM_CFLAGS) -DUSE_GLOBALS 30 | LDFLAGS= -L$(LUALIB) -L$(LMDB_LIBDIR) -llmdb $(PLATFORM_LDFLAGS) 31 | INCS= -I$(LUAINC) -I$(LMDB_INCDIR) 32 | 33 | MYNAME= lightningmdb 34 | MYLIB= $(MYNAME) 35 | T= $(MYNAME).so 36 | OBJS= $(MYLIB).o 37 | TEST= test.lua 38 | 39 | all: test 40 | 41 | test: $T 42 | $(LUABIN) $(TEST) 43 | 44 | o: $(MYLIB).o 45 | 46 | so: $T 47 | 48 | $T: $(OBJS) 49 | $(CC) -o $@ -shared $(OBJS) $(CFLAGS) $(LDFLAGS) 50 | 51 | clean: 52 | rm -f $(OBJS) $T core core.* a.out 53 | 54 | doc: 55 | @echo "$(MYNAME) library:" 56 | @fgrep '/**' $(MYLIB).c | cut -f2 -d/ | tr -d '*' | sort | column 57 | 58 | # distribution 59 | 60 | D= $(MYNAME) 61 | A= $(MYLIB).tar.gz 62 | TOTAR= Makefile,README.md,$(MYLIB).c,test*.lua 63 | 64 | tar: clean 65 | tar zcvf $A -C .. $D/{$(TOTAR)} 66 | 67 | distr: tar 68 | touch -r $A .stamp 69 | 70 | diff: clean 71 | tar zxf $(FTP)/$A 72 | diff $D . 73 | 74 | # eof 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # General 2 | 3 | Lightningmdb is a thin wrapper around [OpenLDAP Lightning Memory-Mapped Database (LMDB)](http://symas.com/) . 4 | 5 | 6 | # Installation 7 | 8 | ## Prerequisites 9 | 10 | * Lua 5.1.x or greater. It should be compatible with Luajit though it wasn't thoroughly tested. 11 | * [OpenLDAP Lightning Memory-Mapped Database (LMDB)](http://symas.com/). A clone of just the LMDB code is also available on [Github](https://github.com/LMDB/lmdb) 12 | 13 | ## Building 14 | ### LMDB 15 | * Get LMDB by `git clone https://github.com/LMDB/lmdb` 16 | * Building LMDB is a simple 17 | ``` 18 | cd lmdb/libraries/liblmdb/ 19 | make 20 | make install 21 | ``` 22 | ### Lightningmdb 23 | #### Luarocks 24 | `luarocks` can be used to build and install the files from source. 25 | 26 | #### Manually 27 | * edit the Lightningmdb `Makefile` and set the Lua and Lightningmdb paths. 28 | * run `make` to generate the library. 29 | 30 | #### Docker 31 | A set of docker files are provided also, primarily for building the library against multiple Lua versions. Run `./docker/build_container.sh lua5.1` to test that the library successfully builds with Lua 5.1 (versions 5.2 and 5.3 are supported as well). 32 | 33 | # Usage 34 | Every attempt was made to honor the original naming convention. The documentation is therefore scarce and the [database's documentation](http://www.lmdb.tech/doc/) should be used. 35 | 36 | 5 lua _objects_ are wrapping the access to the DB. Their mappings to the LMDB functions/constants is provided below. 37 | 38 | The (Lua) tests files provide usage reference. Some of them are direct translation of LMDB's test files. 39 | 40 | ## lightningmdb 41 | All the LMDB enums and defines are available through this table as well as the following functions: 42 | 43 | * `version` - `lmdb_version` 44 | * `strerror` - `mdb_strerror` 45 | * `env_create` - `mdb_env_create` 46 | 47 | ## env 48 | * `open` - `mdb_env_open` 49 | * `copy` - `mmddbb__env_copy` 50 | * `stat` - `mdb_env_stat` 51 | * `info` - `mdb_env_info` 52 | * `sync` - `mdb_env_sync` 53 | * `close` - `mdb_env_close` 54 | * `set_flags` - `mdb_env_set_flags` 55 | * `get_flags` - `mdb_env_get_flags` 56 | * `get_path` - `mdb_env_get_path` 57 | * `set_mapsize` - `mdb_env_set_mapsize` 58 | * `set_maxreaders` - `mdb_env_set_maxreaders` 59 | * `get_maxreaders` - `mdb_env_get_maxreaders` 60 | * `set_maxdbs` - `mdb_env_set_maxdbs` 61 | * `txn_begin` - `mdb_env_txn_begin` 62 | * `dbi_close` - `mdb_env_dbi_close` 63 | 64 | ## txn 65 | * `commit` - `mdb_txn_commit` 66 | * `abort` - `mdb_txn_abort` 67 | * `reset` - `mdb_txn_reset` 68 | * `renew` - `mdb_txn_renew` 69 | * `dbi_open` - `mdb_txn_dbi_open` 70 | * `stat` - `mdb_txn_stat` 71 | * `dbi_drop` - `mdb_txn_dbi_drop` 72 | * `get` - `mdb_txn_get` 73 | * `put` - `mdb_txn_put` 74 | * `del` - `mdb_txn_del` 75 | * `cmp` - `mdb_txn_cmp` 76 | * `dcmp` - `mdb_txn_dcmp` 77 | * `cursor_open` - `mdb_txn_cursor_open` 78 | * `cursor_renew` - `mdb_txn_cursor_renew` 79 | 80 | ## cursor 81 | * `close` - `mdb_cursor_close` 82 | * `txn` - `mdb_cursor_txn` 83 | * `dbi` - `mdb_cursor_dbi` 84 | * `get` - `mdb_cursor_get` 85 | * `get_key` - `mdb_cursor_get` but the data is not returned (this isn't a part of the original API). 86 | * `put` - `mdb_cursor_put` 87 | * `del` - `mdb_cursor_del` 88 | * `count` - `mdb_cursor_count` 89 | 90 | 91 | ## lpack 92 | As a utility, [LHF's lpack](http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/index.html#lpack) is included in the library for Lua versions lower than 5.3. 93 | 94 | ### Usage 95 | The following is copied verbatim from lpack's original documentation. 96 | 97 | The library adds two functions to the string library: pack and unpack. 98 | 99 | pack is called as follows: pack(F,x1,x2,...), where F is a string describing 100 | how the values x1, x2, ... are to be interpreted and formatted. Each letter 101 | in the format string F consumes one of the given values. Only values of type 102 | number or string are accepted. pack returns a (binary) string containing the 103 | values packed as described in F. The letter codes understood by pack are listed 104 | in lpack.c (they are inspired by Perl's codes but are not the same). Numbers 105 | following letter codes in F indicate repetitions. 106 | 107 | unpack is called as follows: unpack(s,F,[init]), where s is a (binary) string 108 | containing data packed as if by pack, F is a format string describing what is 109 | to be read from s, and the optional init marks where in s to begin reading the 110 | values. unpack returns one value per letter in F until F or s is exhausted 111 | (the letters codes are the same as for pack, except that numbers following `A' 112 | are interpreted as the number of characters to read into the string, not as 113 | repetitions). 114 | 115 | The first value returned by unpack is the next unread position in s, which can 116 | be used as the init position in a subsequent call to unpack. This allows you to 117 | unpack values in a loop or in several steps. If the position returned by unpack 118 | is beyond the end of s, then s has been exhausted; any calls to unpack starting 119 | beyond the end of s will always return nil values. 120 | 121 | 122 | 123 | # License 124 | 125 | * Lightningmdb is distributed under the MIT license. 126 | * [OpenLDAP Lightning Memory-Mapped Database (LMDB)](https://www.symas.com/symas-open-source-licenses) is distributed under the OpenLDAP Public license. 127 | * lpack's code was placed under the public domain by its author. 128 | 129 | ## MIT License 130 | Copyright (c) 2012,2015 Trusteer Ltd. 131 | 132 | Author: Shmulik Regev 133 | 134 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, sub}}` 135 | 136 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 137 | 138 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 139 | 140 | ## The OpenLDAP Public License 141 | Version 2.8, 17 August 2003 142 | 143 | Redistribution and use of this software and associated documentation 144 | ("Software"), with or without modification, are permitted provided 145 | that the following conditions are met: 146 | 147 | 1. Redistributions in source form must retain copyright statements 148 | and notices, 149 | 150 | 2. Redistributions in binary form must reproduce applicable copyright 151 | statements and notices, this list of conditions, and the following 152 | disclaimer in the documentation and/or other materials provided 153 | with the distribution, and 154 | 155 | 3. Redistributions must contain a verbatim copy of this document. 156 | 157 | The OpenLDAP Foundation may revise this license from time to time. 158 | Each revision is distinguished by a version number. You may use 159 | this Software under terms of this license revision or under the 160 | terms of any subsequent revision of the license. 161 | 162 | THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS 163 | CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, 164 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 165 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 166 | SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) 167 | OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, 168 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 169 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 170 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 171 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 172 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 173 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 174 | POSSIBILITY OF SUCH DAMAGE. 175 | 176 | The names of the authors and copyright holders must not be used in 177 | advertising or otherwise to promote the sale, use or other dealing 178 | in this Software without specific, written prior permission. Title 179 | to copyright in this Software shall at all times remain with copyright 180 | holders. 181 | 182 | OpenLDAP is a registered trademark of the OpenLDAP Foundation. 183 | 184 | Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, 185 | California, USA. All Rights Reserved. Permission to copy and 186 | distribute verbatim copies of this document is granted. 187 | -------------------------------------------------------------------------------- /docker/Dockerfile-lua5.1: -------------------------------------------------------------------------------- 1 | FROM phusion/baseimage:0.9.19 2 | 3 | ENV lua_ver 5.1 4 | ENV luarocks_ver 2.3.0 5 | ENV lmdb_ver 0.9.19 6 | 7 | 8 | RUN apt-get update -y && apt-get install -y unzip curl make git 9 | 10 | RUN apt-get install -y lua${lua_ver} liblua${lua_ver} liblua${lua_ver}-dev 11 | 12 | # Install luarocks 13 | RUN cd /tmp && \ 14 | curl -L -O http://luarocks.org/releases/luarocks-${luarocks_ver}.tar.gz && \ 15 | tar zxpf luarocks-${luarocks_ver}.tar.gz && \ 16 | rm luarocks-${luarocks_ver}.tar.gz && \ 17 | cd luarocks-${luarocks_ver} && \ 18 | ./configure --prefix=/usr && \ 19 | make bootstrap && \ 20 | cd /tmp && \ 21 | rm -r /tmp/luarocks-${luarocks_ver} 22 | 23 | RUN cd /tmp && \ 24 | curl -L -O https://github.com/LMDB/lmdb/archive/LMDB_${lmdb_ver}.tar.gz && \ 25 | tar -xzf LMDB_${lmdb_ver}.tar.gz && \ 26 | rm LMDB_${lmdb_ver}.tar.gz && \ 27 | cd lmdb-LMDB_${lmdb_ver}/libraries/liblmdb && \ 28 | make all install && \ 29 | ldconfig && \ 30 | cd /tmp && \ 31 | rm -r /tmp/lmdb-LMDB_${lmdb_ver} 32 | 33 | ENV HOME /root 34 | RUN mkdir -p $HOME/lightningmdb/temp 35 | WORKDIR $HOME/lightningmdb 36 | 37 | COPY *.lua *.c Makefile *.rockspec $HOME/lightningmdb/ 38 | RUN make BASE_DIR=/usr LUAINC=/usr/include/lua${lua_ver} 39 | -------------------------------------------------------------------------------- /docker/Dockerfile-lua5.2: -------------------------------------------------------------------------------- 1 | FROM phusion/baseimage:0.9.19 2 | ENV lua_ver 5.2 3 | ENV luarocks_ver 2.3.0 4 | ENV lmdb_ver 0.9.19 5 | 6 | 7 | RUN apt-get update -y && apt-get install -y unzip curl make git 8 | 9 | RUN apt-get install -y lua${lua_ver} liblua${lua_ver} liblua${lua_ver}-dev 10 | 11 | # Install luarocks 12 | RUN cd /tmp && \ 13 | curl -L -O http://luarocks.org/releases/luarocks-${luarocks_ver}.tar.gz && \ 14 | tar zxpf luarocks-${luarocks_ver}.tar.gz && \ 15 | rm luarocks-${luarocks_ver}.tar.gz && \ 16 | cd luarocks-${luarocks_ver} && \ 17 | ./configure --prefix=/usr && \ 18 | make bootstrap && \ 19 | cd /tmp && \ 20 | rm -r /tmp/luarocks-${luarocks_ver} 21 | 22 | RUN cd /tmp && \ 23 | curl -L -O https://github.com/LMDB/lmdb/archive/LMDB_${lmdb_ver}.tar.gz && \ 24 | tar -xzf LMDB_${lmdb_ver}.tar.gz && \ 25 | rm LMDB_${lmdb_ver}.tar.gz && \ 26 | cd lmdb-LMDB_${lmdb_ver}/libraries/liblmdb && \ 27 | make all install && \ 28 | ldconfig && \ 29 | cd /tmp && \ 30 | rm -r /tmp/lmdb-LMDB_${lmdb_ver} 31 | 32 | 33 | ENV HOME /root 34 | RUN mkdir -p $HOME/lightningmdb/temp 35 | WORKDIR $HOME/lightningmdb 36 | 37 | COPY *.lua *.c Makefile *.rockspec $HOME/lightningmdb/ 38 | RUN make BASE_DIR=/usr LUAINC=/usr/include/lua${lua_ver} 39 | -------------------------------------------------------------------------------- /docker/Dockerfile-lua5.3: -------------------------------------------------------------------------------- 1 | FROM phusion/baseimage:0.9.19 2 | ENV lua_ver 5.3 3 | ENV luarocks_ver 2.3.0 4 | ENV lmdb_ver 0.9.19 5 | 6 | 7 | RUN apt-get update -y && apt-get install -y unzip curl make git 8 | 9 | RUN apt-get install -y lua${lua_ver} liblua${lua_ver} liblua${lua_ver}-dev 10 | 11 | # Install luarocks 12 | RUN cd /tmp && \ 13 | curl -L -O http://luarocks.org/releases/luarocks-${luarocks_ver}.tar.gz && \ 14 | tar zxpf luarocks-${luarocks_ver}.tar.gz && \ 15 | rm luarocks-${luarocks_ver}.tar.gz && \ 16 | cd luarocks-${luarocks_ver} && \ 17 | ./configure --prefix=/usr && \ 18 | make bootstrap && \ 19 | cd /tmp && \ 20 | rm -r /tmp/luarocks-${luarocks_ver} 21 | 22 | RUN cd /tmp && \ 23 | curl -L -O https://github.com/LMDB/lmdb/archive/LMDB_${lmdb_ver}.tar.gz && \ 24 | tar -xzf LMDB_${lmdb_ver}.tar.gz && \ 25 | rm LMDB_${lmdb_ver}.tar.gz && \ 26 | cd lmdb-LMDB_${lmdb_ver}/libraries/liblmdb && \ 27 | make all install && \ 28 | ldconfig && \ 29 | cd /tmp && \ 30 | rm -r /tmp/lmdb-LMDB_${lmdb_ver} 31 | 32 | 33 | ENV HOME /root 34 | RUN mkdir -p $HOME/lightningmdb/temp 35 | WORKDIR $HOME/lightningmdb 36 | 37 | COPY *.lua *.c Makefile *.rockspec $HOME/lightningmdb/ 38 | RUN make BASE_DIR=/usr LUAINC=/usr/include/lua${lua_ver} LUABIN=/usr/bin/lua5.3 39 | -------------------------------------------------------------------------------- /docker/build_container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Usage: docker/build_container.sh lua5.2 5 | # 6 | 7 | platform=$1 8 | if [ -z "$platform" ] ; then 9 | echo "Usage: $0 [docker build params]" 10 | echo "" 11 | echo "Example: $0 lua5.2" 12 | exit 1 13 | fi 14 | 15 | shift 16 | # Docker repo name 17 | echo $platform 18 | repo="lightningmdb.$platform" 19 | 20 | projectrootrelative="$(dirname $0)/.." 21 | projectroot=$(cd $projectrootrelative && pwd) # Get the absolute path 22 | dockerfile="$projectroot/docker/Dockerfile-$platform" 23 | if [ ! -f "$dockerfile" ] ; then 24 | echo "Missing docker file: $dockerfile" 25 | exit 2 26 | fi 27 | 28 | docker build --file=$dockerfile --tag=$repo:$platform $@ $projectroot 29 | -------------------------------------------------------------------------------- /docker/run_tests_in_container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Usage: docker/run_tests_in_container.sh lua5.2 5 | # 6 | 7 | platform=$1 8 | if [ -z "$platform" ] ; then 9 | echo "Usage: $0 [docker build params]" 10 | echo "" 11 | echo "Example: $0 lua5.2" 12 | exit 1 13 | fi 14 | 15 | shift 16 | # Docker repo name 17 | repo=lightningmdb 18 | 19 | projectrootrelative="$(dirname $0)/.." 20 | projectroot=$(cd $projectrootrelative && pwd) # Get the absolute path 21 | dockerfile="$projectroot/docker/Dockerfile-$platform" 22 | if [ ! -f "$dockerfile" ] ; then 23 | echo "Missing docker file: $dockerfile" 24 | exit 2 25 | fi 26 | 27 | containername="lightningmdb_test_$platform" 28 | docker run --name=$containername $repo:$platform 29 | retval=$? 30 | docker rm $containername 31 | 32 | exit $retval 33 | -------------------------------------------------------------------------------- /lightningmdb-0.9.22.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Lightningmdb" 2 | version = "0.9.22.1-1" 3 | source = { 4 | dir = "lightningmdb-"..version, 5 | url = "https://github.com/shmul/lightningmdb/archive/"..version..".zip" 6 | } 7 | description = { 8 | summary = "A thin wrapper around OpenLDAP Lightning Memory-Mapped Database (LMDB).", 9 | detailed = [[ 10 | LMDB is a key-value embedded store that is a part of the OpenLDAP project. This rock provides a Lua interface to to it. 11 | ]], 12 | homepage = "https://github.com/shmul/lightningmdb", 13 | license = "MIT/X11" -- or whatever you like 14 | } 15 | dependencies = { 16 | "lua >= 5.1" 17 | } 18 | external_dependencies = { 19 | LMDB = { 20 | header = "lmdb.h", 21 | library = "lmdb", 22 | } 23 | } 24 | build = { 25 | type = "builtin", 26 | modules = { 27 | lightningmdb = { 28 | sources = {"lightningmdb.c"}, 29 | defines = {"USE_GLOBALS"}, 30 | libraries = {"lmdb"}, 31 | incdirs = {"$(LMDB_INCDIR)"}, 32 | libdirs = {"$(LMDB_LIBDIR)"} 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /lightningmdb-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Lightningmdb" 2 | version = "scm-1" 3 | source = { 4 | url = "git://github.com/shmul/lightningdbm.git" 5 | } 6 | description = { 7 | summary = "A thin wrapper around OpenLDAP Lightning Memory-Mapped Database (LMDB).", 8 | detailed = [[ 9 | LMDB is a key-value embedded store that is a part of the OpenLDAP project. This rock provides a Lua interface to to it. 10 | ]], 11 | homepage = "https://github.com/shmul/lightningmdb", 12 | license = "MIT/X11" -- or whatever you like 13 | } 14 | dependencies = { 15 | "lua >= 5.1" 16 | } 17 | external_dependencies = { 18 | LMDB = { 19 | header = "lmdb.h", 20 | library = "lmdb", 21 | } 22 | } 23 | build = { 24 | type = "builtin", 25 | modules = { 26 | lightningmdb = { 27 | sources = {"lightningmdb.c"}, 28 | defines = {"USE_GLOBALS"}, 29 | libraries = {"lmdb"}, 30 | incdirs = {"$(LMDB_INCDIR)"}, 31 | libdirs = {"$(LMDB_LIBDIR)"} 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lightningmdb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Shmulik Regev 3 | */ 4 | /* -*- c-basic-offset: 2 -*- */ 5 | /* -*- c-default-style: "k&r" -*- */ 6 | 7 | #include 8 | #include 9 | 10 | #include "lmdb.h" 11 | 12 | #include "lua.h" 13 | #include "lualib.h" 14 | #include "lauxlib.h" 15 | 16 | #if LUA_VERSION_NUM<=501 17 | # define lua_type_error luaL_typerror 18 | void lua_set_funcs(lua_State *L, const char *libname,const luaL_Reg *l) { 19 | lua_setglobal(L,libname); 20 | luaL_register(L,libname,l); 21 | } 22 | 23 | #else 24 | void lua_set_funcs(lua_State *L, const char *libname,const luaL_Reg *l) { 25 | luaL_setfuncs(L,l,0); 26 | } 27 | int lua_type_error(lua_State *L,int narg,const char *tname) { 28 | const char *msg = lua_pushfstring(L, "%s expected, got %s", 29 | tname, luaL_typename(L, narg)); 30 | return luaL_argerror(L, narg, msg); 31 | } 32 | #endif 33 | 34 | #include "lpack.c" 35 | 36 | #define LIGHTNING "lightningmdb" 37 | #define ENV "lightningmdb_env" 38 | #define TXN "lightningmdb_txn" 39 | #define CURSOR "lightningmdb_cursor" 40 | 41 | #define setfield_enum(x) lua_pushinteger(L,x); lua_setfield(L,-2,#x) 42 | 43 | #define DEFINE_register_methods(x,X) \ 44 | void x##_register(lua_State* L) { \ 45 | luaL_newmetatable(L, X); \ 46 | lua_pushstring(L, "__index"); \ 47 | lua_pushvalue(L, -2); /* push metatable */ \ 48 | lua_rawset(L, -3); /* metatable.__index = metatable */ \ 49 | lua_set_funcs(L,X,x##_methods); \ 50 | } 51 | 52 | #define set_meta_and_return(x,X) \ 53 | *(MDB_##x **)(lua_newuserdata(L,sizeof(MDB_##x *))) = x; \ 54 | luaL_getmetatable(L,X); \ 55 | lua_setmetatable(L,-2); \ 56 | return 1; 57 | 58 | 59 | static int clean_metatable(lua_State* L) { 60 | lua_pushnil(L); 61 | lua_setmetatable(L,-2); 62 | return 0; 63 | } 64 | 65 | 66 | static int str_error_and_out(lua_State* L,const char* err) { 67 | lua_pushnil(L); 68 | if ( err ) { 69 | lua_pushstring(L,err); 70 | } else { 71 | lua_pushstring(L,"Unknown error"); 72 | } 73 | return 2; 74 | } 75 | 76 | static int error_and_out(lua_State* L,int err) { 77 | str_error_and_out(L,mdb_strerror(err)); 78 | lua_pushinteger(L,err); 79 | return 3; 80 | } 81 | 82 | static int success_or_err(lua_State* L,int err) { 83 | if ( err ) { 84 | return error_and_out(L,err); 85 | } 86 | lua_settop(L,1); 87 | return 1; 88 | } 89 | 90 | static int unimplemented(lua_State* L) { 91 | return str_error_and_out(L,"method unimplemented"); 92 | } 93 | 94 | #define DEFINE_check(x,NAME) \ 95 | static MDB_##x* check_##x(lua_State *L, int index) { \ 96 | MDB_##x* y; \ 97 | luaL_checktype(L, index, LUA_TUSERDATA); \ 98 | y = *(MDB_##x**)luaL_checkudata(L, index, NAME); \ 99 | if (y == NULL) lua_type_error(L,index,NAME); \ 100 | return y; \ 101 | } 102 | 103 | DEFINE_check(env,ENV) 104 | DEFINE_check(txn,TXN) 105 | DEFINE_check(cursor,CURSOR) 106 | 107 | static int stat_to_table(lua_State *L,MDB_stat *stat) { 108 | lua_newtable(L); 109 | lua_pushinteger(L,stat->ms_psize); 110 | lua_setfield(L,-2,"ms_psize"); 111 | lua_pushinteger(L,stat->ms_depth); 112 | lua_setfield(L,-2,"ms_depth"); 113 | lua_pushinteger(L,stat->ms_branch_pages); 114 | lua_setfield(L,-2,"ms_branch_pages"); 115 | lua_pushinteger(L,stat->ms_leaf_pages); 116 | lua_setfield(L,-2,"ms_leaf_pages"); 117 | lua_pushinteger(L,stat->ms_overflow_pages); 118 | lua_setfield(L,-2,"ms_overflow_pages"); 119 | lua_pushinteger(L,stat->ms_entries); 120 | lua_setfield(L,-2,"ms_entries"); 121 | return 1; 122 | } 123 | 124 | 125 | static MDB_val* pop_val(lua_State* L,int index,MDB_val* val) { 126 | if ( lua_isnil(L,index) ) { 127 | return NULL; 128 | } 129 | val->mv_data = (void*)luaL_checklstring(L,index,&val->mv_size); 130 | return val; 131 | } 132 | 133 | /* env */ 134 | static int env_open(lua_State *L) { 135 | MDB_env* env = check_env(L,1); 136 | const char* path = luaL_checkstring(L,2); 137 | mode_t flags = (mode_t)luaL_checkinteger(L,3); 138 | int mode = luaL_checkinteger(L,4); 139 | int err; 140 | 141 | if ( !path ) { 142 | return str_error_and_out(L,"path required"); 143 | } 144 | err = mdb_env_open(env,path,flags,mode); 145 | return success_or_err(L,err); 146 | } 147 | 148 | static int env_copy(lua_State *L) { 149 | MDB_env* env = check_env(L,1); 150 | const char* path = luaL_checkstring(L,2); 151 | int err; 152 | 153 | if ( !path ) { 154 | return str_error_and_out(L,"path required"); 155 | } 156 | err = mdb_env_copy(env,path); 157 | return success_or_err(L,err); 158 | } 159 | 160 | static int env_stat(lua_State *L) { 161 | MDB_env* env = check_env(L,1); 162 | MDB_stat stat; 163 | mdb_env_stat(env,&stat); 164 | return stat_to_table(L,&stat); 165 | } 166 | 167 | static int env_info(lua_State *L) { 168 | MDB_env* env = check_env(L,1); 169 | MDB_envinfo info; 170 | mdb_env_info(env,&info); 171 | lua_newtable(L); 172 | 173 | lua_pushinteger(L,info.me_mapsize); 174 | lua_setfield(L,-2,"me_mapsize"); 175 | lua_pushinteger(L,info.me_last_pgno); 176 | lua_setfield(L,-2,"me_last_pgno"); 177 | lua_pushinteger(L,info.me_last_txnid); 178 | lua_setfield(L,-2,"me_last_txnid"); 179 | lua_pushinteger(L,info.me_maxreaders); 180 | lua_setfield(L,-2,"me_maxreaders"); 181 | lua_pushinteger(L,info.me_numreaders); 182 | lua_setfield(L,-2,"me_numreaders"); 183 | return 1; 184 | 185 | } 186 | 187 | static int env_sync(lua_State *L) { 188 | MDB_env* env = check_env(L,1); 189 | int force = luaL_checkinteger(L,2); 190 | int err = mdb_env_sync(env,force); 191 | return success_or_err(L,err); 192 | } 193 | 194 | static int env_close(lua_State *L) { 195 | MDB_env* env = check_env(L,1); 196 | mdb_env_close(env); 197 | clean_metatable(L); 198 | return 0; 199 | } 200 | 201 | static int env_set_flags(lua_State *L) { 202 | MDB_env* env = check_env(L,1); 203 | unsigned int flags = luaL_checkinteger(L,2); 204 | int onoff = luaL_checkinteger(L,3); 205 | int err = mdb_env_set_flags(env,flags,onoff); 206 | return success_or_err(L,err); 207 | } 208 | 209 | static int env_get_flags(lua_State *L) { 210 | MDB_env* env = check_env(L,1); 211 | unsigned int flags = 0; 212 | int err = mdb_env_get_flags(env,&flags); 213 | if ( err ) { 214 | return error_and_out(L,err); 215 | } 216 | lua_pushinteger(L,flags); 217 | return 1; 218 | } 219 | 220 | static int env_get_path(lua_State *L) { 221 | MDB_env* env = check_env(L,1); 222 | const char* path; 223 | int err = mdb_env_get_path(env,&path); 224 | if ( err ) { 225 | return error_and_out(L,err); 226 | } 227 | lua_pushstring(L,path); 228 | return 1; 229 | } 230 | 231 | static int env_set_mapsize(lua_State *L) { 232 | MDB_env* env = check_env(L,1); 233 | size_t size = luaL_checkinteger(L,2); 234 | int err = mdb_env_set_mapsize(env,size); 235 | return success_or_err(L,err); 236 | } 237 | 238 | static int env_set_maxreaders(lua_State *L) { 239 | /* TODO */ 240 | return unimplemented(L); 241 | } 242 | 243 | static int env_get_maxreaders(lua_State *L) { 244 | /* TODO */ 245 | return unimplemented(L); 246 | } 247 | 248 | static int env_set_maxdbs(lua_State *L) { 249 | MDB_env* env = check_env(L,1); 250 | int num = luaL_checkinteger(L,2); 251 | int err = mdb_env_set_maxdbs(env,num); 252 | return success_or_err(L,err); 253 | } 254 | 255 | static int env_txn_begin(lua_State* L) { 256 | MDB_env* env = check_env(L,1); 257 | MDB_txn* parent = lua_isnil(L,2) ? NULL : check_txn(L,2); 258 | unsigned int flags = luaL_checkinteger(L,3); 259 | int err; 260 | MDB_txn* txn; 261 | if ( !env ) { 262 | return str_error_and_out(L,"bad params"); 263 | } 264 | 265 | err = mdb_txn_begin(env,parent,flags,&txn); 266 | if ( err ) { 267 | return error_and_out(L,err); 268 | } 269 | set_meta_and_return(txn,TXN); 270 | } 271 | 272 | static int env_dbi_close(lua_State* L) { 273 | MDB_env* env = check_env(L,1); 274 | MDB_dbi dbi = luaL_checkinteger(L,2); 275 | 276 | mdb_dbi_close(env,dbi); 277 | return 0; 278 | } 279 | 280 | 281 | static const luaL_Reg env_methods[] = { 282 | #if LUA_VERSION_NUM >= 504 283 | {"__close",env_close}, 284 | #endif 285 | {"__gc",env_close}, 286 | {"open",env_open}, 287 | {"copy",env_copy}, 288 | {"stat",env_stat}, 289 | {"info",env_info}, 290 | {"sync",env_sync}, 291 | {"close",env_close}, 292 | {"set_flags",env_set_flags}, 293 | {"get_flags",env_get_flags}, 294 | {"get_path",env_get_path}, 295 | {"set_mapsize",env_set_mapsize}, 296 | {"set_maxreaders",env_set_maxreaders}, 297 | {"get_maxreaders",env_get_maxreaders}, 298 | {"set_maxdbs",env_set_maxdbs}, 299 | {"txn_begin",env_txn_begin}, 300 | {"dbi_close",env_dbi_close}, 301 | {0,0} 302 | }; 303 | 304 | 305 | DEFINE_register_methods(env,ENV) 306 | 307 | /* cursor */ 308 | static int cursor_close(lua_State *L) { 309 | MDB_cursor* cursor = check_cursor(L,1); 310 | mdb_cursor_close(cursor); 311 | clean_metatable(L); 312 | return 0; 313 | } 314 | 315 | static int cursor_txn(lua_State *L) { 316 | MDB_cursor* cursor = check_cursor(L,1); 317 | MDB_txn* txn = mdb_cursor_txn(cursor); 318 | (void)txn; 319 | /* TODO - we'll need a mapping from MDB_txn* to the lua userdata */ 320 | return unimplemented(L); 321 | } 322 | 323 | static int cursor_dbi(lua_State *L) { 324 | MDB_cursor* cursor = check_cursor(L,1); 325 | MDB_dbi dbi = mdb_cursor_dbi(cursor); 326 | lua_pushinteger(L,dbi); 327 | return 1; 328 | } 329 | 330 | static int cursor_get(lua_State *L) { 331 | int with_value = (lua_gettop(L) > 3); 332 | MDB_cursor* cursor = check_cursor(L,1); 333 | MDB_val k,v; 334 | MDB_cursor_op op = luaL_checkinteger(L,with_value?4:3); 335 | int err; 336 | pop_val(L,2,&k); 337 | if (with_value) pop_val(L,3,&v); 338 | err = mdb_cursor_get(cursor,&k,&v,op); 339 | switch (err) { 340 | case MDB_NOTFOUND: 341 | lua_pushnil(L); 342 | return 1; 343 | case 0: 344 | lua_pushlstring(L,k.mv_data,k.mv_size); 345 | lua_pushlstring(L,v.mv_data,v.mv_size); 346 | return 2; 347 | } 348 | return error_and_out(L,err); 349 | } 350 | 351 | static int cursor_get_key(lua_State *L) { 352 | MDB_cursor* cursor = check_cursor(L,1); 353 | MDB_val k; 354 | MDB_cursor_op op = luaL_checkinteger(L,3); 355 | int err; 356 | pop_val(L,2,&k); 357 | err = mdb_cursor_get(cursor,&k,NULL,op); 358 | switch (err) { 359 | case MDB_NOTFOUND: 360 | lua_pushnil(L); 361 | return 1; 362 | case 0: 363 | lua_pushlstring(L,k.mv_data,k.mv_size); 364 | return 1; 365 | } 366 | return error_and_out(L,err); 367 | } 368 | 369 | static int cursor_put(lua_State *L) { 370 | MDB_cursor* cursor = check_cursor(L,1); 371 | MDB_val k,v; 372 | unsigned int flags = luaL_checkinteger(L,4); 373 | int err; 374 | pop_val(L,2,&k); 375 | pop_val(L,3,&v); 376 | err = mdb_cursor_put(cursor,&k,&v,flags); 377 | return success_or_err(L,err); 378 | } 379 | 380 | static int cursor_del(lua_State *L) { 381 | MDB_cursor* cursor = check_cursor(L,1); 382 | unsigned int flags = luaL_checkinteger(L,2); 383 | int err = mdb_cursor_del(cursor,flags); 384 | return success_or_err(L,err); 385 | } 386 | 387 | static int cursor_count(lua_State *L) { 388 | MDB_cursor* cursor = check_cursor(L,1); 389 | size_t count = 0; 390 | int err = mdb_cursor_count(cursor,&count); 391 | if ( err ) { 392 | return error_and_out(L,err); 393 | } 394 | lua_pushinteger(L,count); 395 | return 1; 396 | } 397 | 398 | static const luaL_Reg cursor_methods[] = { 399 | #if LUA_VERSION_NUM >= 504 400 | {"__close",cursor_close}, 401 | #endif 402 | {"__gc",cursor_close}, 403 | {"close",cursor_close}, 404 | {"txn",cursor_txn}, 405 | {"dbi",cursor_dbi}, 406 | {"get",cursor_get}, 407 | {"get_key",cursor_get_key}, 408 | {"put",cursor_put}, 409 | {"del",cursor_del}, 410 | {"count",cursor_count}, 411 | 412 | {0,0} 413 | }; 414 | 415 | DEFINE_register_methods(cursor,CURSOR) 416 | 417 | /* txn */ 418 | 419 | static int txn_commit(lua_State* L) { 420 | MDB_txn* txn = check_txn(L,1); 421 | int err = mdb_txn_commit(txn); 422 | 423 | if ( err ) { 424 | return error_and_out(L,err); 425 | } 426 | 427 | clean_metatable(L); 428 | lua_pushboolean(L,1); 429 | return 1; 430 | } 431 | 432 | static int txn_abort(lua_State* L) { 433 | MDB_txn* txn = check_txn(L,1); 434 | mdb_txn_abort(txn); 435 | clean_metatable(L); 436 | return 0; 437 | } 438 | 439 | static int txn_reset(lua_State* L) { 440 | MDB_txn* txn = check_txn(L,1); 441 | mdb_txn_reset(txn); 442 | return 0; 443 | } 444 | 445 | static int txn_renew(lua_State* L) { 446 | MDB_txn* txn = check_txn(L,1); 447 | mdb_txn_renew(txn); 448 | return 0; 449 | } 450 | 451 | static int txn_dbi_open(lua_State* L) { 452 | MDB_txn* txn = check_txn(L,1); 453 | const char* name = lua_isnil(L,2) ? NULL : luaL_checkstring(L,2); 454 | unsigned int flags = luaL_checkinteger(L,3); 455 | MDB_dbi dbi; 456 | int err = mdb_dbi_open(txn,name,flags,&dbi); 457 | if ( err ) { 458 | return error_and_out(L,err); 459 | } 460 | 461 | lua_pushinteger(L,dbi); 462 | return 1; 463 | } 464 | 465 | static int txn_stat(lua_State* L) { 466 | MDB_txn* txn = check_txn(L,1); 467 | MDB_dbi dbi = luaL_checkinteger(L,2); 468 | MDB_stat stat; 469 | mdb_stat(txn,dbi,&stat); 470 | return stat_to_table(L,&stat); 471 | } 472 | 473 | static int txn_dbi_drop(lua_State* L) { 474 | MDB_txn* txn = check_txn(L,1); 475 | MDB_dbi dbi = luaL_checkinteger(L,2); 476 | int del = luaL_checkinteger(L,3); 477 | int err = mdb_drop(txn,dbi,del); 478 | return success_or_err(L,err); 479 | } 480 | 481 | static int txn_get(lua_State* L) { 482 | MDB_txn* txn = check_txn(L,1); 483 | MDB_dbi dbi = luaL_checkinteger(L,2); 484 | MDB_val k,v; 485 | int err; 486 | 487 | err = mdb_get(txn,dbi,pop_val(L,3,&k),&v); 488 | switch (err) { 489 | case MDB_NOTFOUND: 490 | lua_pushnil(L); 491 | return 1; 492 | case 0: 493 | lua_pushlstring(L,v.mv_data,v.mv_size); 494 | return 1; 495 | } 496 | return error_and_out(L,err); 497 | } 498 | 499 | static int txn_put(lua_State* L) { 500 | MDB_txn* txn = check_txn(L,1); 501 | MDB_dbi dbi = luaL_checkinteger(L,2); 502 | MDB_val k,v; 503 | unsigned int flags = luaL_checkinteger(L,5); 504 | int err; 505 | 506 | err = mdb_put(txn,dbi,pop_val(L,3,&k),pop_val(L,4,&v),flags); 507 | return success_or_err(L,err); 508 | } 509 | 510 | static int txn_del(lua_State* L) { 511 | MDB_txn* txn = check_txn(L,1); 512 | MDB_dbi dbi = luaL_checkinteger(L,2); 513 | MDB_val k,v; 514 | int err; 515 | pop_val(L,3,&k); 516 | err = mdb_del(txn,dbi,&k,pop_val(L,4,&v)); 517 | return success_or_err(L,err); 518 | } 519 | 520 | static int txn_cmp_helper(lua_State* L,int mode) { 521 | MDB_txn* txn = check_txn(L,1); 522 | MDB_dbi dbi = luaL_checkinteger(L,2); 523 | MDB_val a,b; 524 | pop_val(L,3,&a); 525 | pop_val(L,4,&b); 526 | 527 | if ( mode==MDB_DUPSORT ) 528 | return mdb_dcmp(txn,dbi,&a,&b); 529 | return mdb_cmp(txn,dbi,&a,&b); 530 | } 531 | 532 | static int txn_cmp(lua_State* L) { 533 | return txn_cmp_helper(L,0); 534 | } 535 | 536 | static int txn_dcmp(lua_State* L) { 537 | return txn_cmp_helper(L,MDB_DUPSORT); 538 | } 539 | 540 | static int txn_id(lua_State* L) { 541 | MDB_txn* txn = check_txn(L,1); 542 | lua_pushinteger(L,mdb_txn_id(txn)); 543 | return 1; 544 | } 545 | 546 | static int txn_cursor_open(lua_State* L) { 547 | MDB_txn* txn = check_txn(L,1); 548 | MDB_dbi dbi = luaL_checkinteger(L,2); 549 | MDB_cursor* cursor; 550 | int err = mdb_cursor_open(txn,dbi,&cursor); 551 | if ( err ) { 552 | return error_and_out(L,err); 553 | } 554 | 555 | set_meta_and_return(cursor,CURSOR); 556 | } 557 | 558 | static int txn_cursor_renew(lua_State *L) { 559 | MDB_txn* txn = check_txn(L,1); 560 | MDB_cursor* cursor = check_cursor(L,2); 561 | int err = mdb_cursor_renew(txn,cursor); 562 | return success_or_err(L,err); 563 | } 564 | 565 | static const luaL_Reg txn_methods[] = { 566 | #if LUA_VERSION_NUM >= 504 567 | {"__close",clean_metatable}, 568 | #endif 569 | {"__gc",clean_metatable}, 570 | {"id",txn_id}, 571 | {"commit",txn_commit}, 572 | {"abort",txn_abort}, 573 | {"reset",txn_reset}, 574 | {"renew",txn_renew}, 575 | {"dbi_open",txn_dbi_open}, 576 | {"stat",txn_stat}, 577 | {"dbi_drop",txn_dbi_drop}, 578 | {"get",txn_get}, 579 | {"put",txn_put}, 580 | {"del",txn_del}, 581 | {"cmp",txn_cmp}, 582 | {"dcmp",txn_dcmp}, 583 | {"cursor_open",txn_cursor_open}, 584 | {"cursor_renew",txn_cursor_renew}, 585 | {0,0} 586 | }; 587 | 588 | DEFINE_register_methods(txn,TXN) 589 | 590 | /* globals */ 591 | static int lmdb_version(lua_State *L) { 592 | const char* ver = mdb_version(NULL,NULL,NULL); 593 | lua_pushstring(L,ver); 594 | return 1; 595 | } 596 | 597 | static int lmdb_strerror(lua_State *L) { 598 | int err = luaL_checkinteger(L,1); 599 | lua_pushstring(L,mdb_strerror(err)); 600 | return 1; 601 | } 602 | 603 | static int lmdb_env_create(lua_State *L) { 604 | MDB_env* env = NULL; 605 | int err = mdb_env_create(&env); 606 | if ( err ) { 607 | lua_pushnil(L); 608 | lua_pushstring(L,mdb_strerror(err)); 609 | return 2; 610 | } 611 | 612 | set_meta_and_return(env,ENV); 613 | } 614 | 615 | static const luaL_Reg globals[] = { 616 | {"version",lmdb_version}, 617 | {"strerror",lmdb_strerror}, 618 | {"env_create",lmdb_env_create}, 619 | {NULL, NULL} 620 | }; 621 | 622 | 623 | 624 | int luaopen_lightningmdb(lua_State *L) { 625 | luaopen_pack(L); 626 | 627 | luaL_newmetatable(L,LIGHTNING); 628 | lua_set_funcs(L,LIGHTNING,globals); 629 | setfield_enum(MDB_NOOVERWRITE); 630 | setfield_enum(MDB_NODUPDATA); 631 | setfield_enum(MDB_CURRENT); 632 | setfield_enum(MDB_RESERVE); 633 | setfield_enum(MDB_APPEND); 634 | setfield_enum(MDB_APPENDDUP); 635 | setfield_enum(MDB_MULTIPLE); 636 | 637 | setfield_enum(MDB_SUCCESS); 638 | setfield_enum(MDB_KEYEXIST); 639 | setfield_enum(MDB_NOTFOUND); 640 | setfield_enum(MDB_PAGE_NOTFOUND); 641 | setfield_enum(MDB_CORRUPTED); 642 | setfield_enum(MDB_PANIC); 643 | setfield_enum(MDB_VERSION_MISMATCH); 644 | setfield_enum(MDB_INVALID); 645 | setfield_enum(MDB_MAP_FULL); 646 | setfield_enum(MDB_DBS_FULL); 647 | setfield_enum(MDB_READERS_FULL); 648 | setfield_enum(MDB_TLS_FULL); 649 | setfield_enum(MDB_TXN_FULL); 650 | setfield_enum(MDB_CURSOR_FULL); 651 | setfield_enum(MDB_PAGE_FULL); 652 | setfield_enum(MDB_MAP_RESIZED); 653 | setfield_enum(MDB_INCOMPATIBLE); 654 | 655 | setfield_enum(MDB_REVERSEKEY); 656 | setfield_enum(MDB_DUPSORT); 657 | setfield_enum(MDB_INTEGERKEY); 658 | setfield_enum(MDB_DUPFIXED); 659 | setfield_enum(MDB_INTEGERDUP); 660 | setfield_enum(MDB_REVERSEDUP); 661 | setfield_enum(MDB_CREATE); 662 | 663 | setfield_enum(MDB_FIXEDMAP); 664 | setfield_enum(MDB_NOSUBDIR); 665 | setfield_enum(MDB_RDONLY); 666 | setfield_enum(MDB_WRITEMAP); 667 | setfield_enum(MDB_NOMETASYNC); 668 | setfield_enum(MDB_NOSYNC); 669 | setfield_enum(MDB_MAPASYNC); 670 | setfield_enum(MDB_NOTLS); 671 | setfield_enum(MDB_NOLOCK); 672 | setfield_enum(MDB_NORDAHEAD); 673 | setfield_enum(MDB_NOMEMINIT); 674 | 675 | setfield_enum(MDB_FIRST); 676 | setfield_enum(MDB_FIRST_DUP); 677 | setfield_enum(MDB_GET_BOTH); 678 | setfield_enum(MDB_GET_BOTH_RANGE); 679 | setfield_enum(MDB_GET_CURRENT); 680 | setfield_enum(MDB_GET_MULTIPLE); 681 | setfield_enum(MDB_LAST); 682 | setfield_enum(MDB_LAST_DUP); 683 | setfield_enum(MDB_NEXT); 684 | setfield_enum(MDB_NEXT_DUP); 685 | setfield_enum(MDB_NEXT_MULTIPLE); 686 | setfield_enum(MDB_NEXT_NODUP); 687 | setfield_enum(MDB_PREV); 688 | setfield_enum(MDB_PREV_DUP); 689 | setfield_enum(MDB_PREV_NODUP); 690 | setfield_enum(MDB_SET); 691 | setfield_enum(MDB_SET_KEY); 692 | setfield_enum(MDB_SET_RANGE ); 693 | 694 | env_register(L); 695 | txn_register(L); 696 | cursor_register(L); 697 | luaL_getmetatable(L,LIGHTNING); 698 | return 1; 699 | } 700 | -------------------------------------------------------------------------------- /lpack.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shmul/lightningmdb/af8c04c581ec50d9c47c8d375dbfb1c060b3faad/lpack.c -------------------------------------------------------------------------------- /mdb_test.lua: -------------------------------------------------------------------------------- 1 | require "test_common" 2 | 3 | local function cursor_pairs(cursor_,key_,op_) 4 | return coroutine.wrap( 5 | function() 6 | local k = key_ 7 | repeat 8 | k,v = cursor_:get(k,op_ or MDB.NEXT) 9 | if k then 10 | coroutine.yield(k,v) 11 | end 12 | until not k 13 | end) 14 | end 15 | 16 | local function mtest() 17 | print("--- mtest2") 18 | local count = math.random(10)+15 19 | local values = {} 20 | math.randomseed(os.time()) 21 | for i=1,count do 22 | values[i] = math.random(1024) 23 | end 24 | 25 | local e = lightningmdb.env_create() 26 | e:set_mapsize(10485760) 27 | local dir = test_setup("testdb") 28 | e:open(dir,MDB.FIXEDMAP,420) 29 | local t = e:txn_begin(nil,0) 30 | local d = t:dbi_open(nil,0) 31 | 32 | print("adding values:",count) 33 | local j = 0 34 | for i,v in ipairs(values) do 35 | local rc = t:put(d,string.format("%03x",v),string.format("%d foo bar",v), 36 | MDB.NOOVERWRITE) 37 | if not rc then 38 | j = j + 1 39 | end 40 | end 41 | 42 | print(j,"duplicates skipped") 43 | t:commit() 44 | ps(e) 45 | 46 | t = e:txn_begin(nil,0) 47 | c = t:cursor_open(d) 48 | local k 49 | 50 | 51 | for k,v in cursor_pairs(c) do 52 | print(k,v) 53 | end 54 | 55 | c:close() 56 | t:abort() 57 | math.randomseed(os.time()) 58 | j = 0 59 | for i=count,1,-math.random(5) do 60 | j = j + 1 61 | t = e:txn_begin(nil,0) 62 | local key = string.format("%03x",values[i]) 63 | if not t:del(d,key,nil) then 64 | j = j - 1 65 | t:abort() 66 | else 67 | t:commit() 68 | end 69 | end 70 | 71 | print("deleted",j,"values") 72 | ps(e) 73 | t = e:txn_begin(nil,0) 74 | c = t:cursor_open(d) 75 | print("cursor next") 76 | local key 77 | for k,v in cursor_pairs(c,nil,MDB.NEXT) do 78 | print(k,v) 79 | key = k 80 | end 81 | 82 | print("cursor prev") 83 | 84 | for k,v in cursor_pairs(c,key,MDB.PREV) do 85 | print(k,v) 86 | end 87 | 88 | c:close() 89 | e:dbi_close(d) 90 | 91 | t:abort() 92 | e:close() 93 | end 94 | 95 | local function mtest2() 96 | print("--- mtest2") 97 | 98 | local count = math.random(10)+15 99 | local values = {} 100 | math.randomseed(os.time()) 101 | for i=1,count do 102 | values[i] = math.random(1024) 103 | end 104 | 105 | local e = lightningmdb.env_create() 106 | e:set_mapsize(10485760) 107 | e:set_maxdbs(4) 108 | local dir = test_setup("testdb") 109 | e:open(dir,MDB.FIXEDMAP + MDB.NOSYNC,420) 110 | local t = e:txn_begin(nil,0) 111 | local d = t:dbi_open("id1",MDB.CREATE) 112 | 113 | print("adding values:",count) 114 | local j = 0 115 | for i,v in ipairs(values) do 116 | local rc = t:put(d,string.format("%03x",v),string.format("%d foo bar",v), 117 | MDB.NOOVERWRITE) 118 | if not rc then 119 | j = j + 1 120 | end 121 | end 122 | 123 | print(j,"duplicates skipped") 124 | t:commit() 125 | ps(e) 126 | 127 | t = e:txn_begin(nil,0) 128 | c = t:cursor_open(d) 129 | local k 130 | 131 | 132 | for k,v in cursor_pairs(c) do 133 | print(k,v) 134 | end 135 | 136 | c:close() 137 | t:abort() 138 | math.randomseed(os.time()) 139 | j = 0 140 | for i=count,1,-math.random(5) do 141 | j = j + 1 142 | t = e:txn_begin(nil,0) 143 | local key = string.format("%03x",values[i]) 144 | if not t:del(d,key,nil) then 145 | j = j - 1 146 | t:abort() 147 | else 148 | t:commit() 149 | end 150 | end 151 | 152 | print("deleted",j,"values") 153 | 154 | ps(e) 155 | t = e:txn_begin(nil,0) 156 | c = t:cursor_open(d) 157 | print("cursor next") 158 | local key 159 | for k,v in cursor_pairs(c,nil,MDB.NEXT) do 160 | print(k,v) 161 | key = k 162 | end 163 | 164 | print("cursor prev") 165 | 166 | for k,v in cursor_pairs(c,key,MDB.PREV) do 167 | print(k,v) 168 | end 169 | 170 | c:close() 171 | e:dbi_close(d) 172 | 173 | t:abort() 174 | e:close() 175 | end 176 | 177 | local function mtest3() 178 | print("--- mtest3") 179 | 180 | local count = math.random(10)+15 181 | local values = {} 182 | math.randomseed(os.time()) 183 | for i=1,count do 184 | values[i] = math.random(1024) 185 | end 186 | 187 | local e = lightningmdb.env_create() 188 | e:set_mapsize(10485760) 189 | e:set_maxdbs(4) 190 | local dir = test_setup("testdb") 191 | e:open(dir,MDB.FIXEDMAP + MDB.NOSYNC,420) 192 | 193 | local t = e:txn_begin(nil,0) 194 | local d = t:dbi_open("id2",MDB.CREATE+MDB.DUPSORT) 195 | 196 | print("adding values:",count) 197 | local j = 0 198 | for i,v in ipairs(values) do 199 | if i%5==0 then 200 | v = values[i-1] 201 | end 202 | local rc = t:put(d,string.format("%03x",v),string.format("%d foo bar",v), 203 | MDB.NODUPDATA) 204 | if not rc then 205 | j = j + 1 206 | end 207 | end 208 | if j>0 then 209 | print("duplicate skipped",j) 210 | end 211 | t:commit() 212 | ps(e) 213 | 214 | t = e:txn_begin(nil,0) 215 | c = t:cursor_open(d) 216 | 217 | for k,v in cursor_pairs(c,nil,MDB.NEXT) do 218 | print(k,v) 219 | end 220 | 221 | end 222 | 223 | 224 | mtest() 225 | mtest2() 226 | mtest3() 227 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | require "test_common" 2 | 3 | local function basic_test() 4 | print("Lightning MDB version:",lightningmdb.version()) 5 | print("Lightning error:",lightningmdb.strerror(0)) 6 | 7 | print("-- globals --") 8 | pt(lightningmdb) 9 | 10 | -- env 11 | local e = lightningmdb.env_create() 12 | print(e) 13 | local dir = test_setup("foo") 14 | print(e:open(dir,0,420)) 15 | print("fixedmap",MDB.FIXEDMAP) 16 | print("read only",MDB.RDONLY) 17 | 18 | print("-- stats --") 19 | pt(e:stat()) 20 | 21 | print("-- info --") 22 | pt(e:info()) 23 | print("get_path",e:get_path()) 24 | 25 | 26 | --txn 27 | local t = e:txn_begin(nil,0) 28 | print("txn",t) 29 | t:commit() 30 | t = e:txn_begin(nil,0) 31 | print("txn",t) 32 | t:reset() 33 | t:renew() 34 | --t:abort() 35 | local db = t:dbi_open(nil,0) 36 | print(string.format("-- txn stat [%d] --",t:id())) 37 | pt(t:stat(db)) 38 | t:abort() 39 | e:close() 40 | end 41 | 42 | local function grow_db() 43 | print("--- grow_db ---") 44 | local num_pages = 5 45 | local e 46 | 47 | local dir = test_setup("bar") 48 | 49 | local function grow() 50 | e = lightningmdb.env_create() 51 | num_pages = num_pages * 2 52 | print(e:set_mapsize(num_pages*4096)) 53 | print(e:open(dir,0,420)) 54 | end 55 | 56 | grow() 57 | local t = e:txn_begin(nil,0) 58 | local db = t:dbi_open(nil,MDB.DUPSORT) 59 | for i=1,600 do 60 | local rc,err = t:put(db,"hello "..i,"cruel world",MDB.NODUPDATA) 61 | if not rc then 62 | if err:find("MDB_MAP_FULL",1,true) then 63 | print("making more room at",i) 64 | t:abort() 65 | e:close() 66 | 67 | grow() 68 | t = e:txn_begin(nil,0) 69 | db = t:dbi_open(nil,MDB.DUPSORT) 70 | else 71 | print(rc,err) 72 | end 73 | end 74 | end 75 | end 76 | 77 | basic_test() 78 | grow_db() 79 | 80 | print("\n\n\n**** If you are seeing this, all is good (at least as far as lightningmdb is concerned). ****") 81 | -------------------------------------------------------------------------------- /test_common.lua: -------------------------------------------------------------------------------- 1 | lightningmdb_lib=require "lightningmdb" 2 | 3 | lightningmdb = _VERSION>="Lua 5.2" and lightningmdb_lib or lightningmdb 4 | MDB = setmetatable({}, {__index = function(t, k) 5 | return lightningmdb["MDB_" .. k] 6 | end}) 7 | 8 | function pt(t) 9 | for k,v in pairs(t) do 10 | print(k,v) 11 | end 12 | end 13 | 14 | function ps(e) 15 | print("--- env stat") 16 | pt(e:stat()) 17 | print("---") 18 | end 19 | 20 | function test_setup(dir_) 21 | local dir = "./temp/"..dir_ 22 | os.execute("mkdir -p "..dir) 23 | os.execute("rm -rf "..dir.."/data.mdb") 24 | os.execute("rm -rf "..dir.."/lock.mdb") 25 | return dir 26 | end 27 | --------------------------------------------------------------------------------