Skip to content

Native Postgres driver

dox_postgres_native is the default Postgres driver of the Dox ORM. Rust talks to the database. Your models, queries and transactions stay the same. You do not change any config.

  • Rust (tokio-postgres) owns the connections and the pool.
  • Dart sends many queries to Rust in one call. Rust sends many results back in one message.
  • Each connection keeps its prepared statements. The same SQL is not parsed again.
  • Values have the same Dart types as with the postgres package. Errors are the same ServerException classes.

Alpha

This driver is new. If you see a problem, use the Dart driver and tell us.

Speed

One find-by-id query (SELECT ... WHERE id = @id), AOT build (dart build cli), Postgres 14 on the same machine (Apple M-series), 4 connections:

Dart driver Native driver
One query at a time 95 µs 60 µs
64 queries in flight, one isolate 45 µs per query 12.5 µs per query
4 isolates, 64 in flight each, 16 connections per pool 39,500 queries/s 64,000 queries/s

The gain is biggest when many requests run at the same time, like in a web server.

Install

dox_orm depends on dox_postgres_native. You do not add it yourself. Add it only to use PgNativePool without the ORM:

dart pub add dox_postgres_native

The build hook builds the Rust library with cargo, so you need Rust installed. Ready-built libraries (like the ones dox_http_native has) are not published for this driver yet. Without Rust, the ORM still works: it uses the Dart driver and prints one warning line (see below).

Use dart run, dart test or dart build cli. dart compile exe does not run build hooks.

Native is the default

You do not change any config. The ORM picks the driver like this:

  1. driverFactory is set: the ORM uses your driver. It wins over everything below.
  2. You ask for the Dart driver (see below), or the connection uses TLS: the ORM uses the Dart driver.
  3. The native library loads: the ORM uses the native driver.
  4. The native library does not load (no library for your system, no Rust to build it, or a load error): the ORM uses the Dart driver. It prints one warning line to stderr, like this:
[dox] warning: dox_postgres_native did not load, using the Dart postgres driver (...)

Named connections (withConnection('x'), transaction(..., connection: 'x')) follow the same rules.

Use the Dart driver

For all connections, set the env var:

DOX_DB_DRIVER=dart dart run bin/server.dart

For one connection, set native: false in extra:

ConnectionConfig(
  driver: Driver.postgres,
  // ... host, user, password, database as before
  extra: <String, dynamic>{'native': false},
  debug: false,
),

Postgres with TLS (useSSL: true or extra['sslMode']) always uses the Dart driver, because the native driver has no TLS yet.

Options

The driver reads these keys from extra of the ConnectionConfig:

Key Default What it does
maxConnectionCount 10 Most connections in the pool.
timeZone UTC Time zone of the session, like the Dart driver.
applicationName none Name of the app in pg_stat_activity.
threads 1 Rust threads for all pools of the process. 0 is one thread with a single-thread runtime. Only the first pool sets it.

Each isolate that runs setup() makes its own pool. To share one pool between isolates, give driver.pool.handle (an int) to the other isolates and use PostgresNativeDriver(PgNativePool.fromHandle(handle)) there. PostgresNativeDriver comes from package:dox_orm/dox_orm.dart.

Use it without the ORM

PgNativePool pool = PgNativePool.open(
  host: 'localhost',
  user: 'postgres',
  password: 'postgres',
  database: 'postgres',
);

PgNativeResult result = await pool.query(
  'SELECT id, name FROM users WHERE id = @id',
  parameters: <String, dynamic>{'id': 1},
);
print(result.rows); // [{id: 1, name: Ada}]

int tx = await pool.begin();
await pool.query('UPDATE users SET name = @n', parameters: <String, dynamic>{'n': 'Bob'}, transaction: tx);
await pool.commit(tx); // or pool.rollback(tx)
  • parameters is a map for @name, or a list for $1, $2.
  • A transaction runs on one connection. Its queries run in the order you send them.

Types

The same as the postgres package:

Postgres Dart
int2, int4, int8 int
float4, float8 double
numeric String
text, varchar, char String
bool bool
timestamp, timestamptz, date DateTime in UTC
json, jsonb the decoded JSON value
uuid String
bytea Uint8List
arrays of most types above List
NULL null

Other types decode with the type registry of the postgres package.

Errors

A server error throws the same exception as the Dart driver: ServerException, or UniqueViolationException and ForeignKeyViolationException, with the same code, detail, constraintName and other fields. Other errors (eg. the server is down) throw PgException.

Not yet

  • SSL. A connection with useSSL: true uses the Dart driver.
  • maxConnectionAge, queryTimeout and the other extra keys of the Dart driver are not read.
  • A nested transaction() joins the outer one, like the Dart driver (no savepoints).