From c0690a09b6c1cdaf1be58a8c8a85c5fa46dddd3c Mon Sep 17 00:00:00 2001 From: Dan Bauman Date: Mon, 8 Feb 2021 19:05:24 -0600 Subject: [PATCH 1/3] [DDB-15] [DDB-16] --- doc/capi.rst | 79 ++++++++++++++++++ doc/cnf.rst | 19 ++++- doc/index.rst | 44 ++++++---- doc/luaapi.rst | 82 ++++++++++++++++++ doc/pythonapi.rst | 142 ++++++++++++++++++++++++++++++- doc/query.rst | 119 ++++++++++++++++++++++++++ doc/structure.rst | 27 ++++++ lua/discodb.lua | 148 +++++++++++++++++++++++++++++++++ python/discodbmodule.c | 2 +- python/setup.py | 2 +- python/setup_self_contained.py | 2 +- 11 files changed, 641 insertions(+), 25 deletions(-) create mode 100644 doc/luaapi.rst create mode 100644 lua/discodb.lua diff --git a/doc/capi.rst b/doc/capi.rst index 6b87cf8..af230e4 100644 --- a/doc/capi.rst +++ b/doc/capi.rst @@ -5,3 +5,82 @@ capi -- C library to read/write DiscoDBs ==================================================================== The C library is the native control application for reading and writing DiscoDBs + + +Examples +======== + +Opening a discodb +------------------ +Likely want to check a few things when opening a ddb file. + + +.. code-block:: c + + static struct ddb *open_discodb(const char *file) + { + struct ddb *db; + int fd; + if (!(db = ddb_new())){ + fprintf(stderr, "Couldn't initialize discodb: Out of memory\n"); + exit(1); + } + if ((fd = open(file, O_RDONLY)) == -1){ + fprintf(stderr, "Couldn't open discodb %s\n", file); + exit(1); + } + if (ddb_load(db, fd)){ + const char *err; + ddb_error(db, &err); + fprintf(stderr, "Invalid discodb in %s: %s\n", file, err); + exit(1); + } + return db; + } + + + +Adding key/values to a ddb +-------------------------- + +You'll need to create a :code:`ddb_entry` from memory available on the stack or heap. During the :code:`ddb_cons_add` call, the entries will be duplicated into the discodb and the original stack/heap memory can be reused or freed. + + +.. code-block:: c + + uint64_t flags = 0; + flags |= getenv("DONT_COMPRESS") ? DDB_OPT_DISABLE_COMPRESSION: 0; + flags |= getenv("UNIQUE_ITEMS") ? DDB_OPT_UNIQUE_ITEMS: 0; + flags |= DDB_OPT_UNIQUE_ITEMS; + + + char key[MAX_KV_SIZE] = "Key\0"; + char val[MAX_KV_SIZE] = "Value\0"; + struct ddb_entry key_e = {.data = key, .length = strlen(key)}; + struct ddb_entry val_e = {.data = val, .length = strlen(val)}; + + + if (ddb_cons_add(db, &key_e, &val_e)){ + fprintf(stderr, "Adding '%s':'%s' failed\n", key, val); + exit(1); + } + + + if (!(data = ddb_cons_finalize(db, &size, flags))){ + fprintf(stderr, "Packing the index failed\n"); + exit(1); + } + + ddb_cons_free(db); + + if (!(out = fopen("/path/to/ddb", "w"))){ + fprintf(stderr, "Opening file %s failed\n", ddbfile); + exit(1); + } + if (!fwrite(data, size, 1, out)){ + fprintf(stderr, "Writing file %s failed\n", ddbfile); + exit(1); + } + fflush(out); + fclose(out); + free(data); diff --git a/doc/cnf.rst b/doc/cnf.rst index db99fc7..e7426f2 100644 --- a/doc/cnf.rst +++ b/doc/cnf.rst @@ -1,7 +1,22 @@ .. cnf: -cnf -- Query Language +cnf -- Conjunctive Normal Form (used by Query Language) ==================================================================== -Both the Python API and C API will interpret CNF based queries to access values +Both the Python API and C API will interpret CNF based queries into a normalized form for queries. + + + +.. code-block:: python + + + >>> Q.parse('~(B | C)') == Q.parse('~B & ~C') + True + >>> Q.parse('(A & B) | C') == Q.parse('(A | C) & (B | C)') + True + >>> Q.parse('A & (B | (D & E))') == Q.parse('A & (B | D) & (B | E)') + True + >>> Q.parse(str(Q.parse('a | b | c & d | e'))) == Q.parse('a | b | c & d | e') + True + diff --git a/doc/index.rst b/doc/index.rst index 28cfdb0..614c962 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -49,20 +49,26 @@ indexing system, `discodex`_. Install ------- -DiscoDB does not depend on Disco in any way, although it is a core -component in `discodex`_. You can use it without Disco as a general-purpose -scalable, immutable datastructure for Python. To install only DiscoDB without -rest of Disco, clone Disco, and run:: +.. code-block:: bash - make install-discodb + mkdir cmake-release && cd cmake-release; + cmake -DCMAKE_BUILD_TYPE=Release ..; + make; + make test; + sudo install libdiscodb.so /usr/lib/libdiscodb.so ; + sudo install ../src/*.h /usr/include/. ; -or if you just want to build it locally:: +to install the python wrapper - cd contrib/discodb +.. code-block:: bash + + cd python python setup.py build + DiscoDB requires `CMPH library v0.9 or newer -`_ (``libcmph-dev`` in Debian). +`_ (``libcmph-dev`` in Debian) (``cmph-dev`` for headers and ``libcmph0`` for libs on ubuntu) (``cmph-dev`` for headers ``libcmph`` for libs on alpine). + Example ------- @@ -71,17 +77,17 @@ Here is a simple example that builds a simple discodb and queries it:: from discodb import DiscoDB, Q - data = {'mammals': ['cow', 'dog', 'cat', 'whale'], - 'pets': ['dog', 'cat', 'goldfish'], - 'aquatic': ['goldfish', 'whale']} + data = {b'mammals': [b'cow', b'dog', b'cat', b'whale'], + b'pets': [b'dog', b'cat', b'goldfish'], + b'aquatic': [b'goldfish', b'whale']} db = DiscoDB(data) # create an immutable discodb object - print list(db.keys()) # => mammals, aquatic, pets - print list(db['pets']) # => dog, cat, goldfish - print list(db.query(Q.parse('mammals & aquatic'))) # => whale - print list(db.query(Q.parse('pets & ~aquatic'))) # => dog, cat - print list(db.query(Q.parse('pets | aquatic'))) # => dog, cat, whale, goldfish + print(list(db.keys())) # => mammals, aquatic, pets + print(list(db[b'pets'])) # => dog, cat, goldfish + print(list(db.query(Q.parse('mammals & aquatic')))) # => whale + print(list(db.query(Q.parse('pets & ~aquatic')))) # => dog, cat + print(list(db.query(Q.parse('pets | aquatic')))) # => dog, cat, whale, goldfish db.dump(file('animals.db', 'w')) # dump discodb to a file @@ -146,11 +152,13 @@ See Also :caption: Contents: index - structure pythonapi + capi + luaapi cnf query - capi + structure + .. _conjunctive normal form: http://en.wikipedia.org/wiki/Conjunctive_normal_form diff --git a/doc/luaapi.rst b/doc/luaapi.rst new file mode 100644 index 0000000..45b6a39 --- /dev/null +++ b/doc/luaapi.rst @@ -0,0 +1,82 @@ + +.. luaapi: + +luaapi -- Lua library to read/write DiscoDBs +==================================================================== + +The lua API links against libdiscodb at runtime and provides a thin wrapper to the functionality. + +This api is currently **read only** + +The following examples demonstrate a prebuilt ddb obtained using + +.. code-block:: bash + + wget http://pkgs.bauman.space/discodb/myths.ddb -O /tmp/myths.ddb` + + + + +Examples +======== + +Basic discodb wrapper for Lua. + +libdiscodb.so should be in :code:`LD_LIBRARY_PATH` + +Open and list keys +------------------ + +.. code-block:: lua + + local ffi = require("ffi") + local ddblib = ffi.load("libdiscodb.so", true) -- Load lib into global + local discodb = require("discodb") + local db = discodb.open("/tmp/myths.ddb") + local iter = db:keys() + for i = 1,#iter do + print(i, iter:next()) + end + + +Open and get individual key +--------------------------- + +.. code-block:: lua + + local ffi = require("ffi") + local ddblib = ffi.load("libdiscodb.so", true) -- Load lib into global + local discodb = require("discodb") + local db = discodb.open("/tmp/myths.ddb") + local iter = db:get("Major") + for i = 1,#iter do + print(i, iter:next()) + end + + + +Check Key Exists Before Accessing +--------------------------------- +Depends on the control flow of choice. + +Either handle the error of attempting to access :code:`next()` on a nil iter +or check iter is not nil before calling :code:`next()` on it. + +.. code-block:: lua + + local ffi = require("ffi") + local ddblib = ffi.load("libdiscodb.so", true) -- Load lib into global + local discodb = require("discodb") + local db = discodb.open("/tmp/myths.ddb") + local iter = db:get("Major") + iter = db:get("key-should-not-exist") + if iter then + for i = 1,#iter do + print(i, iter:next()) + end + else + print("key does not exist") + end + + + diff --git a/doc/pythonapi.rst b/doc/pythonapi.rst index 56aa707..b5d86c3 100644 --- a/doc/pythonapi.rst +++ b/doc/pythonapi.rst @@ -11,8 +11,146 @@ By default the python API links against libdiscodb at runtime and provides a thi Examples ======== -incrementally adding key/value pairs to a constructor:: +incrementally adding key/value pairs to a constructor +----------------------------------------------------- - from discodb import DiscoDBConstructor +to incrementally build a discodb, start a DiscoDBCostructor and add key values one at a time. +Keys and/or values may be duplicated, DiscoDB will take care of it. + +.. code-block:: python + + import discodb + from time import sleep + a = discodb.DiscoDBConstructor() + a.add(b"k", b"v") + a.add(b"db1", b"a value") + o = a.finalize(unique_items=True) + with open("/path/to/ddb", 'wb') as f: + o.dump(f) + + +loading the discodb from disk +----------------------------------- + +.. code-block:: python + + import discodb + with open("/path/to/ddb", 'rb') as f: + ddb = discodb.DiscoDB.load(f) + print("DB1 Keys and Values read from disk") + first_keys = [k for k in ddb.keys()] + first_values = [v for v in ddb.values()] + + + +Merging multiple ddbs into a single ddb +--------------------------------------- + +.. code-block:: python + + import discodb + c = discodb.DiscoDBConstructor() # combined ddb + c.add(b"k", b"2") # feel free to add other keys before/after the merge call + c.add(b"k", b"3") + with open("/path/to/ddb", 'rb') as f: + prior_ddb = discodb.DiscoDB.load(f) + c.merge(prior_ddb) + # can open / merge multiple ddbs + # note CMPH algorithm preserves value order, + # so the arrangement of merges will affect key order in final ddb + merged_output = c.finalize(unique_items=True) + with open("/path/to/merged_ddb", 'wb') as f: + o.dump(f) + + + +Overwriting DDB values during merge +----------------------------------- +merging multiple ddbs into a single ddb where they values are replaced by a single explicit value. + +A DiscoDB contains key/value pairs. In the case where a key/static-value pair is required (perhaps in a distributed index), :code:`merge_with_explicit_value` can be used during merge. Every key will have the same explicit static value after the merge. + +.. code-block:: python + + import discodb + c = discodb.DiscoDBConstructor() # combined ddb + c.add(b"k", b"2") # feel free to add other keys before/after the merge call + c.add(b"k", b"3") + with open("/path/to/ddb", 'rb') as f: + prior_ddb = discodb.DiscoDB.load(f) + c.merge_with_explicit_value(e, b"new_value_for_all_keys") + # can open / merge multiple ddbs + # note CMPH algorithm preserves value order, + # so the arrangement of merges will affect key order in final ddb + merged_output = c.finalize(unique_items=True) + with open("/path/to/merged_ddb", 'wb') as f: + o.dump(f) + + +Inverting a DDB +--------------- +In cases where a ddb contains key/value pairs, and it needs inverted to value/key pairs (accessed by value rather than key) + +.. code-block:: python + + import discodb + c = discodb.DiscoDBConstructor() # combined ddb + c.add(b"k", b"2") # feel free to add other keys before/after the merge call + c.add(b"k", b"3") + with open("/path/to/ddb", 'rb') as f: + prior_ddb = discodb.DiscoDB.load(f) + c.invert(prior_ddb) + inverted_output = c.finalize(unique_items=True) + with open("/path/to/inverted_ddb", 'wb') as f: + o.dump(f) + + + +DiscoDB memory maps the on-disk file, so the file can be quite large (technically 2^64 bytes) without requiring excessive process memory. + +In cases where memory allows it, as determined by the user, Disco can load it's entire structure into a native python dictionary + +**warning** DiscoDB will make no effort to verify the size of the file can be allocated into process memory as a dict + +It is up to the user to check the size of the ddb and compare to available memory + +Therefore, it is by design that the the :code:`.__dict__()` function will not automatically call the :code:`dump_dict` function + +.. code-block:: python + + import discodb + with open("/path/to/ddb", 'rb') as f: + ddb = discodb.DiscoDB.load(f) + native_dict = ddb.dump_dict() # native dict + + + + +Passing around DDB as in-memory buffers +--------------------------------------- + +This may largely defeat a key point of DiscoDB in that DDBs can be generated on systems with large memory and read/used on systems with low memory due to memory mapping the structure rather than loading it in its entirety. + +Passing around memory buffers may be useful in a cross-language system where a DDB can be exchanged between a C application, a Python application, and a Lua application all accessing the same DDB structure. + +In general, there may better in-memory key value stores. + +use the :code:`dumps` command on a **finalized** constructor to retreive a bytes buffer. Transfer those bytes via any means to another system capable of loading the buffer. + +use the :code:`discodb.DiscoDB.loads` command to load the buffer obtained from the other system. + +.. code-block:: python + + import discodb + + c = discodb.DiscoDBConstructor() # combined ddb + c.add(b"key", b"value") + o = c.finalize() + + ddb_buffer = o.dumps() + + ddb = discodb.DiscoDB.loads(ddb_buffer) + native_dict = ddb.dump_dict() + print("should be {b'key': [b'value']}") \ No newline at end of file diff --git a/doc/query.rst b/doc/query.rst index e7108bc..a65cf7a 100644 --- a/doc/query.rst +++ b/doc/query.rst @@ -5,3 +5,122 @@ query -- Access methods to DiscoDB Overview of access methods +Direct Access +------------- +The same path as the default getter + +.. code-block:: python + + d = DiscoDB({'A': ['B', 'C'], 'B': 'D', 'C': 'E', 'D': 'F', 'E': 'G'}) + sorted(d.query(Q.parse('A'))) + ['B', 'C'] + + +Indirect Access +--------------- +Gather the values which are accessed by the value at the named key + +1) A points to B and C +2) B points to D +3) C points to E + +therefore :code:`*A` points to :code:`[D, E]` + +.. code-block:: python + + sorted(d.query(Q.parse('*A'))) + ['D', 'E'] + + +Double Indirect Access +---------------------- + +:code:`**A` + +1) A points to B and C +2) B points to D (first indirect) +3) D points to F (second indirect) +4) C points to E (first indirect) +5) E points to G (second indirect) + +therefore :code:`**A` points to :code:`[F, G]` + +.. code-block:: python + + sorted(d.query(Q.parse('**A'))) + ['F', 'G'] + + +Arbitrary Depth Indirects +------------------------- + +DiscoDB can execute arbitrary depth queries. + +:code:`***A` + +1) A points to B and C +2) B points to D (first indirect) +3) D points to F (second indirect) +4) C points to E (first indirect) +5) E points to G (second indirect) +6) F is not a valid key (third indirect) +7) G is not a valid key (third indirect) + +therefore :code:`***A` points to :code:`[ ]` + +**Note:** generation of Q object will throw a :code:`TypeError` if the number of redirects exceeds the linkages provided by the underlying DDB + +.. code-block:: python + + sorted(d.query(Q.parse('***A'))) + [ ] + sorted(d.query(Q.parse('****A'))) + [ ] + sorted(d.query(Q.parse('*****A'))) + TypeError: reduce() of empty sequence with no initial value + + +Or Operation +------------ +Gather the values by any of the keys + +1) A points to B and C +2) B points to D + +therefore :code:`A | B` points to :code:`[B, C, D]` + +.. code-block:: python + + sorted(d.query(Q.parse('A | B'))) + ['B', 'C', 'D'] + +Mixing Indirects with Logical +----------------------------- + +.. code-block:: python + + sorted(d.query(Q.parse('*A | B'))) + ['D', 'E'] + sorted(d.query(Q.parse('**A | *B'))) + ['F', 'G'] + +Meta Queries +------------ + +Metaqueries document how DiscoDB arrived at the result of the query + +This may be helpful when the CNF parser rearranges the query prior to runtime. + +.. code-block:: python + + >>> sorted((str(k), sorted(vs)) for k, vs in d.metaquery(Q.parse('A'))) + [('A', ['B', 'C'])] + >>> sorted((str(k), sorted(vs)) for k, vs in d.metaquery(Q.parse('*A'))) + [('B', ['D']), ('C', ['E'])] + >>> sorted((str(k), sorted(vs)) for k, vs in d.metaquery(Q.parse('A | B'))) + [('A | B', ['B', 'C', 'D'])] + >>> sorted((str(k), sorted(vs)) for k, vs in d.metaquery(Q.parse('*A | B'))) + [('B', ['D']), ('C | B', ['D', 'E'])] + >>> sorted((str(k), sorted(vs)) for k, vs in d.metaquery(Q.parse('**A | *B'))) + [('D', ['F']), ('E | D', ['F', 'G'])] + diff --git a/doc/structure.rst b/doc/structure.rst index a01d64e..1627ef4 100644 --- a/doc/structure.rst +++ b/doc/structure.rst @@ -5,3 +5,30 @@ structure -- C library to read/write DiscoDBs ==================================================================== The C library is the native control application for reading and writing DiscoDBs + +.. image:: images/discodb_format.png + +Security +-------- +This means that the DiscoDB keys are littered throughout the database file and the and values are at the end of the database in memory and on disk and in memory. + + +If the content of the keys/values are secure data there are a few options. + +Loosly using the term "key" to refer to the key/value pair. Using the terms "symmetric key" and "asymmetric key" to refer to cryptographic keys. + +In all cases, the DiscoDB file would be presumed widely available, and the symmetric key would be a held secret or the asymmetric keys would be used as intended. + + +1) Encrypt the keys and values before inserting with :code:`add`. The end user may not need to know this key if the system provides it at an intermediate layer. (**Note** Relationships of encrypted keys to encrypted values would be visible). + + a) Encrypt the search token (key) with a known symmetric key the tokens of the key before calling :code:`get` or preparing a :code:`Q` object + b) Decrypt each value with a known symmetric key before returning each key to the end user. + +2) Encrypt the entire DiscoDB file with a symmetric or asymmetric key + + a) use an encryption utility (examples: GPG, sodium, etc) to encrypt the entire DiscoDB file once it is written to disk locally. + b) make the encrypted version of the DiscoDB file available on shared systems + c) decrypt and access a local copy of the DiscoDB as needed + + diff --git a/lua/discodb.lua b/lua/discodb.lua new file mode 100644 index 0000000..f229cf9 --- /dev/null +++ b/lua/discodb.lua @@ -0,0 +1,148 @@ +--[[ + Basic discodb wrapper for Lua. + Assuming you have discodb library loaded / linked: + + local discodb = require("discodb") + local db = discodb.open("/path/to/ddb") + local iter = db:keys() + for i = 1,#iter do + print(i, iter:next()) + end + + etc. + + TODO: + - wrap CNF queries + - discodb construction (currently read-only) + - report actual errors coming from discodb +]] + +local ffi = require("ffi") + +ffi.cdef[[ +int fileno(void *FILE); + +struct ddb; +struct ddb_cursor; +struct ddb_entry { + const char *data; + uint32_t length; +}; + +struct ddb *ddb_new(); +int ddb_load(struct ddb *db, int fd); + +struct ddb_cursor *ddb_keys(struct ddb *db); +struct ddb_cursor *ddb_values(struct ddb *db); +struct ddb_cursor *ddb_unique_values(struct ddb *db); +struct ddb_cursor *ddb_getitem(struct ddb *db, const struct ddb_entry *key); + +void ddb_free(struct ddb *db); +int ddb_error(const struct ddb *db, const char **errstr); +int ddb_free_cursor(struct ddb_cursor *cur); +int ddb_notfound(const struct ddb_cursor *c); + +const struct ddb_entry *ddb_next(struct ddb_cursor *cur, int *errcode); +uint64_t ddb_resultset_size(const struct ddb_cursor *cur); +uint64_t ddb_cursor_count(struct ddb_cursor *c, int *err); +]] + +local Entry = { + new = function (str) + local entry = ffi.new('struct ddb_entry') + if str then + entry:store(str) + end + return entry + end, + + __index = { + store = function (entry, str) + entry.data = str + entry.length = #str + return entry + end + }, + + __tostring = function (entry) + return ffi.string(entry.data, entry.length) + end +} + +local Cursor = { + __index = { + next = function (cursor) + local err = ffi.new('int[1]') + local entry = ffi.C.ddb_next(cursor, err) + if err[0] > 0 then + error("ddb_next") + end + return entry + end, + + size = function (cursor) + return ffi.C.ddb_resultset_size(cursor) + end, + + count = function (cursor) + local err = ffi.new('int[1]') + local count = ffi.C.ddb_cursor_count(cursor) + if err[0] > 0 then + error("ddb_count") + end + return count + end, + + notfound = function (cursor) + return ffi.C.ddb_notfound(cursor) ~= 0 + end + }, + + __len = function (cursor) + return tonumber(cursor:size()) + end +} + +local DiscoDB = { + __index = { + keys = function (db) + return ffi.gc(ffi.C.ddb_keys(db), ffi.C.ddb_free_cursor) + end, + + values = function (db) + return ffi.gc(ffi.C.ddb_values(db), ffi.C.ddb_free_cursor) + end, + + unique_values = function (db) + return ffi.gc(ffi.C.ddb_unique_values(db), ffi.C.ddb_free_cursor) + end, + + getitem = function (db, k) + return ffi.gc(ffi.C.ddb_getitem(db, k), ffi.C.ddb_free_cursor) + end, + + get = function (db, key, default) + local values = db:getitem(Entry.new(key)) + if values:notfound() then + return default + end + return values + end + } +} + +ffi.metatype('struct ddb_entry', Entry) +ffi.metatype('struct ddb_cursor', Cursor) +ffi.metatype('struct ddb', DiscoDB) + +return { + open = function (path) + local file = assert(io.open(path, "r")) + local fd = ffi.C.fileno(file) + local db = ffi.gc(ffi.C.ddb_new(), ffi.C.ddb_free) + if ffi.C.ddb_load(db, fd) > 0 then + error("ddb_load") + end + return db + end +} diff --git a/python/discodbmodule.c b/python/discodbmodule.c index 52204b5..534b3ce 100644 --- a/python/discodbmodule.c +++ b/python/discodbmodule.c @@ -138,7 +138,7 @@ DiscoDB_new(PyTypeObject *type, PyObject *args, PyObject *kwds) *iteritems = NULL, *none = NULL, #if PY_MAJOR_VERSION >= 3 - *typedict = Py_BuildValue("{y:O}", "ddb_type", type); + *typedict = Py_BuildValue("{s:O}", "ddb_type", type); #else *typedict = Py_BuildValue("{s:O}", "ddb_type", type); #endif diff --git a/python/setup.py b/python/setup.py index 4abcee3..f08f874 100644 --- a/python/setup.py +++ b/python/setup.py @@ -15,7 +15,7 @@ ) setup(name='discodb', - version='0.9', + version='0.9.1', description='An efficient, immutable, persistent mapping object.', author='Nokia Research Center', install_requires=[ diff --git a/python/setup_self_contained.py b/python/setup_self_contained.py index b565fb9..a7ac37a 100644 --- a/python/setup_self_contained.py +++ b/python/setup_self_contained.py @@ -37,7 +37,7 @@ ) setup(name='discodb', - version='0.9', + version='0.9.1', description='An efficient, immutable, persistent mapping object.', author='Nokia Research Center', install_requires=[ From c07a90cfe0968e95d6cdd6e2c99ffa53ca84ce26 Mon Sep 17 00:00:00 2001 From: Dan Bauman Date: Mon, 8 Feb 2021 19:09:46 -0600 Subject: [PATCH 2/3] [DDB-15] [DDB-16] --- lua/discotest.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 lua/discotest.lua diff --git a/lua/discotest.lua b/lua/discotest.lua new file mode 100644 index 0000000..2803d9f --- /dev/null +++ b/lua/discotest.lua @@ -0,0 +1,12 @@ +local ffi = require("ffi") +local ddblib = ffi.load("libdiscodb.so", true) -- Load lib into global +local discodb = require("discodb") +local db = discodb.open("/tmp/myths.ddb") +local iter = db:get("Roman") +if iter then + for i = 1,#iter do + print(i, iter:next()) + end +else + print("key does not exist") +end From c69588a4e816c4f2b53e8344ccf195ef77d6ef1a Mon Sep 17 00:00:00 2001 From: Dan Bauman Date: Mon, 8 Feb 2021 19:13:07 -0600 Subject: [PATCH 3/3] [DDB-15] [DDB-16] --- .github/workflows/build-and-test-u20.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test-u20.yml b/.github/workflows/build-and-test-u20.yml index 9a9c37f..279dc1d 100644 --- a/.github/workflows/build-and-test-u20.yml +++ b/.github/workflows/build-and-test-u20.yml @@ -18,7 +18,7 @@ jobs: sudo rm -rf /usr/include/discodb.h /usr/include/ddb*.h /usr/lib/libdiscodb.so - name: install c dependency run: - sudo apt-get install -y wget libcmph-dev cmake make build-essential; + sudo apt-get install -y wget libcmph-dev cmake make build-essential luajit; wget http://pkgs.bauman.space/discodb/sample.ddb -O /tmp/sample.ddb; wget http://pkgs.bauman.space/discodb/animals.ddb -O /tmp/animals.ddb; wget http://pkgs.bauman.space/discodb/cjk.ddb -O /tmp/cjk.ddb; @@ -49,3 +49,7 @@ jobs: find . -name "*.so" | xargs -P1 -n1 ldd ; pip3 install --force-reinstall dist/discodb*.whl ; python3 util/test_merge.py ; + - name: verify lua library + run: + cd lua; + luajit discotest.lua ;