Skip to content

Commit

Permalink
statements sortBy
Browse files Browse the repository at this point in the history
  • Loading branch information
mmkal committed Apr 1, 2024
1 parent 76e8f7f commit d5a6f5d
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 52 deletions.
11 changes: 11 additions & 0 deletions packages/migra/src/statements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ export class Statements {
return this.array[Symbol.iterator]()
}

/** Use this to reorder statements. The score function receives the statement, the original index and the full statement array (like `.map`). */
sortBy(fn: (statement: string, index: number, array: string[]) => string | number): void {
this.array = this.array
.map((s, i, arr) => ({
value: s,
score: fn(s, i, arr),
}))
.sort((a, b) => (a.score < b.score ? -1 : a.score > b.score ? 1 : 0))
.map(x => x.value)
}

toJSON() {
return this.array
}
Expand Down
15 changes: 3 additions & 12 deletions packages/migrator/src/migrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,18 +377,9 @@ export class Migrator extends umzug.Umzug<MigratorContext> {
* class Migrator extends Base {
* async runMigra() {
* const migration = await super.runMigra()
* const ordering = Object.fromEntries(
* migration.statements.array.map((s, i, array) => {
* if (s.match(/create type .*my_type/)) {
* const insertBefore = array.findIndex(other => other.match(/create table .*my_table/))
* return [s, insertBefore - 1]
* }
* return [s, i]
* })
* )
*
* migration.statements.array.sort((a, b) => ordering[a] - ordering[b])
*
* migration.statements.sortBy((s, i, array) => {
* return s.match(/create type/) ? Math.min(i, array.findIndex(other => other.match(/create table/)) - 1) : i
* })
* return migration
* }
* }
Expand Down
72 changes: 32 additions & 40 deletions packages/migrator/test/definitions.test.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,51 @@
import {fsSyncer} from 'fs-syncer'
import * as path from 'path'
import {describe, expect, test, beforeEach} from 'vitest'
import {describe, expect, test} from 'vitest'
import {Migrator as Base} from './migrator'
import {getPoolHelper} from './pool-helper'

class Migrator extends Base {
async runMigra() {
const migration = await super.runMigra()
migration.statements.array = migration.statements.array
.map((s, i, arr) => {
if (/create type .*address/.test(s)) {
i = arr.findIndex(other => other.match(/create table .*patient/)) - 0.5
}
return {s, i, arr}
})
.sort((a, b) => a.i - b.i)
.map(x => x.s)

return migration
}
}

const {pool, ...helper} = getPoolHelper({__filename})

describe('sort sql statements', () => {
const migrationsPath = path.join(__dirname, `generated/${helper.id}`)

// problem: migra doesn't do a toplogoical sort of the statements. it statically orders in a sensible way, but doesn't allow for tables to depend on functions, for example.
// https://github.com/djrobstep/migra/issues/196
const syncer = fsSyncer(migrationsPath, {
'01.one.sql': 'create table patient(id int primary key, name text)',
'02.two.sql': `
create type address as (street text, city text, state text, zip text);
create type patient_type as enum ('human', 'animal');
`,
'03.three.sql': `
alter table patient add column address address;
alter table patient add column type patient_type;
`,
})

let migrator: Migrator

beforeEach(async () => {
test('definitions', async () => {
// problem: migra doesn't do a toplogoical sort of the statements. it statically orders in a sensible way, but doesn't allow for tables to depend on functions, for example.
// https://github.com/djrobstep/migra/issues/196
class Migrator extends Base {
async runMigra() {
const migration = await super.runMigra()
migration.statements.sortBy((s, i, arr) => {
return /create type .*address/.test(s) //
? arr.findIndex(other => other.match(/create table .*patient/)) - 1 // move address type definition to before patient table creation
: i
})

return migration
}
}

const migrationsPath = path.join(__dirname, `generated/${helper.id}`)

const syncer = fsSyncer(migrationsPath, {
'01.one.sql': 'create table patient(id int primary key, name text)',
'02.two.sql': `
create type address as (street text, city text, state text, zip text);
create type patient_type as enum ('human', 'animal');
`,
'03.three.sql': `
alter table patient add column address address;
alter table patient add column type patient_type;
`,
})
syncer.sync()
migrator = new Migrator({

const migrator = new Migrator({
client: pool,
migrationsPath,
migrationTableName: 'migrations',
})

await migrator.up()
})

test('definitions', async () => {
await migrator.writeDefinitionFile(syncer.baseDir + '/definitions.sql')

expect(syncer.read()).toMatchInlineSnapshot(`
Expand Down

0 comments on commit d5a6f5d

Please sign in to comment.