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
# 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.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,13 @@
name: angel3_migration_runner
version: 4.0.0-beta.1
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:
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel3_migration: ^4.0.0-beta.1
angel3_orm: ^4.0.0
angel3_orm: ^4.0.0-beta.1
args: ^2.1.0
charcode: ^1.2.0
postgres: ^2.3.2
pedantic: ^1.11.0