Module d2sqlite3

D2SQLite3 provides a thin and convenient wrapper around the SQLite C API.


  • Use reference-counted structs (Database, Statement) instead of SQLite objects pointers.
  • Run multistatement SQL code with
  • Use built-in integral types, floating point types, string, immutable(ubyte)[] and Nullable types directly: conversions to and from SQLite types is automatic and GC-safe.
  • Bind multiple values to a prepare statement with Statement.bindAll() or Statement.inject(). It's also possible to bind the fields of a struct automatically with Statement.inject().
  • Handle the results of a query as a range of Rows, and the columns of a row as a range of ColumnData (equivalent of a Variant fit for SQLite types).
  • Access the data in a result row directly, by index or by name, with the Row.peek!T() methods.
  • Make a struct out of the data of a row with!T().
  • Register D functions as SQLite callbacks, with Database.setUpdateHook() et al.
  • Create new SQLite functions, aggregates or collations out of D functions or delegate, with automatic type converions, with Database.createFunction() et al.
  • Store all the rows and columns resulting from a query at once with the cached function (sometimes useful even if not memory-friendly...).
  • Use an unlock notification when two or more connections access the same database in shared-cache mode, either using SQLite's dedicated API (sqlite_unlock_notify) or using an emulated equivalent.



// Note: exception handling is left aside for clarity.
import d2sqlite3;
import std.typecons : Nullable;

// Open a database in memory.
auto db = Database(":memory:");

// Create a table"DROP TABLE IF EXISTS person;
        CREATE TABLE person (
          id    INTEGER PRIMARY KEY,
          name  TEXT NOT NULL,
          score FLOAT

// Prepare an INSERT statement
Statement statement = db.prepare(
    "INSERT INTO person (name, score)
     VALUES (:name, :score)"

// Bind values one by one (by parameter name or index)
statement.bind(":name", "John");
statement.bind(2, 77.5);
statement.reset(); // Need to reset the statement after execution.

// Bind muliple values at the same time
statement.bindAll("John", null);

// Bind, execute and reset in one call
statement.inject("Clara", 88.1);

// Count the changes
assert(db.totalChanges == 3);

// Count the Johns in the table.
auto count = db.execute("SELECT count(*) FROM person WHERE name == 'John'")
assert(count == 2);

// Read the data from the table lazily
ResultRange results = db.execute("SELECT * FROM person");
foreach (Row row; results)
    // Retrieve "id", which is the column at index 0, and contains an int,
    // e.g. using the peek function (best performance).
    auto id = row.peek!long(0);

    // Retrieve "name", e.g. using opIndex(string), which returns a ColumnData.
    auto name = row["name"].as!string;

    // Retrieve "score", which is at index 2, e.g. using the peek function,
    // using a Nullable type
    auto score = row.peek!(Nullable!double)(2);
    if (!score.isNull)
        // ...


config(code, args) Sets a configuration option.
initialize() Manually initializes (or shuts down) SQLite.
isCompiledWith(option) Tests if an SQLite compile option is set
shutdown() Manually initializes (or shuts down) SQLite.
sourceID() Gets the library's version string (e.g. "3.8.7"), version number (e.g. 3_008_007) or source ID.
threadSafe() Tells whether SQLite was compiled with the thread-safe options.
versionNumber() Gets the library's version string (e.g. "3.8.7"), version number (e.g. 3_008_007) or source ID.
versionString() Gets the library's version string (e.g. "3.8.7"), version number (e.g. 3_008_007) or source ID.