Lightweight cross-platform Delphi wrapper for SQLite. Dynamically loads the platform-native SQLite library at runtime.
| Platform | Library | Calling Convention | Availability |
|---|---|---|---|
| Windows Win32 | winsqlite3.dll |
stdcall | Ships with Windows 10 1803+ |
| Windows Win64 | winsqlite3.dll |
stdcall | Ships with Windows 10 1803+ |
| Linux x64 | libsqlite3.so |
cdecl | apt install libsqlite3-0 or equivalent |
| macOS x64/ARM | libsqlite3.dylib |
cdecl | Ships with macOS |
| iOS ARM | libsqlite3.dylib |
cdecl | Ships with iOS |
| Android ARM/x64 | libsqlite.so |
cdecl | Ships with Android |
- Newer version of Delphi (uses inline variables,
PUTF8Char) - SQLite library available on the target platform (see table above)
Add Source\Delphi.SQLite3.pas to your project (ensure Source\ is in the include path for the .inc file).
uses Delphi.SQLite3;
var DB := TSQLite3.Create('mydata.db');
try
DB.ExecSQL('CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT, price REAL)');
DB.ExecSQL('INSERT INTO items (name, price) VALUES (:n, :p)', ['Widget', 9.99]);
var Q := DB.Query('SELECT id, name, price FROM items');
try
while Q.Next do
WriteLn(Q.AsInteger(0), ' | ', Q.AsString(1), ' | ', Q.AsFloat(2):0:2);
finally
Q.Free;
end;
finally
DB.Free;
end;Database connection. Opens (or creates) a SQLite database file.
constructor Create(const ADbPath: string);// DDL or DML with no result set.
procedure ExecSQL(const ASQL: string);
procedure ExecSQL(const ASQL: string; const AParams: array of const);
// Query returning a result set. Caller must free the returned object.
function Query(const ASQL: string): TSQLite3Query;
function Query(const ASQL: string; const AParams: array of const): TSQLite3Query;Return the first column of the first row. Raise ESQLite3Error if the query returns no rows.
function QueryValue(const ASQL: string): string;
function QueryValue(const ASQL: string; const AParams: array of const): string;
function QueryInt(const ASQL: string): Integer;
function QueryInt(const ASQL: string; const AParams: array of const): Integer;procedure StartTransaction;
procedure Commit;
procedure Rollback;function Changes: Integer; // rows affected by last INSERT/UPDATE/DELETE
function LastInsertRowId: Int64; // rowid of most recent INSERT
class function LibVersion: string; // e.g. '3.39.4'
class function IsAvailable: Boolean;// True if the platform SQLite library can be loadedForward-only cursor returned by TSQLite3.Query. Call Next to advance; read columns by 0-based index.
function Next: Boolean; // advance to next row; False when done
function EOF: Boolean; // True after the last row
property ColumnCount: Integer;
function ColumnName(AIndex: Integer): string;
function IndexOf(const AName: string): Integer; // -1 if not found (case-insensitive)
function AsString(AIndex: Integer): string;
function AsInteger(AIndex: Integer): Integer;
function AsInt64(AIndex: Integer): Int64;
function AsFloat(AIndex: Integer): Double;
function AsBlob(AIndex: Integer): TBytes;
function IsNull(AIndex: Integer): Boolean;Parameters use :name placeholders in SQL and array of const values passed positionally. Supported Delphi types:
| Delphi type | SQLite binding |
|---|---|
Integer |
sqlite3_bind_int |
Int64 |
sqlite3_bind_int64 |
Double / Extended |
sqlite3_bind_double |
Currency |
sqlite3_bind_double |
string |
sqlite3_bind_text (UTF-8) |
Boolean |
sqlite3_bind_int (0 or 1) |
nil |
sqlite3_bind_null |
DB.ExecSQL('INSERT INTO t (name, amount, active) VALUES (:n, :a, :f)', ['Bolt', 3.50, True]);
DB.ExecSQL('UPDATE t SET name = :n WHERE id = :id', ['Nut', 42]);
DB.ExecSQL('INSERT INTO t (optional) VALUES (:v)', [nil]); // NULLAll errors raise ESQLite3Error with the SQLite error code:
type
ESQLite3Error = class(Exception)
property ErrorCode: Integer read FErrorCode;
end;try
DB.ExecSQL('SELECT * FROM nonexistent');
except
on E: ESQLite3Error do
WriteLn('SQLite error ', E.ErrorCode, ': ', E.Message);
end;DB.StartTransaction;
try
for var I := 1 to 1000 do
DB.ExecSQL('INSERT INTO items (name) VALUES (:n)', ['Item ' + I.ToString]);
DB.Commit;
except
DB.Rollback;
raise;
end;DB.ExecSQL('INSERT INTO items (name) VALUES (:n)', ['New item']);
var NewId := DB.LastInsertRowId;
WriteLn('Inserted row ID: ', NewId);var Q := DB.Query('SELECT id, name, price FROM items');
try
var ColPrice := Q.IndexOf('price');
while Q.Next do
WriteLn(Q.AsFloat(ColPrice):0:2);
finally
Q.Free;
end;if TSQLite3.IsAvailable then
WriteLn('SQLite ', TSQLite3.LibVersion)
else
WriteLn('SQLite library not found on this platform');The wrapper uses stdcall on Windows (matching winsqlite3.dll) and cdecl on all POSIX platforms. This is handled via the Delphi.SQLite3.cc.inc include file that expands to the correct convention for the target platform.
All strings are converted to UTF-8 before passing to SQLite and converted back to Delphi string (UTF-16) on read. The conversions use UTF8String and PUTF8Char for type safety.
The library is loaded once on first use. On Windows this uses LoadLibrary / GetProcAddress; on POSIX platforms (Linux, macOS, iOS, Android) it uses dlopen / dlsym from Posix.Dlfcn. The module handle is freed in the unit finalization section.
If the library is not present, TSQLite3.Create raises ESQLite3Error. Use TSQLite3.IsAvailable to check without raising.
SQLite itself is thread-safe (serialized mode by default). However, a single TSQLite3 instance and its associated TSQLite3Query objects should be used from one thread at a time, or the caller must serialize access. For multi-threaded applications, create a separate TSQLite3 connection per thread.
A VCL application in projects/APIBackup/ that demonstrates TSQLite3 and TSQLite3Backup working together. The form has a TStringGrid showing todo entries from todo.db3, an edit control with an Add button to insert rows, and Backup/Restore buttons.
- Create -- opens (or creates)
todo.db3on startup with WAL journal mode - Read -- populates the grid from the
todostable - Insert -- adds a new row from the edit control
- Backup -- saves to
todo.backup.db3viaTSQLite3Backup.BackupToFile(single-file output, no WAL sidecars) - Restore -- loads from the backup via
TSQLite3Backup.RestoreFromFileand refreshes the grid
The backup unit (Delphi.SQLite3.Backup.pas in source/) also serves as a reference for extending TSQLite3 via raw API access. It resolves sqlite3_backup_init/step/finish/remaining/pagecount at runtime using TSQLite3.GetAPIProc and TSQLite3.Handle, without modifying the core unit.
The test suite uses DUnitX and covers 64 tests:
| Category | Tests |
|---|---|
| Availability and version | 2 |
| Open / close | 3 |
| DDL (CREATE TABLE) | 2 |
| INSERT / SELECT | 7 |
| Scalar query helpers | 5 |
| Result set navigation | 7 |
| UPDATE / DELETE | 3 |
| LastInsertRowId | 2 |
| Transactions | 2 |
| Open flags | 4 |
| Busy timeout | 2 |
| Pragmas | 6 |
| Progress handler | 3 |
| Error handling | 2 |
| Multiple queries | 1 |
| Type coercion | 2 |
| Blob read | 1 |
| NULL handling | 1 |
| Backup / restore | 9 |
Open Tests\Delphi.SQLite3.Tests.dpr in the Delphi IDE and create a project file, or build from the command line:
dcc64 -Q -B -NSSystem;System.Win;Winapi ^
-I"Source" ^
-E"Tests\Win64\Debug" -N"Tests\Win64\Debug" ^
-U"<RADStudio>\lib\Win64\release;<RADStudio>\lib\Win64\debug" ^
Tests\Delphi.SQLite3.Tests.dpr
Run:
Tests\Win64\Debug\Delphi.SQLite3.Tests.exe --exit:Continue
delphi-wrapper-sqlite3/
source/
Delphi.SQLite3.pas -- core wrapper (TSQLite3, TSQLite3Query)
Delphi.SQLite3.cc.inc -- calling convention include (stdcall/cdecl)
Delphi.SQLite3.Backup.pas -- online backup/restore via raw API access
test/
Delphi.SQLite3.Tests.dpr -- DUnitX test project (64 tests)
Delphi.SQLite3.Test.pas -- core wrapper tests
Delphi.SQLite3.Backup.Test.pas-- backup/restore tests
projects/
APIBackup/ -- VCL demo: todo list with backup/restore
This tool is part of the Continuous-Delphi ecosystem, focused on improving engineering discipline for long-lived Delphi systems.

