-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Johan
committed
Aug 23, 2023
1 parent
ccd43a4
commit 053d43c
Showing
9 changed files
with
412 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
migrated from https://github.com/nexys-system/sql-migrations/blob/master/README.md | ||
|
||
# SQL-Migrations | ||
|
||
[![npm version](https://badge.fury.io/js/@nexys%2Fsql-migrations.svg)](https://www.npmjs.com/package/@nexys/sql-migrations) | ||
[![npm version](https://img.shields.io/npm/v/@nexys%2Fsql-migrations.svg)](https://www.npmjs.com/package/@nexys/sql-migrations) | ||
[![Build and Test Package](https://github.com/nexys-system/sql-migrations/actions/workflows/yarn.yml/badge.svg)](https://github.com/nexys-system/sql-migrations/actions/workflows/yarn.yml) | ||
[![Build and Test Package and (publish)](https://github.com/nexys-system/sql-migrations/actions/workflows/publish.yml/badge.svg)](https://github.com/nexys-system/sql-migrations/actions/workflows/publish.yml) | ||
[![Code style](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io/) | ||
[![Bundlephobia](https://badgen.net/bundlephobia/min/@nexys/sql-migrations)](https://bundlephobia.com/result?p=@nexys/sql-migrations) | ||
|
||
Migrations for MySQL. Same interfaces and compatibility with [flyway](https://flywaydb.org/) | ||
|
||
## Get started | ||
|
||
- Install `yarn` | ||
- Build`yarn build` | ||
- Test `yarn test` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import * as I from "./index"; | ||
|
||
test("import/exports", () => { | ||
expect(typeof I.Migrations).toEqual("object"); | ||
expect(typeof I.Utils).toEqual("object"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * as Migrations from "./migrations"; | ||
export * as Utils from "./utils"; | ||
export * as Type from "./type"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { | ||
OkPacket, | ||
ResultSetHeader, | ||
RowDataPacket, | ||
FieldPacket, | ||
} from "mysql2/promise"; | ||
|
||
//import * as T from "@nexys/sql-migrations/dist/type"; | ||
//import * as U from "@nexys/sql-migrations/dist/utils"; | ||
import * as T from "./type"; | ||
import * as U from "./utils"; | ||
|
||
type Response = [ | ||
OkPacket | ResultSetHeader | RowDataPacket[] | RowDataPacket[][] | OkPacket[], | ||
FieldPacket[] | ||
]; | ||
|
||
interface SQL { | ||
execQuery: (query: string) => Promise<Response>; | ||
} | ||
|
||
// manages migration | ||
// inspiration from flyway - https://flywaydb.org/ | ||
export const runMigrations = async ( | ||
migrations: T.Migration[], | ||
s: SQL | ||
): Promise<T.MigrationRow[]> => { | ||
U.checkSequence(migrations); | ||
// create table if not exists | ||
//console.log(createMigrationTable); | ||
await s.execQuery(U.createMigrationTable); | ||
|
||
// get all migrations | ||
const [r] = await s.execQuery(U.getMigrations); | ||
const y = r as RowDataPacket[] as T.MigrationRow[]; | ||
|
||
const { installed_rank: lastRank } = U.getLastRow(y); | ||
|
||
const pRows = migrations.map(async (migration, i) => { | ||
const version = U.toVersion(migration.version, migration.idx); | ||
const checksum = U.getChecksum(migration.sql); | ||
|
||
// find previous migration with same version and compare checksums | ||
if (U.findPreviousMigrations(version, checksum, y)) { | ||
return; | ||
} | ||
|
||
const t1 = new Date().getTime(); | ||
const [rm] = await s.execQuery(migration.sql); | ||
const t2 = new Date().getTime(); | ||
|
||
const success: number = getSuccess(rm as OkPacket | OkPacket[]); | ||
|
||
const row: T.MigrationRow = U.migrationToRow( | ||
migration.name, | ||
version, | ||
t2 - t1, | ||
success, | ||
checksum, | ||
lastRank + i + 1 | ||
); | ||
|
||
return row; | ||
}); | ||
|
||
const rawRows = await Promise.all(pRows); | ||
|
||
// note here that we compare raw rows since some can be empyt because the migrations were applied earlier | ||
if (rawRows.length !== migrations.length) { | ||
throw "something went wrong while applying migrations"; | ||
} | ||
|
||
const rows: T.MigrationRow[] = rawRows.filter(isNotNull); | ||
|
||
if (rows.length === 0) { | ||
return []; | ||
} | ||
|
||
// enter result in flyway table | ||
const sql = U.migrationsToSQL(rows); | ||
const [rm] = await s.execQuery(sql); | ||
console.log(rm); | ||
|
||
return rows; | ||
}; | ||
|
||
/** | ||
* @note more information on serverStatus: https://github.com/mysqljs/mysql/issues/745 and https://dev.mysql.com/doc/internals/en/status-flags.html | ||
* @param rm: can be an array or a scalar | ||
* @returns the serverstatus of the last call | ||
*/ | ||
const getSuccess = (rm: OkPacket | OkPacket[]): number => { | ||
// if array return the last one | ||
if (Array.isArray(rm)) { | ||
const l = rm.length; | ||
return getSuccess(rm[l - 1]); | ||
} | ||
|
||
return rm.serverStatus; | ||
}; | ||
|
||
const isNotNull = <A>(x: A | null | undefined): x is A => | ||
x !== null && x !== undefined; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export interface Migration { | ||
name: string; | ||
sql: string; | ||
version: number; | ||
idx: number; | ||
} | ||
|
||
export interface MigrationRow { | ||
checksum: number; | ||
description: string; | ||
execution_time: number; | ||
installed_by: string; | ||
installed_on: string; | ||
installed_rank: number; | ||
script: string; | ||
success: number; | ||
type: string; | ||
version: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import * as M from "./utils"; | ||
|
||
describe("check sequence", () => { | ||
test("right seq", () => { | ||
const s = [{ version: 0, idx: 1 }]; | ||
|
||
expect(M.checkSequence(s)).toEqual(undefined); | ||
}); | ||
|
||
test("right seq2", () => { | ||
const s = [ | ||
{ version: 0, idx: 1 }, | ||
{ version: 0, idx: 2 }, | ||
{ version: 1, idx: 1 }, | ||
{ version: 1, idx: 3 }, | ||
{ version: 2, idx: 2 }, | ||
]; | ||
|
||
expect(M.checkSequence(s)).toEqual(undefined); | ||
}); | ||
|
||
test("wrong versions", () => { | ||
const s = [ | ||
{ version: 0, idx: 1 }, | ||
{ version: 0, idx: 2 }, | ||
{ version: 2, idx: 1 }, | ||
{ version: 1, idx: 3 }, | ||
{ version: 2, idx: 2 }, | ||
]; | ||
|
||
try { | ||
M.checkSequence(s); | ||
} catch (err) { | ||
expect(typeof err).toEqual("object"); | ||
} | ||
}); | ||
}); | ||
|
||
test("to version", () => { | ||
expect(M.toVersion(2, 4)).toEqual("2.4"); | ||
}); | ||
|
||
test("to script", () => { | ||
expect(M.toScript("2.4", "myname")).toEqual("v2_4__myname.sql"); | ||
}); | ||
|
||
test("migration to sql", () => { | ||
const row = M.migrationToRow( | ||
"myname", | ||
"2.3", | ||
123, | ||
1, | ||
1234567, | ||
2, | ||
"admin", | ||
new Date(2021, 12, 1, 13, 45) | ||
); | ||
|
||
expect(M.migrationsToSQL([row])).toEqual( | ||
"INSERT INTO `flyway_schema_history` (`installed_rank`, `version`, `description`, `type`, `script`, `checksum`, `installed_by`, `installed_on`, `execution_time`, `success`) VALUES (2, 2.3, 'myname', 'SQL', 'v2_3__myname.sql', 1234567, 'admin', '2022-01-01 13:45:00', 123, 1);" | ||
); | ||
}); | ||
|
||
test("checksum", () => { | ||
const cs = -1064643516; | ||
const s = | ||
"CREATE TABLE `user` (`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, `uuid` VARCHAR(64) NOT NULL, `first_name` VARCHAR(512) NOT NULL, `last_name` VARCHAR(512) NOT NULL, `email` VARCHAR(512) NOT NULL, `status_id` BIGINT NOT NULL, `log_date_added` DATETIME NOT NULL DEFAULT NOW(), `instance_id` BIGINT NOT NULL, `lang` VARCHAR(512) NOT NULL);"; | ||
expect(M.getChecksum(s)).toEqual(cs); | ||
}); | ||
|
||
describe("find previous migrations", () => { | ||
const y = [{ version: "2.1", checksum: 123 }]; | ||
|
||
test("does not exist", () => { | ||
expect(M.findPreviousMigrations("2.2", 23, y)).toEqual(false); | ||
}); | ||
|
||
test("already exists", () => { | ||
expect(M.findPreviousMigrations("2.1", 123, y)).toEqual(true); | ||
}); | ||
|
||
test("already exists but different checksum", () => { | ||
try { | ||
M.findPreviousMigrations("2.1", 43, y); | ||
} catch (err) { | ||
expect(typeof err).toEqual("object"); | ||
} | ||
}); | ||
}); | ||
|
||
test("get last row", () => { | ||
expect(M.getLastRow([])).toEqual({ installed_rank: 0 }); | ||
expect(M.getLastRow([{ installed_rank: 1 }, { installed_rank: 2 }])).toEqual({ | ||
installed_rank: 2, | ||
}); | ||
}); |
Oops, something went wrong.