/* * Copyright © 2016-2025 The LmdbJava Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.lmdbjava; import static java.lang.Boolean.getBoolean; import static java.util.Objects.requireNonNull; import static org.lmdbjava.ByteBufferProxy.PROXY_OPTIMAL; import static org.lmdbjava.EnvFlags.MDB_NOSUBDIR; import static org.lmdbjava.EnvFlags.MDB_RDONLY_ENV; import static org.lmdbjava.Library.LIB; import static org.lmdbjava.Library.RUNTIME; import static org.lmdbjava.ResultCodeMapper.checkRc; import java.io.File; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import jnr.ffi.Pointer; import jnr.ffi.byref.IntByReference; import jnr.ffi.byref.PointerByReference; import org.lmdbjava.Library.MDB_envinfo; import org.lmdbjava.Library.MDB_stat; /** * LMDB environment. * * @param buffer type */ public final class Env implements AutoCloseable { /** Java system property name that can be set to disable optional checks. */ public static final String DISABLE_CHECKS_PROP = "lmdbjava.disable.checks"; /** * The default {@link Charset} used to convert DB names from a byte[] to a String or to encode a * String as a byte[]. Only used if not explicit {@link Charset} is provided. */ public static final Charset DEFAULT_NAME_CHARSET = StandardCharsets.UTF_8; /** * Indicates whether optional checks should be applied in LmdbJava. Optional checks are only * disabled in critical paths (see package-level JavaDocs). Non-critical paths have optional * checks performed at all times, regardless of this property. */ public static final boolean SHOULD_CHECK = !getBoolean(DISABLE_CHECKS_PROP); private boolean closed; private final int maxKeySize; private final boolean noSubDir; private final BufferProxy proxy; private final Pointer ptr; private final boolean readOnly; private final Path path; private final EnvFlagSet envFlagSet; private Env( final BufferProxy proxy, final Pointer ptr, final boolean readOnly, final boolean noSubDir, final Path path, final EnvFlagSet envFlagSet) { this.proxy = proxy; this.readOnly = readOnly; this.noSubDir = noSubDir; this.ptr = ptr; // cache max key size to avoid further JNI calls this.maxKeySize = LIB.mdb_env_get_maxkeysize(ptr); this.path = path; this.envFlagSet = envFlagSet; } /** * Create an {@link Env} using the {@link ByteBufferProxy#PROXY_OPTIMAL}. * * @return the environment (never null) */ public static Builder create() { return new Builder<>(PROXY_OPTIMAL); } /** * Create an {@link Env} using the passed {@link BufferProxy}. * * @param buffer type * @param proxy the proxy to use (required) * @return the environment (never null) */ public static Builder create(final BufferProxy proxy) { return new Builder<>(proxy); } /** * @param path file system destination * @param size size in megabytes * @param flags the flags for this new environment * @return env the environment (never null) * @deprecated Instead use {@link Env#create()} or {@link Env#create(BufferProxy)} *

Opens an environment with a single default database in 0664 mode using the {@link * ByteBufferProxy#PROXY_OPTIMAL}. */ @Deprecated public static Env open(final File path, final int size, final EnvFlags... flags) { return new Builder<>(PROXY_OPTIMAL).setMapSize(size, ByteUnit.MEBIBYTES).open(path, flags); } /** * Close the handle. * *

Will silently return if already closed or never opened. */ @Override public void close() { if (closed) { return; } closed = true; LIB.mdb_env_close(ptr); } /** * Copies an LMDB environment to the specified destination path. * *

This function may be used to make a backup of an existing environment. No lockfile is * created, since it gets recreated at need. * *

If this environment was created using {@link EnvFlags#MDB_NOSUBDIR}, the destination path * must be a directory that exists but contains no files. If {@link EnvFlags#MDB_NOSUBDIR} was * used, the destination path must not exist, but it must be possible to create a file at the * provided path. * *

Note: This call can trigger significant file size growth if run in parallel with write * transactions, because it employs a read-only transaction. See long-lived transactions under * "Caveats" in the LMDB native documentation. * * @param path writable destination path as described above * @param flags special options for this copy * @deprecated Use {@link Env#copy(Path, CopyFlagSet)} */ @Deprecated public void copy(final File path, final CopyFlags... flags) { requireNonNull(path); copy(path.toPath(), CopyFlagSet.of(flags)); } /** * Copies an LMDB environment to the specified destination path. * *

This function may be used to make a backup of an existing environment. No lockfile is * created, since it gets recreated at need. * *

If this environment was created using {@link EnvFlags#MDB_NOSUBDIR}, the destination path * must be a directory that exists but contains no files. If {@link EnvFlags#MDB_NOSUBDIR} was * used, the destination path must not exist, but it must be possible to create a file at the * provided path. * *

Note: This call can trigger significant file size growth if run in parallel with write * transactions, because it employs a read-only transaction. See long-lived transactions under * "Caveats" in the LMDB native documentation. * * @param path writable destination path as described above */ public void copy(final Path path) { copy(path, CopyFlagSet.EMPTY); } /** * Copies an LMDB environment to the specified destination path. * *

This function may be used to make a backup of an existing environment. No lockfile is * created, since it gets recreated at need. * *

If this environment was created using {@link EnvFlags#MDB_NOSUBDIR}, the destination path * must be a directory that exists but contains no files. If {@link EnvFlags#MDB_NOSUBDIR} was * used, the destination path must not exist, but it must be possible to create a file at the * provided path. * *

Note: This call can trigger significant file size growth if run in parallel with write * transactions, because it employs a read-only transaction. See long-lived transactions under * "Caveats" in the LMDB native documentation. * * @param path writable destination path as described above * @param flags special options for this copy */ public void copy(final Path path, final CopyFlagSet flags) { requireNonNull(path); requireNonNull(flags); validatePath(path); checkRc(LIB.mdb_env_copy2(ptr, path.toAbsolutePath().toString(), flags.getMask())); } /** * Obtain the DBI names. * *

This method is only compatible with {@link Env}s that use named databases. If an unnamed * {@link Dbi} is being used to store data, this method will attempt to return all such keys from * the unnamed database. * *

This method must not be called from concurrent threads. * * @return a list of DBI names (never null) */ public List getDbiNames() { final List result = new ArrayList<>(); // The unnamed DB is special so the names of the named DBs are held as keys in it. try (final Txn readTxn = txnRead()) { final Dbi unnamedDb = new Dbi<>(this, readTxn, null, proxy, DbiFlagSet.EMPTY); try (final Cursor cursor = unnamedDb.openCursor(readTxn)) { if (!cursor.first()) { return Collections.emptyList(); } do { final byte[] name = proxy.getBytes(cursor.key()); result.add(name); } while (cursor.next()); } } return Collections.unmodifiableList(result); } /** * Obtain the DBI names. * *

This method is only compatible with {@link Env}s that use named databases. If an unnamed * {@link Dbi} is being used to store data, this method will attempt to return all such keys from * the unnamed database. * *

This method must not be called from concurrent threads. * * @return a list of DBI names (never null) */ public List getDbiNames(final Charset charset) { final List dbiNames = getDbiNames(); return dbiNames.stream() .map(nameBytes -> Dbi.getNameAsString(nameBytes, charset)) .collect(Collectors.toList()); } /** * Set the size of the data memory map. * * @param mapSize the new size, in bytes */ public void setMapSize(final long mapSize) { if (mapSize < 0) { throw new IllegalArgumentException("Negative value; overflow?"); } checkRc(LIB.mdb_env_set_mapsize(ptr, mapSize)); } /** * Set the size of the data memory map. * * @param mapSize new map size in the units of byteUnit. * @param byteUnit The unit that mapSize is in. */ public void setMapSize(final long mapSize, final ByteUnit byteUnit) { requireNonNull(byteUnit); setMapSize(byteUnit.toBytes(mapSize)); } /** * Get the maximum size of keys and MDB_DUPSORT data we can write. * * @return the maximum size of keys. */ public int getMaxKeySize() { return maxKeySize; } /** * Return information about this environment. * * @return an immutable information object. */ public EnvInfo info() { if (closed) { throw new AlreadyClosedException(); } final MDB_envinfo info = new MDB_envinfo(RUNTIME); checkRc(LIB.mdb_env_info(ptr, info)); final long mapAddress; if (info.f0_me_mapaddr.get() == null) { mapAddress = 0; } else { mapAddress = info.f0_me_mapaddr.get().address(); } return new EnvInfo( mapAddress, info.f1_me_mapsize.longValue(), info.f2_me_last_pgno.longValue(), info.f3_me_last_txnid.longValue(), info.f4_me_maxreaders.intValue(), info.f5_me_numreaders.intValue()); } /** * Indicates whether this environment has been closed. * * @return true if closed */ public boolean isClosed() { return closed; } /** * Indicates if this environment was opened with {@link EnvFlags#MDB_RDONLY_ENV}. * * @return true if read-only */ public boolean isReadOnly() { return readOnly; } /** * Returns a builder for creating and opening a {@link Dbi} instance in this {@link Env}. * *

The flag {@link DbiFlags#MDB_CREATE} needs to be set on the builder if you need to create a * new database before opening it. * * @return A new builder instance for creating/opening a {@link Dbi}. */ public DbiBuilder createDbi() { return new DbiBuilder<>(this, proxy, readOnly); } /** * @param name name of the database (or null if no name is required) * @param flags to open the database with * @return a database that is ready to use * @deprecated Instead use {@link Env#createDbi()} *

Convenience method that opens a {@link Dbi} with a UTF-8 database name and default * {@link Comparator} that is not invoked from native code. */ @Deprecated() public Dbi openDbi(final String name, final DbiFlags... flags) { return openDbi(Dbi.getNameBytes(name), null, false, flags); } /** * @param name name of the database (or null if no name is required) * @param comparator custom comparator for cursor start/stop key comparisons. If null, LMDB's * comparator will be used. * @param flags to open the database with * @return a database that is ready to use * @deprecated Instead use {@link Env#createDbi()} *

Convenience method that opens a {@link Dbi} with a UTF-8 database name and associated * {@link Comparator} for use by {@link CursorIterable} when comparing start/stop keys. *

It is very important that the passed comparator behaves in the same way as the * comparator LMDB uses for its insertion order (for the type of data that will be stored in * the database), or you fully understand the implications of them behaving differently. * LMDB's comparator is unsigned lexicographical, unless {@link DbiFlags#MDB_INTEGERKEY} is * used. */ @Deprecated() public Dbi openDbi( final String name, final Comparator comparator, final DbiFlags... flags) { return openDbi(Dbi.getNameBytes(name), comparator, false, flags); } /** * @param name name of the database (or null if no name is required) * @param comparator custom comparator for cursor start/stop key comparisons and optionally for * LMDB to call back to. If null, LMDB's comparator will be used. * @param nativeCb whether LMDB native code calls back to the Java comparator * @param flags to open the database with * @return a database that is ready to use * @deprecated Instead use {@link Env#createDbi()} *

Convenience method that opens a {@link Dbi} with a UTF-8 database name and associated * {@link Comparator}. The comparator will be used by {@link CursorIterable} when comparing * start/stop keys as a minimum. If nativeCb is {@code true}, this comparator will also be * called by LMDB to determine insertion/iteration order. Calling back to a java comparator * may significantly impact performance. */ @Deprecated() public Dbi openDbi( final String name, final Comparator comparator, final boolean nativeCb, final DbiFlags... flags) { return openDbi(Dbi.getNameBytes(name), comparator, nativeCb, flags); } /** * @param name name of the database (or null if no name is required) * @param flags to open the database with * @return a database that is ready to use * @deprecated Instead use {@link Env#createDbi()} *

Convenience method that opens a {@link Dbi} with a default {@link Comparator} that is * not invoked from native code. */ @Deprecated() public Dbi openDbi(final byte[] name, final DbiFlags... flags) { return openDbi(name, null, false, flags); } /** * @param name name of the database (or null if no name is required) * @param comparator custom iterator comparator (or null to use LMDB default) * @param flags to open the database with * @return a database that is ready to use * @deprecated Instead use {@link Env#createDbi()} *

Convenience method that opens a {@link Dbi} with an associated {@link Comparator} that * is not invoked from native code. */ @Deprecated() public Dbi openDbi( final byte[] name, final Comparator comparator, final DbiFlags... flags) { return openDbi(name, comparator, false, flags); } /** * @param name name of the database (or null if no name is required) * @param comparator custom comparator callback (or null to use LMDB default) * @param nativeCb whether native code calls back to the Java comparator * @param flags to open the database with * @return a database that is ready to use * @deprecated Instead use {@link Env#createDbi()} *

Convenience method that opens a {@link Dbi} with an associated {@link Comparator} that * may be invoked from native code if specified. *

This method will automatically commit the private transaction before returning. This * ensures the Dbi is available in the Env. */ @Deprecated() public Dbi openDbi( final byte[] name, final Comparator comparator, final boolean nativeCb, final DbiFlags... flags) { try (Txn txn = readOnly ? txnRead() : txnWrite()) { final Dbi dbi = new Dbi<>(this, txn, name, comparator, nativeCb, proxy, DbiFlagSet.of(flags)); txn.commit(); // even RO Txns require a commit to retain Dbi in Env return dbi; } } /** * @param txn transaction to use (required; not closed) * @param name name of the database (or null if no name is required) * @param comparator custom comparator callback (or null to use LMDB default) * @param nativeCb whether native LMDB code should call back to the Java comparator * @param flags to open the database with * @return a database that is ready to use * @deprecated Instead use {@link Env#createDbi()} *

Open the {@link Dbi} using the passed {@link Txn}. *

The caller must commit the transaction after this method returns in order to retain the * Dbi in the Env. *

A {@link Comparator} may be provided when calling this method. Such comparator is * primarily used by {@link CursorIterable} instances. A secondary (but uncommon) use of the * comparator is to act as a callback from the native library if nativeCb is * true. This is usually avoided due to the overhead of native code calling back * into Java. It is instead highly recommended to set the correct {@link DbiFlags} to allow * the native library to correctly order the intended keys. *

A default comparator will be provided if null is passed as the comparator. * If a custom comparator is provided, it must strictly match the lexicographical order of * keys in the native LMDB database. *

This method (and its overloaded convenience variants) must not be called from concurrent * threads. */ @Deprecated() public Dbi openDbi( final Txn txn, final byte[] name, final Comparator comparator, final boolean nativeCb, final DbiFlags... flags) { return new Dbi<>(this, txn, name, comparator, nativeCb, proxy, DbiFlagSet.of(flags)); } /** * Return statistics about this environment. * * @return an immutable statistics object. */ public Stat stat() { if (closed) { throw new AlreadyClosedException(); } final MDB_stat stat = new MDB_stat(RUNTIME); checkRc(LIB.mdb_env_stat(ptr, stat)); return new Stat( stat.f0_ms_psize.intValue(), stat.f1_ms_depth.intValue(), stat.f2_ms_branch_pages.longValue(), stat.f3_ms_leaf_pages.longValue(), stat.f4_ms_overflow_pages.longValue(), stat.f5_ms_entries.longValue()); } /** * Flushes the data buffers to disk. * * @param force force a synchronous flush (otherwise if the environment has the MDB_NOSYNC flag * set the flushes will be omitted, and with MDB_MAPASYNC they will be asynchronous) */ public void sync(final boolean force) { if (closed) { throw new AlreadyClosedException(); } final int f = force ? 1 : 0; checkRc(LIB.mdb_env_sync(ptr, f)); } /** * @param parent parent transaction (may be null if no parent) * @param flags applicable flags (eg for a reusable, read-only transaction) * @return a transaction (never null) * @deprecated Instead use {@link Env#txn(Txn, TxnFlagSet)} *

Obtain a transaction with the requested parent and flags. */ @Deprecated public Txn txn(final Txn parent, final TxnFlags... flags) { checkNotClosed(); return new Txn<>(this, parent, proxy, TxnFlagSet.of(flags)); } /** * Obtain a transaction with the requested parent and flags. * * @param parent parent transaction (may be null if no parent) * @return a transaction (never null) */ public Txn txn(final Txn parent) { checkNotClosed(); return new Txn<>(this, parent, proxy, TxnFlagSet.EMPTY); } /** * Obtain a transaction with the requested parent and flags. * * @param parent parent transaction (may be null if no parent) * @param flags applicable flags (e.g. for a reusable, read-only transaction). If the set of flags * is used frequently it is recommended to hold a static instance of the {@link TxnFlagSet} * for re-use. * @return a transaction (never null) */ public Txn txn(final Txn parent, final TxnFlagSet flags) { checkNotClosed(); return new Txn<>(this, parent, proxy, flags); } /** * Obtain a read-only transaction. * * @return a read-only transaction */ public Txn txnRead() { checkNotClosed(); return new Txn<>(this, null, proxy, TxnFlags.MDB_RDONLY_TXN); } /** * Obtain a read-write transaction. * * @return a read-write transaction */ public Txn txnWrite() { checkNotClosed(); return new Txn<>(this, null, proxy, TxnFlagSet.EMPTY); } Pointer pointer() { return ptr; } void checkNotClosed() { if (closed) { throw new AlreadyClosedException(); } } private void validateDirectoryEmpty(final Path path) { if (!Files.exists(path)) { throw new InvalidCopyDestination("Path does not exist"); } if (!Files.isDirectory(path)) { throw new InvalidCopyDestination("Path must be a directory"); } final long fileCount = FileUtil.count(path); if (fileCount > 0) { throw new InvalidCopyDestination("Path must contain no files"); } } private void validatePath(final Path path) { if (noSubDir) { if (Files.exists(path)) { throw new InvalidCopyDestination("Path must not exist for MDB_NOSUBDIR"); } return; } validateDirectoryEmpty(path); } /** * Check for stale entries in the reader lock table. * * @return 0 on success, non-zero on failure */ public int readerCheck() { final IntByReference resultPtr = new IntByReference(); checkRc(LIB.mdb_reader_check(ptr, resultPtr)); return resultPtr.intValue(); } /** For testing use. */ EnvFlagSet getEnvFlagSet() { return envFlagSet; } @Override public String toString() { return "Env{" + "closed=" + closed + ", maxKeySize=" + maxKeySize + ", noSubDir=" + noSubDir + ", readOnly=" + readOnly + ", path=" + path + ", envFlagSet=" + envFlagSet + '}'; } /** Object has already been closed and the operation is therefore prohibited. */ public static final class AlreadyClosedException extends LmdbException { private static final long serialVersionUID = 1L; /** Creates a new instance. */ public AlreadyClosedException() { super("Environment has already been closed"); } } /** Object has already been opened and the operation is therefore prohibited. */ public static final class AlreadyOpenException extends LmdbException { private static final long serialVersionUID = 1L; /** Creates a new instance. */ public AlreadyOpenException() { super("Environment has already been opened"); } } /** * Builder for configuring and opening Env. * * @param buffer type */ public static final class Builder { static final int MAX_READERS_DEFAULT = 126; static final long MAP_SIZE_DEFAULT = ByteUnit.MEBIBYTES.toBytes(1); static final int POSIX_MODE_DEFAULT = 0664; private long mapSize = MAP_SIZE_DEFAULT; private int maxDbs = 1; private int maxReaders = MAX_READERS_DEFAULT; private boolean opened; private final BufferProxy proxy; private int mode = POSIX_MODE_DEFAULT; private final AbstractFlagSet.Builder flagSetBuilder = EnvFlagSet.builder(); Builder(final BufferProxy proxy) { requireNonNull(proxy); this.proxy = proxy; } /** * Opens the environment. * * @param path file system destination * @param mode Unix permissions to set on created files and semaphores * @param flags the flags for this new environment * @return an environment ready for use * @deprecated Instead use {@link Builder#open(Path)}, {@link Builder#setFilePermissions(int)} * and {@link Builder#setEnvFlags(EnvFlags...)}. */ @Deprecated public Env open(final File path, final int mode, final EnvFlags... flags) { setFilePermissions(mode); setEnvFlags(flags); return open(requireNonNull(path).toPath()); } /** * Opens the environment. * * @param path file system destination * @return an environment ready for use * @deprecated Instead use {@link Builder#open(Path)} */ @Deprecated public Env open(final File path) { return open(requireNonNull(path).toPath()); } /** * Opens the environment with 0664 mode. * * @param path file system destination * @param flags the flags for this new environment * @return an environment ready for use * @deprecated Instead use {@link Builder#open(Path)} and {@link * Builder#setEnvFlags(EnvFlags...)}. */ @Deprecated public Env open(final File path, final EnvFlags... flags) { setEnvFlags(flags); return open(requireNonNull(path).toPath()); } /** * Opens the environment. * * @param path file system destination * @return an environment ready for use */ public Env open(final Path path) { requireNonNull(path); if (opened) { throw new AlreadyOpenException(); } opened = true; final PointerByReference envPtr = new PointerByReference(); checkRc(LIB.mdb_env_create(envPtr)); final Pointer ptr = envPtr.getValue(); try { checkRc(LIB.mdb_env_set_mapsize(ptr, mapSize)); checkRc(LIB.mdb_env_set_maxdbs(ptr, maxDbs)); checkRc(LIB.mdb_env_set_maxreaders(ptr, maxReaders)); final EnvFlagSet flags = flagSetBuilder.build(); final boolean readOnly = flags.isSet(MDB_RDONLY_ENV); final boolean noSubDir = flags.isSet(MDB_NOSUBDIR); checkRc(LIB.mdb_env_open(ptr, path.toAbsolutePath().toString(), flags.getMask(), mode)); return new Env<>(proxy, ptr, readOnly, noSubDir, path, flags); } catch (final LmdbNativeException e) { LIB.mdb_env_close(ptr); throw e; } } /** * Sets the map size in bytes. * * @param mapSize new limit in bytes * @return the builder */ public Builder setMapSize(final long mapSize) { if (opened) { throw new AlreadyOpenException(); } if (mapSize < 0) { throw new IllegalArgumentException("Negative value; overflow?"); } this.mapSize = mapSize; return this; } /** * Sets the map size in the supplied unit. * * @param mapSize new map size in the units of byteUnit. * @param byteUnit The unit that mapSize is in. * @return the builder */ public Builder setMapSize(final long mapSize, final ByteUnit byteUnit) { requireNonNull(byteUnit); if (mapSize < 0) { throw new IllegalArgumentException("Negative value; overflow?"); } return setMapSize(byteUnit.toBytes(mapSize)); } /** * Sets the maximum number of databases (ie {@link Dbi}s permitted. * * @param dbs new limit * @return the builder */ public Builder setMaxDbs(final int dbs) { if (opened) { throw new AlreadyOpenException(); } this.maxDbs = dbs; return this; } /** * Sets the maximum number of databases permitted. * * @param readers new limit * @return the builder */ public Builder setMaxReaders(final int readers) { if (opened) { throw new AlreadyOpenException(); } this.maxReaders = readers; return this; } /** * Sets the Unix file permissions to use on created files and semaphores, e.g. {@code 0664}. If * this method is not called, the default of {@code 0664} will be used. * * @param mode Unix permissions to set on created files and semaphores * @return the builder */ public Builder setFilePermissions(final int mode) { if (opened) { throw new AlreadyOpenException(); } this.mode = mode; return this; } /** * Sets all the flags used to open this {@link Env}. * * @param envFlags The flags to use. Clears any existing flags. A null value results in no flags * being set. * @return this builder instance. */ public Builder setEnvFlags(final Collection envFlags) { flagSetBuilder.clear(); if (envFlags != null) { envFlags.stream().filter(Objects::nonNull).forEach(flagSetBuilder::addFlag); } return this; } /** * Sets all the flags used to open this {@link Env}. * * @param envFlags The flags to use. Clears any existing flags. A null value results in no flags * being set. * @return this builder instance. */ public Builder setEnvFlags(final EnvFlags... envFlags) { flagSetBuilder.clear(); if (envFlags != null) { Arrays.stream(envFlags).filter(Objects::nonNull).forEach(this.flagSetBuilder::addFlag); } return this; } /** * Sets all the flags used to open this {@link Env}. * * @param envFlagSet The flags to use. Clears any existing flags. A null value results in no * flags being set. * @return this builder instance. */ public Builder setEnvFlags(final EnvFlagSet envFlagSet) { flagSetBuilder.clear(); if (envFlagSet != null) { this.flagSetBuilder.setFlags(envFlagSet.getFlags()); } return this; } /** * Adds a single {@link EnvFlags} to any existing flags. * * @param envFlag The flag to add to any existing flags. A null value is a no-op. * @return this builder instance. */ public Builder addEnvFlag(final EnvFlags envFlag) { this.flagSetBuilder.addFlag(envFlag); return this; } /** * Adds the contents of an {@link EnvFlagSet} to any existing flags. * * @param envFlagSet The set of flags to add to any existing flags. A null value is a no-op. * @return this builder instance. */ public Builder addEnvFlags(final EnvFlagSet envFlagSet) { if (envFlagSet != null) { flagSetBuilder.addFlags(envFlagSet.getFlags()); } return this; } /** * Adds a {@link Collection} of {@link EnvFlags} to any existing flags. * * @param envFlags The {@link Collection} of flags to add to any existing flags. A null value is * a no-op. * @return this builder instance. */ public Builder addEnvFlags(final Collection envFlags) { if (envFlags != null) { flagSetBuilder.addFlags(envFlags); } return this; } } /** File is not a valid LMDB file. */ public static final class FileInvalidException extends LmdbNativeException { static final int MDB_INVALID = -30_793; private static final long serialVersionUID = 1L; FileInvalidException() { super(MDB_INVALID, "File is not a valid LMDB file"); } } /** The specified copy destination is invalid. */ public static final class InvalidCopyDestination extends LmdbException { private static final long serialVersionUID = 1L; /** * Creates a new instance. * * @param message the reason */ public InvalidCopyDestination(final String message) { super(message); } } /** Environment mapsize reached. */ public static final class MapFullException extends LmdbNativeException { static final int MDB_MAP_FULL = -30_792; private static final long serialVersionUID = 1L; MapFullException() { super(MDB_MAP_FULL, "Environment mapsize reached"); } } /** Environment maxreaders reached. */ public static final class ReadersFullException extends LmdbNativeException { static final int MDB_READERS_FULL = -30_790; private static final long serialVersionUID = 1L; ReadersFullException() { super(MDB_READERS_FULL, "Environment maxreaders reached"); } } /** Environment version mismatch. */ public static final class VersionMismatchException extends LmdbNativeException { static final int MDB_VERSION_MISMATCH = -30_794; private static final long serialVersionUID = 1L; VersionMismatchException() { super(MDB_VERSION_MISMATCH, "Environment version mismatch"); } } }