Published migration_runner

This commit is contained in:
thomashii 2021-05-18 22:36:40 +08:00
parent d2eb602b2d
commit 88bdabc5c9
10 changed files with 85 additions and 64 deletions

View file

@ -1,4 +1,4 @@
# 4.0.0 # 4.0.0-beta.1
* Migrated to support Dart SDK 2.12.x NNBD * Migrated to support Dart SDK 2.12.x NNBD
# 3.0.0 # 3.0.0

View file

@ -1,2 +1,8 @@
# migration # angel3_migration_runner
[![version](https://img.shields.io/badge/pub-v4.0.0-brightgreen)](https://pub.dartlang.org/packages/angel3_migration_runner)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/angel3/packages/orm/angel_migration_runner/LICENSE)
A PostgreSQL database migration framework built on Angel's ORM. A PostgreSQL database migration framework built on Angel's ORM.

View file

@ -1,3 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer: analyzer:
strong-mode: strong-mode:
implicit-casts: false implicit-casts: false

View file

@ -4,24 +4,26 @@ import 'runner.dart';
/// Runs the Angel Migration CLI. /// Runs the Angel Migration CLI.
Future runMigrations(MigrationRunner migrationRunner, List<String> args) { Future runMigrations(MigrationRunner migrationRunner, List<String> args) {
var cmd = new CommandRunner('migration_runner', 'Executes Angel migrations.') var cmd = CommandRunner('migration_runner', 'Executes Angel migrations.')
..addCommand(new _UpCommand(migrationRunner)) ..addCommand(_UpCommand(migrationRunner))
..addCommand(new _RefreshCommand(migrationRunner)) ..addCommand(_RefreshCommand(migrationRunner))
..addCommand(new _ResetCommand(migrationRunner)) ..addCommand(_ResetCommand(migrationRunner))
..addCommand(new _RollbackCommand(migrationRunner)); ..addCommand(_RollbackCommand(migrationRunner));
return cmd.run(args).then((_) => migrationRunner.close()); return cmd.run(args).then((_) => migrationRunner.close());
} }
class _UpCommand extends Command { class _UpCommand extends Command {
_UpCommand(this.migrationRunner); _UpCommand(this.migrationRunner);
@override
String get name => 'up'; String get name => 'up';
@override
String get description => 'Runs outstanding migrations.'; String get description => 'Runs outstanding migrations.';
final MigrationRunner migrationRunner; final MigrationRunner migrationRunner;
@override @override
run() { Future run() {
return migrationRunner.up(); return migrationRunner.up();
} }
} }
@ -29,13 +31,15 @@ class _UpCommand extends Command {
class _ResetCommand extends Command { class _ResetCommand extends Command {
_ResetCommand(this.migrationRunner); _ResetCommand(this.migrationRunner);
@override
String get name => 'reset'; String get name => 'reset';
@override
String get description => 'Resets the database.'; String get description => 'Resets the database.';
final MigrationRunner migrationRunner; final MigrationRunner migrationRunner;
@override @override
run() { Future run() {
return migrationRunner.reset(); return migrationRunner.reset();
} }
} }
@ -43,14 +47,16 @@ class _ResetCommand extends Command {
class _RefreshCommand extends Command { class _RefreshCommand extends Command {
_RefreshCommand(this.migrationRunner); _RefreshCommand(this.migrationRunner);
@override
String get name => 'refresh'; String get name => 'refresh';
@override
String get description => String get description =>
'Resets the database, and then re-runs all migrations.'; 'Resets the database, and then re-runs all migrations.';
final MigrationRunner migrationRunner; final MigrationRunner migrationRunner;
@override @override
run() { Future run() {
return migrationRunner.reset().then((_) => migrationRunner.up()); return migrationRunner.reset().then((_) => migrationRunner.up());
} }
} }
@ -58,13 +64,15 @@ class _RefreshCommand extends Command {
class _RollbackCommand extends Command { class _RollbackCommand extends Command {
_RollbackCommand(this.migrationRunner); _RollbackCommand(this.migrationRunner);
@override
String get name => 'rollback'; String get name => 'rollback';
@override
String get description => 'Undoes the last batch of migrations.'; String get description => 'Undoes the last batch of migrations.';
final MigrationRunner migrationRunner; final MigrationRunner migrationRunner;
@override @override
run() { Future run() {
return migrationRunner.rollback(); return migrationRunner.rollback();
} }
} }

View file

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'package:angel_migration/angel_migration.dart'; import 'package:angel3_migration/angel3_migration.dart';
import 'package:postgres/postgres.dart'; import 'package:postgres/postgres.dart';
import '../runner.dart'; import '../runner.dart';
import '../util.dart'; import '../util.dart';
@ -9,7 +9,7 @@ import 'schema.dart';
class PostgresMigrationRunner implements MigrationRunner { class PostgresMigrationRunner implements MigrationRunner {
final Map<String, Migration> migrations = {}; final Map<String, Migration> migrations = {};
final PostgreSQLConnection connection; final PostgreSQLConnection connection;
final Queue<Migration> _migrationQueue = new Queue(); final Queue<Migration> _migrationQueue = Queue();
bool _connected = false; bool _connected = false;
PostgresMigrationRunner(this.connection, PostgresMigrationRunner(this.connection,
@ -27,7 +27,7 @@ class PostgresMigrationRunner implements MigrationRunner {
while (_migrationQueue.isNotEmpty) { while (_migrationQueue.isNotEmpty) {
var migration = _migrationQueue.removeFirst(); var migration = _migrationQueue.removeFirst();
var path = await absoluteSourcePath(migration.runtimeType); var path = await absoluteSourcePath(migration.runtimeType);
migrations.putIfAbsent(path.replaceAll("\\", "\\\\"), () => migration); migrations.putIfAbsent(path.replaceAll('\\', '\\\\'), () => migration);
} }
if (!_connected) { if (!_connected) {
@ -49,8 +49,8 @@ class PostgresMigrationRunner implements MigrationRunner {
Future up() async { Future up() async {
await _init(); await _init();
var r = await connection.query('SELECT path from migrations;'); var r = await connection.query('SELECT path from migrations;');
Iterable<String> existing = r.expand((x) => x).cast<String>(); var existing = r.expand((x) => x).cast<String>();
List<String> toRun = []; var toRun = <String>[];
migrations.forEach((k, v) { migrations.forEach((k, v) {
if (!existing.contains(k)) toRun.add(k); if (!existing.contains(k)) toRun.add(k);
@ -58,12 +58,12 @@ class PostgresMigrationRunner implements MigrationRunner {
if (toRun.isNotEmpty) { if (toRun.isNotEmpty) {
var r = await connection.query('SELECT MAX(batch) from migrations;'); var r = await connection.query('SELECT MAX(batch) from migrations;');
int curBatch = (r[0][0] ?? 0) as int; var curBatch = (r[0][0] ?? 0) as int;
int batch = curBatch + 1; var batch = curBatch + 1;
for (var k in toRun) { for (var k in toRun) {
var migration = migrations[k]!; var migration = migrations[k]!;
var schema = new PostgresSchema(); var schema = PostgresSchema();
migration.up(schema); migration.up(schema);
print('Bringing up "$k"...'); print('Bringing up "$k"...');
await schema.run(connection).then((_) { await schema.run(connection).then((_) {
@ -81,11 +81,11 @@ class PostgresMigrationRunner implements MigrationRunner {
await _init(); await _init();
var r = await connection.query('SELECT MAX(batch) from migrations;'); var r = await connection.query('SELECT MAX(batch) from migrations;');
int curBatch = (r[0][0] ?? 0) as int; var curBatch = (r[0][0] ?? 0) as int;
r = await connection r = await connection
.query('SELECT path from migrations WHERE batch = $curBatch;'); .query('SELECT path from migrations WHERE batch = $curBatch;');
Iterable<String> existing = r.expand((x) => x).cast<String>(); var existing = r.expand((x) => x).cast<String>();
List<String> toRun = []; var toRun = <String>[];
migrations.forEach((k, v) { migrations.forEach((k, v) {
if (existing.contains(k)) toRun.add(k); if (existing.contains(k)) toRun.add(k);
@ -94,7 +94,7 @@ class PostgresMigrationRunner implements MigrationRunner {
if (toRun.isNotEmpty) { if (toRun.isNotEmpty) {
for (var k in toRun.reversed) { for (var k in toRun.reversed) {
var migration = migrations[k]!; var migration = migrations[k]!;
var schema = new PostgresSchema(); var schema = PostgresSchema();
migration.down(schema); migration.down(schema);
print('Bringing down "$k"...'); print('Bringing down "$k"...');
await schema.run(connection).then((_) { await schema.run(connection).then((_) {
@ -112,13 +112,13 @@ class PostgresMigrationRunner implements MigrationRunner {
await _init(); await _init();
var r = await connection var r = await connection
.query('SELECT path from migrations ORDER BY batch DESC;'); .query('SELECT path from migrations ORDER BY batch DESC;');
Iterable<String> existing = r.expand((x) => x).cast<String>(); var existing = r.expand((x) => x).cast<String>();
var toRun = existing.where(migrations.containsKey).toList(); var toRun = existing.where(migrations.containsKey).toList();
if (toRun.isNotEmpty) { if (toRun.isNotEmpty) {
for (var k in toRun.reversed) { for (var k in toRun.reversed) {
var migration = migrations[k]!; var migration = migrations[k]!;
var schema = new PostgresSchema(); var schema = PostgresSchema();
migration.down(schema); migration.down(schema);
print('Bringing down "$k"...'); print('Bringing down "$k"...');
await schema.run(connection).then((_) { await schema.run(connection).then((_) {

View file

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:angel_migration/angel_migration.dart'; import 'package:angel3_migration/angel3_migration.dart';
import 'package:postgres/postgres.dart'; import 'package:postgres/postgres.dart';
import 'package:angel_migration_runner/src/postgres/table.dart'; import 'package:angel3_migration_runner/src/postgres/table.dart';
class PostgresSchema extends Schema { class PostgresSchema extends Schema {
final int _indent; final int _indent;
@ -9,14 +9,14 @@ class PostgresSchema extends Schema {
PostgresSchema._(this._buf, this._indent); PostgresSchema._(this._buf, this._indent);
factory PostgresSchema() => new PostgresSchema._(new StringBuffer(), 0); factory PostgresSchema() => PostgresSchema._(StringBuffer(), 0);
Future run(PostgreSQLConnection connection) => connection.execute(compile()); Future run(PostgreSQLConnection connection) => connection.execute(compile());
String compile() => _buf.toString(); String compile() => _buf.toString();
void _writeln(String str) { void _writeln(String str) {
for (int i = 0; i < _indent; i++) { for (var i = 0; i < _indent; i++) {
_buf.write(' '); _buf.write(' ');
} }
@ -24,23 +24,24 @@ class PostgresSchema extends Schema {
} }
@override @override
void drop(String tableName, {bool cascade: false}) { void drop(String tableName, {bool cascade = false}) {
var c = cascade == true ? ' CASCADE' : ''; var c = cascade == true ? ' CASCADE' : '';
_writeln('DROP TABLE "$tableName"$c;'); _writeln('DROP TABLE "$tableName"$c;');
} }
@override @override
void alter(String tableName, void callback(MutableTable table)) { void alter(String tableName, void Function(MutableTable table) callback) {
var tbl = new PostgresAlterTable(tableName); var tbl = PostgresAlterTable(tableName);
callback(tbl); callback(tbl);
_writeln('ALTER TABLE "$tableName"'); _writeln('ALTER TABLE "$tableName"');
tbl.compile(_buf, _indent + 1); tbl.compile(_buf, _indent + 1);
_buf.write(';'); _buf.write(';');
} }
void _create(String tableName, void callback(Table table), bool ifNotExists) { void _create(
String tableName, void Function(Table table) callback, bool ifNotExists) {
var op = ifNotExists ? ' IF NOT EXISTS' : ''; var op = ifNotExists ? ' IF NOT EXISTS' : '';
var tbl = new PostgresTable(); var tbl = PostgresTable();
callback(tbl); callback(tbl);
_writeln('CREATE TABLE$op "$tableName" ('); _writeln('CREATE TABLE$op "$tableName" (');
tbl.compile(_buf, _indent + 1); tbl.compile(_buf, _indent + 1);
@ -49,10 +50,11 @@ class PostgresSchema extends Schema {
} }
@override @override
void create(String tableName, void callback(Table table)) => void create(String tableName, void Function(Table table) callback) =>
_create(tableName, callback, false); _create(tableName, callback, false);
@override @override
void createIfNotExists(String tableName, void callback(Table table)) => void createIfNotExists(
String tableName, void Function(Table table) callback) =>
_create(tableName, callback, true); _create(tableName, callback, true);
} }

View file

@ -1,27 +1,28 @@
import 'dart:collection'; import 'dart:collection';
import 'package:angel_orm/angel_orm.dart'; import 'package:angel3_orm/angel3_orm.dart';
import 'package:angel_migration/angel_migration.dart'; import 'package:angel3_migration/angel3_migration.dart';
import 'package:charcode/ascii.dart'; import 'package:charcode/ascii.dart';
abstract class PostgresGenerator { abstract class PostgresGenerator {
static String columnType(MigrationColumn column) { static String columnType(MigrationColumn column) {
var str = column.type!.name; var str = column.type!.name;
if (column.length != null) if (column.length != null) {
return '$str(${column.length})'; return '$str(${column.length})';
else } else {
return str; return str;
} }
}
static String compileColumn(MigrationColumn column) { static String compileColumn(MigrationColumn column) {
var buf = new StringBuffer(columnType(column)); var buf = StringBuffer(columnType(column));
if (column.isNullable == false) buf.write(' NOT NULL'); if (column.isNullable == false) buf.write(' NOT NULL');
if (column.defaultValue != null) { if (column.defaultValue != null) {
String s; String s;
var value = column.defaultValue; var value = column.defaultValue;
if (value is RawSql) if (value is RawSql) {
s = value.value; s = value.value;
else if (value is String) { } else if (value is String) {
var b = StringBuffer(); var b = StringBuffer();
for (var ch in value.codeUnits) { for (var ch in value.codeUnits) {
if (ch == $single_quote) { if (ch == $single_quote) {
@ -38,10 +39,11 @@ abstract class PostgresGenerator {
buf.write(' DEFAULT $s'); buf.write(' DEFAULT $s');
} }
if (column.indexType == IndexType.unique) if (column.indexType == IndexType.unique) {
buf.write(' UNIQUE'); buf.write(' UNIQUE');
else if (column.indexType == IndexType.primaryKey) } else if (column.indexType == IndexType.primaryKey) {
buf.write(' PRIMARY KEY'); buf.write(' PRIMARY KEY');
}
for (var ref in column.externalReferences) { for (var ref in column.externalReferences) {
buf.write(' ' + compileReference(ref)); buf.write(' ' + compileReference(ref));
@ -51,8 +53,8 @@ abstract class PostgresGenerator {
} }
static String compileReference(MigrationColumnReference ref) { static String compileReference(MigrationColumnReference ref) {
var buf = new StringBuffer( var buf =
'REFERENCES "${ref.foreignTable}"("${ref.foreignKey}")'); StringBuffer('REFERENCES "${ref.foreignTable}"("${ref.foreignKey}")');
if (ref.behavior != null) buf.write(' ' + ref.behavior!); if (ref.behavior != null) buf.write(' ' + ref.behavior!);
return buf.toString(); return buf.toString();
} }
@ -63,21 +65,22 @@ class PostgresTable extends Table {
@override @override
MigrationColumn declareColumn(String name, Column column) { MigrationColumn declareColumn(String name, Column column) {
if (_columns.containsKey(name)) if (_columns.containsKey(name)) {
throw new StateError('Cannot redeclare column "$name".'); throw StateError('Cannot redeclare column "$name".');
var col = new MigrationColumn.from(column); }
var col = MigrationColumn.from(column);
_columns[name] = col; _columns[name] = col;
return col; return col;
} }
void compile(StringBuffer buf, int indent) { void compile(StringBuffer buf, int indent) {
int i = 0; var i = 0;
_columns.forEach((name, column) { _columns.forEach((name, column) {
var col = PostgresGenerator.compileColumn(column); var col = PostgresGenerator.compileColumn(column);
if (i++ > 0) buf.writeln(','); if (i++ > 0) buf.writeln(',');
for (int i = 0; i < indent; i++) { for (var i = 0; i < indent; i++) {
buf.write(' '); buf.write(' ');
} }
@ -89,19 +92,19 @@ class PostgresTable extends Table {
class PostgresAlterTable extends Table implements MutableTable { class PostgresAlterTable extends Table implements MutableTable {
final Map<String, MigrationColumn> _columns = {}; final Map<String, MigrationColumn> _columns = {};
final String tableName; final String tableName;
final Queue<String> _stack = new Queue<String>(); final Queue<String> _stack = Queue<String>();
PostgresAlterTable(this.tableName); PostgresAlterTable(this.tableName);
void compile(StringBuffer buf, int indent) { void compile(StringBuffer buf, int indent) {
int i = 0; var i = 0;
while (_stack.isNotEmpty) { while (_stack.isNotEmpty) {
var str = _stack.removeFirst(); var str = _stack.removeFirst();
if (i++ > 0) buf.writeln(','); if (i++ > 0) buf.writeln(',');
for (int i = 0; i < indent; i++) { for (var i = 0; i < indent; i++) {
buf.write(' '); buf.write(' ');
} }
@ -115,7 +118,7 @@ class PostgresAlterTable extends Table implements MutableTable {
var col = PostgresGenerator.compileColumn(column); var col = PostgresGenerator.compileColumn(column);
if (i++ > 0) buf.writeln(','); if (i++ > 0) buf.writeln(',');
for (int i = 0; i < indent; i++) { for (var i = 0; i < indent; i++) {
buf.write(' '); buf.write(' ');
} }
@ -125,9 +128,10 @@ class PostgresAlterTable extends Table implements MutableTable {
@override @override
MigrationColumn declareColumn(String name, Column column) { MigrationColumn declareColumn(String name, Column column) {
if (_columns.containsKey(name)) if (_columns.containsKey(name)) {
throw new StateError('Cannot redeclare column "$name".'); throw StateError('Cannot redeclare column "$name".');
var col = new MigrationColumn.from(column); }
var col = MigrationColumn.from(column);
_columns[name] = col; _columns[name] = col;
return col; return col;
} }
@ -145,8 +149,7 @@ class PostgresAlterTable extends Table implements MutableTable {
@override @override
void changeColumnType(String name, ColumnType type, {int? length}) { void changeColumnType(String name, ColumnType type, {int? length}) {
_stack.add('ALTER COLUMN "$name" TYPE ' + _stack.add('ALTER COLUMN "$name" TYPE ' +
PostgresGenerator.columnType( PostgresGenerator.columnType(MigrationColumn(type, length: length)));
new MigrationColumn(type, length: length)));
} }
@override @override

View file

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:angel_migration/angel_migration.dart'; import 'package:angel3_migration/angel3_migration.dart';
abstract class MigrationRunner { abstract class MigrationRunner {
void addMigration(Migration migration); void addMigration(Migration migration);

View file

@ -1,12 +1,13 @@
name: angel3_migration_runner name: angel3_migration_runner
version: 4.0.0-beta.1 version: 4.0.0-beta.1
description: Command-line based database migration runner for Angel's ORM. description: Command-line based database migration runner for Angel's ORM.
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/orm/angel3_migration_runner homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/orm/angel_migration_runner
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel3_migration: ^4.0.0-beta.1 angel3_migration: ^4.0.0-beta.1
angel3_orm: ^4.0.0 angel3_orm: ^4.0.0-beta.1
args: ^2.1.0 args: ^2.1.0
charcode: ^1.2.0 charcode: ^1.2.0
postgres: ^2.3.2 postgres: ^2.3.2
pedantic: ^1.11.0