Detailed documentation about database drivers
Each driver is a separate Dart package that implements the AnakiDriver interface and communicates with the database via FFI to a native Rust library.
| Package | Database | Dialect | Status |
|---|---|---|---|
anaki_sqlite |
SQLite | sqlite |
✅ Ready |
anaki_postgres |
PostgreSQL | generic |
✅ Ready |
anaki_mysql |
MySQL | generic |
✅ Ready |
anaki_mssql |
SQL Server | mssql |
✅ Ready |
anaki_redis |
Redis (key-value) | —¹ | ✅ Ready |
anaki_mongodb |
MongoDB (documents) | —¹ | ✅ Ready |
anaki_oracle |
Oracle | — | 🔜 Deferred |
¹ Non-SQL clients: they do not implement AnakiDriver and are not used through AnakiDb — see section 6.5.
dependencies:
anaki_orm: ^0.1.0
anaki_sqlite: ^0.1.0import 'package:anaki_orm/anaki_orm.dart';
import 'package:anaki_sqlite/anaki_sqlite.dart';
void main() async {
// File database
final db = AnakiDb(SqliteDriver('database.db'));
// Or in-memory database
final memDb = AnakiDb(SqliteDriver(':memory:'));
await db.open();
// ... operations
await db.close();
}final driver = SqliteDriver(
'database.db',
poolConfig: PoolConfig(
minConnections: 1,
maxConnections: 10,
),
);| Feature | Value |
|---|---|
| Dialect | SqlDialect.sqlite |
| Auto-increment | INTEGER PRIMARY KEY AUTOINCREMENT |
| Pagination | LIMIT/OFFSET |
| Parameters | @name → $1, $2, ... |
| Rust crate | sqlx (feature sqlite) |
- Local development
- Integration tests
- Embedded applications
- Rapid prototyping
dependencies:
anaki_orm: ^0.1.0
anaki_postgres: ^0.1.0import 'package:anaki_orm/anaki_orm.dart';
import 'package:anaki_postgres/anaki_postgres.dart';
void main() async {
final db = AnakiDb(PostgresDriver(
host: 'localhost',
port: 5432,
database: 'myapp',
username: 'postgres',
password: 'secret',
));
await db.open();
// ... operations
await db.close();
}final driver = PostgresDriver(
host: 'localhost',
port: 5432,
database: 'myapp',
username: 'postgres',
password: 'secret',
poolConfig: PoolConfig(
minConnections: 2,
maxConnections: 20,
),
ssl: false, // or true for SSL connections
);| Feature | Value |
|---|---|
| Dialect | SqlDialect.generic |
| Auto-increment | SERIAL / BIGSERIAL |
| Pagination | LIMIT/OFFSET |
| Parameters | @name → $1, $2, ... |
| Rust crate | sqlx (feature postgres) |
# docker-compose.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
ports:
- "5432:5432"docker compose up -ddependencies:
anaki_orm: ^0.1.0
anaki_mysql: ^0.1.0import 'package:anaki_orm/anaki_orm.dart';
import 'package:anaki_mysql/anaki_mysql.dart';
void main() async {
final db = AnakiDb(MysqlDriver(
host: 'localhost',
port: 3306,
database: 'myapp',
username: 'root',
password: 'secret',
));
await db.open();
// ... operations
await db.close();
}final driver = MysqlDriver(
host: 'localhost',
port: 3306,
database: 'myapp',
username: 'root',
password: 'secret',
poolConfig: PoolConfig(
minConnections: 2,
maxConnections: 20,
),
);| Feature | Value |
|---|---|
| Dialect | SqlDialect.generic |
| Auto-increment | AUTO_INCREMENT |
| Pagination | LIMIT/OFFSET |
| Parameters | @name → ?, ?, ... |
| Rust crate | sqlx (feature mysql) |
# docker-compose.yml
services:
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: myapp
ports:
- "3306:3306"dependencies:
anaki_orm: ^0.1.0
anaki_mssql: ^0.1.0import 'package:anaki_orm/anaki_orm.dart';
import 'package:anaki_mssql/anaki_mssql.dart';
void main() async {
final db = AnakiDb(MssqlDriver(
host: 'localhost',
port: 1433,
database: 'myapp',
username: 'sa',
password: 'Anaki@Strong1',
));
await db.open();
// ... operations
await db.close();
}final driver = MssqlDriver(
host: 'localhost',
port: 1433,
database: 'myapp',
username: 'sa',
password: 'Anaki@Strong1',
trustServerCertificate: true, // for development
);| Feature | Value |
|---|---|
| Dialect | SqlDialect.mssql |
| Auto-increment | INT IDENTITY(1,1) |
| Pagination | OFFSET M ROWS FETCH NEXT N ROWS ONLY |
| Parameters | @name → @P1, @P2, ... |
| Boolean | BIT (0/1) |
| Rust crate | tiberius 0.12 |
Pagination requires ORDER BY:
// ✅ Correct
final page = await db.queryPaged(
'SELECT * FROM users ORDER BY id',
page: 1,
pageSize: 20,
);
// ❌ Error — MSSQL requires ORDER BY for OFFSET/FETCH
final page = await db.queryPaged(
'SELECT * FROM users',
page: 1,
pageSize: 20,
);Database must be created manually:
-- Execute in SQL Server before connecting
CREATE DATABASE myapp;# docker-compose.yml
services:
mssql:
image: mcr.microsoft.com/mssql/server:2022-latest
platform: linux/amd64 # Required for Mac ARM
environment:
ACCEPT_EULA: Y
SA_PASSWORD: Anaki@Strong1
ports:
- "1433:1433"Note: On ARM Macs, the container runs via Rosetta (x64 emulation).
The Oracle driver is planned but deferred due to the complexity of Oracle Instant Client (OCI).
- Rust: Structure prepared with
sibylcrate - Dart:
anaki_oraclepackage created (stub) - Blocker: Requires OCI installed on the system
- Oracle Instant Client installed
- Environment variables configured
- Oracle licensing
Redis and MongoDB reuse the same native Rust connector infrastructure (10 FFI
functions, JSON wire, per-driver dylib) but are not SQL databases, so they
ship dedicated facades instead of implementing AnakiDriver:
anaki_redis |
anaki_mongodb |
|
|---|---|---|
| Entry point | AnakiRedis |
AnakiMongoDb + MongoCollection |
| Command wire | JSON array (["SET","k","v"]) |
JSON envelope ({"op":"find",...}) |
| Transactions | ✗ (atomic pipeline() = MULTI/EXEC) |
✓ real sessions — requires replica set |
| Pagination | — | findPaged (skip/limit + countDocuments) |
Reused from anaki_orm |
exceptions, PoolConfig, RowAdapter |
exceptions, PoolConfig, RowAdapter, PagedResult |
| Escape hatch | command([...]) |
runCommand({...}) |
| Rust crate | redis (feature redis) |
mongodb (feature mongodb) |
// Redis
final redis = AnakiRedis(host: 'localhost', password: 'secret');
await redis.open();
await redis.set('k', 'v', ttl: Duration(minutes: 5));
await redis.pipeline((p) { p.incr('visits'); p.expire('visits', Duration(days: 1)); });
// MongoDB
final mongo = AnakiMongoDb(MongoDriver(host: 'localhost', database: 'app'));
await mongo.open();
final id = await mongo.collection('users').insertOne({'name': 'Ana', 'createdAt': DateTime.now()});
final ana = await mongo.collection('users').findOne({'_id': id});
await mongo.transaction((tx) async { /* multi-document, replica set required */ });Notes:
- MongoDB documents cross the FFI boundary as relaxed extended JSON; the Dart
facade converts
{"$oid"}/{"$date"}toObjectId/DateTimeautomatically (opt out withextendedJsonCodec: false). insertOnereturns the_id, generating a client-sideObjectIdwhen absent.- Redis v1 cuts: pub/sub, blocking commands, WATCH, TLS. MongoDB v1 cuts: findOneAndUpdate, mixed bulkWrite, change streams, GridFS.
- Integration environments live in
example/shelf_redis_example/(redis:7 withrequirepass) andexample/shelf_mongodb_example/(mongo:7 single-node replica set — the compose healthcheck runsrs.initiate).
All drivers implement this interface:
abstract class AnakiDriver {
SqlDialect get dialect => SqlDialect.generic;
Future<void> rawOpen();
Future<void> rawClose();
Future<List<Map<String, dynamic>>> rawQuery(String sql, Map<String, dynamic>? params);
Future<int> rawExecute(String sql, Map<String, dynamic>? params);
Future<int> rawExecuteBatch(String sql, List<Map<String, dynamic>> paramsList);
Future<void> rawBeginTransaction();
Future<void> rawCommit();
Future<void> rawRollback();
Future<bool> rawPing();
}- Implement
DatabaseConnectortrait in Rust - Add feature flag in
Cargo.toml - Register in
lib.rswith#[cfg(feature = "...")] - Create Dart package
anaki_<driver>/ - Implement
AnakiDriverin Dart - Create FFI bindings
- Add to build script
- Write integration tests
- Compile binaries for all platforms
// rust/src/newdb.rs
use crate::connector::DatabaseConnector;
use crate::error::AnakiError;
pub struct NewDbConnector {
// ...
}
#[async_trait::async_trait]
impl DatabaseConnector for NewDbConnector {
async fn open(config_json: &str) -> Result<Self, AnakiError> { ... }
async fn close(&self) -> Result<(), AnakiError> { ... }
async fn query(&self, sql: &str, params_json: &str) -> Result<Vec<...>, AnakiError> { ... }
async fn execute(&self, sql: &str, params_json: &str) -> Result<u64, AnakiError> { ... }
async fn execute_batch(&self, sql: &str, params_list_json: &str) -> Result<u64, AnakiError> { ... }
async fn begin_transaction(&self) -> Result<(), AnakiError> { ... }
async fn commit(&self) -> Result<(), AnakiError> { ... }
async fn rollback(&self) -> Result<(), AnakiError> { ... }
async fn ping(&self) -> Result<bool, AnakiError> { ... }
}// packages/anaki_newdb/lib/src/newdb_driver.dart
class NewDbDriver implements AnakiDriver {
@override
SqlDialect get dialect => SqlDialect.generic; // ... or specific
@override
Future<void> rawOpen() async { ... }
@override
Future<void> rawClose() async { ... }
// ... other methods
}Each driver package contains pre-compiled binaries in native_libs/:
anaki_sqlite/native_libs/
├── libanaki_sqlite-darwin-arm64.dylib # macOS ARM
├── libanaki_sqlite-darwin-x64.dylib # macOS Intel
├── libanaki_sqlite-linux-x64.so # Linux x64
└── anaki_sqlite-windows-x64.dll # Windows x64
# Specific driver (local)
./scripts/build_native.sh sqlite --local
# Specific driver (all platforms)
./scripts/build_native.sh sqlite
# All drivers
./scripts/build_native.sh allThe driver attempts to load the native library from multiple locations:
- Next to the executable
- Current directory
native_libs/(platform-specific name)native_libs/(generic name)- Package path (for
path:dependencies) - System default
| Feature | SQLite | PostgreSQL | MySQL | MSSQL |
|---|---|---|---|---|
| Auto-increment | INTEGER PRIMARY KEY AUTOINCREMENT |
SERIAL |
AUTO_INCREMENT |
IDENTITY(1,1) |
| Pagination | LIMIT/OFFSET |
LIMIT/OFFSET |
LIMIT/OFFSET |
OFFSET/FETCH |
| Boolean | INTEGER (0/1) |
BOOLEAN |
TINYINT(1) |
BIT |
| String concat | || |
|| |
CONCAT() |
+ |
| Current time | CURRENT_TIMESTAMP |
NOW() |
NOW() |
GETDATE() |
| IF NOT EXISTS | ✅ | ✅ | ✅ | ❌ (use IF NOT EXISTS (SELECT...)) |
ConnectionException: Failed to load native library: libanaki_sqlite.dylib
Solutions:
- Check if the binary exists in
native_libs/ - Run
./scripts/build_native.sh sqlite --local - For AOT, copy the binary next to the executable
ConnectionException: Connection refused
Solutions:
- Check if the database is running
- Confirm host/port
- Check firewall/network
QueryException: ORDER BY is mandatory for OFFSET/FETCH
Solution: Add ORDER BY to paginated queries for MSSQL.
ConnectionException: Cannot open database "myapp"
Solution: Create the database manually before connecting:
CREATE DATABASE myapp;