init method

Future<void> init(
  1. String dbPath, {
  2. LMDBInitConfig? config,
  3. LMDBFlagSet? flags,
})

Initializes a new LMDB environment at the specified path with optional configuration and flags.

The dbPath parameter specifies where the database should be created or opened. If the directory doesn't exist, it will be created automatically.

The optional config parameter allows fine-tuning of the database environment:

await db.init('/path/to/db',
  config: LMDBInitConfig(
    mapSize: 10 * 1024 * 1024,  // 10 MB mapped to memory
    maxDbs: 5,                  // Support up to 5 named databases
    mode: "0644",               // File permissions
  )
);

The optional flags parameter enables specific LMDB features:

await db.init('/path/to/db',
  flags: LMDBFlagSet()
  ..add(MDB_NOSUBDIR) // Use path as filename
  ..add(MDB_NOSYNC) // Don't sync to disk immediately
);

Common flag combinations are available as presets:

await db.init('/path/to/db', flags: LMDBFlagSet.readOnly);
await db.init('/path/to/db', flags: LMDBFlagSet.highPerformance);

If no config is provided, default values will be used:

  • mapSize: Minimum allowed size (typically 10MB)
  • maxDbs: 1 (single unnamed database)
  • mode: "0664" (rw-rw-r--)

Throws StateError if:

  • Database is already initialized (call close first)
  • Database is closed (create a new instance)

Throws LMDBException if:

  • Environment creation fails (insufficient permissions, invalid path)
  • Map size setting fails (invalid size)
  • Environment opening fails (file system issues, incompatible flags)

Example usage:

final db = LMDB();

// Basic initialization
await db.init('/path/to/db');

// With custom configuration
await db.init('/path/to/db',
  config: LMDBInitConfig(mapSize: 1024 * 1024 * 1024), // 1GB
  flags: LMDBFlagSet()..add(MDB_NOSUBDIR)
);

// Don't forget to close when done
db.close();

Implementation

Future<void> init(
  String dbPath, {
  LMDBInitConfig? config,
  LMDBFlagSet? flags,
}) async {
  if (_env != null) {
    close();
  }

  final effectiveFlags = flags ?? LMDBFlagSet.defaultFlags;
  final effectiveConfig = config ??
      LMDBInitConfig(
        mapSize: LMDBConfig.minMapSize,
        maxDbs: _defaultMaxDbs,
        mode: _defaultMode,
      );

  // Determine if we're in NOSUBDIR mode
  if (effectiveFlags.contains(MDB_NOSUBDIR)) {
    // For NOSUBDIR mode, ensure parent directory exists
    final parentDir = Directory(path.dirname(dbPath));
    if (!parentDir.existsSync()) {
      parentDir.createSync(recursive: true);
    }
  } else {
    // Normal mode: create directory if it doesn't exist
    final dir = Directory(dbPath);
    if (!dir.existsSync()) {
      dir.createSync(recursive: true);
    }
  }

  return _withAllocated<void, Pointer<MDB_env>>((envPtr) {
    final result = _lib.mdb_env_create(envPtr);
    if (result != 0) {
      throw LMDBException('Failed to create environment', result);
    }

    _env = envPtr.value;

    try {
      final setSizeResult = _lib.mdb_env_set_mapsize(
        env,
        effectiveConfig.mapSize,
      );

      if (setSizeResult != 0) {
        throw LMDBException('Failed to set map size', setSizeResult);
      }

      if (effectiveConfig.maxDbs > 1) {
        final setDbsResult = _lib.mdb_env_set_maxdbs(
          env,
          effectiveConfig.maxDbs,
        );

        if (setDbsResult != 0) {
          throw LMDBException('Failed to set max DBs', setDbsResult);
        }
      }

      final pathPtr = dbPath.toNativeUtf8();
      try {
        final openResult = _lib.mdb_env_open(
          env,
          pathPtr.cast(),
          effectiveFlags.value,
          effectiveConfig.modeAsInt,
        );

        if (openResult != 0) {
          throw LMDBException('Failed to open environment', openResult);
        }
      } finally {
        calloc.free(pathPtr);
      }
    } catch (e) {
      _lib.mdb_env_close(_env!);
      _env = null;
      rethrow;
    }
  }, calloc<Pointer<MDB_env>>());
}