Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions Mayflower/ConnectionContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
Expand All @@ -9,6 +10,7 @@ namespace Mayflower
public enum DatabaseProvider
{
SqlServer = 0,
MySql = 1
}

class ConnectionContext : IDisposable
Expand Down Expand Up @@ -37,10 +39,15 @@ internal ConnectionContext(Options options)
switch (Provider)
{
case DatabaseProvider.SqlServer:
_sql = new SqlServerStatements(options.GetMigrationsTable());
_sql = new SqlServerStatements(options.GetMigrationsTable(DatabaseProvider.SqlServer));
_connection = new SqlConnection(connStr);
Database = new SqlConnectionStringBuilder(connStr).InitialCatalog;
break;
case DatabaseProvider.MySql:
_sql = new MySqlStatements(options.GetMigrationsTable(DatabaseProvider.MySql));
_connection = new MySqlConnection(connStr);
Database = new MySqlConnectionStringBuilder(connStr).Database;
break;
default:
throw new Exception("Unsupported DatabaseProvider " + options.Provider);
}
Expand Down Expand Up @@ -85,7 +92,11 @@ internal void Rollback()
internal bool MigrationTableExists()
{
var cmd = _connection.NewCommand(_sql.DoesMigrationsTableExist);
return (int)cmd.ExecuteScalar() == 1;

if (Provider == DatabaseProvider.MySql)
return (long)cmd.ExecuteScalar() == 1;
else
return (int)cmd.ExecuteScalar() == 1;
}

internal void CreateMigrationsTable()
Expand Down
61 changes: 49 additions & 12 deletions Mayflower/ISqlStatements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,42 @@ interface ISqlStatements
string GetAlreadyRan { get; }
}

class MySqlStatements : ISqlStatements
{
public Regex CommandSplitter { get; } = new Regex(@";", RegexOptions.IgnoreCase | RegexOptions.Multiline);
public string DoesMigrationsTableExist { get; }
public string CreateMigrationsTable { get; }
public string RenameMigration { get; }
public string UpdateMigrationHash { get; }
public string InsertMigration { get; }
public string GetAlreadyRan { get; }

internal MySqlStatements(string migrationsTableName)
{
DoesMigrationsTableExist = $"SELECT count(*) FROM information_schema.tables WHERE table_name = '{migrationsTableName}';";

CreateMigrationsTable = $@"
CREATE TABLE {migrationsTableName}
(
Id int not null AUTO_INCREMENT,
Filename nvarchar(260) not null,
Hash varchar(40) not null,
ExecutionDate datetime not null,
Duration int not null,

constraint PK_{migrationsTableName} primary key clustered (Id),
constraint UX_{migrationsTableName}_Filename unique (Filename),
constraint UX_{migrationsTableName}_Hash unique (Hash)
);
";

RenameMigration = $"update {migrationsTableName} set Filename = @Filename where Hash = @Hash;";
UpdateMigrationHash = $"update {migrationsTableName} set Hash = @Hash, ExecutionDate = @ExecutionDate, Duration = @Duration where Filename = @Filename;";
InsertMigration = $"insert {migrationsTableName} (Filename, Hash, ExecutionDate, Duration) values (@Filename, @Hash, @ExecutionDate, @Duration);";
GetAlreadyRan = $"select * from {migrationsTableName} order by ExecutionDate, Id;";
}
}

class SqlServerStatements : ISqlStatements
{
public Regex CommandSplitter { get; } = new Regex(@"^\s*GO\s*$", RegexOptions.IgnoreCase | RegexOptions.Multiline);
Expand All @@ -49,19 +85,20 @@ internal SqlServerStatements(string migrationsTableName)
DoesMigrationsTableExist = $"SELECT count(*) FROM sys.tables WHERE name = '{migrationsTableName}';";

CreateMigrationsTable = $@"
CREATE TABLE [{migrationsTableName}]
(
Id int not null Identity(1,1),
Filename nvarchar(260) not null,
Hash varchar(40) not null,
ExecutionDate datetime not null,
Duration int not null,
CREATE TABLE [{migrationsTableName}]
(
Id int not null Identity(1,1),
Filename nvarchar(260) not null,
Hash varchar(40) not null,
ExecutionDate datetime not null,
Duration int not null,

constraint PK_{migrationsTableName} primary key clustered (Id),
constraint UX_{migrationsTableName}_Filename unique (Filename),
constraint UX_{migrationsTableName}_Hash unique (Hash)
);
";

constraint PK_{migrationsTableName} primary key clustered (Id),
constraint UX_{migrationsTableName}_Filename unique (Filename),
constraint UX_{migrationsTableName}_Hash unique (Hash)
);
";
RenameMigration = $"update [{migrationsTableName}] set Filename = @Filename where Hash = @Hash;";
UpdateMigrationHash = $"update [{migrationsTableName}] set Hash = @Hash, ExecutionDate = @ExecutionDate, Duration = @Duration where Filename = @Filename;";
InsertMigration = $"insert [{migrationsTableName}] (Filename, Hash, ExecutionDate, Duration) values (@Filename, @Hash, @ExecutionDate, @Duration);";
Expand Down
7 changes: 7 additions & 0 deletions Mayflower/Mayflower.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="MySql.Data, Version=6.9.9.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.9\lib\net45\MySql.Data.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
Expand All @@ -51,6 +54,10 @@
<Compile Include="Options.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
22 changes: 15 additions & 7 deletions Mayflower/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class Options
public string MigrationsTable { get; set; }
public TextWriter Output { get; set; }
public bool Force { get; set; }
public DatabaseProvider Provider { get; set; } // there's no command line param for this yet because there's only one provider
public DatabaseProvider Provider { get; set; }

public void AssertValid()
{
Expand All @@ -34,19 +34,27 @@ public void AssertValid()

internal string GetConnectionString(DatabaseProvider provider)
{
if (provider != DatabaseProvider.SqlServer)
throw new Exception($"Unsupported DatabaseProvider " + provider);

if (!string.IsNullOrEmpty(ConnectionString))
return ConnectionString;

var server = string.IsNullOrEmpty(Server) ? "localhost" : Server;
return $"Persist Security Info=False;Integrated Security=true;Initial Catalog={Database};server={server}";

if (provider == DatabaseProvider.SqlServer)
return $"Persist Security Info=False;Integrated Security=true;Initial Catalog={Database};server={server}";
else if (provider == DatabaseProvider.MySql)
return $"Database={Database};server={server}";
else
throw new Exception("Database provider is not supported.");
}

internal string GetMigrationsTable()
internal string GetMigrationsTable(DatabaseProvider provider)
{
return string.IsNullOrEmpty(MigrationsTable) ? "Migrations" : MigrationsTable;
if (provider == DatabaseProvider.SqlServer)
return string.IsNullOrEmpty(MigrationsTable) ? "Migrations" : MigrationsTable;
else if (provider == DatabaseProvider.MySql)
return string.IsNullOrEmpty(MigrationsTable) ? "migrations" : MigrationsTable;
else
throw new Exception("Database provider is not supported.");
}

internal string GetFolder()
Expand Down
9 changes: 9 additions & 0 deletions Mayflower/app.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.9.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>
</configuration>
4 changes: 4 additions & 0 deletions Mayflower/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MySql.Data" version="6.9.9" targetFramework="net452" />
</packages>
2 changes: 1 addition & 1 deletion MayflowerCLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ static Command TryParseArgs(string[] args, out Options options)
var showVersion = false;
var getCount = false;
var optionsTmp = options = new Options();

var optionSet = new OptionSet()
{
{ "h|help", "Shows this help message.", v => showHelp= v != null },
{"c|connection=", "A SQL Server connection string. For integrated auth, you can use --database and --server instead.", v => optionsTmp.ConnectionString = v },
{"d|database=", "Generates an integrated auth connection string for the specified database.", v => optionsTmp.Database = v },
{"p|provider=", "The database provider.", v => optionsTmp.Provider = (DatabaseProvider) Enum.Parse(typeof(DatabaseProvider), v) },
{"s|server=", "Generates an integrated auth connection string with the specified server (default: localhost).", v=> optionsTmp.Server = v },
{"f|folder=", "The folder containing your .sql migration files (defaults to current working directory).", v => optionsTmp.MigrationsFolder = v },
{"timeout=", "Command timeout duration in seconds (default: 30)", v => optionsTmp.CommandTimeout = int.Parse(v) },
Expand Down
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Mayflower.NET

Mayflower is a simple, forward-only, database migrator for SQL Server based on the migrator which Stack Overflow uses.
Mayflower is a simple, forward-only, database migrator for SQL Server and MySql based on the migrator which Stack Overflow uses.

## Usage

### Creating Migrations

A migration is just plain T-SQL saved in a .sql file. Individual commands are separated with the `GO` keyword, just like when using [SSMS](https://msdn.microsoft.com/en-us/library/mt238290.aspx). For example:
A migration is just plain T-SQL saved in a .sql file.

#### SQL
Individual commands are separated with the `GO` keyword, just like when using [SSMS](https://msdn.microsoft.com/en-us/library/mt238290.aspx). For example:

```sql
CREATE TABLE One
Expand All @@ -22,6 +25,21 @@ INSERT INTO One (Name) VALUES ('Wystan')
GO
```

#### MySql
Individual commands are separated with the `;`. For example:

```sql
CREATE TABLE One
(
Id int not null AUTO_INCREMENT,
Name nvarchar(50) not null,

constraint PK_One primary key clustered (Id)
);

INSERT INTO One (Name) VALUES ('Wystan');
```

> Migrations are run in a transaction by default, which allows them to be rolled back if any command fails. You can disable this transaction for a specific migration by beginning the file with `-- no transaction --`.

We recommend prefixing migration file names with a zero-padded number so that the migrations are listed in chronological order. For example, a directory of migrations might look like:
Expand Down Expand Up @@ -67,6 +85,8 @@ Usage: mayflower [OPTIONS]+
instead.
-d, --database=VALUE Generates an integrated auth connection string
for the specified database.
-p, --provider=VALUE The database provider you want to use. Options are
SQL and MySql.
-s, --server=VALUE Generates an integrated auth connection string
with the specified server (default: localhost).
-f, --folder=VALUE The folder containing your .sql migration files
Expand Down
15 changes: 0 additions & 15 deletions SampleMigrations/0001 - Add One and Two tables.sql

This file was deleted.

8 changes: 0 additions & 8 deletions SampleMigrations/0002 - Add Three table.sql

This file was deleted.

13 changes: 0 additions & 13 deletions SampleMigrations/0003 - Insert Three data.sql

This file was deleted.

2 changes: 0 additions & 2 deletions SampleMigrations/0004 - Add FavoriteNumber to Two.sql

This file was deleted.

13 changes: 13 additions & 0 deletions SampleMigrations/MySql/0001 - Add One and Two tables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
create table One
(
Id int not null AUTO_INCREMENT,

constraint PK_One primary key clustered (Id)
);

create table Two
(
Id int not null AUTO_INCREMENT,

constraint PK_Two primary key clustered (Id)
);
7 changes: 7 additions & 0 deletions SampleMigrations/MySql/0002 - Add Three table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
create table Three
(
Id int not null AUTO_INCREMENT,
Name nvarchar(50) not null,

constraint PK_Three primary key clustered (Id)
);
9 changes: 9 additions & 0 deletions SampleMigrations/MySql/0003 - Insert Three data.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- no transaction --

insert into Three (Name) values ('Adrian');

insert into Three (Name) values ('Adrienne');

insert into Three (Name) values ('Adriano');

insert into Three (Name) values ('Adriana');
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alter table Two add FavoriteNumber int null;
Loading