Native MySQL driver
dox_mysql_native is the MySQL and MariaDB driver of the Dox ORM, written in Rust.
You use it through your models and queries. It needs no extra config.
Rust (mysql_async) owns the connections and the pool.
Each connection keeps its prepared statements.
It works with MySQL 8.4 (the default caching_sha2_password login) and MariaDB 11.
New driver
This driver is new. If you see a problem, please open an issue.
Speed
A find-by-id query takes about 65 µs alone, and about 10 µs each with 32 queries in flight (AOT build, local MySQL 8.4 or MariaDB 11, Apple M-series). See Performance: database drivers.
Install
dox_orm depends on dox_mysql_native. You do not add it yourself.
Add it only to use MysqlNativePool without the ORM:
You do not need Rust. The build hook downloads a ready-built library for your system.
The package lives in its own repo: dartondox/dox-mysql-native.
When the library does not load
MySQL has no other driver in Dox. When the native library does not load,
SqlQueryBuilder.initializeWithDatabaseConfig (and every other place that
opens a MySQL connection) throws an UnsupportedError like this:
dox_mysql_native did not load: <reason>.
Dox needs it to talk to MySQL. Fix: run the app with `dart run` or `dart build cli`. ...
To fix it:
- Run the app with
dart run,dart testordart build cli. They run the build hook.dart compile exedoes not. - Or install Rust. The build hook then builds the library from source.
- When you set
lib_pathinpubspec.yaml, check that the file is there and is built for your system.
A driverFactory on the connection wins over all of this: the ORM uses your
driver.
Options
The driver reads the normal fields of the ConnectionConfig (host,
port, user, password, database, useSSL) and these keys from
extra:
| Key | Default | What it does |
|---|---|---|
maxConnectionCount |
10 |
Most connections in the pool. |
maxConnectionAge |
12 hours | A connection older than this is closed and opened again. |
preparedStatementCacheSize |
100 |
Prepared statements kept per connection. |
threads |
1 |
Rust threads for all pools of the process. Only the first pool sets it. One thread is the fastest for most apps. |
verifyCertificate |
true |
With useSSL, TLS checks the server certificate. Set it to false only for a self-signed certificate in development. |
Isolates that open a pool with the same settings share one pool in Rust.
So maxConnectionCount is for the whole process, not per isolate. You can
also give driver.pool.handle (an int) to another isolate and use
MysqlNativeDriver(MysqlNativePool.fromHandle(handle)) there.
MysqlNativeDriver comes from package:dox_orm/dox_orm.dart.
A CREATE, ALTER, RENAME or DROP makes every connection prepare its statements again. If your app runs CREATE TEMP TABLE or DROP on every request, this happens each time. Do it once at start up, not per request.
A request (pool slot, connect and query) fails after 300 s with no answer. A transaction that gets no request for 300 s is rolled back, so its connection goes back to the pool.
Use it without the ORM
MysqlNativePool pool = MysqlNativePool.open(
host: 'localhost',
user: 'root',
password: 'password',
database: 'app',
);
MysqlNativeResult result = await pool.query(
'SELECT id, name FROM users WHERE id = ?',
parameters: <Object?>[1],
);
print(result.rows); // [{id: 1, name: Ada}]
MysqlNativeResult insert = await pool.query(
'INSERT INTO users (name) VALUES (@name)',
parameters: <String, dynamic>{'name': 'Bob'},
);
print(insert.insertId); // the new AUTO_INCREMENT id
print(insert.affectedRows); // 1
int tx = await pool.begin();
await pool.execute('UPDATE users SET name = ?', parameters: <Object?>['Cy'], transaction: tx);
await pool.commit(tx); // or pool.rollback(tx)
parametersis a list for?, or a map for@name.executeis likequery, but the rows are not sent back to Dart.- A transaction runs on one connection. Its queries run in the order you send them.
Types
| MySQL / MariaDB | Dart |
|---|---|
TINYINT(1) (boolean()) |
bool |
TINYINT, SMALLINT, INT, BIGINT, YEAR |
int |
FLOAT, DOUBLE, DECIMAL |
double |
CHAR, VARCHAR, TEXT, ENUM |
String |
DATE, DATETIME, TIMESTAMP |
DateTime in UTC |
TIME |
String, like 12:34:56 |
JSON |
the decoded JSON value |
BLOB, BINARY, VARBINARY |
Uint8List |
NULL |
null |
MariaDB keeps JSON columns as LONGTEXT. So on MariaDB, TEXT that is a
JSON object or array comes back decoded.
Errors
Every error throws MysqlException. For an error from the server, code
is the MySQL error number (eg. 1062 for a duplicate key) and sqlState
is the 5 letter SQLSTATE. For other errors (eg. the server is down) both
are null. MysqlException comes from package:dox_orm/dox_orm.dart.
try {
await QueryBuilder.table('users').insert(<String, dynamic>{'email': email});
} on MysqlException catch (error) {
if (error.code == 1062) print('taken');
}
Not yet
- The
timeZone,collation,serverPublicKeyandtimeoutMskeys are not read. The connection usesutf8mb4. - A login with
sha256_password(not the default) does not work. The defaultcaching_sha2_passwordandmysql_native_passwordwork.