Skip to content

Commit cb3c248

Browse files
authored
Audit exports. (#60)
1 parent 20cf36f commit cb3c248

24 files changed

Lines changed: 170 additions & 219 deletions

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ close(d)
5656
using LMDB
5757

5858
env = Environment("/tmp/mydb"; mapsize = 1<<30, maxreaders = 510,
59-
flags = MDB_NOTLS | MDB_NORDAHEAD)
59+
flags = LMDB.MDB_NOTLS | LMDB.MDB_NORDAHEAD)
6060
try
6161
start(env) do txn # auto-commits/aborts
6262
open(txn) do dbi
@@ -70,7 +70,7 @@ try
7070
end
7171

7272
# Cursor walk over the LMDB-owned mmap (zero-copy access).
73-
start(env; flags = MDB_RDONLY) do txn
73+
start(env; flags = LMDB.MDB_RDONLY) do txn
7474
open(txn) do dbi
7575
open(txn, dbi) do cur
7676
LMDB.walk(cur, String, String) do k, v
@@ -87,7 +87,8 @@ end
8787
The package decodes `String`, `Vector{T}` for any bitstype `T`, and the
8888
primitive numeric types out of the box. To plug in a custom representation,
8989
define a `Base.read(io::IO, ::Type{T})` method; it will be picked up by `tryget`,
90-
`get`, `walk(f, cur, K, V)`, and the cursor accessors `key`/`value`/`item`.
90+
`get`, `walk(f, cur, K, V)`, and the cursor accessors
91+
`LMDB.key`/`LMDB.value`/`LMDB.item`.
9192
Status-code matchers live on `LMDBError`:
9293

9394
```julia

docs/src/man/cursors.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ scans, range queries, or to amortise the per-lookup overhead of
1111
## Opening a cursor
1212

1313
```julia
14-
start(env; flags = MDB_RDONLY) do txn
14+
start(env; flags = LMDB.MDB_RDONLY) do txn
1515
open(txn) do dbi
1616
open(txn, dbi) do cur
1717
# use cur
@@ -48,16 +48,16 @@ seek_range!(cur, "users/", String)
4848
## Reading at the current position
4949

5050
```julia
51-
key(cur, K) # current key, decoded as K
52-
value(cur, V) # current value, decoded as V
53-
item(cur, K, V) # Pair{K, V}
51+
LMDB.key(cur, K) # current key, decoded as K
52+
LMDB.value(cur, V) # current value, decoded as V
53+
LMDB.item(cur, K, V) # Pair{K, V}
5454
```
5555

5656
The defaults are `K = V = Vector{UInt8}`.
5757

5858
```julia
5959
seek_range!(cur, "users/", String) === nothing && return
60-
@show key(cur, String), value(cur, String)
60+
@show LMDB.key(cur, String), LMDB.value(cur, String)
6161
```
6262

6363
## Range scans
@@ -66,12 +66,12 @@ A typical pattern for "all keys with a given prefix":
6666

6767
```julia
6868
prefix = "users/"
69-
start(env; flags = MDB_RDONLY) do txn
69+
start(env; flags = LMDB.MDB_RDONLY) do txn
7070
open(txn) do dbi
7171
open(txn, dbi) do cur
7272
k = seek_range!(cur, prefix, String)
7373
while k !== nothing && startswith(k, prefix)
74-
v = value(cur, String)
74+
v = LMDB.value(cur, String)
7575
handle(k, v)
7676
k = next!(cur, String)
7777
end
@@ -124,9 +124,9 @@ position:
124124

125125
```julia
126126
put!(cur, key, val)
127-
put!(cur, key, val; flags = MDB_NOOVERWRITE)
127+
put!(cur, key, val; flags = LMDB.MDB_NOOVERWRITE)
128128
delete!(cur)
129-
delete!(cur; flags = MDB_NODUPDATA)
129+
delete!(cur; flags = LMDB.MDB_NODUPDATA)
130130
```
131131

132132
`count(cur)` returns the number of duplicate values for the current
@@ -174,7 +174,7 @@ expensive. Park the txn with [`reset`](@ref Base.reset(::LMDB.Transaction))
174174
and refresh both the txn and the cursor with `renew(txn, cur)`:
175175

176176
```julia
177-
txn = start(env; flags = MDB_RDONLY)
177+
txn = start(env; flags = LMDB.MDB_RDONLY)
178178
cur = open(txn, dbi)
179179
while running
180180
... # use cur

docs/src/man/databases.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ to `Environment` to support multiple named sub-databases.
1313
```julia
1414
dbi = open(txn) # main (unnamed) DB
1515
dbi = open(txn, "users") # named sub-DB; needs maxdbs >= 1
16-
dbi = open(txn, "edges"; flags = MDB_CREATE | MDB_DUPSORT)
16+
dbi = open(txn, "edges"; flags = LMDB.MDB_CREATE | LMDB.MDB_DUPSORT)
1717
```
1818

1919
The do-block form closes the DBI on the way out:
@@ -69,7 +69,7 @@ tryget(txn, dbi, key, UInt64) # → Union{UInt64, Nothing}
6969

7070
```julia
7171
put!(txn, dbi, key, val)
72-
put!(txn, dbi, key, val; flags = MDB_NOOVERWRITE)
72+
put!(txn, dbi, key, val; flags = LMDB.MDB_NOOVERWRITE)
7373
delete!(txn, dbi, key) # → Bool: true if removed
7474
delete!(txn, dbi, key, val) # DUPSORT: delete one specific dup
7575
replace!(txn, dbi, key, val) # atomic put-and-return-old
@@ -89,7 +89,7 @@ Useful write flags:
8989
start(env) do txn
9090
open(txn) do dbi
9191
for (k, v) in sorted_pairs
92-
put!(txn, dbi, k, v; flags = MDB_APPEND)
92+
put!(txn, dbi, k, v; flags = LMDB.MDB_APPEND)
9393
end
9494
end
9595
end
@@ -131,8 +131,8 @@ live = (s.branch_pages + s.leaf_pages + s.overflow_pages) * s.psize
131131
## Dropping a database
132132

133133
```julia
134-
drop(txn, dbi) # empty the DB (handle still valid)
135-
drop(txn, dbi; delete = true) # delete the DB and close the handle
134+
LMDB.drop(txn, dbi) # empty the DB (handle still valid)
135+
LMDB.drop(txn, dbi; delete = true) # delete the DB and close the handle
136136
```
137137

138138
For named sub-DBs, `delete = true` removes the entry from the env's

docs/src/man/dupsort.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ values" patterns.
1313
```julia
1414
env = Environment("/tmp/edges"; mapsize = 1 << 30, maxdbs = 1)
1515
start(env) do txn
16-
dbi = open(txn, "edges"; flags = MDB_CREATE | MDB_DUPSORT)
16+
dbi = open(txn, "edges"; flags = LMDB.MDB_CREATE | LMDB.MDB_DUPSORT)
1717
put!(txn, dbi, "a", "b")
1818
put!(txn, dbi, "a", "c")
1919
put!(txn, dbi, "a", "d")

docs/src/man/environments.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ and `open`s the directory in one go:
1717
env = Environment("/tmp/mydb"; mapsize = 1 << 30, # 1 GiB virtual map
1818
maxreaders = 510,
1919
maxdbs = 8,
20-
flags = MDB_NOTLS)
20+
flags = LMDB.MDB_NOTLS)
2121
```
2222

2323
If anything fails between `create` and a successful `open`, the
@@ -30,7 +30,7 @@ env = create()
3030
env[:MapSize] = 1 << 30
3131
env[:Readers] = 510
3232
env[:DBs] = 8
33-
open(env, "/tmp/mydb"; flags = MDB_NOTLS)
33+
open(env, "/tmp/mydb"; flags = LMDB.MDB_NOTLS)
3434
```
3535

3636
The `[:Flags]`/`[:Readers]`/`[:MapSize]`/`[:DBs]` keys map directly to
@@ -46,7 +46,7 @@ The do-block constructor `environment(f, path; flags, mode)` opens the
4646
env, calls `f(env)`, and closes the env on the way out:
4747

4848
```julia
49-
environment("/tmp/mydb"; flags = MDB_NOTLS) do env
49+
environment("/tmp/mydb"; flags = LMDB.MDB_NOTLS) do env
5050
# use env
5151
end
5252
```
@@ -68,7 +68,7 @@ end
6868
| `MDB_NOLOCK` | the caller takes responsibility for locking |
6969

7070
`MDB_RDONLY` can only be set at `open` time. Calling `set!(env,
71-
MDB_RDONLY)` on an open env will return `EINVAL`.
71+
LMDB.MDB_RDONLY)` on an open env will return `EINVAL`.
7272

7373
## Sizing the map
7474

docs/src/man/essentials.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ when GC runs.
8484
The do-block constructors are usually what you want:
8585

8686
```julia
87-
environment("/tmp/mydb"; flags = MDB_NOTLS) do env
87+
environment("/tmp/mydb"; flags = LMDB.MDB_NOTLS) do env
8888
start(env) do txn
8989
open(txn) do dbi
9090
put!(txn, dbi, "k", "v")

docs/src/man/transactions.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ time per environment).
1212

1313
```julia
1414
txn = start(env) # read-write
15-
txn = start(env; flags = MDB_RDONLY) # read-only
15+
txn = start(env; flags = LMDB.MDB_RDONLY) # read-only
1616
```
1717

1818
LMDB can hold one writer plus an unlimited number of readers
@@ -48,7 +48,7 @@ Read-only txns are cheap to start and stop, but in a tight loop the
4848
pair is cheaper still:
4949

5050
```julia
51-
txn = start(env; flags = MDB_RDONLY)
51+
txn = start(env; flags = LMDB.MDB_RDONLY)
5252
for batch in batches
5353
open(txn) do dbi
5454
for k in batch
@@ -110,13 +110,13 @@ The most common patterns:
110110

111111
```julia
112112
# Hot read path: many small lookups, no writes
113-
start(env; flags = MDB_RDONLY) do txn ... end
113+
start(env; flags = LMDB.MDB_RDONLY) do txn ... end
114114

115115
# Bulk import: single transaction across many writes (atomic, fast)
116116
start(env) do txn ... end
117117

118118
# Long-running reader (e.g. background scrubber): reset + renew loop
119-
txn = start(env; flags = MDB_RDONLY)
119+
txn = start(env; flags = LMDB.MDB_RDONLY)
120120
while running
121121
...
122122
reset(txn); renew(txn)

src/LMDB.jl

Lines changed: 15 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,36 @@
11
module LMDB
22

33
import Base: open, close, getindex, setindex!, put!, pop!, replace!, reset,
4-
isopen, count, delete!, keys, get, show, show, stat, copy,
4+
isopen, count, delete!, keys, get, show, stat, copy,
55
empty!, length, isempty, iterate, haskey
66
import Base.Iterators: drop
77

8-
export
9-
# error type + matchers
10-
LMDBError, is_notfound, is_keyexist, is_map_full,
11-
12-
# commonly-needed status codes
13-
MDB_NOTFOUND, MDB_KEYEXIST, MDB_MAP_FULL,
14-
15-
# commonly-needed env flags
16-
MDB_RDONLY, MDB_NOTLS, MDB_NORDAHEAD, MDB_NOSUBDIR,
17-
MDB_NOSYNC, MDB_NOMETASYNC, MDB_WRITEMAP, MDB_NOMEMINIT,
18-
19-
# commonly-needed db flags
20-
MDB_CREATE, MDB_DUPSORT, MDB_INTEGERKEY, MDB_REVERSEKEY,
21-
MDB_DUPFIXED, MDB_INTEGERDUP, MDB_REVERSEDUP,
22-
23-
# commonly-needed write flags
24-
MDB_NOOVERWRITE, MDB_NODUPDATA, MDB_APPEND, MDB_RESERVE,
25-
26-
# Julia wrappers: environment
27-
Environment, create, environment,
28-
sync, set!, unset!, info, stat, path, isopen, isflagset,
29-
reader_check, reader_list,
30-
31-
# Julia wrappers: transaction
32-
Transaction, start, abort, commit, reset, renew,
33-
34-
# Julia wrappers: database (DBI)
35-
DBI, drop, get, put!, put_reserved!, delete!, tryget, replace!,
36-
37-
# Julia wrappers: cursor
38-
Cursor, count, transaction, database,
39-
seek!, seek_last!, seek_range!, next!, prev!,
40-
key, value, item, walk,
41-
seek_first_dup!, seek_last_dup!,
42-
next_dup!, prev_dup!, next_nodup!, prev_nodup!,
43-
44-
# High-level interface
45-
LMDBDict
46-
47-
# ---------------------------------------------------------------------------
48-
# Error type. Defined here so the `@checked` macro can reference it; the
49-
# constructor itself defers to `errormsg` (defined after `liblmdb.jl` once
50-
# `mdb_strerror` is in scope).
51-
# ---------------------------------------------------------------------------
52-
53-
"""LMDB exception type. `code` is the raw status code; use `is_notfound`,
54-
`is_keyexist`, `is_map_full` for common matches."""
55-
struct LMDBError <: Exception
56-
code::Cint
57-
msg::AbstractString
58-
LMDBError(code::Integer) = new(Cint(code), errormsg(Cint(code)))
59-
LMDBError(code::Integer, msg::AbstractString) = new(Cint(code), msg)
60-
end
61-
show(io::IO, err::LMDBError) = print(io, "Code[$(err.code)]: $(err.msg)")
62-
63-
"Throw an `LMDBError` if `code` is non-zero. Returns `code` otherwise."
64-
@inline check(code) = iszero(code) ? code : throw(LMDBError(code))
65-
66-
"""
67-
is_notfound(err::LMDBError) -> Bool
68-
69-
`true` if `err.code == MDB_NOTFOUND` (LMDB's "key not present" status).
70-
71-
Use this to recover from a missing key when the lookup is not point-typed
72-
(otherwise `tryget` / `get(..., default)` are simpler):
73-
74-
```julia
75-
try
76-
LMDB.get(txn, dbi, "key", String)
77-
catch e
78-
e isa LMDBError && is_notfound(e) || rethrow()
79-
# treat as missing
8+
# `public name1, name2, ...` on 1.11+, no-op on 1.10.
9+
macro public(names)
10+
@static if VERSION >= v"1.11"
11+
syms = names isa Symbol ? (names,) :
12+
Meta.isexpr(names, :tuple) ? names.args :
13+
error("@public expects a symbol or a comma-separated list of symbols")
14+
return esc(Expr(:public, syms...))
15+
else
16+
return nothing
17+
end
8018
end
81-
```
82-
"""
83-
is_notfound
84-
85-
"""
86-
is_keyexist(err::LMDBError) -> Bool
87-
88-
`true` if `err.code == MDB_KEYEXIST`. Raised by `put!` with `MDB_NOOVERWRITE`
89-
or `MDB_NODUPDATA` when the key (or duplicate) is already present.
90-
"""
91-
is_keyexist
92-
93-
"""
94-
is_map_full(err::LMDBError) -> Bool
9519

96-
`true` if `err.code == MDB_MAP_FULL`. Raised when a write txn would exceed
97-
the environment's `MapSize`. The remedy is to grow `mapsize` (which can be
98-
done after `close(env)` without rewriting the database).
99-
"""
100-
is_map_full
20+
include("error.jl")
10121

102-
# ---------------------------------------------------------------------------
103-
# C API: raw bindings, types, constants. Public-but-unexported.
104-
#
105-
# Every status-returning binding has a `@checked` wrapper (auto-throws) and an
106-
# `unchecked_*` companion (returns the raw `Cint` for callers that need to
107-
# inspect it, e.g. branching on `MDB_NOTFOUND`).
108-
#
109-
# Use as `LMDB.mdb_env_create`, `LMDB.MDB_NOTLS`, `LMDB.MDB_val`. The Julia
110-
# wrappers (`Environment`, `Transaction`, …) wrap these and are what most
111-
# callers should reach for.
112-
# ---------------------------------------------------------------------------
113-
114-
include("checked.jl")
22+
# C API
11523
include("liblmdb.jl")
11624

117-
"""Return a string describing a given LMDB status code."""
118-
errormsg(err::Cint) = unsafe_string(mdb_strerror(err))
119-
120-
# Common status-code matchers, mirroring `is_notfound`/`is_keyexist` patterns.
121-
is_notfound(err::LMDBError) = err.code == MDB_NOTFOUND
122-
is_keyexist(err::LMDBError) = err.code == MDB_KEYEXIST
123-
is_map_full(err::LMDBError) = err.code == MDB_MAP_FULL
124-
125-
# ---------------------------------------------------------------------------
126-
# ccall glue between the C API and the Julia wrappers: `MDBValue`, `MDBArg`,
127-
# and the `MDBValueIO <: IO` wrapper used to plug in custom decoders via
128-
# `Base.read(io, T)`.
129-
# ---------------------------------------------------------------------------
130-
13125
include("common.jl")
13226

133-
# ---------------------------------------------------------------------------
134-
# Julia wrappers around the raw bindings.
135-
# ---------------------------------------------------------------------------
136-
27+
# Julia wrappers
13728
include("env.jl")
13829
include("txn.jl")
13930
include("dbi.jl")
14031
include("cur.jl")
14132

142-
# ---------------------------------------------------------------------------
143-
# High-level interface: `LMDBDict`.
144-
# ---------------------------------------------------------------------------
145-
33+
# High-level abstractions
14634
include("dicts.jl")
14735

14836
end # module

0 commit comments

Comments
 (0)