1import * as Effect from "effect/Effect";
2import type { Connection } from "mysql2/promise";
3import { MigrationError, type SqlExecutor } from "./Format.ts";
4
5/**
6 * Adapt an open `mysql2` connection into the registry's
7 * {@link SqlExecutor}. Batches run in a transaction (MySQL DDL
8 * auto-commits regardless, matching the previous statement-by-statement
9 * behavior).
10 *
11 * The `mysql2` import is type-only — how the connection is opened stays
12 * with the database provider.
13 */
14export const makeMySQLMigrationExecutor = (
15 connection: Connection,
16): SqlExecutor => ({
17 dialect: "mysql",
18 query: (sql, params) =>
19 Effect.tryPromise({
20 try: () =>
21 connection
22 .query(sql, params ? [...params] : undefined)
23 .then(([rows]) => rows as Array<Record<string, unknown>>),
24 catch: (cause) =>
25 new MigrationError({
26 message: `mysql query failed: ${String(cause)}`,
27 cause,
28 }),
29 }),
30 batch: (statements) =>
31 Effect.tryPromise({
32 try: async () => {
33 await connection.query("START TRANSACTION");
34 try {
35 for (const statement of statements) {
36 await connection.query(statement);
37 }
38 await connection.query("COMMIT");
39 } catch (error) {
40 await connection.query("ROLLBACK").catch(() => {});
41 throw error;
42 }
43 },
44 catch: (cause) =>
45 new MigrationError({
46 message: `mysql migration batch failed: ${String(cause)}`,
47 cause,
48 }),
49 }),
50});