Skip to content

Improvements to new Julia API#67

Merged
maleadt merged 15 commits into
mainfrom
tb/julia_api
May 22, 2026
Merged

Improvements to new Julia API#67
maleadt merged 15 commits into
mainfrom
tb/julia_api

Conversation

@maleadt
Copy link
Copy Markdown
Collaborator

@maleadt maleadt commented May 22, 2026

Some renames, dead code removal, and better alignment with Base APIs.

maleadt added 15 commits May 22, 2026 15:36
The catch branch called the undefined LMDB.is_notfound, so deleting a
missing key would have crashed with UndefVarError. The test suite only
ever deleted existing keys, masking the bug.

It's also dead code: LMDB.delete!(txn, dbi, key) returns Bool and never
throws on missing. Drop the try/catch entirely, matching the no-op
shape of Base.delete!(::Dict).
HasLength() is already the default IteratorSize for any iterator that
doesn't override it (Base/generator.jl:80), so the explicit override
on LMDBDict adds nothing.
Clang.Generators emits `export $jll_pkg_name` whenever `jll_pkg_name` is
set, which leaks the C-binding dependency into LMDB's user-facing API.
Strip the export from the generated source and add a post-processing
step so it stays gone across regenerations.
`show(io::IO, err)` controls how the struct is printed in any IO
context (arrays, repr, logs), while `showerror` is what runs when an
exception is actually thrown. Overriding `show` to print "Code[..]: .."
made the struct render unusably and bypassed the canonical error path.

Reroute the formatted message through `Base.showerror` and let `show`
fall back to the default struct printing.
The previous single-method `show` always emitted multi-line output, so
printing an `Environment` inside another container (an array, a tuple,
or `repr`) produced a paragraph instead of a one-liner. Follow the
convention used by `Dict` and other Base containers: the two-argument
`show` is the compact reparseable form, and the three-argument
`MIME"text/plain"` overload is the multi-line REPL display.
These all returned a meaningless Cint(0) — a holdover from the pre-
@checked era when bindings handed back raw status codes. Replace them
with the canonical forms: `close` and `sync` return `nothing`,
`set!`/`unset!` return `env` so calls can chain.
Spell out that empty(::LMDBDict, K, V) returns an in-memory Dict, and
test the resulting copy/merge/filter behavior. The Base default already
delivers this (dict.jl:119), but writing the override makes the type
contract explicit — LMDBDict can't construct itself without a path, so
the fallback type is locked in by design rather than by accident.
Use Julia's idiomatic constructor + do-block form for every wrapper
handle:

  Transaction(env; ...) / Transaction(f, env; ...)
  DBI(txn, name; ...) / DBI(f, txn, name; ...)
  Cursor(txn, dbi) / Cursor(f, txn, dbi)

The Environment(path; ...) constructor already followed this shape;
the do-block sibling is added too. The old `start`, `create`,
`environment`, `open(txn)`, `open(env, path)`, and `open(txn, dbi)`
factory functions go away — they were redundant with the constructors
and used several different idioms for what was conceptually one
operation (allocate handle + configure + register lifetime).

Internal callers in `dictionary.jl`, the test suite, and the user
docs are updated to the new form.
`isflagset` and `walk` were exported but are rarely needed outside the
LMDB namespace: `isflagset` is a generic helper, and `walk` is mostly
subsumed by AbstractDict iteration over LMDBDict. Move them to @public
so callers reach them as `LMDB.isflagset` / `LMDB.walk`, freeing up the
short names in user code.
cursor.jl's `setup_key!` ladder was a copy of the dispatch that
`common.jl` already runs through `MDBArg`. Replace it with a single
`fill_mdbval!(dst::Ref{MDB_val}, k)` helper that reuses the canonical
`cconvert(Ptr{MDB_val}, k)` carrier — one path for building MDB_vals,
one place to add support for new key types.
Replace `tryget(txn, dbi, key, T)` with `get(txn, dbi, key, T, nothing)`
— the same Union{T,Nothing} shape, expressed through the canonical
Base.get(d, k, default) signature so there is one name for the same
operation. Callers wanting `nothing` on miss pass `nothing`; callers
wanting another sentinel pass that instead.

Internal callers in `dictionary.jl`, replace! / pop!, the test suite,
and the docs follow.
The previous commit made empty/copy silently downgrade to an in-memory
Dict. That hides the structural difference: an LMDBDict needs an
on-disk directory, so there is no path-less zero-argument form. Throw
ArgumentError instead, with a message pointing at `Dict(d)` for the
in-memory snapshot or `copy(d.env, path)` for an on-disk clone.

filter(f, d) goes through empty(d) and therefore also throws. merge
builds a Dict via the AbstractDict iteration constructor, so it
continues to work and returns the merged Dict (same shape as the user
would get from Dict(d) by hand).
Only `LMDBDict` and `LMDBError` are still exported. Everything else —
`Environment`, `Transaction`, `DBI`, `Cursor`, the env operations
(`set!`/`unset!`/`sync`/`info`/`path`/`reader_check`/`reader_list`),
the cursor primitives (`seek!`, `next!`, `prev!`, `walk`, dup
variants), and the cursor accessors (`transaction`/`database`/
`key`/`value`/`item`) — moves to `@public`. The bare names are too
generic to safely take over downstream namespaces under
`using LMDB`. Reach for them as `LMDB.Transaction`, `LMDB.seek!`, etc.

The test suite pulls a fixed list of the common names back into scope
via `using LMDB: ...` at the top of `runtests.jl` so the tests stay
readable while the public surface stays narrow.
`Database` reads better than the C-flavoured `DBI`. The struct, all
its method signatures, and every prose mention switch over. The
underlying `MDB_dbi` Cuint type (the C ABI) is unchanged; only the
Julia wrapper renames.

While here, switch the tests from bare names to the `LMDB.X`
qualification that user code actually sees after the recent export
demotion. `LMDBDict` / `LMDBError` stay unprefixed (still exported);
`LMDB.Environment`, `LMDB.Transaction`, `LMDB.Database`, `LMDB.Cursor`,
and the env operations (`LMDB.set!`, `LMDB.unset!`, `LMDB.sync`,
`LMDB.info`, `LMDB.reader_check`, `LMDB.reader_list`) gain the prefix.
The earlier `using LMDB: ...` hack in runtests.jl is gone.
Replace the slightly stiff "Get an item from a database, decoded as T"
phrasing on `get` with "Read the value at `key`, decoded as T", and
swap "whether or not f throws" on `Environment(f, path)` for the
plainer "even if f throws".
@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

❌ Patch coverage is 78.57143% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.40%. Comparing base (8e5494b) to head (ba9faa0).

Files with missing lines Patch % Lines
src/environment.jl 46.87% 17 Missing ⚠️
src/database.jl 88.00% 3 Missing ⚠️
src/error.jl 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #67      +/-   ##
==========================================
- Coverage   83.50%   82.40%   -1.10%     
==========================================
  Files           9        9              
  Lines         776      756      -20     
==========================================
- Hits          648      623      -25     
- Misses        128      133       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@maleadt maleadt merged commit 82ca91d into main May 22, 2026
8 of 10 checks passed
@maleadt maleadt deleted the tb/julia_api branch May 22, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant