Skip to content

MySQL & MariaDB

MySQL and MariaDB share a dialect base but are two entry points with two drivers. Pick the one that matches the server you run: the generated SQL differs, and the MySQL dialect emits JSON operators MariaDB does not have.

Server Entry point Driver
MySQL 8+ uql-orm/mysql mysql2
MariaDB 10.5+ uql-orm/maria mariadb
Either, under Bun uql-orm/bunSql built in
Terminal window
npm install uql-orm mysql2 # or: mariadb
import { setQuerierPool } from 'uql-orm';
import { MySql2QuerierPool } from 'uql-orm/mysql';
// MariaDB: import { MariadbQuerierPool } from 'uql-orm/maria';
export const pool = new MySql2QuerierPool({
host: 'localhost',
user: 'app',
password: process.env.DB_PASSWORD,
database: 'app',
connectionLimit: 10,
});
setQuerierPool(pool);

Both take their driver’s own pool config verbatim, connect lazily, and close with pool.end().

The one behavioral difference worth knowing before choosing. MariaDB 10.5+ has INSERT ... RETURNING, so ids come back exact. MySQL has none: the driver reports only the first id and UQL infers the rest by incrementing it.

const ids = await pool.insertMany(User, users);

That inference holds under innodb_autoinc_lock_mode 0 or 1. MySQL 8 defaults to mode 2 (interleaved), where a concurrent insert into the same table can interleave with your statement’s allocation and leave the block non-contiguous. On a hot table, set lock mode 1 or insert row by row.

  • Tables are ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; the surrogate key is BIGINT UNSIGNED AUTO_INCREMENT.
  • Transactions use START TRANSACTION with the isolation level set on the session just before. All four levels work; the default is repeatable read.
  • Upserts compile to INSERT ... ON DUPLICATE KEY UPDATE, or INSERT IGNORE when every non-conflict column is a conflict key. Neither takes a conflict target: the server picks the unique index.
  • Arrays are not native: a string[] field is stored as JSON and queried with the JSON operators.
  • JSON paths differ: MySQL uses -> / ->>, MariaDB has neither and uses JSON_VALUE() / JSON_EXTRACT(). Emitting the right one is the main reason the entry points are separate.
  • Bound parameters cap at 65,535, so large insertMany payloads are chunked for you.

$text compiles to MATCH(...) AGAINST(...), which needs a FULLTEXT index over exactly the columns searched, in order:

import { Index } from 'uql-orm';
@Index(['title', 'body'], { type: 'fulltext' })

Migrations create it. Without it the server answers “Can’t find FULLTEXT index matching the column list”.

MariaDB 11.7+ has a native VECTOR(n) type with an inline index, so semantic search works with no extension. MySQL has no equivalent.

Both drivers stream natively: mysql2 through its result-set .stream(), mariadb through queryStream.