Skip to content

Commit 50147f6

Browse files
authored
Upgrade to LMDB 1.0 (#71)
1 parent b02ce77 commit 50147f6

15 files changed

Lines changed: 217 additions & 44 deletions

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ LMDB_jll = "6206cf0b-f360-5984-af49-5437264c140e"
1212

1313
[compat]
1414
CEnum = "0.4, 0.5"
15-
LMDB_jll = "0.9.35"
15+
LMDB_jll = "1.0"
1616
julia = "1.10"

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Memory-Mapped Database. LMDB is an embedded, memory-mapped, ACID key-value
1010
store developed by Symas for OpenLDAP. It persists to disk while reading
1111
at near in-memory speeds.
1212

13+
LMDB 1.0 changed the on-disk file format. Databases created by LMDB 0.9
14+
must be exported with a 0.9 `mdb_dump` and imported with a 1.0 `mdb_load`;
15+
LMDB 1.0 does not open 0.9 database files directly.
16+
1317
```julia
1418
using Pkg; Pkg.add("LMDB")
1519
```
@@ -125,3 +129,4 @@ ret == 0 || throw(LMDB.LMDBError(ret))
125129

126130
- LMDB upstream: <https://github.com/LMDB/lmdb>
127131
- LMDB API docs: <http://www.lmdb.tech/doc/>
132+
- LMDB 1.0 upgrade notes: <http://www.lmdb.tech/doc/upgrading.html>

docs/src/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ LMDB is an embedded, memory-mapped, ACID key-value store developed by
77
Symas for OpenLDAP. It persists to disk while reading at near in-memory
88
speeds, limited only by the size of the virtual address space.
99

10+
LMDB 1.0 changed the on-disk file format. Databases created by LMDB 0.9
11+
must be exported with a 0.9 `mdb_dump` and imported with a 1.0
12+
`mdb_load`; LMDB 1.0 does not open 0.9 database files directly.
13+
1014
```julia
1115
using Pkg; Pkg.add("LMDB")
1216
```

docs/src/lib/environments.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ path
2727
## Configuration
2828

2929
`Environment` exposes its tunables through `getindex` / `setindex!` with
30-
symbol keys (`:Flags`, `:Readers`, `:MapSize`, `:DBs`, `:KeySize`):
30+
symbol keys (`:Flags`, `:Readers`, `:MapSize`, `:PageSize`, `:DBs`,
31+
`:KeySize`):
3132

3233
```@docs
3334
Base.setindex!(::Environment, ::Integer, ::Symbol)

docs/src/lib/errors.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ The rest (`MDB_PAGE_NOTFOUND`, `MDB_CORRUPTED`, `MDB_PANIC`,
3030
`MDB_VERSION_MISMATCH`, `MDB_INVALID`, `MDB_DBS_FULL`, `MDB_READERS_FULL`,
3131
`MDB_TLS_FULL`, `MDB_TXN_FULL`, `MDB_CURSOR_FULL`, `MDB_PAGE_FULL`,
3232
`MDB_MAP_RESIZED`, `MDB_INCOMPATIBLE`, `MDB_BAD_RSLOT`, `MDB_BAD_TXN`,
33-
`MDB_BAD_VALSIZE`, `MDB_BAD_DBI`) live under the `LMDB.` prefix.
33+
`MDB_BAD_VALSIZE`, `MDB_BAD_DBI`, `MDB_PROBLEM`, `MDB_BAD_CHECKSUM`,
34+
`MDB_CRYPTO_FAIL`, `MDB_ENV_ENCRYPTION`, `MDB_TXN_PENDING`,
35+
`MDB_CANT_ROLLBACK`, `MDB_DBIS_BUSY`, `MDB_SHORT_WRITE`,
36+
`MDB_ENV_BUSY`, `MDB_IS_READONLY`, `MDB_ADDR_BUSY`) live under the
37+
`LMDB.` prefix.
3438

3539
## Where errors come from at each surface
3640

docs/src/lib/lowlevel.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ ret == 0 || throw(LMDB.LMDBError(ret))
3232

3333
Bindings that return non-status data (`mdb_strerror`, `mdb_version`,
3434
`mdb_txn_id`, `mdb_cmp`, `mdb_dcmp`, `mdb_env_get_maxkeysize`,
35-
`mdb_env_get_userctx`, `mdb_cursor_txn`, `mdb_cursor_dbi`) and
35+
`mdb_env_get_userctx`, `mdb_cursor_txn`, `mdb_cursor_dbi`,
36+
`mdb_cursor_is_db`, `mdb_modload`) and
3637
`Cvoid`-returning ones (`mdb_env_close`, `mdb_dbi_close`,
37-
`mdb_txn_abort`, `mdb_txn_reset`, `mdb_cursor_close`) are left bare,
38-
since there is nothing to check.
38+
`mdb_txn_abort`, `mdb_txn_reset`, `mdb_cursor_close`,
39+
`mdb_modunload`, `mdb_modsetup`) are left bare, since there is
40+
nothing to check.
3941

4042
## Customisation point: `MDBValueIO`
4143

@@ -69,10 +71,12 @@ LMDB.MDB_env # opaque
6971
LMDB.MDB_txn # opaque
7072
LMDB.MDB_cursor # opaque
7173
LMDB.MDB_dbi # = Cuint
74+
LMDB.mdb_size_t # mapsize/page/transaction id integer type
7275
LMDB.MDB_val # struct { mv_size::Csize_t; mv_data::Ptr{Cvoid} }
7376
LMDB.MDB_stat # struct (page sizes, depth, leaf/branch/overflow page counts, entries)
7477
LMDB.MDB_envinfo # struct (mapaddr, mapsize, last_pgno, last_txnid, maxreaders, numreaders)
7578
LMDB.MDB_cursor_op # @cenum: MDB_FIRST … MDB_PREV_MULTIPLE (19 variants)
79+
LMDB.MDB_crypto_funcs
7680
```
7781

7882
### Environment
@@ -83,16 +87,19 @@ mdb_env_open
8387
mdb_env_close
8488
mdb_env_copy mdb_env_copy2
8589
mdb_env_copyfd mdb_env_copyfd2
90+
mdb_env_incr_dump mdb_env_incr_dumpfd mdb_env_incr_loadfd
8691
mdb_env_stat mdb_env_info
8792
mdb_env_sync
8893
mdb_env_set_flags mdb_env_get_flags
8994
mdb_env_get_path mdb_env_get_fd
90-
mdb_env_set_mapsize
95+
mdb_env_set_mapsize mdb_env_set_pagesize
9196
mdb_env_set_maxreaders mdb_env_get_maxreaders
9297
mdb_env_set_maxdbs
9398
mdb_env_get_maxkeysize
9499
mdb_env_set_userctx mdb_env_get_userctx
95100
mdb_env_set_assert
101+
mdb_env_set_encrypt mdb_env_set_checksum
102+
mdb_env_rollback
96103
```
97104

98105
### Transaction
@@ -101,7 +108,9 @@ mdb_env_set_assert
101108
mdb_txn_begin
102109
mdb_txn_env
103110
mdb_txn_id
111+
mdb_txn_flags
104112
mdb_txn_commit
113+
mdb_txn_prepare
105114
mdb_txn_abort
106115
mdb_txn_reset
107116
mdb_txn_renew
@@ -135,18 +144,20 @@ mdb_cursor_close
135144
mdb_cursor_renew
136145
mdb_cursor_txn
137146
mdb_cursor_dbi
147+
mdb_cursor_is_db
138148
mdb_cursor_get
139149
mdb_cursor_put
140150
mdb_cursor_del
141151
mdb_cursor_count
142152
```
143153

144-
### Comparators / readers / version
154+
### Comparators / readers / crypto modules / version
145155

146156
```julia
147157
mdb_cmp mdb_dcmp
148158
mdb_reader_list
149159
mdb_reader_check
160+
mdb_modload mdb_modunload mdb_modsetup
150161
mdb_version
151162
mdb_strerror
152163
```
@@ -155,8 +166,8 @@ mdb_strerror
155166

156167
| group | constants |
157168
|------|-----------|
158-
| Env flags | `MDB_FIXEDMAP`, `MDB_NOSUBDIR`, `MDB_NOSYNC`, `MDB_RDONLY`, `MDB_NOMETASYNC`, `MDB_WRITEMAP`, `MDB_MAPASYNC`, `MDB_NOTLS`, `MDB_NOLOCK`, `MDB_NORDAHEAD`, `MDB_NOMEMINIT` |
169+
| Env flags | `MDB_FIXEDMAP`, `MDB_ENCRYPT`, `MDB_NOSUBDIR`, `MDB_NOSYNC`, `MDB_RDONLY`, `MDB_NOMETASYNC`, `MDB_WRITEMAP`, `MDB_MAPASYNC`, `MDB_NOTLS`, `MDB_NOLOCK`, `MDB_NORDAHEAD`, `MDB_NOMEMINIT`, `MDB_PREVSNAPSHOT`, `MDB_REMAP_CHUNKS` |
159170
| DB flags | `MDB_REVERSEKEY`, `MDB_DUPSORT`, `MDB_INTEGERKEY`, `MDB_DUPFIXED`, `MDB_INTEGERDUP`, `MDB_REVERSEDUP`, `MDB_CREATE` |
160171
| Write flags | `MDB_NOOVERWRITE`, `MDB_NODUPDATA`, `MDB_CURRENT`, `MDB_RESERVE`, `MDB_APPEND`, `MDB_APPENDDUP`, `MDB_MULTIPLE` |
161172
| Copy flag | `MDB_CP_COMPACT` |
162-
| Status codes | `MDB_SUCCESS=0`, `MDB_KEYEXIST`, `MDB_NOTFOUND`, `MDB_PAGE_NOTFOUND`, `MDB_CORRUPTED`, `MDB_PANIC`, `MDB_VERSION_MISMATCH`, `MDB_INVALID`, `MDB_MAP_FULL`, `MDB_DBS_FULL`, `MDB_READERS_FULL`, `MDB_TLS_FULL`, `MDB_TXN_FULL`, `MDB_CURSOR_FULL`, `MDB_PAGE_FULL`, `MDB_MAP_RESIZED`, `MDB_INCOMPATIBLE`, `MDB_BAD_RSLOT`, `MDB_BAD_TXN`, `MDB_BAD_VALSIZE`, `MDB_BAD_DBI` |
173+
| Status codes | `MDB_SUCCESS=0`, `MDB_KEYEXIST`, `MDB_NOTFOUND`, `MDB_PAGE_NOTFOUND`, `MDB_CORRUPTED`, `MDB_PANIC`, `MDB_VERSION_MISMATCH`, `MDB_INVALID`, `MDB_MAP_FULL`, `MDB_DBS_FULL`, `MDB_READERS_FULL`, `MDB_TLS_FULL`, `MDB_TXN_FULL`, `MDB_CURSOR_FULL`, `MDB_PAGE_FULL`, `MDB_MAP_RESIZED`, `MDB_INCOMPATIBLE`, `MDB_BAD_RSLOT`, `MDB_BAD_TXN`, `MDB_BAD_VALSIZE`, `MDB_BAD_DBI`, `MDB_PROBLEM`, `MDB_BAD_CHECKSUM`, `MDB_CRYPTO_FAIL`, `MDB_ENV_ENCRYPTION`, `MDB_TXN_PENDING`, `MDB_CANT_ROLLBACK`, `MDB_DBIS_BUSY`, `MDB_SHORT_WRITE`, `MDB_ENV_BUSY`, `MDB_IS_READONLY`, `MDB_ADDR_BUSY` |

docs/src/man/environments.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ the handle, set mapsize/maxreaders/maxdbs/flags, open the directory:
1515

1616
```julia
1717
env = Environment("/tmp/mydb"; mapsize = 1 << 30, # 1 GiB virtual map
18+
pagesize = 8192,
1819
maxreaders = 510,
1920
maxdbs = 8,
2021
flags = LMDB.MDB_NOTLS)
@@ -23,14 +24,15 @@ env = Environment("/tmp/mydb"; mapsize = 1 << 30, # 1 GiB virtual map
2324
If anything fails on the way through, the half-open env is closed
2425
before the exception propagates.
2526

26-
After the env is open, `[:Flags]` / `[:Readers]` / `[:MapSize]` /
27-
`[:DBs]` setindex! keys map to `mdb_env_set_flags` /
28-
`mdb_env_set_maxreaders` / `mdb_env_set_mapsize` / `mdb_env_set_maxdbs`,
29-
and `set!` / `unset!` flip individual flag bits.
27+
Before the env is open, `pagesize` maps to `mdb_env_set_pagesize`.
28+
The `[:Flags]` / `[:Readers]` / `[:MapSize]` / `[:PageSize]` / `[:DBs]`
29+
setindex! keys map to `mdb_env_set_flags` / `mdb_env_set_maxreaders` /
30+
`mdb_env_set_mapsize` / `mdb_env_set_pagesize` / `mdb_env_set_maxdbs`,
31+
and `set!` / `unset!` flip individual flag bits after open.
3032

3133
`getindex` exposes a few read-only views: `env[:Flags]`,
32-
`env[:Readers]`, and `env[:KeySize]` (the maximum key length, fixed at
33-
compile time of the bundled `LMDB_jll`).
34+
`env[:Readers]`, and `env[:KeySize]` (the maximum key length for the
35+
environment's configured page size).
3436

3537
The do-block form `Environment(f, path; kwargs...)` opens the env,
3638
calls `f(env)`, and closes on the way out:
@@ -66,6 +68,12 @@ LMDB.MDB_RDONLY)` on an open env will return `EINVAL`.
6668
on-disk size. Pick a generous power of two (say, 1 GiB or 8 GiB) up
6769
front. The on-disk file grows incrementally as data is written.
6870

71+
LMDB 1.0 also lets you choose the DB page size with `pagesize`. The
72+
default is the OS page size; larger values can allow larger keys and
73+
`MDB_DUPSORT` data items. After opening, `stat(env).psize` reports the
74+
actual page size and `env[:KeySize]` reports the corresponding maximum
75+
key size.
76+
6977
If a write txn would exceed `mapsize`, LMDB returns `MDB_MAP_FULL`. To
7078
recover, close the env, raise `mapsize`, and reopen. The database
7179
itself does not need rewriting.

docs/src/man/essentials.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ After importing LMDB.jl, you can immediately query the bundled library:
1010
julia> using LMDB
1111
1212
julia> LMDB.version()
13-
(v"0.9.33", "LMDB 0.9.33: (May 21, 2024)")
13+
v"1.0.0"
1414
```
1515

16+
LMDB 1.0 changed the on-disk file format. Databases created by LMDB
17+
0.9 must be exported with a 0.9 `mdb_dump` and imported with a 1.0
18+
`mdb_load`; LMDB 1.0 does not open 0.9 database files directly.
19+
1620
## A complete example
1721

1822
The easiest entry point is the [`LMDBDict`](@ref), a persistent

docs/src/man/lowlevel.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@ This is exactly the pattern `get(txn, dbi, key, T, default)` uses internally.
3939

4040
Bindings that don't return a status (`mdb_strerror`, `mdb_version`,
4141
`mdb_txn_id`, `mdb_cmp`, `mdb_dcmp`, `mdb_env_get_maxkeysize`,
42-
`mdb_cursor_txn`, `mdb_cursor_dbi`) and `Cvoid`-returning ones
42+
`mdb_cursor_txn`, `mdb_cursor_dbi`, `mdb_cursor_is_db`, `mdb_modload`)
43+
and `Cvoid`-returning ones
4344
(`mdb_env_close`, `mdb_dbi_close`, `mdb_txn_abort`, `mdb_txn_reset`,
44-
`mdb_cursor_close`) are left bare; there is nothing to check.
45+
`mdb_cursor_close`, `mdb_modunload`, `mdb_modsetup`) are left bare;
46+
there is nothing to check.
4547

4648
## ccall glue: passing values to `Ptr{MDB_val}`
4749

@@ -148,3 +150,8 @@ Julia wrappers deliberately don't include them:
148150
- `MDB_GET_MULTIPLE` / `MDB_NEXT_MULTIPLE` cursor ops: reachable by
149151
passing the constant directly to `LMDB.mdb_cursor_get`. Useful with
150152
`MDB_DUPFIXED` databases for batched reads.
153+
- LMDB 1.0 incremental backup, encryption/checksum hooks, and
154+
two-phase-commit helpers are exposed as raw bindings:
155+
`mdb_env_incr_dump`, `mdb_env_incr_dumpfd`, `mdb_env_incr_loadfd`,
156+
`mdb_env_set_encrypt`, `mdb_env_set_checksum`, `mdb_txn_prepare`,
157+
and `mdb_env_rollback`.

res/wrap.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const UNCHECKED_CINT = (
1616
"mdb_env_get_maxkeysize", # returns the maximum key size, not a status.
1717
"mdb_cmp", # returns the comparison result (-1/0/1).
1818
"mdb_dcmp", # idem for dup-data.
19+
"mdb_cursor_is_db", # returns a boolean int.
1920
)
2021

2122
# Replace `function $name(args)\n @ccall ...::Cint\nend` with
@@ -42,6 +43,8 @@ function postprocess!(path::AbstractString)
4243
end
4344
write(out, SubString(src, last, lastindex(src)))
4445
src = String(take!(out))
46+
src = replace(src, r"^const MDB_SIZE_MAX = SIZE_MAX$"m =>
47+
"const MDB_SIZE_MAX = typemax(mdb_size_t)")
4548
# Clang.Generators always emits `export $jll_pkg_name` next to its
4649
# `using` line; strip it so LMDB_jll stays an implementation detail.
4750
src = replace(src, r"^export LMDB_jll\n"m => "")

0 commit comments

Comments
 (0)