/** * Postgres + MySQL adapters built on Bun's native SQL client (`Bun.SQL`) — no * external driver dependency. Both speak the same `Driver` interface; only the * dialect (and thus the DDL types + placeholder style) differ. * * `Bun.SQL` pools connections, so transactions use its managed `begin(fn)` to * keep BEGIN/…/COMMIT on one reserved connection. */ import type { Dialect } from "../sql.ts"; import type { Driver, Row, TxHandle } from "../driver.ts"; interface BunSqlClient { unsafe(query: string, params?: unknown[]): Promise; begin(fn: (tx: BunSqlClient) => Promise): Promise; close(): Promise; } interface ExecMeta { count?: number; affectedRows?: number; lastInsertRowid?: number | bigint; insertId?: number | bigint; } function runnerFor(client: BunSqlClient): TxHandle { return { async query(sql, params = []): Promise { const rows = (await client.unsafe(sql, params)) as Iterable; return Array.from(rows); }, async exec(sql, params = []) { const meta = (await client.unsafe(sql, params)) as ExecMeta; const id = meta.lastInsertRowid ?? meta.insertId; return { changes: Number(meta.affectedRows ?? meta.count ?? 0), lastInsertId: id != null ? Number(id) : undefined, }; }, }; } /** Create a Bun.sql-backed driver for the given connection URL + dialect. */ export function bunSql(url: string, dialect: Dialect): Driver { const Ctor = (Bun as unknown as { SQL: new (u: string) => BunSqlClient }).SQL; const client = new Ctor(url); const runner = runnerFor(client); return { dialect, query: runner.query, exec: runner.exec, transaction(fn) { return client.begin((tx) => fn(runnerFor(tx))); }, close() { return client.close(); }, }; } /** PostgreSQL adapter (`postgres://user:pass@host:5432/db`). Placeholders: `$N`. */ export function postgres(url: string): Driver { return bunSql(url, "postgres"); } /** MySQL adapter (`mysql://user:pass@host:3306/db`). Placeholders: `?`. */ export function mysql(url: string): Driver { return bunSql(url, "mysql"); }