From 808eeba49e0c3d75e11a9be1b4d7630b847202b8 Mon Sep 17 00:00:00 2001 From: Dominik Opyd Date: Wed, 22 Oct 2025 18:55:03 +0200 Subject: [PATCH] feat: add `mariadb` connector --- docs/2.connectors/mariadb.md | 30 ++++++++++++++ package.json | 4 ++ pnpm-lock.yaml | 21 +++++++++- src/_connectors.ts | 3 +- src/connectors/mariadb.ts | 70 +++++++++++++++++++++++++++++++++ src/types.ts | 7 +++- test/connectors/_tests.ts | 6 ++- test/connectors/mariadb.test.ts | 15 +++++++ 8 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 docs/2.connectors/mariadb.md create mode 100644 src/connectors/mariadb.ts create mode 100644 test/connectors/mariadb.test.ts diff --git a/docs/2.connectors/mariadb.md b/docs/2.connectors/mariadb.md new file mode 100644 index 0000000..bfaad9a --- /dev/null +++ b/docs/2.connectors/mariadb.md @@ -0,0 +1,30 @@ +--- +icon: simple-icons:mariadb +--- + +# MySQL + +> Connect DB0 to MariaDB Database using mariadb + +## Usage + +For this connector, you need to install [`mariadb`](https://www.npmjs.com/package/mariadb) dependency: + +:pm-install{name="mariadb"} + +Use `mariadb` connector: + +```js +import { createDatabase } from "db0"; +import mariadb from "db0/connectors/mariadb"; + +const db = createDatabase( + mariadb({ + /* options */ + }), +); +``` + +## Options + +:read-more{to="https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/main/types/share.d.ts#L603"} diff --git a/package.json b/package.json index 770d113..03187ba 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "jiti": "^2.6.1", "mlly": "^1.8.0", "mysql2": "^3.15.2", + "mariadb": "^3.4.5", "obuild": "^0.2.1", "pg": "^8.16.3", "prettier": "^3.6.2", @@ -90,6 +91,9 @@ "mysql2": { "optional": true }, + "mariadb": { + "optional": true + }, "@electric-sql/pglite": { "optional": true }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d9b9d3..c1dacbf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,6 +63,9 @@ importers: jiti: specifier: ^2.6.1 version: 2.6.1 + mariadb: + specifier: ^3.4.5 + version: 3.4.5 mlly: specifier: ^1.8.0 version: 1.8.0 @@ -1671,6 +1674,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -3010,6 +3016,10 @@ packages: resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==} engines: {node: '>= 10'} + mariadb@3.4.5: + resolution: {integrity: sha512-gThTYkhIS5rRqkVr+Y0cIdzr+GRqJ9sA2Q34e0yzmyhMCwyApf3OKAC1jnF23aSlIOqJuyaUFUcj7O1qZslmmQ==} + engines: {node: '>= 14'} + md4w@0.2.6: resolution: {integrity: sha512-CBLQ2PxVe9WA+/nndZCx/Y+1C3DtmtSeubmXTPhMIgsXtq9gVGleikREko5FYnV6Dz4cHDWm0Ea+YMLpIjP4Kw==} @@ -4990,6 +5000,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/geojson@7946.0.16': {} + '@types/json-schema@7.0.15': {} '@types/mdast@3.0.15': @@ -6157,7 +6169,6 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - optional: true iconv-lite@0.7.0: dependencies: @@ -6411,6 +6422,14 @@ snapshots: - supports-color optional: true + mariadb@3.4.5: + dependencies: + '@types/geojson': 7946.0.16 + '@types/node': 24.6.1 + denque: 2.1.0 + iconv-lite: 0.6.3 + lru-cache: 10.4.3 + md4w@0.2.6: {} mdast-util-from-markdown@0.8.5: diff --git a/src/_connectors.ts b/src/_connectors.ts index e0171c6..988a55c 100644 --- a/src/_connectors.ts +++ b/src/_connectors.ts @@ -16,7 +16,7 @@ import type { ConnectorOptions as PlanetscaleOptions } from "db0/connectors/plan import type { ConnectorOptions as PostgreSQLOptions } from "db0/connectors/postgresql"; import type { ConnectorOptions as SQLite3Options } from "db0/connectors/sqlite3"; -export type ConnectorName = "better-sqlite3" | "bun-sqlite" | "bun" | "cloudflare-d1" | "cloudflare-hyperdrive-mysql" | "cloudflare-hyperdrive-postgresql" | "libsql-core" | "libsql-http" | "libsql-node" | "libsql" | "libsql-web" | "mysql2" | "node-sqlite" | "sqlite" | "pglite" | "planetscale" | "postgresql" | "sqlite3"; +export type ConnectorName = "better-sqlite3" | "bun-sqlite" | "bun" | "cloudflare-d1" | "cloudflare-hyperdrive-mysql" | "cloudflare-hyperdrive-postgresql" | "libsql-core" | "libsql-http" | "libsql-node" | "libsql" | "libsql-web" | "mariadb" | "mysql2" | "node-sqlite" | "sqlite" | "pglite" | "planetscale" | "postgresql" | "sqlite3"; export type ConnectorOptions = { "better-sqlite3": BetterSQLite3Options; @@ -56,6 +56,7 @@ export const connectors: Record = Object.freeze({ /** alias of libsql-node */ "libsql": "db0/connectors/libsql/node", "libsql-web": "db0/connectors/libsql/web", + "mariadb": "db0/connectors/mariadb", "mysql2": "db0/connectors/mysql2", "node-sqlite": "db0/connectors/node-sqlite", /** alias of node-sqlite */ diff --git a/src/connectors/mariadb.ts b/src/connectors/mariadb.ts new file mode 100644 index 0000000..6afd17d --- /dev/null +++ b/src/connectors/mariadb.ts @@ -0,0 +1,70 @@ +import mariadb from "mariadb"; +import type { Connector, Primitive } from "db0"; +import { BoundableStatement } from "./_internal/statement.ts"; + +export type ConnectorConfig = mariadb.ConnectionConfig; + +type InternalQuery = (sql: string, params?: unknown[]) => Promise; + +export default function mysqlConnector( + opts: ConnectorConfig, +): Connector { + let _connection: mariadb.Connection | undefined; + const getConnection = async () => { + if (_connection) { + return _connection; + } + + _connection = await mariadb.createConnection({ + ...opts, + }); + + return _connection; + }; + + const query: InternalQuery = (sql, params) => + getConnection() + .then((c) => c.query(sql, params)) + .then((res) => res[0]); + + return { + name: "mariadb", + dialect: "mariadb", + getInstance: () => getConnection(), + exec: (sql) => query(sql), + prepare: (sql) => new StatementWrapper(sql, query), + dispose: async () => { + await _connection?.end?.(); + _connection = undefined; + }, + }; +} + +class StatementWrapper extends BoundableStatement { + #query: InternalQuery; + #sql: string; + + constructor(sql: string, query: InternalQuery) { + super(); + this.#sql = sql; + this.#query = query; + } + + async all(...params: Primitive[]) { + const res = await this.#query(this.#sql, params); + return Array.isArray(res) ? res : [res]; + } + + async run(...params: Primitive[]) { + const res = (await this.#query(this.#sql, params)) as mariadb.UpsertResult; + return { + success: true, + ...res, + }; + } + + async get(...params: Primitive[]) { + const res = await this.#query(this.#sql, params); + return Array.isArray(res) ? res[0] : res; + } +} diff --git a/src/types.ts b/src/types.ts index c80bcc0..c54bb56 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,7 +3,12 @@ */ export type Primitive = string | number | boolean | undefined | null; -export type SQLDialect = "mysql" | "postgresql" | "sqlite" | "libsql"; +export type SQLDialect = + | "mysql" + | "postgresql" + | "sqlite" + | "libsql" + | "mariadb"; export type Statement = { /** diff --git a/test/connectors/_tests.ts b/test/connectors/_tests.ts index 7f42d78..04858fb 100644 --- a/test/connectors/_tests.ts +++ b/test/connectors/_tests.ts @@ -40,7 +40,8 @@ export function testConnector(opts: { it("drop and create table", async () => { await db.sql`DROP TABLE IF EXISTS users`; switch (opts.dialect) { - case "mysql": { + case "mysql": + case "mariadb": { await db.sql`CREATE TABLE users (\`id\` VARCHAR(4) PRIMARY KEY, \`firstName\` TEXT, \`lastName\` TEXT, \`email\` TEXT)`; break; } @@ -53,7 +54,8 @@ export function testConnector(opts: { it("insert", async () => { switch (opts.dialect) { - case "mysql": { + case "mysql": + case "mariadb": { await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '')`; break; } diff --git a/test/connectors/mariadb.test.ts b/test/connectors/mariadb.test.ts new file mode 100644 index 0000000..49ef30f --- /dev/null +++ b/test/connectors/mariadb.test.ts @@ -0,0 +1,15 @@ +import { describe } from "vitest"; +import connector from "../../src/connectors/mariadb"; +import { testConnector } from "./_tests"; + +describe.runIf(process.env.MYSQL_URL)("connectors: mariadb.test", () => { + testConnector({ + dialect: "mariadb", + connector: connector({ + host: "localhost", + user: "test", + password: "test", + database: "db0", + }), + }); +});