mssql_driver_with_native_tls 0.5.2
mssql_driver_with_native_tls: ^0.5.2 copied to clipboard
Dart TDS 7.4 Microsoft SQL Server driver with pooling, transactions, Bulk Load, NTLM/Azure AD, and a small native OpenSSL TLS helper.
mssql_driver_with_native_tls #
A Dart-first driver for Microsoft SQL Server, built on the TDS 7.4 wire protocol. SQL Server protocol handling, authentication, queries, pooling, transactions, type encoding/decoding, Bulk Load, and related functionality are implemented in Dart. Native code is used only for the TLS transport.
The native OpenSSL helper is currently required for TLS on Windows, Linux, and Android. This is the compatibility trade-off for reliable encrypted multi-packet requests, Bulk Load, and Attention cancellation on a standard Dart SDK. Unencrypted connections continue to use only Dart and TCP.
dart pub add mssql_driver_with_native_tls
Cleartext (encrypt: false) needs nothing else. For TLS (encrypt: true),
hook/build.dart downloads the matching prebuilt OpenSSL helper from the
GitHub Release for this package version (v + pubspec version) the first
time you dart pub get / dart test / dart run. No manual copy and no
local CMake/OpenSSL install are required on supported targets:
| Target | Release asset |
|---|---|
| Linux x64 | mssql-tls-linux-x64.zip |
| Windows x64 | mssql-tls-windows-x64.zip |
Android arm64 / arm / x64 |
mssql-tls-android.zip |
Override the helper path with MSSQL_TLS_LIBRARY when needed. For offline
or git checkouts before a release exists, build locally
(bash tool/build_native.sh) — the hook prefers native/bin/... when present.
From git (mssql_native) #
Published pub.dev builds may lag this branch. Pin the branch in
pubspec.yaml:
dependencies:
mssql_driver_with_native_tls:
git:
url: https://github.com/Alexqwesa/mssql-dart.git
ref: mssql_native
dart pub get
Optional hook overrides in the app pubspec.yaml:
hooks:
user_defines:
mssql_driver_with_native_tls:
release_tag: v0.5.2 # default: v + package version
local_only: true # never download; require native/bin or dist/android
force_download: true # ignore local native/bin and re-fetch
Choosing a SQL Server package #
There are several Dart and Flutter packages for connecting to Microsoft SQL Server. They use different architectures and target different use cases.
mssql_driver_with_native_tls is a TDS driver implemented primarily in Dart. SQL Server protocol handling, authentication, queries, pooling, transactions, type encoding/decoding, Bulk Load, and related functionality are implemented by the package itself. Native code is used only for the TLS transport.
Architecture and platform comparison #
| Package | Architecture | SQL Server protocol | Native dependency | Platforms |
|---|---|---|---|---|
mssql_driver_with_native_tls |
Dart TDS driver + native TLS | Implemented in Dart | Small OpenSSL TLS helper | Windows, Linux, Android |
mssql 0.6.x |
Pure-Dart TDS driver | Implemented in Dart | None in the package; encrypted connections currently require a patched Dart SDK | Platforms supported by the patched Dart runtime |
mssql_native (Aksoyhlc) |
Flutter/Dart FFI around FreeTDS | FreeTDS | FreeTDS (bundled) | Windows, Linux, macOS, Android, iOS |
mssql_connection |
Flutter/Dart FFI wrapper around FreeTDS | FreeTDS | FreeTDS | Windows, Linux, macOS, Android, iOS |
mssql_io |
Flutter plugin around FreeTDS | FreeTDS | FreeTDS | Windows, Linux, macOS, Android, iOS; Web through a backend proxy |
mssql_connect |
Flutter SQL Server plugin | Platform implementation | Platform-specific | Android, Windows |
The main architectural difference is that mssql_driver_with_native_tls is not a Dart wrapper around FreeTDS. It implements the TDS protocol directly in Dart and uses a small native OpenSSL component only where the current Dart TLS API cannot provide the required encrypted-write semantics.
Feature comparison #
The table below compares publicly documented functionality. Not documented means that the feature is not part of the package's currently documented public API; it does not necessarily mean that the underlying native library is incapable of it.
| Feature | mssql_driver_with_native_tls |
mssql_connection |
mssql_io |
mssql_connect |
|---|---|---|---|---|
| Parameterized queries | ✅ | ✅ | ✅ | Not documented |
| Transactions | ✅ | ✅ | ✅ | Not documented |
| Connection pooling | ✅ | Not documented | Not documented | Not documented |
| Query timeout | ✅ | ✅ login timeout / reconnect | Not documented | Not documented |
| TDS Attention / query cancellation | ✅ | Not documented | Not documented | Not documented |
| Bulk insert | ✅ TDS BCP | ✅ FreeTDS BCP | ✅ | Not documented |
| Cancel active Bulk Load | ✅ | Not documented | Not documented | Not documented |
| Stored procedures / RPC | ✅ | Uses sp_executesql for parameters |
Not documented | Not documented |
| OUTPUT parameters / return status | ✅ | Not documented | Not documented | Not documented |
| Multiple result sets | ✅ | Not documented | Not documented | Not documented |
| Streaming result rows | ✅ | Not documented | Not documented | Not documented |
| SQL authentication | ✅ | ✅ | ✅ | ✅ |
| NTLMv2 authentication | ✅ | Not documented | Not documented | Not documented |
| NTLM TLS channel binding | ✅ | Not documented | Not documented | Not documented |
| Azure AD bearer / FedAuth | ✅ | Not documented | Not documented | Not documented |
| SQL Browser / named instances | ✅ | Named-instance auto-resolution not provided | Not documented | Server/instance connection documented |
| Always On read-only routing | ✅ | Not documented | Not documented | Not documented |
| Failover partner | ✅ | Not documented | Not documented | Not documented |
| Multi-subnet parallel connection | ✅ | Not documented | Not documented | Not documented |
| Session reset for pooling | ✅ | Not documented | Not documented | Not documented |
| Savepoints / isolation levels | ✅ | Basic transactions documented | Basic transactions documented | Not documented |
| SQL Server Change Tracking API | No dedicated API | Not documented | Not documented | ✅ |
| Flutter required | No | Yes / Dart+Flutter package | Yes | Yes |
| Can be used from Dart server/CLI code | ✅ | Package is also marked for Dart | Primarily Flutter | No |
| Own TDS implementation | ✅ | ❌ FreeTDS | ❌ FreeTDS | Platform-specific |
mssql_driver_with_native_tls vs mssql #
These two packages share the same TDS implementation and provide essentially the same high-level SQL Server API. The primary difference is encrypted transport.
mssql_driver_with_native_tls |
mssql 0.6.x |
|
|---|---|---|
| Dart / Flutter SDK | Standard SDK | Patched Dart SDK currently required for encrypted connections |
| TDS implementation | Dart | Dart |
| TLS transport | Native OpenSSL helper via FFI | Dart SecureSocket / RawSecureSocket with explicit TLS-fragment API |
| Package-owned native library | Yes | No |
| Encrypted multi-packet SQL / RPC | Supported | Supported |
| Attention / cancellation over TLS | Supported | Supported |
| Bulk Load over TLS | Supported | Supported |
| TLS TDS packet size | Up to 16,383 bytes | Currently up to 8,191 bytes |
Use mssql_driver_with_native_tls when you need the complete driver on a standard Dart or Flutter SDK today.
Use mssql 0.6.x when you specifically want the pure-Dart transport direction and can use the required Dart SDK patch.
When another package may be a better choice #
mssql_connection / mssql_native (Aksoyhlc) / mssql_io are a good fit when you want broad native platform coverage (including iOS/macOS) and prefer the mature FreeTDS implementation rather than a Dart implementation of TDS.
mssql_connect may be useful for Flutter applications targeting its supported platforms, particularly applications that want its SQL Server Change Tracking functionality.
mssql_driver_with_native_tls is intended for applications that want a Dart-native SQL Server API with direct control over TDS features, authentication, pooling, cancellation, Bulk Load, SQL Server-specific types, and connection behavior, while still working with an unmodified Dart SDK.
Quick start #
import 'package:mssql_driver_with_native_tls/mssql_driver_with_native_tls.dart';
final conn = await MssqlConnection.connect(
host: 'localhost',
port: 1433,
user: 'sa',
password: 'P@ssw0rd',
database: 'MyDb',
);
final result = await conn.query('SELECT id, name FROM users WHERE id = @id', {'id': 1});
print(result[0]['name']); // Alice
await conn.close();
LAN / on-prem cookbook #
Defaults favor local SQL Server / Docker Edge as well as production. Common LAN patterns:
Connection strings #
final conn = await MssqlConnection.connectFromString(
'Server=10.0.0.5,1433;Database=app;User Id=sa;Password=…;'
'Encrypt=false;TrustServerCertificate=true;App Name=my-pos;',
);
// Named instance (SQL Browser UDP 1434 when port omitted):
// Server=sql01\SQLEXPRESS;User Id=sa;Password=…;Encrypt=false;
// URL form: sqlserver://sa:…@10.0.0.5:1433?database=app&encrypt=false
User Id=DOMAIN\user opens NTLM. Pool: MssqlPoolConfig.fromConnectionString(…, max: 10).
Timeouts & identity #
final conn = await MssqlConnection.connect(
host: '10.0.0.5',
user: 'sa',
password: '…',
database: 'app',
encrypt: false, // or true + trustServerCertificate for LAN TLS
appName: 'my-pos', // sys.dm_exec_sessions.program_name
timeout: Duration(seconds: 10), // full login handshake
queryTimeout: Duration(seconds: 30), // Attention + drain on expiry
);
await conn.query('SELECT 1', const {}, Duration(seconds: 2)); // per-call
Protocol limits #
Protocol size limits are enabled by default to avoid allocating unexpectedly
large server-controlled values. Tune them for workloads that intentionally read
large LOB values, use MssqlProtocolLimits.sqlServerMaximums for documented SQL
Server ceilings, or MssqlProtocolLimits.unlimited for the old compatibility
behaviour.
final conn = await MssqlConnection.connect(
host: '10.0.0.5',
user: 'sa',
password: '…',
encrypt: false,
// Limits below are equivalent to the default: const MssqlProtocolLimits().
protocolLimits: const MssqlProtocolLimits(
maximumTokenBytes: 16 * 1024 * 1024,
maximumValueBytes: 64 * 1024 * 1024,
maximumPlpChunkBytes: 4 * 1024 * 1024,
maximumColumns: 4096,
maximumResultSets: 256,
),
);
Pool health & session reset #
final pool = MssqlPool(MssqlPoolConfig(
host: '10.0.0.5',
user: 'sa',
password: '…',
database: 'app',
encrypt: false,
validateOnAcquire: true, // default — SELECT 1; discard dead sockets
resetOnRelease: true, // default — TDS RESETCONNECTION (clears #temp / USE)
));
final conn = await pool.acquire();
try {
await conn.execute('…');
} finally {
await pool.release(conn); // async — always await
}
resetOnRelease clears session temp tables and restores the login database. Disable only if you intentionally share #temp across borrowers.
Named instances #
await MssqlConnection.connect(
host: r'sql01\SQLEXPRESS', // or host: 'sql01', instanceName: 'SQLEXPRESS'
user: 'sa',
password: '…',
encrypt: false,
);
// Explicit port skips Browser: r'sql01\SQLEXPRESS,15001'
Bulk insert & TVP #
await conn.bulkInsert('dbo.Items', ['Id', 'Name'], [
[1, 'a'],
[2, 'b'],
]);
// Destination nullability is detected automatically. To skip that metadata
// query, describe every column explicitly:
await conn.bulkInsert(
'dbo.Items',
['Id', 'Name'],
const [
[3, 'c'],
],
columnTypes: const [
BulkColumn('Id', BulkColumnType.bigInt, nullable: false),
BulkColumn('Name', BulkColumnType.nVarChar, nullable: true),
],
);
// Use an operation handle when Bulk Load must be cancellable. cancel()
// completes after SQL Server acknowledges Attention and the connection is
// reusable; result throws MssqlOperationCancelledException if cancellation wins.
final bulk = conn.startBulkInsert(
'dbo.Items',
['Id', 'Name'],
List.generate(100000, (i) => [i, 'item-$i']),
);
final result = bulk.result;
if (await bulk.transferStarted) {
await bulk.cancel();
}
try {
await result;
} on MssqlOperationCancelledException {
// The Bulk Load statement was cancelled and its response was drained.
}
// Requires: CREATE TYPE dbo.IdList AS TABLE (Id BIGINT);
await conn.query('SELECT Id FROM @ids', {
'ids': MssqlTvp(
typeName: 'dbo.IdList',
columns: [BulkColumn('Id', BulkColumnType.bigInt)],
rows: [[1], [2], [3]],
),
});
NTLM (domain SQL) #
await MssqlConnection.connectNtlm(
host: 'sql01',
domain: 'CONTOSO',
user: 'bob',
password: '…',
encrypt: true,
trustServerCertificate: true,
);
Stored procedures (OUTPUT / RETURN) #
final r = await conn.call('dbo.MyProc', {
'inVal': 5,
'outVal': MssqlOutput(0), // or MssqlOutput(null, 'nvarchar(100)')
});
print(r.returnStatus); // RETURN integer, if any
print(r.output['outVal']);
print(r.resultSets); // SELECT sets inside the proc
Diagnostics & transient retry #
conn.onInfoMessage = (info) {
print('INFO ${info.number}: ${info.message}'); // PRINT / RAISERROR < 11
};
// Pool: connectRetries default 2; optional INFO fan-out
final pool = MssqlPool(MssqlPoolConfig(
host: '10.0.0.5',
user: 'sa',
password: '…',
encrypt: false,
connectRetries: 2,
onInfoMessage: (info) => print(info.message),
));
// App-level retry for deadlocks / brief disconnects
await MssqlTransient.retry(
() => conn.query('UPDATE …'),
retries: 2,
);
Isolation & savepoints #
await conn.transaction((c) async {
await c.execute('INSERT …');
await c.savepoint('sp1');
await c.execute('UPDATE …');
await c.rollbackTo('sp1'); // outer txn still open
}, isolation: MssqlIsolationLevel.repeatableRead);
Parameters and SQL Server-specific types #
Use ordinary Dart values first. The driver infers the usual SQL Server types:
await conn.query('SELECT @id, @price, @enabled, @name, @created, @data, @none', {
'id': 42, // bigint
'price': 19.99, // float
'enabled': true, // bit
'name': 'Ada', // nvarchar
'created': DateTime.now().toUtc(), // datetime2
'data': [0xDE, 0xAD], // varbinary(max)
'none': null,
});
Use an Mssql* helper when the SQL Server type cannot be represented by a
bare Dart value, or when its exact width, encoding, precision, or legacy type
matters:
await conn.query('SELECT @g, @m, @d, @dec, @v, @day, @tod, @dt, @sd, @x, @b, @n, @c, @bin, @rv', {
'g': MssqlGuid('6F9619FF-8B86-D011-B42D-00C04FC964FF'),
'm': MssqlMoney(1234.56),
'd': MssqlDateTimeOffset(
DateTime.utc(2024, 1, 1, 4, 30),
offset: Duration(hours: 5, minutes: 30),
),
'dec': MssqlDecimal(19.99, precision: 10, scale: 2),
'v': MssqlVarchar('bob'),
'day': MssqlDate(2024, 7, 24),
'tod': MssqlTime(hour: 14, minute: 30, second: 0),
'dt': MssqlDateTime(DateTime.utc(2024, 3, 15, 10, 30)),
'sd': MssqlSmallDateTime(DateTime.utc(2024, 3, 15, 10, 30)),
'x': MssqlXml('<root/>'),
'b': MssqlVarbinary([0xDE, 0xAD], length: 16),
'n': MssqlNVarchar('lan', length: 32),
'c': MssqlNChar('ab', length: 4),
'bin': MssqlBinary([1, 2], length: 4),
'rv': MssqlRowVersion.parse('0x00000000000000FF'),
});
Always On / HA #
Connection establishment supports Always On read-only routing, parallel dialing of multi-subnet listeners, and an initial database-mirroring failover partner. This is not transparent failover for an already-running command: an in-flight connection lost during a role change must be retried by the application.
// Read-only routing via AG listener (database required)
final ro = await MssqlConnection.connect(
host: 'ag-listener',
database: 'app',
user: 'sa',
password: '…',
readOnlyIntent: true, // ApplicationIntent=ReadOnly
multiSubnetFailover: true, // parallel-dial multi-subnet listener
);
// Mirroring partner if primary is down at connect time
final conn = await MssqlConnection.connectFromString(
'Server=sql1;Failover Partner=sql2;Database=app;'
'User Id=sa;Password=…;Encrypt=true;TrustServerCertificate=true;',
);
KeepAlive + session init #
final conn = await MssqlConnection.connect(
host: '10.0.0.5',
user: 'sa',
password: '…',
encrypt: false,
keepAlive: Duration(seconds: 30), // TCP keepalive (0 = off)
sessionInitSql: 'SET XACT_ABORT ON; SET LOCK_TIMEOUT 5000;',
);
// Same knobs on the pool; SessionInitSQL re-runs after resetOnRelease
final pool = MssqlPool(MssqlPoolConfig(
host: '10.0.0.5',
user: 'sa',
password: '…',
encrypt: false,
keepAlive: Duration(seconds: 30),
sessionInitSql: 'SET XACT_ABORT ON;',
));
API reference #
MssqlConnection #
Connecting
// SQL Server authentication (username + password)
final conn = await MssqlConnection.connect(
host: 'localhost', // required
port: 1433, // optional, default 1433
user: 'sa', // required
password: 'P@ssw0rd', // required
database: 'MyDb', // optional, default ''
encrypt: true, // optional, default true; set false for local dev containers
trustServerCertificate: false, // optional, accept self-signed certs
timeout: Duration(seconds: 15), // optional, full login handshake (default 15s)
queryTimeout: Duration(seconds: 30), // optional, default query deadline
appName: 'mssql-dart', // optional, program_name in DMVs
);
// Azure AD authentication
final conn = await MssqlConnection.connectAzureAd(
host: 'server.database.windows.net',
azureAdAuth: AzureAdAuth.fromToken(token), // pre-acquired bearer token
database: 'MyDb',
trustServerCertificate: false,
);
Querying
// Returns all rows buffered in a MssqlResult
final result = await conn.query('SELECT id, name FROM users');
// With named parameters (@name syntax)
final result = await conn.query(
'SELECT * FROM orders WHERE customer = @cust AND active = @flag',
{'cust': 'Acme', 'flag': true},
);
// Access by column name or zero-based index
final name = result[0]['name']; // by name
final first = result[0].valueAt(0); // by index
final cols = result[0].columnNames; // ['id', 'name']
final values = result[0].values; // [1, 'Alice']
// Rows and counts
result.rows; // List<MssqlRow>
result.rowsAffected; // int
result.length; // row count
result.isEmpty; // bool
Executing (DML / DDL)
// Returns rows affected
final n = await conn.execute(
'INSERT INTO logs (msg) VALUES (@msg)',
{'msg': 'hello'},
);
print(n); // 1
Multiple result sets
final multi = await conn.queryMultiple('SELECT 1 AS a; SELECT 2 AS b');
final first = multi.first; // MssqlResult for first SELECT
final second = multi.second; // MssqlResult for second SELECT
final all = multi.all; // List<MssqlResult>
Streaming large result sets
await for (final row in conn.queryStream('SELECT * FROM bigTable')) {
process(row);
}
// With parameters
await for (final row in conn.queryStream(
'SELECT * FROM events WHERE date > @since',
{'since': DateTime.now().subtract(Duration(days: 7))},
)) {
print(row['event_type']);
}
Transactions
// Callback form — commits on success, rolls back on any exception
await conn.transaction((c) async {
await c.execute('INSERT INTO accounts (id, balance) VALUES (1, 100)');
await c.execute('INSERT INTO accounts (id, balance) VALUES (2, 200)');
}, isolation: MssqlIsolationLevel.serializable);
// Manual form + savepoints (SQL Server SAVE / ROLLBACK TRANSACTION)
await conn.beginTransaction(isolation: MssqlIsolationLevel.readCommitted);
try {
await conn.execute('UPDATE accounts SET balance = balance - 50 WHERE id = 1');
await conn.savepoint('after_debit');
await conn.execute('UPDATE accounts SET balance = balance + 50 WHERE id = 2');
// undo only the credit:
await conn.rollbackTo('after_debit');
await conn.commitTransaction();
} catch (_) {
await conn.rollbackTransaction();
rethrow;
}
Connection state
conn.isOpen; // bool — false after close() or a fatal error
conn.database; // String — current database (tracks USE / ENVCHANGE)
conn.initialDatabase; // String — database from login
conn.appName; // String — login program_name
await conn.resetSession(); // TDS RESETCONNECTION + SELECT 1
await conn.resetDatabase(); // USE back to initialDatabase
await conn.cancel(); // Attention cancel in-flight query
await conn.close();
MssqlPool #
A connection pool with configurable min/max, idle reaping, and acquire timeouts. Mirrors the node-mssql / tarn pool model.
Creating a pool
final pool = MssqlPool(MssqlPoolConfig(
host: 'localhost',
port: 1433,
user: 'sa',
password: 'P@ssw0rd',
database: 'MyDb',
encrypt: true,
trustServerCertificate: false,
min: 2, // minimum idle connections (default 0)
max: 10, // maximum total connections (default 10)
idleTimeout: Duration(seconds: 30), // close idle connections after (default 30s)
acquireTimeout: Duration(seconds: 15), // throw if no connection within (default 15s)
connectionTimeout: Duration(seconds: 15), // full login handshake (default 15s)
validateOnAcquire: true, // probe idle sockets (default true)
resetOnRelease: true, // TDS RESETCONNECTION (default true)
));
// Pre-warm min connections (optional)
await pool.open();
Pool observability
print(pool.stats);
// MssqlPoolStats(total=2 idle=1 inUse=1 pending=0 max=10 created=… …)
print('${pool.size} ${pool.available} ${pool.borrowed} ${pool.pending}');
pool.onEvent = (e) {
// created / destroyed / acquired / released / acquireTimeout /
// validationFailed / resetFailed
log.info('${e.kind} ${e.stats}');
};
Pool query methods
// Same signatures as MssqlConnection
final result = await pool.query('SELECT * FROM users WHERE id = @id', {'id': 1});
final n = await pool.execute('DELETE FROM tmp WHERE expired = 1');
final multi = await pool.queryMultiple('SELECT 1; SELECT 2');
await for (final row in pool.queryStream('SELECT * FROM bigTable')) {
process(row);
}
Pool transactions
await pool.transaction((conn) async {
await conn.execute('INSERT INTO orders ...');
await conn.execute('UPDATE inventory ...');
// commits on return, rolls back on throw
});
Manual acquire / release
final conn = await pool.acquire();
try {
await conn.execute('...');
} finally {
await pool.release(conn);
}
Closing the pool
await pool.close(); // closes idle connections, rejects any pending acquires
MssqlException #
All driver and server errors throw MssqlException:
try {
await conn.query('SELECT * FROM nonexistent');
} on MssqlException catch (e) {
print(e.message); // SQL Server error message
print(e.errorCode); // SQL Server error number (e.g. 208 = invalid object name)
print(e.severity); // TDS severity level (nullable int)
print(e.precedingErrors); // List<MssqlException> — earlier errors from the same batch
}
Parameters #
Named parameters use @name placeholders. Supported Dart → SQL type mappings:
| Dart type | SQL Server type |
|---|---|
int |
BIGINT |
double |
FLOAT |
bool |
BIT |
String |
NVARCHAR(MAX) or NVARCHAR |
List<int> |
VARBINARY(MAX) |
DateTime |
DATETIME2(7) |
MssqlGuid |
uniqueidentifier |
MssqlMoney / MssqlSmallMoney |
money / smallmoney |
MssqlDateTimeOffset |
datetimeoffset |
MssqlDecimal |
decimal(p,s) / numeric(p,s) |
MssqlVarchar |
varchar (Latin-1; bare String → nvarchar) |
MssqlDate |
date |
MssqlTime |
time(s) |
MssqlDateTime / MssqlSmallDateTime |
datetime / smalldatetime |
MssqlXml |
xml (bare String → nvarchar) |
MssqlVarbinary |
varbinary(n) / varbinary(max) (bare List<int> → max) |
MssqlNVarchar / MssqlNChar |
nvarchar(n|max) / nchar(n) |
MssqlBinary / MssqlRowVersion |
binary(n) / rowversion compare (binary(8)) |
MssqlTvp |
user-defined table type (… READONLY) |
MssqlOutput |
OUTPUT / INPUT-OUTPUT (with [call]) |
null |
NULL (any type) |
Supported SQL Server types (read) #
| Category | Types |
|---|---|
| Integer | TINYINT, SMALLINT, INT, BIGINT, BIT |
| Float | REAL (→ double), FLOAT (→ double) |
| Decimal | DECIMAL, NUMERIC (→ MssqlDecimal) |
| Money | MONEY (→ MssqlMoney), SMALLMONEY (→ MssqlSmallMoney) |
| String | VARCHAR, NVARCHAR, CHAR, NCHAR, TEXT, NTEXT, VARCHAR(MAX), NVARCHAR(MAX) |
| Binary | VARBINARY, BINARY, IMAGE, VARBINARY(MAX) (→ List<int>) |
| Date/Time | DATE, DATETIME, DATETIME2, SMALLDATETIME, TIME, DATETIMEOFFSET (→ DateTime) |
| GUID | UNIQUEIDENTIFIER (→ String in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx form) |
| XML | XML (→ String) |
| Misc | SQL_VARIANT (→ decoded inner value), UDT (→ raw List<int>) |
| Null | NULL for any type (→ null) |
TLS / Encryption #
TDS 7.x wraps the TLS handshake in PRELOGIN packets, then switches to raw TLS for the rest of the session. On Windows, Linux, and Android, encrypted connections use the bundled native OpenSSL transport. It serializes TLS reads and writes so multi-packet requests, Bulk Load, and Attention cancellation remain reliable. Cleartext connections continue to use Dart and TCP only.
// Production (Azure SQL, SQL Server with TLS)
final conn = await MssqlConnection.connect(
host: 'server.database.windows.net',
encrypt: true, // default true
trustServerCertificate: false, // validate cert (default false)
// Optional: custom PEM trust roots
// trustedCertificateFile: 'ca.pem',
// trustedCertificateDirectory: 'certs',
// After Always On redirect to an IP, keep validating against the AG name:
// hostNameInCertificate: 'ag-listener.contoso.local',
...
);
// Local dev container (no TLS)
final conn = await MssqlConnection.connect(
host: 'localhost',
encrypt: false,
...
);
// Local dev container with self-signed cert
final conn = await MssqlConnection.connect(
host: 'localhost',
encrypt: true,
trustServerCertificate: true,
...
);
If the server requires encryption (forceencryption=1) but the client passes
encrypt: false, connect fails immediately with a clear
MssqlException (Server requires encryption…) — it does not advertise
“not supported” and then upgrade (that path caused SQL Error 17828).
Connection-string keys: Encrypt, TrustServerCertificate,
TrustedCertificateFile (CAFile), TrustedCertificateDirectory (CAPath),
and HostNameInCertificate (also on sqlserver:// URLs). The same options
exist on MssqlPoolConfig / MssqlPoolConfig.fromConnectionString.
Requirements #
- Dart SDK >= 3.13
- SQL Server 2008 R2 or later (TDS 7.4 / protocol 0x04000074)
- Azure SQL Database / Azure SQL Edge
- Port 1433 (or custom) reachable from the Dart process
- For
encrypt: true: network access on first build sohook/build.dartcan download the platform helper (or a local build — see below)
Native TLS helper #
TLS uses a small OpenSSL-backed shared library. Published package versions download it automatically via the Dart build hook from the matching GitHub Release zip. Resolution order at runtime:
MSSQL_TLS_LIBRARYenvironment override- Code asset from
hook/build.dart(DynamicLibrary.codeAsset) - Platform linker name (Android
jniLibs, or process search path) - Checked-out
native/bin/<platform>/next to the working directory (dev/CI)
| Platform | Library | Hook source |
|---|---|---|
| Windows x64 | mssql_tls.dll |
Release mssql-tls-windows-x64.zip, or native/bin/windows-x64/ |
| Linux x64 | libmssql_tls.so |
Release mssql-tls-linux-x64.zip, or native/bin/linux-x64/ |
| Android | libmssql_tls.so |
Release mssql-tls-android.zip per ABI, or dist/android/<abi>/ |
Prebuilt artifacts #
Every push to main / mssql_native (and every tag) builds the helpers in
CI / Publish.
Tagged builds attach ZIPs (with SHA256SUMS) to
GitHub Releases — that is
what the hook downloads. Actions artifacts remain available for manual use.
Build locally #
Windows — tool/build_native.ps1
Dependencies:
- Visual Studio 2022 with the C++ desktop workload (needs
VsDevCmd.bat) - CMake ≥ 3.24
- Ninja
- OpenSSL (e.g.
choco install openssl, or any install CMake can find viaOPENSSL_ROOT_DIR)
# optional if OpenSSL is not on the default path:
# $env:OPENSSL_ROOT_DIR = 'C:\Program Files\OpenSSL-Win64'
.\tool\build_native.ps1
This configures, builds, runs the C++ TLS tests, and copies
mssql_tls.dll to native/bin/windows-x64/.
Linux
Dependencies: a C++17 toolchain, CMake ≥ 3.24, Ninja, and OpenSSL headers
(libssl-dev on Debian/Ubuntu).
cmake -S native -B build/native -G Ninja -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build/native
ctest --test-dir build/native --output-on-failure
mkdir -p native/bin/linux-x64
cp build/native/libmssql_tls.so native/bin/linux-x64/
Android
Dependencies: Android NDK r27 (or compatible), CMake, Ninja, Perl, Make, curl. The script downloads pinned OpenSSL and statically links it:
export ANDROID_NDK_HOME=/path/to/android-ndk
bash tool/build_android_native.sh
Outputs land in dist/android/<abi>/.
Testing #
See README_TESTS.md for offline, native TLS, Docker matrix, and opt-in live SQL Server testing instructions.
Limitations #
- Tested with SQL Server 2017, 2019, 2022, and 2025. Earlier versions from SQL Server 2012 onward should be protocol-compatible through TDS 7.4 but are not currently tested.
- TLS on Windows, Linux, and Android requires the native OpenSSL helper. A pure-Dart TLS fallback is not provided in 0.5.0; use 0.4.1 only when its encrypted multi-packet and Bulk Load limitations are acceptable.
- Azure AD authentication requires a bearer token supplied by the caller (e.g. obtained via
azure_identity); the driver does not fetch tokens itself. - Prepared statement handles (
sp_prepare/sp_execute) are not supported. All parameterized queries usesp_executesql, which SQL Server plan-caches by query hash, so repeated-query performance is similar in practice.