commit
19a7f8316d
24 changed files with 445 additions and 66 deletions
|
@ -1,6 +1,6 @@
|
||||||
# Runing Ancillary Docker Services
|
# Docker Services
|
||||||
|
|
||||||
The required ancillary services required by the framework can be run using the compose files provided in this folder.
|
The required applications by the framework can be run using the docker compose files provided in this folder.
|
||||||
|
|
||||||
## PostreSQL
|
## PostreSQL
|
||||||
|
|
||||||
|
@ -30,12 +30,12 @@ The required ancillary services required by the framework can be run using the c
|
||||||
psql --username postgres
|
psql --username postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create database, user and access
|
### Create PostgreSQL database, user and grant access
|
||||||
|
|
||||||
```psql
|
```sql
|
||||||
postgres=# create database orm_test;
|
create database orm_test;
|
||||||
postgres=# create user test with encrypted password 'test123';
|
create user test with encrypted password 'test123';
|
||||||
postgres=# grant all privileges on database orm_test to test;
|
grant all privileges on database orm_test to test;
|
||||||
```
|
```
|
||||||
|
|
||||||
## MariaDB
|
## MariaDB
|
||||||
|
@ -59,6 +59,20 @@ The required ancillary services required by the framework can be run using the c
|
||||||
docker logs maria-mariadb-1 -f
|
docker logs maria-mariadb-1 -f
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Create MariaDB database, user and grant access
|
||||||
|
|
||||||
|
```sql
|
||||||
|
create database orm_test;
|
||||||
|
|
||||||
|
-- Granting localhost access only
|
||||||
|
create user 'test'@'localhost' identified by 'test123';
|
||||||
|
grant all privileges on orm_test.* to 'test'@'localhost';
|
||||||
|
|
||||||
|
-- Granting localhost and remote access
|
||||||
|
create user 'test'@'%' identified by 'test123';
|
||||||
|
grant all privileges on orm_test.* to 'test'@'%';
|
||||||
|
```
|
||||||
|
|
||||||
## MySQL
|
## MySQL
|
||||||
|
|
||||||
### Starting the MySQL container
|
### Starting the MySQL container
|
||||||
|
@ -80,6 +94,20 @@ The required ancillary services required by the framework can be run using the c
|
||||||
docker logs mysql-mysql-1 -f
|
docker logs mysql-mysql-1 -f
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Create MySQL database, user and grant access
|
||||||
|
|
||||||
|
```sql
|
||||||
|
create database orm_test;
|
||||||
|
|
||||||
|
-- Granting localhost access only
|
||||||
|
create user 'test'@'localhost' identified by 'test123';
|
||||||
|
grant all privileges on orm_test.* to 'test'@'localhost';
|
||||||
|
|
||||||
|
-- Granting localhost and remote access
|
||||||
|
create user 'test'@'%' identified by 'test123';
|
||||||
|
grant all privileges on orm_test.* to 'test'@'%';
|
||||||
|
```
|
||||||
|
|
||||||
## MongoDB
|
## MongoDB
|
||||||
|
|
||||||
### Starting the MongoDB container
|
### Starting the MongoDB container
|
||||||
|
|
|
@ -10,7 +10,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- "db:/var/lib/postgresql/data"
|
- "db:/var/lib/postgresql/data"
|
||||||
networks:
|
networks:
|
||||||
- webnet
|
- appnet
|
||||||
|
|
||||||
pgadmin4:
|
pgadmin4:
|
||||||
image: dpage/pgadmin4:latest
|
image: dpage/pgadmin4:latest
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 8.2.1
|
||||||
|
|
||||||
|
* Updated README
|
||||||
|
|
||||||
## 8.2.0
|
## 8.2.0
|
||||||
|
|
||||||
* Require Dart >= 3.3
|
* Require Dart >= 3.3
|
||||||
|
|
|
@ -5,20 +5,14 @@
|
||||||
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
|
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
|
||||||
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/orm/angel_migration/LICENSE)
|
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/orm/angel_migration/LICENSE)
|
||||||
|
|
||||||
A basic database migration framework built for Angel3 ORM.
|
This package contains the abstract classes for implementing database migration in Angel3 framework. It is designed to work with Angel3 ORM. Please refer to the implementation in the [ORM Migration Runner](<https://pub.dev/packages/angel3_migration_runner>) package for more details.
|
||||||
|
|
||||||
## Supported database
|
## Supported Features
|
||||||
|
|
||||||
* PostgreSQL version 10 or later
|
|
||||||
* MariaDB 10.2.x or later
|
|
||||||
* MySQL 8.x or later
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
* Create tables based on ORM models
|
* Create tables based on ORM models
|
||||||
* Drop tables based on ORM models
|
* Drop tables based on ORM models
|
||||||
* Add new tables based ORM models
|
* Add new tables based on ORM models
|
||||||
|
|
||||||
## Limitation
|
## Limitation
|
||||||
|
|
||||||
* Alter table/fields based on updated ORM models not supported
|
* Alter table/fields based on updated ORM models is not supported
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: angel3_migration
|
name: angel3_migration
|
||||||
version: 8.2.0
|
version: 8.2.1
|
||||||
description: Database migration runtime for Angel3 ORM. Use this package to define schemas.
|
description: The abstract classes for implementing database migration in Angel3 framework. Designed to work with Angel3 ORM.
|
||||||
homepage: https://angel3-framework.web.app/
|
homepage: https://angel3-framework.web.app/
|
||||||
repository: https://github.com/dart-backend/angel/tree/master/packages/orm/angel_migration
|
repository: https://github.com/dart-backend/angel/tree/master/packages/orm/angel_migration
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 8.2.1
|
||||||
|
|
||||||
|
* Updated README
|
||||||
|
* Updated examples
|
||||||
|
* Updated `PostgresMigrationRunner` error handling
|
||||||
|
* Fixed `MySqlMigrationRunner` migration issues
|
||||||
|
* Added test cases for `PostgreSQL`, `MySQL` and `MariaDB`
|
||||||
|
* Added auto increment integer primary key suppport to MySQL and MariaDB
|
||||||
|
|
||||||
## 8.2.0
|
## 8.2.0
|
||||||
|
|
||||||
* Require Dart >= 3.3
|
* Require Dart >= 3.3
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
|
[![Discord](https://img.shields.io/discord/1060322353214660698)](https://discord.gg/3X6bxTUdCM)
|
||||||
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/orm/angel_migration_runner/LICENSE)
|
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/orm/angel_migration_runner/LICENSE)
|
||||||
|
|
||||||
Database migration runner for Angel3 ORM.
|
This package contains the implementation of the database migration for the following databases. It is designed to work with Angel3 ORM.
|
||||||
|
|
||||||
Supported database:
|
|
||||||
|
|
||||||
* PostgreSQL 10.x or greater
|
* PostgreSQL 10.x or greater
|
||||||
* MariaDB 10.2.x or greater
|
* MariaDB 10.2.x or greater
|
||||||
|
|
|
@ -2,13 +2,23 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:angel3_migration/angel3_migration.dart';
|
import 'package:angel3_migration/angel3_migration.dart';
|
||||||
import 'package:angel3_migration_runner/angel3_migration_runner.dart';
|
import 'package:angel3_migration_runner/angel3_migration_runner.dart';
|
||||||
|
import 'package:angel3_migration_runner/mysql.dart';
|
||||||
import 'package:angel3_migration_runner/postgres.dart';
|
import 'package:angel3_migration_runner/postgres.dart';
|
||||||
import 'package:angel3_orm/angel3_orm.dart';
|
import 'package:angel3_orm/angel3_orm.dart';
|
||||||
|
import 'package:mysql_client/mysql_client.dart';
|
||||||
import 'package:postgres/postgres.dart';
|
import 'package:postgres/postgres.dart';
|
||||||
|
|
||||||
import 'todo.dart';
|
import 'todo.dart';
|
||||||
|
|
||||||
void main(List<String> args) async {
|
void main(List<String> args) async {
|
||||||
|
// Run migration on PostgreSQL database
|
||||||
|
postgresqlMigration(args);
|
||||||
|
|
||||||
|
// Run migration on MySQL database
|
||||||
|
mysqlMigration(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void postgresqlMigration(List<String> args) async {
|
||||||
var host = Platform.environment['DB_HOST'] ?? 'localhost';
|
var host = Platform.environment['DB_HOST'] ?? 'localhost';
|
||||||
var database = Platform.environment['DB_NAME'] ?? 'demo';
|
var database = Platform.environment['DB_NAME'] ?? 'demo';
|
||||||
var username = Platform.environment['DB_USERNAME'] ?? 'demouser';
|
var username = Platform.environment['DB_USERNAME'] ?? 'demouser';
|
||||||
|
@ -16,14 +26,16 @@ void main(List<String> args) async {
|
||||||
|
|
||||||
print("$host $database $username $password");
|
print("$host $database $username $password");
|
||||||
|
|
||||||
Connection conn = await Connection.open(Endpoint(
|
Connection conn = await Connection.open(
|
||||||
host: host,
|
Endpoint(
|
||||||
port: 5432,
|
host: host,
|
||||||
database: database,
|
port: 5432,
|
||||||
username: username,
|
database: database,
|
||||||
password: password));
|
username: username,
|
||||||
|
password: password),
|
||||||
|
settings: ConnectionSettings(sslMode: SslMode.disable));
|
||||||
|
|
||||||
var postgresqlMigrationRunner = PostgresMigrationRunner(
|
var runner = PostgresMigrationRunner(
|
||||||
conn,
|
conn,
|
||||||
migrations: [
|
migrations: [
|
||||||
UserMigration(),
|
UserMigration(),
|
||||||
|
@ -32,17 +44,25 @@ void main(List<String> args) async {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
runMigrations(runner, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mysqlMigration(List<String> args) async {
|
||||||
|
var host = Platform.environment['MYSQL_HOST'] ?? 'localhost';
|
||||||
|
var database = Platform.environment['MYSQL_DB'] ?? 'orm_test';
|
||||||
|
var username = Platform.environment['MYSQL_USERNAME'] ?? 'test';
|
||||||
|
var password = Platform.environment['MYSQL_PASSWORD'] ?? 'test123';
|
||||||
|
|
||||||
var mySQLConn = await MySQLConnection.createConnection(
|
var mySQLConn = await MySQLConnection.createConnection(
|
||||||
host: host,
|
host: host,
|
||||||
port: 3306,
|
port: 3306,
|
||||||
databaseName: database,
|
databaseName: database,
|
||||||
userName: username,
|
userName: username,
|
||||||
password: password,
|
password: password,
|
||||||
secure: false);
|
secure: true);
|
||||||
|
|
||||||
// ignore: unused_local_variable
|
// ignore: unused_local_variable
|
||||||
var mysqlMigrationRunner = MySqlMigrationRunner(
|
var runner = MySqlMigrationRunner(
|
||||||
mySQLConn,
|
mySQLConn,
|
||||||
migrations: [
|
migrations: [
|
||||||
UserMigration(),
|
UserMigration(),
|
||||||
|
@ -50,9 +70,8 @@ void main(List<String> args) async {
|
||||||
FooMigration(),
|
FooMigration(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
*/
|
|
||||||
|
|
||||||
runMigrations(postgresqlMigrationRunner, args);
|
runMigrations(runner, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
class FooMigration extends Migration {
|
class FooMigration extends Migration {
|
||||||
|
|
|
@ -50,6 +50,7 @@ class _RefreshCommand extends Command {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get name => 'refresh';
|
String get name => 'refresh';
|
||||||
|
|
||||||
@override
|
@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.';
|
||||||
|
@ -67,8 +68,9 @@ class _RollbackCommand extends Command {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get name => 'rollback';
|
String get name => 'rollback';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description => 'Undoes the last batch of migrations.';
|
String get description => 'Undo the last batch of migrations.';
|
||||||
|
|
||||||
final MigrationRunner migrationRunner;
|
final MigrationRunner migrationRunner;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import '../runner.dart';
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
import 'schema.dart';
|
import 'schema.dart';
|
||||||
|
|
||||||
|
/// A MariaDB database migration runner.
|
||||||
class MariaDbMigrationRunner implements MigrationRunner {
|
class MariaDbMigrationRunner implements MigrationRunner {
|
||||||
final _log = Logger('MariaDbMigrationRunner');
|
final _log = Logger('MariaDbMigrationRunner');
|
||||||
|
|
||||||
|
@ -17,8 +18,8 @@ class MariaDbMigrationRunner implements MigrationRunner {
|
||||||
|
|
||||||
MariaDbMigrationRunner(this.connection,
|
MariaDbMigrationRunner(this.connection,
|
||||||
{Iterable<Migration> migrations = const [], bool connected = false}) {
|
{Iterable<Migration> migrations = const [], bool connected = false}) {
|
||||||
if (migrations.isNotEmpty == true) migrations.forEach(addMigration);
|
if (migrations.isNotEmpty) migrations.forEach(addMigration);
|
||||||
_connected = connected == true;
|
_connected = connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -39,7 +40,7 @@ class MariaDbMigrationRunner implements MigrationRunner {
|
||||||
|
|
||||||
await connection.query('''
|
await connection.query('''
|
||||||
CREATE TABLE IF NOT EXISTS migrations (
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
id serial,
|
id integer NOT NULL AUTO_INCREMENT,
|
||||||
batch integer,
|
batch integer,
|
||||||
path varchar(255),
|
path varchar(255),
|
||||||
PRIMARY KEY(id)
|
PRIMARY KEY(id)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:mysql1/mysql1.dart';
|
||||||
|
|
||||||
import 'table.dart';
|
import 'table.dart';
|
||||||
|
|
||||||
|
/// A MariaDB database schema generator
|
||||||
class MariaDbSchema extends Schema {
|
class MariaDbSchema extends Schema {
|
||||||
final _log = Logger('MariaDbSchema');
|
final _log = Logger('MariaDbSchema');
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,12 @@ abstract class MariaDbGenerator {
|
||||||
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 int based primary key, apply NOT NULL
|
||||||
|
// and AUTO_INCREMENT
|
||||||
|
if (column.type == ColumnType.int) {
|
||||||
|
buf.write(' NOT NULL AUTO_INCREMENT');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var ref in column.externalReferences) {
|
for (var ref in column.externalReferences) {
|
||||||
|
@ -105,7 +111,7 @@ class MariaDbTable extends Table {
|
||||||
|
|
||||||
if (indexBuf.isNotEmpty) {
|
if (indexBuf.isNotEmpty) {
|
||||||
for (var i = 0; i < indexBuf.length; i++) {
|
for (var i = 0; i < indexBuf.length; i++) {
|
||||||
buf.write(',\n${indexBuf[$1]}');
|
buf.write(',\n${indexBuf[i]}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import '../runner.dart';
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
import 'schema.dart';
|
import 'schema.dart';
|
||||||
|
|
||||||
|
/// A MySQL database migration runner.
|
||||||
class MySqlMigrationRunner implements MigrationRunner {
|
class MySqlMigrationRunner implements MigrationRunner {
|
||||||
final _log = Logger('MysqlMigrationRunner');
|
final _log = Logger('MysqlMigrationRunner');
|
||||||
|
|
||||||
|
@ -17,8 +18,10 @@ class MySqlMigrationRunner implements MigrationRunner {
|
||||||
|
|
||||||
MySqlMigrationRunner(this.connection,
|
MySqlMigrationRunner(this.connection,
|
||||||
{Iterable<Migration> migrations = const [], bool connected = false}) {
|
{Iterable<Migration> migrations = const [], bool connected = false}) {
|
||||||
if (migrations.isNotEmpty == true) migrations.forEach(addMigration);
|
if (migrations.isNotEmpty) {
|
||||||
_connected = connected == true;
|
migrations.forEach(addMigration);
|
||||||
|
}
|
||||||
|
_connected = connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -39,15 +42,18 @@ class MySqlMigrationRunner implements MigrationRunner {
|
||||||
|
|
||||||
await connection.execute('''
|
await connection.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS migrations (
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
id serial,
|
id integer NOT NULL AUTO_INCREMENT,
|
||||||
batch integer,
|
batch integer,
|
||||||
path varchar(255),
|
path varchar(500),
|
||||||
PRIMARY KEY(id)
|
PRIMARY KEY(id)
|
||||||
);
|
);
|
||||||
''').then((result) {
|
''').then((result) {
|
||||||
|
//print(result);
|
||||||
_log.info('Check and create "migrations" table');
|
_log.info('Check and create "migrations" table');
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
|
//print(e);
|
||||||
_log.severe('Failed to create "migrations" table.');
|
_log.severe('Failed to create "migrations" table.');
|
||||||
|
throw e;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +62,9 @@ class MySqlMigrationRunner implements MigrationRunner {
|
||||||
await _init();
|
await _init();
|
||||||
var result = await connection.execute('SELECT path from migrations;');
|
var result = await connection.execute('SELECT path from migrations;');
|
||||||
var existing = <String>[];
|
var existing = <String>[];
|
||||||
if (result.rows.isNotEmpty) {
|
for (var item in result.rows) {
|
||||||
existing = result.rows.first.assoc().values.cast<String>().toList();
|
var rec = item.assoc().values.first ?? "";
|
||||||
|
existing.add(rec.replaceAll("\\", "\\\\"));
|
||||||
}
|
}
|
||||||
var toRun = <String>[];
|
var toRun = <String>[];
|
||||||
|
|
||||||
|
@ -110,8 +117,9 @@ class MySqlMigrationRunner implements MigrationRunner {
|
||||||
result = await connection
|
result = await connection
|
||||||
.execute('SELECT path from migrations WHERE batch = $curBatch;');
|
.execute('SELECT path from migrations WHERE batch = $curBatch;');
|
||||||
var existing = <String>[];
|
var existing = <String>[];
|
||||||
if (result.rows.isNotEmpty) {
|
for (var item in result.rows) {
|
||||||
existing = result.rows.first.assoc().values.cast<String>().toList();
|
var rec = item.assoc().values.first ?? "";
|
||||||
|
existing.add(rec.replaceAll("\\", "\\\\"));
|
||||||
}
|
}
|
||||||
var toRun = <String>[];
|
var toRun = <String>[];
|
||||||
|
|
||||||
|
@ -140,11 +148,15 @@ class MySqlMigrationRunner implements MigrationRunner {
|
||||||
await _init();
|
await _init();
|
||||||
var result = await connection
|
var result = await connection
|
||||||
.execute('SELECT path from migrations ORDER BY batch DESC;');
|
.execute('SELECT path from migrations ORDER BY batch DESC;');
|
||||||
|
|
||||||
|
// "mysql_client" driver will auto convert path containing "\\" to "\".
|
||||||
|
// So need to revert "\" back to "\\" for the migration logic to work
|
||||||
var existing = <String>[];
|
var existing = <String>[];
|
||||||
if (result.rows.isNotEmpty) {
|
for (var item in result.rows) {
|
||||||
var firstRow = result.rows.first;
|
var rec = item.assoc().values.first ?? "";
|
||||||
existing = firstRow.assoc().values.cast<String>().toList();
|
existing.add(rec.replaceAll("\\", "\\\\"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var toRun = existing.where(migrations.containsKey).toList();
|
var toRun = existing.where(migrations.containsKey).toList();
|
||||||
|
|
||||||
if (toRun.isNotEmpty) {
|
if (toRun.isNotEmpty) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:mysql_client/mysql_client.dart';
|
||||||
|
|
||||||
import 'table.dart';
|
import 'table.dart';
|
||||||
|
|
||||||
|
/// A MySQL database schema generator
|
||||||
class MySqlSchema extends Schema {
|
class MySqlSchema extends Schema {
|
||||||
final _log = Logger('MysqlSchema');
|
final _log = Logger('MysqlSchema');
|
||||||
|
|
||||||
|
@ -21,11 +22,13 @@ class MySqlSchema extends Schema {
|
||||||
await connection.transactional((ctx) async {
|
await connection.transactional((ctx) async {
|
||||||
var sql = compile();
|
var sql = compile();
|
||||||
var result = await ctx.execute(sql).catchError((e) {
|
var result = await ctx.execute(sql).catchError((e) {
|
||||||
|
print(e);
|
||||||
_log.severe('Failed to run query: [ $sql ]', e);
|
_log.severe('Failed to run query: [ $sql ]', e);
|
||||||
throw Exception(e);
|
throw Exception(e);
|
||||||
});
|
});
|
||||||
affectedRows = result.affectedRows.toInt();
|
affectedRows = result.affectedRows.toInt();
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
|
print(e);
|
||||||
_log.severe('Failed to run query in a transaction', e);
|
_log.severe('Failed to run query in a transaction', e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ abstract class MySqlGenerator {
|
||||||
if (column.type == ColumnType.timeStamp) {
|
if (column.type == ColumnType.timeStamp) {
|
||||||
str = ColumnType.dateTime.name;
|
str = ColumnType.dateTime.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (column.type.hasLength) {
|
if (column.type.hasLength) {
|
||||||
return '$str(${column.length})';
|
return '$str(${column.length})';
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,7 +21,11 @@ abstract class MySqlGenerator {
|
||||||
static String compileColumn(MigrationColumn column) {
|
static String compileColumn(MigrationColumn column) {
|
||||||
var buf = StringBuffer(columnType(column));
|
var buf = StringBuffer(columnType(column));
|
||||||
|
|
||||||
if (column.isNullable == false) buf.write(' NOT NULL');
|
if (!column.isNullable) {
|
||||||
|
buf.write(' NOT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default value
|
||||||
if (column.defaultValue != null) {
|
if (column.defaultValue != null) {
|
||||||
String s;
|
String s;
|
||||||
var value = column.defaultValue;
|
var value = column.defaultValue;
|
||||||
|
@ -47,6 +52,12 @@ abstract class MySqlGenerator {
|
||||||
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 int based primary key, apply NOT NULL
|
||||||
|
// and AUTO_INCREMENT
|
||||||
|
if (column.type == ColumnType.int) {
|
||||||
|
buf.write(' NOT NULL AUTO_INCREMENT');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var ref in column.externalReferences) {
|
for (var ref in column.externalReferences) {
|
||||||
|
@ -104,9 +115,11 @@ class MysqlTable extends Table {
|
||||||
|
|
||||||
if (indexBuf.isNotEmpty) {
|
if (indexBuf.isNotEmpty) {
|
||||||
for (var i = 0; i < indexBuf.length; i++) {
|
for (var i = 0; i < indexBuf.length; i++) {
|
||||||
buf.write(',\n${indexBuf[$1]}');
|
buf.write(',\n${indexBuf[i]}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import '../runner.dart';
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
import 'schema.dart';
|
import 'schema.dart';
|
||||||
|
|
||||||
|
/// A PostgreSQL database migration runner
|
||||||
class PostgresMigrationRunner implements MigrationRunner {
|
class PostgresMigrationRunner implements MigrationRunner {
|
||||||
final _log = Logger('PostgresMigrationRunner');
|
final _log = Logger('PostgresMigrationRunner');
|
||||||
|
|
||||||
|
@ -17,8 +18,10 @@ class PostgresMigrationRunner implements MigrationRunner {
|
||||||
|
|
||||||
PostgresMigrationRunner(this.connection,
|
PostgresMigrationRunner(this.connection,
|
||||||
{Iterable<Migration> migrations = const [], bool connected = false}) {
|
{Iterable<Migration> migrations = const [], bool connected = false}) {
|
||||||
if (migrations.isNotEmpty == true) migrations.forEach(addMigration);
|
if (migrations.isNotEmpty) {
|
||||||
_connected = connected == true;
|
migrations.forEach(addMigration);
|
||||||
|
}
|
||||||
|
_connected = connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -26,30 +29,30 @@ class PostgresMigrationRunner implements MigrationRunner {
|
||||||
_migrationQueue.addLast(migration);
|
_migrationQueue.addLast(migration);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _init() async {
|
Future<void> _init() async {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_connected = connection.isOpen;
|
||||||
if (!_connected) {
|
if (!_connected) {
|
||||||
//await connection.open();
|
throw Exception("PostgreSQL connection is not open");
|
||||||
//Connection.open(_endpoint!, settings: _settings);
|
|
||||||
_connected = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await connection.execute('''
|
await connection.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS "migrations" (
|
CREATE TABLE IF NOT EXISTS "migrations" (
|
||||||
id serial,
|
id serial,
|
||||||
batch integer,
|
batch integer,
|
||||||
path varchar,
|
path varchar(500),
|
||||||
PRIMARY KEY(id)
|
PRIMARY KEY(id)
|
||||||
);
|
);
|
||||||
''').then((result) {
|
''').then((result) {
|
||||||
_log.info('Check and create "migrations" table');
|
_log.info('Created "migrations" table');
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
_log.severe('Failed to create "migrations" table.');
|
_log.severe('Failed to create "migrations" table.');
|
||||||
|
throw e;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +76,8 @@ class PostgresMigrationRunner implements MigrationRunner {
|
||||||
var migration = migrations[k]!;
|
var migration = migrations[k]!;
|
||||||
var schema = PostgresSchema();
|
var schema = PostgresSchema();
|
||||||
migration.up(schema);
|
migration.up(schema);
|
||||||
_log.info('Added "$k" into "migrations" table.');
|
|
||||||
await schema.run(connection).then((_) {
|
var result = await schema.run(connection).then((_) {
|
||||||
return connection.runTx((ctx) async {
|
return connection.runTx((ctx) async {
|
||||||
var result = await ctx.execute(
|
var result = await ctx.execute(
|
||||||
"INSERT INTO MIGRATIONS (batch, path) VALUES ($batch, '$k')");
|
"INSERT INTO MIGRATIONS (batch, path) VALUES ($batch, '$k')");
|
||||||
|
@ -85,6 +88,9 @@ class PostgresMigrationRunner implements MigrationRunner {
|
||||||
_log.severe('Failed to insert into "migrations" table.');
|
_log.severe('Failed to insert into "migrations" table.');
|
||||||
return -1;
|
return -1;
|
||||||
});
|
});
|
||||||
|
if (result > 0) {
|
||||||
|
_log.info('Inserted "$k" into "migrations" table.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_log.warning('Nothing to add into "migrations" table.');
|
_log.warning('Nothing to add into "migrations" table.');
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:postgres/postgres.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'table.dart';
|
import 'table.dart';
|
||||||
|
|
||||||
|
/// A PostgreSQL database schema generator
|
||||||
class PostgresSchema extends Schema {
|
class PostgresSchema extends Schema {
|
||||||
final _log = Logger('PostgresSchema');
|
final _log = Logger('PostgresSchema');
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: angel3_migration_runner
|
name: angel3_migration_runner
|
||||||
version: 8.2.0
|
version: 8.2.1
|
||||||
description: Command-line based database migration runner for Angel3's ORM.
|
description: The implementation of database migration for Angel3 framework. Designed to work with Angel3 ORM.
|
||||||
homepage: https://angel3-framework.web.app/
|
homepage: https://angel3-framework.web.app/
|
||||||
repository: https://github.com/dart-backend/angel/tree/master/packages/orm/angel_migration_runner
|
repository: https://github.com/dart-backend/angel/tree/master/packages/orm/angel_migration_runner
|
||||||
environment:
|
environment:
|
||||||
|
@ -16,6 +16,7 @@ dependencies:
|
||||||
logging: ^1.2.0
|
logging: ^1.2.0
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
lints: ^4.0.0
|
lints: ^4.0.0
|
||||||
|
test: ^1.25.0
|
||||||
# dependency_overrides:
|
# dependency_overrides:
|
||||||
# angel3_orm:
|
# angel3_orm:
|
||||||
# path: ../angel_orm
|
# path: ../angel_orm
|
||||||
|
|
51
packages/orm/angel_migration_runner/test/mariadb_test.dart
Normal file
51
packages/orm/angel_migration_runner/test/mariadb_test.dart
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:angel3_migration_runner/angel3_migration_runner.dart';
|
||||||
|
import 'package:angel3_migration_runner/mariadb.dart';
|
||||||
|
import 'package:mysql1/mysql1.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import 'models/mysql_todo.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
late MySqlConnection conn;
|
||||||
|
late MigrationRunner runner;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
print("Setup...");
|
||||||
|
|
||||||
|
var host = Platform.environment['MYSQL_HOST'] ?? 'localhost';
|
||||||
|
var database = Platform.environment['MYSQL_DB'] ?? 'orm_test';
|
||||||
|
var username = Platform.environment['MYSQL_USERNAME'] ?? 'test';
|
||||||
|
var password = Platform.environment['MYSQL_PASSWORD'] ?? 'test123';
|
||||||
|
|
||||||
|
var settings = ConnectionSettings(
|
||||||
|
host: host,
|
||||||
|
port: 3306,
|
||||||
|
db: database,
|
||||||
|
user: username,
|
||||||
|
password: password);
|
||||||
|
conn = await MySqlConnection.connect(settings);
|
||||||
|
|
||||||
|
runner = MariaDbMigrationRunner(
|
||||||
|
conn,
|
||||||
|
migrations: [
|
||||||
|
UserMigration(),
|
||||||
|
TodoMigration(),
|
||||||
|
ItemMigration(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('MariaDB', () {
|
||||||
|
test('migrate tables', () async {
|
||||||
|
print("Test migration up");
|
||||||
|
runner.up();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
print("Teardown...");
|
||||||
|
await conn.close();
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import 'package:angel3_migration/angel3_migration.dart';
|
||||||
|
import 'package:angel3_orm/angel3_orm.dart';
|
||||||
|
|
||||||
|
class UserMigration implements Migration {
|
||||||
|
@override
|
||||||
|
void up(Schema schema) {
|
||||||
|
schema.create('users', (table) {
|
||||||
|
table
|
||||||
|
..integer('id').primaryKey()
|
||||||
|
..varChar('username', length: 32).unique()
|
||||||
|
..varChar('password')
|
||||||
|
..boolean('account_confirmed').defaultsTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void down(Schema schema) {
|
||||||
|
schema.drop('users');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TodoMigration implements Migration {
|
||||||
|
@override
|
||||||
|
void up(Schema schema) {
|
||||||
|
schema.create('todos', (table) {
|
||||||
|
table
|
||||||
|
..integer('id').primaryKey()
|
||||||
|
..integer('user_id').references('users', 'id').onDeleteCascade()
|
||||||
|
..varChar('text')
|
||||||
|
..boolean('completed').defaultsTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void down(Schema schema) {
|
||||||
|
schema.drop('todos');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ItemMigration extends Migration {
|
||||||
|
@override
|
||||||
|
void up(Schema schema) {
|
||||||
|
schema.create('items', (table) {
|
||||||
|
table
|
||||||
|
..integer('id').primaryKey()
|
||||||
|
..varChar('name', length: 64)
|
||||||
|
..timeStamp('created_at').defaultsTo(currentTimestamp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void down(Schema schema) => schema.drop('items');
|
||||||
|
}
|
53
packages/orm/angel_migration_runner/test/models/pg_todo.dart
Normal file
53
packages/orm/angel_migration_runner/test/models/pg_todo.dart
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import 'package:angel3_migration/angel3_migration.dart';
|
||||||
|
import 'package:angel3_orm/angel3_orm.dart';
|
||||||
|
|
||||||
|
class UserMigration implements Migration {
|
||||||
|
@override
|
||||||
|
void up(Schema schema) {
|
||||||
|
schema.create('users', (table) {
|
||||||
|
table
|
||||||
|
..serial('id').primaryKey()
|
||||||
|
..varChar('username', length: 32).unique()
|
||||||
|
..varChar('password')
|
||||||
|
..boolean('account_confirmed').defaultsTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void down(Schema schema) {
|
||||||
|
schema.drop('users');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TodoMigration implements Migration {
|
||||||
|
@override
|
||||||
|
void up(Schema schema) {
|
||||||
|
schema.create('todos', (table) {
|
||||||
|
table
|
||||||
|
..serial('id').primaryKey()
|
||||||
|
..integer('user_id').references('users', 'id').onDeleteCascade()
|
||||||
|
..varChar('text')
|
||||||
|
..boolean('completed').defaultsTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void down(Schema schema) {
|
||||||
|
schema.drop('todos');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ItemMigration extends Migration {
|
||||||
|
@override
|
||||||
|
void up(Schema schema) {
|
||||||
|
schema.create('items', (table) {
|
||||||
|
table
|
||||||
|
..serial('id').primaryKey()
|
||||||
|
..varChar('name', length: 64)
|
||||||
|
..timeStamp('created_at').defaultsTo(currentTimestamp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void down(Schema schema) => schema.drop('items');
|
||||||
|
}
|
63
packages/orm/angel_migration_runner/test/mysql_test.dart
Normal file
63
packages/orm/angel_migration_runner/test/mysql_test.dart
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:angel3_migration_runner/angel3_migration_runner.dart';
|
||||||
|
import 'package:angel3_migration_runner/mysql.dart';
|
||||||
|
import 'package:mysql_client/mysql_client.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import 'models/mysql_todo.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
late MySQLConnection conn;
|
||||||
|
late MigrationRunner runner;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
print("Setup...");
|
||||||
|
|
||||||
|
var host = Platform.environment['MYSQL_HOST'] ?? 'localhost';
|
||||||
|
var database = Platform.environment['MYSQL_DB'] ?? 'orm_test';
|
||||||
|
var username = Platform.environment['MYSQL_USERNAME'] ?? 'test';
|
||||||
|
var password = Platform.environment['MYSQL_PASSWORD'] ?? 'test123';
|
||||||
|
//var secure = !('false' == Platform.environment['MYSQL_SECURE']);
|
||||||
|
|
||||||
|
print("$host $database $username $password");
|
||||||
|
|
||||||
|
conn = await MySQLConnection.createConnection(
|
||||||
|
databaseName: database,
|
||||||
|
port: 3306,
|
||||||
|
host: host,
|
||||||
|
userName: username,
|
||||||
|
password: password,
|
||||||
|
secure: true);
|
||||||
|
|
||||||
|
await conn.connect();
|
||||||
|
|
||||||
|
runner = MySqlMigrationRunner(
|
||||||
|
conn,
|
||||||
|
migrations: [
|
||||||
|
UserMigration(),
|
||||||
|
TodoMigration(),
|
||||||
|
ItemMigration(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Mysql migrate tables', () {
|
||||||
|
test('up', () async {
|
||||||
|
print("Test migration up");
|
||||||
|
await runner.up();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reset', () async {
|
||||||
|
print("Test migration reset");
|
||||||
|
await runner.reset();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
print("Teardown...");
|
||||||
|
if (conn.connected) {
|
||||||
|
await conn.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
59
packages/orm/angel_migration_runner/test/pg_test.dart
Normal file
59
packages/orm/angel_migration_runner/test/pg_test.dart
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:angel3_migration_runner/angel3_migration_runner.dart';
|
||||||
|
import 'package:angel3_migration_runner/postgres.dart';
|
||||||
|
import 'package:postgres/postgres.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import 'models/pg_todo.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
late Connection conn;
|
||||||
|
late MigrationRunner runner;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
print("Setup...");
|
||||||
|
|
||||||
|
var host = Platform.environment['POSTGRES_HOST'] ?? 'localhost';
|
||||||
|
var database = Platform.environment['POSTGRES_DB'] ?? 'orm_test';
|
||||||
|
var username = Platform.environment['POSTGRES_USERNAME'] ?? 'test';
|
||||||
|
var password = Platform.environment['POSTGRES_PASSWORD'] ?? 'test123';
|
||||||
|
|
||||||
|
//print("$host $database $username $password");
|
||||||
|
|
||||||
|
conn = await Connection.open(
|
||||||
|
Endpoint(
|
||||||
|
host: host,
|
||||||
|
port: 5432,
|
||||||
|
database: database,
|
||||||
|
username: username,
|
||||||
|
password: password),
|
||||||
|
settings: ConnectionSettings(sslMode: SslMode.disable));
|
||||||
|
|
||||||
|
runner = PostgresMigrationRunner(
|
||||||
|
conn,
|
||||||
|
migrations: [
|
||||||
|
UserMigration(),
|
||||||
|
TodoMigration(),
|
||||||
|
ItemMigration(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('PostgreSQL migrate tables', () {
|
||||||
|
test('up', () async {
|
||||||
|
print("Test migration up");
|
||||||
|
await runner.up();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reset', () async {
|
||||||
|
print("Test migration reset");
|
||||||
|
await runner.reset();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
print("Teardown...");
|
||||||
|
await conn.close();
|
||||||
|
});
|
||||||
|
}
|
|
@ -89,7 +89,9 @@ Future<PostgreSqlPoolExecutor> connectToPostgresPool(
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
settings: PoolSettings(
|
settings: PoolSettings(
|
||||||
maxConnectionAge: Duration(hours: 1), maxConnectionCount: 5));
|
maxConnectionAge: Duration(hours: 1),
|
||||||
|
maxConnectionCount: 5,
|
||||||
|
sslMode: SslMode.disable));
|
||||||
|
|
||||||
// Run sql to create the tables in a transaction
|
// Run sql to create the tables in a transaction
|
||||||
await dbPool.runTx((conn) async {
|
await dbPool.runTx((conn) async {
|
||||||
|
|
Loading…
Reference in a new issue