Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ on:
branches: [main, master]
tags: ["*"]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/heads/') }}

jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
Expand Down Expand Up @@ -38,3 +43,4 @@ jobs:
with:
files: lcov.info
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
17 changes: 14 additions & 3 deletions .github/workflows/Documenter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@ name: Documenter
on:
push:
branches: [main, master]
tags: [v*]
tags: ['*']
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ startsWith(github.ref, 'refs/heads/') }}

jobs:
Documenter:
name: Documentation
runs-on: ubuntu-latest
permissions:
contents: write
statuses: write
steps:
- uses: actions/checkout@v4
- uses: julia-actions/julia-buildpkg@latest
- uses: julia-actions/julia-docdeploy@latest
- uses: julia-actions/setup-julia@v2
with:
version: '1'
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-docdeploy@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
3 changes: 3 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ uuid = "11f193de-5e89-5f17-923a-7d207d56daf9"
authors = ["Art Wild <wildart@gmail.com>"]
version = "1.0.0"

[workspace]
projects = ["docs", "test"]

[deps]
CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82"
LMDB_jll = "6206cf0b-f360-5984-af49-5437264c140e"
Expand Down
76 changes: 38 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
# LMDB.jl

Julia bindings for [LMDB](http://www.lmdb.tech/doc/), the
Lightning Memory-Mapped Database — an embedded, memory-mapped, ACID
key-value store developed by Symas for OpenLDAP. It is small, fast, and
persists to disk while reading at near in-memory speeds.
[![CI](https://github.com/JuliaDatabases/LMDB.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/JuliaDatabases/LMDB.jl/actions/workflows/CI.yml)
[![codecov](https://codecov.io/gh/JuliaDatabases/LMDB.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/JuliaDatabases/LMDB.jl)
[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaDatabases.github.io/LMDB.jl/stable)
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaDatabases.github.io/LMDB.jl/dev)

Julia bindings for [LMDB](http://www.lmdb.tech/doc/), the Lightning
Memory-Mapped Database: An embedded, memory-mapped, ACID key-value store
developed by Symas for OpenLDAP. Small, fast, persisted to disk, and reads at
near in-memory speeds.

```julia
using Pkg; Pkg.add("LMDB")
```

## Three layers

LMDB.jl exposes three tiers, each with a clear consumer:

```
Tier 3 LMDBDict — `AbstractDict` over a single LMDB file.
Tier 2 Environment, … — Julian wrappers: handles, txns, cursors, dicts.
Tier 1 mdb_*, MDB_* — raw bindings + status-code constants.
```
## Using LMDB.jl

Tier 3 is the easy mode. Tier 2 is what most users want. Tier 1 is for
power users who need to integrate with custom data layouts or skip
allocations on hot paths — its functions auto-throw on non-zero status,
and an `unchecked_*` companion is available for callers that need to
inspect the raw status code.
LMDB.jl exposes the same database through three surfaces:

### Tier 3 — `LMDBDict`
- **High-level interface**: `LMDBDict <: AbstractDict`, an
`AbstractDict{K,V}` over a single LMDB file. Standard library
machinery (`merge!`, `filter!`, `pairs`, iteration, …) works out
of the box. Reach for this when you want a persistent `Dict`.
- **Julia wrappers**: `Environment`, `Transaction`, `DBI`, `Cursor`.
Julia-shaped wrappers around handles, transactions, and cursors,
with finalizers, `do`-block forms, etc. Use this when you want
explicit transactions.
- **C API**: `LMDB.mdb_*` and `LMDB.MDB_*`. Raw `ccall` bindings and
status-code constants. Use this when the Julia wrappers don't expose
a particular API or you want to inspect status codes directly.

`LMDBDict{K,V} <: AbstractDict{K,V}`, so the standard library does most
of the work — `merge!`, `filter!`, `pairs`, `==`, `hash`, `keys`,
`values`, lazy iteration — all come for free:
### `LMDBDict`

```julia
using LMDB
Expand All @@ -49,11 +50,7 @@ end
close(d)
```

Constructor kwargs: `mapsize`, `readers`, `dbs`, `readonly`, `rdahead`.
The env is opened with `MDB_NOTLS` so multiple read txns can coexist on
one thread — needed for interleaved reads or task-parallel access.

### Tier 2 — explicit env / txn / cursor
### Julia wrappers

```julia
using LMDB
Expand All @@ -66,18 +63,18 @@ try
put!(txn, dbi, "k1", "hello")
put!(txn, dbi, "k2", [1.0, 2.0, 3.0])

@show LMDB.tryget(txn, dbi, "k1", String) # "hello"
@show LMDB.tryget(txn, dbi, "k1", String)
@show LMDB.get(txn, dbi, "missing", String, "default")
@show LMDB.stat(txn, dbi).ms_entries # 2
@show LMDB.stat(txn, dbi).entries
end
end

# Cursor walk: zero-copy access to raw MDB_val refs.
# Cursor walk over the LMDB-owned mmap (zero-copy access).
start(env; flags = MDB_RDONLY) do txn
open(txn) do dbi
open(txn, dbi) do cur
LMDB.walk(cur) do k_ref, v_ref
println(LMDB.mbd_unpack(String, k_ref))
LMDB.walk(cur, String, String) do k, v
println(k, " => ", v)
end
end
end
Expand All @@ -87,20 +84,24 @@ finally
end
```

Status-code matchers are in `LMDBError`:
The package decodes `String`, `Vector{T}` for any bitstype `T`, and the
primitive numeric types out of the box. To plug in a custom representation,
define a `Base.read(io::IO, ::Type{T})` method; it will be picked up by `tryget`
/ `get` / `walk(f, cur, K, V)` and the cursor accessors `key`/`value`/`item`.
Status-code matchers live on `LMDBError`:

```julia
try
LMDB.get(txn, dbi, "missing", String)
catch e
e isa LMDBError && LMDB.is_notfound(e) || rethrow()
#
# treat as missing
end
```

### Tier 1 — raw bindings
### C API bindings

The bindings are `LMDB.mdb_*`; constants like `LMDB.MDB_NOTLS`,
The bindings are `LMDB.mdb_*`; constants like `LMDB.MDB_NOTLS` and
`LMDB.MDB_NOTFOUND` are public-but-unexported. Status-returning
bindings have an auto-throwing default and an `unchecked_*` companion:

Expand All @@ -114,7 +115,7 @@ LMDB.mdb_env_set_maxreaders(env, Cuint(510))
LMDB.mdb_env_set_mapsize(env, Csize_t(1 << 30))
LMDB.mdb_env_open(env, "/tmp/mydb",
LMDB.MDB_NOTLS | LMDB.MDB_NORDAHEAD,
Cushort(0o644))
LMDB.mode_t(0o644))

# Inspect the raw status code (e.g. for MDB_NOTFOUND):
ret = LMDB.unchecked_mdb_get(txn, dbi, key, val_ref)
Expand All @@ -126,4 +127,3 @@ ret == 0 || throw(LMDB.LMDBError(ret))

- LMDB upstream: <https://github.com/LMDB/lmdb>
- LMDB API docs: <http://www.lmdb.tech/doc/>
- Julia LMDB.jl issues / PRs: this repository.
181 changes: 0 additions & 181 deletions docs/Manifest.toml

This file was deleted.

3 changes: 3 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
LMDB = "11f193de-5e89-5f17-923a-7d207d56daf9"

[compat]
Documenter = "1"
Loading
Loading